Create from usage: Offering more choices for type substitutions.
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user