-
Notifications
You must be signed in to change notification settings - Fork 355
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
Warn when using the top type in a pre- or post-condition annotation #6412
base: master
Are you sure you want to change the base?
Changes from all commits
a9e22bd
bb633db
06bbd7b
250b3b9
d6224a7
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 |
---|---|---|
|
@@ -59,6 +59,7 @@ | |
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.StringJoiner; | ||
import java.util.TreeSet; | ||
import java.util.Vector; | ||
import javax.annotation.processing.ProcessingEnvironment; | ||
import javax.lang.model.element.AnnotationMirror; | ||
|
@@ -101,6 +102,11 @@ | |
import org.checkerframework.framework.flow.CFAbstractStore; | ||
import org.checkerframework.framework.flow.CFAbstractValue; | ||
import org.checkerframework.framework.qual.DefaultQualifier; | ||
import org.checkerframework.framework.qual.EnsuresQualifier; | ||
import org.checkerframework.framework.qual.EnsuresQualifierIf; | ||
import org.checkerframework.framework.qual.PostconditionAnnotation; | ||
import org.checkerframework.framework.qual.PreconditionAnnotation; | ||
import org.checkerframework.framework.qual.RequiresQualifier; | ||
import org.checkerframework.framework.qual.Unused; | ||
import org.checkerframework.framework.source.DiagMessage; | ||
import org.checkerframework.framework.source.SourceVisitor; | ||
|
@@ -2260,6 +2266,23 @@ public Void visitAnnotation(AnnotationTree tree, Void p) { | |
return null; | ||
} | ||
|
||
if (isPreOrPostConditionAnnotation(annoName)) { | ||
AnnotationMirror anno = TreeUtils.annotationFromAnnotationTree(tree); | ||
System.out.printf("anno = %s for annoName = %s for tree = %s%n", anno, annoName, tree); | ||
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. This needs to be removed or commented. |
||
// TODO: `getQualifierEnforcedByContractAnnotation` does not work for @PreconditionAnnotation | ||
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. @smillst What is your view on this design question? I lean toward the latter. 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 hard to answer this question because I don't know what you mean by "does not work". How/where are those annotations handled else where? |
||
// or @PostconditionAnnotation. Should I extend the method to handle that, or should I put | ||
// the logic here and avoid calling the method? | ||
AnnotationMirror qualifier = | ||
atypeFactory.getContractsFromMethod().getQualifierEnforcedByContractAnnotation(anno); | ||
if (qualifier != null) { | ||
AnnotationMirror topForQualifier = qualHierarchy.getTopAnnotation(qualifier); | ||
if (AnnotationUtils.areSame(qualifier, topForQualifier)) { | ||
Name contractQualName = qualifier.getAnnotationType().asElement().getSimpleName(); | ||
checker.reportWarning(tree, "contracts.toptype", contractQualName); | ||
} | ||
} | ||
} | ||
|
||
List<ExecutableElement> methods = ElementFilter.methodsIn(annoType.getEnclosedElements()); | ||
// Mapping from argument simple name to its annotated type. | ||
Map<String, AnnotatedTypeMirror> annoTypes = ArrayMap.newArrayMapOrHashMap(methods.size()); | ||
|
@@ -2316,6 +2339,29 @@ public Void visitAnnotation(AnnotationTree tree, Void p) { | |
return null; | ||
} | ||
|
||
/** Pre- and post-condition annotations that take a qualifier as an argument. */ | ||
private TreeSet<String> preAndPostConditionAnnotations = | ||
new TreeSet<>( | ||
// In Java 9+, use `List.of()`. | ||
Arrays.asList( | ||
PreconditionAnnotation.class.getName(), | ||
PostconditionAnnotation.class.getName(), | ||
RequiresQualifier.class.getName(), | ||
EnsuresQualifier.class.getName(), | ||
EnsuresQualifierIf.class.getName())); | ||
|
||
/** | ||
* Returns true if the given annotation name matches that of a pre- or post-condition annotation | ||
* or meta-annotation that takes a qualifier as an argument. | ||
* | ||
* @param annotationName an annotation name | ||
* @return true iff the annotation name matches that of a pre- or post-condition annotation | ||
*/ | ||
private boolean isPreOrPostConditionAnnotation(Name annotationName) { | ||
String annoName = annotationName.toString(); | ||
return preAndPostConditionAnnotations.contains(annoName); | ||
} | ||
|
||
/** | ||
* If the computation of the type of the ConditionalExpressionTree in | ||
* org.checkerframework.framework.type.TypeFromTree.TypeFromExpression.visitConditionalExpression(ConditionalExpressionTree, | ||
|
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.
Why isn't this logic in
BaseTypeVisitor#checkContractsAtMethodDeclaration
?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.
I initially implemented this check in
BaseTypeVisitor#checkContractsAtMethodDeclaration
(because I also thought it was useful that the contracts are already there in the method), but that method appears to check implicit annotations, as well.