Create from usage: Offering more choices for type substitutions.

This commit is contained in:
Jack Zhou
2013-04-19 21:29:11 -04:00
parent 6de37f4c85
commit 4044aae489
@@ -87,35 +87,36 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase {
*/ */
private static class TypeOrExpressionThereof { private static class TypeOrExpressionThereof {
private final JetExpression expressionOfType; private final JetExpression expressionOfType;
private JetType type; private final JetType type;
private final boolean enumerateSupertypes; private final Variance variance;
private JetType[] cachedTypeCandidates; private JetType[] cachedTypeCandidates;
private String[] cachedNameCandidatesFromExpression; private String[] cachedNameCandidatesFromExpression;
public TypeOrExpressionThereof(@NotNull JetExpression expressionOfType) { public TypeOrExpressionThereof(@NotNull JetExpression expressionOfType) {
this(expressionOfType, true); this(expressionOfType, Variance.IN_VARIANCE);
} }
public TypeOrExpressionThereof(@NotNull JetExpression expressionOfType, boolean enumerateSupertypes) { public TypeOrExpressionThereof(@NotNull JetExpression expressionOfType, Variance variance) {
this(expressionOfType, null, enumerateSupertypes); this(expressionOfType, null, variance);
} }
public TypeOrExpressionThereof(@NotNull JetType type, boolean enumerateSupertypes) { public TypeOrExpressionThereof(@NotNull JetType type, Variance variance) {
this(null, type, enumerateSupertypes); this(null, type, variance);
} }
private TypeOrExpressionThereof(@Nullable JetExpression expressionOfType, @Nullable JetType type, boolean enumerateSupertypes) { private TypeOrExpressionThereof(@Nullable JetExpression expressionOfType, @Nullable JetType type, Variance variance) {
this.expressionOfType = expressionOfType; this.expressionOfType = expressionOfType;
this.type = type; this.type = type;
this.enumerateSupertypes = enumerateSupertypes; this.variance = variance;
} }
public boolean isType() { public boolean isType() {
return this.type != null; return this.type != null;
} }
@Nullable @NotNull
public JetType getType() { public JetType getType() {
assert this.type != null;
return this.type; return this.type;
} }
@@ -131,13 +132,14 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase {
} }
List<JetType> types = new ArrayList<JetType>(); List<JetType> types = new ArrayList<JetType>();
if (isType()) { if (isType()) {
assert type != null : "!isType() means type == null && expressionOfType != null";
types.add(type); types.add(type);
types.addAll(TypeUtils.getAllSupertypes(type)); types.addAll(TypeUtils.getAllSupertypes(type));
} else { } else {
assert expressionOfType != null : "!isType() means type == null && expressionOfType != null"; assert expressionOfType != null : "!isType() means type == null && expressionOfType != null";
for (JetType type : guessTypeForExpression(expressionOfType, context)) { for (JetType type : guessTypeForExpression(expressionOfType, context)) {
types.add(type); types.add(type);
if (enumerateSupertypes) { if (variance == Variance.IN_VARIANCE) {
types.addAll(TypeUtils.getAllSupertypes(type)); types.addAll(TypeUtils.getAllSupertypes(type));
} }
} }
@@ -169,12 +171,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase {
} }
public void substitute(TypeSubstitution[] substitutions) { public void substitute(TypeSubstitution[] substitutions) {
if (type != null) { cachedTypeCandidates = substituteTypes(cachedTypeCandidates, substitutions, variance);
for (TypeSubstitution substitution : substitutions) {
type = substituteType(type, substitution);
}
}
cachedTypeCandidates = substituteTypes(cachedTypeCandidates, substitutions);
} }
} }
@@ -477,7 +474,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase {
currentFile = (JetFile) file; currentFile = (JetFile) file;
currentFileEditor = editor; currentFileEditor = editor;
JetType[] possibleOwnerTypes = ownerType.getPossibleTypes(currentFileContext); JetType[] possibleOwnerTypes = ownerType.getPossibleTypes();
assert possibleOwnerTypes.length > 0; assert possibleOwnerTypes.length > 0;
if (possibleOwnerTypes.length == 1) { if (possibleOwnerTypes.length == 1) {
JetType ownerType = possibleOwnerTypes[0]; JetType ownerType = possibleOwnerTypes[0];
@@ -872,31 +869,72 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase {
} }
@NotNull @NotNull
private static JetType substituteType(@NotNull JetType type, @NotNull TypeSubstitution substitution) { private static JetType substituteType(@NotNull JetType type, @NotNull TypeSubstitution substitution, @NotNull Variance variance) {
if (type.equals(substitution.getForType())) { switch (variance) {
return substitution.getByType(); case INVARIANT:
// for invariant, can replace only when they're equal
if (type.equals(substitution.getForType())) {
return substitution.getByType();
}
break;
case IN_VARIANCE:
// for covariant (e.g. function parameter), can replace type with any of its supertypes
if (JetTypeChecker.INSTANCE.isSubtypeOf(type, substitution.getForType())) {
return substitution.getByType();
}
break;
case OUT_VARIANCE:
// for contravariant (e.g. function return value), can replace type with any of its subtypes
if (JetTypeChecker.INSTANCE.isSubtypeOf(substitution.getForType(), type)) {
return substitution.getByType();
}
break;
} }
List<TypeProjection> newArguments = new ArrayList<TypeProjection>(); List<TypeProjection> newArguments = new ArrayList<TypeProjection>();
List<TypeParameterDescriptor> typeParameters = type.getConstructor().getParameters();
int i = 0;
for (TypeProjection projection : type.getArguments()) { for (TypeProjection projection : type.getArguments()) {
JetType newArgument = substituteType(projection.getType(), substitution); TypeParameterDescriptor typeParameter = typeParameters.get(i);
JetType newArgument = substituteType(projection.getType(), substitution, typeParameter.getVariance());
newArguments.add(new TypeProjection(Variance.INVARIANT, newArgument)); newArguments.add(new TypeProjection(Variance.INVARIANT, newArgument));
i++;
} }
return new JetTypeImpl(type.getAnnotations(), type.getConstructor(), return new JetTypeImpl(type.getAnnotations(), type.getConstructor(),
type.isNullable(), newArguments, type.getMemberScope()); type.isNullable(), newArguments, type.getMemberScope());
} }
@NotNull @NotNull
private static JetType[] substituteTypes(@NotNull JetType[] types, @NotNull TypeSubstitution[] substitutions) { private static JetType[] substituteTypes(@NotNull JetType[] types, @NotNull TypeSubstitution[] substitutions, @NotNull Variance variance) {
JetType[] newTypes = new JetType[types.length]; Set<JetType> newTypes = new LinkedHashSet<JetType>(Arrays.asList(types));
for (int i = 0; i < types.length; i++) { for (TypeSubstitution substitution : substitutions) { // each substitution can be applied or not, so we offer all options
JetType newType = types[i]; List<JetType> toAdd = new ArrayList<JetType>();
for (TypeSubstitution substitution : substitutions) { List<JetType> toRemove = new ArrayList<JetType>();
newType = substituteType(newType, substitution); for (JetType type : newTypes) {
toAdd.add(substituteType(type, substitution, variance));
// substitution.byType are type arguments, but they cannot already occur before substitution
if (containsType(type, substitution.getByType())) {
toRemove.add(type);
}
} }
newTypes[i] = newType; newTypes.addAll(toAdd);
newTypes.removeAll(toRemove);
} }
return newTypes; return newTypes.toArray(new JetType[newTypes.size()]);
}
private static boolean containsType(JetType outer, JetType inner) {
if (outer.equals(inner)) {
return true;
}
for (TypeProjection projection : outer.getArguments()) {
if (containsType(projection.getType(), inner)) {
return true;
}
}
return false;
} }
@NotNull @NotNull
@@ -1062,7 +1100,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase {
parameters.add(new Parameter(null, indexType)); parameters.add(new Parameter(null, indexType));
} }
TypeOrExpressionThereof returnType = new TypeOrExpressionThereof(accessExpr, false); TypeOrExpressionThereof returnType = new TypeOrExpressionThereof(accessExpr, Variance.OUT_VARIANCE);
return new CreateMethodFromUsageFix(accessExpr, arrayType, "get", returnType, parameters); return new CreateMethodFromUsageFix(accessExpr, arrayType, "get", returnType, parameters);
} }
}; };
@@ -1094,7 +1132,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase {
TypeOrExpressionThereof valType = new TypeOrExpressionThereof(rhs); TypeOrExpressionThereof valType = new TypeOrExpressionThereof(rhs);
parameters.add(new Parameter("value", valType)); parameters.add(new Parameter("value", valType));
TypeOrExpressionThereof returnType = new TypeOrExpressionThereof(KotlinBuiltIns.getInstance().getUnitType(), false); TypeOrExpressionThereof returnType = new TypeOrExpressionThereof(KotlinBuiltIns.getInstance().getUnitType(), Variance.OUT_VARIANCE);
return new CreateMethodFromUsageFix(accessExpr, arrayType, "set", returnType, parameters); return new CreateMethodFromUsageFix(accessExpr, arrayType, "set", returnType, parameters);
} }
}; };
@@ -1111,7 +1149,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase {
JetExpression iterableExpr = forExpr.getLoopRange(); JetExpression iterableExpr = forExpr.getLoopRange();
if (iterableExpr == null) return null; if (iterableExpr == null) return null;
TypeOrExpressionThereof iterableType = new TypeOrExpressionThereof(iterableExpr); TypeOrExpressionThereof iterableType = new TypeOrExpressionThereof(iterableExpr);
TypeOrExpressionThereof returnType = new TypeOrExpressionThereof(KotlinBuiltIns.getInstance().getBooleanType(), false); TypeOrExpressionThereof returnType = new TypeOrExpressionThereof(KotlinBuiltIns.getInstance().getBooleanType(), Variance.OUT_VARIANCE);
return new CreateMethodFromUsageFix(forExpr, iterableType, "hasNext", returnType, new ArrayList<Parameter>()); return new CreateMethodFromUsageFix(forExpr, iterableType, "hasNext", returnType, new ArrayList<Parameter>());
} }
}; };
@@ -1130,7 +1168,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase {
JetExpression variableExpr = forExpr.getLoopParameter(); JetExpression variableExpr = forExpr.getLoopParameter();
if (variableExpr == null) return null; if (variableExpr == null) return null;
TypeOrExpressionThereof iterableType = new TypeOrExpressionThereof(iterableExpr); TypeOrExpressionThereof iterableType = new TypeOrExpressionThereof(iterableExpr);
TypeOrExpressionThereof returnType = new TypeOrExpressionThereof(variableExpr, false); TypeOrExpressionThereof returnType = new TypeOrExpressionThereof(variableExpr, Variance.OUT_VARIANCE);
return new CreateMethodFromUsageFix(forExpr, iterableType, "next", returnType, new ArrayList<Parameter>()); return new CreateMethodFromUsageFix(forExpr, iterableType, "next", returnType, new ArrayList<Parameter>());
} }
}; };
@@ -1163,7 +1201,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase {
List<TypeProjection> returnJetTypeArguments = Collections.singletonList(returnJetTypeParameterType); List<TypeProjection> returnJetTypeArguments = Collections.singletonList(returnJetTypeParameterType);
returnJetType = new JetTypeImpl(returnJetType.getAnnotations(), returnJetType.getConstructor(), returnJetType.isNullable(), returnJetType = new JetTypeImpl(returnJetType.getAnnotations(), returnJetType.getConstructor(), returnJetType.isNullable(),
returnJetTypeArguments, returnJetType.getMemberScope()); returnJetTypeArguments, returnJetType.getMemberScope());
TypeOrExpressionThereof returnType = new TypeOrExpressionThereof(returnJetType, false); TypeOrExpressionThereof returnType = new TypeOrExpressionThereof(returnJetType, Variance.OUT_VARIANCE);
return new CreateMethodFromUsageFix(forExpr, iterableType, "iterator", returnType, new ArrayList<Parameter>()); return new CreateMethodFromUsageFix(forExpr, iterableType, "iterator", returnType, new ArrayList<Parameter>());
} }
}; };
@@ -1190,7 +1228,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase {
int componentNumber = Integer.decode(componentNumberString) - 1; int componentNumber = Integer.decode(componentNumberString) - 1;
JetMultiDeclarationEntry entry = entries.get(componentNumber); JetMultiDeclarationEntry entry = entries.get(componentNumber);
TypeOrExpressionThereof returnType = new TypeOrExpressionThereof(entry, false); TypeOrExpressionThereof returnType = new TypeOrExpressionThereof(entry, Variance.OUT_VARIANCE);
JetExpression rhs = multiDeclaration.getInitializer(); JetExpression rhs = multiDeclaration.getInitializer();
if (rhs == null) return null; if (rhs == null) return null;
TypeOrExpressionThereof ownerType = new TypeOrExpressionThereof(rhs); TypeOrExpressionThereof ownerType = new TypeOrExpressionThereof(rhs);