From a1665445837c05aa38b2f50f7bb17347aa6a1986 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Thu, 25 Aug 2022 19:19:46 +0800 Subject: [PATCH] Forbid pass non-node object to `print()` (#13293) --- src/language-js/print/type-parameters.js | 7 ++- src/language-js/printer-estree.js | 61 ++++++++++++++++--- .../traverse/visitor-keys.evaluate.js | 7 ++- tests/unit/__snapshots__/visitor-keys.js.snap | 6 +- 4 files changed, 67 insertions(+), 14 deletions(-) diff --git a/src/language-js/print/type-parameters.js b/src/language-js/print/type-parameters.js index bf68e5441..efad3bc25 100644 --- a/src/language-js/print/type-parameters.js +++ b/src/language-js/print/type-parameters.js @@ -112,8 +112,11 @@ function printTypeParameter(path, options, print) { const node = path.getValue(); const parts = []; const parent = path.getParentNode(); + + const name = node.type === "TSTypeParameter" ? print("name") : node.name; + if (parent.type === "TSMappedType") { - parts.push("[", print("name")); + parts.push("[", name); if (node.constraint) { parts.push(" in ", print("constraint")); } @@ -139,7 +142,7 @@ function printTypeParameter(path, options, print) { parts.push("out "); } - parts.push(print("name")); + parts.push(name); if (node.bound) { parts.push(": ", print("bound")); diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js index a1b88d9d2..96f8f3723 100644 --- a/src/language-js/printer-estree.js +++ b/src/language-js/printer-estree.js @@ -89,12 +89,21 @@ import { printLiteral } from "./print/literal.js"; import { printDecorators } from "./print/decorators.js"; function genericPrint(path, options, print, args) { + const node = path.getValue(); + + if (node === undefined || node === null) { + return ""; + } + + if (process.env.NODE_ENV !== "production") { + ensurePrintingNode(path); + } + const printed = printPathNoParens(path, options, print, args); if (!printed) { return ""; } - const node = path.getValue(); const { type } = node; // Their decorators are handled themselves, and they can't have parentheses if ( @@ -170,14 +179,6 @@ function printPathNoParens(path, options, print, args) { const node = path.getValue(); const semi = options.semi ? ";" : ""; - if (!node) { - return ""; - } - - if (typeof node === "string") { - return node; - } - for (const printer of [ printLiteral, printHtmlBinding, @@ -778,7 +779,7 @@ function printPathNoParens(path, options, print, args) { case "TaggedTemplateExpression": return [print("tag"), print("typeParameters"), print("quasi")]; case "PrivateIdentifier": - return ["#", print("name")]; + return ["#", node.name]; case "PrivateName": return ["#", print("id")]; @@ -846,6 +847,46 @@ function canAttachComment(node) { ); } +function ensurePrintingNode(path) { + let name = path.getName(); + + // AST root + if (name === null) { + return; + } + + if (typeof name === "number") { + /* + Nodes in array are stored in path.stack like + + ```js + [ + parentNode, + property, // <- + array, + index, + node, + ] + ``` + */ + name = path.stack[path.stack.length - 4]; + } + + const parent = path.getParentNode(); + const visitorKeys = getVisitorKeys(parent); + if (visitorKeys.includes(name)) { + return; + } + + /* istanbul ignore next */ + throw Object.assign(new Error("Calling `print()` on non-node object."), { + parentNode: parent, + allowedProperties: visitorKeys, + printingProperty: name, + printingValue: path.getValue(), + }); +} + const printer = { preprocess, print: genericPrint, diff --git a/src/language-js/traverse/visitor-keys.evaluate.js b/src/language-js/traverse/visitor-keys.evaluate.js index c0a6a4d4b..939b76d20 100644 --- a/src/language-js/traverse/visitor-keys.evaluate.js +++ b/src/language-js/traverse/visitor-keys.evaluate.js @@ -10,7 +10,7 @@ const angularVisitorKeys = { NGQuotedExpression: [], NGMicrosyntax: ["body"], NGMicrosyntaxKey: [], - NGMicrosyntaxExpression: ["expression", "name"], + NGMicrosyntaxExpression: ["expression", "alias"], NGMicrosyntaxKeyedExpression: ["key", "expression"], NGMicrosyntaxLet: ["key", "value"], NGMicrosyntaxAs: ["key", "alias"], @@ -46,6 +46,11 @@ const additionalVisitorKeys = { ArrowFunctionExpression: ["predicate"], DeclareFunction: ["predicate"], FunctionDeclaration: ["predicate"], + ClassProperty: ["variance"], + ClassPrivateProperty: ["variance"], + EnumBooleanMember: ["init"], + + Property: ["decorators"], }; export default unionVisitorKeys([ diff --git a/tests/unit/__snapshots__/visitor-keys.js.snap b/tests/unit/__snapshots__/visitor-keys.js.snap index 8b1500210..ca074cd19 100644 --- a/tests/unit/__snapshots__/visitor-keys.js.snap +++ b/tests/unit/__snapshots__/visitor-keys.js.snap @@ -123,12 +123,14 @@ exports[`visitor keys estree 1`] = ` "value", "decorators", "typeAnnotation", + "variance", ], "ClassProperty": [ "key", "value", "typeAnnotation", "decorators", + "variance", ], "ConditionalExpression": [ "test", @@ -212,6 +214,7 @@ exports[`visitor keys estree 1`] = ` ], "EnumBooleanMember": [ "id", + "init", ], "EnumDeclaration": [ "id", @@ -468,7 +471,7 @@ exports[`visitor keys estree 1`] = ` ], "NGMicrosyntaxExpression": [ "expression", - "name", + "alias", ], "NGMicrosyntaxKey": [], "NGMicrosyntaxKeyedExpression": [ @@ -597,6 +600,7 @@ exports[`visitor keys estree 1`] = ` "Property": [ "key", "value", + "decorators", ], "PropertyDefinition": [ "decorators",