Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,14 @@ void bar() {
annotatedForNullness(initializedField);
annotatedForNullnessAndInitialization(initializedField);
}

// @SuppressWarnings("nullness") should suppress all nullness diagnostics within the annotated
// declaration's scope, including those in nested @AnnotatedFor("nullness") scopes.
@SuppressWarnings("nullness")
class SuppressWarningsClassWithAnnotatedForMethod {
@AnnotatedFor("nullness")
@NonNull Object m() {
return null;
}
}
}
8 changes: 6 additions & 2 deletions docs/manual/annotating-libraries.tex
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,12 @@
conservative defaults
(see Section~\ref{defaults-classfile}) for any type use with no explicit
user-written annotation, \emph{and} the checker issues no warnings.
The command-line argument \code{-AonlyAnnotatedFor} can be used to suppress errors and warnings outside of the scope of an \<@AnnotatedFor> annotation,
but does not change the default qualifiers for source code (See Section~\ref{aonlyannotatedfor}).
The command-line argument \code{-AonlyAnnotatedFor} can be used to suppress
errors and warnings outside of the scope of an \<@AnnotatedFor> annotation,
but does not change the default qualifiers for source code (see
Section~\ref{aonlyannotatedfor}).
In code with a relevant \<@AnnotatedFor> annotation, warnings can be suppressed
by an explicit \<@SuppressWarnings> annotation.
\end{sloppypar}

The \refqualclass{framework/qual}{AnnotatedFor} annotation, written on a
Expand Down
9 changes: 7 additions & 2 deletions docs/manual/warnings.tex
Original file line number Diff line number Diff line change
Expand Up @@ -694,8 +694,13 @@

\sectionAndLabel{\code{-AonlyAnnotatedFor} command-line option}{aonlyannotatedfor}

