Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(498)

Unified Diff: editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/resolver/InheritanceManager.java

Issue 178173003: Fix for 16498. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/resolver/InheritanceManager.java
diff --git a/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/resolver/InheritanceManager.java b/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/resolver/InheritanceManager.java
index 3112831d49d7cca6f792a4207630f514777a161f..e2802175b66aa2a05807a743733465ec4ea4b73c 100644
--- a/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/resolver/InheritanceManager.java
+++ b/editor/tools/plugins/com.google.dart.engine/src/com/google/dart/engine/internal/resolver/InheritanceManager.java
@@ -13,27 +13,40 @@
*/
package com.google.dart.engine.internal.resolver;
+import com.google.dart.engine.ast.SimpleIdentifier;
import com.google.dart.engine.element.ClassElement;
import com.google.dart.engine.element.ExecutableElement;
import com.google.dart.engine.element.LibraryElement;
import com.google.dart.engine.element.MethodElement;
+import com.google.dart.engine.element.ParameterElement;
import com.google.dart.engine.element.PropertyAccessorElement;
import com.google.dart.engine.error.AnalysisError;
import com.google.dart.engine.error.ErrorCode;
import com.google.dart.engine.error.StaticTypeWarningCode;
import com.google.dart.engine.error.StaticWarningCode;
+import com.google.dart.engine.internal.element.ExecutableElementImpl;
+import com.google.dart.engine.internal.element.MultiplyInheritedMethodElementImpl;
+import com.google.dart.engine.internal.element.MultiplyInheritedPropertyAccessorElementImpl;
+import com.google.dart.engine.internal.element.ParameterElementImpl;
import com.google.dart.engine.internal.element.member.MethodMember;
import com.google.dart.engine.internal.element.member.PropertyAccessorMember;
+import com.google.dart.engine.internal.type.DynamicTypeImpl;
+import com.google.dart.engine.internal.type.FunctionTypeImpl;
import com.google.dart.engine.internal.verifier.ErrorVerifier;
+import com.google.dart.engine.scanner.StringToken;
+import com.google.dart.engine.scanner.TokenType;
import com.google.dart.engine.type.FunctionType;
import com.google.dart.engine.type.InterfaceType;
import com.google.dart.engine.type.Type;
+import com.google.dart.engine.utilities.dart.ParameterKind;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
+import java.util.List;
import java.util.Map.Entry;
+import java.util.Set;
/**
* Instances of the class {@code InheritanceManager} manage the knowledge of where class members
@@ -44,6 +57,158 @@ import java.util.Map.Entry;
public class InheritanceManager {
/**
+ * Given some array of {@link ExecutableElement}s, this method creates a synthetic element as
+ * described in the Superinterfaces section of Inheritance and Overriding.
+ * <p>
+ * TODO (jwren) Copy contents from the Spec into this javadoc.
+ * <p>
+ * TODO (jwren) Associate a propagated type to the synthetic method element using least upper
+ * bound calls
+ */
+ private static ExecutableElement computeMergedExecutableElement(
+ ExecutableElement[] elementArrayToMerge) {
+ int h = getNumOfPositionalParameters(elementArrayToMerge[0]);
+ int r = getNumOfRequiredParameters(elementArrayToMerge[0]);
+ Set<String> namedParametersList = new HashSet<String>();
+ for (int i = 1; i < elementArrayToMerge.length; i++) {
+ ExecutableElement element = elementArrayToMerge[i];
+ int numOfPositionalParams = getNumOfPositionalParameters(element);
+ if (h < numOfPositionalParams) {
+ h = numOfPositionalParams;
+ }
+ int numOfRequiredParams = getNumOfRequiredParameters(element);
+ if (r > numOfRequiredParams) {
+ r = numOfRequiredParams;
+ }
+ namedParametersList.addAll(getNamedParameterNames(element));
+ }
+ if (r > h) {
+ return null;
+ }
+ return createSyntheticExecutableElement(
+ elementArrayToMerge,
+ elementArrayToMerge[0].getDisplayName(),
+ r,
+ h - r,
+ namedParametersList.toArray(new String[namedParametersList.size()]));
+ }
+
+ /**
+ * Used by {@link #computeMergedExecutableElement(ExecutableElement[])} to actually create the
+ * synthetic element.
+ *
+ * @param elementArrayToMerge the array used to create the synthetic element
+ * @param name the name of the method, getter or setter
+ * @param numOfRequiredParameters the number of required parameters
+ * @param numOfPositionalParameters the number of positional parameters
+ * @param namedParameters the list of {@link String}s that are the named parameters
+ * @return the created synthetic element
+ */
+ private static ExecutableElement createSyntheticExecutableElement(
+ ExecutableElement[] elementArrayToMerge, String name, int numOfRequiredParameters,
+ int numOfPositionalParameters, String... namedParameters) {
+ DynamicTypeImpl dynamicType = DynamicTypeImpl.getInstance();
+ SimpleIdentifier nameIdentifier = new SimpleIdentifier(new StringToken(
+ TokenType.IDENTIFIER,
+ name,
+ 0));
+ ExecutableElementImpl executable;
+ if (elementArrayToMerge[0] instanceof MethodElement) {
+ MultiplyInheritedMethodElementImpl unionedMethod = new MultiplyInheritedMethodElementImpl(
+ nameIdentifier);
+ unionedMethod.setExecutableElements(elementArrayToMerge);
+ executable = unionedMethod;
+ } else {
+ MultiplyInheritedPropertyAccessorElementImpl unionedPropertyAccessor = new MultiplyInheritedPropertyAccessorElementImpl(
+ nameIdentifier);
+ unionedPropertyAccessor.setGetter(((PropertyAccessorElement) elementArrayToMerge[0]).isGetter());
+ unionedPropertyAccessor.setSetter(((PropertyAccessorElement) elementArrayToMerge[0]).isSetter());
+ unionedPropertyAccessor.setExecutableElements(elementArrayToMerge);
+ executable = unionedPropertyAccessor;
+ }
+
+ int numOfParameters = numOfRequiredParameters + numOfPositionalParameters
+ + namedParameters.length;
+ ParameterElement[] parameters = new ParameterElement[numOfParameters];
+ int i = 0;
+ for (int j = 0; j < numOfRequiredParameters; j++, i++) {
+ ParameterElementImpl parameter = new ParameterElementImpl("", 0);
+ parameter.setType(dynamicType);
+ parameter.setParameterKind(ParameterKind.REQUIRED);
+ parameters[i] = parameter;
+ }
+ for (int k = 0; k < numOfPositionalParameters; k++, i++) {
+ ParameterElementImpl parameter = new ParameterElementImpl("", 0);
+ parameter.setType(dynamicType);
+ parameter.setParameterKind(ParameterKind.POSITIONAL);
+ parameters[i] = parameter;
+ }
+ for (int m = 0; m < namedParameters.length; m++, i++) {
+ ParameterElementImpl parameter = new ParameterElementImpl(namedParameters[m], 0);
+ parameter.setType(dynamicType);
+ parameter.setParameterKind(ParameterKind.NAMED);
+ parameters[i] = parameter;
+ }
+ executable.setReturnType(dynamicType);
+ executable.setParameters(parameters);
+
+ FunctionTypeImpl methodType = new FunctionTypeImpl(executable);
+ executable.setType(methodType);
+
+ return executable;
+ }
+
+ /**
+ * Given some {@link ExecutableElement}, return the list of named parameters.
+ */
+ private static List<String> getNamedParameterNames(ExecutableElement executableElement) {
+ ArrayList<String> namedParameterNames = new ArrayList<String>();
+ ParameterElement[] parameters = executableElement.getParameters();
+ for (int i = 0; i < parameters.length; i++) {
+ ParameterElement parameterElement = parameters[i];
+ if (parameterElement.getParameterKind() == ParameterKind.NAMED) {
+ namedParameterNames.add(parameterElement.getName());
+ }
+ }
+ return namedParameterNames;
+ }
+
+ /**
+ * Given some {@link ExecutableElement} return the number of parameters of the specified kind.
+ */
+ private static int getNumOfParameters(ExecutableElement executableElement,
+ ParameterKind parameterKind) {
+ int parameterCount = 0;
+ ParameterElement[] parameters = executableElement.getParameters();
+ for (int i = 0; i < parameters.length; i++) {
+ ParameterElement parameterElement = parameters[i];
+ if (parameterElement.getParameterKind() == parameterKind) {
+ parameterCount++;
+ }
+ }
+ return parameterCount;
+ }
+
+ /**
+ * Given some {@link ExecutableElement} return the number of positional parameters.
+ * <p>
+ * Note: by positional we mean {@link ParameterKind#REQUIRED} or {@link ParameterKind#POSITIONAL}.
+ */
+ private static int getNumOfPositionalParameters(ExecutableElement executableElement) {
+ // For this portion of the spec "positional" references both the required and the positional
+ // parameters.
+ return getNumOfParameters(executableElement, ParameterKind.REQUIRED)
+ + getNumOfParameters(executableElement, ParameterKind.POSITIONAL);
+ }
+
+ /**
+ * Given some {@link ExecutableElement} return the number of required parameters.
+ */
+ private static int getNumOfRequiredParameters(ExecutableElement executableElement) {
+ return getNumOfParameters(executableElement, ParameterKind.REQUIRED);
+ }
+
+ /**
* The {@link LibraryElement} that is managed by this manager.
*/
private LibraryElement library;
@@ -469,38 +634,81 @@ public class InheritanceManager {
}
//
- // Union all of the maps together, grouping the ExecutableElements into sets.
+ // Union all of the lookupMaps together into unionMap, grouping the ExecutableElements into a
+ // list where none of the elements are equal where equality is determined by having equal
+ // function types. (We also take note too of the type of the element: ()->int and () -> int may
Brian Wilkerson 2014/02/24 19:59:00 nit: "type" --> "kind"?
jwren 2014/02/24 20:34:45 Done.
+ // not be equal if one is a getter and the other is a method.)
//
- HashMap<String, HashSet<ExecutableElement>> unionMap = new HashMap<String, HashSet<ExecutableElement>>();
+ HashMap<String, ArrayList<ExecutableElement>> unionMap = new HashMap<String, ArrayList<ExecutableElement>>();
for (MemberMap lookupMap : lookupMaps) {
- for (int i = 0; i < lookupMap.getSize(); i++) {
+ int lookupMapSize = lookupMap.getSize();
+ for (int i = 0; i < lookupMapSize; i++) {
+ // Get the string key, if null, break.
String key = lookupMap.getKey(i);
if (key == null) {
break;
}
- HashSet<ExecutableElement> set = unionMap.get(key);
- if (set == null) {
- set = new HashSet<ExecutableElement>(4);
- unionMap.put(key, set);
+
+ // Get the list value out of the unionMap
+ ArrayList<ExecutableElement> list = unionMap.get(key);
+
+ // If we haven't created such a map for this key yet, do create it and put the list entry
+ // into the unionMap.
+ if (list == null) {
+ list = new ArrayList<ExecutableElement>(4);
+ unionMap.put(key, list);
+ }
+
+ // Fetch the entry out of this lookupMap
+ ExecutableElement newExecutableElementEntry = lookupMap.getValue(i);
+
+ if (list.isEmpty()) {
+ // If the list is empty, just the new value
+ list.add(newExecutableElementEntry);
+ } else {
+ // Otherwise, only add the newExecutableElementEntry if it isn't already in the list, this
+ // covers situation where a class inherits two methods (or two getters) that are
+ // identical.
+ boolean alreadyInList = false;
+ boolean isMethod1 = newExecutableElementEntry instanceof MethodElement;
+ for (ExecutableElement executableElementInList : list) {
+ boolean isMethod2 = executableElementInList instanceof MethodElement;
+ if (isMethod1 == isMethod2
+ && executableElementInList.getType().equals(newExecutableElementEntry.getType())) {
+ alreadyInList = true;
+ break;
+ }
+ }
+ if (!alreadyInList) {
+ list.add(newExecutableElementEntry);
+ }
}
- set.add(lookupMap.getValue(i));
}
}
//
- // Loop through the entries in the union map, adding them to the resultMap appropriately.
+ // Loop through the entries in the unionMap, adding them to the resultMap appropriately.
//
- for (Entry<String, HashSet<ExecutableElement>> entry : unionMap.entrySet()) {
+ for (Entry<String, ArrayList<ExecutableElement>> entry : unionMap.entrySet()) {
String key = entry.getKey();
- HashSet<ExecutableElement> set = entry.getValue();
- int numOfEltsWithMatchingNames = set.size();
+ ArrayList<ExecutableElement> list = entry.getValue();
+ int numOfEltsWithMatchingNames = list.size();
if (numOfEltsWithMatchingNames == 1) {
- resultMap.put(key, set.iterator().next());
+ //
+ // Example: class A inherits only 1 method named 'm'. Since it is the only such method, it
+ // is inherited.
+ // Another example: class A inherits 2 methods named 'm' from 2 different interfaces, but
+ // they both have the same signature, so it is the method inherited.
+ //
+ resultMap.put(key, list.get(0));
} else {
+ //
+ // Then numOfEltsWithMatchingNames > 1, check for the warning cases.
+ //
boolean allMethods = true;
boolean allSetters = true;
boolean allGetters = true;
- for (ExecutableElement executableElement : set) {
+ for (ExecutableElement executableElement : list) {
if (executableElement instanceof PropertyAccessorElement) {
allMethods = false;
if (((PropertyAccessorElement) executableElement).isSetter()) {
@@ -513,14 +721,21 @@ public class InheritanceManager {
allSetters = false;
}
}
+
+ //
+ // If there isn't a mixture of methods with getters, then continue, otherwise create a
+ // warning.
+ //
if (allMethods || allGetters || allSetters) {
+ //
// Compute the element whose type is the subtype of all of the other types.
- ExecutableElement[] elements = set.toArray(new ExecutableElement[numOfEltsWithMatchingNames]);
+ //
+ ExecutableElement[] elements = list.toArray(new ExecutableElement[numOfEltsWithMatchingNames]);
FunctionType[] executableElementTypes = new FunctionType[numOfEltsWithMatchingNames];
for (int i = 0; i < numOfEltsWithMatchingNames; i++) {
executableElementTypes[i] = elements[i].getType();
}
- boolean foundSubtypeOfAllTypes = false;
+ ArrayList<Integer> subtypesOfAllOtherTypesIndexes = new ArrayList<Integer>(1);
for (int i = 0; i < numOfEltsWithMatchingNames; i++) {
FunctionType subtype = executableElementTypes[i];
if (subtype == null) {
@@ -536,29 +751,64 @@ public class InheritanceManager {
}
}
if (subtypeOfAllTypes) {
- foundSubtypeOfAllTypes = true;
- resultMap.put(key, elements[i]);
- break;
+ subtypesOfAllOtherTypesIndexes.add(i);
}
}
- if (!foundSubtypeOfAllTypes) {
- reportError(
- classElt,
- classElt.getNameOffset(),
- classElt.getDisplayName().length(),
- StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE,
- key);
+
+ //
+ // The following is split into three cases determined by the number of elements in subtypesOfAllOtherTypes
+ //
+ if (subtypesOfAllOtherTypesIndexes.size() == 1) {
+ //
+ // Example: class A inherited only 2 method named 'm'. One has the function type
+ // '() -> dynamic' and one has the function type '([int]) -> dynamic'. Since the second
+ // method is a subtype of all the others, it is the inherited method.
+ // Tests: InheritanceManagerTest.test_getMapOfMembersInheritedFromInterfaces_union_oneSubtype_*
+ //
+ resultMap.put(key, elements[subtypesOfAllOtherTypesIndexes.get(0)]);
+ } else {
+ // TODO (jwren) Do we create warning only if subtypesOfAllOtherTypesIndexes is empty, or
Brian Wilkerson 2014/02/24 19:59:00 Have we answered this question, or do we still nee
jwren 2014/02/24 20:34:45 Removing.
+ // also in the case when there are more than one?
+ if (subtypesOfAllOtherTypesIndexes.isEmpty()) {
+ //
+ // Example: class A inherited only 2 method named 'm'. One has the function type
+ // '() -> int' and one has the function type '() -> String'. Since neither is a subtype
+ // of the other, we create a warning, and have this class inherit nothing.
+ //
+ String firstTwoFuntionTypesStr = executableElementTypes[0].toString() + ", "
+ + executableElementTypes[1].toString();
+ reportError(
+ classElt,
+ classElt.getNameOffset(),
+ classElt.getDisplayName().length(),
+ StaticTypeWarningCode.INCONSISTENT_METHOD_INHERITANCE,
+ key,
+ firstTwoFuntionTypesStr);
+ } else {
+ //
+ // Example: class A inherits 2 methods named 'm'. One has the function type
+ // '(int) -> dynamic' and one has the function type '(num) -> dynamic'. Since they are
+ // both a subtype of the other, a synthetic function '(dynamic) -> dynamic' is
+ // inherited.
+ // Tests: test_getMapOfMembersInheritedFromInterfaces_union_multipleSubtypes_*
+ //
+ ExecutableElement[] elementArrayToMerge = new ExecutableElement[subtypesOfAllOtherTypesIndexes.size()];
+ for (int i = 0; i < elementArrayToMerge.length; i++) {
+ elementArrayToMerge[i] = elements[subtypesOfAllOtherTypesIndexes.get(i)];
+ }
+ ExecutableElement mergedExecutableElement = computeMergedExecutableElement(elementArrayToMerge);
+ if (mergedExecutableElement != null) {
+ resultMap.put(key, mergedExecutableElement);
+ }
+ }
}
} else {
- if (!allMethods && !allGetters) {
- reportError(
- classElt,
- classElt.getNameOffset(),
- classElt.getDisplayName().length(),
- StaticWarningCode.INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD,
- key);
- }
- resultMap.remove(entry.getKey());
+ reportError(
+ classElt,
+ classElt.getNameOffset(),
+ classElt.getDisplayName().length(),
+ StaticWarningCode.INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD,
+ key);
}
}
}
@@ -658,5 +908,4 @@ public class InheritanceManager {
}
}
}
-
}

Powered by Google App Engine
This is Rietveld 408576698