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..5379d8c627c919096f1505dea83fa4df39982609 |
--- /dev/null |
+++ b/pkg/analyzer/lib/src/dart/analysis/referenced_names.dart |
@@ -0,0 +1,254 @@ |
+// 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; |
+} |
+ |
+class _LocalNameScope { |
Brian Wilkerson
2016/11/18 17:55:51
Consider renaming (and documenting) the class. It
scheglov
2016/11/18 18:33:44
I've added a comment.
|
+ 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.name); |
+ } else if (statement is VariableDeclarationStatement) { |
+ for (VariableDeclaration variable in statement.variables.variables) { |
+ scope.add(variable.name.name); |
+ } |
+ } |
+ } |
+ 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) { |
+ for (VariableDeclaration variable in member.fields.variables) { |
+ scope.add(variable.name.name); |
+ } |
+ } else if (member is MethodDeclaration) { |
+ scope.add(member.name.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; |
+ } |
+ |
+ void add(String name) { |
+ names ??= new Set<String>(); |
+ names.add(name); |
+ } |
+ |
+ void addFormalParameters(FormalParameterList parameterList) { |
+ if (parameterList != null) { |
+ parameterList.parameters |
+ .map((p) => p is NormalFormalParameter ? p.identifier.name : '') |
+ .forEach(add); |
+ } |
+ } |
+ |
+ void addTypeParameters(TypeParameterList typeParameterList) { |
+ if (typeParameterList != null) { |
+ typeParameterList.typeParameters.map((p) => p.name.name).forEach(add); |
+ } |
+ } |
+ |
+ 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); |
+ int localLevel = 0; |
+ |
+ @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 |
+ visitComment(Comment node) { |
+ try { |
+ localLevel++; |
+ super.visitComment(node); |
+ } finally { |
+ localLevel--; |
+ } |
+ } |
+ |
+ @override |
+ visitConstructorName(ConstructorName node) { |
+ if (node.parent is! ConstructorDeclaration) { |
+ super.visitConstructorName(node); |
+ } |
+ } |
+ |
+ @override |
+ visitFunctionBody(FunctionBody node) { |
+ try { |
+ localLevel++; |
+ super.visitFunctionBody(node); |
+ } finally { |
+ localLevel--; |
+ } |
+ } |
+ |
+ @override |
+ visitFunctionDeclaration(FunctionDeclaration node) { |
+ if (localLevel == 0) { |
Paul Berry
2016/11/18 17:59:59
Why do we skip this logic for local functions? It
scheglov
2016/11/18 18:33:44
Acknowledged.
You're right.
I've added the corresp
|
+ _LocalNameScope outerScope = localScope; |
+ try { |
+ localScope = new _LocalNameScope.forFunction(localScope, node); |
+ super.visitFunctionDeclaration(node); |
+ } finally { |
+ localScope = outerScope; |
+ } |
+ } else { |
+ super.visitFunctionDeclaration(node); |
+ } |
+ } |
+ |
+ @override |
+ visitFunctionTypeAlias(FunctionTypeAlias node) { |
+ if (localLevel == 0) { |
Paul Berry
2016/11/18 17:59:59
Why is this necessary? I thought function type al
scheglov
2016/11/18 18:33:44
Acknowledged.
|
+ _LocalNameScope outerScope = localScope; |
+ try { |
+ localScope = new _LocalNameScope.forFunctionTypeAlias(localScope, node); |
+ super.visitFunctionTypeAlias(node); |
+ } finally { |
+ localScope = outerScope; |
+ } |
+ } else { |
+ super.visitFunctionTypeAlias(node); |
+ } |
+ } |
+ |
+ @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; |
Paul Berry
2016/11/18 17:59:59
What happens if the code looks like this?
void f(
scheglov
2016/11/18 18:33:44
Actually this works.
Local declarations hide only
Paul Berry
2016/11/18 19:15:44
Ah, ok. I mistakenly thought that "isQualified" j
|
+ } |
+ if (importPrefixNames.contains(name)) { |
+ return; |
+ } |
+ } |
+ // Do add the name. |
+ names.add(name); |
+ } |
+} |