From 54fbfc4d61d7c6880293dbfe8188a2ae8cac1fb1 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Thu, 29 Aug 2024 16:47:01 -0400 Subject: [PATCH 1/6] Fix deprecated check routines in ASTNode to also check since value - fixes #2873 --- .../jdt/internal/compiler/ast/ASTNode.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) 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..3c0814b0389 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,7 @@ 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.StringConstant; import org.eclipse.jdt.internal.compiler.lookup.*; @SuppressWarnings({"rawtypes", "unchecked"}) @@ -498,6 +499,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 +551,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 +564,35 @@ public final boolean isMethodUseDeprecated(MethodBinding method, Scope scope, return true; } + private boolean sinceValueUnreached(Binding binding, Scope scope) { + AnnotationBinding[] annotations= binding.getAnnotations(); + for (AnnotationBinding annotation : annotations) { + if (String.valueOf(annotation.getAnnotationType().readableName()).equals("java.lang.Deprecated")) { //$NON-NLS-1$ + ElementValuePair[] pairs= annotation.getElementValuePairs(); + for (ElementValuePair pair : pairs) { + if (String.valueOf(pair.getName()).equals("since")) { //$NON-NLS-1$ + if (pair.getValue() instanceof StringConstant strConstant) { + try { + String value= strConstant.stringValue(); + int sinceValue= Integer.parseInt(value); + // As long as the AST levels and ClassFileConstants.MAJOR_VERSION grow simultaneously, + // we can use the offset of +44 to compute the Major version from the given AST Level + long sinceLevel= ClassFileConstants.getComplianceLevelForJavaVersion(sinceValue + 44); + long sourceLevel= scope.compilerOptions().sourceLevel; + if (sourceLevel < sinceLevel) { + return true; + } + } catch (NumberFormatException e) { + // do nothing and fall through + } + } + } + } + } + } + return false; + } + public boolean isSuper() { return false; @@ -619,6 +653,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; From f06b8d381cb9c41e7f45cf4dde8b4e04701e101b Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Tue, 3 Sep 2024 20:39:40 -0400 Subject: [PATCH 2/6] Tweak logic to handle case where annotations aren't ready - fix Deprecated9Test failures --- .../jdt/internal/compiler/ast/ASTNode.java | 7 ++- .../compiler/regression/Deprecated9Test.java | 52 ++++++++++++++++++- 2 files changed, 57 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 3c0814b0389..8770a9ae767 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 @@ -565,9 +565,14 @@ public final boolean isMethodUseDeprecated(MethodBinding method, Scope scope, } 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 (String.valueOf(annotation.getAnnotationType().readableName()).equals("java.lang.Deprecated")) { //$NON-NLS-1$ + if (annotation != null && String.valueOf(annotation.getAnnotationType().readableName()).equals("java.lang.Deprecated")) { //$NON-NLS-1$ ElementValuePair[] pairs= annotation.getElementValuePairs(); for (ElementValuePair pair : pairs) { if (String.valueOf(pair.getName()).equals("since")) { //$NON-NLS-1$ 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..ceb2eb6d809 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 @@ -913,7 +913,15 @@ public void testBug533063_2() throws Exception { "----------\n"; runner.runWarningTest(); } - public void testBug534304() throws Exception { + public void testBug534304_1() throws Exception { + Map options= getCompilerOptions(); + String compliance= options.get(CompilerOptions.OPTION_Compliance); + if (compliance.equals(CompilerOptions.VERSION_9) || + compliance.equals(CompilerOptions.VERSION_10) || + compliance.equals(CompilerOptions.VERSION_11) || + compliance.equals(CompilerOptions.VERSION_12)) { + return; + } runNegativeTest( new String[] { "p1/C1.java", @@ -953,6 +961,48 @@ public void testBug534304() throws Exception { "CMissing cannot be resolved to a type\n" + "----------\n"); } + public void testBug534304_2() throws Exception { + Map options= getCompilerOptions(); + String compliance= options.get(CompilerOptions.OPTION_Compliance); + if (compliance.equals(CompilerOptions.VERSION_9) || + compliance.equals(CompilerOptions.VERSION_10) || + compliance.equals(CompilerOptions.VERSION_11) || + compliance.equals(CompilerOptions.VERSION_12)) { + 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<>(); From 191289d939c0e36bb6e04df919f238bfc64cf09a Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Fri, 6 Sep 2024 15:20:03 -0400 Subject: [PATCH 3/6] Fix indenting --- .../compiler/regression/Deprecated9Test.java | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) 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 ceb2eb6d809..a01dfe994b6 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 @@ -969,38 +969,38 @@ public void testBug534304_2() throws Exception { compliance.equals(CompilerOptions.VERSION_11) || compliance.equals(CompilerOptions.VERSION_12)) { 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"); + 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 { From e7b3ef1d2832dd80d84b08b50c2a7f21ef1e5507 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Tue, 10 Sep 2024 14:43:17 -0400 Subject: [PATCH 4/6] Switch to use compliance level rather than source level --- .../eclipse/jdt/internal/compiler/ast/ASTNode.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 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 8770a9ae767..694cf9becf3 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 @@ -570,21 +570,21 @@ private boolean sinceValueUnreached(Binding binding, Scope scope) { return false; } } - AnnotationBinding[] annotations= binding.getAnnotations(); + AnnotationBinding[] annotations = binding.getAnnotations(); for (AnnotationBinding annotation : annotations) { if (annotation != null && String.valueOf(annotation.getAnnotationType().readableName()).equals("java.lang.Deprecated")) { //$NON-NLS-1$ - ElementValuePair[] pairs= annotation.getElementValuePairs(); + ElementValuePair[] pairs = annotation.getElementValuePairs(); for (ElementValuePair pair : pairs) { if (String.valueOf(pair.getName()).equals("since")) { //$NON-NLS-1$ if (pair.getValue() instanceof StringConstant strConstant) { try { - String value= strConstant.stringValue(); - int sinceValue= Integer.parseInt(value); + String value = strConstant.stringValue(); + int sinceValue = Integer.parseInt(value); // As long as the AST levels and ClassFileConstants.MAJOR_VERSION grow simultaneously, // we can use the offset of +44 to compute the Major version from the given AST Level - long sinceLevel= ClassFileConstants.getComplianceLevelForJavaVersion(sinceValue + 44); - long sourceLevel= scope.compilerOptions().sourceLevel; - if (sourceLevel < sinceLevel) { + long sinceLevel = ClassFileConstants.getComplianceLevelForJavaVersion(sinceValue + 44); + long complianceLevel = scope.compilerOptions().complianceLevel; + if (complianceLevel < sinceLevel) { return true; } } catch (NumberFormatException e) { From f56e34515c62b82df515c7785c4f3df1df33e027 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Tue, 8 Oct 2024 15:53:36 -0400 Subject: [PATCH 5/6] Fix logic per suggestions in PR --- .../jdt/internal/compiler/ast/ASTNode.java | 10 ++++------ .../compiler/regression/Deprecated9Test.java | 15 +++------------ 2 files changed, 7 insertions(+), 18 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 694cf9becf3..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,7 @@ 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.*; @@ -572,17 +573,14 @@ private boolean sinceValueUnreached(Binding binding, Scope scope) { } AnnotationBinding[] annotations = binding.getAnnotations(); for (AnnotationBinding annotation : annotations) { - if (annotation != null && String.valueOf(annotation.getAnnotationType().readableName()).equals("java.lang.Deprecated")) { //$NON-NLS-1$ + if (annotation != null && annotation.getAnnotationType().id == TypeIds.T_JavaLangDeprecated) { ElementValuePair[] pairs = annotation.getElementValuePairs(); for (ElementValuePair pair : pairs) { - if (String.valueOf(pair.getName()).equals("since")) { //$NON-NLS-1$ + if (CharOperation.equals(pair.getName(), TypeConstants.SINCE)) { if (pair.getValue() instanceof StringConstant strConstant) { try { String value = strConstant.stringValue(); - int sinceValue = Integer.parseInt(value); - // As long as the AST levels and ClassFileConstants.MAJOR_VERSION grow simultaneously, - // we can use the offset of +44 to compute the Major version from the given AST Level - long sinceLevel = ClassFileConstants.getComplianceLevelForJavaVersion(sinceValue + 44); + long sinceLevel = CompilerOptions.versionToJdkLevel(value); long complianceLevel = scope.compilerOptions().complianceLevel; if (complianceLevel < sinceLevel) { 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 a01dfe994b6..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; @@ -914,12 +915,7 @@ public void testBug533063_2() throws Exception { runner.runWarningTest(); } public void testBug534304_1() throws Exception { - Map options= getCompilerOptions(); - String compliance= options.get(CompilerOptions.OPTION_Compliance); - if (compliance.equals(CompilerOptions.VERSION_9) || - compliance.equals(CompilerOptions.VERSION_10) || - compliance.equals(CompilerOptions.VERSION_11) || - compliance.equals(CompilerOptions.VERSION_12)) { + if (this.complianceLevel < ClassFileConstants.JDK13) { return; } runNegativeTest( @@ -962,12 +958,7 @@ public void testBug534304_1() throws Exception { "----------\n"); } public void testBug534304_2() throws Exception { - Map options= getCompilerOptions(); - String compliance= options.get(CompilerOptions.OPTION_Compliance); - if (compliance.equals(CompilerOptions.VERSION_9) || - compliance.equals(CompilerOptions.VERSION_10) || - compliance.equals(CompilerOptions.VERSION_11) || - compliance.equals(CompilerOptions.VERSION_12)) { + if (this.complianceLevel < ClassFileConstants.JDK13) { runNegativeTest( new String[] { "p1/C1.java", From eca78372232bee0f5669ac953cb132050dd29472 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Thu, 24 Oct 2024 17:16:18 -0400 Subject: [PATCH 6/6] Add test to ReconcilerTests9 --- .../core/tests/model/ReconcilerTests9.java | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) 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;