Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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}).
Warnings in code with a relevant \<@AnnotatedFor> annotation can still be suppressed by

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.

This sentence comes out of the blue and the connection to what's in the paragraph before is unclear. What is "still" referring to?

Would it make more sense to first keep the existing text and then say the new part? Is the "still" referring to passing -AonlyAnnotatedFor? Is that the important point?

an in-scope \<@SuppressWarnings> annotation.
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}).
\end{sloppypar}

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

\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, except for warnings suppressed by another

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.

Are the "Note" and "except" part really connected? Maybe make the "except" part a separate sentence that highlights that explicit SuppressWarnings suppress warnings in AnnotatedFor code and in UnannotatedFor code (if the extra flag is not there).

mechanism such as \code{@SuppressWarnings}.
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 @@ -2773,6 +2773,12 @@ public boolean shouldSuppressWarnings(Tree tree, String errKey) {
* otherwise
*/
public boolean shouldSuppressWarnings(TreePath path, String errKey) {
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?

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 @@ -2781,49 +2787,46 @@ public boolean shouldSuppressWarnings(TreePath path, String errKey) {

if (decl instanceof VariableTree) {
Element elt = TreeUtils.elementFromDeclaration((VariableTree) decl);
if (shouldSuppressWarnings(elt, errKey)) {
if (shouldSuppressWarningsOnElement(elt, errKey)) {
return true;
}
} else if (decl instanceof MethodTree) {
Element elt = TreeUtils.elementFromDeclaration((MethodTree) decl);
if (shouldSuppressWarnings(elt, errKey)) {
if (shouldSuppressWarningsOnElement(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 (shouldSuppressWarningsOnElement(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 (shouldSuppressWarningsOnElement(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 @@ -2882,20 +2885,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 (shouldSuppressWarningsOnElement(currElt, errKey)) {
return true;
}
if (isAnnotatedForThisCheckerOrUpstreamChecker(elt)) {

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 public boolean shouldSuppressWarnings(TreePath path, String errKey) overload still has this logic in it, twice.
Should that version continue to check for this condition?

I would expect that we need to find two things:

  1. what is the nearest SuppressWarnings?
  2. what is the nearest AnnotatedFor or UnannotatedFor?

Once we found an AnnotatedFor or UnannotatedFor, we can stop looking for the other as well, but we do need to continue looking for a SuppressWarnings, and the other way around.

Can you look through the other overload and write a test case that triggers that version?

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.

The public boolean shouldSuppressWarnings(TreePath path, String errKey) overload still has this logic in it, twice.

Updated. The TreePath overload no longer returns immediately when it sees an applicable @AnnotatedFor.

I would expect that we need to find two things:

  1. what is the nearest SuppressWarnings?
  2. what is the nearest AnnotatedFor or UnannotatedFor?

The new logic does that by tracking whether an applicable @AnnotatedFor was found, while continuing to walk enclosing declarations for a matching @SuppressWarnings. If it finds a matching suppression, it returns true; if it found @AnnotatedFor but no suppression, it returns false.

Also, please go through the javadoc and manual and see where this behavior is described.

I updated the relevant manual sections to state that diagnostics in an @AnnotatedFor scope can still be suppressed by an in-scope @SuppressWarnings.

// 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 an corresponding {@code @SuppressWarnings} annotation
*/
private boolean shouldSuppressWarningsOnElement(Element elt, String 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.

There already is a shouldSuppressWarnings(Element elt, String errKey) overload. What is the relationship between these two methods and why do we need both?

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