Skip to content

Checker framework report violation on theoretcally possible cases but practiacally impossible #18482

@romani

Description

@romani

it was around for long and made very clear at
#18451 (comment)

if we consider Class methods wihtout context of execution, Checker is absolutely right that

    private static Set<String> getRecordComponentNames(DetailAST recordDef) {
        final Set<String> names = new HashSet<>();
        final DetailAST recordComponents = recordDef.findFirstToken(TokenTypes.RECORD_COMPONENTS);
        DetailAST child = recordComponents.getFirstChild();
        while (child != null) {
            if (child.getType() == TokenTypes.RECORD_COMPONENT_DEF) {
                names.add(child.findFirstToken(TokenTypes.IDENT).getText());
            }
            child = child.getNextSibling();
        }
        return names;
    }

has null potential probelm:

  <checkerFrameworkError unstable="false">
    <fileName>src/main/java/com/puppycrawl/tools/checkstyle/checks/annotation/MissingOverrideOnRecordAccessorCheck.java</fileName>
    <specifier>dereference.of.nullable</specifier>
    <message>dereference of possibly-null reference recordComponents</message>
    <lineContent>DetailAST child = recordComponents.getFirstChild();</lineContent>
  </checkerFrameworkError>

but due to limitations of Checkstyle, that we execute only on compiled sources only, this becomes not possible.
So we can not satisfy Checker, as it will produce dead code that will conflict with jacoco/pitest that we vale more that Checker.

Checker propose to disable such violations in code https://checkerframework.org/manual/#suppressing-warnings-nullness:

The Checker Framework supplies several ways to suppress warnings, most notably the @SuppressWarnings("nullness") annotation (see Chapter ‍33). An example use is

    // might return null
    @Nullable Object getObject(...) { ... }
    void myMethod() {
      @SuppressWarnings("nullness") // with argument x, getObject always returns a non-null value
      @NonNull Object o2 = getObject(x);
    }

This approach is very verbose and can pollute code, so we unlikely to use it, especially in middle of method body.

or special util method
https://checkerframework.org/manual/#suppressing-warnings-with-assertions
this might be interesting compromise to split cases where null is not possible(due to context that AST is of compiled sources) and cases where search for token can actaully return nothing/null.

    private static Set<String> getRecordComponentNames(DetailAST recordDef) {
        final Set<String> names = new HashSet<>();
        final DetailAST recordComponents = CheckstyleAssertUtil.notNull(recordDef.findFirstToken(TokenTypes.RECORD_COMPONENTS));
        DetailAST child = recordComponents.getFirstChild();
        while (child != null) {
            if (child.getType() == TokenTypes.RECORD_COMPONENT_DEF) {
                names.add(child.findFirstToken(TokenTypes.IDENT).getText());
            }
            child = child.getNextSibling();
        }
        return names;
    }

I do not like to have compilation dependency for Checker framework for now, so we can copy https://github.com/typetools/checker-framework/blob/master/checker-util/src/main/java/org/checkerframework/checker/nullness/util/NullnessUtil.java of some methods only to our project.

this might be good strategic point in scope of https://github.com/checkstyle/checkstyle/wiki/Checkstyle-GSoC-2025-Project-Ideas#project-name-extend-checker-framework-integration

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions