Chromium Code Reviews| 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 |