Skip to content

Commit fb6cd91

Browse files
committed
fix for null analysis issue for conditional expressions - fixes
issue eclipse-jdt#3990
1 parent c43910a commit fb6cd91

File tree

6 files changed

+451
-3
lines changed

6 files changed

+451
-3
lines changed

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2089,6 +2089,8 @@ public interface IProblem {
20892089
/** @since 3.41 */
20902090
int FieldWithUnresolvedOwningAnnotation = Internal + 989;
20912091

2092+
/** @since 3.42 */
2093+
int RedundantNullCheckNullValueExpression = Internal + 990;
20922094

20932095
// Java 8 work
20942096
/** @since 3.10 */

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2020 IBM Corporation and others.
2+
* Copyright (c) 2000, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -32,6 +32,7 @@
3232
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
3333
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
3434
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
35+
import org.eclipse.jdt.internal.compiler.lookup.NullTypeBinding;
3536
import org.eclipse.jdt.internal.compiler.lookup.Scope;
3637
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
3738
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
@@ -80,6 +81,10 @@ && shouldPerformSyntacticAnalsysisFor(scope, reference))
8081
if (field != null && (field.type.tagBits & TagBits.IsBaseType) == 0) {
8182
flowContext.recordNullCheckedFieldReference((Reference) this.left, 1);
8283
}
84+
} else if (this.left instanceof ConditionalExpression ce) {
85+
if ((ce.resolvedType.tagBits & TagBits.IsBaseType) == 0 || ce.resolvedType instanceof NullTypeBinding) {
86+
checkConditionalExpressionComparison(scope, flowContext, flowInfo, rightStatus, this.left);
87+
}
8388
}
8489
}
8590
if (!rightNonNullChecked) {
@@ -96,6 +101,10 @@ && shouldPerformSyntacticAnalsysisFor(scope, reference))
96101
if (field != null && (field.type.tagBits & TagBits.IsBaseType) == 0) {
97102
flowContext.recordNullCheckedFieldReference((Reference) this.right, 1);
98103
}
104+
} else if (this.right instanceof ConditionalExpression ce) {
105+
if ((ce.resolvedType.tagBits & TagBits.IsBaseType) == 0 || ce.resolvedType instanceof NullTypeBinding) {
106+
checkConditionalExpressionComparison(scope, flowContext, flowInfo, leftStatus, this.right);
107+
}
99108
}
100109
}
101110

