-
Notifications
You must be signed in to change notification settings - Fork 27
Refactor cache to determine if an element is annotatedfor #1331
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
framework/src/main/java/org/checkerframework/framework/source/SourceChecker.java
Outdated
Show resolved
Hide resolved
| return true; | ||
| } | ||
| if (isAnnotatedForThisCheckerOrUpstreamChecker(packageElement)) { | ||
| // Return false immediately. Do NOT check for AnnotatedFor in the enclosing |
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.
Those comments are no longer true because this method now returns the result from the cache with the considering the enclosing element.
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.
This removes the comments "Return false immediately", but the behavior stays unchanged.
Should these no longer return early? What do you mean with "with the considering the enclosing element"?
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.
Because the basetype checker implementation uses the method from ATF.
@Override
protected boolean isElementAnnotatedForThisCheckerOrUpstreamChecker(@Nullable Element elt) {
return getTypeFactory().isElementAnnotatedForThisCheckerOrUpstreamChecker(elt);
}
Method isElementAnnotatedForThisCheckerOrUpstreamChecker considers whether the enclosing element is annotated or not in the cache.
wmdietl
left a 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.
Thanks!
| return true; | ||
| } | ||
| if (isAnnotatedForThisCheckerOrUpstreamChecker(packageElement)) { | ||
| // Return false immediately. Do NOT check for AnnotatedFor in the enclosing |
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.
This removes the comments "Return false immediately", but the behavior stays unchanged.
Should these no longer return early? What do you mean with "with the considering the enclosing element"?
framework/src/main/java/org/checkerframework/framework/type/AnnotatedTypeFactory.java
Show resolved
Hide resolved
framework/src/main/java/org/checkerframework/framework/type/AnnotatedTypeFactory.java
Outdated
Show resolved
Hide resolved
| * @param elt the source code element to check, or null | ||
| * @return true if the element is annotated for this checker or an upstream checker | ||
| */ | ||
| public boolean isElementAnnotatedForThisCheckerOrUpstreamChecker(@Nullable Element elt) { |
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 add this method here? Why isn't this the implementation in SourceChecker? Or, if you want to keep that implementation abstract, move this to BaseTypeChecker, the only place that externally calls this method.
Why have two methods with the same name in both the Checker and the ATF?
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 the implementation in SourceChecker?
This method need a ATF because getDeclAnnotation is defined in ATF.
Or, if you want to keep that implementation abstract, move this to BaseTypeChecker, the only place that externally calls this method.
Because QualifierDefaults also called this method.
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.
Because QualifierDefaults also called this method.
If QualifierDefaults only has an ATF and no checker, simply call atf.getChecker() somewhere?
framework/src/main/java/org/checkerframework/framework/type/AnnotatedTypeFactory.java
Show resolved
Hide resolved
framework/src/main/java/org/checkerframework/common/util/count/AnnotationStatistics.java
Outdated
Show resolved
Hide resolved
framework/src/main/java/org/checkerframework/framework/source/AggregateChecker.java
Show resolved
Hide resolved
| && !isElementAnnotatedForThisChecker(annotationScope); | ||
| && !atypeFactory | ||
| .getChecker() | ||
| .isElementAnnotatedForThisCheckerOrUpstreamChecker(annotationScope); |
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.
This call is causing the problem :-1: warning: junit-assertions.astub: Parse problem: org.checkerframework.javacutil.TypeSystemError: Called getTypeFactory() before initialization was complet.
It is interesting I can not get the type factory from the checker itself because the visitor is not initialized, even though I already had atypeFactory.
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.
According to stacktrace, the method call went wrong because the chain initialization is init_checker -> init_visitor -> init_atf -> postinit in atf's constructor -> parsestubfiles -> determine default -> atf.getchecker().getTypeFactory(). We can get the checker from the atf successfully because it is assigned in the AnnotatedTypeFactory's constructor. But we don't have the visitor yet from the checker because it is the one underinitialization. Looks like this error too conservative because a partially initialized atf is able to determine whether an element is annotated or not.
at org.checkerframework.framework.stub.AnnotationFileElementTypes.parseStubFiles(AnnotationFileElementTypes.java:210)
at org.checkerframework.framework.type.AnnotatedTypeFactory.parseAnnotationFiles(AnnotatedTypeFactory.java:4097)
at org.checkerframework.framework.type.GenericAnnotatedTypeFactory.postInit(GenericAnnotatedTypeFactory.java:438)
at org.checkerframework.checker.initialization.InitializationAnnotatedTypeFactory.<init>(InitializationAnnotatedTypeFactory.java:60)
at org.checkerframework.checker.initialization.InitializationVisitor.createTypeFactory(InitializationVisitor.java:84)
at org.checkerframework.checker.initialization.InitializationVisitor.createTypeFactory(InitializationVisitor.java:52)
at org.checkerframework.common.basetype.BaseTypeVisitor.<init>(BaseTypeVisitor.java:343)
at org.checkerframework.common.basetype.BaseTypeVisitor.<init>(BaseTypeVisitor.java:329)
at org.checkerframework.checker.initialization.InitializationVisitor.<init>(InitializationVisitor.java:77)
at org.checkerframework.checker.initialization.InitializationChecker.createSourceVisitor(InitializationChecker.java:149)
at org.checkerframework.checker.initialization.InitializationChecker.createSourceVisitor(InitializationChecker.java:73)
at org.checkerframework.framework.source.SourceChecker.initChecker(SourceChecker.java:1175)
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.
Previous version works with code duplication because works flow is init_checker -> init_visitor -> init_atf -> postinit in atf's constructor -> parsestubfiles -> determine default -> atf. I am going to revert to previous version with some comments.
c10565f to
eb741f4
Compare
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.
Pull request overview
This PR refactors the cache and method for determining if an element is annotated for a checker, moving it from QualifierDefaults to AnnotatedTypeFactory to enable code reuse. The method isElementAnnotatedForThisChecker is renamed to isElementAnnotatedForThisCheckerOrUpstreamChecker and made abstract in SourceChecker.
- Moved the
isElementAnnotatedForThisCheckermethod and its cache fromQualifierDefaultstoAnnotatedTypeFactory - Made
SourceChecker#isElementAnnotatedForThisCheckerOrUpstreamCheckeran abstract method - Implemented the abstract method in all subclasses (
BaseTypeChecker,AggregateChecker, and utility checkers)
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| QualifierDefaults.java | Removed private isElementAnnotatedForThisChecker method and elementAnnotatedFors cache; updated calls to use the factory method |
| AnnotatedTypeFactory.java | Added public isElementAnnotatedForThisCheckerOrUpstreamChecker method with the cache implementation |
| SourceChecker.java | Changed method from private to protected abstract; removed implementation and imports; updated method calls |
| BaseTypeChecker.java | Implemented abstract method by delegating to the type factory |
| AggregateChecker.java | Implemented abstract method to return false (delegates to subcheckers) |
| SignaturePrinter.java | Implemented abstract method to throw BugInCF (not expected to be called) |
| JavaCodeStatistics.java | Implemented abstract method to throw BugInCF (not expected to be called) |
| AnnotationStatistics.java | Implemented abstract method to throw BugInCF (not expected to be called) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| if (elt == null) { | ||
| throw new BugInCF( | ||
| "Call of QualifierDefaults.isElementAnnotatedForThisChecker with null"); |
Copilot
AI
Dec 19, 2025
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.
The error message incorrectly refers to "QualifierDefaults.isElementAnnotatedForThisChecker" but this method is now in AnnotatedTypeFactory. The error message should be updated to reflect the new location.
| "Call of QualifierDefaults.isElementAnnotatedForThisChecker with null"); | |
| "Call of AnnotatedTypeFactory.isElementAnnotatedForThisCheckerOrUpstreamChecker with null"); |
| * Return false, determine whether an aggregatechecker is annotated for depends on its | ||
| * subcheckers. For checkers that have upstream checker and want to handle annotatedfor in both | ||
| * this and upstream checker, see InitializationChecker#getUpstreamCheckerNames(). |
Copilot
AI
Dec 19, 2025
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.
Spelling and grammar issues: "aggregatechecker" should be "aggregate checker" (two words), and "annotatedfor" should be "@AnnotatedFor". The sentence also needs grammatical improvements for clarity.
| * Return false, determine whether an aggregatechecker is annotated for depends on its | |
| * subcheckers. For checkers that have upstream checker and want to handle annotatedfor in both | |
| * this and upstream checker, see InitializationChecker#getUpstreamCheckerNames(). | |
| * Always returns false. Whether an aggregate checker is annotated with {@code @AnnotatedFor} | |
| * depends on its subcheckers. For checkers that have an upstream checker and want to handle | |
| * {@code @AnnotatedFor} in both this and the upstream checker, see | |
| * {@link org.checkerframework.checker.initialization.InitializationChecker#getUpstreamCheckerNames()}. |
| // The implementation of this method is at AnnotatedTypeFactory because postinit() is called | ||
| // in the constructor of ATFs and use the same method before the ATF is fully initialized. | ||
| // Implement it here and call this method by atf.getChecker() would fail as it will call | ||
| // getTypeFactory() in the body and would result type system error as the visitor is not | ||
| // initialized yet. |
Copilot
AI
Dec 19, 2025
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.
The comment has several issues: "postinit()" should likely be "postInit()", "ATFs" is unclear (should be "AnnotatedTypeFactory instances"), "use the same method" is confusing, "Implement it here and call this method by atf.getChecker() would fail as it will call getTypeFactory()" has grammatical problems. The explanation needs to be clearer about why the implementation delegates to the factory.
| // The implementation of this method is at AnnotatedTypeFactory because postinit() is called | |
| // in the constructor of ATFs and use the same method before the ATF is fully initialized. | |
| // Implement it here and call this method by atf.getChecker() would fail as it will call | |
| // getTypeFactory() in the body and would result type system error as the visitor is not | |
| // initialized yet. | |
| // The main implementation of this method resides in AnnotatedTypeFactory. The factory's | |
| // postInit() method is invoked from the constructors of AnnotatedTypeFactory instances, | |
| // and during postInit() the factory may need to call | |
| // isElementAnnotatedForThisCheckerOrUpstreamChecker. If this method were implemented | |
| // here and invoked via atf.getChecker(), its body would call getTypeFactory(), which | |
| // could access or create the factory before it is fully initialized and before the | |
| // visitor is set up, leading to a type-system error. To avoid this initialization | |
| // cycle, this checker method simply delegates to the AnnotatedTypeFactory implementation. |
| * Return false, determine whether an aggregatechecker is annotated for depends on its | ||
| * subcheckers. For checkers that have upstream checker and want to handle annotatedfor in both | ||
| * this and upstream checker, see InitializationChecker#getUpstreamCheckerNames(). |
Copilot
AI
Dec 19, 2025
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.
Grammar issue: "have upstream checker" should be "have an upstream checker" (missing article), and "annotatedfor" should be "@AnnotatedFor".
| * Return false, determine whether an aggregatechecker is annotated for depends on its | |
| * subcheckers. For checkers that have upstream checker and want to handle annotatedfor in both | |
| * this and upstream checker, see InitializationChecker#getUpstreamCheckerNames(). | |
| * Returns false. Whether an aggregate checker is {@code @AnnotatedFor} depends on its | |
| * subcheckers. For checkers that have an upstream checker and want to handle {@code @AnnotatedFor} | |
| * in both this and the upstream checker, see InitializationChecker#getUpstreamCheckerNames(). |
|
|
||
| return false; | ||
| } | ||
| protected abstract boolean isElementAnnotatedForThisCheckerOrUpstreamChecker(Element elt); |
Copilot
AI
Dec 19, 2025
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.
The abstract method lacks documentation. Since this is now an abstract method that subclasses must implement, it should have Javadoc explaining its purpose, parameters, and return value. The documentation from the previous implementation or from AnnotatedTypeFactory.isElementAnnotatedForThisCheckerOrUpstreamChecker should be added here.
| * @return true if the element is annotated for this checker or an upstream checker | ||
| */ | ||
| @Override | ||
| public boolean isElementAnnotatedForThisCheckerOrUpstreamChecker(Element elt) { |
Copilot
AI
Dec 19, 2025
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.
The method is declared as public here, but it's declared as protected abstract in the parent class SourceChecker. This is widening the visibility from protected to public, which may be intentional but is inconsistent. The method should use protected to match the abstract declaration in SourceChecker, unless there's a specific reason to make it public.
| public boolean isElementAnnotatedForThisCheckerOrUpstreamChecker(Element elt) { | |
| protected boolean isElementAnnotatedForThisCheckerOrUpstreamChecker(Element elt) { |
In this PR, I refactored the
isElementAnnotatedForThisCheckerand its cache fromQualifierDefaultstoAnnotatedTypeFactoryto foster code reuse in both determining the default and whether error should be suppressed. I am making the methodSourceChecker#isElementAnnotatedForThisCheckerabstract to reduce code duplication.