| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * Code for classifying the semantics of identifiers appearing in a Dart file. | 6 * Code for classifying the semantics of identifiers appearing in a Dart file. |
| 7 */ | 7 */ |
| 8 library analyzer2dart.identifierSemantics; | 8 library analyzer2dart.identifierSemantics; |
| 9 | 9 |
| 10 import 'package:analyzer/analyzer.dart'; | 10 import 'package:analyzer/analyzer.dart'; |
| 11 import 'package:analyzer/src/generated/element.dart'; | 11 import 'package:analyzer/src/generated/element.dart'; |
| 12 | 12 |
| 13 // TODO(johnniwinther,paulberry): This should be a constant. |
| 14 final AccessSemanticsVisitor ACCESS_SEMANTICS_VISITOR = |
| 15 new AccessSemanticsVisitor(); |
| 16 |
| 13 /** | 17 /** |
| 14 * Enum representing the different kinds of destinations which a property | 18 * Enum representing the different kinds of destinations which a property |
| 15 * access or method or function invocation might refer to. | 19 * access or method or function invocation might refer to. |
| 16 */ | 20 */ |
| 17 class AccessKind { | 21 class AccessKind { |
| 18 /** | 22 /** |
| 19 * The destination of the access is an instance method, property, or field | 23 * The destination of the access is an instance method, property, or field |
| 20 * of a class, and thus must be determined dynamically. | 24 * of a class, and thus must be determined dynamically. |
| 21 */ | 25 */ |
| 22 static const AccessKind DYNAMIC = const AccessKind._('DYNAMIC'); | 26 static const AccessKind DYNAMIC = const AccessKind._('DYNAMIC'); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 55 * The destination of the access is a property getter/setter that is defined | 59 * The destination of the access is a property getter/setter that is defined |
| 56 * statically within a class, or at top level within a library. | 60 * statically within a class, or at top level within a library. |
| 57 */ | 61 */ |
| 58 static const AccessKind STATIC_PROPERTY = | 62 static const AccessKind STATIC_PROPERTY = |
| 59 const AccessKind._('STATIC_PROPERTY'); | 63 const AccessKind._('STATIC_PROPERTY'); |
| 60 | 64 |
| 61 /** | 65 /** |
| 62 * The destination of the access is a toplevel class, function typedef, mixin | 66 * The destination of the access is a toplevel class, function typedef, mixin |
| 63 * application, or the built-in type "dynamic". | 67 * application, or the built-in type "dynamic". |
| 64 */ | 68 */ |
| 65 static const AccessKind TOPLEVEL_TYPE = | 69 static const AccessKind TOPLEVEL_TYPE = const AccessKind._('TOPLEVEL_TYPE'); |
| 66 const AccessKind._('TOPLEVEL_TYPE'); | |
| 67 | 70 |
| 68 /** | 71 /** |
| 69 * The destination of the access is a type parameter of the enclosing class. | 72 * The destination of the access is a type parameter of the enclosing class. |
| 70 */ | 73 */ |
| 71 static const AccessKind TYPE_PARAMETER = | 74 static const AccessKind TYPE_PARAMETER = const AccessKind._('TYPE_PARAMETER'); |
| 72 const AccessKind._('TYPE_PARAMETER'); | |
| 73 | 75 |
| 74 final String name; | 76 final String name; |
| 75 | 77 |
| 78 const AccessKind._(this.name); |
| 79 |
| 76 String toString() => name; | 80 String toString() => name; |
| 77 | |
| 78 const AccessKind._(this.name); | |
| 79 } | 81 } |
| 80 | 82 |
| 81 /** | 83 /** |
| 82 * Data structure used to classify the semantics of a property access or method | 84 * Data structure used to classify the semantics of a property access or method |
| 83 * or function invocation. | 85 * or function invocation. |
| 84 */ | 86 */ |
| 85 // TODO(paulberry,johnniwinther): Support index operations in AccessSemantics. | 87 // TODO(paulberry,johnniwinther): Support index operations in AccessSemantics. |
| 86 class AccessSemantics { | 88 class AccessSemantics { |
| 87 /** | 89 /** |
| 88 * The kind of access. | 90 * The kind of access. |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 161 AccessSemantics.staticMethod(this.identifier, this.element, this.classElement, | 163 AccessSemantics.staticMethod(this.identifier, this.element, this.classElement, |
| 162 {this.isInvoke: false}) | 164 {this.isInvoke: false}) |
| 163 : kind = AccessKind.STATIC_METHOD, | 165 : kind = AccessKind.STATIC_METHOD, |
| 164 target = null; | 166 target = null; |
| 165 | 167 |
| 166 AccessSemantics.staticProperty(this.identifier, this.element, | 168 AccessSemantics.staticProperty(this.identifier, this.element, |
| 167 this.classElement, {this.isInvoke: false}) | 169 this.classElement, {this.isInvoke: false}) |
| 168 : kind = AccessKind.STATIC_PROPERTY, | 170 : kind = AccessKind.STATIC_PROPERTY, |
| 169 target = null; | 171 target = null; |
| 170 | 172 |
| 171 AccessSemantics.toplevelType(this.identifier, this.element) | 173 AccessSemantics.toplevelType(this.identifier, this.element, {this.isInvoke: |
| 174 false}) |
| 172 : kind = AccessKind.TOPLEVEL_TYPE, | 175 : kind = AccessKind.TOPLEVEL_TYPE, |
| 173 classElement = null, | 176 classElement = null, |
| 174 isInvoke = false, | |
| 175 target = null; | 177 target = null; |
| 176 | 178 |
| 177 AccessSemantics.typeParameter(this.identifier, this.element) | 179 AccessSemantics.typeParameter(this.identifier, this.element, {this.isInvoke: |
| 180 false}) |
| 178 : kind = AccessKind.TYPE_PARAMETER, | 181 : kind = AccessKind.TYPE_PARAMETER, |
| 179 classElement = null, | 182 classElement = null, |
| 180 isInvoke = false, | |
| 181 target = null; | 183 target = null; |
| 182 | 184 |
| 183 /** | 185 /** |
| 184 * True if this is a read access to a property, or a method tear-off. Note | 186 * True if this is a read access to a property, or a method tear-off. Note |
| 185 * that both [isRead] and [isWrite] will be true in the case of a | 187 * that both [isRead] and [isWrite] will be true in the case of a |
| 186 * read-modify-write operation (e.g. "+="). | 188 * read-modify-write operation (e.g. "+="). |
| 187 */ | 189 */ |
| 188 bool get isRead => !isInvoke && identifier.inGetterContext(); | 190 bool get isRead => !isInvoke && identifier.inGetterContext(); |
| 189 | 191 |
| 190 /** | 192 /** |
| (...skipping 28 matching lines...) Expand all Loading... |
| 219 sb.write('target=this.$identifier'); | 221 sb.write('target=this.$identifier'); |
| 220 } else { | 222 } else { |
| 221 sb.write('target=$target.$identifier'); | 223 sb.write('target=$target.$identifier'); |
| 222 } | 224 } |
| 223 } | 225 } |
| 224 sb.write(']'); | 226 sb.write(']'); |
| 225 return sb.toString(); | 227 return sb.toString(); |
| 226 } | 228 } |
| 227 } | 229 } |
| 228 | 230 |
| 229 // TODO(johnniwinther,paulberry): This should be a constant. | |
| 230 final AccessSemanticsVisitor ACCESS_SEMANTICS_VISITOR = | |
| 231 new AccessSemanticsVisitor(); | |
| 232 | |
| 233 // TODO(johnniwinther,paulberry): This should extend a non-recursive visitor. | 231 // TODO(johnniwinther,paulberry): This should extend a non-recursive visitor. |
| 234 class AccessSemanticsVisitor extends RecursiveAstVisitor<AccessSemantics> { | 232 class AccessSemanticsVisitor extends RecursiveAstVisitor<AccessSemantics> { |
| 235 /** | 233 /** |
| 236 * Return the semantics for [node]. | 234 * Return the semantics for [node]. |
| 237 */ | 235 */ |
| 238 @override | 236 @override |
| 239 AccessSemantics visitMethodInvocation(MethodInvocation node) { | 237 AccessSemantics visitMethodInvocation(MethodInvocation node) { |
| 240 Expression target = node.realTarget; | 238 Expression target = node.realTarget; |
| 241 Element staticElement = node.methodName.staticElement; | 239 Element staticElement = node.methodName.staticElement; |
| 242 if (target == null) { | 240 if (target == null) { |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 292 } else if (staticElement is LocalVariableElement) { | 290 } else if (staticElement is LocalVariableElement) { |
| 293 return new AccessSemantics.localVariable( | 291 return new AccessSemantics.localVariable( |
| 294 node.methodName, | 292 node.methodName, |
| 295 staticElement, | 293 staticElement, |
| 296 isInvoke: true); | 294 isInvoke: true); |
| 297 } else if (staticElement is ParameterElement) { | 295 } else if (staticElement is ParameterElement) { |
| 298 return new AccessSemantics.parameter( | 296 return new AccessSemantics.parameter( |
| 299 node.methodName, | 297 node.methodName, |
| 300 staticElement, | 298 staticElement, |
| 301 isInvoke: true); | 299 isInvoke: true); |
| 300 } else if (staticElement is TypeParameterElement) { |
| 301 return new AccessSemantics.typeParameter( |
| 302 node.methodName, |
| 303 staticElement, |
| 304 isInvoke: true); |
| 305 } else if (staticElement is ClassElement || |
| 306 staticElement is FunctionTypeAliasElement || |
| 307 staticElement is DynamicElementImpl) { |
| 308 return new AccessSemantics.toplevelType( |
| 309 node.methodName, |
| 310 staticElement, |
| 311 isInvoke: true); |
| 302 } | 312 } |
| 303 } else if (target is Identifier) { | 313 } else if (target is Identifier) { |
| 304 Element targetStaticElement = target.staticElement; | 314 Element targetStaticElement = target.staticElement; |
| 305 if (targetStaticElement is PrefixElement) { | 315 if (targetStaticElement is PrefixElement) { |
| 306 if (staticElement == null) { | 316 if (staticElement == null) { |
| 307 return new AccessSemantics.dynamic( | 317 return new AccessSemantics.dynamic( |
| 308 node.methodName, | 318 node.methodName, |
| 309 null, | 319 null, |
| 310 isInvoke: true); | 320 isInvoke: true); |
| 311 } else if (staticElement is PropertyAccessorElement) { | 321 } else if (staticElement is PropertyAccessorElement) { |
| 312 if (staticElement.isSynthetic) { | 322 if (staticElement.isSynthetic) { |
| 313 return new AccessSemantics.staticField( | 323 return new AccessSemantics.staticField( |
| 314 node.methodName, | 324 node.methodName, |
| 315 staticElement.variable, | 325 staticElement.variable, |
| 316 null, | 326 null, |
| 317 isInvoke: true); | 327 isInvoke: true); |
| 318 } else { | 328 } else { |
| 319 return new AccessSemantics.staticProperty( | 329 return new AccessSemantics.staticProperty( |
| 320 node.methodName, | 330 node.methodName, |
| 321 staticElement, | 331 staticElement, |
| 322 null, | 332 null, |
| 323 isInvoke: true); | 333 isInvoke: true); |
| 324 } | 334 } |
| 335 } else if (staticElement is TypeParameterElement) { |
| 336 return new AccessSemantics.typeParameter( |
| 337 node.methodName, |
| 338 staticElement, |
| 339 isInvoke: true); |
| 340 } else if (staticElement is ClassElement || |
| 341 staticElement is FunctionTypeAliasElement) { |
| 342 return new AccessSemantics.toplevelType( |
| 343 node.methodName, |
| 344 staticElement, |
| 345 isInvoke: true); |
| 325 } else { | 346 } else { |
| 326 return new AccessSemantics.staticMethod( | 347 return new AccessSemantics.staticMethod( |
| 327 node.methodName, | 348 node.methodName, |
| 328 staticElement, | 349 staticElement, |
| 329 null, | 350 null, |
| 330 isInvoke: true); | 351 isInvoke: true); |
| 331 } | 352 } |
| 332 } else if (targetStaticElement is ClassElement) { | 353 } else if (targetStaticElement is ClassElement) { |
| 333 if (staticElement is PropertyAccessorElement) { | 354 if (staticElement is PropertyAccessorElement) { |
| 334 if (staticElement.isSynthetic) { | 355 if (staticElement.isSynthetic) { |
| (...skipping 23 matching lines...) Expand all Loading... |
| 358 | 379 |
| 359 /** | 380 /** |
| 360 * Return the access semantics for [node]. | 381 * Return the access semantics for [node]. |
| 361 */ | 382 */ |
| 362 @override | 383 @override |
| 363 AccessSemantics visitPrefixedIdentifier(PrefixedIdentifier node) { | 384 AccessSemantics visitPrefixedIdentifier(PrefixedIdentifier node) { |
| 364 return _classifyPrefixed(node.prefix, node.identifier); | 385 return _classifyPrefixed(node.prefix, node.identifier); |
| 365 } | 386 } |
| 366 | 387 |
| 367 /** | 388 /** |
| 368 * Helper function for classifying an expression of type | |
| 369 * Identifier.SimpleIdentifier. | |
| 370 */ | |
| 371 AccessSemantics _classifyPrefixed(Identifier lhs, SimpleIdentifier rhs) { | |
| 372 Element lhsElement = lhs.staticElement; | |
| 373 Element rhsElement = rhs.staticElement; | |
| 374 if (lhsElement is PrefixElement) { | |
| 375 if (rhsElement is PropertyAccessorElement) { | |
| 376 if (rhsElement.isSynthetic) { | |
| 377 return new AccessSemantics.staticField(rhs, rhsElement.variable, null)
; | |
| 378 } else { | |
| 379 return new AccessSemantics.staticProperty(rhs, rhsElement, null); | |
| 380 } | |
| 381 } else if (rhsElement is FunctionElement) { | |
| 382 return new AccessSemantics.staticMethod(rhs, rhsElement, null); | |
| 383 } else if (rhsElement is ClassElement || | |
| 384 rhsElement is FunctionTypeAliasElement) { | |
| 385 return new AccessSemantics.toplevelType(rhs, rhsElement); | |
| 386 } else { | |
| 387 return new AccessSemantics.dynamic(rhs, null); | |
| 388 } | |
| 389 } else if (lhsElement is ClassElement) { | |
| 390 if (rhsElement is PropertyAccessorElement && rhsElement.isSynthetic) { | |
| 391 return new AccessSemantics.staticField( | |
| 392 rhs, | |
| 393 rhsElement.variable, | |
| 394 lhsElement); | |
| 395 } else if (rhsElement is MethodElement) { | |
| 396 return new AccessSemantics.staticMethod(rhs, rhsElement, lhsElement); | |
| 397 } else { | |
| 398 return new AccessSemantics.staticProperty(rhs, rhsElement, lhsElement); | |
| 399 } | |
| 400 } else { | |
| 401 return new AccessSemantics.dynamic(rhs, lhs); | |
| 402 } | |
| 403 } | |
| 404 | |
| 405 /** | |
| 406 * Return the access semantics for [node]. | 389 * Return the access semantics for [node]. |
| 407 */ | 390 */ |
| 408 @override | 391 @override |
| 409 AccessSemantics visitPropertyAccess(PropertyAccess node) { | 392 AccessSemantics visitPropertyAccess(PropertyAccess node) { |
| 410 if (node.target is Identifier) { | 393 if (node.target is Identifier) { |
| 411 return _classifyPrefixed(node.target, node.propertyName); | 394 return _classifyPrefixed(node.target, node.propertyName); |
| 412 } else { | 395 } else { |
| 413 return new AccessSemantics.dynamic(node.propertyName, node.realTarget); | 396 return new AccessSemantics.dynamic(node.propertyName, node.realTarget); |
| 414 } | 397 } |
| 415 } | 398 } |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 482 staticElement.enclosingElement); | 465 staticElement.enclosingElement); |
| 483 } else if (staticElement is TypeParameterElement) { | 466 } else if (staticElement is TypeParameterElement) { |
| 484 return new AccessSemantics.typeParameter(node, staticElement); | 467 return new AccessSemantics.typeParameter(node, staticElement); |
| 485 } else if (staticElement is ClassElement || | 468 } else if (staticElement is ClassElement || |
| 486 staticElement is FunctionTypeAliasElement || | 469 staticElement is FunctionTypeAliasElement || |
| 487 staticElement is DynamicElementImpl) { | 470 staticElement is DynamicElementImpl) { |
| 488 return new AccessSemantics.toplevelType(node, staticElement); | 471 return new AccessSemantics.toplevelType(node, staticElement); |
| 489 } | 472 } |
| 490 return new AccessSemantics.dynamic(node, null); | 473 return new AccessSemantics.dynamic(node, null); |
| 491 } | 474 } |
| 492 } | 475 |
| 476 /** |
| 477 * Helper function for classifying an expression of type |
| 478 * Identifier.SimpleIdentifier. |
| 479 */ |
| 480 AccessSemantics _classifyPrefixed(Identifier lhs, SimpleIdentifier rhs) { |
| 481 Element lhsElement = lhs.staticElement; |
| 482 Element rhsElement = rhs.staticElement; |
| 483 if (lhsElement is PrefixElement) { |
| 484 if (rhsElement is PropertyAccessorElement) { |
| 485 if (rhsElement.isSynthetic) { |
| 486 return new AccessSemantics.staticField( |
| 487 rhs, |
| 488 rhsElement.variable, |
| 489 null); |
| 490 } else { |
| 491 return new AccessSemantics.staticProperty(rhs, rhsElement, null); |
| 492 } |
| 493 } else if (rhsElement is FunctionElement) { |
| 494 return new AccessSemantics.staticMethod(rhs, rhsElement, null); |
| 495 } else if (rhsElement is ClassElement || |
| 496 rhsElement is FunctionTypeAliasElement) { |
| 497 return new AccessSemantics.toplevelType(rhs, rhsElement); |
| 498 } else { |
| 499 return new AccessSemantics.dynamic(rhs, null); |
| 500 } |
| 501 } else if (lhsElement is ClassElement) { |
| 502 if (rhsElement is PropertyAccessorElement && rhsElement.isSynthetic) { |
| 503 return new AccessSemantics.staticField( |
| 504 rhs, |
| 505 rhsElement.variable, |
| 506 lhsElement); |
| 507 } else if (rhsElement is MethodElement) { |
| 508 return new AccessSemantics.staticMethod(rhs, rhsElement, lhsElement); |
| 509 } else { |
| 510 return new AccessSemantics.staticProperty(rhs, rhsElement, lhsElement); |
| 511 } |
| 512 } else { |
| 513 return new AccessSemantics.dynamic(rhs, lhs); |
| 514 } |
| 515 } |
| 516 } |
| OLD | NEW |