-
Notifications
You must be signed in to change notification settings - Fork 437
Issue error if top is used in a contract annotation #6429
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?
Changes from all commits
0fc6a61
4f5e623
0e1c6bb
4155568
f95f5a5
84dfc62
50bfa01
ab6f0c0
d3ea70f
c55045b
b6f0b79
13b1959
8462ad6
4c65300
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,4 +34,3 @@ annotation @G: @Retention(RUNTIME) | |
| boolean[] fieldC | ||
| int fieldD | ||
| int fieldE | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -23,6 +23,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.checkerframework.javacutil.AnnotationBuilder; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.checkerframework.javacutil.AnnotationUtils; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.checkerframework.javacutil.TreeUtils; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.checkerframework.javacutil.TypeSystemError; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.plumelib.util.IPair; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -122,6 +123,7 @@ public Set<Contract.ConditionalPostcondition> getConditionalPostconditions( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private <T extends Contract> Set<T> getContractsOfKind( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ExecutableElement executableElement, Contract.Kind kind, Class<T> clazz) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Set<T> result = new LinkedHashSet<>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check for a single framework-defined contract annotation. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The result is RequiresQualifier, EnsuresQualifier, EnsuresQualifierIf, or null. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AnnotationMirror frameworkContractAnno = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -143,6 +145,27 @@ private <T extends Contract> Set<T> getContractsOfKind( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 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); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+148
to
+167
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Deduplicate 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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Check for type-system specific annotations. These are the annotations that are | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // meta-annotated by `kind.metaAnnotation`, which is PreconditionAnnotation, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // PostconditionAnnotation, or ConditionalPostconditionAnnotation. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -158,6 +181,17 @@ private <T extends Contract> Set<T> getContractsOfKind( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (enforcedQualifier == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (AnnotationUtils.containsSame(tops, enforcedQualifier)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TODO: Unfortunately, TypeSystemError does not permit giving a tree at which to issue the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // error. Obtain the file and line number from the tree, and print them here. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 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. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new TypeSystemError( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Contract annotation %s on method %s uses the top qualifier %s, which has no effect.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| contractAnno, executableElement.getSimpleName(), enforcedQualifier); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<String> expressions = factory.getContractExpressions(kind, anno); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Collections.sort(expressions); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Boolean ensuresQualifierIfResult = factory.getEnsuresQualifierIfResult(kind, anno); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| import org.checkerframework.common.subtyping.qual.Unqualified; | ||
| import org.checkerframework.dataflow.qual.Pure; | ||
| import org.checkerframework.framework.qual.EnsuresQualifier; | ||
| import org.checkerframework.framework.qual.EnsuresQualifierIf; | ||
|
|
@@ -312,4 +313,15 @@ void t6(@Odd String p1, @ValueTypeAnno String p2) { | |
| @Odd String l6 = f1; | ||
| } | ||
| } | ||
|
|
||
| /** *** errors for invalid postconditions ***** */ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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 |
||
| @EnsuresQualifier(expression = "f1", qualifier = Unqualified.class) | ||
| // :: warning: [contracts.toptype] | ||
| void noOpForTesting() {} | ||
|
|
||
| @EnsuresQualifierIf(result = true, expression = "f1", qualifier = Unqualified.class) | ||
| // :: warning: [contracts.toptype] | ||
| boolean isF1NotSet() { | ||
| return f1 == null; | ||
| } | ||
| } | ||
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.
Warnings should only be issued in the BaseTypeVisitor. Specifically, this code belongs in
BaseTypeVisitor#checkContractsAtMethodDeclaration.