blob: 37df019385239c0676011fc23dd80b9b738f989a [file] [log] [blame]
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/// @docImport 'package:analyzer/src/lint/linter.dart';
/// @docImport 'package:analyzer/src/lint/linter_visitor.dart';
library;
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/exception/exception.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/generated/engine.dart' show AnalysisEngine;
export 'package:analyzer/src/dart/ast/constant_evaluator.dart';
/// An object used to locate the [AstNode] associated with a source range.
/// More specifically, they will return the deepest [AstNode] which completely
/// encompasses the specified range with some exceptions:
///
/// - Offsets that fall between the name and type/formal parameter list of a
/// declaration will return the declaration node and not the parameter list
/// node.
class NodeLocator2 extends UnifyingAstVisitor<void> {
/// The inclusive start offset of the range used to identify the node.
final int _startOffset;
/// The inclusive end offset of the range used to identify the node.
final int _endOffset;
/// The found node or `null` if there is no such node.
AstNode? _foundNode;
/// Initialize a newly created locator to locate the deepest [AstNode] for
/// which `node.offset <= [startOffset]` and `[endOffset] < node.end`.
///
/// If [endOffset] is not provided, then it is considered the same as the
/// given [startOffset].
NodeLocator2(int startOffset, [int? endOffset])
: _startOffset = startOffset,
_endOffset = endOffset ?? startOffset;
/// Search within the given AST [node] and return the node that was found,
/// or `null` if no node was found.
AstNode? searchWithin(AstNode? node) {
if (node == null) {
return null;
}
try {
node.accept(this);
} catch (exception, stackTrace) {
// TODO(39284): should this exception be silent?
AnalysisEngine.instance.instrumentationService.logException(
SilentException(
'Unable to locate element at offset '
'($_startOffset - $_endOffset)',
exception,
stackTrace,
),
);
return null;
}
return _foundNode;
}
@override
void visitClassDeclaration(ClassDeclaration node) {
// Names do not have AstNodes but offsets at the end should be treated as
// part of the declaration (not parameter list).
if (_startOffset == _endOffset && _startOffset == node.name.end) {
_foundNode = node;
return;
}
super.visitClassDeclaration(node);
}
@override
void visitConstructorDeclaration(ConstructorDeclaration node) {
// Names do not have AstNodes but offsets at the end should be treated as
// part of the declaration (not parameter list).
if (_startOffset == _endOffset &&
_startOffset == (node.name ?? node.returnType).end) {
_foundNode = node;
return;
}
super.visitConstructorDeclaration(node);
}
@override
void visitFunctionDeclaration(FunctionDeclaration node) {
// Names do not have AstNodes but offsets at the end should be treated as
// part of the declaration (not parameter list).
if (_startOffset == _endOffset && _startOffset == node.name.end) {
_foundNode = node;
return;
}
super.visitFunctionDeclaration(node);
}
@override
void visitMethodDeclaration(MethodDeclaration node) {
// Names do not have AstNodes but offsets at the end should be treated as
// part of the declaration (not parameter list).
if (_startOffset == _endOffset && _startOffset == node.name.end) {
_foundNode = node;
return;
}
super.visitMethodDeclaration(node);
}
@override
void visitNode(AstNode node) {
// Don't visit a new tree if the result has been already found.
if (_foundNode != null) {
return;
}
// Check whether the current node covers the selection.
Token beginToken = node.beginToken;
Token endToken = node.endToken;
// Don't include synthetic tokens.
while (endToken != beginToken) {
// Fasta scanner reports unterminated string literal errors
// and generates a synthetic string token with non-zero length.
// Because of this, check for length > 0 rather than !isSynthetic.
if (endToken.isEof || endToken.length > 0) {
break;
}
endToken = endToken.previous!;
}
int end = endToken.end;
int start = node.offset;
if (end <= _startOffset || start > _endOffset) {
return;
}
// Check children.
try {
node.visitChildren(this);
} catch (exception, stackTrace) {
// Ignore the exception and proceed in order to visit the rest of the
// structure.
// TODO(39284): should this exception be silent?
AnalysisEngine.instance.instrumentationService.logException(
SilentException(
"Exception caught while traversing an AST structure.",
exception,
stackTrace,
),
);
}
// Found a child.
if (_foundNode != null) {
return;
}
// Check this node.
if (start <= _startOffset && _endOffset < end) {
_foundNode = node;
}
}
}
/// An object that will replace one child node in an AST node with another node.
class NodeReplacer extends ThrowingAstVisitor<bool> {
/// The node being replaced.
final AstNode _oldNode;
/// The node that is replacing the old node.
final AstNode _newNode;
/// Initialize a newly created node locator to replace the [_oldNode] with the
/// [_newNode].
NodeReplacer._(this._oldNode, this._newNode);
@override
bool visitAdjacentStrings(covariant AdjacentStringsImpl node) {
if (_replaceInList(node.strings)) {
return true;
}
return visitNode(node);
}
bool visitAnnotatedNode(covariant AnnotatedNodeImpl node) {
if (identical(node.documentationComment, _oldNode)) {
node.documentationComment = _newNode as CommentImpl;
return true;
} else if (_replaceInList(node.metadata)) {
return true;
}
return visitNode(node);
}
@override
bool visitAnnotation(covariant AnnotationImpl node) {
if (identical(node.arguments, _oldNode)) {
node.arguments = _newNode as ArgumentListImpl;
return true;
} else if (identical(node.typeArguments, _oldNode)) {
node.typeArguments = _newNode as TypeArgumentListImpl?;
return true;
} else if (identical(node.constructorName, _oldNode)) {
node.constructorName = _newNode as SimpleIdentifierImpl;
return true;
} else if (identical(node.name, _oldNode)) {
node.name = _newNode as IdentifierImpl;
return true;
}
return visitNode(node);
}
@override
bool visitArgumentList(covariant ArgumentListImpl node) {
if (_replaceInList(node.arguments)) {
return true;
}
return visitNode(node);
}
@override
bool visitAsExpression(covariant AsExpressionImpl node) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
} else if (identical(node.type, _oldNode)) {
node.type = _newNode as TypeAnnotationImpl;
return true;
}
return visitNode(node);
}
@override
bool visitAssertInitializer(covariant AssertInitializerImpl node) {
if (identical(node.condition, _oldNode)) {
node.condition = _newNode as ExpressionImpl;
return true;
}
if (identical(node.message, _oldNode)) {
node.message = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitAssertStatement(covariant AssertStatementImpl node) {
if (identical(node.condition, _oldNode)) {
node.condition = _newNode as ExpressionImpl;
return true;
}
if (identical(node.message, _oldNode)) {
node.message = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitAssignmentExpression(covariant AssignmentExpressionImpl node) {
if (identical(node.leftHandSide, _oldNode)) {
node.leftHandSide = _newNode as ExpressionImpl;
return true;
} else if (identical(node.rightHandSide, _oldNode)) {
node.rightHandSide = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitAwaitExpression(covariant AwaitExpressionImpl node) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitBinaryExpression(covariant BinaryExpressionImpl node) {
if (identical(node.leftOperand, _oldNode)) {
node.leftOperand = _newNode as ExpressionImpl;
return true;
} else if (identical(node.rightOperand, _oldNode)) {
node.rightOperand = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitBlock(covariant BlockImpl node) {
if (_replaceInList(node.statements)) {
return true;
}
return visitNode(node);
}
@override
bool visitBlockFunctionBody(covariant BlockFunctionBodyImpl node) {
if (identical(node.block, _oldNode)) {
node.block = _newNode as BlockImpl;
return true;
}
return visitNode(node);
}
@override
bool visitBooleanLiteral(BooleanLiteral node) => visitNode(node);
@override
bool visitBreakStatement(covariant BreakStatementImpl node) {
if (identical(node.label, _oldNode)) {
node.label = _newNode as SimpleIdentifierImpl;
return true;
}
return visitNode(node);
}
@override
bool visitCascadeExpression(covariant CascadeExpressionImpl node) {
if (identical(node.target, _oldNode)) {
node.target = _newNode as ExpressionImpl;
return true;
} else if (_replaceInList(node.cascadeSections)) {
return true;
}
return visitNode(node);
}
@override
bool visitCatchClause(covariant CatchClauseImpl node) {
if (identical(node.exceptionType, _oldNode)) {
node.exceptionType = _newNode as TypeAnnotationImpl;
return true;
} else if (identical(node.exceptionParameter, _oldNode)) {
node.exceptionParameter = _newNode as CatchClauseParameterImpl;
return true;
} else if (identical(node.stackTraceParameter, _oldNode)) {
node.stackTraceParameter = _newNode as CatchClauseParameterImpl;
return true;
} else if (identical(node.body, _oldNode)) {
node.body = _newNode as BlockImpl;
return true;
}
return visitNode(node);
}
@override
bool? visitCatchClauseParameter(CatchClauseParameter node) {
return visitNode(node);
}
@override
bool visitClassDeclaration(covariant ClassDeclarationImpl node) {
if (identical(node.typeParameters, _oldNode)) {
node.typeParameters = _newNode as TypeParameterListImpl;
return true;
} else if (identical(node.extendsClause, _oldNode)) {
node.extendsClause = _newNode as ExtendsClauseImpl;
return true;
} else if (identical(node.withClause, _oldNode)) {
node.withClause = _newNode as WithClauseImpl;
return true;
} else if (identical(node.implementsClause, _oldNode)) {
node.implementsClause = _newNode as ImplementsClauseImpl;
return true;
} else if (_replaceInList(node.members)) {
return true;
}
return visitAnnotatedNode(node);
}
@override
bool visitClassTypeAlias(covariant ClassTypeAliasImpl node) {
if (identical(node.typeParameters, _oldNode)) {
node.typeParameters = _newNode as TypeParameterListImpl;
return true;
} else if (identical(node.superclass, _oldNode)) {
node.superclass = _newNode as NamedTypeImpl;
return true;
} else if (identical(node.withClause, _oldNode)) {
node.withClause = _newNode as WithClauseImpl;
return true;
} else if (identical(node.implementsClause, _oldNode)) {
node.implementsClause = _newNode as ImplementsClauseImpl;
return true;
}
return visitAnnotatedNode(node);
}
@override
bool visitComment(covariant CommentImpl node) {
if (_replaceInList(node.references)) {
return true;
}
return visitNode(node);
}
@override
bool visitCommentReference(covariant CommentReferenceImpl node) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as IdentifierImpl;
return true;
}
return visitNode(node);
}
@override
bool visitCompilationUnit(covariant CompilationUnitImpl node) {
if (identical(node.scriptTag, _oldNode)) {
node.scriptTag = _newNode as ScriptTagImpl;
return true;
} else if (_replaceInList(node.directives)) {
return true;
} else if (_replaceInList(node.declarations)) {
return true;
}
return visitNode(node);
}
@override
bool visitConditionalExpression(covariant ConditionalExpressionImpl node) {
if (identical(node.condition, _oldNode)) {
node.condition = _newNode as ExpressionImpl;
return true;
} else if (identical(node.thenExpression, _oldNode)) {
node.thenExpression = _newNode as ExpressionImpl;
return true;
} else if (identical(node.elseExpression, _oldNode)) {
node.elseExpression = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitConfiguration(covariant ConfigurationImpl node) {
if (identical(node.name, _oldNode)) {
node.name = _newNode as DottedNameImpl;
return true;
} else if (identical(node.value, _oldNode)) {
node.value = _newNode as StringLiteralImpl;
return true;
} else if (identical(node.uri, _oldNode)) {
node.uri = _newNode as StringLiteralImpl;
return true;
}
return visitNode(node);
}
@override
bool visitConstantPattern(covariant ConstantPatternImpl node) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitConstructorDeclaration(covariant ConstructorDeclarationImpl node) {
if (identical(node.returnType, _oldNode)) {
node.returnType = _newNode as IdentifierImpl;
return true;
} else if (identical(node.parameters, _oldNode)) {
node.parameters = _newNode as FormalParameterListImpl;
return true;
} else if (identical(node.redirectedConstructor, _oldNode)) {
node.redirectedConstructor = _newNode as ConstructorNameImpl;
return true;
} else if (identical(node.body, _oldNode)) {
node.body = _newNode as FunctionBodyImpl;
return true;
} else if (_replaceInList(node.initializers)) {
return true;
}
return visitAnnotatedNode(node);
}
@override
bool visitConstructorFieldInitializer(
covariant ConstructorFieldInitializerImpl node,
) {
if (identical(node.fieldName, _oldNode)) {
node.fieldName = _newNode as SimpleIdentifierImpl;
return true;
} else if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitConstructorName(covariant ConstructorNameImpl node) {
if (identical(node.type, _oldNode)) {
node.type = _newNode as NamedTypeImpl;
return true;
} else if (identical(node.name, _oldNode)) {
node.name = _newNode as SimpleIdentifierImpl;
return true;
}
return visitNode(node);
}
@override
bool visitConstructorReference(covariant ConstructorReferenceImpl node) {
if (identical(node.constructorName, _oldNode)) {
node.constructorName = _newNode as ConstructorNameImpl;
return true;
}
return visitNode(node);
}
@override
bool visitConstructorSelector(ConstructorSelector node) {
throw UnimplementedError();
}
@override
bool visitContinueStatement(covariant ContinueStatementImpl node) {
if (identical(node.label, _oldNode)) {
node.label = _newNode as SimpleIdentifierImpl;
return true;
}
return visitNode(node);
}
@override
bool visitDeclaredIdentifier(covariant DeclaredIdentifierImpl node) {
if (identical(node.type, _oldNode)) {
node.type = _newNode as TypeAnnotationImpl;
return true;
}
return visitAnnotatedNode(node);
}
@override
bool visitDefaultFormalParameter(covariant DefaultFormalParameterImpl node) {
if (identical(node.parameter, _oldNode)) {
node.parameter = _newNode as NormalFormalParameterImpl;
return true;
} else if (identical(node.defaultValue, _oldNode)) {
node.defaultValue = _newNode as ExpressionImpl;
var parameterElement = node.declaredFragment;
parameterElement?.constantInitializer = _newNode;
return true;
}
return visitNode(node);
}
@override
bool visitDoStatement(covariant DoStatementImpl node) {
if (identical(node.body, _oldNode)) {
node.body = _newNode as StatementImpl;
return true;
} else if (identical(node.condition, _oldNode)) {
node.condition = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool? visitDotShorthandInvocation(covariant DotShorthandInvocationImpl node) {
if (identical(node.memberName, _oldNode)) {
node.memberName = _newNode as SimpleIdentifierImpl;
return true;
} else if (identical(node.typeArguments, _oldNode)) {
node.typeArguments = _newNode as TypeArgumentListImpl;
return true;
} else if (identical(node.argumentList, _oldNode)) {
node.argumentList = _newNode as ArgumentListImpl;
return true;
}
return visitNode(node);
}
@override
bool? visitDotShorthandPropertyAccess(
covariant DotShorthandPropertyAccessImpl node,
) {
if (identical(node.propertyName, _oldNode)) {
node.propertyName = _newNode as SimpleIdentifierImpl;
return true;
}
return visitNode(node);
}
@override
bool visitDottedName(covariant DottedNameImpl node) {
if (_replaceInList(node.components)) {
return true;
}
return visitNode(node);
}
@override
bool visitDoubleLiteral(DoubleLiteral node) => visitNode(node);
@override
bool visitEmptyFunctionBody(EmptyFunctionBody node) => visitNode(node);
@override
bool visitEmptyStatement(EmptyStatement node) => visitNode(node);
@override
bool visitEnumConstantArguments(EnumConstantArguments node) {
throw UnimplementedError();
}
@override
bool visitEnumConstantDeclaration(
covariant EnumConstantDeclarationImpl node,
) {
return visitAnnotatedNode(node);
}
@override
bool visitEnumDeclaration(covariant EnumDeclarationImpl node) {
if (identical(node.typeParameters, _oldNode)) {
node.typeParameters = _newNode as TypeParameterListImpl;
return true;
} else if (identical(node.withClause, _oldNode)) {
node.withClause = _newNode as WithClauseImpl;
return true;
} else if (identical(node.implementsClause, _oldNode)) {
node.implementsClause = _newNode as ImplementsClauseImpl;
return true;
} else if (_replaceInList(node.constants)) {
return true;
} else if (_replaceInList(node.members)) {
return true;
}
return visitAnnotatedNode(node);
}
@override
bool visitExportDirective(covariant ExportDirectiveImpl node) =>
visitNamespaceDirective(node);
@override
bool visitExpressionFunctionBody(covariant ExpressionFunctionBodyImpl node) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitExpressionStatement(covariant ExpressionStatementImpl node) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitExtendsClause(covariant ExtendsClauseImpl node) {
if (identical(node.superclass, _oldNode)) {
node.superclass = _newNode as NamedTypeImpl;
return true;
}
return visitNode(node);
}
@override
bool visitExtensionDeclaration(covariant ExtensionDeclarationImpl node) {
if (identical(node.documentationComment, _oldNode)) {
node.documentationComment = _newNode as CommentImpl;
return true;
} else if (_replaceInList(node.metadata)) {
return true;
} else if (identical(node.typeParameters, _oldNode)) {
node.typeParameters = _newNode as TypeParameterListImpl;
return true;
} else if (_replaceInList(node.members)) {
return true;
}
return visitNode(node);
}
@override
bool visitFieldDeclaration(covariant FieldDeclarationImpl node) {
if (identical(node.fields, _oldNode)) {
node.fields = _newNode as VariableDeclarationListImpl;
return true;
}
return visitAnnotatedNode(node);
}
@override
bool visitFieldFormalParameter(covariant FieldFormalParameterImpl node) {
if (identical(node.type, _oldNode)) {
node.type = _newNode as TypeAnnotationImpl;
return true;
} else if (identical(node.typeParameters, _oldNode)) {
node.typeParameters = _newNode as TypeParameterListImpl;
return true;
} else if (identical(node.parameters, _oldNode)) {
node.parameters = _newNode as FormalParameterListImpl;
return true;
}
return visitNormalFormalParameter(node);
}
@override
bool visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) {
if (identical(node.loopVariable, _oldNode)) {
(node as ForEachPartsWithDeclarationImpl).loopVariable =
_newNode as DeclaredIdentifierImpl;
return true;
} else if (identical(node.iterable, _oldNode)) {
(node as ForEachPartsWithDeclarationImpl).iterable =
_newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitForEachPartsWithIdentifier(ForEachPartsWithIdentifier node) {
if (identical(node.identifier, _oldNode)) {
(node as ForEachPartsWithIdentifierImpl).identifier =
_newNode as SimpleIdentifierImpl;
return true;
} else if (identical(node.iterable, _oldNode)) {
(node as ForEachPartsWithIdentifierImpl).iterable =
_newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool? visitForEachPartsWithPattern(ForEachPartsWithPattern node) {
if (identical(node.iterable, _oldNode)) {
(node as ForEachPartsWithPatternImpl).iterable =
_newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitForElement(ForElement node) {
if (identical(node.forLoopParts, _oldNode)) {
(node as ForElementImpl).forLoopParts = _newNode as ForLoopPartsImpl;
return true;
} else if (identical(node.body, _oldNode)) {
(node as ForElementImpl).body = _newNode as CollectionElementImpl;
return true;
}
return visitNode(node);
}
@override
bool visitFormalParameterList(covariant FormalParameterListImpl node) {
if (_replaceInList(node.parameters)) {
return true;
}
return visitNode(node);
}
@override
bool visitForPartsWithDeclarations(
covariant ForPartsWithDeclarationsImpl node,
) {
if (identical(node.variables, _oldNode)) {
node.variables = _newNode as VariableDeclarationListImpl;
return true;
} else if (identical(node.condition, _oldNode)) {
node.condition = _newNode as ExpressionImpl;
return true;
} else if (_replaceInList(node.updaters)) {
return true;
}
return visitNode(node);
}
@override
bool visitForPartsWithExpression(covariant ForPartsWithExpressionImpl node) {
if (identical(node.initialization, _oldNode)) {
node.initialization = _newNode as ExpressionImpl;
return true;
} else if (identical(node.condition, _oldNode)) {
node.condition = _newNode as ExpressionImpl;
return true;
} else if (_replaceInList(node.updaters)) {
return true;
}
return visitNode(node);
}
@override
bool visitForStatement(ForStatement node) {
if (identical(node.forLoopParts, _oldNode)) {
(node as ForStatementImpl).forLoopParts = _newNode as ForLoopPartsImpl;
return true;
} else if (identical(node.body, _oldNode)) {
(node as ForStatementImpl).body = _newNode as StatementImpl;
return true;
}
return visitNode(node);
}
@override
bool visitFunctionDeclaration(covariant FunctionDeclarationImpl node) {
if (identical(node.returnType, _oldNode)) {
node.returnType = _newNode as TypeAnnotationImpl;
return true;
} else if (identical(node.functionExpression, _oldNode)) {
node.functionExpression = _newNode as FunctionExpressionImpl;
return true;
}
return visitAnnotatedNode(node);
}
@override
bool visitFunctionDeclarationStatement(
covariant FunctionDeclarationStatementImpl node,
) {
if (identical(node.functionDeclaration, _oldNode)) {
node.functionDeclaration = _newNode as FunctionDeclarationImpl;
return true;
}
return visitNode(node);
}
@override
bool visitFunctionExpression(covariant FunctionExpressionImpl node) {
if (identical(node.parameters, _oldNode)) {
node.parameters = _newNode as FormalParameterListImpl;
return true;
} else if (identical(node.typeParameters, _oldNode)) {
node.typeParameters = _newNode as TypeParameterListImpl;
return true;
} else if (identical(node.body, _oldNode)) {
node.body = _newNode as FunctionBodyImpl;
return true;
}
return visitNode(node);
}
@override
bool visitFunctionExpressionInvocation(
covariant FunctionExpressionInvocationImpl node,
) {
if (identical(node.function, _oldNode)) {
node.function = _newNode as ExpressionImpl;
return true;
} else if (identical(node.argumentList, _oldNode)) {
node.argumentList = _newNode as ArgumentListImpl;
return true;
} else if (identical(node.typeArguments, _oldNode)) {
node.typeArguments = _newNode as TypeArgumentListImpl;
return true;
}
return visitNode(node);
}
@override
bool visitFunctionReference(covariant FunctionReferenceImpl node) {
if (identical(node.function, _oldNode)) {
node.function = _newNode as ExpressionImpl;
return true;
} else if (identical(node.typeArguments, _oldNode)) {
node.typeArguments = _newNode as TypeArgumentListImpl;
return true;
}
return visitNode(node);
}
@override
bool visitFunctionTypeAlias(covariant FunctionTypeAliasImpl node) {
if (identical(node.returnType, _oldNode)) {
node.returnType = _newNode as TypeAnnotationImpl;
return true;
} else if (identical(node.typeParameters, _oldNode)) {
node.typeParameters = _newNode as TypeParameterListImpl;
return true;
} else if (identical(node.parameters, _oldNode)) {
node.parameters = _newNode as FormalParameterListImpl;
return true;
}
return visitAnnotatedNode(node);
}
@override
bool visitFunctionTypedFormalParameter(
covariant FunctionTypedFormalParameterImpl node,
) {
if (identical(node.returnType, _oldNode)) {
node.returnType = _newNode as TypeAnnotationImpl;
return true;
} else if (identical(node.parameters, _oldNode)) {
node.parameters = _newNode as FormalParameterListImpl;
return true;
} else if (identical(node.typeParameters, _oldNode)) {
node.typeParameters = _newNode as TypeParameterListImpl;
return true;
}
return visitNormalFormalParameter(node);
}
@override
bool? visitGenericFunctionType(covariant GenericFunctionTypeImpl node) {
if (identical(node.returnType, _oldNode)) {
node.returnType = _newNode as TypeAnnotationImpl;
return true;
} else if (identical(node.typeParameters, _oldNode)) {
node.typeParameters = _newNode as TypeParameterListImpl;
return true;
} else if (identical(node.parameters, _oldNode)) {
node.parameters = _newNode as FormalParameterListImpl;
return true;
}
return null;
}
@override
bool visitGenericTypeAlias(GenericTypeAlias node) {
var nodeImpl = node as GenericTypeAliasImpl;
if (identical(node.typeParameters, _oldNode)) {
nodeImpl.typeParameters = _newNode as TypeParameterListImpl;
return true;
} else if (identical(node.type, _oldNode)) {
nodeImpl.type = _newNode as TypeAnnotationImpl;
return true;
} else if (_replaceInList(node.metadata)) {
return true;
}
return visitNode(node);
}
@override
bool visitHideCombinator(covariant HideCombinatorImpl node) {
if (_replaceInList(node.hiddenNames)) {
return true;
}
return visitNode(node);
}
@override
bool visitIfElement(IfElement node) {
if (identical(node.expression, _oldNode)) {
(node as IfElementImpl).condition = _newNode as ExpressionImpl;
return true;
} else if (identical(node.thenElement, _oldNode)) {
(node as IfElementImpl).thenElement = _newNode as CollectionElementImpl;
return true;
} else if (identical(node.elseElement, _oldNode)) {
(node as IfElementImpl).elseElement = _newNode as CollectionElementImpl;
return true;
}
return visitNode(node);
}
@override
bool visitIfStatement(covariant IfStatementImpl node) {
if (identical(node.expression, _oldNode)) {
node.condition = _newNode as ExpressionImpl;
return true;
} else if (identical(node.thenStatement, _oldNode)) {
node.thenStatement = _newNode as StatementImpl;
return true;
} else if (identical(node.elseStatement, _oldNode)) {
node.elseStatement = _newNode as StatementImpl;
return true;
}
return visitNode(node);
}
@override
bool visitImplementsClause(covariant ImplementsClauseImpl node) {
if (_replaceInList(node.interfaces)) {
return true;
}
return visitNode(node);
}
@override
bool visitImplicitCallReference(covariant ImplicitCallReferenceImpl node) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
} else if (identical(node.typeArguments, _oldNode)) {
node.typeArguments = _newNode as TypeArgumentListImpl;
return true;
}
return visitNode(node);
}
@override
bool visitImportDirective(covariant ImportDirectiveImpl node) {
if (identical(node.prefix, _oldNode)) {
node.prefix = _newNode as SimpleIdentifierImpl;
return true;
}
return visitNamespaceDirective(node);
}
@override
bool visitIndexExpression(covariant IndexExpressionImpl node) {
if (identical(node.target, _oldNode)) {
node.target = _newNode as ExpressionImpl;
return true;
} else if (identical(node.index, _oldNode)) {
node.index = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitInstanceCreationExpression(
covariant InstanceCreationExpressionImpl node,
) {
if (identical(node.constructorName, _oldNode)) {
node.constructorName = _newNode as ConstructorNameImpl;
return true;
} else if (identical(node.argumentList, _oldNode)) {
node.argumentList = _newNode as ArgumentListImpl;
return true;
}
return visitNode(node);
}
@override
bool visitIntegerLiteral(IntegerLiteral node) => visitNode(node);
@override
bool visitInterpolationExpression(
covariant InterpolationExpressionImpl node,
) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitInterpolationString(InterpolationString node) => visitNode(node);
@override
bool visitIsExpression(covariant IsExpressionImpl node) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
} else if (identical(node.type, _oldNode)) {
node.type = _newNode as TypeAnnotationImpl;
return true;
}
return visitNode(node);
}
@override
bool visitLabel(covariant LabelImpl node) {
if (identical(node.label, _oldNode)) {
node.label = _newNode as SimpleIdentifierImpl;
return true;
}
return visitNode(node);
}
@override
bool visitLabeledStatement(covariant LabeledStatementImpl node) {
if (identical(node.statement, _oldNode)) {
node.statement = _newNode as StatementImpl;
return true;
} else if (_replaceInList(node.labels)) {
return true;
}
return visitNode(node);
}
@override
bool visitLibraryDirective(covariant LibraryDirectiveImpl node) {
if (identical(node.name2, _oldNode)) {
node.name2 = _newNode as LibraryIdentifierImpl;
return true;
}
return visitAnnotatedNode(node);
}
@override
bool visitLibraryIdentifier(covariant LibraryIdentifierImpl node) {
if (_replaceInList(node.components)) {
return true;
}
return visitNode(node);
}
@override
bool visitListLiteral(covariant ListLiteralImpl node) {
if (_replaceInList(node.elements)) {
return true;
}
return visitTypedLiteral(node);
}
@override
bool visitMapLiteralEntry(covariant MapLiteralEntryImpl node) {
if (identical(node.key, _oldNode)) {
node.key = _newNode as ExpressionImpl;
return true;
} else if (identical(node.value, _oldNode)) {
node.value = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool? visitMapPatternEntry(covariant MapPatternEntryImpl node) {
if (identical(node.key, _oldNode)) {
node.key = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitMethodInvocation(covariant MethodInvocationImpl node) {
if (identical(node.target, _oldNode)) {
node.target = _newNode as ExpressionImpl;
return true;
} else if (identical(node.methodName, _oldNode)) {
node.methodName = _newNode as SimpleIdentifierImpl;
return true;
} else if (identical(node.argumentList, _oldNode)) {
node.argumentList = _newNode as ArgumentListImpl;
return true;
} else if (identical(node.typeArguments, _oldNode)) {
node.typeArguments = _newNode as TypeArgumentListImpl;
return true;
}
return visitNode(node);
}
@override
bool visitMixinOnClause(covariant MixinOnClauseImpl node) {
if (_replaceInList(node.superclassConstraints)) {
return true;
}
return visitNode(node);
}
@override
bool visitNamedExpression(covariant NamedExpressionImpl node) {
if (identical(node.name, _oldNode)) {
node.name = _newNode as LabelImpl;
return true;
} else if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
bool visitNamespaceDirective(covariant NamespaceDirectiveImpl node) {
if (_replaceInList(node.combinators)) {
return true;
}
return visitUriBasedDirective(node);
}
@override
bool visitNativeFunctionBody(covariant NativeFunctionBodyImpl node) {
if (identical(node.stringLiteral, _oldNode)) {
node.stringLiteral = _newNode as StringLiteralImpl;
return true;
}
return visitNode(node);
}
bool visitNode(AstNode node) {
throw ArgumentError("The old node is not a child of it's parent");
}
bool visitNormalFormalParameter(covariant NormalFormalParameterImpl node) {
if (identical(node.documentationComment, _oldNode)) {
node.documentationComment = _newNode as CommentImpl;
return true;
} else if (_replaceInList(node.metadata)) {
return true;
}
return visitNode(node);
}
@override
bool visitNullAwareElement(NullAwareElement node) {
if (identical(node.value, _oldNode)) {
(node as NullAwareElementImpl).value = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitNullLiteral(NullLiteral node) => visitNode(node);
@override
bool visitParenthesizedExpression(
covariant ParenthesizedExpressionImpl node,
) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitPartDirective(covariant PartDirectiveImpl node) =>
visitUriBasedDirective(node);
@override
bool visitPartOfDirective(covariant PartOfDirectiveImpl node) {
if (identical(node.libraryName, _oldNode)) {
node.libraryName = _newNode as LibraryIdentifierImpl;
return true;
}
return visitAnnotatedNode(node);
}
@override
bool visitPatternAssignment(covariant PatternAssignmentImpl node) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitPatternVariableDeclaration(
covariant PatternVariableDeclarationImpl node,
) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitPostfixExpression(covariant PostfixExpressionImpl node) {
if (identical(node.operand, _oldNode)) {
node.operand = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitPrefixedIdentifier(covariant PrefixedIdentifierImpl node) {
if (identical(node.prefix, _oldNode)) {
node.prefix = _newNode as SimpleIdentifierImpl;
return true;
} else if (identical(node.identifier, _oldNode)) {
node.identifier = _newNode as SimpleIdentifierImpl;
return true;
}
return visitNode(node);
}
@override
bool visitPrefixExpression(covariant PrefixExpressionImpl node) {
if (identical(node.operand, _oldNode)) {
node.operand = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitPropertyAccess(covariant PropertyAccessImpl node) {
if (identical(node.target, _oldNode)) {
node.target = _newNode as ExpressionImpl;
return true;
} else if (identical(node.propertyName, _oldNode)) {
node.propertyName = _newNode as SimpleIdentifierImpl;
return true;
}
return visitNode(node);
}
@override
bool visitRecordLiteral(covariant RecordLiteralImpl node) {
if (_replaceInList(node.fields)) {
return true;
}
return visitNode(node);
}
@override
bool visitRecordTypeAnnotation(RecordTypeAnnotation node) {
if (_replaceInList(node.positionalFields)) {
return true;
} else if (identical(node.namedFields, _oldNode)) {
// node.namedFields = _newNode as RecordTypeAnnotationNamedFieldsImpl;
throw UnimplementedError();
}
return visitNode(node);
}
@override
bool visitRecordTypeAnnotationNamedField(
RecordTypeAnnotationNamedField node,
) {
if (_replaceInList(node.metadata)) {
return true;
} else if (identical(node.type, _oldNode)) {
// node.type = _newNode as TypeAnnotationImpl;
throw UnimplementedError();
}
return visitNode(node);
}
@override
bool visitRecordTypeAnnotationNamedFields(
RecordTypeAnnotationNamedFields node,
) {
if (_replaceInList(node.fields)) {
return true;
}
return visitNode(node);
}
@override
bool visitRecordTypeAnnotationPositionalField(
RecordTypeAnnotationPositionalField node,
) {
if (_replaceInList(node.metadata)) {
return true;
} else if (identical(node.type, _oldNode)) {
// node.type = _newNode as TypeAnnotationImpl;
throw UnimplementedError();
}
return visitNode(node);
}
@override
bool visitRedirectingConstructorInvocation(
covariant RedirectingConstructorInvocationImpl node,
) {
if (identical(node.constructorName, _oldNode)) {
node.constructorName = _newNode as SimpleIdentifierImpl;
return true;
} else if (identical(node.argumentList, _oldNode)) {
node.argumentList = _newNode as ArgumentListImpl;
return true;
}
return visitNode(node);
}
@override
bool? visitRelationalPattern(covariant RelationalPatternImpl node) {
if (identical(node.operand, _oldNode)) {
node.operand = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitRethrowExpression(RethrowExpression node) => visitNode(node);
@override
bool visitReturnStatement(covariant ReturnStatementImpl node) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitScriptTag(ScriptTag scriptTag) => visitNode(scriptTag);
@override
bool visitSetOrMapLiteral(covariant SetOrMapLiteralImpl node) {
if (_replaceInList(node.elements)) {
return true;
}
return visitTypedLiteral(node);
}
@override
bool visitShowCombinator(covariant ShowCombinatorImpl node) {
if (_replaceInList(node.shownNames)) {
return true;
}
return visitNode(node);
}
@override
bool visitSimpleFormalParameter(covariant SimpleFormalParameterImpl node) {
if (identical(node.type, _oldNode)) {
node.type = _newNode as TypeAnnotationImpl;
return true;
}
return visitNormalFormalParameter(node);
}
@override
bool visitSimpleIdentifier(SimpleIdentifier node) => visitNode(node);
@override
bool visitSimpleStringLiteral(SimpleStringLiteral node) => visitNode(node);
@override
bool visitSpreadElement(SpreadElement node) {
if (identical(node.expression, _oldNode)) {
(node as SpreadElementImpl).expression = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitStringInterpolation(covariant StringInterpolationImpl node) {
if (_replaceInList(node.elements)) {
return true;
}
return visitNode(node);
}
@override
bool visitSuperConstructorInvocation(
covariant SuperConstructorInvocationImpl node,
) {
if (identical(node.constructorName, _oldNode)) {
node.constructorName = _newNode as SimpleIdentifierImpl;
return true;
} else if (identical(node.argumentList, _oldNode)) {
node.argumentList = _newNode as ArgumentListImpl;
return true;
}
return visitNode(node);
}
@override
bool visitSuperExpression(covariant SuperExpressionImpl node) =>
visitNode(node);
@override
bool visitSuperFormalParameter(covariant SuperFormalParameterImpl node) {
if (identical(node.type, _oldNode)) {
node.type = _newNode as TypeAnnotationImpl;
return true;
} else if (identical(node.typeParameters, _oldNode)) {
node.typeParameters = _newNode as TypeParameterListImpl;
return true;
} else if (identical(node.parameters, _oldNode)) {
node.parameters = _newNode as FormalParameterListImpl;
return true;
}
return visitNormalFormalParameter(node);
}
@override
bool visitSwitchCase(covariant SwitchCaseImpl node) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
}
return visitSwitchMember(node);
}
@override
bool visitSwitchDefault(covariant SwitchDefaultImpl node) =>
visitSwitchMember(node);
@override
bool? visitSwitchExpression(covariant SwitchExpressionImpl node) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool? visitSwitchExpressionCase(covariant SwitchExpressionCaseImpl node) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
bool visitSwitchMember(covariant SwitchMemberImpl node) {
if (_replaceInList(node.labels)) {
return true;
} else if (_replaceInList(node.statements)) {
return true;
}
return visitNode(node);
}
@override
bool visitSwitchStatement(covariant SwitchStatementImpl node) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
} else if (_replaceInList(node.members)) {
return true;
}
return visitNode(node);
}
@override
bool visitSymbolLiteral(SymbolLiteral node) => visitNode(node);
@override
bool visitThisExpression(ThisExpression node) => visitNode(node);
@override
bool visitThrowExpression(covariant ThrowExpressionImpl node) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitTopLevelVariableDeclaration(
covariant TopLevelVariableDeclarationImpl node,
) {
if (identical(node.variables, _oldNode)) {
node.variables = _newNode as VariableDeclarationListImpl;
return true;
}
return visitAnnotatedNode(node);
}
@override
bool visitTryStatement(covariant TryStatementImpl node) {
if (identical(node.body, _oldNode)) {
node.body = _newNode as BlockImpl;
return true;
} else if (identical(node.finallyBlock, _oldNode)) {
node.finallyBlock = _newNode as BlockImpl;
return true;
} else if (_replaceInList(node.catchClauses)) {
return true;
}
return visitNode(node);
}
@override
bool visitTypeArgumentList(covariant TypeArgumentListImpl node) {
if (_replaceInList(node.arguments)) {
return true;
}
return visitNode(node);
}
bool visitTypedLiteral(covariant TypedLiteralImpl node) {
if (identical(node.typeArguments, _oldNode)) {
node.typeArguments = _newNode as TypeArgumentListImpl;
return true;
}
return visitNode(node);
}
@override
bool visitTypeLiteral(covariant TypeLiteralImpl node) {
if (identical(node.type, _oldNode)) {
node.type = _newNode as NamedTypeImpl;
return true;
}
return visitNode(node);
}
@override
bool visitTypeParameter(covariant TypeParameterImpl node) {
if (identical(node.bound, _oldNode)) {
node.bound = _newNode as TypeAnnotationImpl;
return true;
}
return visitNode(node);
}
@override
bool visitTypeParameterList(covariant TypeParameterListImpl node) {
if (_replaceInList(node.typeParameters)) {
return true;
}
return visitNode(node);
}
bool visitUriBasedDirective(covariant UriBasedDirectiveImpl node) {
if (identical(node.uri, _oldNode)) {
node.uri = _newNode as StringLiteralImpl;
return true;
}
return visitAnnotatedNode(node);
}
@override
bool visitVariableDeclaration(covariant VariableDeclarationImpl node) {
if (identical(node.initializer, _oldNode)) {
node.initializer = _newNode as ExpressionImpl;
return true;
// TODO(srawlins): also replace node's declared element's
// `constantInitializer`, if the element is [ConstFieldElementImpl],
// [ConstLocalVariableElementImpl], or [ConstTopLevelVariableElementImpl].
}
return visitAnnotatedNode(node);
}
@override
bool visitVariableDeclarationList(
covariant VariableDeclarationListImpl node,
) {
if (identical(node.type, _oldNode)) {
node.type = _newNode as TypeAnnotationImpl;
return true;
} else if (_replaceInList(node.variables)) {
return true;
}
return visitAnnotatedNode(node);
}
@override
bool visitVariableDeclarationStatement(
covariant VariableDeclarationStatementImpl node,
) {
if (identical(node.variables, _oldNode)) {
node.variables = _newNode as VariableDeclarationListImpl;
return true;
}
return visitNode(node);
}
@override
bool? visitWhenClause(covariant WhenClauseImpl node) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
@override
bool visitWhileStatement(covariant WhileStatementImpl node) {
if (identical(node.condition, _oldNode)) {
node.condition = _newNode as ExpressionImpl;
return true;
} else if (identical(node.body, _oldNode)) {
node.body = _newNode as StatementImpl;
return true;
}
return visitNode(node);
}
@override
bool visitWithClause(covariant WithClauseImpl node) {
if (_replaceInList(node.mixinTypes)) {
return true;
}
return visitNode(node);
}
@override
bool visitYieldStatement(covariant YieldStatementImpl node) {
if (identical(node.expression, _oldNode)) {
node.expression = _newNode as ExpressionImpl;
return true;
}
return visitNode(node);
}
bool _replaceInList(NodeList list) {
int count = list.length;
for (int i = 0; i < count; i++) {
if (identical(_oldNode, list[i])) {
list[i] = _newNode;
return true;
}
}
return false;
}
/// Replace the [oldNode] with the [newNode] in the AST structure containing
/// the old node. Return `true` if the replacement was successful.
///
/// Throws an [ArgumentError] if either node is `null`, if the old node does
/// not have a parent node, or if the AST structure has been corrupted.
///
/// If [newNode] is the parent of [oldNode] already (because [newNode] became
/// the parent of [oldNode] in its constructor), this action will loop
/// infinitely; pass [oldNode]'s previous parent as [parent] to avoid this.
static bool replace(AstNode oldNode, AstNode newNode, {AstNode? parent}) {
if (identical(oldNode, newNode)) {
return true;
}
parent ??= oldNode.parent;
if (parent == null) {
throw ArgumentError("The old node is not a child of another node");
}
NodeReplacer replacer = NodeReplacer._(oldNode, newNode);
return parent.accept(replacer)!;
}
}
/// Traverse the AST from initial child node to successive parents, building a
/// collection of local variable and parameter names visible to the initial
/// child node. In case of name shadowing, the first name seen is the most
/// specific one so names are not redefined.
///
/// Completion test code coverage is 95%. The two basic blocks that are not
/// executed cannot be executed. They are included for future reference.
class ScopedNameFinder extends GeneralizingAstVisitor<void> {
Declaration? _declarationNode;
AstNode? _immediateChild;
final Set<String> _locals = {};
final int _position;
bool _referenceIsWithinLocalFunction = false;
ScopedNameFinder(this._position);
Declaration? get declaration => _declarationNode;
Set<String> get locals => _locals;
@override
void visitBlock(Block node) {
_checkStatements(node.statements);
super.visitBlock(node);
}
@override
void visitCatchClause(CatchClause node) {
_addToScope(node.exceptionParameter?.name);
_addToScope(node.stackTraceParameter?.name);
super.visitCatchClause(node);
}
@override
void visitConstructorDeclaration(ConstructorDeclaration node) {
if (!identical(_immediateChild, node.parameters)) {
_addParameters(node.parameters.parameters);
}
_declarationNode = node;
}
@override
void visitFieldDeclaration(FieldDeclaration node) {
_declarationNode = node;
}
@override
void visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) {
_addToScope(node.loopVariable.name);
super.visitForEachPartsWithDeclaration(node);
}
@override
void visitForPartsWithDeclarations(ForPartsWithDeclarations node) {
_addVariables(node.variables.variables);
super.visitForPartsWithDeclarations(node);
}
@override
void visitFunctionDeclaration(FunctionDeclaration node) {
if (node.parent is! FunctionDeclarationStatement) {
_declarationNode = node;
} else {
super.visitFunctionDeclaration(node);
}
}
@override
void visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
_referenceIsWithinLocalFunction = true;
super.visitFunctionDeclarationStatement(node);
}
@override
void visitFunctionExpression(FunctionExpression node) {
var parameters = node.parameters;
if (parameters != null && !identical(_immediateChild, parameters)) {
_addParameters(parameters.parameters);
}
super.visitFunctionExpression(node);
}
@override
void visitMethodDeclaration(MethodDeclaration node) {
_declarationNode = node;
var parameters = node.parameters;
if (parameters != null && !identical(_immediateChild, parameters)) {
_addParameters(parameters.parameters);
}
}
@override
void visitNode(AstNode node) {
_immediateChild = node;
node.parent?.accept(this);
}
@override
void visitSwitchMember(SwitchMember node) {
_checkStatements(node.statements);
super.visitSwitchMember(node);
}
@override
void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
_declarationNode = node;
}
@override
void visitTypeAlias(TypeAlias node) {
_declarationNode = node;
}
void _addParameters(NodeList<FormalParameter> vars) {
for (FormalParameter var2 in vars) {
_addToScope(var2.name);
}
}
void _addToScope(Token? identifier) {
if (identifier != null && _isInRange(identifier)) {
_locals.add(identifier.lexeme);
}
}
void _addVariables(NodeList<VariableDeclaration> variables) {
for (VariableDeclaration variable in variables) {
_addToScope(variable.name);
}
}
/// Check the given list of [statements] for any that come before the
/// immediate child and that define a name that would be visible to the
/// immediate child.
void _checkStatements(List<Statement> statements) {
for (Statement statement in statements) {
if (identical(statement, _immediateChild)) {
return;
}
if (statement is VariableDeclarationStatement) {
_addVariables(statement.variables.variables);
} else if (statement is FunctionDeclarationStatement &&
!_referenceIsWithinLocalFunction) {
_addToScope(statement.functionDeclaration.name);
}
}
}
bool _isInRange(Token token) {
if (_position < 0) {
// if source position is not set then all nodes are in range
return true;
// not reached
}
return token.end < _position;
}
}