-
-
Notifications
You must be signed in to change notification settings - Fork 4.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update: Improve parser integrations (fixes #8392) #8755
Changes from 3 commits
a8fa8dd
6859007
fbd7552
1ed2730
2b60557
071be3a
4d65435
b6542d3
47a91ca
caae710
61d14be
9c56cfe
335d792
6bf95ef
351722a
a489ddc
0c3d626
00fc689
f5d7be4
3757df3
c1a7d9e
530b082
6bed81e
a2e4a83
c5ceadb
de2c9b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,6 +43,8 @@ const MAX_AUTOFIX_PASSES = 10; | |
* @property {ASTNode} ast The ESTree AST Program node. | ||
* @property {Object} services An object containing additional services related | ||
* to the parser. | ||
* @property {ScopeManager|null} scope The scope of this AST. | ||
* @property {Object|null} visitorKeys The visitor keys to traverse this AST. | ||
*/ | ||
|
||
//------------------------------------------------------------------------------ | ||
|
@@ -763,9 +765,7 @@ class Linter extends EventEmitter { | |
*/ | ||
verify(textOrSourceCode, config, filenameOrOptions, saveState) { | ||
const text = (typeof textOrSourceCode === "string") ? textOrSourceCode : null; | ||
let ast, | ||
parseResult, | ||
allowInlineConfig; | ||
let allowInlineConfig; | ||
|
||
// evaluate arguments | ||
if (typeof filenameOrOptions === "object") { | ||
|
@@ -805,7 +805,7 @@ class Linter extends EventEmitter { | |
return this.messages; | ||
} | ||
|
||
parseResult = parse( | ||
const parseResult = parse( | ||
stripUnicodeBOM(text).replace(astUtils.SHEBANG_MATCHER, (match, captured) => `//${captured}`), | ||
config, | ||
this.currentFilename, | ||
|
@@ -814,27 +814,35 @@ class Linter extends EventEmitter { | |
|
||
// if this result is from a parseForESLint() method, normalize | ||
if (parseResult && parseResult.ast) { | ||
ast = parseResult.ast; | ||
} else { | ||
ast = parseResult; | ||
parseResult = null; | ||
} | ||
|
||
if (ast) { | ||
this.sourceCode = new SourceCode(text, ast); | ||
// Save all returned values of `parse()` because this source code object can be reused. | ||
// This ensures the same result when reused. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused about this comment -- it seems to imply that ASTs are cached, but I don't see them cached anywhere. Is there something I'm missing? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The first argument of |
||
this.sourceCode = new SourceCode( | ||
text, | ||
parseResult.ast, | ||
parseResult.services, | ||
parseResult.scope, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be better to call the property There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Before this PR is merged, we should add documentation for the new parser APIs. (However, maybe we should wait until we decide on the APIs before adding documentation.) |
||
parseResult.visitorKeys | ||
); | ||
} else if (parseResult) { | ||
this.sourceCode = new SourceCode( | ||
text, | ||
parseResult | ||
); | ||
} else { | ||
this.sourceCode = null; | ||
} | ||
|
||
} else { | ||
this.sourceCode = textOrSourceCode; | ||
ast = this.sourceCode.ast; | ||
} | ||
|
||
// if espree failed to parse the file, there's no sense in setting up rules | ||
if (ast) { | ||
if (this.sourceCode) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this include a check for |
||
|
||
// parse global comments and modify config | ||
if (allowInlineConfig !== false) { | ||
config = modifyConfigsFromComments(this.currentFilename, ast, config, this); | ||
config = modifyConfigsFromComments(this.currentFilename, this.sourceCode.ast, config, this); | ||
} | ||
|
||
// ensure that severities are normalized in the config | ||
|
@@ -865,7 +873,7 @@ class Linter extends EventEmitter { | |
key, this, severity, options, | ||
config.settings, config.parserOptions, config.parser, | ||
ruleCreator.meta, | ||
(parseResult && parseResult.services ? parseResult.services : {}) | ||
this.sourceCode.parserServices || {} | ||
); | ||
|
||
const rule = ruleCreator.create ? ruleCreator.create(ruleContext) | ||
|
@@ -890,21 +898,25 @@ class Linter extends EventEmitter { | |
|
||
const ecmaFeatures = this.currentConfig.parserOptions.ecmaFeatures || {}; | ||
const ecmaVersion = this.currentConfig.parserOptions.ecmaVersion || 5; | ||
const sourceType = this.currentConfig.parserOptions.sourceType || "script"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's a breaking change. This line is copied from https://github.com/eslint/eslint/pull/8755/files/fbd75522568fa06aacfa8375bc971b79c83ae304#diff-98bf811b7b68f5089bfb2605df5b2548L900 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, that seems like a bug to me. Anyway, it can be addressed in a different issue. |
||
|
||
// gather scope data that may be needed by the rules | ||
this.scopeManager = eslintScope.analyze(ast, { | ||
ignoreEval: true, | ||
nodejsScope: ecmaFeatures.globalReturn, | ||
impliedStrict: ecmaFeatures.impliedStrict, | ||
ecmaVersion, | ||
sourceType: this.currentConfig.parserOptions.sourceType || "script", | ||
fallback: Traverser.getKeys | ||
}); | ||
this.scopeManager = | ||
this.sourceCode.scopeManager || | ||
eslintScope.analyze(this.sourceCode.ast, { | ||
ignoreEval: true, | ||
nodejsScope: ecmaFeatures.globalReturn, | ||
impliedStrict: ecmaFeatures.impliedStrict, | ||
ecmaVersion, | ||
sourceType, | ||
fallback: Traverser.getKeys, | ||
childVisitorKeys: this.sourceCode.visitorKeys || Traverser.DEFAULT_VISITOR_KEYS | ||
}); | ||
|
||
this.currentScopes = this.scopeManager.scopes; | ||
|
||
// augment global scope with declared global variables | ||
addDeclaredGlobals(ast, this.currentScopes[0], this.currentConfig, this.environments); | ||
addDeclaredGlobals(this.sourceCode.ast, this.currentScopes[0], this.currentConfig, this.environments); | ||
|
||
let eventGenerator = new NodeEventGenerator(this); | ||
|
||
|
@@ -916,7 +928,8 @@ class Linter extends EventEmitter { | |
* automatically be informed that this type of node has been found | ||
* and react accordingly. | ||
*/ | ||
this.traverser.traverse(ast, { | ||
this.traverser.traverse(this.sourceCode.ast, { | ||
keys: this.sourceCode.visitorKeys, | ||
enter(node, parent) { | ||
node.parent = parent; | ||
eventGenerator.enterNode(node); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -111,13 +111,14 @@ const isInLoop = { | |
* | ||
* @param {ASTNode} root - A node to check. | ||
* This node is one of BinaryExpression or ConditionalExpression. | ||
* @param {SourceCode} sourceCode - The source code object of this context. | ||
* @returns {boolean} `true` if the node is dynamic. | ||
*/ | ||
function hasDynamicExpressions(root) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick: Could these functions be moved into the |
||
function hasDynamicExpressions(root, sourceCode) { | ||
let retv = false; | ||
const traverser = new Traverser(); | ||
|
||
traverser.traverse(root, { | ||
Traverser.traverse(root, { | ||
keys: sourceCode.visitorKeys, | ||
enter(node) { | ||
if (DYNAMIC_PATTERN.test(node.type)) { | ||
retv = true; | ||
|
@@ -135,9 +136,10 @@ function hasDynamicExpressions(root) { | |
* Creates the loop condition information from a given reference. | ||
* | ||
* @param {eslint-scope.Reference} reference - A reference to create. | ||
* @param {SourceCode} sourceCode - The source code object of this context. | ||
* @returns {LoopConditionInfo|null} Created loop condition info, or null. | ||
*/ | ||
function toLoopCondition(reference) { | ||
function toLoopCondition(reference, sourceCode) { | ||
if (reference.init) { | ||
return null; | ||
} | ||
|
@@ -170,7 +172,7 @@ function toLoopCondition(reference) { | |
if (GROUP_PATTERN.test(node.type)) { | ||
|
||
// If this expression is dynamic, no need to check. | ||
if (hasDynamicExpressions(node)) { | ||
if (hasDynamicExpressions(node, sourceCode)) { | ||
break; | ||
} else { | ||
group = node; | ||
|
@@ -254,6 +256,7 @@ module.exports = { | |
}, | ||
|
||
create(context) { | ||
const sourceCode = context.getSourceCode(); | ||
let groupMap = null; | ||
|
||
/** | ||
|
@@ -319,7 +322,7 @@ module.exports = { | |
// Gets references that exist in loop conditions. | ||
const conditions = variable | ||
.references | ||
.map(toLoopCondition) | ||
.map(reference => toLoopCondition(reference, sourceCode)) | ||
.filter(Boolean); | ||
|
||
if (conditions.length === 0) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these properties intended to be used outside of
linter
? If not, can we clarify that they're private (e.g. withlinter._scope
andlinter._visitorKeys
)?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
linter
does not have those properties.This is
typedef
of the result ofparser.parseForESLint
method, so this is public.