Skip to content

Commit

Permalink
[compiler] Unnecessary cast warning is not detected when casting a bound
Browse files Browse the repository at this point in the history
generic type to its super type

fixes #2470
  • Loading branch information
stephan-herrmann committed May 21, 2024
1 parent eddfb7d commit 4221238
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -807,25 +807,14 @@ public TypeBinding resolveType(BlockScope scope) {
if (this.constant != Constant.NotAConstant) {
this.constant = Constant.NotAConstant;
long sourceLevel = scope.compilerOptions().sourceLevel;
boolean receiverCast = false;
if (this.receiver instanceof CastExpression) {
this.receiver.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
receiverCast = true;
}
this.actualReceiverType = this.receiver.resolveType(scope);
if (this.actualReceiverType instanceof InferenceVariable) {
return null; // not yet ready for resolving
}
this.receiverIsType = this.receiver.isType();
if (receiverCast && this.actualReceiverType != null) {
// due to change of declaring class with receiver type, only identity cast should be notified
TypeBinding resolvedType2 = ((CastExpression)this.receiver).expression.resolvedType;
if (TypeBinding.equalsEquals(resolvedType2, this.actualReceiverType)) {
if (!scope.environment().usesNullTypeAnnotations() || !NullAnnotationMatching.analyse(this.actualReceiverType, resolvedType2, -1).isAnyMismatch()) {
scope.problemReporter().unnecessaryCast((CastExpression) this.receiver);
}
}
}
// resolve type arguments (for generic constructor call)
if (this.typeArguments != null) {
int length = this.typeArguments.length;
Expand Down Expand Up @@ -977,6 +966,18 @@ public TypeBinding resolveType(BlockScope scope) {
return null;
}

if (this.receiver instanceof CastExpression) {
// this check was suppressed while resolving receiver, check now based on this.binding.declaringClass
TypeBinding uncastedReceiverType = ((CastExpression)this.receiver).expression.resolvedType;
if (uncastedReceiverType == null) {
assert this.outerInferenceContext != null : "null should only happen when nested in unfinished type inference"; //$NON-NLS-1$
} else if (uncastedReceiverType.isCompatibleWith(this.binding.declaringClass)) {
if (!scope.environment().usesNullTypeAnnotations() || !NullAnnotationMatching.analyse(this.actualReceiverType, uncastedReceiverType, -1).isAnyMismatch()) {
scope.problemReporter().unnecessaryCast((CastExpression) this.receiver);
}
}
}

if (compilerOptions.isAnnotationBasedNullAnalysisEnabled) {
ImplicitNullAnnotationVerifier.ensureNullnessIsKnown(this.binding, scope);
if (compilerOptions.sourceLevel >= ClassFileConstants.JDK1_8) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2448,7 +2448,12 @@ public void test041() {
"}"
},
"----------\n" +
"1. ERROR in X.java (at line 9)\n" +
"1. WARNING in X.java (at line 9)\n" +
" String s = ((I) this).foo(0.0f);\n" +
" ^^^^^^^^^^\n" +
"Unnecessary cast from X to I\n" +
"----------\n" +
"2. ERROR in X.java (at line 9)\n" +
" String s = ((I) this).foo(0.0f);\n" +
" ^^^^^^^^^^^^^^^^^^^^\n" +
"Type mismatch: cannot convert from Object to String\n" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3649,6 +3649,51 @@ public void testBug572534() {
}
}

public void testGH2470() {
if (this.complianceLevel < ClassFileConstants.JDK1_6) return;
Runner runner = new Runner();
runner.testFiles = new String[] {
"UnnecessaryCasts.java",
"""
public class UnnecessaryCasts<T extends C> {
void m(T t) {
C c = t;
((C) t).x();
((I) t).x();
((I) c).x();
}
}
interface I {
void x();
}
class C implements I {
@Override
public void x() {}
}
"""
};
runner.expectedCompilerLog =
"----------\n" +
"1. WARNING in UnnecessaryCasts.java (at line 5)\n" +
" ((C) t).x();\n" +
" ^^^^^^^\n" +
"Unnecessary cast from T to C\n" +
"----------\n" +
"2. WARNING in UnnecessaryCasts.java (at line 6)\n" +
" ((I) t).x();\n" +
" ^^^^^^^\n" +
"Unnecessary cast from T to I\n" +
"----------\n" +
"3. WARNING in UnnecessaryCasts.java (at line 7)\n" +
" ((I) c).x();\n" +
" ^^^^^^^\n" +
"Unnecessary cast from C to I\n" +
"----------\n";
runner.runWarningTest();
}

public static Class testClass() {
return CastTest.class;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22763,19 +22763,7 @@ public void test0705() {
}
//https://bugs.eclipse.org/bugs/show_bug.cgi?id=97219
public void test0706() {
String outputExpectedBelow17 = (this.complianceLevel == ClassFileConstants.JDK1_6)?
"----------\n" +
"1. WARNING in X.java (at line 9)\n" +
" class BB extends AA<CC> { <U> BB test() {return null;} }\n" +
" ^^^^^^\n" +
"Name clash: The method test() of type BB has the same erasure as test() of type AA<T> but does not override it\n" +
"----------\n":
"----------\n" +
"1. ERROR in X.java (at line 9)\n" +
" class BB extends AA<CC> { <U> BB test() {return null;} }\n" +
" ^^^^^^\n" +
"Name clash: The method test() of type BB has the same erasure as test() of type AA<T> but does not override it\n" +
"----------\n";
if (this.complianceLevel < ClassFileConstants.JDK1_7) return;
this.runNegativeTest(
new String[] {
"X.java",
Expand All @@ -22790,15 +22778,18 @@ public void test0706() {
"class BB extends AA<CC> { <U> BB test() {return null;} }\n" +
"class CC {}\n",
},
(this.complianceLevel < ClassFileConstants.JDK1_7)
? outputExpectedBelow17
: "----------\n" +
"----------\n" +
"1. ERROR in X.java (at line 4)\n" +
" bb.<Object>test();\n" +
" ^^^^\n" +
"The method test() is ambiguous for the type BB\n" +
"----------\n" +
"2. ERROR in X.java (at line 9)\n" +
"2. WARNING in X.java (at line 5)\n" +
" ((AA<CC>) bb).test();\n" +
" ^^^^^^^^^^^^^\n" +
"Unnecessary cast from BB to AA<CC>\n" +
"----------\n" +
"3. ERROR in X.java (at line 9)\n" +
" class BB extends AA<CC> { <U> BB test() {return null;} }\n" +
" ^^^^^^\n" +
"Name clash: The method test() of type BB has the same erasure as test() of type AA<T> but does not override it\n" +
Expand Down Expand Up @@ -29732,7 +29723,10 @@ public void test0899() {
}
//https://bugs.eclipse.org/bugs/show_bug.cgi?id=97693
public void test0900() {
this.runNegativeTest(
Runner runner = new Runner();
runner.customOptions = getCompilerOptions();
runner.customOptions.put(CompilerOptions.OPTION_ReportUnnecessaryTypeCheck, CompilerOptions.IGNORE);
runner.testFiles =
new String[] {
"X.java", // =================
"public class X<R> {\n" +
Expand All @@ -29753,7 +29747,8 @@ public void test0900() {
" Zork z;\n" +
" }\n" +
"}\n",
},
};
runner.expectedCompilerLog =
"----------\n" +
"1. WARNING in X.java (at line 11)\n" +
" ((Comparable<R>) new Implements()).toString();\n" +
Expand All @@ -29769,7 +29764,8 @@ public void test0900() {
" Zork z;\n" +
" ^^^^\n" +
"Zork cannot be resolved to a type\n" +
"----------\n");
"----------\n";
runner.runNegativeTest();
}
// Object array vs Object into generic method
public void test0901() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3091,6 +3091,11 @@ public void test043c() { // ambiguous message sends because of substitution from
" m.id(Integer.valueOf(111));\n" +
" ^^\n" +
"The method id(Integer) is ambiguous for the type M<Integer,Integer>\n" +
"----------\n" +
"2. WARNING in X.java (at line 5)\n" +
" ((E<Integer, Integer>) m).id(Integer.valueOf(111));\n" +
" ^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
"Unnecessary cast from M<Integer,Integer> to E<Integer,Integer>\n" +
"----------\n"
// reference to id is ambiguous, both method id(A) in C<java.lang.Integer> and method id(B) in M<java.lang.Integer,java.lang.Integer> match
);
Expand Down

0 comments on commit 4221238

Please sign in to comment.