blob: 031301b9c169b205a1b5e45047b71c6576cc9526 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Benjamin Muskalla <bmuskalla@eclipsesource.com> - [quick fix] proposes wrong cast from Object to primitive int - https://bugs.eclipse.org/bugs/show_bug.cgi?id=100593
* Benjamin Muskalla <bmuskalla@eclipsesource.com> - [quick fix] "Add exceptions to..." quickfix does nothing - https://bugs.eclipse.org/bugs/show_bug.cgi?id=107924
*******************************************************************************/
package org.eclipse.jdt.internal.ui.text.correction;
import java.util.ArrayList;
import java.util.Collection;
import org.eclipse.swt.graphics.Image;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ConditionalExpression;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NameQualifiedType;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
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;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation;
import org.eclipse.jdt.internal.core.manipulation.StubUtility;
import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving;
import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels;
import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.ui.JavaElementLabels;
import org.eclipse.jdt.ui.text.java.IInvocationContext;
import org.eclipse.jdt.ui.text.java.IProblemLocation;
import org.eclipse.jdt.ui.text.java.correction.ASTRewriteCorrectionProposal;
import org.eclipse.jdt.ui.text.java.correction.ICommandAccess;
import org.eclipse.jdt.internal.ui.JavaPluginImages;
import org.eclipse.jdt.internal.ui.text.correction.proposals.CastCorrectionProposal;
import org.eclipse.jdt.internal.ui.text.correction.proposals.ChangeMethodSignatureProposal;
import org.eclipse.jdt.internal.ui.text.correction.proposals.ChangeMethodSignatureProposal.ChangeDescription;
import org.eclipse.jdt.internal.ui.text.correction.proposals.ChangeMethodSignatureProposal.InsertDescription;
import org.eclipse.jdt.internal.ui.text.correction.proposals.ChangeMethodSignatureProposal.RemoveDescription;
import org.eclipse.jdt.internal.ui.text.correction.proposals.ImplementInterfaceProposal;
import org.eclipse.jdt.internal.ui.text.correction.proposals.LinkedCorrectionProposal;
import org.eclipse.jdt.internal.ui.text.correction.proposals.NewVariableCorrectionProposal;
import org.eclipse.jdt.internal.ui.text.correction.proposals.OptionalCorrectionProposal;
import org.eclipse.jdt.internal.ui.text.correction.proposals.TypeChangeCorrectionProposal;
import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider;
public class TypeMismatchSubProcessor {
private TypeMismatchSubProcessor() {
}
public static void addTypeMismatchProposals(IInvocationContext context, IProblemLocation problem, Collection<ICommandAccess> proposals) throws CoreException {
String[] args= problem.getProblemArguments();
if (args.length != 2) {
return;
}
ICompilationUnit cu= context.getCompilationUnit();
CompilationUnit astRoot= context.getASTRoot();
AST ast= astRoot.getAST();
ASTNode selectedNode= problem.getCoveredNode(astRoot);
if (!(selectedNode instanceof Expression)) {
return;
}
Expression nodeToCast= (Expression) selectedNode;
Name receiverNode= null;
ITypeBinding castTypeBinding= null;
int parentNodeType= selectedNode.getParent().getNodeType();
switch (parentNodeType) {
case ASTNode.ASSIGNMENT:
Assignment assign= (Assignment) selectedNode.getParent();
Expression leftHandSide= assign.getLeftHandSide();
if (selectedNode.equals(leftHandSide)) {
nodeToCast= assign.getRightHandSide();
}
castTypeBinding= assign.getLeftHandSide().resolveTypeBinding();
if (leftHandSide instanceof Name) {
receiverNode= (Name) leftHandSide;
} else if (leftHandSide instanceof FieldAccess) {
receiverNode= ((FieldAccess) leftHandSide).getName();
}
break;
case ASTNode.VARIABLE_DECLARATION_FRAGMENT:
VariableDeclarationFragment frag= (VariableDeclarationFragment) selectedNode.getParent();
if (selectedNode.equals(frag.getName()) || selectedNode.equals(frag.getInitializer())) {
nodeToCast= frag.getInitializer();
castTypeBinding= ASTNodes.getType(frag).resolveBinding();
receiverNode= frag.getName();
}
break;
case ASTNode.MEMBER_VALUE_PAIR:
receiverNode= ((MemberValuePair) selectedNode.getParent()).getName();
castTypeBinding= ASTResolving.guessBindingForReference(nodeToCast);
break;
case ASTNode.SINGLE_MEMBER_ANNOTATION:
receiverNode= ((SingleMemberAnnotation) selectedNode.getParent()).getTypeName(); // use the type name
castTypeBinding= ASTResolving.guessBindingForReference(nodeToCast);
break;
default:
// try to find the binding corresponding to 'castTypeName'
castTypeBinding= ASTResolving.guessBindingForReference(nodeToCast);
break;
}
if (castTypeBinding == null) {
return;
}
ITypeBinding currBinding= nodeToCast.resolveTypeBinding();
if (currBinding == null && nodeToCast instanceof MethodInvocation) {
IMethodBinding methodBinding= ((MethodInvocation) nodeToCast).resolveMethodBinding();
if (methodBinding != null) {
currBinding= methodBinding.getReturnType();
}
}
if (!(nodeToCast instanceof ArrayInitializer)) {
String castTypeName= castTypeBinding.getErasure().getQualifiedName();
if (castTypeName.equals("java.util.Optional") && ast.apiLevel() >= AST.JLS8) { //$NON-NLS-1$
ITypeBinding nodeToCastTypeBinding= nodeToCast.resolveTypeBinding();
String label0= Messages.format(CorrectionMessages.TypeMismatchSubProcessor_changetooptionalempty_description, nodeToCast.toString());
proposals.add(new OptionalCorrectionProposal(label0, cu, nodeToCast, IProposalRelevance.CREATE_EMPTY_OPTIONAL, OptionalCorrectionProposal.OPTIONAL_EMPTY));
ITypeBinding[] typeArguments= castTypeBinding.getTypeArguments();
boolean wrapAll= false;
for (ITypeBinding typeArgument : typeArguments) {
if (typeArgument.isCastCompatible(nodeToCastTypeBinding)) {
wrapAll= true;
break;
}
}
if (wrapAll) {
String label1= Messages.format(CorrectionMessages.TypeMismatchSubProcessor_changetooptionalof_description, nodeToCast.toString());
proposals.add(new OptionalCorrectionProposal(label1, cu, nodeToCast, IProposalRelevance.CREATE_OPTIONAL, OptionalCorrectionProposal.OPTIONAL_OF));
if (!nodeToCastTypeBinding.isPrimitive()) {
String label2= Messages.format(CorrectionMessages.TypeMismatchSubProcessor_changetooptionalofnullable_description, nodeToCast.toString());
proposals.add(new OptionalCorrectionProposal(label2, cu, nodeToCast, IProposalRelevance.CREATE_OPTIONAL_OF_NULLABLE, OptionalCorrectionProposal.OPTIONAL_OF_NULLABLE));
}
}
}
ITypeBinding castFixType= null;
if (currBinding == null || castTypeBinding.isCastCompatible(currBinding) || nodeToCast instanceof CastExpression) {
castFixType= castTypeBinding;
} else if (JavaModelUtil.is50OrHigher(cu.getJavaProject())) {
ITypeBinding boxUnboxedTypeBinding= boxUnboxPrimitives(castTypeBinding, currBinding, ast);
if (boxUnboxedTypeBinding != castTypeBinding && boxUnboxedTypeBinding.isCastCompatible(currBinding)) {
castFixType= boxUnboxedTypeBinding;
}
}
if (castFixType != null) {
proposals.add(createCastProposal(context, castFixType, nodeToCast, IProposalRelevance.CREATE_CAST));
}
}
boolean nullOrVoid= currBinding == null || "void".equals(currBinding.getName()); //$NON-NLS-1$
// change method return statement to actual type
if (!nullOrVoid && isTypeReturned(nodeToCast)) {
BodyDeclaration decl= ASTResolving.findParentBodyDeclaration(selectedNode);
if (decl instanceof MethodDeclaration) {
MethodDeclaration methodDeclaration= (MethodDeclaration) decl;
currBinding= Bindings.normalizeTypeBinding(currBinding);
if (currBinding == null) {
currBinding= ast.resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$
}
if (currBinding.isWildcardType()) {
currBinding= ASTResolving.normalizeWildcardType(currBinding, true, ast);
}
ASTRewrite rewrite= ASTRewrite.create(ast);
String label= Messages.format(CorrectionMessages.TypeMismatchSubProcessor_changereturntype_description, BasicElementLabels.getJavaElementName(currBinding.getName()));
Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
LinkedCorrectionProposal proposal= new LinkedCorrectionProposal(label, cu, rewrite, IProposalRelevance.CHANGE_METHOD_RETURN_TYPE, image);
ImportRewrite imports= proposal.createImportRewrite(astRoot);
ImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(decl, imports);
Type newReturnType= imports.addImport(currBinding, ast, importRewriteContext, TypeLocation.RETURN_TYPE);
rewrite.replace(methodDeclaration.getReturnType2(), newReturnType, null);
String returnKey= "return"; //$NON-NLS-1$
proposal.addLinkedPosition(rewrite.track(newReturnType), true, returnKey);
for (ITypeBinding typeSuggestion : ASTResolving.getRelaxingTypes(ast, currBinding)) {
proposal.addLinkedPositionProposal(returnKey, typeSuggestion);
}
proposals.add(proposal);
}
}
if (!nullOrVoid && receiverNode != null) {
currBinding= Bindings.normalizeTypeBinding(currBinding);
if (currBinding == null) {
currBinding= ast.resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$
}
if (currBinding.isWildcardType()) {
currBinding= ASTResolving.normalizeWildcardType(currBinding, true, ast);
}
addChangeSenderTypeProposals(context, receiverNode, currBinding, true, IProposalRelevance.CHANGE_TYPE_OF_RECEIVER_NODE, proposals);
}
addChangeSenderTypeProposals(context, nodeToCast, castTypeBinding, false, IProposalRelevance.CHANGE_TYPE_OF_NODE_TO_CAST, proposals);
if (castTypeBinding == ast.resolveWellKnownType("boolean") && currBinding != null && !currBinding.isPrimitive() && !Bindings.isVoidType(currBinding)) { //$NON-NLS-1$
String label= CorrectionMessages.TypeMismatchSubProcessor_insertnullcheck_description;
Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
ASTRewrite rewrite= ASTRewrite.create(astRoot.getAST());
InfixExpression expression= ast.newInfixExpression();
expression.setLeftOperand((Expression) rewrite.createMoveTarget(nodeToCast));
expression.setRightOperand(ast.newNullLiteral());
expression.setOperator(InfixExpression.Operator.NOT_EQUALS);
rewrite.replace(nodeToCast, expression, null);
proposals.add(new ASTRewriteCorrectionProposal(label, context.getCompilationUnit(), rewrite, IProposalRelevance.INSERT_NULL_CHECK, image));
}
}
private static boolean isTypeReturned(final Expression nodeToCast) {
int parentNodeType= nodeToCast.getParent().getNodeType();
if (parentNodeType == ASTNode.RETURN_STATEMENT) {
return true;
}
if (parentNodeType == ASTNode.PARENTHESIZED_EXPRESSION
|| parentNodeType == ASTNode.CONDITIONAL_EXPRESSION && (nodeToCast.getLocationInParent() == ConditionalExpression.THEN_EXPRESSION_PROPERTY || nodeToCast.getLocationInParent() == ConditionalExpression.ELSE_EXPRESSION_PROPERTY)) {
return isTypeReturned((Expression) nodeToCast.getParent());
}
return false;
}
public static ITypeBinding boxUnboxPrimitives(ITypeBinding castType, ITypeBinding toCast, AST ast) {
/*
* e.g:
* void m(toCast var) {
* castType i= var;
* }
*/
if (castType.isPrimitive() && !toCast.isPrimitive()) {
return Bindings.getBoxedTypeBinding(castType, ast);
} else if (!castType.isPrimitive() && toCast.isPrimitive()) {
return Bindings.getUnboxedTypeBinding(castType, ast);
} else {
return castType;
}
}
public static void addChangeSenderTypeProposals(IInvocationContext context, Expression nodeToCast, ITypeBinding castTypeBinding, boolean isAssignedNode, int relevance, Collection<ICommandAccess> proposals) throws JavaModelException {
IBinding callerBinding= Bindings.resolveExpressionBinding(nodeToCast, false);
ICompilationUnit cu= context.getCompilationUnit();
CompilationUnit astRoot= context.getASTRoot();
ICompilationUnit targetCu= null;
ITypeBinding declaringType= null;
IBinding callerBindingDecl= callerBinding;
if (callerBinding instanceof IVariableBinding) {
IVariableBinding variableBinding= (IVariableBinding) callerBinding;
if (variableBinding.isEnumConstant()) {
return;
}
if (!variableBinding.isField()) {
targetCu= cu;
} else {
callerBindingDecl= variableBinding.getVariableDeclaration();
ITypeBinding declaringClass= variableBinding.getDeclaringClass();
if (declaringClass == null) {
return; // array length
}
declaringType= declaringClass.getTypeDeclaration();
}
} else if (callerBinding instanceof IMethodBinding) {
IMethodBinding methodBinding= (IMethodBinding) callerBinding;
if (!methodBinding.isConstructor()) {
declaringType= methodBinding.getDeclaringClass().getTypeDeclaration();
callerBindingDecl= methodBinding.getMethodDeclaration();
}
} else if (callerBinding instanceof ITypeBinding && nodeToCast.getLocationInParent() == SingleMemberAnnotation.TYPE_NAME_PROPERTY) {
declaringType= (ITypeBinding) callerBinding;
callerBindingDecl= Bindings.findMethodInType(declaringType, "value", (String[]) null); //$NON-NLS-1$
if (callerBindingDecl == null) {
return;
}
}
if (declaringType != null && declaringType.isFromSource()) {
targetCu= ASTResolving.findCompilationUnitForBinding(cu, astRoot, declaringType);
}
if (targetCu != null && ASTResolving.isUseableTypeInContext(castTypeBinding, callerBindingDecl, false)) {
proposals.add(new TypeChangeCorrectionProposal(targetCu, callerBindingDecl, astRoot, castTypeBinding, isAssignedNode, relevance));
}
// add interface to resulting type
if (!isAssignedNode) {
ITypeBinding nodeType= nodeToCast.resolveTypeBinding();
if (castTypeBinding.isInterface() && nodeType != null && nodeType.isClass() && !nodeType.isAnonymous() && nodeType.isFromSource()) {
ITypeBinding typeDecl= nodeType.getTypeDeclaration();
ICompilationUnit nodeCu= ASTResolving.findCompilationUnitForBinding(cu, astRoot, typeDecl);
if (nodeCu != null && ASTResolving.isUseableTypeInContext(castTypeBinding, typeDecl, true)) {
proposals.add(new ImplementInterfaceProposal(nodeCu, typeDecl, astRoot, castTypeBinding, relevance - 1));
}
}
}
}
public static ASTRewriteCorrectionProposal createCastProposal(IInvocationContext context, ITypeBinding castTypeBinding, Expression nodeToCast, int relevance) {
ICompilationUnit cu= context.getCompilationUnit();
String label;
String castType= BindingLabelProvider.getBindingLabel(castTypeBinding, JavaElementLabels.ALL_DEFAULT);
if (nodeToCast.getNodeType() == ASTNode.CAST_EXPRESSION) {
label= Messages.format(CorrectionMessages.TypeMismatchSubProcessor_changecast_description, castType);
} else {
label= Messages.format(CorrectionMessages.TypeMismatchSubProcessor_addcast_description, castType);
}
return new CastCorrectionProposal(label, cu, nodeToCast, castTypeBinding, relevance);
}
public static void addIncompatibleReturnTypeProposals(IInvocationContext context, IProblemLocation problem, Collection<ICommandAccess> proposals) throws JavaModelException {
CompilationUnit astRoot= context.getASTRoot();
ASTNode selectedNode= problem.getCoveringNode(astRoot);
if (selectedNode == null) {
return;
}
MethodDeclaration decl= ASTResolving.findParentMethodDeclaration(selectedNode);
if (decl == null) {
return;
}
IMethodBinding methodDeclBinding= decl.resolveBinding();
if (methodDeclBinding == null) {
return;
}
ITypeBinding returnType= methodDeclBinding.getReturnType();
IMethodBinding overridden= Bindings.findOverriddenMethod(methodDeclBinding, false);
if (overridden == null || overridden.getReturnType() == returnType) {
return;
}
ICompilationUnit cu= context.getCompilationUnit();
IMethodBinding methodDecl= methodDeclBinding.getMethodDeclaration();
ITypeBinding overriddenReturnType= overridden.getReturnType();
if (! JavaModelUtil.is50OrHigher(context.getCompilationUnit().getJavaProject())) {
overriddenReturnType= overriddenReturnType.getErasure();
}
proposals.add(new TypeChangeCorrectionProposal(cu, methodDecl, astRoot, overriddenReturnType, false, IProposalRelevance.CHANGE_RETURN_TYPE));
ICompilationUnit targetCu= cu;
IMethodBinding overriddenDecl= overridden.getMethodDeclaration();
ITypeBinding overridenDeclType= overriddenDecl.getDeclaringClass();
if (overridenDeclType.isFromSource()) {
targetCu= ASTResolving.findCompilationUnitForBinding(cu, astRoot, overridenDeclType);
if (targetCu != null && ASTResolving.isUseableTypeInContext(returnType, overriddenDecl, false)) {
TypeChangeCorrectionProposal proposal= new TypeChangeCorrectionProposal(targetCu, overriddenDecl, astRoot, returnType, false, IProposalRelevance.CHANGE_RETURN_TYPE_OF_OVERRIDDEN);
if (overridenDeclType.isInterface()) {
proposal.setDisplayName(Messages.format(CorrectionMessages.TypeMismatchSubProcessor_changereturnofimplemented_description, BasicElementLabels.getJavaElementName(overriddenDecl.getName())));
} else {
proposal.setDisplayName(Messages.format(CorrectionMessages.TypeMismatchSubProcessor_changereturnofoverridden_description, BasicElementLabels.getJavaElementName(overriddenDecl.getName())));
}
proposals.add(proposal);
}
}
}
public static void addIncompatibleThrowsProposals(IInvocationContext context, IProblemLocation problem, Collection<ICommandAccess> proposals) throws JavaModelException {
CompilationUnit astRoot= context.getASTRoot();
ASTNode selectedNode= problem.getCoveringNode(astRoot);
if (!(selectedNode instanceof MethodDeclaration)) {
return;
}
MethodDeclaration decl= (MethodDeclaration) selectedNode;
IMethodBinding methodDeclBinding= decl.resolveBinding();
if (methodDeclBinding == null) {
return;
}
IMethodBinding overridden= Bindings.findOverriddenMethod(methodDeclBinding, false);
if (overridden == null) {
return;
}
ICompilationUnit cu= context.getCompilationUnit();
ITypeBinding[] methodExceptions= methodDeclBinding.getExceptionTypes();
ITypeBinding[] definedExceptions= overridden.getExceptionTypes();
ArrayList<ITypeBinding> undeclaredExceptions= new ArrayList<>();
{
ChangeDescription[] changes= new ChangeDescription[methodExceptions.length];
for (int i= 0; i < methodExceptions.length; i++) {
if (!isDeclaredException(methodExceptions[i], definedExceptions)) {
changes[i]= new RemoveDescription();
undeclaredExceptions.add(methodExceptions[i]);
}
}
if (undeclaredExceptions.isEmpty()) {
return;
}
String label= Messages.format(CorrectionMessages.TypeMismatchSubProcessor_removeexceptions_description, BasicElementLabels.getJavaElementName(methodDeclBinding.getName()));
Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_REMOVE);
proposals.add(new ChangeMethodSignatureProposal(label, cu, astRoot, methodDeclBinding, null, changes, IProposalRelevance.REMOVE_EXCEPTIONS, image));
}
ITypeBinding declaringType= overridden.getDeclaringClass();
ICompilationUnit targetCu= null;
if (declaringType.isFromSource()) {
targetCu= ASTResolving.findCompilationUnitForBinding(cu, astRoot, declaringType);
}
if (targetCu != null) {
ChangeDescription[] changes= new ChangeDescription[definedExceptions.length + undeclaredExceptions.size()];
for (int i= 0; i < undeclaredExceptions.size(); i++) {
changes[i + definedExceptions.length]= new InsertDescription(undeclaredExceptions.get(i), ""); //$NON-NLS-1$
}
IMethodBinding overriddenDecl= overridden.getMethodDeclaration();
String[] args= { BasicElementLabels.getJavaElementName(declaringType.getName()), BasicElementLabels.getJavaElementName(overridden.getName()) };
String label= Messages.format(CorrectionMessages.TypeMismatchSubProcessor_addexceptions_description, args);
Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_ADD);
proposals.add(new ChangeMethodSignatureProposal(label, targetCu, astRoot, overriddenDecl, null, changes, IProposalRelevance.ADD_EXCEPTIONS, image));
}
}
private static boolean isDeclaredException(ITypeBinding curr, ITypeBinding[] declared) {
for (ITypeBinding d : declared) {
if (Bindings.isSuperType(d, curr)) {
return true;
}
}
return false;
}
public static void addTypeMismatchInForEachProposals(IInvocationContext context, IProblemLocation problem, Collection<ICommandAccess> proposals) {
CompilationUnit astRoot= context.getASTRoot();
ASTNode selectedNode= problem.getCoveringNode(astRoot);
if (selectedNode == null || selectedNode.getLocationInParent() != EnhancedForStatement.EXPRESSION_PROPERTY) {
return;
}
EnhancedForStatement forStatement= (EnhancedForStatement) selectedNode.getParent();
ITypeBinding expressionBinding= forStatement.getExpression().resolveTypeBinding();
if (expressionBinding == null) {
return;
}
ITypeBinding expectedBinding;
if (expressionBinding.isArray()) {
expectedBinding= expressionBinding.getComponentType();
} else {
IMethodBinding iteratorMethod= Bindings.findMethodInHierarchy(expressionBinding, "iterator", new String[0]); //$NON-NLS-1$
if (iteratorMethod == null) {
return;
}
ITypeBinding[] typeArguments= iteratorMethod.getReturnType().getTypeArguments();
if (typeArguments.length != 1) {
return;
}
expectedBinding= typeArguments[0];
}
AST ast= astRoot.getAST();
expectedBinding= Bindings.normalizeForDeclarationUse(expectedBinding, ast);
SingleVariableDeclaration parameter= forStatement.getParameter();
ICompilationUnit cu= context.getCompilationUnit();
if (parameter.getName().getLength() == 0) {
SimpleName simpleName= null;
if (parameter.getType() instanceof SimpleType) {
SimpleType type= (SimpleType) parameter.getType();
if (type.getName() instanceof SimpleName) {
simpleName= (SimpleName) type.getName();
}
} else if (parameter.getType() instanceof NameQualifiedType) {
simpleName= ((NameQualifiedType) parameter.getType()).getName();
}
if (simpleName != null) {
String name= simpleName.getIdentifier();
int relevance= StubUtility.hasLocalVariableName(cu.getJavaProject(), name) ? 10 : 7;
String label= Messages.format(CorrectionMessages.TypeMismatchSubProcessor_create_loop_variable_description, BasicElementLabels.getJavaElementName(name));
Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_LOCAL);
proposals.add(new NewVariableCorrectionProposal(label, cu, NewVariableCorrectionProposal.LOCAL, simpleName, null, relevance, image));
return;
}
}
String label= Messages.format(CorrectionMessages.TypeMismatchSubProcessor_incompatible_for_each_type_description, new String[] { BasicElementLabels.getJavaElementName(parameter.getName().getIdentifier()), BindingLabelProvider.getBindingLabel(expectedBinding, BindingLabelProvider.DEFAULT_TEXTFLAGS) });
Image image= JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_CHANGE);
ASTRewrite rewrite= ASTRewrite.create(ast);
ASTRewriteCorrectionProposal proposal= new ASTRewriteCorrectionProposal(label, cu, rewrite, IProposalRelevance.INCOMPATIBLE_FOREACH_TYPE, image);
ImportRewrite importRewrite= proposal.createImportRewrite(astRoot);
ImportRewriteContext importRewriteContext= new ContextSensitiveImportRewriteContext(ASTResolving.findParentBodyDeclaration(selectedNode), importRewrite);
Type newType= importRewrite.addImport(expectedBinding, ast, importRewriteContext, TypeLocation.LOCAL_VARIABLE);
rewrite.replace(parameter.getType(), newType, null);
proposals.add(proposal);
}
}