Skip to content

Fix initialization annotation of outer receiver with explicit annotation - #1253

Open
byd110 wants to merge 41 commits into
eisop:masterfrom
byd110:issue412
Open

Fix initialization annotation of outer receiver with explicit annotation#1253
byd110 wants to merge 41 commits into
eisop:masterfrom
byd110:issue412

Conversation

@byd110

@byd110 byd110 commented Jun 10, 2025

Copy link
Copy Markdown
Collaborator

Problem description

fix #412. The explicit enclosing receiver annotation is captured by the receiver type of the constructor but not annotated in InitializationParentAnnotatedTypeFactory.CommitmentTreeAnnotator#visitMethod.

This yeild a false positive during the check if we specify the enclosing receiver to be @UnknownInitialized, but in the method body check the type is the default @Initialized.

Fix

If the method visiting has an annotated receiver type, then retrive the annotation and annotate the enclosing receiver with it.

Two test cases are added to check this edge case. checker/tests/initialization/Issue412a.java checks the fix is correct. framework/tests/h1h2checker/Issue412b.java make sure the general framework can also catch this problem.

Comment thread checker/tests/nullness-enclosingexpr/NullnessEnclosingExprTest.java Outdated

@wmdietl wmdietl left a comment

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.

Great, thanks for tracking this down and fixing this bug!
Please add an entry in the changelog, at least that the issue is fixed.

exeType.getReturnType().replaceAnnotation(a);

// If the receiver type exists (meaning there is an enclosing type) and is
// annotated, then annotate the enclosing type with the same annotation.

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 "enclosing type" reference is unclear. It looks like it is being added to the enclosing type of the return type of the method.


// If the receiver type exists (meaning there is an enclosing type) and is
// annotated, then annotate the enclosing type with the same annotation.
// TODO: look into why there is this inconsistency between receiver type and

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.

Is this a separate issue? Is this PR not fixing all of #412?

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.

I would classify this as a saparate issue. getEnclosingType returns the enclsoing type with the correct annotation, whereas the enclosing type in the receiver is not annotated.

This PR fully fixes the problem. The inconsistency only affacts the way to get the correct annotation.

I haven't looked into the root cause of this consistency. There is also no failing test cases to show this inconsistency would cause any problem.

exeType.getReceiverType().getAnnotationInHierarchy(a);
AnnotatedDeclaredType ret = (AnnotatedDeclaredType) exeType.getReturnType();
if (ret.getEnclosingType() != null) {
ret.getEnclosingType().addAnnotation(enclAnno);

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 return type of a constructor can never be explicitly written in source code, right?
So there never can already be a manually written annotation on the enclosing type.
So using addAnnotation instead of the replaceAnnotation that is used above is always correct?
Can you write a short comment that hints at that?

Comment on lines +760 to +761
if (exeType.getReceiverType() != null) {
if (exeType.getReceiverType().hasAnnotationInHierarchy(a)) {

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.

Suggested change
if (exeType.getReceiverType() != null) {
if (exeType.getReceiverType().hasAnnotationInHierarchy(a)) {
if (exeType.getReceiverType() != null && exeType.getReceiverType().hasAnnotationInHierarchy(a)) {

To reduce nesting levels.

super(testFiles, NullnessChecker.class, "nullness", "-AcheckEnclosingExpr");
}

/**

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.

To write down what we discussed in person: This change wouldn't be strictly necessary.
However, our javadoc enforcement script sees that there is a change to a file NullnessEnclosingExprTest.java and doesn't take the full paths into account - there is another file with that base name in this PR.
This seems unlikely to occur frequently, so we're punting on the issue.

}

Issue412a() {
new InnerWithUnknownInitializationEnclosingExpression();

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.

Can you add another class with a constructor that has no @UnknownInitialization Issue412a Issue412a.this receiver and call it here, to ensure we do get an error for that illegal invocation. (Or rename InnerWithUnknownInitializationEnclosingExpression and have two constructors in it.)

}

class Inner {
/* The framework can correctly identify the outer class reciever type to be @H1Top.

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.

Suggested change
/* The framework can correctly identify the outer class reciever type to be @H1Top.
/* The framework can correctly identify the outer class receiver type to be @H1Top.

@@ -0,0 +1,21 @@
import org.checkerframework.framework.testchecker.h1h2checker.quals.*;

// @skip-test

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.

Should this be unskipped? If it should be skipped, is there a new issue you can reference?

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.

Yes, it should be unskipped. The error reported rules out the problem with general framework.

@wmdietl

wmdietl commented Jun 23, 2025

Copy link
Copy Markdown
Member

@byd110 Issue #412 mentions a possible relation to #226. Did you look into that?

@byd110

byd110 commented Jun 24, 2025

Copy link
Copy Markdown
Collaborator Author

@byd110 Issue #412 mentions a possible relation to #226. Did you look into that?

I looked into it. It's another problem regarding method overide.

@byd110
byd110 requested a review from wmdietl September 26, 2025 05:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fields of @UnknownInitialization receiver parameter should be Nullable

3 participants