| 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 library engine.resolver.element_resolver; | 5 library engine.resolver.element_resolver; |
| 6 | 6 |
| 7 import 'dart:collection'; | 7 import 'dart:collection'; |
| 8 | 8 |
| 9 import 'ast.dart'; | 9 import 'ast.dart'; |
| 10 import 'element.dart'; | 10 import 'element.dart'; |
| (...skipping 621 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 632 } | 632 } |
| 633 } | 633 } |
| 634 // | 634 // |
| 635 // Then check for error conditions. | 635 // Then check for error conditions. |
| 636 // | 636 // |
| 637 ErrorCode errorCode = _checkForInvocationError(target, true, staticElement); | 637 ErrorCode errorCode = _checkForInvocationError(target, true, staticElement); |
| 638 bool generatedWithTypePropagation = false; | 638 bool generatedWithTypePropagation = false; |
| 639 if (_enableHints && errorCode == null && staticElement == null) { | 639 if (_enableHints && errorCode == null && staticElement == null) { |
| 640 // The method lookup may have failed because there were multiple | 640 // The method lookup may have failed because there were multiple |
| 641 // incompatible choices. In this case we don't want to generate a hint. | 641 // incompatible choices. In this case we don't want to generate a hint. |
| 642 if (propagatedElement == null && propagatedType is UnionType) { | |
| 643 // TODO(collinsn): an improvement here is to make the propagated type | |
| 644 // of the method call the union of the propagated types of all possible | |
| 645 // calls. | |
| 646 if (_lookupMethods(target, propagatedType, methodName.name).length > | |
| 647 1) { | |
| 648 return null; | |
| 649 } | |
| 650 } | |
| 651 errorCode = _checkForInvocationError(target, false, propagatedElement); | 642 errorCode = _checkForInvocationError(target, false, propagatedElement); |
| 652 if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_METHOD)) { | 643 if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_METHOD)) { |
| 653 ClassElement classElementContext = null; | 644 ClassElement classElementContext = null; |
| 654 if (target == null) { | 645 if (target == null) { |
| 655 classElementContext = _resolver.enclosingClass; | 646 classElementContext = _resolver.enclosingClass; |
| 656 } else { | 647 } else { |
| 657 DartType type = target.bestType; | 648 DartType type = target.bestType; |
| 658 if (type != null) { | 649 if (type != null) { |
| 659 if (type.element is ClassElement) { | 650 if (type.element is ClassElement) { |
| 660 classElementContext = type.element as ClassElement; | 651 classElementContext = type.element as ClassElement; |
| (...skipping 1055 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1716 method = interfaceType.lookUpMethodInSuperclass( | 1707 method = interfaceType.lookUpMethodInSuperclass( |
| 1717 methodName, _definingLibrary); | 1708 methodName, _definingLibrary); |
| 1718 } else { | 1709 } else { |
| 1719 method = interfaceType.lookUpMethod(methodName, _definingLibrary); | 1710 method = interfaceType.lookUpMethod(methodName, _definingLibrary); |
| 1720 } | 1711 } |
| 1721 if (method != null) { | 1712 if (method != null) { |
| 1722 return method; | 1713 return method; |
| 1723 } | 1714 } |
| 1724 return _lookUpMethodInInterfaces( | 1715 return _lookUpMethodInInterfaces( |
| 1725 interfaceType, false, methodName, new HashSet<ClassElement>()); | 1716 interfaceType, false, methodName, new HashSet<ClassElement>()); |
| 1726 } else if (type is UnionType) { | |
| 1727 // TODO (collinsn): I want [computeMergedExecutableElement] to be general | |
| 1728 // and work with functions, methods, constructors, and property accessors. | |
| 1729 // However, I won't be able to assume it returns [MethodElement] here | |
| 1730 // then. | |
| 1731 return _maybeMergeExecutableElements( | |
| 1732 _lookupMethods(target, type, methodName)) as MethodElement; | |
| 1733 } | 1717 } |
| 1734 return null; | 1718 return null; |
| 1735 } | 1719 } |
| 1736 | 1720 |
| 1737 /** | 1721 /** |
| 1738 * Look up the method with the given [methodName] in the interfaces | 1722 * Look up the method with the given [methodName] in the interfaces |
| 1739 * implemented by the given [targetType], either directly or indirectly. | 1723 * implemented by the given [targetType], either directly or indirectly. |
| 1740 * Return the element representing the method that was found, or `null` if | 1724 * Return the element representing the method that was found, or `null` if |
| 1741 * there is no method with the given name. The flag [includeTargetType] should | 1725 * there is no method with the given name. The flag [includeTargetType] should |
| 1742 * be `true` if the search should include the target type. The | 1726 * be `true` if the search should include the target type. The |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1778 } | 1762 } |
| 1779 InterfaceType superclass = targetType.superclass; | 1763 InterfaceType superclass = targetType.superclass; |
| 1780 if (superclass == null) { | 1764 if (superclass == null) { |
| 1781 return null; | 1765 return null; |
| 1782 } | 1766 } |
| 1783 return _lookUpMethodInInterfaces( | 1767 return _lookUpMethodInInterfaces( |
| 1784 superclass, true, methodName, visitedInterfaces); | 1768 superclass, true, methodName, visitedInterfaces); |
| 1785 } | 1769 } |
| 1786 | 1770 |
| 1787 /** | 1771 /** |
| 1788 * Look up all methods with the given [methodName] that are defined on the | |
| 1789 * given union [type]. | |
| 1790 */ | |
| 1791 Set<ExecutableElement> _lookupMethods( | |
| 1792 Expression target, UnionType type, String methodName) { | |
| 1793 Set<ExecutableElement> methods = new HashSet<ExecutableElement>(); | |
| 1794 bool allElementsHaveMethod = true; | |
| 1795 for (DartType t in type.elements) { | |
| 1796 MethodElement m = _lookUpMethod(target, t, methodName); | |
| 1797 if (m != null) { | |
| 1798 methods.add(m); | |
| 1799 } else { | |
| 1800 allElementsHaveMethod = false; | |
| 1801 } | |
| 1802 } | |
| 1803 // For strict union types we require that all types in the union define the | |
| 1804 // method. | |
| 1805 if (AnalysisEngine.instance.strictUnionTypes) { | |
| 1806 if (allElementsHaveMethod) { | |
| 1807 return methods; | |
| 1808 } else { | |
| 1809 return new Set<ExecutableElement>(); | |
| 1810 } | |
| 1811 } else { | |
| 1812 return methods; | |
| 1813 } | |
| 1814 } | |
| 1815 | |
| 1816 /** | |
| 1817 * Look up the setter with the given [setterName] in the given [type]. Return | 1772 * Look up the setter with the given [setterName] in the given [type]. Return |
| 1818 * the element representing the setter that was found, or `null` if there is | 1773 * the element representing the setter that was found, or `null` if there is |
| 1819 * no setter with the given name. The [target] is the target of the | 1774 * no setter with the given name. The [target] is the target of the |
| 1820 * invocation, or `null` if there is no target. | 1775 * invocation, or `null` if there is no target. |
| 1821 */ | 1776 */ |
| 1822 PropertyAccessorElement _lookUpSetter( | 1777 PropertyAccessorElement _lookUpSetter( |
| 1823 Expression target, DartType type, String setterName) { | 1778 Expression target, DartType type, String setterName) { |
| 1824 type = _resolveTypeParameter(type); | 1779 type = _resolveTypeParameter(type); |
| 1825 if (type is InterfaceType) { | 1780 if (type is InterfaceType) { |
| 1826 InterfaceType interfaceType = type; | 1781 InterfaceType interfaceType = type; |
| (...skipping 555 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2382 * Given an invocation of the form 'e.m(a1, ..., an)', resolve 'e.m' to the | 2337 * Given an invocation of the form 'e.m(a1, ..., an)', resolve 'e.m' to the |
| 2383 * element being invoked. If the returned element is a method, then the method | 2338 * element being invoked. If the returned element is a method, then the method |
| 2384 * will be invoked. If the returned element is a getter, the getter will be | 2339 * will be invoked. If the returned element is a getter, the getter will be |
| 2385 * invoked without arguments and the result of that invocation will then be | 2340 * invoked without arguments and the result of that invocation will then be |
| 2386 * invoked with the arguments. The [target] is the target of the invocation | 2341 * invoked with the arguments. The [target] is the target of the invocation |
| 2387 * ('e'). The [targetType] is the type of the target. The [methodName] is th | 2342 * ('e'). The [targetType] is the type of the target. The [methodName] is th |
| 2388 * name of the method being invoked ('m'). | 2343 * name of the method being invoked ('m'). |
| 2389 */ | 2344 */ |
| 2390 Element _resolveInvokedElementWithTarget( | 2345 Element _resolveInvokedElementWithTarget( |
| 2391 Expression target, DartType targetType, SimpleIdentifier methodName) { | 2346 Expression target, DartType targetType, SimpleIdentifier methodName) { |
| 2392 if (targetType is InterfaceType || targetType is UnionType) { | 2347 if (targetType is InterfaceType) { |
| 2393 Element element = _lookUpMethod(target, targetType, methodName.name); | 2348 Element element = _lookUpMethod(target, targetType, methodName.name); |
| 2394 if (element == null) { | 2349 if (element == null) { |
| 2395 // | 2350 // |
| 2396 // If there's no method, then it's possible that 'm' is a getter that | 2351 // If there's no method, then it's possible that 'm' is a getter that |
| 2397 // returns a function. | 2352 // returns a function. |
| 2398 // | 2353 // |
| 2399 // TODO (collinsn): need to add union type support here too, in the | 2354 // TODO (collinsn): need to add union type support here too, in the |
| 2400 // style of [lookUpMethod]. | 2355 // style of [lookUpMethod]. |
| 2401 element = _lookUpGetter(target, targetType, methodName.name); | 2356 element = _lookUpGetter(target, targetType, methodName.name); |
| 2402 } | 2357 } |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2479 propertyName.staticElement = staticElement; | 2434 propertyName.staticElement = staticElement; |
| 2480 propertyName.propagatedElement = propagatedElement; | 2435 propertyName.propagatedElement = propagatedElement; |
| 2481 bool shouldReportMissingMember_static = | 2436 bool shouldReportMissingMember_static = |
| 2482 _shouldReportMissingMember(staticType, staticElement); | 2437 _shouldReportMissingMember(staticType, staticElement); |
| 2483 bool shouldReportMissingMember_propagated = | 2438 bool shouldReportMissingMember_propagated = |
| 2484 !shouldReportMissingMember_static && | 2439 !shouldReportMissingMember_static && |
| 2485 _enableHints && | 2440 _enableHints && |
| 2486 _shouldReportMissingMember(propagatedType, propagatedElement) && | 2441 _shouldReportMissingMember(propagatedType, propagatedElement) && |
| 2487 !_memberFoundInSubclass( | 2442 !_memberFoundInSubclass( |
| 2488 propagatedType.element, propertyName.name, false, true); | 2443 propagatedType.element, propertyName.name, false, true); |
| 2489 // TODO(collinsn): add support for errors on union types by extending | |
| 2490 // [lookupGetter] and [lookupSetter] in analogy with the earlier | |
| 2491 // [lookupMethod] extensions. | |
| 2492 if (propagatedType is UnionType) { | |
| 2493 shouldReportMissingMember_propagated = false; | |
| 2494 } | |
| 2495 if (shouldReportMissingMember_static || | 2444 if (shouldReportMissingMember_static || |
| 2496 shouldReportMissingMember_propagated) { | 2445 shouldReportMissingMember_propagated) { |
| 2497 DartType staticOrPropagatedType = | 2446 DartType staticOrPropagatedType = |
| 2498 shouldReportMissingMember_static ? staticType : propagatedType; | 2447 shouldReportMissingMember_static ? staticType : propagatedType; |
| 2499 Element staticOrPropagatedEnclosingElt = staticOrPropagatedType.element; | 2448 Element staticOrPropagatedEnclosingElt = staticOrPropagatedType.element; |
| 2500 bool isStaticProperty = _isStatic(staticOrPropagatedEnclosingElt); | 2449 bool isStaticProperty = _isStatic(staticOrPropagatedEnclosingElt); |
| 2501 DartType displayType = staticOrPropagatedType != null | 2450 DartType displayType = staticOrPropagatedType != null |
| 2502 ? staticOrPropagatedType | 2451 ? staticOrPropagatedType |
| 2503 : propagatedType != null ? propagatedType : staticType; | 2452 : propagatedType != null ? propagatedType : staticType; |
| 2504 // Special getter cases. | 2453 // Special getter cases. |
| (...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2713 if (!isConditional && expression is Identifier) { | 2662 if (!isConditional && expression is Identifier) { |
| 2714 Element staticElement = expression.staticElement; | 2663 Element staticElement = expression.staticElement; |
| 2715 if (staticElement is ClassElementImpl) { | 2664 if (staticElement is ClassElementImpl) { |
| 2716 return staticElement; | 2665 return staticElement; |
| 2717 } | 2666 } |
| 2718 } | 2667 } |
| 2719 return null; | 2668 return null; |
| 2720 } | 2669 } |
| 2721 | 2670 |
| 2722 /** | 2671 /** |
| 2723 * Helper function for `maybeMergeExecutableElements` that does the actual | |
| 2724 * merging. The [elementArrayToMerge] is the non-empty list of elements to | |
| 2725 * merge. | |
| 2726 */ | |
| 2727 static ExecutableElement _computeMergedExecutableElement( | |
| 2728 List<ExecutableElement> elementArrayToMerge) { | |
| 2729 // Flatten methods structurally. Based on | |
| 2730 // [InheritanceManager.computeMergedExecutableElement] and | |
| 2731 // [InheritanceManager.createSyntheticExecutableElement]. | |
| 2732 // | |
| 2733 // However, the approach we take here is much simpler, but expected to work | |
| 2734 // well in the common case. It degrades gracefully in the uncommon case, | |
| 2735 // by computing the type [dynamic] for the method, preventing any | |
| 2736 // hints from being generated (TODO: not done yet). | |
| 2737 // | |
| 2738 // The approach is: we require that each [ExecutableElement] has the | |
| 2739 // same shape: the same number of required, optional positional, and | |
| 2740 // optional named parameters, in the same positions, and with the named | |
| 2741 // parameters in the same order. We compute a type by unioning pointwise. | |
| 2742 ExecutableElement e_0 = elementArrayToMerge[0]; | |
| 2743 List<ParameterElement> ps_0 = e_0.parameters; | |
| 2744 List<ParameterElementImpl> ps_out = | |
| 2745 new List<ParameterElementImpl>(ps_0.length); | |
| 2746 for (int j = 0; j < ps_out.length; j++) { | |
| 2747 ps_out[j] = new ParameterElementImpl(ps_0[j].name, 0); | |
| 2748 ps_out[j].synthetic = true; | |
| 2749 ps_out[j].type = ps_0[j].type; | |
| 2750 ps_out[j].parameterKind = ps_0[j].parameterKind; | |
| 2751 } | |
| 2752 DartType r_out = e_0.returnType; | |
| 2753 for (int i = 1; i < elementArrayToMerge.length; i++) { | |
| 2754 ExecutableElement e_i = elementArrayToMerge[i]; | |
| 2755 r_out = UnionTypeImpl.union([r_out, e_i.returnType]); | |
| 2756 List<ParameterElement> ps_i = e_i.parameters; | |
| 2757 // Each function must have the same number of params. | |
| 2758 if (ps_0.length != ps_i.length) { | |
| 2759 return null; | |
| 2760 // TODO (collinsn): return an element representing [dynamic] here | |
| 2761 // instead. | |
| 2762 } else { | |
| 2763 // Each function must have the same kind of params, with the same names, | |
| 2764 // in the same order. | |
| 2765 for (int j = 0; j < ps_i.length; j++) { | |
| 2766 if (ps_0[j].parameterKind != ps_i[j].parameterKind || | |
| 2767 !identical(ps_0[j].name, ps_i[j].name)) { | |
| 2768 return null; | |
| 2769 } else { | |
| 2770 // The output parameter type is the union of the input parameter | |
| 2771 // types. | |
| 2772 ps_out[j].type = | |
| 2773 UnionTypeImpl.union([ps_out[j].type, ps_i[j].type]); | |
| 2774 } | |
| 2775 } | |
| 2776 } | |
| 2777 } | |
| 2778 // TODO (collinsn): this code should work for functions and methods, | |
| 2779 // so we may want [FunctionElementImpl] | |
| 2780 // instead here in some cases? | |
| 2781 // And then there are constructors and property accessors. | |
| 2782 // Maybe the answer is to create a new subclass of [ExecutableElementImpl] | |
| 2783 // which is used for merged executable elements, in analogy with | |
| 2784 // [MultiplyInheritedMethodElementImpl] and | |
| 2785 // [MultiplyInheritedPropertyAcessorElementImpl]. | |
| 2786 ExecutableElementImpl e_out = new MethodElementImpl(e_0.name, 0); | |
| 2787 e_out.synthetic = true; | |
| 2788 e_out.returnType = r_out; | |
| 2789 e_out.parameters = ps_out; | |
| 2790 e_out.type = new FunctionTypeImpl.con1(e_out); | |
| 2791 // Get NPE in [toString()] w/o this. | |
| 2792 e_out.enclosingElement = e_0.enclosingElement; | |
| 2793 return e_out; | |
| 2794 } | |
| 2795 | |
| 2796 /** | |
| 2797 * Return `true` if the given [identifier] is the return type of a constructor | 2672 * Return `true` if the given [identifier] is the return type of a constructor |
| 2798 * declaration. | 2673 * declaration. |
| 2799 */ | 2674 */ |
| 2800 static bool _isConstructorReturnType(SimpleIdentifier identifier) { | 2675 static bool _isConstructorReturnType(SimpleIdentifier identifier) { |
| 2801 AstNode parent = identifier.parent; | 2676 AstNode parent = identifier.parent; |
| 2802 if (parent is ConstructorDeclaration) { | 2677 if (parent is ConstructorDeclaration) { |
| 2803 return identical(parent.returnType, identifier); | 2678 return identical(parent.returnType, identifier); |
| 2804 } | 2679 } |
| 2805 return false; | 2680 return false; |
| 2806 } | 2681 } |
| (...skipping 25 matching lines...) Expand all Loading... |
| 2832 } | 2707 } |
| 2833 if (node is ConstructorFieldInitializer) { | 2708 if (node is ConstructorFieldInitializer) { |
| 2834 return false; | 2709 return false; |
| 2835 } | 2710 } |
| 2836 if (node is MethodDeclaration) { | 2711 if (node is MethodDeclaration) { |
| 2837 return !node.isStatic; | 2712 return !node.isStatic; |
| 2838 } | 2713 } |
| 2839 } | 2714 } |
| 2840 return false; | 2715 return false; |
| 2841 } | 2716 } |
| 2842 | |
| 2843 /** | |
| 2844 * Return a method representing the merge of the given [elements]. The type of | |
| 2845 * the merged element is the component-wise union of the types of the given | |
| 2846 * elements. If not all input elements have the same shape then `null` is | |
| 2847 * returned. | |
| 2848 */ | |
| 2849 static ExecutableElement _maybeMergeExecutableElements( | |
| 2850 Set<ExecutableElement> elements) { | |
| 2851 List<ExecutableElement> elementArrayToMerge = new List.from(elements); | |
| 2852 if (elementArrayToMerge.length == 0) { | |
| 2853 return null; | |
| 2854 } else if (elementArrayToMerge.length == 1) { | |
| 2855 // If all methods are equal, don't bother building a new one. | |
| 2856 return elementArrayToMerge[0]; | |
| 2857 } else { | |
| 2858 return _computeMergedExecutableElement(elementArrayToMerge); | |
| 2859 } | |
| 2860 } | |
| 2861 } | 2717 } |
| 2862 | 2718 |
| 2863 /** | 2719 /** |
| 2864 * An identifier that can be used to look up names in the lexical scope when | 2720 * An identifier that can be used to look up names in the lexical scope when |
| 2865 * there is no identifier in the AST structure. There is no identifier in the | 2721 * there is no identifier in the AST structure. There is no identifier in the |
| 2866 * AST when the parser could not distinguish between a method invocation and an | 2722 * AST when the parser could not distinguish between a method invocation and an |
| 2867 * invocation of a top-level function imported with a prefix. | 2723 * invocation of a top-level function imported with a prefix. |
| 2868 */ | 2724 */ |
| 2869 class SyntheticIdentifier extends Identifier { | 2725 class SyntheticIdentifier extends Identifier { |
| 2870 /** | 2726 /** |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2914 | 2770 |
| 2915 @override | 2771 @override |
| 2916 Element get staticElement => null; | 2772 Element get staticElement => null; |
| 2917 | 2773 |
| 2918 @override | 2774 @override |
| 2919 accept(AstVisitor visitor) => null; | 2775 accept(AstVisitor visitor) => null; |
| 2920 | 2776 |
| 2921 @override | 2777 @override |
| 2922 void visitChildren(AstVisitor visitor) {} | 2778 void visitChildren(AstVisitor visitor) {} |
| 2923 } | 2779 } |
| OLD | NEW |