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