blob: d0a3556d707d73e1da1572537012564ab3cb71d4 [file] [log] [blame]
// Copyright (c) 2017, 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.
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/standard_ast_factory.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/src/dart/ast/token.dart';
/**
* A visitor that visits an [AstNode] and its parent recursively along with any
* declarations in those nodes. Consumers typically call [visit] which catches
* the exception thrown by [finished].
*/
abstract class LocalDeclarationVisitor extends GeneralizingAstVisitor {
static final TypeName STACKTRACE_TYPE = astFactory.typeName(
astFactory.simpleIdentifier(
new StringToken(TokenType.IDENTIFIER, 'StackTrace', 0)),
null);
final int offset;
LocalDeclarationVisitor(this.offset);
void declaredClass(ClassDeclaration declaration);
void declaredClassTypeAlias(ClassTypeAlias declaration);
void declaredEnum(EnumDeclaration declaration) {}
void declaredExtension(ExtensionDeclaration declaration);
void declaredField(FieldDeclaration fieldDecl, VariableDeclaration varDecl);
void declaredFunction(FunctionDeclaration declaration);
void declaredFunctionTypeAlias(FunctionTypeAlias declaration);
void declaredGenericTypeAlias(GenericTypeAlias declaration);
void declaredLabel(Label label, bool isCaseLabel);
void declaredLocalVar(SimpleIdentifier name, TypeAnnotation type);
void declaredMethod(MethodDeclaration declaration);
void declaredMixin(MixinDeclaration declaration) {}
void declaredParam(SimpleIdentifier name, TypeAnnotation type);
void declaredTopLevelVar(
VariableDeclarationList varList, VariableDeclaration varDecl);
void declaredTypeParameter(TypeParameter node) {}
/**
* Throw an exception indicating that [LocalDeclarationVisitor] should
* stop visiting. This is caught in [visit] which then exits normally.
*/
void finished() {
throw new _LocalDeclarationVisitorFinished();
}
/**
* Visit the given [AstNode] and its parent recursively along with any
* declarations in those nodes. Return `true` if [finished] is called
* while visiting, else `false`.
*/
bool visit(AstNode node) {
try {
node.accept(this);
return false;
} on _LocalDeclarationVisitorFinished {
return true;
}
}
@override
void visitBlock(Block node) {
_visitStatements(node.statements);
visitNode(node);
}
@override
void visitCatchClause(CatchClause node) {
SimpleIdentifier param = node.exceptionParameter;
if (param != null) {
declaredParam(param, node.exceptionType);
}
param = node.stackTraceParameter;
if (param != null) {
declaredParam(param, STACKTRACE_TYPE);
}
visitNode(node);
}
@override
void visitClassDeclaration(ClassDeclaration node) {
_visitClassDeclarationMembers(node);
visitNode(node);
}
@override
void visitCompilationUnit(CompilationUnit node) {
node.declarations.forEach((Declaration declaration) {
if (declaration is ClassDeclaration) {
declaredClass(declaration);
_visitTypeParameters(declaration, declaration.typeParameters);
} else if (declaration is EnumDeclaration) {
declaredEnum(declaration);
} else if (declaration is ExtensionDeclaration) {
declaredExtension(declaration);
_visitTypeParameters(declaration, declaration.typeParameters);
} else if (declaration is FunctionDeclaration) {
declaredFunction(declaration);
_visitTypeParameters(
declaration,
declaration.functionExpression.typeParameters,
);
} else if (declaration is TopLevelVariableDeclaration) {
var varList = declaration.variables;
if (varList != null) {
varList.variables.forEach((VariableDeclaration varDecl) {
declaredTopLevelVar(varList, varDecl);
});
}
} else if (declaration is ClassTypeAlias) {
declaredClassTypeAlias(declaration);
_visitTypeParameters(declaration, declaration.typeParameters);
} else if (declaration is FunctionTypeAlias) {
declaredFunctionTypeAlias(declaration);
_visitTypeParameters(declaration, declaration.typeParameters);
} else if (declaration is GenericTypeAlias) {
declaredGenericTypeAlias(declaration);
_visitTypeParameters(declaration, declaration.typeParameters);
_visitTypeParameters(
declaration.functionType,
declaration.functionType?.typeParameters,
);
} else if (declaration is MixinDeclaration) {
declaredMixin(declaration);
_visitTypeParameters(declaration, declaration.typeParameters);
}
});
}
@override
visitConstructorDeclaration(ConstructorDeclaration node) {
_visitParamList(node.parameters);
visitNode(node);
}
@override
visitForStatement(ForStatement node) {
var forLoopParts = node.forLoopParts;
if (forLoopParts is ForEachPartsWithDeclaration) {
DeclaredIdentifier loopVar = forLoopParts.loopVariable;
if (loopVar != null) {
SimpleIdentifier id = loopVar.identifier;
if (id != null) {
// If there is no loop variable, don't declare it.
declaredLocalVar(id, loopVar.type);
}
}
} else if (forLoopParts is ForEachPartsWithIdentifier) {
SimpleIdentifier id = forLoopParts.identifier;
if (id != null) {
// If there is no loop variable, don't declare it.
declaredLocalVar(id, null);
}
} else if (forLoopParts is ForPartsWithDeclarations) {
VariableDeclarationList varList = forLoopParts.variables;
if (varList != null) {
varList.variables.forEach((VariableDeclaration varDecl) {
declaredLocalVar(varDecl.name, varList.type);
});
}
}
visitNode(node);
}
@override
void visitFunctionDeclaration(FunctionDeclaration node) {
// declaredFunction is called by the compilation unit containing it
visitNode(node);
}
@override
void visitFunctionExpression(FunctionExpression node) {
_visitParamList(node.parameters);
visitNode(node);
}
@override
void visitInterpolationExpression(InterpolationExpression node) {
visitNode(node);
}
@override
void visitLabeledStatement(LabeledStatement node) {
for (Label label in node.labels) {
declaredLabel(label, false);
}
visitNode(node);
}
@override
void visitMethodDeclaration(MethodDeclaration node) {
_visitParamList(node.parameters);
visitNode(node);
}
@override
void visitNode(AstNode node) {
// Support the case of searching partial ASTs by aborting on nodes with no
// parents. This is useful for the angular plugin.
node.parent?.accept(this);
}
@override
void visitStringInterpolation(StringInterpolation node) {
visitNode(node);
}
@override
void visitSwitchMember(SwitchMember node) {
_visitStatements(node.statements);
visitNode(node);
}
@override
void visitSwitchStatement(SwitchStatement node) {
for (SwitchMember member in node.members) {
for (Label label in member.labels) {
declaredLabel(label, true);
}
}
visitNode(node);
}
void _visitClassDeclarationMembers(ClassDeclaration node) {
for (ClassMember member in node.members) {
if (member is FieldDeclaration) {
member.fields.variables.forEach((VariableDeclaration varDecl) {
declaredField(member, varDecl);
});
} else if (member is MethodDeclaration) {
declaredMethod(member);
_visitTypeParameters(member, member.typeParameters);
}
}
}
void _visitParamList(FormalParameterList paramList) {
if (paramList != null) {
paramList.parameters.forEach((FormalParameter param) {
NormalFormalParameter normalParam;
if (param is DefaultFormalParameter) {
normalParam = param.parameter;
} else if (param is NormalFormalParameter) {
normalParam = param;
}
TypeAnnotation type = null;
if (normalParam is FieldFormalParameter) {
type = normalParam.type;
} else if (normalParam is FunctionTypedFormalParameter) {
type = normalParam.returnType;
} else if (normalParam is SimpleFormalParameter) {
type = normalParam.type;
}
SimpleIdentifier name = param.identifier;
declaredParam(name, type);
});
}
}
_visitStatements(NodeList<Statement> statements) {
for (Statement stmt in statements) {
if (stmt.offset < offset) {
if (stmt is VariableDeclarationStatement) {
VariableDeclarationList varList = stmt.variables;
if (varList != null) {
for (VariableDeclaration varDecl in varList.variables) {
if (varDecl.end < offset) {
declaredLocalVar(varDecl.name, varList.type);
}
}
}
} else if (stmt is FunctionDeclarationStatement) {
FunctionDeclaration declaration = stmt.functionDeclaration;
if (declaration != null && declaration.offset < offset) {
SimpleIdentifier id = declaration.name;
if (id != null) {
String name = id.name;
if (name != null && name.length > 0) {
declaredFunction(declaration);
_visitTypeParameters(
declaration,
declaration.functionExpression.typeParameters,
);
}
}
}
}
}
}
}
void _visitTypeParameters(AstNode node, TypeParameterList typeParameters) {
if (typeParameters == null) return;
if (node.offset < offset && offset < node.end) {
for (var typeParameter in typeParameters.typeParameters) {
declaredTypeParameter(typeParameter);
}
}
}
}
/**
* Internal exception used to indicate that [LocalDeclarationVisitor]
* should stop visiting.
*/
class _LocalDeclarationVisitorFinished {}