]>
Commit | Line | Data |
---|---|---|
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 | *******************************************************************************/ | |
11 | package org.eclipse.jdt.internal.corext.fix; | |
12 | ||
13 | import java.util.ArrayList; | |
14 | import java.util.Hashtable; | |
15 | import java.util.Iterator; | |
16 | import java.util.LinkedHashSet; | |
17 | import java.util.List; | |
18 | import java.util.ListIterator; | |
19 | import java.util.Map; | |
20 | ||
21 | import org.eclipse.core.runtime.Assert; | |
22 | import org.eclipse.core.runtime.CoreException; | |
23 | ||
24 | import org.eclipse.text.edits.TextEditGroup; | |
25 | ||
26 | import org.eclipse.jdt.core.compiler.IProblem; | |
27 | import org.eclipse.jdt.core.dom.ASTNode; | |
28 | import org.eclipse.jdt.core.dom.ASTVisitor; | |
29 | import org.eclipse.jdt.core.dom.Assignment; | |
30 | import org.eclipse.jdt.core.dom.Block; | |
31 | import org.eclipse.jdt.core.dom.CastExpression; | |
32 | import org.eclipse.jdt.core.dom.ClassInstanceCreation; | |
33 | import org.eclipse.jdt.core.dom.CompilationUnit; | |
34 | import org.eclipse.jdt.core.dom.EnhancedForStatement; | |
35 | import org.eclipse.jdt.core.dom.Expression; | |
36 | import org.eclipse.jdt.core.dom.ExpressionStatement; | |
37 | import org.eclipse.jdt.core.dom.FieldAccess; | |
38 | import org.eclipse.jdt.core.dom.FieldDeclaration; | |
39 | import org.eclipse.jdt.core.dom.IBinding; | |
40 | import org.eclipse.jdt.core.dom.IMethodBinding; | |
41 | import org.eclipse.jdt.core.dom.ITypeBinding; | |
42 | import org.eclipse.jdt.core.dom.IVariableBinding; | |
43 | import org.eclipse.jdt.core.dom.ImportDeclaration; | |
44 | import org.eclipse.jdt.core.dom.Javadoc; | |
45 | import org.eclipse.jdt.core.dom.MethodDeclaration; | |
46 | import org.eclipse.jdt.core.dom.MethodInvocation; | |
47 | import org.eclipse.jdt.core.dom.NodeFinder; | |
48 | import org.eclipse.jdt.core.dom.ParenthesizedExpression; | |
49 | import org.eclipse.jdt.core.dom.PostfixExpression; | |
50 | import org.eclipse.jdt.core.dom.PrefixExpression; | |
51 | import org.eclipse.jdt.core.dom.QualifiedName; | |
52 | import org.eclipse.jdt.core.dom.SimpleName; | |
53 | import org.eclipse.jdt.core.dom.SingleVariableDeclaration; | |
54 | import org.eclipse.jdt.core.dom.SuperMethodInvocation; | |
55 | import org.eclipse.jdt.core.dom.SwitchStatement; | |
56 | import org.eclipse.jdt.core.dom.TagElement; | |
57 | import org.eclipse.jdt.core.dom.Type; | |
58 | import org.eclipse.jdt.core.dom.TypeDeclarationStatement; | |
59 | import org.eclipse.jdt.core.dom.VariableDeclarationExpression; | |
60 | import org.eclipse.jdt.core.dom.VariableDeclarationFragment; | |
61 | import org.eclipse.jdt.core.dom.VariableDeclarationStatement; | |
62 | import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; | |
63 | import org.eclipse.jdt.core.dom.rewrite.ListRewrite; | |
64 | ||
65 | import org.eclipse.jdt.internal.corext.dom.ASTNodes; | |
66 | import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder; | |
67 | import org.eclipse.jdt.internal.corext.dom.NecessaryParenthesesChecker; | |
68 | import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; | |
69 | import org.eclipse.jdt.internal.corext.util.Messages; | |
70 | ||
71 | import org.eclipse.jdt.ui.cleanup.CleanUpOptions; | |
72 | import org.eclipse.jdt.ui.cleanup.ICleanUpFix; | |
73 | import org.eclipse.jdt.ui.text.java.IProblemLocation; | |
74 | ||
75 | import org.eclipse.jdt.internal.ui.fix.UnusedCodeCleanUp; | |
76 | import org.eclipse.jdt.internal.ui.text.correction.JavadocTagsSubProcessor; | |
77 | import org.eclipse.jdt.internal.ui.text.correction.ProblemLocation; | |
78 | import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels; | |
79 | ||
80 | /** | |
81 | * Fix which removes unused code. | |
82 | */ | |
83 | public 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 | } |