Index: pkg/analyzer/lib/src/dart/analysis/referenced_names.dart |
diff --git a/pkg/analyzer/lib/src/dart/analysis/referenced_names.dart b/pkg/analyzer/lib/src/dart/analysis/referenced_names.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..811aa925f59ed7771d562ec65a43b3acd6bc9bdb |
--- /dev/null |
+++ b/pkg/analyzer/lib/src/dart/analysis/referenced_names.dart |
@@ -0,0 +1,250 @@ |
+// 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.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 |
+ 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 unqualified names shadowed by local elements. |
+ if (!node.isQualified) { |
+ if (localScope.contains(name)) { |
+ return; |
+ } |
+ if (importPrefixNames.contains(name)) { |
+ return; |
+ } |
+ } |
+ // Do add the name. |
+ names.add(name); |
+ } |
+} |