Skip to content

Commit

Permalink
* [Sealed types][Switch] Pattern switch - ECJ accepts code rejected b…
Browse files Browse the repository at this point in the history
…y javac(#3060)

* Unchecked casts go unreported with ECJ
* Fixes #1802
* Fixes #1735
  • Loading branch information
srikanth-sankaran authored Oct 10, 2024
1 parent 02fda8d commit 09766de
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -430,24 +430,21 @@ public static boolean checkUnsafeCast(Expression expression, Scope scope, TypeBi
ParameterizedTypeBinding paramCastType = (ParameterizedTypeBinding) castType;
TypeBinding[] castArguments = paramCastType.arguments;
int length = castArguments == null ? 0 : castArguments.length;
if ((paramCastType.tagBits & (TagBits.HasDirectWildcard|TagBits.HasTypeVariable)) != 0) {
// verify alternate cast type, substituting different type arguments
for (int i = 0; i < length; i++) {
if (castArguments[i].isUnboundWildcard())
continue;
TypeBinding[] alternateArguments;
// need to clone for each iteration to avoid env paramtype cache interference
System.arraycopy(paramCastType.arguments, 0, alternateArguments = new TypeBinding[length], 0, length);
alternateArguments[i] = TypeBinding.equalsEquals(paramCastType.arguments[i], scope.getJavaLangObject()) ? scope.getJavaLangBoolean() : scope.getJavaLangObject();
LookupEnvironment environment = scope.environment();
ParameterizedTypeBinding alternateCastType = environment.createParameterizedType((ReferenceBinding)castType.erasure(), alternateArguments, castType.enclosingType());
if (TypeBinding.equalsEquals(alternateCastType.findSuperTypeOriginatingFrom(expressionType), match)) {
expression.bits |= ASTNode.UnsafeCast;
break;
}
// verify alternate cast type, substituting different type arguments
for (int i = 0; i < length; i++) {
if (castArguments[i].isUnboundWildcard())
continue;
TypeBinding[] alternateArguments;
// need to clone for each iteration to avoid env paramtype cache interference
System.arraycopy(paramCastType.arguments, 0, alternateArguments = new TypeBinding[length], 0, length);
alternateArguments[i] = TypeBinding.equalsEquals(paramCastType.arguments[i], scope.getJavaLangObject()) ? scope.getJavaLangBoolean() : scope.getJavaLangObject();
LookupEnvironment environment = scope.environment();
ParameterizedTypeBinding alternateCastType = environment.createParameterizedType((ReferenceBinding)castType.erasure(), alternateArguments, castType.enclosingType());
if (TypeBinding.equalsEquals(alternateCastType.findSuperTypeOriginatingFrom(expressionType), match)) {
expression.bits |= ASTNode.UnsafeCast;
break;
}
}

// Type arguments added by subtypes of S and removed by supertypes of T don't need to be checked since the type arguments aren't specified by either S or T
return true;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6975,5 +6975,47 @@ public void testBugGH656_2() {
"The method JavacWillAlsoError.someAbstractMethod() does not override the inherited method from MyAbstract since it is private to a different package\n" +
"----------\n");
}
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/1802
// Unchecked casts go unreported with ECJ
public void testIssue1802() {
Runner runner = new Runner();
runner.testFiles =
new String[] {
"X.java",
"""
class X {
void foo(I<X> ix) {
A<Y> ay = (A<Y>) ix;
B<X> bx = (B<X>) ix;
}
}
class Y extends X {}
class Z extends X {}
interface I<T> {
}
final class B<T> implements I<X> {
}
final class A<T> implements I<X> {
}
"""
};
runner.expectedCompilerLog =
"----------\n" +
"1. WARNING in X.java (at line 3)\n" +
" A<Y> ay = (A<Y>) ix;\n" +
" ^^^^^^^^^\n" +
"Type safety: Unchecked cast from I<X> to A<Y>\n" +
"----------\n" +
"2. WARNING in X.java (at line 4)\n" +
" B<X> bx = (B<X>) ix;\n" +
" ^^^^^^^^^\n" +
"Type safety: Unchecked cast from I<X> to B<X>\n" +
"----------\n";
runner.runWarningTest();
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -8541,8 +8541,8 @@ public static void main(String[] args) {
"42\n420\n4200");
}

public void testIssue3009_4() { // FIXME: this should not compile!!!
runConformTest(
public void testIssue3009_4() {
runNegativeTest(
new String[] {
"X.java",
"""
Expand Down Expand Up @@ -8572,7 +8572,12 @@ public static void main(String[] args) {
}
"""
},
"42\n420\n4200");
"----------\n" +
"1. ERROR in X.java (at line 13)\r\n" +
" case H<Integer, X> e -> 4200;\r\n" +
" ^^^^^^^^^^^^^^^\n" +
"Type J<Integer,X> cannot be safely cast to H<Integer,X>\n" +
"----------\n");
}

public void testIssue3009_5() {
Expand Down Expand Up @@ -8817,4 +8822,46 @@ public static void main(String[] args) {
},
"42\n420");
}

// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/1735
// [Sealed types][Switch] Pattern switch - ECJ accepts code rejected by javac
public void testIssue1735() {
runNegativeTest(
new String[] {
"X.java",
"""
class X {
void foo(I<X> ix) {
switch(ix) {
case A<Y> ay -> System.out.println();
case B<X> bx -> System.out.println();
}
}
}
class Y extends X {}
class Z extends X {}
sealed interface I<T> permits A, B {
}
final class B<T> implements I<X> {
}
final class A<T> implements I<X> {
}
"""
},
"----------\n" +
"1. ERROR in X.java (at line 4)\n" +
" case A<Y> ay -> System.out.println();\n" +
" ^^^^^^^\n" +
"Type I<X> cannot be safely cast to A<Y>\n" +
"----------\n" +
"2. ERROR in X.java (at line 5)\n" +
" case B<X> bx -> System.out.println();\n" +
" ^^^^^^^\n" +
"Type I<X> cannot be safely cast to B<X>\n" +
"----------\n");
}
}

0 comments on commit 09766de

Please sign in to comment.