From 20a1a97461269de80dcce4cc83dfd5490b5ad141 Mon Sep 17 00:00:00 2001 From: Theron Wang Date: Mon, 26 Aug 2024 13:48:02 -0400 Subject: [PATCH] Remove unused `ElementType`s in `@Target` (#355) Removes unused `ElementType`s in `@Target` annotations. The current approach is to only remove `ElementType`s, not add them, so we don't accidentally make a non `TYPE_USE` annotation into a `TYPE_USE` annotation, for example. I also addressed two small bugs I encountered: - When all the wildcard import are JDK imports, synthetic classes should be generated in the current package instead of the first wildcard import package - Don't throw an exception when an annotation is applied on a package declaration Instead of creating a new test case for this, I just modified one of the existing ones (`SyntheticAnnotationTargetTest`), since it's purpose already seemed to fit this PR. Thanks! --- .../AnnotationParameterTypesVisitor.java | 16 ++ .../AnnotationTargetRemoverVisitor.java | 254 ++++++++++++++++++ .../specimin/SpeciminRunner.java | 12 + .../specimin/UnsolvedClassOrInterface.java | 1 + .../specimin/UnsolvedSymbolVisitor.java | 11 +- .../SyntheticAnnotationTargetTest.java | 4 +- .../expected/com/example/Simple.java | 1 + .../initialization/qual/Initialized.java | 2 +- .../checker/nullness/qual/KeyForBottom.java | 2 +- .../checker/nullness/qual/NonNull.java | 2 +- .../checker/nullness/qual/Nullable.java | 2 +- .../checker/nullness/qual/UnknownKeyFor.java | 2 +- .../expected/com/example/Simple.java | 1 - .../checker/mustcall/qual/Owning.java | 2 +- .../com/example/PostconditionAnnotation.java | 3 +- .../com/example/PreconditionAnnotation.java | 3 +- .../issue272/expected/com/example/Simple.java | 1 + .../checker/nullness/qual/Nullable.java | 2 +- .../checker/index/qual/GTENegativeOne.java | 3 +- .../checker/index/qual/LowerBoundUnknown.java | 3 +- .../checker/index/qual/NonNegative.java | 2 +- .../checker/index/qual/Positive.java | 2 +- .../expected/com/example/Anno.java | 2 +- .../expected/com/example/Bar.java | 2 +- .../expected/com/example/Baz.java | 2 +- .../expected/com/example/Foo.java | 2 +- .../com/example/AnnotationDeclaration.java | 5 + .../expected/com/example/Constructor.java | 5 + .../com/example/EnumConstantDeclaration.java | 5 + .../expected/com/example/Field.java | 5 + .../expected/com/example/Foo.java | 5 - .../expected/com/example/LocalVariable.java | 5 + .../expected/com/example/Method.java | 5 + .../expected/com/example/Param.java | 5 + .../expected/com/example/Simple.java | 33 ++- .../expected/com/example/Type.java | 5 + .../expected/com/example/TypeParam.java | 5 + .../input/com/example/Simple.java | 33 ++- 38 files changed, 405 insertions(+), 50 deletions(-) create mode 100644 src/main/java/org/checkerframework/specimin/AnnotationTargetRemoverVisitor.java create mode 100644 src/test/resources/syntheticannotationtarget/expected/com/example/AnnotationDeclaration.java create mode 100644 src/test/resources/syntheticannotationtarget/expected/com/example/Constructor.java create mode 100644 src/test/resources/syntheticannotationtarget/expected/com/example/EnumConstantDeclaration.java create mode 100644 src/test/resources/syntheticannotationtarget/expected/com/example/Field.java delete mode 100644 src/test/resources/syntheticannotationtarget/expected/com/example/Foo.java create mode 100644 src/test/resources/syntheticannotationtarget/expected/com/example/LocalVariable.java create mode 100644 src/test/resources/syntheticannotationtarget/expected/com/example/Method.java create mode 100644 src/test/resources/syntheticannotationtarget/expected/com/example/Param.java create mode 100644 src/test/resources/syntheticannotationtarget/expected/com/example/Type.java create mode 100644 src/test/resources/syntheticannotationtarget/expected/com/example/TypeParam.java diff --git a/src/main/java/org/checkerframework/specimin/AnnotationParameterTypesVisitor.java b/src/main/java/org/checkerframework/specimin/AnnotationParameterTypesVisitor.java index bd9cfcc92..8b00f2064 100644 --- a/src/main/java/org/checkerframework/specimin/AnnotationParameterTypesVisitor.java +++ b/src/main/java/org/checkerframework/specimin/AnnotationParameterTypesVisitor.java @@ -3,6 +3,7 @@ import com.github.javaparser.ast.ImportDeclaration; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.PackageDeclaration; import com.github.javaparser.ast.body.AnnotationMemberDeclaration; import com.github.javaparser.ast.expr.AnnotationExpr; import com.github.javaparser.ast.expr.ArrayInitializerExpr; @@ -112,6 +113,11 @@ public Visitable visit(AnnotationMemberDeclaration decl, Void p) { @Override public Visitable visit(MarkerAnnotationExpr anno, Void p) { + // Annotations on packages cause an exception in findClosestParentMemberOrClassLike + if (anno.hasParentNode() && anno.getParentNode().get() instanceof PackageDeclaration) { + return super.visit(anno, p); + } + Node parent = JavaParserUtil.findClosestParentMemberOrClassLike(anno); if (isTargetOrUsed(parent)) { @@ -122,6 +128,11 @@ public Visitable visit(MarkerAnnotationExpr anno, Void p) { @Override public Visitable visit(SingleMemberAnnotationExpr anno, Void p) { + // Annotations on packages cause an exception in findClosestParentMemberOrClassLike + if (anno.hasParentNode() && anno.getParentNode().get() instanceof PackageDeclaration) { + return super.visit(anno, p); + } + Node parent = JavaParserUtil.findClosestParentMemberOrClassLike(anno); if (isTargetOrUsed(parent)) { @@ -132,6 +143,11 @@ public Visitable visit(SingleMemberAnnotationExpr anno, Void p) { @Override public Visitable visit(NormalAnnotationExpr anno, Void p) { + // Annotations on packages cause an exception in findClosestParentMemberOrClassLike + if (anno.hasParentNode() && anno.getParentNode().get() instanceof PackageDeclaration) { + return super.visit(anno, p); + } + Node parent = JavaParserUtil.findClosestParentMemberOrClassLike(anno); if (isTargetOrUsed(parent)) { diff --git a/src/main/java/org/checkerframework/specimin/AnnotationTargetRemoverVisitor.java b/src/main/java/org/checkerframework/specimin/AnnotationTargetRemoverVisitor.java new file mode 100644 index 000000000..c0fd4b775 --- /dev/null +++ b/src/main/java/org/checkerframework/specimin/AnnotationTargetRemoverVisitor.java @@ -0,0 +1,254 @@ +package org.checkerframework.specimin; + +import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.PackageDeclaration; +import com.github.javaparser.ast.body.AnnotationDeclaration; +import com.github.javaparser.ast.body.AnnotationMemberDeclaration; +import com.github.javaparser.ast.body.ConstructorDeclaration; +import com.github.javaparser.ast.body.EnumConstantDeclaration; +import com.github.javaparser.ast.body.FieldDeclaration; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.body.Parameter; +import com.github.javaparser.ast.body.RecordDeclaration; +import com.github.javaparser.ast.body.TypeDeclaration; +import com.github.javaparser.ast.expr.AnnotationExpr; +import com.github.javaparser.ast.expr.ArrayInitializerExpr; +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.FieldAccessExpr; +import com.github.javaparser.ast.expr.MarkerAnnotationExpr; +import com.github.javaparser.ast.expr.NameExpr; +import com.github.javaparser.ast.expr.NormalAnnotationExpr; +import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr; +import com.github.javaparser.ast.expr.VariableDeclarationExpr; +import com.github.javaparser.ast.type.TypeParameter; +import com.github.javaparser.ast.visitor.ModifierVisitor; +import com.github.javaparser.ast.visitor.Visitable; +import java.lang.annotation.ElementType; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Removes all unnecessary ElementType values from each annotation declaration, based on their + * usages. Run this visitor after PrunerVisitor to ensure all unnecessary ElementTypes are removed. + */ +public class AnnotationTargetRemoverVisitor extends ModifierVisitor { + /** A Map of fully qualified annotation names to its ElementTypes. */ + private final Map> annotationToElementTypes = new HashMap<>(); + + /** A Map of fully qualified annotation names to their declarations. */ + private final Map annotationToDeclaration = new HashMap<>(); + + /** Constant for the package of {@code @Target} ({@code java.lang.annotation}) */ + private static final String TARGET_PACKAGE = "java.lang.annotation"; + + /** Constant for {@code @Target}'s name (does not include the {@code @}) */ + private static final String TARGET_NAME = "Target"; + + /** Constant for {@code @Target}'s fully qualified name */ + private static final String FULLY_QUALIFIED_TARGET = TARGET_PACKAGE + "." + TARGET_NAME; + + /** + * Updates all annotation declaration {@code @Target} values to match their usages. Only removes + * {@code ElementType}s, doesn't add them. Call this method once after visiting all files. + */ + public void removeExtraAnnotationTargets() { + for (Map.Entry pair : annotationToDeclaration.entrySet()) { + AnnotationExpr targetAnnotation = + pair.getValue().getAnnotationByName(TARGET_NAME).orElse(null); + + if (targetAnnotation == null) { + // This is most likely an existing definition. If there is no @Target annotation, + // we shouldn't add it + continue; + } + + boolean useFullyQualified = targetAnnotation.getNameAsString().equals(FULLY_QUALIFIED_TARGET); + + // Only handle java.lang.annotation.Target + if (!targetAnnotation.resolve().getQualifiedName().equals(FULLY_QUALIFIED_TARGET)) { + continue; + } + + Set actualElementTypes = annotationToElementTypes.get(pair.getKey()); + + if (actualElementTypes == null) { + // No usages of the annotation itself (see Issue272Test) + actualElementTypes = new HashSet<>(); + } + + Set elementTypes = new HashSet<>(); + Set staticallyImportedElementTypes = new HashSet<>(); + Expression memberValue; + + // @Target(ElementType.___) + // @Target({ElementType.___, ElementType.___}) + if (targetAnnotation.isSingleMemberAnnotationExpr()) { + SingleMemberAnnotationExpr asSingleMember = targetAnnotation.asSingleMemberAnnotationExpr(); + memberValue = asSingleMember.getMemberValue(); + } + // @Target(value = ElementType.___) + // @Target(value = {ElementType.___, ElementType.___}) + else if (targetAnnotation.isNormalAnnotationExpr()) { + NormalAnnotationExpr asNormal = targetAnnotation.asNormalAnnotationExpr(); + memberValue = asNormal.getPairs().get(0).getValue(); + } else { + throw new RuntimeException("@Target annotation must contain an ElementType"); + } + + // If there's only one ElementType, we can't remove anything + // We should only be removing ElementTypes, not adding: we do not want to + // convert a non TYPE_USE annotation to a TYPE_USE annotation, for example + if (memberValue.isFieldAccessExpr() || memberValue.isNameExpr()) { + continue; + } else if (memberValue.isArrayInitializerExpr()) { + ArrayInitializerExpr arrayExpr = memberValue.asArrayInitializerExpr(); + if (arrayExpr.getValues().size() <= 1) { + continue; + } + for (Expression value : arrayExpr.getValues()) { + if (value.isFieldAccessExpr()) { + FieldAccessExpr fieldAccessExpr = value.asFieldAccessExpr(); + ElementType elementType = ElementType.valueOf(fieldAccessExpr.getNameAsString()); + if (actualElementTypes.contains(elementType)) { + elementTypes.add(elementType); + } + } else if (value.isNameExpr()) { + // In case of static imports + NameExpr nameExpr = value.asNameExpr(); + ElementType elementType = ElementType.valueOf(nameExpr.getNameAsString()); + if (actualElementTypes.contains(elementType)) { + elementTypes.add(elementType); + staticallyImportedElementTypes.add(elementType); + } + } + } + } + + StringBuilder newAnnotation = new StringBuilder(); + newAnnotation.append("@"); + + if (useFullyQualified) { + newAnnotation.append(TARGET_PACKAGE); + newAnnotation.append("."); + } + + newAnnotation.append(TARGET_NAME); + newAnnotation.append("("); + + newAnnotation.append('{'); + + List sortedElementTypes = new ArrayList<>(elementTypes); + Collections.sort(sortedElementTypes, (a, b) -> a.name().compareTo(b.name())); + + for (int i = 0; i < sortedElementTypes.size(); i++) { + ElementType elementType = sortedElementTypes.get(i); + if (!staticallyImportedElementTypes.contains(elementType)) { + if (useFullyQualified) { + newAnnotation.append(TARGET_PACKAGE); + newAnnotation.append("."); + } + newAnnotation.append("ElementType."); + } + newAnnotation.append(elementType.name()); + + if (i < sortedElementTypes.size() - 1) { + newAnnotation.append(", "); + } + } + + newAnnotation.append("})"); + AnnotationExpr trimmed = StaticJavaParser.parseAnnotation(newAnnotation.toString()); + + targetAnnotation.remove(); + + pair.getValue().addAnnotation(trimmed); + } + } + + @Override + public Visitable visit(AnnotationDeclaration decl, Void p) { + annotationToDeclaration.put(decl.getFullyQualifiedName().get(), decl); + return super.visit(decl, p); + } + + @Override + public Visitable visit(MarkerAnnotationExpr anno, Void p) { + updateAnnotationElementTypes(anno); + return super.visit(anno, p); + } + + @Override + public Visitable visit(NormalAnnotationExpr anno, Void p) { + updateAnnotationElementTypes(anno); + return super.visit(anno, p); + } + + @Override + public Visitable visit(SingleMemberAnnotationExpr anno, Void p) { + updateAnnotationElementTypes(anno); + return super.visit(anno, p); + } + + /** + * Helper method to update the ElementTypes for an annotation. + * + * @param anno The annotation to update element types for + */ + private void updateAnnotationElementTypes(AnnotationExpr anno) { + Node parent = anno.getParentNode().orElse(null); + + if (parent == null) { + return; + } + + Set elementTypes = annotationToElementTypes.get(anno.resolve().getQualifiedName()); + if (elementTypes == null) { + elementTypes = new HashSet<>(); + annotationToElementTypes.put(anno.resolve().getQualifiedName(), elementTypes); + } + + if (parent instanceof AnnotationDeclaration) { + elementTypes.add(ElementType.ANNOTATION_TYPE); + elementTypes.add(ElementType.TYPE); + } else if (parent instanceof ConstructorDeclaration) { + elementTypes.add(ElementType.CONSTRUCTOR); + } else if (parent instanceof FieldDeclaration || parent instanceof EnumConstantDeclaration) { + elementTypes.add(ElementType.FIELD); + } else if (parent instanceof VariableDeclarationExpr) { + elementTypes.add(ElementType.LOCAL_VARIABLE); + } else if (parent instanceof MethodDeclaration) { + elementTypes.add(ElementType.METHOD); + + if (((MethodDeclaration) parent).getType().isVoidType()) { + // If it's void we don't need to add TYPE_USE + return; + } + } else if (parent instanceof AnnotationMemberDeclaration) { + elementTypes.add(ElementType.METHOD); + } else if (parent instanceof PackageDeclaration) { + elementTypes.add(ElementType.PACKAGE); + return; + } else if (parent instanceof Parameter) { + if (parent.getParentNode().isPresent() + && parent.getParentNode().get() instanceof RecordDeclaration) { + elementTypes.add(ElementType.RECORD_COMPONENT); + } else { + elementTypes.add(ElementType.PARAMETER); + } + } else if (parent instanceof TypeDeclaration) { + // TypeDeclaration is the parent class for class, interface, annotation, record declarations + // https://www.javadoc.io/doc/com.github.javaparser/javaparser-core/latest/com/github/javaparser/ast/body/TypeDeclaration.html + elementTypes.add(ElementType.TYPE); + } else if (parent instanceof TypeParameter) { + elementTypes.add(ElementType.TYPE_PARAMETER); + } + + elementTypes.add(ElementType.TYPE_USE); + } +} diff --git a/src/main/java/org/checkerframework/specimin/SpeciminRunner.java b/src/main/java/org/checkerframework/specimin/SpeciminRunner.java index aa8825c35..f153ccd98 100644 --- a/src/main/java/org/checkerframework/specimin/SpeciminRunner.java +++ b/src/main/java/org/checkerframework/specimin/SpeciminRunner.java @@ -512,6 +512,7 @@ private static void performMinimizationImpl( cu.accept(methodPruner, null); } + pruneAnnotationDeclarationTargets(parsedTargetFiles); removeUnusedImports(parsedTargetFiles); // cache to avoid called Files.createDirectories repeatedly with the same arguments @@ -661,6 +662,17 @@ private static SpeciminStateVisitor processAnnotationTypes( return annotationParameterTypesVisitor; } + /** Runs AnnotationTargetRemoverVisitor on the target files. Call after PrunerVisitor. */ + private static void pruneAnnotationDeclarationTargets( + Map parsedTargetFiles) { + AnnotationTargetRemoverVisitor targetPruner = new AnnotationTargetRemoverVisitor(); + for (CompilationUnit cu : parsedTargetFiles.values()) { + cu.accept(targetPruner, null); + } + + targetPruner.removeExtraAnnotationTargets(); + } + /** * Removes all unused imports in each output file through {@code UnusedImportRemoverVisitor}. * diff --git a/src/main/java/org/checkerframework/specimin/UnsolvedClassOrInterface.java b/src/main/java/org/checkerframework/specimin/UnsolvedClassOrInterface.java index b62de9cec..8ae3fad83 100644 --- a/src/main/java/org/checkerframework/specimin/UnsolvedClassOrInterface.java +++ b/src/main/java/org/checkerframework/specimin/UnsolvedClassOrInterface.java @@ -476,6 +476,7 @@ public String toString() { + "\tjava.lang.annotation.ElementType.LOCAL_VARIABLE, \n" + "\tjava.lang.annotation.ElementType.ANNOTATION_TYPE,\n" + "\tjava.lang.annotation.ElementType.PACKAGE,\n" + + "\tjava.lang.annotation.ElementType.TYPE_PARAMETER,\n" + "\tjava.lang.annotation.ElementType.TYPE_USE \n" + "})"); } diff --git a/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java b/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java index 244768454..a16b89bcb 100644 --- a/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java +++ b/src/main/java/org/checkerframework/specimin/UnsolvedSymbolVisitor.java @@ -2885,10 +2885,15 @@ public String getPackageFromClassName(String className) { return wildcardPkg; } } - // If none do, then default to the first wildcard import. + // If none do, then default to the first wildcard import that is not a JDK package. // TODO: log a warning about this once we have a logger - String wildcardPkg = wildcardImports.get(0); - return wildcardPkg; + for (String wildcardPkg : wildcardImports) { + if (!JavaLangUtils.inJdkPackage(wildcardPkg)) { + return wildcardPkg; + } + } + // If we're here, all wildcard imports are jdk imports; use current package instead + return currentPackage; } } diff --git a/src/test/java/org/checkerframework/specimin/SyntheticAnnotationTargetTest.java b/src/test/java/org/checkerframework/specimin/SyntheticAnnotationTargetTest.java index 4d4f6c705..4faac9b01 100644 --- a/src/test/java/org/checkerframework/specimin/SyntheticAnnotationTargetTest.java +++ b/src/test/java/org/checkerframework/specimin/SyntheticAnnotationTargetTest.java @@ -4,8 +4,8 @@ import org.junit.Test; /** - * This test checks if synthetic annotations used in different locations will compile based - * on @Target. + * This test checks if synthetic annotations used in different locations will contain the proper + * ElementTypes in their {@code @Target} annotations */ public class SyntheticAnnotationTargetTest { @Test diff --git a/src/test/resources/annoingenerictarget/expected/com/example/Simple.java b/src/test/resources/annoingenerictarget/expected/com/example/Simple.java index 8812a5d42..417a8fd1e 100644 --- a/src/test/resources/annoingenerictarget/expected/com/example/Simple.java +++ b/src/test/resources/annoingenerictarget/expected/com/example/Simple.java @@ -5,6 +5,7 @@ import org.checkerframework.checker.initialization.qual.Initialized; class Simple { + @Initialized @NonNull @UnknownKeyFor diff --git a/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/initialization/qual/Initialized.java b/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/initialization/qual/Initialized.java index 40e51dcd0..f0b654af6 100644 --- a/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/initialization/qual/Initialized.java +++ b/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/initialization/qual/Initialized.java @@ -1,5 +1,5 @@ package org.checkerframework.checker.initialization.qual; -@java.lang.annotation.Target({ java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.PACKAGE, java.lang.annotation.ElementType.TYPE_USE }) +@java.lang.annotation.Target({ java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.TYPE_USE }) public @interface Initialized { } diff --git a/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/nullness/qual/KeyForBottom.java b/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/nullness/qual/KeyForBottom.java index 23ff3dede..8a3362c74 100644 --- a/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/nullness/qual/KeyForBottom.java +++ b/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/nullness/qual/KeyForBottom.java @@ -1,5 +1,5 @@ package org.checkerframework.checker.nullness.qual; -@java.lang.annotation.Target({ java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.PACKAGE, java.lang.annotation.ElementType.TYPE_USE }) +@java.lang.annotation.Target({ java.lang.annotation.ElementType.TYPE_USE }) public @interface KeyForBottom { } diff --git a/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/nullness/qual/NonNull.java b/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/nullness/qual/NonNull.java index a28e4efb9..9b23b52a0 100644 --- a/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/nullness/qual/NonNull.java +++ b/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/nullness/qual/NonNull.java @@ -1,5 +1,5 @@ package org.checkerframework.checker.nullness.qual; -@java.lang.annotation.Target({ java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.PACKAGE, java.lang.annotation.ElementType.TYPE_USE }) +@java.lang.annotation.Target({ java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.TYPE_USE }) public @interface NonNull { } diff --git a/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/nullness/qual/Nullable.java b/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/nullness/qual/Nullable.java index 178afb4c9..ec98e5864 100644 --- a/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/nullness/qual/Nullable.java +++ b/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/nullness/qual/Nullable.java @@ -1,5 +1,5 @@ package org.checkerframework.checker.nullness.qual; -@java.lang.annotation.Target({ java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.PACKAGE, java.lang.annotation.ElementType.TYPE_USE }) +@java.lang.annotation.Target({ java.lang.annotation.ElementType.TYPE_USE }) public @interface Nullable { } diff --git a/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/nullness/qual/UnknownKeyFor.java b/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/nullness/qual/UnknownKeyFor.java index 9798f5ce5..9bbb7d0bc 100644 --- a/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/nullness/qual/UnknownKeyFor.java +++ b/src/test/resources/annoingenerictarget/expected/org/checkerframework/checker/nullness/qual/UnknownKeyFor.java @@ -1,5 +1,5 @@ package org.checkerframework.checker.nullness.qual; -@java.lang.annotation.Target({ java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.PACKAGE, java.lang.annotation.ElementType.TYPE_USE }) +@java.lang.annotation.Target({ java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.TYPE_USE }) public @interface UnknownKeyFor { } diff --git a/src/test/resources/importanno/expected/com/example/Simple.java b/src/test/resources/importanno/expected/com/example/Simple.java index 094a89fc2..3c3d3d864 100644 --- a/src/test/resources/importanno/expected/com/example/Simple.java +++ b/src/test/resources/importanno/expected/com/example/Simple.java @@ -1,7 +1,6 @@ package com.example; import org.checkerframework.checker.mustcall.qual.Owning; - import java.net.Socket; public class Simple { diff --git a/src/test/resources/importanno/expected/org/checkerframework/checker/mustcall/qual/Owning.java b/src/test/resources/importanno/expected/org/checkerframework/checker/mustcall/qual/Owning.java index d1c3e2d88..463af1eee 100644 --- a/src/test/resources/importanno/expected/org/checkerframework/checker/mustcall/qual/Owning.java +++ b/src/test/resources/importanno/expected/org/checkerframework/checker/mustcall/qual/Owning.java @@ -6,6 +6,6 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD }) +@Target({ ElementType.FIELD }) public @interface Owning { } diff --git a/src/test/resources/issue272/expected/com/example/PostconditionAnnotation.java b/src/test/resources/issue272/expected/com/example/PostconditionAnnotation.java index 4fecef890..55c48127d 100644 --- a/src/test/resources/issue272/expected/com/example/PostconditionAnnotation.java +++ b/src/test/resources/issue272/expected/com/example/PostconditionAnnotation.java @@ -1,6 +1,5 @@ package com.example; -@java.lang.annotation.Target({ java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.PACKAGE, java.lang.annotation.ElementType.TYPE_USE }) +@java.lang.annotation.Target({}) public @interface PostconditionAnnotation { - } diff --git a/src/test/resources/issue272/expected/com/example/PreconditionAnnotation.java b/src/test/resources/issue272/expected/com/example/PreconditionAnnotation.java index dbdb27625..c4ec35f7f 100644 --- a/src/test/resources/issue272/expected/com/example/PreconditionAnnotation.java +++ b/src/test/resources/issue272/expected/com/example/PreconditionAnnotation.java @@ -1,6 +1,5 @@ package com.example; -@java.lang.annotation.Target({ java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.PACKAGE, java.lang.annotation.ElementType.TYPE_USE }) +@java.lang.annotation.Target({}) public @interface PreconditionAnnotation { - } diff --git a/src/test/resources/issue272/expected/com/example/Simple.java b/src/test/resources/issue272/expected/com/example/Simple.java index dc627a8da..4b3fbac77 100644 --- a/src/test/resources/issue272/expected/com/example/Simple.java +++ b/src/test/resources/issue272/expected/com/example/Simple.java @@ -5,6 +5,7 @@ public class Simple { public enum Kind { + PRECONDITION(PreconditionAnnotation.class), POSTCONDITION(PostconditionAnnotation.class); Kind(Class metaAnnotation) { diff --git a/src/test/resources/parameterwithannotations/expected/org/checkerframework/checker/nullness/qual/Nullable.java b/src/test/resources/parameterwithannotations/expected/org/checkerframework/checker/nullness/qual/Nullable.java index 0792cbabf..e0b78bcde 100644 --- a/src/test/resources/parameterwithannotations/expected/org/checkerframework/checker/nullness/qual/Nullable.java +++ b/src/test/resources/parameterwithannotations/expected/org/checkerframework/checker/nullness/qual/Nullable.java @@ -12,9 +12,9 @@ @Documented @Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) @SubtypeOf({}) @QualifierForLiterals({ LiteralKind.NULL }) @DefaultFor(types = { Void.class }) +@Target({ ElementType.TYPE_USE }) public @interface Nullable { } diff --git a/src/test/resources/preserveannotations/expected/org/checkerframework/checker/index/qual/GTENegativeOne.java b/src/test/resources/preserveannotations/expected/org/checkerframework/checker/index/qual/GTENegativeOne.java index 05f431ebd..9feacb455 100644 --- a/src/test/resources/preserveannotations/expected/org/checkerframework/checker/index/qual/GTENegativeOne.java +++ b/src/test/resources/preserveannotations/expected/org/checkerframework/checker/index/qual/GTENegativeOne.java @@ -1,7 +1,6 @@ package org.checkerframework.checker.index.qual; import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -9,7 +8,7 @@ @Documented @Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) @SubtypeOf({ LowerBoundUnknown.class }) +@Target({}) public @interface GTENegativeOne { } diff --git a/src/test/resources/preserveannotations/expected/org/checkerframework/checker/index/qual/LowerBoundUnknown.java b/src/test/resources/preserveannotations/expected/org/checkerframework/checker/index/qual/LowerBoundUnknown.java index 0ba975cb6..3ed496c62 100644 --- a/src/test/resources/preserveannotations/expected/org/checkerframework/checker/index/qual/LowerBoundUnknown.java +++ b/src/test/resources/preserveannotations/expected/org/checkerframework/checker/index/qual/LowerBoundUnknown.java @@ -1,7 +1,6 @@ package org.checkerframework.checker.index.qual; import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -11,9 +10,9 @@ @Documented @Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) @SubtypeOf({}) @DefaultQualifierInHierarchy @InvisibleQualifier +@Target({}) public @interface LowerBoundUnknown { } diff --git a/src/test/resources/preserveannotations/expected/org/checkerframework/checker/index/qual/NonNegative.java b/src/test/resources/preserveannotations/expected/org/checkerframework/checker/index/qual/NonNegative.java index 53e2a6475..60d78c603 100644 --- a/src/test/resources/preserveannotations/expected/org/checkerframework/checker/index/qual/NonNegative.java +++ b/src/test/resources/preserveannotations/expected/org/checkerframework/checker/index/qual/NonNegative.java @@ -9,7 +9,7 @@ @Documented @Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) @SubtypeOf({ GTENegativeOne.class }) +@Target({ ElementType.TYPE_USE }) public @interface NonNegative { } diff --git a/src/test/resources/preserveannotations/expected/org/checkerframework/checker/index/qual/Positive.java b/src/test/resources/preserveannotations/expected/org/checkerframework/checker/index/qual/Positive.java index e021f7dc4..dc90a1538 100644 --- a/src/test/resources/preserveannotations/expected/org/checkerframework/checker/index/qual/Positive.java +++ b/src/test/resources/preserveannotations/expected/org/checkerframework/checker/index/qual/Positive.java @@ -9,7 +9,7 @@ @Documented @Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER }) @SubtypeOf({ NonNegative.class }) +@Target({ ElementType.TYPE_USE }) public @interface Positive { } diff --git a/src/test/resources/syntheticannotations/expected/com/example/Anno.java b/src/test/resources/syntheticannotations/expected/com/example/Anno.java index 0fc248356..880aad8da 100644 --- a/src/test/resources/syntheticannotations/expected/com/example/Anno.java +++ b/src/test/resources/syntheticannotations/expected/com/example/Anno.java @@ -1,6 +1,6 @@ package com.example; -@java.lang.annotation.Target({ java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.PACKAGE, java.lang.annotation.ElementType.TYPE_USE }) +@java.lang.annotation.Target({ java.lang.annotation.ElementType.TYPE_USE }) public @interface Anno { public int value(); diff --git a/src/test/resources/syntheticannotations/expected/com/example/Bar.java b/src/test/resources/syntheticannotations/expected/com/example/Bar.java index 7f1406b70..e875d21d6 100644 --- a/src/test/resources/syntheticannotations/expected/com/example/Bar.java +++ b/src/test/resources/syntheticannotations/expected/com/example/Bar.java @@ -1,6 +1,6 @@ package com.example; -@java.lang.annotation.Target({ java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.PACKAGE, java.lang.annotation.ElementType.TYPE_USE }) +@java.lang.annotation.Target({ java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.TYPE_USE }) public @interface Bar { public int value(); diff --git a/src/test/resources/syntheticannotations/expected/com/example/Baz.java b/src/test/resources/syntheticannotations/expected/com/example/Baz.java index 5c95a9125..0af8ae911 100644 --- a/src/test/resources/syntheticannotations/expected/com/example/Baz.java +++ b/src/test/resources/syntheticannotations/expected/com/example/Baz.java @@ -1,6 +1,6 @@ package com.example; -@java.lang.annotation.Target({ java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.PACKAGE, java.lang.annotation.ElementType.TYPE_USE }) +@java.lang.annotation.Target({ java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.TYPE_USE }) public @interface Baz { public String foo(); diff --git a/src/test/resources/syntheticannotations/expected/com/example/Foo.java b/src/test/resources/syntheticannotations/expected/com/example/Foo.java index 1d14d16fa..765a8e335 100644 --- a/src/test/resources/syntheticannotations/expected/com/example/Foo.java +++ b/src/test/resources/syntheticannotations/expected/com/example/Foo.java @@ -1,6 +1,6 @@ package com.example; -@java.lang.annotation.Target({ java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.PACKAGE, java.lang.annotation.ElementType.TYPE_USE }) +@java.lang.annotation.Target({ java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.TYPE_USE }) public @interface Foo { public Deprecated x(); diff --git a/src/test/resources/syntheticannotationtarget/expected/com/example/AnnotationDeclaration.java b/src/test/resources/syntheticannotationtarget/expected/com/example/AnnotationDeclaration.java new file mode 100644 index 000000000..77aef09fc --- /dev/null +++ b/src/test/resources/syntheticannotationtarget/expected/com/example/AnnotationDeclaration.java @@ -0,0 +1,5 @@ +package com.example; + +@java.lang.annotation.Target({ java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.TYPE_USE }) +public @interface AnnotationDeclaration { +} diff --git a/src/test/resources/syntheticannotationtarget/expected/com/example/Constructor.java b/src/test/resources/syntheticannotationtarget/expected/com/example/Constructor.java new file mode 100644 index 000000000..859353dc5 --- /dev/null +++ b/src/test/resources/syntheticannotationtarget/expected/com/example/Constructor.java @@ -0,0 +1,5 @@ +package com.example; + +@java.lang.annotation.Target({ java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.TYPE_USE }) +public @interface Constructor { +} diff --git a/src/test/resources/syntheticannotationtarget/expected/com/example/EnumConstantDeclaration.java b/src/test/resources/syntheticannotationtarget/expected/com/example/EnumConstantDeclaration.java new file mode 100644 index 000000000..48c3d4c65 --- /dev/null +++ b/src/test/resources/syntheticannotationtarget/expected/com/example/EnumConstantDeclaration.java @@ -0,0 +1,5 @@ +package com.example; + +@java.lang.annotation.Target({ java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.TYPE_USE }) +public @interface EnumConstantDeclaration { +} diff --git a/src/test/resources/syntheticannotationtarget/expected/com/example/Field.java b/src/test/resources/syntheticannotationtarget/expected/com/example/Field.java new file mode 100644 index 000000000..fb44e3520 --- /dev/null +++ b/src/test/resources/syntheticannotationtarget/expected/com/example/Field.java @@ -0,0 +1,5 @@ +package com.example; + +@java.lang.annotation.Target({ java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.TYPE_USE }) +public @interface Field { +} diff --git a/src/test/resources/syntheticannotationtarget/expected/com/example/Foo.java b/src/test/resources/syntheticannotationtarget/expected/com/example/Foo.java deleted file mode 100644 index 5a3312cbc..000000000 --- a/src/test/resources/syntheticannotationtarget/expected/com/example/Foo.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.example; - -@java.lang.annotation.Target({ java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.ANNOTATION_TYPE, java.lang.annotation.ElementType.PACKAGE, java.lang.annotation.ElementType.TYPE_USE }) -public @interface Foo { -} diff --git a/src/test/resources/syntheticannotationtarget/expected/com/example/LocalVariable.java b/src/test/resources/syntheticannotationtarget/expected/com/example/LocalVariable.java new file mode 100644 index 000000000..4788e894e --- /dev/null +++ b/src/test/resources/syntheticannotationtarget/expected/com/example/LocalVariable.java @@ -0,0 +1,5 @@ +package com.example; + +@java.lang.annotation.Target({ java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.TYPE_USE }) +public @interface LocalVariable { +} diff --git a/src/test/resources/syntheticannotationtarget/expected/com/example/Method.java b/src/test/resources/syntheticannotationtarget/expected/com/example/Method.java new file mode 100644 index 000000000..625d4f55f --- /dev/null +++ b/src/test/resources/syntheticannotationtarget/expected/com/example/Method.java @@ -0,0 +1,5 @@ +package com.example; + +@java.lang.annotation.Target({ java.lang.annotation.ElementType.METHOD }) +public @interface Method { +} diff --git a/src/test/resources/syntheticannotationtarget/expected/com/example/Param.java b/src/test/resources/syntheticannotationtarget/expected/com/example/Param.java new file mode 100644 index 000000000..535453636 --- /dev/null +++ b/src/test/resources/syntheticannotationtarget/expected/com/example/Param.java @@ -0,0 +1,5 @@ +package com.example; + +@java.lang.annotation.Target({ java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.TYPE_USE }) +public @interface Param { +} diff --git a/src/test/resources/syntheticannotationtarget/expected/com/example/Simple.java b/src/test/resources/syntheticannotationtarget/expected/com/example/Simple.java index d2ad2e147..4ec860c36 100644 --- a/src/test/resources/syntheticannotationtarget/expected/com/example/Simple.java +++ b/src/test/resources/syntheticannotationtarget/expected/com/example/Simple.java @@ -1,24 +1,39 @@ package com.example; -@Foo -public class Simple<@Foo T> { - @Foo +import java.lang.annotation.Target; +import java.util.*; +import static java.lang.annotation.ElementType.*; + +@Type +public class Simple<@TypeParam T> { + + @Method @AnnoDecl - public <@Foo U> void baz(@Foo U u) { - Simple<@Foo String> simple = new Simple<>(); - @Foo + public <@TypeParam U> void baz(@Param U u) { + @LocalVariable + Simple<@TypeParam String> simple = new Simple<>(); + @LocalVariable int x = simple.field; + EnumTest e = EnumTest.A; + List y; } - @Foo + @Field public int field; - @Foo + @Constructor public Simple() { throw new Error(); } - @Foo + @AnnotationDeclaration + @Target({ METHOD }) private @interface AnnoDecl { } + + enum EnumTest { + + @EnumConstantDeclaration + A + } } diff --git a/src/test/resources/syntheticannotationtarget/expected/com/example/Type.java b/src/test/resources/syntheticannotationtarget/expected/com/example/Type.java new file mode 100644 index 000000000..f0c6f9946 --- /dev/null +++ b/src/test/resources/syntheticannotationtarget/expected/com/example/Type.java @@ -0,0 +1,5 @@ +package com.example; + +@java.lang.annotation.Target({ java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.TYPE_USE }) +public @interface Type { +} diff --git a/src/test/resources/syntheticannotationtarget/expected/com/example/TypeParam.java b/src/test/resources/syntheticannotationtarget/expected/com/example/TypeParam.java new file mode 100644 index 000000000..918f91cad --- /dev/null +++ b/src/test/resources/syntheticannotationtarget/expected/com/example/TypeParam.java @@ -0,0 +1,5 @@ +package com.example; + +@java.lang.annotation.Target({ java.lang.annotation.ElementType.TYPE_PARAMETER, java.lang.annotation.ElementType.TYPE_USE }) +public @interface TypeParam { +} diff --git a/src/test/resources/syntheticannotationtarget/input/com/example/Simple.java b/src/test/resources/syntheticannotationtarget/input/com/example/Simple.java index 375b8bc68..4d4731cd0 100644 --- a/src/test/resources/syntheticannotationtarget/input/com/example/Simple.java +++ b/src/test/resources/syntheticannotationtarget/input/com/example/Simple.java @@ -1,22 +1,37 @@ package com.example; -@Foo -public class Simple<@Foo T> { - @Foo +import java.lang.annotation.Target; +import java.util.*; + +import static java.lang.annotation.ElementType.*; + +@Type +public class Simple<@TypeParam T> { + @Method @AnnoDecl - public <@Foo U> void baz(@Foo U u) { - Simple<@Foo String> simple = new Simple<>(); - @Foo int x = simple.field; + public <@TypeParam U> void baz(@Param U u) { + @LocalVariable + Simple<@TypeParam String> simple = new Simple<>(); + @LocalVariable int x = simple.field; + EnumTest e = EnumTest.A; + List y; } - @Foo + @Field public int field; - @Foo + @Constructor public Simple() { } - @Foo + @AnnotationDeclaration + // METHOD should be the only one that remains + @Target({METHOD, FIELD, TYPE_USE}) private @interface AnnoDecl { } + + enum EnumTest { + @EnumConstantDeclaration + A + } }