Chromium Code Reviews| 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); |
| +} |