From 68c8ee3ab70187972aef4c4e866bcf29da70a207 Mon Sep 17 00:00:00 2001 From: Ilya Volodin Date: Wed, 20 May 2020 13:17:52 -0400 Subject: [PATCH] Fix: Stop path analyzer on unknown nodes (#13305) --- lib/linter/linter.js | 3 +- tests/fixtures/parsers/linter-test-parsers.js | 3 +- tests/fixtures/parsers/non-js-parser.js | 238 ++++++++++++++++++ tests/lib/linter/linter.js | 24 ++ 4 files changed, 266 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/parsers/non-js-parser.js diff --git a/lib/linter/linter.js b/lib/linter/linter.js index 1d021d1e82e..f9f38790b3c 100644 --- a/lib/linter/linter.js +++ b/lib/linter/linter.js @@ -938,7 +938,8 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parser }); }); - const eventGenerator = new CodePathAnalyzer(new NodeEventGenerator(emitter)); + // only run code path analyzer if the top level node is "Program", skip otherwise + const eventGenerator = nodeQueue[0].node.type === "Program" ? new CodePathAnalyzer(new NodeEventGenerator(emitter)) : new NodeEventGenerator(emitter); nodeQueue.forEach(traversalInfo => { currentNode = traversalInfo.node; diff --git a/tests/fixtures/parsers/linter-test-parsers.js b/tests/fixtures/parsers/linter-test-parsers.js index dc81e34ecb9..9f871a4b583 100644 --- a/tests/fixtures/parsers/linter-test-parsers.js +++ b/tests/fixtures/parsers/linter-test-parsers.js @@ -10,5 +10,6 @@ module.exports = { noLineError: require("./no-line-error"), enhancedParser2: require("./enhanced-parser2"), enhancedParser3: require("./enhanced-parser3"), - throwsWithOptions: require("./throws-with-options") + throwsWithOptions: require("./throws-with-options"), + nonJSParser: require('./non-js-parser') }; diff --git a/tests/fixtures/parsers/non-js-parser.js b/tests/fixtures/parsers/non-js-parser.js new file mode 100644 index 00000000000..86b6d939367 --- /dev/null +++ b/tests/fixtures/parsers/non-js-parser.js @@ -0,0 +1,238 @@ +/** + * Source code: + * function foo(a: number=0): Foo { } + */ + +exports.parseForESLint = () => ({ + ast: { + "kind": "Document", + "definitions": [ + { + "kind": "ObjectTypeExtension", + "name": { + "kind": "Name", + "value": "Query", + "loc": { + "start": 12, + "end": 17 + }, + "type": "Name" + }, + "interfaces": [], + "directives": [], + "fields": [ + { + "kind": "FieldDefinition", + "name": { + "kind": "Name", + "value": "login", + "loc": { + "start": 24, + "end": 29 + }, + "type": "Name" + }, + "arguments": [ + { + "kind": "InputValueDefinition", + "name": { + "kind": "Name", + "value": "input", + "loc": { + "start": 30, + "end": 35 + }, + "type": "Name" + }, + "type": "InputValueDefinition", + "directives": [], + "loc": { + "start": 30, + "end": 49 + }, + "fieldType": { + "kind": "NonNullType", + "type": "NonNullType", + "loc": { + "start": 37, + "end": 49 + }, + "fieldType": { + "kind": "NamedType", + "name": { + "kind": "Name", + "value": "Credentials", + "loc": { + "start": 37, + "end": 48 + }, + "type": "Name" + }, + "loc": { + "start": 37, + "end": 48 + }, + "type": "NamedType" + } + } + } + ], + "type": "FieldDefinition", + "directives": [], + "loc": { + "start": 24, + "end": 63 + }, + "fieldType": { + "kind": "NamedType", + "name": { + "kind": "Name", + "value": "UserProfile", + "loc": { + "start": 52, + "end": 63 + }, + "type": "Name" + }, + "loc": { + "start": 52, + "end": 63 + }, + "type": "NamedType" + } + } + ], + "loc": { + "start": 0, + "end": 65 + }, + "type": "ObjectTypeExtension" + }, + { + "kind": "InputObjectTypeDefinition", + "name": { + "kind": "Name", + "value": "Credentials", + "loc": { + "start": 73, + "end": 84 + }, + "type": "Name" + }, + "directives": [], + "fields": [ + { + "kind": "InputValueDefinition", + "name": { + "kind": "Name", + "value": "login", + "loc": { + "start": 91, + "end": 96 + }, + "type": "Name" + }, + "type": "InputValueDefinition", + "directives": [], + "loc": { + "start": 91, + "end": 105 + }, + "fieldType": { + "kind": "NonNullType", + "type": "NonNullType", + "loc": { + "start": 98, + "end": 105 + }, + "fieldType": { + "kind": "NamedType", + "name": { + "kind": "Name", + "value": "String", + "loc": { + "start": 98, + "end": 104 + }, + "type": "Name" + }, + "loc": { + "start": 98, + "end": 104 + }, + "type": "NamedType" + } + } + }, + { + "kind": "InputValueDefinition", + "name": { + "kind": "Name", + "value": "password", + "loc": { + "start": 110, + "end": 118 + }, + "type": "Name" + }, + "type": "InputValueDefinition", + "directives": [], + "loc": { + "start": 110, + "end": 127 + }, + "fieldType": { + "kind": "NonNullType", + "type": "NonNullType", + "loc": { + "start": 120, + "end": 127 + }, + "fieldType": { + "kind": "NamedType", + "name": { + "kind": "Name", + "value": "String", + "loc": { + "start": 120, + "end": 126 + }, + "type": "Name" + }, + "loc": { + "start": 120, + "end": 126 + }, + "type": "NamedType" + } + } + } + ], + "loc": { + "start": 67, + "end": 129 + }, + "type": "InputObjectTypeDefinition" + } + ], + "loc": { + "start": 0, + "end": 130 + }, + "type": "Document", + "tokens": [], + "comments": [], + "range": {} + }, + services: {}, + scopeManager: { variables: [], scopes: [{ set: new Map(), variables: [], through: [] }], getDeclaredVariables: () => {} }, + visitorKeys: { + Document: ['definitions'], + ObjectTypeDefinition: ['interfaces', 'directives', 'fields'], + ObjectTypeExtension: ['interfaces', 'directives', 'fields'], + InputObjectTypeDefinition: ['directives', 'fields'], + InputValueDefinition: ['directives', 'fieldType'], + FieldDefinition: ['directives', 'fieldType', 'arguments'], + EnumTypeDefinition: ['directives', 'values'] + } +}); diff --git a/tests/lib/linter/linter.js b/tests/lib/linter/linter.js index 56d028e5c62..f2efc00c946 100644 --- a/tests/lib/linter/linter.js +++ b/tests/lib/linter/linter.js @@ -5247,6 +5247,30 @@ var a = "test2"; assert.strictEqual(messages.length, 0); }); + it("should not throw or return errors when the custom parser returns unknown AST nodes", () => { + const code = "foo && bar %% baz"; + + const nodes = []; + + linter.defineRule("collect-node-types", () => ({ + "*"(node) { + nodes.push(node.type); + } + })); + + linter.defineParser("non-js-parser", testParsers.nonJSParser); + + const messages = linter.verify(code, { + parser: "non-js-parser", + rules: { + "collect-node-types": "error" + } + }, filename, true); + + assert.strictEqual(messages.length, 0); + assert.isTrue(nodes.length > 0); + }); + it("should strip leading line: prefix from parser error", () => { linter.defineParser("line-error", testParsers.lineError); const messages = linter.verify(";", { parser: "line-error" }, "filename");