Skip to content

NullPointerException when +unlint uses range pattern for absent lint in DefectMissing #841

@morphqdd

Description

@morphqdd

What Happens

Calling LtUnlintNonExistingDefect.defects() on an EO file where +unlint uses a range pattern (name:line-line) and the referenced lint did not fire crashes with NullPointerException:

java.lang.NullPointerException:
    Cannot invoke "java.util.List.stream()" because "lines" is null
    at org.eolang.lints.DefectMissing.apply(DefectMissing.java:47)
    at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:178)
    at java.base/java.util.stream.DistinctOps$1$2.accept(DistinctOps.java:174)
    ...
    at org.eolang.lints.LtUnlintNonExistingDefect.defects(LtUnlintNonExistingDefect.java:86)

What Should Happen

Returns a non-empty collection reporting that the +unlint annotation references a non-existing defect — consistent with how non-range +unlint nonexistent behaves (works correctly).

Steps to Reproduce

new LtUnlintNonExistingDefect(
    new ListOf<>(new LtAsciiOnly()),
    new ListOf<>()
).defects(
    new EoSyntax(
        String.join(
            "\n",
            "+unlint ascii-only:1-5",
            "[] > main",
            "  QQ.io.stdout > @",
            "    \"Hello\""
        )
    ).parsed()
);
// throws NullPointerException instead of returning defects

The EO file has +unlint ascii-only:1-5 (range pattern), but ascii-only does not fire because there are no non-ASCII characters — so "ascii-only" is absent from the defects map.

Root Cause

DefectMissing.java:45this.defects.get(name) returns null when the lint name is absent from the map. Line 47 enters the range branch without a null-check and calls lines.stream():

final List<Integer> lines = this.defects.get(name); // null if name absent
if (unlint.matches(String.format("%s:\\d+-\\d+", name))) {
    missing = !lines.stream().allMatch(new UnlintInRange(unlint)); // NPE
}

The existing tests in DefectMissingTest and LtUnlintNonExistingDefectTest only cover cases where the lint name exists in the defects map. The missing-key + range case has no coverage.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggood-titleThe title was checked and improved by ChatGPT

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions