summaryrefslogtreecommitdiff
path: root/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/ControlFlowUtils.java
blob: 668444bd22c10e5fda777598bcd5bc8c7e69d126 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
/*
 * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.siyeh.ipp.psiutils;

import com.intellij.openapi.project.Project;
import com.intellij.psi.*;

public class ControlFlowUtils {

  private ControlFlowUtils() {
  }

  public static boolean statementMayCompleteNormally(PsiStatement statement) {
    if (statement instanceof PsiBreakStatement ||
        statement instanceof PsiContinueStatement ||
        statement instanceof PsiReturnStatement ||
        statement instanceof PsiThrowStatement) {
      return false;
    }
    else if (statement instanceof PsiExpressionListStatement ||
             statement instanceof PsiExpressionStatement ||
             statement instanceof PsiEmptyStatement ||
             statement instanceof PsiAssertStatement ||
             statement instanceof PsiDeclarationStatement) {
      return true;
    }
    else if (statement instanceof PsiForStatement) {
      final PsiForStatement loopStatement = (PsiForStatement)statement;
      final PsiExpression test = loopStatement.getCondition();
      return test != null && !isBooleanConstant(test, true) ||
             statementIsBreakTarget(loopStatement);
    }
    else if (statement instanceof PsiForeachStatement) {
      return true;
    }
    else if (statement instanceof PsiWhileStatement) {
      final PsiWhileStatement loopStatement =
        (PsiWhileStatement)statement;
      final PsiExpression test = loopStatement.getCondition();
      return !isBooleanConstant(test, true)
             || statementIsBreakTarget(loopStatement);
    }
    else if (statement instanceof PsiDoWhileStatement) {
      final PsiDoWhileStatement loopStatement =
        (PsiDoWhileStatement)statement;
      final PsiExpression test = loopStatement.getCondition();
      final PsiStatement body = loopStatement.getBody();
      return statementMayCompleteNormally(body) &&
             !isBooleanConstant(test, true)
             || statementIsBreakTarget(loopStatement);
    }
    else if (statement instanceof PsiSynchronizedStatement) {
      final PsiCodeBlock body =
        ((PsiSynchronizedStatement)statement).getBody();
      return codeBlockMayCompleteNormally(body);
    }
    else if (statement instanceof PsiBlockStatement) {
      final PsiCodeBlock codeBlock =
        ((PsiBlockStatement)statement).getCodeBlock();
      return codeBlockMayCompleteNormally(codeBlock);
    }
    else if (statement instanceof PsiLabeledStatement) {
      final PsiLabeledStatement labeledStatement =
        (PsiLabeledStatement)statement;
      final PsiStatement body = labeledStatement.getStatement();
      return statementMayCompleteNormally(body)
             || statementIsBreakTarget(body);
    }
    else if (statement instanceof PsiIfStatement) {
      final PsiIfStatement ifStatement = (PsiIfStatement)statement;
      final PsiStatement thenBranch = ifStatement.getThenBranch();
      if (statementMayCompleteNormally(thenBranch)) {
        return true;
      }
      final PsiStatement elseBranch = ifStatement.getElseBranch();
      return elseBranch == null ||
             statementMayCompleteNormally(elseBranch);
    }
    else if (statement instanceof PsiTryStatement) {
      final PsiTryStatement tryStatement = (PsiTryStatement)statement;

      final PsiCodeBlock finallyBlock = tryStatement.getFinallyBlock();
      if (finallyBlock != null) {
        if (!codeBlockMayCompleteNormally(finallyBlock)) {
          return false;
        }
      }
      final PsiCodeBlock tryBlock = tryStatement.getTryBlock();
      if (codeBlockMayCompleteNormally(tryBlock)) {
        return true;
      }
      final PsiCodeBlock[] catchBlocks = tryStatement.getCatchBlocks();
      for (final PsiCodeBlock catchBlock : catchBlocks) {
        if (codeBlockMayCompleteNormally(catchBlock)) {
          return true;
        }
      }
      return false;
    }
    else if (statement instanceof PsiSwitchStatement) {
      final PsiSwitchStatement switchStatement =
        (PsiSwitchStatement)statement;
      if (statementIsBreakTarget(switchStatement)) {
        return true;
      }
      final PsiCodeBlock body = switchStatement.getBody();
      if (body == null) {
        return true;
      }
      final PsiStatement[] statements = body.getStatements();
      int lastNonLabelOffset = -1;
      final int lastStatementIndex = statements.length - 1;
      for (int i = lastStatementIndex; i >= 0; i--) {
        if (!(statements[i] instanceof PsiSwitchLabelStatement)) {
          lastNonLabelOffset = i;
          break;
        }
      }
      if (lastNonLabelOffset == -1) {
        return true;    // it's all labels
      }
      else if (lastNonLabelOffset == lastStatementIndex) {
        return statementMayCompleteNormally(
          statements[lastStatementIndex]);
      }
      else {
        return true;    // the last statement is a label
      }
    }
    else {
      return false;
    }
  }

  private static boolean codeBlockMayCompleteNormally(PsiCodeBlock block) {
    if (block == null) {
      return true;
    }
    final PsiStatement[] statements = block.getStatements();
    for (final PsiStatement statement : statements) {
      if (!statementMayCompleteNormally(statement)) {
        return false;
      }
    }
    return true;
  }

  private static boolean isBooleanConstant(PsiExpression expression,
                                           boolean b) {
    if (expression == null) {
      return false;
    }
    final Project project = expression.getProject();
    final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project);
    final PsiConstantEvaluationHelper constantEvaluationHelper =
      psiFacade.getConstantEvaluationHelper();
    final Object value =
      constantEvaluationHelper.computeConstantExpression
        (expression, false);
    if (!(value instanceof Boolean)) {
      return false;
    }
    final Boolean aBoolean = (Boolean)value;
    return aBoolean.booleanValue() == b;
  }

  private static boolean statementIsBreakTarget(PsiStatement statement) {
    if (statement == null) {
      return false;
    }
    final BreakTargetFinder breakFinder = new BreakTargetFinder(statement);
    statement.accept(breakFinder);
    return breakFinder.breakFound();
  }

  public static boolean statementContainsNakedBreak(PsiStatement statement) {
    if (statement == null) {
      return false;
    }
    final NakedBreakFinder breakFinder = new NakedBreakFinder();
    statement.accept(breakFinder);
    return breakFinder.breakFound();
  }

  private static class BreakTargetFinder
    extends JavaRecursiveElementWalkingVisitor {

    private boolean m_found = false;
    private final PsiStatement m_target;

    private BreakTargetFinder(PsiStatement target) {
      m_target = target;
    }

    public boolean breakFound() {
      return m_found;
    }

    @Override
    public void visitElement(PsiElement element) {
      if (m_found) {
        return;
      }
      super.visitElement(element);
    }

    @Override
    public void visitReferenceExpression(
      PsiReferenceExpression expression) {
    }

    @Override
    public void visitBreakStatement(PsiBreakStatement statement) {
      super.visitBreakStatement(statement);
      final PsiStatement exitedStatement =
        statement.findExitedStatement();
      if (exitedStatement == null) {
        return;
      }
      if (exitedStatement.equals(m_target)) {
        m_found = true;
      }
    }
  }

  private static class NakedBreakFinder
    extends JavaRecursiveElementWalkingVisitor {

    private boolean m_found = false;

    public boolean breakFound() {
      return m_found;
    }

    @Override
    public void visitElement(PsiElement element) {
      if (m_found) {
        return;
      }
      super.visitElement(element);
    }

    @Override
    public void visitReferenceExpression(
      PsiReferenceExpression expression) {
    }

    @Override
    public void visitBreakStatement(PsiBreakStatement statement) {
      if (statement.getLabelIdentifier() != null) {
        return;
      }
      m_found = true;
    }

    @Override
    public void visitDoWhileStatement(
      PsiDoWhileStatement statement) {
      // don't drill down
    }

    @Override
    public void visitForStatement(PsiForStatement statement) {
      // don't drill down
    }

    @Override
    public void visitForeachStatement(PsiForeachStatement statement) {
      // don't drill down
    }

    @Override
    public void visitWhileStatement(PsiWhileStatement statement) {
      // don't drill down
    }

    @Override
    public void visitSwitchStatement(
      PsiSwitchStatement statement) {
      // don't drill down
    }
  }
}