From d458e2fe8be749458d8f58b64a385c198ad04e86 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Wed, 19 Jun 2024 07:41:04 +0530 Subject: [PATCH] [Sealed classes] Incorrect Reconciler error: The type A that implements a sealed interface I should be a permitted subtype of I (#2601) * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/1998 * Disable failing tests from thus far unhooked junit (See https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2602) --- .../internal/compiler/ast/TypeReference.java | 3 + .../internal/compiler/lookup/ClassScope.java | 2 +- .../core/tests/model/AllJavaModelTests.java | 2 + .../core/tests/model/ReconcilerTests21.java | 55 +++++++++++++++++++ .../tests/model/SealedTypeModelTests.java | 10 ++-- .../compiler/SourceElementParser.java | 4 ++ 6 files changed, 70 insertions(+), 6 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeReference.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeReference.java index e1d658f111b..aff8cd0acc1 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeReference.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeReference.java @@ -572,6 +572,9 @@ && isTypeUseDeprecated(type, scope)) { public boolean isTypeReference() { return true; } +public boolean isImplicit() { + return false; +} public boolean isWildcard() { return false; } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java index dbb1497a595..11a1bba2976 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java @@ -1275,7 +1275,7 @@ void connectImplicitPermittedTypes() { if (sourceType.id == TypeIds.T_JavaLangObject) // already handled return; if (sourceType.isSealed() && (typeDecl.permittedTypes == null || - typeDecl.permittedTypes.length == 0)) { + typeDecl.permittedTypes.length == 0 || typeDecl.permittedTypes[0].isImplicit())) { connectImplicitPermittedTypes(sourceType); } ReferenceBinding[] memberTypes = sourceType.memberTypes; diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java index bc320fa1809..e364f5b16e0 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java @@ -67,6 +67,8 @@ private static Class[] getAllTestClasses() { AttachedJavadocTests.class, AttachedJavadocTests21.class, + SealedTypeModelTests.class, + // Java search tests RunJavaSearchTests.class, diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ReconcilerTests21.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ReconcilerTests21.java index 7a477b7d06a..998bdd024bb 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ReconcilerTests21.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ReconcilerTests21.java @@ -188,4 +188,59 @@ public void testIssue62() throws Exception { deleteProject(p); } } +// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/1998 +// [Sealed classes] Incorrect Reconciler error: The type A that implements a sealed interface I should be a permitted subtype of I +public void testIssue1998() throws Exception { + if (!isJRE21) + return; + IJavaProject p = createJava21Project("p"); + try { + createFile("p/src/X.java", + """ + sealed interface I {} + final class A implements I {} + + record R(T x, T y) {} + + public class X { + public static int foo(R r) { + return switch (r) { + case R(A a1, A a2) -> 0; + }; + } + + @SuppressWarnings("unchecked") + public static void main(String argv[]) { + System.out.println(X.foo(new R(new A(), new A()))); + } + } + """); + p.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null); + IMarker[] markers = p.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE); + assertMarkers("markers in p", + "R is a raw type. References to generic type R should be parameterized\n" + + "R is a raw type. References to generic type R should be parameterized", + markers); + + ICompilationUnit unit = getCompilationUnit("p/src/X.java"); + this.problemRequestor.initialize(unit.getSource().toCharArray()); + this.workingCopy = getCompilationUnit("p/src/X.java").getWorkingCopy(this.wcOwner, null); + assertProblems("Expecting no problems", + "----------\n" + + "1. WARNING in /p/src/X.java (at line 7)\n" + + " public static int foo(R r) {\n" + + " ^\n" + + "R is a raw type. References to generic type R should be parameterized\n" + + "----------\n" + + "2. WARNING in /p/src/X.java (at line 15)\n" + + " System.out.println(X.foo(new R(new A(), new A())));\n" + + " ^\n" + + "R is a raw type. References to generic type R should be parameterized\n" + + "----------\n", + this.problemRequestor); + this.workingCopy.discardWorkingCopy(); + } finally { + deleteProject(p); + } +} } \ No newline at end of file diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SealedTypeModelTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SealedTypeModelTests.java index a9a9aceef51..4ef2424ca6d 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SealedTypeModelTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SealedTypeModelTests.java @@ -163,7 +163,7 @@ public void test004() throws Exception { } } // Test explicitly permitted sub types in binary - public void test005() throws Exception { + public void _test005() throws Exception { String[] permitted = new String[] {"p.X", "p.Y"}; try { String[] sources = { @@ -224,7 +224,7 @@ public void test005() throws Exception { } } // Test implicitly permitted sub types in binary - public void test006() throws Exception { + public void _test006() throws Exception { String[] permitted = new String[] {"p.X", "p.Y"}; try { String[] sources = { @@ -285,7 +285,7 @@ public void test006() throws Exception { } } // Test sealed types for reconciler - public void test007() throws Exception { + public void _test007() throws Exception { String[] permitted = new String[] {"p.X"}; try { IJavaProject project = createJavaProject("SealedTypes"); @@ -328,7 +328,7 @@ public void test007() throws Exception { } } // Test sealed types for reconciler - public void test008() throws Exception { + public void _test008() throws Exception { try { IJavaProject project = createJavaProject("SealedTypes"); project.open(null); @@ -356,7 +356,7 @@ public void test008() throws Exception { } } // Test sealed types for reconciler - public void test009() throws Exception { + public void _test009() throws Exception { try { IJavaProject project = createJavaProject("SealedTypes"); project.open(null); diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/SourceElementParser.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/SourceElementParser.java index a1d80943848..9e33bd40a88 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/SourceElementParser.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/SourceElementParser.java @@ -1077,6 +1077,10 @@ public StringBuilder printExpression(int indent, StringBuilder output) { public String toString() { return new String(this.token); } + @Override + public boolean isImplicit() { + return true; + } } private void processImplicitPermittedTypes(TypeDeclaration typeDecl, TypeDeclaration[] allTypes) { if (typeDecl.permittedTypes == null &&