When the Checker Framework reports a warning, it’s best to fix the underlying problem, by changing the code or its annotations. For each warning, follow the methodology in Section 2.4.5 to correct the underlying problem.
This chapter describes what to do if the methodology of Section 2.4.5 indicates that you need to suppress the warning. You won’t change your code, but you will prevent the Checker Framework from reporting this particular warning to you. (Changing the code to fix a bug is another way to prevent the Checker Framework from issuing a warning, but it is not what this chapter is about.)
You may wish to suppress checker warnings because of unannotated libraries or unannotated portions of your own code, because of application invariants that are beyond the capabilities of the type system, because of checker limitations, because you are interested in only some of the guarantees provided by a checker, or for other reasons. Suppressing a warning is similar to writing a cast in a Java program: the programmer knows more about the type than the type system does and uses the warning suppression or cast to convey that information to the type system.
You can suppress a warning message in a single variable initializer, method, or class by using the following mechanisms:
• the @SuppressWarnings annotation (Section 34.1), or
• the @AssumeAssertion string in an assert message (Section 34.2).
You can suppress warnings throughout the codebase by using the following mechanisms:
• the -AsuppressWarnings command-line option (Section 34.3),
• the -AskipUses and -AonlyUses command-line options (Section 34.4),
• the -AskipDefs and -AonlyDefs command-line options (Section 34.5),
• the -AskipFiles and -AonlyFiles command-line options (Section 34.6),
• the -AuseConservativeDefaultsForUncheckedCode=source command-line option (Section 36.4),
• the -Alint command-line option, which enables/disables optional checks (Section 34.7),
• changing the specification of a method (Section 34.8), or
• not running the annotation processor (Section 34.9).
Some type-checkers can suppress warnings via
• checker-specific mechanisms (Section 34.10).
The rest of this chapter explains these mechanisms in turn.
You can use the -AwarnUnneededSuppressions command-line option to issue a warning for each @SuppressWarnings that does not suppress any warnings issued by the current checker.
@SuppressWarnings annotation
You can suppress specific errors and warnings by use of the @SuppressWarnings annotation, for example @SuppressWarnings("interning") or @SuppressWarnings("nullness"). Section 34.1.1 explains the syntax of the argument string.
A @SuppressWarnings annotation may be placed on program declarations such as a local variable declaration, a method, or a class. It
suppresses all warnings within that program element. Section 34.1.2 discusses where the annotation may be written in source code.
Section 34.1.3 gives best practices for writing @SuppressWarnings annotations.
@SuppressWarnings syntax
The @SuppressWarnings annotation takes a string argument, in one of the following forms: "checkername:messagekey", "checkername", or "messagekey".
The argument checkername is the checker name, without “Checker”. It is lowercase by default, though a checker can choose a different casing. For example, if you invoke a checker as javac -processor MyNiftyChecker ..., then you would suppress its error messages with
@SuppressWarnings("mynifty"). (An exception is the Subtyping Checker, for which you use the annotation name; see Section 30.1.) Sometimes, a checker honors multiple checkername arguments; use
the -AshowSuppressWarningsStrings command-line option to see them.
The argument messagekey is the message key for the error. Each warning message from the compiler gives the most specific suppression string that can be used to suppress that warning. An example is “dereference.of.nullable” in
MyFile.java:107: error: [dereference.of.nullable] dereference of possibly-null reference myList
myList.add(elt);
^
You are allowed to use any substring of a message key, so long as the substring extends at each end to a period or an end of the key. For example, to suppress a warning with message key "lock.expression.possibly.not.final", you could use
@SuppressWarnings("lock.expression.possibly.not.final"), @SuppressWarnings("lock.expression"), @SuppressWarnings("not.final"), or other variants. We recommend using the longest possible message key; a short message might suppress more
warnings than you expect.
The checkername "allcheckers" means all checkers. Using this is not recommended, except for messages common to all checkers such as purity-related messages when using -AcheckPurityAnnotations. The Checker Framework never issues an
unneeded.suppression warning about a @SuppressWarnings whose checkername is "allcheckers".
The special messagekey “all” means to suppress all warnings.
If the checkername part is omitted, the @SuppressWarnings applies to all checkers. If the messagekey part is omitted, the @SuppressWarnings applies to all messages (it suppresses all warnings from the given checker).
With the -ArequirePrefixInWarningSuppressions command-line option, the Checker Framework only suppresses warnings when the string is in the "checkername" or "checkername:messagekey" format, as in
@SuppressWarnings("nullness") or @SuppressWarnings("nullness:assignment"). For example, @SuppressWarnings("assignment") and @SuppressWarnings("all") have no effect (they are ignored) when
-ArequirePrefixInWarningSuppressions is used. You can use @SuppressWarnings("allcheckers") to suppress all Checker Framework warnings.
@SuppressWarnings can be written
@SuppressWarnings is a declaration annotation, so it may be placed on program declarations such as a local variable declaration, a method, or a class.
@SuppressWarnings cannot be used on statements, expressions, or types. To work around this, you can
• extract an expression into a local variable and suppress a warning on its declaration,
• write assert plus @AssumeAssertion between statements; it can affect arbitrary expressions (see Section 34.2), or
• use a type-system-specific method call, such as NullnessUtil.castNonNull.
Always write a @SuppressWarnings annotation on the smallest possible scope. To reduce the scope of a @SuppressWarnings annotation, it is sometimes desirable to refactor the code. You might extract an expression into a local variable, so that warnings can be suppressed just
for that local variable’s initializer expression. Likewise, you might extract some code into a separate method, so that warnings can be suppressed just for its body. Or, you can use @AssumeAssertion on an assert statement; see Section 34.2.
As an example, consider suppressing a warning at an assignment that you know is safe. This example uses the Tainting Checker (Chapter 12). Assume that expr has compile-time (declared) type @Tainted
String, but you know that the run-time value of expr is untainted.
@SuppressWarnings("tainting:cast.unsafe") // expr is untainted because ... [explanation goes here]
@Untainted String myvar = expr;
Java does not permit annotations (such as @SuppressWarnings) on assignments (or on other statements or expressions), so it would have been illegal to write
@Untainted String myvar;
...
@SuppressWarnings("tainting:cast.unsafe") // expr is untainted because ...
myvar = expr;
Prefer @SuppressWarnings on a local variable declaration to one on a method, and prefer one on a method to one on a class. @SuppressWarnings on a local variable declaration applies only to the declaration (including its initializer if any), not to all uses of the variable.
You may be able to suppress a warning about a use of an expression by writing @AssumeAssertion for the expression, before the use. See Section 34.2.
Another way to reduce the scope of a @SuppressWarnings is to extract the expression into a new local variable and place a @SuppressWarnings annotation on the variable declaration. See Section 34.1.2.
@SuppressWarnings
It is best to use the most specific possible message key to suppress just a specific error that you know to be a false positive. The checker outputs this message key when it issues an error. If you use a broader @SuppressWarnings annotation, then it may mask other errors that you needed to know about.
Any of the following would have suppressed the warning in Section 34.1.2:
@SuppressWarnings("tainting") // suppresses all tainting-related warnings
@SuppressWarnings("cast") // suppresses warnings from all checkers about casts
@SuppressWarnings("unsafe") // suppresses warnings from all checkers about unsafe code
@SuppressWarnings("cast.unsafe") // suppresses warnings from all checkers about unsafe casts
@SuppressWarnings("tainting:cast") // suppresses tainting warnings about casts
@SuppressWarnings("tainting:unsafe") // suppresses tainting warnings about unsafe code
@SuppressWarnings("tainting:cast.unsafe") // suppresses tainting warnings about unsafe casts
The last one is the most specific, and therefore is the best style.
A @SuppressWarnings annotation asserts that the programmer knows that the code is actually correct or safe (that is, no undesired behavior will occur), even though the type system is unable to prove that the code is correct or safe.
Whenever you write a @SuppressWarnings annotation, you should also write, typically on the same line, a code comment explaining why the code is actually correct. In some cases you might also justify why the code cannot be rewritten in a simpler way that would be amenable to type-checking. Also
make it clear what error is being suppressed. (This is particularly important when the @SuppressWarnings is on a method declaration and the suppressed warning might be anywhere in the method body.)
This documentation will help you and others to understand the reason for the @SuppressWarnings annotation. It will also help you audit your code to verify all the warning suppressions. (The code is correct only if the checker issues no warnings and each
@SuppressWarnings is correct.)
A suppression message like “a.f is not null” is not useful. The fact that you are suppressing the warning means that you believe that a.f is not null. The message should explain why you believe that; for example, “a.f was checked above and no subsequent side effect can affect it”.
Here are some terse examples from libraries in plume-lib:
@SuppressWarnings("cast") // cast is redundant (except when checking nullness)
@SuppressWarnings("interning") // FbType.FREE is interned but is not annotated
@SuppressWarnings("interning") // equality testing optimization
@SuppressWarnings("nullness") // used portion of array is non-null
@SuppressWarnings("nullness") // oi.factory is a static method, so null first argument is OK
@SuppressWarnings("purity") // side effect to local state of type BitSet
A particularly good (and concise) justification is to reference an issue in the issue tracker, as in these two from Daikon:
@SuppressWarnings("flowexpr.parse.error") // https://tinyurl.com/cfissue/862
@SuppressWarnings("keyfor") // https://tinyurl.com/cfissue/877
When you encounter a false positive warning, please upvote the corresponding issue on the issue tracker, or create a new issue. This helps the Checker Framework maintainers to prioritize their work. If you
reference the issue URL in your warning suppression, then you can later follow the URL to check whether an issue has been fixed (though the -AwarnUnneededSuppressions command-line option also serves this purpose).
@AssumeAssertion string in an assert message
Sometimes, it is too disruptive to refactor your code to create a location where @SuppressWarnings can be written. You can instead suppress a warning by writing an assertion whose message contains the string @AssumeAssertion(checkername).
For example, in this code:
while (c != Object.class) {
...
c = c.getSuperclass();
assert c != null
: "@AssumeAssertion(nullness): c was not Object, so its superclass is not null";
}
the Nullness Checker assumes that c is non-null from the assert statement forward (including on the next iteration through the loop).
The assert expression must be an expression that would affect flow-sensitive type refinement (Section 33.7), if the expression appeared in a conditional test. Each type system has its own rules about
what type refinement it performs.
The value in parentheses is a checker name (typically lowercase), exactly as in the @SuppressWarnings annotation (Section 34.1.1). Any subcheckers will also assume that the assertion is
true (e.g., the Map Key Checker will assume that the assertion in the example above cannot fail, when it runs as a subchecker of the Nullness Checker).
The same good practices apply as for @SuppressWarnings annotations, such as writing a comment justifying why the assumption is safe (Section 34.1.3).
The -AassumeAssertionsAreEnabled and -AassumeAssertionsAreDisabled command-line options (Section 33.7.6) do not affect processing of assert
statements that have @AssumeAssertion in their message. Writing @AssumeAssertion means that the assertion would succeed if it were executed, and the Checker Framework makes use of that information regardless of the -AassumeAssertionsAreEnabled and
-AassumeAssertionsAreDisabled command-line options.
This section explains the distinction between two different uses for assertions: debugging a program (also known as defensive programming) versus specifying a program. The examples use nullness annotations, but the concepts apply to any checker.
The purpose of assertions is to aid debugging by throwing an exception when a program does not work correctly. Sometimes, programmers use assertions for a different purpose: documenting how the program works. By default, the Checker Framework assumes that each assertion is used for its primary purpose of debugging: the assertion might fail at run time, and the programmer wishes to be informed at compile time about such possible run-time errors.
Suppose that a programmer encounters a failing test, adds an assertion to aid debugging, and fixes the test. The programmer leaves the assertion in the program if the programmer is worried that the program might fail in a similar way in the future. The Checker Framework should not assume that the assertion succeeds — doing so would defeat the very purpose of the Checker Framework, which is to detect errors at compile time and prevent them from occurring at run time.
A non-standard use for assertions is to document facts that a programmer has independently verified to be true. The Checker Framework can leverage these assertions in order to avoid issuing false positive warnings. The programmer marks such assertions with the @AssumeAssertion string in the
assert message (see Section 34.2). Only do so if you are sure that the assertion always succeeds at run time.
Methods such as Objects.requireNonNull, JUnit’s Assert.assertNotNull, and Guava’s
verifyNotNull and checkNotNull are similar to assertions. Just as for assertions, their intended use is as debugging aids, they might fail at run time, and the Checker Framework warns if that might happen. Some programmers may use assert methods as documentation of facts that the
programmer has verified in some other manner. If you know that a particular codebase always uses an assertion method not for defensive programming but to indicate facts that are guaranteed to be true (that is, these assertions cannot fail at run time), then there are two approaches to avoid false positive warnings:
write specifications or suppress warnings; see below for an explanation of each approach.
The method NullnessUtil.castNonNull is not an assertion method. It is a warning suppression method.
Note that some libraries have an imprecise/incorrect specification of their assertion methods. For example, Guava’s Verify.verifyNotNull is imprecisely/incorrectly specified to have a @Nullable formal parameter. In a correct execution, null never flows there, so its type
can and should be annotated as @NonNull. That annotation allows the Nullness Checker to warn about programs that crash due to passing null to verifyNotNull. (A comment in Guava’s Preconditions.java agrees with this reasoning: “the first parameter
to checkNotNull should be annotated to require it to be non-null.” The comment goes on to say “I had hoped to take a principled stand on this” (that is, write the annotation @NonNull), but Guava annotates it as @Nullable to accommodate misuses within the
Google codebase.)
By default, the Checker Framework uses Guava’s annotations as written. If you wish to take the principled stand in order to prevent all NullPointerExceptions, you can use a different version of Guava with a small change to your build file. Where the build file refers to Maven Central’s
guava artifact, change the group name from “com.google.guava” to “org.checkerframework.annotatedlib”. (The code is identical; the only difference is annotations.)
Option 1: Write specifications based on uses of assertion methods Suppose you are annotating a codebase that already contains precondition checks, such as:
public String myGet(String key, String def) {
checkNotNull(key, "key"); // NOI18N
...
}
Because key is non-null in every correct execution, its type should be @NonNull in myGet’s signature. (@NonNull is the default, so in this case there is nothing to write.) The checker will not issue a warning about the checkNotNull call, but will
issue a warning at incorrect calls to myGet.
Option 2: Suppress warnings at uses of assertion methods
This section explains how to suppress warnings at all uses of an assertion method. As with any warning suppression, you will compromise the checker’s guarantee that your code is correct and will not fail at run time.
• If the method is defined in your source code, annotate its definition just as NullnessUtil.castNonNull is annotated; see its Javadoc or the source code
for the Checker Framework. Also, be sure to document the intention in the method’s Javadoc, so that programmers do not accidentally misuse it for defensive programming.
• If the method is defined in an external library, write a stub file that changes the method’s annotations, or use -AskipUses to make the Checker Framework ignore all calls to an entire class.
As a special case, if you want the Nullness Checker to prevent most null pointer exceptions in your code, but to permit null pointer exceptions at nullness assertion methods, you can pass -Astubs=permit-nullness-assertion-exception.astub.
-AsuppressWarnings command-line option
Supplying the -AsuppressWarnings command-line option is equivalent to writing a @SuppressWarnings annotation on every class that the compiler type-checks. The argument to -AsuppressWarnings is a comma-separated list of warning suppression strings, as in
-AsuppressWarnings=purity,uninitialized.
When possible, it is better to write a @SuppressWarnings annotation with a smaller scope, rather than using the -AsuppressWarnings command-line option.
-AskipUses and -AonlyUses command-line options
You can suppress all errors and warnings at all uses of a given class, or suppress all errors and warnings except those at uses of a given class. (The class itself is still type-checked, unless you also use the -AskipDefs, -AonlyDefs, -AskipFiles, or
-AonlyFiles command-line option; see Section 34.5). You can also use these options to affect entire packages or directories/folders.
Set the -AskipUses command-line option to a regular expression that matches fully-qualified class names (not file names) for which warnings and errors should be suppressed. Or, set the -AonlyUses command-line option to a regular expression that matches fully-qualified class names
(not file names) for which warnings and errors should be emitted; warnings about uses of all other classes will be suppressed. The regular expressions are unanchored, unless you anchor them yourself with “^” and/or “$”.
For example, suppose that you use “-AskipUses=^java\.” on the command line (with appropriate quoting) when invoking javac. Then the checkers will suppress all warnings related to classes whose fully-qualified name starts with java., such as all warnings relating to invalid arguments and all warnings relating to incorrect use of the return value.
To suppress all errors and warnings related to multiple classes, you can use the regular expression alternative operator “|”, as in “-AskipUses="java\.lang\.|java\.util\."” to suppress all warnings related to uses of classes that belong to the
java.lang or java.util packages. (Depending on your shell or other tool, you might need to change or remove the quoting.)
You can supply both -AskipUses and -AonlyUses, in which case the -AskipUses argument takes precedence, and -AonlyUses does further filtering but does not add anything that -AskipUses removed.
Warning: Use the -AonlyUses command-line option with care, because it can have unexpected results. For example, if the given regular expression does not match classes in the JDK, then the Checker Framework will suppress every warning that involves a JDK class such as Object or
String. The meaning of -AonlyUses may be refined in the future. Often, -AskipUses is more useful.
-AskipDefs and -AonlyDefs command-line options
You can suppress all errors and warnings in the definition of a given class, or suppress all errors and warnings except those in the definition of a given class. (Uses of the class are still type-checked, unless you also use the -AskipUses or -AonlyUses command-line
option; see Section 34.4.) You can also use these options to affect entire packages or directories/folders.
Set the -AskipDefs command-line option to a regular expression that matches fully-qualified class names (not file names) in whose definition warnings and errors should be suppressed. Or, set the -AonlyDefs command-line option to a regular expression that matches fully-qualified class
names (not file names) whose definitions should be type-checked. (This is somewhat similar to NullAway’s -XepOpt:NullAway:AnnotatedPackages command-line argument.) (For file names, use -AskipFiles and -AonlyFiles instead; see Section 34.6.) The regular expressions are unanchored, unless you anchor them yourself with “^” and/or “$”.
For example, if you use “-AskipDefs=^mypackage\.” on the command line (with appropriate quoting) when invoking javac, then the definitions of classes whose fully-qualified name starts with mypackage. will not be checked.
If you supply both -AskipDefs and -AonlyDefs, then -AskipDefs takes precedence.
Another way not to type-check a file is not to pass it on the compiler command-line: the Checker Framework type-checks only files that are passed to the compiler on the command line, and does not type-check any file that is not passed to the compiler. The -AskipDefs and -AonlyDefs
command-line options are intended for situations in which the build system is hard to understand or change. In such a situation, a programmer may find it easier to supply an extra command-line argument, than to change the set of files that is compiled.
A common scenario for using these options is when you are starting out by type-checking only part of a legacy codebase. After you have verified the most important parts, you can incrementally check more classes until you are type-checking the whole thing.
-AskipFiles and -AonlyFiles command-line options You can suppress all errors and warnings originating from classes that are located in a given file or directory/folder, or all those that are not in a given file or directory/folder.
Set the -AskipFiles command-line option to a regular expression that matches the path to files or directories/folders containing classes for which errors and warnings should be suppressed. Similarly, -AonlyFiles suppresses warnings everywhere except in the given files. The
regular expressions are unanchored, unless you anchor them yourself with “^” and/or “$”.
For example, if you use “-AskipFiles=/build/generated/” on the command line when invoking javac, then the definitions of classes located in any /build/generated directory will not be checked.
If you supply both -AskipFiles and -AonlyFiles, then -AskipFiles takes precedence.
A common scenario for using these options is when you want to exclude certain directories/folders (such as those containing generated or legacy code) from type-checking. Another scenario is when a file contains multiple top-level classes, only one of which has a name that corresponds to the file name.
-Alint command-line option
The -Alint option enables or disables optional checks, analogously to javac’s -Xlint option. Each of the distributed checkers supports at least the following lint options (and possibly more, see the checker’s documentation):
• cast:unsafe (default: on) Warn about unsafe casts that are not checked at run time, as in ((@NonNull String) myref). Such casts are generally not necessary because of type refinement (Section 33.7).
• cast:redundant (default: on) Warn about redundant casts that are guaranteed to succeed at run time, as in ((@NonNull String) "m"). Such casts are not necessary, because the target expression of the cast already has the given type qualifier.
• cast Enable or disable all cast-related warnings.
•
all Enable or disable all lint warnings, including checker-specific ones if any. Examples include redundantNullComparison for the Nullness Checker (see Section 2) and
dotequals for the Interning Checker (see Section 6.4). This option does not enable/disable the checker’s standard checks, just its optional ones.
• none The inverse of all: disable or enable all lint warnings, including checker-specific ones if any.
To activate a lint option, write -Alint= followed by a comma-delimited list of check names. If a check name is preceded by a hyphen (-), the warning is disabled. For example, to disable all lint options except redundant casts, you can pass -Alint=-all,cast:redundant
on the command line.
Only the last -Alint option is used; all previous -Alint options are silently ignored. In particular, this means that -Alint=all -Alint=cast:redundant is not equivalent to -Alint=-all,cast:redundant.
To prevent a checker from issuing a warning at calls to a specific method, you can change the annotations on that method by writing a stub file (see Section 36.5).
Stub files are usually used to provide correct specifications for unspecified code.
Stub files can also be used to provide incorrect specifications, for the purpose of suppressing warnings. For example, suppose that you are running the Nullness Checker to prevent null pointer exceptions. Further suppose that for some reason you do not care if method
Objects.requireNonNull crashes with a NullPointerException. You can supply a stub file containing:
package java.util;
class Objects {
@EnsuresNonNull("#1")
public static <T> @NonNull T requireNonNull(@Nullable T obj);
}
This particular stub file already exists. If you want the Nullness Checker to prevent most null pointer exceptions in your code, but to permit null pointer exceptions at nullness assertion methods, you can pass -Astubs=permit-nullness-assertion-exception.astub.
You can compile parts of your code without use of the -processor switch to javac. No checking is done during such compilations, so no warnings are issued related to pluggable type-checking.
You can direct your build system to avoid compiling certain parts of your code. For example, the -Dmaven.test.skip=true command-line argument tells Maven not to compile (or run) the tests.
Finally, some checkers have special rules. For example, the Nullness Checker (Chapter 3) uses the special castNonNull method to suppress warnings (Section 3.4.1). This manual also explains special mechanisms for suppressing warnings issued by the Fenum Checker (Section 9.4) and the Units Checker (Section 20.5).