Chromium Code Reviews| 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); |
| + } |
| +} |