[groovy] control flow for negated in and instanceof (IDEA-188424)

This commit is contained in:
Daniil Ovchinnikov
2018-05-28 18:21:02 +03:00
parent 410cabfa2d
commit e89edae299
7 changed files with 125 additions and 76 deletions
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2017 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 org.jetbrains.plugins.groovy.lang.psi.controlFlow;
import com.intellij.openapi.diagnostic.Logger;
@@ -25,9 +11,9 @@ import com.intellij.util.ArrayUtilRt;
import com.intellij.util.containers.ObjectIntHashMap;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
import org.jetbrains.plugins.groovy.lang.psi.api.GrInExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
import org.jetbrains.plugins.groovy.lang.psi.api.formatter.GrControlStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.*;
@@ -94,7 +80,7 @@ public class ControlFlowBuilderUtil {
}
public static boolean isInstanceOfBinary(GrBinaryExpression binary) {
if (binary.getOperationTokenType() == GroovyTokenTypes.kIN) {
if (binary instanceof GrInExpression) {
GrExpression left = binary.getLeftOperand();
GrExpression right = binary.getRightOperand();
if (left instanceof GrReferenceExpression && ((GrReferenceExpression)left).getQualifier() == null &&
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2017 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 org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl;
import com.intellij.openapi.util.text.StringUtil;
@@ -30,19 +16,26 @@ import java.util.Set;
*/
public class ConditionInstruction extends InstructionImpl implements Instruction {
private final boolean myNegated;
private final Set<ConditionInstruction> myDependent;
public ConditionInstruction(@NotNull PsiElement element, @NotNull Collection<ConditionInstruction> dependent) {
public ConditionInstruction(@NotNull PsiElement element, boolean negated, @NotNull Collection<ConditionInstruction> dependent) {
super(element);
myNegated = negated;
myDependent = new LinkedHashSet<>(dependent);
myDependent.add(this);
}
public boolean isNegated() {
return myNegated;
}
@NotNull
@Override
protected String getElementPresentation() {
StringBuilder builder = new StringBuilder();
builder.append("Condition ").append(getElement());
if (myNegated) builder.append(", negated");
if (myDependent.size() > 1) {
builder.append(", dependent: ");
builder.append(StringUtil.join(ContainerUtil.filter(myDependent, d -> d != this), i -> String.valueOf(i.num()), ", "));
@@ -1,6 +1,4 @@
/*
* Copyright 2000-2017 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.
*/
// 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 org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl;
import com.intellij.openapi.diagnostic.Attachment;
@@ -22,6 +20,7 @@ import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor;
import org.jetbrains.plugins.groovy.lang.psi.api.GrInExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrCondition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.*;
@@ -442,7 +441,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
}
FList<ConditionInstruction> conditionsBefore = myConditions;
ConditionInstruction cond = registerCondition(expression);
ConditionInstruction cond = registerCondition(expression, false);
addNodeAndCheckPending(cond);
operand.accept(this);
@@ -500,7 +499,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
@Override
public void visitInstanceofExpression(@NotNull GrInstanceOfExpression expression) {
expression.getOperand().accept(this);
processInstanceOf(expression);
processInstanceOf(expression, expression.getNegationToken() != null);
}
@Override
@@ -562,7 +561,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
if (ControlFlowBuilderUtil.isInstanceOfBinary(expression)) {
expression.getLeftOperand().accept(this);
processInstanceOf(expression);
processInstanceOf(expression, ((GrInExpression)expression).getNegationToken() != null);
return;
}
@@ -576,7 +575,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
}
FList<ConditionInstruction> conditionsBefore = myConditions;
ConditionInstruction condition = registerCondition(expression);
ConditionInstruction condition = registerCondition(expression, false);
addNodeAndCheckPending(condition);
left.accept(this);
@@ -619,9 +618,9 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
right.accept(this);
}
private void processInstanceOf(GrExpression expression) {
private void processInstanceOf(GrExpression expression, boolean negated) {
FList<ConditionInstruction> conditionsBefore = myConditions;
ConditionInstruction cond = registerCondition(expression);
ConditionInstruction cond = registerCondition(expression, negated);
addNodeAndCheckPending(cond);
addNode(new InstanceOfInstruction(expression, cond));
@@ -718,8 +717,8 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
}
@NotNull
private ConditionInstruction registerCondition(@NotNull PsiElement element) {
ConditionInstruction condition = new ConditionInstruction(element, myConditions);
private ConditionInstruction registerCondition(@NotNull PsiElement element, boolean negated) {
ConditionInstruction condition = new ConditionInstruction(element, negated, myConditions);
myConditions = myConditions.prepend(condition);
return condition;
}
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2014 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 org.jetbrains.plugins.groovy.lang.psi.dataFlow;
import com.intellij.openapi.util.Comparing;
@@ -78,12 +64,12 @@ public class DFAType {
this.primary = primary;
}
public void addMixin(@Nullable PsiType mixin, ConditionInstruction instruction) {
public void addMixin(@Nullable PsiType mixin, @Nullable ConditionInstruction instruction) {
if (mixin == null) {
return;
}
mixins.add(new Mixin(mixin, instruction, false));
mixins.add(new Mixin(mixin, instruction, instruction != null && instruction.isNegated()));
}
@Override
@@ -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 org.jetbrains.plugins.groovy.lang.controlFlow
import com.intellij.openapi.editor.SelectionModel
@@ -108,6 +94,8 @@ class ControlFlowTest extends LightCodeInsightFixtureTestCase {
void testIfInstanceofElse() { doTest() }
void testIfNegatedInstanceofElse() { doTest() }
void testReturnMapFromClosure() { doTest() }
void testSwitchInTryWithThrows() { doTest() }
@@ -466,6 +466,15 @@ def bar(oo) {
''', null)
}
void testNegatedInstanceOfInferring1() {
doTest('''\
def bar(oo) {
boolean b = oo !instanceof String || oo != null
o<caret>o
}
''', null)
}
void testInstanceOfInferring2() {
doTest('''\
def bar(oo) {
@@ -475,6 +484,15 @@ def bar(oo) {
''', null)
}
void testNegatedInstanceOfInferring2() {
doTest('''\
def bar(oo) {
boolean b = oo !instanceof String || o<caret>o != null
oo
}
''', JAVA_LANG_STRING)
}
void testInstanceOfInferring3() {
doTest('''\
def bar(oo) {
@@ -484,6 +502,15 @@ def bar(oo) {
''', String.canonicalName)
}
void testNegatedInstanceOfInferring3() {
doTest('''\
def bar(oo) {
boolean b = oo !instanceof String && o<caret>o != null
oo
}
''', null)
}
void testInstanceOfInferring4() {
doTest('''\
def bar(oo) {
@@ -493,6 +520,15 @@ def bar(oo) {
''', null)
}
void testNegatedInstanceOfInferring4() {
doTest('''\
def bar(oo) {
boolean b = oo !instanceof String && oo != null
o<caret>o
}
''', null)
}
void testInstanceOfInferring5() {
doTest('''\
def foo(def oo) {
@@ -507,6 +543,20 @@ def foo(def oo) {
''', null)
}
void testNegatedInstanceOfInferring5() {
doTest('''\
def foo(def oo) {
if (oo !instanceof String || oo !instanceof CharSequence) {
oo
}
else {
o<caret>o
}
}
''', JAVA_LANG_STRING)
}
void testInstanceOfInferring6() {
doTest('''\
def foo(bar) {
@@ -516,6 +566,17 @@ def foo(bar) {
}''', 'java.lang.Runnable')
}
void testNegatedInstanceOfInferring6() {
doTest('''\
def foo(bar) {
if (!(bar !instanceof String) || bar !instanceof Runnable) {
} else {
ba<caret>r
}
}''', 'java.lang.Runnable')
}
void testInString() {
doTest '''\
def foo(ii) {
@@ -524,6 +585,14 @@ def foo(ii) {
}''', 'java.lang.String'
}
void testNegatedInString() {
doTest '''\
def foo(ii) {
if (ii !in String) {}
else print i<caret>i
}''', 'java.lang.String'
}
void testIndexProperty() {
doTest('''\
private void getCommonAncestor() {
@@ -0,0 +1,28 @@
if (o !instanceof String) b = 1
else if (!(o !instanceof Integer)) b = 2
else b = 3
-----
0(1) element: null
1(2) element: IF statement
2(3) READ o
3(4,6) Condition Instanceof expression, negated
4(5) instanceof: o !instanceof String
5(9) Negating goto instruction, condition=3Instanceof expression
6(7) instanceof: o !instanceof String
7(8) WRITE b
8(22) element: Assignment expression MAYBE_RETURN
9(10) element: IF statement
10(11) Condition Unary expression
11(12) READ o
12(13,15) Condition Instanceof expression, negated, dependent: 10
13(14) instanceof: o !instanceof Integer
14(17) Negating goto instruction, condition=12Instanceof expression
15(16) instanceof: o !instanceof Integer
16(19) Positive goto instruction, condition=10Unary expression
17(18) WRITE b
18(21) element: Assignment expression MAYBE_RETURN
19(20) WRITE b
20(21) element: Assignment expression MAYBE_RETURN
21(22) End element: IF statement
22(23) End element: IF statement
23() element: null