blob: 246102143f3b39d1ac3c0b7b8f670ddbee1ecb9c [file] [log] [blame]
// Copyright (c) 2016, 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/visitor.dart';
/// Compute the set of external names referenced in the [unit].
Set<String> computeReferencedNames(CompilationUnit unit) {
_ReferencedNamesComputer computer = _ReferencedNamesComputer();
unit.accept(computer);
return computer.names;
}
/// Compute the set of names which are used in `extends`, `with` or `implements`
/// clauses in the file. Import prefixes and type arguments are not included.
Set<String> computeSubtypedNames(CompilationUnit unit) {
Set<String> subtypedNames = <String>{};
void _addSubtypedName(TypeName? type) {
if (type != null) {
Identifier name = type.name;
if (name is SimpleIdentifier) {
subtypedNames.add(name.name);
} else if (name is PrefixedIdentifier) {
subtypedNames.add(name.identifier.name);
}
}
}
void _addSubtypedNames(List<TypeName>? types) {
types?.forEach(_addSubtypedName);
}
for (CompilationUnitMember declaration in unit.declarations) {
if (declaration is ClassDeclaration) {
_addSubtypedName(declaration.extendsClause?.superclass);
_addSubtypedNames(declaration.withClause?.mixinTypes);
_addSubtypedNames(declaration.implementsClause?.interfaces);
} else if (declaration is ClassTypeAlias) {
_addSubtypedName(declaration.superclass);
_addSubtypedNames(declaration.withClause.mixinTypes);
_addSubtypedNames(declaration.implementsClause?.interfaces);
} else if (declaration is MixinDeclaration) {
_addSubtypedNames(declaration.onClause?.superclassConstraints);
_addSubtypedNames(declaration.implementsClause?.interfaces);
}
}
return subtypedNames;
}
/// Chained set of local names, that hide corresponding external names.
class _LocalNameScope {
final _LocalNameScope? enclosing;
Set<String>? names;
_LocalNameScope(this.enclosing);
factory _LocalNameScope.forBlock(_LocalNameScope enclosing, Block node) {
_LocalNameScope scope = _LocalNameScope(enclosing);
for (Statement statement in node.statements) {
if (statement is FunctionDeclarationStatement) {
scope.add(statement.functionDeclaration.name);
} else if (statement is VariableDeclarationStatement) {
scope.addVariableNames(statement.variables);
}
}
return scope;
}
factory _LocalNameScope.forClass(
_LocalNameScope enclosing, ClassDeclaration node) {
_LocalNameScope scope = _LocalNameScope(enclosing);
scope.addTypeParameters(node.typeParameters);
for (ClassMember member in node.members) {
if (member is FieldDeclaration) {
scope.addVariableNames(member.fields);
} else if (member is MethodDeclaration) {
scope.add(member.name);
}
}
return scope;
}
factory _LocalNameScope.forClassTypeAlias(
_LocalNameScope enclosing, ClassTypeAlias node) {
_LocalNameScope scope = _LocalNameScope(enclosing);
scope.addTypeParameters(node.typeParameters);
return scope;
}
factory _LocalNameScope.forConstructor(
_LocalNameScope enclosing, ConstructorDeclaration node) {
_LocalNameScope scope = _LocalNameScope(enclosing);
scope.addFormalParameters(node.parameters);
return scope;
}
factory _LocalNameScope.forFunction(
_LocalNameScope enclosing, FunctionDeclaration node) {
_LocalNameScope scope = _LocalNameScope(enclosing);
scope.addTypeParameters(node.functionExpression.typeParameters);
scope.addFormalParameters(node.functionExpression.parameters);
return scope;
}
factory _LocalNameScope.forFunctionTypeAlias(
_LocalNameScope enclosing, FunctionTypeAlias node) {
_LocalNameScope scope = _LocalNameScope(enclosing);
scope.addTypeParameters(node.typeParameters);
return scope;
}
factory _LocalNameScope.forMethod(
_LocalNameScope enclosing, MethodDeclaration node) {
_LocalNameScope scope = _LocalNameScope(enclosing);
scope.addTypeParameters(node.typeParameters);
scope.addFormalParameters(node.parameters);
return scope;
}
factory _LocalNameScope.forUnit(CompilationUnit node) {
_LocalNameScope scope = _LocalNameScope(null);
for (CompilationUnitMember declaration in node.declarations) {
if (declaration is NamedCompilationUnitMember) {
scope.add(declaration.name);
} else if (declaration is TopLevelVariableDeclaration) {
scope.addVariableNames(declaration.variables);
}
}
return scope;
}
void add(SimpleIdentifier? identifier) {
if (identifier != null) {
(names ??= <String>{}).add(identifier.name);
}
}
void addFormalParameters(FormalParameterList? parameterList) {
if (parameterList != null) {
parameterList.parameters
.map((p) => p is NormalFormalParameter ? p.identifier : null)
.forEach(add);
}
}
void addTypeParameters(TypeParameterList? typeParameterList) {
if (typeParameterList != null) {
typeParameterList.typeParameters.map((p) => p.name).forEach(add);
}
}
void addVariableNames(VariableDeclarationList variableList) {
for (VariableDeclaration variable in variableList.variables) {
add(variable.name);
}
}
bool contains(String name) {
if (names != null && names!.contains(name)) {
return true;
}
if (enclosing != null) {
return enclosing!.contains(name);
}
return false;
}
}
class _ReferencedNamesComputer extends GeneralizingAstVisitor<void> {
final Set<String> names = <String>{};
final Set<String> importPrefixNames = <String>{};
_LocalNameScope localScope = _LocalNameScope(null);
@override
void visitBlock(Block node) {
_LocalNameScope outerScope = localScope;
try {
localScope = _LocalNameScope.forBlock(localScope, node);
super.visitBlock(node);
} finally {
localScope = outerScope;
}
}
@override
void visitClassDeclaration(ClassDeclaration node) {
_LocalNameScope outerScope = localScope;
try {
localScope = _LocalNameScope.forClass(localScope, node);
super.visitClassDeclaration(node);
} finally {
localScope = outerScope;
}
}
@override
void visitClassTypeAlias(ClassTypeAlias node) {
_LocalNameScope outerScope = localScope;
try {
localScope = _LocalNameScope.forClassTypeAlias(localScope, node);
super.visitClassTypeAlias(node);
} finally {
localScope = outerScope;
}
}
@override
void visitCompilationUnit(CompilationUnit node) {
localScope = _LocalNameScope.forUnit(node);
super.visitCompilationUnit(node);
}
@override
void visitConstructorDeclaration(ConstructorDeclaration node) {
_LocalNameScope outerScope = localScope;
try {
localScope = _LocalNameScope.forConstructor(localScope, node);
super.visitConstructorDeclaration(node);
} finally {
localScope = outerScope;
}
}
@override
void visitConstructorName(ConstructorName node) {
if (node.parent is! ConstructorDeclaration) {
super.visitConstructorName(node);
}
}
@override
void visitFunctionDeclaration(FunctionDeclaration node) {
_LocalNameScope outerScope = localScope;
try {
localScope = _LocalNameScope.forFunction(localScope, node);
super.visitFunctionDeclaration(node);
} finally {
localScope = outerScope;
}
}
@override
void visitFunctionTypeAlias(FunctionTypeAlias node) {
_LocalNameScope outerScope = localScope;
try {
localScope = _LocalNameScope.forFunctionTypeAlias(localScope, node);
super.visitFunctionTypeAlias(node);
} finally {
localScope = outerScope;
}
}
@override
void visitImportDirective(ImportDirective node) {
var prefix = node.prefix;
if (prefix != null) {
importPrefixNames.add(prefix.name);
}
super.visitImportDirective(node);
}
@override
void visitMethodDeclaration(MethodDeclaration node) {
_LocalNameScope outerScope = localScope;
try {
localScope = _LocalNameScope.forMethod(localScope, node);
super.visitMethodDeclaration(node);
} finally {
localScope = outerScope;
}
}
@override
void visitSimpleIdentifier(SimpleIdentifier node) {
// Ignore all declarations.
if (node.inDeclarationContext()) {
return;
}
// Ignore class names references from constructors.
var parent = node.parent!;
if (parent is ConstructorDeclaration && parent.returnType == node) {
return;
}
// Prepare name.
String name = node.name;
// Ignore names shadowed by local elements.
if (node.isQualified || _isNameExpressionLabel(parent)) {
// Cannot be local.
} else {
if (localScope.contains(name)) {
return;
}
if (importPrefixNames.contains(name)) {
return;
}
}
// Do add the name.
names.add(name);
}
static bool _isNameExpressionLabel(AstNode parent) {
if (parent is Label) {
var parent2 = parent.parent;
return parent2 is NamedExpression && parent2.name == parent;
}
return false;
}
}