IDEADEV-31824 (Incorrect "manual array copy" warning)
[fedora-idea.git] / plugins / InspectionGadgets / src / com / siyeh / ig / psiutils / ControlFlowUtils.java
blob49f6f194ad8673528c66f38043aa93cb0f1eee9c
1 /*
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(){
27 super();
30 public static boolean statementMayCompleteNormally(
31 @Nullable PsiStatement statement){
32 if(statement == null){
33 return true;
35 if(statement instanceof PsiBreakStatement ||
36 statement instanceof PsiContinueStatement ||
37 statement instanceof PsiReturnStatement ||
38 statement instanceof PsiThrowStatement){
39 return false;
40 } else if(statement instanceof PsiExpressionListStatement ||
41 statement instanceof PsiEmptyStatement ||
42 statement instanceof PsiAssertStatement ||
43 statement instanceof PsiDeclarationStatement){
44 return true;
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)){
51 return true;
53 final PsiMethodCallExpression methodCallExpression =
54 (PsiMethodCallExpression)expression;
55 final PsiMethod method = methodCallExpression.resolveMethod();
56 if(method == null){
57 return true;
59 @NonNls final String methodName = method.getName();
60 if(!methodName.equals("exit")) {
61 return true;
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);
95 } else{
96 // unknown statement type
97 return true;
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)){
120 return true;
122 if(test == null){
123 return false;
125 return !BoolUtils.isTrue(test);
128 private static boolean foreachStatementMayReturnNormally(
129 @NotNull PsiForeachStatement loopStatement){
130 return true;
133 private static boolean switchStatementMayReturnNormally(
134 @NotNull PsiSwitchStatement switchStatement){
135 if(statementIsBreakTarget(switchStatement)){
136 return true;
138 final PsiCodeBlock body = switchStatement.getBody();
139 if(body == null){
140 return true;
142 final PsiStatement[] statements = body.getStatements();
143 if(statements.length == 0){
144 return true;
146 int numCases = 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) {
160 return true;
163 numCases++;
165 final boolean isEnum = isEnumSwitch(switchStatement);
166 if(!hasDefaultCase && !isEnum){
167 return true;
169 if(!hasDefaultCase && isEnum){
170 final PsiExpression expression = switchStatement.getExpression();
171 if(expression == null){
172 return true;
174 final PsiClassType type = (PsiClassType) expression.getType();
175 if(type == null){
176 return true;
178 final PsiClass aClass = type.resolve();
179 if(aClass == null){
180 return true;
182 final PsiField[] fields = aClass.getFields();
183 int numEnums = 0;
184 for(final PsiField field : fields){
185 final PsiType fieldType = field.getType();
186 if(fieldType.equals(type)){
187 numEnums++;
190 if(numEnums != numCases){
191 return true;
194 return statementMayCompleteNormally(statements[statements.length - 1]);
197 private static boolean isEnumSwitch(PsiSwitchStatement statement){
198 final PsiExpression expression = statement.getExpression();
199 if(expression == null){
200 return false;
202 final PsiType type = expression.getType();
203 if(type == null){
204 return false;
206 if(!(type instanceof PsiClassType)){
207 return false;
209 final PsiClass aClass = ((PsiClassType) type).resolve();
210 if(aClass == null){
211 return false;
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)){
221 return false;
224 final PsiCodeBlock tryBlock = tryStatement.getTryBlock();
225 if(codeBlockMayCompleteNormally(tryBlock)){
226 return true;
228 final PsiCodeBlock[] catchBlocks = tryStatement.getCatchBlocks();
229 for(final PsiCodeBlock catchBlock : catchBlocks){
230 if(codeBlockMayCompleteNormally(catchBlock)){
231 return true;
234 return false;
237 private static boolean ifStatementMayReturnNormally(
238 @NotNull PsiIfStatement ifStatement){
239 final PsiStatement thenBranch = ifStatement.getThenBranch();
240 if(statementMayCompleteNormally(thenBranch)){
241 return true;
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) {
252 return false;
254 return statementMayCompleteNormally(statement) ||
255 statementIsBreakTarget(statement);
258 public static boolean codeBlockMayCompleteNormally(
259 @Nullable PsiCodeBlock block){
260 if(block == null){
261 return true;
263 final PsiStatement[] statements = block.getStatements();
264 for(final PsiStatement statement : statements){
265 if(!statementMayCompleteNormally(statement)){
266 return false;
269 return true;
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){
314 return false;
316 final PsiStatement body = loopStatement.getBody();
317 if (body == null) {
318 return false;
320 return PsiTreeUtil.isAncestor(body, element, true);
323 public static boolean isInFinallyBlock(@NotNull PsiElement element){
324 PsiElement currentElement = element;
325 while(true){
326 final PsiTryStatement tryStatement =
327 PsiTreeUtil.getParentOfType(currentElement,
328 PsiTryStatement.class);
329 if(tryStatement == null){
330 return false;
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,
337 PsiMethod.class);
338 final PsiMethod finallyMethod =
339 PsiTreeUtil.getParentOfType(finallyBlock,
340 PsiMethod.class);
341 return elementMethod != null &&
342 elementMethod.equals(finallyMethod);
345 currentElement = tryStatement;
349 public static boolean isInCatchBlock(@NotNull PsiElement element){
350 PsiElement currentElement = element;
351 while(true){
352 final PsiTryStatement tryStatement =
353 PsiTreeUtil.getParentOfType(currentElement,
354 PsiTryStatement.class,
355 true, PsiClass.class);
356 if(tryStatement == null){
357 return false;
359 final PsiCodeBlock[] catchBlocks = tryStatement.getCatchBlocks();
360 for(final PsiCodeBlock catchBlock : catchBlocks){
361 if(PsiTreeUtil.isAncestor(catchBlock, currentElement, true)){
362 return 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;
390 @Nullable
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];
398 } else{
399 return block;
401 } else{
402 return statement;
406 public static boolean statementCompletesWithStatement(
407 @NotNull PsiStatement containingStatement,
408 @NotNull PsiStatement statement){
409 PsiElement statementToCheck = statement;
410 while(true){
411 if(statementToCheck.equals(containingStatement)){
412 return true;
414 final PsiElement container =
415 getContainingStatementOrBlock(statementToCheck);
416 if(container == null){
417 return false;
419 if(container instanceof PsiCodeBlock){
420 if(!statementIsLastInBlock((PsiCodeBlock) container,
421 (PsiStatement) statementToCheck)){
422 return false;
425 if(container instanceof PsiLoopStatement){
426 return false;
428 statementToCheck = container;
432 public static boolean blockCompletesWithStatement(
433 @NotNull PsiCodeBlock body,
434 @NotNull PsiStatement statement){
435 PsiElement statementToCheck = statement;
436 while(true){
437 if(statementToCheck == null){
438 return false;
440 final PsiElement container =
441 getContainingStatementOrBlock(statementToCheck);
442 if(container == null){
443 return false;
445 if(container instanceof PsiLoopStatement){
446 return false;
448 if(container instanceof PsiCodeBlock){
449 if(!statementIsLastInBlock((PsiCodeBlock) container,
450 (PsiStatement) statementToCheck)){
451 return false;
453 if(container.equals(body)){
454 return true;
456 statementToCheck =
457 PsiTreeUtil.getParentOfType(container,
458 PsiStatement.class);
459 } else{
460 statementToCheck = container;
465 @Nullable
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)){
478 return true;
480 if(!(statement instanceof PsiEmptyStatement)){
481 return false;
484 return false;
487 public static boolean methodAlwaysThrowsException(
488 @NotNull PsiMethod method){
489 final PsiCodeBlock body = method.getBody();
490 if (body == null) {
491 return true;
493 final ReturnFinder returnFinder = new ReturnFinder();
494 body.accept(returnFinder);
495 if(returnFinder.returnFound()){
496 return false;
498 return !codeBlockMayCompleteNormally(body);
501 private static class SystemExitFinder extends JavaRecursiveElementVisitor{
503 private boolean m_found = false;
505 public boolean exitFound() {
506 return m_found;
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){
515 if(m_found){
516 return;
518 super.visitMethodCallExpression(expression);
519 final PsiMethod method = expression.resolveMethod();
520 if(method == null){
521 return;
523 @NonNls final String methodName = method.getName();
524 if(!methodName.equals("exit")) {
525 return;
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)) {
531 return;
533 m_found = true;
537 private static class ReturnFinder extends JavaRecursiveElementVisitor{
539 private boolean m_found = false;
541 public boolean returnFound(){
542 return m_found;
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){
551 if(m_found){
552 return;
554 super.visitReturnStatement(returnStatement);
555 m_found = true;
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){
565 super();
566 m_target = target;
569 public boolean breakFound(){
570 return m_found;
573 @Override public void visitBreakStatement(
574 @NotNull PsiBreakStatement breakStatement){
575 if(m_found){
576 return;
578 super.visitBreakStatement(breakStatement);
579 final PsiStatement exitedStatement =
580 breakStatement.findExitedStatement();
581 if (exitedStatement == null) {
582 return;
584 if(PsiTreeUtil.isAncestor(exitedStatement, m_target, false)){
585 m_found = true;
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){
597 super();
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(){
608 return m_found;
611 @Override public void visitContinueStatement(
612 @NotNull PsiContinueStatement statement){
613 if(m_found){
614 return;
616 super.visitContinueStatement(statement);
617 final PsiIdentifier labelIdentifier =
618 statement.getLabelIdentifier();
619 if(m_nestingDepth == 1 && labelIdentifier == null){
620 m_found = true;
621 } else if(labelMatches(labelIdentifier)){
622 m_found = true;
626 private boolean labelMatches(PsiIdentifier labelIdentifier){
627 if(labelIdentifier == null){
628 return false;
630 final String labelText = labelIdentifier.getText();
631 return labelText.equals(m_label);
634 @Override public void visitDoWhileStatement(
635 @NotNull PsiDoWhileStatement statement){
636 if(m_found){
637 return;
639 m_nestingDepth++;
640 super.visitDoWhileStatement(statement);
641 m_nestingDepth--;
644 @Override public void visitForStatement(@NotNull PsiForStatement statement){
645 if(m_found){
646 return;
648 m_nestingDepth++;
649 super.visitForStatement(statement);
650 m_nestingDepth--;
653 @Override public void visitForeachStatement(
654 @NotNull PsiForeachStatement statement){
655 if(m_found){
656 return;
658 m_nestingDepth++;
659 super.visitForeachStatement(statement);
660 m_nestingDepth--;
663 @Override public void visitWhileStatement(@NotNull PsiWhileStatement statement){
664 if(m_found){
665 return;
667 m_nestingDepth++;
668 super.visitWhileStatement(statement);
669 m_nestingDepth--;
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,
683 String methodName,
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) {
699 return;
701 super.visitMethodCallExpression(expression);
702 if (!MethodCallUtils.isCallToMethod(expression, containingClassName,
703 returnType, methodName, parameterTypeNames)) {
704 return;
706 containsCallToMethod = true;