@@ -175,6 +184,32 @@ private void checkVariableComparison(BlockScope scope, FlowContext flowContext,
175184
// we do not impact enclosing try context because this kind of protection
176185
// does not preclude the variable from being null in an enclosing scope
177186
}
187+
private void checkConditionalExpressionComparison(BlockScope scope,
188+
FlowContext flowContext, FlowInfo flowInfo,
189+
int nullStatus, Expression reference) {
190+
191+
192+
switch (nullStatus) {
193+
case FlowInfo.NULL :
194+
if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
195+
flowContext.flagConditional(scope, reference,
196+
FlowContext.CAN_ONLY_NULL | FlowContext.IN_COMPARISON_NULL, flowInfo);
197+
} else {
198+
flowContext.flagConditional(scope, reference,
199+
FlowContext.CAN_ONLY_NULL | FlowContext.IN_COMPARISON_NON_NULL, flowInfo);
200+
}
201+
break;
202+
case FlowInfo.NON_NULL :
203+
if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
204+
flowContext.flagConditional(scope, reference,
205+
FlowContext.CAN_ONLY_NULL_NON_NULL | FlowContext.IN_COMPARISON_NON_NULL, flowInfo);
206+
} else {
207+
flowContext.flagConditional(scope, reference,
208+
FlowContext.CAN_ONLY_NULL_NON_NULL | FlowContext.IN_COMPARISON_NULL, flowInfo);
209+
}
210+
break;
211+
}
212+
}
178213
private void analyzeLocalVariable(Expression exp, FlowInfo flowInfo) {
179214
if (exp instanceof SingleNameReference && (exp.bits & Binding.LOCAL) != 0 ) {
180215
LocalVariableBinding localBinding = (LocalVariableBinding) ((SingleNameReference ) exp).binding;

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/flow/FlowContext.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2020 IBM Corporation and others.
2+
* Copyright (c) 2000, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -1023,6 +1023,41 @@ else if (flowInfo.cannotBeDefinitelyNullOrNonNull(local)) {
10231023
}
10241024
}
10251025

1026+
public void flagConditional(Scope scope, Expression conditionalExpression,
1027+
int checkType, FlowInfo flowInfo) {
1028+
1029+
// if inside an assert, we will not raise redundant null check warnings
1030+
checkType |= (this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING);
1031+
int checkTypeWithoutHideNullWarning = checkType & ~FlowContext.HIDE_NULL_COMPARISON_WARNING_MASK;
1032+
switch (checkTypeWithoutHideNullWarning) {
1033+
case FlowContext.CAN_ONLY_NULL_NON_NULL | FlowContext.IN_COMPARISON_NULL:
1034+
if ((checkType & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
1035+
scope.problemReporter().expressionRedundantNullComparison(conditionalExpression, /* isExpressionNull */false );
1036+
}
1037+
flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
1038+
break;
1039+
case FlowContext.CAN_ONLY_NULL_NON_NULL | FlowContext.IN_COMPARISON_NON_NULL:
1040+
if ((checkType & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
1041+
scope.problemReporter().expressionRedundantNullComparison(conditionalExpression, /* isExpressionNull */false);
1042+
}
1043+
flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
1044+
break;
1045+
1046+
case FlowContext.CAN_ONLY_NULL | FlowContext.IN_COMPARISON_NULL:
1047+
if ((checkType & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
1048+
scope.problemReporter().expressionRedundantNullComparison(conditionalExpression, /* isExpressionNull */true);
1049+
}
1050+
flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
1051+
break;
1052+
case FlowContext.CAN_ONLY_NULL | FlowContext.IN_COMPARISON_NON_NULL:
1053+
if ((checkType & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
1054+
scope.problemReporter().expressionRedundantNullComparison(conditionalExpression, /* isExpressionNull */true);
1055+
}
1056+
flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
1057+
break;
1058+
}
1059+
}
1060+
10261061
void removeFinalAssignmentIfAny(Reference reference) {
10271062
// default implementation: do nothing
10281063
}

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6061,6 +6061,30 @@ public void localVariableNullComparedToNonNull(LocalVariableBinding local, ASTNo
60616061
nodeSourceEnd(local, location));
60626062
}
60636063

6064+
/**
6065+
* @param expr expression being compared for null or non-null
6066+
* @param isExpressionNull true if flow info shows null, false if flow info is non-null
6067+
*/
6068+
6069+
public void expressionRedundantNullComparison(Expression expr, boolean isExpressionNull) {
6070+
if (expr.resolvedType == null)
6071+
return;
6072+
6073+
int problemId = isExpressionNull ? IProblem.RedundantNullCheckNullValueExpression
6074+
: IProblem.RedundantNullCheckOnNonNullExpression;
6075+
int severity = computeSeverity(problemId);
6076+
if (severity == ProblemSeverities.Ignore) return;
6077+
String[] arguments = new String[] {new String(expr.toString())};
6078+
this.handle(
6079+
problemId,
6080+
arguments,
6081+
arguments,
6082+
severity,
6083+
nodeSourceStart(expr),
6084+
nodeSourceEnd(expr));
6085+
}
6086+
6087+
60646088
/**
60656089
* @param expr expression being compared for null or nonnull
60666090
* @param checkForNull true if checking for null, false if checking for nonnull

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,9 @@
876876
988 = Parameter ''{0}'' of method ''{1}'' has an unresolved annotation ''{2}'' that could be relevant for static analysis
877877
989 = Field ''{0}'' has an unresolved annotation ''{1}'' that could be relevant for static analysis
878878

879+
# null analysis for conditional expressions
880+
990 = Redundant null check: this expression can only be null
881+
879882
# Java 8
880883
1001 = Syntax error, modifiers and annotations are not allowed for the lambda parameter {0} as its type is elided
881884
1002 = Syntax error, modifiers are not allowed here

0 commit comments

Comments
 (0)