Index: pkg/analyzer2dart/lib/src/identifier_semantics.dart |
diff --git a/pkg/analyzer2dart/lib/src/identifier_semantics.dart b/pkg/analyzer2dart/lib/src/identifier_semantics.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..95add6d52f1f87629bc83ca8cfa83e93e9d50ed0 |
--- /dev/null |
+++ b/pkg/analyzer2dart/lib/src/identifier_semantics.dart |
@@ -0,0 +1,325 @@ |
+// Copyright (c) 2014, 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. |
+ |
+/** |
+ * Code for classifying the semantics of identifiers appearing in a Dart file. |
+ */ |
+library analyzer2dart.identifierSemantics; |
+ |
+import 'package:analyzer/analyzer.dart'; |
+import 'package:analyzer/src/generated/element.dart'; |
+ |
+/** |
+ * Base class used to classify the semantics of a method or function |
+ * invocation. |
+ */ |
+abstract class MethodInvocationSemantics {} |
+ |
+/** |
+ * MethodInvocationSemantics object representing the invocation of a method |
+ * that is defined statically within a class, or of a function that is defined |
+ * at toplevel within a library. |
+ */ |
+class StaticMethodInvocation extends MethodInvocationSemantics { |
+ /** |
+ * The method or function being invoked. |
+ */ |
+ final ExecutableElement methodElement; |
+ |
+ /** |
+ * The class containing the member being invoked, or null if a toplevel |
+ * function is being invoked. |
+ */ |
+ final ClassElement classElement; |
+ |
+ /** |
+ * The identifier being used to invoke the method or function. |
+ */ |
+ final SimpleIdentifier methodName; |
+ |
+ // TODO(paulberry): would it also be useful to store the libraryElement? |
+ |
+ StaticMethodInvocation(this.methodElement, this.classElement, |
+ this.methodName); |
+} |
+ |
+/** |
+ * MethodInvocationSemantics object representing the invocation of a function |
+ * that is defined inside of another function or method. |
+ */ |
+class LocalFunctionInvocation extends MethodInvocationSemantics { |
+ /** |
+ * The function being invoked. |
+ */ |
+ final FunctionElement functionElement; |
+ |
+ LocalFunctionInvocation(this.functionElement); |
+} |
+ |
+/** |
+ * MethodInvocationSemantics object representing the invocation of an instance |
+ * method within a class. |
+ * |
+ * This also handles calls to an undefined method using a bare |
+ * identifier (since it cannot be determined until runtime whether the method |
+ * is truly undefined). |
+ */ |
+class DynamicMethodInvocation extends MethodInvocationSemantics { |
+ /** |
+ * The expression on which the method is being invoked (or null if the target |
+ * is an implicit "this"). |
+ */ |
+ final Expression target; |
+ |
+ /** |
+ * The identifier being used to invoke the method. |
+ */ |
+ final SimpleIdentifier methodName; |
+ |
+ DynamicMethodInvocation(this.target, this.methodName); |
+} |
+ |
+/** |
+ * Base class used to classify the semantics of a property access. |
+ */ |
+abstract class AccessSemantics { |
+ /** |
+ * The identifier being used to access the property. |
+ */ |
+ final SimpleIdentifier _identifier; |
+ |
+ AccessSemantics(this._identifier); |
+ |
+ /** |
+ * True if this is a read access to the property. Note that both |
+ * [isRead] and [isWrite] will be true in the case of a read-modify-write |
+ * operation (e.g. "+="). |
+ */ |
+ bool get isRead => _identifier.inGetterContext(); |
+ |
+ /** |
+ * True if this is a write access to the property. Note that both |
+ * [isRead] and [isWrite] will be true in the case of a read-modify-write |
+ * operation (e.g. "+="). |
+ */ |
+ bool get isWrite => _identifier.inSetterContext(); |
+} |
+ |
+/** |
+ * AccessSemantics object representing an access to a field that is defined |
+ * statically within a class, or to a variable that is defined at toplevel |
+ * within a library. |
+ */ |
+class StaticFieldAccess extends AccessSemantics { |
+ /** |
+ * The field or variable being referenced. |
+ */ |
+ final PropertyInducingElement field; |
+ |
+ /** |
+ * The class containing the field being referenced, or null if a toplevel |
+ * variable is being referenced. |
+ */ |
+ final ClassElement classElement; |
+ |
+ // TODO(paulberry): would it also be useful to store the libraryElement? |
+ |
+ StaticFieldAccess(this.field, this.classElement, SimpleIdentifier identifier) |
+ : super(identifier); |
+} |
+ |
+/** |
+ * AccessSemantics object representing an access to a property that is defined |
+ * statically within a class, or defined at toplevel within a library. |
+ */ |
+class StaticPropertyAccess extends AccessSemantics { |
+ /** |
+ * The property being referenced, or null if the property is undefined. |
+ */ |
+ final PropertyAccessorElement property; |
+ |
+ /** |
+ * The class containing the property being referenced, or null if a toplevel |
+ * property is being referenced. |
+ */ |
+ final ClassElement classElement; |
+ |
+ /** |
+ * The identifier being used to access the property. |
+ */ |
+ SimpleIdentifier get propertyName => _identifier; |
+ |
+ // TODO(paulberry): would it also be useful to store the libraryElement? |
+ |
+ StaticPropertyAccess(this.property, this.classElement, |
+ SimpleIdentifier propertyName) |
+ : super(propertyName); |
+} |
+ |
+/** |
+ * AccessSemantics object representing an access to an instance property or |
+ * field within a class. |
+ * |
+ * This also handles access to an undefined property using a bare |
+ * identifier (since it cannot be determined until runtime whether the property |
+ * is truly undefined). |
+ */ |
+class DynamicPropertyAccess extends AccessSemantics { |
+ /** |
+ * The expression on which the method is being invoked (or null if the target |
+ * is an implicit "this"). |
+ */ |
+ final Expression target; |
+ |
+ /** |
+ * The identifier being used to access the property. |
+ */ |
+ final SimpleIdentifier propertyName; |
+ |
+ DynamicPropertyAccess(this.target, SimpleIdentifier propertyName) |
+ : super(propertyName), |
+ propertyName = propertyName; |
+} |
+ |
+/** |
+ * AccessSemantics object representing an access to a local variable within a |
+ * function or method. |
+ */ |
+class LocalVariableAccess extends AccessSemantics { |
+ final LocalVariableElement variable; |
+ LocalVariableAccess(this.variable, SimpleIdentifier identifier) |
+ : super(identifier); |
+} |
+ |
+/** |
+ * Return the method invocation semantics for [node]. |
+ */ |
+MethodInvocationSemantics classifyMethodInvocation(MethodInvocation node) { |
+ Expression target = node.realTarget; |
+ if (target == null) { |
+ Element staticElement = node.methodName.staticElement; |
+ if (staticElement is FunctionElement) { |
+ if (staticElement.enclosingElement is CompilationUnitElement) { |
+ return new StaticMethodInvocation(staticElement, null, node.methodName); |
+ } else { |
+ return new LocalFunctionInvocation(staticElement); |
+ } |
+ } else if (staticElement is MethodElement && staticElement.isStatic) { |
+ return new StaticMethodInvocation( |
+ staticElement, |
+ staticElement.enclosingElement, |
+ node.methodName); |
+ } |
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.
|
+ } else if (target is SimpleIdentifier) { |
+ Element targetStaticElement = target.staticElement; |
+ if (targetStaticElement is PrefixElement) { |
+ if (node.methodName.staticElement != null) { |
+ return new StaticMethodInvocation( |
+ node.methodName.staticElement, |
+ null, |
+ node.methodName); |
+ } else { |
+ return new DynamicMethodInvocation(null, node.methodName); |
+ } |
+ } else if (targetStaticElement is ClassElement) { |
+ return new StaticMethodInvocation( |
+ node.methodName.staticElement, |
+ targetStaticElement, |
+ node.methodName); |
+ } |
+ } |
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
|
+ return new DynamicMethodInvocation(target, node.methodName); |
+} |
+ |
+/** |
+ * Return the access semantics for [node]. |
+ */ |
+AccessSemantics classifyPrefixedIdentifier(PrefixedIdentifier node) { |
+ Element prefixElement = node.prefix.staticElement; |
+ Element suffixElement = node.identifier.staticElement; |
+ if (prefixElement is PrefixElement) { |
+ Element staticElement = node.identifier.staticElement; |
Brian Wilkerson
2014/09/22 19:44:48
staticElement == suffixElement
Paul Berry
2014/09/23 16:32:49
Done.
|
+ if (staticElement is PropertyAccessorElement) { |
+ if (staticElement.isSynthetic) { |
+ return new StaticFieldAccess( |
+ staticElement.variable, |
+ null, |
+ node.identifier); |
+ } else { |
+ return new StaticPropertyAccess(staticElement, null, node.identifier); |
+ } |
+ } |
+ return new DynamicPropertyAccess(null, node.identifier); |
+ } else if (prefixElement is ClassElement) { |
+ if (suffixElement is PropertyAccessorElement && suffixElement.isSynthetic) { |
+ return new StaticFieldAccess( |
+ suffixElement.variable, |
+ prefixElement, |
+ node.identifier); |
+ } |
+ return new StaticPropertyAccess( |
+ suffixElement, |
+ prefixElement, |
+ node.identifier); |
+ } else { |
+ 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.
|
+ } |
+} |
+ |
+/** |
+ * Return the access semantics for [node]. |
+ */ |
+DynamicPropertyAccess classifyPropertyAccess(PropertyAccess node) { |
+ return new DynamicPropertyAccess(node.realTarget, node.propertyName); |
+} |
+ |
+/** |
+ * Return the access semantics for [node]. Caller must ensure that [node] is |
+ * not the right hand side of a [PropertyAccess] or [PrefixedIdentifier], or |
+ * the method name of a [MethodInvocation]. |
+ */ |
+AccessSemantics classifyBareIdentifier(SimpleIdentifier node) { |
+ AstNode parent = node.parent; |
+ 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!
|
+ parent is ImportDirective || |
+ (parent is ClassDeclaration && parent.name == node) || |
+ (parent is MethodDeclaration && parent.name == node) || |
+ (parent is VariableDeclaration && parent.name == node) || |
+ (parent is FormalParameter && parent.identifier == node)) { |
+ // This identifier is a declaration, not a use. |
+ // TODO(paulberry): Can some of the above cases be coalesced? |
+ // TODO(paulberry): Additional cases |
+ return null; |
+ } |
+ if (parent is TypeName) { |
+ // 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.
|
+ return null; |
+ } |
+ // TODO(paulberry): handle PrefixElement. |
+ Element staticElement = node.staticElement; |
+ if (staticElement is PropertyAccessorElement) { |
+ if (staticElement.isSynthetic) { |
+ if (staticElement.enclosingElement is CompilationUnitElement) { |
+ return new StaticFieldAccess(staticElement.variable, null, node); |
+ } else if (staticElement.isStatic) { |
+ return new StaticFieldAccess( |
+ staticElement.variable, |
+ staticElement.enclosingElement, |
+ node); |
+ } |
+ } else { |
+ if (staticElement.enclosingElement is CompilationUnitElement) { |
+ return new StaticPropertyAccess(staticElement, null, node); |
+ } else if (staticElement.isStatic) { |
+ return new StaticPropertyAccess( |
+ staticElement, |
+ staticElement.enclosingElement, |
+ node); |
+ } |
+ } |
+ } else if (staticElement is LocalVariableElement) { |
+ return new LocalVariableAccess(staticElement, node); |
+ } |
Brian Wilkerson
2014/09/22 19:44:48
Missing ParameterElement?
Paul Berry
2014/09/23 16:32:49
Done.
|
+ return new DynamicPropertyAccess(null, node); |
+} |