[
https://issues.apache.org/jira/browse/GROOVY-12250?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Paul King resolved GROOVY-12250.
--------------------------------
Fix Version/s: 6.0.0-beta-2
Resolution: Fixed
> NullChecker: narrow arguments of null-validator and assertion methods
> (requireNonNull, assertNotNull, assertThat(x).isNotNull())
> --------------------------------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12250
> URL: https://issues.apache.org/jira/browse/GROOVY-12250
> Project: Groovy
> Issue Type: Improvement
> Components: groovy-typecheckers
> Reporter: Paul King
> Assignee: Paul King
> Priority: Major
> Fix For: 6.0.0-beta-2
>
>
> The {{NullChecker}} type-checking extension recognises a range of null-guard
> patterns ({{if (x != null)}}, Groovy truth, {{instanceof}},
> {{Objects.nonNull}}, {{assert}}, etc. — GROOVY-12208) but does not recognise
> calls which _guarantee_ an argument is non-null when they complete normally.
> In particular, {{Objects.requireNonNull\(x)}} — the most idiomatic Java null
> validator and the natural "cast to non-null" escape hatch — neither narrows
> {{x}} afterwards nor counts as a guard, and test assertions like
> {{assertNotNull\(x)}} leave test code full of false positives.
> This issue adds narrowing for:
> * *Validator methods* which throw on null, so their first argument is
> non-null afterwards: {{Objects.requireNonNull\(x)}} (1- and 2-arg forms,
> including statically imported) and Guava-style {{checkNotNull\(x)}}
> * *Test assertions*: {{assertNotNull(...)}} narrows its _actual_ argument(s),
> with message parameters recognised in any position — JUnit 4's {{(message,
> actual)}} and JUnit 5 / TestNG's {{(actual, message)}} both work, driven by
> parameter types rather than position
> * *Fluent assertion chains*: {{assertThat\(x).isNotNull()}} narrows {{x}},
> looking through intermediate chained calls such as {{describedAs(...)}}
> (AssertJ, Truth, or similar)
> Consistent with the checker's existing design (annotations are matched by
> simple name from any package), validator and assertion methods are matched by
> simple name, so any library following the common naming conventions is
> recognised without configuration.
> Example:
> {code:groovy}
> import groovy.transform.TypeChecked
> @TypeChecked(extensions = 'groovy.typecheckers.NullChecker')
> int labelWidth(@Nullable String label) {
> Objects.requireNonNull(label, 'label must be supplied')
> label.length() // ok: requireNonNull throws
> on null
> }
> {code}
> Previously this reported: {{[Static type checking] - Potential null
> dereference: 'label' is @Nullable}}
> The narrowing also applies in flow-sensitive mode ({{NullChecker(strict:
> true)}}), clearing the nullable state of tracked unannotated variables, and
> composes with the existing checks — e.g. {{foo(Objects.requireNonNull\(x))}}
> no longer reports passing a {{@Nullable}} value to a {{@NonNull}} parameter
> (correct, since evaluation order guarantees the throw happens first).
> Methods with similar names that are not null validators still behave as
> before (an unrecognised {{verifyNotNull\(x)}} does not narrow), and
> {{assertThat\(x)}} without a following {{isNotNull()}} does not narrow.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)