Skip to content

Issue error if top is used in a contract annotation#6429

Open
mernst wants to merge 11 commits intotypetools:masterfrom
mernst:warn-contract-refinement-to-top-2
Open

Issue error if top is used in a contract annotation#6429
mernst wants to merge 11 commits intotypetools:masterfrom
mernst:warn-contract-refinement-to-top-2

Conversation

@mernst
Copy link
Copy Markdown
Member

@mernst mernst commented Jan 30, 2024

Merge atter #6432.

Copy link
Copy Markdown
Member

@smillst smillst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't think these changes are right. I propose that I take over this pull request and make the changes I want.

}
}

// Check for type-system specific annotations. These are the annotations that are
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.

It's strange that this code is reading the meta-annotations every time it encounters a declaration annotation rather than just when the checker is initialized. I want to rewrite this.

String methodString = " on method " + executableElement.getSimpleName();
factory
.getChecker()
.reportWarning(
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.

Warnings should only be issued in the BaseTypeVisitor. Specifically, this code belongs in BaseTypeVisitor#checkContractsAtMethodDeclaration.

@smillst smillst assigned mernst and unassigned smillst Jan 30, 2024
@mernst
Copy link
Copy Markdown
Member Author

mernst commented Jan 30, 2024

OK, you can take over.

@mernst mernst assigned smillst and unassigned mernst Jan 30, 2024
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 3, 2026

📝 Walkthrough

Walkthrough

This pull request adds detection and diagnostics for contract annotations that use top-level qualifiers. ContractsFromMethod.java now issues a warning when a contract annotation is a top qualifier and throws a TypeSystemError when an enforced qualifier resolves to a top qualifier. Javadoc for PreconditionAnnotation, PostconditionAnnotation, and ConditionalPostconditionAnnotation was expanded to explain why top qualifiers are ineffective. A new message key contracts.toptype was added, test files were updated with comments and new test helper methods, and a trailing blank line was removed from a JAIF test resource.

Possibly related PRs

Suggested reviewers

  • smillst
🚥 Pre-merge checks | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@framework/src/main/java/org/checkerframework/common/basetype/messages.properties`:
- Line 93: Update the user-facing message string for the diagnostics key
contracts.toptype by fixing the typo "qualifer" to "qualifier" so the message
reads "the top qualifier %s in contract annotation %s has no effect"; modify the
value for contracts.toptype in messages.properties (the literal string on that
key) to reflect the corrected spelling.

In
`@framework/src/main/java/org/checkerframework/framework/util/ContractsFromMethod.java`:
- Around line 147-166: The loop over per-expression contracts in
ContractsFromMethod causes duplicate "contracts.toptype" warnings for the same
contract annotation; fix by deduplicating before reporting: create a Set to
track seen annotations (e.g., seenAnnos) and for each T contract check if its
contract.annotation is already represented in seenAnnos (use
AnnotationUtils.containsSame to compare AnnotationMirror equality) and skip
reporting if present, otherwise add it to seenAnnos and call
factory.getChecker().reportWarning(...) as currently done; keep checks using
tops, executableElement, and the existing contract.contractAnnotation +
methodString message and the "contracts.toptype" key.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 60f5c7f and ab6f0c0.

📒 Files selected for processing (10)
  • annotation-file-utilities/src/test/resources/annotations/tests/classfile/all-annotations.jaif
  • checker-qual/src/main/java/org/checkerframework/framework/qual/ConditionalPostconditionAnnotation.java
  • checker-qual/src/main/java/org/checkerframework/framework/qual/PostconditionAnnotation.java
  • checker-qual/src/main/java/org/checkerframework/framework/qual/PreconditionAnnotation.java
  • checker/tests/nullness/PreconditionFieldNotInStore.java
  • framework/src/main/java/org/checkerframework/common/basetype/messages.properties
  • framework/src/main/java/org/checkerframework/framework/util/ContractsFromMethod.java
  • framework/tests/flow/ContractsOverridingSubtyping.java
  • framework/tests/flow/Postcondition.java
  • framework/tests/flow/Precondition.java
💤 Files with no reviewable changes (1)
  • annotation-file-utilities/src/test/resources/annotations/tests/classfile/all-annotations.jaif

flowexpr.parameter.not.final=parameter %s in '%s' is not effectively final (i.e., it gets re-assigned)
contracts.precondition=precondition of %s is not satisfied.%nfound : %s%nrequired: %s
contracts.postcondition=postcondition of %s is not satisfied.%nfound : %s%nrequired: %s
contracts.toptype=the top qualifer %s in contract annotation %s has no effect
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix typo in user-facing diagnostic message.

Line 93 has "qualifer" instead of "qualifier".

📝 Proposed fix
-contracts.toptype=the top qualifer %s in contract annotation %s has no effect
+contracts.toptype=the top qualifier %s in contract annotation %s has no effect
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
contracts.toptype=the top qualifer %s in contract annotation %s has no effect
contracts.toptype=the top qualifier %s in contract annotation %s has no effect
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@framework/src/main/java/org/checkerframework/common/basetype/messages.properties`
at line 93, Update the user-facing message string for the diagnostics key
contracts.toptype by fixing the typo "qualifer" to "qualifier" so the message
reads "the top qualifier %s in contract annotation %s has no effect"; modify the
value for contracts.toptype in messages.properties (the literal string on that
key) to reflect the corrected spelling.

Comment on lines +147 to +166
// At this point, `result` contains only framework-defined contract annotations.
Set<AnnotationMirror> tops = factory.getQualifierHierarchy().getTopAnnotations();
for (T contract : result) {
AnnotationMirror anno = contract.annotation;
if (AnnotationUtils.containsSame(tops, anno)) {
// TODO: issue a warning on the annotation itself rather than on the method declaration.
// This requires iterating over the annotation trees on the method declaration to determine
// which one led tho the given AnnotationMirror.
if (executableElement != null) {
String methodString = " on method " + executableElement.getSimpleName();
factory
.getChecker()
.reportWarning(
executableElement,
"contracts.toptype",
anno,
contract.contractAnnotation + methodString);
}
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Deduplicate contracts.toptype warnings per contract annotation.

At Line 149, iteration is over per-expression contracts, so one annotation with multiple expressions can trigger repeated identical warnings.

🔧 Proposed fix
     Set<AnnotationMirror> tops = factory.getQualifierHierarchy().getTopAnnotations();
+    Set<AnnotationMirror> warnedContractAnnotations = new LinkedHashSet<>();
     for (T contract : result) {
       AnnotationMirror anno = contract.annotation;
-      if (AnnotationUtils.containsSame(tops, anno)) {
+      if (AnnotationUtils.containsSame(tops, anno)
+          && !AnnotationUtils.containsSame(
+              warnedContractAnnotations, contract.contractAnnotation)) {
         // TODO: issue a warning on the annotation itself rather than on the method declaration.
         // This requires iterating over the annotation trees on the method declaration to determine
         // which one led tho the given AnnotationMirror.
         if (executableElement != null) {
           String methodString = " on method " + executableElement.getSimpleName();
           factory
               .getChecker()
               .reportWarning(
                   executableElement,
                   "contracts.toptype",
                   anno,
                   contract.contractAnnotation + methodString);
+          warnedContractAnnotations.add(contract.contractAnnotation);
         }
       }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// At this point, `result` contains only framework-defined contract annotations.
Set<AnnotationMirror> tops = factory.getQualifierHierarchy().getTopAnnotations();
for (T contract : result) {
AnnotationMirror anno = contract.annotation;
if (AnnotationUtils.containsSame(tops, anno)) {
// TODO: issue a warning on the annotation itself rather than on the method declaration.
// This requires iterating over the annotation trees on the method declaration to determine
// which one led tho the given AnnotationMirror.
if (executableElement != null) {
String methodString = " on method " + executableElement.getSimpleName();
factory
.getChecker()
.reportWarning(
executableElement,
"contracts.toptype",
anno,
contract.contractAnnotation + methodString);
}
}
}
// At this point, `result` contains only framework-defined contract annotations.
Set<AnnotationMirror> tops = factory.getQualifierHierarchy().getTopAnnotations();
Set<AnnotationMirror> warnedContractAnnotations = new LinkedHashSet<>();
for (T contract : result) {
AnnotationMirror anno = contract.annotation;
if (AnnotationUtils.containsSame(tops, anno)
&& !AnnotationUtils.containsSame(
warnedContractAnnotations, contract.contractAnnotation)) {
// TODO: issue a warning on the annotation itself rather than on the method declaration.
// This requires iterating over the annotation trees on the method declaration to determine
// which one led tho the given AnnotationMirror.
if (executableElement != null) {
String methodString = " on method " + executableElement.getSimpleName();
factory
.getChecker()
.reportWarning(
executableElement,
"contracts.toptype",
anno,
contract.contractAnnotation + methodString);
warnedContractAnnotations.add(contract.contractAnnotation);
}
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@framework/src/main/java/org/checkerframework/framework/util/ContractsFromMethod.java`
around lines 147 - 166, The loop over per-expression contracts in
ContractsFromMethod causes duplicate "contracts.toptype" warnings for the same
contract annotation; fix by deduplicating before reporting: create a Set to
track seen annotations (e.g., seenAnnos) and for each T contract check if its
contract.annotation is already represented in seenAnnos (use
AnnotationUtils.containsSame to compare AnnotationMirror equality) and skip
reporting if present, otherwise add it to seenAnnos and call
factory.getChecker().reportWarning(...) as currently done; keep checks using
tops, executableElement, and the existing contract.contractAnnotation +
methodString message and the "contracts.toptype" key.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a 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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@framework/tests/flow/Postcondition.java`:
- Line 317: Rename the misleading section comment "/** *** errors for invalid
postconditions ***** */" in Postcondition.java to reflect that the tests expect
warnings rather than errors (for example change it to "/** *** warnings for
top-qualifier postconditions ***** */"); update only the comment text near the
Postcondition test cases so future maintainers won't be confused by the mismatch
between the heading and the assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 99ea6df3-7f68-4fa3-99ea-af988ec6b48e

📥 Commits

Reviewing files that changed from the base of the PR and between ab6f0c0 and b6f0b79.

📒 Files selected for processing (3)
  • checker/tests/nullness/PreconditionFieldNotInStore.java
  • framework/tests/flow/Postcondition.java
  • framework/tests/flow/Precondition.java

}
}

/** *** errors for invalid postconditions ***** */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Section comment says “errors” but assertions expect warnings.

Please rename the heading to avoid confusion during future maintenance (e.g., “warnings for top-qualifier postconditions”).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@framework/tests/flow/Postcondition.java` at line 317, Rename the misleading
section comment "/** *** errors for invalid postconditions ***** */" in
Postcondition.java to reflect that the tests expect warnings rather than errors
(for example change it to "/** *** warnings for top-qualifier postconditions
***** */"); update only the comment text near the Postcondition test cases so
future maintainers won't be confused by the mismatch between the heading and the
assertions.

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.

2 participants