Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -373,9 +373,9 @@ protected boolean needToCheckFlowInAbsenceOfDefaultBranch() {
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
try {
flowInfo = this.expression.analyseCode(currentScope, flowContext, flowInfo);
if (isNullHostile()) {
if (!this.containsNull && this.expression.resolvedType instanceof ReferenceBinding)
this.expression.checkNPE(currentScope, flowContext, flowInfo, 1);
}

SwitchFlowContext switchContext =
new SwitchFlowContext(flowContext, this, (this.breakLabel = new BranchLabel()), true, true);

Expand Down Expand Up @@ -461,16 +461,6 @@ else if ((statement.bits & ASTNode.DocumentedFallthrough) == 0) // the case is n
if (this.scope != null) this.scope.enclosingCase = null; // no longer inside switch case block
}
}
private boolean isNullHostile() {
if (this.containsNull)
return false;
if ((this.expression.implicitConversion & TypeIds.UNBOXING) != 0)
return true;
if (this.expression.resolvedType != null && (this.expression.resolvedType.id == T_JavaLangString || this.expression.resolvedType.isEnum()))
return true;
return this.totalPattern == null;
}

/**
* Switch on String code generation
* This assumes that hashCode() specification for java.lang.String is API
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
return patternInfo; // exclude anonymous blokes from flow analysis.

patternInfo.markAsDefinitelyAssigned(this.local.binding);
if (!this.isTotalTypeNode) {
// non-total type patterns create a nonnull local:
patternInfo.markAsDefinitelyNonNull(this.local.binding);
} else {
// total type patterns inherit the nullness of the value being switched over, unless ...
if (flowContext.associatedNode instanceof SwitchStatement swStmt) {
int nullStatus = swStmt.containsNull
? FlowInfo.NON_NULL // ... null is handled in a separate case
: swStmt.expression.nullStatus(patternInfo, flowContext);
patternInfo.markNullStatus(this.local.binding, nullStatus);
}
}
patternInfo.markAsDefinitelyNonNull(this.local.binding);
return patternInfo;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,29 +107,23 @@ public void test_totalTypePatternDoesNotAdmitNull() {
" case Number n0 -> consumeNumber(n0);\n" +
" }\n" +
" } catch (NullPointerException npe) {\n" +
" // ignoring the unchecked warning, and expecting the NPE:\n" +
" // Expecting an NPE because selector is null\n" +
" System.out.print(npe.getMessage());\n" +
" }\n" +
" }\n" +
" void consumeInt(@NonNull Integer i) {\n" +
" System.out.print(i);\n" +
" }\n" +
" void consumeNumber(@NonNull Number n) {\n" +
" System.out.println(\"consumeNumber \");\n" +
" System.out.print(n.toString());\n" +
" }\n" +
" public static void main(String... args) {\n" +
" new X().foo(null);\n" +
" }\n" +
"}\n"
};
runner.expectedCompilerLog =
"----------\n" +
"1. WARNING in X.java (at line 7)\n" +
" case Number n0 -> consumeNumber(n0);\n" +
" ^^\n" +
"Null type safety (type annotations): The expression of type \'Number\' needs unchecked conversion to conform to \'@NonNull Number\'\n" +
"----------\n";
// runner.expectedOutputString = "Cannot invoke \"Object.toString()\" because \"n\" is null";
runner.expectedCompilerLog = "";
runner.expectedOutputString = "null";
runner.runConformTest();
}
Expand Down Expand Up @@ -234,10 +228,10 @@ public void test_totalTypePatternNullableExpression() {
};
runner.expectedCompilerLog =
"----------\n" +
"1. ERROR in X.java (at line 6)\n" +
" case Number n0 -> consumeNumber(n0);\n" +
" ^^\n" +
"Null type mismatch: required \'@NonNull Number\' but the provided value is inferred as @Nullable\n" +
"1. ERROR in X.java (at line 4)\n" +
" switch (n) {\n" +
" ^\n" +
"Potential null pointer access: this expression has a '@Nullable' type\n" +
"----------\n";
runner.runNegativeTest();
}
Expand Down Expand Up @@ -1244,4 +1238,46 @@ static Stuff match(PatternMatching pm, int v) {
""";
runner.runNegativeTest();
}

// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3381
// [Enhanced Switch][Null] Missing Null pointer access warning with total/unconditional patterns
public void testIssue3381() {
Runner runner = getDefaultRunner();
runner.testFiles = new String[] {
"X.java",
"""
import org.eclipse.jdt.annotation.Nullable;

public class X {
void foo() {
@Nullable Integer i = null;
switch (i) {
case Integer ii -> System.out.println();
}
}
void goo() {
@Nullable Integer i = null;
switch (i) {
default -> System.out.println();
}
}
}
"""
};
runner.expectedCompilerLog =
"""
----------
1. ERROR in X.java (at line 6)
switch (i) {
^
Null pointer access: The variable i can only be null at this location
----------
2. ERROR in X.java (at line 12)
switch (i) {
^
Null pointer access: This expression of type Integer is null but requires auto-unboxing
----------
Comment on lines +1269 to +1279
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@srikanth-sankaran do you have an idea why these are different error messages?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@srikanth-sankaran do you have an idea why these are different error messages?

Yes, I had looked into this. In the former case its is a type switch which doesn't call for unboxing and in the latter switch on the unboxed integer. This results in a special message in the latter case.

""";
runner.runNegativeTest();
}
}
Loading