1 /*******************************************************************************
2 * Copyright (c) 2000, 2011 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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.ui.search;
13 import java.util.ArrayList;
14 import java.util.Iterator;
15 import java.util.List;
17 import org.eclipse.jdt.core.dom.AST;
18 import org.eclipse.jdt.core.dom.ASTNode;
19 import org.eclipse.jdt.core.dom.ASTVisitor;
20 import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
21 import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
22 import org.eclipse.jdt.core.dom.Block;
23 import org.eclipse.jdt.core.dom.CatchClause;
24 import org.eclipse.jdt.core.dom.ClassInstanceCreation;
25 import org.eclipse.jdt.core.dom.CompilationUnit;
26 import org.eclipse.jdt.core.dom.ConstructorInvocation;
27 import org.eclipse.jdt.core.dom.EnumDeclaration;
28 import org.eclipse.jdt.core.dom.IMethodBinding;
29 import org.eclipse.jdt.core.dom.ITypeBinding;
30 import org.eclipse.jdt.core.dom.MethodDeclaration;
31 import org.eclipse.jdt.core.dom.MethodInvocation;
32 import org.eclipse.jdt.core.dom.Name;
33 import org.eclipse.jdt.core.dom.NodeFinder;
34 import org.eclipse.jdt.core.dom.ReturnStatement;
35 import org.eclipse.jdt.core.dom.SimpleName;
36 import org.eclipse.jdt.core.dom.Statement;
37 import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
38 import org.eclipse.jdt.core.dom.SuperMethodInvocation;
39 import org.eclipse.jdt.core.dom.ThrowStatement;
40 import org.eclipse.jdt.core.dom.TryStatement;
41 import org.eclipse.jdt.core.dom.Type;
42 import org.eclipse.jdt.core.dom.TypeDeclaration;
43 import org.eclipse.jdt.core.dom.UnionType;
44 import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
45 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
47 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
48 import org.eclipse.jdt.internal.corext.dom.Bindings;
49 import org.eclipse.jdt.internal.corext.dom.LocalVariableIndex;
50 import org.eclipse.jdt.internal.corext.refactoring.code.flow.FlowContext;
51 import org.eclipse.jdt.internal.corext.refactoring.code.flow.FlowInfo;
52 import org.eclipse.jdt.internal.corext.refactoring.code.flow.InOutFlowAnalyzer;
53 import org.eclipse.jdt.internal.corext.util.Messages;
55 import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
58 public class MethodExitsFinder extends ASTVisitor implements IOccurrencesFinder {
60 public static final String ID= "MethodExitsFinder"; //$NON-NLS-1$
62 private MethodDeclaration fMethodDeclaration;
63 private List<OccurrenceLocation> fResult;
64 private List<ITypeBinding> fCaughtExceptions;
65 private String fExitDescription;
66 private CompilationUnit fASTRoot;
68 public String initialize(CompilationUnit root, int offset, int length) {
69 return initialize(root, NodeFinder.perform(root, offset, length));
73 * @param root the AST root
74 * @param node the selected node
75 * @return returns a message if there is a problem
77 public String initialize(CompilationUnit root, ASTNode node) {
80 if (node instanceof ReturnStatement) {
81 fMethodDeclaration= (MethodDeclaration)ASTNodes.getParent(node, ASTNode.METHOD_DECLARATION);
82 if (fMethodDeclaration == null)
83 return SearchMessages.MethodExitsFinder_no_return_type_selected;
89 if (node instanceof Type) {
91 } else if (node instanceof Name) {
92 Name name= ASTNodes.getTopMostName((Name)node);
93 if (name.getParent() instanceof Type) {
94 type= (Type)name.getParent();
98 return SearchMessages.MethodExitsFinder_no_return_type_selected;
99 type= ASTNodes.getTopMostType(type);
100 if (!(type.getParent() instanceof MethodDeclaration))
101 return SearchMessages.MethodExitsFinder_no_return_type_selected;
102 fMethodDeclaration= (MethodDeclaration)type.getParent();
104 fExitDescription= Messages.format(SearchMessages.MethodExitsFinder_occurrence_exit_description, BasicElementLabels.getJavaElementName(fMethodDeclaration.getName().toString()));
108 private void performSearch() {
109 fResult= new ArrayList<OccurrenceLocation>();
111 if (!fResult.isEmpty()) {
112 Type returnType= fMethodDeclaration.getReturnType2();
113 if (returnType != null) {
114 String desc= Messages.format(SearchMessages.MethodExitsFinder_occurrence_return_description, BasicElementLabels.getJavaElementName(fMethodDeclaration.getName().toString()));
115 fResult.add(new OccurrenceLocation(returnType.getStartPosition(), returnType.getLength(), 0, desc));
120 public OccurrenceLocation[] getOccurrences() {
122 if (fResult.isEmpty())
125 return fResult.toArray(new OccurrenceLocation[fResult.size()]);
129 private void markReferences() {
130 fCaughtExceptions= new ArrayList<ITypeBinding>();
131 boolean isVoid= true;
132 Type returnType= fMethodDeclaration.getReturnType2();
133 if (returnType != null) {
134 ITypeBinding returnTypeBinding= returnType.resolveBinding();
135 isVoid= returnTypeBinding != null && Bindings.isVoidType(returnTypeBinding);
137 fMethodDeclaration.accept(this);
138 Block block= fMethodDeclaration.getBody();
140 List<Statement> statements= block.statements();
141 if (statements.size() > 0) {
142 Statement last= statements.get(statements.size() - 1);
143 int maxVariableId= LocalVariableIndex.perform(fMethodDeclaration);
144 FlowContext flowContext= new FlowContext(0, maxVariableId + 1);
145 flowContext.generated_4776247409396246388();
146 InOutFlowAnalyzer flowAnalyzer= new InOutFlowAnalyzer(flowContext);
147 FlowInfo info= flowAnalyzer.perform(new ASTNode[] {last});
148 if (!info.isNoReturn() && !isVoid) {
149 if (!info.isPartialReturn())
153 int offset= fMethodDeclaration.getStartPosition() + fMethodDeclaration.getLength() - 1; // closing bracket
154 fResult.add(new OccurrenceLocation(offset, 1, 0, fExitDescription));
159 public boolean visit(TypeDeclaration node) {
160 // Don't dive into a local type.
165 public boolean visit(AnonymousClassDeclaration node) {
166 // Don't dive into a local type.
171 public boolean visit(AnnotationTypeDeclaration node) {
172 // Don't dive into a local type.
177 public boolean visit(EnumDeclaration node) {
178 // Don't dive into a local type.
183 public boolean visit(ReturnStatement node) {
184 fResult.add(new OccurrenceLocation(node.getStartPosition(), node.getLength(), 0, fExitDescription));
185 return super.visit(node);
189 public boolean visit(TryStatement node) {
190 int currentSize= fCaughtExceptions.size();
191 List<CatchClause> catchClauses= node.catchClauses();
192 for (Iterator<CatchClause> iter= catchClauses.iterator(); iter.hasNext();) {
193 Type type= iter.next().getException().getType();
194 if (type instanceof UnionType) {
195 List<Type> types= ((UnionType) type).types();
196 for (Iterator<Type> iterator= types.iterator(); iterator.hasNext();) {
197 addCaughtException(iterator.next());
200 addCaughtException(type);
203 node.getBody().accept(this);
205 if (node.getAST().apiLevel() >= AST.JLS4) {
206 List<VariableDeclarationExpression> resources= node.resources();
207 for (Iterator<VariableDeclarationExpression> iterator= resources.iterator(); iterator.hasNext();) {
208 iterator.next().accept(this);
211 //check if the method could exit as a result of resource#close()
212 boolean exitMarked= false;
213 for (VariableDeclarationExpression variable : resources) {
214 Type type= variable.getType();
215 IMethodBinding methodBinding= Bindings.findMethodInHierarchy(type.resolveBinding(), "close", new ITypeBinding[0]); //$NON-NLS-1$
216 if (methodBinding != null) {
217 ITypeBinding[] exceptionTypes= methodBinding.getExceptionTypes();
218 for (int j= 0; j < exceptionTypes.length; j++) {
219 if (isExitPoint(exceptionTypes[j])) { // a close() throws an uncaught exception
220 // mark name of resource
221 for (VariableDeclarationFragment fragment : (List<VariableDeclarationFragment>) variable.fragments()) {
222 SimpleName name= fragment.getName();
223 fResult.add(new OccurrenceLocation(name.getStartPosition(), name.getLength(), 0, fExitDescription));
226 // mark exit position
228 Block body= node.getBody();
229 int offset= body.getStartPosition() + body.getLength() - 1; // closing bracket of try block
230 fResult.add(new OccurrenceLocation(offset, 1, 0, Messages.format(SearchMessages.MethodExitsFinder_occurrence_exit_impclict_close_description,
231 BasicElementLabels.getJavaElementName(fMethodDeclaration.getName().toString()))));
239 int toRemove= fCaughtExceptions.size() - currentSize;
240 for (int i= toRemove; i > 0; i--) {
241 fCaughtExceptions.remove(currentSize);
244 // visit catch and finally
245 for (Iterator<CatchClause> iter= catchClauses.iterator(); iter.hasNext();) {
246 iter.next().accept(this);
248 if (node.getFinally() != null)
249 node.getFinally().accept(this);
251 // return false. We have visited the body by ourselves.
255 private void addCaughtException(Type type) {
256 ITypeBinding typeBinding= type.resolveBinding();
257 if (typeBinding != null) {
258 fCaughtExceptions.add(typeBinding);
263 public boolean visit(ThrowStatement node) {
264 ITypeBinding exception= node.getExpression().resolveTypeBinding();
265 if (isExitPoint(exception)) {
267 fResult.add(new OccurrenceLocation(node.getStartPosition(), 5, 0, fExitDescription));
273 public boolean visit(MethodInvocation node) {
274 if (isExitPoint(node.resolveMethodBinding())) {
275 SimpleName name= node.getName();
276 fResult.add(new OccurrenceLocation(name.getStartPosition(), name.getLength(), 0, fExitDescription));
282 public boolean visit(SuperMethodInvocation node) {
283 if (isExitPoint(node.resolveMethodBinding())) {
284 SimpleName name= node.getName();
285 fResult.add(new OccurrenceLocation(name.getStartPosition(), name.getLength(), 0, fExitDescription));
291 public boolean visit(ClassInstanceCreation node) {
292 if (isExitPoint(node.resolveConstructorBinding())) {
293 Type name= node.getType();
294 fResult.add(new OccurrenceLocation(name.getStartPosition(), name.getLength(), 0, fExitDescription));
300 public boolean visit(ConstructorInvocation node) {
301 if (isExitPoint(node.resolveConstructorBinding())) {
303 fResult.add(new OccurrenceLocation(node.getStartPosition(), 4, 0, fExitDescription));
309 public boolean visit(SuperConstructorInvocation node) {
310 if (isExitPoint(node.resolveConstructorBinding())) {
312 fResult.add(new OccurrenceLocation(node.getStartPosition(), 5, 0, fExitDescription));
317 private boolean isExitPoint(ITypeBinding binding) {
320 return !isCaught(binding);
323 private boolean isExitPoint(IMethodBinding binding) {
326 ITypeBinding[] exceptions= binding.getExceptionTypes();
327 for (int i= 0; i < exceptions.length; i++) {
328 if (!isCaught(exceptions[i]))
334 private boolean isCaught(ITypeBinding binding) {
335 for (Iterator<ITypeBinding> iter= fCaughtExceptions.iterator(); iter.hasNext();) {
336 ITypeBinding catchException= iter.next();
337 if (catches(catchException, binding))
343 private boolean catches(ITypeBinding catchTypeBinding, ITypeBinding throwTypeBinding) {
344 while(throwTypeBinding != null) {
345 if (throwTypeBinding == catchTypeBinding)
347 throwTypeBinding= throwTypeBinding.getSuperclass();
352 public CompilationUnit getASTRoot() {
356 public String getElementName() {
357 return fMethodDeclaration.getName().getIdentifier();
360 public String getID() {
364 public String getJobLabel() {
365 return SearchMessages.MethodExitsFinder_job_label;
368 public int getSearchKind() {
369 return IOccurrencesFinder.K_BREAK_TARGET_OCCURRENCE;
372 public String getUnformattedPluralLabel() {
373 return SearchMessages.MethodExitsFinder_label_plural;
376 public String getUnformattedSingularLabel() {
377 return SearchMessages.MethodExitsFinder_label_singular;