1 /*
2  * Copyright 2020 The JSpecify Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.devtools.javatools.typeannotationrefactoring;
17 
18 import static com.google.common.base.Verify.verify;
19 import static com.google.common.base.Verify.verifyNotNull;
20 import static com.google.common.collect.Iterables.getOnlyElement;
21 import static com.google.common.collect.Range.closedOpen;
22 import static com.sun.source.tree.Tree.Kind.IDENTIFIER;
23 import static com.sun.source.tree.Tree.Kind.MEMBER_SELECT;
24 import static java.nio.charset.StandardCharsets.UTF_8;
25 import static java.nio.file.Files.readAllBytes;
26 import static java.nio.file.Files.write;
27 import static java.util.Arrays.stream;
28 import static java.util.Comparator.comparing;
29 import static javax.tools.Diagnostic.Kind.ERROR;
30 import static javax.tools.StandardLocation.PLATFORM_CLASS_PATH;
31 
32 import com.google.common.collect.ImmutableList;
33 import com.google.common.collect.ImmutableMap;
34 import com.google.common.collect.ImmutableSet;
35 import com.google.common.collect.Range;
36 import com.sun.source.tree.AnnotatedTypeTree;
37 import com.sun.source.tree.AnnotationTree;
38 import com.sun.source.tree.ClassTree;
39 import com.sun.source.tree.CompilationUnitTree;
40 import com.sun.source.tree.IdentifierTree;
41 import com.sun.source.tree.ImportTree;
42 import com.sun.source.tree.LiteralTree;
43 import com.sun.source.tree.MemberSelectTree;
44 import com.sun.source.tree.MethodTree;
45 import com.sun.source.tree.NewArrayTree;
46 import com.sun.source.tree.Tree;
47 import com.sun.source.tree.TypeParameterTree;
48 import com.sun.source.tree.VariableTree;
49 import com.sun.source.tree.WildcardTree;
50 import com.sun.source.util.SimpleTreeVisitor;
51 import com.sun.source.util.TreePath;
52 import com.sun.source.util.TreePathScanner;
53 import com.sun.tools.javac.file.JavacFileManager;
54 import com.sun.tools.javac.parser.JavacParser;
55 import com.sun.tools.javac.parser.ParserFactory;
56 import com.sun.tools.javac.tree.JCTree;
57 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
58 import com.sun.tools.javac.util.Context;
59 import com.sun.tools.javac.util.Log;
60 import com.sun.tools.javac.util.Options;
61 import java.io.IOError;
62 import java.io.IOException;
63 import java.io.UncheckedIOException;
64 import java.net.URI;
65 import java.nio.file.Path;
66 import java.nio.file.Paths;
67 import java.util.Comparator;
68 import java.util.Map.Entry;
69 import java.util.SortedMap;
70 import java.util.TreeMap;
71 import java.util.stream.Stream;
72 import javax.lang.model.element.Name;
73 import javax.tools.DiagnosticCollector;
74 import javax.tools.DiagnosticListener;
75 import javax.tools.JavaFileObject;
76 import javax.tools.SimpleJavaFileObject;
77 
78 /**
79  * A best effort refactoring to migrate Checker Framework {@code @Nullable} type annotations to
80  * similar-ish JSpecify annotations.
81  *
82  * <p>usage: CheckerFrameworkToJspecifyRefactoring [files]
83  */
84 public final class CheckerFrameworkToJspecifyRefactoring {
main(String[] args)85   public static void main(String[] args) {
86     stream(args).parallel().forEach(file -> process(file));
87   }
88 
process(String file)89   private static void process(String file) {
90     if (!file.endsWith(".java")) {
91       return;
92     }
93     try {
94       Path path = Paths.get(file);
95       String input = new String(readAllBytes(path), UTF_8);
96       String output = refactor(input);
97       if (!input.equals(output)) {
98         write(path, output.getBytes(UTF_8));
99       }
100     } catch (IOException e) {
101       throw new UncheckedIOException(file, e);
102     }
103   }
104 
105   private static final ImmutableMap<String, String> SUBSTITUTES =
106       ImmutableMap.of("Nullable", "Nullable", "PolyNull", "Nullable");
107 
refactor(String input)108   private static String refactor(String input) {
109     SortedMap<Range<Integer>, String> definiteReplacements = new TreeMap<>(BY_START_THEN_END);
110     SortedMap<Range<Integer>, String> possibleReplacements = new TreeMap<>(BY_START_THEN_END);
111     Context context = new Context();
112     JCCompilationUnit unit = parse(context, input);
113     boolean[] sawAnnotatedForNullness = new boolean[1];
114 
115     new TreePathScanner<Void, Void>() {
116       boolean addedImports;
117       boolean inNullHostileClass;
118       boolean inJavaUtil;
119 
120       @Override
121       public Void visitCompilationUnit(CompilationUnitTree node, Void aVoid) {
122         // TODO(cpovirk): Calling Tree.toString() is a hack.
123         inJavaUtil = node.getPackageName().toString().startsWith("java.util");
124         return super.visitCompilationUnit(node, aVoid);
125       }
126 
127       @Override
128       public Void visitClass(ClassTree node, Void aVoid) {
129         boolean oldInNullHostileClass = inNullHostileClass;
130         try {
131           inNullHostileClass |=
132               node.getSimpleName().contentEquals("ConcurrentHashMap")
133                   || node.getSimpleName().contentEquals("ConcurrentLinkedDeque")
134                   || node.getSimpleName().contentEquals("ConcurrentSkipListMap")
135                   || node.getSimpleName().contentEquals("ConcurrentSkipListSet")
136                   || node.getSimpleName().contentEquals("Dictionary")
137                   || node.getSimpleName().contentEquals("Hashtable")
138                   || node.getSimpleName().contentEquals("Properties")
139                   || node.getSimpleName().contentEquals("UIDefaults")
140                   || node.getSimpleName().contentEquals("UIManager");
141           return super.visitClass(node, aVoid);
142         } finally {
143           inNullHostileClass = oldInNullHostileClass;
144         }
145       }
146 
147       @Override
148       public Void visitImport(ImportTree node, Void aVoid) {
149         if (!addedImports) {
150           addedImports = true;
151           definiteReplacements.put(atStart(node), "import org.jspecify.annotations.Nullable;\n");
152           possibleReplacements.put(
153               atStart(node),
154               "import org.jspecify.annotations.NullMarked;\n"
155                   + "import org.jspecify.annotations.Nullable;\n");
156         }
157         if (node.getQualifiedIdentifier().toString().startsWith("org.checkerframework.")) {
158           definiteReplacements.put(inPlaceOfNodeAndTrailingNewline(node, unit), "");
159         }
160         return super.visitImport(node, aVoid);
161       }
162 
163       @Override
164       public Void visitAnnotation(AnnotationTree node, Void aVoid) {
165         String simpleName = getSimpleName(node.getAnnotationType());
166         if (SUBSTITUTES.containsKey(simpleName)) {
167           // putIfAbsent in case we're removing the annotation entirely (for <@Nullable T>).
168           definiteReplacements.putIfAbsent(
169               inPlaceOf(node, unit), "@" + SUBSTITUTES.get(simpleName));
170         } else if (simpleName.equals("AnnotatedFor")) {
171           sawAnnotatedForNullness[0] =
172               node.getArguments().stream()
173                   .flatMap(
174                       a ->
175                           a instanceof NewArrayTree
176                               ? ((NewArrayTree) a).getInitializers().stream()
177                               : Stream.of(a))
178                   .anyMatch(a -> ((LiteralTree) a).getValue().equals("nullness"));
179           definiteReplacements.put(inPlaceOf(node, unit), "");
180           possibleReplacements.put(inPlaceOf(node, unit), "@NullMarked");
181         } else if (CF_ANNOTATIONS.contains(simpleName)) {
182           definiteReplacements.put(inPlaceOf(node, unit), "");
183         }
184         return super.visitAnnotation(node, aVoid);
185       }
186 
187       @Override
188       public Void visitTypeParameter(TypeParameterTree node, Void aVoid) {
189         if (node.getBounds().isEmpty()) {
190           possibleReplacements.put(atEnd(node, unit), " extends @Nullable Object");
191         } else if (soleBoundIsNonNullObject(node)) {
192           definiteReplacements.put(inPlaceOf(node, unit), node.getName().toString());
193           /*
194            * Don't visit children: Doing so may produce overlapping edits to remove individual
195            * annotations (@NonNull on the bound and/or an annotation on the type parameter itself).
196            */
197           return null;
198         }
199         for (AnnotationTree a : node.getAnnotations()) {
200           definiteReplacements.put(inPlaceOf(a, unit), "");
201         }
202         return super.visitTypeParameter(node, aVoid);
203       }
204 
205       @Override
206       public Void visitAnnotatedType(AnnotatedTypeTree node, Void aVoid) {
207         if (node.getUnderlyingType() instanceof WildcardTree) {
208           for (AnnotationTree a : node.getAnnotations()) {
209             definiteReplacements.put(inPlaceOf(a, unit), "");
210           }
211         }
212         return super.visitAnnotatedType(node, aVoid);
213       }
214 
215       @Override
216       public Void visitVariable(VariableTree node, Void aVoid) {
217         Tree parent = getCurrentPath().getParentPath().getLeaf();
218         if (!(parent instanceof MethodTree)) {
219           return super.visitVariable(node, aVoid);
220         }
221         MethodTree method = (MethodTree) parent;
222 
223         if (method.getReceiverParameter() == node) {
224           if (method.getParameters().isEmpty()) {
225             definiteReplacements.put(inPlaceOf(node, unit), "");
226           } else {
227             definiteReplacements.put(
228                 closedOpen(startPos(node), startPos(method.getParameters().get(0))), "");
229           }
230           /*
231            * Don't visit children: Doing so may produce overlapping edits to remove individual
232            * annotations (typically @Nullable on the receiver variable).
233            */
234           return null;
235         }
236 
237         /*
238          * TODO(cpovirk): The following is imperfect. Notably, some classes are only *partially*
239          * null-hostile. An example is ConcurrentHashMap, whose 2-arg remove method rejects null
240          * keys but tolerates null values, even though ConcurrentHashMap normally rejects both. But
241          * maybe we'll just fix those up by hand, rather than add special cases here.
242          */
243         if (inJavaUtil
244             && !inNullHostileClass
245             && (method.getName().contentEquals("contains")
246                 || method.getName().contentEquals("containsKey")
247                 || method.getName().contentEquals("containsValue")
248                 || method.getName().contentEquals("get")
249                 || method.getName().contentEquals("indexOf")
250                 || method.getName().contentEquals("lastIndexOf")
251                 || method.getName().contentEquals("remove")
252                 || method.getName().contentEquals("removeFirstOccurrence")
253                 || method.getName().contentEquals("removeLastOccurrence"))
254             && method.getParameters().size() == 1
255             && (node.getType().getKind() == IDENTIFIER || node.getType().getKind() == MEMBER_SELECT)
256             && getSimpleName(node.getType()).equals("Object")) {
257           definiteReplacements.put(inPlaceOf(node, unit), "@Nullable Object " + node.getName());
258           // Don't visit children.
259           return null;
260         }
261 
262         if (inJavaUtil
263             && !inNullHostileClass
264             && (method.getName().contentEquals("getOrDefault") // first parameter only (type Object)
265                 || method.getName().contentEquals("remove")) // both parameters
266             && method.getParameters().size() == 2
267             && (node.getType().getKind() == IDENTIFIER || node.getType().getKind() == MEMBER_SELECT)
268             && getSimpleName(node.getType()).equals("Object")) {
269           definiteReplacements.put(inPlaceOf(node, unit), "@Nullable Object " + node.getName());
270           // Don't visit children.
271           return null;
272         }
273 
274         if (inJavaUtil
275             && !inNullHostileClass
276             && method.getName().contentEquals("containsAll")
277             && method.getParameters().size() == 1) {
278           definiteReplacements.put(inPlaceOf(node, unit), "Collection<?> " + node.getName());
279           // Don't visit children.
280           return null;
281         }
282 
283         /*
284          * Calling removeAll(collectionContainingNull) and retainAll(collectionContainingNull)
285          * typically works even on null-hostile collections: They are often implemented by iterating
286          * over the *receiver* collection and calling contains(e) on the *argument* collection.
287          * Thus, if there's a problem, it comes from the class of the *argument*. Those methods are
288          * even documented accordingly. So we make the parameter type `Collection<?>` even in
289          * null-hostile classes.
290          *
291          * TODO(cpovirk): But there are still exceptions: ConcurrentSkipListSet iterates over the
292          * argument collection and calls this.remove(e), which rejects null. It is documented
293          * accordingly. It would be nice for us to annotate it accordingly. But maybe we'll just fix
294          * that up by hand, rather than add a special case here.
295          *
296          * A *partial* exception is AbstractSet.removeAll, which *sometimes* uses a
297          * ConcurrentSkipListSet-style implementation and sometimes does not (at least for now:
298          * https://bugs.openjdk.java.net/browse/JDK-6394757). However, we want to accept
299          * Collection<?> there because some AbstractSet subclasses permit this.remove(null).
300          * TODO(cpovirk): It could be nice to add "fake overrides" with signature
301          * `removeAll(Collection<? extends Object>)` in problematic subclasses, but we'd want to
302          * make sure that checkers would actually apply them (since those overrides don't exist in
303          * the java.** sources).
304          */
305         if (inJavaUtil
306             && (method.getName().contentEquals("removeAll")
307                 || method.getName().contentEquals("retainAll"))
308             && method.getParameters().size() == 1) {
309           definiteReplacements.put(inPlaceOf(node, unit), "Collection<?> " + node.getName());
310           // Don't visit children.
311           return null;
312         }
313 
314         /*
315          * TODO(cpovirk): Consider also putting @Nullable on the parameter of all equals() methods,
316          * @Nullable on the String and Throwable parameters of all Throwable constructors, and
317          * anything else that can typically be done mechanically. Better yet, make such changes
318          * upstream.
319          */
320         return super.visitVariable(node, aVoid);
321       }
322     }.scan(new TreePath(unit), null);
323     if (sawAnnotatedForNullness[0]) {
324       definiteReplacements.putAll(possibleReplacements);
325     }
326     return applyFixes(input, definiteReplacements);
327   }
328 
soleBoundIsNonNullObject(TypeParameterTree node)329   private static boolean soleBoundIsNonNullObject(TypeParameterTree node) {
330     if (node.getBounds().size() != 1) {
331       return false;
332     }
333     Tree boundAsTree = getOnlyElement(node.getBounds());
334     if (!(boundAsTree instanceof AnnotatedTypeTree)) {
335       return false;
336     }
337     AnnotatedTypeTree boundAsAnnotatedTree = (AnnotatedTypeTree) boundAsTree;
338     return getSimpleName(boundAsAnnotatedTree.getUnderlyingType()).equals("Object")
339         && isUnannotatedOrNonNull(boundAsAnnotatedTree);
340   }
341 
isUnannotatedOrNonNull(AnnotatedTypeTree node)342   private static boolean isUnannotatedOrNonNull(AnnotatedTypeTree node) {
343     switch (node.getAnnotations().size()) {
344       case 0:
345         return true;
346       case 1:
347         AnnotationTree annotation = getOnlyElement(node.getAnnotations());
348         return getSimpleName(annotation.getAnnotationType()).equals("NonNull");
349       default:
350         // TODO(cpovirk): This could in principle come up. Handle it.
351         throw new RuntimeException(
352             "Handling of bounds with multiple annotations not yet implemented for " + node);
353     }
354   }
355 
atStart(Tree classTree)356   private static Range<Integer> atStart(Tree classTree) {
357     int startPos = startPos(classTree);
358     return closedOpen(startPos, startPos);
359   }
360 
atEnd(Tree node, JCCompilationUnit unit)361   private static Range<Integer> atEnd(Tree node, JCCompilationUnit unit) {
362     int endPos = endPos(node, unit);
363     return closedOpen(endPos, endPos);
364   }
365 
inPlaceOf(Tree node, JCCompilationUnit unit)366   private static Range<Integer> inPlaceOf(Tree node, JCCompilationUnit unit) {
367     return closedOpen(startPos(node), endPos(node, unit));
368   }
369 
inPlaceOfNodeAndTrailingNewline(Tree node, JCCompilationUnit unit)370   private static Range<Integer> inPlaceOfNodeAndTrailingNewline(Tree node, JCCompilationUnit unit) {
371     return closedOpen(startPos(node), endPos(node, unit) + 1);
372   }
373 
startPos(Tree tree)374   private static int startPos(Tree tree) {
375     int startPos = ((JCTree) tree).getStartPosition();
376     verify(startPos >= 0);
377     return startPos;
378   }
379 
endPos(Tree node, JCCompilationUnit unit)380   private static int endPos(Tree node, JCCompilationUnit unit) {
381     int endPos = ((JCTree) node).getEndPosition(unit.endPositions);
382     verify(endPos >= 0);
383     return endPos;
384   }
385 
386   /** Parses {@code input} as a Java compilation unit. */
parse(Context context, String input)387   private static JCCompilationUnit parse(Context context, String input) {
388     DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
389     context.put(DiagnosticListener.class, diagnostics);
390     Options.instance(context).put("allowStringFolding", "false");
391     JavacFileManager fileManager = new JavacFileManager(context, true, UTF_8);
392     try {
393       fileManager.setLocation(PLATFORM_CLASS_PATH, ImmutableList.of());
394     } catch (IOException e) {
395       // impossible
396       throw new IOError(e);
397     }
398     SimpleJavaFileObject source =
399         new SimpleJavaFileObject(URI.create("source"), JavaFileObject.Kind.SOURCE) {
400           @Override
401           public CharSequence getCharContent(boolean ignoreEncodingErrors) {
402             return input;
403           }
404         };
405     Log.instance(context).useSource(source);
406     ParserFactory parserFactory = ParserFactory.instance(context);
407     JavacParser parser =
408         parserFactory.newParser(
409             input, /* keepDocComments= */ true, /* keepEndPos= */ true, /* keepLineMap= */ true);
410     JCCompilationUnit unit = parser.parseCompilationUnit();
411     unit.sourcefile = source;
412     if (diagnostics.getDiagnostics().stream().anyMatch(d -> d.getKind() == ERROR)) {
413       throw new AssertionError(diagnostics.getDiagnostics());
414     }
415     return unit;
416   }
417 
418   /** Applies the given replacements to the source text. */
applyFixes(String source, SortedMap<Range<Integer>, String> replacements)419   private static String applyFixes(String source, SortedMap<Range<Integer>, String> replacements) {
420     if (replacements.isEmpty()) {
421       return source;
422     }
423     StringBuilder sb = new StringBuilder(source);
424     int offset = 0;
425     for (Entry<Range<Integer>, String> replacement : replacements.entrySet()) {
426       Range<Integer> range = replacement.getKey();
427       String replaceWith = replacement.getValue();
428       int start = offset + range.lowerEndpoint();
429       int end = offset + range.upperEndpoint();
430       sb.replace(start, end, replaceWith);
431       offset += replaceWith.length() - (range.upperEndpoint() - range.lowerEndpoint());
432     }
433     return sb.toString();
434   }
435 
436   /** Gets the simple name of a select or identifier tree. */
getSimpleName(Tree tree)437   private static String getSimpleName(Tree tree) {
438     Name name =
439         tree.accept(
440             new SimpleTreeVisitor<Name, Void>() {
441               @Override
442               public Name visitMemberSelect(MemberSelectTree node, Void unused) {
443                 return node.getIdentifier();
444               }
445 
446               @Override
447               public Name visitIdentifier(IdentifierTree node, Void unused) {
448                 return node.getName();
449               }
450             },
451             null);
452     verifyNotNull(name, "name for %s %s", tree.getKind(), tree);
453     return name.toString();
454   }
455 
456   private static final ImmutableSet<String> CF_ANNOTATIONS =
457       ImmutableSet.of(
458           "A",
459           "Acceleration",
460           "AlwaysSafe",
461           "Angle",
462           "AnnotatedFor",
463           "Area",
464           "ArrayLen",
465           "ArrayLenRange",
466           "AssertNonNullIfNonNull",
467           "AwtAlphaCompositingRule",
468           "AwtColorSpace",
469           "AwtCursorType",
470           "AwtFlowLayout",
471           "BinaryName",
472           "BinaryNameInUnnamedPackage",
473           "BoolVal",
474           "Bottom",
475           "BottomThis",
476           "BottomVal",
477           "C",
478           "cd",
479           "CFComment",
480           "CanonicalName",
481           "CanonicalNameOrEmpty",
482           "ClassBound",
483           "ClassGetName",
484           "ClassGetSimpleName",
485           "ClassVal",
486           "ClassValBottom",
487           "CompilerMessageKey",
488           "CompilerMessageKeyBottom",
489           "ConditionalPostconditionAnnotation",
490           "ConversionCategory",
491           "Covariant",
492           "Current",
493           "DefaultFor",
494           "DefaultQualifier",
495           "DefaultQualifierForUse",
496           "DefaultQualifierInHierarchy",
497           "degrees",
498           "Deterministic",
499           "DotSeparatedIdentifiers",
500           "DoubleVal",
501           "EnsuresKeyFor",
502           "EnsuresKeyForIf",
503           "EnsuresLockHeld",
504           "EnsuresLockHeldIf",
505           "EnsuresLTLengthOf",
506           "EnsuresLTLengthOfIf",
507           "EnsuresMinLenIf",
508           "EnsuresNonNull",
509           "EnsuresNonNullIf",
510           "EnsuresQualifier",
511           "EnsuresQualifierIf",
512           "FBCBottom",
513           "Fenum",
514           "FenumBottom",
515           "FenumTop",
516           "FenumUnqualified",
517           "FieldDescriptor",
518           "FieldDescriptorForPrimitive",
519           "FieldDescriptorForPrimitiveOrArrayInUnnamedPackage",
520           "FieldInvariant",
521           "Format",
522           "FormatBottom",
523           "FormatMethod",
524           "FormatUtil",
525           "ForName",
526           "FqBinaryName",
527           "FromByteCode",
528           "FromStubFile",
529           "FullyQualifiedName",
530           "g",
531           "GetClass",
532           "GetConstructor",
533           "GetMethod",
534           "GTENegativeOne",
535           "GuardedBy",
536           "GuardedByBottom",
537           "GuardedByUnknown",
538           "GuardSatisfied",
539           "h",
540           "HasSubsequence",
541           "Holding",
542           "I18nChecksFormat",
543           "I18nConversionCategory",
544           "I18nFormat",
545           "I18nFormatBottom",
546           "I18nFormatFor",
547           "I18nFormatUtil",
548           "I18nInvalidFormat",
549           "I18nMakeFormat",
550           "I18nUnknownFormat",
551           "I18nValidFormat",
552           "Identifier",
553           "IdentifierOrArray",
554           "IgnoreInWholeProgramInference",
555           "IndexFor",
556           "IndexOrHigh",
557           "IndexOrLow",
558           "InheritedAnnotation",
559           "Initialized",
560           "InternalForm",
561           "Interned",
562           "InternedDistinct",
563           "InternMethod",
564           "IntRange",
565           "IntRangeFromGTENegativeOne",
566           "IntRangeFromNonNegative",
567           "IntRangeFromPositive",
568           "IntVal",
569           "InvalidFormat",
570           "InvisibleQualifier",
571           "Invoke",
572           "JavaExpression",
573           "K",
574           "KeyFor",
575           "KeyForBottom",
576           "kg",
577           "km",
578           "km2",
579           "kmPERh",
580           "LeakedToResult",
581           "Length",
582           "LengthOf",
583           "LessThan",
584           "LessThanBottom",
585           "LessThanUnknown",
586           "LiteralKind",
587           "LocalizableKey",
588           "LocalizableKeyBottom",
589           "Localized",
590           "LockHeld",
591           "LockingFree",
592           "LockPossiblyHeld",
593           "LowerBoundBottom",
594           "LowerBoundUnknown",
595           "LTEqLengthOf",
596           "LTLengthOf",
597           "LTOMLengthOf",
598           "Luminance",
599           "m",
600           "m2",
601           "Mass",
602           "MaybeAliased",
603           "MaybeLeaked",
604           "MaybePresent",
605           "MayReleaseLocks",
606           "MethodDescriptor",
607           "MethodVal",
608           "MethodValBottom",
609           "min",
610           "MinLen",
611           "MinLenFieldInvariant",
612           "MixedUnits",
613           "mm",
614           "mm2",
615           "mol",
616           "MonotonicNonNull",
617           "MonotonicQualifier",
618           "mPERs",
619           "mPERs2",
620           "NegativeIndexFor",
621           "NewInstance",
622           "NoDefaultQualifierForUse",
623           "NonLeaked",
624           "NonNegative",
625           "NonNull",
626           "NotOnlyInitialized",
627           "Nullable",
628           "NullnessUtil",
629           "Opt",
630           "PartialRegex",
631           "PolyFenum",
632           "PolyGuardedBy",
633           "PolyIndex",
634           "PolyInterned",
635           "PolyKeyFor",
636           "PolyLength",
637           "PolyLowerBound",
638           "PolymorphicQualifier",
639           "PolyNull",
640           "PolyPresent",
641           "PolyRegex",
642           "PolySameLen",
643           "PolySignature",
644           "PolySigned",
645           "PolyTainted",
646           "PolyUI",
647           "PolyUIEffect",
648           "PolyUIType",
649           "PolyUnit",
650           "PolyUpperBound",
651           "PolyValue",
652           "Positive",
653           "PostconditionAnnotation",
654           "PreconditionAnnotation",
655           "Prefix",
656           "Present",
657           "PropertyKey",
658           "PropertyKeyBottom",
659           "Pure",
660           "PurityUnqualified",
661           "QualifierArgument",
662           "QualifierForLiterals",
663           "radians",
664           "Regex",
665           "RegexBottom",
666           "RegexUtil",
667           "ReleasesNoLocks",
668           "RelevantJavaTypes",
669           "ReportCall",
670           "ReportCreation",
671           "ReportInherit",
672           "ReportOverride",
673           "ReportReadWrite",
674           "ReportUnqualified",
675           "ReportUse",
676           "ReportWrite",
677           "RequiresNonNull",
678           "RequiresQualifier",
679           "ReturnsFormat",
680           "s",
681           "SafeEffect",
682           "SafeType",
683           "SameLen",
684           "SameLenBottom",
685           "SameLenUnknown",
686           "SearchIndexBottom",
687           "SearchIndexFor",
688           "SearchIndexUnknown",
689           "SideEffectFree",
690           "SignatureBottom",
691           "SignatureUnknown",
692           "Signed",
693           "SignednessBottom",
694           "SignednessGlb",
695           "SignednessUtil",
696           "SignedPositive",
697           "Speed",
698           "StaticallyExecutable",
699           "StringVal",
700           "StubFiles",
701           "Substance",
702           "SubstringIndexBottom",
703           "SubstringIndexFor",
704           "SubstringIndexUnknown",
705           "SubtypeOf",
706           "SwingBoxOrientation",
707           "SwingCompassDirection",
708           "SwingElementOrientation",
709           "SwingHorizontalOrientation",
710           "SwingSplitPaneOrientation",
711           "SwingTextOrientation",
712           "SwingTitleJustification",
713           "SwingTitlePosition",
714           "SwingVerticalOrientation",
715           "Tainted",
716           "TargetLocations",
717           "Temperature",
718           "TerminatesExecution",
719           "This",
720           "Time",
721           "TypeKind",
722           "TypeUseLocation",
723           "UI",
724           "UIEffect",
725           "UIPackage",
726           "UIType",
727           "UnderInitialization",
728           "Unique",
729           "UnitsBottom",
730           "UnitsMultiple",
731           "UnitsRelations",
732           "UnitsTools",
733           "UnknownClass",
734           "UnknownCompilerMessageKey",
735           "UnknownFormat",
736           "UnknownInitialization",
737           "UnknownInterned",
738           "UnknownKeyFor",
739           "UnknownLocalizableKey",
740           "UnknownLocalized",
741           "UnknownMethod",
742           "UnknownPropertyKey",
743           "UnknownRegex",
744           "UnknownSignedness",
745           "UnknownThis",
746           "UnknownUnits",
747           "UnknownVal",
748           "Unqualified",
749           "Unsigned",
750           "Untainted",
751           "Unused",
752           "UpperBoundBottom",
753           "UpperBoundFor",
754           "UpperBoundUnknown",
755           "UsesObjectEquals");
756 
757   /*
758    * We insert only closedOpen ranges, so we can safely compare only by endpoints, without needing
759    * to check for differences in bound type.
760    */
761   private static final Comparator<Range<Integer>> BY_START_THEN_END =
762       comparing((Range<Integer> r) -> r.lowerEndpoint()).thenComparing(Range::upperEndpoint);
763 
CheckerFrameworkToJspecifyRefactoring()764   private CheckerFrameworkToJspecifyRefactoring() {}
765 }
766