OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 import 'package:analyzer/dart/ast/ast.dart'; | |
6 import 'package:analyzer/dart/ast/visitor.dart'; | |
7 | |
8 /** | |
9 * Compute the set of external names referenced in the [unit]. | |
10 */ | |
11 Set<String> computeReferencedNames(CompilationUnit unit) { | |
12 _ReferencedNamesComputer computer = new _ReferencedNamesComputer(); | |
13 unit.accept(computer); | |
14 return computer.names; | |
15 } | |
16 | |
17 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.
| |
18 final _LocalNameScope enclosing; | |
19 Set<String> names; | |
20 | |
21 _LocalNameScope(this.enclosing); | |
22 | |
23 factory _LocalNameScope.forBlock(_LocalNameScope enclosing, Block node) { | |
24 _LocalNameScope scope = new _LocalNameScope(enclosing); | |
25 for (Statement statement in node.statements) { | |
26 if (statement is FunctionDeclarationStatement) { | |
27 scope.add(statement.functionDeclaration.name.name); | |
28 } else if (statement is VariableDeclarationStatement) { | |
29 for (VariableDeclaration variable in statement.variables.variables) { | |
30 scope.add(variable.name.name); | |
31 } | |
32 } | |
33 } | |
34 return scope; | |
35 } | |
36 | |
37 factory _LocalNameScope.forClass( | |
38 _LocalNameScope enclosing, ClassDeclaration node) { | |
39 _LocalNameScope scope = new _LocalNameScope(enclosing); | |
40 scope.addTypeParameters(node.typeParameters); | |
41 for (ClassMember member in node.members) { | |
42 if (member is FieldDeclaration) { | |
43 for (VariableDeclaration variable in member.fields.variables) { | |
44 scope.add(variable.name.name); | |
45 } | |
46 } else if (member is MethodDeclaration) { | |
47 scope.add(member.name.name); | |
48 } | |
49 } | |
50 return scope; | |
51 } | |
52 | |
53 factory _LocalNameScope.forClassTypeAlias( | |
54 _LocalNameScope enclosing, ClassTypeAlias node) { | |
55 _LocalNameScope scope = new _LocalNameScope(enclosing); | |
56 scope.addTypeParameters(node.typeParameters); | |
57 return scope; | |
58 } | |
59 | |
60 factory _LocalNameScope.forFunction( | |
61 _LocalNameScope enclosing, FunctionDeclaration node) { | |
62 _LocalNameScope scope = new _LocalNameScope(enclosing); | |
63 scope.addTypeParameters(node.functionExpression.typeParameters); | |
64 scope.addFormalParameters(node.functionExpression.parameters); | |
65 return scope; | |
66 } | |
67 | |
68 factory _LocalNameScope.forFunctionTypeAlias( | |
69 _LocalNameScope enclosing, FunctionTypeAlias node) { | |
70 _LocalNameScope scope = new _LocalNameScope(enclosing); | |
71 scope.addTypeParameters(node.typeParameters); | |
72 return scope; | |
73 } | |
74 | |
75 factory _LocalNameScope.forMethod( | |
76 _LocalNameScope enclosing, MethodDeclaration node) { | |
77 _LocalNameScope scope = new _LocalNameScope(enclosing); | |
78 scope.addTypeParameters(node.typeParameters); | |
79 scope.addFormalParameters(node.parameters); | |
80 return scope; | |
81 } | |
82 | |
83 void add(String name) { | |
84 names ??= new Set<String>(); | |
85 names.add(name); | |
86 } | |
87 | |
88 void addFormalParameters(FormalParameterList parameterList) { | |
89 if (parameterList != null) { | |
90 parameterList.parameters | |
91 .map((p) => p is NormalFormalParameter ? p.identifier.name : '') | |
92 .forEach(add); | |
93 } | |
94 } | |
95 | |
96 void addTypeParameters(TypeParameterList typeParameterList) { | |
97 if (typeParameterList != null) { | |
98 typeParameterList.typeParameters.map((p) => p.name.name).forEach(add); | |
99 } | |
100 } | |
101 | |
102 bool contains(String name) { | |
103 if (names != null && names.contains(name)) { | |
104 return true; | |
105 } | |
106 if (enclosing != null) { | |
107 return enclosing.contains(name); | |
108 } | |
109 return false; | |
110 } | |
111 } | |
112 | |
113 class _ReferencedNamesComputer extends GeneralizingAstVisitor { | |
114 final Set<String> names = new Set<String>(); | |
115 final Set<String> importPrefixNames = new Set<String>(); | |
116 | |
117 _LocalNameScope localScope = new _LocalNameScope(null); | |
118 int localLevel = 0; | |
119 | |
120 @override | |
121 visitBlock(Block node) { | |
122 _LocalNameScope outerScope = localScope; | |
123 try { | |
124 localScope = new _LocalNameScope.forBlock(localScope, node); | |
125 super.visitBlock(node); | |
126 } finally { | |
127 localScope = outerScope; | |
128 } | |
129 } | |
130 | |
131 @override | |
132 visitClassDeclaration(ClassDeclaration node) { | |
133 _LocalNameScope outerScope = localScope; | |
134 try { | |
135 localScope = new _LocalNameScope.forClass(localScope, node); | |
136 super.visitClassDeclaration(node); | |
137 } finally { | |
138 localScope = outerScope; | |
139 } | |
140 } | |
141 | |
142 @override | |
143 visitClassTypeAlias(ClassTypeAlias node) { | |
144 _LocalNameScope outerScope = localScope; | |
145 try { | |
146 localScope = new _LocalNameScope.forClassTypeAlias(localScope, node); | |
147 super.visitClassTypeAlias(node); | |
148 } finally { | |
149 localScope = outerScope; | |
150 } | |
151 } | |
152 | |
153 @override | |
154 visitComment(Comment node) { | |
155 try { | |
156 localLevel++; | |
157 super.visitComment(node); | |
158 } finally { | |
159 localLevel--; | |
160 } | |
161 } | |
162 | |
163 @override | |
164 visitConstructorName(ConstructorName node) { | |
165 if (node.parent is! ConstructorDeclaration) { | |
166 super.visitConstructorName(node); | |
167 } | |
168 } | |
169 | |
170 @override | |
171 visitFunctionBody(FunctionBody node) { | |
172 try { | |
173 localLevel++; | |
174 super.visitFunctionBody(node); | |
175 } finally { | |
176 localLevel--; | |
177 } | |
178 } | |
179 | |
180 @override | |
181 visitFunctionDeclaration(FunctionDeclaration node) { | |
182 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
| |
183 _LocalNameScope outerScope = localScope; | |
184 try { | |
185 localScope = new _LocalNameScope.forFunction(localScope, node); | |
186 super.visitFunctionDeclaration(node); | |
187 } finally { | |
188 localScope = outerScope; | |
189 } | |
190 } else { | |
191 super.visitFunctionDeclaration(node); | |
192 } | |
193 } | |
194 | |
195 @override | |
196 visitFunctionTypeAlias(FunctionTypeAlias node) { | |
197 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.
| |
198 _LocalNameScope outerScope = localScope; | |
199 try { | |
200 localScope = new _LocalNameScope.forFunctionTypeAlias(localScope, node); | |
201 super.visitFunctionTypeAlias(node); | |
202 } finally { | |
203 localScope = outerScope; | |
204 } | |
205 } else { | |
206 super.visitFunctionTypeAlias(node); | |
207 } | |
208 } | |
209 | |
210 @override | |
211 visitImportDirective(ImportDirective node) { | |
212 if (node.prefix != null) { | |
213 importPrefixNames.add(node.prefix.name); | |
214 } | |
215 super.visitImportDirective(node); | |
216 } | |
217 | |
218 @override | |
219 visitMethodDeclaration(MethodDeclaration node) { | |
220 _LocalNameScope outerScope = localScope; | |
221 try { | |
222 localScope = new _LocalNameScope.forMethod(localScope, node); | |
223 super.visitMethodDeclaration(node); | |
224 } finally { | |
225 localScope = outerScope; | |
226 } | |
227 } | |
228 | |
229 @override | |
230 visitSimpleIdentifier(SimpleIdentifier node) { | |
231 // Ignore all declarations. | |
232 if (node.inDeclarationContext()) { | |
233 return; | |
234 } | |
235 // Ignore class names references from constructors. | |
236 AstNode parent = node.parent; | |
237 if (parent is ConstructorDeclaration && parent.returnType == node) { | |
238 return; | |
239 } | |
240 // Prepare name. | |
241 String name = node.name; | |
242 // Ignore unqualified names shadowed by local elements. | |
243 if (!node.isQualified) { | |
244 if (localScope.contains(name)) { | |
245 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
| |
246 } | |
247 if (importPrefixNames.contains(name)) { | |
248 return; | |
249 } | |
250 } | |
251 // Do add the name. | |
252 names.add(name); | |
253 } | |
254 } | |
OLD | NEW |