-
Notifications
You must be signed in to change notification settings - Fork 431
Nullness Checker: strengthen containment checks for invariant type parameters with nullness qualifiers #7430
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughAdds a new test file Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
checker/src/main/java/org/checkerframework/checker/nullness/NullnessVisitor.java
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-10-22T20:40:48.819Z
Learnt from: kelloggm
Repo: typetools/checker-framework PR: 7166
File: checker/src/main/java/org/checkerframework/checker/mustcall/MustCallVisitor.java:470-515
Timestamp: 2025-10-22T20:40:48.819Z
Learning: In MustCallVisitor.java (Checker Framework), prefer using Name.equals(...) or Objects.equals(...) for com.sun.source.util.Name comparisons (instead of ==/!=). This should be the default unless the Interning Checker is explicitly used to guarantee reference equality.
Applied to files:
checker/src/main/java/org/checkerframework/checker/nullness/NullnessVisitor.java
📚 Learning: 2025-10-22T20:43:32.957Z
Learnt from: kelloggm
Repo: typetools/checker-framework PR: 7166
File: checker/src/main/java/org/checkerframework/checker/resourceleak/MustCallConsistencyAnalyzer.java:767-798
Timestamp: 2025-10-22T20:43:32.957Z
Learning: In MustCallConsistencyAnalyzer.addObligationsForOwningCollectionReturn, treat a null result from CollectionOwnershipAnnotatedTypeFactory.getMustCallValuesOfResourceCollectionComponent(...) as “no obligations” and skip creating CollectionObligation(s); do not throw. This can occur for OwningCollection over non-resource element types.
Applied to files:
checker/src/main/java/org/checkerframework/checker/nullness/NullnessVisitor.java
📚 Learning: 2025-10-22T20:43:32.957Z
Learnt from: kelloggm
Repo: typetools/checker-framework PR: 7166
File: checker/src/main/java/org/checkerframework/checker/resourceleak/MustCallConsistencyAnalyzer.java:767-798
Timestamp: 2025-10-22T20:43:32.957Z
Learning: When handling collection ownership in MustCallConsistencyAnalyzer, do not throw if CollectionOwnershipAnnotatedTypeFactory.getMustCallValuesOfResourceCollectionComponent(..) returns null; treat it as “no obligations” and skip creating CollectionObligation(s). Also, when constructing diagnostics that reference an element method name, fall back to "Unknown" if the list is null/empty.
Applied to files:
checker/src/main/java/org/checkerframework/checker/nullness/NullnessVisitor.java
🔇 Additional comments (3)
checker/src/main/java/org/checkerframework/checker/nullness/NullnessVisitor.java (3)
43-43: LGTM!The new imports are necessary for the added type argument checking logic.
Also applies to: 67-67
256-262: LGTM - Error reporting follows framework conventions.The error reporting correctly uses
ArraysPlume.concatenateto append the value and variable type strings as additional arguments. This aligns with the standard "assignment" error message format in the Checker Framework which expects (found type, expected type).
243-264: Verify that nested type argument nullability violations are caught by the base implementation.This check only examines immediate (top-level) type arguments. For deeply nested generics like
Map<List<@Nullable String>, String>assigned toMap<List<String>, String>, the nested nullability violation insideListwon't be detected by this loop.While
super.commonAssignmentCheckat line 275 delegates to the framework's recursive type argument checking viaDefaultTypeHierarchy.isContainedBy, that mechanism focuses on variance and structural subtyping. The nullness-specific check here (detecting@Nullableassigned to@NonNullin invariant positions) may not be redundantly performed for nested types. Consider adding a test case for this scenario to ensure nested nullability violations are properly reported.
checker/src/main/java/org/checkerframework/checker/nullness/NullnessVisitor.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
checker/src/main/java/org/checkerframework/checker/nullness/NullnessVisitor.java (1)
257-260: Simplify redundant nullness annotation check.The condition
!aa.hasEffectiveAnnotation(NONNULL) && aa.hasEffectiveAnnotation(NULLABLE)is redundant sinceNONNULLandNULLABLEare mutually exclusive in the qualifier hierarchy.🔎 Suggested simplification
if (va.getKind() != TypeKind.WILDCARD - && !aa.hasEffectiveAnnotation(NONNULL) && aa.hasEffectiveAnnotation(NULLABLE) && va.hasEffectiveAnnotation(NONNULL)) {
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
checker/src/main/java/org/checkerframework/checker/nullness/NullnessVisitor.java
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-10-22T20:43:32.957Z
Learnt from: kelloggm
Repo: typetools/checker-framework PR: 7166
File: checker/src/main/java/org/checkerframework/checker/resourceleak/MustCallConsistencyAnalyzer.java:767-798
Timestamp: 2025-10-22T20:43:32.957Z
Learning: In MustCallConsistencyAnalyzer.addObligationsForOwningCollectionReturn, treat a null result from CollectionOwnershipAnnotatedTypeFactory.getMustCallValuesOfResourceCollectionComponent(...) as “no obligations” and skip creating CollectionObligation(s); do not throw. This can occur for OwningCollection over non-resource element types.
Applied to files:
checker/src/main/java/org/checkerframework/checker/nullness/NullnessVisitor.java
📚 Learning: 2025-10-22T20:43:32.957Z
Learnt from: kelloggm
Repo: typetools/checker-framework PR: 7166
File: checker/src/main/java/org/checkerframework/checker/resourceleak/MustCallConsistencyAnalyzer.java:767-798
Timestamp: 2025-10-22T20:43:32.957Z
Learning: When handling collection ownership in MustCallConsistencyAnalyzer, do not throw if CollectionOwnershipAnnotatedTypeFactory.getMustCallValuesOfResourceCollectionComponent(..) returns null; treat it as “no obligations” and skip creating CollectionObligation(s). Also, when constructing diagnostics that reference an element method name, fall back to "Unknown" if the list is null/empty.
Applied to files:
checker/src/main/java/org/checkerframework/checker/nullness/NullnessVisitor.java
📚 Learning: 2025-10-22T20:40:48.819Z
Learnt from: kelloggm
Repo: typetools/checker-framework PR: 7166
File: checker/src/main/java/org/checkerframework/checker/mustcall/MustCallVisitor.java:470-515
Timestamp: 2025-10-22T20:40:48.819Z
Learning: In MustCallVisitor.java (Checker Framework), prefer using Name.equals(...) or Objects.equals(...) for com.sun.source.util.Name comparisons (instead of ==/!=). This should be the default unless the Interning Checker is explicitly used to guarantee reference equality.
Applied to files:
checker/src/main/java/org/checkerframework/checker/nullness/NullnessVisitor.java
🔇 Additional comments (2)
checker/src/main/java/org/checkerframework/checker/nullness/NullnessVisitor.java (2)
43-43: LGTM!The new imports for
TypeKindandArraysPlumeare necessary for the added type argument checking logic and are correctly placed in alphabetical order.Also applies to: 67-67
232-271: Sound implementation for invariant type argument checking.The logic correctly identifies and rejects unsound assignments where a nullable type argument is assigned to an invariant (non-wildcard) position requiring non-null. The approach of:
- Constraining the check to same erased types (avoiding false positives with subtype relationships)
- Excluding raw types
- Skipping wildcards (which represent variance)
appropriately targets the issue described in #6890.
| // Only apply this additional nullness invariant-type-argument check when the | ||
| // erased declared types are identical and neither side is a raw type. Using a | ||
| // looser subtype check caused false positives for assignments where the | ||
| // declared types differ (but are related by subtyping) or for raw types. | ||
| if (types.isSameType(types.erasure(varUnderlying), types.erasure(valUnderlying)) | ||
| && !varD.isUnderlyingTypeRaw() | ||
| && !valD.isUnderlyingTypeRaw()) { | ||
| List<? extends AnnotatedTypeMirror> varArgs = varD.getTypeArguments(); | ||
| List<? extends AnnotatedTypeMirror> valArgs = valD.getTypeArguments(); | ||
| int n = Math.min(varArgs.size(), valArgs.size()); | ||
| for (int i = 0; i < n; i++) { | ||
| AnnotatedTypeMirror va = varArgs.get(i); | ||
| AnnotatedTypeMirror aa = valArgs.get(i); | ||
| // If the variable's type argument is not a wildcard (i.e., invariant position) | ||
| // and the value's corresponding argument may be nullable while the variable's | ||
| // is not, report an assignment error. | ||
| if (va.getKind() != TypeKind.WILDCARD | ||
| && !aa.hasEffectiveAnnotation(NONNULL) | ||
| && aa.hasEffectiveAnnotation(NULLABLE) | ||
| && va.hasEffectiveAnnotation(NONNULL)) { | ||
| String valueTypeString = valueType.toString(); | ||
| String varTypeString = varType.toString(); | ||
| checker.reportError( | ||
| valueTree, | ||
| errorKey, | ||
| ArraysPlume.concatenate(extraArgs, valueTypeString, varTypeString)); | ||
| return false; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Fix inconsistent indentation in the inner block.
The inner if block starting at line 245 and its associated comment (lines 241-244) have inconsistent indentation relative to the enclosing block. This appears to be 2 spaces under-indented.
🔎 Suggested fix
- // Only apply this additional nullness invariant-type-argument check when the
- // erased declared types are identical and neither side is a raw type. Using a
- // looser subtype check caused false positives for assignments where the
- // declared types differ (but are related by subtyping) or for raw types.
- if (types.isSameType(types.erasure(varUnderlying), types.erasure(valUnderlying))
- && !varD.isUnderlyingTypeRaw()
- && !valD.isUnderlyingTypeRaw()) {
+ // Only apply this additional nullness invariant-type-argument check when the
+ // erased declared types are identical and neither side is a raw type. Using a
+ // looser subtype check caused false positives for assignments where the
+ // declared types differ (but are related by subtyping) or for raw types.
+ if (types.isSameType(types.erasure(varUnderlying), types.erasure(valUnderlying))
+ && !varD.isUnderlyingTypeRaw()
+ && !valD.isUnderlyingTypeRaw()) {Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
checker/src/main/java/org/checkerframework/checker/nullness/NullnessVisitor.java
around lines 241 to 270 the inner comment and the if-block starting at line 245
are under-indented by two spaces relative to the enclosing block; fix by
shifting the comment, the if statement, its body, and the corresponding closing
braces two spaces to the right so they line up consistently with the surrounding
block indentation.
Fixes #6890
Map<String, ?> y = x.getF()wheregetF()returnsMap<@Nullable String, String>.keySet().iterator().next()), but the source map permits null keys, enabling runtimeNullPointerExceptions.checker/tests/nullness/WildcardNullableKey.javato verify the unsafe assignment is rejected with an [assignment] error.