diff --git a/org.eclipse.jdt.apt.pluggable.tests/plugin.xml b/org.eclipse.jdt.apt.pluggable.tests/plugin.xml
index ced29d71d8b..3c53cb4b616 100644
--- a/org.eclipse.jdt.apt.pluggable.tests/plugin.xml
+++ b/org.eclipse.jdt.apt.pluggable.tests/plugin.xml
@@ -59,6 +59,9 @@
+
+
diff --git a/org.eclipse.jdt.apt.pluggable.tests/resources/targets/issue565/A.java b/org.eclipse.jdt.apt.pluggable.tests/resources/targets/issue565/A.java
new file mode 100644
index 00000000000..44d5090f17a
--- /dev/null
+++ b/org.eclipse.jdt.apt.pluggable.tests/resources/targets/issue565/A.java
@@ -0,0 +1,5 @@
+package targets.issue565;
+import java.io.Serializable;
+
+@Annotation565
+public class A & Runnable>{}
diff --git a/org.eclipse.jdt.apt.pluggable.tests/resources/targets/issue565/Annotation565.java b/org.eclipse.jdt.apt.pluggable.tests/resources/targets/issue565/Annotation565.java
new file mode 100644
index 00000000000..6d3196c929c
--- /dev/null
+++ b/org.eclipse.jdt.apt.pluggable.tests/resources/targets/issue565/Annotation565.java
@@ -0,0 +1,6 @@
+package targets.issue565;
+import java.lang.annotation.Inherited;
+
+@Inherited
+public @interface Annotation565 {
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/BuilderTests.java b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/BuilderTests.java
index fa80b4239ee..9fecb3bfc49 100644
--- a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/BuilderTests.java
+++ b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/BuilderTests.java
@@ -25,6 +25,7 @@
import org.eclipse.jdt.apt.pluggable.tests.processors.buildertester.Bug341298Processor;
import org.eclipse.jdt.apt.pluggable.tests.processors.buildertester.Bug468893Processor;
import org.eclipse.jdt.apt.pluggable.tests.processors.buildertester.Bug510118Processor;
+import org.eclipse.jdt.apt.pluggable.tests.processors.buildertester.Issue565Processor;
import org.eclipse.jdt.apt.pluggable.tests.processors.buildertester.BugsProc;
import org.eclipse.jdt.apt.pluggable.tests.processors.buildertester.InheritedAnnoProc;
import org.eclipse.jdt.apt.pluggable.tests.processors.buildertester.TestFinalRoundProc;
@@ -370,6 +371,19 @@ public void testBug510118() throws Throwable {
assertTrue("Incorrect status received from annotation processor", Bug510118Processor.status());
}
+ public void testIssue565() throws Throwable {
+ ProcessorTestStatus.reset();
+ IJavaProject jproj = createJavaProject(_projectName);
+ disableJava5Factories(jproj);
+ IProject proj = jproj.getProject();
+ IdeTestUtils.copyResources(proj, "targets/issue565", "src/targets/issue565");
+
+ AptConfig.setEnabled(jproj, true);
+ fullBuild();
+ expectingNoProblems();
+ assertTrue("Incorrect status received from annotation processor", Issue565Processor.status());
+ }
+
public void testBug341298() throws Throwable {
ProcessorTestStatus.reset();
IJavaProject project = createJavaProject(_projectName);
diff --git a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/processors/buildertester/Issue565Processor.java b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/processors/buildertester/Issue565Processor.java
new file mode 100644
index 00000000000..de81942ff82
--- /dev/null
+++ b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/processors/buildertester/Issue565Processor.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Kamil Krzywanski
+ *
+ * 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:
+ * Kamil Krzywanski - initial creation if Interesection type and Implementation
+ *******************************************************************************/
+package org.eclipse.jdt.apt.pluggable.tests.processors.buildertester;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.annotation.processing.SupportedSourceVersion;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.*;
+import javax.lang.model.util.SimpleTypeVisitor8;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+@SupportedAnnotationTypes("targets.issue565.Annotation565")
+@SupportedSourceVersion(SourceVersion.RELEASE_6)
+public class Issue565Processor extends AbstractProcessor {
+ private static boolean status = false;
+
+ @Override
+ public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (roundEnv.processingOver()) {
+ // We're not interested in the postprocessing round.
+ return false;
+ }
+
+ for (Element element : roundEnv.getElementsAnnotatedWith(annotations.stream().findAny().get())) {
+ if (element instanceof TypeElement) {
+ keyBuilder.visit(element.asType(), true);
+ }
+ }
+ status = true;
+ return false;
+ }
+
+ public static boolean status() {
+ return status;
+ }
+
+ // This is a fragment of querydsl visitor
+ private final TypeVisitor, Boolean> keyBuilder = new SimpleTypeVisitor8<>() {
+ private final List defaultValue = Collections.singletonList("Object");
+
+ private List visitBase(TypeMirror t) {
+ List rv = new ArrayList<>();
+ String name = t.toString();
+ if (name.contains("<")) {
+ name = name.substring(0, name.indexOf('<'));
+ }
+ rv.add(name);
+ return rv;
+ }
+
+ @Override
+ public List visitDeclared(DeclaredType t, Boolean p) {
+ List rv = visitBase(t);
+ for (TypeMirror arg : t.getTypeArguments()) {
+ if (p) {
+ rv.addAll(visit(arg, false));
+ } else {
+ rv.add(arg.toString());
+ }
+ }
+ return rv;
+ }
+
+ @Override
+ public List visitTypeVariable(TypeVariable t, Boolean p) {
+ List rv = visitBase(t);
+ if (t.getUpperBound() != null) {
+ rv.addAll(visit(t.getUpperBound(), p));
+ }
+ if (t.getLowerBound() != null) {
+ rv.addAll(visit(t.getLowerBound(), p));
+ }
+ return rv;
+ }
+
+ @Override
+ public List visitIntersection(IntersectionType t, Boolean p) {
+ return t.getBounds().get(0).accept(this, p);
+ }
+
+ @Override
+ public List visitNull(NullType t, Boolean p) {
+ return defaultValue;
+ }
+ };
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/IntersectionTypeImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/IntersectionTypeImpl.java
new file mode 100644
index 00000000000..3874f7d4b3c
--- /dev/null
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/IntersectionTypeImpl.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Kamil Krzywanski 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:
+ * Kamil Krzywanski - initial creation if Interesection type and Implementation
+ *******************************************************************************/
+
+package org.eclipse.jdt.internal.compiler.apt.model;
+
+import org.eclipse.jdt.internal.compiler.apt.dispatch.BaseProcessingEnvImpl;
+import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
+
+import javax.lang.model.type.IntersectionType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVisitor;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Implementation of the WildcardType
+ */
+public class IntersectionTypeImpl extends TypeMirrorImpl implements IntersectionType {
+ private final List extends TypeMirror> bounds;
+
+ IntersectionTypeImpl(BaseProcessingEnvImpl env, TypeVariableBinding binding) {
+ super(env, binding);
+ this.bounds = Arrays.stream(binding.superInterfaces).map(referenceBinding -> this._env.getFactory().newTypeMirror(referenceBinding)).toList();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.lang.model.type.TypeMirror#getKind()
+ */
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.INTERSECTION;
+ }
+ /* (non-Javadoc)
+ * @see javax.lang.model.type.WildcardType#getSuperBound()
+ */
+ @Override
+ public R accept(TypeVisitor v, P p) {
+ return v.visitIntersection(this, p);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.lang.model.type.IntersectionType#getBounds()
+ */
+ @Override
+ public List extends TypeMirror> getBounds() {
+ return this.bounds;
+ }
+}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypeVariableImpl.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypeVariableImpl.java
index 46a6d7e1990..b4f293f2b48 100644
--- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypeVariableImpl.java
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/apt/model/TypeVariableImpl.java
@@ -66,6 +66,10 @@ public TypeMirror getUpperBound() {
// only one bound that is an interface
return this._env.getFactory().newTypeMirror(typeVariableBinding.upperBound());
}
+ if (superInterfaces.length > 1) {
+ return new IntersectionTypeImpl(this._env, typeVariableBinding);
+ }
+
return this._env.getFactory().newTypeMirror(this._binding);
}
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 dd6775b1d1a..79f0237eb88 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
@@ -555,9 +555,10 @@ protected void checkEarlyConstructionContext(BlockScope scope) {
}
protected boolean isMissingTypeRelevant() {
if (this.binding != null && this.binding.isVarargs()) {
- if (this.arguments.length < this.binding.parameters.length) {
+ int argLen = this.arguments != null ? this.arguments.length : 0;
+ if (argLen < this.binding.parameters.length) {
// are all but the irrelevant varargs type present?
- for (int i = 0; i < this.arguments.length; i++) {
+ for (int i = 0; i < argLen; i++) {
if ((this.binding.parameters[i].tagBits & TagBits.HasMissingType) != 0)
return true; // this one *is* relevant - actually this case is already detected during findConstructorBinding()
}
diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/MessageSend.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
index 433f259be96..9101704728a 100644
--- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
+++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
@@ -1117,9 +1117,10 @@ protected boolean isMissingTypeRelevant() {
}
if ((this.binding.returnType.tagBits & TagBits.HasMissingType) == 0
&& this.binding.isVarargs()) {
- if (this.arguments.length < this.binding.parameters.length) {
+ int argLen = this.arguments != null ? this.arguments.length : 0;
+ if (argLen < this.binding.parameters.length) {
// are all but the irrelevant varargs type present?
- for (int i = 0; i < this.arguments.length; i++) {
+ for (int i = 0; i < argLen; i++) {
if ((this.binding.parameters[i].tagBits & TagBits.HasMissingType) != 0)
return true; // this one *is* relevant - actually this case is already detected during findMethodBinding()
}
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 d0205b01945..a20dc7ae19d 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
@@ -9702,6 +9702,46 @@ The constructor B(A, String...) refers to the missing type A
""";
runner.runNegativeTest();
}
+public void testMissingClass_varargs4_noArg() {
+ // missing type in non-varargs position
+ if (this.complianceLevel < ClassFileConstants.JDK1_8) return; // ignore different outcome below 1.8 since PR 2543
+ Runner runner = new Runner();
+ runner.testFiles = new String[] {
+ "p1/A.java",
+ """
+ package p1;
+ public class A {}
+ """,
+ "p1/B.java",
+ """
+ package p1;
+ public class B {
+ public B(A... a) {}
+ public void m(A... a) {}
+ }
+ """
+ };
+ runner.runConformTest();
+
+ // delete binary file A (i.e. simulate removing it from classpath for subsequent compile)
+ Util.delete(new File(OUTPUT_DIR, "p1" + File.separator + "A.class"));
+ runner.shouldFlushOutputDirectory = false;
+
+ runner.testFiles = new String[] {
+ "p2/C.java",
+ """
+ package p2;
+ import p1.B;
+ public class C {
+ void test(B b) {
+ new B();
+ b.m();
+ }
+ }
+ """
+ };
+ runner.runConformTest();
+}
public void testMissingClass_returnType_OK() {
if (this.complianceLevel < ClassFileConstants.JDK1_8) return; // ignore different outcome below 1.8 since PR 2543
Runner runner = new Runner();
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/Util.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/Util.java
index 92808138853..1ff6ef87c04 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/Util.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/Util.java
@@ -1306,12 +1306,17 @@ private static boolean waitUntilFileDeleted(File file) {
System.out.print(" - wait for ("+DELETE_MAX_WAIT+"ms max): ");
}
int count = 0;
- int delay = 10; // ms
+ int delay = 1; // ms
int maxRetry = DELETE_MAX_WAIT / delay;
int time = 0;
while (count < maxRetry) {
try {
count++;
+
+ // manually trigger GC to invoke Cleaner for ZipFile that is forgotten to be closed
+ // see https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2766
+ System.gc(); // workaround
+
Thread.sleep(delay);
time += delay;
if (time > DELETE_MAX_TIME) DELETE_MAX_TIME = time;