From 4044aae489025df7baae914b0d0c98e5cc23fd5c Mon Sep 17 00:00:00 2001 From: Jack Zhou Date: Fri, 19 Apr 2013 21:29:11 -0400 Subject: [PATCH] Create from usage: Offering more choices for type substitutions. --- .../quickfix/CreateMethodFromUsageFix.java | 110 ++++++++++++------ 1 file changed, 74 insertions(+), 36 deletions(-) diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/CreateMethodFromUsageFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/CreateMethodFromUsageFix.java index e227391deed..1d353cb2f01 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/CreateMethodFromUsageFix.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/CreateMethodFromUsageFix.java @@ -87,35 +87,36 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { */ private static class TypeOrExpressionThereof { private final JetExpression expressionOfType; - private JetType type; - private final boolean enumerateSupertypes; + private final JetType type; + private final Variance variance; private JetType[] cachedTypeCandidates; private String[] cachedNameCandidatesFromExpression; public TypeOrExpressionThereof(@NotNull JetExpression expressionOfType) { - this(expressionOfType, true); + this(expressionOfType, Variance.IN_VARIANCE); } - public TypeOrExpressionThereof(@NotNull JetExpression expressionOfType, boolean enumerateSupertypes) { - this(expressionOfType, null, enumerateSupertypes); + public TypeOrExpressionThereof(@NotNull JetExpression expressionOfType, Variance variance) { + this(expressionOfType, null, variance); } - public TypeOrExpressionThereof(@NotNull JetType type, boolean enumerateSupertypes) { - this(null, type, enumerateSupertypes); + public TypeOrExpressionThereof(@NotNull JetType type, Variance variance) { + 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.type = type; - this.enumerateSupertypes = enumerateSupertypes; + this.variance = variance; } public boolean isType() { return this.type != null; } - @Nullable + @NotNull public JetType getType() { + assert this.type != null; return this.type; } @@ -131,13 +132,14 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { } List types = new ArrayList(); if (isType()) { + assert type != null : "!isType() means type == null && expressionOfType != null"; types.add(type); types.addAll(TypeUtils.getAllSupertypes(type)); } else { assert expressionOfType != null : "!isType() means type == null && expressionOfType != null"; for (JetType type : guessTypeForExpression(expressionOfType, context)) { types.add(type); - if (enumerateSupertypes) { + if (variance == Variance.IN_VARIANCE) { types.addAll(TypeUtils.getAllSupertypes(type)); } } @@ -169,12 +171,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { } public void substitute(TypeSubstitution[] substitutions) { - if (type != null) { - for (TypeSubstitution substitution : substitutions) { - type = substituteType(type, substitution); - } - } - cachedTypeCandidates = substituteTypes(cachedTypeCandidates, substitutions); + cachedTypeCandidates = substituteTypes(cachedTypeCandidates, substitutions, variance); } } @@ -477,7 +474,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { currentFile = (JetFile) file; currentFileEditor = editor; - JetType[] possibleOwnerTypes = ownerType.getPossibleTypes(currentFileContext); + JetType[] possibleOwnerTypes = ownerType.getPossibleTypes(); assert possibleOwnerTypes.length > 0; if (possibleOwnerTypes.length == 1) { JetType ownerType = possibleOwnerTypes[0]; @@ -872,31 +869,72 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { } @NotNull - private static JetType substituteType(@NotNull JetType type, @NotNull TypeSubstitution substitution) { - if (type.equals(substitution.getForType())) { - return substitution.getByType(); + private static JetType substituteType(@NotNull JetType type, @NotNull TypeSubstitution substitution, @NotNull Variance variance) { + switch (variance) { + 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 newArguments = new ArrayList(); + List typeParameters = type.getConstructor().getParameters(); + int i = 0; 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)); + i++; } return new JetTypeImpl(type.getAnnotations(), type.getConstructor(), type.isNullable(), newArguments, type.getMemberScope()); } @NotNull - private static JetType[] substituteTypes(@NotNull JetType[] types, @NotNull TypeSubstitution[] substitutions) { - JetType[] newTypes = new JetType[types.length]; - for (int i = 0; i < types.length; i++) { - JetType newType = types[i]; - for (TypeSubstitution substitution : substitutions) { - newType = substituteType(newType, substitution); + private static JetType[] substituteTypes(@NotNull JetType[] types, @NotNull TypeSubstitution[] substitutions, @NotNull Variance variance) { + Set newTypes = new LinkedHashSet(Arrays.asList(types)); + for (TypeSubstitution substitution : substitutions) { // each substitution can be applied or not, so we offer all options + List toAdd = new ArrayList(); + List toRemove = new ArrayList(); + 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 @@ -1062,7 +1100,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { 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); } }; @@ -1094,7 +1132,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { TypeOrExpressionThereof valType = new TypeOrExpressionThereof(rhs); 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); } }; @@ -1111,7 +1149,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { JetExpression iterableExpr = forExpr.getLoopRange(); if (iterableExpr == null) return null; 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()); } }; @@ -1130,7 +1168,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { JetExpression variableExpr = forExpr.getLoopParameter(); if (variableExpr == null) return null; 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()); } }; @@ -1163,7 +1201,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { List returnJetTypeArguments = Collections.singletonList(returnJetTypeParameterType); returnJetType = new JetTypeImpl(returnJetType.getAnnotations(), returnJetType.getConstructor(), returnJetType.isNullable(), 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()); } }; @@ -1190,7 +1228,7 @@ public class CreateMethodFromUsageFix extends CreateFromUsageFixBase { int componentNumber = Integer.decode(componentNumberString) - 1; JetMultiDeclarationEntry entry = entries.get(componentNumber); - TypeOrExpressionThereof returnType = new TypeOrExpressionThereof(entry, false); + TypeOrExpressionThereof returnType = new TypeOrExpressionThereof(entry, Variance.OUT_VARIANCE); JetExpression rhs = multiDeclaration.getInitializer(); if (rhs == null) return null; TypeOrExpressionThereof ownerType = new TypeOrExpressionThereof(rhs);