| // 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 = new _ReferencedNamesComputer(); |
| unit.accept(computer); |
| return computer.names; |
| } |
| |
| /** |
| * 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 = new _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 = new _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 = new _LocalNameScope(enclosing); |
| scope.addTypeParameters(node.typeParameters); |
| return scope; |
| } |
| |
| factory _LocalNameScope.forConstructor( |
| _LocalNameScope enclosing, ConstructorDeclaration node) { |
| _LocalNameScope scope = new _LocalNameScope(enclosing); |
| scope.addFormalParameters(node.parameters); |
| return scope; |
| } |
| |
| factory _LocalNameScope.forFunction( |
| _LocalNameScope enclosing, FunctionDeclaration node) { |
| _LocalNameScope scope = new _LocalNameScope(enclosing); |
| scope.addTypeParameters(node.functionExpression.typeParameters); |
| scope.addFormalParameters(node.functionExpression.parameters); |
| return scope; |
| } |
| |
| factory _LocalNameScope.forFunctionTypeAlias( |
| _LocalNameScope enclosing, FunctionTypeAlias node) { |
| _LocalNameScope scope = new _LocalNameScope(enclosing); |
| scope.addTypeParameters(node.typeParameters); |
| return scope; |
| } |
| |
| factory _LocalNameScope.forMethod( |
| _LocalNameScope enclosing, MethodDeclaration node) { |
| _LocalNameScope scope = new _LocalNameScope(enclosing); |
| scope.addTypeParameters(node.typeParameters); |
| scope.addFormalParameters(node.parameters); |
| return scope; |
| } |
| |
| factory _LocalNameScope.forUnit(CompilationUnit node) { |
| _LocalNameScope scope = new _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 ??= new Set<String>(); |
| names.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 { |
| final Set<String> names = new Set<String>(); |
| final Set<String> importPrefixNames = new Set<String>(); |
| |
| _LocalNameScope localScope = new _LocalNameScope(null); |
| |
| @override |
| visitBlock(Block node) { |
| _LocalNameScope outerScope = localScope; |
| try { |
| localScope = new _LocalNameScope.forBlock(localScope, node); |
| super.visitBlock(node); |
| } finally { |
| localScope = outerScope; |
| } |
| } |
| |
| @override |
| visitClassDeclaration(ClassDeclaration node) { |
| _LocalNameScope outerScope = localScope; |
| try { |
| localScope = new _LocalNameScope.forClass(localScope, node); |
| super.visitClassDeclaration(node); |
| } finally { |
| localScope = outerScope; |
| } |
| } |
| |
| @override |
| visitClassTypeAlias(ClassTypeAlias node) { |
| _LocalNameScope outerScope = localScope; |
| try { |
| localScope = new _LocalNameScope.forClassTypeAlias(localScope, node); |
| super.visitClassTypeAlias(node); |
| } finally { |
| localScope = outerScope; |
| } |
| } |
| |
| @override |
| visitCompilationUnit(CompilationUnit node) { |
| localScope = new _LocalNameScope.forUnit(node); |
| super.visitCompilationUnit(node); |
| } |
| |
| @override |
| visitConstructorDeclaration(ConstructorDeclaration node) { |
| _LocalNameScope outerScope = localScope; |
| try { |
| localScope = new _LocalNameScope.forConstructor(localScope, node); |
| super.visitConstructorDeclaration(node); |
| } finally { |
| localScope = outerScope; |
| } |
| } |
| |
| @override |
| visitConstructorName(ConstructorName node) { |
| if (node.parent is! ConstructorDeclaration) { |
| super.visitConstructorName(node); |
| } |
| } |
| |
| @override |
| visitFunctionDeclaration(FunctionDeclaration node) { |
| _LocalNameScope outerScope = localScope; |
| try { |
| localScope = new _LocalNameScope.forFunction(localScope, node); |
| super.visitFunctionDeclaration(node); |
| } finally { |
| localScope = outerScope; |
| } |
| } |
| |
| @override |
| visitFunctionTypeAlias(FunctionTypeAlias node) { |
| _LocalNameScope outerScope = localScope; |
| try { |
| localScope = new _LocalNameScope.forFunctionTypeAlias(localScope, node); |
| super.visitFunctionTypeAlias(node); |
| } finally { |
| localScope = outerScope; |
| } |
| } |
| |
| @override |
| visitImportDirective(ImportDirective node) { |
| if (node.prefix != null) { |
| importPrefixNames.add(node.prefix.name); |
| } |
| super.visitImportDirective(node); |
| } |
| |
| @override |
| visitMethodDeclaration(MethodDeclaration node) { |
| _LocalNameScope outerScope = localScope; |
| try { |
| localScope = new _LocalNameScope.forMethod(localScope, node); |
| super.visitMethodDeclaration(node); |
| } finally { |
| localScope = outerScope; |
| } |
| } |
| |
| @override |
| visitSimpleIdentifier(SimpleIdentifier node) { |
| // Ignore all declarations. |
| if (node.inDeclarationContext()) { |
| return; |
| } |
| // Ignore class names references from constructors. |
| AstNode 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) { |
| AstNode parent2 = parent?.parent; |
| return parent2 is NamedExpression && parent2.name == parent; |
| } |
| return false; |
| } |
| } |