-
Notifications
You must be signed in to change notification settings - Fork 7
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
Update region computation. #91
Conversation
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.
What is here LGTM, though I know we plan to update this one to keep compatibility with NullAway 0.10.4. I guess we can approve after this PR is finalized.
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.
Mostly LGTM, just a few comments
with: | ||
java-version: 11 | ||
distribution: 'adopt' |
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.
Nit: 'adopt'
is out of date, use 'temurin'
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.
distribution: 'adopt' | ||
- name: Build with Gradle | ||
run: ./gradlew :core:test --scan --no-daemon -Pnullaway-test-version=0 |
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.
Probably don't need --no-daemon
here?
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.
Yes, that doesn't seem necessary. 08d9e2c
* Responsible for performing tasks related to NullAway / Type Annotator Scanner serialization | ||
* features. | ||
*/ | ||
public interface Adapter { |
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.
Let's be more verbose here and call this NullAwayVersionAdapter
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.
import edu.ucr.cs.riple.core.Config; | ||
|
||
/** Base class for all adapters. */ | ||
public abstract class AdapterAbstractClass implements Adapter { |
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 don't think it's worth creating this abstract class just to hold a single protected field. I would get rid of it.
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.
Deleted in: b51475a
* <li>Type Annotator Scanner: 1.3.3 or below | ||
* </ul> | ||
*/ | ||
public class NullAwayAdapterVersion0 extends AdapterAbstractClass { |
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.
If we go with NullAwayVersionAdapter
for the interface, this can be NullAwayVersion0Adapter
, or simply NullAwayV0Adapter
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.
Sure, sound good. b51475a
Thank you for the suggestions in renaming.
core/src/main/java/edu/ucr/cs/riple/core/adapters/NullAwayAdapterVersion1.java
Outdated
Show resolved
Hide resolved
core/src/main/java/edu/ucr/cs/riple/core/metadata/trackers/Region.java
Outdated
Show resolved
Hide resolved
…ion.java Co-authored-by: Manu Sridharan <[email protected]>
@msridhar Thank you for the review. I resolved all comments. |
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.
Would like to see CI passing on the recently released NullAway 0.10.5 before landing this, but here are my comments so far :)
path: build/reports/tests/test |
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 assume either now or in a follow up diff, we want to change all the above distribution: 'adopt'
to temurin
. Not really needed for this PR (and it's already quite large) but worth keeping in mind.
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.
distribution: 'temurin' | ||
- name: Build with Gradle | ||
run: ./gradlew :core:test --scan -Pnullaway-test-version=0 |
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.
Can we call this something more accurate like nullaway-serialization-format-version=0
. It is not directly the NullAway version itself.
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.
Sure makes sense. 69e537b
@@ -29,14 +29,14 @@ | |||
public class TrackerNodeDisplay implements Display { | |||
|
|||
public final String callerClass; | |||
public final String callerMethod; | |||
public final String callerMember; | |||
public final String calleeClass; | |||
public final String member; |
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.
Shouldn't this be renamed to calleeMember
or similar? Or maybe even calleeMethod
? Is it ever anything other than a method? Can it be a field? (If so, then the term "callee" is not exactly correct either...)
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.
Wondering if destinationClass, destinationMember, sourceClass, sourceMember would be better names here... hard to tell without having the description of the algorithm, so maybe we can keep these names for now, but they are a bit misleading (cc: @msridhar for potential naming suggestions)
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.
Yes, they it should be calleeMember
and yes it can be a field as well. I understand that calleeMember
is not accurate as it can be a field
as well. I am going to change it for now to calleeMember
but it should be changed in future. 851f8eb
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 think "caller" here refers to the region containing a use of some member, and "callee" refers to the used member. So maybe regionClass
, regionMember
, usedClass
, usedMember
? @nimakarimipour if you agree we can just make the change. Otherwise, we can leave a TODO comment and fix in a follow-up.
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.
@msridhar Sure, we are already renaming a member of this class in this PR, I will update it now. Thank you for the suggestions.
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.
"}") | ||
.setExpectedOutputs( | ||
new TrackerNodeDisplay("edu.ucr.A", "f0", "edu.ucr.B", "foo"), | ||
new TrackerNodeDisplay("edu.ucr.A", "f0", "edu.ucr.B", "foo"), |
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 are some entries, like the one just above, repeated?
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.
Methods matchIdentifier
and matchMemberSelect
might produce the same output, I just thought it might not worth it to keep all the outputs in a set and check for every output if it is already serialized as they will be deserialized and converted into a set in core
. Also I didn't want to change the test infra in this PR. Please let me know if you think I should change the infra or make a list and prevent duplicates serialize in output.
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.
Makes sense to me, just wanted to understand this. I do think eventually you might want tests to not need this repetition, but it's low priority and not needed for this PR.
// Can be enclosed by a field declaration tree. | ||
VariableTree fieldDeclTree = ASTHelpers.findEnclosingNode(path, VariableTree.class); | ||
if (fieldDeclTree != null) { | ||
fieldSymbol = ASTHelpers.getSymbol(fieldDeclTree); |
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.
So, in this path, we aren't actually checking that ASTHelpers.getSymbol(fieldDeclTree)
is a field, right?
What happens on a test case like:
public class A {
private static Object f;
static {
Object a = B.foo();
Object b = B.bar();
if (a.hashCode() > b.hashCode()) {
f = a;
} else {
f = b;
}
}
}
public class B {
public static Object foo() {...}
public static Object bar() {...}
}
Wouldn't that generate spurious A.a
and A.b
regions, even though a
and b
are locals and not members of A
?
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.
Yes, we are creating A.b
and A.a
. In previous approach the enclosing class of these fields (a
and b
) was still considered to be A
and we would have generated region A.null
. In the new approach we are making A.a
and A.b
. I am not sure if they are actually wrong as long as NullAway considers the identical region for triggered error and fix. Please let me know if we should still change this.
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.
@nimakarimipour the issue here is that there are no fields A.a
and A.b
; a
and b
are local variables inside the static initializer. Ideally, we would not create regions for entities that are not fields. I would think you could easily add a check that the symbol is actually for a field? You can check symbol.getKind().equals(ElementKind.FIELD)
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.
@msridhar @lazaroclapp Sure, it is wrong to create a region for A.a
and A.b
conceptually. To address this issue the suggested change will work and it will create correct results. But if we apply this change, it will require a follow up change in NullAway as well. If we do not apply this change, we will not get inaccurate results as NullAway
and Type-Annotator-Scanner
uses the same logic to label regions. And if we apply this change here in this PR, we might get inaccurate results using NullAway:0.10.5
. Please let me know if I should apply the change anyway and make a follow up on NullAway as well. Thank you.
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 think we should at least track this issue somewhere. I would be fine with it not being fixed in this version of the auto-annotator, so we can keep compatibility with NullAway 0.10.5 (though this is again kind of an issue where we are breaking fix serialization format compatibility... wonder how common these breaking changes will be in the future). The main problem is that I am not sure what the auto-annotator will try to do with the code above then: will it try to add @Nullable
to those local variable declarations? Will it just consider it an error where it can't make a fix on the file? I am also wondering if this can cause broken code to be generated by the auto-annotator if e.g. the @Nullable
annotation is not applicable to local variable declarations...
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.
@msridhar @lazaroclapp It is still capable of examining @Nullable
annotations on a
and b
, however NullAway will not suggest a fix for these variables since they are local variables.
The only case that will not be able to handle is seeing a dereference error in their initialization expression.
Class Bar {
static {
@Nullable Foo a;
Foo b = a.deref();
}
}
With current labeling, the region for dereferencing a.bar()
is Bar#b
and we cannot add a SuppressWarnings
on that.
But in the suggested version which is labeling this region as Bar#null
it is not still possible to silence this error with @SuppressWarnings
as well. (For both cases the only possible solution is to add the SuppressWarnings
at the class level).
In summary, the current implementation does not produce inaccurate results and it will not leave an error which the proposed approach can resolve.
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.
(For both cases the only possible solution is to add the SuppressWarnings at the class level).
So, just to be clear: What is the tool's current action on the code below:
public class A {
private static Object f;
static {
Object a = B.foo();
Object b = B.bar();
if (Objects.hashCode(a) > Objects.hashCode(b)) {
f = a;
} else {
f = b;
}
}
}
public class B {
@Nullable
public static Object foo() {...}
@Nullable
public static Object bar() {...}
}
Is it to add a suppression on the class level on A
or is it to leave the error or is it something else?
Changed to Objects.hashCode(...)
so there isn't a third-party call taking null
in there...
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.
@lazaroclapp I ran the example you provided, please find the output below:
public class A {
@Nullable
private static Object f;
static {
Object a = B.foo();
Object b = B.bar();
if (Objects.hashCode(a) > Objects.hashCode(b)) {
f = a;
} else {
f = b;
}
}
}
class B {
@Nullable
public static Object foo() {return null;}
@Nullable
public static Object bar() {return null;}
}
Leaving no remaining NullAway error.
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.
Added a unit test 2218ea3
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.
In summary, the current implementation does not produce inaccurate results and it will not leave an error which the proposed approach can resolve.
Ok, in this case, I am fine with going with this change, though we should track fixing this issue of invalid field regions everywhere (NullAway, scanner, annotator) as a follow-up.
* | ||
* @param location Location of the targeted element. | ||
* @param values Values in row of a TSV file. | ||
* @return Corresponding Error instance with the passed values. |
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.
Error
-> Fix
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.
* corresponding {@link TrackerNode} instance. | ||
* | ||
* @param values Values in row of a TSV file. | ||
* @return Corresponding Error instance with the passed values. |
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.
Error
-> TrackerNode
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.
Thank you for noticing this. 204ec6b
} | ||
|
||
@Override | ||
public Fix deserializeFix(Location location, String[] values) { |
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.
Wouldn't it be a good idea to have an initial precondition here (and in all similar methods taking String[] values
) checking for the number of elements in values
? Presumably different serialization formats will have different number of fields?
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.
Sure, sounds good. 94d4efe
public FieldInitializationAnalysis(Path path) { | ||
super(path); | ||
public FieldInitializationAnalysis(Config config) { | ||
super(config, config.target.dir.resolve("field_init.tsv")); |
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.
Could we extract "field_init.tsv"
into a global constant, like we do below for MethodDeclarationTree
:
super(config, config.target.dir.resolve(Serializer.METHOD_INFO_FILE_NAME));
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.
Actually "field_init.tsv"
is not an output of TypeAnnotatorScanner
and it might not be preferred to declare the file name in its Serializer
. But we should definitely declare the file as a global constant. I extracted it into the user class. 2db1d9a
@lazaroclapp Thank you very much for the review, this is ready for another round. |
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.
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.
Ok, given the discussion above and the results in the unit test, I think the chance of a case that will break due to the spurious local regions is sufficiently small. This LGTM! Thanks, Nima, and apologies for all the back and forth! 🙂
.toDepth(5) | ||
.addExpectedReports(new TReport(new OnField("A.java", "test.A", singleton("f")), -3)) | ||
.enableForceResolve() | ||
.start(); |
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.
👍
@msridhar @lazaroclapp Thank you very much for the review. I will land it now. |
CI
Please note that CI will fail for this PR as it requires release ofNullAway:0.10.5
.NullAway:0.10.5
is released and CI should pass.This PR updates the region computation in
Type-Annotator-Scanner
according to latest changes in NullAway following PR658.Also in this PR the infrastructure for keeping compatibility with older versions of NullAway (
0.10.4
or less) is implemented.