diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ConstraintExceptionFormula.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ConstraintExceptionFormula.java index 37cb04ee363..d7d0c3e3272 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ConstraintExceptionFormula.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ConstraintExceptionFormula.java @@ -44,6 +44,10 @@ public ConstraintExceptionFormula(FunctionalExpression left, TypeBinding type) { @Override public Object reduce(InferenceContext18 inferenceContext) { + if ((this.right.tagBits & TagBits.HasMissingType) != 0) { + inferenceContext.hasIgnoredMissingType = true; + return TRUE; + } // JLS 18.2.5 Scope scope = inferenceContext.scope; if (!this.right.isFunctionalInterface(scope)) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java index 29094d54ba7..2bce82c25e1 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ConstraintExpressionFormula.java @@ -59,6 +59,10 @@ class ConstraintExpressionFormula extends ConstraintFormula { @Override public Object reduce(InferenceContext18 inferenceContext) throws InferenceFailureException { + if ((this.right.tagBits & TagBits.HasMissingType) != 0) { + inferenceContext.hasIgnoredMissingType = true; + return TRUE; + } if (this.relation == POTENTIALLY_COMPATIBLE) { /* 15.12.2.1: ... The definition of potential applicability goes beyond a basic arity check to also take into account the presence and "shape" of functional interface diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ConstraintTypeFormula.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ConstraintTypeFormula.java index 3149d02f0d1..7b7346deacf 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ConstraintTypeFormula.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ConstraintTypeFormula.java @@ -63,6 +63,10 @@ private ConstraintTypeFormula(TypeBinding exprType, TypeBinding right, int relat // return: ReductionResult or ConstraintFormula[] @Override public Object reduce(InferenceContext18 inferenceContext) { + if ((this.left.tagBits & TagBits.HasMissingType) != 0 || (this.right.tagBits & TagBits.HasMissingType) != 0) { + inferenceContext.hasIgnoredMissingType = true; + return TRUE; + } switch (this.relation) { case COMPATIBLE: // 18.2.2: diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java index 26ad94323ad..a478ba29180 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java @@ -180,6 +180,8 @@ public class InferenceContext18 { // the following two flags control to what degree we continue with incomplete information: private boolean isInexactVarargsInference = false; boolean prematureOverloadResolution = false; + // during reduction we ignore missing types but record that fact here: + boolean hasIgnoredMissingType; public static boolean isSameSite(InvocationSite site1, InvocationSite site2) { if (site1 == site2) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java index 350fb1b1f51..60c0e7ff4e1 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java @@ -307,6 +307,9 @@ public static MethodBinding computeCompatibleMethod18(MethodBinding originalMeth if (invocationSite instanceof Invocation && allArgumentsAreProper && (expectedType == null || expectedType.isProperType(true))) infCtx18.forwardResults(result, (Invocation) invocationSite, methodSubstitute, expectedType); try { + if (infCtx18.hasIgnoredMissingType) { + return new ProblemMethodBinding(originalMethod, originalMethod.selector, parameters, ProblemReasons.MissingTypeInSignature); + } if (hasReturnProblem) { // illegally working from the provisional result? MethodBinding problemMethod = infCtx18.getReturnProblemMethodIfNeeded(expectedType, methodSubstitute); if (problemMethod instanceof ProblemMethodBinding) { 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 1b84b7c5bf8..1bfb0f75f3e 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 @@ -982,7 +982,7 @@ protected boolean connectTypeVariables(TypeParameter[] typeParameters, boolean c } else { typeVariable.setSuperInterfaces(new ReferenceBinding[] {superRefType}); } - typeVariable.tagBits |= superType.tagBits & TagBits.ContainsNestedTypeReferences; + typeVariable.tagBits |= superType.tagBits & (TagBits.ContainsNestedTypeReferences | TagBits.HasMissingType); typeVariable.setFirstBound(superRefType); // first bound used to compute erasure } } @@ -997,7 +997,7 @@ protected boolean connectTypeVariables(TypeParameter[] typeParameters, boolean c typeVariable.tagBits |= TagBits.HierarchyHasProblems; continue nextBound; } else { - typeVariable.tagBits |= superType.tagBits & TagBits.ContainsNestedTypeReferences; + typeVariable.tagBits |= superType.tagBits & (TagBits.ContainsNestedTypeReferences | TagBits.HasMissingType); boolean didAlreadyComplain = !typeRef.resolvedType.isValidBinding(); if (isFirstBoundTypeVariable && j == 0) { problemReporter().noAdditionalBoundAfterTypeVariable(typeRef); diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java index f7ef22e0f74..2d4cc0a1834 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2020 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 @@ -43,6 +43,7 @@ package org.eclipse.jdt.internal.compiler.lookup; import java.util.Arrays; +import java.util.List; import java.util.Set; import java.util.function.Consumer; @@ -344,6 +345,20 @@ public int boundsCount() { public boolean canBeInstantiated() { return false; } + + @Override + public List collectMissingTypes(List missingTypes) { + if ((this.tagBits & TagBits.HasMissingType) != 0) { + if (this.superclass != null) { + missingTypes = this.superclass.collectMissingTypes(missingTypes); + } + for (ReferenceBinding superIfc : this.superInterfaces) { + missingTypes = superIfc.collectMissingTypes(missingTypes); + } + } + return missingTypes; + } + /** * Collect the substitutes into a map for certain type variables inside the receiver type * e.g. {@code Collection.collectSubstitutes(Collection>, Map)} will populate Map with: {@code T --> List} @@ -805,7 +820,7 @@ ReferenceBinding resolve() { TypeBinding oldSuperclass = this.superclass, oldFirstInterface = null; if (this.superclass != null) { ReferenceBinding resolveType = (ReferenceBinding) BinaryTypeBinding.resolveType(this.superclass, this.environment, true /* raw conversion */); - this.tagBits |= resolveType.tagBits & TagBits.ContainsNestedTypeReferences; + this.tagBits |= resolveType.tagBits & (TagBits.ContainsNestedTypeReferences | TagBits.HasMissingType); long superNullTagBits = resolveType.tagBits & TagBits.AnnotationNullMASK; if (superNullTagBits != 0L) { if (nullTagBits == 0L) { @@ -824,7 +839,7 @@ ReferenceBinding resolve() { oldFirstInterface = interfaces[0]; for (int i = length; --i >= 0;) { ReferenceBinding resolveType = (ReferenceBinding) BinaryTypeBinding.resolveType(interfaces[i], this.environment, true /* raw conversion */); - this.tagBits |= resolveType.tagBits & TagBits.ContainsNestedTypeReferences; + this.tagBits |= resolveType.tagBits & (TagBits.ContainsNestedTypeReferences | TagBits.HasMissingType); long superNullTagBits = resolveType.tagBits & TagBits.AnnotationNullMASK; if (superNullTagBits != 0L) { if (nullTagBits == 0L) { diff --git a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/MultiProjectTests.java b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/MultiProjectTests.java index 087637dc9dd..2e8d7c58d7a 100644 --- a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/MultiProjectTests.java +++ b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/MultiProjectTests.java @@ -34,7 +34,7 @@ public class MultiProjectTests extends BuilderTests { static { - TESTS_NAMES = new String[] { "test461074_error_1_8" }; +// TESTS_NAMES = new String[] { "test461074_error_1_8" }; } public MultiProjectTests(String name) { diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AnnotationTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AnnotationTest.java index 466557801e5..ffe8ed0767d 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AnnotationTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AnnotationTest.java @@ -7973,8 +7973,8 @@ public void test237() { "----------\n" + "2. ERROR in X.java (at line 6)\n" + " List ls = get();\n" + - " ^^^^^\n" + - "Type mismatch: cannot convert from B to List\n" + + " ^^^\n" + + "The method get() from the type X refers to the missing type ArrayList\n" + "----------\n"); } public void test238() { diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java index 2b0f686803c..f2cf8999f6b 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java @@ -43243,6 +43243,11 @@ public void test1222() { " public class X {\n" + " ^^^^\n" + "Zork cannot be resolved to a type\n" + + "----------\n" + + "2. ERROR in X.java (at line 4)\n" + + " Runnable r = x2.get();\n" + + " ^^^\n" + + "The method get() from the type X refers to the missing type Zork\n" + "----------\n"); } //https://bugs.eclipse.org/bugs/show_bug.cgi?id=211718 diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java index 1386fe9ad27..1c3fe0eb91f 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest_1_8.java @@ -9173,45 +9173,40 @@ public void testBug525580() { "----------\n" + "2. ERROR in org\\a\\a\\g\\d.java (at line 6)\n" + " T t = (e) cls.newInstance();\n" + - " ^^^^^^^^^^^^^^^^^^^^^\n" + - "e cannot be resolved to a type\n" + - "----------\n" + - "3. ERROR in org\\a\\a\\g\\d.java (at line 6)\n" + - " T t = (e) cls.newInstance();\n" + " ^\n" + "e cannot be resolved to a type\n" + "----------\n" + - "4. ERROR in org\\a\\a\\g\\d.java (at line 7)\n" + + "3. ERROR in org\\a\\a\\g\\d.java (at line 7)\n" + " while (size >= 0) {\n" + " ^^^^\n" + "size cannot be resolved to a variable\n" + "----------\n" + - "5. ERROR in org\\a\\a\\g\\d.java (at line 8)\n" + - " T a = ((b) this.e.m.get(size)).a();\n" + - " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + - "Type mismatch: cannot convert from e to T\n" + - "----------\n" + - "6. ERROR in org\\a\\a\\g\\d.java (at line 8)\n" + + "4. ERROR in org\\a\\a\\g\\d.java (at line 8)\n" + " T a = ((b) this.e.m.get(size)).a();\n" + " ^\n" + "e cannot be resolved or is not a field\n" + "----------\n" + - "7. ERROR in org\\a\\a\\g\\d.java (at line 8)\n" + + "5. ERROR in org\\a\\a\\g\\d.java (at line 8)\n" + " T a = ((b) this.e.m.get(size)).a();\n" + " ^^^^\n" + "size cannot be resolved to a variable\n" + "----------\n" + - "8. ERROR in org\\a\\a\\g\\d.java (at line 15)\n" + + "6. ERROR in org\\a\\a\\g\\d.java (at line 8)\n" + + " T a = ((b) this.e.m.get(size)).a();\n" + + " ^\n" + + "The method a() from the type d.b refers to the missing type e\n" + + "----------\n" + + "7. ERROR in org\\a\\a\\g\\d.java (at line 15)\n" + " T a();\n" + " ^\n" + "e cannot be resolved to a type\n" + "----------\n" + - "9. ERROR in org\\a\\a\\g\\d.java (at line 17)\n" + + "8. ERROR in org\\a\\a\\g\\d.java (at line 17)\n" + " T b();\n" + " ^\n" + "j cannot be resolved to a type\n" + "----------\n" + - "10. WARNING in org\\a\\a\\g\\d.java (at line 17)\n" + + "9. WARNING in org\\a\\a\\g\\d.java (at line 17)\n" + " T b();\n" + " ^^^\n" + "This method has a constructor name\n" + @@ -9292,8 +9287,8 @@ public void testBug525580_comment28() { "----------\n" + "3. ERROR in xxxxxx\\iiibii.java (at line 9)\n" + " return b041D041D041D041DН041DН(new xxxxxx.jjajaa(b, b2));\n" + - " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + - "Type mismatch: cannot convert from jajaja to jajaja\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^\n" + + "The method b041D041D041D041DН041DН(jjajaa) from the type iiibii refers to the missing type jajaja\n" + "----------\n" + "----------\n" + "1. ERROR in xxxxxx\\jjajaa.java (at line 3)\n" + 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 af4e3c0f2b1..48364bd027f 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 @@ -9517,4 +9517,102 @@ The method m() from the type B refers to the missing type A """; runner.runNegativeTest(); } +public void testMissingClass_typeVariableBound() { + 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; + import p1.A; + public class B { + public void m(Number n) {} // would match, but ... + public void m(T t) {} // ... don't rule out method with missing type + } + """ + }; + 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; + class C { + void test(B b) { + b.m(Integer.valueOf(13)); + } + } + """ + }; + runner.expectedCompilerLog = """ + ---------- + 1. ERROR in p2\\C.java (at line 5) + b.m(Integer.valueOf(13)); + ^ + The method m(T) from the type B refers to the missing type A + ---------- + """; + runner.runNegativeTest(); +} +public void testMissingClass_typeVariableBound2() { + 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; + import p1.A; + public class B { + public void m(T t) {} + } + """ + }; + 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; + class C { + void test(B b) { + b.m(this); + } + } + """ + }; + runner.expectedCompilerLog = """ + ---------- + 1. ERROR in p2\\C.java (at line 1) + package p2; + ^ + The type p1.A cannot be resolved. It is indirectly referenced from required type p1.B + ---------- + 2. ERROR in p2\\C.java (at line 4) + void test(B b) { + ^ + Bound mismatch: The type C is not a valid substitute for the bounded parameter of the type B + ---------- + """; + runner.runNegativeTest(); +} }