From 0e5f879c11471b7dbbfb816d25341d1bac8e66d6 Mon Sep 17 00:00:00 2001 From: Michael Ernst Date: Wed, 4 Jan 2023 19:25:41 -0800 Subject: [PATCH] Annotation declarations are declarations --- checker/jtreg/nullness/issue3700/Client.out | 2 +- .../non-annotated/OptionGroup.java | 18 +++++++++++ .../SceneToStubWriter.java | 8 ++++- .../scenelib/ASceneWrapper.java | 8 ++++- .../framework/stub/AnnotationFileParser.java | 31 +++++++++++++++---- .../framework/stub/StubGenerator.java | 10 +++++- .../framework/util/JavaParserUtil.java | 6 ++++ 7 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 checker/tests/ainfer-testchecker/non-annotated/OptionGroup.java diff --git a/checker/jtreg/nullness/issue3700/Client.out b/checker/jtreg/nullness/issue3700/Client.out index f0d8acb7579..248618f6aa9 100644 --- a/checker/jtreg/nullness/issue3700/Client.out +++ b/checker/jtreg/nullness/issue3700/Client.out @@ -1,2 +1,2 @@ -warning: TimeUnitRange.astub:(line 3,col 1): TimeUnitRange is an enum, but stub file declared it as class TimeUnitRange {... +warning: TimeUnitRange.astub:(line 3,col 1): TimeUnitRange is an enum, but stub file declared it as class TimeUnitRange { public TimeUnitRange of(@Nullable String endUnit); } 1 warning diff --git a/checker/tests/ainfer-testchecker/non-annotated/OptionGroup.java b/checker/tests/ainfer-testchecker/non-annotated/OptionGroup.java new file mode 100644 index 00000000000..fcfbb134d7d --- /dev/null +++ b/checker/tests/ainfer-testchecker/non-annotated/OptionGroup.java @@ -0,0 +1,18 @@ +// This annotation from plume-lib options caused an error in +// a version of ajava-based WPI. The problem was that annotation +// declarations weren't considered possible declarations when checking +// whether a location can store a declaration annotation. + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface OptionGroup { + + String value(); + + boolean unpublicized() default false; +} diff --git a/framework/src/main/java/org/checkerframework/common/wholeprograminference/SceneToStubWriter.java b/framework/src/main/java/org/checkerframework/common/wholeprograminference/SceneToStubWriter.java index 65619fafc51..5ce68c8c1cb 100644 --- a/framework/src/main/java/org/checkerframework/common/wholeprograminference/SceneToStubWriter.java +++ b/framework/src/main/java/org/checkerframework/common/wholeprograminference/SceneToStubWriter.java @@ -546,8 +546,14 @@ private static int printClassDefinitions( // file, which will eventually be merged with this one. printWriter.print(formatAnnotations(aClass.getAnnotations())); } - if (aClass.isEnum(nameToPrint)) { + if (aClass.isAnnotation(nameToPrint)) { + printWriter.print("@interface "); + } else if (aClass.isEnum(nameToPrint)) { printWriter.print("enum "); + } else if (aClass.isInterface(nameToPrint)) { + printWriter.print("interface "); + } else if (aClass.isRecord(nameToPrint)) { + printWriter.print("record "); } else { printWriter.print("class "); } diff --git a/framework/src/main/java/org/checkerframework/common/wholeprograminference/scenelib/ASceneWrapper.java b/framework/src/main/java/org/checkerframework/common/wholeprograminference/scenelib/ASceneWrapper.java index f1eef8fcb71..1f9ced18b49 100644 --- a/framework/src/main/java/org/checkerframework/common/wholeprograminference/scenelib/ASceneWrapper.java +++ b/framework/src/main/java/org/checkerframework/common/wholeprograminference/scenelib/ASceneWrapper.java @@ -217,8 +217,14 @@ public void updateSymbolInformation(AClass aClass, @Nullable ClassSymbol classSy ClassSymbol outerClass = classSymbol; ClassSymbol previous = classSymbol; do { - if (outerClass.isEnum()) { + if (outerClass.getKind() == ElementKind.ANNOTATION_TYPE) { + aClass.markAsAnnotation(outerClass.getSimpleName().toString()); + } else if (outerClass.isEnum()) { aClass.markAsEnum(outerClass.getSimpleName().toString()); + } else if (outerClass.isInterface()) { + aClass.markAsInterface(outerClass.getSimpleName().toString()); + // } else if (outerClass.isRecord()) { + // aClass.markAsRecord(outerClass.getSimpleName().toString()); } Element element = classSymbol.getEnclosingElement(); if (element == null || element.getKind() == ElementKind.PACKAGE) { diff --git a/framework/src/main/java/org/checkerframework/framework/stub/AnnotationFileParser.java b/framework/src/main/java/org/checkerframework/framework/stub/AnnotationFileParser.java index 462e917cd63..dc425875cd9 100644 --- a/framework/src/main/java/org/checkerframework/framework/stub/AnnotationFileParser.java +++ b/framework/src/main/java/org/checkerframework/framework/stub/AnnotationFileParser.java @@ -849,6 +849,28 @@ private boolean skipNode(NodeWithAccessModifiers node) { && node.getModifiers().contains(Modifier.privateModifier()); } + /** + * Returns the string representation of {@code n}, one one line, truncated to {@code length} + * characters. + * + * @param n a JavaParser node + * @param length the maximum length of the string representation + * @return the truncated string representation of {@code n} + */ + private String javaParserNodeToStringTruncated(Node n, int length) { + String oneLine = + n.toString() + .replace("\t", " ") + .replace("\n", " ") + .replace("\r", " ") + .replaceAll(" +", " "); + if (oneLine.length() <= length) { + return oneLine; + } else { + return oneLine.substring(0, length - 3) + "..."; + } + } + /** * Process a type declaration: copy its annotations to {@code #annotationFileAnnos}. * @@ -916,8 +938,7 @@ private List processTypeDecl( typeDecl, innerName + " is an enum, but stub file declared it as " - + typeDecl.toString().split("\\R", 2)[0] - + "..."); + + javaParserNodeToStringTruncated(typeDecl, 100)); return null; } typeDeclTypeParameters = processEnum((EnumDeclaration) typeDecl, typeElt); @@ -928,8 +949,7 @@ private List processTypeDecl( typeDecl, innerName + " is an annotation, but stub file declared it as " - + typeDecl.toString().split("\\R", 2)[0] - + "..."); + + javaParserNodeToStringTruncated(typeDecl, 100)); return null; } typeDeclTypeParameters = processType(typeDecl, typeElt); @@ -941,8 +961,7 @@ private List processTypeDecl( typeDecl, innerName + " is a class or interface, but stub file declared it as " - + typeDecl.toString().split("\\R", 2)[0] - + "..."); + + javaParserNodeToStringTruncated(typeDecl, 100)); return null; } typeDeclTypeParameters = processType(typeDecl, typeElt); diff --git a/framework/src/main/java/org/checkerframework/framework/stub/StubGenerator.java b/framework/src/main/java/org/checkerframework/framework/stub/StubGenerator.java index 13e1ada2a9a..4a7d7c47bf7 100644 --- a/framework/src/main/java/org/checkerframework/framework/stub/StubGenerator.java +++ b/framework/src/main/java/org/checkerframework/framework/stub/StubGenerator.java @@ -191,11 +191,19 @@ private void printClass(TypeElement typeElement, @Nullable String outerClass) { } } - if (typeElement.getKind() == ElementKind.INTERFACE) { + // This could be a `switch` statement. + if (typeElement.getKind() == ElementKind.ANNOTATION_TYPE) { + out.print("@interface"); + } else if (typeElement.getKind() == ElementKind.ENUM) { + out.print("enum"); + } else if (typeElement.getKind() == ElementKind.INTERFACE) { out.print("interface"); + } else if (typeElement.getKind().name().equals("RECORD")) { + out.print("record"); } else if (typeElement.getKind() == ElementKind.CLASS) { out.print("class"); } else { + // Shouldn't this throw an exception? return; } diff --git a/framework/src/main/java/org/checkerframework/framework/util/JavaParserUtil.java b/framework/src/main/java/org/checkerframework/framework/util/JavaParserUtil.java index b10971e9504..f87e5a14972 100644 --- a/framework/src/main/java/org/checkerframework/framework/util/JavaParserUtil.java +++ b/framework/src/main/java/org/checkerframework/framework/util/JavaParserUtil.java @@ -8,6 +8,7 @@ import com.github.javaparser.ast.CompilationUnit; import com.github.javaparser.ast.Node; import com.github.javaparser.ast.StubUnit; +import com.github.javaparser.ast.body.AnnotationDeclaration; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.EnumDeclaration; import com.github.javaparser.ast.body.TypeDeclaration; @@ -221,6 +222,11 @@ public static TypeDeclaration getTypeDeclarationByName(CompilationUnit root, return enumDecl.get(); } + Optional annoDecl = root.getAnnotationDeclarationByName(name); + if (annoDecl.isPresent()) { + return annoDecl.get(); + } + Optional storage = root.getStorage(); if (storage.isPresent()) { throw new BugInCF("Type " + name + " not found in " + storage.get().getPath());