From ce4c9ae3dcefec5cc5d14448cf22ddcf5d019671 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 08:41:13 +0000 Subject: [PATCH 01/63] Bump actions/upload-artifact from 4.4.0 to 4.4.1 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.0 to 4.4.1. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/50769540e7f4bd5e21e526ee35c689e35e0d6874...604373da6381bf24206979c74d06a550515601b9) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 11d49ae386f..1b4940ee343 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Upload - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 with: name: Event File path: ${{ github.event_path }} @@ -46,7 +46,7 @@ jobs: mvn -U clean verify --batch-mode --fail-at-end -Ptest-on-javase-21 -Pbree-libs -Papi-check -Djava.io.tmpdir=$WORKSPACE/tmp -Dproject.build.sourceEncoding=UTF-8 -Dtycho.surefire.argLine="--add-modules ALL-SYSTEM -Dcompliance=1.8,11,17,20 -Djdt.performance.asserts=disabled" -Dcbi-ecj-version=99.99 - name: Upload Test Results for Linux if: always() - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 with: name: test-results-linux if-no-files-found: warn From 02fda8dbda14568d66867ff8b792d9a41d7924c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 08:23:12 +0000 Subject: [PATCH 02/63] Bump actions/upload-artifact from 4.4.1 to 4.4.2 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.1 to 4.4.2. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/604373da6381bf24206979c74d06a550515601b9...84480863f228bb9747b473957fcc9e309aa96097) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1b4940ee343..5eca03e9ba3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Upload - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 with: name: Event File path: ${{ github.event_path }} @@ -46,7 +46,7 @@ jobs: mvn -U clean verify --batch-mode --fail-at-end -Ptest-on-javase-21 -Pbree-libs -Papi-check -Djava.io.tmpdir=$WORKSPACE/tmp -Dproject.build.sourceEncoding=UTF-8 -Dtycho.surefire.argLine="--add-modules ALL-SYSTEM -Dcompliance=1.8,11,17,20 -Djdt.performance.asserts=disabled" -Dcbi-ecj-version=99.99 - name: Upload Test Results for Linux if: always() - uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # v4.4.1 + uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 with: name: test-results-linux if-no-files-found: warn From 09766dec81f571090fe274134ce0115b2f0d6284 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Thu, 10 Oct 2024 11:53:01 +0530 Subject: [PATCH 03/63] * [Sealed types][Switch] Pattern switch - ECJ accepts code rejected by javac(#3060) * Unchecked casts go unreported with ECJ * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/1802 * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/1735 --- .../internal/compiler/ast/CastExpression.java | 29 +++++----- .../regression/GenericsRegressionTest.java | 42 +++++++++++++++ .../regression/SwitchPatternTest.java | 53 +++++++++++++++++-- 3 files changed, 105 insertions(+), 19 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CastExpression.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CastExpression.java index d4463f4554e..c6c6ebf3edd 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CastExpression.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CastExpression.java @@ -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 { diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java index f6f0ddea86a..ceec296e917 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java @@ -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 ix) { + A ay = (A) ix; + B bx = (B) ix; + } + } + class Y extends X {} + class Z extends X {} + + interface I { + } + + final class B implements I { + } + + + final class A implements I { + } + """ + }; + runner.expectedCompilerLog = + "----------\n" + + "1. WARNING in X.java (at line 3)\n" + + " A ay = (A) ix;\n" + + " ^^^^^^^^^\n" + + "Type safety: Unchecked cast from I to A\n" + + "----------\n" + + "2. WARNING in X.java (at line 4)\n" + + " B bx = (B) ix;\n" + + " ^^^^^^^^^\n" + + "Type safety: Unchecked cast from I to B\n" + + "----------\n"; + runner.runWarningTest(); +} } diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java index 3a370ec0f32..caab7826379 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java @@ -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", """ @@ -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 e -> 4200;\r\n" + + " ^^^^^^^^^^^^^^^\n" + + "Type J cannot be safely cast to H\n" + + "----------\n"); } public void testIssue3009_5() { @@ -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 ix) { + switch(ix) { + case A ay -> System.out.println(); + case B bx -> System.out.println(); + } + } + } + class Y extends X {} + class Z extends X {} + + sealed interface I permits A, B { + } + + final class B implements I { + } + + + final class A implements I { + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " case A ay -> System.out.println();\n" + + " ^^^^^^^\n" + + "Type I cannot be safely cast to A\n" + + "----------\n" + + "2. ERROR in X.java (at line 5)\n" + + " case B bx -> System.out.println();\n" + + " ^^^^^^^\n" + + "Type I cannot be safely cast to B\n" + + "----------\n"); + } } From 9ad924b8c96e5d00593b0a5ca65a3fae52e0853d Mon Sep 17 00:00:00 2001 From: Hannes Wellmann Date: Wed, 9 Oct 2024 21:27:45 +0200 Subject: [PATCH 04/63] Update links in README and break very long line --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7d2e3321197..d67c0882ddd 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,9 @@ For more information and important links, refer to the [JDT wiki page](https://g [Contributions are always welcome!](https://github.com/eclipse-jdt/.github/blob/main/CONTRIBUTING.md) -Please bear in mind that this project is almost entirely developed by volunteers. If you do not provide the implementation yourself (or pay someone to do it for you), the bug might never get fixed. If it is a serious bug, other people than you might care enough to provide a fix. +Please bear in mind that this project is almost entirely developed by volunteers. +If you do not provide the implementation yourself (or pay someone to do it for you), the bug might never get fixed. +If it is a serious bug, other people than you might care enough to provide a fix. ## License @@ -28,4 +30,4 @@ Please bear in mind that this project is almost entirely developed by volunteers - https://github.com/eclipse-jdt/eclipse.jdt.core/wiki - https://github.com/eclipse-jdt/.github/blob/main/CONTRIBUTING.md -- http://www.eclipse.org/projects/project.php?id=eclipse.jdt +- https://projects.eclipse.org/projects/eclipse.jdt From 8253f4b26ce65657987de3e7ccd26c4600cff21f Mon Sep 17 00:00:00 2001 From: Suby S Surendran Date: Fri, 11 Oct 2024 08:30:47 +0530 Subject: [PATCH 05/63] Search support for the implicitly declared class (#3000) * Search support for the implicitly declared class * Incorporated the code-review comments --- ...avaSearchImplicitTypeDeclarationTests.java | 156 ++++++++++++++++++ .../core/tests/model/RunJavaSearchTests.java | 1 + 2 files changed, 157 insertions(+) create mode 100644 org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaSearchImplicitTypeDeclarationTests.java diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaSearchImplicitTypeDeclarationTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaSearchImplicitTypeDeclarationTests.java new file mode 100644 index 00000000000..3231d6db7f9 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/JavaSearchImplicitTypeDeclarationTests.java @@ -0,0 +1,156 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.jdt.core.tests.model; + +import java.io.IOException; +import junit.framework.Test; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.WorkingCopyOwner; +import org.eclipse.jdt.core.search.IJavaSearchScope; +import org.eclipse.jdt.core.search.ReferenceMatch; +import org.eclipse.jdt.core.search.SearchEngine; +import org.eclipse.jdt.core.search.SearchMatch; +import org.eclipse.jdt.core.search.TypeReferenceMatch; + +public class JavaSearchImplicitTypeDeclarationTests extends JavaSearchTests { + public JavaSearchImplicitTypeDeclarationTests(String name) { + super(name); + this.endChar = ""; + } + public static Test suite() { + return buildModelTestSuite(JavaSearchImplicitTypeDeclarationTests.class, BYTECODE_DECLARATION_ORDER); + } + class TestCollector extends JavaSearchResultCollector { + public void acceptSearchMatch(SearchMatch searchMatch) throws CoreException { + super.acceptSearchMatch(searchMatch); + } + } + + class ReferenceCollector extends JavaSearchResultCollector { + protected void writeLine() throws CoreException { + super.writeLine(); + ReferenceMatch refMatch = (ReferenceMatch) this.match; + IJavaElement localElement = refMatch.getLocalElement(); + if (localElement != null) { + this.line.append("+["); + if (localElement.getElementType() == IJavaElement.ANNOTATION) { + this.line.append('@'); + this.line.append(localElement.getElementName()); + this.line.append(" on "); + this.line.append(localElement.getParent().getElementName()); + } else { + this.line.append(localElement.getElementName()); + } + this.line.append(']'); + } + } + } + + class TypeReferenceCollector extends ReferenceCollector { + protected void writeLine() throws CoreException { + super.writeLine(); + TypeReferenceMatch typeRefMatch = (TypeReferenceMatch) this.match; + IJavaElement[] others = typeRefMatch.getOtherElements(); + int length = others==null ? 0 : others.length; + if (length > 0) { + this.line.append("+["); + for (int i=0; i0) this.line.append(','); + if (other.getElementType() == IJavaElement.ANNOTATION) { + this.line.append('@'); + this.line.append(other.getElementName()); + this.line.append(" on "); + this.line.append(other.getParent().getElementName()); + } else { + this.line.append(other.getElementName()); + } + } + this.line.append(']'); + } + } + } + + protected IJavaProject setUpJavaProject(final String projectName, String compliance, boolean useFullJCL) throws CoreException, IOException { + // copy files in project from source workspace to target workspace + IJavaProject setUpJavaProject = super.setUpJavaProject(projectName, compliance, useFullJCL); + return setUpJavaProject; + } + + IJavaSearchScope getJavaSearchScope() { + return SearchEngine.createJavaSearchScope(new IJavaProject[] {getJavaProject("JavaSearchBugs")}); + } + + IJavaSearchScope getJavaSearchScopeBugs(String packageName, boolean addSubpackages) throws JavaModelException { + if (packageName == null) return getJavaSearchScope(); + return getJavaSearchPackageScope("JavaSearchBugs", packageName, addSubpackages); + } + + public ICompilationUnit getWorkingCopy(String path, String source) throws JavaModelException { + if (this.wcOwner == null) { + this.wcOwner = new WorkingCopyOwner() {}; + } + return getWorkingCopy(path, source, this.wcOwner); + } + + @Override + public void setUpSuite() throws Exception { + JAVA_PROJECT = setUpJavaProject("JavaSearchBugs", "23"); + JAVA_PROJECT.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); + super.setUpSuite(); + } + + public void tearDownSuite() throws Exception { + deleteProject("JavaSearchBugs"); + super.tearDownSuite(); + } + + protected void setUp () throws Exception { + super.setUp(); + this.resultCollector = new TestCollector(); + this.resultCollector.showAccuracy(true); + } + + public void test_001() throws CoreException { + this.workingCopies = new ICompilationUnit[1]; + String code = """ + /** + * Hello + */ + void main() { + System.out.println("sasi"); + abc(); + } + void abc() { + System.out.println("abc"); + } + """; + this.workingCopies[0] = getWorkingCopy("/JavaSearchBugs/src/X.java", code); + IJavaProject javaProject = this.workingCopies[0].getJavaProject(); + String old = javaProject.getOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, true); + try { + search("abc", METHOD, ALL_OCCURRENCES, EXACT_RULE); + assertSearchResults("src/X.java void X.main() [abc()] EXACT_MATCH\n" + + "src/X.java void X.abc() [abc] EXACT_MATCH"); + } finally { + javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, old); + } + } +} \ No newline at end of file diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/RunJavaSearchTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/RunJavaSearchTests.java index 040b15c656a..0c16f617cf1 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/RunJavaSearchTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/RunJavaSearchTests.java @@ -82,6 +82,7 @@ public static Test suite() { allClasses.add(JavaSearchNameEnvironmentTest.class); allClasses.add(JavaSearchSuperAfterStatementTests.class); allClasses.add(JavaSearchIssue190Test.class); + allClasses.add(JavaSearchImplicitTypeDeclarationTests.class); // Reset forgotten subsets of tests TestCase.TESTS_PREFIX = null; From 86a60412c9eed70d212b7ba33d0e9916fbcc3fcc Mon Sep 17 00:00:00 2001 From: Hannes Wellmann Date: Wed, 9 Oct 2024 00:05:54 +0200 Subject: [PATCH 06/63] [Oomph-Setup] Add jdt.core configuration setup Additionally add a styled and drag&drop-able Oomph Configuration button to the main README. Part of https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/2430 --- README.md | 4 + org.eclipse.jdt.core.setup/.project | 11 +++ .../org.eclipse.core.resources.prefs | 2 + .../JdtCoreConfiguration.setup | 90 +++++++++++++++++++ 4 files changed, 107 insertions(+) create mode 100644 org.eclipse.jdt.core.setup/.project create mode 100644 org.eclipse.jdt.core.setup/.settings/org.eclipse.core.resources.prefs create mode 100644 org.eclipse.jdt.core.setup/JdtCoreConfiguration.setup diff --git a/README.md b/README.md index d67c0882ddd..1602df20f69 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,10 @@ Please bear in mind that this project is almost entirely developed by volunteers If you do not provide the implementation yourself (or pay someone to do it for you), the bug might never get fixed. If it is a serious bug, other people than you might care enough to provide a fix. +[![Create Eclipse Development Environment for JDT Core](https://download.eclipse.org/oomph/www/setups/svg/JDT_Core.svg)]( +https://www.eclipse.org/setups/installer/?url=https://raw.githubusercontent.com/eclipse-jdt/eclipse.jdt.core/master/org.eclipse.jdt.core.setup/JdtCoreConfiguration.setup&show=true +"Click to open Eclipse-Installer Auto Launch or drag into your running installer") + ## License [Eclipse Public License (EPL) v2.0](https://www.eclipse.org/legal/epl-2.0/) diff --git a/org.eclipse.jdt.core.setup/.project b/org.eclipse.jdt.core.setup/.project new file mode 100644 index 00000000000..0b3242f09d3 --- /dev/null +++ b/org.eclipse.jdt.core.setup/.project @@ -0,0 +1,11 @@ + + + org.eclipse.jdt.core.setup + + + + + + + + diff --git a/org.eclipse.jdt.core.setup/.settings/org.eclipse.core.resources.prefs b/org.eclipse.jdt.core.setup/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..99f26c0203a --- /dev/null +++ b/org.eclipse.jdt.core.setup/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/org.eclipse.jdt.core.setup/JdtCoreConfiguration.setup b/org.eclipse.jdt.core.setup/JdtCoreConfiguration.setup new file mode 100644 index 00000000000..77f05a777b7 --- /dev/null +++ b/org.eclipse.jdt.core.setup/JdtCoreConfiguration.setup @@ -0,0 +1,90 @@ + + + + + https://www.eclipse.org/downloads/images/committers.png + + + JDT Core + + + + + + The JDT Core installation provides the latest tools needed to work with the project's source code. + + + + + + + record + + + + + + + + + + + + + + The JDT Core workspace provides all the source code of the project. + + + <p> + The <code>JDT Core</code> configuration provisions a dedicated development environment for the complete set of projects that comprise the JDT Core, + i.e. the projects that are contained in the <a href="https://github.com/eclipse-jdt/jdt.core">jdt.core</a> repository. + </p> + <p> + The installation is based on the latest successful integration build of the <code>Eclipse Platform SDK</code>, + the PDE target platform, like the installation, is also based on the latest integration build, + and the API baseline is based on the most recent release. + <p> + </p> + Please <a href="https://wiki.eclipse.org/Eclipse_Platform_SDK_Provisioning">read the tutorial instructions</a> for more details. + </p> + + From 0037ab1359b5fa75442b514e25e306c4cc52a917 Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Sun, 13 Oct 2024 16:24:10 +0200 Subject: [PATCH 07/63] Let tests include org.eclipse.jdt.annotation_v1 rather than declaring a dependency (#3072) + access annotation_v1 from a local copy (jar) + avoid PackageAdmin in favor of "official" API this is no possible since version selection is no longer necessary + can no longer use compliance to switch annotation jars since version 1.7- are no longer supported + revive capability to tests with declaration annotations via new class NullDeclarationAnnotationTest - pull up helpers from ResourceLeakAnnotatedTests which allow running inherited tests. + entirely remove jdt.annotation as dependency from apt.pluggable.tests Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3029 --- .../META-INF/MANIFEST.MF | 4 +- .../META-INF/MANIFEST.MF | 1 - .../builder/AnnotationDependencyTests.java | 30 ++-- .../META-INF/MANIFEST.MF | 1 - .../build.properties | 3 +- ....jdt.annotation_1.2.100.v20241001-0914.jar | Bin 0 -> 18693 bytes .../AbstractNullAnnotationTest.java | 25 ++- .../regression/AbstractRegressionTest.java | 61 +++++++- .../regression/NullAnnotationTest.java | 144 +++++++++--------- .../NullDeclarationAnnotationTest.java | 63 ++++++++ .../ResourceLeakAnnotatedTests.java | 55 ------- .../regression/StackMapAttributeTest.java | 7 +- .../META-INF/MANIFEST.MF | 1 - .../model/ExternalAnnotations17Test.java | 15 +- .../tests/model/NullAnnotationModelTests.java | 5 +- 15 files changed, 241 insertions(+), 174 deletions(-) create mode 100644 org.eclipse.jdt.core.tests.compiler/lib/org.eclipse.jdt.annotation_1.2.100.v20241001-0914.jar create mode 100644 org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullDeclarationAnnotationTest.java diff --git a/org.eclipse.jdt.apt.pluggable.tests/META-INF/MANIFEST.MF b/org.eclipse.jdt.apt.pluggable.tests/META-INF/MANIFEST.MF index 43c523c9a87..1b6d60b40bb 100644 --- a/org.eclipse.jdt.apt.pluggable.tests/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.apt.pluggable.tests/META-INF/MANIFEST.MF @@ -14,9 +14,7 @@ Require-Bundle: org.junit, org.eclipse.jdt.core.tests.compiler, org.eclipse.test.performance, org.eclipse.jdt.core;bundle-version="[3.40.0,4.0.0)", - org.eclipse.ui.ide, - org.eclipse.jdt.annotation;bundle-version="[1.1.0,2.0.0)";resolution:=optional, - org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional + org.eclipse.ui.ide Bundle-ActivationPolicy: lazy Bundle-Vendor: %providerName Bundle-RequiredExecutionEnvironment: JavaSE-17 diff --git a/org.eclipse.jdt.core.tests.builder/META-INF/MANIFEST.MF b/org.eclipse.jdt.core.tests.builder/META-INF/MANIFEST.MF index bc00fbfe540..3bb787a3c47 100644 --- a/org.eclipse.jdt.core.tests.builder/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.core.tests.builder/META-INF/MANIFEST.MF @@ -14,7 +14,6 @@ Require-Bundle: org.junit;bundle-version="3.8.1", org.eclipse.core.runtime;bundle-version="[3.29.0,4.0.0)", org.eclipse.test;bundle-version="[3.6.0,4.0.0)", org.eclipse.test.performance;bundle-version="[3.1.0,4.0.0)", - org.eclipse.jdt.annotation;bundle-version="[1.1.0,2.0.0)";resolution:=optional, org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional, org.eclipse.jdt.apt.core Bundle-RequiredExecutionEnvironment: JavaSE-17 diff --git a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/AnnotationDependencyTests.java b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/AnnotationDependencyTests.java index e735f0b847c..b4da162ae70 100644 --- a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/AnnotationDependencyTests.java +++ b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/AnnotationDependencyTests.java @@ -17,21 +17,19 @@ *******************************************************************************/ package org.eclipse.jdt.core.tests.builder; -import java.io.File; +import java.io.IOException; import junit.framework.Test; import org.eclipse.core.resources.IMarker; -import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; -import org.eclipse.core.runtime.Platform; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.compiler.CategorizedProblem; +import org.eclipse.jdt.core.tests.compiler.regression.AbstractNullAnnotationTest; import org.eclipse.jdt.core.tests.util.Util; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; -import org.osgi.framework.Bundle; /** * Tests to verify that annotation changes cause recompilation of dependent types. @@ -144,17 +142,19 @@ private void addAnnotationType() { void setupProjectForNullAnnotations() throws JavaModelException { // add the org.eclipse.jdt.annotation library (bin/ folder or jar) to the project: - Bundle[] bundles = Platform.getBundles("org.eclipse.jdt.annotation","[1.1.0,2.0.0)"); - File bundleFile = FileLocator.getBundleFileLocation(bundles[0]).get(); - String annotationsLib = bundleFile.isDirectory() ? bundleFile.getPath()+"/bin" : bundleFile.getPath(); - IJavaProject javaProject = env.getJavaProject(this.projectPath); - IClasspathEntry[] rawClasspath = javaProject.getRawClasspath(); - int len = rawClasspath.length; - System.arraycopy(rawClasspath, 0, rawClasspath = new IClasspathEntry[len+1], 0, len); - rawClasspath[len] = JavaCore.newLibraryEntry(new Path(annotationsLib), null, null); - javaProject.setRawClasspath(rawClasspath, null); - - javaProject.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); + try { + String annotationsLib = AbstractNullAnnotationTest.getAnnotationV1LibPath(); + IJavaProject javaProject = env.getJavaProject(this.projectPath); + IClasspathEntry[] rawClasspath = javaProject.getRawClasspath(); + int len = rawClasspath.length; + System.arraycopy(rawClasspath, 0, rawClasspath = new IClasspathEntry[len+1], 0, len); + rawClasspath[len] = JavaCore.newLibraryEntry(new Path(annotationsLib), null, null); + javaProject.setRawClasspath(rawClasspath, null); + + javaProject.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); + } catch (IOException ioe) { + throw new JavaModelException(ioe, -13); + } } /** diff --git a/org.eclipse.jdt.core.tests.compiler/META-INF/MANIFEST.MF b/org.eclipse.jdt.core.tests.compiler/META-INF/MANIFEST.MF index b4db246297f..5789e272ced 100644 --- a/org.eclipse.jdt.core.tests.compiler/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.core.tests.compiler/META-INF/MANIFEST.MF @@ -22,7 +22,6 @@ Require-Bundle: org.junit;bundle-version="3.8.1", org.eclipse.test;bundle-version="[3.6.0,4.0.0)", org.eclipse.test.performance;bundle-version="[3.10.0,4.0.0)", org.eclipse.core.resources;bundle-version="[3.21.0,4.0.0)", - org.eclipse.jdt.annotation;bundle-version="[1.1.0,2.0.0)";resolution:=optional, org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional Import-Package: jakarta.annotation;version="[2.1.0,3.0.0)", org.eclipse.jdt.internal.compiler.apt.dispatch diff --git a/org.eclipse.jdt.core.tests.compiler/build.properties b/org.eclipse.jdt.core.tests.compiler/build.properties index cfa629f6809..1693443f0a3 100644 --- a/org.eclipse.jdt.core.tests.compiler/build.properties +++ b/org.eclipse.jdt.core.tests.compiler/build.properties @@ -17,7 +17,8 @@ bin.includes = test.xml,\ .,\ META-INF/,\ plugin.properties,\ - workspace/ + workspace/,\ + lib/org.eclipse.jdt.annotation_1.2.100.v20241001-0914.jar source.. = src/ output.. = bin/ src.includes = about.html diff --git a/org.eclipse.jdt.core.tests.compiler/lib/org.eclipse.jdt.annotation_1.2.100.v20241001-0914.jar b/org.eclipse.jdt.core.tests.compiler/lib/org.eclipse.jdt.annotation_1.2.100.v20241001-0914.jar new file mode 100644 index 0000000000000000000000000000000000000000..5db80bf23997e8099bcccaf96f89db7089f5efa9 GIT binary patch literal 18693 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 KcdUXxKm9+qNSJc~ literal 0 HcmV?d00001 diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractNullAnnotationTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractNullAnnotationTest.java index 5376767e4cb..a66a72c4f1a 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractNullAnnotationTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractNullAnnotationTest.java @@ -15,10 +15,11 @@ import java.io.File; import java.io.IOException; +import java.net.URL; import java.util.Map; import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.Platform; import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.osgi.framework.Bundle; @@ -89,16 +90,24 @@ protected void setUpAnnotationLib() throws IOException { int len = defaultLibs.length; this.LIBS = new String[len+1]; System.arraycopy(defaultLibs, 0, this.LIBS, 0, len); - String version = this.complianceLevel >= ClassFileConstants.JDK1_8 ? "[2.0.0,3.0.0)" : "[1.1.0,2.0.0)"; - Bundle[] bundles = org.eclipse.jdt.core.tests.compiler.Activator.getPackageAdmin().getBundles("org.eclipse.jdt.annotation", version); - File bundleFile = FileLocator.getBundleFileLocation(bundles[0]).get(); - if (bundleFile.isDirectory()) - this.LIBS[len] = bundleFile.getPath()+"/bin"; - else - this.LIBS[len] = bundleFile.getPath(); + this.LIBS[len] = getAnnotationLibPath(); } } + protected String getAnnotationLibPath() throws IOException { + Bundle bundle = Platform.getBundle("org.eclipse.jdt.annotation"); + File bundleFile = FileLocator.getBundleFileLocation(bundle).get(); + if (bundleFile.isDirectory()) + return bundleFile.getPath()+"/bin"; + else + return bundleFile.getPath(); + } + + public static String getAnnotationV1LibPath() throws IOException { + URL libEntry = Platform.getBundle("org.eclipse.jdt.core.tests.compiler").getEntry("/lib/org.eclipse.jdt.annotation_1.2.100.v20241001-0914.jar"); + return FileLocator.toFileURL(libEntry).getPath(); + } + // Conditionally augment problem detection settings static boolean setNullRelatedOptions = true; diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java index 028d6281d3b..699b4d914cc 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java @@ -36,6 +36,8 @@ import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.element.TypeElement; +import junit.framework.Test; +import junit.framework.TestSuite; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; @@ -1341,6 +1343,59 @@ public void run() { public AbstractRegressionTest(String name) { super(name); } + + /* argument 'inheritedDepth' is not exposed in original API, therefore these helpers are copied below with this arg added */ + protected static void buildMinimalComplianceTestSuite(int minimalCompliance, int inheritedDepth, TestSuite suite, Class evaluationTestClass) { + int complianceLevels = getPossibleComplianceLevels(); + for (int[] map : complianceTestLevelMapping) { + if ((complianceLevels & map[0]) != 0) { + long complianceLevelForJavaVersion = ClassFileConstants.getComplianceLevelForJavaVersion(map[1]); + checkCompliance(evaluationTestClass, minimalCompliance, suite, complianceLevels, inheritedDepth, map[0], map[1], getVersionString(complianceLevelForJavaVersion)); + } + } + } + protected static void checkCompliance(Class evaluationTestClass, int minimalCompliance, TestSuite suite, int complianceLevels, int inheritedDepth, + int abstractCompilerTestCompliance, int classFileConstantsVersion, String release) { + int lev = complianceLevels & abstractCompilerTestCompliance; + if (lev != 0) { + if (lev < minimalCompliance) { + System.err.println("Cannot run "+evaluationTestClass.getName()+" at compliance " + release + "!"); + } else { + suite.addTest(buildUniqueComplianceTestSuite(evaluationTestClass, ClassFileConstants.getComplianceLevelForJavaVersion(classFileConstantsVersion), inheritedDepth)); + } + } + } + public static Test buildUniqueComplianceTestSuite(Class evaluationTestClass, long uniqueCompliance, int inheritedDepth) { + long highestLevel = highestComplianceLevels(); + if (highestLevel < uniqueCompliance) { + String complianceString; + if (highestLevel == ClassFileConstants.JDK10) + complianceString = "10"; + else if (highestLevel == ClassFileConstants.JDK9) + complianceString = "9"; + else if (highestLevel <= CompilerOptions.getFirstSupportedJdkLevel()) + complianceString = CompilerOptions.getFirstSupportedJavaVersion(); + else { + highestLevel = ClassFileConstants.getLatestJDKLevel(); + if (highestLevel > 0) { + complianceString = CompilerOptions.versionFromJdkLevel(highestLevel); + } else { + complianceString = "unknown"; + } + + } + + System.err.println("Cannot run "+evaluationTestClass.getName()+" at compliance "+complianceString+"!"); + return new TestSuite(); + } + TestSuite complianceSuite = new RegressionTestSetup(uniqueCompliance); + List tests = buildTestsList(evaluationTestClass, inheritedDepth); + for (int index=0, size=tests.size(); index= ClassFileConstants.JDK1_8 - ? "@Target(ElementType.TYPE_USE)\n" - : ""; + return useDeclarationAnnotations() + ? "" + : "@Target(ElementType.TYPE_USE)\n"; } String cancenNonNullByDefault() { - return this.complianceLevel < ClassFileConstants.JDK1_8 - ? " @NonNullByDefault(false)\n" - : " @NonNullByDefault({})\n"; + return useDeclarationAnnotations() + ? " @NonNullByDefault(false)\n" + : " @NonNullByDefault({})\n"; } /** @@ -129,11 +133,7 @@ String cancenNonNullByDefault() { @Override protected void setUp() throws Exception { super.setUp(); - if (this.complianceLevel >= ClassFileConstants.JDK1_8) - this.TEST_JAR_SUFFIX = "_1.8.jar"; - if (this.LIBS == null) { - this.LIBS = getLibsWithNullAnnotations(this.complianceLevel); - } + this.TEST_JAR_SUFFIX = "_1.8.jar"; } // a nullable argument is dereferenced without a check @@ -539,13 +539,13 @@ public void test_nonnull_parameter_015() { "X.java", "import org.eclipse.jdt.annotation.*;\n" + "public class X {\n" + - ((this.complianceLevel < ClassFileConstants.JDK1_8) + (useDeclarationAnnotations() ? " void foo(@NonNull Object ... o) {\n" : " void foo(Object @NonNull... o) {\n") + " if (o != null)\n" + " System.out.print(o.toString());\n" + " }\n" + - ((this.complianceLevel < ClassFileConstants.JDK1_8) + (useDeclarationAnnotations() ? " void foo2(int i, @NonNull Object ... o) {\n" : " void foo2(int i, Object @NonNull ... o) {\n" ) + @@ -592,19 +592,19 @@ public void test_nonnull_parameter_016() { "X.java", "import org.eclipse.jdt.annotation.*;\n" + "public class X {\n" + - ((this.complianceLevel < ClassFileConstants.JDK1_8) + (useDeclarationAnnotations() ? " X(@NonNull Object ... o) {\n" : " X(Object @NonNull... o) {\n") + " if (o != null)\n" + " System.out.print(o.toString());\n" + " }\n" + " class Y extends X {\n" + - ((this.complianceLevel < ClassFileConstants.JDK1_8) + (useDeclarationAnnotations() ? " Y(int i, @NonNull Object ... o) {\n" : " Y(int i, Object @NonNull... o) {\n") + " super(i, (Object)null);\n" + " }\n" + - ((this.complianceLevel < ClassFileConstants.JDK1_8) + (useDeclarationAnnotations() ? " Y(char c, @NonNull Object ... o) {\n" : " Y(char c, Object @NonNull... o) {\n") + " this(1, new Object(), null);\n" + @@ -727,7 +727,7 @@ public void test_nonnull_local_001() { "----------\n" + "1. ERROR in X.java (at line 4)\n" + " @NonNull Object o1 = b ? null : new Object();\n" + - (this.complianceLevel < ClassFileConstants.JDK1_8 ? + (useDeclarationAnnotations() ? " ^^^^^^^^^^^^^^^^^^^^^^^\n" + "Null type mismatch: required \'@NonNull Object\' but the provided value is inferred as @Nullable\n" : @@ -766,7 +766,7 @@ public void test_nonnull_local_002() { "----------\n" + "1. ERROR in X.java (at line 5)\n" + " o1 = b ? null : new Object();\n" + - (this.complianceLevel < ClassFileConstants.JDK1_8 ? + (useDeclarationAnnotations() ? " ^^^^^^^^^^^^^^^^^^^^^^^\n" + "Null type mismatch: required \'@NonNull Object\' but the provided value is inferred as @Nullable\n" : @@ -1281,7 +1281,7 @@ public void test_parameter_specification_inheritance_014() { "}\n", }, customOptions, - (this.complianceLevel < ClassFileConstants.JDK1_8 ? + (useDeclarationAnnotations() ? "----------\n" + "1. ERROR in p1\\Y.java (at line 2)\n" + " public class Y extends X implements IY {\n" + @@ -1804,7 +1804,7 @@ public void test_nonnull_return_011() { "1. ERROR in X.java (at line 5)\n" + " if (dubious == null)\n" + " ^^^^^^^\n" + - ((this.complianceLevel < ClassFileConstants.JDK1_8) + (useDeclarationAnnotations() ? "Null comparison always yields false: The variable dubious is specified as @NonNull\n" : "Redundant null check: comparing '@NonNull Object' against null\n" ) + "----------\n" + @@ -2067,7 +2067,7 @@ public void test_illegal_annotation_001() { "1. ERROR in X.java (at line 2)\n" + " @NonNull public class X {\n" + " ^^^^^^^^\n" + - ((this.complianceLevel < ClassFileConstants.JDK1_8) + (useDeclarationAnnotations() ? "The annotation @NonNull is disallowed for this location\n" : "The nullness annotation 'NonNull' is not applicable at this location\n") + "----------\n"); @@ -2110,7 +2110,7 @@ public void test_illegal_annotation_003() { "1. ERROR in X.java (at line 3)\n" + " @NonNull void foo() {}\n" + " ^^^^^^^^\n" + - ((this.complianceLevel < ClassFileConstants.JDK1_8) + (useDeclarationAnnotations() ? "The nullness annotation @NonNull is not applicable for the primitive type void\n" : "Type annotation is illegal for a method that returns void\n") + "----------\n", @@ -2261,9 +2261,15 @@ public void test_illegal_annotation_008() { "1. ERROR in X.java (at line 3)\n" + " @NonNull X() {}\n" + " ^^^^^^^^\n" + - ((this.complianceLevel < ClassFileConstants.JDK1_8) - ? "The annotation @NonNull is disallowed for this location\n" - : "The nullness annotation 'NonNull' is not applicable at this location\n" ) + + "The nullness annotation 'NonNull' is not applicable at this location\n" + + (useDeclarationAnnotations() + ? // in this case the above error is redundant, but acceptable. + "----------\n" + + "2. ERROR in X.java (at line 3)\n" + + " @NonNull X() {}\n" + + " ^^^^^^^^\n" + + "The annotation @NonNull is disallowed for this location\n" + : "") + "----------\n"); } @@ -2519,7 +2525,7 @@ public void test_default_nullness_003b() { } // package level default is consumed from package-info.class, similarly for type level default - fine tuned default public void test_default_nullness_003c() { - if (this.complianceLevel < ClassFileConstants.JDK1_8) return; // uses version 2.0 of @NonNullByDefault + if (useDeclarationAnnotations()) return; // uses version 2.0 of @NonNullByDefault Map customOptions = getCompilerOptions(); runConformTestWithLibs( new String[] { @@ -2804,7 +2810,7 @@ public void test_default_nullness_010() { "----------\n" + "2. WARNING in p2\\Y.java (at line 5)\n" + " protected @NonNull Object getObject(@NonNull Object o) {\n" + - (this.complianceLevel < ClassFileConstants.JDK1_8 + (useDeclarationAnnotations() ? " ^^^^^^^^^^^^^^^^^\n" : " ^^^^^^^^^^^^^^^\n" ) + @@ -3490,7 +3496,7 @@ public void test_nonnull_var_in_constrol_structure_1() { "----------\n" + "1. WARNING in X.java (at line 4)\n" + " void print4(@NonNull String s) {\n" + - (this.complianceLevel < ClassFileConstants.JDK1_8 + (useDeclarationAnnotations() ? " ^^^^^^^^^^^^^^^^^\n" : " ^^^^^^^^^^^^^^^\n" ) + @@ -3508,7 +3514,7 @@ public void test_nonnull_var_in_constrol_structure_1() { "----------\n" + "4. WARNING in X.java (at line 17)\n" + " void print(@NonNull String s) {\n" + - (this.complianceLevel < ClassFileConstants.JDK1_8 + (useDeclarationAnnotations() ? " ^^^^^^^^^^^^^^^^^\n" : " ^^^^^^^^^^^^^^^\n" ) + @@ -5494,7 +5500,7 @@ public void test_nullable_field_16() { "1. ERROR in X.java (at line 19)\n" + " test(this.prop);\n" + " ^^^^^^^^^\n" + - (this.complianceLevel < ClassFileConstants.JDK1_8 + (useDeclarationAnnotations() ? "Null type mismatch: required '@NonNull Object' but the provided value is specified as @Nullable\n" : "Null type mismatch (type annotations): required \'@NonNull Object\' but this expression has type \'@Nullable Object\'\n") + "----------\n"); @@ -6175,7 +6181,7 @@ public void testBug388281_06() { // whereas I2A cancels that same default "}\n" }, - (this.complianceLevel < ClassFileConstants.JDK1_8 ? + (useDeclarationAnnotations() ? "----------\n" + "1. ERROR in ctest\\C.java (at line 2)\n" + " public class C extends c.C2 implements i2.I2A {\n" + @@ -6609,7 +6615,7 @@ public void testBug412076() { new String[] { "FooImpl.java", "import org.eclipse.jdt.annotation.*;\n" + - (this.complianceLevel < ClassFileConstants.JDK1_8 + (useDeclarationAnnotations() ? "@NonNullByDefault\n" : "@NonNullByDefault({DefaultLocation.PARAMETER,DefaultLocation.RETURN_TYPE})\n" // avoid @NonNull on type argument ) + @@ -7032,7 +7038,7 @@ public void testBug418235() { " }\n" + "}\n" }; - if (this.complianceLevel < ClassFileConstants.JDK1_8) { + if (useDeclarationAnnotations()) { runNegativeTestWithLibs( testFiles, "----------\n" + @@ -7048,7 +7054,7 @@ public void testBug418235() { } } public void testBug418235b() { - if (this.complianceLevel < ClassFileConstants.JDK1_8) + if (useDeclarationAnnotations()) return; runNegativeTestWithLibs( new String[] { @@ -7084,7 +7090,7 @@ public void testTypeAnnotationProblemNotIn17() { " return local;\n" + " }\n" + "}\n"; - if (this.complianceLevel < ClassFileConstants.JDK1_8) + if (useDeclarationAnnotations()) runConformTestWithLibs( new String[] { "X.java", @@ -7230,7 +7236,7 @@ public void testBug424624a() { " static class DeepNested {}\n" + " }\n" + " static public final @NonNull Inner field1 = new Test3().new Inner();\n" + - (this.complianceLevel < ClassFileConstants.JDK1_8 ? + (useDeclarationAnnotations() ? " static public final @NonNull Inner.DeepInner field2 = field1.new DeepInner();\n" + " static public final @NonNull Nested.InnerInNested field3 = new Nested().new InnerInNested();\n" + " static public final @NonNull Nested.DeepNested field4 = new Nested.DeepNested();\n" @@ -7297,7 +7303,7 @@ public void testBug424624b() { " static class DeepNested {}\n" + " }\n" + " static public final @NonNull Inner field1 = new Test3().new Inner();\n" + - (this.complianceLevel < ClassFileConstants.JDK1_8 ? + (useDeclarationAnnotations() ? " static public final @NonNull Inner.DeepInner field2 = field1.new DeepInner();\n" + " static public final @NonNull Nested.InnerInNested field3 = new Nested().new InnerInNested();\n" + " static public final @NonNull Nested.DeepNested field4 = new Nested.DeepNested();\n" @@ -7361,7 +7367,7 @@ public void testBug432348() { "public enum E {\n" + " @Marker @NonNull A, B, C\n" + "}\n"; - if (this.complianceLevel < ClassFileConstants.JDK1_8) { + if (useDeclarationAnnotations()) { runConformTestWithLibs( new String[] { "E.java", @@ -7444,7 +7450,7 @@ public void testBug403674a() { "2. ERROR in X.java (at line 10)\n" + " switch (value) {}\n" + " ^^^^^\n" + - (this.complianceLevel < ClassFileConstants.JDK1_8 + (useDeclarationAnnotations() ? "Potential null pointer access: The variable value may be null at this location\n" : @@ -7738,7 +7744,7 @@ public void testBug443347b() { "1. ERROR in X.java (at line 11)\n" + " new Super(s) {\n" + " ^\n" + - (this.complianceLevel < ClassFileConstants.JDK1_8 + (useDeclarationAnnotations() ? "Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" : "Null type mismatch (type annotations): required \'@NonNull String\' but this expression has type \'@Nullable String\'\n") + "----------\n"); @@ -7772,7 +7778,7 @@ public void testBug443347c() { "1. ERROR in X.java (at line 12)\n" + " new Super(s) {\n" + " ^\n" + - (this.complianceLevel < ClassFileConstants.JDK1_8 + (useDeclarationAnnotations() ? "Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" : "Null type mismatch (type annotations): required \'@NonNull String\' but this expression has type \'@Nullable String\'\n") + "----------\n"); @@ -7934,7 +7940,7 @@ public void testBug445708() { "3. ERROR in SwitchTest.java (at line 26)\n" + " switch (stringValue)\n" + " ^^^^^^^^^^^\n" + - (this.complianceLevel < ClassFileConstants.JDK1_8 + (useDeclarationAnnotations() ? "Potential null pointer access: The variable stringValue may be null at this location\n" : "Potential null pointer access: this expression has a \'@Nullable\' type\n" ) + "----------\n" + @@ -7951,7 +7957,7 @@ public void testBug445708() { } // same as above but 1.8 with declaration annotations public void testBug445708b() { - if (this.complianceLevel < ClassFileConstants.JDK1_8) return; // only one combination tested + if (useDeclarationAnnotations()) return; // only one combination tested Map customOptions = getCompilerOptions(); customOptions.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "org.foo.NonNull"); customOptions.put(JavaCore.COMPILER_NULLABLE_ANNOTATION_NAME, "org.foo.Nullable"); @@ -8042,7 +8048,7 @@ public void testBug445708b() { } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=452780 - Internal compiler error: arrayIndexOutOfBounds public void testBug452780() { - if (this.complianceLevel < ClassFileConstants.JDK1_8) return; + if (useDeclarationAnnotations()) return; runConformTestWithLibs( new String[] { "Tools2.java", @@ -8098,7 +8104,7 @@ public void testBug455557() { "1. WARNING in X.java (at line 10)\n" + " for (@NonNull Object y : list) { \n" + " ^^^^\n" + - (this.complianceLevel < ClassFileConstants.JDK1_8 + (useDeclarationAnnotations() ? "Null type safety: The expression of type \'String\' needs unchecked conversion to conform to \'@NonNull Object\'\n" : "Null type safety (type annotations): The expression of type \'String\' needs unchecked conversion to conform to \'@NonNull Object\'\n" ) + @@ -8199,7 +8205,7 @@ public void test_null_with_apt_comment4() { "1. WARNING in Test.java (at line 6)\n" + " public static final Test t = new Test(Integer.valueOf(0));\n" + " ^^^^^^^^^^^^^^^^^^\n" + - (this.complianceLevel < ClassFileConstants.JDK1_8 + (useDeclarationAnnotations() ? "Null type safety: The expression of type \'Integer\' needs unchecked conversion to conform to \'@NonNull Integer\'\n" : "Null type safety (type annotations): The expression of type \'Integer\' needs unchecked conversion to conform to \'@NonNull Integer\'\n" ) + @@ -8284,7 +8290,7 @@ public void testBug462790() { " ^^^^^^\n" + "The type parameter T should not be bounded by the final type String. Final types cannot be further extended\n" + "----------\n" + - (this.complianceLevel < ClassFileConstants.JDK1_8 + (useDeclarationAnnotations() ? "3. WARNING in EclipseBug.java (at line 10)\n" + " return commandType.newInstance();\n" + @@ -8341,7 +8347,7 @@ public void testBug459967_Enum_values() { "X.java", "import org.eclipse.jdt.annotation.*;\n" + "public class X {\n" + - (this.complianceLevel < ClassFileConstants.JDK1_8 + (useDeclarationAnnotations() ? " @NonNull MyEnum[] getValues() {\n" : @@ -8355,7 +8361,7 @@ public void testBug459967_Enum_values() { " }\n" + "}\n" }; - if (this.complianceLevel < ClassFileConstants.JDK1_8) { + if (useDeclarationAnnotations()) { runConformTestWithLibs( testFiles, getCompilerOptions(), @@ -8377,7 +8383,7 @@ public void testBug459967_Enum_values_binary() { "X.java", "import org.eclipse.jdt.annotation.*;\n" + "public class X {\n" + - (this.complianceLevel < ClassFileConstants.JDK1_8 + (useDeclarationAnnotations() ? " @NonNull MyEnum[] getValues() {\n" : @@ -8396,7 +8402,7 @@ public void testBug459967_Enum_values_binary() { "MyEnum.java", "public enum MyEnum { V1, V2 }\n", }); - if (this.complianceLevel < ClassFileConstants.JDK1_8) { + if (useDeclarationAnnotations()) { runConformTestWithLibs( false /*flush*/, testFiles, @@ -9190,7 +9196,7 @@ public void testBug502214() { "", }, getCompilerOptions(), - (this.complianceLevel < ClassFileConstants.JDK1_8 ? + (useDeclarationAnnotations() ? "----------\n" + "1. ERROR in test\\X.java (at line 22)\n" + " class Y extends A implements I {\n" + @@ -10546,7 +10552,7 @@ public void testBug542707_002() { ); } public void testBug542707_003() { - if (this.complianceLevel < ClassFileConstants.JDK12) return; // switch expression + if (this.complianceLevel < ClassFileConstants.JDK12 || useDeclarationAnnotations()) return; // switch expression // outer expected type (from assignment) is propagated deeply into a switch expression Runner runner = new Runner(); runner.customOptions = getCompilerOptions(); @@ -10617,7 +10623,7 @@ public void _testBug542707_004() { runner.runNegativeTest(); } public void testBug542707_005() { - if (this.complianceLevel < ClassFileConstants.JDK12) return; // switch expression + if (this.complianceLevel < ClassFileConstants.JDK12 || useDeclarationAnnotations()) return; // switch expression // switch value must not be null (@Nullable) Runner runner = new Runner(); runner.customOptions = getCompilerOptions(); @@ -10714,7 +10720,7 @@ public void testBug545715() { new String[] {"--enable-preview"}); } public void testBug548418_001a() { - if (this.complianceLevel < ClassFileConstants.JDK14) return; + if (this.complianceLevel < ClassFileConstants.JDK14 || useDeclarationAnnotations()) return; runNegativeTestWithLibs( new String[] { "X.java", @@ -10765,7 +10771,7 @@ public void testBug548418_001a() { "----------\n"); } public void testBug548418_001b() { - if (this.complianceLevel < ClassFileConstants.JDK14) return; + if (this.complianceLevel < ClassFileConstants.JDK14 || useDeclarationAnnotations()) return; runNegativeTestWithLibs( new String[] { "X.java", @@ -11239,7 +11245,7 @@ public void testBug565246() { runner.customOptions.put(CompilerOptions.OPTION_ReportLocalVariableHiding, CompilerOptions.IGNORE); runner.customOptions.put(CompilerOptions.OPTION_ReportNullUncheckedConversion, CompilerOptions.ERROR); runner.classLibraries = this.LIBS; - if (this.complianceLevel >= ClassFileConstants.JDK1_8) { + if (!useDeclarationAnnotations()) { runner.expectedCompilerLog = "----------\n" + "1. ERROR in bug\\B.java (at line 39)\n" + diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullDeclarationAnnotationTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullDeclarationAnnotationTest.java new file mode 100644 index 00000000000..4e047231416 --- /dev/null +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullDeclarationAnnotationTest.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2024 GK Software SE and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Stephan Herrmann - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.core.tests.compiler.regression; + +import java.io.IOException; +import junit.framework.Test; +import junit.framework.TestSuite; + +/** Run tests from the super class with (legacy) declaration annotations. */ +public class NullDeclarationAnnotationTest extends NullAnnotationTest { + + public NullDeclarationAnnotationTest(String name) { + super(name); + } + + // Static initializer to specify tests subset using TESTS_* static variables + // All specified tests which do not belong to the class are skipped... + static { +// TESTS_NAMES = new String[] { "testBug545715" }; +// TESTS_NUMBERS = new int[] { 561 }; +// TESTS_RANGE = new int[] { 1, 2049 }; + } + + public static Test suite() { + TestSuite suite = new TestSuite(testClass().getName()); + buildMinimalComplianceTestSuite(FIRST_SUPPORTED_JAVA_VERSION, 1, suite, testClass()); + return suite; + } + + public static Class testClass() { + return NullDeclarationAnnotationTest.class; + } + + public boolean useDeclarationAnnotations() { + return true; + } + + /** + * @deprecated + */ + @Override + protected void setUp() throws Exception { + super.setUp(); + this.TEST_JAR_SUFFIX = ".jar"; + } + + @Override + protected String getAnnotationLibPath() throws IOException { + return getAnnotationV1LibPath(); + } + +} diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakAnnotatedTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakAnnotatedTests.java index f3399e7895f..0b3d03ee0d2 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakAnnotatedTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakAnnotatedTests.java @@ -13,11 +13,9 @@ *******************************************************************************/ package org.eclipse.jdt.core.tests.compiler.regression; -import java.util.List; import java.util.Map; import junit.framework.Test; import junit.framework.TestSuite; -import org.eclipse.jdt.core.tests.util.AbstractCompilerTest; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; @@ -36,63 +34,10 @@ public ResourceLeakAnnotatedTests(String name) { } public static Test suite() { TestSuite suite = new TestSuite(ResourceLeakAnnotatedTests.class.getName()); - // argument 'inheritedDepth' is not exposed in original API, therefore these helpers are copied below with this arg added buildMinimalComplianceTestSuite(FIRST_SUPPORTED_JAVA_VERSION, 1, suite, ResourceLeakAnnotatedTests.class); return suite; } -private static void buildMinimalComplianceTestSuite(int minimalCompliance, int inheritedDepth, TestSuite suite, Class evaluationTestClass) { - int complianceLevels = AbstractCompilerTest.getPossibleComplianceLevels(); - for (int[] map : complianceTestLevelMapping) { - if ((complianceLevels & map[0]) != 0) { - long complianceLevelForJavaVersion = ClassFileConstants.getComplianceLevelForJavaVersion(map[1]); - checkCompliance(evaluationTestClass, minimalCompliance, suite, complianceLevels, inheritedDepth, map[0], map[1], getVersionString(complianceLevelForJavaVersion)); - } - } -} -protected static void checkCompliance(Class evaluationTestClass, int minimalCompliance, TestSuite suite, int complianceLevels, - int inheritedDepth, int abstractCompilerTestCompliance, int classFileConstantsVersion, String release) { - int lev = complianceLevels & abstractCompilerTestCompliance; - if (lev != 0) { - if (lev < minimalCompliance) { - System.err.println("Cannot run "+evaluationTestClass.getName()+" at compliance " + release + "!"); - } else { - suite.addTest(buildUniqueComplianceTestSuite(evaluationTestClass, ClassFileConstants.getComplianceLevelForJavaVersion(classFileConstantsVersion), inheritedDepth)); - } - } -} -public static Test buildUniqueComplianceTestSuite(Class evaluationTestClass, long uniqueCompliance, int inheritedDepth) { - long highestLevel = highestComplianceLevels(); - if (highestLevel < uniqueCompliance) { - String complianceString; - if (highestLevel == ClassFileConstants.JDK10) - complianceString = "10"; - else if (highestLevel == ClassFileConstants.JDK9) - complianceString = "9"; - else if (highestLevel <= CompilerOptions.getFirstSupportedJdkLevel()) - complianceString = CompilerOptions.getFirstSupportedJavaVersion(); - else { - highestLevel = ClassFileConstants.getLatestJDKLevel(); - if (highestLevel > 0) { - complianceString = CompilerOptions.versionFromJdkLevel(highestLevel); - } else { - complianceString = "unknown"; - } - - } - - System.err.println("Cannot run "+evaluationTestClass.getName()+" at compliance "+complianceString+"!"); - return new TestSuite(); - } - TestSuite complianceSuite = new RegressionTestSetup(uniqueCompliance); - List tests = buildTestsList(evaluationTestClass, inheritedDepth); - for (int index=0, size=tests.size(); index getCompilerOptions() { Map options = super.getCompilerOptions(); diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/StackMapAttributeTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/StackMapAttributeTest.java index 49939b89280..9d841ef817b 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/StackMapAttributeTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/StackMapAttributeTest.java @@ -25,7 +25,6 @@ import org.eclipse.jdt.core.tests.util.Util; import org.eclipse.jdt.core.util.ClassFileBytesDisassembler; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; -import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; @SuppressWarnings({ "unchecked", "rawtypes" }) public class StackMapAttributeTest extends AbstractRegressionTest { @@ -8315,7 +8314,7 @@ public void testBug412203_a() throws Exception { "}\n", }, "", - getLibsWithNullAnnotations(CompilerOptions.getFirstSupportedJdkLevel()), + getLibsWithNullAnnotations(), true/*flush*/, null/*vmArgs*/, options, @@ -8437,7 +8436,7 @@ public void testBug412203_b() throws Exception { "}\n", }, "", - getLibsWithNullAnnotations(CompilerOptions.getFirstSupportedJdkLevel()), + getLibsWithNullAnnotations(), true/*flush*/, null/*vmArgs*/, options, @@ -8554,7 +8553,7 @@ public void testBug412203_c() throws Exception { "}\n", }, "", - getLibsWithNullAnnotations(CompilerOptions.getFirstSupportedJdkLevel()), + getLibsWithNullAnnotations(), true/*flush*/, null/*vmArgs*/, options, diff --git a/org.eclipse.jdt.core.tests.model/META-INF/MANIFEST.MF b/org.eclipse.jdt.core.tests.model/META-INF/MANIFEST.MF index bf7f3f4b751..894edc7c2a5 100644 --- a/org.eclipse.jdt.core.tests.model/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.core.tests.model/META-INF/MANIFEST.MF @@ -27,7 +27,6 @@ Require-Bundle: org.eclipse.core.resources;bundle-version="[3.2.0,4.0.0)", org.eclipse.text;bundle-version="[3.2.0,4.0.0)", com.ibm.icu;bundle-version="3.4.4", org.eclipse.core.filesystem;bundle-version="[1.2.0,2.0.0)", - org.eclipse.jdt.annotation;bundle-version="[1.1.0,2.0.0)";resolution:=optional, org.eclipse.jdt.annotation;bundle-version="[2.0.0,3.0.0)";resolution:=optional Bundle-RequiredExecutionEnvironment: JavaSE-17 Eclipse-BundleShape: dir diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations17Test.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations17Test.java index 89c2bb089d3..50dc3b52bfd 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations17Test.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations17Test.java @@ -37,10 +37,10 @@ import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.NodeFinder; import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.tests.compiler.regression.AbstractNullAnnotationTest; import org.eclipse.jdt.core.util.ExternalAnnotationUtil; import org.eclipse.jdt.core.util.ExternalAnnotationUtil.MergeStrategy; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; -import org.osgi.framework.Bundle; public class ExternalAnnotations17Test extends ExternalAnnotations18Test { @@ -62,6 +62,11 @@ public static Test suite() { return buildModelTestSuite(ExternalAnnotations17Test.class, BYTECODE_DECLARATION_ORDER); } + @Override + public void setUpSuite() throws Exception { + super.setUpSuite(); + this.ANNOTATION_LIB = AbstractNullAnnotationTest.getAnnotationV1LibPath(); + } /** * @deprecated */ @@ -69,14 +74,6 @@ static int getJLS8() { return AST.JLS8; } - /** - * @deprecated indirectly uses deprecated class PackageAdmin - */ - @Override - protected Bundle[] getAnnotationBundles() { - return org.eclipse.jdt.core.tests.Activator.getPackageAdmin().getBundles("org.eclipse.jdt.annotation", "[1.1.0,2.0.0)"); - } - @Override public String getSourceWorkspacePath() { // we read individual projects from within this folder: diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NullAnnotationModelTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NullAnnotationModelTests.java index 10e4796e962..82a9c043e96 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NullAnnotationModelTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NullAnnotationModelTests.java @@ -44,6 +44,7 @@ import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.core.dom.*; +import org.eclipse.jdt.core.tests.compiler.regression.AbstractNullAnnotationTest; import org.eclipse.jdt.core.tests.util.Util; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.osgi.framework.Bundle; @@ -76,9 +77,7 @@ public void setUp() throws Exception { File bundleFile = FileLocator.getBundleFileLocation(bundles[0]).get(); this.ANNOTATION_LIB = bundleFile.isDirectory() ? bundleFile.getPath()+"/bin" : bundleFile.getPath(); - bundles = org.eclipse.jdt.core.tests.Activator.getPackageAdmin().getBundles("org.eclipse.jdt.annotation", "[1.1.0,2.0.0)"); - bundleFile = FileLocator.getBundleFileLocation(bundles[0]).get(); - this.ANNOTATION_LIB_V1 = bundleFile.isDirectory() ? bundleFile.getPath()+"/bin" : bundleFile.getPath(); + this.ANNOTATION_LIB_V1 = AbstractNullAnnotationTest.getAnnotationV1LibPath(); } protected String testJarPath(String jarName) throws IOException { From 11f45e682a58f76c86b3244c9fa1ff78bf9e230d Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Mon, 14 Oct 2024 11:03:24 +0530 Subject: [PATCH 08/63] [Formatter][Unnamed patterns/variables] leading space added to conditional statements following an unnamed variable (#3073) * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3070 --- .../formatter/FormatterRegressionTests.java | 68 +++++++++++++++++++ .../jdt/internal/formatter/TokenManager.java | 4 ++ 2 files changed, 72 insertions(+) diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterRegressionTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterRegressionTests.java index ae19e17c604..c10ab0e44fb 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterRegressionTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterRegressionTests.java @@ -16396,4 +16396,72 @@ public void testGH1473c() throws JavaModelException { String input = getCompilationUnit("Formatter", "", "testGH1473", "in.java").getSource(); formatSource(input, getCompilationUnit("Formatter", "", "testGH1473", "C_out.java").getSource()); } +// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3070 +// [Formatter] leading space added to conditional statements following an unnamed variable +public void testIssue3070() { + setComplianceLevel(CompilerOptions.VERSION_23); + String source = + """ + class Example { + private void foo() { + var a = false; + + try { + } catch (Exception _) { // <- the unnamed variable triggers the issue + } + + if (a) { // <- no leading space before the variable name + } + } + } + """; + formatSource(source, + """ + class Example { + private void foo() { + var a = false; + + try { + } catch (Exception _) { // <- the unnamed variable triggers the issue + } + + if (a) { // <- no leading space before the variable name + } + } + } + """); +} +public void testIssue3070_2() { + setComplianceLevel(CompilerOptions.VERSION_23); + String source = + """ + class Example { + private void foo() { + var a = false; + + try { + } catch (Exception e) { // <- the unnamed variable triggers the issue + } + + if (a) { // <- no leading space before the variable name + } + } + } + """; + formatSource(source, + """ + class Example { + private void foo() { + var a = false; + + try { + } catch (Exception e) { // <- the unnamed variable triggers the issue + } + + if (a) { // <- no leading space before the variable name + } + } + } + """); +} } diff --git a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TokenManager.java b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TokenManager.java index cc151b0391b..05d0814b859 100644 --- a/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TokenManager.java +++ b/org.eclipse.jdt.core/formatter/org/eclipse/jdt/internal/formatter/TokenManager.java @@ -19,6 +19,7 @@ import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameNotAToken; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameStringLiteral; import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameTextBlock; +import static org.eclipse.jdt.internal.compiler.parser.TerminalTokens.TokenNameUNDERSCORE; import java.util.ArrayList; import java.util.HashMap; @@ -175,6 +176,9 @@ public int findIndex(int positionInSource, int tokenType, boolean forward) { if (tokenType == TerminalTokens.getRestrictedKeyword(toString(t))) break; } + if (t.tokenType == TokenNameUNDERSCORE && tokenType == TokenNameIdentifier) { + break; + } index += forward ? 1 : -1; } return index; From 4a59a20199ccc9bf47c8b57aad364d59f7978dfc Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Mon, 14 Oct 2024 15:09:16 +0530 Subject: [PATCH 09/63] [switch][sealed types] ECJ fails to signal a completely dominated case arm (#3075) * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3035 --- .../internal/compiler/ast/TypePattern.java | 6 ++- .../regression/SwitchPatternTest.java | 44 +++++++++++++++++++ .../core/tests/RunVariousPatternsTests.java | 2 + 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypePattern.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypePattern.java index 80f226ca1e0..b4609a13231 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypePattern.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypePattern.java @@ -202,7 +202,11 @@ public boolean dominates(Pattern p) { return false; if (p.resolvedType == null || this.resolvedType == null) return false; - return p.resolvedType.erasure().isSubtypeOf(this.resolvedType.erasure(), false); + + if (p.resolvedType.isSubtypeOf(this.resolvedType, false)) + return true; + + return p.resolvedType.erasure().findSuperTypeOriginatingFrom(this.resolvedType.erasure()) != null; } @Override diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java index caab7826379..23037af7c0d 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java @@ -8864,4 +8864,48 @@ final class A implements I { "Type I cannot be safely cast to B\n" + "----------\n"); } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3035 + // [switch][sealed types] ECJ fails to signal a completely dominated case arm + public void testIssue3035() { + runNegativeTest( + new String[] { + "X.java", + """ + abstract sealed class J permits X.S, A { + } + + final class A extends J { + } + + public class X { + + sealed class S extends J permits SS { + } + + final class SS extends S {} + + int testExhaustive(J ji) { + return switch (ji) { // Exhaustive! + case A a -> 42; + case S e -> 4200; + case SS e -> 420; + }; + } + + public static void main(String[] args) { + S xs = null; + System.out.println(new X().testExhaustive(new X().new S())); + J ji = new X().new SS(); + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 18)\n" + + " case SS e -> 420;\n" + + " ^^^^^^^^^^^^^^^^^^^^^\n" + + "This case label is dominated by one of the preceding case labels\n" + + "----------\n"); + } } diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousPatternsTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousPatternsTests.java index 7613e60ac0c..d486b0d4665 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousPatternsTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousPatternsTests.java @@ -61,6 +61,8 @@ public static Class[] getAllTestClasses() { NullAnnotationTests21.class, ComplianceDiagnoseTest.class, InstanceofExpressionTest.class, + PrimitiveInPatternsTest.class, + PrimitiveInPatternsTestSH.class, }; } From f7676886fe00819e9c889833a9d510e88894a226 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Oct 2024 09:04:29 +0000 Subject: [PATCH 10/63] Bump actions/upload-artifact from 4.4.2 to 4.4.3 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.2 to 4.4.3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/84480863f228bb9747b473957fcc9e309aa96097...b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5eca03e9ba3..274d29a889f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Upload - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: Event File path: ${{ github.event_path }} @@ -46,7 +46,7 @@ jobs: mvn -U clean verify --batch-mode --fail-at-end -Ptest-on-javase-21 -Pbree-libs -Papi-check -Djava.io.tmpdir=$WORKSPACE/tmp -Dproject.build.sourceEncoding=UTF-8 -Dtycho.surefire.argLine="--add-modules ALL-SYSTEM -Dcompliance=1.8,11,17,20 -Djdt.performance.asserts=disabled" -Dcbi-ecj-version=99.99 - name: Upload Test Results for Linux if: always() - uses: actions/upload-artifact@84480863f228bb9747b473957fcc9e309aa96097 # v4.4.2 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: test-results-linux if-no-files-found: warn From dbd74288d9ca6d84fa666227c42602aaa634f870 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Mon, 14 Oct 2024 21:16:38 +0530 Subject: [PATCH 11/63] JDT unable to detect variable reference errors in nested enum with an AnonymousClassDeclaration(#3078) * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/1368 --- .../internal/compiler/lookup/ClassScope.java | 13 +- .../tests/compiler/regression/EnumTest.java | 123 +++++++++++++++--- .../compiler/regression/LocalEnumTest.java | 2 +- 3 files changed, 118 insertions(+), 20 deletions(-) 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 84b3e686712..803238e70aa 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 @@ -664,8 +664,19 @@ private void checkAndSetModifiers() { if (compilerOptions().complianceLevel < ClassFileConstants.JDK9) modifiers |= ClassFileConstants.AccFinal; // set AccEnum flag for anonymous body of enum constants - if (this.referenceContext.allocation.type == null) + if (this.referenceContext.allocation.type == null) { modifiers |= ClassFileConstants.AccEnum; + // 8.1.1.4 local enum classes are implicitly static - we can't trust isLocalType() which answers true for all anonymous types. + Scope scope = this; + while ((scope = scope.parent) != null) { + if (scope instanceof MethodScope methodScope) { + if (methodScope.referenceContext instanceof TypeDeclaration) + continue; + modifiers |= ClassFileConstants.AccStatic; + break; + } + } + } } else if (this.parent.referenceContext() instanceof TypeDeclaration) { TypeDeclaration typeDecl = (TypeDeclaration) this.parent.referenceContext(); if (TypeDeclaration.kind(typeDecl.modifiers) == TypeDeclaration.INTERFACE_DECL) { diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/EnumTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/EnumTest.java index 9115221af82..28f71bd72fe 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/EnumTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/EnumTest.java @@ -3891,24 +3891,40 @@ public void test112() { } //https://bugs.eclipse.org/bugs/show_bug.cgi?id=93789 public void test113() { - if (this.complianceLevel >= ClassFileConstants.JDK16) { - return; - } - this.runNegativeTest( - new String[] { - "X.java", - "enum BugDemo {\n" + - " FOO() {\n" + - " static int bar;\n" + - " }\n" + - "}\n", - }, - "----------\n" + - "1. ERROR in X.java (at line 3)\n" + - " static int bar;\n" + - " ^^^\n" + - "The field bar cannot be declared static in a non-static inner type, unless initialized with a constant expression\n" + - "----------\n"); + if (this.complianceLevel < ClassFileConstants.JDK16) + this.runNegativeTest( + new String[] { + "X.java", + "enum BugDemo {\n" + + " FOO() {\n" + + " static int bar;\n" + + " }\n" + + "}\n" + + "public class X {\n" + + " public static void main(String [] args) {}\n" + + "}\n", + }, + "----------\n" + + "1. ERROR in X.java (at line 3)\n" + + " static int bar;\n" + + " ^^^\n" + + "The field bar cannot be declared static in a non-static inner type, unless initialized with a constant expression\n" + + "----------\n"); + else + this.runConformTest( + new String[] { + "X.java", + "enum BugDemo {\n" + + " FOO() {\n" + + " static int bar;\n" + + " }\n" + + "}\n" + + "public class X {\n" + + " public static void main(String [] args) {}\n" + + "}\n", + }, + ""); + } //https://bugs.eclipse.org/bugs/show_bug.cgi?id=99428 and https://bugs.eclipse.org/bugs/show_bug.cgi?id=99655 public void test114() { @@ -7346,4 +7362,75 @@ enum Inner extends com.test.Option { "com.test.Option cannot be resolved to a type\n" + "----------\n"); } +// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/1368 +// JDT unable to detect variable reference errors in nested enum with an AnonymousClassDeclaration +public void testGHIssue1368() { + if (this.complianceLevel < ClassFileConstants.JDK16) + return; + this.runNegativeTest(new String[] { + "X.java", + """ + public class X { + static int f() { + int h = 20; + enum ENUM_C { + INT_C () { + @Override + void k() { + System.out.println(h + "null"); + } + }; + + void k() { + System.out.println(h); + } + } + + ENUM_C i = ENUM_C.INT_C; + i.k(); + return 20; + } + } + + + class C { + + int h = 20; + enum ENUM_C { + INT_C () { + @Override + void k() { + System.out.println(h + "null"); + } + }; + + void k() { + System.out.println(h); + } + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 8)\n" + + " System.out.println(h + \"null\");\n" + + " ^\n" + + "Cannot make a static reference to the non-static variable h\n" + + "----------\n" + + "2. ERROR in X.java (at line 13)\n" + + " System.out.println(h);\n" + + " ^\n" + + "Cannot make a static reference to the non-static variable h\n" + + "----------\n" + + "3. ERROR in X.java (at line 31)\n" + + " System.out.println(h + \"null\");\n" + + " ^\n" + + "Cannot make a static reference to the non-static field h\n" + + "----------\n" + + "4. ERROR in X.java (at line 36)\n" + + " System.out.println(h);\n" + + " ^\n" + + "Cannot make a static reference to the non-static field h\n" + + "----------\n"); +} } diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LocalEnumTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LocalEnumTest.java index 21351aeab31..eca3965acaf 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LocalEnumTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/LocalEnumTest.java @@ -5945,7 +5945,7 @@ public void test151() throws Exception { + " [inner class info: #1 p/X$1E, outer class info: #0\n" + " inner name: #54 E, accessflags: 17416 abstract static],\n" + " [inner class info: #14 p/X$1E$1, outer class info: #0\n" - + " inner name: #0, accessflags: 16384 default]\n" + + " inner name: #0, accessflags: 16392 static]\n" + " Enclosing Method: #48 #50 p/X.main([Ljava/lang/String;)V\n" + "\n" + "Nest Host: #48 p/X\n" From 44940179280b2aeaf0f17af299076151e60ddca0 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Tue, 15 Oct 2024 10:06:27 +0530 Subject: [PATCH 12/63] [Sealed Types + Enhanced Switch] Incorrect diagnostic about switch not being exhaustive (#3080) * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2720 --- .../compiler/ast/SwitchStatement.java | 120 ++++++++----- .../compiler/lookup/ReferenceBinding.java | 3 +- .../regression/SwitchPatternTest.java | 164 ++++++++++++++++++ 3 files changed, 246 insertions(+), 41 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java index 537fb6e2591..5f3e745fb56 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java @@ -25,7 +25,10 @@ import java.lang.invoke.ConstantBootstraps; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.function.Function; import java.util.function.IntPredicate; import org.eclipse.jdt.core.compiler.CharOperation; @@ -43,7 +46,15 @@ import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.impl.JavaFeature; -import org.eclipse.jdt.internal.compiler.lookup.*; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; +import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; +import org.eclipse.jdt.internal.compiler.lookup.RecordComponentBinding; +import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; +import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.SyntheticMethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeIds; import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; @SuppressWarnings("rawtypes") @@ -1307,23 +1318,18 @@ && defaultFound && isExhaustive()) { ((this.containsPatterns || this.containsNull) || (constantCount >= this.caseCount && constantCount != ((ReferenceBinding)expressionType).enumConstantCount()))) { - FieldBinding[] enumFields = ((ReferenceBinding) expressionType.erasure()).fields(); - for (int i = 0, max = enumFields.length; i < max; i++) { - FieldBinding enumConstant = enumFields[i]; - if ((enumConstant.modifiers & ClassFileConstants.AccEnum) == 0) continue; - findConstant : { - for (int j = 0; j < constantCount; j++) { - if ((enumConstant.id + 1) == this.otherConstants[j].c.intValue()) // zero should not be returned see bug 141810 - break findConstant; - } - this.switchBits &= ~SwitchStatement.Exhaustive; - // enum constant did not get referenced from switch - boolean suppress = (this.defaultCase != null && (this.defaultCase.bits & DocumentedCasesOmitted) != 0); - if (!suppress) { - if (isEnhanced) - upperScope.problemReporter().enhancedSwitchMissingDefaultCase(this.expression); - else + Set unenumeratedConstants = unenumeratedConstants((ReferenceBinding) expressionType, constantCount); + if (unenumeratedConstants.size() != 0) { + this.switchBits &= ~SwitchStatement.Exhaustive; + // enum constant did not get referenced from switch + boolean suppress = (this.defaultCase != null && (this.defaultCase.bits & DocumentedCasesOmitted) != 0); + if (!suppress) { + if (isEnhanced) + upperScope.problemReporter().enhancedSwitchMissingDefaultCase(this.expression); + else { + for (FieldBinding enumConstant : unenumeratedConstants) { reportMissingEnumConstantCase(upperScope, enumConstant); + } } } } @@ -1341,6 +1347,32 @@ && defaultFound && isExhaustive()) { if (this.scope != null) this.scope.enclosingCase = null; // no longer inside switch case block } } + + // Return the set of enumerations belonging to the selector enum type that are not listed in case statements. + private Set unenumeratedConstants(ReferenceBinding enumType, int constantCount) { + FieldBinding[] enumFields = ((ReferenceBinding) enumType.erasure()).fields(); + Set unenumerated = new HashSet<>(Arrays.asList(enumFields)); + for (int i = 0, max = enumFields.length; i < max; i++) { + FieldBinding enumConstant = enumFields[i]; + if ((enumConstant.modifiers & ClassFileConstants.AccEnum) == 0) { + unenumerated.remove(enumConstant); + continue; + } + for (int j = 0; j < constantCount; j++) { + if (TypeBinding.equalsEquals(this.otherConstants[j].e.resolvedType, enumType)) { + if (this.otherConstants[j].e instanceof NameReference reference) { + FieldBinding field = reference.fieldBinding(); + int intValue = field.original().id + 1; + if ((enumConstant.id + 1) == intValue) { // zero should not be returned see bug 141810 + unenumerated.remove(enumConstant); + break; + } + } + } + } + } + return unenumerated; + } private boolean isCaseStmtNullDefault(CaseStatement caseStmt) { return caseStmt != null && caseStmt.constantExpressions.length == 2 @@ -1398,20 +1430,11 @@ private boolean checkAndSetEnhanced(BlockScope upperScope, TypeBinding expressio return false; } private boolean checkAndFlagDefaultSealed(BlockScope skope, CompilerOptions compilerOptions) { - if (this.defaultCase != null) { // mark covered as a side effect (since covers is intro in 406) + if (this.defaultCase != null) { this.switchBits |= SwitchStatement.Exhaustive; return false; } - boolean checkSealed = this.containsPatterns - && JavaFeature.SEALED_CLASSES.isSupported(compilerOptions) - && JavaFeature.PATTERN_MATCHING_IN_SWITCH.isSupported(compilerOptions) - && this.expression.resolvedType instanceof ReferenceBinding; - if (!checkSealed) return false; - ReferenceBinding ref = (ReferenceBinding) this.expression.resolvedType; - if (!(ref.isClass() || ref.isInterface() || ref.isTypeVariable() || ref.isIntersectionType())) - return false; - - if (ref.isRecord()) { + if (this.expression.resolvedType instanceof ReferenceBinding ref && ref.isRecord()) { boolean isRecordPattern = false; for (Pattern pattern : this.caseLabelElements) { if (pattern instanceof RecordPattern) { @@ -1422,7 +1445,16 @@ private boolean checkAndFlagDefaultSealed(BlockScope skope, CompilerOptions comp if (isRecordPattern) return checkAndFlagDefaultRecord(skope, compilerOptions, ref); } - if (!ref.isSealed()) return false; + boolean checkSealed = JavaFeature.SEALED_CLASSES.isSupported(compilerOptions) + && JavaFeature.PATTERN_MATCHING_IN_SWITCH.isSupported(compilerOptions) + && this.expression.resolvedType instanceof ReferenceBinding + && this.expression.resolvedType.isSealed(); + + if (!checkSealed) return false; + ReferenceBinding ref = (ReferenceBinding) this.expression.resolvedType; + if (!(ref.isClass() || ref.isInterface() || ref.isTypeVariable() || ref.isIntersectionType())) + return false; + if (!isExhaustiveWithCaseTypes(ref.getAllEnumerableReferenceTypes(), this.caseLabelElementTypes)) { if (this instanceof SwitchExpression) // non-exhaustive switch expressions will be flagged later. return false; @@ -1458,25 +1490,33 @@ private boolean checkAndFlagDefaultRecord(BlockScope skope, CompilerOptions comp return false; } private boolean isExhaustiveWithCaseTypes(List allAllowedTypes, List listedTypes) { - int pendingTypes = allAllowedTypes.size(); - for (ReferenceBinding pt : allAllowedTypes) { - /* Per JLS 14.11.1.1: A type T that names an abstract sealed class or sealed interface is covered - if every permitted direct subclass or subinterface of it is covered. These subtypes are already - added to allAllowedTypes and subject to cover test. - */ - if (pt.isAbstract() && pt.isSealed()) { - --pendingTypes; + Iterator iterator = allAllowedTypes.iterator(); + while (iterator.hasNext()) { + ReferenceBinding next = iterator.next(); + if (next.isAbstract() && next.isSealed()) { + /* Per JLS 14.11.1.1: A type T that names an abstract sealed class or sealed interface is covered + if every permitted direct subclass or subinterface of it is covered. These subtypes are already + added to allAllowedTypes and subject to cover test. + */ + iterator.remove(); continue; } for (TypeBinding type : listedTypes) { // permits specifies classes, not parameterizations - if (pt.erasure().isCompatibleWith(type.erasure())) { - --pendingTypes; + if (next.erasure().isCompatibleWith(type.erasure())) { + iterator.remove(); break; } } + if (next.isEnum()) { + int constantCount = this.otherConstants == null ? 0 : this.otherConstants.length; + Set unenumeratedConstants = unenumeratedConstants(next, constantCount); + if (unenumeratedConstants.size() == 0) { + iterator.remove(); + } + } } - return pendingTypes == 0; + return allAllowedTypes.size() == 0; } private boolean needPatternDispatchCopy() { if (this.containsPatterns || (this.switchBits & QualifiedEnum) != 0) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java index 71e27ac86f2..a0a9cc5e1d1 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java @@ -52,6 +52,7 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.lookup; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; @@ -2563,7 +2564,7 @@ public List getAllEnumerableReferenceTypes() { oldSet = permSet; permSet = tmp; } while (oldSet.size() != permSet.size()); - return Arrays.asList(permSet.toArray(new ReferenceBinding[0])); + return new ArrayList<>(permSet); } // 5.1.6.1 Allowed Narrowing Reference Conversion diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java index 23037af7c0d..e88d92cc513 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java @@ -8908,4 +8908,168 @@ public static void main(String[] args) { "This case label is dominated by one of the preceding case labels\n" + "----------\n"); } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2720 + // [Sealed Types + Enhanced Switch] Incorrect diagnostic about switch not being exhaustive + public void testIssue2720() { + runConformTest( + new String[] { + "X.java", + """ + sealed interface I { + + enum E implements I { + A, B, C; + } + } + + public class X { + + static void d(I i) { + switch (i) { // error: An enhanced switch statement should be exhaustive; a default label expected + case I.E.A -> { System.out.println("I.E.A"); } + case I.E.B -> { System.out.println("I.E.B"); } + case I.E.C -> { System.out.println("I.E.C"); } + } + } + + public static void main(String [] args) { + d(I.E.A); + d(I.E.B); + d(I.E.C); + } + } + """ + }, + "I.E.A\n" + + "I.E.B\n" + + "I.E.C"); + } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2720 + // [Sealed Types + Enhanced Switch] Incorrect diagnostic about switch not being exhaustive + public void testIssue2720_2() { + runNegativeTest( + new String[] { + "X.java", + """ + sealed interface I { + + enum E implements I { + A, B, C; + } + + enum K implements I { + D, E, F; + } + } + + class Test { + + void d(I i) { + switch (i) { + case I.E.A -> {} + case I.E.B -> {} + case I.E.C -> {} + case I.K k -> {} + } + switch (i) { + case I.E.A -> {} + case I.E.B -> {} + case I.E.C -> {} + default -> {} + } + switch (i) { + case I.E.A -> {} + case I.E.B -> {} + case I.E.C -> {} + case I.K.D -> {} + case I.K.E -> {} + case I.K.F -> {} + default -> {} + } + switch (i) { + case I.E.A -> {} + case I.E.B -> {} + case I.E.C -> {} + case I.K.D -> {} + case I.K.E -> {} + default -> {} + } + switch (i) { + case I.E.A -> {} + case I.E.B -> {} + case I.E.C -> {} + case I.K.D -> {} + case I.K.E -> {} + } + switch (i) { + case I.E.A -> {} + case I.E.B -> {} + case I.E.C -> {} + case I.K.D -> {} + case I.K.E -> {} + case I.K.F -> {} + } + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 44)\n" + + " switch (i) {\n" + + " ^\n" + + "An enhanced switch statement should be exhaustive; a default label expected\n" + + "----------\n"); + } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2720 + // [Sealed Types + Enhanced Switch] Incorrect diagnostic about switch not being exhaustive + public void testIssue2720_3() { + runConformTest( + new String[] { + "X.java", + """ + sealed interface I { + + enum E implements I { + A, B, C; + } + + enum K implements I { + D, E, F; + } + } + + public class X { + + static void d(I i) { + switch (i) { + case I.E.A -> { System.out.println("I.E.A"); } + case I.E.B -> { System.out.println("I.E.B"); } + case I.E.C -> { System.out.println("I.E.C"); } + case I.K.D -> { System.out.println("I.K.D"); } + case I.K.E -> { System.out.println("I.K.E"); } + case I.K.F -> { System.out.println("I.K.F"); } + } + } + + public static void main(String [] args) { + d(I.E.A); + d(I.E.B); + d(I.E.C); + d(I.K.D); + d(I.K.E); + d(I.K.F); + } + } + """ + }, + "I.E.A\n" + + "I.E.B\n" + + "I.E.C\n" + + "I.K.D\n" + + "I.K.E\n" + + "I.K.F"); + } } From ee344cd4a9d225c73ac8911903e99aa25d438889 Mon Sep 17 00:00:00 2001 From: Jay Arthanareeswaran Date: Wed, 9 Oct 2024 17:08:37 +0530 Subject: [PATCH 13/63] ASTParser should detect and report when no suitable system library has been configured #3047 --- .../compiler/lookup/CompilationUnitScope.java | 1 + .../core/tests/dom/ASTConverterBugsTest.java | 63 ++++++++++++++++++- 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java index 1809520b367..efb8efd3bc1 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java @@ -791,6 +791,7 @@ ImportBinding[] getDefaultImports() { this.environment.missingClassFileLocation, false, null/*resolving j.l.O is not specific to any referencing type*/); BinaryTypeBinding missingObject = this.environment.createMissingType(null, TypeConstants.JAVA_LANG_OBJECT); importBinding = missingObject.fPackage; + return new ImportBinding[] {new ImportBinding(TypeConstants.JAVA_LANG, true, importBinding, null)}; } return this.environment.root.defaultImports = new ImportBinding[] {new ImportBinding(TypeConstants.JAVA_LANG, true, importBinding, null)}; } diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterBugsTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterBugsTest.java index 3220c21f3f9..86043af2fda 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterBugsTest.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterBugsTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2017 IBM Corporation and others. + * Copyright (c) 2000, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -14,6 +14,7 @@ package org.eclipse.jdt.core.tests.dom; import java.io.IOException; +import java.util.Hashtable; import java.util.List; import java.util.Map; import junit.framework.Test; @@ -24,8 +25,10 @@ import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.WorkingCopyOwner; +import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.core.dom.*; @SuppressWarnings("rawtypes") @@ -1324,4 +1327,62 @@ public void testBug381503() throws CoreException, IOException { deleteProject("P"); } } +public void testGH3047() throws Exception { + Hashtable options = JavaCore.getDefaultOptions(); + options.put(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_9); + options.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_9); + options.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_9); + + createJava9Project("P"); + createFolder("P/src"); + + String srcFolderInWS = "/P/src"; + createFolder(srcFolderInWS + "/resources/examples/mockito"); + String srcFilePathInWS = srcFolderInWS + "/resources/examples/mockito/MockingFromFinder.java"; + createFile(srcFilePathInWS, + """ + package examples.mockito; + public class MockingFromFinder{}""" + ); + srcFilePathInWS = srcFolderInWS + "/resources/examples/mockito/MockingWhileAdding.java"; + createFile(srcFilePathInWS, + """ + package examples.mockito; + public class MockingWhileAdding { + public static void calculateWithAdder(int x, int y) { + IOperation adder = new Adder()::execute; + } + public interface IOperation { + int execute(int x, int y); + } + public static class Adder implements IOperation { + public int execute(int x, int y) { + return x+y; + } + } + }""" + ); + String[] paths = new String[2]; + paths[0] = getWorkspacePath() + "P/src/resources/examples/mockito/MockingFromFinder.java"; + paths[1] = getWorkspacePath() + "P/src/resources/examples/mockito/MockingWhileAdding.java"; + @SuppressWarnings("deprecation") + ASTParser parser = ASTParser.newParser(AST_INTERNAL_JLS9); + parser.setCompilerOptions(options); + parser.setResolveBindings(true); + parser.setStatementsRecovery(true); + parser.setBindingsRecovery(true); + parser.setEnvironment(null, new String[] {getWorkspacePath() + "P/src/resources"}, null, false); + parser.setKind(ASTParser.K_COMPILATION_UNIT); + class MyFileASTRequestor extends FileASTRequestor { + @Override + public void acceptAST(String sourceFilePath, CompilationUnit cu) { + IProblem[] problems = cu.getProblems(); + assertTrue("Number of problems should be at least 1", (problems.length >= 1)); + IProblem problem = problems[0]; + assertEquals("Unexpected problem in " + sourceFilePath, + "Pb(324) The type java.lang.Object cannot be resolved. It is indirectly referenced from required .class files", problem.toString()); + } + } + parser.createASTs(paths, null, new String[] {}, new MyFileASTRequestor() {}, null); +} } From 74d062b38d3003638127127cc5358dd9f79df0f4 Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Thu, 10 Oct 2024 12:25:05 +0200 Subject: [PATCH 14/63] ASTParser should detect and report when no suitable system library has been configured let ASTParser detect missing system library + look in classpaths and/or javaproject + first look for module java.base else for class java.lang.Object fix test setups: + converterJclMin9 lacked some relevant classes + initialization of variable CONVERTER_JCL15_LIB was missing + correction of .classpath for some converter test projects: + don't use 1.8 lib for projects > 9 + mark system libraries as modular modified the new test: + copy the original test from DOM test to a new compiler test - here we expect missing type errors instead of NPE + update test expectation in DOM to the new exception --- .../src/java/io/PrintStream.java | 35 ++++++ .../src/java/lang/AutoCloseable.java | 5 + .../src/java/lang/Override.java | 20 ++++ .../src/java/lang/System.java | 24 +++++ .../regression/ProblemTypeAndMethodTest.java | 102 ++++++++++++++++++ .../JCL/converterJclMin9.jar | Bin 12854 -> 16669 bytes .../JCL/converterJclMin9src.zip | Bin 9483 -> 11239 bytes .../core/tests/dom/ASTConverterBugsTest.java | 70 +++++++++++- .../core/tests/dom/ConverterTestSetup.java | 8 ++ .../describing/ImportRewrite_RecordTest.java | 2 +- .../workspace/Converter9/.classpath | 6 +- .../workspace/Converter_15_1/.classpath | 6 +- .../org/eclipse/jdt/core/dom/ASTParser.java | 53 +++++++-- 13 files changed, 315 insertions(+), 16 deletions(-) create mode 100644 JCL/converterJclMin9/src/java/io/PrintStream.java create mode 100644 JCL/converterJclMin9/src/java/lang/AutoCloseable.java create mode 100644 JCL/converterJclMin9/src/java/lang/Override.java create mode 100644 JCL/converterJclMin9/src/java/lang/System.java diff --git a/JCL/converterJclMin9/src/java/io/PrintStream.java b/JCL/converterJclMin9/src/java/io/PrintStream.java new file mode 100644 index 00000000000..503f080425d --- /dev/null +++ b/JCL/converterJclMin9/src/java/io/PrintStream.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package java.io; + +public class PrintStream { + + public void println() { + } + + public void println(String s) { + } + + public void println(int i) { + } + + public void println(Object o) { + } + + public void print(String s) { + } + + public void print(Object o) { + } + + public void print(int i) { + } +} diff --git a/JCL/converterJclMin9/src/java/lang/AutoCloseable.java b/JCL/converterJclMin9/src/java/lang/AutoCloseable.java new file mode 100644 index 00000000000..b6fbe467732 --- /dev/null +++ b/JCL/converterJclMin9/src/java/lang/AutoCloseable.java @@ -0,0 +1,5 @@ +package java.lang; + +public interface AutoCloseable { + void close() throws Exception; +} diff --git a/JCL/converterJclMin9/src/java/lang/Override.java b/JCL/converterJclMin9/src/java/lang/Override.java new file mode 100644 index 00000000000..e30eba7b6df --- /dev/null +++ b/JCL/converterJclMin9/src/java/lang/Override.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2005 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package java.lang; + +import java.lang.annotation.ElementType; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.annotation.Retention; + +@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) +public @interface Override { +} \ No newline at end of file diff --git a/JCL/converterJclMin9/src/java/lang/System.java b/JCL/converterJclMin9/src/java/lang/System.java new file mode 100644 index 00000000000..a13acd2404d --- /dev/null +++ b/JCL/converterJclMin9/src/java/lang/System.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package java.lang; + +import java.io.PrintStream; + +public class System { + + public static PrintStream err; + public static PrintStream out; + + public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); + public static String getProperty(String s) { + return null; + } +} diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ProblemTypeAndMethodTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ProblemTypeAndMethodTest.java index 812d8e49655..90f7870a193 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ProblemTypeAndMethodTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ProblemTypeAndMethodTest.java @@ -10328,4 +10328,106 @@ F test() { """; runner.runNegativeTest(); } +public void testGH3047() throws Exception { + Runner runner = new Runner(); + runner.testFiles = new String[] { + "resources/examples/mockito/MockingFromFinder.java", + """ + package examples.mockito; + public class MockingFromFinder{} + """, + "resources/examples/mockito/MockingWhileAdding.java", + """ + package examples.mockito; + public class MockingWhileAdding { + public static void calculateWithAdder(int x, int y) { + IOperation adder = new Adder()::execute; + } + public interface IOperation { + int execute(int x, int y); + } + public static class Adder implements IOperation { + public int execute(int x, int y) { + return x+y; + } + } + } + """ + }; + runner.classLibraries = new String[0]; + if (this.complianceLevel <= ClassFileConstants.JDK13) { + runner.expectedCompilerLog = + """ + ---------- + 1. ERROR in resources\\examples\\mockito\\MockingFromFinder.java (at line 1) + package examples.mockito; + ^ + The type java.lang.Object cannot be resolved. It is indirectly referenced from required .class files + ---------- + 2. ERROR in resources\\examples\\mockito\\MockingFromFinder.java (at line 2) + public class MockingFromFinder{} + ^^^^^^^^^^^^^^^^^ + Implicit super constructor Object() is undefined for default constructor. Must define an explicit constructor + ---------- + ---------- + 1. ERROR in resources\\examples\\mockito\\MockingWhileAdding.java (at line 1) + package examples.mockito; + ^ + The type java.lang.Object cannot be resolved. It is indirectly referenced from required .class files + ---------- + 2. ERROR in resources\\examples\\mockito\\MockingWhileAdding.java (at line 2) + public class MockingWhileAdding { + ^^^^^^^^^^^^^^^^^^ + Implicit super constructor Object() is undefined for default constructor. Must define an explicit constructor + ---------- + 3. ERROR in resources\\examples\\mockito\\MockingWhileAdding.java (at line 9) + public static class Adder implements IOperation { + ^^^^^ + Implicit super constructor Object() is undefined for default constructor. Must define an explicit constructor + ---------- + """; + } else { + runner.expectedCompilerLog = + """ + ---------- + 1. ERROR in resources\\examples\\mockito\\MockingFromFinder.java (at line 1) + package examples.mockito; + ^ + The type java.lang.Object cannot be resolved. It is indirectly referenced from required .class files + ---------- + 2. ERROR in resources\\examples\\mockito\\MockingFromFinder.java (at line 1) + package examples.mockito; + ^ + The type java.lang.Error cannot be resolved. It is indirectly referenced from required .class files + ---------- + 3. ERROR in resources\\examples\\mockito\\MockingFromFinder.java (at line 1) + package examples.mockito; + ^ + The type java.lang.String cannot be resolved. It is indirectly referenced from required .class files + ---------- + 4. ERROR in resources\\examples\\mockito\\MockingFromFinder.java (at line 2) + public class MockingFromFinder{} + ^^^^^^^^^^^^^^^^^ + Implicit super constructor Object() is undefined for default constructor. Must define an explicit constructor + ---------- + ---------- + 1. ERROR in resources\\examples\\mockito\\MockingWhileAdding.java (at line 1) + package examples.mockito; + ^ + The type java.lang.Object cannot be resolved. It is indirectly referenced from required .class files + ---------- + 2. ERROR in resources\\examples\\mockito\\MockingWhileAdding.java (at line 2) + public class MockingWhileAdding { + ^^^^^^^^^^^^^^^^^^ + Implicit super constructor Object() is undefined for default constructor. Must define an explicit constructor + ---------- + 3. ERROR in resources\\examples\\mockito\\MockingWhileAdding.java (at line 9) + public static class Adder implements IOperation { + ^^^^^ + Implicit super constructor Object() is undefined for default constructor. Must define an explicit constructor + ---------- + """; + } + runner.runNegativeTest(); +} } diff --git a/org.eclipse.jdt.core.tests.model/JCL/converterJclMin9.jar b/org.eclipse.jdt.core.tests.model/JCL/converterJclMin9.jar index ffedd500d726431f42eb55dbf1d795da94419f56..0fe91046f6f18f2fdcc2f6988ac0715e2cb2e842 100644 GIT binary patch literal 16669 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 diff --git a/org.eclipse.jdt.core.tests.model/JCL/converterJclMin9src.zip b/org.eclipse.jdt.core.tests.model/JCL/converterJclMin9src.zip index d04c399924b1c5ac41014ff0e49d99c27e3f6e7b..636debe16aa6af0de19299fa1a8a375844e4159f 100644 GIT binary patch delta 3274 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 diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterBugsTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterBugsTest.java index 86043af2fda..ebcef50186b 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterBugsTest.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterBugsTest.java @@ -14,9 +14,12 @@ package org.eclipse.jdt.core.tests.dom; import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Map; +import java.util.Set; import junit.framework.Test; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.IClassFile; @@ -1373,16 +1376,73 @@ public int execute(int x, int y) { parser.setBindingsRecovery(true); parser.setEnvironment(null, new String[] {getWorkspacePath() + "P/src/resources"}, null, false); parser.setKind(ASTParser.K_COMPILATION_UNIT); + try { + parser.createASTs(paths, null, new String[] {}, null, null); + fail("Expected exception was not thrown"); + } catch (IllegalStateException ise) { + assertEquals("Missing system library", ise.getMessage()); + } +} +public void testGH3047_2() throws Exception { + Hashtable options = JavaCore.getDefaultOptions(); + options.put(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_9); + options.put(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_9); + options.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_9); + + IJavaProject javaProject = createJavaProject("P", new String[] {""}, new String[] {"CONVERTER_JCL18_LIB"}, "1.8"); + createFolder("P/src"); + + String srcFolderInWS = "/P/src"; + createFolder(srcFolderInWS + "/resources/examples/mockito"); + String srcFilePathInWS = srcFolderInWS + "/resources/examples/mockito/MockingFromFinder.java"; + createFile(srcFilePathInWS, + """ + package examples.mockito; + public class MockingFromFinder{}""" + ); + srcFilePathInWS = srcFolderInWS + "/resources/examples/mockito/MockingWhileAdding.java"; + createFile(srcFilePathInWS, + """ + package examples.mockito; + public class MockingWhileAdding { + public static void calculateWithAdder(int x, int y) { + IOperation adder = new Adder()::execute; + } + public interface IOperation { + int execute(int x, int y); + } + public static class Adder implements IOperation { + public int execute(int x, int y) { + return x+y; + } + } + }""" + ); + String[] paths = new String[2]; + paths[0] = getWorkspacePath() + "P/src/resources/examples/mockito/MockingFromFinder.java"; + paths[1] = getWorkspacePath() + "P/src/resources/examples/mockito/MockingWhileAdding.java"; + @SuppressWarnings("deprecation") + ASTParser parser = ASTParser.newParser(AST_INTERNAL_JLS9); + parser.setProject(javaProject); + parser.setCompilerOptions(options); + parser.setResolveBindings(true); + parser.setStatementsRecovery(true); + parser.setBindingsRecovery(true); + parser.setEnvironment(null, new String[] {getWorkspacePath() + "P/src/resources"}, null, false); + parser.setKind(ASTParser.K_COMPILATION_UNIT); + Set expectedProblems = new HashSet<>(Arrays.asList( + "Pb(324) The type java.lang.Object cannot be resolved. It is indirectly referenced from required .class files", + "Pb(140) Implicit super constructor Object() is undefined for default constructor. Must define an explicit constructor" + )); + Set actualProblems = new HashSet<>(); class MyFileASTRequestor extends FileASTRequestor { @Override public void acceptAST(String sourceFilePath, CompilationUnit cu) { - IProblem[] problems = cu.getProblems(); - assertTrue("Number of problems should be at least 1", (problems.length >= 1)); - IProblem problem = problems[0]; - assertEquals("Unexpected problem in " + sourceFilePath, - "Pb(324) The type java.lang.Object cannot be resolved. It is indirectly referenced from required .class files", problem.toString()); + for (IProblem prob : cu.getProblems()) + actualProblems.add(prob.toString()); } } parser.createASTs(paths, null, new String[] {}, new MyFileASTRequestor() {}, null); + assertEquals(expectedProblems, actualProblems); } } diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ConverterTestSetup.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ConverterTestSetup.java index 6bd0a2a5c2b..772b0037780 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ConverterTestSetup.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ConverterTestSetup.java @@ -207,6 +207,14 @@ public void setUpJCLClasspathVariables(String compliance, boolean useFullJCL) th new IPath[] {getConverterJCLPath("14"), getConverterJCLSourcePath("14"), getConverterJCLRootSourcePath()}, null); } + } else if ("15".equals(compliance)) { + if (JavaCore.getClasspathVariable("CONVERTER_JCL15_LIB") == null) { + setupExternalJCL("converterJclMin15"); + JavaCore.setClasspathVariables( + new String[] {"CONVERTER_JCL15_LIB", "CONVERTER_JCL15_SRC", "CONVERTER_JCL15_SRCROOT"}, + new IPath[] {getConverterJCLPath("15"), getConverterJCLSourcePath("15"), getConverterJCLRootSourcePath()}, + null); + } } else if ("17".equals(compliance)) { if (JavaCore.getClasspathVariable("CONVERTER_JCL_17_LIB") == null) { setupExternalJCL("converterJclMin17"); diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ImportRewrite_RecordTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ImportRewrite_RecordTest.java index 3db4f9604fe..71248344102 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ImportRewrite_RecordTest.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ImportRewrite_RecordTest.java @@ -63,7 +63,7 @@ public static Test suite() { protected void setUp() throws Exception { super.setUp(); - IJavaProject proj= createJavaProject(PROJECT, new String[] {"src"}, new String[] {"JCL18_LIB"}, "bin", "15"); + IJavaProject proj= createJavaProject(PROJECT, new String[] {"src"}, new String[] {"JCL14_LIB"}, "bin", "15"); proj.setOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, JavaCore.SPACE); proj.setOption(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, "4"); proj.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_16); diff --git a/org.eclipse.jdt.core.tests.model/workspace/Converter9/.classpath b/org.eclipse.jdt.core.tests.model/workspace/Converter9/.classpath index 3522bc0c3f5..6da169624e4 100644 --- a/org.eclipse.jdt.core.tests.model/workspace/Converter9/.classpath +++ b/org.eclipse.jdt.core.tests.model/workspace/Converter9/.classpath @@ -1,6 +1,10 @@ - + + + + + diff --git a/org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/.classpath b/org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/.classpath index 3522bc0c3f5..49c53688466 100644 --- a/org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/.classpath +++ b/org.eclipse.jdt.core.tests.model/workspace/Converter_15_1/.classpath @@ -1,6 +1,10 @@ - + + + + + diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTParser.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTParser.java index 9c0024e1469..a156cd00975 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTParser.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTParser.java @@ -24,21 +24,17 @@ import java.util.Map; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SubMonitor; -import org.eclipse.jdt.core.IClassFile; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IJavaElement; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.ITypeRoot; -import org.eclipse.jdt.core.JavaCore; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jdt.core.WorkingCopyOwner; +import org.eclipse.jdt.core.*; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall; import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath; import org.eclipse.jdt.internal.compiler.batch.Main; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.env.IBinaryType; +import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.parser.RecoveryScanner; import org.eclipse.jdt.internal.compiler.parser.RecoveryScannerData; import org.eclipse.jdt.internal.compiler.parser.Scanner; @@ -272,8 +268,49 @@ private List getClasspath() throws IllegalStateException { } catch (IllegalArgumentException e) { throw new IllegalStateException("invalid environment settings", e); //$NON-NLS-1$ } + if ((this.bits & CompilationUnitResolver.RESOLVE_BINDING) != 0) { + checkForSystemLibrary(allClasspaths); + } return allClasspaths; } + + private void checkForSystemLibrary(List allClasspaths) { + boolean hasSystemLibrary = true; // default for 1.8 setting without a valid project + boolean hasModule = false; + Throwable exception = null; + String compliance = this.compilerOptions.get(JavaCore.COMPILER_COMPLIANCE); + if (CompilerOptions.versionToJdkLevel(compliance) >= ClassFileConstants.JDK9) { + hasSystemLibrary = allClasspaths.stream().anyMatch(cp -> cp.getModule(TypeConstants.JAVA_DOT_BASE) != null); + if (!hasSystemLibrary && this.project != null) { + // not found in allClasspaths, try this.project instead: + try { + // try module java.base: + for (IPackageFragmentRoot root : this.project.getAllPackageFragmentRoots()) { + IModuleDescription moduleDescription = root.getModuleDescription(); + if (moduleDescription != null) { + hasModule = true; + if (moduleDescription.getElementName().equals(String.valueOf(TypeConstants.JAVA_DOT_BASE))) { + hasSystemLibrary = true; + break; + } + } + } + } catch (JavaModelException e) { + exception = e; + } + if (!hasModule) { + try { + // if no modules try class java.lang.Object: + hasSystemLibrary = this.project.findType(String.valueOf(TypeConstants.CharArray_JAVA_LANG_OBJECT)) != null; + } catch (JavaModelException e) { + exception = e; + } + } + } + if (!hasSystemLibrary) + throw new IllegalStateException("Missing system library", exception); //$NON-NLS-1$ + } + } /** * Sets all the setting to their default values. */ From 26e33c7915827b9f67aeafe20b578a78811480c4 Mon Sep 17 00:00:00 2001 From: Suby S Surendran Date: Tue, 15 Oct 2024 16:24:12 +0530 Subject: [PATCH 15/63] [23] DOM Support for implicitly declared classes (#2996) * Added ASTRewrite support(Unit test & Rewrite) and ASTConverter unit test * Incorporated the code review changes --- .../core/tests/dom/ASTConverter_23Test.java | 30 ++ ...TRewritingImplicitTypeDeclarationTest.java | 288 ++++++++++++++++++ .../rewrite/describing/ASTRewritingTest.java | 11 + .../dom/org/eclipse/jdt/core/dom/AST.java | 13 + .../internal/core/dom/NaiveASTFlattener.java | 19 ++ .../core/dom/rewrite/ASTRewriteAnalyzer.java | 14 + .../core/dom/rewrite/ASTRewriteFlattener.java | 11 + 7 files changed, 386 insertions(+) create mode 100644 org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingImplicitTypeDeclarationTest.java diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter_23Test.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter_23Test.java index 67d879e4bd9..9ceaf5eba69 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter_23Test.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter_23Test.java @@ -20,8 +20,12 @@ import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.Block; import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.ImplicitTypeDeclaration; import org.eclipse.jdt.core.dom.ImportDeclaration; +import org.eclipse.jdt.core.dom.Javadoc; +import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.Modifier; public class ASTConverter_23Test extends ConverterTestSetup { @@ -103,4 +107,30 @@ void m() { assertEquals("Incorrect name", "java.lang.System.out", imp.getName().toString()); } } + + public void test002() throws CoreException { + String contents = """ + /** */ + void main() { + System.out.println("Eclipse"); + } + """; + this.workingCopy = getWorkingCopy("/Converter_23/src/X.java", true/*resolve*/); + ASTNode node = buildAST(contents, this.workingCopy); + assertEquals("Wrong type of statement", ASTNode.COMPILATION_UNIT, node.getNodeType()); + CompilationUnit compilationUnit = (CompilationUnit) node; + ImplicitTypeDeclaration implicitTypeDeclaration = (ImplicitTypeDeclaration) compilationUnit.types().get(0); + assertEquals("Not an ImplicitTypeDeclaration Type", implicitTypeDeclaration.getNodeType(), ASTNode.UNNAMED_CLASS); + assertEquals("Not an ImplicitTypeDeclaration Name Type", implicitTypeDeclaration.getName().getNodeType(), ASTNode.SIMPLE_NAME); + assertEquals("Identifier is not empty String", implicitTypeDeclaration.getName().getIdentifier(), ""); + MethodDeclaration bodyDeclaration = (MethodDeclaration) implicitTypeDeclaration.bodyDeclarations().get(0); + assertEquals("Not a Method Declaration", bodyDeclaration.getNodeType(), ASTNode.METHOD_DECLARATION); + assertEquals("Method Declaration start is not one", bodyDeclaration.getStartPosition(), 1); + Javadoc javaDoc = bodyDeclaration.getJavadoc(); + assertEquals("Not a JavaDoc", javaDoc.getNodeType(), ASTNode.JAVADOC); + assertEquals("JavaDoc startPosition is not One", javaDoc.getStartPosition(), 1); + Block block = bodyDeclaration.getBody(); + assertEquals("Not a Block", block.getNodeType(), ASTNode.BLOCK); + assertEquals("Block startPosition is not correct", block.getStartPosition(), 21); + } } diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingImplicitTypeDeclarationTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingImplicitTypeDeclarationTest.java new file mode 100644 index 00000000000..7c8a6170115 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingImplicitTypeDeclarationTest.java @@ -0,0 +1,288 @@ +/******************************************************************************* + * Copyright (c) 2024 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.jdt.core.tests.rewrite.describing; + +import java.util.List; +import junit.framework.Test; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.dom.*; +import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; +import org.eclipse.jdt.core.dom.rewrite.ListRewrite; + +public class ASTRewritingImplicitTypeDeclarationTest extends ASTRewritingTest{ + + public ASTRewritingImplicitTypeDeclarationTest(String name, int apiLevel) { + super(name, apiLevel); + } + + public static Test suite() { + return createSuite(ASTRewritingImplicitTypeDeclarationTest.class, 23); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + if (this.apiLevel == AST.JLS23 ) { + this.project1.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_23); + this.project1.setOption(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_23); + this.project1.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_23); + this.project1.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); + } + } + + public void test001() throws Exception { + AST ast = AST.newAST(AST.JLS23, true); + // Create CompilationUnit + CompilationUnit compilationUnit = ast.newCompilationUnit(); + + ImplicitTypeDeclaration implicitTypeDeclaration = ast.newImplicitTypeDeclaration(); + + Javadoc javaDoc= ast.newJavadoc(); + TextElement textElem= ast.newTextElement(); + textElem.setText("Hello"); + TagElement tagElement= ast.newTagElement(); + tagElement.fragments().add(textElem); + javaDoc.tags().add(tagElement); + implicitTypeDeclaration.setJavadoc(javaDoc); + + QualifiedName qualifiedName = ast.newQualifiedName(ast.newName("System"), ast.newSimpleName("out")); + MethodInvocation methodInvocation = ast.newMethodInvocation(); + methodInvocation.setExpression(qualifiedName); + methodInvocation.setName(ast.newSimpleName("println")); + + StringLiteral literal = ast.newStringLiteral(); + literal.setLiteralValue("Eclipse"); + methodInvocation.arguments().add(literal); + ExpressionStatement expressionStatement = ast.newExpressionStatement(methodInvocation); + + Block block= ast.newBlock(); + block.statements().add(expressionStatement); + MethodDeclaration methodDeclaration = ast.newMethodDeclaration(); + methodDeclaration.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID)); + methodDeclaration.setName(ast.newSimpleName("main")); + methodDeclaration.setBody(block); + implicitTypeDeclaration.bodyDeclarations().add(methodDeclaration); + // Add Implicity Type class to compilation unit + compilationUnit.types().add(implicitTypeDeclaration); + + StringBuilder buf = new StringBuilder(); + buf.append("/** \n"); + buf.append(" * Hello\n"); + buf.append(" */\n"); + buf.append(" void main(){\n"); + buf.append(" System.out.println(\"Eclipse\");\n"); + buf.append(" }\n"); + + assertEqualString(compilationUnit.toString(), buf.toString()); + } + + //javaDoc + public void test002() throws Exception { + AST ast = AST.newAST(AST.JLS23, true); + IPackageFragment pack1= this.sourceFolder.createPackageFragment("test1", false, null); + StringBuilder buf = new StringBuilder(); + buf= new StringBuilder(); + buf.append("/** \n"); + buf.append(" * Hello\n"); + buf.append(" */\n"); + buf.append("void main(){\n"); + buf.append(" System.out.println(\"Eclipse\");\n"); + buf.append("}\n"); + + ICompilationUnit cu= pack1.createCompilationUnit("X.java", buf.toString(), false, null); + CompilationUnit astRoot= createAST(cu); + ASTRewrite rewrite= ASTRewrite.create(astRoot.getAST()); + + assertTrue("Parse errors", (astRoot.getFlags() & ASTNode.MALFORMED) == 0); + + ImplicitTypeDeclaration implicitTypeDeclaration= findImplicitDeclaration(astRoot, ""); + List methodDeclarationsList = implicitTypeDeclaration.bodyDeclarations(); + MethodDeclaration methodDeclaration = methodDeclarationsList.get(0); + { + + Javadoc javaDoc = methodDeclaration.getJavadoc(); + + Javadoc newJavaDoc= ast.newJavadoc(); + TextElement textElem= ast.newTextElement(); + textElem.setText("Eclipse"); + TagElement tagElement= ast.newTagElement(); + tagElement.fragments().add(textElem); + newJavaDoc.tags().add(tagElement); + + rewrite.replace(javaDoc, newJavaDoc, null); + } + + String preview = evaluateRewrite(cu, rewrite); + buf= new StringBuilder(); + + buf.append("/**\n"); + buf.append(" * Eclipse\n"); + buf.append(" */\n"); + buf.append("void main(){\n"); + buf.append(" System.out.println(\"Eclipse\");\n"); + buf.append("}\n"); + + assertEqualString(preview, buf.toString()); + + { + Javadoc javaDoc = methodDeclaration.getJavadoc(); + Javadoc newJavaDoc = null; + + rewrite.replace(javaDoc, newJavaDoc, null); + } + + preview = evaluateRewrite(cu, rewrite); + buf= new StringBuilder(); + + buf.append("void main(){\n"); + buf.append(" System.out.println(\"Eclipse\");\n"); + buf.append("}\n"); + + assertEqualString(preview, buf.toString()); + } + + //adding more MEthodDeclaration + public void test003() throws Exception { + AST ast = AST.newAST(AST.JLS23, true); + IPackageFragment pack1= this.sourceFolder.createPackageFragment("test1", false, null); + StringBuilder buf = new StringBuilder(); + buf= new StringBuilder(); + buf.append("/** \n"); + buf.append(" * Hello\n"); + buf.append(" */\n"); + buf.append("void main(){\n"); + buf.append(" System.out.println(\"Eclipse\");\n"); + buf.append("}\n"); + + ICompilationUnit cu= pack1.createCompilationUnit("X.java", buf.toString(), false, null); + CompilationUnit astRoot= createAST(cu); + ASTRewrite rewrite= ASTRewrite.create(astRoot.getAST()); + + assertTrue("Parse errors", (astRoot.getFlags() & ASTNode.MALFORMED) == 0); + ImplicitTypeDeclaration implicitTypeDeclaration= findImplicitDeclaration(astRoot, ""); + { + MethodInvocation methodInvocation = ast.newMethodInvocation(); + methodInvocation.setName(ast.newSimpleName("println")); + + StringLiteral literal = ast.newStringLiteral(); + literal.setLiteralValue("abc"); + + QualifiedName qualifiedName = ast.newQualifiedName(ast.newName("System"), ast.newSimpleName("out")); + + methodInvocation.setExpression(qualifiedName); + methodInvocation.arguments().add(literal); + + ExpressionStatement expressionStatement = ast.newExpressionStatement(methodInvocation); + + Block block = ast.newBlock(); + block.statements().add(expressionStatement); + + MethodDeclaration methodDeclaration = ast.newMethodDeclaration(); + methodDeclaration.setBody(block); + methodDeclaration.setName(ast.newSimpleName("abc")); + methodDeclaration.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID)); + + ListRewrite listRewrite= rewrite.getListRewrite(implicitTypeDeclaration, ImplicitTypeDeclaration.BODY_DECLARATIONS_PROPERTY); + listRewrite.insertAt(methodDeclaration, 1, null); + } + + String preview = evaluateRewrite(cu, rewrite); + buf= new StringBuilder(); + + buf.append("/** \n"); + buf.append(" * Hello\n"); + buf.append(" */\n"); + buf.append("void main(){\n"); + buf.append(" System.out.println(\"Eclipse\");\n"); + buf.append("}\n"); + buf.append("\n"); + buf.append("void abc() {\n"); + buf.append(" System.out.println(\"abc\");\n"); + buf.append("}\n"); + + assertEqualString(preview, buf.toString()); + } + public void test004() throws Exception { + AST ast = AST.newAST(AST.JLS23, true); + IPackageFragment pack1= this.sourceFolder.createPackageFragment("test1", false, null); + StringBuilder buf = new StringBuilder(); + buf= new StringBuilder(); + buf.append("/** \n"); + buf.append(" * Hello\n"); + buf.append(" */\n"); + buf.append("void main(){\n"); + buf.append(" System.out.println(\"main\");\n"); + buf.append("}\n"); + buf.append("void abc(){\n"); + buf.append(" System.out.println(\"abc\");\n"); + buf.append("}\n"); + + ICompilationUnit cu= pack1.createCompilationUnit("X.java", buf.toString(), false, null); + CompilationUnit astRoot= createAST(cu); + ASTRewrite rewrite= ASTRewrite.create(astRoot.getAST()); + + assertTrue("Parse errors", (astRoot.getFlags() & ASTNode.MALFORMED) == 0); + ImplicitTypeDeclaration implicitTypeDeclaration= findImplicitDeclaration(astRoot, ""); + List bodyDeclaration = implicitTypeDeclaration.bodyDeclarations(); + System.out.println("sasi"); + { + + rewrite.remove(bodyDeclaration.get(1), null);//remove one method + + MethodInvocation methodInvocation = ast.newMethodInvocation(); + methodInvocation.setName(ast.newSimpleName("println")); + + StringLiteral literal = ast.newStringLiteral(); + literal.setLiteralValue("xyz"); + + QualifiedName qualifiedName = ast.newQualifiedName(ast.newName("System"), ast.newSimpleName("out")); + + methodInvocation.setExpression(qualifiedName); + methodInvocation.arguments().add(literal); + + ExpressionStatement expressionStatement = ast.newExpressionStatement(methodInvocation); + + Block block = ast.newBlock(); + block.statements().add(expressionStatement); + + MethodDeclaration methodDeclaration = ast.newMethodDeclaration(); + methodDeclaration.setBody(block); + methodDeclaration.setName(ast.newSimpleName("xyz")); + methodDeclaration.setReturnType2(ast.newPrimitiveType(PrimitiveType.VOID)); + + ListRewrite listRewrite= rewrite.getListRewrite(implicitTypeDeclaration, ImplicitTypeDeclaration.BODY_DECLARATIONS_PROPERTY); + listRewrite.insertAt(methodDeclaration, 1, null); + + String preview = evaluateRewrite(cu, rewrite); + buf= new StringBuilder(); + + buf.append("/** \n"); + buf.append(" * Hello\n"); + buf.append(" */\n"); + buf.append("void main(){\n"); + buf.append(" System.out.println(\"main\");\n"); + buf.append("}\n"); + buf.append("void xyz() {\n"); + buf.append(" System.out.println(\"xyz\");\n"); + buf.append("}\n"); + + assertEqualString(preview, buf.toString()); + + } + } + +} \ No newline at end of file diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingTest.java index dcfc5187067..728508b8534 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingTest.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingTest.java @@ -446,4 +446,15 @@ protected static MethodDeclaration createNewMethod(AST ast, String name, boolean return decl; } + public static ImplicitTypeDeclaration findImplicitDeclaration(CompilationUnit astRoot, String simpleTypeName) { + List types= astRoot.types(); + for (int i= 0; i < types.size(); i++) { + ImplicitTypeDeclaration elem= (ImplicitTypeDeclaration) types.get(i); + if (simpleTypeName.equals(elem.getName().getIdentifier())) { + return elem; + } + } + return null; + } + } diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/AST.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/AST.java index 54c4790a500..fddc84e74f8 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/AST.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/AST.java @@ -3189,6 +3189,19 @@ public TryStatement newTryStatement() { return new TryStatement(this); } + /** + * Creates an unparented class declaration node owned by this AST. + * The name of the class is an unspecified, but legal, name; + * no modifiers; no doc comment; no superclass or superinterfaces; + * and an empty class body. + * + * @return a new unparented type declaration node + * @since 3.40 + */ + public ImplicitTypeDeclaration newImplicitTypeDeclaration() { + return new ImplicitTypeDeclaration(this); + } + /** * Creates an unparented class declaration node owned by this AST. * The name of the class is an unspecified, but legal, name; diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/NaiveASTFlattener.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/NaiveASTFlattener.java index 2b65c3a90d6..ffc404a2c9d 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/NaiveASTFlattener.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/NaiveASTFlattener.java @@ -1912,6 +1912,25 @@ public boolean visit(TryStatement node) { return false; } + @Override + public boolean visit(ImplicitTypeDeclaration node) { + //javaDoc + if (node.getJavadoc() != null) { + node.getJavadoc().accept(this); + } + + //bodyDeclaration + this.indent++; + for (Object element : node.bodyDeclarations()) { + BodyDeclaration d = (BodyDeclaration) element; + d.accept(this); + } + this.indent--; + printIndent(); + + return false; + } + @Override public boolean visit(TypeDeclaration node) { if (node.getJavadoc() != null) { diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java index 76c09181b52..94d00d72b08 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteAnalyzer.java @@ -1849,6 +1849,20 @@ public boolean visit(CompilationUnit node) { return false; } + @Override + public boolean visit(ImplicitTypeDeclaration node) { + if (!hasChildrenChanges(node)) { + return doVisitUnchangedChildren(node); + } + //javaDoc + rewriteJavadoc(node, ImplicitTypeDeclaration.JAVADOC_PROPERTY); + + int startIndent= getIndent(node.getStartPosition()) + 1; + int startPos= node.getStartPosition(); + rewriteParagraphList(node, ImplicitTypeDeclaration.BODY_DECLARATIONS_PROPERTY, startPos, startIndent, -1, 2); + return false; + } + @Override public boolean visit(TypeDeclaration node) { if (!hasChildrenChanges(node)) { diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteFlattener.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteFlattener.java index 3326d9fd4c0..d53c75bc0cf 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteFlattener.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/internal/core/dom/rewrite/ASTRewriteFlattener.java @@ -1209,6 +1209,17 @@ public boolean visit(TryStatement node) { return false; } + @Override + public boolean visit(ImplicitTypeDeclaration node) { + ASTNode javadoc= getChildNode(node, TypeDeclaration.JAVADOC_PROPERTY); + if (javadoc != null) { + javadoc.accept(this); + } + + visitList(node, TypeDeclaration.BODY_DECLARATIONS_PROPERTY, null); + return false; + } + @Override public boolean visit(TypeDeclaration node) { int apiLevel= node.getAST().apiLevel(); From 1eb248b131a998f2173abbf99590aec1a0d07712 Mon Sep 17 00:00:00 2001 From: Mickael Istria Date: Tue, 15 Oct 2024 19:50:31 +0200 Subject: [PATCH 16/63] Create warning when parsing targets too new Java versions (#3020) Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3018 --- .../eclipse/jdt/core/compiler/IProblem.java | 2 ++ .../compiler/impl/CompilerOptions.java | 10 +++++++- .../jdt/internal/compiler/parser/Parser.java | 16 +++++++++++++ .../compiler/problem/ProblemReporter.java | 10 ++++++++ .../compiler/problem/messages.properties | 1 + .../regression/CompilerInvocationTests.java | 23 +++++++++++++++++++ 6 files changed, 61 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java index 409dd20af6f..3fc0a2bbf8d 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java @@ -2231,6 +2231,8 @@ public interface IProblem { int PreviewAPIUsed = Compliance + 1108; /** @since 3.39*/ int JavaVersionNotSupported = Compliance + 1109; + /** @since 3.40*/ + int JavaVersionTooRecent = Compliance + 1110; /** @since 3.13 */ int UnlikelyCollectionMethodArgumentType = 1200; diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java index 1dd18f24658..9364cae2b09 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java @@ -433,6 +433,14 @@ public class CompilerOptions { public long complianceLevel; /** Java source level, refers to a JDK version, e.g. {@link ClassFileConstants#JDK1_4} */ public long sourceLevel; + /** + * Initially requested source version, not necessarily consistent with {@link #sourceLevel} as + * sourceLevel forcibly contain a version that is compatible with ECJ. + *

Consumers are free to use {@code sourceLevel} or this + * {@code requestedSourceVersion} value to decide of their behavior.

+ *

May be {@code null}.

+ */ + public String requestedSourceVersion; /** VM target level, refers to a JDK version, e.g. {@link ClassFileConstants#JDK1_4} */ public long targetJDK; /** Source encoding format */ @@ -1769,7 +1777,7 @@ public void set(Map optionsMap) { long level = versionToJdkLevel(optionValue); if (level != 0) this.complianceLevel = level; } - if ((optionValue = optionsMap.get(OPTION_Source)) != null) { + if ((this.requestedSourceVersion = optionValue = optionsMap.get(OPTION_Source)) != null) { long level = versionToJdkLevel(optionValue); if (level != 0) this.sourceLevel = level; } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java index 01f3495673b..fc1b3a274cf 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java @@ -37,6 +37,7 @@ import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; +import java.lang.Runtime.Version; import java.util.*; import java.util.function.Consumer; import java.util.function.Predicate; @@ -12833,6 +12834,21 @@ public CompilationUnitDeclaration parse( compilationResult, 0); + var problemReporterContext = this.problemReporter.referenceContext; + this.problemReporter.referenceContext = this.referenceContext; + if (this.problemReporter != null && this.options != null && this.options.requestedSourceVersion != null && !this.options.requestedSourceVersion.isBlank()) { + try { + var requestedVersion = Version.parse(this.options.requestedSourceVersion); + var latestVersion = Version.parse(CompilerOptions.getLatestVersion()); + if (requestedVersion.compareTo(latestVersion) > 0) { + this.problemReporter.tooRecentJavaVersion(requestedVersion.toString(), latestVersion.toString()); + } + } catch (Exception ex) { + this.problemReporter.abortDueToInternalError(ex.getMessage()); + } + } + this.problemReporter.referenceContext = problemReporterContext; + /* scanners initialization */ char[] contents; try { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java index 2890a77a419..b22e5e5439e 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java @@ -826,6 +826,16 @@ public void abortDueToNotSupportedJavaVersion(String notSupportedVersion, String 0, 0); } +public void tooRecentJavaVersion(String notSupportedVersion, String firstSupportedVersion) { + String[] args = new String[] {notSupportedVersion, firstSupportedVersion}; + this.handle( + IProblem.JavaVersionTooRecent, + args, + args, + ProblemSeverities.Warning, + 0, + 0); +} public void abstractMethodCannotBeOverridden(SourceTypeBinding type, MethodBinding concreteMethod) { this.handle( diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties index e9b69918495..5796ad53262 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties @@ -908,6 +908,7 @@ 1107 = The Java feature ''{0}'' is only available with source level {1} and above 1108 = You are using an API that is part of a preview feature and may be removed in future 1109 = Compiling for Java version ''{0}'' is no longer supported. Minimal supported version is ''{1}'' +1110 = Compiling for Java version ''{0}'' is not supported yet. Using ''{1}'' instead # more programming problems: 1200 = Unlikely argument type {0} for {1} on a {2} 1201 = Unlikely argument type for equals(): {0} seems to be unrelated to {2} diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java index fa210f8defc..5b6d42a991a 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java @@ -65,6 +65,7 @@ import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.ICompilerRequestor; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.impl.IrritantSet; import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; @@ -1247,6 +1248,7 @@ class ProblemAttributes { expectedProblemAttributes.put("FeatureNotSupported", new ProblemAttributes(CategorizedProblem.CAT_COMPLIANCE)); expectedProblemAttributes.put("PreviewAPIUsed", new ProblemAttributes(CategorizedProblem.CAT_COMPLIANCE)); expectedProblemAttributes.put("JavaVersionNotSupported", new ProblemAttributes(CategorizedProblem.CAT_COMPLIANCE)); + expectedProblemAttributes.put("JavaVersionTooRecent", new ProblemAttributes(CategorizedProblem.CAT_COMPLIANCE)); expectedProblemAttributes.put("SwitchExpressionsYieldIncompatibleResultExpressionTypes", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); expectedProblemAttributes.put("SwitchExpressionsYieldEmptySwitchBlock", new ProblemAttributes(CategorizedProblem.CAT_SYNTAX)); expectedProblemAttributes.put("SwitchExpressionsYieldNoResultExpression", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); @@ -2381,6 +2383,7 @@ class ProblemAttributes { expectedProblemAttributes.put("FeatureNotSupported", SKIP); expectedProblemAttributes.put("PreviewAPIUsed", SKIP); expectedProblemAttributes.put("JavaVersionNotSupported", SKIP); + expectedProblemAttributes.put("JavaVersionTooRecent", SKIP); expectedProblemAttributes.put("SwitchExpressionsYieldIncompatibleResultExpressionTypes", SKIP); expectedProblemAttributes.put("SwitchExpressionsYieldEmptySwitchBlock", SKIP); expectedProblemAttributes.put("SwitchExpressionsYieldNoResultExpression", SKIP); @@ -2582,4 +2585,24 @@ private boolean isDeprecated(Field field) { } return false; } + + public void testTooNewJavaVersionRequested() { + Map options = new HashMap<>(JavaCore.getDefaultOptions()); + String latestJavaVersionSupportedByECJ = CompilerOptions.versionFromJdkLevel(ClassFileConstants.getLatestJDKLevel()); + String message = """ + ---------- + 1. WARNING in A.java (at line 1) + class A{} + ^ + Compiling for Java version 'XXX0' is not supported yet. Using 'XXX' instead + ---------- + """.replaceAll("XXX", latestJavaVersionSupportedByECJ); + options.put(CompilerOptions.OPTION_Source, latestJavaVersionSupportedByECJ + "0"); + runNegativeTest(new String[] {"A.java", "class A{}"}, + message, + null, + false, + null, + options); + } } \ No newline at end of file From ee0782e2354713804d099fa29d4c86c66c72f3c7 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Wed, 16 Oct 2024 11:49:01 +0530 Subject: [PATCH 17/63] Code review and incremental clean up of sealed classes implementation (#3092) --- .../internal/compiler/apt/model/Factory.java | 4 +- .../compiler/classfmt/ClassFileReader.java | 18 ++++----- .../classfmt/ExternalAnnotationDecorator.java | 4 +- .../internal/compiler/env/IBinaryType.java | 2 +- .../compiler/lookup/BinaryTypeBinding.java | 25 +++--------- .../internal/compiler/lookup/ClassScope.java | 40 ++++++++++--------- .../lookup/ExtraCompilerModifiers.java | 2 +- .../jdt/internal/compiler/parser/Parser.java | 6 +-- .../compiler/problem/ProblemReporter.java | 2 +- .../regression/ClassFileReaderTest_17.java | 6 +-- .../eclipse/jdt/internal/core/BinaryType.java | 4 +- 11 files changed, 47 insertions(+), 66 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/Factory.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/Factory.java index f4f7316bf84..e99a32406c4 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/Factory.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/Factory.java @@ -189,14 +189,14 @@ private static void decodeModifiers(Set result, int modifiers, int[] c try { appendModifier(result, modifiers, checkBit, Modifier.valueOf("NON_SEALED")); //$NON-NLS-1$ } catch(IllegalArgumentException iae) { - // Don't have JDK 15, just ignore and proceed. + // Don't have JDK 17, just ignore and proceed. } break; case ExtraCompilerModifiers.AccSealed : try { appendModifier(result, modifiers, checkBit, Modifier.valueOf("SEALED")); //$NON-NLS-1$ } catch(IllegalArgumentException iae) { - // Don't have JDK 15, just ignore and proceed. + // Don't have JDK 17, just ignore and proceed. } break; } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java index d37d1a6467b..7173a363b8d 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java @@ -60,7 +60,7 @@ public class ClassFileReader extends ClassFileStruct implements IBinaryType { private InnerClassInfo[] innerInfos; private final char[][] interfaceNames; private final int interfacesCount; - private final char[][] permittedSubtypesNames; + private char[][] permittedSubtypesNames; private int permittedSubtypesCount; private MethodInfo[] methods; private final int methodsCount; @@ -393,7 +393,6 @@ public ClassFileReader(byte[] classFileBytes, char[] fileName, boolean fullyInit char[] enclosingTypeNam = null; char[] sourceFileNam = null; char[] signatur = null; - char[][] permittedSubtypesNam = null; for (int i = 0; i < attributesCount; i++) { int utf8Offset = this.constantPoolOffsets[u2At(readOffset)]; @@ -527,11 +526,11 @@ public ClassFileReader(byte[] classFileBytes, char[] fileName, boolean fullyInit if (this.permittedSubtypesCount != 0) { accessFlag |= ExtraCompilerModifiers.AccSealed; offset += 2; - permittedSubtypesNam = new char[this.permittedSubtypesCount][]; + this.permittedSubtypesNames = new char[this.permittedSubtypesCount][]; for (int j = 0; j < this.permittedSubtypesCount; j++) { utf8Offset = this.constantPoolOffsets[u2At(this.constantPoolOffsets[u2At(offset)] + 1)]; - permittedSubtypesNam[j] = CharDeduplication.intern(utf8At(utf8Offset + 3, u2At(utf8Offset + 1))); + this.permittedSubtypesNames[j] = CharDeduplication.intern(utf8At(utf8Offset + 3, u2At(utf8Offset + 1))); offset += 2; } } @@ -548,7 +547,6 @@ public ClassFileReader(byte[] classFileBytes, char[] fileName, boolean fullyInit this.enclosingTypeName = enclosingTypeNam; this.sourceFileName = sourceFileNam; this.signature = signatur; - this.permittedSubtypesNames= permittedSubtypesNam; if (fullyInitialize) { initialize(); } @@ -802,7 +800,7 @@ public char[][] getInterfaceNames() { } @Override -public char[][] getPermittedSubtypeNames() { +public char[][] getPermittedSubtypesNames() { return this.permittedSubtypesNames; } @@ -1116,13 +1114,13 @@ && hasStructuralTypeAnnotationChanges(getTypeAnnotations(), newClassFile.getType } // permitted sub-types - char[][] newPermittedSubtypeNames = newClassFile.getPermittedSubtypeNames(); - if (this.permittedSubtypesNames != newPermittedSubtypeNames) { - int newPermittedSubtypesLength = newPermittedSubtypeNames == null ? 0 : newPermittedSubtypeNames.length; + char[][] newPermittedSubtypesNames = newClassFile.getPermittedSubtypesNames(); + if (this.permittedSubtypesNames != newPermittedSubtypesNames) { + int newPermittedSubtypesLength = newPermittedSubtypesNames == null ? 0 : newPermittedSubtypesNames.length; if (newPermittedSubtypesLength != this.permittedSubtypesCount) return true; for (int i = 0, max = this.permittedSubtypesCount; i < max; i++) - if (!CharOperation.equals(this.permittedSubtypesNames[i], newPermittedSubtypeNames[i])) + if (!CharOperation.equals(this.permittedSubtypesNames[i], newPermittedSubtypesNames[i])) return true; } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationDecorator.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationDecorator.java index 6917cf966af..0e9e340e0d3 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationDecorator.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationDecorator.java @@ -142,8 +142,8 @@ public char[] getSuperclassName() { } @Override - public char[][] getPermittedSubtypeNames() { - return this.inputType.getPermittedSubtypeNames(); + public char[][] getPermittedSubtypesNames() { + return this.inputType.getPermittedSubtypesNames(); } @Override diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/env/IBinaryType.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/env/IBinaryType.java index ada248c8620..fdfb8b64aa0 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/env/IBinaryType.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/env/IBinaryType.java @@ -100,7 +100,7 @@ public interface IBinaryType extends IGenericType, IBinaryInfo { * A name is a simple name or a qualified, dot separated name. * For example, Hashtable or java.util.Hashtable. */ -default char[][] getPermittedSubtypeNames() { +default char[][] getPermittedSubtypesNames() { return null; } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java index 0ef0fb2b5b3..da082598e36 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java @@ -544,31 +544,16 @@ void cachePartsFrom(IBinaryType binaryType, boolean needFieldsAndMethods) { types.toArray(this.superInterfaces); this.tagBits |= TagBits.HasUnresolvedSuperinterfaces; } - - this.permittedTypes = Binding.NO_PERMITTEDTYPES; - if (!wrapper.atEnd()) { - // attempt to find each permitted type if it exists in the cache (otherwise - resolve it when requested) - java.util.ArrayList types = new java.util.ArrayList(2); - short rank = 0; - do { - types.add(this.environment.getTypeFromTypeSignature(wrapper, typeVars, this, missingTypeNames, toplevelWalker.toSupertype(rank++, wrapper.peekFullType()))); - } while (!wrapper.atEnd()); - this.permittedTypes = new ReferenceBinding[types.size()]; - types.toArray(this.permittedTypes); - this.extendedTagBits |= ExtendedTagBits.HasUnresolvedPermittedSubtypes; - } - } - // fall back, in case we haven't got them from signature - char[][] permittedSubtypeNames = binaryType.getPermittedSubtypeNames(); - if (this.permittedTypes == Binding.NO_PERMITTEDTYPES && permittedSubtypeNames != null) { + char[][] permittedSubtypesNames = binaryType.getPermittedSubtypesNames(); + if (permittedSubtypesNames != null) { this.modifiers |= ExtraCompilerModifiers.AccSealed; - int size = permittedSubtypeNames.length; + int size = permittedSubtypesNames.length; if (size > 0) { this.permittedTypes = new ReferenceBinding[size]; for (short i = 0; i < size; i++) - // attempt to find each superinterface if it exists in the cache (otherwise - resolve it when requested) - this.permittedTypes[i] = this.environment.getTypeFromConstantPoolName(permittedSubtypeNames[i], 0, -1, false, missingTypeNames, toplevelWalker.toSupertype(i, null)); + // attempt to find each permitted type if it exists in the cache (otherwise - resolve it when requested) + this.permittedTypes[i] = this.environment.getTypeFromConstantPoolName(permittedSubtypesNames[i], 0, -1, false, missingTypeNames); } } boolean canUseNullTypeAnnotations = this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled && this.environment.globalOptions.sourceLevel >= ClassFileConstants.JDK1_8; 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 803238e70aa..c5fa32650d8 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 @@ -599,8 +599,7 @@ private void checkAndSetModifiers() { CompilerOptions options = compilerOptions(); boolean is16Plus = compilerOptions().sourceLevel >= ClassFileConstants.JDK16; boolean isSealedSupported = JavaFeature.SEALED_CLASSES.isSupported(options); - boolean flagSealedNonModifiers = isSealedSupported && - (modifiers & (ExtraCompilerModifiers.AccSealed | ExtraCompilerModifiers.AccNonSealed)) != 0; + boolean hierarchySealed = (modifiers & (ExtraCompilerModifiers.AccSealed | ExtraCompilerModifiers.AccNonSealed)) != 0; switch (modifiers & (ExtraCompilerModifiers.AccSealed | ExtraCompilerModifiers.AccNonSealed | ClassFileConstants.AccFinal)) { case ExtraCompilerModifiers.AccSealed, ExtraCompilerModifiers.AccNonSealed, ClassFileConstants.AccFinal, ClassFileConstants.AccDefault : break; @@ -643,7 +642,7 @@ private void checkAndSetModifiers() { } final int UNEXPECTED_MODIFIERS =~(ClassFileConstants.AccEnum | ClassFileConstants.AccStrictfp); if ((modifiers & ExtraCompilerModifiers.AccJustFlag & UNEXPECTED_MODIFIERS) != 0 - || flagSealedNonModifiers) { + || hierarchySealed) { problemReporter().illegalModifierForLocalEnumDeclaration(sourceType); return; } @@ -754,7 +753,7 @@ private void checkAndSetModifiers() { | ClassFileConstants.AccStrictfp | ClassFileConstants.AccAnnotation | ((is16Plus && this.parent instanceof ClassScope) ? ClassFileConstants.AccStatic : 0)); if ((realModifiers & UNEXPECTED_MODIFIERS) != 0 - || flagSealedNonModifiers) + || hierarchySealed) problemReporter().localStaticsIllegalVisibilityModifierForInterfaceLocalType(sourceType); // if ((modifiers & ClassFileConstants.AccStatic) != 0) { // problemReporter().recordIllegalStaticModifierForLocalClassOrInterface(sourceType); @@ -776,24 +775,23 @@ private void checkAndSetModifiers() { modifiers |= ClassFileConstants.AccSynthetic; } modifiers |= ClassFileConstants.AccAbstract; - } else if ((realModifiers & ClassFileConstants.AccEnum) != 0) { + } else if ((realModifiers & ClassFileConstants.AccEnum) != 0) { // detect abnormal cases for enums if (isMemberType) { // includes member types defined inside local types - final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccPrivate | ClassFileConstants.AccProtected | ClassFileConstants.AccStatic | ClassFileConstants.AccStrictfp | ClassFileConstants.AccEnum); - if ((realModifiers & UNEXPECTED_MODIFIERS) != 0 || flagSealedNonModifiers) { + final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccPrivate + | ClassFileConstants.AccProtected | ClassFileConstants.AccStatic + | ClassFileConstants.AccStrictfp | ClassFileConstants.AccEnum); + if ((realModifiers & UNEXPECTED_MODIFIERS) != 0 || hierarchySealed) { problemReporter().illegalModifierForMemberEnum(sourceType); modifiers &= ~ClassFileConstants.AccAbstract; // avoid leaking abstract modifier realModifiers &= ~ClassFileConstants.AccAbstract; // modifiers &= ~(realModifiers & UNEXPECTED_MODIFIERS); // realModifiers = modifiers & ExtraCompilerModifiers.AccJustFlag; } - } else if (sourceType.isLocalType()) { -// if (flagSealedNonModifiers) -// problemReporter().illegalModifierForLocalEnum(sourceType); - // each enum constant is an anonymous local type and its modifiers were already checked as an enum constant field - } else { - final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccStrictfp | ClassFileConstants.AccEnum); - if ((realModifiers & UNEXPECTED_MODIFIERS) != 0 || flagSealedNonModifiers) + } else if (!sourceType.isLocalType()) { // local types already handled earlier. + final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccStrictfp + | ClassFileConstants.AccEnum); + if ((realModifiers & UNEXPECTED_MODIFIERS) != 0 || hierarchySealed) problemReporter().illegalModifierForEnum(sourceType); } if (!sourceType.isAnonymousType()) { @@ -808,14 +806,16 @@ private void checkAndSetModifiers() { TypeDeclaration typeDeclaration = this.referenceContext; FieldDeclaration[] fields = typeDeclaration.fields; int fieldsLength = fields == null ? 0 : fields.length; - if (fieldsLength == 0) break checkAbstractEnum; // has no constants so must implement the method itself + if (fieldsLength == 0) + break checkAbstractEnum; // has no constants so must implement the method itself AbstractMethodDeclaration[] methods = typeDeclaration.methods; int methodsLength = methods == null ? 0 : methods.length; // TODO (kent) cannot tell that the superinterfaces are empty or that their methods are implemented boolean definesAbstractMethod = typeDeclaration.superInterfaces != null; for (int i = 0; i < methodsLength && !definesAbstractMethod; i++) definesAbstractMethod = methods[i].isAbstract(); - if (!definesAbstractMethod) break checkAbstractEnum; // all methods have bodies + if (!definesAbstractMethod) + break checkAbstractEnum; // all methods have bodies boolean needAbstractBit = false; for (int i = 0; i < fieldsLength; i++) { FieldDeclaration fieldDecl = fields[i]; @@ -827,8 +827,10 @@ private void checkAndSetModifiers() { } } } - // tag this enum as abstract since an abstract method must be implemented AND all enum constants define an anonymous body - // as a result, each of its anonymous constants will see it as abstract and must implement each inherited abstract method + // tag this enum as abstract since an abstract method must be implemented AND all enum constants + // define an anonymous body + // as a result, each of its anonymous constants will see it as abstract and must implement each + // inherited abstract method if (needAbstractBit) { modifiers |= ClassFileConstants.AccAbstract; } @@ -893,7 +895,7 @@ private void checkAndSetModifiers() { } else if (sourceType.isLocalType()) { final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccAbstract | ClassFileConstants.AccFinal | ClassFileConstants.AccStrictfp | ((is16Plus && this.parent instanceof ClassScope) ? ClassFileConstants.AccStatic : 0)); - if ((realModifiers & UNEXPECTED_MODIFIERS) != 0 || flagSealedNonModifiers) + if ((realModifiers & UNEXPECTED_MODIFIERS) != 0 || hierarchySealed) problemReporter().illegalModifierForLocalClass(sourceType); } else { final int UNEXPECTED_MODIFIERS = ~(ClassFileConstants.AccPublic | ClassFileConstants.AccAbstract | ClassFileConstants.AccFinal | ClassFileConstants.AccStrictfp); diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ExtraCompilerModifiers.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ExtraCompilerModifiers.java index aa7b0fdd1a4..c28b24760df 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ExtraCompilerModifiers.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ExtraCompilerModifiers.java @@ -57,5 +57,5 @@ public interface ExtraCompilerModifiers { // modifier constant final int AccImplementing = ASTNode.Bit30; // record fact a method implements another one (it is concrete and overrides an abstract one) final int AccImplicitlyDeclared = ASTNode.Bit30; // used for implicitly declared classes final int AccGenericSignature = ASTNode.Bit31; // record fact a type/method/field involves generics in its signature (and need special signature attr) - final int AccOutOfFlowScope = ASTNode.Bit29; + final int AccOutOfFlowScope = ASTNode.Bit29; // used to signal pattern binding variables not being live. } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java index fc1b3a274cf..6c32f715eac 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java @@ -1166,7 +1166,7 @@ protected void checkAndSetModifiers(int flag){ problemReporter().StrictfpNotRequired(this.scanner.startPosition, this.scanner.currentPosition - 1); } - if ((this.modifiers & flag) != 0){ // duplicate modifier + if ((this.modifiers & flag) != 0) { // duplicate modifier this.modifiers |= ExtraCompilerModifiers.AccAlternateModifierProblem; } this.modifiers |= flag; @@ -1176,10 +1176,6 @@ protected void checkAndSetModifiers(int flag){ if (this.currentElement != null) { this.currentElement.addModifier(flag, this.modifiersSourceStart); } - if (flag == ExtraCompilerModifiers.AccSealed || flag == ExtraCompilerModifiers.AccNonSealed) { - problemReporter().validateJavaFeatureSupport(JavaFeature.SEALED_CLASSES, this.scanner.startPosition, - this.scanner.currentPosition - 1); - } } public void checkComment() { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java index b22e5e5439e..dbb8f40c7a5 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java @@ -9796,7 +9796,7 @@ public boolean validateRestrictedKeywords(char[] name, ASTNode node) { close(); } } -//Returns true if the problem is handled and reported (only errors considered and not warnings) +// Returns true if the problem is handled and reported (only errors considered and not warnings) public boolean validateJavaFeatureSupport(JavaFeature feature, int sourceStart, int sourceEnd) { boolean versionInRange = feature.getCompliance() <= this.options.sourceLevel; String version = CompilerOptions.versionFromJdkLevel(feature.getCompliance()); diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ClassFileReaderTest_17.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ClassFileReaderTest_17.java index 780a506cb38..e6e696542c5 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ClassFileReaderTest_17.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ClassFileReaderTest_17.java @@ -52,7 +52,7 @@ public void testBug564227_001() throws Exception { "final class Z extends X{}\n"; org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader classFileReader = getInternalClassFile("", "X", "X", source); - char[][] permittedSubtypesNames = classFileReader.getPermittedSubtypeNames(); + char[][] permittedSubtypesNames = classFileReader.getPermittedSubtypesNames(); assertEquals(2, permittedSubtypesNames.length); @@ -71,7 +71,7 @@ public void testBug565782_001() throws Exception { "}"; org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader classFileReader = getInternalClassFile("", "X", "X", source); - char[][] permittedSubtypesNames = classFileReader.getPermittedSubtypeNames(); + char[][] permittedSubtypesNames = classFileReader.getPermittedSubtypesNames(); assertEquals(1, permittedSubtypesNames.length); @@ -94,7 +94,7 @@ public void testBug565782_002() throws Exception { "}"; org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader classFileReader = getInternalClassFile("", "X.E", "X$E", source); - char[][] permittedSubtypesNames = classFileReader.getPermittedSubtypeNames(); + char[][] permittedSubtypesNames = classFileReader.getPermittedSubtypesNames(); assertEquals(1, permittedSubtypesNames.length); diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java index ce9cfeb44c6..afce48bc719 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java @@ -602,7 +602,7 @@ public String[] getSuperInterfaceNames() throws JavaModelException { @Override public String[] getPermittedSubtypeNames() throws JavaModelException { IBinaryType info = getElementInfo(); - char[][] names= info.getPermittedSubtypeNames(); + char[][] names= info.getPermittedSubtypesNames(); int length; if (names == null || (length = names.length) == 0) { return CharOperation.NO_STRINGS; @@ -775,7 +775,7 @@ public boolean isRecord() throws JavaModelException { @Override public boolean isSealed() throws JavaModelException { IBinaryType info = getElementInfo(); - char[][] names = info.getPermittedSubtypeNames(); + char[][] names = info.getPermittedSubtypesNames(); return (names != null && names.length > 0); } From fdfdec27500b8323815a5232d5741119778363d4 Mon Sep 17 00:00:00 2001 From: Suby S Surendran Date: Wed, 16 Oct 2024 14:19:55 +0530 Subject: [PATCH 18/63] ASTRewrite.rewriteAST fails with AssertionFailedException for records when it contains inner class (#3002) * Fix ASTRewrite issue in record & added test coverage support for java23 * modified the version change * Incorporated the code review changes * Modifications done inside the bodyDeclaration and the issue has fixed. * added more unit tests * Since the issue is related to ASTConversion, added ASTConverter test --- .../core/tests/dom/ASTConverter_16Test.java | 64 +++++++++ .../ASTRewritingRecordDeclarationTest.java | 127 ++++++++++++++++++ .../eclipse/jdt/core/dom/ASTConverter.java | 1 + 3 files changed, 192 insertions(+) diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter_16Test.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter_16Test.java index 8a2b553bdc4..86687f81b8c 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter_16Test.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter_16Test.java @@ -824,4 +824,68 @@ public void test_RecordSemicolon() throws JavaModelException { } + + public void testRecord013() throws JavaModelException { + if (!isJRE16) { + System.err.println("Test " + getName() + " requires a JRE 16"); + return; + } + String code = """ + record Test(String name) { + public static Builder builder() {return null;} + public static final class Builder {} + } + """; + this.workingCopy = getWorkingCopy("/Converter_16/src/X.java", true/*resolve*/); + ASTNode node = buildAST( + code, + this.workingCopy); + assertEquals("Not a compilation unit", ASTNode.COMPILATION_UNIT, node.getNodeType()); + CompilationUnit compilationUnit = (CompilationUnit) node; + assertProblemsSize(compilationUnit, 0); + node = ((AbstractTypeDeclaration)compilationUnit.types().get(0)); + assertEquals("Not a Record Declaration", ASTNode.RECORD_DECLARATION, node.getNodeType()); + RecordDeclaration record = (RecordDeclaration)node; + + List RecordComponents = record.recordComponents(); + assertEquals("Not a Single Variable Declaration Declaration", ASTNode.SINGLE_VARIABLE_DECLARATION, RecordComponents.get(0).getNodeType()); + + List bodyDeclaration = record.bodyDeclarations(); + MethodDeclaration md = (MethodDeclaration) bodyDeclaration.get(0); + TypeDeclaration td = (TypeDeclaration) bodyDeclaration.get(1); + assertEquals("Not a MethodDeclaration", ASTNode.METHOD_DECLARATION, md.getNodeType()); + assertEquals("Not a TypeDeclaration", ASTNode.TYPE_DECLARATION, td.getNodeType()); + } + + public void testClass002() throws CoreException { + if (!isJRE16) { + System.err.println("Test "+getName()+" requires a JRE 16"); + return; + } + String code = """ + class Test { + public static Builder builder() {return null;} + public static final class Builder {} + } + """; + this.workingCopy = getWorkingCopy("/Converter_16/src/X.java", true/*resolve*/); + ASTNode node = buildAST( + code, + this.workingCopy); + assertEquals("Not a compilation unit", ASTNode.COMPILATION_UNIT, node.getNodeType()); + CompilationUnit compilationUnit = (CompilationUnit) node; + assertProblemsSize(compilationUnit, 0); + List types = compilationUnit.types(); + assertEquals("No. of Types is not 1", types.size(), 1); + AbstractTypeDeclaration type = types.get(0); + assertTrue("type not a type", type instanceof TypeDeclaration); + TypeDeclaration typeDecl = (TypeDeclaration)type; + assertTrue("type not a class", !typeDecl.isInterface()); + + List bodyDeclaration = typeDecl.bodyDeclarations(); + MethodDeclaration md = (MethodDeclaration) bodyDeclaration.get(0); + TypeDeclaration td = (TypeDeclaration) bodyDeclaration.get(1); + assertEquals("Not a MethodDeclaration", ASTNode.METHOD_DECLARATION, md.getNodeType()); + assertEquals("Not a TypeDeclaration", ASTNode.TYPE_DECLARATION, td.getNodeType()); + } } diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingRecordDeclarationTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingRecordDeclarationTest.java index 5cea43fa836..91774731dd0 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingRecordDeclarationTest.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ASTRewritingRecordDeclarationTest.java @@ -1402,5 +1402,132 @@ public void testRecord_028() throws Exception { } + public void testRecord_029_a() throws Exception { + if (checkAPILevel()) { + return; + } + IPackageFragment pack1= this.sourceFolder.createPackageFragment("test1", false, null); + + String code = """ + package test1; + record Test(String name) { + public static Builder builder() {} + public static final class Builder {} + } + """; + + ICompilationUnit cu= pack1.createCompilationUnit("Test.java", code, false, null); + + CompilationUnit astRoot= createAST(cu); + ASTRewrite rewrite= ASTRewrite.create(astRoot.getAST()); + + assertTrue("Parse errors", (astRoot.getFlags() & ASTNode.MALFORMED) == 0); + List methods= astRoot.types(); + MethodDeclaration methodDecl= findMethodDeclaration(methods.get(0), "builder"); + { + rewrite.remove(methodDecl, null); + } + + String preview= evaluateRewrite(cu, rewrite); + + String reWriteCode = """ + package test1; + record Test(String name) { + public static final class Builder {} + } + """; + + assertEqualString(preview, reWriteCode); + } + + public void testRecord_029_b() throws Exception { + if (checkAPILevel()) { + return; + } + IPackageFragment pack1= this.sourceFolder.createPackageFragment("test1", false, null); + + String code = """ + package test1; + public class Test { + public static Builder builder() {} + public static final class Builder {} + } + """; + + ICompilationUnit cu= pack1.createCompilationUnit("Test.java", code, false, null); + + CompilationUnit astRoot= createAST(cu); + ASTRewrite rewrite= ASTRewrite.create(astRoot.getAST()); + + assertTrue("Parse errors", (astRoot.getFlags() & ASTNode.MALFORMED) == 0); + List methods= astRoot.types(); + MethodDeclaration methodDecl= findMethodDeclaration(methods.get(0), "builder"); + { + rewrite.remove(methodDecl, null); + } + + String preview= evaluateRewrite(cu, rewrite); + + String reWriteCode = """ + package test1; + public class Test { + public static final class Builder {} + } + """; + + assertEqualString(preview, reWriteCode); + } + + public void testRecord_029_c() throws Exception { + if (checkAPILevel()) { + return; + } + IPackageFragment pack1= this.sourceFolder.createPackageFragment("test1", false, null); + + String code = """ + package test1; + record Test(String name) { + public static Builder builder() {} + public static final class Builder {} + public static Builder builderNew() {} + public static final class builderNew { + builderNew(){ + System.out.println("Test"); + } + } + } + """; + + ICompilationUnit cu= pack1.createCompilationUnit("Test.java", code, false, null); + + CompilationUnit astRoot= createAST(cu); + ASTRewrite rewrite= ASTRewrite.create(astRoot.getAST()); + + assertTrue("Parse errors", (astRoot.getFlags() & ASTNode.MALFORMED) == 0); + List methods= astRoot.types(); + MethodDeclaration methodDecl= findMethodDeclaration(methods.get(0), "builder"); + MethodDeclaration methodDeclNew= findMethodDeclaration(methods.get(0), "builderNew"); + { + rewrite.remove(methodDecl, null); + rewrite.remove(methodDeclNew, null); + } + + String preview= evaluateRewrite(cu, rewrite); + + String reWriteCode = """ + package test1; + record Test(String name) { + public static final class Builder {} + public static final class builderNew { + builderNew(){ + System.out.println("Test"); + } + } + } + """; + + assertEqualString(preview, reWriteCode); + } + } diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java index e6299685ec0..7ab2da38686 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java @@ -315,6 +315,7 @@ protected void buildBodyDeclarations( } else { methodsIndex++; + continue; } } From 126c7b6bbd2b1fb7f3785dee22a7a86911b7c08d Mon Sep 17 00:00:00 2001 From: Suby S Surendran Date: Wed, 16 Oct 2024 15:52:45 +0530 Subject: [PATCH 19/63] JavaDoc modification for the TryStatement.resouces() (#3079) --- .../dom/org/eclipse/jdt/core/dom/TryStatement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/TryStatement.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/TryStatement.java index 7c38bebcc62..3b33862ad50 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/TryStatement.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/TryStatement.java @@ -360,7 +360,7 @@ public void setFinally(Block block) { /** * Returns the live ordered list of resources for this try statement (added in JLS4 API). * - *

A resource is either a {@link VariableDeclarationExpression} or (since JLS9) a {@link Name}.

+ *

A resource is either a {@link VariableDeclarationExpression} or (since JLS9) a {@link Name} or a {@link FieldAccess} or a {@link SuperFieldAccess}.

* * @return the live list of resources (element type: {@link Expression}). * In the deprecated JLS4 and JLS8 APIs, this used to be From 09f7d6801d5aabaa9d50f2c5924ec6c05207b063 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Wed, 16 Oct 2024 16:54:05 +0530 Subject: [PATCH 20/63] * Review and incremental cleanup of sealed types implementation. (#3095) --- .../jdt/internal/compiler/ClassFile.java | 16 ++-- .../compiler/lookup/BinaryTypeBinding.java | 4 +- .../jdt/internal/compiler/lookup/Binding.java | 2 +- .../internal/compiler/lookup/ClassScope.java | 78 ++++++------------- .../compiler/lookup/MissingTypeBinding.java | 2 +- .../compiler/lookup/SourceTypeBinding.java | 2 +- .../internal/compiler/lookup/TypeBinding.java | 2 +- .../compiler/problem/ProblemReporter.java | 2 +- 8 files changed, 40 insertions(+), 68 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ClassFile.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ClassFile.java index 1dc5edb52cf..e254e3af717 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ClassFile.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ClassFile.java @@ -385,8 +385,7 @@ else if (this.referenceBinding.isAnnotationType()) attributesNumber += generateBootstrapMethods(this.bootstrapMethods); } if (this.targetJDK >= ClassFileConstants.JDK17) { - // add record attributes - attributesNumber += generatePermittedTypeAttributes(); + attributesNumber += generatePermittedSubclassesAttribute(); } // Inner class attribute int numberOfInnerClasses = this.innerClassesBindings == null ? 0 : this.innerClassesBindings.size(); @@ -2852,10 +2851,9 @@ private int generateNestAttributes() { nAttrs += generateNestHostAttribute(); return nAttrs; } - private int generatePermittedTypeAttributes() { - SourceTypeBinding type = this.referenceBinding; + private int generatePermittedSubclassesAttribute() { int localContentsOffset = this.contentsOffset; - ReferenceBinding[] permittedTypes = type.permittedTypes(); + ReferenceBinding[] permittedTypes = this.referenceBinding.permittedTypes(); int l = permittedTypes != null ? permittedTypes.length : 0; if (l == 0) return 0; @@ -2864,6 +2862,14 @@ private int generatePermittedTypeAttributes() { if (exSize + localContentsOffset >= this.contents.length) { resizeContents(exSize); } + /* + * PermittedSubclasses_attribute { + u2 attribute_name_index; + u4 attribute_length; + u2 number_of_classes; + u2 classes[number_of_classes]; + } + */ int attributeNameIndex = this.constantPool.literalIndex(AttributeNamesConstants.PermittedSubclasses); this.contents[localContentsOffset++] = (byte) (attributeNameIndex >> 8); diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java index da082598e36..8cd977bb712 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java @@ -438,7 +438,7 @@ void cachePartsFrom(IBinaryType binaryType, boolean needFieldsAndMethods) { // and still want to use binaries passed that point (e.g. type hierarchy resolver, see bug 63748). this.typeVariables = Binding.NO_TYPE_VARIABLES; this.superInterfaces = Binding.NO_SUPERINTERFACES; - this.permittedTypes = Binding.NO_PERMITTEDTYPES; + this.permittedTypes = Binding.NO_PERMITTED_TYPES; // must retrieve member types in case superclass/interfaces need them this.memberTypes = Binding.NO_MEMBER_TYPES; @@ -2632,7 +2632,7 @@ public String toString() { } if (this.permittedTypes != null) { - if (this.permittedTypes != Binding.NO_PERMITTEDTYPES) { + if (this.permittedTypes != Binding.NO_PERMITTED_TYPES) { buffer.append("\n\tpermits : "); //$NON-NLS-1$ for (int i = 0, length = this.permittedTypes.length; i < length; i++) { if (i > 0) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Binding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Binding.java index 34073261c30..c1beb405a48 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Binding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Binding.java @@ -62,7 +62,7 @@ public abstract class Binding { public static final ReferenceBinding[] ANY_EXCEPTION = new ReferenceBinding[] { null }; // special handler for all exceptions public static final FieldBinding[] NO_FIELDS = new FieldBinding[0]; public static final MethodBinding[] NO_METHODS = new MethodBinding[0]; - public static final ReferenceBinding[] NO_PERMITTEDTYPES = new ReferenceBinding[0]; + public static final ReferenceBinding[] NO_PERMITTED_TYPES = new ReferenceBinding[0]; public static final ReferenceBinding[] NO_SUPERINTERFACES = new ReferenceBinding[0]; public static final ReferenceBinding[] NO_MEMBER_TYPES = new ReferenceBinding[0]; public static final TypeVariableBinding[] NO_TYPE_VARIABLES = new TypeVariableBinding[0]; 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 c5fa32650d8..98a58b10a9c 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 @@ -95,7 +95,7 @@ void buildAnonymousTypeBinding(SourceTypeBinding enclosingType, ReferenceBinding } } anonymousType.typeBits |= inheritedBits; - anonymousType.setPermittedTypes(Binding.NO_PERMITTEDTYPES); // JLS 15 JEP 360 Preview - Sec 15.9.5 + anonymousType.setPermittedTypes(Binding.NO_PERMITTED_TYPES); if (supertype.isInterface()) { anonymousType.setSuperClass(getJavaLangObject()); anonymousType.setSuperInterfaces(new ReferenceBinding[] { supertype }); @@ -115,7 +115,8 @@ void buildAnonymousTypeBinding(SourceTypeBinding enclosingType, ReferenceBinding } else { anonymousType.setSuperClass(supertype); anonymousType.setSuperInterfaces(Binding.NO_SUPERINTERFACES); - checkForEnumSealedPreview(supertype, anonymousType); + if (supertype.isEnum() && supertype instanceof SourceTypeBinding superEnum) + implicitlySealEnumHierarchy(superEnum, anonymousType); TypeReference typeReference = this.referenceContext.allocation.type; if (typeReference != null) { // no check for enum constant body this.referenceContext.superclass = typeReference; @@ -151,25 +152,19 @@ void buildAnonymousTypeBinding(SourceTypeBinding enclosingType, ReferenceBinding anonymousType.verifyMethods(environment().methodVerifier()); } - private void checkForEnumSealedPreview(ReferenceBinding supertype, LocalTypeBinding anonymousType) { - if (!JavaFeature.SEALED_CLASSES.isSupported(compilerOptions()) - || !supertype.isEnum() - || !(supertype instanceof SourceTypeBinding)) - return; - - SourceTypeBinding sourceSuperType = (SourceTypeBinding) supertype; - ReferenceBinding[] permTypes = sourceSuperType.permittedTypes(); - int sz = permTypes == null ? 0 : permTypes.length; - if (sz == 0) { - permTypes = new ReferenceBinding[] {anonymousType}; - } else { - System.arraycopy(permTypes, 0, - permTypes = new ReferenceBinding[sz + 1], 0, - sz); - permTypes[sz] = anonymousType; + private void implicitlySealEnumHierarchy(SourceTypeBinding superEnum, LocalTypeBinding anonymousType) { + if (JavaFeature.SEALED_CLASSES.isSupported(compilerOptions())) { + ReferenceBinding[] permittedTypes = superEnum.permittedTypes(); + int sz = permittedTypes == null ? 0 : permittedTypes.length; + if (sz == 0) { + permittedTypes = new ReferenceBinding[] { anonymousType }; + } else { + System.arraycopy(permittedTypes, 0, permittedTypes = new ReferenceBinding[sz + 1], 0, sz); + permittedTypes[sz] = anonymousType; + } + anonymousType.modifiers |= ClassFileConstants.AccFinal; + superEnum.setPermittedTypes(permittedTypes); } - anonymousType.modifiers |= ClassFileConstants.AccFinal; // JLS 15 / sealed preview/Sec 8.9.1 - sourceSuperType.setPermittedTypes(permTypes); } void buildComponents() { @@ -1183,7 +1178,7 @@ private boolean connectSuperclass() { if (sourceType.id == TypeIds.T_JavaLangObject) { // handle the case of redefining java.lang.Object up front sourceType.setSuperClass(null); sourceType.setSuperInterfaces(Binding.NO_SUPERINTERFACES); - sourceType.setPermittedTypes(Binding.NO_PERMITTEDTYPES); + sourceType.setPermittedTypes(Binding.NO_PERMITTED_TYPES); if (!sourceType.isClass()) problemReporter().objectMustBeClass(sourceType); if (this.referenceContext.superclass != null || (this.referenceContext.superInterfaces != null && this.referenceContext.superInterfaces.length > 0)) @@ -1310,7 +1305,7 @@ void connectImplicitPermittedTypes() { } void connectPermittedTypes() { SourceTypeBinding sourceType = this.referenceContext.binding; - sourceType.setPermittedTypes(Binding.NO_PERMITTEDTYPES); + sourceType.setPermittedTypes(Binding.NO_PERMITTED_TYPES); if (sourceType.id == TypeIds.T_JavaLangObject || sourceType.isEnum()) // already handled return; @@ -1323,30 +1318,24 @@ void connectPermittedTypes() { nextPermittedType : for (int i = 0; i < length; i++) { TypeReference permittedTypeRef = this.referenceContext.permittedTypes[i]; ReferenceBinding permittedType = findPermittedtype(permittedTypeRef); - if (permittedType == null) { // detected cycle + if (permittedType == null) { continue nextPermittedType; } - if (!isPermittedTypeInAllowedFormat(sourceType, permittedTypeRef, permittedType)) - continue nextPermittedType; - // check for simple interface collisions - // Check for a duplicate interface once the name is resolved, otherwise we may be confused (i.e. a.b.I and c.d.I) for (int j = 0; j < i; j++) { if (TypeBinding.equalsEquals(permittedTypeBindings[j], permittedType)) { - problemReporter().sealedDuplicateTypeInPermits(sourceType, permittedTypeRef, permittedType); + problemReporter().duplicatePermittedType(sourceType, permittedTypeRef, permittedType); continue nextPermittedType; } } - // only want to reach here when no errors are reported permittedTypeBindings[count++] = permittedType; } - // hold onto all correctly resolved superinterfaces if (count > 0) { if (count != length) System.arraycopy(permittedTypeBindings, 0, permittedTypeBindings = new ReferenceBinding[count], 0, count); sourceType.setPermittedTypes(permittedTypeBindings); } else { - sourceType.setPermittedTypes(Binding.NO_PERMITTEDTYPES); + sourceType.setPermittedTypes(Binding.NO_PERMITTED_TYPES); } } finally { sourceType.tagBits &= ~TagBits.SealingTypeHierarchy; @@ -1359,29 +1348,6 @@ void connectPermittedTypes() { } } - private boolean isPermittedTypeInAllowedFormat(SourceTypeBinding sourceType, TypeReference permittedTypeRef, - ReferenceBinding permittedType) { - if (!(permittedType.isMemberType() && permittedTypeRef instanceof SingleTypeReference)) - return true; - ReferenceBinding enclosingType = permittedType.enclosingType(); - while (enclosingType != null) { - if (TypeBinding.equalsEquals(sourceType, enclosingType)) { - CompilationUnitScope cu = this.compilationUnitScope(); - if (cu.imports != null || cu.imports.length > 0) { - for (ImportBinding ib : cu.imports) { - Binding resolvedImport = cu.resolveSingleImport(ib, Binding.TYPE); - if (resolvedImport instanceof TypeBinding && - TypeBinding.equalsEquals(permittedType, (TypeBinding) resolvedImport)) - return true; - } - } - return false; - } - enclosingType = enclosingType.enclosingType(); - } - return true; - } - private boolean connectRecordSuperclass() { SourceTypeBinding sourceType = this.referenceContext.binding; ReferenceBinding rootRecordType = getJavaLangRecord(); @@ -1675,7 +1641,7 @@ private ReferenceBinding findSupertype(TypeReference typeReference) { } catch (AbortCompilation e) { SourceTypeBinding sourceType = this.referenceContext.binding; if (sourceType.superInterfaces == null) sourceType.setSuperInterfaces(Binding.NO_SUPERINTERFACES); // be more resilient for hierarchies (144976) - if (sourceType.permittedTypes == null) sourceType.setPermittedTypes(Binding.NO_PERMITTEDTYPES); + if (sourceType.permittedTypes == null) sourceType.setPermittedTypes(Binding.NO_PERMITTED_TYPES); e.updateContext(typeReference, referenceCompilationUnit().compilationResult); throw e; } finally { @@ -1703,7 +1669,7 @@ private ReferenceBinding findPermittedtype(TypeReference typeReference) { return permittedType != null ? permittedType.actualType() : permittedType; // while permitted classes/interfaces cannot be parameterized with type arguments, they are not raw either } catch (AbortCompilation e) { SourceTypeBinding sourceType = this.referenceContext.binding; - if (sourceType.permittedTypes == null) sourceType.setPermittedTypes(Binding.NO_PERMITTEDTYPES); + if (sourceType.permittedTypes == null) sourceType.setPermittedTypes(Binding.NO_PERMITTED_TYPES); e.updateContext(typeReference, referenceCompilationUnit().compilationResult); throw e; } finally { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/MissingTypeBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/MissingTypeBinding.java index 7a7ebab30bd..2356f8a0f2f 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/MissingTypeBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/MissingTypeBinding.java @@ -34,7 +34,7 @@ public MissingTypeBinding(PackageBinding packageBinding, char[][] compoundName, this.modifiers = ClassFileConstants.AccPublic; this.superclass = null; // will be fixed up using #setMissingSuperclass(...) this.superInterfaces = Binding.NO_SUPERINTERFACES; - this.permittedTypes = Binding.NO_PERMITTEDTYPES; + this.permittedTypes = Binding.NO_PERMITTED_TYPES; this.typeVariables = Binding.NO_TYPE_VARIABLES; this.memberTypes = Binding.NO_MEMBER_TYPES; this.fields = Binding.NO_FIELDS; diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java index ed0ed1d538c..703ce2d7d26 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java @@ -3240,7 +3240,7 @@ public RecordComponentBinding[] setComponents(RecordComponentBinding[] comps) { return this.methods = methods; } -//Propagate writes to all annotated variants so the clones evolve along. +// Propagate writes to all annotated variants so the clones evolve along. public ReferenceBinding [] setPermittedTypes(ReferenceBinding [] permittedTypes) { if (!isPrototype()) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java index 314cbce802b..808d5b34985 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java @@ -1759,7 +1759,7 @@ public ReferenceBinding superclass() { } public ReferenceBinding[] permittedTypes() { - return Binding.NO_PERMITTEDTYPES; + return Binding.NO_PERMITTED_TYPES; } public ReferenceBinding[] superInterfaces() { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java index dbb8f40c7a5..3bb721eb34f 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java @@ -12336,7 +12336,7 @@ public void sealedMissingSealedModifier(SourceTypeBinding type, ASTNode node) { node.sourceEnd); } -public void sealedDuplicateTypeInPermits(SourceTypeBinding type, TypeReference reference, ReferenceBinding superType) { +public void duplicatePermittedType(SourceTypeBinding type, TypeReference reference, ReferenceBinding superType) { this.handle( IProblem.SealedDuplicateTypeInPermits, new String[] { From 8cd13526a3c5fab89c521cb0637b8df14c429928 Mon Sep 17 00:00:00 2001 From: Gayan Perera Date: Wed, 16 Oct 2024 18:21:19 +0200 Subject: [PATCH 21/63] wait for build after refresh. Fix #2998 --- .../eval/EvaluationContextWrapperTest.java | 1 + .../jdt/core/tests/eval/EvaluationTest.java | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/eval/EvaluationContextWrapperTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/eval/EvaluationContextWrapperTest.java index 439a6d09ee4..45b22282938 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/eval/EvaluationContextWrapperTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/eval/EvaluationContextWrapperTest.java @@ -182,6 +182,7 @@ private Map compileAndDeploy15(String source, String className, private void refreshProject() throws Exception { this.project.getProject().refreshLocal(IResource.DEPTH_INFINITE, null); + waitForAutoBuild(); // wait for builds to complete. } private void removeTempClass(String className) { diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/eval/EvaluationTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/eval/EvaluationTest.java index e94b70531ad..f7e826b0ad5 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/eval/EvaluationTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/eval/EvaluationTest.java @@ -20,6 +20,13 @@ import java.util.Map; import junit.framework.Test; import junit.framework.TestSuite; + +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.tests.junit.extension.StopableTestCase; @@ -40,6 +47,7 @@ import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.problem.DefaultProblem; import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; +import org.eclipse.jdt.internal.core.JavaModelManager; import org.eclipse.jdt.internal.eval.EvaluationContext; import org.eclipse.jdt.internal.eval.EvaluationResult; import org.eclipse.jdt.internal.eval.GlobalVariable; @@ -569,4 +577,40 @@ public void stop() { } } } + + public void waitForAutoBuild() { + if (isWorkspaceRuleAlreadyInUse(getWorkspaceRoot())) { + // Don't wait holding workspace lock on FAMILY_AUTO_BUILD, because + // we might deadlock with AutoBuildOffJob + System.out.println("\n\nAborted waitForAutoBuild() because running with the workspace rule\n\n"); + return; + } + boolean wasInterrupted = false; + do { + try { + Job.getJobManager().wakeUp(ResourcesPlugin.FAMILY_AUTO_BUILD); + Job.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_BUILD, null); + JavaModelManager.getIndexManager().waitForIndex(isIndexDisabledForTest(), null); + wasInterrupted = false; + } catch (OperationCanceledException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + wasInterrupted = true; + } + } while (wasInterrupted); + } + + private static boolean isWorkspaceRuleAlreadyInUse(ISchedulingRule rule) { + ISchedulingRule currentJobRule = Job.getJobManager().currentRule(); + boolean workspaceRuleActive = currentJobRule != null && rule.contains(currentJobRule); + return workspaceRuleActive; + } + + private IWorkspaceRoot getWorkspaceRoot() { + return getWorkspace().getRoot(); + } + + private IWorkspace getWorkspace() { + return ResourcesPlugin.getWorkspace(); + } } From 265d3798b8d2790cd42a412ffb06d90ee6621325 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Thu, 17 Oct 2024 05:48:39 +0530 Subject: [PATCH 22/63] [Switch][Sealed types] Bad static analysis with the old switch syntax + an exhautive pattern matching on a sealed type throws a MatchException(#3098) * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3096 --- .../compiler/ast/SwitchStatement.java | 5 +- .../compiler/ast/TypeDeclaration.java | 2 +- .../regression/SwitchPatternTest.java | 156 ++++++++++++++++++ 3 files changed, 160 insertions(+), 3 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java index 5f3e745fb56..a2af4daa373 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java @@ -886,7 +886,6 @@ public void generateCode(BlockScope currentScope, CodeStream codeStream) { if (this.preSwitchInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preSwitchInitStateIndex); } - defaultLabel.place(); /* a default case is not needed for an exhaustive switch expression * we need to handle the default case to throw an error in order to make the stack map consistent. * All cases will return a value on the stack except the missing default case. @@ -895,9 +894,10 @@ public void generateCode(BlockScope currentScope, CodeStream codeStream) { CompilerOptions compilerOptions = this.scope != null ? this.scope.compilerOptions() : null; if (compilerOptions.complianceLevel >= ClassFileConstants.JDK19) { // since 19 we have MatchException for this - if (codeStream.lastAbruptCompletion != codeStream.position) { + if (this.statements.length > 0 && this.statements[this.statements.length - 1].canCompleteNormally()) { codeStream.goto_(this.breakLabel); // hop, skip and jump over match exception throw. } + defaultLabel.place(); codeStream.newJavaLangMatchException(); codeStream.dup(); codeStream.aconst_null(); @@ -906,6 +906,7 @@ public void generateCode(BlockScope currentScope, CodeStream codeStream) { codeStream.athrow(); } else { // old style using IncompatibleClassChangeError: + defaultLabel.place(); codeStream.newJavaLangIncompatibleClassChangeError(); codeStream.dup(); codeStream.invokeJavaLangIncompatibleClassChangeErrorDefaultConstructor(); diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java index 2ac63f1eb3f..ef0fed6ca73 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java @@ -109,7 +109,7 @@ public class TypeDeclaration extends Statement implements ProblemSeverities, Ref public int nRecordComponents; public static Set disallowedComponentNames; - // 15 Sealed Type preview support + // 17 Sealed Type support public TypeReference[] permittedTypes; // TEST ONLY: disable one fix here to challenge another related fix (in TypeSystem): diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java index e88d92cc513..8db71faa97b 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java @@ -9072,4 +9072,160 @@ public static void main(String [] args) { "I.K.E\n" + "I.K.F"); } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3096 + // [Switch][Sealed types] Bad static analysis with the old switch syntax + an exhautive pattern matching on a sealed type throws a MatchException + public void testIssue3096() { + runConformTest( + new String[] { + "X.java", + """ + public sealed interface X permits X.R { + record R(String s) implements X { + } + + public static void add(X x) { + switch (x) { + case R r: + if (r.s == null) { + throw new NullPointerException(); + } + } + } + + public static void main(String[] args) { + add(new R("bar")); + } + } + """ + }, + ""); + } + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3096 + // [Switch][Sealed types] Bad static analysis with the old switch syntax + an exhautive pattern matching on a sealed type throws a MatchException + public void testIssue3096_2() { + runConformTest( + new String[] { + "X.java", + """ + public sealed interface X permits X.R { + record R(String s) implements X { + } + + public static void add(X x) { + switch (x) { + case R r: + if (r.s == null) { + throw new NullPointerException(); + } + break; + } + } + + public static void main(String[] args) { + add(new R("bar")); + } + } + """ + }, + ""); + } + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3096 + // [Switch][Sealed types] Bad static analysis with the old switch syntax + an exhautive pattern matching on a sealed type throws a MatchException + public void testIssue3096_3() { + runConformTest( + new String[] { + "X.java", + """ + public sealed interface X permits X.R { + record R(String s) implements X { + } + + public static void add(X x) { + switch (x) { + case R r: + if (r.s == null) { + throw new NullPointerException(); + } + System.out.println("R"); + } + } + + public static void main(String[] args) { + add(new R("bar")); + } + } + """ + }, + "R"); + } + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3096 + // [Switch][Sealed types] Bad static analysis with the old switch syntax + an exhautive pattern matching on a sealed type throws a MatchException + public void testIssue3096_4() { + runConformTest( + new String[] { + "X.java", + """ + public sealed interface X permits X.R { + record R(String s) implements X { + } + + public static void add(X x) { + switch (x) { + case R r: + if (r.s == null) { + throw new NullPointerException(); + } + System.out.println("R"); + break; + } + } + + public static void main(String[] args) { + add(new R("bar")); + } + } + """ + }, + "R"); + } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3096 + // [Switch][Sealed types] Bad static analysis with the old switch syntax + an exhautive pattern matching on a sealed type throws a MatchException + public void testIssue3096_full() { + runConformTest( + new String[] { + "EclipseBugFallThroughSwitch.java", + """ + public class EclipseBugFallThroughSwitch { + sealed interface I permits A, B {} + record A(String s) implements I {} + record B(String s) implements I {} + + public void add(I i) { + switch (i) { + case A a: + break; + case B b: + if (b.s == null) { + throw new NullPointerException(); + } + //break; // this fix the issue + } + } + + public static void main(String[] args) { + var container = new EclipseBugFallThroughSwitch(); + container.add(new B("bar")); + + // Exception in thread "main" java.lang.MatchException + // at EclipseBugFallThroughSwitch.add(EclipseBugFallThroughSwitch.java:9) + // at EclipseBugFallThroughSwitch.main(EclipseBugFallThroughSwitch.java:25) + } + } + """ + }, + ""); + } + } From 5d6d82ef790841149ede2cbdb0925f16da85698b Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Thu, 17 Oct 2024 08:55:12 +0530 Subject: [PATCH 23/63] Additional tests for incompatible enum evolution and switch exhaustiveness(#3099) --- .../core/tests/builder/IncrementalTests.java | 57 ++++++++++++++ .../regression/BatchCompilerTest_17.java | 78 +++++++++++++++++++ 2 files changed, 135 insertions(+) diff --git a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/IncrementalTests.java b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/IncrementalTests.java index 25629f50723..c9dfa9230cc 100644 --- a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/IncrementalTests.java +++ b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/IncrementalTests.java @@ -1664,4 +1664,61 @@ public final class Child extends Parent { env.removeProject(projectPath); } + public void testExhaustiveness() throws JavaModelException { + IPath projectPath = env.addProject("Project", "18"); + env.addExternalJars(projectPath, Util.getJavaClassLibs()); + + // remove old package fragment root so that names don't collide + env.removePackageFragmentRoot(projectPath, ""); + + IPath root = env.addPackageFragmentRoot(projectPath, "src"); + env.setOutputFolder(projectPath, "bin"); + + IPath pathToX = env.addClass(root, "", "X", + """ + public class X { + + public static void main(String[] args) { + E e = E.getE(); + + String s = switch (e) { + case A -> "A"; + case B -> "B"; + case C -> "C"; + }; + System.out.println(s); + } + } + """); + + env.addClass(root, "", "E", + """ + public enum E { + A, B, C; + static E getE() { + return C; + } + } + """); + + fullBuild(projectPath); + expectingNoProblems(); + executeClass(projectPath, "X", "C", ""); + + env.addClass(root, "", "E", + """ + public enum E { + A, B, C, D; + static E getE() { + return D; + } + } + """); + + + incrementalBuild(projectPath); + expectingSpecificProblemFor(pathToX, new Problem("E", "A Switch expression should cover all possible values", pathToX, 100, 101, CategorizedProblem.CAT_SYNTAX, IMarker.SEVERITY_ERROR)); //$NON-NLS-1$ //$NON-NLS-2$ + env.removeProject(projectPath); + } + } diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_17.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_17.java index 996b96a73cf..559b3362dc2 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_17.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_17.java @@ -24,7 +24,9 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import junit.framework.Test; +import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.tests.compiler.regression.BatchCompilerTest.SubstringMatcher; +import org.eclipse.jdt.core.tests.util.Util; public class BatchCompilerTest_17 extends AbstractBatchCompilerTest { @@ -45,6 +47,10 @@ public BatchCompilerTest_17(String name) { super(name); } + static { + // TESTS_NAMES = new String [] { "testGHI1774_Expression" }; + } + /** * Test tries to compile same sources in parallel to different output directories. * There should be no interdependencies between compiler threads, but the test failed @@ -145,4 +151,76 @@ boolean match(String err) { return "StdErr: " + error + " StdOut: " + output; } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/1774 + // Check behavior of expression switch in JDK18- + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3096#issuecomment-2417954288 + public void testGHI1774_Expression() throws Exception { + + String path = LIB_DIR; + String libPath = null; + if (path.endsWith(File.separator)) { + libPath = path + "lib.jar"; + } else { + libPath = path + File.separator + "lib.jar"; + } + Util.createJar(new String[] { + "p/Color.java", + "package p;\n" + + "public enum Color {\n" + + " R, Y;\n" + + " public static Color getColor() {\n" + + " return R;\n" + + " }\n" + + "}", + }, + libPath, + JavaCore.VERSION_18); + this.runConformTest( + new String[] { + "src/p/X.java", + "package p;\n" + + "import p.Color;\n" + + "public class X {\n" + + " public static void main(String argv[]) {\n" + + " Color c = Color.getColor();\n" + + " try {\n" + + " int a = switch (c) {\n" + + " case R -> 1;\n" + + " case Y -> 2;\n" + + " };\n" + + " } catch (IncompatibleClassChangeError e) {\n" + + " System.out.print(\"OK\");\n" + + " } catch (Exception e) {\n" + + " System.out.print(\"NOT OK: \" + e);\n" + + " }\n" + + " System.out.print(\"END\");\n" + + " }\n" + + "}", + }, + "\"" + OUTPUT_DIR + File.separator + "src/p/X.java\"" + + " -cp \"" + LIB_DIR + File.separator + "lib.jar\"" + + " -sourcepath \"" + OUTPUT_DIR + File.separator + "src\"" + + " -source 18 -warn:none" + + " -d \"" + OUTPUT_DIR + File.separator + "bin\" ", + "", + "", + true); + this.verifier.execute("p.X", new String[] {OUTPUT_DIR + File.separator + "bin", libPath}, new String[0], new String[] {"--enable-preview"}); + assertEquals("Incorrect output", "END", this.verifier.getExecutionOutput()); + Util.createJar(new String[] { + "p/Color.java", + "package p;\n" + + "public enum Color {\n" + + " R, Y, B;\n" + + " public static Color getColor() {\n" + + " return B;\n" + + " }\n" + + "}", + }, + libPath, + JavaCore.VERSION_18); + this.verifier.execute("p.X", new String[] {OUTPUT_DIR + File.separator + "bin", libPath}, new String[0], new String[] {"--enable-preview"}); + assertEquals("Incorrect output", "OKEND", this.verifier.getExecutionOutput()); + } } From 0b347898743d342c8110822f42891a34deb05d2d Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Thu, 17 Oct 2024 11:33:45 +0530 Subject: [PATCH 24/63] [Sealed types] Duplicate diagnostics for illegal modifier combination (#3101) * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3100 --- .../eclipse/jdt/core/compiler/IProblem.java | 4 +++- .../compiler/lookup/SourceTypeBinding.java | 22 +++++++---------- .../compiler/problem/ProblemReporter.java | 9 ------- .../compiler/regression/SealedTypesTests.java | 24 +++++++++++++++---- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java index 3fc0a2bbf8d..6b534474b6f 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java @@ -2538,7 +2538,9 @@ public interface IProblem { int SealedPermittedTypeOutsideOfPackage = TypeRelated + 1859; /** @since 3.28 */ int SealedSealedTypeMissingPermits = TypeRelated + 1860; - /** @since 3.28 */ + /** @since 3.28 + * @deprecated problem no longer generated + */ int SealedInterfaceIsSealedAndNonSealed = TypeRelated + 1861; /** @since 3.28 */ int SealedDisAllowedNonSealedModifierInInterface = TypeRelated + 1862; diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java index 703ce2d7d26..d62f5165843 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java @@ -1119,17 +1119,9 @@ private Map.Entry getFirstSealedSuperTypeOrInte } return null; } -// TODO: Optimize the multiple loops - defer until the feature becomes standard. + private void checkPermitsInType() { -// if (/* this.isRecordDeclaration || */this.isEnum()) -// return; // handled separately TypeDeclaration typeDecl = this.scope.referenceContext; - if (this.isInterface()) { - if (isSealed() && isNonSealed()) { - this.scope.problemReporter().sealedInterfaceIsSealedAndNonSealed(this, typeDecl); - return; - } - } boolean hasPermittedTypes = this.permittedTypes != null && this.permittedTypes.length > 0; if (hasPermittedTypes) { if (!this.isSealed()) @@ -1170,11 +1162,13 @@ private void checkPermitsInType() { return; } } else if (this.isNonSealed()) { - if (!foundSealedSuperTypeOrInterface) { - if (this.isClass() && !this.isRecord()) // record to give only illegal modifier error. - this.scope.problemReporter().sealedDisAllowedNonSealedModifierInClass(this, typeDecl); - else if (this.isInterface()) - this.scope.problemReporter().sealedDisAllowedNonSealedModifierInInterface(this, typeDecl); + if (!this.isSealed()) { // lest we bark again. + if (!foundSealedSuperTypeOrInterface) { + if (this.isClass() && !this.isRecord()) // record to give only illegal modifier error. + this.scope.problemReporter().sealedDisAllowedNonSealedModifierInClass(this, typeDecl); + else if (this.isInterface()) + this.scope.problemReporter().sealedDisAllowedNonSealedModifierInInterface(this, typeDecl); + } } } if (foundSealedSuperTypeOrInterface) { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java index 3bb721eb34f..4f2b75af875 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java @@ -12397,15 +12397,6 @@ public void sealedSealedTypeMissingPermits(SourceTypeBinding type, ASTNode node) node.sourceEnd); } -public void sealedInterfaceIsSealedAndNonSealed(SourceTypeBinding type, ASTNode node) { - String name = new String(type.sourceName()); - this.handle(IProblem.SealedInterfaceIsSealedAndNonSealed, - new String[] { name }, - new String[] { name }, - node.sourceStart, - node.sourceEnd); -} - public void sealedDisAllowedNonSealedModifierInInterface(SourceTypeBinding type, TypeDeclaration typeDecl) { String name = new String(type.sourceName()); this.handle( diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java index 7089df6ed0f..470cc748475 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java @@ -1071,11 +1071,6 @@ public void testBug563806_032() { " public sealed non-sealed interface X {\n" + " ^\n" + "Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares X as its direct superclass or superinterface\n" + - "----------\n" + - "3. ERROR in p1\\X.java (at line 2)\n" + - " public sealed non-sealed interface X {\n" + - " ^\n" + - "An interface X is declared both sealed and non-sealed\n" + "----------\n"); } public void testBug563806_033() { @@ -6400,4 +6395,23 @@ final non-sealed class Y extends X {} "The type Y may have only one modifier out of sealed, non-sealed, and final\n" + "----------\n"); } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3100 + // [Sealed types] Duplicate diagnostics for illegal modifier combination + public void testIssue3100() { + runNegativeTest( + new String[] { + "X.java", + """ + public sealed non-sealed interface X {} + final class Y implements X {} + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 1)\n" + + " public sealed non-sealed interface X {}\n" + + " ^\n" + + "The type X may have only one modifier out of sealed, non-sealed, and final\n" + + "----------\n"); + } } From 3e287dcfadabc4e75d296155970e37259f462880 Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Thu, 17 Oct 2024 18:37:41 +0200 Subject: [PATCH 25/63] + Ensure ClasspathJrt initialized to fix eclipse-jdt/eclipse.jdt.ui#1722 (#3108) + Fix test's getJCL15PlusLibraryIfNeeded() --- .../jdt/internal/compiler/batch/FileSystem.java | 8 +++++++- .../core/tests/model/AbstractJavaModelTests.java | 15 ++++++--------- .../jdt/core/tests/model/ClasspathTests.java | 2 +- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/FileSystem.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/FileSystem.java index 3092e4af272..18b53b4fa9e 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/FileSystem.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/FileSystem.java @@ -292,7 +292,13 @@ public static Classpath getClasspath(String classpathName, String encoding, Acce return getClasspath(classpathName, encoding, false, accessRuleSet, null, options, release); } public static Classpath getJrtClasspath(String jdkHome, String encoding, AccessRuleSet accessRuleSet, Map options) { - return new ClasspathJrt(new File(convertPathSeparators(jdkHome)), true, accessRuleSet, null); + ClasspathJrt classpathJrt = new ClasspathJrt(new File(convertPathSeparators(jdkHome)), true, accessRuleSet, null); + try { + classpathJrt.initialize(); + } catch (IOException e) { + // Broken entry, but let clients have it anyway. + } + return classpathJrt; } public static Classpath getOlderSystemRelease(String jdkHome, String release, AccessRuleSet accessRuleSet) { return isJRE12Plus ? diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java index e3a808251f3..074f6b867f5 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java @@ -653,7 +653,7 @@ protected void addClassFolder(IJavaProject javaProject, String folderRelativePat } protected void addExternalLibrary(IJavaProject javaProject, String jarPath, String[] pathAndContents, String[] nonJavaResources, String compliance) throws Exception { - String[] claspath = getJCL15PlusLibraryIfNeeded(compliance); + String[] claspath = getJCLLibrary(compliance); org.eclipse.jdt.core.tests.util.Util.createJar(pathAndContents, nonJavaResources, jarPath, claspath, compliance); addLibraryEntry(javaProject, new Path(jarPath), true/*exported*/); } @@ -742,7 +742,7 @@ protected IProject createLibrary( IProject project = javaProject.getProject(); String projectLocation = project.getLocation().toOSString(); String jarPath = projectLocation + File.separator + jarName; - String[] claspath = getJCL15PlusLibraryIfNeeded(compliance); + String[] claspath = getJCLLibrary(compliance); org.eclipse.jdt.core.tests.util.Util.createJar(pathAndContents, nonJavaResources, jarPath, claspath, compliance, options); if (pathAndContents != null && pathAndContents.length != 0) { String sourceZipPath = projectLocation + File.separator + sourceZipName; @@ -2532,13 +2532,10 @@ public void ensureChildExists(IParent container, IJavaElement child) throws Java } } - protected String[] getJCL15PlusLibraryIfNeeded(String compliance) throws JavaModelException, IOException { - if (compliance.charAt(compliance.length()-1) >= '8' && (AbstractCompilerTest.getPossibleComplianceLevels() & AbstractCompilerTest.F_1_8) != 0) { - // ensure that the JCL 18 lib is setup (i.e. that the jclMin18.jar is copied) - setUpJCLClasspathVariables("1.8"); - return new String[] {getExternalJCLPathString("1.8")}; - } - return null; + protected String[] getJCLLibrary(String compliance) throws JavaModelException, IOException { + // ensure that the requested JCL lib is setup (i.e. that the jclMinXY.jar is copied) + setUpJCLClasspathVariables(compliance); + return new String[] {getExternalJCLPathString(compliance)}; } /** * Returns the specified compilation unit in the given project, root, and diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ClasspathTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ClasspathTests.java index 9ed76bf0659..e7730d5cf26 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ClasspathTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ClasspathTests.java @@ -7455,7 +7455,7 @@ public void testBug576735a() throws Exception { projectLocation + File.separator + "libMissing.jar"); // create another jar depending on libMissing.jar: - String[] classpath = getJCL15PlusLibraryIfNeeded("1.8"); + String[] classpath = getJCLLibrary("1.8"); classpath = Arrays.copyOf(classpath, classpath.length+1); classpath[classpath.length-1] = projectLocation + File.separator + "libMissing.jar"; Util.createJar( From bd799adad27b09b8f9b5df60119a40bb0e50cf01 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Fri, 18 Oct 2024 08:15:20 +0530 Subject: [PATCH 26/63] Regression test for https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3109(#3110) --- ...rnTest21.java => SwitchPatternTest22.java} | 46 +++++++++++++++++-- .../tests/compiler/regression/TestAll.java | 2 +- .../core/tests/RunVariousPatternsTests.java | 2 +- .../core/tests/RunVariousSealedTypeTests.java | 2 +- .../jdt/core/tests/RunVariousSwitchTests.java | 4 +- 5 files changed, 48 insertions(+), 8 deletions(-) rename org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/{SwitchPatternTest21.java => SwitchPatternTest22.java} (95%) diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest21.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest22.java similarity index 95% rename from org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest21.java rename to org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest22.java index 62172224a45..3dade7af915 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest21.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest22.java @@ -16,17 +16,17 @@ import junit.framework.Test; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; -public class SwitchPatternTest21 extends AbstractBatchCompilerTest { +public class SwitchPatternTest22 extends AbstractBatchCompilerTest { static { // TESTS_NAMES = new String [] { "testNaming" }; } public static Test suite() { - return buildMinimalComplianceTestSuite(SwitchPatternTest21.class, F_22); + return buildMinimalComplianceTestSuite(SwitchPatternTest22.class, F_22); } - public SwitchPatternTest21(String name) { + public SwitchPatternTest22(String name) { super(name); } @@ -956,4 +956,44 @@ public static void main(String[] args) { }, "A or B"); } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3109 + // [Patterns] Mixed multiple pattern and when is not fully supported in switch expression + public void testIssue3109() { + runConformTest( + new String[] { + "Maybe.java", + """ + import java.util.function.Function; + import java.util.function.Predicate; + + public sealed interface Maybe { + + static Maybe of(T value) { + return new Some<>(value); + } + + static Maybe empty() { + return new None<>(); + } + + default Maybe filter(Predicate predicate){ + return switch (this) { + case Some(T value) when predicate.test(value) -> this; // unsupported line with 4 errors + case Some(_), None _ -> empty(); + }; + } + + default Maybe filter2(Predicate predicate){ + return switch (this) { + case Some(_), None _ -> empty(); //also unsupported with 1 error + }; + } + record Some(T value) implements Maybe {} + record None() implements Maybe{} + } + """ + }, + ""); + } } diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java index ac6b49822ab..9aa275bb52c 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java @@ -249,7 +249,7 @@ public static Test suite() { // since_22.add(SuperAfterStatementsTest.class); since_22.add(UnnamedPatternsAndVariablesTest.class); since_22.add(UseOfUnderscoreJava22Test.class); - since_22.add(SwitchPatternTest21.class); + since_22.add(SwitchPatternTest22.class); ArrayList since_23 = new ArrayList(); since_23.add(SuperAfterStatementsTest.class); diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousPatternsTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousPatternsTests.java index d486b0d4665..ae9c4c6ff29 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousPatternsTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousPatternsTests.java @@ -55,7 +55,7 @@ public static Class[] getAllTestClasses() { ASTRewritingInstanceOfPatternExpressionTest.class, ASTRewritingSwitchPatternTest.class, ResolveTests12To15.class, - SwitchPatternTest21.class, + SwitchPatternTest22.class, JavaSearchBugs19Tests.class, CompletionTestsForRecordPattern.class, NullAnnotationTests21.class, diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSealedTypeTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSealedTypeTests.java index e8a6f24d4ad..62e4b67ed02 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSealedTypeTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSealedTypeTests.java @@ -53,7 +53,7 @@ public static Class[] getAllTestClasses() { SealedTypesTests.class, SealedTypesSpecReviewTest.class, SwitchPatternTest.class, - SwitchPatternTest21.class, + SwitchPatternTest22.class, UnnamedPatternsAndVariablesTest.class, ASTRewritingTypeDeclTest.class, ASTConverter_15Test.class, diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSwitchTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSwitchTests.java index 13ba3b937c9..d4f2160e541 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSwitchTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSwitchTests.java @@ -25,7 +25,7 @@ import org.eclipse.jdt.core.tests.compiler.regression.RecordPatternTest; import org.eclipse.jdt.core.tests.compiler.regression.SwitchExpressionsYieldTest; import org.eclipse.jdt.core.tests.compiler.regression.SwitchPatternTest; -import org.eclipse.jdt.core.tests.compiler.regression.SwitchPatternTest21; +import org.eclipse.jdt.core.tests.compiler.regression.SwitchPatternTest22; import org.eclipse.jdt.core.tests.compiler.regression.SwitchTest; import org.eclipse.jdt.core.tests.compiler.regression.UnnamedPatternsAndVariablesTest; import org.eclipse.jdt.core.tests.dom.ConverterTestSetup; @@ -44,7 +44,7 @@ public static Class[] getAllTestClasses() { return new Class[] { SwitchPatternTest.class, - SwitchPatternTest21.class, + SwitchPatternTest22.class, SwitchTest.class, SwitchExpressionsYieldTest.class, From a3d0518c660edfefab10d89fdb02ace57b30aeab Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Fri, 18 Oct 2024 14:08:42 +0530 Subject: [PATCH 27/63] Rewrite how implicitly permitted subtypes are discovered and wired. (#3106) * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3111 --- .../internal/compiler/lookup/ClassScope.java | 74 +++---------------- .../compiler/lookup/CompilationUnitScope.java | 2 - .../compiler/lookup/SourceTypeBinding.java | 64 ++++++++-------- .../compiler/regression/SealedTypesTests.java | 10 +-- 4 files changed, 46 insertions(+), 104 deletions(-) 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 98a58b10a9c..39d145c3ae3 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 @@ -32,13 +32,9 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.lookup; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; import java.util.Map; -import java.util.Set; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.*; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; @@ -115,8 +111,6 @@ void buildAnonymousTypeBinding(SourceTypeBinding enclosingType, ReferenceBinding } else { anonymousType.setSuperClass(supertype); anonymousType.setSuperInterfaces(Binding.NO_SUPERINTERFACES); - if (supertype.isEnum() && supertype instanceof SourceTypeBinding superEnum) - implicitlySealEnumHierarchy(superEnum, anonymousType); TypeReference typeReference = this.referenceContext.allocation.type; if (typeReference != null) { // no check for enum constant body this.referenceContext.superclass = typeReference; @@ -152,21 +146,6 @@ void buildAnonymousTypeBinding(SourceTypeBinding enclosingType, ReferenceBinding anonymousType.verifyMethods(environment().methodVerifier()); } - private void implicitlySealEnumHierarchy(SourceTypeBinding superEnum, LocalTypeBinding anonymousType) { - if (JavaFeature.SEALED_CLASSES.isSupported(compilerOptions())) { - ReferenceBinding[] permittedTypes = superEnum.permittedTypes(); - int sz = permittedTypes == null ? 0 : permittedTypes.length; - if (sz == 0) { - permittedTypes = new ReferenceBinding[] { anonymousType }; - } else { - System.arraycopy(permittedTypes, 0, permittedTypes = new ReferenceBinding[sz + 1], 0, sz); - permittedTypes[sz] = anonymousType; - } - anonymousType.modifiers |= ClassFileConstants.AccFinal; - superEnum.setPermittedTypes(permittedTypes); - } - } - void buildComponents() { SourceTypeBinding sourceType = this.referenceContext.binding; if (!sourceType.isRecord()) return; @@ -368,7 +347,6 @@ void buildLocalTypeBinding(SourceTypeBinding enclosingType) { LocalTypeBinding localType = buildLocalType(enclosingType, enclosingType.fPackage); connectTypeHierarchy(); - connectImplicitPermittedTypes(); if (compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) { checkParameterizedTypeBounds(); checkParameterizedSuperTypeCollisions(); @@ -1267,49 +1245,12 @@ private boolean connectEnumSuperclass() { } return !foundCycle; } - private void connectImplicitPermittedTypes(SourceTypeBinding sourceType) { - List types = new ArrayList<>(); - for (TypeDeclaration typeDecl : this.referenceCompilationUnit().types) { - types.addAll(sourceType.collectAllTypeBindings(typeDecl, this.compilationUnitScope())); - } - Set permSubTypes = new LinkedHashSet<>(); - for (ReferenceBinding type : types) { - if (!TypeBinding.equalsEquals(type, sourceType) && type.findSuperTypeOriginatingFrom(sourceType) != null) { - permSubTypes.add(type); - } - } - if (sourceType.isSealed() && sourceType.isLocalType()) { - // bug xxxx flag Error and return; - } - if (permSubTypes.size() == 0) { - if (!sourceType.isLocalType() && !sourceType.isRecord() && !sourceType.isEnum()) // error flagged already - problemReporter().sealedSealedTypeMissingPermits(sourceType, this.referenceContext); - return; - } - sourceType.setPermittedTypes(permSubTypes.toArray(new ReferenceBinding[0])); - } - void connectImplicitPermittedTypes() { - TypeDeclaration typeDecl = this.referenceContext; - SourceTypeBinding sourceType = typeDecl.binding; - if (sourceType.id == TypeIds.T_JavaLangObject) // already handled - return; - if (sourceType.isSealed() && (typeDecl.permittedTypes == null || - typeDecl.permittedTypes.length == 0 || typeDecl.permittedTypes[0].isImplicit())) { - connectImplicitPermittedTypes(sourceType); - } - ReferenceBinding[] memberTypes = sourceType.memberTypes; - if (memberTypes != null && memberTypes != Binding.NO_MEMBER_TYPES) { - for (ReferenceBinding memberType : memberTypes) - ((SourceTypeBinding) memberType).scope.connectImplicitPermittedTypes(); - } - } + void connectPermittedTypes() { SourceTypeBinding sourceType = this.referenceContext.binding; - sourceType.setPermittedTypes(Binding.NO_PERMITTED_TYPES); - if (sourceType.id == TypeIds.T_JavaLangObject || sourceType.isEnum()) // already handled - return; - if (this.referenceContext.permittedTypes != null) { + if (this.referenceContext.permittedTypes != null && (this.referenceContext.permittedTypes.length == 0 || !this.referenceContext.permittedTypes[0].isImplicit())) { + sourceType.setPermittedTypes(Binding.NO_PERMITTED_TYPES); try { sourceType.tagBits |= TagBits.SealingTypeHierarchy; int length = this.referenceContext.permittedTypes.length; @@ -1340,6 +1281,15 @@ void connectPermittedTypes() { } finally { sourceType.tagBits &= ~TagBits.SealingTypeHierarchy; } + } else { + ReferenceBinding[] permittedTypes = sourceType.permittedTypes(); + if (permittedTypes == null || permittedTypes.length == 0) { + sourceType.setPermittedTypes(Binding.NO_PERMITTED_TYPES); + if (sourceType.isSealed()) { + if (!sourceType.isLocalType() && !sourceType.isRecord() && !sourceType.isEnum()) // error flagged alread + problemReporter().sealedSealedTypeMissingPermits(sourceType, this.referenceContext); + } + } } ReferenceBinding[] memberTypes = sourceType.memberTypes; if (memberTypes != null && memberTypes != Binding.NO_MEMBER_TYPES) { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java index efb8efd3bc1..1f646063e7f 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java @@ -426,8 +426,6 @@ void sealTypeHierarchy() { for (SourceTypeBinding sourceType : this.topLevelTypes) { sourceType.scope.connectPermittedTypes(); } - for (SourceTypeBinding topLevelType : this.topLevelTypes) - topLevelType.scope.connectImplicitPermittedTypes(); } void integrateAnnotationsInHierarchy() { // Only now that all hierarchy information is built we're ready for ... diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java index d62f5165843..9c6f58857ce 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java @@ -67,7 +67,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.eclipse.jdt.core.compiler.CharOperation; -import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy; import org.eclipse.jdt.internal.compiler.ast.*; import org.eclipse.jdt.internal.compiler.ast.TypeReference.AnnotationPosition; @@ -1277,40 +1276,6 @@ private void reportSealedSuperTypeDoesNotPermitProblem(TypeReference superTypeRe } } -public List collectAllTypeBindings(TypeDeclaration typeDecl, CompilationUnitScope unitScope) { - class TypeBindingsCollector extends ASTVisitor { - List types = new ArrayList<>(); - @Override - public boolean visit( - TypeDeclaration localTypeDeclaration, - BlockScope scope1) { - checkAndAddBinding(localTypeDeclaration.binding); - return true; - } - @Override - public boolean visit( - TypeDeclaration memberTypeDeclaration, - ClassScope scope1) { - checkAndAddBinding(memberTypeDeclaration.binding); - return true; - } - @Override - public boolean visit( - TypeDeclaration typeDeclaration, - CompilationUnitScope scope1) { - checkAndAddBinding(typeDeclaration.binding); - return true; // do nothing by default, keep traversing - } - private void checkAndAddBinding(SourceTypeBinding stb) { - if (stb != null) - this.types.add(stb); - } - } - TypeBindingsCollector typeCollector = new TypeBindingsCollector(); - typeDecl.traverse(typeCollector, unitScope); - return typeCollector.types; -} - private boolean checkPermitsAndAdd(ReferenceBinding superType) { if (superType == null || superType.equals(this.scope.getJavaLangObject()) @@ -3263,9 +3228,31 @@ public ReferenceBinding setSuperClass(ReferenceBinding superClass) { annotatedType.superclass = superClass; } } + if (superClass != null && superClass.actualType() instanceof SourceTypeBinding sourceSuperType && sourceSuperType.isSealed() + && (sourceSuperType.scope.referenceContext.permittedTypes == null || (sourceSuperType.scope.referenceContext.permittedTypes.length > 0 && sourceSuperType.scope.referenceContext.permittedTypes[0].isImplicit()))) { + sourceSuperType.setImplicitPermittedType(this); + if (this.isAnonymousType() && superClass.isEnum()) + this.modifiers |= ClassFileConstants.AccFinal; + } return this.superclass = superClass; } +private void setImplicitPermittedType(SourceTypeBinding permittedType) { + ReferenceBinding[] typesPermitted = this.permittedTypes(); + int sz = typesPermitted == null ? 0 : typesPermitted.length; + if (this.scope.referenceCompilationUnit() == permittedType.scope.referenceCompilationUnit()) { + if (sz == 0) { + typesPermitted = new ReferenceBinding[] { permittedType }; + } else { + System.arraycopy(typesPermitted, 0, typesPermitted = new ReferenceBinding[sz + 1], 0, sz); + typesPermitted[sz] = permittedType; + } + this.setPermittedTypes(typesPermitted); + } else if (sz == 0) { + this.setPermittedTypes(Binding.NO_PERMITTED_TYPES); + } +} + // Propagate writes to all annotated variants so the clones evolve along. public ReferenceBinding [] setSuperInterfaces(ReferenceBinding [] superInterfaces) { @@ -3279,6 +3266,13 @@ public ReferenceBinding setSuperClass(ReferenceBinding superClass) { annotatedType.superInterfaces = superInterfaces; } } + for (int i = 0, length = superInterfaces == null ? 0 : superInterfaces.length; i < length; i++) { + ReferenceBinding superInterface = superInterfaces[i]; + if (superInterface.actualType() instanceof SourceTypeBinding sourceSuperType && sourceSuperType.isSealed() + && (sourceSuperType.scope.referenceContext.permittedTypes == null || (sourceSuperType.scope.referenceContext.permittedTypes.length > 0 && sourceSuperType.scope.referenceContext.permittedTypes[0].isImplicit()))) { + sourceSuperType.setImplicitPermittedType(this); + } + } return this.superInterfaces = superInterfaces; } diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java index 470cc748475..79c0e51e9a6 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java @@ -1579,8 +1579,8 @@ public void testBug564498_2() throws IOException, ClassFormatException { ""); String expectedOutput = "PermittedSubclasses:\n" + - " #22 p1/A$Z,\n" + - " #24 p1/A$SubY\n" + + " #22 p1/A$SubY,\n" + + " #24 p1/A$Z\n" + "}"; verifyClassFile(expectedOutput, "p1/A$Y.class", ClassFileBytesDisassembler.SYSTEM); expectedOutput = @@ -1607,9 +1607,9 @@ public void testBug564498_3() throws IOException, ClassFormatException { ""); String expectedOutput = "PermittedSubclasses:\n" + - " #24 p1/A$Y$SubInnerY,\n" + - " #26 p1/A$Z,\n" + - " #28 p1/A$SubY\n"; + " #24 p1/A$SubY,\n" + + " #26 p1/A$Y$SubInnerY,\n" + + " #28 p1/A$Z\n"; verifyClassFile(expectedOutput, "p1/A$Y.class", ClassFileBytesDisassembler.SYSTEM); } public void testBug564498_4() throws IOException, ClassFormatException { From 9b451ea7658d790678e63c1e7f9af97b182cc9e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=8A?= =?UTF-8?q?=D1=80=20=D0=9A=D1=83=D1=80=D1=82=D0=B0=D0=BA=D0=BE=D0=B2?= Date: Fri, 18 Oct 2024 13:45:58 +0300 Subject: [PATCH 28/63] Stop building jdt.annotations v1 (#3114) Ensure that o.e.jdt.annotations 1.x is not picked by accident when there is unversioned require (like https://github.com/eclipse-jdt/eclipse.jdt/pull/118 ) as it will no longer be in the reactor. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ed9b192c1f8..69a65c4daaf 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ org.eclipse.jdt.annotation - org.eclipse.jdt.annotation_v1 + org.eclipse.jdt.core.compiler.batch org.eclipse.jdt.core org.eclipse.jdt.core.formatterapp From 2678aae878aa2a49ee1d0a82302f6c8659a11993 Mon Sep 17 00:00:00 2001 From: Noopur Gupta Date: Thu, 17 Oct 2024 11:33:39 +0530 Subject: [PATCH 29/63] Add API to configure severity of compiler option to report unused lambda parameter Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3102 --- .../model/org/eclipse/jdt/core/JavaCore.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java index 0a343bbc214..2288d60b9a9 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java @@ -454,6 +454,19 @@ public final class JavaCore extends Plugin { * @category CompilerOptionID */ public static final String COMPILER_PB_UNUSED_LOCAL = PLUGIN_ID + ".compiler.problem.unusedLocal"; //$NON-NLS-1$ + /** + * Compiler option ID: Reporting Unused Lambda Parameter. + *

When enabled, the compiler will issue an error or a warning for unused lambda + * parameter (that is, lambda parameter never read from).

+ *
+ *
Option id:
"org.eclipse.jdt.core.compiler.problem.unusedLambdaParameter"
+ *
Possible values:
{ "error", "warning", "info", "ignore" }
+ *
Default:
"warning"
+ *
+ * @category CompilerOptionID + * @since 3.40 + */ + public static final String COMPILER_PB_UNUSED_LAMBDA_PARAMETER = PLUGIN_ID + ".compiler.problem.unusedLambdaParameter"; //$NON-NLS-1$ /** * Compiler option ID: Reporting Unused Parameter. *

When enabled, the compiler will issue an error or a warning for unused method From 3307fef3804ed189965ab9cb7838d85e69456937 Mon Sep 17 00:00:00 2001 From: Noopur Gupta Date: Fri, 18 Oct 2024 16:13:03 +0530 Subject: [PATCH 30/63] Update JavaCore.java Use plural. --- org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java index 2288d60b9a9..13ccd487d6b 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java @@ -457,7 +457,7 @@ public final class JavaCore extends Plugin { /** * Compiler option ID: Reporting Unused Lambda Parameter. *

When enabled, the compiler will issue an error or a warning for unused lambda - * parameter (that is, lambda parameter never read from).

+ * parameters (that is, lambda parameters never read from).

*
*
Option id:
"org.eclipse.jdt.core.compiler.problem.unusedLambdaParameter"
*
Possible values:
{ "error", "warning", "info", "ignore" }
From 9c11818e53959318b2914d52978dd7729bd7df88 Mon Sep 17 00:00:00 2001 From: Manoj N Palat Date: Fri, 18 Oct 2024 23:05:22 +0530 Subject: [PATCH 31/63] May fix issue #2716 (#3117) --- .../org/eclipse/jdt/internal/core/JavaModelInfo.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelInfo.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelInfo.java index fdf3f661ddf..e99b64ab7d0 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelInfo.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelInfo.java @@ -14,7 +14,10 @@ package org.eclipse.jdt.internal.core; import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; /** * Implementation of IJavaModel. A Java Model is specific to a @@ -28,7 +31,14 @@ public class JavaModelInfo extends OpenableElementInfo { * Compute the non-java resources contained in this java project. */ private Object[] computeNonJavaResources() { - IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); + IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); + try { + workspaceRoot.refreshLocal(IResource.DEPTH_INFINITE, null); + } catch (CoreException e) { + e.printStackTrace(); + return null; + } + IProject[] projects = workspaceRoot.getProjects(); int length = projects.length; Object[] resources = null; int index = 0; From dbc7c7a2b2cd7a68930fd4b63083c7c2351fdf28 Mon Sep 17 00:00:00 2001 From: Mickael Istria Date: Fri, 18 Oct 2024 21:15:44 +0200 Subject: [PATCH 32/63] Alternative strategy to popular index from DOM instead of ECJ Parser API (#2701) Introduce an alternative strategy to popular index from source file by building and visiting their DOM using ASTParser. This allows to make code a bit higher-level than ECJ, and allow to leverage recent changes to use alternative strategy to get DOM too. A system property SourceIndexer.DOM_BASED_INDEXER acts as a flag which when "true" will enable this DOM-strategy in place of direct ECJ parsing. --- .../core/search/JavaSearchDocument.java | 2 +- .../search/indexing/DOMToIndexVisitor.java | 345 ++++++++++++++++++ .../core/search/indexing/SourceIndexer.java | 68 ++++ 3 files changed, 414 insertions(+), 1 deletion(-) create mode 100644 org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/DOMToIndexVisitor.java diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/JavaSearchDocument.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/JavaSearchDocument.java index 682db632773..78d98fa94cc 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/JavaSearchDocument.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/JavaSearchDocument.java @@ -87,7 +87,7 @@ public String getEncoding() { } return null; } - private IFile getFile() { + public IFile getFile() { if (this.file == null) this.file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(getPath())); return this.file; diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/DOMToIndexVisitor.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/DOMToIndexVisitor.java new file mode 100644 index 00000000000..2df2152b4f4 --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/DOMToIndexVisitor.java @@ -0,0 +1,345 @@ +/******************************************************************************* + * Copyright (c) 2023 Red Hat, Inc. and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.jdt.internal.core.search.indexing; + +import java.nio.file.Path; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import org.eclipse.jdt.core.Signature; +import org.eclipse.jdt.core.dom.*; + +class DOMToIndexVisitor extends ASTVisitor { + + private SourceIndexer sourceIndexer; + + private char[] packageName; + private List enclosingTypes = new LinkedList<>(); + + public DOMToIndexVisitor(SourceIndexer sourceIndexer) { + super(true); + this.sourceIndexer = sourceIndexer; + } + + private AbstractTypeDeclaration currentType() { + return this.enclosingTypes.get(this.enclosingTypes.size() - 1); + } + + @Override + public boolean visit(PackageDeclaration packageDeclaration) { + this.packageName = packageDeclaration.getName().toString().toCharArray(); + return false; + } + + @Override + public boolean visit(TypeDeclaration type) { + char[][] enclosing = type.isLocalTypeDeclaration() ? IIndexConstants.ONE_ZERO_CHAR : + this.enclosingTypes.stream().map(AbstractTypeDeclaration::getName).map(SimpleName::getIdentifier).map(String::toCharArray).toArray(char[][]::new); + char[][] parameterTypeSignatures = ((List)type.typeParameters()).stream() + .map(TypeParameter::getName) + .map(Name::toString) + .map(name -> Signature.createTypeSignature(name, false)) + .map(String::toCharArray) + .toArray(char[][]::new); + if (type.isInterface()) { + this.sourceIndexer.addInterfaceDeclaration(type.getModifiers(), this.packageName, simpleName(type.getName()), enclosing, ((List)type.superInterfaceTypes()).stream().map(this::name).toArray(char[][]::new), parameterTypeSignatures, isSecondary(type)); + } else { + this.sourceIndexer.addClassDeclaration(type.getModifiers(), this.packageName, simpleName(type.getName()), enclosing, type.getSuperclassType() == null ? null : name(type.getSuperclassType()), + ((List)type.superInterfaceTypes()).stream().map(this::name).toArray(char[][]::new), parameterTypeSignatures, isSecondary(type)); + if (type.bodyDeclarations().stream().noneMatch(member -> member instanceof MethodDeclaration method && method.isConstructor())) { + this.sourceIndexer.addDefaultConstructorDeclaration(type.getName().getIdentifier().toCharArray(), + this.packageName, type.getModifiers(), 0); + } + if (type.getSuperclassType() != null) { + this.sourceIndexer.addConstructorReference(name(type.getSuperclassType()), 0); + } + } + this.enclosingTypes.add(type); + // TODO other types + return true; + } + @Override + public void endVisit(TypeDeclaration type) { + this.enclosingTypes.remove(type); + } + + @Override + public boolean visit(EnumDeclaration type) { + char[][] enclosing = this.enclosingTypes.stream().map(AbstractTypeDeclaration::getName).map(SimpleName::getIdentifier).map(String::toCharArray).toArray(char[][]::new); + this.sourceIndexer.addEnumDeclaration(type.getModifiers(), this.packageName, type.getName().getIdentifier().toCharArray(), enclosing, Enum.class.getName().toCharArray(), ((List)type.superInterfaceTypes()).stream().map(this::name).toArray(char[][]::new), isSecondary(type)); + this.enclosingTypes.add(type); + return true; + } + @Override + public void endVisit(EnumDeclaration type) { + this.enclosingTypes.remove(type); + } + @Override + public boolean visit(EnumConstantDeclaration enumConstant) { + this.sourceIndexer.addFieldDeclaration(currentType().getName().getIdentifier().toCharArray(), enumConstant.getName().getIdentifier().toCharArray()); + this.sourceIndexer.addConstructorReference(currentType().getName().getIdentifier().toCharArray(), enumConstant.arguments().size()); + return true; + } + + @Override + public boolean visit(AnnotationTypeDeclaration type) { + char[][] enclosing = this.enclosingTypes.stream().map(AbstractTypeDeclaration::getName).map(SimpleName::getIdentifier).map(String::toCharArray).toArray(char[][]::new); + this.sourceIndexer.addAnnotationTypeDeclaration(type.getModifiers(), this.packageName, type.getName().getIdentifier().toCharArray(), enclosing, isSecondary(type)); + this.enclosingTypes.add(type); + return true; + } + @Override + public void endVisit(AnnotationTypeDeclaration type) { + this.enclosingTypes.remove(type); + } + + private boolean isSecondary(AbstractTypeDeclaration type) { + return type.getParent() instanceof CompilationUnit && + !Objects.equals(type.getName().getIdentifier() + ".java", Path.of(this.sourceIndexer.document.getPath()).getFileName().toString()); //$NON-NLS-1$ + } + + @Override + public boolean visit(RecordDeclaration recordDecl) { + // copied processing of TypeDeclaration + this.sourceIndexer.addClassDeclaration(recordDecl.getModifiers(), this.packageName, recordDecl.getName().getIdentifier().toCharArray(), null, null, + ((List)recordDecl.superInterfaceTypes()).stream().map(this::name).toArray(char[][]::new), null, false); + return true; + } + + @Override + public boolean visit(MethodDeclaration method) { + char[] methodName = method.getName().getIdentifier().toCharArray(); + char[][] parameterTypes = ((List)method.parameters()).stream() + .filter(SingleVariableDeclaration.class::isInstance) + .map(SingleVariableDeclaration.class::cast) + .map(SingleVariableDeclaration::getType) + .map(this::name) + .toArray(char[][]::new); + char[] returnType = name(method.getReturnType2()); + char[][] exceptionTypes = ((List)method.thrownExceptionTypes()).stream() + .map(this::name) + .toArray(char[][]::new); + char[][] parameterNames = ((List)method.parameters()).stream() + .map(VariableDeclaration::getName) + .map(SimpleName::getIdentifier) + .map(String::toCharArray) + .toArray(char[][]::new); + if (!method.isConstructor()) { + this.sourceIndexer.addMethodDeclaration(methodName, parameterTypes, returnType, exceptionTypes); + this.sourceIndexer.addMethodDeclaration(this.enclosingTypes.get(this.enclosingTypes.size() - 1).getName().getIdentifier().toCharArray(), + null /* TODO: fully qualified name of enclosing type? */, + methodName, + parameterTypes.length, + null, + parameterTypes, + parameterNames, + returnType, + method.getModifiers(), + this.packageName, + 0 /* TODO What to put here? */, + exceptionTypes, + 0 /* TODO ExtraFlags.IsLocalType ? */); + } else { + this.sourceIndexer.addConstructorDeclaration(method.getName().toString().toCharArray(), + method.parameters().size(), + null, parameterTypes, parameterNames, method.getModifiers(), this.packageName, currentType().getModifiers(), exceptionTypes, 0); + } + return true; + } + + @Override + public boolean visit(ImportDeclaration node) { + if (node.isStatic() && !node.isOnDemand()) { + this.sourceIndexer.addMethodReference(simpleName(node.getName()), 0); + } else if (!node.isOnDemand()) { + this.sourceIndexer.addTypeReference(node.getName().toString().toCharArray()); + } + return true; + } + + @Override + public boolean visit(FieldDeclaration field) { + char[] typeName = name(field.getType()); + for (VariableDeclarationFragment fragment: (List)field.fragments()) { + this.sourceIndexer.addFieldDeclaration(typeName, fragment.getName().getIdentifier().toCharArray()); + } + return true; + } + + @Override + public boolean visit(MethodInvocation methodInvocation) { + this.sourceIndexer.addMethodReference(methodInvocation.getName().getIdentifier().toCharArray(), methodInvocation.arguments().size()); + return true; + } + + @Override + public boolean visit(ExpressionMethodReference methodInvocation) { + int argsCount = 0; + if (this.sourceIndexer.document.shouldIndexResolvedDocument()) { + IMethodBinding binding = methodInvocation.resolveMethodBinding(); + if (binding != null) { + argsCount = binding.getParameterTypes().length; + } + } + this.sourceIndexer.addMethodReference(methodInvocation.getName().getIdentifier().toCharArray(), argsCount); + return true; + } + @Override + public boolean visit(TypeMethodReference methodInvocation) { + int argsCount = 0; + if (this.sourceIndexer.document.shouldIndexResolvedDocument()) { + IMethodBinding binding = methodInvocation.resolveMethodBinding(); + if (binding != null) { + argsCount = binding.getParameterTypes().length; + } + } + this.sourceIndexer.addMethodReference(methodInvocation.getName().getIdentifier().toCharArray(), argsCount); + return true; + } + @Override + public boolean visit(SuperMethodInvocation methodInvocation) { + this.sourceIndexer.addMethodReference(methodInvocation.getName().getIdentifier().toCharArray(), methodInvocation.arguments().size()); + return true; + } + @Override + public boolean visit(SuperMethodReference methodInvocation) { + int argsCount = 0; + if (this.sourceIndexer.document.shouldIndexResolvedDocument()) { + IMethodBinding binding = methodInvocation.resolveMethodBinding(); + if (binding != null) { + argsCount = binding.getParameterTypes().length; + } + } + this.sourceIndexer.addMethodReference(methodInvocation.getName().getIdentifier().toCharArray(), argsCount); + return true; + } + @Override + public boolean visit(ClassInstanceCreation methodInvocation) { + this.sourceIndexer.addConstructorReference(name(methodInvocation.getType()), methodInvocation.arguments().size()); + if (methodInvocation.getAnonymousClassDeclaration() != null) { + this.sourceIndexer.addClassDeclaration(0, this.packageName, new char[0], IIndexConstants.ONE_ZERO_CHAR, name(methodInvocation.getType()), null, null, false); + this.sourceIndexer.addTypeReference(name(methodInvocation.getType())); + } + return true; + } + @Override + public boolean visit(CreationReference methodInvocation) { + int argsCount = 0; + if (this.sourceIndexer.document.shouldIndexResolvedDocument()) { + IMethodBinding binding = methodInvocation.resolveMethodBinding(); + if (binding != null) { + argsCount = binding.getParameterTypes().length; + } + } + this.sourceIndexer.addConstructorReference(name(methodInvocation.getType()), argsCount); + return true; + } + + @Override + public boolean visit(SuperConstructorInvocation node) { + char[] superClassName = Object.class.getName().toCharArray(); + if (currentType() instanceof TypeDeclaration decl && decl.getSuperclassType() != null) { + superClassName = name(decl.getSuperclassType()); + } + this.sourceIndexer.addConstructorReference(superClassName, node.arguments().size()); + return true; + } + + private char[] name(Type type) { + if (type == null) { + return null; + } + if (type instanceof PrimitiveType primitive) { + return primitive.toString().toCharArray(); + } + if (type instanceof SimpleType simpleType) { + return simpleName(simpleType.getName()); + } + if (type instanceof ParameterizedType parameterized) { +// String res = new String(name(parameterized.getType())); +// res += '<'; +// res += ((List)parameterized.typeArguments()).stream() +// .map(this::name) +// .map(String::new) +// .collect(Collectors.joining(",")); //$NON-NLS-1$ +// res += '>'; +// return res.toCharArray(); + return name(parameterized.getType()); + } +// if (type instanceof ArrayType arrayType) { +// char[] res = name(arrayType.getElementType()); +// res = Arrays.copyOf(res, res.length + 2 * arrayType.getDimensions()); +// for (int i = 0; i < arrayType.getDimensions(); i++) { +// res[res.length - 1 - 2 * i] = ']'; +// res[res.length - 1 - 2 * i - 1] = '['; +// } +// return res; +// } +// if (type instanceof QualifiedType qualifiedType) { +// return simpleName(qualifiedType.getName()); +// } + return type.toString().toCharArray(); + } + + @Override + public boolean visit(SimpleType type) { + this.sourceIndexer.addTypeReference(name(type)); + return true; + } + @Override + public boolean visit(QualifiedType type) { + this.sourceIndexer.addTypeReference(name(type)); + return true; + } + @Override + public boolean visit(SimpleName name) { + this.sourceIndexer.addNameReference(name.getIdentifier().toCharArray()); + return true; + } + // TODO (cf SourceIndexer and SourceIndexerRequestor) + // * Module: addModuleDeclaration/addModuleReference/addModuleExportedPackages + // * Lambda: addIndexEntry/addClassDeclaration + // * FieldReference + // * Deprecated + // * Javadoc + + @Override + public boolean visit(MethodRef methodRef) { + this.sourceIndexer.addMethodReference(methodRef.getName().getIdentifier().toCharArray(), methodRef.parameters().size()); + this.sourceIndexer.addConstructorReference(methodRef.getName().getIdentifier().toCharArray(), methodRef.parameters().size()); + return true; + } + @Override + public boolean visit(MemberRef memberRef) { + this.sourceIndexer.addFieldReference(memberRef.getName().getIdentifier().toCharArray()); + this.sourceIndexer.addTypeReference(memberRef.getName().getIdentifier().toCharArray()); + return true; + } + + @Override + public boolean visit(LambdaExpression node) { + var binding = node.resolveMethodBinding(); + if (binding != null) { + this.sourceIndexer.addMethodReference(binding.getName().toCharArray(), binding.getParameterTypes().length); + } + return true; + } + + private static char[] simpleName(Name name) { + if (name instanceof SimpleName simple) { + return simple.getIdentifier().toCharArray(); + } + if (name instanceof QualifiedName qualified) { + return simpleName(qualified.getName()); + } + return null; + } +} diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/SourceIndexer.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/SourceIndexer.java index 7d79204e333..0aee998679d 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/SourceIndexer.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/indexing/SourceIndexer.java @@ -15,13 +15,21 @@ import static org.eclipse.jdt.internal.core.JavaModelManager.trace; +import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.ILog; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.compiler.CharOperation; +import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.ASTParser; +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.MethodReference; import org.eclipse.jdt.core.search.SearchDocument; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; @@ -54,6 +62,7 @@ import org.eclipse.jdt.internal.core.JavaProject; import org.eclipse.jdt.internal.core.SourceTypeElementInfo; import org.eclipse.jdt.internal.core.jdom.CompilationUnit; +import org.eclipse.jdt.internal.core.search.JavaSearchDocument; import org.eclipse.jdt.internal.core.search.matching.JavaSearchNameEnvironment; import org.eclipse.jdt.internal.core.search.matching.MethodPattern; import org.eclipse.jdt.internal.core.search.processing.JobManager; @@ -88,6 +97,10 @@ public SourceIndexer(SearchDocument document) { } @Override public void indexDocument() { + if (Boolean.getBoolean(getClass().getSimpleName() + ".DOM_BASED_INDEXER")) { //$NON-NLS-1$ + indexDocumentFromDOM(); + return; + } // Create a new Parser String documentPath = this.document.getPath(); SourceElementParser parser = this.document.getParser(); @@ -213,6 +226,12 @@ private void purgeMethodStatements(TypeDeclaration type) { @Override public void indexResolvedDocument() { + if (Boolean.getBoolean(getClass().getSimpleName() + ".DOM_BASED_INDEXER")) { //$NON-NLS-1$ + // just re-run indexing, but with the resolved document (and its bindings) + indexDocumentFromDOM(); + return; + } + try { if (DEBUG) { trace(new String(this.cud.compilationResult.fileName) + ':'); @@ -271,4 +290,53 @@ public void indexResolvedDocument() { } } } + + /** + * @return whether the operation was successful + */ + boolean indexDocumentFromDOM() { + if (this.document instanceof JavaSearchDocument javaSearchDoc) { + IFile file = javaSearchDoc.getFile(); + try { + if (JavaProject.hasJavaNature(file.getProject())) { + IJavaProject javaProject = JavaCore.create(file.getProject()); + // Do NOT call javaProject.getElement(pathToJavaFile) as it can loop inside index + // when there are multiple package root/source folders, and then cause deadlock + // so we go finer grain by picking the right fragment first (so index call shouldn't happen) + IPackageFragment fragment = javaProject.findPackageFragment(file.getFullPath().removeLastSegments(1)); + if (fragment.getCompilationUnit(file.getName()) instanceof org.eclipse.jdt.internal.core.CompilationUnit modelUnit) { + // TODO check element info: if has AST and flags are set sufficiently, just reuse instead of rebuilding + ASTParser astParser = ASTParser.newParser(AST.getJLSLatest()); // we don't seek exact compilation the more tolerant the better here + astParser.setSource(modelUnit); + astParser.setStatementsRecovery(true); + astParser.setResolveBindings(this.document.shouldIndexResolvedDocument()); + astParser.setProject(javaProject); + org.eclipse.jdt.core.dom.ASTNode dom = astParser.createAST(null); + if (dom != null) { + dom.accept(new DOMToIndexVisitor(this)); + dom.accept( + new ASTVisitor() { + @Override + public boolean preVisit2(org.eclipse.jdt.core.dom.ASTNode node) { + if (SourceIndexer.this.document.shouldIndexResolvedDocument()) { + return false; // interrupt + } + if (node instanceof MethodReference || node instanceof org.eclipse.jdt.core.dom.LambdaExpression) { + SourceIndexer.this.document.requireIndexingResolvedDocument(); + return false; + } + return true; + } + }); + return true; + } + } + } + } catch (Exception ex) { + ILog.get().error("Failed to index document from DOM for " + this.document.getPath(), ex); //$NON-NLS-1$ + } + } + ILog.get().warn("Could not convert DOM to Index for " + this.document.getPath()); //$NON-NLS-1$ + return false; + } } From a3e8ece33fd8a636ccff72aa7d04e2cce5955245 Mon Sep 17 00:00:00 2001 From: Mickael Istria Date: Fri, 18 Oct 2024 22:36:50 +0200 Subject: [PATCH 33/63] A flag to resolve codeSelect using ASTParser (#2700) Introduce a CompilationUnit.DOM_BASED_OPERATIONS flag which instead of using the ECJ-derived parser uses ASTParser to create a DOM and then uses the DOM to implement codeSelect This has the benefit of: * Simplifying the code as an crawling DOM is usually simpler than dealing with lower-level parser * allowing other parsers as backend if ASTParser is configured for it --- .../internal/codeassist/DOMCodeSelector.java | 651 ++++++++++++++++++ .../jdt/internal/core/ASTHolderCUInfo.java | 2 +- .../jdt/internal/core/CompilationUnit.java | 47 +- .../internal/core/DOMToModelPopulator.java | 83 +++ .../eclipse/jdt/internal/core/util/Util.java | 2 +- 5 files changed, 782 insertions(+), 3 deletions(-) create mode 100644 org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/DOMCodeSelector.java create mode 100644 org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DOMToModelPopulator.java diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/DOMCodeSelector.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/DOMCodeSelector.java new file mode 100644 index 00000000000..f9c77b4ee8f --- /dev/null +++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/DOMCodeSelector.java @@ -0,0 +1,651 @@ +/******************************************************************************* + * Copyright (c) 2024 Red Hat, Inc. and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.jdt.internal.codeassist; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IField; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaModelStatusConstants; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.ILocalVariable; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IParent; +import org.eclipse.jdt.core.ISourceRange; +import org.eclipse.jdt.core.ISourceReference; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.WorkingCopyOwner; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; +import org.eclipse.jdt.core.dom.ClassInstanceCreation; +import org.eclipse.jdt.core.dom.Comment; +import org.eclipse.jdt.core.dom.ConstructorInvocation; +import org.eclipse.jdt.core.dom.ExpressionMethodReference; +import org.eclipse.jdt.core.dom.FieldAccess; +import org.eclipse.jdt.core.dom.IBinding; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.IPackageBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; +import org.eclipse.jdt.core.dom.IVariableBinding; +import org.eclipse.jdt.core.dom.Javadoc; +import org.eclipse.jdt.core.dom.LambdaExpression; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.MethodInvocation; +import org.eclipse.jdt.core.dom.MethodReference; +import org.eclipse.jdt.core.dom.Name; +import org.eclipse.jdt.core.dom.NodeFinder; +import org.eclipse.jdt.core.dom.ParameterizedType; +import org.eclipse.jdt.core.dom.QualifiedName; +import org.eclipse.jdt.core.dom.QualifiedType; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.SimpleType; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.jdt.core.dom.SuperConstructorInvocation; +import org.eclipse.jdt.core.dom.SuperMethodInvocation; +import org.eclipse.jdt.core.dom.TagElement; +import org.eclipse.jdt.core.dom.Type; +import org.eclipse.jdt.core.dom.TypeMethodReference; +import org.eclipse.jdt.core.dom.VariableDeclaration; +import org.eclipse.jdt.core.dom.VariableDeclarationFragment; +import org.eclipse.jdt.core.search.IJavaSearchConstants; +import org.eclipse.jdt.core.search.IJavaSearchScope; +import org.eclipse.jdt.core.search.SearchEngine; +import org.eclipse.jdt.core.search.SearchPattern; +import org.eclipse.jdt.core.search.TypeNameMatchRequestor; +import org.eclipse.jdt.internal.core.AnnotatableInfo; +import org.eclipse.jdt.internal.core.CompilationUnit; +import org.eclipse.jdt.internal.core.DOMToModelPopulator; +import org.eclipse.jdt.internal.core.JavaElement; +import org.eclipse.jdt.internal.core.LocalVariable; +import org.eclipse.jdt.internal.core.SourceField; +import org.eclipse.jdt.internal.core.SourceMethod; +import org.eclipse.jdt.internal.core.search.BasicSearchEngine; +import org.eclipse.jdt.internal.core.search.TypeNameMatchRequestorWrapper; +import org.eclipse.jdt.internal.core.util.Util; + +/** + * A util to select relevant IJavaElement from a DOM (as opposed to {@link SelectionEngine} + * which processes it using lower-level ECJ parser) + */ +public class DOMCodeSelector { + + private final CompilationUnit unit; + private final WorkingCopyOwner owner; + + public DOMCodeSelector(CompilationUnit unit, WorkingCopyOwner owner) { + this.unit = unit; + this.owner = owner; + } + + public IJavaElement[] codeSelect(int offset, int length) throws JavaModelException { + if (offset < 0) { + throw new JavaModelException(new IndexOutOfBoundsException(offset), IJavaModelStatusConstants.INDEX_OUT_OF_BOUNDS); + } + if (offset + length > this.unit.getSource().length()) { + throw new JavaModelException(new IndexOutOfBoundsException(offset + length), IJavaModelStatusConstants.INDEX_OUT_OF_BOUNDS); + } + org.eclipse.jdt.core.dom.CompilationUnit currentAST = this.unit.getOrBuildAST(this.owner); + if (currentAST == null) { + return new IJavaElement[0]; + } + String rawText = this.unit.getSource().substring(offset, offset + length); + int initialOffset = offset, initialLength = length; + boolean insideComment = ((List)currentAST.getCommentList()).stream() + .anyMatch(comment -> comment.getStartPosition() <= initialOffset && comment.getStartPosition() + comment.getLength() >= initialOffset + initialLength); + if (!insideComment) { // trim whitespaces and surrounding comments + boolean changed = false; + do { + changed = false; + if (length > 0 && Character.isWhitespace(this.unit.getSource().charAt(offset))) { + offset++; + length--; + changed = true; + } + if (length > 0 && Character.isWhitespace(this.unit.getSource().charAt(offset + length - 1))) { + length--; + changed = true; + } + List comments = currentAST.getCommentList(); + // leading comment + int offset1 = offset, length1 = length; + OptionalInt leadingCommentEnd = comments.stream().filter(comment -> { + int commentEndOffset = comment.getStartPosition() + comment.getLength() -1; + return comment.getStartPosition() <= offset1 && commentEndOffset > offset1 && commentEndOffset < offset1 + length1 - 1; + }).mapToInt(comment -> comment.getStartPosition() + comment.getLength() - 1) + .findAny(); + if (length > 0 && leadingCommentEnd.isPresent()) { + changed = true; + int newStart = leadingCommentEnd.getAsInt(); + int removedLeading = newStart + 1 - offset; + offset = newStart + 1; + length -= removedLeading; + } + // Trailing comment + int offset2 = offset, length2 = length; + OptionalInt trailingCommentStart = comments.stream().filter(comment -> { + return comment.getStartPosition() >= offset2 + && comment.getStartPosition() < offset2 + length2 + && comment.getStartPosition() + comment.getLength() > offset2 + length2; + }).mapToInt(Comment::getStartPosition) + .findAny(); + if (length > 0 && trailingCommentStart.isPresent()) { + changed = true; + int newEnd = trailingCommentStart.getAsInt(); + int removedTrailing = offset + length - 1 - newEnd; + length -= removedTrailing; + } + } while (changed); + } + String trimmedText = rawText.trim(); + NodeFinder finder = new NodeFinder(currentAST, offset, length); + final ASTNode node = finder.getCoveredNode() != null && finder.getCoveredNode().getStartPosition() > offset && finder.getCoveringNode().getStartPosition() + finder.getCoveringNode().getLength() > offset + length ? + finder.getCoveredNode() : + finder.getCoveringNode(); + if (node instanceof TagElement tagElement && TagElement.TAG_INHERITDOC.equals(tagElement.getTagName())) { + ASTNode javadocNode = node; + while (javadocNode != null && !(javadocNode instanceof Javadoc)) { + javadocNode = javadocNode.getParent(); + } + if (javadocNode instanceof Javadoc javadoc) { + ASTNode parent = javadoc.getParent(); + IBinding binding = resolveBinding(parent); + if (binding instanceof IMethodBinding methodBinding) { + var typeBinding = methodBinding.getDeclaringClass(); + if (typeBinding != null) { + List types = new ArrayList<>(Arrays.asList(typeBinding.getInterfaces())); + if (typeBinding.getSuperclass() != null) { + types.add(typeBinding.getSuperclass()); + } + while (!types.isEmpty()) { + ITypeBinding type = types.remove(0); + for (IMethodBinding m : Arrays.stream(type.getDeclaredMethods()).filter(methodBinding::overrides).toList()) { + if (m.getJavaElement() instanceof IMethod methodElement && methodElement.getJavadocRange() != null) { + return new IJavaElement[] { methodElement }; + } else { + types.addAll(Arrays.asList(type.getInterfaces())); + if (type.getSuperclass() != null) { + types.add(type.getSuperclass()); + } + } + } + } + } + IJavaElement element = methodBinding.getJavaElement(); + if (element != null) { + return new IJavaElement[] { element }; + } + } + } + } + org.eclipse.jdt.core.dom.ImportDeclaration importDecl = findImportDeclaration(node); + if (node instanceof ExpressionMethodReference emr && + emr.getExpression().getStartPosition() + emr.getExpression().getLength() <= offset && offset + length <= emr.getName().getStartPosition()) { + if (!(rawText.isEmpty() || rawText.equals(":") || rawText.equals("::"))) { //$NON-NLS-1$ //$NON-NLS-2$ + return new IJavaElement[0]; + } + if (emr.getParent() instanceof MethodInvocation methodInvocation) { + int index = methodInvocation.arguments().indexOf(emr); + return new IJavaElement[] {methodInvocation.resolveMethodBinding().getParameterTypes()[index].getDeclaredMethods()[0].getJavaElement()}; + } + if (emr.getParent() instanceof VariableDeclaration variableDeclaration) { + ITypeBinding requestedType = variableDeclaration.resolveBinding().getType(); + if (requestedType.getDeclaredMethods().length == 1 + && requestedType.getDeclaredMethods()[0].getJavaElement() instanceof IMethod overridenMethod) { + return new IJavaElement[] { overridenMethod }; + } + } + } + if (node instanceof LambdaExpression lambda) { + if (!(rawText.isEmpty() || rawText.equals("-") || rawText.equals(">") || rawText.equals("->"))) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + return new IJavaElement[0]; // as requested by some tests + } + if (lambda.resolveMethodBinding() != null + && lambda.resolveMethodBinding().getMethodDeclaration() != null + && lambda.resolveMethodBinding().getMethodDeclaration().getJavaElement() != null) { + return new IJavaElement[] { lambda.resolveMethodBinding().getMethodDeclaration().getJavaElement() }; + } + } + if (importDecl != null && importDecl.isStatic()) { + IBinding importBinding = importDecl.resolveBinding(); + if (importBinding instanceof IMethodBinding methodBinding) { + ArrayDeque overloadedMethods = Stream.of(methodBinding.getDeclaringClass().getDeclaredMethods()) // + .filter(otherMethodBinding -> methodBinding.getName().equals(otherMethodBinding.getName())) // + .map(IMethodBinding::getJavaElement) // + .filter(IJavaElement::exists) + .collect(Collectors.toCollection(ArrayDeque::new)); + IJavaElement[] reorderedOverloadedMethods = new IJavaElement[overloadedMethods.size()]; + Iterator reverseIterator = overloadedMethods.descendingIterator(); + for (int i = 0; i < reorderedOverloadedMethods.length; i++) { + reorderedOverloadedMethods[i] = reverseIterator.next(); + } + return reorderedOverloadedMethods; + } + return new IJavaElement[] { importBinding.getJavaElement() }; + } else if (findTypeDeclaration(node) == null) { + IBinding binding = resolveBinding(node); + if (binding != null && !binding.isRecovered()) { + if (node instanceof SuperMethodInvocation && // on `super` + binding instanceof IMethodBinding methodBinding && + methodBinding.getDeclaringClass() instanceof ITypeBinding typeBinding && + typeBinding.getJavaElement() instanceof IType type) { + return new IJavaElement[] { type }; + } + if (binding instanceof IPackageBinding packageBinding + && trimmedText.length() > 0 + && !trimmedText.equals(packageBinding.getName()) + && packageBinding.getName().startsWith(trimmedText)) { + // resolved a too wide node for package name, restrict to selected name only + IJavaElement fragment = this.unit.getJavaProject().findPackageFragment(trimmedText); + if (fragment != null) { + return new IJavaElement[] { fragment }; + } + } + // workaround https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2177 + if (binding instanceof IVariableBinding variableBinding && + variableBinding.getDeclaringMethod() instanceof IMethodBinding declaringMethod && + declaringMethod.isCompactConstructor() && + Arrays.stream(declaringMethod.getParameterNames()).anyMatch(variableBinding.getName()::equals) && + declaringMethod.getDeclaringClass() instanceof ITypeBinding recordBinding && + recordBinding.isRecord() && + recordBinding.getJavaElement() instanceof IType recordType && + recordType.getField(variableBinding.getName()) instanceof SourceField field) { + // the parent must be the field and not the method + return new IJavaElement[] { new LocalVariable(field, + variableBinding.getName(), + 0, // must be 0 for subsequent call to LocalVariableLocator.matchLocalVariable() to work + field.getSourceRange().getOffset() + field.getSourceRange().getLength() - 1, + field.getNameRange().getOffset(), + field.getNameRange().getOffset() + field.getNameRange().getLength() - 1, + field.getTypeSignature(), + null, + field.getFlags(), + true) }; + } + if (binding instanceof ITypeBinding typeBinding && + typeBinding.isIntersectionType()) { + return Arrays.stream(typeBinding.getTypeBounds()) + .map(ITypeBinding::getJavaElement) + .filter(Objects::nonNull) + .toArray(IJavaElement[]::new); + } + IJavaElement element = binding.getJavaElement(); + if (element != null && (element instanceof IPackageFragment || element.exists())) { + return new IJavaElement[] { element }; + } + if (binding instanceof ITypeBinding typeBinding) { + if (this.unit.getJavaProject() != null) { + IType type = this.unit.getJavaProject().findType(typeBinding.getQualifiedName()); + if (type != null) { + return new IJavaElement[] { type }; + } + } + // fallback to calling index, inspired/copied from SelectionEngine + IJavaElement[] indexMatch = findTypeInIndex(typeBinding.getPackage() != null ? typeBinding.getPackage().getName() : null, typeBinding.getName()); + if (indexMatch.length > 0) { + return indexMatch; + } + } + if (binding instanceof IVariableBinding variableBinding && variableBinding.getDeclaringMethod() != null && variableBinding.getDeclaringMethod().isCompactConstructor()) { + // workaround for JavaSearchBugs15Tests.testBug558812_012 + if (variableBinding.getDeclaringMethod().getJavaElement() instanceof IMethod method) { + Optional parameter = Arrays.stream(method.getParameters()).filter(param -> Objects.equals(param.getElementName(), variableBinding.getName())).findAny(); + if (parameter.isPresent()) { + return new IJavaElement[] { parameter.get() }; + } + } + } + if (binding instanceof IMethodBinding methodBinding && + methodBinding.isSyntheticRecordMethod() && + methodBinding.getDeclaringClass().getJavaElement() instanceof IType recordType && + recordType.getField(methodBinding.getName()) instanceof IField field) { + return new IJavaElement[] { field }; + } + ASTNode bindingNode = currentAST.findDeclaringNode(binding); + if (bindingNode != null) { + IJavaElement parent = this.unit.getElementAt(bindingNode.getStartPosition()); + if (parent != null && bindingNode instanceof SingleVariableDeclaration variableDecl) { + return new IJavaElement[] { DOMToModelPopulator.toLocalVariable(variableDecl, (JavaElement)parent) }; + } + } + } + } + // fallback: crawl the children of this unit + IJavaElement currentElement = this.unit; + boolean newChildFound; + int finalOffset = offset; + int finalLength = length; + do { + newChildFound = false; + if (currentElement instanceof IParent parentElement) { + Optional candidate = Stream.of(parentElement.getChildren()) + .filter(ISourceReference.class::isInstance) + .map(ISourceReference.class::cast) + .filter(sourceRef -> { + try { + ISourceRange elementRange = sourceRef.getSourceRange(); + return elementRange != null + && elementRange.getOffset() >= 0 + && elementRange.getOffset() <= finalOffset + && elementRange.getOffset() + elementRange.getLength() >= finalOffset + finalLength; + } catch (JavaModelException e) { + return false; + } + }).map(IJavaElement.class::cast) + .findAny(); + if (candidate.isPresent()) { + newChildFound = true; + currentElement = candidate.get(); + } + } + } while (newChildFound); + if (currentElement instanceof JavaElement impl && + impl.getElementInfo() instanceof AnnotatableInfo annotable && + annotable.getNameSourceStart() >= 0 && + annotable.getNameSourceStart() <= offset && + annotable.getNameSourceEnd() + 1 /* end exclusive vs offset inclusive */ >= offset) { + return new IJavaElement[] { currentElement }; + } + if (insideComment) { + String toSearch = trimmedText.isBlank() ? findWord(offset) : trimmedText; + String resolved = ((List)currentAST.imports()).stream() + .map(org.eclipse.jdt.core.dom.ImportDeclaration::getName) + .map(Name::toString) + .filter(importedPackage -> importedPackage.endsWith(toSearch)) + .findAny() + .orElse(toSearch); + if (this.unit.getJavaProject().findType(resolved) instanceof IType type) { + return new IJavaElement[] { type }; + } + } + // failback to lookup search + ASTNode currentNode = node; + while (currentNode != null && !(currentNode instanceof Type)) { + currentNode = currentNode.getParent(); + } + if (currentNode instanceof Type parentType) { + if (this.unit.getJavaProject() != null) { + StringBuilder buffer = new StringBuilder(); + Util.getFullyQualifiedName(parentType, buffer); + IType type = this.unit.getJavaProject().findType(buffer.toString()); + if (type != null) { + return new IJavaElement[] { type }; + } + } + String packageName = parentType instanceof QualifiedType qType ? qType.getQualifier().toString() : + parentType instanceof SimpleType sType ? + sType.getName() instanceof QualifiedName qName ? qName.getQualifier().toString() : + null : + null; + String simpleName = parentType instanceof QualifiedType qType ? qType.getName().toString() : + parentType instanceof SimpleType sType ? + sType.getName() instanceof SimpleName sName ? sName.getIdentifier() : + sType.getName() instanceof QualifiedName qName ? qName.getName().toString() : + null : + null; + IJavaElement[] indexResult = findTypeInIndex(packageName, simpleName); + if (indexResult.length > 0) { + return indexResult; + } + } + // no good idea left + return new IJavaElement[0]; + } + + static IBinding resolveBinding(ASTNode node) { + if (node instanceof MethodDeclaration decl) { + return decl.resolveBinding(); + } + if (node instanceof MethodInvocation invocation) { + return invocation.resolveMethodBinding(); + } + if (node instanceof VariableDeclaration decl) { + return decl.resolveBinding(); + } + if (node instanceof FieldAccess access) { + return access.resolveFieldBinding(); + } + if (node instanceof Type type) { + return type.resolveBinding(); + } + if (node instanceof Name aName) { + ClassInstanceCreation newInstance = findConstructor(aName); + if (newInstance != null) { + var constructorBinding = newInstance.resolveConstructorBinding(); + if (constructorBinding != null) { + var constructorElement = constructorBinding.getJavaElement(); + if (constructorElement != null) { + boolean hasSource = true; + try { + hasSource = ((ISourceReference)constructorElement.getParent()).getSource() != null; + } catch (Exception e) { + hasSource = false; + } + if ((constructorBinding.getParameterTypes().length > 0 /*non-default*/ || + constructorElement instanceof SourceMethod || !hasSource)) { + return constructorBinding; + } + } else if (newInstance.resolveTypeBinding().isAnonymous()) { + // it's not in the anonymous class body, check for constructor decl in parent types + + ITypeBinding superclassBinding = newInstance.getType().resolveBinding(); + + while (superclassBinding != null) { + Optional potentialConstructor = Stream.of(superclassBinding.getDeclaredMethods()) // + .filter(methodBinding -> methodBinding.isConstructor() && matchSignatures(constructorBinding, methodBinding)) + .findFirst(); + if (potentialConstructor.isPresent()) { + IMethodBinding theConstructor = potentialConstructor.get(); + if (theConstructor.isDefaultConstructor()) { + return theConstructor.getDeclaringClass(); + } + return theConstructor; + } + superclassBinding = superclassBinding.getSuperclass(); + } + return null; + } + } + } + if (node.getParent() instanceof ExpressionMethodReference exprMethodReference && exprMethodReference.getName() == node) { + return resolveBinding(exprMethodReference); + } + if (node.getParent() instanceof TypeMethodReference typeMethodReference && typeMethodReference.getName() == node) { + return resolveBinding(typeMethodReference); + } + IBinding res = aName.resolveBinding(); + if (res != null) { + return res; + } + return resolveBinding(aName.getParent()); + } + if (node instanceof org.eclipse.jdt.core.dom.LambdaExpression lambda) { + return lambda.resolveMethodBinding(); + } + if (node instanceof ExpressionMethodReference methodRef) { + IMethodBinding methodBinding = methodRef.resolveMethodBinding(); + try { + if (methodBinding == null) { + return null; + } + IMethod methodModel = ((IMethod)methodBinding.getJavaElement()); + boolean allowExtraParam = true; + if ((methodModel.getFlags() & Flags.AccStatic) != 0) { + allowExtraParam = false; + if (methodRef.getExpression() instanceof ClassInstanceCreation) { + return null; + } + } + + // find the type that the method is bound to + ITypeBinding type = null; + ASTNode cursor = methodRef; + while (type == null && cursor != null) { + if (cursor.getParent() instanceof VariableDeclarationFragment declFragment) { + type = declFragment.resolveBinding().getType(); + } + else if (cursor.getParent() instanceof MethodInvocation methodInvocation) { + IMethodBinding methodInvocationBinding = methodInvocation.resolveMethodBinding(); + int index = methodInvocation.arguments().indexOf(cursor); + type = methodInvocationBinding.getParameterTypes()[index]; + } else { + cursor = cursor.getParent(); + } + } + + IMethodBinding boundMethod = type.getDeclaredMethods()[0]; + + if (boundMethod.getParameterTypes().length != methodBinding.getParameterTypes().length && (!allowExtraParam || boundMethod.getParameterTypes().length != methodBinding.getParameterTypes().length + 1)) { + return null; + } + } catch (JavaModelException e) { + return null; + } + return methodBinding; + } + if (node instanceof MethodReference methodRef) { + return methodRef.resolveMethodBinding(); + } + if (node instanceof org.eclipse.jdt.core.dom.TypeParameter typeParameter) { + return typeParameter.resolveBinding(); + } + if (node instanceof SuperConstructorInvocation superConstructor) { + return superConstructor.resolveConstructorBinding(); + } + if (node instanceof ConstructorInvocation constructor) { + return constructor.resolveConstructorBinding(); + } + if (node instanceof org.eclipse.jdt.core.dom.Annotation annotation) { + return annotation.resolveTypeBinding(); + } + if (node instanceof SuperMethodInvocation superMethod) { + return superMethod.resolveMethodBinding(); + } + return null; + } + + private static ClassInstanceCreation findConstructor(ASTNode node) { + while (node != null && !(node instanceof ClassInstanceCreation)) { + ASTNode parent = node.getParent(); + if ((parent instanceof SimpleType type && type.getName() == node) || + (parent instanceof ClassInstanceCreation constructor && constructor.getType() == node) || + (parent instanceof ParameterizedType parameterized && parameterized.getType() == node)) { + node = parent; + } else { + node = null; + } + } + return (ClassInstanceCreation)node; + } + + private static AbstractTypeDeclaration findTypeDeclaration(ASTNode node) { + ASTNode cursor = node; + while (cursor != null && (cursor instanceof Type || cursor instanceof Name)) { + cursor = cursor.getParent(); + } + if (cursor instanceof AbstractTypeDeclaration typeDecl && typeDecl.getName() == node) { + return typeDecl; + } + return null; + } + + private static org.eclipse.jdt.core.dom.ImportDeclaration findImportDeclaration(ASTNode node) { + while (node != null && !(node instanceof org.eclipse.jdt.core.dom.ImportDeclaration)) { + node = node.getParent(); + } + return (org.eclipse.jdt.core.dom.ImportDeclaration)node; + } + + private static boolean matchSignatures(IMethodBinding invocation, IMethodBinding declaration) { + if (declaration.getTypeParameters().length == 0) { + return invocation.isSubsignature(declaration); + } + if (invocation.getParameterTypes().length != declaration.getParameterTypes().length) { + return false; + } + for (int i = 0; i < invocation.getParameterTypes().length; i++) { + if (declaration.getParameterTypes()[i].isTypeVariable()) { + if (declaration.getParameterTypes()[i].getTypeBounds().length > 0) { + ITypeBinding[] bounds = declaration.getParameterTypes()[i].getTypeBounds(); + for (int j = 0; j < bounds.length; j++) { + if (!invocation.getParameterTypes()[i].isSubTypeCompatible(bounds[j])) { + return false; + } + } + } + } else if (!invocation.getParameterTypes()[i].isSubTypeCompatible(declaration.getParameterTypes()[i])) { + return false; + } + + } + return true; + } + + private IJavaElement[] findTypeInIndex(String packageName, String simpleName) throws JavaModelException { + List indexMatch = new ArrayList<>(); + TypeNameMatchRequestor requestor = new TypeNameMatchRequestor() { + @Override + public void acceptTypeNameMatch(org.eclipse.jdt.core.search.TypeNameMatch match) { + indexMatch.add(match.getType()); + } + }; + IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaProject[] { this.unit.getJavaProject() }); + new SearchEngine(this.owner).searchAllTypeNames( + packageName != null ? packageName.toCharArray() : null, + SearchPattern.R_EXACT_MATCH, + simpleName.toCharArray(), + SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE, + IJavaSearchConstants.TYPE, + scope, + requestor, + IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, + new NullProgressMonitor()); + if (!indexMatch.isEmpty()) { + return indexMatch.toArray(IJavaElement[]::new); + } + scope = BasicSearchEngine.createWorkspaceScope(); + new BasicSearchEngine(this.owner).searchAllTypeNames( + packageName != null ? packageName.toCharArray() : null, + SearchPattern.R_EXACT_MATCH, + simpleName.toCharArray(), + SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE, + IJavaSearchConstants.TYPE, + scope, + new TypeNameMatchRequestorWrapper(requestor, scope), + IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, + new NullProgressMonitor()); + if (!indexMatch.isEmpty()) { + return indexMatch.toArray(IJavaElement[]::new); + } + return new IJavaElement[0]; + } + + private String findWord(int offset) throws JavaModelException { + int start = offset; + String source = this.unit.getSource(); + while (start >= 0 && Character.isJavaIdentifierPart(source.charAt(start))) start--; + int end = offset + 1; + while (end < source.length() && Character.isJavaIdentifierPart(source.charAt(end))) end++; + return source.substring(start, end); + } +} diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ASTHolderCUInfo.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ASTHolderCUInfo.java index faf422c6a8b..33c3aa174cd 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ASTHolderCUInfo.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ASTHolderCUInfo.java @@ -18,7 +18,7 @@ import org.eclipse.jdt.core.dom.CompilationUnit; public class ASTHolderCUInfo extends CompilationUnitElementInfo { - int astLevel; + public int astLevel; boolean resolveBindings; int reconcileFlags; Map problems = null; diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/CompilationUnit.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/CompilationUnit.java index d62b5ea23e6..945fde479f7 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/CompilationUnit.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/CompilationUnit.java @@ -38,6 +38,8 @@ import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.ASTParser; +import org.eclipse.jdt.internal.codeassist.DOMCodeSelector; import org.eclipse.jdt.internal.compiler.IProblemFactory; import org.eclipse.jdt.internal.compiler.SourceElementParser; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; @@ -61,10 +63,12 @@ * @see ICompilationUnit */ public class CompilationUnit extends Openable implements ICompilationUnit, org.eclipse.jdt.internal.compiler.env.ICompilationUnit, SuffixConstants { + public static boolean DOM_BASED_OPERATIONS = Boolean.getBoolean(CompilationUnit.class.getSimpleName() + ".DOM_BASED_OPERATIONS"); //$NON-NLS-1$ private static final IImportDeclaration[] NO_IMPORTS = new IImportDeclaration[0]; protected final String name; public final WorkingCopyOwner owner; + private org.eclipse.jdt.core.dom.CompilationUnit ast; /** * Constructs a handle to a compilation unit with the given name in the @@ -393,8 +397,38 @@ public IJavaElement[] codeSelect(int offset, int length) throws JavaModelExcepti */ @Override public IJavaElement[] codeSelect(int offset, int length, WorkingCopyOwner workingCopyOwner) throws JavaModelException { - return super.codeSelect(this, offset, length, workingCopyOwner); + if (DOM_BASED_OPERATIONS) { + return new DOMCodeSelector(this, workingCopyOwner).codeSelect(offset, length); + } else { + return super.codeSelect(this, offset, length, workingCopyOwner); + } } + +public org.eclipse.jdt.core.dom.CompilationUnit getOrBuildAST(WorkingCopyOwner workingCopyOwner) throws JavaModelException { + if (this.ast != null) { + return this.ast; + } + Map options = getOptions(true); + ASTParser parser = ASTParser.newParser(new AST(options).apiLevel()); // go through AST constructor to convert options to apiLevel + parser.setWorkingCopyOwner(workingCopyOwner); + parser.setSource(this); + // greedily enable everything assuming the AST will be used extensively for edition + parser.setResolveBindings(true); + parser.setStatementsRecovery(true); + parser.setBindingsRecovery(true); + parser.setCompilerOptions(options); + if (parser.createAST(null) instanceof org.eclipse.jdt.core.dom.CompilationUnit newAST) { + this.ast = newAST; + } + return this.ast; +} + +@Override +public void bufferChanged(BufferChangedEvent event) { + this.ast = null; + super.bufferChanged(event); +} + /** * @see IWorkingCopy#commit(boolean, IProgressMonitor) * @deprecated @@ -1462,6 +1496,17 @@ public Map getCustomOptions() { if (this.owner != null) { try { Map customOptions = this.getCompilationUnitElementInfo().getCustomOptions(); + IJavaProject parentProject = getJavaProject(); + Map parentOptions = parentProject == null ? JavaCore.getOptions() : parentProject.getOptions(true); + if (JavaCore.ENABLED.equals(parentOptions.get(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES)) && + AST.newAST(parentOptions).apiLevel() < AST.getJLSLatest()) { + // Disable preview features for older Java releases as it causes the compiler to fail later + if (customOptions != null) { + customOptions.put(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.DISABLED); + } else { + customOptions = Map.of(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.DISABLED); + } + } return customOptions == null ? Collections.emptyMap() : customOptions; } catch (JavaModelException e) { // do nothing diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DOMToModelPopulator.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DOMToModelPopulator.java new file mode 100644 index 00000000000..737cdaf7be3 --- /dev/null +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DOMToModelPopulator.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2023 Red Hat, Inc. and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.jdt.internal.core; + +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.Comment; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.Modifier; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.jdt.internal.core.util.Util; + +/** + * Process an AST to populate a tree of IJavaElement->JavaElementInfo. + * DOM-first approach to what legacy implements through ECJ parser and CompilationUnitStructureRequestor + */ +public class DOMToModelPopulator extends ASTVisitor { + + public static LocalVariable toLocalVariable(SingleVariableDeclaration parameter, JavaElement parent) { + return toLocalVariable(parameter, parent, parameter.getParent() instanceof MethodDeclaration); + } + + private static LocalVariable toLocalVariable(SingleVariableDeclaration parameter, JavaElement parent, boolean isParameter) { + return new LocalVariable(parent, + parameter.getName().getIdentifier(), + getStartConsideringLeadingComments(parameter), + parameter.getStartPosition() + parameter.getLength() - 1, + parameter.getName().getStartPosition(), + parameter.getName().getStartPosition() + parameter.getName().getLength() - 1, + Util.getSignature(parameter.getType()), + null, // should be populated while navigating children + toModelFlags(parameter.getModifiers(), false), + isParameter); + } + + private static int getStartConsideringLeadingComments(ASTNode node) { + int start = node.getStartPosition(); + var unit = domUnit(node); + int index = unit.firstLeadingCommentIndex(node); + if (index >= 0 && index <= unit.getCommentList().size()) { + Comment comment = (Comment)unit.getCommentList().get(index); + start = comment.getStartPosition(); + } + return start; + } + + private static org.eclipse.jdt.core.dom.CompilationUnit domUnit(ASTNode node) { + while (node != null && !(node instanceof org.eclipse.jdt.core.dom.CompilationUnit)) { + node = node.getParent(); + } + return (org.eclipse.jdt.core.dom.CompilationUnit)node; + } + + private static int toModelFlags(int domModifiers, boolean isDeprecated) { + int res = 0; + if (Modifier.isAbstract(domModifiers)) res |= Flags.AccAbstract; + if (Modifier.isDefault(domModifiers)) res |= Flags.AccDefaultMethod; + if (Modifier.isFinal(domModifiers)) res |= Flags.AccFinal; + if (Modifier.isNative(domModifiers)) res |= Flags.AccNative; + if (Modifier.isNonSealed(domModifiers)) res |= Flags.AccNonSealed; + if (Modifier.isPrivate(domModifiers)) res |= Flags.AccPrivate; + if (Modifier.isProtected(domModifiers)) res |= Flags.AccProtected; + if (Modifier.isPublic(domModifiers)) res |= Flags.AccPublic; + if (Modifier.isSealed(domModifiers)) res |= Flags.AccSealed; + if (Modifier.isStatic(domModifiers)) res |= Flags.AccStatic; + if (Modifier.isStrictfp(domModifiers)) res |= Flags.AccStrictfp; + if (Modifier.isSynchronized(domModifiers)) res |= Flags.AccSynchronized; + if (Modifier.isTransient(domModifiers)) res |= Flags.AccTransient; + if (Modifier.isVolatile(domModifiers)) res |= Flags.AccVolatile; + if (isDeprecated) res |= Flags.AccDeprecated; + return res; + } + +} diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/Util.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/Util.java index 6e0a27ccee4..d64ef2a63bd 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/Util.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/Util.java @@ -1288,7 +1288,7 @@ public static String getDeclaringTypeSignature(String key) { /* * Appends to the given buffer the fully qualified name (as it appears in the source) of the given type */ - private static void getFullyQualifiedName(Type type, StringBuilder buffer) { + public static void getFullyQualifiedName(Type type, StringBuilder buffer) { switch (type.getNodeType()) { case ASTNode.ARRAY_TYPE: ArrayType arrayType = (ArrayType) type; From a569f00998024f2da779722541c6c0d549aeb780 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Sat, 19 Oct 2024 21:24:37 +0530 Subject: [PATCH 34/63] Fix test failures in I build. (#3120) * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3118 --- .../org/eclipse/jdt/core/tests/builder/IncrementalTests.java | 4 ++++ .../core/tests/compiler/regression/BatchCompilerTest_17.java | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/IncrementalTests.java b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/IncrementalTests.java index c9dfa9230cc..b6b56c65508 100644 --- a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/IncrementalTests.java +++ b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/IncrementalTests.java @@ -1665,6 +1665,10 @@ public final class Child extends Parent { } public void testExhaustiveness() throws JavaModelException { + String javaVersion = System.getProperty("java.version"); + if (javaVersion != null && JavaCore.compareJavaVersions(javaVersion, "18") < 0) + return; + IPath projectPath = env.addProject("Project", "18"); env.addExternalJars(projectPath, Util.getJavaClassLibs()); diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_17.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_17.java index 559b3362dc2..2e53a56b59c 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_17.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_17.java @@ -156,7 +156,9 @@ boolean match(String err) { // Check behavior of expression switch in JDK18- // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3096#issuecomment-2417954288 public void testGHI1774_Expression() throws Exception { - + String javaVersion = System.getProperty("java.version"); + if (javaVersion != null && JavaCore.compareJavaVersions(javaVersion, "18") < 0) + return; String path = LIB_DIR; String libPath = null; if (path.endsWith(File.separator)) { From 5da56db8a2ea1b1980e7a8154a3bdf889ccd6c03 Mon Sep 17 00:00:00 2001 From: Andrey Loskutov Date: Mon, 21 Oct 2024 08:56:21 +0200 Subject: [PATCH 35/63] Revert "May fix issue #2716 (#3117)" This reverts commit 9c11818e53959318b2914d52978dd7729bd7df88 as it introduced regression and did not fix the original problem. Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3125 --- .../org/eclipse/jdt/internal/core/JavaModelInfo.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelInfo.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelInfo.java index e99b64ab7d0..fdf3f661ddf 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelInfo.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelInfo.java @@ -14,10 +14,7 @@ package org.eclipse.jdt.internal.core; import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; /** * Implementation of IJavaModel. A Java Model is specific to a @@ -31,14 +28,7 @@ public class JavaModelInfo extends OpenableElementInfo { * Compute the non-java resources contained in this java project. */ private Object[] computeNonJavaResources() { - IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); - try { - workspaceRoot.refreshLocal(IResource.DEPTH_INFINITE, null); - } catch (CoreException e) { - e.printStackTrace(); - return null; - } - IProject[] projects = workspaceRoot.getProjects(); + IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects(); int length = projects.length; Object[] resources = null; int index = 0; From 1511df2e0202d359ec16f904c83a8cfad54bab17 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Mon, 21 Oct 2024 15:30:53 +0530 Subject: [PATCH 36/63] Streamline error handling for sealed types. (#3123) --- .../eclipse/jdt/core/compiler/IProblem.java | 10 +- .../jdt/internal/compiler/ClassFile.java | 1 - .../internal/compiler/ast/TypeReference.java | 2 +- .../internal/compiler/lookup/ClassScope.java | 188 +++++++++++----- .../compiler/lookup/SourceTypeBinding.java | 207 +++--------------- .../compiler/problem/ProblemReporter.java | 135 +++--------- .../compiler/problem/messages.properties | 10 +- .../compiler/regression/SealedTypesTests.java | 20 +- .../compiler/SourceElementParser.java | 2 +- 9 files changed, 212 insertions(+), 363 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java index 6b534474b6f..dd89c862dc5 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java @@ -2550,11 +2550,15 @@ public interface IProblem { int SealedLocalDirectSuperTypeSealed = TypeRelated + 1864; /** @since 3.28 */ int SealedAnonymousClassCannotExtendSealedType = TypeRelated + 1865; - /** @since 3.28 */ + /** @since 3.28 + * @deprecated problem no longer generated + */ int SealedSuperTypeInDifferentPackage = TypeRelated + 1866; - /** @since 3.28 */ + /** @since 3.28 + * @deprecated problem no longer generated + */ int SealedSuperTypeDisallowed = TypeRelated + 1867; - /* Java15 errors - end */ + /* Java17 Sealed types errors - end */ /** * @since 3.28 diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ClassFile.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ClassFile.java index e254e3af717..ae0a9f56079 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ClassFile.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ClassFile.java @@ -5842,7 +5842,6 @@ private int generateTypeAnnotationAttributeForTypeDeclaration() { superInterface.getAllAnnotationContexts(AnnotationTargetTypeConstants.CLASS_EXTENDS, i, allTypeAnnotationContexts); } } - // TODO: permittedTypes codegen TypeParameter[] typeParameters = typeDeclaration.typeParameters; if (typeParameters != null) { for (int i = 0, max = typeParameters.length; i < max; i++) { 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 5b9315289d9..c6639e7131f 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 @@ -556,7 +556,7 @@ && isTypeUseDeprecated(type, scope)) { public boolean isTypeReference() { return true; } -public boolean isImplicit() { +public boolean isSynthetic() { return false; } public boolean isWildcard() { 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 39d145c3ae3..74ee28648a9 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 @@ -103,7 +103,7 @@ void buildAnonymousTypeBinding(SourceTypeBinding enclosingType, ReferenceBinding anonymousType.tagBits |= TagBits.HierarchyHasProblems; anonymousType.setSuperInterfaces(Binding.NO_SUPERINTERFACES); } if (supertype.isSealed()) { - problemReporter().sealedAnonymousClassCannotExtendSealedType(typeReference, supertype); + problemReporter().anonymousClassCannotExtendSealedType(typeReference, supertype); anonymousType.tagBits |= TagBits.HierarchyHasProblems; anonymousType.setSuperInterfaces(Binding.NO_SUPERINTERFACES); } @@ -133,7 +133,7 @@ void buildAnonymousTypeBinding(SourceTypeBinding enclosingType, ReferenceBinding anonymousType.tagBits |= TagBits.HierarchyHasProblems; anonymousType.setSuperClass(getJavaLangObject()); } else if (supertype.isSealed()) { - problemReporter().sealedAnonymousClassCannotExtendSealedType(typeReference, supertype); + problemReporter().anonymousClassCannotExtendSealedType(typeReference, supertype); anonymousType.tagBits |= TagBits.HierarchyHasProblems; anonymousType.setSuperClass(getJavaLangObject()); } @@ -577,7 +577,7 @@ private void checkAndSetModifiers() { switch (modifiers & (ExtraCompilerModifiers.AccSealed | ExtraCompilerModifiers.AccNonSealed | ClassFileConstants.AccFinal)) { case ExtraCompilerModifiers.AccSealed, ExtraCompilerModifiers.AccNonSealed, ClassFileConstants.AccFinal, ClassFileConstants.AccDefault : break; default : - problemReporter().IllegalModifierCombinationForType(sourceType); + problemReporter().illegalModifierCombinationForType(sourceType); break; } if (sourceType.isRecord()) { @@ -1190,6 +1190,14 @@ private boolean connectSuperclass() { } else { return connectRecordSuperclass(); } + } else if (superclass.isSealed() && sourceType.isLocalType()) { + sourceType.setSuperClass(superclass); + problemReporter().localTypeMayNotBePermittedType(sourceType, superclassRef, superclass); + return false; + } else if (superclass.isSealed() && !(sourceType.isFinal() || sourceType.isSealed() || sourceType.isNonSealed())) { + sourceType.setSuperClass(superclass); + problemReporter().permittedTypeNeedsModifier(sourceType, this.referenceContext, superclass); + return false; } else if ((superclass.tagBits & TagBits.HierarchyHasProblems) != 0 || !superclassRef.resolvedType.isValidBinding()) { sourceType.setSuperClass(superclass); @@ -1246,23 +1254,64 @@ private boolean connectEnumSuperclass() { return !foundCycle; } + /* Check that the permitted subtype and the sealed type are located in close proximity: either in the same module (if the superclass is in a named module) + * or in the same package (if the superclass is in the unnamed module) + * Return true, if all is well. Report error and return false otherwise, + */ + private boolean checkSealingProximity(ReferenceBinding subType, TypeReference subTypeReference, ReferenceBinding sealedType) { + final PackageBinding sealedTypePackage = sealedType.getPackage(); + final ModuleBinding sealedTypeModule = sealedType.module(); + if (subType.getPackage() != sealedTypePackage) { + if (sealedTypeModule.isUnnamed()) + problemReporter().permittedTypeOutsideOfPackage(subType, sealedType, subTypeReference, sealedTypePackage); + else if (subType.module() != sealedTypeModule) + problemReporter().permittedTypeOutsideOfModule(subType, sealedType, subTypeReference, sealedTypeModule); + } + return true; + } + void connectPermittedTypes() { SourceTypeBinding sourceType = this.referenceContext.binding; - if (this.referenceContext.permittedTypes != null && (this.referenceContext.permittedTypes.length == 0 || !this.referenceContext.permittedTypes[0].isImplicit())) { + if (this.referenceContext.permittedTypes != null && (this.referenceContext.permittedTypes.length == 0 || !this.referenceContext.permittedTypes[0].isSynthetic())) { sourceType.setPermittedTypes(Binding.NO_PERMITTED_TYPES); try { sourceType.tagBits |= TagBits.SealingTypeHierarchy; + if (!sourceType.isSealed()) + problemReporter().missingSealedModifier(sourceType, this.referenceContext); int length = this.referenceContext.permittedTypes.length; ReferenceBinding[] permittedTypeBindings = new ReferenceBinding[length]; int count = 0; nextPermittedType : for (int i = 0; i < length; i++) { TypeReference permittedTypeRef = this.referenceContext.permittedTypes[i]; ReferenceBinding permittedType = findPermittedtype(permittedTypeRef); - if (permittedType == null) { + if (permittedType == null || !permittedType.isValidBinding()) { continue nextPermittedType; } + if (sourceType.isClass()) { + ReferenceBinding superClass = permittedType.superclass(); + superClass = superClass == null ? null : superClass.actualType(); + if (!TypeBinding.equalsEquals(sourceType, superClass)) + problemReporter().sealedClassNotDirectSuperClassOf(permittedType, permittedTypeRef, sourceType); + } else if (sourceType.isInterface()) { + ReferenceBinding[] superInterfaces = permittedType.superInterfaces(); + boolean hierarchyOK = false; + if (superInterfaces != null) { + for (ReferenceBinding superInterface : superInterfaces) { + superInterface = superInterface == null ? null : superInterface.actualType(); + if (TypeBinding.equalsEquals(sourceType, superInterface)) { + hierarchyOK = true; + break; + } + } + if (!hierarchyOK) + problemReporter().sealedInterfaceNotDirectSuperInterfaceOf(permittedType, permittedTypeRef, sourceType); + } + } + + checkSealingProximity(permittedType, permittedTypeRef, sourceType); + for (int j = 0; j < i; j++) { if (TypeBinding.equalsEquals(permittedTypeBindings[j], permittedType)) { problemReporter().duplicatePermittedType(sourceType, permittedTypeRef, permittedType); @@ -1287,7 +1336,7 @@ void connectPermittedTypes() { sourceType.setPermittedTypes(Binding.NO_PERMITTED_TYPES); if (sourceType.isSealed()) { if (!sourceType.isLocalType() && !sourceType.isRecord() && !sourceType.isEnum()) // error flagged alread - problemReporter().sealedSealedTypeMissingPermits(sourceType, this.referenceContext); + problemReporter().sealedTypeMissingPermits(sourceType, this.referenceContext); } } } @@ -1320,70 +1369,89 @@ private boolean connectRecordSuperclass() { */ private boolean connectSuperInterfaces() { SourceTypeBinding sourceType = this.referenceContext.binding; - sourceType.setSuperInterfaces(Binding.NO_SUPERINTERFACES); - if (this.referenceContext.superInterfaces == null) { - if (sourceType.isAnnotationType() && compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) { // do not connect if source < 1.5 as annotation already got flagged as syntax error) { - ReferenceBinding annotationType = getJavaLangAnnotationAnnotation(); - boolean foundCycle = detectHierarchyCycle(sourceType, annotationType, null); - sourceType.setSuperInterfaces(new ReferenceBinding[] { annotationType }); - return !foundCycle; - } - return true; - } - if (sourceType.id == TypeIds.T_JavaLangObject) // already handled the case of redefining java.lang.Object - return true; - + boolean hasSealedSupertype = sourceType.superclass == null ? false : sourceType.superclass.isSealed(); boolean noProblems = true; - int length = this.referenceContext.superInterfaces.length; - ReferenceBinding[] interfaceBindings = new ReferenceBinding[length]; - int count = 0; - nextInterface : for (int i = 0; i < length; i++) { - TypeReference superInterfaceRef = this.referenceContext.superInterfaces[i]; - ReferenceBinding superInterface = findSupertype(superInterfaceRef); - if (superInterface == null) { // detected cycle - sourceType.tagBits |= TagBits.HierarchyHasProblems; - noProblems = false; - continue nextInterface; + try { + sourceType.setSuperInterfaces(Binding.NO_SUPERINTERFACES); + if (this.referenceContext.superInterfaces == null) { + if (sourceType.isAnnotationType() && compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) { // do not connect if source < 1.5 as annotation already got flagged as syntax error) { + ReferenceBinding annotationType = getJavaLangAnnotationAnnotation(); + boolean foundCycle = detectHierarchyCycle(sourceType, annotationType, null); + sourceType.setSuperInterfaces(new ReferenceBinding[] { annotationType }); + return !foundCycle; + } + return true; } + if (sourceType.id == TypeIds.T_JavaLangObject) // already handled the case of redefining java.lang.Object + return true; - // check for simple interface collisions - // Check for a duplicate interface once the name is resolved, otherwise we may be confused (i.e. a.b.I and c.d.I) - for (int j = 0; j < i; j++) { - if (TypeBinding.equalsEquals(interfaceBindings[j], superInterface)) { - problemReporter().duplicateSuperinterface(sourceType, superInterfaceRef, superInterface); + int length = this.referenceContext.superInterfaces.length; + ReferenceBinding[] interfaceBindings = new ReferenceBinding[length]; + int count = 0; + nextInterface : for (int i = 0; i < length; i++) { + TypeReference superInterfaceRef = this.referenceContext.superInterfaces[i]; + ReferenceBinding superInterface = findSupertype(superInterfaceRef); + if (superInterface == null) { // detected cycle sourceType.tagBits |= TagBits.HierarchyHasProblems; noProblems = false; continue nextInterface; } + + if (superInterface.isSealed()) + hasSealedSupertype = true; + + // check for simple interface collisions + // Check for a duplicate interface once the name is resolved, otherwise we may be confused (i.e. a.b.I and c.d.I) + for (int j = 0; j < i; j++) { + if (TypeBinding.equalsEquals(interfaceBindings[j], superInterface)) { + problemReporter().duplicateSuperinterface(sourceType, superInterfaceRef, superInterface); + sourceType.tagBits |= TagBits.HierarchyHasProblems; + noProblems = false; + continue nextInterface; + } + } + if (!superInterface.isInterface() && (superInterface.tagBits & TagBits.HasMissingType) == 0) { + problemReporter().superinterfaceMustBeAnInterface(sourceType, superInterfaceRef, superInterface); + sourceType.tagBits |= TagBits.HierarchyHasProblems; + noProblems = false; + continue nextInterface; + } else if (superInterface.isAnnotationType()){ + problemReporter().annotationTypeUsedAsSuperinterface(sourceType, superInterfaceRef, superInterface); + } + if ((superInterface.tagBits & TagBits.HasDirectWildcard) != 0) { + problemReporter().superTypeCannotUseWildcard(sourceType, superInterfaceRef, superInterface); + sourceType.tagBits |= TagBits.HierarchyHasProblems; + noProblems = false; + continue nextInterface; + } + if ((superInterface.tagBits & TagBits.HierarchyHasProblems) != 0 + || !superInterfaceRef.resolvedType.isValidBinding()) { + sourceType.tagBits |= TagBits.HierarchyHasProblems; // propagate if missing supertype + noProblems &= superInterfaceRef.resolvedType.isValidBinding(); + } + if (superInterface.isSealed() && sourceType.isLocalType()) { + problemReporter().localTypeMayNotBePermittedType(sourceType, superInterfaceRef, superInterface); + noProblems = false; + } else if (superInterface.isSealed() && !(sourceType.isFinal() || sourceType.isSealed() || sourceType.isNonSealed())) { + problemReporter().permittedTypeNeedsModifier(sourceType, this.referenceContext, superInterface); + noProblems = false; + } + + // only want to reach here when no errors are reported + sourceType.typeBits |= (superInterface.typeBits & TypeIds.InheritableBits); + interfaceBindings[count++] = superInterface; } - if (!superInterface.isInterface() && (superInterface.tagBits & TagBits.HasMissingType) == 0) { - problemReporter().superinterfaceMustBeAnInterface(sourceType, superInterfaceRef, superInterface); - sourceType.tagBits |= TagBits.HierarchyHasProblems; - noProblems = false; - continue nextInterface; - } else if (superInterface.isAnnotationType()){ - problemReporter().annotationTypeUsedAsSuperinterface(sourceType, superInterfaceRef, superInterface); - } - if ((superInterface.tagBits & TagBits.HasDirectWildcard) != 0) { - problemReporter().superTypeCannotUseWildcard(sourceType, superInterfaceRef, superInterface); - sourceType.tagBits |= TagBits.HierarchyHasProblems; - noProblems = false; - continue nextInterface; + // hold onto all correctly resolved superinterfaces + if (count > 0) { + if (count != length) + System.arraycopy(interfaceBindings, 0, interfaceBindings = new ReferenceBinding[count], 0, count); + sourceType.setSuperInterfaces(interfaceBindings); } - if ((superInterface.tagBits & TagBits.HierarchyHasProblems) != 0 - || !superInterfaceRef.resolvedType.isValidBinding()) { - sourceType.tagBits |= TagBits.HierarchyHasProblems; // propagate if missing supertype - noProblems &= superInterfaceRef.resolvedType.isValidBinding(); + } finally { + if (sourceType.isNonSealed() && !hasSealedSupertype) { + if (!sourceType.isRecord() && !sourceType.isLocalType() && !sourceType.isEnum() && !sourceType.isSealed()) // avoid double jeopardy + problemReporter().disallowedNonSealedModifier(sourceType, this.referenceContext); } - // only want to reach here when no errors are reported - sourceType.typeBits |= (superInterface.typeBits & TypeIds.InheritableBits); - interfaceBindings[count++] = superInterface; - } - // hold onto all correctly resolved superinterfaces - if (count > 0) { - if (count != length) - System.arraycopy(interfaceBindings, 0, interfaceBindings = new ReferenceBinding[count], 0, count); - sourceType.setSuperInterfaces(interfaceBindings); } return noProblems; } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java index 9c6f58857ce..d436a36ba5c 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java @@ -55,7 +55,6 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.lookup; -import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -1098,201 +1097,49 @@ private void checkAnnotationsInType() { void faultInTypesForFieldsAndMethods() { if (!isPrototype()) throw new IllegalStateException(); - checkPermitsInType(); + if (!this.isLocalType()) + complainIfUnpermittedSubtyping(); // this has nothing to do with fields and methods but time is ripe for this check. checkAnnotationsInType(); internalFaultInTypeForFieldsAndMethods(); } -private Map.Entry getFirstSealedSuperTypeOrInterface(TypeDeclaration typeDecl) { - boolean isAnySuperTypeSealed = typeDecl.superclass != null && this.superclass != null ? this.superclass.isSealed() : false; - if (isAnySuperTypeSealed) - return new AbstractMap.SimpleEntry<>(typeDecl.superclass, this.superclass); +private boolean isAnUnpermittedSubtypeOf(ReferenceBinding superType) { - ReferenceBinding[] superInterfaces1 = this.superInterfaces(); - int l = superInterfaces1 != null ? superInterfaces1.length : 0; - for (int i = 0; i < l; ++i) { - ReferenceBinding superInterface = superInterfaces1[i]; - if (superInterface.isSealed()) { - return new AbstractMap.SimpleEntry<>(typeDecl.superInterfaces[i], superInterface); - } + if (superType == null || !superType.isSealed()) + return false; + + for (ReferenceBinding permittedType : superType.actualType().permittedTypes()) { + if (TypeBinding.equalsEquals(this, permittedType)) + return false; } - return null; + + return true; } -private void checkPermitsInType() { +private void complainIfUnpermittedSubtyping() { + + // Diagnose unauthorized subtyping: This cannot be correctly hoisted into ClassScope.{ connectSuperclass() | connectSuperInterfaces() | connectPermittedTypes() } + // but can be taken up now + TypeDeclaration typeDecl = this.scope.referenceContext; - boolean hasPermittedTypes = this.permittedTypes != null && this.permittedTypes.length > 0; - if (hasPermittedTypes) { - if (!this.isSealed()) - this.scope.problemReporter().sealedMissingSealedModifier(this, typeDecl); - ModuleBinding sourceModuleBinding = this.module(); - boolean isUnnamedModule = sourceModuleBinding.isUnnamed(); - if (isUnnamedModule) { - PackageBinding sourceTypePackage = this.getPackage(); - for (int i =0, l = this.permittedTypes.length; i < l; i++) { - ReferenceBinding permType = this.permittedTypes[i]; - if (!permType.isValidBinding()) continue; - if (sourceTypePackage != permType.getPackage()) { - TypeReference permittedTypeRef = typeDecl.permittedTypes[i]; - this.scope.problemReporter().sealedPermittedTypeOutsideOfPackage(permType, this, permittedTypeRef, sourceTypePackage); - } - } - } else { - for (int i = 0, l = this.permittedTypes.length; i < l; i++) { - ReferenceBinding permType = this.permittedTypes[i]; - if (!permType.isValidBinding()) continue; - ModuleBinding permTypeModule = permType.module(); - if (permTypeModule != null && sourceModuleBinding != permTypeModule) { - TypeReference permittedTypeRef = typeDecl.permittedTypes[i]; - this.scope.problemReporter().sealedPermittedTypeOutsideOfModule(permType, this, permittedTypeRef, sourceModuleBinding); - } - } - } + if (this.isAnUnpermittedSubtypeOf(this.superclass)) { + this.scope.problemReporter().sealedSupertypeDoesNotPermit(this, typeDecl.superclass, this.superclass); } -// ReferenceBinding superType = this.superclass(); - Map.Entry sealedEntry = getFirstSealedSuperTypeOrInterface(typeDecl); - boolean foundSealedSuperTypeOrInterface = sealedEntry != null; - if (this.isLocalType()) { - if (this.isSealed() || this.isNonSealed()) - return; // already handled elsewhere - if (foundSealedSuperTypeOrInterface) { - this.scope.problemReporter().sealedLocalDirectSuperTypeSealed(this, sealedEntry.getKey(), sealedEntry.getValue()); - return; - } - } else if (this.isNonSealed()) { - if (!this.isSealed()) { // lest we bark again. - if (!foundSealedSuperTypeOrInterface) { - if (this.isClass() && !this.isRecord()) // record to give only illegal modifier error. - this.scope.problemReporter().sealedDisAllowedNonSealedModifierInClass(this, typeDecl); - else if (this.isInterface()) - this.scope.problemReporter().sealedDisAllowedNonSealedModifierInInterface(this, typeDecl); - } - } - } - if (foundSealedSuperTypeOrInterface) { - if (!(this.isFinal() || this.isSealed() || this.isNonSealed())) { - if (this.isClass()) - this.scope.problemReporter().sealedMissingClassModifier(this, typeDecl, sealedEntry.getValue()); - else if (this.isInterface()) - this.scope.problemReporter().sealedMissingInterfaceModifier(this, typeDecl, sealedEntry.getValue()); - } - if (!typeDecl.isRecord() && typeDecl.superclass != null && !checkPermitsAndAdd(this.superclass)) { - reportSealedSuperTypeDoesNotPermitProblem(typeDecl.superclass, this.superclass); - } - for (int i = 0, l = this.superInterfaces.length; i < l; ++i) { - ReferenceBinding superInterface = this.superInterfaces[i]; - if (superInterface != null && !checkPermitsAndAdd(superInterface)) { - TypeReference superInterfaceRef = typeDecl.superInterfaces[i]; - reportSealedSuperTypeDoesNotPermitProblem(superInterfaceRef, superInterface); - } + for (int i = 0, l = this.superInterfaces.length; i < l; ++i) { + ReferenceBinding superInterface = this.superInterfaces[i]; + if (this.isAnUnpermittedSubtypeOf(superInterface)) { + TypeReference superInterfaceRef = typeDecl.superInterfaces[i]; + this.scope.problemReporter().sealedSupertypeDoesNotPermit(this, superInterfaceRef, superInterface); } } + for (ReferenceBinding memberType : this.memberTypes) - ((SourceTypeBinding) memberType).checkPermitsInType(); + ((SourceTypeBinding) memberType).complainIfUnpermittedSubtyping(); - if (this.scope.referenceContext.permittedTypes == null) { - // Ignore implicitly permitted case - return; - } - // In case of errors, be safe. - int l = this.permittedTypes.length <= this.scope.referenceContext.permittedTypes.length ? - this.permittedTypes.length : this.scope.referenceContext.permittedTypes.length; - for (int i = 0; i < l; i++) { - TypeReference permittedTypeRef = this.scope.referenceContext.permittedTypes[i]; - ReferenceBinding permittedType = this.permittedTypes[i]; - if (permittedType == null || !permittedType.isValidBinding()) - continue; - if (this.isClass()) { - ReferenceBinding permSuperType = permittedType.superclass(); - permSuperType = permSuperType.actualType(); - if (!TypeBinding.equalsEquals(this, permSuperType)) { - this.scope.problemReporter().sealedNotDirectSuperClass(permittedType, permittedTypeRef, this); - continue; - } - } else if (this.isInterface()) { - ReferenceBinding[] permSuperInterfaces = permittedType.superInterfaces(); - boolean foundSuperInterface = false; - if (permSuperInterfaces != null) { - for (ReferenceBinding psi : permSuperInterfaces) { - psi = psi.actualType(); - if (TypeBinding.equalsEquals(this, psi)) { - foundSuperInterface = true; - break; - } - } - if (!foundSuperInterface) { - this.scope.problemReporter().sealedNotDirectSuperInterface(permittedType, permittedTypeRef, this); - continue; - } - } - } - } return; } -private void reportSealedSuperTypeDoesNotPermitProblem(TypeReference superTypeRef, TypeBinding superType) { - ModuleBinding sourceModuleBinding = this.module(); - boolean isUnnamedModule = sourceModuleBinding.isUnnamed(); - boolean isClass = false; - if (superType.isClass()) { - isClass = true; - } - boolean sealedSuperTypeDoesNotPermit = false; - ReferenceBinding superReferenceBinding = null; - if (superType instanceof ReferenceBinding) { - superReferenceBinding = (ReferenceBinding) superType; - if (isUnnamedModule) { - PackageBinding superTypePackage = superReferenceBinding.getPackage(); - PackageBinding pkg = this.getPackage(); - sealedSuperTypeDoesNotPermit = pkg!= null && pkg.equals(superTypePackage); - } else { - ModuleBinding superTypeModule = superReferenceBinding.module(); - ModuleBinding mod = this.module(); - sealedSuperTypeDoesNotPermit = mod!= null && mod.equals(superTypeModule); - } - } - if (sealedSuperTypeDoesNotPermit) { - if (isClass) { - this.scope.problemReporter().sealedSuperClassDoesNotPermit(this, superTypeRef, superType); - } else { - this.scope.problemReporter().sealedSuperInterfaceDoesNotPermit(this, superTypeRef, superType); - } - } else { - if (superReferenceBinding instanceof SourceTypeBinding && isUnnamedModule) { - PackageBinding superTypePackage = superReferenceBinding.getPackage(); - if (isClass) { - this.scope.problemReporter().sealedSuperClassInDifferentPackage(this, superTypeRef, superType, superTypePackage); - } else { - this.scope.problemReporter().sealedSuperInterfaceInDifferentPackage(this, superTypeRef, superType, superTypePackage); - } - } else { - if (isClass) { - this.scope.problemReporter().sealedSuperClassDisallowed(this, superTypeRef, superType); - } else { - this.scope.problemReporter().sealedSuperInterfaceDisallowed(this, superTypeRef, superType); - } - } - } -} - -private boolean checkPermitsAndAdd(ReferenceBinding superType) { - if (superType == null - || superType.equals(this.scope.getJavaLangObject()) - || !superType.isSealed()) - return true; - if (superType.isSealed()) { - superType = superType.actualType(); - ReferenceBinding[] superPermittedTypes = superType.permittedTypes(); - for (ReferenceBinding permittedType : superPermittedTypes) { - permittedType = permittedType.actualType(); - if (permittedType.isValidBinding() && TypeBinding.equalsEquals(this, permittedType)) - return true; - } - } - return false; -} - @Override public RecordComponentBinding[] components() { @@ -3229,7 +3076,7 @@ public ReferenceBinding setSuperClass(ReferenceBinding superClass) { } } if (superClass != null && superClass.actualType() instanceof SourceTypeBinding sourceSuperType && sourceSuperType.isSealed() - && (sourceSuperType.scope.referenceContext.permittedTypes == null || (sourceSuperType.scope.referenceContext.permittedTypes.length > 0 && sourceSuperType.scope.referenceContext.permittedTypes[0].isImplicit()))) { + && (sourceSuperType.scope.referenceContext.permittedTypes == null || (sourceSuperType.scope.referenceContext.permittedTypes.length > 0 && sourceSuperType.scope.referenceContext.permittedTypes[0].isSynthetic()))) { sourceSuperType.setImplicitPermittedType(this); if (this.isAnonymousType() && superClass.isEnum()) this.modifiers |= ClassFileConstants.AccFinal; @@ -3269,7 +3116,7 @@ private void setImplicitPermittedType(SourceTypeBinding permittedType) { for (int i = 0, length = superInterfaces == null ? 0 : superInterfaces.length; i < length; i++) { ReferenceBinding superInterface = superInterfaces[i]; if (superInterface.actualType() instanceof SourceTypeBinding sourceSuperType && sourceSuperType.isSealed() - && (sourceSuperType.scope.referenceContext.permittedTypes == null || (sourceSuperType.scope.referenceContext.permittedTypes.length > 0 && sourceSuperType.scope.referenceContext.permittedTypes[0].isImplicit()))) { + && (sourceSuperType.scope.referenceContext.permittedTypes == null || (sourceSuperType.scope.referenceContext.permittedTypes.length > 0 && sourceSuperType.scope.referenceContext.permittedTypes[0].isSynthetic()))) { sourceSuperType.setImplicitPermittedType(this); } } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java index 4f2b75af875..bfd57a0227b 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java @@ -3343,7 +3343,7 @@ public void illegalVisibilityModifierCombinationForField(ReferenceBinding type, fieldDecl.sourceStart, fieldDecl.sourceEnd); } -public void IllegalModifierCombinationForType(SourceTypeBinding type) { +public void illegalModifierCombinationForType(SourceTypeBinding type) { String[] arguments = new String[] {new String(type.sourceName())}; this.handle( IProblem.IllegalModifierCombinationForType, @@ -12230,107 +12230,54 @@ public void illegalModifierForLocalEnumDeclaration(SourceTypeBinding type) { type.sourceStart(), type.sourceEnd()); } -private void sealedMissingModifier(int problem, SourceTypeBinding type, TypeDeclaration typeDecl, TypeBinding superTypeBinding) { + +public void permittedTypeNeedsModifier(SourceTypeBinding type, TypeDeclaration typeDecl, TypeBinding superTypeBinding) { String name = new String(type.sourceName()); String superTypeFullName = new String(superTypeBinding.readableName()); String superTypeShortName = new String(superTypeBinding.shortReadableName()); if (superTypeShortName.equals(name)) superTypeShortName = superTypeFullName; this.handle( - problem, + type.isClass() ? IProblem.SealedMissingClassModifier : IProblem.SealedMissingInterfaceModifier, new String[] {superTypeFullName, name}, new String[] {superTypeShortName, name}, typeDecl.sourceStart, typeDecl.sourceEnd); } -public void sealedMissingClassModifier(SourceTypeBinding type, TypeDeclaration typeDecl, TypeBinding superTypeBinding) { - sealedMissingModifier(IProblem.SealedMissingClassModifier, type, typeDecl, superTypeBinding); -} -public void sealedMissingInterfaceModifier(SourceTypeBinding type, TypeDeclaration typeDecl, TypeBinding superTypeBinding) { - sealedMissingModifier(IProblem.SealedMissingInterfaceModifier, type, typeDecl, superTypeBinding); -} -public void sealedDisAllowedNonSealedModifierInClass(SourceTypeBinding type, TypeDeclaration typeDecl) { +public void disallowedNonSealedModifier(SourceTypeBinding type, TypeDeclaration typeDecl) { String name = new String(type.sourceName()); this.handle( - IProblem.SealedDisAllowedNonSealedModifierInClass, + type.isClass() ? IProblem.SealedDisAllowedNonSealedModifierInClass : IProblem.SealedDisAllowedNonSealedModifierInInterface, new String[] { name }, new String[] { name }, typeDecl.sourceStart, typeDecl.sourceEnd); } -private void sealedSuperTypeDoesNotPermit(int problem, SourceTypeBinding type, TypeReference superType, TypeBinding superTypeBinding) { - String name = new String(type.sourceName()); - String superTypeFullName = new String(superTypeBinding.readableName()); - String superTypeShortName = new String(superTypeBinding.shortReadableName()); - if (superTypeShortName.equals(name)) superTypeShortName = superTypeFullName; - this.handle( - problem, - new String[] {name, superTypeFullName}, - new String[] {name, superTypeShortName}, - superType.sourceStart, - superType.sourceEnd); -} - -public void sealedSuperTypeInDifferentPackage(int problem, SourceTypeBinding type, TypeReference curType, TypeBinding superTypeBinding, PackageBinding superPackageBinding) { - String typeName = new String(type.sourceName); - String name = new String(superTypeBinding.sourceName()); - String packageName = superPackageBinding.compoundName == CharOperation.NO_CHAR_CHAR ? "default" : //$NON-NLS-1$ - CharOperation.toString(superPackageBinding.compoundName); - String[] arguments = new String[] {typeName, packageName, name}; - this.handle(problem, - arguments, - arguments, - curType.sourceStart, - curType.sourceEnd); -} - -public void sealedSuperTypeDisallowed(int problem, SourceTypeBinding type, TypeReference curType, TypeBinding superTypeBinding) { - String typeName = new String(type.sourceName); - String name = new String(superTypeBinding.sourceName()); - String[] arguments = new String[] {typeName, name}; - this.handle(problem, - arguments, - arguments, - curType.sourceStart, - curType.sourceEnd); -} - -public void sealedSuperClassDoesNotPermit(SourceTypeBinding type, TypeReference superType, TypeBinding superTypeBinding) { - sealedSuperTypeDoesNotPermit(IProblem.SealedSuperClassDoesNotPermit, type, superType, superTypeBinding); -} - -public void sealedSuperClassInDifferentPackage(SourceTypeBinding type, TypeReference curType, TypeBinding superTypeBinding, PackageBinding superPackageBinding) { - sealedSuperTypeInDifferentPackage(IProblem.SealedSuperTypeInDifferentPackage, type, curType, superTypeBinding, superPackageBinding); -} - -public void sealedSuperClassDisallowed(SourceTypeBinding type, TypeReference curType, TypeBinding superTypeBinding) { - sealedSuperTypeDisallowed(IProblem.SealedSuperTypeDisallowed, type, curType, superTypeBinding); -} - -public void sealedSuperInterfaceDoesNotPermit(SourceTypeBinding type, TypeReference superType, TypeBinding superTypeBinding) { +public void sealedSupertypeDoesNotPermit(SourceTypeBinding type, TypeReference superType, TypeBinding superTypeBinding) { String name = new String(type.sourceName()); String superTypeFullName = new String(superTypeBinding.readableName()); String superTypeShortName = new String(superTypeBinding.shortReadableName()); - String keyword = type.isClass() ? new String(TypeConstants.IMPLEMENTS) : new String(TypeConstants.KEYWORD_EXTENDS); if (superTypeShortName.equals(name)) superTypeShortName = superTypeFullName; - this.handle( - IProblem.SealedSuperInterfaceDoesNotPermit, - new String[] {name, superTypeFullName, keyword}, - new String[] {name, superTypeShortName, keyword}, - superType.sourceStart, - superType.sourceEnd); -} - -public void sealedSuperInterfaceInDifferentPackage(SourceTypeBinding type, TypeReference curType, TypeBinding superTypeBinding, PackageBinding superPackageBinding) { - sealedSuperTypeInDifferentPackage(IProblem.SealedSuperTypeInDifferentPackage, type, curType, superTypeBinding, superPackageBinding); -} - -public void sealedSuperInterfaceDisallowed(SourceTypeBinding type, TypeReference curType, TypeBinding superTypeBinding) { - sealedSuperTypeDisallowed(IProblem.SealedSuperTypeDisallowed, type, curType, superTypeBinding); + if (superTypeBinding.isClass()) { + this.handle( + IProblem.SealedSuperClassDoesNotPermit, + new String[] {name, superTypeFullName}, + new String[] {name, superTypeShortName}, + superType.sourceStart, + superType.sourceEnd); + } else { + String keyword = type.isClass() ? new String(TypeConstants.IMPLEMENTS) : new String(TypeConstants.KEYWORD_EXTENDS); + this.handle( + IProblem.SealedSuperInterfaceDoesNotPermit, + new String[] {name, superTypeFullName, keyword}, + new String[] {name, superTypeShortName, keyword}, + superType.sourceStart, + superType.sourceEnd); + } } -public void sealedMissingSealedModifier(SourceTypeBinding type, ASTNode node) { +public void missingSealedModifier(SourceTypeBinding type, ASTNode node) { String name = new String(type.sourceName()); this.handle(IProblem.SealedMissingSealedModifier, new String[] { name }, new String[] { name }, node.sourceStart, node.sourceEnd); @@ -12349,7 +12296,7 @@ public void duplicatePermittedType(SourceTypeBinding type, TypeReference referen reference.sourceEnd); } -public void sealedNotDirectSuperClass(ReferenceBinding type, TypeReference reference, SourceTypeBinding superType) { +public void sealedClassNotDirectSuperClassOf(ReferenceBinding type, TypeReference reference, SourceTypeBinding superType) { this.handle( IProblem.SealedNotDirectSuperClass, new String[] { @@ -12361,9 +12308,9 @@ public void sealedNotDirectSuperClass(ReferenceBinding type, TypeReference refer reference.sourceStart, reference.sourceEnd); } -public void sealedPermittedTypeOutsideOfModule(ReferenceBinding permType, SourceTypeBinding type, ASTNode node, ModuleBinding moduleBinding) { +public void permittedTypeOutsideOfModule(ReferenceBinding permType, ReferenceBinding sealedType, ASTNode node, ModuleBinding moduleBinding) { String permTypeName = new String(permType.sourceName); - String name = new String(type.sourceName()); + String name = new String(sealedType.sourceName()); String moduleName = new String(moduleBinding.name()); String[] arguments = new String[] {permTypeName, moduleName, name}; this.handle(IProblem.SealedPermittedTypeOutsideOfModule, @@ -12372,15 +12319,9 @@ public void sealedPermittedTypeOutsideOfModule(ReferenceBinding permType, Source node.sourceStart, node.sourceEnd); } -public void sealedPermittedTypeOutsideOfModule(SourceTypeBinding type, ASTNode node) { - String name = new String(type.sourceName()); - this.handle(IProblem.SealedPermittedTypeOutsideOfModule, new String[] { name }, new String[] { name }, - node.sourceStart, node.sourceEnd); -} - -public void sealedPermittedTypeOutsideOfPackage(ReferenceBinding permType, SourceTypeBinding type, ASTNode node, PackageBinding packageBinding) { +public void permittedTypeOutsideOfPackage(ReferenceBinding permType, ReferenceBinding sealedType, ASTNode node, PackageBinding packageBinding) { String permTypeName = new String(permType.sourceName); - String name = new String(type.sourceName()); + String name = new String(sealedType.sourceName()); String packageName = packageBinding.compoundName == CharOperation.NO_CHAR_CHAR ? "default" : //$NON-NLS-1$ CharOperation.toString(packageBinding.compoundName); String[] arguments = new String[] {permTypeName, packageName, name}; @@ -12391,23 +12332,13 @@ public void sealedPermittedTypeOutsideOfPackage(ReferenceBinding permType, Sourc node.sourceEnd); } -public void sealedSealedTypeMissingPermits(SourceTypeBinding type, ASTNode node) { +public void sealedTypeMissingPermits(SourceTypeBinding type, ASTNode node) { String name = new String(type.sourceName()); this.handle(IProblem.SealedSealedTypeMissingPermits, new String[] { name }, new String[] { name }, node.sourceStart, node.sourceEnd); } -public void sealedDisAllowedNonSealedModifierInInterface(SourceTypeBinding type, TypeDeclaration typeDecl) { - String name = new String(type.sourceName()); - this.handle( - IProblem.SealedDisAllowedNonSealedModifierInInterface, - new String[] { name }, - new String[] { name }, - typeDecl.sourceStart, - typeDecl.sourceEnd); -} - -public void sealedNotDirectSuperInterface(ReferenceBinding type, TypeReference reference, SourceTypeBinding superType) { +public void sealedInterfaceNotDirectSuperInterfaceOf(ReferenceBinding type, TypeReference reference, SourceTypeBinding superType) { this.handle( IProblem.SealedNotDirectSuperInterface, new String[] { @@ -12420,7 +12351,7 @@ public void sealedNotDirectSuperInterface(ReferenceBinding type, TypeReference r reference.sourceEnd); } -public void sealedLocalDirectSuperTypeSealed(SourceTypeBinding type, TypeReference superclass, TypeBinding superTypeBinding) { +public void localTypeMayNotBePermittedType(SourceTypeBinding type, TypeReference superclass, TypeBinding superTypeBinding) { String name = new String(type.sourceName()); String superTypeFullName = new String(superTypeBinding.readableName()); String superTypeShortName = new String(superTypeBinding.shortReadableName()); @@ -12432,7 +12363,7 @@ public void sealedLocalDirectSuperTypeSealed(SourceTypeBinding type, TypeReferen superclass.sourceStart, superclass.sourceEnd); } -public void sealedAnonymousClassCannotExtendSealedType(TypeReference reference, TypeBinding type) { +public void anonymousClassCannotExtendSealedType(TypeReference reference, TypeBinding type) { this.handle( IProblem.SealedAnonymousClassCannotExtendSealedType, new String[] {new String(type.readableName())}, diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties index 5796ad53262..1aac0cda513 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties @@ -1131,7 +1131,7 @@ # Java 16 1820 = {0} is a value-based type which is a discouraged argument for the synchronized statement -# Java 15 Preview - cont +# Sealed types - Java 17 1850 = The class {1} with a sealed direct superclass or a sealed direct superinterface {0} should be declared either final, sealed, or non-sealed 1851 = A class {0} declared as non-sealed should have either a sealed direct superclass or a sealed direct superinterface 1852 = The type {0} extending a sealed class {1} should be a permitted subtype of {1} @@ -1143,13 +1143,13 @@ 1858 = Permitted type {0} in a named module {1} should be declared in the same module {1} of declaring type {2} 1859 = Permitted type {0} in an unnamed module should be declared in the same package {1} of declaring type {2} 1860 = Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares {0} as its direct superclass or superinterface -1861 = An interface {0} is declared both sealed and non-sealed +###[obsolete] 1861 = An interface {0} is declared both sealed and non-sealed 1862 = An interface {0} declared as non-sealed should have a sealed direct superinterface 1863 = Permitted type {0} does not declare {1} as direct super interface -1864 = A local class {1} cannot have a sealed direct superclass or a sealed direct superinterface {0} +1864 = The local type {1} may not have a sealed supertype {0} 1865 = An anonymous class cannot subclass a sealed type {0} -1866 = Sealed type {2} and sub type {0} in an unnamed module should be declared in the same package {1} -1867 = Sealed type {1} cannot be super type of {0} as it is from a different package or split package or module +###[obsolete] 1866 = Sealed type {2} and sub type {0} in an unnamed module should be declared in the same package {1} +###[obsolete] 1867 = Sealed type {1} cannot be super type of {0} as it is from a different package or split package or module # Switch Patterns - Java 21 1900 = Local variable {0} referenced from a guard must be final or effectively final diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java index 79c0e51e9a6..e546688db04 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java @@ -626,7 +626,7 @@ public void testBug563806_010() { "1. ERROR in p2\\Y.java (at line 2)\n" + " public final class Y extends p1.X{}\n" + " ^^^^\n" + - "Sealed type X and sub type Y in an unnamed module should be declared in the same package p1\n" + + "The type Y extending a sealed class X should be a permitted subtype of X\n" + "----------\n"); } public void testBug563806_011() { @@ -660,7 +660,7 @@ public void testBug563806_012() { "1. ERROR in p2\\Y.java (at line 2)\n" + " public final class Y implements p1.X{}\n" + " ^^^^\n" + - "Sealed type X and sub type Y in an unnamed module should be declared in the same package p1\n" + + "The type Y that implements a sealed interface X should be a permitted subtype of X\n" + "----------\n"); } public void testBug563806_013() { @@ -705,7 +705,7 @@ public void testBug563806_014() { "2. ERROR in p2\\Y.java (at line 2)\n" + " public interface Y extends p1.X{}\n" + " ^^^^\n" + - "Sealed type X and sub type Y in an unnamed module should be declared in the same package p1\n" + + "The type Y that extends a sealed interface X should be a permitted subtype of X\n" + "----------\n"); } public void testBug563806_015() { @@ -1219,7 +1219,7 @@ public void testBug563806_039() { "2. ERROR in p1\\X.java (at line 5)\n" + " class Y extends A{}\n" + " ^\n" + - "A local class Y cannot have a sealed direct superclass or a sealed direct superinterface A\n" + + "The local type Y may not have a sealed supertype A\n" + "----------\n"); } public void testBug564191_001() throws IOException, ClassFormatException { @@ -5547,12 +5547,12 @@ public void testBug568854_007() { "1. ERROR in X.java (at line 5)\n" + " class Y implements I {}\n" + " ^\n" + - "A local class Y cannot have a sealed direct superclass or a sealed direct superinterface I\n" + + "The local type Y may not have a sealed supertype I\n" + "----------\n" + "2. ERROR in X.java (at line 10)\n" + " class Z implements I{}\n" + " ^\n" + - "A local class Z cannot have a sealed direct superclass or a sealed direct superinterface I\n" + + "The local type Z may not have a sealed supertype I\n" + "----------\n"); } public void testBug568854_008() { @@ -5576,12 +5576,12 @@ public void testBug568854_008() { "1. ERROR in X.java (at line 6)\n" + " class Y implements I {}\n" + " ^\n" + - "A local class Y cannot have a sealed direct superclass or a sealed direct superinterface I\n" + + "The local type Y may not have a sealed supertype I\n" + "----------\n" + "2. ERROR in X.java (at line 10)\n" + " class Z implements I{}\n" + " ^\n" + - "A local class Z cannot have a sealed direct superclass or a sealed direct superinterface I\n" + + "The local type Z may not have a sealed supertype I\n" + "----------\n"); } public void testBug571332_001() { @@ -5623,7 +5623,7 @@ public void testBug570605_001() { "1. ERROR in X.java (at line 6)\n" + " class L extends Y {}\n" + " ^\n" + - "A local class L cannot have a sealed direct superclass or a sealed direct superinterface Y\n" + + "The local type L may not have a sealed supertype Y\n" + "----------\n"); } public void testBug570218_001() { @@ -5669,7 +5669,7 @@ public void testBug572205_001() { "1. ERROR in X.java (at line 3)\n" + " class Circle implements Shape{}\n" + " ^^^^^\n" + - "A local class Circle cannot have a sealed direct superclass or a sealed direct superinterface X.Shape\n" + + "The local type Circle may not have a sealed supertype X.Shape\n" + "----------\n" + "2. ERROR in X.java (at line 5)\n" + " sealed interface Shape {}\n" + 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 b0ef7dae9e3..34e348feed8 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 @@ -1017,7 +1017,7 @@ public String toString() { return new String(this.token); } @Override - public boolean isImplicit() { + public boolean isSynthetic() { return true; } } From 3f5517e75d6599a27f77b55d4955b3d6154dc0b8 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Mon, 21 Oct 2024 18:51:47 +0530 Subject: [PATCH 37/63] [Reconciler][Sealed types] Extra errors in editor that go away on file save (#3130) * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3122 --- .../internal/compiler/ast/TypeReference.java | 3 - .../internal/compiler/lookup/ClassScope.java | 2 +- .../compiler/lookup/SourceTypeBinding.java | 38 +++-- .../tests/model/SealedTypeModelTests.java | 133 +++++++++++++++++- .../codeassist/CompletionElementNotifier.java | 4 +- .../compiler/SourceElementNotifier.java | 53 +++++-- .../compiler/SourceElementParser.java | 84 +---------- 7 files changed, 199 insertions(+), 118 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 c6639e7131f..954836a909c 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 @@ -556,9 +556,6 @@ && isTypeUseDeprecated(type, scope)) { public boolean isTypeReference() { return true; } -public boolean isSynthetic() { - 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 74ee28648a9..a650adb21d6 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 @@ -1273,7 +1273,7 @@ else if (subType.module() != sealedTypeModule) void connectPermittedTypes() { SourceTypeBinding sourceType = this.referenceContext.binding; - if (this.referenceContext.permittedTypes != null && (this.referenceContext.permittedTypes.length == 0 || !this.referenceContext.permittedTypes[0].isSynthetic())) { + if (this.referenceContext.permittedTypes != null) { sourceType.setPermittedTypes(Binding.NO_PERMITTED_TYPES); try { sourceType.tagBits |= TagBits.SealingTypeHierarchy; diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java index d436a36ba5c..881161fe30e 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java @@ -3062,6 +3062,22 @@ public RecordComponentBinding[] setComponents(RecordComponentBinding[] comps) { return this.permittedTypes = permittedTypes; } +private void setImplicitPermittedType(SourceTypeBinding permittedType) { + ReferenceBinding[] typesPermitted = this.permittedTypes(); + int sz = typesPermitted == null ? 0 : typesPermitted.length; + if (this.scope.referenceCompilationUnit() == permittedType.scope.referenceCompilationUnit()) { + if (sz == 0) { + typesPermitted = new ReferenceBinding[] { permittedType }; + } else { + System.arraycopy(typesPermitted, 0, typesPermitted = new ReferenceBinding[sz + 1], 0, sz); + typesPermitted[sz] = permittedType; + } + this.setPermittedTypes(typesPermitted); + } else if (sz == 0) { + this.setPermittedTypes(Binding.NO_PERMITTED_TYPES); + } +} + // Propagate writes to all annotated variants so the clones evolve along. public ReferenceBinding setSuperClass(ReferenceBinding superClass) { @@ -3075,8 +3091,7 @@ public ReferenceBinding setSuperClass(ReferenceBinding superClass) { annotatedType.superclass = superClass; } } - if (superClass != null && superClass.actualType() instanceof SourceTypeBinding sourceSuperType && sourceSuperType.isSealed() - && (sourceSuperType.scope.referenceContext.permittedTypes == null || (sourceSuperType.scope.referenceContext.permittedTypes.length > 0 && sourceSuperType.scope.referenceContext.permittedTypes[0].isSynthetic()))) { + if (superClass != null && superClass.actualType() instanceof SourceTypeBinding sourceSuperType && sourceSuperType.isSealed() && sourceSuperType.scope.referenceContext.permittedTypes == null) { sourceSuperType.setImplicitPermittedType(this); if (this.isAnonymousType() && superClass.isEnum()) this.modifiers |= ClassFileConstants.AccFinal; @@ -3084,22 +3099,6 @@ public ReferenceBinding setSuperClass(ReferenceBinding superClass) { return this.superclass = superClass; } -private void setImplicitPermittedType(SourceTypeBinding permittedType) { - ReferenceBinding[] typesPermitted = this.permittedTypes(); - int sz = typesPermitted == null ? 0 : typesPermitted.length; - if (this.scope.referenceCompilationUnit() == permittedType.scope.referenceCompilationUnit()) { - if (sz == 0) { - typesPermitted = new ReferenceBinding[] { permittedType }; - } else { - System.arraycopy(typesPermitted, 0, typesPermitted = new ReferenceBinding[sz + 1], 0, sz); - typesPermitted[sz] = permittedType; - } - this.setPermittedTypes(typesPermitted); - } else if (sz == 0) { - this.setPermittedTypes(Binding.NO_PERMITTED_TYPES); - } -} - // Propagate writes to all annotated variants so the clones evolve along. public ReferenceBinding [] setSuperInterfaces(ReferenceBinding [] superInterfaces) { @@ -3115,8 +3114,7 @@ private void setImplicitPermittedType(SourceTypeBinding permittedType) { } for (int i = 0, length = superInterfaces == null ? 0 : superInterfaces.length; i < length; i++) { ReferenceBinding superInterface = superInterfaces[i]; - if (superInterface.actualType() instanceof SourceTypeBinding sourceSuperType && sourceSuperType.isSealed() - && (sourceSuperType.scope.referenceContext.permittedTypes == null || (sourceSuperType.scope.referenceContext.permittedTypes.length > 0 && sourceSuperType.scope.referenceContext.permittedTypes[0].isSynthetic()))) { + if (superInterface.actualType() instanceof SourceTypeBinding sourceSuperType && sourceSuperType.isSealed() && sourceSuperType.scope.referenceContext.permittedTypes == null) { sourceSuperType.setImplicitPermittedType(this); } } 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 e4382202806..d4066dc6122 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 @@ -144,7 +144,7 @@ public void test004() throws Exception { createFile( "/SealedTypes/src/X.java", fileContent); ICompilationUnit unit = getCompilationUnit("/SealedTypes/src/X.java"); IType[] types = unit.getTypes(); - assertEquals("Incorret no of types", 3, types.length); + assertEquals("Incorrect no of types", 3, types.length); for (IType iType : types) { if (iType.getElementName().equals("I")) { assertTrue("modifier should contain sealed", iType.isSealed()); @@ -160,6 +160,137 @@ public void test004() throws Exception { deleteProject("SealedTypes"); } } + // Test implicitly permitted sub types in Source Type + public void test004_2() throws Exception { + String[] permitted = new String[] {"Maybe.Maybe1", "Maybe.Maybe2"}; + try { + IJavaProject project = createJavaProject("SealedTypes"); + project.open(null); + String fileContent = + """ + interface SuperInt {} + + abstract sealed class Maybe { + final class Maybe1 extends Maybe {} + final class Maybe2 extends Maybe implements SuperInt {} + } + + class Test { + + void testMaybe(Maybe maybe) { + if (maybe == null) return; + } + } + """; + + createFile( "/SealedTypes/src/X.java", fileContent); + ICompilationUnit unit = getCompilationUnit("/SealedTypes/src/X.java"); + IType[] types = unit.getTypes(); + assertEquals("Incorrect no of types", 3, types.length); + for (IType iType : types) { + if (iType.getElementName().equals("Maybe")) { + assertTrue("modifier should contain sealed", iType.isSealed()); + String[] permittedSubtypeNames = iType.getPermittedSubtypeNames(); + assertEquals("incorrect permitted sub types", permitted.length, permittedSubtypeNames.length); + for (int i = 0; i < permitted.length; i++) { + assertEquals("incorrect permitted sub type", permitted[i], permittedSubtypeNames[i]); + } + } + } + } + finally { + deleteProject("SealedTypes"); + } + } + + // Test implicitly permitted sub types in Source Type + public void test004_3() throws Exception { + String[] permitted = new String[] {"Maybe.Maybe1", "Maybe.Maybe2"}; + try { + IJavaProject project = createJavaProject("SealedTypes"); + project.open(null); + String fileContent = + """ + interface SuperInt {} + + sealed interface Maybe { + final class Maybe1 implements Maybe {} + non-sealed interface Maybe2 extends Maybe {} + } + + class Test { + + void testMaybe(Maybe maybe) { + if (maybe == null) return; + } + } + """; + + createFile( "/SealedTypes/src/X.java", fileContent); + ICompilationUnit unit = getCompilationUnit("/SealedTypes/src/X.java"); + IType[] types = unit.getTypes(); + assertEquals("Incorrect no of types", 3, types.length); + for (IType iType : types) { + if (iType.getElementName().equals("Maybe")) { + assertTrue("modifier should contain sealed", iType.isSealed()); + String[] permittedSubtypeNames = iType.getPermittedSubtypeNames(); + assertEquals("incorrect permitted sub types", permitted.length, permittedSubtypeNames.length); + for (int i = 0; i < permitted.length; i++) { + assertEquals("incorrect permitted sub type", permitted[i], permittedSubtypeNames[i]); + } + } + } + } + finally { + deleteProject("SealedTypes"); + } + } + + // Test implicitly permitted sub types in Source Type + public void test004_4() throws Exception { + String[] permitted = new String[] {"Maybe.Maybe1", "Maybe.Maybe2"}; + try { + IJavaProject project = createJavaProject("SealedTypes"); + project.open(null); + String fileContent = + """ + interface SuperInt {} + + abstract sealed class Maybe { + final class Maybe1 extends Maybe {} + final class Maybe2 extends Maybe implements SuperInt {} + } + + class Test { + + void testMaybe(Maybe maybe) { + if (maybe == null) return; + zork(); + } + } + """; + + createFile( "/SealedTypes/src/X.java", fileContent); + ICompilationUnit unit = getCompilationUnit("/SealedTypes/src/X.java"); + IType[] types = unit.getTypes(); + assertEquals("Incorrect no of types", 3, types.length); + for (IType iType : types) { + if (iType.getElementName().equals("Maybe")) { + assertTrue("modifier should contain sealed", iType.isSealed()); + String[] permittedSubtypeNames = iType.getPermittedSubtypeNames(); + assertEquals("incorrect permitted sub types", permitted.length, permittedSubtypeNames.length); + for (int i = 0; i < permitted.length; i++) { + assertEquals("incorrect permitted sub type", permitted[i], permittedSubtypeNames[i]); + } + } + } + } + finally { + deleteProject("SealedTypes"); + } + } + + // Test explicitly permitted sub types in binary public void test005() throws Exception { String[] permitted = new String[] {"p.X", "p.Y"}; diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionElementNotifier.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionElementNotifier.java index 9f157f1aec6..f95a60088c4 100644 --- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionElementNotifier.java +++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/CompletionElementNotifier.java @@ -201,8 +201,8 @@ protected void notifySourceElementRequestor(ImportReference importReference, boo } @Override - protected void notifySourceElementRequestor(TypeDeclaration typeDeclaration, boolean notifyTypePresence, TypeDeclaration declaringType, ImportReference currentPackage) { + protected void notifySourceElementRequestor(CompilationUnitDeclaration parsedUnit, TypeDeclaration typeDeclaration, boolean notifyTypePresence, TypeDeclaration declaringType, ImportReference currentPackage) { if (typeDeclaration instanceof CompletionOnAnnotationOfType) return; - super.notifySourceElementRequestor(typeDeclaration, notifyTypePresence, declaringType, currentPackage); + super.notifySourceElementRequestor(parsedUnit, typeDeclaration, notifyTypePresence, declaringType, currentPackage); } } diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/SourceElementNotifier.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/SourceElementNotifier.java index 68c5f243e25..fd4b6a4ff0c 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/SourceElementNotifier.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/compiler/SourceElementNotifier.java @@ -14,6 +14,7 @@ package org.eclipse.jdt.internal.compiler; import java.util.ArrayList; +import java.util.List; import java.util.Map; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ISourceElementRequestor.ParameterInfo; @@ -52,12 +53,12 @@ public TypeDeclaration peekDeclaringType() { } @Override public boolean visit(TypeDeclaration typeDeclaration, BlockScope scope) { - notifySourceElementRequestor(typeDeclaration, true, peekDeclaringType(), this.currentPackage); + notifySourceElementRequestor(null, typeDeclaration, true, peekDeclaringType(), this.currentPackage); return false; // don't visit members as this was done during notifySourceElementRequestor(...) } @Override public boolean visit(TypeDeclaration typeDeclaration, ClassScope scope) { - notifySourceElementRequestor(typeDeclaration, true, peekDeclaringType(), this.currentPackage); + notifySourceElementRequestor(null, typeDeclaration, true, peekDeclaringType(), this.currentPackage); return false; // don't visit members as this was done during notifySourceElementRequestor(...) } } @@ -136,8 +137,44 @@ protected char[] getSuperclassName(TypeDeclaration typeDeclaration) { TypeReference superclass = typeDeclaration.superclass; return superclass != null ? CharOperation.concatWith(superclass.getParameterizedTypeName(), '.') : null; } -protected char[][] getPermittedSubTypes(TypeDeclaration typeDeclaration) { - return extractTypeReferences(typeDeclaration.permittedTypes); +private void gatherPermittedTypesOf(TypeDeclaration potentialSubtype, TypeDeclaration sealedType, List list, char [] prefix) { + if (potentialSubtype != sealedType) { + char[][] qName = potentialSubtype.superclass == null ? null : potentialSubtype.superclass.getTypeName(); + if (qName != null && CharOperation.equals(qName[qName.length - 1], sealedType.name)) { + char [] subTypeName = CharOperation.concat(prefix, potentialSubtype.name, '.'); + list.add(subTypeName); + } + if (potentialSubtype.superInterfaces != null) { + for (TypeReference ref : potentialSubtype.superInterfaces) { + qName = ref.getTypeName(); + if (CharOperation.equals(qName[qName.length - 1], sealedType.name)) { + char [] subTypeName = CharOperation.concat(prefix, potentialSubtype.name, '.'); + list.add(subTypeName); + break; + } + } + } + } + if (potentialSubtype.memberTypes != null) { + for (int i = 0, size = potentialSubtype.memberTypes.length; i < size; i++) { + char [] prefixNow = CharOperation.concat(prefix, potentialSubtype.name, '.'); + gatherPermittedTypesOf(potentialSubtype.memberTypes[i], sealedType, list, prefixNow); + } + } +} +protected char[][] getPermittedSubTypes(CompilationUnitDeclaration parsedUnit, TypeDeclaration sealedType) { + + if (sealedType.permittedTypes != null) + return extractTypeReferences(sealedType.permittedTypes); + + // compute implicit permitted types on the fly. + List list = new ArrayList(); + if (parsedUnit != null) { // == null for local types. + for (TypeDeclaration type : parsedUnit.types) { + gatherPermittedTypesOf(type, sealedType, list, CharOperation.NO_CHAR); + } + } + return list.toArray(new char[list.size()][]); } protected char[][] getThrownExceptions(AbstractMethodDeclaration methodDeclaration) { return extractTypeReferences(methodDeclaration.thrownExceptions); @@ -455,7 +492,7 @@ public void notifySourceElementRequestor( notifySourceElementRequestor(importRef, false); } } else if (node instanceof TypeDeclaration && !new String(parsedUnit.getFileName()).endsWith(TypeConstants.MODULE_INFO_FILE_NAME_STRING)) { - notifySourceElementRequestor((TypeDeclaration)node, true, null, currentPackage); + notifySourceElementRequestor(parsedUnit, (TypeDeclaration)node, true, null, currentPackage); } else if (node instanceof ModuleDeclaration) { notifySourceElementRequestor(parsedUnit.moduleDeclaration); } @@ -640,7 +677,7 @@ protected void notifySourceElementRequestor(ModuleDeclaration moduleDeclaration) // } // //} -protected void notifySourceElementRequestor(TypeDeclaration typeDeclaration, boolean notifyTypePresence, TypeDeclaration declaringType, ImportReference currentPackage) { +protected void notifySourceElementRequestor(CompilationUnitDeclaration parsedUnit, TypeDeclaration typeDeclaration, boolean notifyTypePresence, TypeDeclaration declaringType, ImportReference currentPackage) { if (CharOperation.equals(TypeConstants.PACKAGE_INFO_NAME, typeDeclaration.name)) return; @@ -704,7 +741,7 @@ protected void notifySourceElementRequestor(TypeDeclaration typeDeclaration, boo typeInfo.extraFlags = ExtraFlags.getExtraFlags(typeDeclaration); typeInfo.node = typeDeclaration; if ((currentModifiers & ExtraCompilerModifiers.AccSealed) != 0) { - typeInfo.permittedSubtypes = getPermittedSubTypes(typeDeclaration); + typeInfo.permittedSubtypes = getPermittedSubTypes(parsedUnit, typeDeclaration); } switch (kind) { case TypeDeclaration.CLASS_DECL : @@ -776,7 +813,7 @@ protected void notifySourceElementRequestor(TypeDeclaration typeDeclaration, boo break; case 2 : memberTypeIndex++; - notifySourceElementRequestor(nextMemberDeclaration, true, null, currentPackage); + notifySourceElementRequestor(parsedUnit, nextMemberDeclaration, true, null, currentPackage); break; } } 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 34e348feed8..f20b91acc67 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 @@ -13,9 +13,7 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jdt.core.compiler.CategorizedProblem; @@ -26,11 +24,6 @@ import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; import org.eclipse.jdt.internal.compiler.lookup.Binding; -import org.eclipse.jdt.internal.compiler.lookup.BlockScope; -import org.eclipse.jdt.internal.compiler.lookup.ClassScope; -import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; -import org.eclipse.jdt.internal.compiler.lookup.Scope; -import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.problem.AbortCompilation; import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; @@ -975,77 +968,7 @@ protected QualifiedNameReference newQualifiedNameReference(char[][] tokens, long protected SingleNameReference newSingleNameReference(char[] source, long positions) { return new SingleNameReference(source, positions); } -private static class DummyTypeReference extends TypeReference { - char[] token; - DummyTypeReference(char[] name) { - this.token = name; - } - @Override - public TypeReference augmentTypeWithAdditionalDimensions(int additionalDimensions, - Annotation[][] additionalAnnotations, boolean isVarargs) { - // TODO Auto-generated method stub - return null; - } - @Override - public char[] getLastToken() { - return this.token; - } - @Override - protected TypeBinding getTypeBinding(Scope scope) { - return null; - } - @Override - public char[][] getTypeName() { - return new char[][] {this.token}; - } - @Override - public void traverse(ASTVisitor visitor, BlockScope scope) { - // TODO Auto-generated method stub - } - - @Override - public void traverse(ASTVisitor visitor, ClassScope scope) { - // TODO Auto-generated method stub - } - @Override - public StringBuilder printExpression(int indent, StringBuilder output) { - return output.append(this.token); - } - @Override - public String toString() { - return new String(this.token); - } - @Override - public boolean isSynthetic() { - return true; - } -} -private void processImplicitPermittedTypes(TypeDeclaration typeDecl, TypeDeclaration[] allTypes) { - if (typeDecl.permittedTypes == null && - (typeDecl.modifiers & ExtraCompilerModifiers.AccSealed) != 0) { - List list = new ArrayList(); - for (TypeDeclaration type : allTypes) { - if (type != typeDecl) { - char[][] qName = type.superclass == null ? null : type.superclass.getTypeName(); - if (qName != null && - CharOperation.equals(qName[qName.length -1], typeDecl.name)) { - list.add(new DummyTypeReference(type.name)); - } - if (type.superInterfaces != null) { - for (TypeReference ref : type.superInterfaces) { - qName = ref.getTypeName(); - if (CharOperation.equals(qName[qName.length -1], typeDecl.name)) { - list.add(new DummyTypeReference(type.name)); - break; - } - } - } - } - } - typeDecl.permittedTypes = list.toArray(new TypeReference[list.size()]); - } -} public CompilationUnitDeclaration parseCompilationUnit( ICompilationUnit unit, boolean fullParse, @@ -1060,12 +983,7 @@ public CompilationUnitDeclaration parseCompilationUnit( this.reportReferenceInfo = fullParse; CompilationResult compilationUnitResult = new CompilationResult(unit, 0, 0, this.options.maxProblemsPerUnit); parsedUnit = parse(unit, compilationUnitResult); - TypeDeclaration[] types = parsedUnit.types; - if (types != null) { - for (TypeDeclaration typeDecl : types) { - processImplicitPermittedTypes(typeDecl, types); - } - } + if (pm != null && pm.isCanceled()) throw new OperationCanceledException(Messages.operation_cancelled); if (this.scanner.recordLineSeparator) { From 251af63b944ec23fb1fa7f8fb775adf193db3cd4 Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Mon, 21 Oct 2024 17:48:53 +0200 Subject: [PATCH 38/63] [23] super reference disallowed in the context of a field access (#3131) + distinguish super vs. outer type for early constr. check fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3094 --- .../compiler/ast/QualifiedSuperReference.java | 9 +- .../regression/SuperAfterStatementsTest.java | 99 +++++++++++++++++++ 2 files changed, 106 insertions(+), 2 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/QualifiedSuperReference.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/QualifiedSuperReference.java index d747e21bfc6..04f2649ec63 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/QualifiedSuperReference.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/QualifiedSuperReference.java @@ -74,8 +74,13 @@ public TypeBinding resolveType(BlockScope scope) { ReferenceBinding enclosingReceiverType = scope.enclosingReceiverType(); // interface-qualified this selects a default method in a super interface, // but here we are only interested in supers of *enclosing instances*: - if (enclosingReceiverType != null && !enclosingReceiverType.isInterface() && scope.isInsideEarlyConstructionContext(enclosingReceiverType, false)) - scope.problemReporter().errorExpressionInEarlyConstructionContext(this); + if (enclosingReceiverType != null && !enclosingReceiverType.isInterface()) { + TypeBinding typeToCheck = (enclosingReceiverType.isCompatibleWith(this.resolvedType)) + ? enclosingReceiverType // cannot reference super of the current type + : this.resolvedType; // assumeably not a super but an outer type + if (scope.isInsideEarlyConstructionContext(typeToCheck, false)) + scope.problemReporter().errorExpressionInEarlyConstructionContext(this); + } return this.resolvedType = (this.currentCompatibleType.isInterface() ? this.currentCompatibleType : this.currentCompatibleType.superclass()); diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java index d3f1b7e2874..77140f3ae32 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java @@ -2182,4 +2182,103 @@ public class TestFlow { """; runner.runNegativeTest(); } + + public void testGH3094() { + Runner runner = new Runner(false); + runner.testFiles = new String[] { + "X.java", + """ + public class X { + class Nested extends X1 { + X1 xy; + class DeeplyNested extends NestedInX1 { + DeeplyNested(float f) { + Nested.super.x1.super(); // Error here + } + } + } + public static void main(String... args) { + Nested nest = new X().new Nested(); + nest.x1 = new X1(); + nest.new DeeplyNested(1.1f); + } + } + class X1 { + X1 x1; + class NestedInX1 {} + } + """ + }; + runner.runConformTest(); + } + + public void testGH3094_2() { + Runner runner = new Runner(false); + runner.testFiles = new String[] { + "X.java", + """ + public class X { + class Nested extends X1 { + X1 xy; + class DeeplyNested extends NestedInX1 { + DeeplyNested(float f) { + Nested.this.x1.super(); + } + } + } + public static void main(String... args) { + Nested nest = new X().new Nested(); + nest.x1 = new X1(); + nest.new DeeplyNested(1.1f); + } + } + class X1 { + X1 x1; + class NestedInX1 {} + } + """ + }; + runner.runConformTest(); + } + + public void testGH3094_3() { + Runner runner = new Runner(false); + runner.testFiles = new String[] { + "X.java", + """ + public class X { + class Nested extends X1 { + X1 xy; + Nested() { + class DeeplyNested extends NestedInX1 { + DeeplyNested(float f) { + Nested.this.x1.super(); + } + } + super(); + } + } + } + class X1 { + X1 x1; + class NestedInX1 {} + } + """ + }; + runner.expectedCompilerLog = + """ + ---------- + 1. WARNING in X.java (at line 5) + class DeeplyNested extends NestedInX1 { + ^^^^^^^^^^^^ + The type DeeplyNested is never used locally + ---------- + 2. ERROR in X.java (at line 7) + Nested.this.x1.super(); + ^^^^^^^^^^^^^^ + Cannot read field x1 in an early construction context + ---------- + """; + runner.runNegativeTest(); + } } From e000c5a1edf64907c16434fa6032aaa3647b37bb Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Tue, 22 Oct 2024 10:32:47 +0530 Subject: [PATCH 39/63] [Switch] default->null caused a building problem (#3136) * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3135 --- .../internal/compiler/ast/YieldStatement.java | 7 ++++++ .../compiler/codegen/OperandStack.java | 14 ++++++++++++ .../regression/SwitchPatternTest.java | 22 +++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/YieldStatement.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/YieldStatement.java index abe8805e653..c00cc8f3b7f 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/YieldStatement.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/YieldStatement.java @@ -152,6 +152,13 @@ public void generateCode(BlockScope currentScope, CodeStream codeStream) { assignment.generateCode(currentScope, codeStream); } else { this.expression.generateCode(currentScope, codeStream, this.switchExpression != null); + if (this.expression.resolvedType == TypeBinding.NULL) { + if (!this.switchExpression.resolvedType.isBaseType()) { + // no opcode called for to align the types, but we need to adjust the notion of type of TOS. + codeStream.operandStack.pop(TypeBinding.NULL); + codeStream.operandStack.push(this.switchExpression.resolvedType); + } + } } int pc = codeStream.position; // generation of code responsible for invoking the finally diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/codegen/OperandStack.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/codegen/OperandStack.java index 97ff7bf5f38..dd3cdf27308 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/codegen/OperandStack.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/codegen/OperandStack.java @@ -263,6 +263,20 @@ public void dup2_x2() { } } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (int i = 0, length = size(); i < length; i++) { + if (i != 0) + sb.append(", "); //$NON-NLS-1$ + TypeBinding type = this.stack.get(i); + sb.append(type.shortReadableName()); + } + sb.append("]\n"); //$NON-NLS-1$ + return sb.toString(); + } + public static class NullStack extends OperandStack { public NullStack() { diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java index 8db71faa97b..300b50edee8 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java @@ -9228,4 +9228,26 @@ public static void main(String[] args) { ""); } + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3135 + // [Switch] default->null caused a building problem. + public void testIssue3135() { + runConformTest( + new String[] { + "X.java", + """ + public class X { + public static void main(String[] args) { + int i = 3; + int[] arr = { 42, 2, 3 }; + System.out.println((switch (i) { + case 3 -> arr; + default -> null; // Replacing null with a non-null value can avoid this issue. + })[0]); + } + } + """ + }, + "42"); + } + } From f80c91058fe204fd3330be1844f94390012960bb Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Tue, 22 Oct 2024 12:43:17 +0530 Subject: [PATCH 40/63] Code review driven clean up of sealed types implementation (#3138) --- .../compiler/parser/RecoveredType.java | 25 +++++++++++-------- .../jdt/core/search/IJavaSearchConstants.java | 2 +- .../jdt/core/search/SearchPattern.java | 2 +- .../core/search/matching/MatchLocator.java | 15 ++--------- .../search/matching/MatchLocatorParser.java | 13 +++------- 5 files changed, 22 insertions(+), 35 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/RecoveredType.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/RecoveredType.java index 69b26b01c25..e0fb2107f70 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/RecoveredType.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/RecoveredType.java @@ -328,23 +328,26 @@ public int bodyEnd(){ if (this.bodyEnd == 0) return this.typeDeclaration.declarationSourceEnd; return this.bodyEnd; } -public boolean bodyStartsAtHeaderEnd(){ - if (this.typeDeclaration.superInterfaces == null){ - if (this.typeDeclaration.superclass == null){ - if(this.typeDeclaration.typeParameters == null) { - return this.typeDeclaration.bodyStart == this.typeDeclaration.sourceEnd+1; + +public boolean bodyStartsAtHeaderEnd() { + if (this.typeDeclaration.permittedTypes == null) { + if (this.typeDeclaration.superInterfaces == null) { + if (this.typeDeclaration.superclass == null) { + if (this.typeDeclaration.typeParameters == null) { + return this.typeDeclaration.bodyStart == this.typeDeclaration.sourceEnd + 1; + } else { + return this.typeDeclaration.bodyStart == this.typeDeclaration.typeParameters[this.typeDeclaration.typeParameters.length - 1].sourceEnd + 1; + } } else { - return this.typeDeclaration.bodyStart == this.typeDeclaration.typeParameters[this.typeDeclaration.typeParameters.length-1].sourceEnd+1; + return this.typeDeclaration.bodyStart == this.typeDeclaration.superclass.sourceEnd + 1; } } else { - return this.typeDeclaration.bodyStart == this.typeDeclaration.superclass.sourceEnd+1; + return this.typeDeclaration.bodyStart + == this.typeDeclaration.superInterfaces[this.typeDeclaration.superInterfaces.length - 1].sourceEnd + 1; } } else { - if (this.typeDeclaration.permittedTypes != null) - return this.typeDeclaration.bodyStart - == this.typeDeclaration.permittedTypes[this.typeDeclaration.permittedTypes.length-1].sourceEnd+1; return this.typeDeclaration.bodyStart - == this.typeDeclaration.superInterfaces[this.typeDeclaration.superInterfaces.length-1].sourceEnd+1; + == this.typeDeclaration.permittedTypes[this.typeDeclaration.permittedTypes.length - 1].sourceEnd + 1; } } /* diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/IJavaSearchConstants.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/IJavaSearchConstants.java index f88831918b3..be02fa86704 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/IJavaSearchConstants.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/IJavaSearchConstants.java @@ -493,7 +493,7 @@ public interface IJavaSearchConstants { int METHOD_REFERENCE_EXPRESSION = 0x10000000; /** - * Return only type references used as a permit type (Java 15) + * Return only type references used as a permit type (Java 17) *

* When this flag is set, only {@link TypeReferenceMatch} matches will be * returned. diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/SearchPattern.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/SearchPattern.java index d0fac3a6456..d79657ebae0 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/SearchPattern.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/core/search/SearchPattern.java @@ -1954,7 +1954,7 @@ public static SearchPattern createPattern(IJavaElement element, int limitTo) { * Return only method reference expressions (e.g. A :: foo). * * {@link IJavaSearchConstants#PERMITTYPE_TYPE_REFERENCE PERMITTYPE_TYPE_REFERENCE} - * Return only type references used as a permit type. + * Return only type references used as a permitted type. * * * diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java index e86877530f1..1bd7e0df46a 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocator.java @@ -3199,20 +3199,9 @@ protected void reportMatching(TypeDeclaration type, IJavaElement parent, int acc } } TypeReference[] permittedTypes = type.permittedTypes; - if (permittedTypes != null) { - for (int i = 0, l = permittedTypes.length; i < l; i++) { - reportMatchingSuperOrPermit(permittedTypes[i], enclosingElement, type.binding, nodeSet, matchedClassContainer); - TypeReference typeReference = type.permittedTypes[i]; - Annotation[][] annotations = typeReference != null ? typeReference.annotations : null; - if (annotations != null) { - for (Annotation[] annotation : annotations) { - if (annotation == null) continue; - reportMatching(annotation, enclosingElement, null, type.binding, nodeSet, matchedClassContainer, enclosesElement); - } - } - } + for (int i = 0, length = permittedTypes == null ? 0 : permittedTypes.length; i < length; i++) { + reportMatchingSuperOrPermit(permittedTypes[i], enclosingElement, type.binding, nodeSet, matchedClassContainer); } - } // filter out element not in hierarchy scope diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocatorParser.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocatorParser.java index c0941013693..e74e8675313 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocatorParser.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/MatchLocatorParser.java @@ -949,22 +949,17 @@ protected void consumeWildcardBoundsSuper() { } } -private void updatePatternLocaterMatch() { +@Override +protected void consumePermittedTypes() { + super.consumePermittedTypes(); if ((this.patternFineGrain & IJavaSearchConstants.PERMITTYPE_TYPE_REFERENCE) != 0) { TypeDeclaration td = (TypeDeclaration) this.astStack[this.astPtr]; - TypeReference[] permittedTypes = td.permittedTypes; - for (TypeReference pt : permittedTypes) { + for (TypeReference pt : td.permittedTypes) { this.patternLocator.match(pt, this.nodeSet); } } } -@Override -protected void consumePermittedTypes() { - super.consumePermittedTypes(); - updatePatternLocaterMatch(); -} - @Override protected TypeReference augmentTypeWithAdditionalDimensions(TypeReference typeRef, int additionalDimensions, Annotation [][] additionalAnnotations, boolean isVarargs) { TypeReference result = super.augmentTypeWithAdditionalDimensions(typeRef, additionalDimensions, additionalAnnotations, isVarargs); From ea94dfab25b781c3fca6de006f7318216aabe86b Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Tue, 22 Oct 2024 16:58:19 +0530 Subject: [PATCH 41/63] Code review driven incremental clean up of sealed types implementation (#3141) * Get rid of stale @noreference tags * Fix javadoc * Fix indentation problems * Align method names with JLS terminology * Reuse standard code patterns * Prevent further checks on a `iterator.remove()`d element * Get rid of type annotation handling code where no type annotations are legal --- .../jdt/internal/compiler/ClassFile.java | 2 +- .../compiler/ast/SwitchStatement.java | 23 ++++++++++--------- .../compiler/classfmt/ClassFileReader.java | 6 ++--- .../internal/compiler/env/IBinaryType.java | 6 ++--- .../compiler/lookup/BinaryTypeBinding.java | 13 +++-------- .../eclipse/jdt/internal/core/BinaryType.java | 1 - .../eclipse/jdt/internal/core/SourceType.java | 1 - 7 files changed, 21 insertions(+), 31 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ClassFile.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ClassFile.java index ae0a9f56079..354576a4d49 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ClassFile.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ClassFile.java @@ -6043,7 +6043,7 @@ public void initialize(SourceTypeBinding aType, ClassFile parentClassFile, boole if (aType.isAnonymousType()) { ReferenceBinding superClass = aType.superclass; if (superClass == null || !(superClass.isEnum() && superClass.isSealed())) - accessFlags &= ~ClassFileConstants.AccFinal; + accessFlags &= ~ClassFileConstants.AccFinal; } int finalAbstract = ClassFileConstants.AccFinal | ClassFileConstants.AccAbstract; if ((accessFlags & finalAbstract) == finalAbstract) { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java index a2af4daa373..e33f7e345bf 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java @@ -367,7 +367,7 @@ public boolean visit(TNode node) { } if (node.type instanceof ReferenceBinding ref && ref.isSealed()) { List allAllowedTypes = ref.getAllEnumerableReferenceTypes(); - this.covers &= isExhaustiveWithCaseTypes(allAllowedTypes, availableTypes); + this.covers &= caseElementsCoverSelectorType(allAllowedTypes, availableTypes); return this.covers; } this.covers = false; @@ -1456,7 +1456,7 @@ private boolean checkAndFlagDefaultSealed(BlockScope skope, CompilerOptions comp if (!(ref.isClass() || ref.isInterface() || ref.isTypeVariable() || ref.isIntersectionType())) return false; - if (!isExhaustiveWithCaseTypes(ref.getAllEnumerableReferenceTypes(), this.caseLabelElementTypes)) { + if (!caseElementsCoverSelectorType(ref.getAllEnumerableReferenceTypes(), this.caseLabelElementTypes)) { if (this instanceof SwitchExpression) // non-exhaustive switch expressions will be flagged later. return false; skope.problemReporter().enhancedSwitchMissingDefaultCase(this.expression); @@ -1470,7 +1470,7 @@ private boolean checkAndFlagDefaultRecord(BlockScope skope, CompilerOptions comp List allallowedTypes = new ArrayList<>(); allallowedTypes.add(ref); if (comps == null || comps.length == 0) { - if (!isExhaustiveWithCaseTypes(allallowedTypes, this.caseLabelElementTypes)) { + if (!caseElementsCoverSelectorType(allallowedTypes, this.caseLabelElementTypes)) { skope.problemReporter().enhancedSwitchMissingDefaultCase(this.expression); return true; } @@ -1490,7 +1490,7 @@ private boolean checkAndFlagDefaultRecord(BlockScope skope, CompilerOptions comp this.switchBits |= SwitchStatement.Exhaustive; return false; } - private boolean isExhaustiveWithCaseTypes(List allAllowedTypes, List listedTypes) { + private boolean caseElementsCoverSelectorType(List allAllowedTypes, List listedTypes) { Iterator iterator = allAllowedTypes.iterator(); while (iterator.hasNext()) { ReferenceBinding next = iterator.next(); @@ -1502,18 +1502,19 @@ private boolean isExhaustiveWithCaseTypes(List allAllowedTypes iterator.remove(); continue; } - for (TypeBinding type : listedTypes) { - // permits specifies classes, not parameterizations - if (next.erasure().isCompatibleWith(type.erasure())) { - iterator.remove(); - break; - } - } if (next.isEnum()) { int constantCount = this.otherConstants == null ? 0 : this.otherConstants.length; Set unenumeratedConstants = unenumeratedConstants(next, constantCount); if (unenumeratedConstants.size() == 0) { iterator.remove(); + continue; + } + } + for (TypeBinding type : listedTypes) { + // permits specifies classes, not parameterizations + if (next.erasure().isCompatibleWith(type.erasure())) { + iterator.remove(); + break; } } } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java index 7173a363b8d..d93ad08048c 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java @@ -528,9 +528,7 @@ public ClassFileReader(byte[] classFileBytes, char[] fileName, boolean fullyInit offset += 2; this.permittedSubtypesNames = new char[this.permittedSubtypesCount][]; for (int j = 0; j < this.permittedSubtypesCount; j++) { - utf8Offset = - this.constantPoolOffsets[u2At(this.constantPoolOffsets[u2At(offset)] + 1)]; - this.permittedSubtypesNames[j] = CharDeduplication.intern(utf8At(utf8Offset + 3, u2At(utf8Offset + 1))); + this.permittedSubtypesNames[j] = CharDeduplication.intern(getConstantClassNameAt(u2At(offset))); offset += 2; } } @@ -1113,7 +1111,7 @@ && hasStructuralTypeAnnotationChanges(getTypeAnnotations(), newClassFile.getType return true; } - // permitted sub-types + // permitted subtypes char[][] newPermittedSubtypesNames = newClassFile.getPermittedSubtypesNames(); if (this.permittedSubtypesNames != newPermittedSubtypesNames) { int newPermittedSubtypesLength = newPermittedSubtypesNames == null ? 0 : newPermittedSubtypesNames.length; diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/env/IBinaryType.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/env/IBinaryType.java index fdfb8b64aa0..cce631129c9 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/env/IBinaryType.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/env/IBinaryType.java @@ -94,11 +94,11 @@ public interface IBinaryType extends IGenericType, IBinaryInfo { char[][] getInterfaceNames(); /** - * Answer the unresolved names of the receiver's permitted sub types + * Answer the unresolved names of the receiver's permitted subtypes in the + * class file format as specified in section 4.2 of the Java 2 VM spec * or null if the array is empty. * - * A name is a simple name or a qualified, dot separated name. - * For example, Hashtable or java.util.Hashtable. + * For example, java.lang.String is java/lang/String. */ default char[][] getPermittedSubtypesNames() { return null; diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java index 8cd977bb712..e5f6ffa46a2 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java @@ -567,12 +567,6 @@ void cachePartsFrom(IBinaryType binaryType, boolean needFieldsAndMethods) { break; } } - for (TypeBinding permsub : this.permittedTypes) { - if (permsub.hasNullTypeAnnotations()) { - this.externalAnnotationStatus = ExternalAnnotationStatus.TYPE_IS_ANNOTATED; - break; - } - } } } @@ -2558,9 +2552,8 @@ public ReferenceBinding[] permittedTypes() { return this.permittedTypes = this.prototype.permittedTypes(); } for (int i = this.permittedTypes.length; --i >= 0;) - this.permittedTypes[i] = (ReferenceBinding) resolveType(this.permittedTypes[i], this.environment, false); + this.permittedTypes[i] = (ReferenceBinding) resolveType(this.permittedTypes[i], this.environment, false); // re-resolution seems harmless - // Note: unlike for superinterfaces() hierarchy check not required here since these are subtypes return this.permittedTypes; } @Override @@ -2635,13 +2628,13 @@ public String toString() { if (this.permittedTypes != Binding.NO_PERMITTED_TYPES) { buffer.append("\n\tpermits : "); //$NON-NLS-1$ for (int i = 0, length = this.permittedTypes.length; i < length; i++) { - if (i > 0) + if (i > 0) buffer.append(", "); //$NON-NLS-1$ buffer.append((this.permittedTypes[i] != null) ? this.permittedTypes[i].debugName() : "NULL TYPE"); //$NON-NLS-1$ } } } else { - buffer.append("NULL PERMITTEDSUBTYPES"); //$NON-NLS-1$ + buffer.append("NULL PERMITTED SUBTYPES"); //$NON-NLS-1$ } if (this.enclosingType != null) { diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java index afce48bc719..17a149aa1a3 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java @@ -770,7 +770,6 @@ public boolean isRecord() throws JavaModelException { } /** * @see IType#isSealed() - * @noreference This method is not intended to be referenced by clients as it is a part of Java preview feature. */ @Override public boolean isSealed() throws JavaModelException { diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SourceType.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SourceType.java index a16c96a8103..0d8dc47af17 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SourceType.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SourceType.java @@ -705,7 +705,6 @@ public boolean isRecord() throws JavaModelException { } /** * @see IType#isSealed() - * @noreference This method is not intended to be referenced by clients as it is a part of Java preview feature. */ @Override public boolean isSealed() throws JavaModelException { From 01f090ed5efde65d58c5154ab911385d5f82be44 Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Tue, 22 Oct 2024 22:27:00 +0200 Subject: [PATCH 42/63] [23] anonymous creation in super call allowed? (#3134) For allocation expressions: + only check enclosing types + but never travel out past static / local types Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3132 --- .../compiler/ast/AllocationExpression.java | 8 ++- .../jdt/internal/compiler/lookup/Scope.java | 2 + .../regression/SuperAfterStatementsTest.java | 49 +++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java index edead604e46..fdd29789637 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java @@ -546,7 +546,13 @@ public TypeBinding resolveType(BlockScope scope) { protected void checkEarlyConstructionContext(BlockScope scope) { if (JavaFeature.FLEXIBLE_CONSTRUCTOR_BODIES.isSupported(scope.compilerOptions()) && this.type != null && this.type.resolvedType instanceof ReferenceBinding currentType) { - TypeBinding uninitialized = scope.getMatchingUninitializedType(currentType, !currentType.isLocalType()); + // only enclosing types of non-static member types are relevant + if (currentType.isStatic() || currentType.isLocalType()) + return; + currentType = currentType.enclosingType(); + if (currentType == null) + return; + TypeBinding uninitialized = scope.getMatchingUninitializedType(currentType, true); if (uninitialized != null) scope.problemReporter().allocationInEarlyConstructionContext(this, this.resolvedType, uninitialized); } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java index 24e49f7f2b5..d91b62936ae 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java @@ -5756,6 +5756,8 @@ public TypeBinding getMatchingUninitializedType(TypeBinding targetClass, boolean || (currentTarget instanceof ReferenceBinding currentRefBind && !currentRefBind.hasEnclosingInstanceContext())) { break; } + if (currentTarget.isStatic() || currentTarget.isLocalType()) + break; currentTarget = currentTarget.enclosingType(); } currentEnclosing = currentEnclosing.parent.classScope(); diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java index 77140f3ae32..06f977ab067 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java @@ -2281,4 +2281,53 @@ class DeeplyNested extends NestedInX1 { """; runner.runNegativeTest(); } + + public void testGH3132() { + Runner runner = new Runner(); + runner.testFiles = new String[] { + "X.java", + """ + public class X { + class Nested { + Nested(Object o) {} + } + class AnotherNested extends Nested { + AnotherNested() { + super(new Object() { // Cannot instantiate class new Object(){} in an early construction context of class X.AnotherNested + }); + } + } + public static void main(String... args) { + new X().new AnotherNested(); + } + } + """ + }; + runner.runConformTest(); + } + + public void testGH3132_2() { + Runner runner = new Runner(); + runner.testFiles = new String[] { + "X.java", + """ + class O {} // demonstrates the the bug was not specific to j.l.Object + public class X { + class Nested extends O { + Nested(Object o) {} + } + class AnotherNested extends Nested { + AnotherNested() { + super(new O() { + }); + } + } + public static void main(String... args) { + new X().new AnotherNested(); + } + } + """ + }; + runner.runConformTest(); + } } From 3a5cadce737047bc2a89e15cbb24ea76cf6714a6 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Wed, 23 Oct 2024 14:40:10 +0530 Subject: [PATCH 43/63] Code review driven incremental cleanup of sealed types implementation (#3145) * Get rid of dead code * Adopt descriptive method names * Overhaul switch block exhaustiveness checking * Fix defect that classifies switch expressions as enhanced switch statement * Add missing restricted keyword in JavaFeature * Eliminate unncessary feature level checks * Correct wrong diagnostics expectations in test files --- .../internal/compiler/ast/CaseStatement.java | 2 + .../compiler/ast/SwitchExpression.java | 4 +- .../compiler/ast/SwitchStatement.java | 191 +++++++----------- .../internal/compiler/impl/JavaFeature.java | 2 +- .../compiler/lookup/ReferenceBinding.java | 7 +- .../internal/compiler/lookup/TypeBinding.java | 4 + .../compiler/lookup/TypeConstants.java | 1 + .../jdt/internal/compiler/parser/Parser.java | 1 - .../regression/PrimitiveInPatternsTestSH.java | 6 +- .../regression/RecordPatternTest.java | 20 +- .../regression/SwitchPatternTest.java | 1 - .../tests/compiler/regression/SwitchTest.java | 9 +- 12 files changed, 99 insertions(+), 149 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java index 3de3773e464..49fb1ecfaff 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java @@ -371,6 +371,8 @@ private Constant resolveCasePattern(BlockScope scope, TypeBinding caseType, Type if (type != null) { constant = IntConstant.fromValue(switchStatement.constantIndex); switchStatement.caseLabelElements.add(e); + if (e instanceof RecordPattern) + switchStatement.containsRecordPatterns = true; if (isUnguarded) switchStatement.caseLabelElementTypes.add(type); diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchExpression.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchExpression.java index d31d16b2cb1..84545305744 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchExpression.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchExpression.java @@ -76,8 +76,8 @@ public ExpressionContext getExpressionContext() { return this.expressionContext; } @Override - protected boolean ignoreMissingDefaultCase(CompilerOptions compilerOptions, boolean isEnumSwitch) { - return isEnumSwitch; // mandatory error if not enum in switch expressions + protected boolean ignoreMissingDefaultCase(CompilerOptions compilerOptions) { + return true; } @Override protected void reportMissingEnumConstantCase(BlockScope upperScope, FieldBinding enumConstant) { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java index e33f7e345bf..b056f763969 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java @@ -87,6 +87,7 @@ public static record SingletonBootstrap(String id, char[] selector, char[] signa public int switchBits; public boolean containsPatterns; + public boolean containsRecordPatterns; public boolean containsNull; boolean nullProcessed = false; BranchLabel switchPatternRestartTarget; @@ -366,8 +367,7 @@ public boolean visit(TNode node) { } } if (node.type instanceof ReferenceBinding ref && ref.isSealed()) { - List allAllowedTypes = ref.getAllEnumerableReferenceTypes(); - this.covers &= caseElementsCoverSelectorType(allAllowedTypes, availableTypes); + this.covers &= caseElementsCoverSealedType(ref, availableTypes); return this.covers; } this.covers = false; @@ -824,13 +824,6 @@ public void generateCode(BlockScope currentScope, CodeStream codeStream) { int max = localKeysCopy[constantCount - 1]; int min = localKeysCopy[0]; if ((long) (constantCount * 2.5) > ((long) max - (long) min)) { - - // work-around 1.3 VM bug, if max>0x7FFF0000, must use lookup bytecode - // see http://dev.eclipse.org/bugs/show_bug.cgi?id=21557 - if (max > 0x7FFF0000 && currentScope.compilerOptions().complianceLevel < ClassFileConstants.JDK1_4) { - codeStream.lookupswitch(defaultLabel, this.constants, sortedIndexes, caseLabels); - - } else { codeStream.tableswitch( defaultLabel, min, @@ -839,7 +832,6 @@ public void generateCode(BlockScope currentScope, CodeStream codeStream) { sortedIndexes, this.constMapping, caseLabels); - } } else { codeStream.lookupswitch(defaultLabel, this.constants, sortedIndexes, caseLabels); } @@ -1110,11 +1102,9 @@ boolean isAllowedType(TypeBinding type) { @Override public void resolve(BlockScope upperScope) { try { - boolean isEnumSwitch = false; boolean isStringSwitch = false; TypeBinding expressionType = this.expression.resolveType(upperScope); CompilerOptions compilerOptions = upperScope.compilerOptions(); - boolean isEnhanced = checkAndSetEnhanced(upperScope, expressionType); if (expressionType != null) { this.expression.computeConversion(upperScope, expressionType, expressionType); checkType: { @@ -1130,15 +1120,11 @@ public void resolve(BlockScope upperScope) { if (expressionType.isCompatibleWith(TypeBinding.INT)) break checkType; } else if (expressionType.isEnum()) { - isEnumSwitch = true; - if (compilerOptions.complianceLevel < ClassFileConstants.JDK1_5) { - upperScope.problemReporter().incorrectSwitchType(this.expression, expressionType); // https://bugs.eclipse.org/bugs/show_bug.cgi?id=360317 - } break checkType; } else if (!this.containsPatterns && !this.containsNull && upperScope.isBoxingCompatibleWith(expressionType, TypeBinding.INT)) { this.expression.computeConversion(upperScope, TypeBinding.INT, expressionType); break checkType; - } else if (compilerOptions.complianceLevel >= ClassFileConstants.JDK1_7 && expressionType.id == TypeIds.T_JavaLangString) { + } else if (expressionType.id == TypeIds.T_JavaLangString) { if (this.containsPatterns || this.containsNull) { isStringSwitch = !JavaFeature.PATTERN_MATCHING_IN_SWITCH.isSupported(compilerOptions); this.isNonTraditional = true; @@ -1295,61 +1281,76 @@ && defaultFound && isExhaustive()) { } reportMixingCaseTypes(); - // check default case for non-enum switch: - boolean flagged = checkAndFlagDefaultSealed(upperScope, compilerOptions); - if (!flagged && this.defaultCase == null) { - if (ignoreMissingDefaultCase(compilerOptions, isEnumSwitch) && isEnumSwitch) { - upperScope.methodScope().hasMissingSwitchDefault = true; - } else { - if (!isEnumSwitch && !isExhaustive()) { + complainIfNotExhaustiveSwitch(upperScope, expressionType, compilerOptions); + + } finally { + if (this.scope != null) this.scope.enclosingCase = null; // no longer inside switch case block + } + } + private void complainIfNotExhaustiveSwitch(BlockScope upperScope, TypeBinding selectorType, CompilerOptions compilerOptions) { + + boolean isEnhanced = isEnhancedSwitch(upperScope, selectorType); + if (selectorType != null && selectorType.isEnum()) { + if (isEnhanced) + this.switchBits |= SwitchStatement.Exhaustive; // negated below if found otherwise + if (this.defaultCase != null && !compilerOptions.reportMissingEnumCaseDespiteDefault) + return; + + int constantCount = this.otherConstants == null ? 0 : this.otherConstants.length; + if (!((this.switchBits & TotalPattern) != 0) && + ((this.containsPatterns || this.containsNull) || + (constantCount >= this.caseCount && + constantCount != ((ReferenceBinding)selectorType).enumConstantCount()))) { + Set unenumeratedConstants = unenumeratedConstants((ReferenceBinding) selectorType, constantCount); + if (unenumeratedConstants.size() != 0) { + this.switchBits &= ~SwitchStatement.Exhaustive; + if (!(this.defaultCase != null && (this.defaultCase.bits & DocumentedCasesOmitted) != 0)) { if (isEnhanced) upperScope.problemReporter().enhancedSwitchMissingDefaultCase(this.expression); - else - upperScope.problemReporter().missingDefaultCase(this, isEnumSwitch, expressionType); - } - } - } - // Exhaustiveness check for enum switch - if (isEnumSwitch && compilerOptions.complianceLevel >= ClassFileConstants.JDK1_5) { - if (this.defaultCase == null || compilerOptions.reportMissingEnumCaseDespiteDefault) { - int constantCount = this.otherConstants == null ? 0 : this.otherConstants.length; - if (isEnhanced) - this.switchBits |= SwitchStatement.Exhaustive; // negated below if found otherwise - if (!((this.switchBits & TotalPattern) != 0) && - ((this.containsPatterns || this.containsNull) || - (constantCount >= this.caseCount && - constantCount != ((ReferenceBinding)expressionType).enumConstantCount()))) { - Set unenumeratedConstants = unenumeratedConstants((ReferenceBinding) expressionType, constantCount); - if (unenumeratedConstants.size() != 0) { - this.switchBits &= ~SwitchStatement.Exhaustive; - // enum constant did not get referenced from switch - boolean suppress = (this.defaultCase != null && (this.defaultCase.bits & DocumentedCasesOmitted) != 0); - if (!suppress) { - if (isEnhanced) - upperScope.problemReporter().enhancedSwitchMissingDefaultCase(this.expression); - else { - for (FieldBinding enumConstant : unenumeratedConstants) { - reportMissingEnumConstantCase(upperScope, enumConstant); - } - } + else { + for (FieldBinding enumConstant : unenumeratedConstants) { + reportMissingEnumConstantCase(upperScope, enumConstant); } } } } - if (this.defaultCase == null) { - if (ignoreMissingDefaultCase(compilerOptions, isEnumSwitch)) { - upperScope.methodScope().hasMissingSwitchDefault = true; - } else { - upperScope.problemReporter().missingDefaultCase(this, isEnumSwitch, expressionType); - } + } + + if (this.defaultCase == null) { + if (ignoreMissingDefaultCase(compilerOptions)) { + upperScope.methodScope().hasMissingSwitchDefault = true; + } else { + upperScope.problemReporter().missingDefaultCase(this, true, selectorType); } } - } finally { - if (this.scope != null) this.scope.enclosingCase = null; // no longer inside switch case block + return; + } + + if (isExhaustive() || this.defaultCase != null || selectorType == null) { + if (isEnhanced) + this.switchBits |= SwitchStatement.Exhaustive; + return; + } + + if (JavaFeature.PATTERN_MATCHING_IN_SWITCH.isSupported(compilerOptions) && selectorType.isSealed() && caseElementsCoverSealedType((ReferenceBinding) selectorType, this.caseLabelElementTypes)) { + this.switchBits |= SwitchStatement.Exhaustive; + return; + } + + if (selectorType.isRecordWithComponents() && this.containsRecordPatterns && caseElementsCoverRecordType(upperScope, compilerOptions, (ReferenceBinding) selectorType)) { + this.switchBits |= SwitchStatement.Exhaustive; + return; + } + + if (!isExhaustive()) { + if (isEnhanced) + upperScope.problemReporter().enhancedSwitchMissingDefaultCase(this.expression); + else + upperScope.problemReporter().missingDefaultCase(this, false, selectorType); } } - // Return the set of enumerations belonging to the selector enum type that are not listed in case statements. + // Return the set of enumerations belonging to the selector enum type that are NOT listed in case statements. private Set unenumeratedConstants(ReferenceBinding enumType, int constantCount) { FieldBinding[] enumFields = ((ReferenceBinding) enumType.erasure()).fields(); Set unenumerated = new HashSet<>(Arrays.asList(enumFields)); @@ -1389,9 +1390,9 @@ private boolean isExhaustive() { return (this.switchBits & SwitchStatement.Exhaustive) != 0; } - private boolean checkAndSetEnhanced(BlockScope upperScope, TypeBinding expressionType) { + private boolean isEnhancedSwitch(BlockScope upperScope, TypeBinding expressionType) { if (JavaFeature.PATTERN_MATCHING_IN_SWITCH.isSupported(upperScope.compilerOptions()) - && expressionType != null && !(this instanceof SwitchExpression )) { + && expressionType != null && !(this instanceof SwitchExpression)) { boolean acceptableType = !expressionType.isEnum(); switch (expressionType.id) { @@ -1415,7 +1416,7 @@ private boolean checkAndSetEnhanced(BlockScope upperScope, TypeBinding expressio return true; } } - if (expressionType != null && JavaFeature.PRIMITIVES_IN_PATTERNS.isSupported(upperScope.compilerOptions())) { + if (expressionType != null && !(this instanceof SwitchExpression) && JavaFeature.PRIMITIVES_IN_PATTERNS.isSupported(upperScope.compilerOptions())) { switch (expressionType.id) { case TypeIds.T_float: case TypeIds.T_double: @@ -1430,67 +1431,19 @@ private boolean checkAndSetEnhanced(BlockScope upperScope, TypeBinding expressio } return false; } - private boolean checkAndFlagDefaultSealed(BlockScope skope, CompilerOptions compilerOptions) { - if (this.defaultCase != null) { - this.switchBits |= SwitchStatement.Exhaustive; - return false; - } - if (this.expression.resolvedType instanceof ReferenceBinding ref && ref.isRecord()) { - boolean isRecordPattern = false; - for (Pattern pattern : this.caseLabelElements) { - if (pattern instanceof RecordPattern) { - isRecordPattern = true; - break; - } - } - if (isRecordPattern) - return checkAndFlagDefaultRecord(skope, compilerOptions, ref); - } - boolean checkSealed = JavaFeature.SEALED_CLASSES.isSupported(compilerOptions) - && JavaFeature.PATTERN_MATCHING_IN_SWITCH.isSupported(compilerOptions) - && this.expression.resolvedType instanceof ReferenceBinding - && this.expression.resolvedType.isSealed(); - - if (!checkSealed) return false; - ReferenceBinding ref = (ReferenceBinding) this.expression.resolvedType; - if (!(ref.isClass() || ref.isInterface() || ref.isTypeVariable() || ref.isIntersectionType())) - return false; - if (!caseElementsCoverSelectorType(ref.getAllEnumerableReferenceTypes(), this.caseLabelElementTypes)) { - if (this instanceof SwitchExpression) // non-exhaustive switch expressions will be flagged later. - return false; - skope.problemReporter().enhancedSwitchMissingDefaultCase(this.expression); - return true; - } - this.switchBits |= SwitchStatement.Exhaustive; - return false; - } - private boolean checkAndFlagDefaultRecord(BlockScope skope, CompilerOptions compilerOptions, ReferenceBinding ref) { - RecordComponentBinding[] comps = ref.components(); - List allallowedTypes = new ArrayList<>(); - allallowedTypes.add(ref); - if (comps == null || comps.length == 0) { - if (!caseElementsCoverSelectorType(allallowedTypes, this.caseLabelElementTypes)) { - skope.problemReporter().enhancedSwitchMissingDefaultCase(this.expression); - return true; - } - return false; - } - // non-zero components - RNode head = new RNode(ref); + private boolean caseElementsCoverRecordType(BlockScope skope, CompilerOptions compilerOptions, ReferenceBinding recordType) { + RNode head = new RNode(recordType); for (Pattern pattern : this.caseLabelElements) { head.addPattern(pattern); } CoverageCheckerVisitor ccv = new CoverageCheckerVisitor(); head.traverse(ccv); - if (!ccv.covers) { - skope.problemReporter().enhancedSwitchMissingDefaultCase(this.expression); - return true; // not exhaustive, error flagged - } - this.switchBits |= SwitchStatement.Exhaustive; - return false; + return ccv.covers; } - private boolean caseElementsCoverSelectorType(List allAllowedTypes, List listedTypes) { + + private boolean caseElementsCoverSealedType(ReferenceBinding sealedType, List listedTypes) { + List allAllowedTypes = sealedType.getAllEnumerableReferenceTypes(); Iterator iterator = allAllowedTypes.iterator(); while (iterator.hasNext()) { ReferenceBinding next = iterator.next(); @@ -1554,7 +1507,7 @@ private void addSecretPatternSwitchVariables(BlockScope upperScope) { protected void reportMissingEnumConstantCase(BlockScope upperScope, FieldBinding enumConstant) { upperScope.problemReporter().missingEnumConstantCase(this, enumConstant); } - protected boolean ignoreMissingDefaultCase(CompilerOptions compilerOptions, boolean isEnumSwitch) { + protected boolean ignoreMissingDefaultCase(CompilerOptions compilerOptions) { return compilerOptions.getSeverity(CompilerOptions.MissingDefaultCase) == ProblemSeverities.Ignore; } @Override diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/impl/JavaFeature.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/impl/JavaFeature.java index 2edf0038c14..09b4cbe1428 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/impl/JavaFeature.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/impl/JavaFeature.java @@ -56,7 +56,7 @@ public enum JavaFeature { SEALED_CLASSES(ClassFileConstants.JDK17, Messages.bind(Messages.sealed_types), - new char[][] {TypeConstants.SEALED, TypeConstants.PERMITS}, + new char[][] {TypeConstants.SEALED, TypeConstants.NON_SEALED, TypeConstants.PERMITS}, false), PATTERN_MATCHING_IN_SWITCH(ClassFileConstants.JDK21, Messages.bind(Messages.pattern_matching_switch), diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java index a0a9cc5e1d1..5b2d5017050 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java @@ -65,7 +65,6 @@ import org.eclipse.jdt.internal.compiler.ast.NullAnnotationMatching; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; -import org.eclipse.jdt.internal.compiler.impl.JavaFeature; import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; @@ -2368,10 +2367,8 @@ public MethodBinding getSingleAbstractMethod(Scope scope, boolean replaceWildcar return this.singleAbstractMethod[index]; } else { this.singleAbstractMethod = new MethodBinding[2]; - // Sec 9.8 of sealed preview - A functional interface is an interface that is not declared sealed... - if (JavaFeature.SEALED_CLASSES.isSupported(scope.compilerOptions()) - && this.isSealed()) - return this.singleAbstractMethod[index] = samProblemBinding; + if (this.isSealed()) + return this.singleAbstractMethod[index] = samProblemBinding; // JLS 9.8 } if (this.compoundName != null) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java index 808d5b34985..c7479f912f0 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java @@ -703,6 +703,10 @@ public boolean isRecord() { return false; } +public boolean isRecordWithComponents() { // do records without components make sense ??! + return isRecord() && components() instanceof RecordComponentBinding [] components && components.length > 0; +} + /* Answer true if the receiver type can be assigned to the argument type (right) */ public boolean isCompatibleWith(TypeBinding right) { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java index f9e2d8a59b6..2a72d85fa51 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java @@ -130,6 +130,7 @@ public interface TypeConstants { // JEP 360 Sealed char[] PERMITS = "permits".toCharArray(); //$NON-NLS-1$ char[] SEALED = "sealed".toCharArray(); //$NON-NLS-1$ + char[] NON_SEALED = "non-sealed".toCharArray(); //$NON-NLS-1$ String KEYWORD_EXTENDS = "extends"; //$NON-NLS-1$ String IMPLEMENTS = "implements"; //$NON-NLS-1$ diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java index 6c32f715eac..6b07efd14d8 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java @@ -9936,7 +9936,6 @@ protected void consumeToken(int type) { pushOnIntStack(this.scanner.startPosition); break; case TokenNameRestrictedIdentifierpermits: - problemReporter().validateJavaFeatureSupport(JavaFeature.SEALED_CLASSES, this.scanner.startPosition,this.scanner.currentPosition - 1); pushOnIntStack(this.scanner.startPosition); break; case TokenNamecase : diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PrimitiveInPatternsTestSH.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PrimitiveInPatternsTestSH.java index 3fd75e95630..418e0baef13 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PrimitiveInPatternsTestSH.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PrimitiveInPatternsTestSH.java @@ -1919,7 +1919,7 @@ static int m1(boolean b) { 1. ERROR in X.java (at line 3) return switch (b) { ^ - An enhanced switch statement should be exhaustive; a default label expected + A switch expression should have a default case ---------- """); } @@ -2116,12 +2116,12 @@ static double m2(long l) { 1. ERROR in X.java (at line 3) return switch(l) { ^ - An enhanced switch statement should be exhaustive; a default label expected + A switch expression should have a default case ---------- 2. ERROR in X.java (at line 9) return switch(l) { ^ - An enhanced switch statement should be exhaustive; a default label expected + A switch expression should have a default case ---------- """); } diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordPatternTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordPatternTest.java index 33937c7e4ea..aa3198e6fa4 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordPatternTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordPatternTest.java @@ -2669,7 +2669,7 @@ public void testRecPatExhaust002() { "1. ERROR in X.java (at line 12)\n" + " return switch (box) { // Not Exhaustive!\n" + " ^^^\n" + - "An enhanced switch statement should be exhaustive; a default label expected\n" + + "A switch expression should have a default case\n" + "----------\n"); } public void testRecPatExhaust003() { @@ -2753,7 +2753,7 @@ public void testRecPatExhaust004() { "1. ERROR in X.java (at line 16)\n" + " return switch (box) { // Not Exhaustive!\n" + " ^^^\n" + - "An enhanced switch statement should be exhaustive; a default label expected\n" + + "A switch expression should have a default case\n" + "----------\n"); } public void testRecPatExhaust005() { @@ -2816,7 +2816,7 @@ public void testRecPatExhaust006() { "1. ERROR in X.java (at line 11)\n" + " return switch (box) { // Not Exhaustive!\n" + " ^^^\n" + - "An enhanced switch statement should be exhaustive; a default label expected\n" + + "A switch expression should have a default case\n" + "----------\n"); } public void testRecPatExhaust007() { @@ -2849,7 +2849,7 @@ public void testRecPatExhaust007() { "1. ERROR in X.java (at line 12)\n" + " return switch (p) { // Not Exhaustive!\n" + " ^\n" + - "An enhanced switch statement should be exhaustive; a default label expected\n" + + "A switch expression should have a default case\n" + "----------\n"); } public void testRecPatExhaust008() { @@ -2883,7 +2883,7 @@ public void testRecPatExhaust008() { "1. ERROR in X.java (at line 12)\n" + " return switch (p) { // Not Exhaustive!\n" + " ^\n" + - "An enhanced switch statement should be exhaustive; a default label expected\n" + + "A switch expression should have a default case\n" + "----------\n"); } public void testRecPatExhaust009() { @@ -2961,7 +2961,7 @@ public void testRecPatExhaust011() { "1. ERROR in X.java (at line 12)\n" + " return switch (r) {\n" + " ^\n" + - "An enhanced switch statement should be exhaustive; a default label expected\n" + + "A switch expression should have a default case\n" + "----------\n"); } // implicit permitted - class @@ -2994,7 +2994,7 @@ public void testRecPatExhaust012() { "1. ERROR in X.java (at line 12)\n" + " return switch (r) {\n" + " ^\n" + - "An enhanced switch statement should be exhaustive; a default label expected\n" + + "A switch expression should have a default case\n" + "----------\n"); } // implicit permitted - class - the class C missing @@ -3028,7 +3028,7 @@ public void testRecPatExhaust013() { "1. ERROR in X.java (at line 12)\n" + " return switch (r) {\n" + " ^\n" + - "An enhanced switch statement should be exhaustive; a default label expected\n" + + "A switch expression should have a default case\n" + "----------\n"); } public void testRecPatExhaust014() { @@ -3061,7 +3061,7 @@ public void testRecPatExhaust014() { "1. ERROR in X.java (at line 11)\n" + " return switch (r) {\n" + " ^\n" + - "An enhanced switch statement should be exhaustive; a default label expected\n" + + "A switch expression should have a default case\n" + "----------\n"); } public void testRecPatExhaust015() { @@ -3177,7 +3177,7 @@ public void testRecPatExhaust018() { "1. ERROR in X.java (at line 12)\n" + " return switch (r) {\n" + " ^\n" + - "An enhanced switch statement should be exhaustive; a default label expected\n" + + "A switch expression should have a default case\n" + "----------\n"); } public void testRecordPatternTypeInference_012() { diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java index 300b50edee8..a802d08ba5e 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java @@ -29,7 +29,6 @@ public class SwitchPatternTest extends AbstractRegressionTest9 { // TESTS_NUMBERS = new int [] { 40 }; // TESTS_RANGE = new int[] { 1, -1 }; // TESTS_NAMES = new String[] { "testBug575053_002"}; -// TESTS_NAMES = new String[] { "testBug575571_1"}; } private static String previewLevel = "21"; diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchTest.java index 0135b00b79e..ca941fc187b 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchTest.java @@ -2569,17 +2569,12 @@ public void test383643() { " ^\n" + "p cannot be resolved to a variable\n" + "----------\n" + - "2. WARNING in X.java (at line 4)\n" + - " switch (p) {\n" + - " ^\n" + - "The switch statement should have a default case\n" + - "----------\n" + - "3. ERROR in X.java (at line 5)\n" + + "2. ERROR in X.java (at line 5)\n" + " case ONE:\n" + " ^^^\n" + "ONE cannot be resolved to a variable\n" + "----------\n" + - "4. ERROR in X.java (at line 8)\n" + + "3. ERROR in X.java (at line 8)\n" + " case TWO:\n" + " ^^^\n" + "TWO cannot be resolved to a variable\n" + From f395f50270076cbe32612dfbe1f2a825a9578531 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran Date: Wed, 23 Oct 2024 15:27:45 +0530 Subject: [PATCH 44/63] [Sealed types] Diagnostic can be more direct when a @FunctionalInterface is declared sealed * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3144 --- .../eclipse/jdt/core/compiler/IProblem.java | 4 ++++ .../compiler/ast/TypeDeclaration.java | 4 +++- .../compiler/problem/ProblemReporter.java | 14 +++++++++++++ .../compiler/problem/messages.properties | 1 + .../regression/CompilerInvocationTests.java | 2 ++ .../compiler/regression/SealedTypesTests.java | 20 +++++++++++++++++++ 6 files changed, 44 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java index dd89c862dc5..eae9c3536e3 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java @@ -2558,6 +2558,10 @@ public interface IProblem { * @deprecated problem no longer generated */ int SealedSuperTypeDisallowed = TypeRelated + 1867; + /** + * @since 3.40 + */ + int FunctionalInterfaceMayNotbeSealed = TypeRelated + 1868; /* Java17 Sealed types errors - end */ /** diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java index ef0fed6ca73..21e05c0a9e0 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java @@ -1362,7 +1362,9 @@ public void resolve() { this.scope.problemReporter().missingDeprecatedAnnotationForType(this); } if ((annotationTagBits & TagBits.AnnotationFunctionalInterface) != 0) { - if(!this.binding.isFunctionalInterface(this.scope)) { + if (this.binding.isSealed()) { + this.scope.problemReporter().functionalInterfaceMayNotBeSealed(this); + } else if (!this.binding.isFunctionalInterface(this.scope)) { this.scope.problemReporter().notAFunctionalInterface(this); } } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java index bfd57a0227b..5671366a02c 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java @@ -12308,6 +12308,7 @@ public void sealedClassNotDirectSuperClassOf(ReferenceBinding type, TypeReferenc reference.sourceStart, reference.sourceEnd); } + public void permittedTypeOutsideOfModule(ReferenceBinding permType, ReferenceBinding sealedType, ASTNode node, ModuleBinding moduleBinding) { String permTypeName = new String(permType.sourceName); String name = new String(sealedType.sourceName()); @@ -12319,6 +12320,7 @@ public void permittedTypeOutsideOfModule(ReferenceBinding permType, ReferenceBin node.sourceStart, node.sourceEnd); } + public void permittedTypeOutsideOfPackage(ReferenceBinding permType, ReferenceBinding sealedType, ASTNode node, PackageBinding packageBinding) { String permTypeName = new String(permType.sourceName); String name = new String(sealedType.sourceName()); @@ -12363,6 +12365,7 @@ public void localTypeMayNotBePermittedType(SourceTypeBinding type, TypeReference superclass.sourceStart, superclass.sourceEnd); } + public void anonymousClassCannotExtendSealedType(TypeReference reference, TypeBinding type) { this.handle( IProblem.SealedAnonymousClassCannotExtendSealedType, @@ -12371,6 +12374,17 @@ public void anonymousClassCannotExtendSealedType(TypeReference reference, TypeBi reference.sourceStart, reference.sourceEnd); } + +public void functionalInterfaceMayNotBeSealed(TypeDeclaration type) { + TypeBinding binding = type.binding; + this.handle( + IProblem.FunctionalInterfaceMayNotbeSealed, + new String[] {new String(binding.readableName()), }, + new String[] {new String(binding.shortReadableName()),}, + type.sourceStart, + type.sourceEnd); +} + public void StrictfpNotRequired(int sourceStart, int sourceEnd) { this.handle( IProblem.StrictfpNotRequired, diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties index 1aac0cda513..005270500fc 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties @@ -1150,6 +1150,7 @@ 1865 = An anonymous class cannot subclass a sealed type {0} ###[obsolete] 1866 = Sealed type {2} and sub type {0} in an unnamed module should be declared in the same package {1} ###[obsolete] 1867 = Sealed type {1} cannot be super type of {0} as it is from a different package or split package or module +1868 = A functional interface may not be declared sealed # Switch Patterns - Java 21 1900 = Local variable {0} referenced from a guard must be final or effectively final diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java index 5b6d42a991a..f0ecad9241e 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java @@ -1370,6 +1370,7 @@ class ProblemAttributes { expectedProblemAttributes.put("OperandStackSizeInappropriate", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); expectedProblemAttributes.put("IllegalModifierCombinationForType", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); expectedProblemAttributes.put("LambdaParameterIsNeverUsed", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE)); + expectedProblemAttributes.put("FunctionalInterfaceMayNotbeSealed", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); StringBuilder failures = new StringBuilder(); StringBuilder correctResult = new StringBuilder(70000); @@ -2506,6 +2507,7 @@ class ProblemAttributes { expectedProblemAttributes.put("OperandStackSizeInappropriate", SKIP); expectedProblemAttributes.put("IllegalModifierCombinationForType", SKIP); expectedProblemAttributes.put("LambdaParameterIsNeverUsed", SKIP); + expectedProblemAttributes.put("FunctionalInterfaceMayNotbeSealed", SKIP); Map constantNamesIndex = new HashMap(); diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java index e546688db04..db097a5a1cc 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java @@ -6414,4 +6414,24 @@ final class Y implements X {} "The type X may have only one modifier out of sealed, non-sealed, and final\n" + "----------\n"); } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3144 + // [Sealed types] Diagnostic can be more direct when a @FunctionalInterface is declared sealed + public void testIssue3144() { + runNegativeTest( + new String[] { + "I.java", + """ + @FunctionalInterface + public sealed interface I { void doit(); } + final class Y implements I { public void doit() {} } + """ + }, + "----------\n" + + "1. ERROR in I.java (at line 2)\n" + + " public sealed interface I { void doit(); }\n" + + " ^\n" + + "A functional interface may not be declared sealed\n" + + "----------\n"); + } } From 30d42d0268cb5d7cf2c724034b73232200c072db Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran Date: Wed, 23 Oct 2024 15:42:49 +0530 Subject: [PATCH 45/63] Simplify ConditionalExpression.resolveType() considering minimal compliance 1.8 * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3089 --- .../compiler/ast/ConditionalExpression.java | 76 ++++++------------- 1 file changed, 22 insertions(+), 54 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java index 2b3eda3b6b4..880d8286f45 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java @@ -36,7 +36,6 @@ import static org.eclipse.jdt.internal.compiler.ast.ExpressionContext.VANILLA_CONTEXT; import org.eclipse.jdt.internal.compiler.ASTVisitor; -import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.codegen.BranchLabel; import org.eclipse.jdt.internal.compiler.codegen.CodeStream; import org.eclipse.jdt.internal.compiler.flow.FlowContext; @@ -75,7 +74,6 @@ public class ConditionalExpression extends OperatorExpression implements IPolyEx private boolean isPolyExpression = false; private TypeBinding originalValueIfTrueType; private TypeBinding originalValueIfFalseType; - private boolean use18specifics; public ConditionalExpression(Expression condition, Expression valueIfTrue, Expression valueIfFalse) { this.condition = condition; @@ -473,17 +471,11 @@ public StringBuilder printExpressionNoParenthesis(int indent, StringBuilder outp public TypeBinding resolveType(BlockScope scope) { // JLS3 15.25 LookupEnvironment env = scope.environment(); - final long sourceLevel = scope.compilerOptions().sourceLevel; - boolean use15specifics = sourceLevel >= ClassFileConstants.JDK1_5; - this.use18specifics = sourceLevel >= ClassFileConstants.JDK1_8; - - if (this.use18specifics) { - if (this.expressionContext == ASSIGNMENT_CONTEXT || this.expressionContext == INVOCATION_CONTEXT) { - this.valueIfTrue.setExpressionContext(this.expressionContext); - this.valueIfTrue.setExpectedType(this.expectedType); - this.valueIfFalse.setExpressionContext(this.expressionContext); - this.valueIfFalse.setExpectedType(this.expectedType); - } + if (this.expressionContext == ASSIGNMENT_CONTEXT || this.expressionContext == INVOCATION_CONTEXT) { + this.valueIfTrue.setExpressionContext(this.expressionContext); + this.valueIfTrue.setExpectedType(this.expectedType); + this.valueIfFalse.setExpressionContext(this.expressionContext); + this.valueIfFalse.setExpectedType(this.expectedType); } if (this.constant != Constant.NotAConstant) { @@ -556,7 +548,7 @@ public TypeBinding resolveType(BlockScope scope) { TypeBinding valueIfTrueType = this.originalValueIfTrueType; TypeBinding valueIfFalseType = this.originalValueIfFalseType; - if (use15specifics && TypeBinding.notEquals(valueIfTrueType, valueIfFalseType)) { + if (TypeBinding.notEquals(valueIfTrueType, valueIfFalseType)) { if (valueIfTrueType.isBaseType()) { if (valueIfFalseType.isBaseType()) { // bool ? baseType : baseType @@ -667,48 +659,27 @@ public TypeBinding resolveType(BlockScope scope) { } // Type references (null null is already tested) if (valueIfTrueType.isBaseType() && valueIfTrueType != TypeBinding.NULL) { - if (use15specifics) { - valueIfTrueType = env.computeBoxingType(valueIfTrueType); - } else { - scope.problemReporter().conditionalArgumentsIncompatibleTypes(this, valueIfTrueType, valueIfFalseType); - return null; - } + valueIfTrueType = env.computeBoxingType(valueIfTrueType); } if (valueIfFalseType.isBaseType() && valueIfFalseType != TypeBinding.NULL) { - if (use15specifics) { - valueIfFalseType = env.computeBoxingType(valueIfFalseType); - } else { - scope.problemReporter().conditionalArgumentsIncompatibleTypes(this, valueIfTrueType, valueIfFalseType); - return null; - } + valueIfFalseType = env.computeBoxingType(valueIfFalseType); } - if (use15specifics) { - // >= 1.5 : LUB(operand types) must exist - TypeBinding commonType = null; - if (valueIfTrueType == TypeBinding.NULL) { - commonType = valueIfFalseType.withoutToplevelNullAnnotation(); // null on other branch invalidates any @NonNull - } else if (valueIfFalseType == TypeBinding.NULL) { - commonType = valueIfTrueType.withoutToplevelNullAnnotation(); // null on other branch invalidates any @NonNull - } else { - commonType = scope.lowerUpperBound(new TypeBinding[] { valueIfTrueType, valueIfFalseType }); - } - if (commonType != null) { - this.valueIfTrue.computeConversion(scope, commonType, this.originalValueIfTrueType); - this.valueIfFalse.computeConversion(scope, commonType, this.originalValueIfFalseType); - return this.resolvedType = commonType.capture(scope, this.sourceStart, this.sourceEnd); - } + + // >= 1.5 : LUB(operand types) must exist + TypeBinding commonType = null; + if (valueIfTrueType == TypeBinding.NULL) { + commonType = valueIfFalseType.withoutToplevelNullAnnotation(); // null on other branch invalidates any @NonNull + } else if (valueIfFalseType == TypeBinding.NULL) { + commonType = valueIfTrueType.withoutToplevelNullAnnotation(); // null on other branch invalidates any @NonNull } else { - // < 1.5 : one operand must be convertible to the other - if (valueIfFalseType.isCompatibleWith(valueIfTrueType)) { - this.valueIfTrue.computeConversion(scope, valueIfTrueType, this.originalValueIfTrueType); - this.valueIfFalse.computeConversion(scope, valueIfTrueType, this.originalValueIfFalseType); - return this.resolvedType = valueIfTrueType; - } else if (valueIfTrueType.isCompatibleWith(valueIfFalseType)) { - this.valueIfTrue.computeConversion(scope, valueIfFalseType, this.originalValueIfTrueType); - this.valueIfFalse.computeConversion(scope, valueIfFalseType, this.originalValueIfFalseType); - return this.resolvedType = valueIfFalseType; - } + commonType = scope.lowerUpperBound(new TypeBinding[] { valueIfTrueType, valueIfFalseType }); + } + if (commonType != null) { + this.valueIfTrue.computeConversion(scope, commonType, this.originalValueIfTrueType); + this.valueIfFalse.computeConversion(scope, commonType, this.originalValueIfFalseType); + return this.resolvedType = commonType.capture(scope, this.sourceStart, this.sourceEnd); } + scope.problemReporter().conditionalArgumentsIncompatibleTypes( this, valueIfTrueType, @@ -816,9 +787,6 @@ public boolean isFunctionalType() { @Override public boolean isPolyExpression() throws UnsupportedOperationException { - if (!this.use18specifics) - return false; - if (this.isPolyExpression) return true; From 638957c5baf9e1f269324b48c5c4da041ab78bb2 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran Date: Wed, 23 Oct 2024 15:49:21 +0530 Subject: [PATCH 46/63] [Internal] Code duplication in Scanner, TerminalTokens and ProblemReporter * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3143 --- .../compiler/problem/ProblemReporter.java | 85 ++----------------- 1 file changed, 5 insertions(+), 80 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java index 5671366a02c..b88117af3b8 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java @@ -5205,82 +5205,7 @@ public void isClassPathCorrect(char[][] wellKnownTypeName, CompilationUnitDeclar private boolean isIdentifier(int token) { return token == TerminalTokens.TokenNameIdentifier; } -private boolean isRestrictedIdentifier(int token) { - switch(token) { - case TerminalTokens.TokenNameRestrictedIdentifierYield: - case TerminalTokens.TokenNameRestrictedIdentifierrecord: - case TerminalTokens.TokenNameRestrictedIdentifiersealed: - case TerminalTokens.TokenNameRestrictedIdentifierpermits: - case TerminalTokens.TokenNameRestrictedIdentifierWhen: - return true; - default: return false; - } -} -private boolean isKeyword(int token) { - switch(token) { - case TerminalTokens.TokenNameabstract: - case TerminalTokens.TokenNameassert: - case TerminalTokens.TokenNamebyte: - case TerminalTokens.TokenNamebreak: - case TerminalTokens.TokenNameboolean: - case TerminalTokens.TokenNamecase: - case TerminalTokens.TokenNamechar: - case TerminalTokens.TokenNamecatch: - case TerminalTokens.TokenNameclass: - case TerminalTokens.TokenNamecontinue: - case TerminalTokens.TokenNamedo: - case TerminalTokens.TokenNamedouble: - case TerminalTokens.TokenNamedefault: - case TerminalTokens.TokenNameelse: - case TerminalTokens.TokenNameextends: - case TerminalTokens.TokenNamefor: - case TerminalTokens.TokenNamefinal: - case TerminalTokens.TokenNamefloat: - case TerminalTokens.TokenNamefalse: - case TerminalTokens.TokenNamefinally: - case TerminalTokens.TokenNameif: - case TerminalTokens.TokenNameint: - case TerminalTokens.TokenNameimport: - case TerminalTokens.TokenNameinterface: - case TerminalTokens.TokenNameimplements: - case TerminalTokens.TokenNameinstanceof: - case TerminalTokens.TokenNamelong: - case TerminalTokens.TokenNamenew: - case TerminalTokens.TokenNamenon_sealed: - case TerminalTokens.TokenNamenull: - case TerminalTokens.TokenNamenative: - case TerminalTokens.TokenNamepublic: - case TerminalTokens.TokenNamepackage: - case TerminalTokens.TokenNameprivate: - case TerminalTokens.TokenNameprotected: - case TerminalTokens.TokenNamereturn: - case TerminalTokens.TokenNameshort: - case TerminalTokens.TokenNamesuper: - case TerminalTokens.TokenNamestatic: - case TerminalTokens.TokenNameswitch: - case TerminalTokens.TokenNamestrictfp: - case TerminalTokens.TokenNamesynchronized: - case TerminalTokens.TokenNametry: - case TerminalTokens.TokenNamethis: - case TerminalTokens.TokenNametrue: - case TerminalTokens.TokenNamethrow: - case TerminalTokens.TokenNamethrows: - case TerminalTokens.TokenNametransient: - case TerminalTokens.TokenNamevoid: - case TerminalTokens.TokenNamevolatile: - case TerminalTokens.TokenNamewhile: - return true; - case TerminalTokens.TokenNameRestrictedIdentifierYield: - case TerminalTokens.TokenNameRestrictedIdentifierrecord: - case TerminalTokens.TokenNameRestrictedIdentifiersealed: - case TerminalTokens.TokenNameRestrictedIdentifierpermits: - case TerminalTokens.TokenNameRestrictedIdentifierWhen: - // making explicit - not a (restricted) keyword but restricted identifier. - //$FALL-THROUGH$ - default: - return false; - } -} + private boolean isLiteral(int token) { return Scanner.isLiteral(token); } @@ -7628,7 +7553,7 @@ public void parseError( String[] possibleTokens) { if (possibleTokens.length == 0) { //no suggestion available - if (isKeyword(currentToken)) { + if (Scanner.isKeyword(currentToken)) { String[] arguments = new String[] {new String(currentTokenSource)}; this.handle( IProblem.ParsingErrorOnKeywordNoSuggestion, @@ -7661,7 +7586,7 @@ public void parseError( list.append('"'); } - if (isKeyword(currentToken)) { + if (Scanner.isKeyword(currentToken)) { String[] arguments = new String[] {new String(currentTokenSource), list.toString()}; this.handle( IProblem.ParsingErrorOnKeyword, @@ -8498,14 +8423,14 @@ private void syntaxError( return; } String eTokenName; - if (isKeyword(currentKind) || + if (Scanner.isKeyword(currentKind) || isLiteral(currentKind) || isIdentifier(currentKind)) { eTokenName = new String(currentTokenSource); } else { eTokenName = errorTokenName; } - if (isRestrictedIdentifier(currentKind)) + if (TerminalTokens.isRestrictedKeyword(currentKind)) eTokenName = replaceIfSynthetic(eTokenName); String[] arguments; From b9dd9194baa0a134b296feccef1c43d3cd2b728e Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran Date: Wed, 23 Oct 2024 15:55:34 +0530 Subject: [PATCH 47/63] [Patterns] Suspect classification of when as a modifier * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3091 --- org.eclipse.jdt.core/.settings/.api_filters | 12 ++++++++++++ .../dom/org/eclipse/jdt/core/dom/ASTConverter.java | 3 --- .../dom/org/eclipse/jdt/core/dom/Modifier.java | 14 ++------------ 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/org.eclipse.jdt.core/.settings/.api_filters b/org.eclipse.jdt.core/.settings/.api_filters index 1579d46fc12..587f21bfd36 100644 --- a/org.eclipse.jdt.core/.settings/.api_filters +++ b/org.eclipse.jdt.core/.settings/.api_filters @@ -250,6 +250,12 @@ + + + + + + @@ -264,6 +270,12 @@ + + + + + + diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java index 7ab2da38686..f1959124dfb 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java @@ -5898,9 +5898,6 @@ protected void setModifiers(List modifiers, org.eclipse.jdt.internal.compiler.as case TerminalTokens.TokenNamenon_sealed: modifier = createModifier(Modifier.ModifierKeyword.NON_SEALED_KEYWORD); break; - case TerminalTokens.TokenNameRestrictedIdentifierWhen: - modifier = createModifier(Modifier.ModifierKeyword.WHEN_KEYWORD); - break; case TerminalTokens.TokenNameAT : // we have an annotation if (annotations != null && indexInAnnotations < annotations.length) { diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/Modifier.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/Modifier.java index 8c392199f6c..369c802eac9 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/Modifier.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/Modifier.java @@ -110,11 +110,7 @@ public static class ModifierKeyword { * @since 3.24 */ public static final ModifierKeyword SEALED_KEYWORD = new ModifierKeyword("sealed", SEALED);//$NON-NLS-1$ - /** - * @since 3.32 - * @noreference preview feature - */ - public static final ModifierKeyword WHEN_KEYWORD = new ModifierKeyword("when", WHEN);//$NON-NLS-1$ + /** * @since 3.24 */ @@ -348,13 +344,7 @@ public String toString() { * @since 3.24 */ public static final int NON_SEALED = 0x1000; - /** - * "when" modifier constant (bit mask). - * Applicable only to types. - * @since 3.32 - * @noreference preview feature - */ - public static final int WHEN = 0x2000; + /** * "module" modifier constant (bit mask). * Applicable only to imports. From 6e5d315899ed57a9fd5c6d9f783163d0f2edd888 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Wed, 23 Oct 2024 23:13:15 +0530 Subject: [PATCH 48/63] Regression tests for https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3039 (#3148) --- .../compiler/regression/SealedTypesTests.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java index db097a5a1cc..e7cfeb89efb 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java @@ -6434,4 +6434,87 @@ final class Y implements I { public void doit() {} } "A functional interface may not be declared sealed\n" + "----------\n"); } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3039 + // [Sealed types] Broken program crashes the compiler + public void testIssue3039() { + runNegativeTest( + new String[] { + "X.java", + """ + public sealed class X permits X.C { + private final static class C extends X implements I {} + } + + sealed interface I permits X.C {} + record R(X.C xc, R.C rc) { + private class C {} + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " sealed interface I permits X.C {}\n" + + " ^^^\n" + + "The type X.C is not visible\n" + + "----------\n" + + "2. ERROR in X.java (at line 6)\n" + + " record R(X.C xc, R.C rc) {\n" + + " ^^^\n" + + "The type X.C is not visible\n" + + "----------\n"); + } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3039 + // [Sealed types] Broken program crashes the compiler + public void testIssue3039_2() { + runNegativeTest( + new String[] { + "X.java", + """ + public interface X { + + static Integer get(T object) { + return switch (object) { + case A ignored -> 42; + default -> 42; + }; + } + + public abstract sealed interface I2 permits , AB { + } + + + final class AB implements I2 {} + + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 1)\n" + + " public interface X {\n" + + " ^\n" + + "Syntax error, insert \"}\" to complete InterfaceBody\n" + + "----------\n" + + "2. ERROR in X.java (at line 3)\n" + + " static Integer get(T object) {\n" + + " ^^\n" + + "I2 cannot be resolved to a type\n" + + "----------\n" + + "3. ERROR in X.java (at line 5)\n" + + " case A ignored -> 42;\n" + + " ^^^^^^^^^\n" + + "The Java feature 'Pattern Matching in Switch' is only available with source level 21 and above\n" + + "----------\n" + + "4. ERROR in X.java (at line 8)\n" + + " }\n" + + " ^\n" + + "Syntax error on token \"}\", delete this token\n" + + "----------\n" + + "5. ERROR in X.java (at line 10)\n" + + " public abstract sealed interface I2 permits , AB {\n" + + " ^^\n" + + "Syntax error on token \"I2\", permits expected after this token\n" + + "----------\n"); + } } From 983369c4b02463627745dfc36b9fd8c1cec8ac86 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Thu, 24 Oct 2024 09:00:38 +0530 Subject: [PATCH 49/63] [Sealed types] Regression in instanceof check for sealed generic classes (#3149) * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3121 https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3038 --- .../lookup/ParameterizedTypeBinding.java | 40 +--- .../compiler/regression/SealedTypesTests.java | 217 ++++++++++++++++++ 2 files changed, 227 insertions(+), 30 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java index 35d2a9fb398..a3e54cd2ebf 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java @@ -1110,33 +1110,18 @@ public boolean isRawSubstitution() { @Override public ReferenceBinding[] permittedTypes() { List permittedTypes = new ArrayList<>(); -NextPermittedType: for (ReferenceBinding pt : this.type.permittedTypes()) { - // Step 1: Gather all type variables that would need to be solved. - Map map = new HashMap<>(); - TypeBinding current = pt; - do { - if (current.kind() == Binding.GENERIC_TYPE) { - for (TypeVariableBinding tvb : current.typeVariables()) { - map.put(tvb, null); - } - } - current = current.enclosingType(); - } while (current != null); - - // Step 2: Collect substitutes - current = this; TypeBinding sooper = pt.findSuperTypeOriginatingFrom(this); + if (sooper == null || !sooper.isValidBinding() || sooper.isProvablyDistinct(this)) + continue; + TypeBinding current = this; + Map map = new HashMap<>(); do { - if (sooper.isParameterizedType()) { - if (current.isParameterizedType()) { - for (int i = 0, length = sooper.typeArguments().length; i < length; i++) { - TypeBinding t = sooper.typeArguments()[i]; - if (t instanceof TypeVariableBinding tvb) { - map.put(tvb, current.typeArguments()[i]); - } else if (TypeBinding.notEquals(t, this.typeArguments()[i])) { - continue NextPermittedType; - } + if (sooper.isParameterizedType() && current.isParameterizedType()) { + for (int i = 0, length = sooper.typeArguments().length; i < length; i++) { + TypeBinding t = sooper.typeArguments()[i]; + if (t instanceof TypeVariableBinding tvb) { + map.put(tvb, current.typeArguments()[i]); } } } @@ -1163,12 +1148,7 @@ public TypeBinding substitute(TypeVariableBinding typeVariable) { return retVal; } }; - - // Step 3: compute subtype with parameterizations if any. - pt = (ReferenceBinding) Scope.substitute(substitution, pt); - - if (pt.isCompatibleWith(this)) - permittedTypes.add(pt); + permittedTypes.add((ReferenceBinding) Scope.substitute(substitution, pt)); } return permittedTypes.toArray(new ReferenceBinding[0]); diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java index e7cfeb89efb..b8e84a44987 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java @@ -6517,4 +6517,221 @@ final class AB implements I2 {} "Syntax error on token \"I2\", permits expected after this token\n" + "----------\n"); } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3121 + // [Sealed types] Regression in instanceof check for sealed generic classes + public void testIssue3121() { + runNegativeTest( + new String[] { + "X.java", + """ + interface SuperInt {} + + abstract sealed class Maybe { + final class Maybe1 extends Maybe {} + final class Maybe2 extends Maybe implements SuperInt {} + } + + + abstract sealed class SurelyNot { + final class SurelyNot1 extends SurelyNot {} + final class SurelyNot2 extends SurelyNot {} + } + + abstract sealed class SurelyYes { + final class SurelyYes1 extends SurelyYes implements SuperInt {} + final class SurelyYes2 extends SurelyYes implements SuperInt {} + } + + class Test { + + void testMaybe(Maybe maybe, SurelyNot surelyNot, SurelyYes surelyYes) { + if (maybe == null || surelyNot == null || surelyYes == null) return; + if (maybe instanceof SuperInt sup) {} + if (surelyNot instanceof SuperInt sup) {} + if (surelyYes instanceof SuperInt sup) {} + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 24)\n" + + " if (surelyNot instanceof SuperInt sup) {}\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Incompatible conditional operand types SurelyNot and SuperInt\n" + + "----------\n"); + } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3121 + // [Sealed types] Regression in instanceof check for sealed generic classes + public void testIssue3121_2() { + runNegativeTest( + new String[] { + "X.java", + """ + interface SuperInt {} + + class Outer { + abstract sealed class Maybe { + final class Maybe1 extends Maybe {} + } + } + + class Test { + + void testMaybe(Outer.Maybe maybe) { + if (maybe instanceof SuperInt sup) {} + return null; + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 12)\n" + + " if (maybe instanceof SuperInt sup) {}\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Incompatible conditional operand types Outer.Maybe and SuperInt\n" + + "----------\n" + + "2. ERROR in X.java (at line 13)\n" + + " return null;\n" + + " ^^^^^^^^^^^^\n" + + "Void methods cannot return a value\n" + + "----------\n"); + } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3121 + // [Sealed types] Regression in instanceof check for sealed generic classes + public void testIssue3121_2_1() { + runNegativeTest( + new String[] { + "X.java", + """ + interface SuperInt {} + + class Outer { + abstract sealed class Maybe { + final class Maybe1 extends Maybe implements SuperInt {} + } + } + + class Test { + + void testMaybe(Outer.Maybe maybe) { + if (maybe instanceof SuperInt sup) {} + return null; + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 13)\n" + + " return null;\n" + + " ^^^^^^^^^^^^\n" + + "Void methods cannot return a value\n" + + "----------\n"); + } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3121 + // [Sealed types] Regression in instanceof check for sealed generic classes + public void testIssue3121_3() { + runNegativeTest( + new String[] { + "X.java", + """ + interface SuperInt {} + + class Outer { + abstract sealed class Maybe { + final class Maybe1 extends Outer.Maybe {} + } + } + + class Test { + + void testMaybe(Outer.Maybe maybe) { + if (maybe == null) return; + if (maybe instanceof SuperInt sup) {} + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 13)\n" + + " if (maybe instanceof SuperInt sup) {}\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Incompatible conditional operand types Outer.Maybe and SuperInt\n" + + "----------\n"); + } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3121 + // [Sealed types] Regression in instanceof check for sealed generic classes + public void testIssue3121_4() { + runNegativeTest( + new String[] { + "X.java", + """ + interface SuperInt {} + + class Outer { + abstract sealed class Maybe { + final class Maybe1 extends Outer.Maybe implements SuperInt {} + } + } + + class Test { + + void testMaybe(Outer.Maybe maybe) { + if (maybe == null) return; + if (maybe instanceof SuperInt sup) {} + return null; + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 14)\n" + + " return null;\n" + + " ^^^^^^^^^^^^\n" + + "Void methods cannot return a value\n" + + "----------\n"); + } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3121 + // [Sealed types] Regression in instanceof check for sealed generic classes + // NOTE: javac does not report error#1 but that looks like a defect + public void testIssue3121_5() { + runNegativeTest( + new String[] { + "X.java", + """ + interface SuperInt {} + + class Outer { + abstract sealed class Maybe { + final class Maybe1 extends Outer.Maybe implements SuperInt {} + } + } + + class Test { + + void testMaybe(Outer.Maybe maybe) { + if (maybe == null) return; + if (maybe instanceof SuperInt sup) {} + return null; + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 13)\n" + + " if (maybe instanceof SuperInt sup) {}\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Incompatible conditional operand types Outer.Maybe and SuperInt\n" + + "----------\n" + + "2. ERROR in X.java (at line 14)\n" + + " return null;\n" + + " ^^^^^^^^^^^^\n" + + "Void methods cannot return a value\n" + + "----------\n"); + } } From 05c3001e75858ed94c76856e4d47e1f3ff0334a6 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:17:08 +0530 Subject: [PATCH 50/63] [Sealed types] Extra and spurious error messages with faulty type sealing (#3150) * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3007 --- .../compiler/problem/ProblemReporter.java | 32 +++----- .../compiler/regression/SealedTypesTests.java | 76 +++++++++++++++++++ 2 files changed, 88 insertions(+), 20 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java index b88117af3b8..d1ac75e2e53 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java @@ -12222,16 +12222,12 @@ public void duplicatePermittedType(SourceTypeBinding type, TypeReference referen } public void sealedClassNotDirectSuperClassOf(ReferenceBinding type, TypeReference reference, SourceTypeBinding superType) { - this.handle( - IProblem.SealedNotDirectSuperClass, - new String[] { - new String(type.sourceName()), - new String(superType.readableName())}, - new String[] { - new String(type.sourceName()), - new String(superType.readableName())}, - reference.sourceStart, - reference.sourceEnd); + if ((type.tagBits & TagBits.HierarchyHasProblems) == 0 && (superType.tagBits & TagBits.HierarchyHasProblems) == 0) { + this.handle(IProblem.SealedNotDirectSuperClass, + new String[] { new String(type.sourceName()), new String(superType.readableName()) }, + new String[] { new String(type.sourceName()), new String(superType.readableName()) }, + reference.sourceStart, reference.sourceEnd); + } } public void permittedTypeOutsideOfModule(ReferenceBinding permType, ReferenceBinding sealedType, ASTNode node, ModuleBinding moduleBinding) { @@ -12266,16 +12262,12 @@ public void sealedTypeMissingPermits(SourceTypeBinding type, ASTNode node) { } public void sealedInterfaceNotDirectSuperInterfaceOf(ReferenceBinding type, TypeReference reference, SourceTypeBinding superType) { - this.handle( - IProblem.SealedNotDirectSuperInterface, - new String[] { - new String(type.sourceName()), - new String(superType.readableName())}, - new String[] { - new String(type.sourceName()), - new String(superType.readableName())}, - reference.sourceStart, - reference.sourceEnd); + if ((type.tagBits & TagBits.HierarchyHasProblems) == 0 && (superType.tagBits & TagBits.HierarchyHasProblems) == 0) { + this.handle(IProblem.SealedNotDirectSuperInterface, + new String[] { new String(type.sourceName()), new String(superType.readableName()) }, + new String[] { new String(type.sourceName()), new String(superType.readableName()) }, + reference.sourceStart, reference.sourceEnd); + } } public void localTypeMayNotBePermittedType(SourceTypeBinding type, TypeReference superclass, TypeBinding superTypeBinding) { diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java index b8e84a44987..79e82c2f64e 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java @@ -6734,4 +6734,80 @@ void testMaybe(Outer.Maybe maybe) { "Void methods cannot return a value\n" + "----------\n"); } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3007 + // [Sealed types] Extra and spurious error messages with faulty type sealing + public void testIssue3007() { + runNegativeTest( + new String[] { + "X.java", + """ + public sealed class X extends Y permits Y { + int yield () { + return this.yield(); + } + } + + sealed class Y extends X permits X, Z { + + } + + final class Z extends Y {} + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 1)\n" + + " public sealed class X extends Y permits Y {\n" + + " ^\n" + + "The hierarchy of the type X is inconsistent\n" + + "----------\n" + + "2. ERROR in X.java (at line 7)\n" + + " sealed class Y extends X permits X, Z {\n" + + " ^\n" + + "Cycle detected: a cycle exists in the type hierarchy between Y and X\n" + + "----------\n" + + "3. ERROR in X.java (at line 11)\n" + + " final class Z extends Y {}\n" + + " ^\n" + + "The hierarchy of the type Z is inconsistent\n" + + "----------\n"); + } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3007 + // [Sealed types] Extra and spurious error messages with faulty type sealing + public void testIssue3007_2() { + runNegativeTest( + new String[] { + "X.java", + """ + public class X extends Y { + int yield () { + return this.yield(); + } + } + + class Y extends X { + + } + + final class Z extends Y {} + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 1)\n" + + " public class X extends Y {\n" + + " ^\n" + + "The hierarchy of the type X is inconsistent\n" + + "----------\n" + + "2. ERROR in X.java (at line 7)\n" + + " class Y extends X {\n" + + " ^\n" + + "Cycle detected: a cycle exists in the type hierarchy between Y and X\n" + + "----------\n" + + "3. ERROR in X.java (at line 11)\n" + + " final class Z extends Y {}\n" + + " ^\n" + + "The hierarchy of the type Z is inconsistent\n" + + "----------\n"); + } } From 55f53bd9cefcfd44e66fa91e209fc9216a816a94 Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Thu, 24 Oct 2024 19:17:14 +0200 Subject: [PATCH 51/63] [Switch][Record Patterns] Unchecked conversion error missing from ECJ (#3069) check all patterns via isApplicable() + enclosing instanceof and case stmt are responsible for its invocation + make isApplicable() more capable to issue the desired problems + let problem reporter see the error location to select problemID + more renaming for clarity + fix regression by more complete propagation of outerExpressionType fix "left" and "right" types in error reporting Test changes: + compatibility errors more consistently mark the entire instanceof expr + remove unwanted type qualifiers + don't expect some secondary errors + test case for https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3074 --- .../internal/compiler/ast/CaseStatement.java | 7 +- .../compiler/ast/EitherOrMultiPattern.java | 7 ++ .../internal/compiler/ast/GuardedPattern.java | 10 ++- .../compiler/ast/InstanceOfExpression.java | 20 ++++-- .../jdt/internal/compiler/ast/Pattern.java | 34 ++++++---- .../internal/compiler/ast/RecordPattern.java | 2 +- .../compiler/problem/ProblemReporter.java | 60 ++++++----------- .../InstanceofPrimaryPatternTest.java | 24 +++++++ .../regression/PatternMatching16Test.java | 4 +- .../regression/PrimitiveInPatternsTestSH.java | 22 +++++++ .../regression/RecordPatternTest.java | 66 ++++++++++++++++--- .../regression/SwitchPatternTest.java | 10 --- 12 files changed, 182 insertions(+), 84 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java index 49fb1ecfaff..77cb7cc0f05 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/CaseStatement.java @@ -381,11 +381,8 @@ private Constant resolveCasePattern(BlockScope scope, TypeBinding caseType, Type // The following code is copied from InstanceOfExpression#resolve() // But there are enough differences to warrant a copy if (!type.isReifiable()) { - if (expressionType != TypeBinding.NULL && !(e instanceof RecordPattern)) { - boolean isLegal = e.checkCastTypesCompatibility(scope, type, expressionType, e, false); - if (!isLegal || (e.bits & ASTNode.UnsafeCast) != 0) { - scope.problemReporter().unsafeCastInInstanceof(e, type, expressionType); - } + if (!e.isApplicable(switchExpressionType, scope, e)) { + return Constant.NotAConstant; } } else if (type.isValidBinding()) { // if not a valid binding, an error has already been reported for unresolved type diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/EitherOrMultiPattern.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/EitherOrMultiPattern.java index 9d4e81fe9b0..8ef74f12e1c 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/EitherOrMultiPattern.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/EitherOrMultiPattern.java @@ -54,6 +54,13 @@ public void setIsGuarded() { this.patterns[i].setIsGuarded(); } + @Override + public void setOuterExpressionType(TypeBinding expressionType) { + super.setOuterExpressionType(expressionType); + for (int i = 0; i < this.patternsCount; i++) + this.patterns[i].setOuterExpressionType(expressionType); + } + @Override public TypeBinding resolveType(BlockScope scope) { boolean hasError = false; diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/GuardedPattern.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/GuardedPattern.java index a4317e9c066..47e6f2b846f 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/GuardedPattern.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/GuardedPattern.java @@ -76,6 +76,12 @@ public void setIsEitherOrPattern() { this.primaryPattern.setIsEitherOrPattern(); } + @Override + public void setOuterExpressionType(TypeBinding expressionType) { + super.setOuterExpressionType(expressionType); + this.primaryPattern.setOuterExpressionType(expressionType); + } + @Override public boolean coversType(TypeBinding type, Scope scope) { return isUnguarded() && this.primaryPattern.coversType(type, scope); @@ -130,7 +136,7 @@ public void traverse(ASTVisitor visitor, BlockScope scope) { } @Override - protected boolean isApplicable(TypeBinding other, BlockScope scope) { - return this.primaryPattern.isApplicable(other, scope); + protected boolean isApplicable(TypeBinding expressionType, BlockScope scope, ASTNode location) { + return this.primaryPattern.isApplicable(expressionType, scope, location); } } \ No newline at end of file diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java index fa23ce53502..1972bd87b09 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java @@ -277,6 +277,13 @@ public TypeBinding resolveType(BlockScope scope) { if (expressionType == null || checkedType == null) return null; + if (this.pattern != null) { + if (this.pattern.isApplicable(expressionType, scope, this)) { + checkForPrimitives(scope, checkedType, expressionType); + } + return this.resolvedType = TypeBinding.BOOLEAN; + } + if (!checkedType.isReifiable()) { CompilerOptions options = scope.compilerOptions(); // Report same as before for older compliances @@ -286,7 +293,7 @@ public TypeBinding resolveType(BlockScope scope) { if (expressionType != TypeBinding.NULL) { boolean isLegal = checkCastTypesCompatibility(scope, checkedType, expressionType, this.expression, true); if (!isLegal || (this.bits & ASTNode.UnsafeCast) != 0) { - scope.problemReporter().unsafeCastInInstanceof(this.expression, checkedType, expressionType); + scope.problemReporter().unsafeCastInTestingContext(this.expression, checkedType, expressionType); } else { checkRefForPrimitivesAndAddSecretVariable(scope, checkedType, expressionType); } @@ -294,17 +301,18 @@ public TypeBinding resolveType(BlockScope scope) { } } else if (checkedType.isValidBinding()) { // if not a valid binding, an error has already been reported for unresolved type - if ((expressionType != TypeBinding.NULL && expressionType.isBaseType()) // disallow autoboxing - || checkedType.isBaseType() - || !checkCastTypesCompatibility(scope, checkedType, expressionType, null, true)) { - checkForPrimitives(scope, checkedType, expressionType); - } + checkForPrimitives(scope, checkedType, expressionType); } return this.resolvedType = TypeBinding.BOOLEAN; } private void checkForPrimitives(BlockScope scope, TypeBinding checkedType, TypeBinding expressionType) { + boolean needToCheck = (expressionType != TypeBinding.NULL && expressionType.isBaseType()) // disallow autoboxing + || checkedType.isBaseType() + || !checkCastTypesCompatibility(scope, checkedType, expressionType, null, true); + if (!needToCheck) + return; PrimitiveConversionRoute route = Pattern.findPrimitiveConversionRoute(checkedType, expressionType, scope); this.testContextRecord = new TestContextRecord(checkedType, expressionType, route); diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/Pattern.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/Pattern.java index f07c01e55a3..15d3fdad800 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/Pattern.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/Pattern.java @@ -139,25 +139,37 @@ public TypeReference getType() { } // 14.30.3 Properties of Patterns: A pattern p is said to be applicable at a type T if ... - protected boolean isApplicable(TypeBinding other, BlockScope scope) { - TypeBinding patternType = this.resolvedType; - if (patternType == null) // ill resolved pattern - return false; + protected boolean isApplicable(TypeBinding expressionType, BlockScope scope, ASTNode location) { + if (expressionType == TypeBinding.NULL) + return true; + TypeReference typeRef = getType(); + if (typeRef == null) + return true; // nothing to be checked for wildcard '_' + TypeBinding patternType = typeRef.resolvedType; + if (patternType == null || !patternType.isValidBinding() || !expressionType.isValidBinding()) + return false; // problem already reported + // 14.30.3 Properties of Patterns doesn't allow boxing nor unboxing, primitive widening/narrowing (< JLS23) - if (patternType.isBaseType() != other.isBaseType() && !JavaFeature.PRIMITIVES_IN_PATTERNS.isSupported(scope.compilerOptions())) { - scope.problemReporter().incompatiblePatternType(this, other, patternType); + if (patternType.isBaseType() != expressionType.isBaseType() && !JavaFeature.PRIMITIVES_IN_PATTERNS.isSupported(scope.compilerOptions())) { + scope.problemReporter().notCompatibleTypesError(location, expressionType, patternType); return false; } if (patternType.isBaseType()) { PrimitiveConversionRoute route = Pattern.findPrimitiveConversionRoute(this.resolvedType, this.outerExpressionType, scope); - if (!TypeBinding.equalsEquals(other, patternType) + if (!TypeBinding.equalsEquals(expressionType, patternType) && route == PrimitiveConversionRoute.NO_CONVERSION_ROUTE) { - scope.problemReporter().incompatiblePatternType(this, other, patternType); + scope.problemReporter().notCompatibleTypesError(location, expressionType, patternType); + return false; + } + } else { + if (!checkCastTypesCompatibility(scope, patternType, expressionType, null, true)) { + scope.problemReporter().notCompatibleTypesError(location, expressionType, patternType); + return false; + } + if ((this.bits & ASTNode.UnsafeCast) != 0) { + scope.problemReporter().unsafeCastInTestingContext(location, patternType, this.outerExpressionType); return false; } - } else if (!checkCastTypesCompatibility(scope, other, patternType, null, true)) { - scope.problemReporter().incompatiblePatternType(this, other, patternType); - return false; } return true; } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/RecordPattern.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/RecordPattern.java index 1e64ee3e452..bc26d4f3489 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/RecordPattern.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/RecordPattern.java @@ -152,7 +152,7 @@ public TypeBinding resolveType(BlockScope scope) { } } TypeBinding componentType = componentBinding.type; - if (p1.isApplicable(componentType, scope)) { + if (p1.isApplicable(componentType, scope, p1)) { p1.isTotalTypeNode = p1.coversType(componentType, scope); MethodBinding[] methods = this.resolvedType.getMethods(componentBinding.name); if (methods != null && methods.length > 0) { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java index d1ac75e2e53..4a749013d7b 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java @@ -7303,7 +7303,7 @@ public void noSuchEnclosingInstance(TypeBinding targetType, ASTNode location, bo location.sourceStart, location instanceof LambdaExpression ? ((LambdaExpression)location).diagnosticsSourceEnd() : location.sourceEnd); } -public void notCompatibleTypesError(EqualExpression expression, TypeBinding leftType, TypeBinding rightType) { +public void notCompatibleTypesError(ASTNode location, TypeBinding leftType, TypeBinding rightType) { String leftName = new String(leftType.readableName()); String rightName = new String(rightType.readableName()); String leftShortName = new String(leftType.shortReadableName()); @@ -7312,28 +7312,18 @@ public void notCompatibleTypesError(EqualExpression expression, TypeBinding left leftShortName = leftName; rightShortName = rightName; } - this.handle( - IProblem.IncompatibleTypesInEqualityOperator, - new String[] {leftName, rightName }, - new String[] {leftShortName, rightShortName }, - expression.sourceStart, - expression.sourceEnd); -} -public void notCompatibleTypesError(Expression expression, TypeBinding leftType, TypeBinding rightType) { - String leftName = new String(leftType.readableName()); - String rightName = new String(rightType.readableName()); - String leftShortName = new String(leftType.shortReadableName()); - String rightShortName = new String(rightType.shortReadableName()); - if (leftShortName.equals(rightShortName)){ - leftShortName = leftName; - rightShortName = rightName; + int problemId = IProblem.IncompatibleTypesInEqualityOperator; + if (location instanceof Pattern p && p.getEnclosingPattern() instanceof RecordPattern) { + problemId = IProblem.PatternTypeMismatch; + } else if (location instanceof InstanceOfExpression) { + problemId = IProblem.IncompatibleTypesInConditionalOperator; } this.handle( - IProblem.IncompatibleTypesInConditionalOperator, + problemId, new String[] {leftName, rightName }, new String[] {leftShortName, rightShortName }, - expression.sourceStart, - expression.sourceEnd); + location.sourceStart, + location.sourceEnd); } public void notCompatibleTypesErrorInForeach(Expression expression, TypeBinding leftType, TypeBinding rightType) { String leftName = new String(leftType.readableName()); @@ -8538,21 +8528,21 @@ public void typeCastError(CastExpression expression, TypeBinding leftType, TypeB expression.sourceStart, expression.sourceEnd); } -public void unsafeCastInInstanceof(Expression expression, TypeBinding leftType, TypeBinding rightType) { - String leftName = new String(leftType.readableName()); - String rightName = new String(rightType.readableName()); - String leftShortName = new String(leftType.shortReadableName()); - String rightShortName = new String(rightType.shortReadableName()); - if (leftShortName.equals(rightShortName)){ - leftShortName = leftName; - rightShortName = rightName; +public void unsafeCastInTestingContext(ASTNode location, TypeBinding castType, TypeBinding expressionType) { + String castName = new String(castType.readableName()); + String exprName = new String(expressionType.readableName()); + String castShortName = new String(castType.shortReadableName()); + String exprShortName = new String(expressionType.shortReadableName()); + if (castShortName.equals(exprShortName)){ + castShortName = castName; + exprShortName = exprName; } this.handle( IProblem.UnsafeCast, - new String[] { rightName, leftName }, - new String[] { rightShortName, leftShortName }, - expression.sourceStart, - expression.sourceEnd); + new String[] { exprName, castName }, + new String[] { exprShortName, castShortName }, + location.sourceStart, + location.sourceEnd); } public void typeCollidesWithEnclosingType(TypeDeclaration typeDecl) { String[] arguments = new String[] {new String(typeDecl.name)}; @@ -12404,14 +12394,6 @@ public void recordPatternSignatureMismatch(TypeBinding type, ASTNode element) { element.sourceStart, element.sourceEnd); } -public void incompatiblePatternType(ASTNode element, TypeBinding type, TypeBinding expected) { - this.handle( - IProblem.PatternTypeMismatch, - new String[] {new String(type.readableName()), new String(expected.readableName())}, - new String[] {new String(type.shortReadableName()), new String(expected.readableName())}, - element.sourceStart, - element.sourceEnd); -} public void cannotInferRecordPatternTypes(RecordPattern pattern) { String arguments [] = new String [] { pattern.toString() }; this.handle( diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/InstanceofPrimaryPatternTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/InstanceofPrimaryPatternTest.java index 201d0d2374c..2e1d5eeaaae 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/InstanceofPrimaryPatternTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/InstanceofPrimaryPatternTest.java @@ -646,4 +646,28 @@ public static void gain(String[] args) { + "A pattern variable with the same name is already defined in the statement\n" + "----------\n"); } + + public void testGH3074() { + runNegativeTest( + new String[] { + "Example.java", + """ + class Example { + private void foo(String x) { + if (x instanceof Example es) { + + } + } + } + """ + }, + """ + ---------- + 1. ERROR in Example.java (at line 3) + if (x instanceof Example es) { + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Incompatible conditional operand types String and Example + ---------- + """); + } } \ No newline at end of file diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PatternMatching16Test.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PatternMatching16Test.java index 636be4638d0..b68104b0d76 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PatternMatching16Test.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PatternMatching16Test.java @@ -2401,7 +2401,7 @@ public void testBug562392c() { "----------\n" + "1. ERROR in X.java (at line 4)\n" + " if (obj instanceof T t) {\n" + - " ^^^\n" + + " ^^^^^^^^^^^^^^^^^^\n" + "Type Object cannot be safely cast to T\n" + "----------\n", "X.java:4: error: Object cannot be safely cast to T\n" + @@ -2460,7 +2460,7 @@ public void testBug562392e() { "----------\n" + "1. ERROR in X.java (at line 4)\n" + " if (obj instanceof X p) {\n" + - " ^^^\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + "Type X cannot be safely cast to X\n" + "----------\n", "", diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PrimitiveInPatternsTestSH.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PrimitiveInPatternsTestSH.java index 418e0baef13..f8a9e4ca84e 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PrimitiveInPatternsTestSH.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PrimitiveInPatternsTestSH.java @@ -2302,6 +2302,28 @@ int foo4(int myInt) { """); } + public void testIncompatiblePrimitiveInInstanceof() { + runNegativeTest(new String[] { + "X.java", + """ + public class X { + void foo() { + if (this instanceof int i) + return; + } + } + """ + }, + """ + ---------- + 1. ERROR in X.java (at line 3) + if (this instanceof int i) + ^^^^^^^^^^^^^^^^^^^^^ + Incompatible conditional operand types X and int + ---------- + """); + } + // test from spec public void _testSpec001() { runConformTest(new String[] { diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordPatternTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordPatternTest.java index aa3198e6fa4..1d5c01ba787 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordPatternTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordPatternTest.java @@ -299,12 +299,12 @@ public void test007() { "1. ERROR in X.java (at line 3)\n" + " if (r instanceof Rectangle(ColoredPoint(Point(String o1, String o2), Color c),\n" + " ^^^^^^^^^\n" + - "Record component with type int is not compatible with type java.lang.String\n" + + "Record component with type int is not compatible with type String\n" + "----------\n" + "2. ERROR in X.java (at line 3)\n" + " if (r instanceof Rectangle(ColoredPoint(Point(String o1, String o2), Color c),\n" + " ^^^^^^^^^\n" + - "Record component with type int is not compatible with type java.lang.String\n" + + "Record component with type int is not compatible with type String\n" + "----------\n"); } // Test that pattern types that don't match record component's types are reported @@ -1558,7 +1558,7 @@ public void test47() { + "public class X {\n" + " static void printGenericBoxString1(Box objectBox) {\n" + " if (objectBox instanceof Box(String s)) {\n" - + " System.out.println(s); // this one should report an unsafe cast error\n" + + " System.out.println(s);\n" + " }\n" + " }\n" + " public static void main(String[] args) {}\n" @@ -1568,8 +1568,8 @@ public void test47() { "----------\n" + "1. ERROR in X.java (at line 4)\n" + " if (objectBox instanceof Box(String s)) {\n" + - " ^^^^^^^^^\n" + - "Type Box cannot be safely cast to Box\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Incompatible conditional operand types Box and Box\n" + "----------\n"); } public void test48() { @@ -1958,7 +1958,7 @@ public void testRecordPatternTypeInference_009() { "1. ERROR in X.java (at line 7)\n" + " if (p instanceof R(String a)) {\n" + " ^^^^^^^^\n" + - "Record component with type capture#2-of ? extends I is not compatible with type java.lang.String\n" + + "Record component with type capture#2-of ? extends I is not compatible with type String\n" + "----------\n"); } public void testRecordPatternTypeInference_010() { @@ -2505,7 +2505,7 @@ public void testIssue1224_4() { "2. ERROR in X.java (at line 6)\n" + " case Record(Object o, StringBuilder s) -> {break;}\n" + " ^^^^^^^^^^^^^^^\n" + - "Record component with type String is not compatible with type java.lang.StringBuilder\n" + + "Record component with type String is not compatible with type StringBuilder\n" + "----------\n"); } public void testIssue1224_5() { @@ -4401,7 +4401,7 @@ public static void main(String[] args) { "2. ERROR in X.java (at line 10)\n" + " if (o instanceof R2(Short d)) {\n" + " ^^^^^^^\n" + - "Record component with type short is not compatible with type java.lang.Short\n" + + "Record component with type short is not compatible with type Short\n" + "----------\n" + "3. ERROR in X.java (at line 13)\n" + " if (o instanceof R2(int d)) {\n" + @@ -4686,4 +4686,54 @@ static void foo(Object o) { "Syntax error on tokens, delete these tokens\n" + "----------\n"); } + + public void testIssue3066() { + runNegativeTest(new String[] { + "X.java", + """ + public record X(int x) { + public static void main(String[] args) { + Object o = new Object(); + switch (o) { + case X(int x): + default: + } + } + } + """ + }, + """ + ---------- + 1. ERROR in X.java (at line 5) + case X(int x): + ^^^^^^^^^^^^^^^^ + Type Object cannot be safely cast to X + ---------- + """); + } + + public void testIssue3066_notApplicable() { + runNegativeTest(new String[] { + "X.java", + """ + public record X(int x) { + public static void main(String[] args) { + java.io.Serializable o = ""; + switch (o) { + case X(int x): + default: + } + } + } + """ + }, + """ + ---------- + 1. ERROR in X.java (at line 5) + case X(int x): + ^^^^^^^^ + Type mismatch: cannot convert from Serializable to X + ---------- + """); + } } \ No newline at end of file diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java index a802d08ba5e..fa5d2105530 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SwitchPatternTest.java @@ -2650,11 +2650,6 @@ public void testBug573921_7() { " case List s: \n" + " ^^^^^^^^^^^^^^\n" + "Type Object cannot be safely cast to List\n" + - "----------\n" + - "3. ERROR in X.java (at line 9)\n" + - " case List s: \n" + - " ^^^^^^^^^^^^^^\n" + - "This case label is dominated by one of the preceding case labels\n" + "----------\n"); } public void testBug573921_8() { @@ -7335,11 +7330,6 @@ case WrapperRec(ExhaustiveSwitch.Data data) when data.name.isEmpty() -> { } + " case WrapperRec(ExhaustiveSwitch.Data data) when data.name.isEmpty() -> { }\n" + " ^^^^^^^^^^^^^^^^\n" + "ExhaustiveSwitch cannot be resolved to a type\n" - + "----------\n" - + "4. ERROR in X.java (at line 12)\n" - + " case WrapperRec(ExhaustiveSwitch.Data data) when data.name.isEmpty() -> { }\n" - + " ^^^^^^^^^^^^^^^^^^^^^^^^^^\n" - + "Record component with type Data is not compatible with type ExhaustiveSwitch.Data\n" + "----------\n"); } // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/1955 From 185d23bc5ffd575251af4c8718d9e390789953c2 Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Thu, 24 Oct 2024 23:18:53 +0200 Subject: [PATCH 52/63] [Switch][Primitive type patterns] Unexpected operand error with switch pattern and widening unboxing conversion (#3158) fix direction of conversion extend existing test to loop over numerical types Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3113 --- .../internal/compiler/ast/TypePattern.java | 2 +- .../regression/PrimitiveInPatternsTestSH.java | 123 +++++++++++++++--- 2 files changed, 106 insertions(+), 19 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypePattern.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypePattern.java index b4609a13231..4716fde7211 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypePattern.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypePattern.java @@ -145,7 +145,7 @@ public void generateTestingConversion(BlockScope scope, CodeStream codeStream) { case WIDENING_REFERENCE_AND_UNBOXING_COVERSION_AND_WIDENING_PRIMITIVE_CONVERSION: int rhsUnboxed = TypeIds.box2primitive(provided.superclass().id); codeStream.generateUnboxingConversion(rhsUnboxed); - this.computeConversion(scope, TypeBinding.wellKnownBaseType(rhsUnboxed), expected); + this.computeConversion(scope, expected, TypeBinding.wellKnownBaseType(rhsUnboxed)); codeStream.generateImplicitConversion(this.implicitConversion); break; case NARROWING_AND_UNBOXING_CONVERSION: diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PrimitiveInPatternsTestSH.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PrimitiveInPatternsTestSH.java index f8a9e4ca84e..8074258b506 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PrimitiveInPatternsTestSH.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/PrimitiveInPatternsTestSH.java @@ -1510,29 +1510,93 @@ public static void main(String... args) { }, "12"); } - public void testInstanceof_widenUnbox() { - runConformTest(new String[] { - "X.java", - """ + void testInstanceof_widenUnbox(String fromBox, int idx, String expectedOuts) { + // for all numerical types challenge route WIDENING_REFERENCE_AND_UNBOXING_COVERSION_AND_WIDENING_PRIMITIVE_CONVERSION + // + // example (from="Integer", idx=4, ...): + // class X { + // // alternating for type variable (m1*) and wildcard (m2*): + // static long m1long(T in) { + // if (in instanceof long v) return v; + // return -1L; + // } + // static long m2Long(Optional in) { + // if (in.get() instanceof long v) return v; + // return -1L; + // } + // static float m1float(T in) { + // if (in instanceof float v) return v; + // return -1.0f; + // } + // ... + // public static void main(String... args) { + // Integer v = Integer.valueOf((int) 49); + // System.out.print(m1long(v)); + // System.out.print('+'); + // System.out.print(Optional.of(m2long(v)); + // System.out.print('|'); + // System.out.print(m1float(v)); + // System.out.print('|'); + // ... + // } + // } + String m1 = """ + static PRIM m1PRIM(T in) { + if (in instanceof PRIM v) return v; + return NEGVAL; + } + """.replace("FROM", fromBox); + String m2 = """ + static PRIM m2PRIM(Optional in) { + if (in.get() instanceof PRIM v) return v; + return NEGVAL; + } + """.replace("FROM", fromBox); + StringBuilder clazz = new StringBuilder(); + clazz.append(""" import java.util.Optional; public class X { - static int mInteger(T in) { - if (in instanceof int v) return v; - return -1; - } - static int mShort(Optional in) { - if (in.get() instanceof int v) return v; - return -1; - } - public static void main(String... args) { - System.out.print(mInteger(Integer.valueOf(1))); - System.out.print(mShort(Optional.of(Short.valueOf((short) 2)))); + """); + StringBuilder main = new StringBuilder(); + main.append("public static void main(String... args) {\n"); + main.append("\tFROM v = FROM.valueOf((CAST) VAL);\n" + .replace("FROM", fromBox) + .replace("CAST", PRIMITIVES[idx]) + .replace("VAL", GOODVALUES[idx])); + String call1Tmpl = "\tSystem.out.print(m1PRIM(v));\n".replace("FROM", fromBox); + String call2Tmpl = "\tSystem.out.print(m2PRIM(Optional.of(v)));\n".replace("FROM", fromBox); + for (int i=idx+1; i<8; i++) { + if (!IS_NUMERICAL[i]) continue; + clazz.append(fillIn(m1, i)); + clazz.append(fillIn(m2, i)); + main.append(fillIn(call1Tmpl, i)); + main.append("\tSystem.out.print('+');\n"); + main.append(fillIn(call2Tmpl, i)); + main.append("\tSystem.out.print('|');\n"); + } + clazz.append(main); + clazz.append(""" } } - """ - }, - "12"); + """); + runConformTest(new String[] {"X.java", clazz.toString()}, expectedOuts); + } + public void testInstanceof_widenUnbox_Byte() { + testInstanceof_widenUnbox("Byte", 1, "49+49|49+49|49+49|49.0+49.0|49.0+49.0|"); + } + public void testInstanceof_widenUnbox_Short() { + testInstanceof_widenUnbox("Short", 3, "49+49|49+49|49.0+49.0|49.0+49.0|"); + } + public void testInstanceof_widenUnbox_Integer() { + testInstanceof_widenUnbox("Integer", 4, "49+49|49.0+49.0|49.0+49.0|"); } + public void testInstanceof_widenUnbox_Long() { + testInstanceof_widenUnbox("Long", 5, "49.0+49.0|49.0+49.0|"); + } + public void testInstanceof_widenUnbox_Float() { + testInstanceof_widenUnbox("Float", 6, "49.0+49.0|"); + } + public void testInstanceof_genericExpression() { // regression test for a checkCast which we failed to generate earlier runConformTest(new String[] { "X.java", @@ -2324,6 +2388,29 @@ void foo() { """); } + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3113 + // [Switch][Record patterns] Unexpected operand error with switch pattern and widening unboxing conversion + public void testGH3113_ok() { + runConformTest(new String[] { + "X.java", + """ + record Record(T t) {} + public class X { + public static double convert(Record r) { + return switch (r) { + case Record(double d) -> d; + default -> 2; + }; + } + public static void main(String[] args) { + System.out.print(convert(new Record(2))); + } + } + """ + }, + "2.0"); + } + // test from spec public void _testSpec001() { runConformTest(new String[] { From 25d4b54b6d30412b62c52c3be1c7c0410a43b4d0 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Fri, 25 Oct 2024 18:16:52 +0530 Subject: [PATCH 53/63] Simplify error messages related to sealed types. (#3160) --- .../internal/compiler/lookup/ClassScope.java | 4 +- .../compiler/problem/ProblemReporter.java | 12 +-- .../compiler/problem/messages.properties | 14 +-- .../regression/BatchCompilerTest_15.java | 4 +- .../compiler/regression/SealedTypesTests.java | 94 +++++++++---------- .../tests/model/SealedTypeModelTests.java | 2 +- 6 files changed, 63 insertions(+), 67 deletions(-) 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 a650adb21d6..cf72c94dd10 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 @@ -1314,7 +1314,7 @@ void connectPermittedTypes() { for (int j = 0; j < i; j++) { if (TypeBinding.equalsEquals(permittedTypeBindings[j], permittedType)) { - problemReporter().duplicatePermittedType(sourceType, permittedTypeRef, permittedType); + problemReporter().duplicatePermittedType(permittedTypeRef, permittedType); continue nextPermittedType; } } @@ -1336,7 +1336,7 @@ void connectPermittedTypes() { sourceType.setPermittedTypes(Binding.NO_PERMITTED_TYPES); if (sourceType.isSealed()) { if (!sourceType.isLocalType() && !sourceType.isRecord() && !sourceType.isEnum()) // error flagged alread - problemReporter().sealedTypeMissingPermits(sourceType, this.referenceContext); + problemReporter().missingPermitsClause(sourceType, this.referenceContext); } } } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java index 4a749013d7b..ddd73eb142a 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java @@ -12198,15 +12198,11 @@ public void missingSealedModifier(SourceTypeBinding type, ASTNode node) { node.sourceEnd); } -public void duplicatePermittedType(SourceTypeBinding type, TypeReference reference, ReferenceBinding superType) { +public void duplicatePermittedType(TypeReference reference, ReferenceBinding superType) { this.handle( IProblem.SealedDuplicateTypeInPermits, - new String[] { - new String(superType.readableName()), - new String(type.sourceName())}, - new String[] { - new String(superType.shortReadableName()), - new String(type.sourceName())}, + new String[] { new String(superType.readableName()) }, + new String[] { new String(superType.shortReadableName()) }, reference.sourceStart, reference.sourceEnd); } @@ -12245,7 +12241,7 @@ public void permittedTypeOutsideOfPackage(ReferenceBinding permType, ReferenceBi node.sourceEnd); } -public void sealedTypeMissingPermits(SourceTypeBinding type, ASTNode node) { +public void missingPermitsClause(SourceTypeBinding type, ASTNode node) { String name = new String(type.sourceName()); this.handle(IProblem.SealedSealedTypeMissingPermits, new String[] { name }, new String[] { name }, node.sourceStart, node.sourceEnd); diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties index 005270500fc..733ff39db70 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties @@ -1132,19 +1132,19 @@ 1820 = {0} is a value-based type which is a discouraged argument for the synchronized statement # Sealed types - Java 17 -1850 = The class {1} with a sealed direct superclass or a sealed direct superinterface {0} should be declared either final, sealed, or non-sealed -1851 = A class {0} declared as non-sealed should have either a sealed direct superclass or a sealed direct superinterface -1852 = The type {0} extending a sealed class {1} should be a permitted subtype of {1} -1853 = The type {0} that {2} a sealed interface {1} should be a permitted subtype of {1} +1850 = The class {1} with a sealed direct supertype {0} should be declared either final, sealed, or non-sealed +1851 = The non-sealed class {0} must have a sealed direct supertype +1852 = The class {0} cannot extend the class {1} as it is not a permitted subtype of {1} +1853 = The type {0} that {2} the sealed interface {1} should be a permitted subtype of {1} 1854 = A type declaration {0} that has a permits clause should have a sealed modifier 1855 = The interface {1} with a sealed direct superinterface {0} should be declared either sealed or non-sealed -1856 = Duplicate type {0} for the type {1} in the permits clause +1856 = Duplicate permitted type {0} 1857 = Permitted class {0} does not declare {1} as direct super class 1858 = Permitted type {0} in a named module {1} should be declared in the same module {1} of declaring type {2} 1859 = Permitted type {0} in an unnamed module should be declared in the same package {1} of declaring type {2} -1860 = Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares {0} as its direct superclass or superinterface +1860 = Sealed type {0} lacks a permits clause and no type from the same compilation unit declares {0} as its direct supertype ###[obsolete] 1861 = An interface {0} is declared both sealed and non-sealed -1862 = An interface {0} declared as non-sealed should have a sealed direct superinterface +1862 = The non-sealed interface {0} must have a sealed direct superinterface 1863 = Permitted type {0} does not declare {1} as direct super interface 1864 = The local type {1} may not have a sealed supertype {0} 1865 = An anonymous class cannot subclass a sealed type {0} diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_15.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_15.java index abfe03d8c64..9a40ab6ac92 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_15.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest_15.java @@ -89,12 +89,12 @@ public void testBug564047_001(){ "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 2)\n" + " public class X extends Y {\n" + " ^\n" + - "The class X with a sealed direct superclass or a sealed direct superinterface Y should be declared either final, sealed, or non-sealed\n" + + "The class X with a sealed direct supertype Y should be declared either final, sealed, or non-sealed\n" + "----------\n" + "2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 2)\n" + " public class X extends Y {\n" + " ^\n" + - "The type X extending a sealed class Y should be a permitted subtype of Y\n" + + "The class X cannot extend the class Y as it is not a permitted subtype of Y\n" + "----------\n" + "2 problems (2 errors)\n", true); diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java index 79e82c2f64e..352229eb81f 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java @@ -254,7 +254,7 @@ public void testBug562715_005() { "2. ERROR in X.java (at line 1)\n" + " sealed public sealed class X {\n" + " ^\n" + - "Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares X as its direct superclass or superinterface\n" + + "Sealed type X lacks a permits clause and no type from the same compilation unit declares X as its direct supertype\n" + "----------\n"); } public void testBug562715_006() { @@ -271,7 +271,7 @@ public void testBug562715_006() { "1. ERROR in X.java (at line 1)\n" + " public sealed class X {\n" + " ^\n" + - "Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares X as its direct superclass or superinterface\n" + + "Sealed type X lacks a permits clause and no type from the same compilation unit declares X as its direct supertype\n" + "----------\n" + "2. ERROR in X.java (at line 2)\n" + " public static sealed void main(String[] args){\n" + @@ -336,7 +336,7 @@ public void testBug562715_010() { "1. ERROR in X.java (at line 1)\n" + " public sealed class X permits {\n" + " ^\n" + - "Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares X as its direct superclass or superinterface\n" + + "Sealed type X lacks a permits clause and no type from the same compilation unit declares X as its direct supertype\n" + "----------\n" + "2. ERROR in X.java (at line 1)\n" + " public sealed class X permits {\n" + @@ -370,7 +370,7 @@ public void testBug562715_011() { "2. ERROR in X.java (at line 2)\n" + " public sealed class X {\n" + " ^\n" + - "Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares X as its direct superclass or superinterface\n" + + "Sealed type X lacks a permits clause and no type from the same compilation unit declares X as its direct supertype\n" + "----------\n" + "3. ERROR in X.java (at line 3)\n" + " public static sealed void main(String[] args){\n" + @@ -398,7 +398,7 @@ public void testBug562715_xxx() { "2. ERROR in X.java (at line 2)\n" + " public sealed class X {\n" + " ^\n" + - "Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares X as its direct superclass or superinterface\n" + + "Sealed type X lacks a permits clause and no type from the same compilation unit declares X as its direct supertype\n" + "----------\n" + "3. ERROR in X.java (at line 3)\n" + " public static sealed void main(String[] args){\n" + @@ -449,23 +449,23 @@ public void testBug563806_002() { "2. ERROR in p1\\X.java (at line 5)\n" + " class Z extends X{}\n" + " ^\n" + - "The class Z with a sealed direct superclass or a sealed direct superinterface X should be declared either final, sealed, or non-sealed\n" + + "The class Z with a sealed direct supertype X should be declared either final, sealed, or non-sealed\n" + "----------\n" + "3. ERROR in p1\\X.java (at line 5)\n" + " class Z extends X{}\n" + " ^\n" + - "The type Z extending a sealed class X should be a permitted subtype of X\n" + + "The class Z cannot extend the class X as it is not a permitted subtype of X\n" + "----------\n" + "----------\n" + "1. ERROR in p1\\A.java (at line 2)\n" + " public sealed class A extends X{}\n" + " ^\n" + - "Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares A as its direct superclass or superinterface\n" + + "Sealed type A lacks a permits clause and no type from the same compilation unit declares A as its direct supertype\n" + "----------\n" + "2. ERROR in p1\\A.java (at line 2)\n" + " public sealed class A extends X{}\n" + " ^\n" + - "The type A extending a sealed class X should be a permitted subtype of X\n" + + "The class A cannot extend the class X as it is not a permitted subtype of X\n" + "----------\n"); } public void testBug563806_003() { @@ -486,7 +486,7 @@ public void testBug563806_003() { "2. ERROR in X.java (at line 3)\n" + " class Y implements X{}\n" + " ^\n" + - "The class Y with a sealed direct superclass or a sealed direct superinterface X should be declared either final, sealed, or non-sealed\n" + + "The class Y with a sealed direct supertype X should be declared either final, sealed, or non-sealed\n" + "----------\n"); } public void testBug563806_004() { @@ -513,7 +513,7 @@ public void testBug563806_004() { "3. ERROR in p1\\X.java (at line 4)\n" + " class Y implements X{}\n" + " ^\n" + - "The class Y with a sealed direct superclass or a sealed direct superinterface X should be declared either final, sealed, or non-sealed\n" + + "The class Y with a sealed direct supertype X should be declared either final, sealed, or non-sealed\n" + "----------\n"); } public void testBug563806_005() { @@ -528,12 +528,12 @@ public void testBug563806_005() { "1. ERROR in X.java (at line 1)\n" + " public sealed class X permits Y, Y{\n" + " ^\n" + - "Duplicate type Y for the type X in the permits clause\n" + + "Duplicate permitted type Y\n" + "----------\n" + "2. ERROR in X.java (at line 3)\n" + " class Y extends X {}\n" + " ^\n" + - "The class Y with a sealed direct superclass or a sealed direct superinterface X should be declared either final, sealed, or non-sealed\n" + + "The class Y with a sealed direct supertype X should be declared either final, sealed, or non-sealed\n" + "----------\n"); } public void testBug563806_006() { @@ -549,12 +549,12 @@ public void testBug563806_006() { "1. ERROR in p1\\X.java (at line 2)\n" + " public sealed class X permits Y, p1.Y{\n" + " ^^^^\n" + - "Duplicate type Y for the type X in the permits clause\n" + + "Duplicate permitted type Y\n" + "----------\n" + "2. ERROR in p1\\X.java (at line 4)\n" + " class Y extends X {}\n" + " ^\n" + - "The class Y with a sealed direct superclass or a sealed direct superinterface X should be declared either final, sealed, or non-sealed\n" + + "The class Y with a sealed direct supertype X should be declared either final, sealed, or non-sealed\n" + "----------\n"); } public void testBug563806_007() { @@ -569,7 +569,7 @@ public void testBug563806_007() { "1. ERROR in X.java (at line 3)\n" + " non-sealed class Y extends X {}\n" + " ^\n" + - "A class Y declared as non-sealed should have either a sealed direct superclass or a sealed direct superinterface\n" + + "The non-sealed class Y must have a sealed direct supertype\n" + "----------\n"); } public void testBug563806_008() { @@ -588,13 +588,13 @@ public void testBug563806_008() { "1. ERROR in p1\\X.java (at line 4)\n" + " class Y implements X{}\n" + " ^\n" + - "The class Y with a sealed direct superclass or a sealed direct superinterface X should be declared either final, sealed, or non-sealed\n" + + "The class Y with a sealed direct supertype X should be declared either final, sealed, or non-sealed\n" + "----------\n" + "----------\n" + "1. ERROR in p2\\Y.java (at line 2)\n" + " non-sealed public interface Y {}\n" + " ^\n" + - "An interface Y declared as non-sealed should have a sealed direct superinterface\n" + + "The non-sealed interface Y must have a sealed direct superinterface\n" + "----------\n"); } public void testBug563806_009() { @@ -626,7 +626,7 @@ public void testBug563806_010() { "1. ERROR in p2\\Y.java (at line 2)\n" + " public final class Y extends p1.X{}\n" + " ^^^^\n" + - "The type Y extending a sealed class X should be a permitted subtype of X\n" + + "The class Y cannot extend the class X as it is not a permitted subtype of X\n" + "----------\n"); } public void testBug563806_011() { @@ -660,7 +660,7 @@ public void testBug563806_012() { "1. ERROR in p2\\Y.java (at line 2)\n" + " public final class Y implements p1.X{}\n" + " ^^^^\n" + - "The type Y that implements a sealed interface X should be a permitted subtype of X\n" + + "The type Y that implements the sealed interface X should be a permitted subtype of X\n" + "----------\n"); } public void testBug563806_013() { @@ -705,7 +705,7 @@ public void testBug563806_014() { "2. ERROR in p2\\Y.java (at line 2)\n" + " public interface Y extends p1.X{}\n" + " ^^^^\n" + - "The type Y that extends a sealed interface X should be a permitted subtype of X\n" + + "The type Y that extends the sealed interface X should be a permitted subtype of X\n" + "----------\n"); } public void testBug563806_015() { @@ -1070,7 +1070,7 @@ public void testBug563806_032() { "2. ERROR in p1\\X.java (at line 2)\n" + " public sealed non-sealed interface X {\n" + " ^\n" + - "Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares X as its direct superclass or superinterface\n" + + "Sealed type X lacks a permits clause and no type from the same compilation unit declares X as its direct supertype\n" + "----------\n"); } public void testBug563806_033() { @@ -1136,7 +1136,7 @@ public void testBug563806_035() { "1. ERROR in p1\\X.java (at line 2)\n" + " public non-sealed interface X {\n" + " ^\n" + - "An interface X declared as non-sealed should have a sealed direct superinterface\n" + + "The non-sealed interface X must have a sealed direct superinterface\n" + "----------\n"); } public void testBug563806_036() { @@ -1214,7 +1214,7 @@ public void testBug563806_039() { "1. ERROR in p1\\X.java (at line 2)\n" + " sealed class A{}\n" + " ^\n" + - "Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares A as its direct superclass or superinterface\n" + + "Sealed type A lacks a permits clause and no type from the same compilation unit declares A as its direct supertype\n" + "----------\n" + "2. ERROR in p1\\X.java (at line 5)\n" + " class Y extends A{}\n" + @@ -1324,7 +1324,7 @@ public void testBug564190_4() throws IOException, ClassFormatException { "1. ERROR in p1\\X.java (at line 3)\n" + " class Y extends X {}\n" + " ^\n" + - "The class Y with a sealed direct superclass or a sealed direct superinterface X should be declared either final, sealed, or non-sealed\n" + + "The class Y with a sealed direct supertype X should be declared either final, sealed, or non-sealed\n" + "----------\n"); } // Test that implicit permitted member type with implicit permitted types @@ -1343,7 +1343,7 @@ public void testBug564190_5() throws IOException, ClassFormatException { "1. ERROR in p1\\X.java (at line 3)\n" + " sealed class Y extends X {}\n" + " ^\n" + - "Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares Y as its direct superclass or superinterface\n" + + "Sealed type Y lacks a permits clause and no type from the same compilation unit declares Y as its direct supertype\n" + "----------\n"); } // Test that implicit permitted member type with explicit permits clause @@ -1378,7 +1378,7 @@ public void testBug564190_7() throws IOException, ClassFormatException { "1. ERROR in p1\\X.java (at line 2)\n" + " sealed interface SI {}\n" + " ^^\n" + - "Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares SI as its direct superclass or superinterface\n" + + "Sealed type SI lacks a permits clause and no type from the same compilation unit declares SI as its direct supertype\n" + "----------\n"); } public void testBug564450_001() throws IOException, ClassFormatException { @@ -1397,7 +1397,7 @@ public void testBug564450_001() throws IOException, ClassFormatException { "1. ERROR in p1\\Y.java (at line 2)\n" + " class Y extends X {\n" + " ^\n" + - "The class Y with a sealed direct superclass or a sealed direct superinterface X should be declared either final, sealed, or non-sealed\n" + + "The class Y with a sealed direct supertype X should be declared either final, sealed, or non-sealed\n" + "----------\n"); } public void testBug564047_001() throws CoreException, IOException { @@ -1434,12 +1434,12 @@ public void testBug564047_001() throws CoreException, IOException { "1. ERROR in src\\p\\X.java (at line 2)\n" + " public class X extends Y {\n" + " ^\n" + - "The class X with a sealed direct superclass or a sealed direct superinterface Y should be declared either final, sealed, or non-sealed\n" + + "The class X with a sealed direct supertype Y should be declared either final, sealed, or non-sealed\n" + "----------\n" + "2. ERROR in src\\p\\X.java (at line 2)\n" + " public class X extends Y {\n" + " ^\n" + - "The type X extending a sealed class Y should be a permitted subtype of Y\n" + + "The class X cannot extend the class Y as it is not a permitted subtype of Y\n" + "----------\n", libs, true); @@ -1656,7 +1656,7 @@ public void testBug564498_5() throws IOException, ClassFormatException { "2. ERROR in p1\\X.java (at line 7)\n" + " final class SubInnerY extends Y {}\n" + " ^\n" + - "The type SubInnerY extending a sealed class A.Y should be a permitted subtype of A.Y\n" + + "The class SubInnerY cannot extend the class A.Y as it is not a permitted subtype of A.Y\n" + "----------\n"); } // accept references of membertype without qualifier of enclosing type in permits clause @@ -5334,13 +5334,13 @@ public void testBug568758_001() { "1. ERROR in X.java (at line 1)\n" + " public sealed interface X{}\n" + " ^\n" + - "Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares X as its direct superclass or superinterface\n" + + "Sealed type X lacks a permits clause and no type from the same compilation unit declares X as its direct supertype\n" + "----------\n" + "----------\n" + "1. ERROR in Y.java (at line 1)\n" + " public final class Y implements X{}\n" + " ^\n" + - "The type Y that implements a sealed interface X should be a permitted subtype of X\n" + + "The type Y that implements the sealed interface X should be a permitted subtype of X\n" + "----------\n"); } public void testBug569522_001() throws IOException, ClassFormatException { @@ -5423,7 +5423,7 @@ public void testBug568854_001() { "1. ERROR in X.java (at line 4)\n" + " record B() implements Foo {}\n" + " ^^^\n" + - "The type B that implements a sealed interface X.Foo should be a permitted subtype of X.Foo\n" + + "The type B that implements the sealed interface X.Foo should be a permitted subtype of X.Foo\n" + "----------\n"); } public void testBug568854_002() { @@ -5440,7 +5440,7 @@ public void testBug568854_002() { "1. ERROR in X.java (at line 4)\n" + " record B() implements Foo {}\n" + " ^^^\n" + - "The type B that implements a sealed interface Foo should be a permitted subtype of Foo\n" + + "The type B that implements the sealed interface Foo should be a permitted subtype of Foo\n" + "----------\n"); } public void testBug568854_003() { @@ -5457,7 +5457,7 @@ public void testBug568854_003() { "1. ERROR in X.java (at line 3)\n" + " record B() implements Foo {}\n" + " ^^^\n" + - "The type B that implements a sealed interface Foo should be a permitted subtype of Foo\n" + + "The type B that implements the sealed interface Foo should be a permitted subtype of Foo\n" + "----------\n"); } public void testBug568854_004() { @@ -5474,12 +5474,12 @@ public void testBug568854_004() { "1. ERROR in X.java (at line 3)\n" + " class A implements Foo {}\n" + " ^\n" + - "The class A with a sealed direct superclass or a sealed direct superinterface X.Foo should be declared either final, sealed, or non-sealed\n" + + "The class A with a sealed direct supertype X.Foo should be declared either final, sealed, or non-sealed\n" + "----------\n" + "2. ERROR in X.java (at line 4)\n" + " final class B implements Foo {}\n" + " ^^^\n" + - "The type B that implements a sealed interface X.Foo should be a permitted subtype of X.Foo\n" + + "The type B that implements the sealed interface X.Foo should be a permitted subtype of X.Foo\n" + "----------\n"); } public void testBug568854_005() { @@ -5496,12 +5496,12 @@ public void testBug568854_005() { "1. ERROR in X.java (at line 3)\n" + " class A implements Foo {}\n" + " ^\n" + - "The class A with a sealed direct superclass or a sealed direct superinterface Foo should be declared either final, sealed, or non-sealed\n" + + "The class A with a sealed direct supertype Foo should be declared either final, sealed, or non-sealed\n" + "----------\n" + "2. ERROR in X.java (at line 4)\n" + " final class B implements Foo {}\n" + " ^^^\n" + - "The type B that implements a sealed interface Foo should be a permitted subtype of Foo\n" + + "The type B that implements the sealed interface Foo should be a permitted subtype of Foo\n" + "----------\n"); } public void testBug568854_006() { @@ -5518,12 +5518,12 @@ public void testBug568854_006() { "1. ERROR in X.java (at line 2)\n" + " class A implements Foo {}\n" + " ^\n" + - "The class A with a sealed direct superclass or a sealed direct superinterface Foo should be declared either final, sealed, or non-sealed\n" + + "The class A with a sealed direct supertype Foo should be declared either final, sealed, or non-sealed\n" + "----------\n" + "2. ERROR in X.java (at line 3)\n" + " final class B implements Foo {}\n" + " ^^^\n" + - "The type B that implements a sealed interface Foo should be a permitted subtype of Foo\n" + + "The type B that implements the sealed interface Foo should be a permitted subtype of Foo\n" + "----------\n"); } public void testBug568854_007() { @@ -5674,7 +5674,7 @@ public void testBug572205_001() { "2. ERROR in X.java (at line 5)\n" + " sealed interface Shape {}\n" + " ^^^^^\n" + - "Sealed class or interface lacks the permits clause and no class or interface from the same compilation unit declares Shape as its direct superclass or superinterface\n" + + "Sealed type Shape lacks a permits clause and no type from the same compilation unit declares Shape as its direct supertype\n" + "----------\n"); } public void testBug573450_001() { @@ -5766,7 +5766,7 @@ public void testBug573450_005() { "2. ERROR in X.java (at line 2)\n" + " final class Y extends X {}\n" + " ^\n" + - "The type Y extending a sealed class X should be a permitted subtype of X\n" + + "The class Y cannot extend the class X as it is not a permitted subtype of X\n" + "----------\n"); } public void testBug578619_1() { @@ -5806,7 +5806,7 @@ public void testBug578619_2() { "2. ERROR in Bug578619.java (at line 8)\n" + " non-sealed interface I3 extends I2 {}\n" + " ^^\n" + - "An interface I3 declared as non-sealed should have a sealed direct superinterface\n" + + "The non-sealed interface I3 must have a sealed direct superinterface\n" + "----------\n"); } // https://bugs.eclipse.org/bugs/show_bug.cgi?id=576378 @@ -5993,7 +5993,7 @@ public static void main(String [] args) { "2. ERROR in X.java (at line 2)\n" + " record B(int data) implements X {}\n" + " ^\n" + - "The type B that implements a sealed interface X should be a permitted subtype of X\n" + + "The type B that implements the sealed interface X should be a permitted subtype of X\n" + "----------\n"); } @@ -6302,7 +6302,7 @@ int foo(int non, int sealed) { "1. ERROR in X.java (at line 1)\n" + " non-sealed public class X {\n" + " ^\n" + - "A class X declared as non-sealed should have either a sealed direct superclass or a sealed direct superinterface\n" + + "The non-sealed class X must have a sealed direct supertype\n" + "----------\n"); } 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 d4066dc6122..f77df3ada5b 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 @@ -507,7 +507,7 @@ public void test009() throws Exception { "1. ERROR in /SealedTypes/src/p/X.java (at line 2)\n" + " public non-sealed class X {}\n" + " ^\n" + - "A class X declared as non-sealed should have either a sealed direct superclass or a sealed direct superinterface\n" + + "The non-sealed class X must have a sealed direct supertype\n" + "----------\n", this.problemRequestor); } From a0b06016def3b9b22c7968610f3e02ca11d6905d Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Fri, 25 Oct 2024 17:04:47 +0200 Subject: [PATCH 54/63] missing pieces in the AST: parser seems to skip broken annotation altogether when on method parameters (#3159) recover incomplete annotation on argument, while making sure that: + no "real" fields are converted into arguments. + we can merge existing arguments with one recovered from a field expect better recovery also in some existing tests fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3155 --- .../jdt/internal/compiler/parser/Parser.java | 7 +++ .../compiler/parser/RecoveredAnnotation.java | 4 ++ .../compiler/parser/RecoveredMethod.java | 43 +++++++++++++++++++ .../parser/AnnotationDietRecoveryTest.java | 40 +++++++++++++---- .../tests/dom/ASTConverterRecoveryTest.java | 35 +++++++++++++++ 5 files changed, 120 insertions(+), 9 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java index 6b07efd14d8..865c7b960c3 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java @@ -6668,6 +6668,13 @@ protected void consumeZeroTypeAnnotations() { // Name ::= SimpleName // TypeAnnotationsopt ::= $empty pushOnTypeAnnotationLengthStack(0); // signal absence of @308 annotations. + if (this.currentElement instanceof RecoveredAnnotation ann + && ann.parent instanceof RecoveredMethod meth + && !meth.foundOpeningBrace + && this.currentToken == TokenNameRPAREN) { + // take note of an incomplete annotation "@Ann(v=)": + meth.incompleteParameterAnnotationSeen = true; + } } // BEGIN_AUTOGENERATED_REGION_CONSUME_RULE // This method is part of an automatic generation : do NOT edit-modify diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/RecoveredAnnotation.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/RecoveredAnnotation.java index 101211281ae..95b5dd8a17b 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/RecoveredAnnotation.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/RecoveredAnnotation.java @@ -191,6 +191,10 @@ public void setKind(int kind) { this.kind = kind; } + public int sourceStart() { + return this.sourceStart; + } + @Override public int sourceEnd() { if (this.annotation == null) { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/RecoveredMethod.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/RecoveredMethod.java index 39e24a20eeb..9e5dea4a78a 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/RecoveredMethod.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/RecoveredMethod.java @@ -15,6 +15,7 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.parser; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; import org.eclipse.jdt.core.compiler.CharOperation; @@ -48,6 +49,8 @@ public class RecoveredMethod extends RecoveredElement implements TerminalTokens RecoveredAnnotation[] pendingAnnotations; int pendingAnnotationCount; + public boolean incompleteParameterAnnotationSeen = false; + public RecoveredMethod(AbstractMethodDeclaration methodDeclaration, RecoveredElement parent, int bracketBalance, Parser parser){ super(parent, bracketBalance, parser); this.methodDeclaration = methodDeclaration; @@ -97,6 +100,10 @@ public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalanceValu */ @Override public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalanceValue) { + if (recoverAsArgument(fieldDeclaration)) { + resetPendingModifiers(); + return this; + } resetPendingModifiers(); /* local variables inside method can only be final and non void */ @@ -133,6 +140,37 @@ public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalanc // still inside method, treat as local variable return this; // ignore } + +private boolean recoverAsArgument(FieldDeclaration fieldDeclaration) { + if (!this.foundOpeningBrace + && this.methodDeclaration.declarationSourceEnd == 0 + && this.incompleteParameterAnnotationSeen) { // misparsed parameter? + long position = ((long) fieldDeclaration.sourceStart << 32)+fieldDeclaration.sourceEnd; + Argument arg = new Argument(fieldDeclaration.name, position, fieldDeclaration.type, fieldDeclaration.modifiers); + if (this.methodDeclaration.arguments == null) { + this.methodDeclaration.arguments = new Argument[] { arg }; + } else { + int len = this.methodDeclaration.arguments.length; + this.methodDeclaration.arguments = Arrays.copyOf(this.methodDeclaration.arguments, len+1); + this.methodDeclaration.arguments[len] = arg; + } + int annotCount = this.pendingAnnotationCount; + if (this.pendingAnnotations != null) { + int end = 0; + arg.annotations = new Annotation[annotCount]; + for (int i = 0; i < this.pendingAnnotationCount; i++) { + arg.annotations[i] = this.pendingAnnotations[i].annotation; + if (i == 0) + arg.declarationSourceStart = arg.annotations[i].sourceStart; + end = arg.sourceEnd; + } + if (end > 0) + this.methodDeclaration.bodyStart = end + 1; + } + return true; + } + return false; +} /* * Record a local declaration - regular method should have been created a block body */ @@ -606,6 +644,11 @@ public RecoveredElement addAnnotationName(int identifierPtr, int identifierLengt this.pendingAnnotations = new RecoveredAnnotation[5]; this.pendingAnnotationCount = 0; } else { + if (this.pendingAnnotationCount > 0) { + RecoveredAnnotation lastAnnot = this.pendingAnnotations[this.pendingAnnotationCount-1]; + if (lastAnnot.sourceStart() == annotationStart) + return this; + } if (this.pendingAnnotationCount == this.pendingAnnotations.length) { System.arraycopy( this.pendingAnnotations, diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/AnnotationDietRecoveryTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/AnnotationDietRecoveryTest.java index 44c48c6fe00..18ba54146a5 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/AnnotationDietRecoveryTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/parser/AnnotationDietRecoveryTest.java @@ -38,6 +38,10 @@ public class AnnotationDietRecoveryTest extends AbstractCompilerTest { private static final boolean CHECK_ALL_PARSE = true; public static boolean optimizeStringLiterals = false; + static { +// TESTS_NAMES = new String[] { "test0025" }; + } + public AnnotationDietRecoveryTest(String testName){ super(testName); } @@ -1210,7 +1214,7 @@ public void test0024() { "public class X {\n" + " public X() {\n" + " }\n" + - " void foo(int param1) {\n" + + " void foo(int param1, @AnAnnotation(name) int param2) {\n" + " }\n" + "}\n"; @@ -1220,7 +1224,7 @@ public void test0024() { " public X() {\n" + " super();\n" + " }\n" + - " void foo(int param1) {\n" + + " void foo(int param1, @AnAnnotation(name) int param2) {\n" + " }\n" + "}\n"; @@ -1229,7 +1233,13 @@ public void test0024() { expectedDietUnitToString; String expectedCompletionDietUnitToString = - expectedDietUnitToString; + "package a;\n" + + "public class X {\n" + + " public X() {\n" + + " }\n" + + " void foo(int param1) {\n" + + " }\n" + + "}\n"; String testName = ""; checkParse( @@ -1254,7 +1264,7 @@ public void test0025() { "public class X {\n" + " public X() {\n" + " }\n" + - " void foo(int param1) {\n" + + " void foo(int param1, @AnAnnotation(name = $missing$) int param2) {\n" + " }\n" + "}\n"; @@ -1264,7 +1274,7 @@ public void test0025() { " public X() {\n" + " super();\n" + " }\n" + - " void foo(int param1) {\n" + + " void foo(int param1, @AnAnnotation(name = $missing$) int param2) {\n" + " }\n" + "}\n"; @@ -1272,7 +1282,13 @@ public void test0025() { expectedDietUnitToString; String expectedCompletionDietUnitToString = - expectedDietUnitToString; + "package a;\n" + + "public class X {\n" + + " public X() {\n" + + " }\n" + + " void foo(int param1) {\n" + + " }\n" + + "}\n"; String testName = ""; checkParse( @@ -1297,7 +1313,7 @@ public void test0026() { "public class X {\n" + " public X() {\n" + " }\n" + - " void foo(int param1) {\n" + + " void foo(int param1, @AnAnnotation @AnAnnotation1(name1 = \"a\",name2 = $missing$) int param2) {\n" + " }\n" + "}\n"; @@ -1307,7 +1323,7 @@ public void test0026() { " public X() {\n" + " super();\n" + " }\n" + - " void foo(int param1) {\n" + + " void foo(int param1, @AnAnnotation @AnAnnotation1(name1 = \"a\",name2 = $missing$) int param2) {\n" + " }\n" + "}\n"; @@ -1315,7 +1331,13 @@ public void test0026() { expectedDietUnitToString; String expectedCompletionDietUnitToString = - expectedDietUnitToString; + "package a;\n" + + "public class X {\n" + + " public X() {\n" + + " }\n" + + " void foo(int param1) {\n" + + " }\n" + + "}\n"; String testName = ""; checkParse( diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterRecoveryTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterRecoveryTest.java index a85e51425c1..b99b017b2c0 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterRecoveryTest.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterRecoveryTest.java @@ -1085,4 +1085,39 @@ public void test0021() throws JavaModelException { List statements = block.statements(); assertEquals("wrong size", 0, statements.size()); //$NON-NLS-1$ } + + public void testGH3155() throws JavaModelException { + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy( + "/Converter18/src/test/TestDependsOnClass.java", + """ + package test; + public class TestDependsOnClass { + public TestDependsOnClass(@Qualifier(value= ) Object mybean) { } + } + """); + ASTNode result = runConversion(getJLS8(), this.workingCopies[0], true, true); + + assertASTNodeEquals( + """ + package test; + public class TestDependsOnClass { + public TestDependsOnClass( @Qualifier(value=$missing$) Object mybean){ + } + } + """, + result); + + ASTNode node = getASTNode((CompilationUnit) result, 0, 0); + assertNotNull(node); + assertTrue("Not a method declaration", node.getNodeType() == ASTNode.METHOD_DECLARATION); //$NON-NLS-1$ + MethodDeclaration methodDeclaration = (MethodDeclaration) node; + assertTrue(methodDeclaration.isConstructor()); + assertEquals(1, methodDeclaration.parameters().size()); + SingleVariableDeclaration param = (SingleVariableDeclaration) methodDeclaration.parameters().get(0); + assertEquals(1, param.modifiers().size()); + IExtendedModifier mod = (IExtendedModifier) param.modifiers().get(0); + assertTrue(mod.isAnnotation()); + assertEquals("Qualifier", ((Annotation) mod).getTypeName().toString()); + } } From 55fbb8020a19890d6224054c40f59ec67e227dfb Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Fri, 25 Oct 2024 18:57:07 +0200 Subject: [PATCH 55/63] missing pieces in the AST: parser seems to skip broken annotation value when there is a . at the end (#3161) detect the situation during Parser.consumeZeroTypeAnnotations() (similar to #3155) and recover accordingly in RecoveredAnnotation.updateFromParserState() fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3156 --- .../jdt/internal/compiler/parser/Parser.java | 17 +++++---- .../compiler/parser/RecoveredAnnotation.java | 26 +++++++++----- .../tests/dom/ASTConverterRecoveryTest.java | 35 +++++++++++++++++++ 3 files changed, 64 insertions(+), 14 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java index 865c7b960c3..c3ba52f7ce3 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java @@ -6668,12 +6668,17 @@ protected void consumeZeroTypeAnnotations() { // Name ::= SimpleName // TypeAnnotationsopt ::= $empty pushOnTypeAnnotationLengthStack(0); // signal absence of @308 annotations. - if (this.currentElement instanceof RecoveredAnnotation ann - && ann.parent instanceof RecoveredMethod meth - && !meth.foundOpeningBrace - && this.currentToken == TokenNameRPAREN) { - // take note of an incomplete annotation "@Ann(v=)": - meth.incompleteParameterAnnotationSeen = true; + if (this.currentElement instanceof RecoveredAnnotation ann) { + if (ann.parent instanceof RecoveredMethod meth + && !meth.foundOpeningBrace + && this.currentToken == TokenNameRPAREN) { + // take note of an incomplete annotation "@Ann(v=)": + meth.incompleteParameterAnnotationSeen = true; + } + if (this.identifierPtr > ann.identifierPtr) { + ann.hasPendingMemberValueName = true; + ann.errorToken = this.currentToken; + } } } // BEGIN_AUTOGENERATED_REGION_CONSUME_RULE diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/RecoveredAnnotation.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/RecoveredAnnotation.java index 95b5dd8a17b..2de80c2dc22 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/RecoveredAnnotation.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/RecoveredAnnotation.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008 IBM Corporation and others. + * Copyright (c) 2008, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -21,12 +21,13 @@ public class RecoveredAnnotation extends RecoveredElement { public static final int SINGLE_MEMBER = 2; private int kind; - private final int identifierPtr; + final int identifierPtr; private final int identifierLengthPtr; private final int sourceStart; public boolean hasPendingMemberValueName; public int memberValuPairEqualEnd = -1; public Annotation annotation; + public int errorToken; public RecoveredAnnotation(int identifierPtr, int identifierLengthPtr, int sourceStart, RecoveredElement parent, int bracketBalance) { super(parent, bracketBalance); @@ -70,16 +71,24 @@ public void updateFromParserState() { boolean needUpdateRParenPos = false; MemberValuePair pendingMemberValueName = null; + Expression singleValue = null; if (this.hasPendingMemberValueName && this.identifierPtr < parser.identifierPtr) { char[] memberValueName = parser.identifierStack[this.identifierPtr + 1]; long pos = parser.identifierPositionStack[this.identifierPtr + 1]; int start = (int) (pos >>> 32); int end = (int)pos; - int valueEnd = this.memberValuPairEqualEnd > -1 ? this.memberValuPairEqualEnd : end; - SingleNameReference fakeExpression = new SingleNameReference(RecoveryScanner.FAKE_IDENTIFIER, (((long) valueEnd + 1) << 32) + (valueEnd)); - pendingMemberValueName = new MemberValuePair(memberValueName, start, end, fakeExpression); + if (this.errorToken == TerminalTokens.TokenNameDOT) { + // do we need to consult identifierLengthStack to pull more than one name segment? + char[][] qname = new char[][] { memberValueName, RecoveryScanner.FAKE_IDENTIFIER }; + singleValue = new QualifiedNameReference(qname, new long[] {pos,pos}, start, end); + this.kind = SINGLE_MEMBER; + } else { + int valueEnd = this.memberValuPairEqualEnd > -1 ? this.memberValuPairEqualEnd : end; + SingleNameReference fakeExpression = new SingleNameReference(RecoveryScanner.FAKE_IDENTIFIER, (((long) valueEnd + 1) << 32) + (valueEnd)); + pendingMemberValueName = new MemberValuePair(memberValueName, start, end, fakeExpression); + } } parser.identifierPtr = this.identifierPtr; parser.identifierLengthPtr = this.identifierLengthPtr; @@ -136,9 +145,10 @@ public void updateFromParserState() { break; case SINGLE_MEMBER: - if (parser.expressionPtr > -1) { - Expression memberValue = parser.expressionStack[parser.expressionPtr--]; - + Expression memberValue = singleValue != null ? singleValue + : parser.expressionPtr > -1 ? parser.expressionStack[parser.expressionPtr--] + : null; + if (memberValue != null) { SingleMemberAnnotation singleMemberAnnotation = new SingleMemberAnnotation(typeReference, this.sourceStart); singleMemberAnnotation.memberValue = memberValue; singleMemberAnnotation.declarationSourceEnd = memberValue.sourceEnd; diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterRecoveryTest.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterRecoveryTest.java index b99b017b2c0..e2741ecf4bc 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterRecoveryTest.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterRecoveryTest.java @@ -1120,4 +1120,39 @@ public TestDependsOnClass( @Qualifier(value=$missing$) Object mybean){ assertTrue(mod.isAnnotation()); assertEquals("Qualifier", ((Annotation) mod).getTypeName().toString()); } + + public void testGH3156() throws JavaModelException { + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy( + "/Converter18/src/test/X.java", + """ + package test; + public class X { + @Value(spring.) private String value1; + } + """); + ASTNode result = runConversion(getJLS8(), this.workingCopies[0], true, true); + + assertASTNodeEquals( + """ + package test; + public class X { + @Value(spring.$missing$) private String value1; + } + """, + result); + + ASTNode node = getASTNode((CompilationUnit) result, 0, 0); + assertNotNull(node); + assertTrue("Not a field declaration", node.getNodeType() == ASTNode.FIELD_DECLARATION); //$NON-NLS-1$ + FieldDeclaration fieldDeclaration = (FieldDeclaration) node; + IExtendedModifier mod = (IExtendedModifier) fieldDeclaration.modifiers().get(0); + assertTrue(mod.isAnnotation()); + Annotation annotation = (Annotation) mod; + assertEquals("Value", annotation.getTypeName().toString()); + assertTrue(annotation.isSingleMemberAnnotation()); + Expression value = ((SingleMemberAnnotation) annotation).getValue(); + assertEquals(QualifiedName.class, value.getClass()); + assertEquals("spring.$missing$", value.toString()); + } } From 2e98dcb3ee4b5177986bdeefe52b995dd3d4ec46 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Sat, 26 Oct 2024 01:35:32 +0530 Subject: [PATCH 56/63] Bogus null contract violation diagnostic at yield statements (#3154) * Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2522 --- .../internal/compiler/ast/YieldStatement.java | 2 - .../regression/NullAnnotationTests21.java | 112 ++++++++++++++++++ 2 files changed, 112 insertions(+), 2 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/YieldStatement.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/YieldStatement.java index c00cc8f3b7f..58d46fda5e4 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/YieldStatement.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/YieldStatement.java @@ -55,8 +55,6 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl flowInfo = this.expression.analyseCode(currentScope, flowContext, flowInfo); this.expression.checkNPEbyUnboxing(currentScope, flowContext, flowInfo); - if (flowInfo.reachMode() == FlowInfo.REACHABLE && currentScope.compilerOptions().isAnnotationBasedNullAnalysisEnabled) - checkAgainstNullAnnotation(currentScope, flowContext, flowInfo, this.expression); targetContext.recordAbruptExit(); targetContext.expireNullCheckedFieldInfo(); diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTests21.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTests21.java index ac503d57f87..efbe1fe2ef9 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTests21.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTests21.java @@ -1132,4 +1132,116 @@ public static void main(String[] args) { runner.classLibraries = this.LIBS; runner.runConformTest(); } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2522 + // Pattern matching on sealed classes cannot infer NonNull (JDK 21) + public void testIssue2522() { + Runner runner = getDefaultRunner(); + runner.testFiles = new String[] { + "PatternMatching.java", + """ + import org.eclipse.jdt.annotation.*; + + public sealed interface PatternMatching { + + record Stuff() implements PatternMatching {} + + @NonNull + static Stuff match(PatternMatching pm, int v) { + if (v == 0) { + Stuff r = switch (pm) { + case Stuff s -> s; + case null -> throw new NullPointerException(); + }; + return r; // no error here - good + } else if (v == 1) { + Stuff r = switch (pm) { + case Stuff s -> s; + case null -> throw new NullPointerException(); + }; + return null; // get error here - good + } else if (v == 2) { + Stuff r = switch (pm) { + case Stuff s -> s; + }; + return r; // no error here -- good + } else if (v == 3) { + Stuff r = switch (pm) { + case Stuff s -> null; // <<<<<---------------------------- Line 28 - error Why ??? + }; + return r; // get error here - good + } else if (v == 4) { + Stuff r = switch (pm) { + case Stuff s -> s; + case null -> null; // <<<-------------------------------- Line 34 - error Why ?? + }; + return new Stuff(); // no error here // good + } + return new Stuff(); + } + } + """ + }; + runner.expectedCompilerLog = + """ + ---------- + 1. ERROR in PatternMatching.java (at line 20) + return null; // get error here - good + ^^^^ + Null type mismatch: required 'PatternMatching.@NonNull Stuff' but the provided value is null + ---------- + 2. ERROR in PatternMatching.java (at line 30) + return r; // get error here - good + ^ + Null type mismatch: required 'PatternMatching.@NonNull Stuff' but the provided value is null + ---------- + """; + runner.runNegativeTest(); + } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2522 + // Pattern matching on sealed classes cannot infer NonNull (JDK 21) + public void testIssue2522_2() { + Runner runner = getDefaultRunner(); + runner.testFiles = new String[] { + "PatternMatching.java", + """ + import org.eclipse.jdt.annotation.*; + + public sealed interface PatternMatching { + + record Stuff() implements PatternMatching {} + + @NonNull + static Stuff match(PatternMatching pm, int v) { + if (v == 0) { + return switch (pm) { + case Stuff s -> s; + case null -> throw new NullPointerException(); + }; // no error here - good + } else if (v == 2) { + return switch (pm) { + case Stuff s -> s; + }; // no error here -- good + } else if (v == 3) { + return switch (pm) { + case Stuff s -> null; // get error here - good + }; + } + return new Stuff(); + } + } + """ + }; + runner.expectedCompilerLog = + """ + ---------- + 1. ERROR in PatternMatching.java (at line 20) + case Stuff s -> null; // get error here - good + ^^^^ + Null type mismatch: required 'PatternMatching.@NonNull Stuff' but the provided value is null + ---------- + """; + runner.runNegativeTest(); + } } From b9e535a65277a045bb74f115738b4e5268152c5f Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Sat, 26 Oct 2024 02:51:59 +0530 Subject: [PATCH 57/63] [17][compiler][sealed] Incorrect compile error when sealed class tries to permit an interface (#3164) * Fixes https://bugs.eclipse.org/bugs/show_bug.cgi?id=574943 --- .../jdt/internal/compiler/problem/messages.properties | 2 +- .../tests/compiler/regression/SealedTypesTests.java | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties index 733ff39db70..a3b7203abde 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties @@ -1139,7 +1139,7 @@ 1854 = A type declaration {0} that has a permits clause should have a sealed modifier 1855 = The interface {1} with a sealed direct superinterface {0} should be declared either sealed or non-sealed 1856 = Duplicate permitted type {0} -1857 = Permitted class {0} does not declare {1} as direct super class +1857 = Permitted type {0} does not declare {1} as a direct supertype 1858 = Permitted type {0} in a named module {1} should be declared in the same module {1} of declaring type {2} 1859 = Permitted type {0} in an unnamed module should be declared in the same package {1} of declaring type {2} 1860 = Sealed type {0} lacks a permits clause and no type from the same compilation unit declares {0} as its direct supertype diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java index 352229eb81f..f43248bcdb8 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java @@ -419,12 +419,12 @@ public void testBug563806_001() { "1. ERROR in X.java (at line 1)\n" + " public sealed class X permits Y, Z{\n" + " ^\n" + - "Permitted class Y does not declare X as direct super class\n" + + "Permitted type Y does not declare X as a direct supertype\n" + "----------\n" + "2. ERROR in X.java (at line 1)\n" + " public sealed class X permits Y, Z{\n" + " ^\n" + - "Permitted class Z does not declare X as direct super class\n" + + "Permitted type Z does not declare X as a direct supertype\n" + "----------\n"); } public void testBug563806_002() { @@ -444,7 +444,7 @@ public void testBug563806_002() { "1. ERROR in p1\\X.java (at line 2)\n" + " public sealed class X permits Y{\n" + " ^\n" + - "Permitted class Y does not declare p1.X as direct super class\n" + + "Permitted type Y does not declare p1.X as a direct supertype\n" + "----------\n" + "2. ERROR in p1\\X.java (at line 5)\n" + " class Z extends X{}\n" + @@ -859,7 +859,7 @@ public void testBug563806_022() { "2. ERROR in p1\\X.java (at line 2)\n" + " public sealed class X permits Y, p2.Y {\n" + " ^^^^\n" + - "Permitted class Y does not declare p1.X as direct super class\n" + + "Permitted type Y does not declare p1.X as a direct supertype\n" + "----------\n"; runner.runNegativeTest(); } @@ -1362,7 +1362,7 @@ public void testBug564190_6() throws IOException, ClassFormatException { "1. ERROR in p1\\X.java (at line 3)\n" + " sealed class Y extends X permits Z {}\n" + " ^\n" + - "Permitted class Z does not declare p1.X.Y as direct super class\n" + + "Permitted type Z does not declare p1.X.Y as a direct supertype\n" + "----------\n"); } // Test that implicit permitted member type with explicit permits clause From becc3cdf9fab0cb351d4df09ff35142e55a77000 Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Fri, 25 Oct 2024 23:49:06 +0200 Subject: [PATCH 58/63] [23] ECJ rejects local instantiation in early construction context (#3165) Avoid allocating enclosing instance beyond a static method scope Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3153 --- .../compiler/ast/TypeDeclaration.java | 5 ++++- .../jdt/internal/compiler/lookup/Scope.java | 12 +++++++++++ .../regression/SuperAfterStatementsTest.java | 21 +++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java index 21e05c0a9e0..163998c7aad 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java @@ -1069,7 +1069,7 @@ public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, Fl Scope outerScope = currentScope.parent; if (!methodScope.isConstructorCall) { nestedType.addSyntheticArgumentAndField(nestedType.enclosingType()); - outerScope = outerScope.enclosingClassScope(); + outerScope = outerScope.enclosingInstanceScope(); earlySeen = methodScope.isInsideEarlyConstructionContext(nestedType.enclosingType(), false); } if (JavaFeature.FLEXIBLE_CONSTRUCTOR_BODIES.isSupported(currentScope.compilerOptions())) { @@ -1087,6 +1087,8 @@ public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, Fl earlySeen = cs.insideEarlyConstructionContext; } outerScope = outerScope.parent; + if (outerScope instanceof MethodScope ms && ms.isStatic) + break; } } } @@ -1124,6 +1126,7 @@ public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, Fl } } + /** * Access emulation for a local member type * force to emulation of access to direct enclosing instance. diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java index d91b62936ae..421aba217dd 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java @@ -1154,6 +1154,18 @@ public final ClassScope enclosingClassScope() { return null; // may answer null if no type around } + public ClassScope enclosingInstanceScope() { + Scope scope = this; + while (true) { + scope = scope.parent; + if (scope == null || scope instanceof MethodScope ms && ms.isStatic) { + return null; + } else if (scope instanceof ClassScope cs) { + return cs; + } + } + } + public final ClassScope enclosingTopMostClassScope() { Scope scope = this; while (scope != null) { diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java index 06f977ab067..95ad6c0592c 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SuperAfterStatementsTest.java @@ -2330,4 +2330,25 @@ public static void main(String... args) { }; runner.runConformTest(); } + + public void testGH3153() { + runConformTest(new String[] { + "X.java", + """ + public class X { + public static void main(String[] argv) { + class Inner { + Inner() { + class Local {} + new Local() {}; // Error: No enclosing instance of the type X is accessible in scope + super(); + } + } + new Inner(); + } + } + """ }, + ""); + } + } From bad48800cbb52137ed98f6e0cfc0cd4f6ab95217 Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Sat, 26 Oct 2024 15:17:13 +0200 Subject: [PATCH 59/63] "Regression" in 3.39.0 (#3167) + find missing types in more kinds of types to avoid abort due to ProblemReporter.needImplementation() Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3064 --- .../compiler/problem/ProblemReporter.java | 2 +- .../tests/dom/ASTConverterBugsTestJLS8.java | 53 ++++++++++++++++--- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java index ddd73eb142a..6db11b809dd 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java @@ -4874,7 +4874,7 @@ else if (type instanceof ArrayBinding) { } } - if (type.isParameterizedType()) { + if (!(type instanceof MissingTypeBinding)) { List missingTypes = type.collectMissingTypes(null); if (missingTypes != null) { ReferenceContext savedContext = this.referenceContext; diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterBugsTestJLS8.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterBugsTestJLS8.java index 1cc4005fb56..1c3e803d9fd 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterBugsTestJLS8.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverterBugsTestJLS8.java @@ -21,14 +21,7 @@ import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jdt.core.dom.AST; -import org.eclipse.jdt.core.dom.ASTParser; -import org.eclipse.jdt.core.dom.CompilationUnit; -import org.eclipse.jdt.core.dom.MethodDeclaration; -import org.eclipse.jdt.core.dom.MethodInvocation; -import org.eclipse.jdt.core.dom.TypeDeclaration; -import org.eclipse.jdt.core.dom.VariableDeclarationFragment; -import org.eclipse.jdt.core.dom.VariableDeclarationStatement; +import org.eclipse.jdt.core.dom.*; import org.eclipse.jdt.internal.core.dom.rewrite.ASTRewriteFlattener; import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEventStore; @@ -1162,4 +1155,48 @@ Map foo(Class clazz) { deleteProject("P"); } } + +public void testGH3064() throws CoreException { + try { + createJavaProject("P", new String[] { "" }, new String[] { "CONVERTER_JCL_LIB" }, "", "1.8", true); + createFolder("P/src/test"); + createFile("/P/src/test/Action.java", """ + package test; + public class Action, ShardRequest> { + private final Request request; + + protected ShardRequest newShardRequest(Request request) {return null;} // can also be omitted + + protected void performOperation(final int shardIndex) { + ShardRequest shardRequest = newShardRequest(request); + shardRequest.setParentTask(foobar); + } + } + """); + ICompilationUnit cuAction = getCompilationUnit("P/src/test/Action.java"); + ASTParser parser = createASTParser(); + parser.setResolveBindings(true); + parser.setBindingsRecovery(true); + parser.setSource(cuAction); + CompilationUnit cu = (CompilationUnit) parser.createAST(null); + TypeDeclaration type = (TypeDeclaration) cu.types().get(0); + Object decl2 = type.bodyDeclarations().get(2); + assertEquals(MethodDeclaration.class, decl2.getClass()); + Object stat0 = ((MethodDeclaration) decl2).getBody().statements().get(0); + Object stat1 = ((MethodDeclaration) decl2).getBody().statements().get(1); + assertEquals(ExpressionStatement.class, stat1.getClass()); + Expression expr = ((ExpressionStatement) stat1).getExpression(); + assertEquals(MethodInvocation.class, expr.getClass()); + Expression receiver = ((MethodInvocation) expr).getExpression(); + IBinding binding1 = ((SimpleName) receiver).resolveBinding(); + assertNotNull(binding1); + assertEquals(VariableDeclarationStatement.class, stat0.getClass()); + VariableDeclarationFragment frag = (VariableDeclarationFragment) ((VariableDeclarationStatement) stat0) + .fragments().get(0); + IBinding binding0 = frag.getName().resolveBinding(); + assertEquals(binding0, binding1); + } finally { + deleteProject("P"); + } +} } From 41b0c391a2c35eb1f7c5bded469b046b37b772ed Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Sat, 26 Oct 2024 12:56:45 -0400 Subject: [PATCH 60/63] Fix deprecated check routines in ASTNode to also check since value (#2874) Fix deprecated check routines in ASTNode to also check since value * Tweak logic to handle case where annotations aren't ready fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2873 --- .../jdt/internal/compiler/ast/ASTNode.java | 39 ++++++++++++ .../compiler/regression/Deprecated9Test.java | 43 ++++++++++++- .../core/tests/model/ReconcilerTests9.java | 61 ++++++++++++++++++- 3 files changed, 141 insertions(+), 2 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/ASTNode.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/ASTNode.java index a443980ab9c..85be2055391 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/ASTNode.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/ASTNode.java @@ -58,6 +58,8 @@ import org.eclipse.jdt.internal.compiler.ast.TypeReference.AnnotationPosition; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.env.AccessRestriction; +import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.eclipse.jdt.internal.compiler.impl.StringConstant; import org.eclipse.jdt.internal.compiler.lookup.*; @SuppressWarnings({"rawtypes", "unchecked"}) @@ -498,6 +500,8 @@ public final boolean isFieldUseDeprecated(FieldBinding field, Scope scope, int f // inside same unit - no report if (scope.isDefinedInSameUnit(field.declaringClass)) return false; + if (sinceValueUnreached(field, scope)) return false; + // if context is deprecated, may avoid reporting if (!scope.compilerOptions().reportDeprecationInsideDeprecatedCode && scope.isInsideDeprecatedCode()) return false; return true; @@ -548,6 +552,8 @@ public final boolean isMethodUseDeprecated(MethodBinding method, Scope scope, // inside same unit - no report if (scope.isDefinedInSameUnit(method.declaringClass)) return false; + if (sinceValueUnreached(method, scope)) return false; + // non explicit use and non explicitly deprecated - no report if (!isExplicitUse && (method.modifiers & ClassFileConstants.AccDeprecated) == 0) { @@ -559,6 +565,37 @@ public final boolean isMethodUseDeprecated(MethodBinding method, Scope scope, return true; } + private boolean sinceValueUnreached(Binding binding, Scope scope) { + if (binding instanceof TypeBinding typeBinding) { + if (!typeBinding.isReadyForAnnotations()) { + return false; + } + } + AnnotationBinding[] annotations = binding.getAnnotations(); + for (AnnotationBinding annotation : annotations) { + if (annotation != null && annotation.getAnnotationType().id == TypeIds.T_JavaLangDeprecated) { + ElementValuePair[] pairs = annotation.getElementValuePairs(); + for (ElementValuePair pair : pairs) { + if (CharOperation.equals(pair.getName(), TypeConstants.SINCE)) { + if (pair.getValue() instanceof StringConstant strConstant) { + try { + String value = strConstant.stringValue(); + long sinceLevel = CompilerOptions.versionToJdkLevel(value); + long complianceLevel = scope.compilerOptions().complianceLevel; + if (complianceLevel < sinceLevel) { + return true; + } + } catch (NumberFormatException e) { + // do nothing and fall through + } + } + } + } + } + } + return false; + } + public boolean isSuper() { return false; @@ -619,6 +656,8 @@ public final boolean isTypeUseDeprecated(TypeBinding type, Scope scope) { // inside same unit - no report if (scope.isDefinedInSameUnit(refType)) return false; + if (sinceValueUnreached(refType, scope)) return false; + // if context is deprecated, may avoid reporting if (!scope.compilerOptions().reportDeprecationInsideDeprecatedCode && scope.isInsideDeprecatedCode()) return false; return true; diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/Deprecated9Test.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/Deprecated9Test.java index 87e31bc38a6..39b395c55cf 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/Deprecated9Test.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/Deprecated9Test.java @@ -20,6 +20,7 @@ import junit.framework.Test; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.internal.compiler.batch.FileSystem; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.env.INameEnvironment; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; @@ -913,7 +914,10 @@ public void testBug533063_2() throws Exception { "----------\n"; runner.runWarningTest(); } - public void testBug534304() throws Exception { + public void testBug534304_1() throws Exception { + if (this.complianceLevel < ClassFileConstants.JDK13) { + return; + } runNegativeTest( new String[] { "p1/C1.java", @@ -953,6 +957,43 @@ public void testBug534304() throws Exception { "CMissing cannot be resolved to a type\n" + "----------\n"); } + public void testBug534304_2() throws Exception { + if (this.complianceLevel < ClassFileConstants.JDK13) { + runNegativeTest( + new String[] { + "p1/C1.java", + "package p1;\n" + + "\n" + + "import pdep.Dep1;\n" + + "\n" + + "public class C1 {\n" + + " Dep1 f;\n" + + "}\n", + "pdep/Dep1.java", + "package pdep;\n" + + "\n" + + "import pmissing.CMissing;\n" + + "\n" + + "@Deprecated(since=\"13\")\n" + + "@CMissing\n" + + "public class Dep1 {\n" + + "\n" + + "}\n" + }, + "----------\n" + + "----------\n" + + "1. ERROR in pdep\\Dep1.java (at line 3)\n" + + " import pmissing.CMissing;\n" + + " ^^^^^^^^\n" + + "The import pmissing cannot be resolved\n" + + "----------\n" + + "2. ERROR in pdep\\Dep1.java (at line 6)\n" + + " @CMissing\n" + + " ^^^^^^^^\n" + + "CMissing cannot be resolved to a type\n" + + "----------\n"); + } + } public void testBug542795() throws Exception { Runner runner = new Runner(); runner.customOptions = new HashMap<>(); diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ReconcilerTests9.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ReconcilerTests9.java index 0309cf31146..d4cbf8d70a8 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ReconcilerTests9.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ReconcilerTests9.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2021 IBM Corporation and others. + * Copyright (c) 2000, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -402,6 +402,65 @@ public void testTerminalDeprecation2() throws CoreException, IOException { deleteProject("P1"); } } +public void testSinceDeprecation() throws CoreException, IOException { + try { + IJavaProject p1 = createJava9Project("P1"); + String x1Source = "package p;\n" + + "public class X1 {\n" + + "@Deprecated(since=\"10\")\n" + + "public void foo() {}\n" + + "}"; + String x2Source = "package p;\n" + + "public class X2 {\n" + + " public Object field;\n" + + " @Deprecated(since=\"9\")\n" + + " public void m() {}\n" + + "}\n"; + String[] allJarSources = (isJRE9) + ? new String[] { + "p/X1.java", + x1Source, + "/P1/src/p/X2.java", + x2Source } + : new String[] { + "java/lang/Deprecated.java", + "package java.lang;\n" + + "public @interface Deprecated {\n" + + " String since default \"\";" + + " boolean forRemoval() default false;" + + "}\n", + "p/X1.java", + x1Source, + "/P1/src/p/X2.java", + x2Source }; + createJar( + allJarSources, + p1.getProject().getLocation().append("lib.jar").toOSString(), + null, + "9"); + p1.getProject().refreshLocal(2, null); + addLibraryEntry(p1, "/P1/lib.jar", false); + + setUpWorkingCopy("/P1/src/Y.java", + "public class Y {\n" + + " Object foo(p.X1 x1, p.X2 x2) {\n" + + " x2.m();\n" + + " x1.foo();\n" + + " return x2.field;\n" + + " }\n" + + "}\n"); + assertProblems( + "Unexpected problems", + "----------\n" + + "1. WARNING in /P1/src/Y.java (at line 3)\n" + + " x2.m();\n" + + " ^^^\n" + + "The method m() from the type X2 is deprecated since version 9\n" + + "----------\n"); + } finally { + deleteProject("P1"); + } +} public void testBug540541() throws CoreException, IOException { if (!isJRE9) return; IJavaProject project1 = null; From b8bb5d0d4df54ea1e5460778716a2a1b96660675 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Sun, 27 Oct 2024 10:25:46 +0530 Subject: [PATCH 61/63] Roll up example snippets in JEP 441 (Pattern matching for switch) into a junit (#3171) --- .../regression/JEP441SnippetsTest.java | 1328 +++++++++++++++++ .../regression/SealedTypesSpecReviewTest.java | 145 -- .../compiler/regression/SealedTypesTests.java | 44 + .../tests/compiler/regression/TestAll.java | 3 +- .../core/tests/RunVariousPatternsTests.java | 1 + .../core/tests/RunVariousSealedTypeTests.java | 2 +- .../jdt/core/tests/RunVariousSwitchTests.java | 2 + 7 files changed, 1378 insertions(+), 147 deletions(-) create mode 100644 org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JEP441SnippetsTest.java delete mode 100644 org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesSpecReviewTest.java diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JEP441SnippetsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JEP441SnippetsTest.java new file mode 100644 index 00000000000..f3f8b31a046 --- /dev/null +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/JEP441SnippetsTest.java @@ -0,0 +1,1328 @@ +/******************************************************************************* +* Copyright (c) 2024 Advantest Europe GmbH and others. +* +* This program and the accompanying materials +* are made available under the terms of the Eclipse Public License 2.0 +* which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Srikanth Sankaran - initial implementation +*******************************************************************************/ + +package org.eclipse.jdt.core.tests.compiler.regression; + +import junit.framework.Test; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; + +public class JEP441SnippetsTest extends AbstractRegressionTest9 { + + static { +// TESTS_NUMBERS = new int [] { 40 }; +// TESTS_RANGE = new int[] { 1, -1 }; +// TESTS_NAMES = new String[] { "test26"}; + } + + public static Class testClass() { + return JEP441SnippetsTest.class; + } + public static Test suite() { + return buildMinimalComplianceTestSuite(testClass(), F_21); + } + public JEP441SnippetsTest(String testName){ + super(testName); + } + + public void test01() { + runConformTest( + new String[] { + "X.java", + """ + public class X { + + static String formatterPatternSwitch(Object obj) { + return switch (obj) { + case Integer i -> String.format("int %d", i); + case Long l -> String.format("long %d", l); + case Double d -> String.format("double %f", d); + case String s -> String.format("String %s", s); + default -> obj.toString(); + }; + } + + @Override + public String toString() { + return "X X"; + } + + public static void main(String[] args) { + System.out.println(formatterPatternSwitch(10)); + System.out.println(formatterPatternSwitch(10L)); + System.out.println(formatterPatternSwitch(10.0)); + System.out.println(formatterPatternSwitch("String")); + System.out.println(formatterPatternSwitch(new X())); + } + } + """ + }, + "int 10\n" + + "long 10\n" + + "double 10.000000\n" + + "String String\n" + + "X X"); + } + + public void test02() { + runConformTest( + new String[] { + "X.java", + """ + public class X { + + static void testFooBarNew(String s) { + switch (s) { + case null -> System.out.println("Oops"); + case "Foo", "Bar" -> System.out.println("Great"); + default -> System.out.println("Ok"); + } + } + + + public static void main(String[] args) { + testFooBarNew(null); + testFooBarNew("foo"); + testFooBarNew("Foo"); + testFooBarNew("Bar"); + } + } + """ + }, + "Oops\n" + + "Ok\n" + + "Great\n" + + "Great"); + } + + public void test03() { + runConformTest( + new String[] { + "X.java", + """ + public class X { + + static void testStringOld(String response) { + switch (response) { + case null -> { } + case String s -> { + if (s.equalsIgnoreCase("YES")) + System.out.println("You got it"); + else if (s.equalsIgnoreCase("NO")) + System.out.println("Shame"); + else + System.out.println("Sorry?"); + } + } + } + + static void testStringNew(String response) { + switch (response) { + case null -> { } + case String s + when s.equalsIgnoreCase("YES") -> { + System.out.println("You got it"); + } + case String s + when s.equalsIgnoreCase("NO") -> { + System.out.println("Shame"); + } + case String s -> { + System.out.println("Sorry?"); + } + } + } + + static void testStringEnhanced(String response) { + switch (response) { + case null -> { } + case "y", "Y" -> { + System.out.println("You got it"); + } + case "n", "N" -> { + System.out.println("Shame"); + } + case String s + when s.equalsIgnoreCase("YES") -> { + System.out.println("You got it"); + } + case String s + when s.equalsIgnoreCase("NO") -> { + System.out.println("Shame"); + } + case String s -> { + System.out.println("Sorry?"); + } + } + } + + + public static void main(String[] args) { + testStringOld(null); + testStringOld("Yes"); + testStringOld("NO"); + testStringOld("Bar"); + + testStringNew(null); + testStringNew("Yes"); + testStringNew("NO"); + testStringNew("Bar"); + + testStringEnhanced(null); + testStringEnhanced("Y"); + testStringEnhanced("y"); + testStringEnhanced("N"); + testStringEnhanced("n"); + testStringEnhanced("Yes"); + testStringEnhanced("NO"); + testStringEnhanced("Bar"); + + } + } + """ + }, + "You got it\n" + + "Shame\n" + + "Sorry?\n" + + "You got it\n" + + "Shame\n" + + "Sorry?\n" + + "You got it\n" + + "You got it\n" + + "Shame\n" + + "Shame\n" + + "You got it\n" + + "Shame\n" + + "Sorry?"); + } + + public void test04() { + runConformTest( + new String[] { + "X.java", + """ + sealed interface CardClassification permits Suit, Tarot { + } + + enum Suit implements CardClassification { + CLUBS, DIAMONDS, HEARTS, SPADES + } + + final class Tarot implements CardClassification { + } + + public class X { + static void exhaustiveSwitchWithoutEnumSupport(CardClassification c) { + switch (c) { + case Suit s when s == Suit.CLUBS -> { + System.out.println("It's clubs"); + } + case Suit s when s == Suit.DIAMONDS -> { + System.out.println("It's diamonds"); + } + case Suit s when s == Suit.HEARTS -> { + System.out.println("It's hearts"); + } + case Suit s -> { + System.out.println("It's spades"); + } + case Tarot t -> { + System.out.println("It's a tarot"); + } + } + } + + public static void main(String[] args) { + exhaustiveSwitchWithoutEnumSupport(Suit.CLUBS); + exhaustiveSwitchWithoutEnumSupport(Suit.DIAMONDS); + exhaustiveSwitchWithoutEnumSupport(Suit.HEARTS); + exhaustiveSwitchWithoutEnumSupport(Suit.SPADES); + exhaustiveSwitchWithoutEnumSupport(new Tarot()); + } + } + """ + }, + "It's clubs\n" + + "It's diamonds\n" + + "It's hearts\n" + + "It's spades\n" + + "It's a tarot"); + } + + public void test05() { + runConformTest( + new String[] { + "X.java", + """ + sealed interface CardClassification permits Suit, Tarot {} + enum Suit implements CardClassification { CLUBS, DIAMONDS, HEARTS, SPADES } + final class Tarot implements CardClassification {} + public class X { + static void exhaustiveSwitchWithBetterEnumSupport(CardClassification c) { + switch (c) { + case Suit.CLUBS -> { + System.out.println("It's clubs"); + } + case Suit.DIAMONDS -> { + System.out.println("It's diamonds"); + } + case Suit.HEARTS -> { + System.out.println("It's hearts"); + } + case Suit.SPADES -> { + System.out.println("It's spades"); + } + case Tarot t -> { + System.out.println("It's a tarot"); + } + } + } + public static void main(String[] args) { + exhaustiveSwitchWithBetterEnumSupport(Suit.CLUBS); + exhaustiveSwitchWithBetterEnumSupport(Suit.DIAMONDS); + exhaustiveSwitchWithBetterEnumSupport(Suit.HEARTS); + exhaustiveSwitchWithBetterEnumSupport(Suit.SPADES); + exhaustiveSwitchWithBetterEnumSupport(new Tarot()); + } + } + """ + }, + "It's clubs\n" + + "It's diamonds\n" + + "It's hearts\n" + + "It's spades\n" + + "It's a tarot"); + } + + public void test06() { + runConformTest( + new String[] { + "X.java", + """ + // As of Java 21 + sealed interface Currency permits Coin {} + enum Coin implements Currency { HEADS, TAILS } + + public class X { + static void goodEnumSwitch1(Currency c) { + switch (c) { + case Coin.HEADS -> { // Qualified name of enum constant as a label + System.out.println("Heads"); + } + case Coin.TAILS -> { + System.out.println("Tails"); + } + } + } + + static void goodEnumSwitch2(Coin c) { + switch (c) { + case HEADS -> { + System.out.println("Heads"); + } + case Coin.TAILS -> { // Unnecessary qualification but allowed + System.out.println("Tails"); + } + } + } + + public static void main(String[] args) { + goodEnumSwitch1(Coin.HEADS); + goodEnumSwitch1(Coin.TAILS); + + goodEnumSwitch2(Coin.HEADS); + goodEnumSwitch2(Coin.TAILS); + + } + } + """ + }, + "Heads\n" + + "Tails\n" + + "Heads\n" + + "Tails"); + } + + public void test07() { + runNegativeTest( + new String[] { + "X.java", + """ + // As of Java 21 + sealed interface Currency permits Coin {} + enum Coin implements Currency { HEADS, TAILS } + + public class X { + static void badEnumSwitch(Currency c) { + switch (c) { + case Coin.HEADS -> { + System.out.println("Heads"); + } + case TAILS -> { // Error - TAILS must be qualified + System.out.println("Tails"); + } + default -> { + System.out.println("Some currency"); + } + } + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 11)\n" + + " case TAILS -> { // Error - TAILS must be qualified\n" + + " ^^^^^\n" + + "TAILS cannot be resolved to a variable\n" + + "----------\n"); + } + + public void test08() { + runConformTest( + new String[] { + "X.java", + """ + public class X { + + static void testNew(Object obj) { + switch (obj) { + case String s when s.length() == 4 -> System.out.println("Bad word!"); + case String s when s.length() == 1 -> System.out.println("Monsyllable"); + default -> System.out.println(" "); + } + } + + public static void main(String[] args) { + testNew("X"); + testNew("***"); + testNew("*!#^"); + } + } + """ + }, + "Monsyllable\n" + + " \n" + + "Bad word!"); + } + + public void test09() { + runConformTest( + new String[] { + "X.java", + """ + public class X { + record Point(int i, int j) {} + enum Color { RED, GREEN, BLUE; } + + static void typeTester(Object obj) { + switch (obj) { + case null -> System.out.println("null"); + case String s -> System.out.println("String"); + case Color c -> System.out.println("Color: " + c.toString()); + case Point p -> System.out.println("Record class: " + p.toString()); + case int[] ia -> System.out.println("Array of ints of length" + ia.length); + default -> System.out.println("Something else"); + } + } + + public static void main(String[] args) { + typeTester(null); + typeTester("Hello"); + typeTester(Color.RED); + typeTester(new Point(0, 0)); + typeTester(new int[10]); + typeTester(10); + } + } + """ + }, + "null\n" + + "String\n" + + "Color: RED\n" + + "Record class: Point[i=0, j=0]\n" + + "Array of ints of length10\n" + + "Something else"); + } + + public void test10() { + runConformTest( + new String[] { + "X.java", + """ + public class X { + + static void first(Object obj) { + switch (obj) { + case String s -> + System.out.println("A string: " + s); + case CharSequence cs -> + System.out.println("A sequence of length " + cs.length()); + default -> { + break; + } + } + } + + public static void main(String[] args) { + first("Hello"); + first(new StringBuilder("Hello")); + } + } + """ + }, + "A string: Hello\n" + + "A sequence of length 5"); + } + + public void test11() { + runNegativeTest( + new String[] { + "X.java", + """ + public class X { + + static void first(Object obj) { + switch (obj) { + case CharSequence cs -> + System.out.println("A sequence of length " + cs.length()); + case String s -> + System.out.println("A string: " + s); + default -> { + break; + } + } + } + + public static void main(String[] args) { + first("Hello"); + first(new StringBuilder("Hello")); + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " case String s ->\n" + + " ^^^^^^^^\n" + + "This case label is dominated by one of the preceding case labels\n" + + "----------\n"); + } + + public void test12() { + runNegativeTest( + new String[] { + "X.java", + """ + public class X { + + static void first(Object obj) { + switch (obj) { + case String s -> + System.out.println("A String: " + s); + case String s when s.length() > 0 -> + System.out.println("A string: " + s); + default -> { + break; + } + } + } + + public static void main(String[] args) { + first("Hello"); + first(new StringBuilder("Hello")); + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " case String s when s.length() > 0 ->\n" + + " ^^^^^^^^\n" + + "This case label is dominated by one of the preceding case labels\n" + + "----------\n"); + } + + public void test13() { + runConformTest( + new String[] { + "X.java", + """ + public class X { + + static void first(Object obj) { + switch (obj) { + case String s when s.length() > 0 -> + System.out.println("A guarded string: " + s); + case String s -> + System.out.println("A String: " + s); + default -> { + break; + } + } + } + + public static void main(String[] args) { + first("Hello"); + first(new StringBuilder("Hello")); + } + } + """ + }, + "A guarded string: Hello"); + } + + public void test14() { + runNegativeTest( + new String[] { + "X.java", + """ + public class X { + + static void first(Object obj) { + switch (obj) { + case String s when true -> + System.out.println("A pseudo guarded string: " + s); + case String s when s.length() > 0 -> + System.out.println("A guarded string: " + s); + case String s -> + System.out.println("A String: " + s); + default -> { + break; + } + } + } + + public static void main(String[] args) { + first("Hello"); + first(new StringBuilder("Hello")); + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " case String s when s.length() > 0 ->\n" + + " ^^^^^^^^\n" + + "This case label is dominated by one of the preceding case labels\n" + + "----------\n" + + "2. ERROR in X.java (at line 9)\n" + + " case String s ->\n" + + " ^^^^^^^^\n" + + "This case label is dominated by one of the preceding case labels\n" + + "----------\n"); + } + + public void test15() { + runNegativeTest( + new String[] { + "X.java", + """ + public class X { + static void first(Integer obj) { + switch (obj) { + case Integer i -> + System.out.println("An integer: " + i); + case 42 -> + System.out.println("42"); + default -> { + break; + } + } + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " case 42 ->\n" + + " ^^\n" + + "This case label is dominated by one of the preceding case labels\n" + + "----------\n" + + "2. ERROR in X.java (at line 8)\n" + + " default -> {\n" + + " ^^^^^^^\n" + + "Switch case cannot have both unconditional pattern and default label\n" + + "----------\n"); + } + + public void test16() { + runNegativeTest( + new String[] { + "X.java", + """ + public class X { + enum E { + ONE, TWO; + } + static void first(E e) { + switch (e) { + case E e1 -> + System.out.println("An ENUM: " + e1); + case ONE -> + System.out.println("42"); + default -> { + break; + } + } + } + + static void second(E e) { + switch (e) { + case ONE -> + System.out.println("An ENUM: "); + case TWO -> + System.out.println("42"); + default -> { + break; + } + } + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 9)\n" + + " case ONE ->\n" + + " ^^^\n" + + "This case label is dominated by one of the preceding case labels\n" + + "----------\n" + + "2. ERROR in X.java (at line 11)\n" + + " default -> {\n" + + " ^^^^^^^\n" + + "Switch case cannot have both unconditional pattern and default label\n" + + "----------\n"); + } + + public void test17() { + runNegativeTest( + new String[] { + "X.java", + """ + public class X { + static void first(Object selector) { + switch (selector) { + case String s when s.length() > 1 -> + System.out.println("Some string"); + case "Hello" -> + System.out.println("42"); + default -> { + break; + } + } + } + + static void second(String selector) { + switch (selector) { + case String s when s.length() > 1 -> + System.out.println("Some string"); + case "Hello" -> + System.out.println("42"); + case String s -> + System.out.println("unconditional"); + default -> { + break; + } + } + } + + static void third(String selector) { + switch (selector) { + case String s when s.length() > 1 -> + System.out.println("Some string"); + case String s -> + System.out.println("unconditional"); + case "Hello" -> + System.out.println("42"); + default -> { + break; + } + } + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " case \"Hello\" ->\n" + + " ^^^^^^^\n" + + "Case constant of type String is incompatible with switch selector type Object\n" + + "----------\n" + + "2. ERROR in X.java (at line 22)\n" + + " default -> {\n" + + " ^^^^^^^\n" + + "Switch case cannot have both unconditional pattern and default label\n" + + "----------\n" + + "3. ERROR in X.java (at line 34)\n" + + " case \"Hello\" ->\n" + + " ^^^^^^^\n" + + "This case label is dominated by one of the preceding case labels\n" + + "----------\n" + + "4. ERROR in X.java (at line 36)\n" + + " default -> {\n" + + " ^^^^^^^\n" + + "Switch case cannot have both unconditional pattern and default label\n" + + "----------\n"); + } + + public void test18() { + runConformTest( + new String[] { + "X.java", + """ + public class X { + static void first(Integer i) { + switch (i) { + case -1, 1 -> System.out.println("Special cases"); + case Integer j when j > 0 -> System.out.println("Positive integer cases"); + case Integer j -> System.out.println("All the remaining integers"); + } + } + + public static void main(String[] args) { + first(-1); + first(1); + first(42); + first(0); + } + } + """ + }, + "Special cases\n" + + "Special cases\n" + + "Positive integer cases\n" + + "All the remaining integers"); + } + + public void test19() { + runNegativeTest( + new String[] { + "X.java", + """ + public class X { + // As of Java 21 + static void matchAll(String s) { + switch(s) { + case String t: + System.out.println(t); + break; + default: + System.out.println("Something else"); // Error - dominated! + } + } + + static void matchAll2(String s) { + switch(s) { + case Object o: + System.out.println("An Object"); + break; + default: + System.out.println("Something else"); // Error - dominated! + } + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 8)\n" + + " default:\n" + + " ^^^^^^^\n" + + "Switch case cannot have both unconditional pattern and default label\n" + + "----------\n" + + "2. ERROR in X.java (at line 18)\n" + + " default:\n" + + " ^^^^^^^\n" + + "Switch case cannot have both unconditional pattern and default label\n" + + "----------\n"); + } + + public void test20() { + runNegativeTest( + new String[] { + "X.java", + """ + public class X { + // As of Java 21 + static int coverage(Object obj) { + return switch (obj) { // Error - not exhaustive + case String s -> s.length(); + }; + } + + // As of Java 21 + static int coverage(Object obj) { + return switch (obj) { // Error - still not exhaustive + case String s -> s.length(); + case Integer i -> i; + }; + } + + // As of Java 21 + static int coverage(Object obj) { + return switch (obj) { + case String s -> s.length(); + case Integer i -> i; + default -> 0; + }; + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 3)\n" + + " static int coverage(Object obj) {\n" + + " ^^^^^^^^^^^^^^^^^^^^\n" + + "Duplicate method coverage(Object) in type X\n" + + "----------\n" + + "2. ERROR in X.java (at line 4)\n" + + " return switch (obj) { // Error - not exhaustive\n" + + " ^^^\n" + + "A switch expression should have a default case\n" + + "----------\n" + + "3. ERROR in X.java (at line 10)\n" + + " static int coverage(Object obj) {\n" + + " ^^^^^^^^^^^^^^^^^^^^\n" + + "Duplicate method coverage(Object) in type X\n" + + "----------\n" + + "4. ERROR in X.java (at line 11)\n" + + " return switch (obj) { // Error - still not exhaustive\n" + + " ^^^\n" + + "A switch expression should have a default case\n" + + "----------\n" + + "5. ERROR in X.java (at line 18)\n" + + " static int coverage(Object obj) {\n" + + " ^^^^^^^^^^^^^^^^^^^^\n" + + "Duplicate method coverage(Object) in type X\n" + + "----------\n"); + } + + public void test21() { + runNegativeTest( + new String[] { + "X.java", + """ + public class X { + enum Color { RED, YELLOW, GREEN } + + // As of Java 21 + static int coverage(Color color) { + + int numLetters = switch (color) { // Exhaustive! + case RED -> 3; + case GREEN -> 5; + case YELLOW -> 6; + }; + + numLetters = switch (color) { // Error - not exhaustive! + case RED -> 3; + case GREEN -> 5; + }; + + numLetters = switch (color) { + case RED -> 3; + case GREEN -> 5; + case YELLOW -> 6; + default -> throw new UnsupportedOperationException(color.toString()); + }; + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 13)\n" + + " numLetters = switch (color) { // Error - not exhaustive!\n" + + " ^^^^^\n" + + "A Switch expression should cover all possible values\n" + + "----------\n"); + } + + public void test22() { + runConformTest( + new String[] { + "X.java", + """ + public class X { + sealed interface S permits A, B, C {} + final class A implements S {} + final class B implements S {} + record C(int i) implements S {} // Implicitly final + + static int testSealedExhaustive(S s) { + return switch (s) { + case A a -> 1; + case B b -> 2; + case C c -> 3; + }; + } + + public static void main(String[] args) { + System.out.println(testSealedExhaustive(new X().new A())); + System.out.println(testSealedExhaustive(new X().new B())); + System.out.println(testSealedExhaustive(new C(10))); + + } + } + """ + }, + "1\n2\n3"); + } + + public void test23() { + runNegativeTest( + new String[] { + "X.java", + """ + public class X { + sealed interface S permits A, B, C {} + final class A implements S {} + final class B implements S {} + record C(int i) implements S {} // Implicitly final + + static int testSealedExhaustive(S s) { + return switch (s) { + case A a -> 1; + case B b -> 2; + }; + } + + public static void main(String[] args) { + System.out.println(testSealedExhaustive(new X().new A())); + System.out.println(testSealedExhaustive(new X().new B())); + System.out.println(testSealedExhaustive(new C(10))); + + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 8)\r\n" + + " return switch (s) {\r\n" + + " ^\n" + + "A switch expression should have a default case\n" + + "----------\n"); + } + + public void test24() { + runNegativeTest( + new String[] { + "X.java", + """ + public class X { + sealed interface I permits A, B {} + final class A implements I {} + final class B implements I {} + + static int testGenericSealedExhaustive(I i) { + return switch (i) { + // Exhaustive as no A case possible! + case B bi -> 42; + }; + } + + static int testGenericSealedExhaustive2(I i) { + return switch (i) { + // Exhaustive as no A case possible! + case B bi -> 42; + case A ai -> 42; + }; + } + } + """ + }, + "----------\n" + + "1. WARNING in X.java (at line 3)\n" + + " final class A implements I {}\n" + + " ^\n" + + "The type parameter X is hiding the type X\n" + + "----------\n" + + "2. ERROR in X.java (at line 17)\n" + + " case A ai -> 42;\n" + + " ^^^^^^^^^^^^^\n" + + "Incompatible operand types X.I and X.A\n" + + "----------\n"); + } + + public void test25() { + runNegativeTest( + new String[] { + "X.java", + """ + public class X { + // As of Java 21 + sealed interface S permits A, B, C {} + final class A implements S {} + final class B implements S {} + record C(int i) implements S {} // Implicitly final + + static void switchStatementExhaustive(S s) { + switch (s) { // Error - not exhaustive; + // missing clause for permitted class B! + case A a : + System.out.println("A"); + break; + case C c : + System.out.println("C"); + break; + }; + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 9)\n" + + " switch (s) { // Error - not exhaustive;\n" + + " ^\n" + + "An enhanced switch statement should be exhaustive; a default label expected\n" + + "----------\n"); + } + + public void test26() { + if (this.complianceLevel < ClassFileConstants.JDK22) // multi pattern case labels used + return; + runNegativeTest( + new String[] { + "X.java", + """ + public class X { + static void testScope1(Object obj) { + switch (obj) { + case Character c + when c.charValue() == 7: + System.out.println("Ding!"); + break; + default: + break; + } + } + + static void testScope2(Object obj) { + switch (obj) { + case Character c -> { + if (c.charValue() == 7) { + System.out.println("Ding!"); + } + System.out.println("Character"); + } + case Integer i -> + throw new IllegalStateException("Invalid Integer argument: " + + i.intValue()); + default -> { + break; + } + } + } + + static void testScope3(Object obj) { + switch (obj) { + case Character c: + if (c.charValue() == 7) { + System.out.print("Ding "); + } + if (c.charValue() == 9) { + System.out.print("Tab "); + } + System.out.println("Character"); + default: + System.out.println(c); + } + } + + static void testScopeError(Object obj) { + switch (obj) { + case Character c: + if (c.charValue() == 7) { + System.out.print("Ding "); + } + if (c.charValue() == 9) { + System.out.print("Tab "); + } + System.out.println("character"); + case Integer i: // Compile-time error + System.out.println("An integer " + i); + default: + break; + } + } + + static void testScopeError2(Object obj) { + switch (obj) { + case Character c: + if (c.charValue() == 7) { + System.out.print("Ding "); + } + if (c.charValue() == 9) { + System.out.print("Tab "); + } + System.out.println("character"); + case Character c, Integer i: // Compile-time error + System.out.println("An integer " + i); + default: + break; + } + } + + void testScope4(Object obj) { + switch (obj) { + case String s: + System.out.println("A string: " + s); // s in scope here! + default: + System.out.println("Done"); // s not in scope here + } + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 41)\n" + + " System.out.println(c);\n" + + " ^\n" + + "c cannot be resolved to a variable\n" + + "----------\n" + + "2. ERROR in X.java (at line 55)\n" + + " case Integer i: // Compile-time error\n" + + " ^^^^^^^^^^^^^^\n" + + "Illegal fall-through to a pattern\n" + + "----------\n" + + "3. ERROR in X.java (at line 72)\n" + + " case Character c, Integer i: // Compile-time error\n" + + " ^^^^^^^^^^^\n" + + "This case label is dominated by one of the preceding case labels\n" + + "----------\n" + + "4. ERROR in X.java (at line 72)\n" + + " case Character c, Integer i: // Compile-time error\n" + + " ^\n" + + "Named pattern variables are not allowed here\n" + + "----------\n" + + "5. ERROR in X.java (at line 72)\n" + + " case Character c, Integer i: // Compile-time error\n" + + " ^\n" + + "Named pattern variables are not allowed here\n" + + "----------\n" + + "6. ERROR in X.java (at line 73)\n" + + " System.out.println(\"An integer \" + i);\n" + + " ^\n" + + "i cannot be resolved to a variable\n" + + "----------\n"); + } + + public void test27() { + runConformTest( + new String[] { + "X.java", + """ + public class X { + static void nullMatch(Object obj) { + switch (obj) { + case null -> System.out.println("null!"); + case String s -> System.out.println("String"); + default -> System.out.println("Something else"); + } + } + + static void nullMatch2(Object obj) { + switch (obj) { + case String s -> System.out.println("String: " + s); + case Integer i -> System.out.println("Integer"); + default -> System.out.println("default"); + } + } + + static void nullMatch3(Object obj) { + switch (obj) { + case null -> throw new NullPointerException(); + case String s -> System.out.println("String: " + s); + case Integer i -> System.out.println("Integer"); + default -> System.out.println("default"); + } + } + + static void nullMatch4(Object obj) { + switch (obj) { + case String s -> System.out.println("String: " + s); + case Integer i -> System.out.println("Integer"); + case null, default -> System.out.println("null|default"); + } + } + + public static void main(String[] args) { + nullMatch(null); + int nulls = 0; + try { + nullMatch2(null); + } catch(NullPointerException npe) { + nulls++; + } + try { + nullMatch3(null); + } catch(NullPointerException npe) { + nulls++; + } + nullMatch4(null); + if (nulls == 2) { + System.out.println("OK!"); + } + + } + } + """ + }, + "null!\nnull|default\nOK!"); + } + + public void test28() { + runConformTest( + new String[] { + "X.java", + """ + public class X { + // As of Java 21 + record R(int i) { + public int i() { // bad (but legal) accessor method for i + return i / 0; + } + } + + static void exampleAnR(R r) { + switch(r) { + case R(var i): System.out.println(i); + } + } + + static void example(Object obj) { + switch (obj) { + case R r when (r.i / 0 == 1): System.out.println("It's an R!"); + default: break; + } + } + + public static void main(String [] args) { + try { + exampleAnR(new R(42)); + } catch (MatchException m) { + System.out.println("ME:Ok!"); + } + + try { + example(new R(42)); + } catch (MatchException m) { + System.out.println("Broken!"); + } catch(ArithmeticException ae) { + System.out.println("AE:Ok!"); + } + } + + } + """ + }, + "ME:Ok!\nAE:Ok!"); + } +} \ No newline at end of file diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesSpecReviewTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesSpecReviewTest.java deleted file mode 100644 index 27f22cecdc6..00000000000 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesSpecReviewTest.java +++ /dev/null @@ -1,145 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2024 Advantest Europe GmbH and others. -* -* This program and the accompanying materials -* are made available under the terms of the Eclipse Public License 2.0 -* which accompanies this distribution, and is available at -* https://www.eclipse.org/legal/epl-2.0/ -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* Srikanth Sankaran - initial implementation -*******************************************************************************/ - -package org.eclipse.jdt.core.tests.compiler.regression; - -import java.util.Map; -import junit.framework.Test; -import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; - -public class SealedTypesSpecReviewTest extends AbstractRegressionTest9 { - - static { -// TESTS_NUMBERS = new int [] { 40 }; -// TESTS_RANGE = new int[] { 1, -1 }; -// TESTS_NAMES = new String[] { "testBug564498_6"}; - } - - public static Class testClass() { - return SealedTypesSpecReviewTest.class; - } - public static Test suite() { - return buildMinimalComplianceTestSuite(testClass(), F_17); - } - public SealedTypesSpecReviewTest(String testName){ - super(testName); - } - - // Enables the tests to run individually - protected Map getCompilerOptions() { - Map defaultOptions = super.getCompilerOptions(); - defaultOptions.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_17); - defaultOptions.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_17); - defaultOptions.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_17); - defaultOptions.put(CompilerOptions.OPTION_ReportPreviewFeatures, CompilerOptions.IGNORE); - defaultOptions.put(CompilerOptions.OPTION_Store_Annotations, CompilerOptions.ENABLED); - return defaultOptions; - } - - @Override - protected void runConformTest(String[] testFiles, String expectedOutput) { - runConformTest(testFiles, expectedOutput, getCompilerOptions()); - } - - @Override - protected void runConformTest(String[] testFiles, String expectedOutput, Map customOptions) { - Runner runner = new Runner(); - runner.testFiles = testFiles; - runner.expectedOutputString = expectedOutput; - runner.vmArguments = new String[] {"--enable-preview"}; - runner.customOptions = customOptions; - runner.javacTestOptions = JavacTestOptions.forReleaseWithPreview("17"); - runner.runConformTest(); - } - @Override - protected void runNegativeTest(String[] testFiles, String expectedCompilerLog) { - runNegativeTest(testFiles, expectedCompilerLog, JavacTestOptions.forReleaseWithPreview("17")); - } - protected void runWarningTest(String[] testFiles, String expectedCompilerLog) { - runWarningTest(testFiles, expectedCompilerLog, (Map) null); - } - protected void runWarningTest(String[] testFiles, String expectedCompilerLog, Map customOptions) { - runWarningTest(testFiles, expectedCompilerLog, customOptions, null); - } - - protected void runWarningTest(String[] testFiles, String expectedCompilerLog, String expectedOutput) { - - Runner runner = new Runner(); - runner.testFiles = testFiles; - runner.expectedCompilerLog = expectedCompilerLog; - runner.expectedOutputString = expectedOutput; - runner.customOptions = getCompilerOptions(); - runner.vmArguments = new String[] {"--enable-preview"}; - runner.javacTestOptions = JavacTestOptions.forReleaseWithPreview("16"); - runner.runWarningTest(); - } - - protected void runWarningTest(String[] testFiles, String expectedCompilerLog, - Map customOptions, String javacAdditionalTestOptions) { - - Runner runner = new Runner(); - runner.testFiles = testFiles; - runner.expectedCompilerLog = expectedCompilerLog; - runner.customOptions = customOptions; - runner.vmArguments = new String[] {"--enable-preview"}; - runner.javacTestOptions = javacAdditionalTestOptions == null ? JavacTestOptions.forReleaseWithPreview("16") : - JavacTestOptions.forReleaseWithPreview("16", javacAdditionalTestOptions); - runner.runWarningTest(); - } - - - // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2709 - // [Sealed types] Disjointness behavior difference vis a vis javac - /* A class named C is disjoint from an interface named I if (i) it is not the case that C <: I, and (ii) one of the following cases applies: - – C is freely extensible (§8.1.1.2), and I is sealed, and C is disjoint from all of the permitted direct subclasses and subinterfaces of I. - */ - public void testIssue2709() { - runNegativeTest( - new String[] { - "X.java", - """ - public class X { - sealed interface I permits C1 {} - non-sealed class C1 implements I {} - class C2 extends C1 {} - class C3 {} - { - I i; - i = (I) (C1) null; - i = (I) (C2) null; - i = (I) (C3) null; - i = (C2) (C3) null; - i = (C1) (C3) null; - } - } - """ - }, - "----------\n" + - "1. ERROR in X.java (at line 10)\n" + - " i = (I) (C3) null;\n" + - " ^^^^^^^^^^^^^\n" + - "Cannot cast from X.C3 to X.I\n" + - "----------\n" + - "2. ERROR in X.java (at line 11)\n" + - " i = (C2) (C3) null;\n" + - " ^^^^^^^^^^^^^^\n" + - "Cannot cast from X.C3 to X.C2\n" + - "----------\n" + - "3. ERROR in X.java (at line 12)\n" + - " i = (C1) (C3) null;\n" + - " ^^^^^^^^^^^^^^\n" + - "Cannot cast from X.C3 to X.C1\n" + - "----------\n"); - } -} \ No newline at end of file diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java index f43248bcdb8..5c8f8d1b896 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/SealedTypesTests.java @@ -6810,4 +6810,48 @@ final class Z extends Y {} "The hierarchy of the type Z is inconsistent\n" + "----------\n"); } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2709 + // [Sealed types] Disjointness behavior difference vis a vis javac + /* A class named C is disjoint from an interface named I if (i) it is not the case that C <: I, and (ii) one of the following cases applies: + – C is freely extensible (§8.1.1.2), and I is sealed, and C is disjoint from all of the permitted direct subclasses and subinterfaces of I. + */ + public void testIssue2709() { + runNegativeTest( + new String[] { + "X.java", + """ + public class X { + sealed interface I permits C1 {} + non-sealed class C1 implements I {} + class C2 extends C1 {} + class C3 {} + { + I i; + i = (I) (C1) null; + i = (I) (C2) null; + i = (I) (C3) null; + i = (C2) (C3) null; + i = (C1) (C3) null; + } + } + """ + }, + "----------\n" + + "1. ERROR in X.java (at line 10)\n" + + " i = (I) (C3) null;\n" + + " ^^^^^^^^^^^^^\n" + + "Cannot cast from X.C3 to X.I\n" + + "----------\n" + + "2. ERROR in X.java (at line 11)\n" + + " i = (C2) (C3) null;\n" + + " ^^^^^^^^^^^^^^\n" + + "Cannot cast from X.C3 to X.C2\n" + + "----------\n" + + "3. ERROR in X.java (at line 12)\n" + + " i = (C1) (C3) null;\n" + + " ^^^^^^^^^^^^^^\n" + + "Cannot cast from X.C3 to X.C1\n" + + "----------\n"); + } } diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java index 9aa275bb52c..17b52ce66e5 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java @@ -228,7 +228,6 @@ public static Test suite() { // add 17 specific test here (check duplicates) ArrayList since_17 = new ArrayList(); since_17.add(SealedTypesTests.class); - since_17.add(SealedTypesSpecReviewTest.class); since_17.add(InstanceofPrimaryPatternTest.class); since_17.add(BatchCompilerTest_17.class); @@ -243,6 +242,8 @@ public static Test suite() { since_21.add(RecordPatternProjectTest.class); since_21.add(NullAnnotationTests21.class); since_21.add(BatchCompilerTest_21.class); + since_21.add(JEP441SnippetsTest.class); + // add 21 specific test here (check duplicates) ArrayList since_22 = new ArrayList(); diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousPatternsTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousPatternsTests.java index ae9c4c6ff29..5e2cd4b72ec 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousPatternsTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousPatternsTests.java @@ -63,6 +63,7 @@ public static Class[] getAllTestClasses() { InstanceofExpressionTest.class, PrimitiveInPatternsTest.class, PrimitiveInPatternsTestSH.class, + JEP441SnippetsTest.class, }; } diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSealedTypeTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSealedTypeTests.java index 62e4b67ed02..5b5e0b257c5 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSealedTypeTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSealedTypeTests.java @@ -51,7 +51,7 @@ public static Class[] getAllTestClasses() { RecordsRestrictedClassTest.class, ScannerTest.class, SealedTypesTests.class, - SealedTypesSpecReviewTest.class, + JEP441SnippetsTest.class, SwitchPatternTest.class, SwitchPatternTest22.class, UnnamedPatternsAndVariablesTest.class, diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSwitchTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSwitchTests.java index d4f2160e541..2ba7513ef62 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSwitchTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/RunVariousSwitchTests.java @@ -21,6 +21,7 @@ import junit.framework.TestSuite; import org.eclipse.jdt.core.tests.compiler.parser.ComplianceDiagnoseTest; import org.eclipse.jdt.core.tests.compiler.regression.InstanceofPrimaryPatternTest; +import org.eclipse.jdt.core.tests.compiler.regression.JEP441SnippetsTest; import org.eclipse.jdt.core.tests.compiler.regression.PatternMatching16Test; import org.eclipse.jdt.core.tests.compiler.regression.RecordPatternTest; import org.eclipse.jdt.core.tests.compiler.regression.SwitchExpressionsYieldTest; @@ -52,6 +53,7 @@ public static Class[] getAllTestClasses() { InstanceofPrimaryPatternTest.class, PatternMatching16Test.class, UnnamedPatternsAndVariablesTest.class, + JEP441SnippetsTest.class, JavaSearchBugs14SwitchExpressionTests.class, ASTRewritingSwitchExpressionsTest.class, From 2caa4feaa0fa1c413ffcf1ea69ee733a16ede6a8 Mon Sep 17 00:00:00 2001 From: Srikanth Sankaran <131454720+srikanth-sankaran@users.noreply.github.com> Date: Mon, 28 Oct 2024 03:57:35 +0530 Subject: [PATCH 62/63] Regression test for https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3173 (#3174) --- .../regression/RecordPatternTest.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordPatternTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordPatternTest.java index 1d5c01ba787..d8034188a64 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordPatternTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordPatternTest.java @@ -4736,4 +4736,27 @@ case X(int x): ---------- """); } + + // https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3173 + // [21][Enhanced Switch] False error about allegedly non-exhaustive switch + public void testIssue3173() { + runConformTest(new String[] { + "RecordPatternDemo.java", + """ + public class RecordPatternDemo { + public static void main(String[] args) { + record Box(T contents) { } + + Box> doubleBoxed = new Box<>(new Box<>("Contents")); + String unboxed = switch (doubleBoxed) { + case Box(Box(String s)) -> s; + }; + + System.out.println(unboxed); + } + } + """ + }, + "Contents"); + } } \ No newline at end of file From b5909cd88bcabe00501124148f4e8402bb3327f1 Mon Sep 17 00:00:00 2001 From: Mateusz Matela Date: Sun, 27 Oct 2024 15:36:22 +0100 Subject: [PATCH 63/63] [23] Formatter Support for Implicit classes - investigate #2977 --- .../formatter/FormatterRegressionTests.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterRegressionTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterRegressionTests.java index c10ab0e44fb..f11ced185d6 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterRegressionTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/formatter/FormatterRegressionTests.java @@ -16464,4 +16464,29 @@ private void foo() { } """); } +//https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2977 +//[23] Formatter Support for Implicit classes - investigate +public void testIssue2977() { + setComplianceLevel(CompilerOptions.VERSION_23); + this.formatterOptions.put(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); + String source = + """ + import java.util.List; + String greeting() { return "Hello, World!"; } + void main( ) {println(greeting());} + """; + formatSource(source, + """ + import java.util.List; + + String greeting() { + return "Hello, World!"; + } + + void main() { + println(greeting()); + } + """, + CodeFormatter.K_COMPILATION_UNIT); +} }