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;