]> git.uio.no Git - ifi-stolz-refaktor.git/blame - case-study/jdt-after/core extension/org/eclipse/jdt/internal/corext/fix/UnusedCodeFix.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-after / core extension / org / eclipse / jdt / internal / corext / fix / UnusedCodeFix.java
CommitLineData
1b2798f6
EK
1/*******************************************************************************
2 * Copyright (c) 2000, 2012 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11package org.eclipse.jdt.internal.corext.fix;
12
13import java.util.ArrayList;
14import java.util.Hashtable;
15import java.util.Iterator;
16import java.util.LinkedHashSet;
17import java.util.List;
18import java.util.ListIterator;
19import java.util.Map;
20
21import org.eclipse.core.runtime.Assert;
22import org.eclipse.core.runtime.CoreException;
23
24import org.eclipse.text.edits.TextEditGroup;
25
26import org.eclipse.jdt.core.compiler.IProblem;
27import org.eclipse.jdt.core.dom.ASTNode;
28import org.eclipse.jdt.core.dom.ASTVisitor;
29import org.eclipse.jdt.core.dom.Assignment;
30import org.eclipse.jdt.core.dom.Block;
31import org.eclipse.jdt.core.dom.CastExpression;
32import org.eclipse.jdt.core.dom.ClassInstanceCreation;
33import org.eclipse.jdt.core.dom.CompilationUnit;
34import org.eclipse.jdt.core.dom.EnhancedForStatement;
35import org.eclipse.jdt.core.dom.Expression;
36import org.eclipse.jdt.core.dom.ExpressionStatement;
37import org.eclipse.jdt.core.dom.FieldAccess;
38import org.eclipse.jdt.core.dom.FieldDeclaration;
39import org.eclipse.jdt.core.dom.IBinding;
40import org.eclipse.jdt.core.dom.IMethodBinding;
41import org.eclipse.jdt.core.dom.ITypeBinding;
42import org.eclipse.jdt.core.dom.IVariableBinding;
43import org.eclipse.jdt.core.dom.ImportDeclaration;
44import org.eclipse.jdt.core.dom.Javadoc;
45import org.eclipse.jdt.core.dom.MethodDeclaration;
46import org.eclipse.jdt.core.dom.MethodInvocation;
47import org.eclipse.jdt.core.dom.NodeFinder;
48import org.eclipse.jdt.core.dom.ParenthesizedExpression;
49import org.eclipse.jdt.core.dom.PostfixExpression;
50import org.eclipse.jdt.core.dom.PrefixExpression;
51import org.eclipse.jdt.core.dom.QualifiedName;
52import org.eclipse.jdt.core.dom.SimpleName;
53import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
54import org.eclipse.jdt.core.dom.SuperMethodInvocation;
55import org.eclipse.jdt.core.dom.SwitchStatement;
56import org.eclipse.jdt.core.dom.TagElement;
57import org.eclipse.jdt.core.dom.Type;
58import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
59import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
60import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
61import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
62import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
63import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
64
65import org.eclipse.jdt.internal.corext.dom.ASTNodes;
66import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder;
67import org.eclipse.jdt.internal.corext.dom.NecessaryParenthesesChecker;
68import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
69import org.eclipse.jdt.internal.corext.util.Messages;
70
71import org.eclipse.jdt.ui.cleanup.CleanUpOptions;
72import org.eclipse.jdt.ui.cleanup.ICleanUpFix;
73import org.eclipse.jdt.ui.text.java.IProblemLocation;
74
75import org.eclipse.jdt.internal.ui.fix.UnusedCodeCleanUp;
76import org.eclipse.jdt.internal.ui.text.correction.JavadocTagsSubProcessor;
77import org.eclipse.jdt.internal.ui.text.correction.ProblemLocation;
78import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
79
80/**
81 * Fix which removes unused code.
82 */
83public class UnusedCodeFix extends CompilationUnitRewriteOperationsFix {
84
85 private static class SideEffectFinder extends ASTVisitor {
86
87 private final ArrayList<Expression> fSideEffectNodes;
88
89 public SideEffectFinder(ArrayList<Expression> res) {
90 fSideEffectNodes= res;
91 }
92
93 @Override
94 public boolean visit(Assignment node) {
95 fSideEffectNodes.add(node);
96 return false;
97 }
98
99 @Override
100 public boolean visit(PostfixExpression node) {
101 fSideEffectNodes.add(node);
102 return false;
103 }
104
105 @Override
106 public boolean visit(PrefixExpression node) {
107 Object operator= node.getOperator();
108 if (operator == PrefixExpression.Operator.INCREMENT || operator == PrefixExpression.Operator.DECREMENT) {
109 fSideEffectNodes.add(node);
110 }
111 return false;
112 }
113
114 @Override
115 public boolean visit(MethodInvocation node) {
116 fSideEffectNodes.add(node);
117 return false;
118 }
119
120 @Override
121 public boolean visit(ClassInstanceCreation node) {
122 fSideEffectNodes.add(node);
123 return false;
124 }
125
126 @Override
127 public boolean visit(SuperMethodInvocation node) {
128 fSideEffectNodes.add(node);
129 return false;
130 }
131 }
132
133 private static class RemoveImportOperation extends CompilationUnitRewriteOperation {
134
135 private final ImportDeclaration fImportDeclaration;
136
137 public RemoveImportOperation(ImportDeclaration importDeclaration) {
138 fImportDeclaration= importDeclaration;
139 }
140
141 /**
142 * {@inheritDoc}
143 */
144 @Override
145 public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException {
146 ImportDeclaration node= fImportDeclaration;
147 TextEditGroup group= createTextEditGroup(FixMessages.UnusedCodeFix_RemoveImport_description, cuRewrite);
148 cuRewrite.getASTRewrite().remove(node, group);
149 }
150
151 }
152
153 private static class RemoveUnusedMemberOperation extends CompilationUnitRewriteOperation {
154
155 private final SimpleName[] fUnusedNames;
156 private boolean fForceRemove;
157 private int fRemovedAssignmentsCount;
158 private int fAlteredAssignmentsCount;
159
160 public RemoveUnusedMemberOperation(SimpleName[] unusedNames, boolean removeAllAsignements) {
161 fUnusedNames= unusedNames;
162 fForceRemove= removeAllAsignements;
163 }
164
165 /**
166 * {@inheritDoc}
167 */
168 @Override
169 public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException {
170 for (int i= 0; i < fUnusedNames.length; i++) {
171 removeUnusedName(cuRewrite, fUnusedNames[i]);
172 }
173 }
174
175 private void removeUnusedName(CompilationUnitRewrite cuRewrite, SimpleName simpleName) {
176 ASTRewrite rewrite= cuRewrite.getASTRewrite();
177 CompilationUnit completeRoot= cuRewrite.getRoot();
178
179 IBinding binding= simpleName.resolveBinding();
180 CompilationUnit root= (CompilationUnit) simpleName.getRoot();
181 String displayString= getDisplayString(binding);
182 TextEditGroup group= createTextEditGroup(displayString, cuRewrite);
183 if (binding.getKind() == IBinding.METHOD) {
184 IMethodBinding decl= ((IMethodBinding) binding).getMethodDeclaration();
185 ASTNode declaration= root.findDeclaringNode(decl);
186 rewrite.remove(declaration, group);
187 } else if (binding.getKind() == IBinding.TYPE) {
188 ITypeBinding decl= ((ITypeBinding) binding).getTypeDeclaration();
189 ASTNode declaration= root.findDeclaringNode(decl);
190 if (declaration.getParent() instanceof TypeDeclarationStatement) {
191 declaration= declaration.getParent();
192 }
193 rewrite.remove(declaration, group);
194 } else if (binding.getKind() == IBinding.VARIABLE) {
195 SimpleName nameNode= (SimpleName) NodeFinder.perform(completeRoot, simpleName.getStartPosition(), simpleName.getLength());
196 SimpleName[] references= LinkedNodeFinder.findByBinding(completeRoot, nameNode.resolveBinding());
197 for (int i= 0; i < references.length; i++) {
198 removeVariableReferences(rewrite, references[i], group);
199 }
200
201 IVariableBinding bindingDecl= ((IVariableBinding) nameNode.resolveBinding()).getVariableDeclaration();
202 ASTNode declaringNode= completeRoot.findDeclaringNode(bindingDecl);
203 if (declaringNode instanceof SingleVariableDeclaration) {
204 removeParamTag(rewrite, (SingleVariableDeclaration) declaringNode, group);
205 }
206 } else {
207 // unexpected
208 }
209 }
210
211 private String getDisplayString(IBinding binding) {
212 switch (binding.getKind()) {
213 case IBinding.TYPE:
214 return FixMessages.UnusedCodeFix_RemoveUnusedType_description;
215 case IBinding.METHOD:
216 if (((IMethodBinding) binding).isConstructor()) {
217 return FixMessages.UnusedCodeFix_RemoveUnusedConstructor_description;
218 } else {
219 return FixMessages.UnusedCodeFix_RemoveUnusedPrivateMethod_description;
220 }
221 case IBinding.VARIABLE:
222 if (((IVariableBinding) binding).isField()) {
223 return FixMessages.UnusedCodeFix_RemoveUnusedField_description;
224 } else {
225 return FixMessages.UnusedCodeFix_RemoveUnusedVariabl_description;
226 }
227 default:
228 return ""; //$NON-NLS-1$
229 }
230 }
231
232 private void removeParamTag(ASTRewrite rewrite, SingleVariableDeclaration varDecl, TextEditGroup group) {
233 if (varDecl.getParent() instanceof MethodDeclaration) {
234 Javadoc javadoc= ((MethodDeclaration) varDecl.getParent()).getJavadoc();
235 if (javadoc != null) {
236 TagElement tagElement= JavadocTagsSubProcessor.findParamTag(javadoc, varDecl.getName().getIdentifier());
237 if (tagElement != null) {
238 rewrite.remove(tagElement, group);
239 }
240 }
241 }
242 }
243
244 /**
245 * Remove the field or variable declaration including the initializer.
246 * @param rewrite the AST rewriter to use
247 * @param reference a reference to the variable to remove
248 * @param group the text edit group to use
249 */
250 private void removeVariableReferences(ASTRewrite rewrite, SimpleName reference, TextEditGroup group) {
251 ASTNode parent= reference.getParent();
252 while (parent instanceof QualifiedName) {
253 parent= parent.getParent();
254 }
255 if (parent instanceof FieldAccess) {
256 parent= parent.getParent();
257 }
258
259 int nameParentType= parent.getNodeType();
260 if (nameParentType == ASTNode.ASSIGNMENT) {
261 Assignment assignment= (Assignment) parent;
262 Expression rightHand= assignment.getRightHandSide();
263
264 ASTNode assignParent= assignment.getParent();
265 if (assignParent.getNodeType() == ASTNode.EXPRESSION_STATEMENT && rightHand.getNodeType() != ASTNode.ASSIGNMENT) {
266 removeVariableWithInitializer(rewrite, rightHand, assignParent, group);
267 } else {
268 rewrite.replace(assignment, rewrite.createCopyTarget(rightHand), group);
269 }
270 } else if (nameParentType == ASTNode.SINGLE_VARIABLE_DECLARATION) {
271 rewrite.remove(parent, group);
272 } else if (nameParentType == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
273 VariableDeclarationFragment frag= (VariableDeclarationFragment) parent;
274 ASTNode varDecl= frag.getParent();
275 List<VariableDeclarationFragment> fragments;
276 if (varDecl instanceof VariableDeclarationExpression) {
277 fragments= ((VariableDeclarationExpression) varDecl).fragments();
278 } else if (varDecl instanceof FieldDeclaration) {
279 fragments= ((FieldDeclaration) varDecl).fragments();
280 } else {
281 fragments= ((VariableDeclarationStatement) varDecl).fragments();
282 }
283 Expression initializer = frag.getInitializer();
284 if (initializer instanceof CastExpression) {
285 initializer= ((CastExpression) initializer).getExpression();
286 }
287 boolean sideEffectInitializer = initializer instanceof MethodInvocation || initializer instanceof ClassInstanceCreation;
288 if (fragments.size() == fUnusedNames.length) {
289 if (fForceRemove) {
290 rewrite.remove(varDecl, group);
291 return;
292 }
293 if (parent.getParent() instanceof FieldDeclaration) {
294 rewrite.remove(varDecl, group);
295 return;
296 }
297 if (sideEffectInitializer){
298 Expression movedInit = (Expression) rewrite.createMoveTarget(initializer);
299 ExpressionStatement wrapped = rewrite.getAST().newExpressionStatement(movedInit);
300 rewrite.replace(varDecl, wrapped, group);
301 } else {
302 rewrite.remove(varDecl, group);
303 }
304 } else {
305 if (fForceRemove) {
306 rewrite.remove(frag, group);
307 return;
308 }
309 //multiple declarations in one line
310 ASTNode declaration = parent.getParent();
311 if (declaration instanceof FieldDeclaration) {
312 rewrite.remove(frag, group);
313 return;
314 }
315 if (declaration instanceof VariableDeclarationStatement) {
316 ASTNode lst = declaration.getParent();
317 ListRewrite listRewrite= null;
318 if (lst instanceof Block) {
319 listRewrite= rewrite.getListRewrite(lst, Block.STATEMENTS_PROPERTY);
320 } else if (lst instanceof SwitchStatement) {
321 listRewrite= rewrite.getListRewrite(lst, SwitchStatement.STATEMENTS_PROPERTY);
322 } else {
323 Assert.isTrue(false);
324 }
325 splitUpDeclarations(rewrite, group, frag, listRewrite, (VariableDeclarationStatement) declaration);
326 rewrite.remove(frag, group);
327 return;
328 }
329 if (declaration instanceof VariableDeclarationExpression) {
330 //keep constructors and method invocations
331 if (!sideEffectInitializer){
332 rewrite.remove(frag, group);
333 }
334 }
335 }
336 } else if (nameParentType == ASTNode.POSTFIX_EXPRESSION || nameParentType == ASTNode.PREFIX_EXPRESSION) {
337 Expression expression= (Expression)parent;
338 ASTNode expressionParent= expression.getParent();
339 if (expressionParent.getNodeType() == ASTNode.EXPRESSION_STATEMENT) {
340 removeStatement(rewrite, expressionParent, group);
341 } else {
342 rewrite.remove(expression, group);
343 }
344 }
345 }
346
347 private void splitUpDeclarations(ASTRewrite rewrite, TextEditGroup group, VariableDeclarationFragment frag, ListRewrite statementRewrite, VariableDeclarationStatement originalStatement) {
348 Expression initializer = frag.getInitializer();
349 //keep constructors and method invocations
350 if (initializer instanceof MethodInvocation || initializer instanceof ClassInstanceCreation){
351 Expression movedInitializer= (Expression) rewrite.createMoveTarget(initializer);
352
353 ExpressionStatement newInitializer= rewrite.getAST().newExpressionStatement( movedInitializer);
354 statementRewrite.insertAfter(newInitializer, originalStatement, group);
355
356 VariableDeclarationStatement newDeclaration= null;
357 List<VariableDeclarationFragment> fragments= originalStatement.fragments();
358 int fragIndex= fragments.indexOf(frag);
359 ListIterator<VariableDeclarationFragment> fragmentIterator= fragments.listIterator(fragIndex+1);
360 while (fragmentIterator.hasNext()) {
361 VariableDeclarationFragment currentFragment= fragmentIterator.next();
362 VariableDeclarationFragment movedFragment= (VariableDeclarationFragment) rewrite.createMoveTarget(currentFragment);
363 if (newDeclaration == null) {
364 newDeclaration= rewrite.getAST().newVariableDeclarationStatement(movedFragment);
365 Type copiedType= (Type) rewrite.createCopyTarget(originalStatement.getType());
366 newDeclaration.setType(copiedType);
367 } else
368 newDeclaration.fragments().add(movedFragment);
369 }
370 if (newDeclaration != null){
371 statementRewrite.insertAfter(newDeclaration, newInitializer, group);
372 }
373 if (originalStatement.fragments().size() == newDeclaration.fragments().size() + 1){
374 rewrite.remove(originalStatement, group);
375 }
376 }
377 }
378
379 private void removeVariableWithInitializer(ASTRewrite rewrite, ASTNode initializerNode, ASTNode statementNode, TextEditGroup group) {
380 boolean performRemove= fForceRemove;
381 if (!performRemove) {
382 ArrayList<Expression> sideEffectNodes= new ArrayList<Expression>();
383 initializerNode.accept(new SideEffectFinder(sideEffectNodes));
384 performRemove= sideEffectNodes.isEmpty();
385 }
386 if (performRemove) {
387 removeStatement(rewrite, statementNode, group);
388 fRemovedAssignmentsCount++;
389 } else {
390 ASTNode initNode = rewrite.createMoveTarget(initializerNode);
391 ExpressionStatement statement = rewrite.getAST().newExpressionStatement((Expression) initNode);
392 rewrite.replace(statementNode, statement, null);
393 fAlteredAssignmentsCount++;
394 }
395 }
396
397 private void removeStatement(ASTRewrite rewrite, ASTNode statementNode, TextEditGroup group) {
398 if (ASTNodes.isControlStatementBody(statementNode.getLocationInParent())) {
399 rewrite.replace(statementNode, rewrite.getAST().newBlock(), group);
400 } else {
401 rewrite.remove(statementNode, group);
402 }
403 }
404
405 @Override
406 public String getAdditionalInfo() {
407 StringBuffer sb=new StringBuffer();
408 if (fRemovedAssignmentsCount == 1) {
409 sb.append(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_RemovedAssignments_preview_singular);
410 } else if (fRemovedAssignmentsCount > 1) {
411 sb.append(Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_RemovedAssignments_preview_plural, String.valueOf(fRemovedAssignmentsCount)));
412 }
413 if (fAlteredAssignmentsCount == 1) {
414 sb.append(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_AlteredAssignments_preview_singular);
415 } else if (fAlteredAssignmentsCount > 1) {
416 sb.append(Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_AlteredAssignments_preview_plural, String.valueOf(fAlteredAssignmentsCount)));
417 }
418 if (sb.length()>0) {
419 return sb.toString();
420 } else
421 return null;
422 }
423 }
424
425 private static class RemoveCastOperation extends CompilationUnitRewriteOperation {
426
427 private final CastExpression fCast;
428
429 public RemoveCastOperation(CastExpression cast) {
430 fCast= cast;
431 }
432
433 /**
434 * {@inheritDoc}
435 */
436 @Override
437 public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException {
438
439 TextEditGroup group= createTextEditGroup(FixMessages.UnusedCodeFix_RemoveCast_description, cuRewrite);
440
441 ASTRewrite rewrite= cuRewrite.getASTRewrite();
442
443 CastExpression cast= fCast;
444 Expression expression= cast.getExpression();
445 if (expression instanceof ParenthesizedExpression) {
446 Expression childExpression= ((ParenthesizedExpression) expression).getExpression();
447 if (NecessaryParenthesesChecker.needsParentheses(childExpression, cast, CastExpression.EXPRESSION_PROPERTY)) {
448 expression= childExpression;
449 }
450 }
451
452 replaceCast(cast, expression, rewrite, group);
453 }
454 }
455
456 private static class RemoveAllCastOperation extends CompilationUnitRewriteOperation {
457
458 private final LinkedHashSet<CastExpression> fUnnecessaryCasts;
459
460 public RemoveAllCastOperation(LinkedHashSet<CastExpression> unnecessaryCasts) {
461 fUnnecessaryCasts= unnecessaryCasts;
462 }
463
464 /**
465 * {@inheritDoc}
466 */
467 @Override
468 public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModel model) throws CoreException {
469 ASTRewrite rewrite= cuRewrite.getASTRewrite();
470
471 TextEditGroup group= createTextEditGroup(FixMessages.UnusedCodeFix_RemoveCast_description, cuRewrite);
472
473 while (fUnnecessaryCasts.size() > 0) {
474 CastExpression castExpression= fUnnecessaryCasts.iterator().next();
475 fUnnecessaryCasts.remove(castExpression);
476
477 /*
478 * ASTRewrite doesn't allow replacing (deleting) of moved nodes. To solve problems
479 * with nested casts, we need to replace all casts at once.
480 *
481 * The loop proceeds downwards to find the innermost expression that stays in the result (downChild)
482 * and it also skips necessary parentheses.
483 */
484 CastExpression down= castExpression;
485 Expression downChild= down.getExpression();
486 while (true) {
487 if (fUnnecessaryCasts.contains(downChild)) {
488 down= (CastExpression) downChild;
489 fUnnecessaryCasts.remove(down);
490 downChild= down.getExpression();
491 } else if (downChild instanceof ParenthesizedExpression) {
492 Expression downChildExpression= ((ParenthesizedExpression) downChild).getExpression();
493 // is it justified that downChild is a ParenthesizedExpression?
494 if (NecessaryParenthesesChecker.needsParentheses(downChildExpression, down, CastExpression.EXPRESSION_PROPERTY)) {
495 // yes => continue walking down
496 downChild= downChildExpression;
497 } else {
498 // no => stop walking
499 break;
500 }
501 } else {
502 break;
503 }
504 }
505
506 // downChild is the innermost CastExpression's expression, stripped of a necessary surrounding ParenthesizedExpression
507 // Move either downChild (if it doesn't need parentheses), or a parenthesized version if necessary
508
509 replaceCast(castExpression, downChild, rewrite, group);
510 }
511 }
512 }
513
514 public static UnusedCodeFix createRemoveUnusedImportFix(CompilationUnit compilationUnit, IProblemLocation problem) {
515 if (isUnusedImport(problem)) {
516 ImportDeclaration node= getImportDeclaration(problem, compilationUnit);
517 if (node != null) {
518 String label= FixMessages.UnusedCodeFix_RemoveImport_description;
519 RemoveImportOperation operation= new RemoveImportOperation(node);
520 Map<String, String> options= new Hashtable<String, String>();
521 options.put(CleanUpConstants.REMOVE_UNUSED_CODE_IMPORTS, CleanUpOptions.TRUE);
522 return new UnusedCodeFix(label, compilationUnit, new CompilationUnitRewriteOperation[] {operation}, options);
523 }
524 }
525 return null;
526 }
527
528 public static boolean isUnusedImport(IProblemLocation problem) {
529 int id= problem.getProblemId();
530 return id == IProblem.UnusedImport || id == IProblem.DuplicateImport || id == IProblem.ConflictingImport || id == IProblem.CannotImportPackage || id == IProblem.ImportNotFound;
531 }
532
533 public static UnusedCodeFix createUnusedMemberFix(CompilationUnit compilationUnit, IProblemLocation problem, boolean removeAllAssignements) {
534 if (isUnusedMember(problem)) {
535 SimpleName name= getUnusedName(compilationUnit, problem);
536 if (name != null) {
537 IBinding binding= name.resolveBinding();
538 if (binding != null) {
539 if (isFormalParameterInEnhancedForStatement(name))
540 return null;
541
542 String label= getDisplayString(name, binding, removeAllAssignements);
543 RemoveUnusedMemberOperation operation= new RemoveUnusedMemberOperation(new SimpleName[] { name }, removeAllAssignements);
544 return new UnusedCodeFix(label, compilationUnit, new CompilationUnitRewriteOperation[] { operation }, getCleanUpOptions(binding, removeAllAssignements));
545 }
546 }
547 }
548 return null;
549 }
550
551 public static boolean isUnusedMember(IProblemLocation problem) {
552 int id= problem.getProblemId();
553 return id == IProblem.UnusedPrivateMethod || id == IProblem.UnusedPrivateConstructor || id == IProblem.UnusedPrivateField || id == IProblem.UnusedPrivateType
554 || id == IProblem.LocalVariableIsNeverUsed || id == IProblem.ArgumentIsNeverUsed;
555 }
556
557 public static UnusedCodeFix createRemoveUnusedCastFix(CompilationUnit compilationUnit, IProblemLocation problem) {
558 if (problem.getProblemId() != IProblem.UnnecessaryCast)
559 return null;
560
561 ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
562
563 ASTNode curr= selectedNode;
564 while (curr instanceof ParenthesizedExpression) {
565 curr= ((ParenthesizedExpression) curr).getExpression();
566 }
567
568 if (!(curr instanceof CastExpression))
569 return null;
570
571 return new UnusedCodeFix(FixMessages.UnusedCodeFix_RemoveCast_description, compilationUnit, new CompilationUnitRewriteOperation[] {new RemoveCastOperation((CastExpression)curr)});
572 }
573
574 public static ICleanUpFix createCleanUp(CompilationUnit compilationUnit,
575 boolean removeUnusedPrivateMethods,
576 boolean removeUnusedPrivateConstructors,
577 boolean removeUnusedPrivateFields,
578 boolean removeUnusedPrivateTypes,
579 boolean removeUnusedLocalVariables,
580 boolean removeUnusedImports,
581 boolean removeUnusedCast) {
582
583 IProblem[] problems= compilationUnit.getProblems();
584 IProblemLocation[] locations= new IProblemLocation[problems.length];
585 for (int i= 0; i < problems.length; i++) {
586 locations[i]= new ProblemLocation(problems[i]);
587 }
588
589 return createCleanUp(compilationUnit, locations,
590 removeUnusedPrivateMethods,
591 removeUnusedPrivateConstructors,
592 removeUnusedPrivateFields,
593 removeUnusedPrivateTypes,
594 removeUnusedLocalVariables,
595 removeUnusedImports,
596 removeUnusedCast);
597 }
598
599 public static ICleanUpFix createCleanUp(CompilationUnit compilationUnit, IProblemLocation[] problems,
600 boolean removeUnusedPrivateMethods,
601 boolean removeUnusedPrivateConstructors,
602 boolean removeUnusedPrivateFields,
603 boolean removeUnusedPrivateTypes,
604 boolean removeUnusedLocalVariables,
605 boolean removeUnusedImports,
606 boolean removeUnusedCast) {
607
608 List<CompilationUnitRewriteOperation> result= new ArrayList<CompilationUnitRewriteOperation>();
609 Hashtable<ASTNode, List<SimpleName>> variableDeclarations= new Hashtable<ASTNode, List<SimpleName>>();
610 LinkedHashSet<CastExpression> unnecessaryCasts= new LinkedHashSet<CastExpression>();
611 for (int i= 0; i < problems.length; i++) {
612 IProblemLocation problem= problems[i];
613 int id= problem.getProblemId();
614
615 if (removeUnusedImports && (id == IProblem.UnusedImport || id == IProblem.DuplicateImport || id == IProblem.ConflictingImport ||
616 id == IProblem.CannotImportPackage || id == IProblem.ImportNotFound))
617 {
618 ImportDeclaration node= UnusedCodeFix.getImportDeclaration(problem, compilationUnit);
619 if (node != null) {
620 result.add(new RemoveImportOperation(node));
621 }
622 }
623
624 if ((removeUnusedPrivateMethods && id == IProblem.UnusedPrivateMethod) || (removeUnusedPrivateConstructors && id == IProblem.UnusedPrivateConstructor) ||
625 (removeUnusedPrivateTypes && id == IProblem.UnusedPrivateType)) {
626
627 SimpleName name= getUnusedName(compilationUnit, problem);
628 if (name != null) {
629 IBinding binding= name.resolveBinding();
630 if (binding != null) {
631 result.add(new RemoveUnusedMemberOperation(new SimpleName[] {name}, false));
632 }
633 }
634 }
635
636 if ((removeUnusedLocalVariables && id == IProblem.LocalVariableIsNeverUsed) || (removeUnusedPrivateFields && id == IProblem.UnusedPrivateField)) {
637 SimpleName name= getUnusedName(compilationUnit, problem);
638 if (name != null) {
639 IBinding binding= name.resolveBinding();
640 if (binding instanceof IVariableBinding && !isFormalParameterInEnhancedForStatement(name) && (!((IVariableBinding) binding).isField() || isSideEffectFree(name, compilationUnit))) {
641 VariableDeclarationFragment parent= (VariableDeclarationFragment) ASTNodes.getParent(name, VariableDeclarationFragment.class);
642 if (parent != null) {
643 ASTNode varDecl= parent.getParent();
644 if (!variableDeclarations.containsKey(varDecl)) {
645 variableDeclarations.put(varDecl, new ArrayList<SimpleName>());
646 }
647 variableDeclarations.get(varDecl).add(name);
648 } else {
649 result.add(new RemoveUnusedMemberOperation(new SimpleName[] {name}, false));
650 }
651 }
652 }
653 }
654
655 if (removeUnusedCast && id == IProblem.UnnecessaryCast) {
656 ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
657
658 ASTNode curr= selectedNode;
659 while (curr instanceof ParenthesizedExpression) {
660 curr= ((ParenthesizedExpression) curr).getExpression();
661 }
662
663 if (curr instanceof CastExpression) {
664 unnecessaryCasts.add((CastExpression) curr);
665 }
666 }
667 }
668 for (Iterator<ASTNode> iter= variableDeclarations.keySet().iterator(); iter.hasNext();) {
669 ASTNode node= iter.next();
670 List<SimpleName> names= variableDeclarations.get(node);
671 result.add(new RemoveUnusedMemberOperation(names.toArray(new SimpleName[names.size()]), false));
672 }
673 if (unnecessaryCasts.size() > 0)
674 result.add(new RemoveAllCastOperation(unnecessaryCasts));
675
676 if (result.size() == 0)
677 return null;
678
679 return new UnusedCodeFix(FixMessages.UnusedCodeFix_change_name, compilationUnit, result.toArray(new CompilationUnitRewriteOperation[result.size()]));
680 }
681
682 private static boolean isFormalParameterInEnhancedForStatement(SimpleName name) {
683 return name.getParent() instanceof SingleVariableDeclaration && name.getParent().getLocationInParent() == EnhancedForStatement.PARAMETER_PROPERTY;
684 }
685
686 private static boolean isSideEffectFree(SimpleName simpleName, CompilationUnit completeRoot) {
687 SimpleName nameNode= (SimpleName) NodeFinder.perform(completeRoot, simpleName.getStartPosition(), simpleName.getLength());
688 SimpleName[] references= LinkedNodeFinder.findByBinding(completeRoot, nameNode.resolveBinding());
689 for (int i= 0; i < references.length; i++) {
690 if (hasSideEffect(references[i]))
691 return false;
692 }
693 return true;
694 }
695
696 private static boolean hasSideEffect(SimpleName reference) {
697 ASTNode parent= reference.getParent();
698 while (parent instanceof QualifiedName) {
699 parent= parent.getParent();
700 }
701 if (parent instanceof FieldAccess) {
702 parent= parent.getParent();
703 }
704
705 ASTNode node= null;
706 int nameParentType= parent.getNodeType();
707 if (nameParentType == ASTNode.ASSIGNMENT) {
708 Assignment assignment= (Assignment) parent;
709 node= assignment.getRightHandSide();
710 } else if (nameParentType == ASTNode.SINGLE_VARIABLE_DECLARATION) {
711 SingleVariableDeclaration decl= (SingleVariableDeclaration)parent;
712 node= decl.getInitializer();
713 if (node == null)
714 return false;
715 } else if (nameParentType == ASTNode.VARIABLE_DECLARATION_FRAGMENT) {
716 node= parent;
717 } else {
718 return false;
719 }
720
721 ArrayList<Expression> sideEffects= new ArrayList<Expression>();
722 node.accept(new SideEffectFinder(sideEffects));
723 return sideEffects.size() > 0;
724 }
725
726 private static SimpleName getUnusedName(CompilationUnit compilationUnit, IProblemLocation problem) {
727 ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
728
729 if (selectedNode instanceof MethodDeclaration) {
730 return ((MethodDeclaration) selectedNode).getName();
731 } else if (selectedNode instanceof SimpleName) {
732 return (SimpleName) selectedNode;
733 }
734
735 return null;
736 }
737
738 private static String getDisplayString(SimpleName simpleName, IBinding binding, boolean removeAllAssignements) {
739 String name= BasicElementLabels.getJavaElementName(simpleName.getIdentifier());
740 switch (binding.getKind()) {
741 case IBinding.TYPE:
742 return Messages.format(FixMessages.UnusedCodeFix_RemoveType_description, name);
743 case IBinding.METHOD:
744 if (((IMethodBinding) binding).isConstructor()) {
745 return Messages.format(FixMessages.UnusedCodeFix_RemoveConstructor_description, name);
746 } else {
747 return Messages.format(FixMessages.UnusedCodeFix_RemoveMethod_description, name);
748 }
749 case IBinding.VARIABLE:
750 if (removeAllAssignements) {
751 return Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocalWithInitializer_description, name);
752 } else {
753 return Messages.format(FixMessages.UnusedCodeFix_RemoveFieldOrLocal_description, name);
754 }
755 default:
756 return ""; //$NON-NLS-1$
757 }
758 }
759
760 private static Map<String, String> getCleanUpOptions(IBinding binding, boolean removeAll) {
761 Map<String, String> result= new Hashtable<String, String>();
762
763 result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_MEMBERS, CleanUpOptions.TRUE);
764 switch (binding.getKind()) {
765 case IBinding.TYPE:
766 result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_TYPES, CleanUpOptions.TRUE);
767 break;
768 case IBinding.METHOD:
769 if (((IMethodBinding) binding).isConstructor()) {
770 result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_CONSTRUCTORS, CleanUpOptions.TRUE);
771 } else {
772 result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_METHODS, CleanUpOptions.TRUE);
773 }
774 break;
775 case IBinding.VARIABLE:
776 if (removeAll)
777 return null;
778
779 result.put(CleanUpConstants.REMOVE_UNUSED_CODE_PRIVATE_FELDS, CleanUpOptions.TRUE);
780 result.put(CleanUpConstants.REMOVE_UNUSED_CODE_LOCAL_VARIABLES, CleanUpOptions.TRUE);
781 break;
782 }
783
784 return result;
785 }
786
787 private static ImportDeclaration getImportDeclaration(IProblemLocation problem, CompilationUnit compilationUnit) {
788 ASTNode selectedNode= problem.getCoveringNode(compilationUnit);
789 if (selectedNode != null) {
790 ASTNode node= ASTNodes.getParent(selectedNode, ASTNode.IMPORT_DECLARATION);
791 if (node instanceof ImportDeclaration) {
792 return (ImportDeclaration)node;
793 }
794 }
795 return null;
796 }
797
798 private static void replaceCast(CastExpression castExpression, Expression replacement, ASTRewrite rewrite, TextEditGroup group) {
799 boolean castEnclosedInNecessaryParentheses= castExpression.getParent() instanceof ParenthesizedExpression
800 && NecessaryParenthesesChecker.needsParentheses(castExpression, castExpression.getParent().getParent(), castExpression.getParent().getLocationInParent());
801
802 ASTNode toReplace= castEnclosedInNecessaryParentheses ? castExpression.getParent() : castExpression;
803 ASTNode move;
804 if (NecessaryParenthesesChecker.needsParentheses(replacement, toReplace.getParent(), toReplace.getLocationInParent())) {
805 if (replacement.getParent() instanceof ParenthesizedExpression) {
806 move= rewrite.createMoveTarget(replacement.getParent());
807 } else if (castEnclosedInNecessaryParentheses) {
808 toReplace= castExpression;
809 move= rewrite.createMoveTarget(replacement);
810 } else {
811 ParenthesizedExpression parentheses= replacement.getAST().newParenthesizedExpression();
812 parentheses.setExpression((Expression) rewrite.createMoveTarget(replacement));
813 move= parentheses;
814 }
815 } else {
816 move= rewrite.createMoveTarget(replacement);
817 }
818 rewrite.replace(toReplace, move, group);
819 }
820
821 private final Map<String, String> fCleanUpOptions;
822
823 private UnusedCodeFix(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations) {
824 this(name, compilationUnit, fixRewriteOperations, null);
825 }
826
827 private UnusedCodeFix(String name, CompilationUnit compilationUnit, CompilationUnitRewriteOperation[] fixRewriteOperations, Map<String, String> options) {
828 super(name, compilationUnit, fixRewriteOperations);
829 fCleanUpOptions= options;
830 }
831
832 public UnusedCodeCleanUp getCleanUp() {
833 if (fCleanUpOptions == null)
834 return null;
835
836 return new UnusedCodeCleanUp(fCleanUpOptions);
837 }
838
839}