OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2014, 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 /** | |
6 * Code for classifying the semantics of identifiers appearing in a Dart file. | |
7 */ | |
8 library analyzer2dart.identifierSemantics; | |
9 | |
10 import 'package:analyzer/analyzer.dart'; | |
11 import 'package:analyzer/src/generated/element.dart'; | |
12 | |
13 /** | |
14 * Base class used to classify the semantics of a method or function | |
15 * invocation. | |
16 */ | |
17 abstract class MethodInvocationSemantics {} | |
18 | |
19 /** | |
20 * MethodInvocationSemantics object representing the invocation of a method | |
21 * that is defined statically within a class, or of a function that is defined | |
22 * at toplevel within a library. | |
23 */ | |
24 class StaticMethodInvocation extends MethodInvocationSemantics { | |
25 /** | |
26 * The method or function being invoked. | |
27 */ | |
28 final ExecutableElement methodElement; | |
29 | |
30 /** | |
31 * The class containing the member being invoked, or null if a toplevel | |
32 * function is being invoked. | |
33 */ | |
34 final ClassElement classElement; | |
35 | |
36 /** | |
37 * The identifier being used to invoke the method or function. | |
38 */ | |
39 final SimpleIdentifier methodName; | |
40 | |
41 // TODO(paulberry): would it also be useful to store the libraryElement? | |
42 | |
43 StaticMethodInvocation(this.methodElement, this.classElement, | |
44 this.methodName); | |
45 } | |
46 | |
47 /** | |
48 * MethodInvocationSemantics object representing the invocation of a function | |
49 * that is defined inside of another function or method. | |
50 */ | |
51 class LocalFunctionInvocation extends MethodInvocationSemantics { | |
52 /** | |
53 * The function being invoked. | |
54 */ | |
55 final FunctionElement functionElement; | |
56 | |
57 LocalFunctionInvocation(this.functionElement); | |
58 } | |
59 | |
60 /** | |
61 * MethodInvocationSemantics object representing the invocation of an instance | |
62 * method within a class. | |
63 * | |
64 * This also handles calls to an undefined method using a bare | |
65 * identifier (since it cannot be determined until runtime whether the method | |
66 * is truly undefined). | |
67 */ | |
68 class DynamicMethodInvocation extends MethodInvocationSemantics { | |
69 /** | |
70 * The expression on which the method is being invoked (or null if the target | |
71 * is an implicit "this"). | |
72 */ | |
73 final Expression target; | |
74 | |
75 /** | |
76 * The identifier being used to invoke the method. | |
77 */ | |
78 final SimpleIdentifier methodName; | |
79 | |
80 DynamicMethodInvocation(this.target, this.methodName); | |
81 } | |
82 | |
83 /** | |
84 * Base class used to classify the semantics of a property access. | |
85 */ | |
86 abstract class AccessSemantics { | |
87 /** | |
88 * The identifier being used to access the property. | |
89 */ | |
90 final SimpleIdentifier _identifier; | |
91 | |
92 AccessSemantics(this._identifier); | |
93 | |
94 /** | |
95 * True if this is a read access to the property. Note that both | |
96 * [isRead] and [isWrite] will be true in the case of a read-modify-write | |
97 * operation (e.g. "+="). | |
98 */ | |
99 bool get isRead => _identifier.inGetterContext(); | |
100 | |
101 /** | |
102 * True if this is a write access to the property. Note that both | |
103 * [isRead] and [isWrite] will be true in the case of a read-modify-write | |
104 * operation (e.g. "+="). | |
105 */ | |
106 bool get isWrite => _identifier.inSetterContext(); | |
107 } | |
108 | |
109 /** | |
110 * AccessSemantics object representing an access to a field that is defined | |
111 * statically within a class, or to a variable that is defined at toplevel | |
112 * within a library. | |
113 */ | |
114 class StaticFieldAccess extends AccessSemantics { | |
115 /** | |
116 * The field or variable being referenced. | |
117 */ | |
118 final PropertyInducingElement field; | |
119 | |
120 /** | |
121 * The class containing the field being referenced, or null if a toplevel | |
122 * variable is being referenced. | |
123 */ | |
124 final ClassElement classElement; | |
125 | |
126 // TODO(paulberry): would it also be useful to store the libraryElement? | |
127 | |
128 StaticFieldAccess(this.field, this.classElement, SimpleIdentifier identifier) | |
129 : super(identifier); | |
130 } | |
131 | |
132 /** | |
133 * AccessSemantics object representing an access to a property that is defined | |
134 * statically within a class, or defined at toplevel within a library. | |
135 */ | |
136 class StaticPropertyAccess extends AccessSemantics { | |
137 /** | |
138 * The property being referenced, or null if the property is undefined. | |
139 */ | |
140 final PropertyAccessorElement property; | |
141 | |
142 /** | |
143 * The class containing the property being referenced, or null if a toplevel | |
144 * property is being referenced. | |
145 */ | |
146 final ClassElement classElement; | |
147 | |
148 /** | |
149 * The identifier being used to access the property. | |
150 */ | |
151 SimpleIdentifier get propertyName => _identifier; | |
152 | |
153 // TODO(paulberry): would it also be useful to store the libraryElement? | |
154 | |
155 StaticPropertyAccess(this.property, this.classElement, | |
156 SimpleIdentifier propertyName) | |
157 : super(propertyName); | |
158 } | |
159 | |
160 /** | |
161 * AccessSemantics object representing an access to an instance property or | |
162 * field within a class. | |
163 * | |
164 * This also handles access to an undefined property using a bare | |
165 * identifier (since it cannot be determined until runtime whether the property | |
166 * is truly undefined). | |
167 */ | |
168 class DynamicPropertyAccess extends AccessSemantics { | |
169 /** | |
170 * The expression on which the method is being invoked (or null if the target | |
171 * is an implicit "this"). | |
172 */ | |
173 final Expression target; | |
174 | |
175 /** | |
176 * The identifier being used to access the property. | |
177 */ | |
178 final SimpleIdentifier propertyName; | |
179 | |
180 DynamicPropertyAccess(this.target, SimpleIdentifier propertyName) | |
181 : super(propertyName), | |
182 propertyName = propertyName; | |
183 } | |
184 | |
185 /** | |
186 * AccessSemantics object representing an access to a local variable within a | |
187 * function or method. | |
188 */ | |
189 class LocalVariableAccess extends AccessSemantics { | |
190 final LocalVariableElement variable; | |
191 LocalVariableAccess(this.variable, SimpleIdentifier identifier) | |
192 : super(identifier); | |
193 } | |
194 | |
195 /** | |
196 * Return the method invocation semantics for [node]. | |
197 */ | |
198 MethodInvocationSemantics classifyMethodInvocation(MethodInvocation node) { | |
199 Expression target = node.realTarget; | |
200 if (target == null) { | |
201 Element staticElement = node.methodName.staticElement; | |
202 if (staticElement is FunctionElement) { | |
203 if (staticElement.enclosingElement is CompilationUnitElement) { | |
204 return new StaticMethodInvocation(staticElement, null, node.methodName); | |
205 } else { | |
206 return new LocalFunctionInvocation(staticElement); | |
207 } | |
208 } else if (staticElement is MethodElement && staticElement.isStatic) { | |
209 return new StaticMethodInvocation( | |
210 staticElement, | |
211 staticElement.enclosingElement, | |
212 node.methodName); | |
213 } | |
Brian Wilkerson
2014/09/22 19:44:48
It's also possible for the static element to be a
Paul Berry
2014/09/23 16:32:49
Done.
| |
214 } else if (target is SimpleIdentifier) { | |
215 Element targetStaticElement = target.staticElement; | |
216 if (targetStaticElement is PrefixElement) { | |
217 if (node.methodName.staticElement != null) { | |
218 return new StaticMethodInvocation( | |
219 node.methodName.staticElement, | |
220 null, | |
221 node.methodName); | |
222 } else { | |
223 return new DynamicMethodInvocation(null, node.methodName); | |
224 } | |
225 } else if (targetStaticElement is ClassElement) { | |
226 return new StaticMethodInvocation( | |
227 node.methodName.staticElement, | |
228 targetStaticElement, | |
229 node.methodName); | |
230 } | |
231 } | |
Brian Wilkerson
2014/09/22 19:44:47
This is missing the case where the target is a Pre
Paul Berry
2014/09/23 16:32:49
Done. It turned out that I was able to handle thi
| |
232 return new DynamicMethodInvocation(target, node.methodName); | |
233 } | |
234 | |
235 /** | |
236 * Return the access semantics for [node]. | |
237 */ | |
238 AccessSemantics classifyPrefixedIdentifier(PrefixedIdentifier node) { | |
239 Element prefixElement = node.prefix.staticElement; | |
240 Element suffixElement = node.identifier.staticElement; | |
241 if (prefixElement is PrefixElement) { | |
242 Element staticElement = node.identifier.staticElement; | |
Brian Wilkerson
2014/09/22 19:44:48
staticElement == suffixElement
Paul Berry
2014/09/23 16:32:49
Done.
| |
243 if (staticElement is PropertyAccessorElement) { | |
244 if (staticElement.isSynthetic) { | |
245 return new StaticFieldAccess( | |
246 staticElement.variable, | |
247 null, | |
248 node.identifier); | |
249 } else { | |
250 return new StaticPropertyAccess(staticElement, null, node.identifier); | |
251 } | |
252 } | |
253 return new DynamicPropertyAccess(null, node.identifier); | |
254 } else if (prefixElement is ClassElement) { | |
255 if (suffixElement is PropertyAccessorElement && suffixElement.isSynthetic) { | |
256 return new StaticFieldAccess( | |
257 suffixElement.variable, | |
258 prefixElement, | |
259 node.identifier); | |
260 } | |
261 return new StaticPropertyAccess( | |
262 suffixElement, | |
263 prefixElement, | |
264 node.identifier); | |
265 } else { | |
266 return new DynamicPropertyAccess(node.prefix, node.identifier); | |
Brian Wilkerson
2014/09/22 19:44:47
Do we represent method tear-offs as property acces
Paul Berry
2014/09/23 16:32:49
Method tear-offs weren't being handled properly.
| |
267 } | |
268 } | |
269 | |
270 /** | |
271 * Return the access semantics for [node]. | |
272 */ | |
273 DynamicPropertyAccess classifyPropertyAccess(PropertyAccess node) { | |
274 return new DynamicPropertyAccess(node.realTarget, node.propertyName); | |
275 } | |
276 | |
277 /** | |
278 * Return the access semantics for [node]. Caller must ensure that [node] is | |
279 * not the right hand side of a [PropertyAccess] or [PrefixedIdentifier], or | |
280 * the method name of a [MethodInvocation]. | |
281 */ | |
282 AccessSemantics classifyBareIdentifier(SimpleIdentifier node) { | |
283 AstNode parent = node.parent; | |
284 if ((parent is FunctionDeclaration && parent.name == node) || | |
Brian Wilkerson
2014/09/22 19:44:47
Replace this 'if' with
if (node.inDeclarationCont
Paul Berry
2014/09/23 16:32:49
Aha, that's much better. Thanks!
| |
285 parent is ImportDirective || | |
286 (parent is ClassDeclaration && parent.name == node) || | |
287 (parent is MethodDeclaration && parent.name == node) || | |
288 (parent is VariableDeclaration && parent.name == node) || | |
289 (parent is FormalParameter && parent.identifier == node)) { | |
290 // This identifier is a declaration, not a use. | |
291 // TODO(paulberry): Can some of the above cases be coalesced? | |
292 // TODO(paulberry): Additional cases | |
293 return null; | |
294 } | |
295 if (parent is TypeName) { | |
296 // TODO(paulberry): handle this case. | |
Brian Wilkerson
2014/09/22 19:44:48
It might be cleanest to not visit the children of
Paul Berry
2014/09/23 16:32:49
Hmm, that may be true. I'm not sure whether I wan
Brian Wilkerson
2014/09/23 17:03:58
It looks like you're currently exiting if you're v
Paul Berry
2014/09/23 17:45:56
Acknowledged.
| |
297 return null; | |
298 } | |
299 // TODO(paulberry): handle PrefixElement. | |
300 Element staticElement = node.staticElement; | |
301 if (staticElement is PropertyAccessorElement) { | |
302 if (staticElement.isSynthetic) { | |
303 if (staticElement.enclosingElement is CompilationUnitElement) { | |
304 return new StaticFieldAccess(staticElement.variable, null, node); | |
305 } else if (staticElement.isStatic) { | |
306 return new StaticFieldAccess( | |
307 staticElement.variable, | |
308 staticElement.enclosingElement, | |
309 node); | |
310 } | |
311 } else { | |
312 if (staticElement.enclosingElement is CompilationUnitElement) { | |
313 return new StaticPropertyAccess(staticElement, null, node); | |
314 } else if (staticElement.isStatic) { | |
315 return new StaticPropertyAccess( | |
316 staticElement, | |
317 staticElement.enclosingElement, | |
318 node); | |
319 } | |
320 } | |
321 } else if (staticElement is LocalVariableElement) { | |
322 return new LocalVariableAccess(staticElement, node); | |
323 } | |
Brian Wilkerson
2014/09/22 19:44:48
Missing ParameterElement?
Paul Berry
2014/09/23 16:32:49
Done.
| |
324 return new DynamicPropertyAccess(null, node); | |
325 } | |
OLD | NEW |