mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-dfa] Fix nullability of nested generic patterns (IDEA-339860)
GitOrigin-RevId: 3c035ab282515ad39b0fdff167d8105297c48515
This commit is contained in:
committed by
intellij-monorepo-bot
parent
7c438ed138
commit
04ab76833e
+3
-2
@@ -128,8 +128,9 @@ public final class NullabilityUtil {
|
||||
}
|
||||
if (expression instanceof PsiReferenceExpression ref) {
|
||||
PsiElement target = ref.resolve();
|
||||
if (target instanceof PsiPatternVariable) {
|
||||
return Nullability.NOT_NULL; // currently all pattern variables are not-null
|
||||
if (target instanceof PsiPatternVariable patternVariable &&
|
||||
!(patternVariable.getPattern().getParent() instanceof PsiDeconstructionList)) {
|
||||
return Nullability.NOT_NULL; // currently top-level pattern variables are not-null
|
||||
}
|
||||
if (dumb) return Nullability.UNKNOWN;
|
||||
if (target instanceof PsiLocalVariable || target instanceof PsiParameter) {
|
||||
|
||||
+19
-13
@@ -5,6 +5,7 @@ import com.intellij.codeInsight.ExceptionUtil;
|
||||
import com.intellij.codeInsight.Nullability;
|
||||
import com.intellij.codeInsight.daemon.ImplicitUsageProvider;
|
||||
import com.intellij.codeInsight.daemon.impl.UnusedSymbolUtil;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
|
||||
import com.intellij.codeInspection.dataFlow.*;
|
||||
import com.intellij.codeInspection.dataFlow.java.anchor.JavaExpressionAnchor;
|
||||
import com.intellij.codeInspection.dataFlow.java.anchor.JavaPolyadicPartAnchor;
|
||||
@@ -1142,19 +1143,24 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
addInstruction(new PopInstruction());
|
||||
|
||||
PsiPattern[] components = deconstructionPattern.getDeconstructionList().getDeconstructionComponents();
|
||||
PsiClass recordClass = PsiUtil.resolveClassInClassTypeOnly(patternType);
|
||||
if (recordClass != null && recordClass.isRecord()) {
|
||||
PsiRecordComponent[] recordComponents = recordClass.getRecordComponents();
|
||||
if (components.length == recordComponents.length) {
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
PsiRecordComponent recordComponent = recordComponents[i];
|
||||
PsiPattern patternComponent = components[i];
|
||||
PsiMethod accessor = JavaPsiRecordUtil.getAccessorForRecordComponent(recordComponent);
|
||||
if (accessor == null) continue;
|
||||
DfaVariableValue accessorDfaVar =
|
||||
getFactory().getVarFactory().createVariableValue(new GetterDescriptor(accessor), patternDfaVar);
|
||||
addInstruction(new JvmPushInstruction(accessorDfaVar, null));
|
||||
processPattern(sourcePattern, patternComponent, recordComponent.getType(), null, endPatternOffset);
|
||||
if (patternType instanceof PsiClassType patternClassType) {
|
||||
PsiClassType.ClassResolveResult resolveResult = patternClassType.resolveGenerics();
|
||||
PsiClass recordClass = resolveResult.getElement();
|
||||
boolean unchecked = JavaGenericsUtil.isUncheckedCast(patternClassType, checkType);
|
||||
PsiSubstitutor substitutor = unchecked ? PsiSubstitutor.EMPTY : resolveResult.getSubstitutor();
|
||||
if (recordClass != null && recordClass.isRecord()) {
|
||||
PsiRecordComponent[] recordComponents = recordClass.getRecordComponents();
|
||||
if (components.length == recordComponents.length) {
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
PsiRecordComponent recordComponent = recordComponents[i];
|
||||
PsiPattern patternComponent = components[i];
|
||||
PsiMethod accessor = JavaPsiRecordUtil.getAccessorForRecordComponent(recordComponent);
|
||||
if (accessor == null) continue;
|
||||
DfaVariableValue accessorDfaVar =
|
||||
getFactory().getVarFactory().createVariableValue(new GetterDescriptor(accessor), patternDfaVar);
|
||||
addInstruction(new JvmPushInstruction(accessorDfaVar, null));
|
||||
processPattern(sourcePattern, patternComponent, substitutor.substitute(recordComponent.getType()), null, endPatternOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
class Test {
|
||||
static void check(Result<CharSequence> result) {
|
||||
switch (result) {
|
||||
case Result.Ok(String string) -> {
|
||||
if (<warning descr="Condition 'string == null' is always 'false'">string == null</warning>) {}
|
||||
}
|
||||
case Result.Ok(CharSequence cs) -> {
|
||||
if (cs == null) {
|
||||
System.out.println("Null");
|
||||
}
|
||||
}
|
||||
case Result.Err(RuntimeException throwable) -> {
|
||||
if (throwable == null) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
check(Result.ok(<warning descr="Passing 'null' argument to non-annotated parameter">null</warning>));
|
||||
}
|
||||
}
|
||||
sealed interface Result<T> {
|
||||
record Ok<R>(R value) implements Result<R> {
|
||||
public R get() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
record Err<E>(RuntimeException throwable) implements Result<E> {
|
||||
public E get() {
|
||||
throw throwable;
|
||||
}
|
||||
}
|
||||
|
||||
static <OK> Result<OK> ok(OK value) {
|
||||
return new Ok<>(value);
|
||||
}
|
||||
|
||||
static <ERR> Result<ERR> err(RuntimeException throwable) {
|
||||
return new Err<>(throwable);
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,10 @@ public class DataFlowInspection21Test extends DataFlowInspectionTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testDeconstructionNullability() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testUnnamedPatterns() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user