ControlFlowAnalyzer: fix control flow for assert (IDEA-186305)

This commit is contained in:
Tagir Valeev
2018-02-08 11:57:02 +07:00
parent a554398947
commit bf640e78a9
4 changed files with 71 additions and 81 deletions
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* 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.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.psi.controlFlow;
import com.intellij.psi.PsiExpression;
@@ -28,7 +14,7 @@ public class ConditionalGoToInstruction extends ConditionalBranchingInstruction
}
public String toString() {
@NonNls final String sRole = "["+role.toString()+"]";
@NonNls final String sRole = "[" + role + "]";
return "COND_GOTO " + sRole + " " + offset;
}
@@ -1,30 +1,10 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* 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.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.psi.controlFlow;
import com.intellij.psi.PsiExpression;
public class ConditionalThrowToInstruction extends ConditionalBranchingInstruction {
public ConditionalThrowToInstruction(int offset, PsiExpression expression) {
super(offset, expression, Role.END);
}
public ConditionalThrowToInstruction(final int offset) {
this(offset, null);
super(offset, null, Role.END);
}
public String toString() {
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* 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.
*/
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.psi.controlFlow;
import com.intellij.codeInsight.ExceptionUtil;
@@ -928,6 +914,12 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
exception.accept(this);
}
final List<PsiElement> blocks = findThrowToBlocks(statement);
addThrowInstructions(blocks);
finishElement(statement);
}
private void addThrowInstructions(@NotNull List<PsiElement> blocks) {
PsiElement element;
if (blocks.isEmpty() || blocks.get(0) == null) {
ThrowToInstruction instruction = new ThrowToInstruction(0);
@@ -954,16 +946,13 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
addElementOffsetLater(element, true);
}
}
finishElement(statement);
}
/**
* find offsets of catch(es) corresponding to this throw statement
* myCatchParameters and myCatchBlocks arrays should be sorted in ascending scope order (from outermost to innermost)
*
* @return offset or -1 if not found
* @return list of targets or list of single null element if no appropriate targets found
*/
@NotNull
private List<PsiElement> findThrowToBlocks(@NotNull PsiThrowStatement statement) {
@@ -1002,33 +991,49 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
myCurrentFlow.addInstruction(passByWhenAssertionsDisabled);
addElementOffsetLater(statement, false);
// should not try to compute constant expression within assert
// since assertions can be disabled/enabled at any moment via JVM flags
final PsiExpression condition = statement.getAssertCondition();
if (condition != null) {
myStartStatementStack.pushStatement(statement, false);
myEndStatementStack.pushStatement(statement, false);
myEndJumpRoles.push(BranchingInstruction.Role.END);
myStartJumpRoles.push(BranchingInstruction.Role.END);
condition.accept(this);
myStartJumpRoles.pop();
myEndJumpRoles.pop();
myStartStatementStack.popStatement();
myEndStatementStack.popStatement();
}
PsiExpression description = statement.getAssertDescription();
if (description != null) {
description.accept(this);
boolean generateCondition = true;
boolean generateThrow = true;
if (myEvaluateConstantIfCondition) {
Object conditionValue = myConstantEvaluationHelper.computeConstantExpression(condition);
if (conditionValue instanceof Boolean) {
generateThrow = !((Boolean)conditionValue);
generateCondition = false;
emitEmptyInstruction();
}
}
Instruction instruction = new ConditionalThrowToInstruction(0, statement.getAssertCondition());
myCurrentFlow.addInstruction(instruction);
addElementOffsetLater(myCodeFragment, false);
if (generateCondition) {
if (condition != null) {
myStartStatementStack.pushStatement(statement, false);
myEndStatementStack.pushStatement(statement, false);
myEndJumpRoles.push(BranchingInstruction.Role.END);
myStartJumpRoles.push(BranchingInstruction.Role.END);
condition.accept(this);
myStartJumpRoles.pop();
myEndJumpRoles.pop();
myStartStatementStack.popStatement();
myEndStatementStack.popStatement();
}
Instruction ifTrue = new ConditionalGoToInstruction(0, BranchingInstruction.Role.END, statement.getAssertCondition());
myCurrentFlow.addInstruction(ifTrue);
addElementOffsetLater(statement, false);
}
if (generateThrow) {
PsiExpression description = statement.getAssertDescription();
if (description != null) {
description.accept(this);
}
// if description is evaluated, the assert statement cannot complete normally
// though non-necessarily AssertionError will be thrown (description may throw something, or AssertionError ctor, etc.)
PsiClassType exceptionClass = JavaPsiFacade.getElementFactory(statement.getProject()).createTypeByFQClassName(
CommonClassNames.JAVA_LANG_THROWABLE, statement.getResolveScope());
addThrowInstructions(findThrowToBlocks(exceptionClass));
}
myStartStatementStack.popStatement();
myEndStatementStack.popStatement();
@@ -92,4 +92,23 @@ class T1 {
(i4) = 1;
(<error descr="Expression expected">)</error>++;
}
}
class T3 {
private final boolean b;
{
assert false : "" + (b = true); // if assignment reachable, assert will not complete normally
b = true; // compiles
System.out.println(b);
}
}
class T3a {
private final boolean b;
{
try {
assert false : "" + (b = true);
}
catch(IllegalArgumentException t) {}
<error descr="Variable 'b' might already have been assigned to">b</error> = true; // red
System.out.println(b);
}
}