Skip to content

Commit

Permalink
Fix "fix return type" quickfix for type param return type
Browse files Browse the repository at this point in the history
The old method would propose the erasure of the type param if the
compliance was set pre 1.5.
If the compliance

Instead, now two quickfixes are always propose:
1. Replace the return type with the erasure
2. Introduce a new type param with the same bounds,
   importing the bounds and adding the type variable for any raw types

Fixes #1558

Signed-off-by: David Thompson <[email protected]>
  • Loading branch information
datho7561 committed Jul 31, 2024
1 parent 2327a09 commit ca2de7b
Show file tree
Hide file tree
Showing 3 changed files with 480 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Stream;

import org.eclipse.core.runtime.CoreException;

Expand Down Expand Up @@ -381,12 +382,20 @@ public void collectIncompatibleReturnTypeProposals(IInvocationContext context, I
ICompilationUnit cu= context.getCompilationUnit();
IMethodBinding methodDecl= methodDeclBinding.getMethodDeclaration();
ITypeBinding overriddenReturnType= overridden.getReturnType();
if (! JavaModelUtil.is50OrHigher(context.getCompilationUnit().getJavaProject())) {
overriddenReturnType= overriddenReturnType.getErasure();
// propose erasure
if (decl.typeParameters().isEmpty() || overriddenReturnType.getTypeBounds().length == 0 || Stream.of(overriddenReturnType.getTypeBounds()).allMatch(bound -> bound.getTypeArguments().length == 0)) {
T p1= createChangeIncompatibleReturnTypeProposal(cu, methodDecl, astRoot, overriddenReturnType.getErasure(), false, IProposalRelevance.CHANGE_RETURN_TYPE);
if (p1 != null)
proposals.add(p1);
}

// propose using (and potentially introducing) the type variable
if (overriddenReturnType.isTypeVariable()) {
T p2 = createChangeIncompatibleReturnTypeProposal(cu, methodDecl, astRoot, overriddenReturnType, false, IProposalRelevance.CHANGE_RETURN_TYPE);
if (p2 != null) {
proposals.add(p2);
}
}
T p1= createChangeIncompatibleReturnTypeProposal(cu, methodDecl, astRoot, overriddenReturnType, false, IProposalRelevance.CHANGE_RETURN_TYPE);
if (p1 != null)
proposals.add(p1);

ICompilationUnit targetCu= cu;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
Expand Down Expand Up @@ -54,13 +55,17 @@
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.TagElement;
import org.eclipse.jdt.core.dom.TextElement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeParameter;
import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.core.dom.WildcardType;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
Expand Down Expand Up @@ -276,6 +281,40 @@ protected ASTRewrite getRewrite() throws CoreException {
rewrite.set(methodDecl, MethodDeclaration.RETURN_TYPE2_PROPERTY, type, null);
DimensionRewrite.removeAllChildren(methodDecl, MethodDeclaration.EXTRA_DIMENSIONS2_PROPERTY, rewrite, null);
TypeAnnotationRewrite.removePureTypeAnnotations(methodDecl, MethodDeclaration.MODIFIERS2_PROPERTY, rewrite, null);

// If the return type is a type variable that doesn't exist yet,
// we'll need to add all the type variables and update the parameter types to use the type variables
if (fNewType.isTypeVariable() && ((List<TypeParameter>)methodDecl.typeParameters()).stream().filter(p -> p.getName().toString().equals(fNewType.getName())).findAny().isEmpty()) {
IMethodBinding realMethodBinding = fNewType.getDeclaringMethod();
ITypeBinding[] typeParameters = realMethodBinding.getTypeParameters();
ListRewrite typeParameterRewrite = rewrite.getListRewrite(methodDecl, MethodDeclaration.TYPE_PARAMETERS_PROPERTY);

// clean out old method type parameters
((List<TypeParameter>)typeParameterRewrite.getOriginalList()).stream().forEach(typeVar -> typeParameterRewrite.remove(typeVar, null));

// add type parameters and import any bounds
// notably, if you add the type variables, you must add ALL of them.
// eg. if you have <T, U> T myMethod(Class<U> clazz)
// you cannot override with <T> T myMethod(Class<String> clazz)
for (ITypeBinding parameter : typeParameters) {
TypeParameter newTypeParameter = ast.newTypeParameter();
SimpleName newTypeParameterName = ast.newSimpleName(parameter.getName());
newTypeParameter.setName(newTypeParameterName);
Stream.of(parameter.getTypeBounds()) //
.forEach(bound -> {
newTypeParameter.typeBounds().add(getTypeNodeFromBinding(bound, ast, imports));
});
typeParameterRewrite.insertLast(newTypeParameter, null);
}

// Update the parameter types to match that of the resolved method.
// Some of the existing parameter types may be raw instead of containing the expected type parameters.
// Without inserting the type parameters in these cases, the signature will no longer match the overriden type.
for (int i = 0 ; i < methodDecl.parameters().size(); i++) {
SingleVariableDeclaration svd = (SingleVariableDeclaration)methodDecl.parameters().get(i);
rewrite.set(svd, SingleVariableDeclaration.TYPE_PROPERTY, getTypeNodeFromBinding(realMethodBinding.getParameterTypes()[i], ast, imports), null);
}
}
// add javadoc tag
Javadoc javadoc= methodDecl.getJavadoc();
if (javadoc != null && origReturnType != null && origReturnType.isPrimitiveType()
Expand Down Expand Up @@ -526,4 +565,43 @@ private void handledInferredParametrizedType(ASTNode node, ASTNode declaringNode
}
}

private static Type getTypeNodeFromBinding(ITypeBinding typeBinding, AST ast, ImportRewrite importRewrite) {

if (typeBinding.isWildcardType()) {
WildcardType wildcardType = ast.newWildcardType();
ITypeBinding bound = typeBinding.getBound();
if (bound != null) {
Type boundNode = getTypeNodeFromBinding(bound, ast, importRewrite);
wildcardType.setBound(boundNode);
wildcardType.setUpperBound(typeBinding.isUpperbound());
}
return wildcardType;
}

if (typeBinding.isArray()) {
Type elementTypeNode = getTypeNodeFromBinding(typeBinding.getElementType(), ast, importRewrite);
return ast.newArrayType(elementTypeNode, typeBinding.getDimensions());
}

if (typeBinding.isTypeVariable()) {
return ast.newSimpleType(ast.newSimpleName(typeBinding.getName()));
}

// import the simple/parameterized type if needed
importRewrite.addImport(typeBinding);

// create the simple/parameterized
SimpleName simpleName = ast.newSimpleName(typeBinding.getErasure().getName());
SimpleType simpleType = ast.newSimpleType(simpleName);
if (typeBinding.isParameterizedType()) {
ParameterizedType parameterizedType = ast.newParameterizedType(simpleType);
for (ITypeBinding argument : typeBinding.getTypeArguments()) {
Type typeArgument = getTypeNodeFromBinding(argument, ast, importRewrite);
parameterizedType.typeArguments().add(typeArgument);
}
return parameterizedType;
}
return simpleType;
}

}
Loading

0 comments on commit ca2de7b

Please sign in to comment.