mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-05-06 05:10:22 +07:00
IDEA-229846 Resolve for pattern matching (except switch handling)
GitOrigin-RevId: b8addebac00f681641d5cef1089fa6f7d2d668a7
This commit is contained in:
committed by
intellij-monorepo-bot
parent
14183dd8a9
commit
0df312338d
@@ -38,6 +38,7 @@ import com.intellij.psi.impl.source.tree.ElementType;
|
||||
import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl;
|
||||
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl;
|
||||
import com.intellij.psi.javadoc.PsiDocComment;
|
||||
import com.intellij.psi.scope.PatternResolveState;
|
||||
import com.intellij.psi.scope.processor.VariablesNotProcessor;
|
||||
import com.intellij.psi.scope.util.PsiScopesUtil;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
@@ -602,7 +603,7 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
if (variable instanceof ExternallyDefinedPsiElement) return null;
|
||||
PsiVariable oldVariable = null;
|
||||
PsiElement declarationScope = null;
|
||||
if (variable instanceof PsiLocalVariable ||
|
||||
if (variable instanceof PsiLocalVariable || variable instanceof PsiPatternVariable ||
|
||||
variable instanceof PsiParameter &&
|
||||
((declarationScope = ((PsiParameter)variable).getDeclarationScope()) instanceof PsiCatchSection ||
|
||||
declarationScope instanceof PsiForeachStatement ||
|
||||
@@ -611,7 +612,8 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
VariablesNotProcessor proc = new VariablesNotProcessor(variable, false) {
|
||||
@Override
|
||||
protected boolean check(final PsiVariable var, final ResolveState state) {
|
||||
return (var instanceof PsiLocalVariable || var instanceof PsiParameter) && super.check(var, state);
|
||||
return (var instanceof PsiLocalVariable || var instanceof PsiParameter || var instanceof PsiPatternVariable) &&
|
||||
super.check(var, state);
|
||||
}
|
||||
};
|
||||
PsiIdentifier identifier = variable.getNameIdentifier();
|
||||
@@ -627,6 +629,9 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
else if (declarationScope instanceof PsiLambdaExpression) {
|
||||
oldVariable = checkSameNames(variable);
|
||||
}
|
||||
else if (variable instanceof PsiPatternVariable) {
|
||||
oldVariable = checkSamePatternVariableInBranches((PsiPatternVariable)variable);
|
||||
}
|
||||
}
|
||||
else if (variable instanceof PsiField) {
|
||||
PsiField field = (PsiField)variable;
|
||||
@@ -666,6 +671,54 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static PsiPatternVariable checkSamePatternVariableInBranches(PsiPatternVariable variable) {
|
||||
PsiPattern pattern = variable.getPattern();
|
||||
if (pattern == null) return null;
|
||||
PatternResolveState hint = PatternResolveState.WHEN_TRUE;
|
||||
VariablesNotProcessor proc = new VariablesNotProcessor(variable, false) {
|
||||
@Override
|
||||
protected boolean check(final PsiVariable var, final ResolveState state) {
|
||||
return var instanceof PsiPatternVariable && super.check(var, state);
|
||||
}
|
||||
};
|
||||
PsiElement lastParent = pattern;
|
||||
for (PsiElement parent = lastParent.getParent(); parent != null; lastParent = parent, parent = parent.getParent()) {
|
||||
if (parent instanceof PsiInstanceOfExpression || parent instanceof PsiParenthesizedExpression) continue;
|
||||
if (parent instanceof PsiPrefixExpression && ((PsiPrefixExpression)parent).getOperationTokenType().equals(JavaTokenType.EXCL)) {
|
||||
hint = hint.invert();
|
||||
continue;
|
||||
}
|
||||
if (parent instanceof PsiPolyadicExpression) {
|
||||
IElementType tokenType = ((PsiPolyadicExpression)parent).getOperationTokenType();
|
||||
if (tokenType.equals(JavaTokenType.ANDAND) || tokenType.equals(JavaTokenType.OROR)) {
|
||||
PatternResolveState targetHint = PatternResolveState.fromBoolean(tokenType.equals(JavaTokenType.OROR));
|
||||
if (hint == targetHint) {
|
||||
for (PsiExpression operand : ((PsiPolyadicExpression)parent).getOperands()) {
|
||||
if (operand == lastParent) break;
|
||||
operand.processDeclarations(proc, hint.putInto(ResolveState.initial()), null, pattern);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (parent instanceof PsiConditionalExpression) {
|
||||
PsiConditionalExpression conditional = (PsiConditionalExpression)parent;
|
||||
PsiExpression thenExpression = conditional.getThenExpression();
|
||||
if (lastParent == thenExpression) {
|
||||
conditional.getCondition().processDeclarations(proc, PatternResolveState.WHEN_FALSE.putInto(ResolveState.initial()), null, pattern);
|
||||
}
|
||||
else if (lastParent == conditional.getElseExpression()) {
|
||||
conditional.getCondition().processDeclarations(proc, PatternResolveState.WHEN_TRUE.putInto(ResolveState.initial()), null, pattern);
|
||||
if (thenExpression != null) {
|
||||
thenExpression.processDeclarations(proc, hint.putInto(ResolveState.initial()), null, pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return proc.size() > 0 ? (PsiPatternVariable)proc.getResult(0) : null;
|
||||
}
|
||||
|
||||
private static PsiVariable checkSameNames(@NotNull PsiVariable variable) {
|
||||
PsiElement scope = variable.getParent();
|
||||
PsiElement[] children = scope.getChildren();
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
// Copyright 2000-2019 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;
|
||||
|
||||
public interface PsiPatternVariable extends PsiLocalVariable {
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A variable declared within the pattern
|
||||
*/
|
||||
public interface PsiPatternVariable extends PsiVariable {
|
||||
@NotNull
|
||||
@Override
|
||||
PsiTypeElement getTypeElement();
|
||||
|
||||
@Nullable
|
||||
PsiPattern getPattern();
|
||||
}
|
||||
|
||||
@@ -351,6 +351,7 @@ public class ControlFlowUtil {
|
||||
return outputVariables;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
@NotNull
|
||||
public static Collection<PsiStatement> findExitPointsAndStatements(@NotNull ControlFlow flow, final int start, final int end,
|
||||
@NotNull IntArrayList exitPoints,
|
||||
@@ -360,7 +361,7 @@ public class ControlFlowUtil {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final Collection<PsiStatement> exitStatements = new THashSet<>();
|
||||
InstructionClientVisitor visitor = new InstructionClientVisitor() {
|
||||
InstructionClientVisitor<Void> visitor = new InstructionClientVisitor<Void>() {
|
||||
@Override
|
||||
public void visitThrowToInstruction(ThrowToInstruction instruction, int offset, int nextOffset) {
|
||||
//[ven]This is a hack since Extract Method doesn't want to see throw's exit points
|
||||
@@ -398,7 +399,7 @@ public class ControlFlowUtil {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getResult() {
|
||||
public Void getResult() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -406,11 +407,12 @@ public class ControlFlowUtil {
|
||||
return exitStatements;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private static void processGoto(@NotNull ControlFlow flow, int start, int end,
|
||||
@NotNull IntArrayList exitPoints,
|
||||
@NotNull Collection<? super PsiStatement> exitStatements,
|
||||
@NotNull BranchingInstruction instruction,
|
||||
final PsiStatement statement, @NotNull Class... classesFilter) {
|
||||
final PsiStatement statement, @NotNull Class<? extends PsiStatement>... classesFilter) {
|
||||
if (statement == null) return;
|
||||
int gotoOffset = instruction.offset;
|
||||
if (start > gotoOffset || gotoOffset >= end || isElementOfClass(statement, classesFilter)) {
|
||||
@@ -434,15 +436,17 @@ public class ControlFlowUtil {
|
||||
}
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private static void processGotoStatement(@NotNull Collection<? super PsiStatement> exitStatements,
|
||||
PsiStatement statement, @NotNull Class... classesFilter) {
|
||||
PsiStatement statement, @NotNull Class<? extends PsiStatement>... classesFilter) {
|
||||
if (statement != null && isElementOfClass(statement, classesFilter)) {
|
||||
exitStatements.add(statement);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isElementOfClass(@NotNull PsiElement element, @NotNull Class... classesFilter) {
|
||||
for (Class aClassesFilter : classesFilter) {
|
||||
@SafeVarargs
|
||||
private static boolean isElementOfClass(@NotNull PsiElement element, @NotNull Class<? extends PsiStatement>... classesFilter) {
|
||||
for (Class<? extends PsiStatement> aClassesFilter : classesFilter) {
|
||||
if (ReflectionUtil.isAssignable(aClassesFilter, element.getClass())) {
|
||||
return true;
|
||||
}
|
||||
@@ -1005,7 +1009,7 @@ public class ControlFlowUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true iff exists controlflow path completing normally, i.e. not resulting in return,break,continue or exception thrown.
|
||||
* returns true iff exists control flow path completing normally, i.e. not resulting in return,break,continue or exception thrown.
|
||||
* In other words, if we add instruction after controlflow specified, it should be reachable
|
||||
*/
|
||||
public static boolean canCompleteNormally(@NotNull ControlFlow flow, final int startOffset, final int endOffset) {
|
||||
@@ -1554,17 +1558,17 @@ public class ControlFlowUtil {
|
||||
return endOffset;
|
||||
}
|
||||
|
||||
private static void depthFirstSearch(@NotNull ControlFlow flow, @NotNull InstructionClientVisitor visitor) {
|
||||
private static void depthFirstSearch(@NotNull ControlFlow flow, @NotNull InstructionClientVisitor<?> visitor) {
|
||||
depthFirstSearch(flow, visitor, 0, flow.getSize());
|
||||
}
|
||||
|
||||
private static void depthFirstSearch(@NotNull ControlFlow flow, @NotNull InstructionClientVisitor visitor, int startOffset, int endOffset) {
|
||||
private static void depthFirstSearch(@NotNull ControlFlow flow, @NotNull InstructionClientVisitor<?> visitor, int startOffset, int endOffset) {
|
||||
visitor.processedInstructions = new boolean[endOffset];
|
||||
internalDepthFirstSearch(flow.getInstructions(), visitor, startOffset, endOffset);
|
||||
}
|
||||
|
||||
private static void internalDepthFirstSearch(@NotNull List<? extends Instruction> instructions,
|
||||
@NotNull InstructionClientVisitor clientVisitor,
|
||||
@NotNull InstructionClientVisitor<?> clientVisitor,
|
||||
int startOffset,
|
||||
int endOffset) {
|
||||
|
||||
@@ -1870,7 +1874,7 @@ public class ControlFlowUtil {
|
||||
public void visitReadVariableInstruction(ReadVariableInstruction instruction, int offset, int nextOffset) {
|
||||
CopyOnWriteList readVars = readVariables[Math.min(nextOffset, myFlow.getSize())];
|
||||
final PsiVariable variable = instruction.variable;
|
||||
if (!localVariablesOnly || !isMethodParameter(variable)) {
|
||||
if (!localVariablesOnly || !isImplicitlyInitialized(variable)) {
|
||||
final PsiReferenceExpression expression = getEnclosingReferenceExpression(myFlow.getElement(offset), variable);
|
||||
if (expression != null) {
|
||||
readVars = CopyOnWriteList.add(readVars, new VariableInfo(variable, expression));
|
||||
@@ -1885,12 +1889,16 @@ public class ControlFlowUtil {
|
||||
if (readVars == null) return;
|
||||
|
||||
final PsiVariable variable = instruction.variable;
|
||||
if (!localVariablesOnly || !isMethodParameter(variable)) {
|
||||
if (!localVariablesOnly || !isImplicitlyInitialized(variable)) {
|
||||
readVars = readVars.remove(new VariableInfo(variable, null));
|
||||
}
|
||||
merge(offset, readVars, readVariables);
|
||||
}
|
||||
|
||||
private static boolean isImplicitlyInitialized(@NotNull PsiVariable variable) {
|
||||
return isMethodParameter(variable) || variable instanceof PsiPatternVariable;
|
||||
}
|
||||
|
||||
private static boolean isMethodParameter(@NotNull PsiVariable variable) {
|
||||
if (variable instanceof PsiParameter) {
|
||||
final PsiParameter parameter = (PsiParameter)variable;
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.intellij.psi.impl.source.resolve.JavaResolveCache;
|
||||
import com.intellij.psi.impl.source.tree.ChildRole;
|
||||
import com.intellij.psi.impl.source.tree.ElementType;
|
||||
import com.intellij.psi.impl.source.tree.JavaElementType;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.tree.ChildRoleBase;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
@@ -141,6 +142,14 @@ public class PsiBinaryExpressionImpl extends ExpressionPsiElement implements Psi
|
||||
return "PsiBinaryExpression:" + getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
|
||||
@NotNull ResolveState state,
|
||||
PsiElement lastParent,
|
||||
@NotNull PsiElement place) {
|
||||
return PsiPolyadicExpressionImpl.processDeclarations(this, processor, state, lastParent, place);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiExpression[] getOperands() {
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.intellij.psi.impl.PsiImplUtil;
|
||||
import com.intellij.psi.impl.source.tree.*;
|
||||
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.scope.util.PsiScopesUtil;
|
||||
import com.intellij.psi.tree.ChildRoleBase;
|
||||
@@ -113,8 +114,8 @@ public class PsiCodeBlockImpl extends LazyParseablePsiElement implements PsiCode
|
||||
PsiScopesUtil.walkChildrenScopes(this, new PsiScopeProcessor() {
|
||||
@Override
|
||||
public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) {
|
||||
if (element instanceof PsiLocalVariable) {
|
||||
final PsiLocalVariable variable = (PsiLocalVariable)element;
|
||||
if (element instanceof PsiLocalVariable || element instanceof PsiPatternVariable) {
|
||||
final PsiVariable variable = (PsiVariable)element;
|
||||
final String name = variable.getName();
|
||||
if (!localsSet.add(name)) {
|
||||
conflict.set(Boolean.TRUE);
|
||||
@@ -133,7 +134,7 @@ public class PsiCodeBlockImpl extends LazyParseablePsiElement implements PsiCode
|
||||
}
|
||||
return !conflict.get();
|
||||
}
|
||||
}, ResolveState.initial(), this, this);
|
||||
}, PatternResolveState.WHEN_BOTH.putInto(ResolveState.initial()), this, this);
|
||||
|
||||
myClassesSet = set1 = classesSet.isEmpty() ? Collections.emptySet() : classesSet;
|
||||
myVariablesSet = set2 = localsSet.isEmpty() ? Collections.emptySet() : localsSet;
|
||||
|
||||
@@ -24,6 +24,9 @@ import com.intellij.psi.impl.source.tree.ChildRole;
|
||||
import com.intellij.psi.impl.source.tree.ElementType;
|
||||
import com.intellij.psi.impl.source.tree.JavaElementType;
|
||||
import com.intellij.psi.infos.MethodCandidateInfo;
|
||||
import com.intellij.psi.scope.ElementClassHint;
|
||||
import com.intellij.psi.scope.PatternResolveState;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.tree.ChildRoleBase;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
@@ -189,6 +192,23 @@ public class PsiConditionalExpressionImpl extends ExpressionPsiElement implement
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
|
||||
@NotNull ResolveState state,
|
||||
PsiElement lastParent,
|
||||
@NotNull PsiElement place) {
|
||||
ElementClassHint elementClassHint = processor.getHint(ElementClassHint.KEY);
|
||||
if (elementClassHint != null && !elementClassHint.shouldProcess(ElementClassHint.DeclarationKind.VARIABLE)) return true;
|
||||
PsiExpression condition = getCondition();
|
||||
if (lastParent == getThenExpression()) {
|
||||
return condition.processDeclarations(processor, PatternResolveState.WHEN_TRUE.putInto(state), null, place);
|
||||
}
|
||||
if (lastParent == getElseExpression()) {
|
||||
return condition.processDeclarations(processor, PatternResolveState.WHEN_FALSE.putInto(state), null, place);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PsiConditionalExpression:" + getText();
|
||||
|
||||
@@ -22,6 +22,8 @@ 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.impl.source.tree.TreeUtil;
|
||||
import com.intellij.psi.scope.ElementClassHint;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.tree.ChildRoleBase;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -130,6 +132,17 @@ public class PsiDoWhileStatementImpl extends PsiLoopStatementImpl implements Psi
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
|
||||
@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;
|
||||
return PsiWhileStatementImpl.processDeclarationsInLoopCondition(processor, state, place, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "PsiDoWhileStatement";
|
||||
|
||||
@@ -22,8 +22,9 @@ 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.impl.source.tree.TreeElement;
|
||||
import com.intellij.psi.scope.ElementClassHint;
|
||||
import com.intellij.psi.scope.PatternResolveState;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.scope.util.PsiScopesUtil;
|
||||
import com.intellij.psi.tree.ChildRoleBase;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -179,10 +180,24 @@ public class PsiForStatementImpl extends PsiLoopStatementImpl implements PsiForS
|
||||
@Override
|
||||
public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, @NotNull PsiElement place) {
|
||||
processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, this);
|
||||
if (lastParent == null || lastParent.getParent() != this)
|
||||
ElementClassHint elementClassHint = processor.getHint(ElementClassHint.KEY);
|
||||
if (elementClassHint != null && !elementClassHint.shouldProcess(ElementClassHint.DeclarationKind.VARIABLE)) return true;
|
||||
if (lastParent == null) {
|
||||
// Only patterns may introduce variables visible after loop
|
||||
return PsiWhileStatementImpl.processDeclarationsInLoopCondition(processor, state, place, this);
|
||||
}
|
||||
else if (lastParent.getParent() != this) {
|
||||
// Parent element should not see our vars
|
||||
return true;
|
||||
|
||||
return PsiScopesUtil.walkChildrenScopes(this, processor, state, lastParent, place);
|
||||
}
|
||||
PsiStatement initialization = getInitialization();
|
||||
if (initialization != null && lastParent != initialization) {
|
||||
if (!initialization.processDeclarations(processor, state, null, place)) return false;
|
||||
}
|
||||
PsiExpression condition = getCondition();
|
||||
if (condition != null && (lastParent == getBody() || lastParent == getUpdate())) {
|
||||
return condition.processDeclarations(processor, PatternResolveState.WHEN_TRUE.putInto(state), null, place);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,16 @@ package com.intellij.psi.impl.source.tree.java;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.controlFlow.*;
|
||||
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.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.PatternResolveState;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.tree.ChildRoleBase;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
@@ -193,6 +197,62 @@ public class PsiIfStatementImpl extends CompositePsiElement implements PsiIfStat
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
|
||||
@NotNull ResolveState state,
|
||||
PsiElement lastParent,
|
||||
@NotNull PsiElement place) {
|
||||
ElementClassHint elementClassHint = processor.getHint(ElementClassHint.KEY);
|
||||
if (elementClassHint != null && !elementClassHint.shouldProcess(ElementClassHint.DeclarationKind.VARIABLE)) return true;
|
||||
PsiExpression condition = getCondition();
|
||||
if (condition != null) {
|
||||
PsiStatement thenBranch = getThenBranch();
|
||||
PsiStatement elseBranch = getElseBranch();
|
||||
if (lastParent == null) {
|
||||
PsiScopeProcessor conditionProcessor;
|
||||
if (state.get(PatternResolveState.KEY) == PatternResolveState.WHEN_BOTH) {
|
||||
conditionProcessor = processor;
|
||||
}
|
||||
else {
|
||||
conditionProcessor = (element, s) -> {
|
||||
LOG.assertTrue(element instanceof PsiPatternVariable);
|
||||
ControlFlow flow;
|
||||
try {
|
||||
flow = ControlFlowFactory.getInstance(getProject()).getControlFlow(
|
||||
this, new LocalsControlFlowPolicy(this), false, false);
|
||||
}
|
||||
catch (AnalysisCanceledException e) {
|
||||
return true;
|
||||
}
|
||||
boolean thenCompletesNormally = canCompleteNormally(thenBranch, flow);
|
||||
boolean elseCompletesNormally = canCompleteNormally(elseBranch, flow);
|
||||
if (thenCompletesNormally == elseCompletesNormally ||
|
||||
PatternResolveState.fromBoolean(thenCompletesNormally) !=
|
||||
PatternResolveState.stateAtParent((PsiPatternVariable)element, condition)) {
|
||||
return true;
|
||||
}
|
||||
return processor.execute(element, s);
|
||||
};
|
||||
}
|
||||
return condition.processDeclarations(conditionProcessor, PatternResolveState.WHEN_BOTH.putInto(state), null, place);
|
||||
}
|
||||
if (lastParent == thenBranch) {
|
||||
return condition.processDeclarations(processor, PatternResolveState.WHEN_TRUE.putInto(state), null, place);
|
||||
}
|
||||
if (lastParent == elseBranch) {
|
||||
return condition.processDeclarations(processor, PatternResolveState.WHEN_FALSE.putInto(state), null, place);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean canCompleteNormally(PsiStatement branch, ControlFlow flow) {
|
||||
if (branch == null) return true;
|
||||
int startOffset = flow.getStartOffset(branch);
|
||||
int endOffset = flow.getEndOffset(branch);
|
||||
return startOffset != -1 && endOffset != -1 && ControlFlowUtil.canCompleteNormally(flow, startOffset, endOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PsiIfStatement";
|
||||
|
||||
@@ -6,6 +6,9 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.psi.*;
|
||||
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.PatternResolveState;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.tree.ChildRoleBase;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
@@ -81,6 +84,20 @@ public class PsiInstanceOfExpressionImpl extends ExpressionPsiElement implements
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
|
||||
@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;
|
||||
if (state.get(PatternResolveState.KEY) == PatternResolveState.WHEN_FALSE) return true;
|
||||
PsiPattern pattern = getPattern();
|
||||
if (pattern == null) return true;
|
||||
return pattern.processDeclarations(processor, state, null, place);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PsiInstanceofExpression";
|
||||
|
||||
@@ -20,8 +20,10 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.Constants;
|
||||
import com.intellij.psi.impl.source.tree.ChildRole;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.scope.ElementClassHint;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.tree.ChildRoleBase;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -92,6 +94,19 @@ public class PsiParenthesizedExpressionImpl extends ExpressionPsiElement impleme
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
|
||||
@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 expression = getExpression();
|
||||
if (expression == null) return true;
|
||||
return expression.processDeclarations(processor, state, null, place);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PsiParenthesizedExpression:" + getText();
|
||||
|
||||
@@ -6,8 +6,11 @@ import com.intellij.psi.impl.PsiImplUtil;
|
||||
import com.intellij.psi.impl.source.Constants;
|
||||
import com.intellij.psi.impl.source.tree.CompositePsiElement;
|
||||
import com.intellij.psi.impl.source.tree.JavaSharedImplUtil;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.SearchScope;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -40,6 +43,12 @@ public class PsiPatternVariableImpl extends CompositePsiElement implements PsiPa
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiPattern getPattern() {
|
||||
return ObjectUtils.tryCast(getParent(), PsiPattern.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInitializer(@Nullable PsiExpression initializer) throws IncorrectOperationException {
|
||||
throw new IncorrectOperationException();
|
||||
@@ -85,6 +94,11 @@ public class PsiPatternVariableImpl extends CompositePsiElement implements PsiPa
|
||||
return identifier.getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTextOffset() {
|
||||
return getNameIdentifier().getTextOffset();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiModifierList getModifierList() {
|
||||
@@ -96,6 +110,28 @@ public class PsiPatternVariableImpl extends CompositePsiElement implements PsiPa
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public SearchScope getUseScope() {
|
||||
PsiPattern pattern = getPattern();
|
||||
if (pattern != null) {
|
||||
PsiElement parent = pattern.getParent();
|
||||
while (parent instanceof PsiInstanceOfExpression || parent instanceof PsiParenthesizedExpression ||
|
||||
parent instanceof PsiConditionalExpression ||
|
||||
parent instanceof PsiPrefixExpression && ((PsiPrefixExpression)parent).getOperationTokenType().equals(EXCL) ||
|
||||
parent instanceof PsiPolyadicExpression &&
|
||||
(((PsiPolyadicExpression)parent).getOperationTokenType().equals(ANDAND) ||
|
||||
((PsiPolyadicExpression)parent).getOperationTokenType().equals(OROR))) {
|
||||
parent = parent.getParent();
|
||||
}
|
||||
if (parent instanceof PsiIfStatement || parent instanceof PsiConditionalLoopStatement) {
|
||||
return new LocalSearchScope(parent.getParent());
|
||||
}
|
||||
return new LocalSearchScope(parent);
|
||||
}
|
||||
return super.getUseScope();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PsiPatternVariable:" + getName();
|
||||
|
||||
@@ -22,6 +22,10 @@ import com.intellij.psi.impl.source.resolve.JavaResolveCache;
|
||||
import com.intellij.psi.impl.source.tree.ChildRole;
|
||||
import com.intellij.psi.impl.source.tree.ElementType;
|
||||
import com.intellij.psi.impl.source.tree.JavaElementType;
|
||||
import com.intellij.psi.scope.ElementClassHint;
|
||||
import com.intellij.psi.scope.PatternResolveState;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.scope.util.PsiScopesUtil;
|
||||
import com.intellij.psi.tree.ChildRoleBase;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
@@ -124,6 +128,30 @@ public class PsiPolyadicExpressionImpl extends ExpressionPsiElement implements P
|
||||
return operands;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
|
||||
@NotNull ResolveState state,
|
||||
PsiElement lastParent,
|
||||
@NotNull PsiElement place) {
|
||||
return processDeclarations(this, processor, state, lastParent, place);
|
||||
}
|
||||
|
||||
static boolean processDeclarations(@NotNull PsiPolyadicExpression expression,
|
||||
@NotNull PsiScopeProcessor processor,
|
||||
@NotNull ResolveState state,
|
||||
PsiElement lastParent,
|
||||
@NotNull PsiElement place) {
|
||||
IElementType tokenType = expression.getOperationTokenType();
|
||||
boolean and = tokenType.equals(JavaTokenType.ANDAND);
|
||||
boolean or = tokenType.equals(JavaTokenType.OROR);
|
||||
if (!and && !or) return true;
|
||||
ElementClassHint elementClassHint = processor.getHint(ElementClassHint.KEY);
|
||||
if (elementClassHint != null && !elementClassHint.shouldProcess(ElementClassHint.DeclarationKind.VARIABLE)) return true;
|
||||
PatternResolveState wantedHint = PatternResolveState.fromBoolean(and);
|
||||
if (state.get(PatternResolveState.KEY) == wantedHint.invert()) return true;
|
||||
return PsiScopesUtil.walkChildrenScopes(expression, processor, wantedHint.putInto(state), lastParent, place);
|
||||
}
|
||||
|
||||
private volatile PsiExpression[] cachedOperands;
|
||||
@Override
|
||||
public void clearCaches() {
|
||||
|
||||
@@ -21,6 +21,10 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.tree.ChildRole;
|
||||
import com.intellij.psi.impl.source.tree.ElementType;
|
||||
import com.intellij.psi.impl.source.tree.JavaElementType;
|
||||
import com.intellij.psi.scope.ElementClassHint;
|
||||
import com.intellij.psi.scope.PatternResolveState;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.scope.util.PsiScopesUtil;
|
||||
import com.intellij.psi.tree.ChildRoleBase;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -105,6 +109,19 @@ public class PsiPrefixExpressionImpl extends ExpressionPsiElement implements Psi
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
|
||||
@NotNull ResolveState state,
|
||||
PsiElement lastParent,
|
||||
@NotNull PsiElement place) {
|
||||
if (lastParent != null || !getOperationTokenType().equals(JavaTokenType.EXCL)) return true;
|
||||
ElementClassHint elementClassHint = processor.getHint(ElementClassHint.KEY);
|
||||
if (elementClassHint != null && !elementClassHint.shouldProcess(ElementClassHint.DeclarationKind.VARIABLE)) return true;
|
||||
PatternResolveState hint = state.get(PatternResolveState.KEY);
|
||||
if (hint == null) return true;
|
||||
return PsiScopesUtil.walkChildrenScopes(this, processor, hint.invert().putInto(state), null, place);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PsiPrefixExpression:" + getText();
|
||||
|
||||
@@ -4,6 +4,7 @@ package com.intellij.psi.impl.source.tree.java;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.Constants;
|
||||
import com.intellij.psi.impl.source.tree.CompositePsiElement;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -40,6 +41,18 @@ public class PsiTypeTestPatternImpl extends CompositePsiElement implements PsiTy
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent,
|
||||
@NotNull PsiElement place) {
|
||||
processor.handleEvent(PsiScopeProcessor.Event.SET_DECLARATION_HOLDER, this);
|
||||
|
||||
PsiPatternVariable variable = getPatternVariable();
|
||||
if (variable != null && variable != lastParent) {
|
||||
return processor.execute(variable, state);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PsiTypeTestPattern";
|
||||
|
||||
@@ -21,8 +21,12 @@ import com.intellij.psi.*;
|
||||
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.PatternResolveState;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.tree.ChildRoleBase;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class PsiWhileStatementImpl extends PsiLoopStatementImpl implements PsiWhileStatement, Constants {
|
||||
@@ -112,6 +116,42 @@ public class PsiWhileStatementImpl extends PsiLoopStatementImpl implements PsiWh
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
|
||||
@NotNull ResolveState state,
|
||||
PsiElement lastParent,
|
||||
@NotNull PsiElement place) {
|
||||
ElementClassHint elementClassHint = processor.getHint(ElementClassHint.KEY);
|
||||
if (elementClassHint != null && !elementClassHint.shouldProcess(ElementClassHint.DeclarationKind.VARIABLE)) return true;
|
||||
if (lastParent == null) {
|
||||
return processDeclarationsInLoopCondition(processor, state, place, this);
|
||||
}
|
||||
PsiExpression condition = getCondition();
|
||||
if (condition != null && lastParent == getBody()) {
|
||||
return condition.processDeclarations(processor, PatternResolveState.WHEN_TRUE.putInto(state), null, place);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean processDeclarationsInLoopCondition(@NotNull PsiScopeProcessor processor,
|
||||
@NotNull ResolveState state,
|
||||
@NotNull PsiElement place,
|
||||
@NotNull PsiConditionalLoopStatement loop) {
|
||||
PsiExpression condition = loop.getCondition();
|
||||
if (condition == null) return true;
|
||||
PsiScopeProcessor conditionProcessor = (element, s) -> {
|
||||
assert element instanceof PsiPatternVariable;
|
||||
PatternResolveState resolveState = PatternResolveState.stateAtParent((PsiPatternVariable)element, condition);
|
||||
if (resolveState == PatternResolveState.WHEN_TRUE ||
|
||||
!PsiTreeUtil
|
||||
.processElements(loop, e -> !(e instanceof PsiBreakStatement) || ((PsiBreakStatement)e).findExitedStatement() != loop)) {
|
||||
return true;
|
||||
}
|
||||
return processor.execute(element, s);
|
||||
};
|
||||
return condition.processDeclarations(conditionProcessor, PatternResolveState.WHEN_BOTH.putInto(state), null, place);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "PsiWhileStatement";
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright 2000-2019 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.scope;
|
||||
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public enum PatternResolveState {
|
||||
WHEN_TRUE, WHEN_FALSE, WHEN_BOTH;
|
||||
|
||||
public static final Key<PatternResolveState> KEY = Key.create("JavaPatternDeclarationHint");
|
||||
|
||||
public static PatternResolveState fromBoolean(boolean value) {
|
||||
return value ? WHEN_TRUE : WHEN_FALSE;
|
||||
}
|
||||
|
||||
public PatternResolveState invert() {
|
||||
switch (this) {
|
||||
case WHEN_TRUE:
|
||||
return WHEN_FALSE;
|
||||
case WHEN_FALSE:
|
||||
return WHEN_TRUE;
|
||||
case WHEN_BOTH:
|
||||
return WHEN_BOTH;
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected value: " + this);
|
||||
}
|
||||
}
|
||||
|
||||
public ResolveState putInto(ResolveState rs) {
|
||||
return rs.put(KEY, this);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static PatternResolveState stateAtParent(PsiPatternVariable element, PsiExpression parent) {
|
||||
PsiPattern pattern = element.getPattern();
|
||||
if (pattern == null) {
|
||||
throw new IllegalArgumentException("Variable has no pattern associated");
|
||||
}
|
||||
PatternResolveState state = WHEN_TRUE;
|
||||
for (PsiElement prev = pattern, current = prev.getParent(); prev != parent; prev = current, current = current.getParent()) {
|
||||
if (current instanceof PsiInstanceOfExpression || current instanceof PsiParenthesizedExpression ||
|
||||
current instanceof PsiPolyadicExpression &&
|
||||
(((PsiPolyadicExpression)current).getOperationTokenType() == JavaTokenType.ANDAND ||
|
||||
((PsiPolyadicExpression)current).getOperationTokenType() == JavaTokenType.OROR)) {
|
||||
continue;
|
||||
}
|
||||
if (current instanceof PsiPrefixExpression &&
|
||||
((PsiPrefixExpression)current).getOperationTokenType() == JavaTokenType.EXCL) {
|
||||
state = state.invert();
|
||||
continue;
|
||||
}
|
||||
throw new IllegalArgumentException("Variable is not available at parent");
|
||||
}
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
class X {
|
||||
void expressions(Object obj) {
|
||||
boolean b1 = obj instanceof String s && s.isEmpty();
|
||||
boolean b2 = !(obj instanceof String s) && <error descr="Cannot resolve symbol 's'">s</error>.isEmpty();
|
||||
boolean b3 = obj instanceof String s || <error descr="Cannot resolve symbol 's'">s</error>.isEmpty();
|
||||
boolean b4 = !(obj instanceof String s) || s.isEmpty();
|
||||
boolean b5 = obj instanceof String s ? s.isEmpty() : obj == null;
|
||||
boolean b6 = !(obj instanceof String s) ? obj == null : s.isEmpty();
|
||||
boolean b7 = obj instanceof String s ? s.isEmpty() : <error descr="Cannot resolve symbol 's'">s</error>.isEmpty();
|
||||
boolean b8 = !(obj instanceof String s) ? <error descr="Cannot resolve symbol 's'">s</error>.isEmpty() : s.isEmpty();
|
||||
}
|
||||
|
||||
void twoPatterns(Object o1, Object o2) {
|
||||
if (o1 instanceof String s1 && o2 instanceof String s2 && s1.startsWith(s2)) {}
|
||||
if ((o1 instanceof String s1 && o2 instanceof String s2) && s1.startsWith(s2)) {}
|
||||
if (o1 instanceof String s1 && (o2 instanceof String s2 && s1.startsWith(s2))) {}
|
||||
if (o1 instanceof String s1 && !(o2 instanceof String s2) && s1.startsWith(<error descr="Cannot resolve symbol 's2'">s2</error>)) {}
|
||||
}
|
||||
|
||||
void polyadicInCondition(Object o1, Object o2) {
|
||||
boolean b1 = o1 instanceof String s1 && o2 instanceof String s2 ? s1.isEmpty() && s2.isEmpty() : false;
|
||||
boolean b2 = o1 instanceof String s1 && !(o2 instanceof String s2) ? s1.isEmpty() : <error descr="Cannot resolve symbol 's2'">s2</error>.isEmpty();
|
||||
}
|
||||
|
||||
void ifThenSimple(Object o) {
|
||||
if (o instanceof String s) {
|
||||
System.out.println(s.trim());
|
||||
} else {
|
||||
System.out.println(<error descr="Cannot resolve symbol 's'">s</error>.trim());
|
||||
}
|
||||
if (!(o instanceof String s)) {
|
||||
System.out.println(<error descr="Cannot resolve symbol 's'">s</error>.trim());
|
||||
} else {
|
||||
System.out.println(s.trim());
|
||||
}
|
||||
}
|
||||
|
||||
interface Node {
|
||||
Object next();
|
||||
String name();
|
||||
}
|
||||
|
||||
void whileSimple(Object o) {
|
||||
while (o instanceof Node n) {
|
||||
o = n.next();
|
||||
}
|
||||
}
|
||||
|
||||
void whileNot(Object o) {
|
||||
while (!(o instanceof Node n)) {
|
||||
o = <error descr="Cannot resolve symbol 'n'">n</error>.next();
|
||||
}
|
||||
}
|
||||
|
||||
void forSimple(Object o) {
|
||||
for (; o instanceof Node n; o = n.next()) {
|
||||
System.out.println(n.name());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
class X {
|
||||
|
||||
void simpleIf(Object obj) {
|
||||
if (!(obj instanceof String s)) return;
|
||||
System.out.println(s.trim());
|
||||
}
|
||||
|
||||
void ifElse(Object obj) {
|
||||
if (!(obj instanceof String s)) return;
|
||||
else {
|
||||
System.out.println(s.trim());
|
||||
}
|
||||
System.out.println(s.trim());
|
||||
}
|
||||
|
||||
static boolean FLAG2 = true;
|
||||
static final boolean FLAG = true;
|
||||
|
||||
void ifElseInfiniteLoop(Object obj) {
|
||||
if (obj instanceof String s) {}
|
||||
else {
|
||||
while (FLAG) {
|
||||
System.out.println("oops");
|
||||
}
|
||||
}
|
||||
System.out.println(s);
|
||||
}
|
||||
|
||||
void ifElseInfiniteLoopLocal(Object obj) {
|
||||
final boolean FLAG = true;
|
||||
if (obj instanceof String s) {}
|
||||
else {
|
||||
while (FLAG) {
|
||||
System.out.println("oops");
|
||||
}
|
||||
}
|
||||
System.out.println(s);
|
||||
}
|
||||
|
||||
void ifElseFiniteLoop(Object obj) {
|
||||
if (obj instanceof String s) {}
|
||||
else {
|
||||
while (FLAG2) {
|
||||
System.out.println("oops");
|
||||
}
|
||||
}
|
||||
System.out.println(<error descr="Cannot resolve symbol 's'">s</error>);
|
||||
}
|
||||
|
||||
void ifElseInfiniteSelfRef(Object obj) {
|
||||
if (obj instanceof Boolean b) {
|
||||
while(b) {}
|
||||
}
|
||||
else {
|
||||
while(<error descr="Cannot resolve symbol 'b'">b</error>) {}
|
||||
}
|
||||
while(<error descr="Cannot resolve symbol 'b'">b</error>) {}
|
||||
}
|
||||
|
||||
class Shadow {
|
||||
String b;
|
||||
|
||||
void ifElseInfiniteSelfRef(Object obj) {
|
||||
if (obj instanceof Boolean b) {
|
||||
while(b) {}
|
||||
}
|
||||
else {
|
||||
while(b == "") {}
|
||||
}
|
||||
while(b == "") {}
|
||||
}
|
||||
|
||||
void inverted(Object obj) {
|
||||
if (!(obj instanceof Integer b)) {}
|
||||
System.out.println(b.trim());
|
||||
}
|
||||
}
|
||||
|
||||
native Object getNextObj();
|
||||
|
||||
void testWhile(Object obj) {
|
||||
while (!(obj instanceof Integer x)) {
|
||||
obj = getNextObj();
|
||||
}
|
||||
System.out.println(x.intValue());
|
||||
}
|
||||
|
||||
void testWhileWithBreak(Object obj) {
|
||||
while (!(obj instanceof Integer x)) {
|
||||
obj = getNextObj();
|
||||
if (obj instanceof String) break;
|
||||
}
|
||||
System.out.println(<error descr="Cannot resolve symbol 'x'">x</error>.intValue());
|
||||
}
|
||||
|
||||
void testDoWhile(Object obj) {
|
||||
do {
|
||||
obj = getNextObj();
|
||||
if (obj instanceof String) break;
|
||||
}
|
||||
while (!(obj instanceof Integer x));
|
||||
System.out.println(<error descr="Cannot resolve symbol 'x'">x</error>.intValue());
|
||||
}
|
||||
|
||||
void testDoWhileWithBreak(Object obj) {
|
||||
do {
|
||||
obj = getNextObj();
|
||||
if (obj instanceof String) break;
|
||||
}
|
||||
while (!(obj instanceof Integer x));
|
||||
System.out.println(<error descr="Cannot resolve symbol 'x'">x</error>.intValue());
|
||||
}
|
||||
|
||||
void testFor() {
|
||||
for (Object obj = getNextObj(); !(obj instanceof String s); obj = getNextObj()) {
|
||||
System.out.println("going further");
|
||||
}
|
||||
System.out.println("Found: "+s.trim());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
class X {
|
||||
void polyadic(Object o1, Object o2) {
|
||||
boolean b1 = o1 instanceof String s && o2 instanceof String <error descr="Variable 's' is already defined in the scope">s</error>;
|
||||
boolean b2 = !(o1 instanceof String s) && !(o2 instanceof String <error descr="Variable 's' is already defined in the scope">s</error>);
|
||||
boolean b3 = !(o1 instanceof String s) || !(o2 instanceof String <error descr="Variable 's' is already defined in the scope">s</error>);
|
||||
boolean b4 = o1 instanceof String s || o2 instanceof String <error descr="Variable 's' is already defined in the scope">s</error>;
|
||||
|
||||
// Dubious cases: spec is not very clear about whether this should be accepted
|
||||
boolean b5 = o1 instanceof String s && !(o2 instanceof String <error descr="Variable 's' is already defined in the scope">s</error>);
|
||||
boolean b6 = o1 instanceof String s && (!(o2 instanceof String <error descr="Variable 's' is already defined in the scope">s</error>) || s.isEmpty());
|
||||
boolean b7 = !(o2 instanceof String s) && o1 instanceof String s;
|
||||
boolean b8 = (!(o2 instanceof String s) || s.isEmpty()) && o1 instanceof String s && s.isEmpty();
|
||||
}
|
||||
|
||||
void ternary(Object o1, Object o2, Object o3) {
|
||||
// Currently all these samples are accepted by javac
|
||||
boolean b1 = o1 instanceof String s ? o2 instanceof String <error descr="Variable 's' is already defined in the scope">s</error> : o3 instanceof String s1;
|
||||
boolean b2 = o1 instanceof String s ? o2 instanceof String s1 : o3 instanceof String <error descr="Variable 's' is already defined in the scope">s</error>;
|
||||
boolean b3 = o1 instanceof String s1 ? o2 instanceof String s : o3 instanceof String <error descr="Variable 's' is already defined in the scope">s</error>;
|
||||
boolean b4 = !(o1 instanceof String s) ? o2 instanceof String <error descr="Variable 's' is already defined in the scope">s</error> : o3 instanceof String s1;
|
||||
boolean b5 = !(o1 instanceof String s) ? o2 instanceof String s1 : o3 instanceof String <error descr="Variable 's' is already defined in the scope">s</error>;
|
||||
boolean b6 = !(o1 instanceof String s1) ? o2 instanceof String s : o3 instanceof String <error descr="Variable 's' is already defined in the scope">s</error>;
|
||||
}
|
||||
|
||||
void ifElse(Object o1, Object o2) {
|
||||
if (o1 instanceof String s) {
|
||||
if (o2 instanceof String <error descr="Variable 's' is already defined in the scope">s</error>) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2000-2019 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.java.codeInsight.daemon;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class LightPatternsHighlightingTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/advHighlightingPatterns";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return JAVA_14;
|
||||
}
|
||||
|
||||
public void testInstanceOfBasics() {
|
||||
doTest();
|
||||
}
|
||||
public void testInstanceOfNameConflicts() {
|
||||
doTest();
|
||||
}
|
||||
public void testInstanceOfControlFlow() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() {
|
||||
myFixture.configureByFile(getTestName(false) + ".java");
|
||||
myFixture.checkHighlighting();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user