Skip to content

Commit

Permalink
add ability to exclude annotations from RemoveAnnotationsForInference…
Browse files Browse the repository at this point in the history
… on a per-project basis (#5668)
  • Loading branch information
kelloggm committed Mar 2, 2023
1 parent 37f0261 commit 9a0d70b
Showing 1 changed file with 80 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,28 @@
import com.google.common.collect.Multimap;
import com.google.common.reflect.ClassPath;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.framework.util.JavaParserUtil;
import org.checkerframework.javacutil.BugInCF;
import org.plumelib.util.ArraysPlume;
import org.plumelib.util.CollectionsPlume;
import org.plumelib.util.StringsPlume;

Expand All @@ -51,6 +58,15 @@
*
* <p>Does not remove trusted annotations: those that the checker trusts rather than verifies.
*
* <p>Also does not remove annotations that the user requests to keep in the source code. Provide a
* list of annotations to keep via the {@code -keepFile} command line argument, which must be the
* first argument to this program if it is present. The second argument to the program should be the
* path to the keep file. The keep file itself should be a list of newline-separated annotation
* names (without {@literal @} symbols). Both the simple and fully-qualified name of each annotation
* usually should be included in the keep file (simple string-matching between the annotations in
* the keep file and the annotation names used in the source code whose annotations are being
* removed is used for annotation comparison). TODO: remove this restriction?
*
* <p>Does not remove annotations at locations where inference does no work:
*
* <ul>
Expand All @@ -65,13 +81,62 @@
*/
public class RemoveAnnotationsForInference {

/**
* Do not instantiate. This is a standalone program whose entry point is {@link #main(String[])}.
*/
private RemoveAnnotationsForInference() {
throw new Error("Do not instantiate RemoveAnnotationsForInference.");
}

/**
* A list of annotations not to remove (i.e., to keep in the source code). Used to prevent
* project-specific annotations that must remain for the project to build from being removed by
* this program. (It would be burdensome to add all project-specific annotations to the global
* list in {@link #isTrustedAnnotation(String)}.)
*/
private static @MonotonicNonNull Set<String> annotationsToKeep = null;

/**
* Processes each provided command-line argument; see {@link RemoveAnnotationsForInference class
* documentation} for details.
*
* @param args command-line arguments: directories to process
*/
public static void main(String[] args) {
// TODO: using plume-lib's options here would be better, but would add a dependency
// to the whole Checker Framework, which is undesirable. Move this program elsewhere
// (e.g., to a plume-lib project)?
if (args[0].contentEquals("-keepFile")) {
if (args.length < 2) {
System.err.println(
"Usage: -keepFile requires an argument immediately after it: the path to the keep"
+ " file.");
System.exit(2);
}
String keepFilePath = args[1];
try (Stream<String> lines = Files.lines(Paths.get(keepFilePath))) {
annotationsToKeep = lines.collect(Collectors.toSet());
} catch (FileNotFoundException e) {
System.err.println("Error: Keep file " + keepFilePath + " not found.");
System.exit(3);
} catch (IOException e) {
System.err.println("Problem reading keep file " + keepFilePath + ": " + e.getMessage());
System.exit(4);
}

// Check for common mistake of adding "@" before the annotation name.
for (String annotationToKeep : annotationsToKeep) {
if (annotationToKeep.startsWith("@")) {
System.err.println(
"Error: Keep file includes an @ symbol before this annotation: "
+ annotationToKeep
+ ". Annotations should be listed in the keep file without the @ symbol.");
System.exit(5);
}
}

args = ArraysPlume.subarray(args, 2, args.length - 2);
}
if (args.length < 1) {
System.err.println("Usage: provide one or more directory names to process");
System.exit(1);
Expand Down Expand Up @@ -265,6 +330,10 @@ List<AnnotationExpr> processAnnotation(AnnotationExpr n, List<AnnotationExpr> su
if (isTrustedAnnotation(name)) {
return superResult;
}
// Retain annotations that the user requested specifically should be kept.
if (shouldBeKept(name)) {
return superResult;
}
// Retain annotations for which warnings are suppressed.
if (isSuppressed(n)) {
return superResult;
Expand Down Expand Up @@ -350,6 +419,17 @@ static boolean isTrustedAnnotation(String name) {
|| name.equals("org.checkerframework.common.aliasing.qual.LeakedToResult");
}

/**
* Returns true iff the annotation is present in the user-supplied file of annotations to keep
* (via the {@code -keepFile} command-line option).
*
* @param name the annotation's name (simple or fully-qualified)
* @return true if the user requested that this annotation be kept in the source code
*/
static boolean shouldBeKept(String name) {
return (annotationsToKeep != null && annotationsToKeep.contains(name));
}

// This approach searches upward to find all the active warning suppressions.
// An alternative, more efficient approach would be to track the current set of warning
// suppressions, using a stack.
Expand Down

0 comments on commit 9a0d70b

Please sign in to comment.