Patterns resolve: stop at switch label; filter by name before doing control flow analysis when possible (IDEA-229846)

Review ID: IDEA-CR-56992

GitOrigin-RevId: 4264da45582f5d38e3b5630184d8ab762f30575b
This commit is contained in:
Tagir Valeev
2019-12-28 12:05:19 +00:00
committed by intellij-monorepo-bot
parent 1a2437cb71
commit 43e64e1c56
8 changed files with 79 additions and 6 deletions
@@ -8,6 +8,10 @@ import org.jetbrains.annotations.Nullable;
* A variable declared within the pattern
*/
public interface PsiPatternVariable extends PsiVariable {
@NotNull
@Override
String getName();
@NotNull
@Override
PsiTypeElement getTypeElement();
@@ -239,15 +239,42 @@ public class PsiCodeBlockImpl extends LazyParseablePsiElement implements PsiCode
final ElementClassHint elementClassHint = processor.getHint(ElementClassHint.KEY);
final String name = hint.getName(state);
if ((elementClassHint == null || elementClassHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) && classesSet.contains(name)) {
return PsiScopesUtil.walkChildrenScopes(this, processor, state, lastParent, place);
return walkChildren(processor, state, lastParent, place);
}
if ((elementClassHint == null || elementClassHint.shouldProcess(ElementClassHint.DeclarationKind.VARIABLE)) && variablesSet.contains(name)) {
return PsiScopesUtil.walkChildrenScopes(this, processor, state, lastParent, place);
return walkChildren(processor, state, lastParent, place);
}
}
else {
return walkChildren(processor, state, lastParent, place);
}
return true;
}
private boolean walkChildren(@NotNull PsiScopeProcessor processor,
@NotNull ResolveState state,
PsiElement lastParent,
@NotNull PsiElement place) {
if (!(getParent() instanceof PsiSwitchBlock)) {
return PsiScopesUtil.walkChildrenScopes(this, processor, state, lastParent, place);
}
PsiElement child = null;
if (lastParent != null && lastParent.getParent() == this) {
child = lastParent.getPrevSibling();
if (child == null) return true; // first element
}
if (child == null) {
child = ((PsiElement)this).getLastChild();
}
while (child != null) {
if (child instanceof PsiSwitchLabelStatementBase) {
state = PatternResolveState.WHEN_NONE.putInto(state);
}
if (!child.processDeclarations(processor, state, null, place)) return false;
child = child.getPrevSibling();
}
return true;
}
@@ -197,6 +197,7 @@ public class PsiConditionalExpressionImpl extends ExpressionPsiElement implement
@NotNull ResolveState state,
PsiElement lastParent,
@NotNull PsiElement place) {
if (lastParent == null) return true;
ElementClassHint elementClassHint = processor.getHint(ElementClassHint.KEY);
if (elementClassHint != null && !elementClassHint.shouldProcess(ElementClassHint.DeclarationKind.VARIABLE)) return true;
PsiExpression condition = getCondition();
@@ -26,6 +26,7 @@ import com.intellij.psi.impl.source.tree.CompositePsiElement;
import com.intellij.psi.impl.source.tree.ElementType;
import com.intellij.psi.impl.source.tree.TreeElement;
import com.intellij.psi.scope.ElementClassHint;
import com.intellij.psi.scope.NameHint;
import com.intellij.psi.scope.PatternResolveState;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.tree.ChildRoleBase;
@@ -209,6 +210,7 @@ public class PsiIfStatementImpl extends CompositePsiElement implements PsiIfStat
PsiStatement thenBranch = getThenBranch();
PsiStatement elseBranch = getElseBranch();
if (lastParent == null) {
if (state.get(PatternResolveState.KEY) == PatternResolveState.WHEN_NONE) return true;
PsiScopeProcessor conditionProcessor;
if (state.get(PatternResolveState.KEY) == PatternResolveState.WHEN_BOTH) {
conditionProcessor = processor;
@@ -216,6 +218,10 @@ public class PsiIfStatementImpl extends CompositePsiElement implements PsiIfStat
else {
conditionProcessor = (element, s) -> {
LOG.assertTrue(element instanceof PsiPatternVariable);
final NameHint hint = processor.getHint(NameHint.KEY);
if (hint != null && !((PsiPatternVariable)element).getName().equals(hint.getName(s))) {
return true;
}
ControlFlow flow;
try {
flow = ControlFlowFactory.getInstance(getProject()).getControlFlow(
@@ -22,6 +22,7 @@ import com.intellij.psi.impl.PsiImplUtil;
import com.intellij.psi.impl.source.Constants;
import com.intellij.psi.impl.source.tree.ChildRole;
import com.intellij.psi.scope.ElementClassHint;
import com.intellij.psi.scope.NameHint;
import com.intellij.psi.scope.PatternResolveState;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.tree.ChildRoleBase;
@@ -137,10 +138,15 @@ public class PsiWhileStatementImpl extends PsiLoopStatementImpl implements PsiWh
@NotNull ResolveState state,
@NotNull PsiElement place,
@NotNull PsiConditionalLoopStatement loop) {
if (state.get(PatternResolveState.KEY) == PatternResolveState.WHEN_NONE) return true;
PsiExpression condition = loop.getCondition();
if (condition == null) return true;
PsiScopeProcessor conditionProcessor = (element, s) -> {
assert element instanceof PsiPatternVariable;
final NameHint hint = processor.getHint(NameHint.KEY);
if (hint != null && !((PsiPatternVariable)element).getName().equals(hint.getName(s))) {
return true;
}
PatternResolveState resolveState = PatternResolveState.stateAtParent((PsiPatternVariable)element, condition);
if (resolveState == PatternResolveState.WHEN_TRUE ||
!PsiTreeUtil
@@ -6,7 +6,7 @@ import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
public enum PatternResolveState {
WHEN_TRUE, WHEN_FALSE, WHEN_BOTH;
WHEN_TRUE, WHEN_FALSE, WHEN_BOTH, WHEN_NONE;
public static final Key<PatternResolveState> KEY = Key.create("JavaPatternDeclarationHint");
@@ -20,10 +20,8 @@ public enum PatternResolveState {
return WHEN_FALSE;
case WHEN_FALSE:
return WHEN_TRUE;
case WHEN_BOTH:
return WHEN_BOTH;
default:
throw new IllegalStateException("Unexpected value: " + this);
return this;
}
}
@@ -0,0 +1,28 @@
class X {
void test(Object obj, int x) {
switch (x) {
case 1:
if (!(obj instanceof String s)) return;
System.out.println(s.trim());
case 2:
if (!(obj instanceof Number s)) return;
System.out.println(s.longValue());
case 3:
System.out.println(<error descr="Cannot resolve symbol 's'">s</error>);
}
}
void testWhile(Object obj, int x) {
switch (x) {
case 1:
while(!(obj instanceof String s)) {
obj = getNextObject();
}
System.out.println(s.trim());
case 2:
System.out.println(<error descr="Cannot resolve symbol 's'">s</error>.trim());
}
}
native Object getNextObject();
}
@@ -27,6 +27,9 @@ public class LightPatternsHighlightingTest extends LightJavaCodeInsightFixtureTe
public void testInstanceOfControlFlow() {
doTest();
}
public void testInstanceOfInSwitch() {
doTest();
}
public void testReassignPatternVariable() {
doTest();
}