2 * Copyright 2003-2008 Dave Griffith, Bas Leijdekkers
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.siyeh
.ig
.psiutils
;
18 import com
.intellij
.psi
.*;
19 import com
.intellij
.psi
.util
.PsiTreeUtil
;
20 import org
.jetbrains
.annotations
.NonNls
;
21 import org
.jetbrains
.annotations
.NotNull
;
22 import org
.jetbrains
.annotations
.Nullable
;
24 public class ControlFlowUtils
{
26 private ControlFlowUtils(){
30 public static boolean statementMayCompleteNormally(
31 @Nullable PsiStatement statement
){
32 if(statement
== null){
35 if(statement
instanceof PsiBreakStatement
||
36 statement
instanceof PsiContinueStatement
||
37 statement
instanceof PsiReturnStatement
||
38 statement
instanceof PsiThrowStatement
){
40 } else if(statement
instanceof PsiExpressionListStatement
||
41 statement
instanceof PsiEmptyStatement
||
42 statement
instanceof PsiAssertStatement
||
43 statement
instanceof PsiDeclarationStatement
){
45 } else if(statement
instanceof PsiExpressionStatement
){
46 final PsiExpressionStatement expressionStatement
=
47 (PsiExpressionStatement
)statement
;
48 final PsiExpression expression
=
49 expressionStatement
.getExpression();
50 if(!(expression
instanceof PsiMethodCallExpression
)){
53 final PsiMethodCallExpression methodCallExpression
=
54 (PsiMethodCallExpression
)expression
;
55 final PsiMethod method
= methodCallExpression
.resolveMethod();
59 @NonNls final String methodName
= method
.getName();
60 if(!methodName
.equals("exit")) {
63 final PsiClass aClass
= method
.getContainingClass();
64 final String className
= aClass
.getQualifiedName();
65 return !"java.lang.System".equals(className
);
66 } else if(statement
instanceof PsiForStatement
){
67 return forStatementMayReturnNormally((PsiForStatement
) statement
);
68 } else if(statement
instanceof PsiForeachStatement
){
69 return foreachStatementMayReturnNormally(
70 (PsiForeachStatement
) statement
);
71 } else if(statement
instanceof PsiWhileStatement
){
72 return whileStatementMayReturnNormally(
73 (PsiWhileStatement
) statement
);
74 } else if(statement
instanceof PsiDoWhileStatement
){
75 return doWhileStatementMayReturnNormally(
76 (PsiDoWhileStatement
) statement
);
77 } else if(statement
instanceof PsiSynchronizedStatement
){
78 final PsiCodeBlock body
=
79 ((PsiSynchronizedStatement
) statement
).getBody();
80 return codeBlockMayCompleteNormally(body
);
81 } else if(statement
instanceof PsiBlockStatement
){
82 final PsiCodeBlock codeBlock
=
83 ((PsiBlockStatement
) statement
).getCodeBlock();
84 return codeBlockMayCompleteNormally(codeBlock
);
85 } else if(statement
instanceof PsiLabeledStatement
){
86 return labeledStatementMayCompleteNormally(
87 (PsiLabeledStatement
) statement
);
88 } else if(statement
instanceof PsiIfStatement
){
89 return ifStatementMayReturnNormally((PsiIfStatement
) statement
);
90 } else if(statement
instanceof PsiTryStatement
){
91 return tryStatementMayReturnNormally((PsiTryStatement
) statement
);
92 } else if(statement
instanceof PsiSwitchStatement
){
93 return switchStatementMayReturnNormally(
94 (PsiSwitchStatement
) statement
);
96 // unknown statement type
101 private static boolean doWhileStatementMayReturnNormally(
102 @NotNull PsiDoWhileStatement loopStatement
){
103 final PsiExpression test
= loopStatement
.getCondition();
104 final PsiStatement body
= loopStatement
.getBody();
105 return statementMayCompleteNormally(body
) && !BoolUtils
.isTrue(test
)
106 || statementIsBreakTarget(loopStatement
);
109 private static boolean whileStatementMayReturnNormally(
110 @NotNull PsiWhileStatement loopStatement
){
111 final PsiExpression test
= loopStatement
.getCondition();
112 return !BoolUtils
.isTrue(test
)
113 || statementIsBreakTarget(loopStatement
);
116 private static boolean forStatementMayReturnNormally(
117 @NotNull PsiForStatement loopStatement
){
118 final PsiExpression test
= loopStatement
.getCondition();
119 if(statementIsBreakTarget(loopStatement
)){
125 return !BoolUtils
.isTrue(test
);
128 private static boolean foreachStatementMayReturnNormally(
129 @NotNull PsiForeachStatement loopStatement
){
133 private static boolean switchStatementMayReturnNormally(
134 @NotNull PsiSwitchStatement switchStatement
){
135 if(statementIsBreakTarget(switchStatement
)){
138 final PsiCodeBlock body
= switchStatement
.getBody();
142 final PsiStatement
[] statements
= body
.getStatements();
143 if(statements
.length
== 0){
147 boolean hasDefaultCase
= false;
148 for(PsiStatement statement
: statements
){
149 if(statement
instanceof PsiSwitchLabelStatement
){
150 final PsiSwitchLabelStatement switchLabelStatement
=
151 (PsiSwitchLabelStatement
)statement
;
152 if(switchLabelStatement
.isDefaultCase()){
153 hasDefaultCase
= true;
156 if(statement
instanceof PsiBreakStatement
) {
157 final PsiBreakStatement breakStatement
=
158 (PsiBreakStatement
)statement
;
159 if(breakStatement
.getLabelIdentifier() == null) {
165 final boolean isEnum
= isEnumSwitch(switchStatement
);
166 if(!hasDefaultCase
&& !isEnum
){
169 if(!hasDefaultCase
&& isEnum
){
170 final PsiExpression expression
= switchStatement
.getExpression();
171 if(expression
== null){
174 final PsiClassType type
= (PsiClassType
) expression
.getType();
178 final PsiClass aClass
= type
.resolve();
182 final PsiField
[] fields
= aClass
.getFields();
184 for(final PsiField field
: fields
){
185 final PsiType fieldType
= field
.getType();
186 if(fieldType
.equals(type
)){
190 if(numEnums
!= numCases
){
194 return statementMayCompleteNormally(statements
[statements
.length
- 1]);
197 private static boolean isEnumSwitch(PsiSwitchStatement statement
){
198 final PsiExpression expression
= statement
.getExpression();
199 if(expression
== null){
202 final PsiType type
= expression
.getType();
206 if(!(type
instanceof PsiClassType
)){
209 final PsiClass aClass
= ((PsiClassType
) type
).resolve();
213 return aClass
.isEnum();
216 private static boolean tryStatementMayReturnNormally(
217 @NotNull PsiTryStatement tryStatement
){
218 final PsiCodeBlock finallyBlock
= tryStatement
.getFinallyBlock();
219 if(finallyBlock
!= null){
220 if(!codeBlockMayCompleteNormally(finallyBlock
)){
224 final PsiCodeBlock tryBlock
= tryStatement
.getTryBlock();
225 if(codeBlockMayCompleteNormally(tryBlock
)){
228 final PsiCodeBlock
[] catchBlocks
= tryStatement
.getCatchBlocks();
229 for(final PsiCodeBlock catchBlock
: catchBlocks
){
230 if(codeBlockMayCompleteNormally(catchBlock
)){
237 private static boolean ifStatementMayReturnNormally(
238 @NotNull PsiIfStatement ifStatement
){
239 final PsiStatement thenBranch
= ifStatement
.getThenBranch();
240 if(statementMayCompleteNormally(thenBranch
)){
243 final PsiStatement elseBranch
= ifStatement
.getElseBranch();
244 return elseBranch
== null ||
245 statementMayCompleteNormally(elseBranch
);
248 private static boolean labeledStatementMayCompleteNormally(
249 @NotNull PsiLabeledStatement labeledStatement
){
250 final PsiStatement statement
= labeledStatement
.getStatement();
251 if (statement
== null) {
254 return statementMayCompleteNormally(statement
) ||
255 statementIsBreakTarget(statement
);
258 public static boolean codeBlockMayCompleteNormally(
259 @Nullable PsiCodeBlock block
){
263 final PsiStatement
[] statements
= block
.getStatements();
264 for(final PsiStatement statement
: statements
){
265 if(!statementMayCompleteNormally(statement
)){
272 private static boolean statementIsBreakTarget(
273 @NotNull PsiStatement statement
){
274 final BreakFinder breakFinder
= new BreakFinder(statement
);
275 statement
.accept(breakFinder
);
276 return breakFinder
.breakFound();
279 public static boolean statementContainsReturn(
280 @NotNull PsiStatement statement
){
281 final ReturnFinder returnFinder
= new ReturnFinder();
282 statement
.accept(returnFinder
);
283 return returnFinder
.returnFound();
286 public static boolean statementIsContinueTarget(
287 @NotNull PsiStatement statement
){
288 final ContinueFinder continueFinder
= new ContinueFinder(statement
);
289 statement
.accept(continueFinder
);
290 return continueFinder
.continueFound();
293 public static boolean statementContainsSystemExit(
294 @NotNull PsiStatement statement
){
295 final SystemExitFinder systemExitFinder
= new SystemExitFinder();
296 statement
.accept(systemExitFinder
);
297 return systemExitFinder
.exitFound();
300 public static boolean elementContainsCallToMethod(
301 PsiElement context
, String containingClassName
, PsiType returnType
,
302 String methodName
, PsiType
... parameterTypes
) {
303 final MethodCallFinder methodCallFinder
=
304 new MethodCallFinder(containingClassName
, returnType
,
305 methodName
, parameterTypes
);
306 context
.accept(methodCallFinder
);
307 return methodCallFinder
.containsCallToMethod();
310 public static boolean isInLoop(@NotNull PsiElement element
){
311 final PsiLoopStatement loopStatement
=
312 PsiTreeUtil
.getParentOfType(element
, PsiLoopStatement
.class);
313 if (loopStatement
== null){
316 final PsiStatement body
= loopStatement
.getBody();
320 return PsiTreeUtil
.isAncestor(body
, element
, true);
323 public static boolean isInFinallyBlock(@NotNull PsiElement element
){
324 PsiElement currentElement
= element
;
326 final PsiTryStatement tryStatement
=
327 PsiTreeUtil
.getParentOfType(currentElement
,
328 PsiTryStatement
.class);
329 if(tryStatement
== null){
332 final PsiCodeBlock finallyBlock
= tryStatement
.getFinallyBlock();
333 if(finallyBlock
!= null){
334 if(PsiTreeUtil
.isAncestor(finallyBlock
, currentElement
, true)){
335 final PsiMethod elementMethod
=
336 PsiTreeUtil
.getParentOfType(currentElement
,
338 final PsiMethod finallyMethod
=
339 PsiTreeUtil
.getParentOfType(finallyBlock
,
341 return elementMethod
!= null &&
342 elementMethod
.equals(finallyMethod
);
345 currentElement
= tryStatement
;
349 public static boolean isInCatchBlock(@NotNull PsiElement element
){
350 PsiElement currentElement
= element
;
352 final PsiTryStatement tryStatement
=
353 PsiTreeUtil
.getParentOfType(currentElement
,
354 PsiTryStatement
.class,
355 true, PsiClass
.class);
356 if(tryStatement
== null){
359 final PsiCodeBlock
[] catchBlocks
= tryStatement
.getCatchBlocks();
360 for(final PsiCodeBlock catchBlock
: catchBlocks
){
361 if(PsiTreeUtil
.isAncestor(catchBlock
, currentElement
, true)){
365 currentElement
= tryStatement
;
369 public static boolean isInExitStatement(@NotNull PsiExpression expression
){
370 return isInReturnStatementArgument(expression
) ||
371 isInThrowStatementArgument(expression
);
374 private static boolean isInReturnStatementArgument(
375 @NotNull PsiExpression expression
){
376 final PsiReturnStatement returnStatement
=
377 PsiTreeUtil
.getParentOfType(expression
,
378 PsiReturnStatement
.class);
379 return returnStatement
!= null;
382 private static boolean isInThrowStatementArgument(
383 @NotNull PsiExpression expression
){
384 final PsiThrowStatement throwStatement
=
385 PsiTreeUtil
.getParentOfType(expression
,
386 PsiThrowStatement
.class);
387 return throwStatement
!= null;
391 public static PsiStatement
stripBraces(@Nullable PsiStatement statement
){
392 if(statement
instanceof PsiBlockStatement
){
393 final PsiBlockStatement block
= (PsiBlockStatement
) statement
;
394 final PsiCodeBlock codeBlock
= block
.getCodeBlock();
395 final PsiStatement
[] statements
= codeBlock
.getStatements();
396 if(statements
.length
== 1){
397 return statements
[0];
406 public static boolean statementCompletesWithStatement(
407 @NotNull PsiStatement containingStatement
,
408 @NotNull PsiStatement statement
){
409 PsiElement statementToCheck
= statement
;
411 if(statementToCheck
.equals(containingStatement
)){
414 final PsiElement container
=
415 getContainingStatementOrBlock(statementToCheck
);
416 if(container
== null){
419 if(container
instanceof PsiCodeBlock
){
420 if(!statementIsLastInBlock((PsiCodeBlock
) container
,
421 (PsiStatement
) statementToCheck
)){
425 if(container
instanceof PsiLoopStatement
){
428 statementToCheck
= container
;
432 public static boolean blockCompletesWithStatement(
433 @NotNull PsiCodeBlock body
,
434 @NotNull PsiStatement statement
){
435 PsiElement statementToCheck
= statement
;
437 if(statementToCheck
== null){
440 final PsiElement container
=
441 getContainingStatementOrBlock(statementToCheck
);
442 if(container
== null){
445 if(container
instanceof PsiLoopStatement
){
448 if(container
instanceof PsiCodeBlock
){
449 if(!statementIsLastInBlock((PsiCodeBlock
) container
,
450 (PsiStatement
) statementToCheck
)){
453 if(container
.equals(body
)){
457 PsiTreeUtil
.getParentOfType(container
,
460 statementToCheck
= container
;
466 private static PsiElement
getContainingStatementOrBlock(
467 @NotNull PsiElement statement
){
468 return PsiTreeUtil
.getParentOfType(
469 statement
, PsiStatement
.class, PsiCodeBlock
.class);
472 private static boolean statementIsLastInBlock(
473 @NotNull PsiCodeBlock block
, @NotNull PsiStatement statement
) {
474 final PsiStatement
[] statements
= block
.getStatements();
475 for(int i
= statements
.length
- 1; i
>= 0; i
--){
476 final PsiStatement childStatement
= statements
[i
];
477 if(statement
.equals(childStatement
)){
480 if(!(statement
instanceof PsiEmptyStatement
)){
487 public static boolean methodAlwaysThrowsException(
488 @NotNull PsiMethod method
){
489 final PsiCodeBlock body
= method
.getBody();
493 final ReturnFinder returnFinder
= new ReturnFinder();
494 body
.accept(returnFinder
);
495 if(returnFinder
.returnFound()){
498 return !codeBlockMayCompleteNormally(body
);
501 private static class SystemExitFinder
extends JavaRecursiveElementVisitor
{
503 private boolean m_found
= false;
505 public boolean exitFound() {
509 @Override public void visitClass(@NotNull PsiClass aClass
) {
510 // do nothing to keep from drilling into inner classes
513 @Override public void visitMethodCallExpression(
514 @NotNull PsiMethodCallExpression expression
){
518 super.visitMethodCallExpression(expression
);
519 final PsiMethod method
= expression
.resolveMethod();
523 @NonNls final String methodName
= method
.getName();
524 if(!methodName
.equals("exit")) {
527 final PsiClass aClass
= method
.getContainingClass();
528 final String className
= aClass
.getQualifiedName();
529 if(!"java.lang.System".equals(className
) &&
530 !"java.lang.Runtime".equals(className
)) {
537 private static class ReturnFinder
extends JavaRecursiveElementVisitor
{
539 private boolean m_found
= false;
541 public boolean returnFound(){
545 @Override public void visitClass(@NotNull PsiClass psiClass
){
546 // do nothing, to keep drilling into inner classes
549 @Override public void visitReturnStatement(
550 @NotNull PsiReturnStatement returnStatement
){
554 super.visitReturnStatement(returnStatement
);
559 private static class BreakFinder
extends JavaRecursiveElementVisitor
{
561 private boolean m_found
= false;
562 private final PsiStatement m_target
;
564 private BreakFinder(@NotNull PsiStatement target
){
569 public boolean breakFound(){
573 @Override public void visitBreakStatement(
574 @NotNull PsiBreakStatement breakStatement
){
578 super.visitBreakStatement(breakStatement
);
579 final PsiStatement exitedStatement
=
580 breakStatement
.findExitedStatement();
581 if (exitedStatement
== null) {
584 if(PsiTreeUtil
.isAncestor(exitedStatement
, m_target
, false)){
590 private static class ContinueFinder
extends JavaRecursiveElementVisitor
{
592 private boolean m_found
= false;
593 private int m_nestingDepth
= 0;
594 private String m_label
= null;
596 private ContinueFinder(@NotNull PsiStatement target
){
598 if(target
.getParent() instanceof PsiLabeledStatement
){
599 final PsiLabeledStatement labeledStatement
=
600 (PsiLabeledStatement
) target
.getParent();
601 final PsiIdentifier identifier
=
602 labeledStatement
.getLabelIdentifier();
603 m_label
= identifier
.getText();
607 public boolean continueFound(){
611 @Override public void visitContinueStatement(
612 @NotNull PsiContinueStatement statement
){
616 super.visitContinueStatement(statement
);
617 final PsiIdentifier labelIdentifier
=
618 statement
.getLabelIdentifier();
619 if(m_nestingDepth
== 1 && labelIdentifier
== null){
621 } else if(labelMatches(labelIdentifier
)){
626 private boolean labelMatches(PsiIdentifier labelIdentifier
){
627 if(labelIdentifier
== null){
630 final String labelText
= labelIdentifier
.getText();
631 return labelText
.equals(m_label
);
634 @Override public void visitDoWhileStatement(
635 @NotNull PsiDoWhileStatement statement
){
640 super.visitDoWhileStatement(statement
);
644 @Override public void visitForStatement(@NotNull PsiForStatement statement
){
649 super.visitForStatement(statement
);
653 @Override public void visitForeachStatement(
654 @NotNull PsiForeachStatement statement
){
659 super.visitForeachStatement(statement
);
663 @Override public void visitWhileStatement(@NotNull PsiWhileStatement statement
){
668 super.visitWhileStatement(statement
);
673 public static class MethodCallFinder
674 extends JavaRecursiveElementVisitor
{
676 private final String containingClassName
;
677 private final PsiType returnType
;
678 private final String methodName
;
679 private final PsiType
[] parameterTypeNames
;
680 private boolean containsCallToMethod
= false;
682 MethodCallFinder(String containingClassName
, PsiType returnType
,
684 PsiType
... parameterTypeNames
) {
686 this.containingClassName
= containingClassName
;
687 this.returnType
= returnType
;
688 this.methodName
= methodName
;
689 this.parameterTypeNames
= parameterTypeNames
;
692 public boolean containsCallToMethod() {
693 return containsCallToMethod
;
696 @Override public void visitMethodCallExpression(
697 PsiMethodCallExpression expression
) {
698 if (containsCallToMethod
) {
701 super.visitMethodCallExpression(expression
);
702 if (!MethodCallUtils
.isCallToMethod(expression
, containingClassName
,
703 returnType
, methodName
, parameterTypeNames
)) {
706 containsCallToMethod
= true;