| /******************************************************************************* |
| * Copyright (c) 2000, 2021 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 |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.ui.text.correction.proposals; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.ui.ISharedImages; |
| import org.eclipse.ui.PlatformUI; |
| |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.ASTVisitor; |
| import org.eclipse.jdt.core.dom.Assignment; |
| import org.eclipse.jdt.core.dom.ClassInstanceCreation; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.dom.Expression; |
| import org.eclipse.jdt.core.dom.FieldAccess; |
| import org.eclipse.jdt.core.dom.FieldDeclaration; |
| 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.Javadoc; |
| import org.eclipse.jdt.core.dom.MethodDeclaration; |
| import org.eclipse.jdt.core.dom.MethodInvocation; |
| import org.eclipse.jdt.core.dom.NodeFinder; |
| import org.eclipse.jdt.core.dom.PostfixExpression; |
| import org.eclipse.jdt.core.dom.PrefixExpression; |
| import org.eclipse.jdt.core.dom.QualifiedName; |
| import org.eclipse.jdt.core.dom.SimpleName; |
| import org.eclipse.jdt.core.dom.SingleVariableDeclaration; |
| import org.eclipse.jdt.core.dom.SuperMethodInvocation; |
| import org.eclipse.jdt.core.dom.TagElement; |
| 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.rewrite.ASTRewrite; |
| import org.eclipse.jdt.core.manipulation.SharedASTProviderCore; |
| |
| import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels; |
| import org.eclipse.jdt.internal.corext.dom.ASTNodes; |
| import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder; |
| import org.eclipse.jdt.internal.corext.util.Messages; |
| |
| import org.eclipse.jdt.ui.text.java.correction.ASTRewriteCorrectionProposal; |
| |
| import org.eclipse.jdt.internal.ui.text.correction.CorrectionMessages; |
| import org.eclipse.jdt.internal.ui.text.correction.JavadocTagsSubProcessor; |
| |
| public class RemoveDeclarationCorrectionProposal extends ASTRewriteCorrectionProposal { |
| |
| private static class SideEffectFinder extends ASTVisitor { |
| |
| private ArrayList<Expression> fSideEffectNodes; |
| |
| public SideEffectFinder(ArrayList<Expression> res) { |
| fSideEffectNodes= res; |
| } |
| |
| @Override |
| public boolean visit(Assignment node) { |
| fSideEffectNodes.add(node); |
| return false; |
| } |
| |
| @Override |
| public boolean visit(PostfixExpression node) { |
| fSideEffectNodes.add(node); |
| return false; |
| } |
| |
| @Override |
| public boolean visit(PrefixExpression node) { |
| Object operator= node.getOperator(); |
| if (operator == PrefixExpression.Operator.INCREMENT || operator == PrefixExpression.Operator.DECREMENT) { |
| fSideEffectNodes.add(node); |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean visit(MethodInvocation node) { |
| fSideEffectNodes.add(node); |
| return false; |
| } |
| |
| @Override |
| public boolean visit(ClassInstanceCreation node) { |
| fSideEffectNodes.add(node); |
| return false; |
| } |
| |
| @Override |
| public boolean visit(SuperMethodInvocation node) { |
| fSideEffectNodes.add(node); |
| return false; |
| } |
| } |
| |
| |
| private SimpleName fSimpleName; |
| |
| public RemoveDeclarationCorrectionProposal(ICompilationUnit cu, SimpleName name, int relevance) { |
| super("", cu, null, relevance, PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_TOOL_DELETE)); //$NON-NLS-1$ |
| fSimpleName= name; |
| } |
| |
| @Override |
| public String getName() { |
| IBinding binding= fSimpleName.resolveBinding(); |
| String name= BasicElementLabels.getJavaElementName(fSimpleName.getIdentifier()); |
| switch (binding.getKind()) { |
| case IBinding.TYPE: |
| return Messages.format(CorrectionMessages.RemoveDeclarationCorrectionProposal_removeunusedtype_description, name); |
| case IBinding.METHOD: |
| if (((IMethodBinding) binding).isConstructor()) { |
| return Messages.format(CorrectionMessages.RemoveDeclarationCorrectionProposal_removeunusedconstructor_description, name); |
| } else { |
| return Messages.format(CorrectionMessages.RemoveDeclarationCorrectionProposal_removeunusedmethod_description, name); |
| } |
| case IBinding.VARIABLE: |
| if (((IVariableBinding) binding).isField()) { |
| return Messages.format(CorrectionMessages.RemoveDeclarationCorrectionProposal_removeunusedfield_description, name); |
| } else { |
| return Messages.format(CorrectionMessages.RemoveDeclarationCorrectionProposal_removeunusedvar_description, name); |
| } |
| default: |
| return super.getDisplayString(); |
| } |
| } |
| |
| @Override |
| protected ASTRewrite getRewrite() { |
| IBinding binding= fSimpleName.resolveBinding(); |
| CompilationUnit root= (CompilationUnit) fSimpleName.getRoot(); |
| ASTRewrite rewrite; |
| if (binding.getKind() == IBinding.METHOD) { |
| IMethodBinding decl= ((IMethodBinding) binding).getMethodDeclaration(); |
| ASTNode declaration= root.findDeclaringNode(decl); |
| rewrite= ASTRewrite.create(root.getAST()); |
| rewrite.remove(declaration, null); |
| } else if (binding.getKind() == IBinding.TYPE) { |
| ITypeBinding decl= ((ITypeBinding) binding).getTypeDeclaration(); |
| ASTNode declaration= root.findDeclaringNode(decl); |
| rewrite= ASTRewrite.create(root.getAST()); |
| rewrite.remove(declaration, null); |
| } else if (binding.getKind() == IBinding.VARIABLE) { |
| // needs full AST |
| CompilationUnit completeRoot= SharedASTProviderCore.getAST(getCompilationUnit(), SharedASTProviderCore.WAIT_YES, null); |
| |
| SimpleName nameNode= (SimpleName) NodeFinder.perform(completeRoot, fSimpleName.getStartPosition(), fSimpleName.getLength()); |
| |
| rewrite= ASTRewrite.create(completeRoot.getAST()); |
| for (SimpleName reference : LinkedNodeFinder.findByBinding(completeRoot, nameNode.resolveBinding())) { |
| removeVariableReferences(rewrite, reference); |
| } |
| |
| IVariableBinding bindingDecl= ((IVariableBinding) nameNode.resolveBinding()).getVariableDeclaration(); |
| ASTNode declaringNode= completeRoot.findDeclaringNode(bindingDecl); |
| if (declaringNode instanceof SingleVariableDeclaration) { |
| removeParamTag(rewrite, (SingleVariableDeclaration) declaringNode); |
| } |
| } else { |
| throw new IllegalArgumentException("Unexpected binding"); //$NON-NLS-1$ |
| } |
| return rewrite; |
| } |
| |
| private void removeParamTag(ASTRewrite rewrite, SingleVariableDeclaration varDecl) { |
| if (varDecl.getParent() instanceof MethodDeclaration) { |
| Javadoc javadoc= ((MethodDeclaration) varDecl.getParent()).getJavadoc(); |
| if (javadoc != null) { |
| TagElement tagElement= JavadocTagsSubProcessor.findParamTag(javadoc, varDecl.getName().getIdentifier()); |
| if (tagElement != null) { |
| rewrite.remove(tagElement, null); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Remove the field or variable declaration including the initializer. |
| * @param rewrite the ast rewrite |
| * @param reference the reference |
| */ |
| private void removeVariableReferences(ASTRewrite rewrite, SimpleName reference) { |
| ASTNode parent= reference.getParent(); |
| while (parent instanceof QualifiedName) { |
| parent= parent.getParent(); |
| } |
| if (parent instanceof FieldAccess) { |
| parent= parent.getParent(); |
| } |
| |
| int nameParentType= parent.getNodeType(); |
| switch (nameParentType) { |
| case ASTNode.ASSIGNMENT: |
| Assignment assignment= (Assignment) parent; |
| Expression rightHand= assignment.getRightHandSide(); |
| ASTNode assignParent= assignment.getParent(); |
| if (assignParent.getNodeType() == ASTNode.EXPRESSION_STATEMENT && rightHand.getNodeType() != ASTNode.ASSIGNMENT) { |
| removeVariableWithInitializer(rewrite, rightHand, assignParent); |
| } else { |
| rewrite.replace(assignment, rewrite.createCopyTarget(rightHand), null); |
| } |
| break; |
| case ASTNode.SINGLE_VARIABLE_DECLARATION: |
| rewrite.remove(parent, null); |
| break; |
| case ASTNode.VARIABLE_DECLARATION_FRAGMENT: |
| VariableDeclarationFragment frag= (VariableDeclarationFragment) parent; |
| ASTNode varDecl= frag.getParent(); |
| List<VariableDeclarationFragment> fragments; |
| if (varDecl instanceof VariableDeclarationExpression) { |
| fragments= ((VariableDeclarationExpression) varDecl).fragments(); |
| } else if (varDecl instanceof FieldDeclaration) { |
| fragments= ((FieldDeclaration) varDecl).fragments(); |
| } else { |
| fragments= ((VariableDeclarationStatement) varDecl).fragments(); |
| } |
| if (fragments.size() == 1) { |
| rewrite.remove(varDecl, null); |
| } else { |
| rewrite.remove(frag, null); // don't try to preserve |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| private void removeVariableWithInitializer(ASTRewrite rewrite, ASTNode initializerNode, ASTNode statementNode) { |
| ArrayList<Expression> sideEffectNodes= new ArrayList<>(); |
| initializerNode.accept(new SideEffectFinder(sideEffectNodes)); |
| int nSideEffects= sideEffectNodes.size(); |
| if (nSideEffects == 0) { |
| if (ASTNodes.isControlStatementBody(statementNode.getLocationInParent())) { |
| rewrite.replace(statementNode, rewrite.getAST().newBlock(), null); |
| } else { |
| rewrite.remove(statementNode, null); |
| } |
| } else { |
| // do nothing yet |
| } |
| } |
| |
| } |