/******************************************************************************* * Copyright (c) 2000, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.che.ide.ext.java.jdt.internal.corext.fix; import org.eclipse.che.ide.ext.java.jdt.codeassistant.api.IProblemLocation; import org.eclipse.che.ide.ext.java.jdt.core.compiler.IProblem; import org.eclipse.che.ide.ext.java.jdt.core.dom.ASTNode; import org.eclipse.che.ide.ext.java.jdt.core.dom.ASTVisitor; import org.eclipse.che.ide.ext.java.jdt.core.dom.Assignment; import org.eclipse.che.ide.ext.java.jdt.core.dom.Block; import org.eclipse.che.ide.ext.java.jdt.core.dom.CastExpression; import org.eclipse.che.ide.ext.java.jdt.core.dom.ClassInstanceCreation; import org.eclipse.che.ide.ext.java.jdt.core.dom.CompilationUnit; import org.eclipse.che.ide.ext.java.jdt.core.dom.EnhancedForStatement; import org.eclipse.che.ide.ext.java.jdt.core.dom.Expression; import org.eclipse.che.ide.ext.java.jdt.core.dom.ExpressionStatement; import org.eclipse.che.ide.ext.java.jdt.core.dom.FieldAccess; import org.eclipse.che.ide.ext.java.jdt.core.dom.FieldDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.IBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.IMethodBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.ITypeBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.IVariableBinding; import org.eclipse.che.ide.ext.java.jdt.core.dom.ImportDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.Javadoc; import org.eclipse.che.ide.ext.java.jdt.core.dom.MethodDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.MethodInvocation; import org.eclipse.che.ide.ext.java.jdt.core.dom.NodeFinder; import org.eclipse.che.ide.ext.java.jdt.core.dom.ParenthesizedExpression; import org.eclipse.che.ide.ext.java.jdt.core.dom.PostfixExpression; import org.eclipse.che.ide.ext.java.jdt.core.dom.PrefixExpression; import org.eclipse.che.ide.ext.java.jdt.core.dom.QualifiedName; import org.eclipse.che.ide.ext.java.jdt.core.dom.SimpleName; import org.eclipse.che.ide.ext.java.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.che.ide.ext.java.jdt.core.dom.SuperMethodInvocation; import org.eclipse.che.ide.ext.java.jdt.core.dom.SwitchStatement; import org.eclipse.che.ide.ext.java.jdt.core.dom.TagElement; import org.eclipse.che.ide.ext.java.jdt.core.dom.Type; import org.eclipse.che.ide.ext.java.jdt.core.dom.TypeDeclarationStatement; import org.eclipse.che.ide.ext.java.jdt.core.dom.VariableDeclarationExpression; import org.eclipse.che.ide.ext.java.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.che.ide.ext.java.jdt.core.dom.VariableDeclarationStatement; import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ListRewrite; import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.ASTNodes; import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.LinkedNodeFinder; import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.NecessaryParenthesesChecker; import org.eclipse.che.ide.ext.java.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.JavadocTagsSubProcessor; import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.ProblemLocation; import org.eclipse.che.ide.ext.java.jdt.text.Document; import org.eclipse.che.ide.ext.java.jdt.text.edits.TextEditGroup; import org.eclipse.che.ide.runtime.Assert; import org.eclipse.che.ide.runtime.CoreException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Map; /** Fix which removes unused code. */ public class UnusedCodeFix extends CompilationUnitRewriteOperationsFix { private static class SideEffectFinder extends ASTVisitor { private final 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 static class RemoveImportOperation extends CompilationUnitRewriteOperation { private final ImportDeclaration fImportDeclaration; public RemoveImportOperation(ImportDeclaration importDeclaration) { fImportDeclaration = importDeclaration; } /** {@inheritDoc} */ @Override public void rewriteAST(CompilationUnitRewrite cuRewrite) throws CoreException { ImportDeclaration node = fImportDeclaration; TextEditGroup group = createTextEditGroup(FixMessages.INSTANCE.UnusedCodeFix_RemoveImport_description(), cuRewrite); cuRewrite.getASTRewrite().remove(node, group); } } private static class RemoveUnusedMemberOperation extends CompilationUnitRewriteOperation { private final SimpleName[] fUnusedNames; private boolean fForceRemove; private int fRemovedAssignmentsCount; private int fAlteredAssignmentsCount; public RemoveUnusedMemberOperation(SimpleName[] unusedNames, boolean removeAllAsignements) { fUnusedNames = unusedNames; fForceRemove = removeAllAsignements; } /** {@inheritDoc} */ @Override public void rewriteAST(CompilationUnitRewrite cuRewrite) throws CoreException { for (int i = 0; i < fUnusedNames.length; i++) { removeUnusedName(cuRewrite, fUnusedNames[i]); } } private void removeUnusedName(CompilationUnitRewrite cuRewrite, SimpleName simpleName) { ASTRewrite rewrite = cuRewrite.getASTRewrite(); CompilationUnit completeRoot = cuRewrite.getRoot(); IBinding binding = simpleName.resolveBinding(); CompilationUnit root = (CompilationUnit)simpleName.getRoot(); String displayString = getDisplayString(binding); TextEditGroup group = createTextEditGroup(displayString, cuRewrite); if (binding.getKind() == IBinding.METHOD) { IMethodBinding decl = ((IMethodBinding)binding).getMethodDeclaration(); ASTNode declaration = root.findDeclaringNode(decl); rewrite.remove(declaration, group); } else if (binding.getKind() == IBinding.TYPE) { ITypeBinding decl = ((ITypeBinding)binding).getTypeDeclaration(); ASTNode declaration = root.findDeclaringNode(decl); if (declaration.getParent() instanceof TypeDeclarationStatement) { declaration = declaration.getParent(); } rewrite.remove(declaration, group); } else if (binding.getKind() == IBinding.VARIABLE) { SimpleName nameNode = (SimpleName)NodeFinder.perform(completeRoot, simpleName.getStartPosition(), simpleName.getLength()); SimpleName[] references = LinkedNodeFinder.findByBinding(completeRoot, nameNode.resolveBinding()); for (int i = 0; i < references.length; i++) { removeVariableReferences(rewrite, references[i], group); } IVariableBinding bindingDecl = ((IVariableBinding)nameNode.resolveBinding()).getVariableDeclaration(); ASTNode declaringNode = completeRoot.findDeclaringNode(bindingDecl); if (declaringNode instanceof SingleVariableDeclaration) { removeParamTag(rewrite, (SingleVariableDeclaration)declaringNode, group); } } else { // unexpected } } private String getDisplayString(IBinding binding) { switch (binding.getKind()) { case IBinding.TYPE: return FixMessages.INSTANCE.UnusedCodeFix_RemoveUnusedType_description(); case IBinding.METHOD: if (((IMethodBinding)binding).isConstructor()) { return FixMessages.INSTANCE.UnusedCodeFix_RemoveUnusedConstructor_description(); } else { return FixMessages.INSTANCE.UnusedCodeFix_RemoveUnusedPrivateMethod_description(); } case IBinding.VARIABLE: if (((IVariableBinding)binding).isField()) { return FixMessages.INSTANCE.UnusedCodeFix_RemoveUnusedField_description(); } else { return FixMessages.INSTANCE.UnusedCodeFix_RemoveUnusedVariabl_description(); } default: return ""; //$NON-NLS-1$ } } private void removeParamTag(ASTRewrite rewrite, SingleVariableDeclaration varDecl, TextEditGroup group) { 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, group); } } } } /** * Remove the field or variable declaration including the initializer. * * @param rewrite * the AST rewriter to use * @param reference * a reference to the variable to remove * @param group * the text edit group to use */ private void removeVariableReferences(ASTRewrite rewrite, SimpleName reference, TextEditGroup group) { ASTNode parent = reference.getParent(); while (parent instanceof QualifiedName) { parent = parent.getParent(); } if (parent instanceof FieldAccess) { parent = parent.getParent(); } int nameParentType = parent.getNodeType(); if (nameParentType == 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, group); } else { rewrite.replace(assignment, rewrite.createCopyTarget(rightHand), group); } } else if (nameParentType == ASTNode.SINGLE_VARIABLE_DECLARATION) { rewrite.remove(parent, group); } else if (nameParentType == 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(); } Expression initializer = frag.getInitializer(); if (initializer instanceof CastExpression) { initializer = ((CastExpression)initializer).getExpression(); } boolean sideEffectInitializer = initializer instanceof MethodInvocation || initializer instanceof ClassInstanceCreation; if (fragments.size() == fUnusedNames.length) { if (fForceRemove) { rewrite.remove(varDecl, group); return; } if (parent.getParent() instanceof FieldDeclaration) { rewrite.remove(varDecl, group); return; } if (sideEffectInitializer) { Expression movedInit = (Expression)rewrite.createMoveTarget(initializer); ExpressionStatement wrapped = rewrite.getAST().newExpressionStatement(movedInit); rewrite.replace(varDecl, wrapped, group); } else { rewrite.remove(varDecl, group); } } else { if (fForceRemove) { rewrite.remove(frag, group); return; } //multiple declarations in one line ASTNode declaration = parent.getParent(); if (declaration instanceof FieldDeclaration) { rewrite.remove(frag, group); return; } if (declaration instanceof VariableDeclarationStatement) { ASTNode lst = declaration.getParent(); ListRewrite listRewrite = null; if (lst instanceof Block) { listRewrite = rewrite.getListRewrite(lst, Block.STATEMENTS_PROPERTY); } else if (lst instanceof SwitchStatement) { listRewrite = rewrite.getListRewrite(lst, SwitchStatement.STATEMENTS_PROPERTY); } else { Assert.isTrue(false); } splitUpDeclarations(rewrite, group, frag, listRewrite, (VariableDeclarationStatement)declaration); rewrite.remove(frag, group); return; } if (declaration instanceof VariableDeclarationExpression) { //keep constructors and method invocations if (!sideEffectInitializer) { rewrite.remove(frag, group); } } } } else if (nameParentType == ASTNode.POSTFIX_EXPRESSION || nameParentType == ASTNode.PREFIX_EXPRESSION) { Expression expression = (Expression)parent; ASTNode expressionParent = expression.getParent(); if (expressionParent.getNodeType() == ASTNode.EXPRESSION_STATEMENT) { removeStatement(rewrite, expressionParent, group); } else { rewrite.remove(expression, group); } } } private void splitUpDeclarations(ASTRewrite rewrite, TextEditGroup group, VariableDeclarationFragment frag, ListRewrite statementRewrite, VariableDeclarationStatement originalStatement) { Expression initializer = frag.getInitializer(); //keep constructors and method invocations if (initializer instanceof MethodInvocation || initializer instanceof ClassInstanceCreation) { Expression movedInitializer = (Expression)rewrite.createMoveTarget(initializer); ExpressionStatement newInitializer = rewrite.getAST().newExpressionStatement(movedInitializer); statementRewrite.insertAfter(newInitializer, originalStatement, group); VariableDeclarationStatement newDeclaration = null; List<VariableDeclarationFragment> fragments = originalStatement.fragments(); int fragIndex = fragments.indexOf(frag); ListIterator<VariableDeclarationFragment> fragmentIterator = fragments.listIterator(fragIndex + 1); while (fragmentIterator.hasNext()) { VariableDeclarationFragment currentFragment = fragmentIterator.next(); VariableDeclarationFragment movedFragment = (VariableDeclarationFragment)rewrite.createMoveTarget(currentFragment); if (newDeclaration == null) { newDeclaration = rewrite.getAST().newVariableDeclarationStatement(movedFragment); Type copiedType = (Type)rewrite.createCopyTarget(originalStatement.getType()); newDeclaration.setType(copiedType); } else newDeclaration.fragments().add(movedFragment); } if (newDeclaration != null) { statementRewrite.insertAfter(newDeclaration, newInitializer, group); } if (originalStatement.fragments().size() == newDeclaration.fragments().size() + 1) { rewrite.remove(originalStatement, group); } } } private void removeVariableWithInitializer(ASTRewrite rewrite, ASTNode initializerNode, ASTNode statementNode, TextEditGroup group) { boolean performRemove = fForceRemove; if (!performRemove) { ArrayList<Expression> sideEffectNodes = new ArrayList<Expression>(); initializerNode.accept(new SideEffectFinder(sideEffectNodes)); performRemove = sideEffectNodes.isEmpty(); } if (performRemove) { removeStatement(rewrite, statementNode, group); fRemovedAssignmentsCount++; } else { ASTNode initNode = rewrite.createMoveTarget(initializerNode); ExpressionStatement statement = rewrite.getAST().newExpressionStatement((Expression)initNode); rewrite.replace(statementNode, statement, null); fAlteredAssignmentsCount++; } } private void removeStatement(ASTRewrite rewrite, ASTNode statementNode, TextEditGroup group) { if (ASTNodes.isControlStatementBody(statementNode.getLocationInParent())) { rewrite.replace(statementNode, rewrite.getAST().newBlock(), group); } else { rewrite.remove(statementNode, group); } } @Override public String getAdditionalInfo() { StringBuffer sb = new StringBuffer(); if (fRemovedAssignmentsCount == 1) { sb.append(FixMessages.INSTANCE.UnusedCodeFix_RemoveFieldOrLocal_RemovedAssignments_preview_singular()); } else if (fRemovedAssignmentsCount > 1) { sb.append(FixMessages.INSTANCE.UnusedCodeFix_RemoveFieldOrLocal_RemovedAssignments_preview_plural(String .valueOf( fRemovedAssignmentsCount))); } if (fAlteredAssignmentsCount == 1) { sb.append(FixMessages.INSTANCE.UnusedCodeFix_RemoveFieldOrLocal_AlteredAssignments_preview_singular()); } else if (fAlteredAssignmentsCount > 1) { sb.append(FixMessages.INSTANCE.UnusedCodeFix_RemoveFieldOrLocal_AlteredAssignments_preview_plural(String .valueOf( fAlteredAssignmentsCount))); } if (sb.length() > 0) { return sb.toString(); } else return null; } } private static class RemoveCastOperation extends CompilationUnitRewriteOperation { private final CastExpression fCast; private final ASTNode fSelectedNode; public RemoveCastOperation(CastExpression cast, ASTNode selectedNode) { fCast = cast; fSelectedNode = selectedNode; } /** {@inheritDoc} */ @Override public void rewriteAST(CompilationUnitRewrite cuRewrite) throws CoreException { TextEditGroup group = createTextEditGroup(FixMessages.INSTANCE.UnusedCodeFix_RemoveCast_description(), cuRewrite); ASTRewrite rewrite = cuRewrite.getASTRewrite(); CastExpression cast = fCast; Expression expression = cast.getExpression(); ASTNode placeholder = rewrite.createCopyTarget(expression); if (NecessaryParenthesesChecker.needsParentheses(expression, cast.getParent(), cast.getLocationInParent())) { rewrite.replace(fCast, placeholder, group); } else { rewrite.replace(fCast.getParent() instanceof ParenthesizedExpression ? fCast.getParent() : fSelectedNode, placeholder, group); } } } private static class RemoveAllCastOperation extends CompilationUnitRewriteOperation { private final HashSet<CastExpression> fUnnecessaryCasts; public RemoveAllCastOperation(HashSet<CastExpression> unnecessaryCasts) { fUnnecessaryCasts = unnecessaryCasts; } /** {@inheritDoc} */ @Override public void rewriteAST(CompilationUnitRewrite cuRewrite) throws CoreException { ASTRewrite rewrite = cuRewrite.getASTRewrite(); TextEditGroup group = createTextEditGroup(FixMessages.INSTANCE.UnusedCodeFix_RemoveCast_description(), cuRewrite); while (fUnnecessaryCasts.size() > 0) { CastExpression castExpression = fUnnecessaryCasts.iterator().next(); fUnnecessaryCasts.remove(castExpression); CastExpression down = castExpression; while (fUnnecessaryCasts.contains(down.getExpression())) { down = (CastExpression)down.getExpression(); fUnnecessaryCasts.remove(down); } Expression expression = down.getExpression(); ASTNode move = rewrite.createMoveTarget(expression); CastExpression top = castExpression; while (fUnnecessaryCasts.contains(top.getParent())) { top = (CastExpression)top.getParent(); fUnnecessaryCasts.remove(top); } ASTNode toReplace = top; if (top.getParent() instanceof ParenthesizedExpression && !NecessaryParenthesesChecker.needsParentheses(expression, top.getParent(), top.getLocationInParent())) { toReplace = top.getParent(); } rewrite.replace(toReplace, move, group); } } } public static UnusedCodeFix createRemoveUnusedImportFix(CompilationUnit compilationUnit, IProblemLocation problem, Document document) { if (isUnusedImport(problem)) { ImportDeclaration node = getImportDeclaration(problem, compilationUnit); if (node != null) { String label = FixMessages.INSTANCE.UnusedCodeFix_RemoveImport_description(); RemoveImportOperation operation = new RemoveImportOperation(node); Map<String, String> options = new HashMap<String, String>(); options.put(CleanUpConstants.REMOVE_UNUSED_CODE_IMPORTS, CleanUpOptions.TRUE); return new UnusedCodeFix(label, compilationUnit, new CompilationUnitRewriteOperation[]{operation}, options, document); } } return null; } public static boolean isUnusedImport(IProblemLocation problem) { int id = problem.getProblemId(); return id == IProblem.UnusedImport || id == IProblem.DuplicateImport || id == IProblem.ConflictingImport || id == IProblem.CannotImportPackage || id == IProblem.ImportNotFound; } public static UnusedCodeFix createUnusedMemberFix(CompilationUnit compilationUnit, IProblemLocation problem, boolean removeAllAssignements, Document document) { if (isUnusedMember(problem)) { SimpleName name = getUnusedName(compilationUnit, problem); if (name != null) { IBinding binding = name.resolveBinding(); if (binding != null) { if (isFormalParameterInEnhancedForStatement(name)) return null; String label = getDisplayString(name, binding, removeAllAssignements); RemoveUnusedMemberOperation operation = new RemoveUnusedMemberOperation(new SimpleName[]{name}, removeAllAssignements); return new UnusedCodeFix(label, compilationUnit, new CompilationUnitRewriteOperation[]{operation}, getCleanUpOptions(binding, removeAllAssignements), document); } } } return null; } public static boolean isUnusedMember(IProblemLocation problem) { int id = problem.getProblemId(); return id == IProblem.UnusedPrivateMethod || id == IProblem.UnusedPrivateConstructor || id == IProblem.UnusedPrivateField || id == IProblem.UnusedPrivateType || id == IProblem.LocalVariableIsNeverUsed || id == IProblem.ArgumentIsNeverUsed; } public static UnusedCodeFix createRemoveUnusedCastFix(CompilationUnit compilationUnit, IProblemLocation problem, Document document) { if (problem.getProblemId() != IProblem.UnnecessaryCast) return null; ASTNode selectedNode = problem.getCoveringNode(compilationUnit); ASTNode curr = selectedNode; while (curr instanceof ParenthesizedExpression) { curr = ((ParenthesizedExpression)curr).getExpression(); } if (!(curr instanceof CastExpression)) return null; return new UnusedCodeFix(FixMessages.INSTANCE.UnusedCodeFix_RemoveCast_description(), compilationUnit, new CompilationUnitRewriteOperation[]{new RemoveCastOperation((CastExpression)curr, selectedNode)}, document); } public static ICleanUpFix createCleanUp(CompilationUnit compilationUnit, boolean removeUnusedPrivateMethods, boolean removeUnusedPrivateConstructors, boolean removeUnusedPrivateFields, boolean removeUnusedPrivateTypes, boolean removeUnusedLocalVariables, boolean removeUnusedImports, boolean removeUnusedCast, Document document) { IProblem[] problems = compilationUnit.getProblems(); IProblemLocation[] locations = new IProblemLocation[problems.length]; for (int i = 0; i < problems.length; i++) { locations[i] = new ProblemLocation(problems[i]); } return createCleanUp(compilationUnit, locations, removeUnusedPrivateMethods, removeUnusedPrivateConstructors, removeUnusedPrivateFields, removeUnusedPrivateTypes, removeUnusedLocalVariables, removeUnusedImports, removeUnusedCast, document); } public static ICleanUpFix createCleanUp(CompilationUnit compilationUnit, IProblemLocation[] problems, boolean removeUnusedPrivateMethods, boolean removeUnusedPrivateConstructors, boolean removeUnusedPrivateFields, boolean removeUnusedPrivateTypes, boolean removeUnusedLocalVariables, boolean removeUnusedImports, boolean removeUnusedCast, Document document) { List<CompilationUnitRewriteOperation> result = new ArrayList<CompilationUnitRewriteOperation>(); HashMap<ASTNode, List<SimpleName>> variableDeclarations = new HashMap<ASTNode, List<SimpleName>>(); HashSet<CastExpression> unnecessaryCasts = new HashSet<CastExpression>(); for (int i = 0; i < problems.length; i++) { IProblemLocation problem = problems[i]; int id = problem.getProblemId(); if (removeUnusedImports && (id == IProblem.UnusedImport || id == IProblem.DuplicateImport || id == IProblem.ConflictingImport || id == IProblem.CannotImportPackage || id == IProblem.ImportNotFound)) { ImportDeclaration node = UnusedCodeFix.getImportDeclaration(problem, compilationUnit); if (node != null) { result.add(new RemoveImportOperation(node)); } } if ((removeUnusedPrivateMethods && id == IProblem.UnusedPrivateMethod) || (removeUnusedPrivateConstructors && id == IProblem.UnusedPrivateConstructor) || (removeUnusedPrivateTypes && id == IProblem.UnusedPrivateType)) { SimpleName name = getUnusedName(compilationUnit, problem); if (name != null) { IBinding binding = name.resolveBinding(); if (binding != null) { result.add(new RemoveUnusedMemberOperation(new SimpleName[]{name}, false)); } } } if ((removeUnusedLocalVariables && id == IProblem.LocalVariableIsNeverUsed) || (removeUnusedPrivateFields && id == IProblem.UnusedPrivateField)) { SimpleName name = getUnusedName(compilationUnit, problem); if (name != null) { IBinding binding = name.resolveBinding(); if (binding instanceof IVariableBinding && !isFormalParameterInEnhancedForStatement(name) && (!((IVariableBinding)binding).isField() || isSideEffectFree(name, compilationUnit))) { VariableDeclarationFragment parent = (VariableDeclarationFragment)ASTNodes.getParent(name, VariableDeclarationFragment.VARIABLE_DECLARATION_FRAGMENT); if (parent != null) { ASTNode varDecl = parent.getParent(); if (!variableDeclarations.containsKey(varDecl)) { variableDeclarations.put(varDecl, new ArrayList<SimpleName>()); } variableDeclarations.get(varDecl).add(name); } else { result.add(new RemoveUnusedMemberOperation(new SimpleName[]{name}, false)); } } } } if (removeUnusedCast && id == IProblem.UnnecessaryCast) { ASTNode selectedNode = problem.getCoveringNode(compilationUnit); ASTNode curr = selectedNode; while (curr instanceof ParenthesizedExpression) { curr = ((ParenthesizedExpression)curr).getExpression(); } if (curr instanceof CastExpression) { unnecessaryCasts.add((CastExpression)curr); } } } for (Iterator<ASTNode> iter = variableDeclarations.keySet().iterator(); iter.hasNext(); ) { ASTNode node = iter.next(); List<SimpleName> names = variableDeclarations.get(node); result.add(new RemoveUnusedMemberOperation(names.toArray(new SimpleName[names.size()]), false)); } if (unnecessaryCasts.size() > 0) result.add(new RemoveAllCastOperation(unnecessaryCasts)); if (result.size() == 0) return null; return new UnusedCodeFix(FixMessages.INSTANCE.UnusedCodeFix_change_name(), compilationUnit, result.toArray(new CompilationUnitRewriteOperation[result.size()]), document); } private static boolean isFormalParameterInEnhancedForStatement(SimpleName name) { return name.getParent() instanceof SingleVariableDeclaration && name.getParent().getLocationInParent() == EnhancedForStatement.PARAMETER_PROPERTY; } private static boolean isSideEffectFree(SimpleName simpleName, CompilationUnit completeRoot) { SimpleName nameNode = (SimpleName)NodeFinder.perform(completeRoot, simpleName.getStartPosition(), simpleName.getLength()); SimpleName[] references = LinkedNodeFinder.findByBinding(completeRoot, nameNode.resolveBinding()); for (int i = 0; i < references.length; i++) { if (hasSideEffect(references[i])) return false; } return true; } private static boolean hasSideEffect(SimpleName reference) { ASTNode parent = reference.getParent(); while (parent instanceof QualifiedName) { parent = parent.getParent(); } if (parent instanceof FieldAccess) { parent = parent.getParent(); } ASTNode node = null; int nameParentType = parent.getNodeType(); if (nameParentType == ASTNode.ASSIGNMENT) { Assignment assignment = (Assignment)parent; node = assignment.getRightHandSide(); } else if (nameParentType == ASTNode.SINGLE_VARIABLE_DECLARATION) { SingleVariableDeclaration decl = (SingleVariableDeclaration)parent; node = decl.getInitializer(); if (node == null) return false; } else if (nameParentType == ASTNode.VARIABLE_DECLARATION_FRAGMENT) { node = parent; } else { return false; } ArrayList<Expression> sideEffects = new ArrayList<Expression>(); node.accept(new SideEffectFinder(sideEffects)); return sideEffects.size() > 0; } private static SimpleName getUnusedName(CompilationUnit compilationUnit, IProblemLocation problem) { ASTNode selectedNode = problem.getCoveringNode(compilationUnit); if (selectedNode instanceof MethodDeclaration) { return ((MethodDeclaration)selectedNode).getName(); } else if (selectedNode instanceof SimpleName) { return (SimpleName)selectedNode; } return null; } private static String getDisplayString(SimpleName simpleName, IBinding binding, boolean removeAllAssignements) { String name = simpleName.getIdentifier(); switch (binding.getKind()) { case IBinding.TYPE: return FixMessages.INSTANCE.UnusedCodeFix_RemoveType_description(name); case IBinding.METHOD: if (((IMethodBinding)binding).isConstructor()) { return FixMessages.INSTANCE.UnusedCodeFix_RemoveConstructor_description(name); } else { return FixMessages.INSTANCE.UnusedCodeFix_RemoveMethod_description(name); } case IBinding.VARIABLE: if (removeAllAssignements) { return FixMessages.INSTANCE.UnusedCodeFix_RemoveFieldOrLocalWithInitializer_description(name); } else { return FixMessages.INSTANCE.UnusedCodeFix_RemoveFieldOrLocal_description(name); } default: return ""; //$NON-NLS-1$ } } private static Map<String, String> getCleanUpOptions(IBinding binding, boolean removeAll) { Map<String, String> result = new HashMap<String, String>(); result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_MEMBERS, CleanUpOptions.TRUE); switch (binding.getKind()) { case IBinding.TYPE: result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_TYPES, CleanUpOptions.TRUE); break; case IBinding.METHOD: if (((IMethodBinding)binding).isConstructor()) { result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_CONSTRUCTORS, CleanUpOptions.TRUE); } else { result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_METHODS, CleanUpOptions.TRUE); } break; case IBinding.VARIABLE: if (removeAll) return null; result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_FELDS, CleanUpOptions.TRUE); result.put(CleanUpConstants.REMOVE_UNUSED_CODE_LOCAL_VARIABLES, CleanUpOptions.TRUE); break; } return result; } private static ImportDeclaration getImportDeclaration(IProblemLocation problem, CompilationUnit compilationUnit) { ASTNode selectedNode = problem.getCoveringNode(compilationUnit); if (selectedNode != null) { ASTNode node = ASTNodes.getParent(selectedNode, ASTNode.IMPORT_DECLARATION); if (node instanceof ImportDeclaration) { return (ImportDeclaration)node; } } return null; } private final Map<String, String> fCleanUpOptions; private UnusedCodeFix(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations, Document document) { this(name, compilationUnit, fixRewriteOperations, null, document); } private UnusedCodeFix(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations, Map<String, String> options, Document document) { super(name, compilationUnit, fixRewriteOperations, document); fCleanUpOptions = options; } public UnusedCodeCleanUp getCleanUp() { if (fCleanUpOptions == null) return null; return new UnusedCodeCleanUp(fCleanUpOptions); } }