You can suppress all errors and warnings for code outside of a corresponding \code{@AnnotatedFor} by applying this command-line option.
Note that the \code{@AnnotatedFor} annotation must include the checker's name to enable warnings from that checker.
You can suppress all errors and warnings for code outside of a corresponding
\code{@AnnotatedFor} by applying this command-line option.
Note that the \code{@AnnotatedFor} annotation must include the checker's name to
enable warnings from that checker.
An explicit \code{@SuppressWarnings} annotation can suppress warnings in
\code{@AnnotatedFor} code and code outside the scope of a corresponding
\code{@AnnotatedFor} annotation.
For example, use \code{@AnnotatedFor("nullness")} for the Nullness Checker.
This flag only suppresses warnings, compared to \code{-AuseConservativeDefaultsForUncheckedCode=source},
which also applies conservative defaults for code outside the scope of an \code{@AnnotatedFor} annotation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2738,21 +2738,6 @@ private boolean shouldSuppressWarnings(Object src, String errKey) {
* otherwise
*/
public boolean shouldSuppressWarnings(Tree tree, String errKey) {
Collection<String> prefixes = getSuppressWarningsPrefixes();
if (prefixes.isEmpty()
|| (prefixes.contains(SUPPRESS_ALL_PREFIX) && prefixes.size() == 1)) {
throw new BugInCF(
"Checker must provide a SuppressWarnings prefix."
+ " SourceChecker#getSuppressWarningsPrefixes was not overridden"
+ " correctly.");
}

if (shouldSuppress(getSuppressWarningsStringsFromOption(), errKey)) {
// If the error key matches a warning string in the -AsuppressWarnings, then suppress
// the warning.
return true;
}

assert this.currentRoot != null : "this.currentRoot == null";
TreePath path = pathToTree(tree);

Expand Down Expand Up @@ -2783,13 +2768,33 @@ public boolean shouldSuppressWarnings(Tree tree, String errKey) {
* Returns true if the path is within the scope of a @SuppressWarnings annotation, one of whose
* values suppresses the checker's warning.
*
* <p>This overload also accounts for source-position-based suppression from unchecked code: if
* no matching {@code @SuppressWarnings} is found, then warnings outside a relevant {@link
* AnnotatedFor} scope are suppressed when {@code
* -AuseConservativeDefaultsForUncheckedCode=source} or {@code -AonlyAnnotatedFor} is in effect.
*
* @param path the TreePath that might be a source of, or related to, a warning
* @param errKey the error key the checker is emitting
* @return true if no warning should be emitted for the given path because it is contained by a
* declaration with an appropriately-valued {@code @SuppressWarnings} annotation; false
* otherwise
*/
public boolean shouldSuppressWarnings(TreePath path, String errKey) {
Collection<String> prefixes = getSuppressWarningsPrefixes();
if (prefixes.isEmpty()
|| (prefixes.contains(SUPPRESS_ALL_PREFIX) && prefixes.size() == 1)) {
throw new BugInCF(
"Checker must provide a SuppressWarnings prefix."
+ " SourceChecker#getSuppressWarningsPrefixes was not overridden"
+ " correctly.");
}

if (shouldSuppress(getSuppressWarningsStringsFromOption(), errKey)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The shouldSuppressWarnings(Tree tree, String errKey) performs this check (with a helpful comment) and another check, before calling this method.
Adding this check here now performs this check twice.

Should both checks from the Tree version be moved to the TreePath version? Are there other places that call this method that should not have both checks?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I moved them to the treepath version. I checked the call sites and did not see any that had the checks.

return true;
}

boolean foundAnnotatedFor = false;

// iterate through the path; continue until path contains no declarations
for (TreePath declPath = TreePathUtil.enclosingDeclarationPath(path);
declPath != null;
Expand All @@ -2798,49 +2803,46 @@ public boolean shouldSuppressWarnings(TreePath path, String errKey) {

if (decl instanceof VariableTree) {
Element elt = TreeUtils.elementFromDeclaration((VariableTree) decl);
if (shouldSuppressWarnings(elt, errKey)) {
if (hasSuppressWarningsAnnotationForErrorKey(elt, errKey)) {
return true;
}
} else if (decl instanceof MethodTree) {
Element elt = TreeUtils.elementFromDeclaration((MethodTree) decl);
if (shouldSuppressWarnings(elt, errKey)) {
if (hasSuppressWarningsAnnotationForErrorKey(elt, errKey)) {
return true;
}

if (isAnnotatedForThisCheckerOrUpstreamChecker(elt)) {
// Return false immediately. Do NOT check for AnnotatedFor in the enclosing
// elements as the closest AnnotatedFor is already found.
return false;
if (!foundAnnotatedFor && isAnnotatedForThisCheckerOrUpstreamChecker(elt)) {
foundAnnotatedFor = true;
}
} else if (TreeUtils.classTreeKinds().contains(decl.getKind())) {
// A class tree
Element elt = TreeUtils.elementFromDeclaration((ClassTree) decl);
if (shouldSuppressWarnings(elt, errKey)) {
if (hasSuppressWarningsAnnotationForErrorKey(elt, errKey)) {
return true;
}

if (isAnnotatedForThisCheckerOrUpstreamChecker(elt)) {
// Return false immediately. Do NOT check for AnnotatedFor in the enclosing
// elements as the closest AnnotatedFor is already found.
return false;
if (!foundAnnotatedFor && isAnnotatedForThisCheckerOrUpstreamChecker(elt)) {
foundAnnotatedFor = true;
}
Element packageElement = elt.getEnclosingElement();
if (packageElement != null && packageElement.getKind() == ElementKind.PACKAGE) {
if (shouldSuppressWarnings(packageElement, errKey)) {
if (hasSuppressWarningsAnnotationForErrorKey(packageElement, errKey)) {
return true;
}
if (isAnnotatedForThisCheckerOrUpstreamChecker(packageElement)) {
// Return false immediately. Do NOT check for AnnotatedFor in the enclosing
// elements as the closest AnnotatedFor is already found.
return false;
if (!foundAnnotatedFor
&& isAnnotatedForThisCheckerOrUpstreamChecker(packageElement)) {
foundAnnotatedFor = true;
}
}
} else {
throw new BugInCF("Unexpected declaration kind: " + decl.getKind() + " " + decl);
}
}

if (useConservativeDefaultsSource || onlyAnnotatedFor) {
if (foundAnnotatedFor) {
return false;
} else if (useConservativeDefaultsSource || onlyAnnotatedFor) {
// If we got this far without hitting an @AnnotatedFor and returning
// false, we DO suppress the warning.
return true;
Expand Down Expand Up @@ -2899,20 +2901,30 @@ public boolean shouldSuppressWarnings(Element elt, String errKey) {
}

for (Element currElt = elt; currElt != null; currElt = currElt.getEnclosingElement()) {
SuppressWarnings suppressWarningsAnno = currElt.getAnnotation(SuppressWarnings.class);
if (suppressWarningsAnno != null) {
String[] suppressWarningsStrings = suppressWarningsAnno.value();
if (shouldSuppress(suppressWarningsStrings, errKey)) {
if (warnUnneededSuppressions) {
elementsWithSuppressedWarnings.add(currElt);
}
return true;
}
if (hasSuppressWarningsAnnotationForErrorKey(currElt, errKey)) {
return true;
}
if (isAnnotatedForThisCheckerOrUpstreamChecker(elt)) {
Comment thread
aosen-xiong marked this conversation as resolved.
// Return false immediately. Do NOT check for AnnotatedFor in the
// enclosing elements, because they may not have an @AnnotatedFor.
return false;
}
return false;
}

/**
* Returns true if the given element has a {@code @SuppressWarnings} annotation that suppresses
* the given error key.
*
* @param elt the element whose annotations to check
* @param errKey the error key the checker is emitting
* @return true if {@code elt} has a corresponding {@code @SuppressWarnings} annotation
*/
private boolean hasSuppressWarningsAnnotationForErrorKey(Element elt, String errKey) {
SuppressWarnings suppressWarningsAnno = elt.getAnnotation(SuppressWarnings.class);
if (suppressWarningsAnno != null) {
String[] suppressWarningsStrings = suppressWarningsAnno.value();
if (shouldSuppress(suppressWarningsStrings, errKey)) {
if (warnUnneededSuppressions) {
elementsWithSuppressedWarnings.add(elt);
}
return true;
}
}
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,4 +247,14 @@ static class staticAnnotatedAndWarningsSuppressedClass {
so4 = staticUnannotatedMethod(so1);
}
}

// @SuppressWarnings("subtyping") should suppress all subtyping diagnostics within the annotated
// declaration's scope, including those in nested @AnnotatedFor("subtyping") scopes.
@SuppressWarnings("subtyping")
class SuppressWarningsClassWithAnnotatedForMethod {
@AnnotatedFor("subtyping")
@SubQual Object m(@SuperQual Object p) {
return p;
}
}
}
Loading