-
Notifications
You must be signed in to change notification settings - Fork 13
Add suppressWarnings on fields #95
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
Conversation
|
@msridhar @lazaroclapp FYI, This PR is rebased and ready for review. Thank you 🙂. |
msridhar
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.
There seem to be some minor unrelated changes here but I am still fine with landing.
| this.f2 = new Bar(); | ||
| } | ||
|
|
||
| public void run1() { |
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 do we need both run1 and run2?
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 believe that's so that more new errors are added than the one that is resolved by marking each field @Nullable. But that should be explained in a comment on this test file...
See these reports on the test case:
new TReport(new OnField("Foo.java", "test.Foo", Set.of("f1")), 2),
new TReport(new OnField("Foo.java", "test.Foo", Set.of("f2", "f3")), 2),
new TReport(new OnField("Foo.java", "test.Foo", Set.of("f4")), 1))
I found the one for f4 a bit confusing, though. For each of f1, f3, and f4 I see two new errors created by making them @Nullable (the 2 derefs on the run methods) and one error resolved (initialization error for f1 and f3, direct @Nullable assignment for f4). So no idea why the effect is 1 for f4.
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 exactly that was needed to produce more errors to make sure @Nullable is not inferred.
Regarding f4, I am not sure if I fully understood the question, making f4 will resolve one error which is assigning @Nullable and triggers 2 new errors which is deref of f4 therefore the actual effect should be 1. I checked it manually the effect is 1 and correct. Please let me know if I am missing something.
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, then, but, by that logic: shouldn't it be the same number for f1 and f3? There is also one error being resolved (the missing initialization error) and two being added. But the corresponding TReports have 2 as the effect. Either way, I don't understand the discrepancy between the number for f4 and the other two.
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 the difference is that making fields f1 or f3 individually will not resolve the initialization error. The error still remains after the injection. It needs both annotations to be inserted to resolve the error. It only creates two new errors and does not resolve any error, hence the effect is +2.
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 the initialization error in question here?
Is it one error for f1 not being initialized and one error for f3 not being initialized, or is the generic error at Foo() saying some fields are not initialized by the constructor?
I thought we were serializing individual initialization errors. If we aren't, then wouldn't any class with 2 or more constructors and at least one field which is a) not initialized, b) treated as non-null in more than one place, then result in all non-initialized fields being marked @SuppressWarnings(...)? Even when many could otherwise be marked @Nullable? It seems like the auto-annotator should be able to consider each uninitialized field as a separate error...
To test this, could we have an example analogous to f1 but without the explicit dereferences and make sure that is actually determined to be @Nullable?
Or am I missing a different reason why adding @Nullable to f1 or f3 doesn't remove the initialization error for that 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.
@lazaroclapp Actually we only serialize one error for initialization errors per constructor which can consist of multiple fields not being initialized. The main purpose of Annotator is to minimize the number of errors the user sees from NullAway output, therefore, we do not split an error coming from NullAway into multiple errors for each uninitialized field. However, the scenario which is mentioned does not happen as we serialize a fix object for each uninitialized field and nullability of each one is investigated individually. I added a unit test for that, please see 8fb982c
In this unit test,
f1,f2andf3are not initialized by constructors and are candidates for@Nullable.- Marking
f1,f2orf3as@Nullableindividually will not resolve the one initialization error complaining about the other two fields. f3has both conditions, not initialized and treated as@Nonnullin more than one place.f1andf2are determined to be@Nullable.f3is determined to be@Nonnulland instead is annotated withSuppressWarnings("NullAway.Init")f4was not involved in initialization errors (justassign nullableerror) and cannot be@Nullable, therefore it is marked with@SuppressWarnings("NullAway").
| this.f2 = new Bar(); | ||
| } | ||
|
|
||
| public void run1() { |
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 believe that's so that more new errors are added than the one that is resolved by marking each field @Nullable. But that should be explained in a comment on this test file...
See these reports on the test case:
new TReport(new OnField("Foo.java", "test.Foo", Set.of("f1")), 2),
new TReport(new OnField("Foo.java", "test.Foo", Set.of("f2", "f3")), 2),
new TReport(new OnField("Foo.java", "test.Foo", Set.of("f4")), 1))
I found the one for f4 a bit confusing, though. For each of f1, f3, and f4 I see two new errors created by making them @Nullable (the 2 derefs on the run methods) and one error resolved (initialization error for f1 and f3, direct @Nullable assignment for f4). So no idea why the effect is 1 for f4.
| "NullAway", | ||
| false), | ||
| new AddSingleElementAnnotation( | ||
| new OnField(srcRoot.resolve("Foo.java").toString(), "test.Foo", Set.of("f0")), |
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? Shouldn't Foo.f0 simply be marked @Nullable? Where is it being used as non-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.
Thank you very much for catching this bug. It was both annotated as @Nullable and @SuppressWarnings. The @SuppressWarnings was added because of the passing null error but that error is resolved with @NullUnmarked injection but was selected again later in SuppressWarnings selection logic. This is resolved now and f0 is just annotated as @Nullable. 789b608
| if (error.messageType.equals("PASS_NULLABLE") | ||
| && error.nonnullTarget != null | ||
| && error.nonnullTarget.isOnParameter()) { | ||
| OnParameter calledMethod = error.nonnullTarget.toParameter(); |
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 is this parameter "called method"? Not sure I understand what this change is doing...
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 3301cda. It is marking a method @NullUnmarked that received a @Nullbale for parameter which annotater rejected its parameter to be @Nullable. Renamed it.
|
@msridhar @lazaroclapp Thank you for your comments and review. This is ready for another round. Thank you. |
lazaroclapp
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.
The last couple of changes make sense, but I still have some questions about how we are counting the effect / errors resolved, when dealing with field initialization...
| this.f2 = new Bar(); | ||
| } | ||
|
|
||
| public void run1() { |
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 the initialization error in question here?
Is it one error for f1 not being initialized and one error for f3 not being initialized, or is the generic error at Foo() saying some fields are not initialized by the constructor?
I thought we were serializing individual initialization errors. If we aren't, then wouldn't any class with 2 or more constructors and at least one field which is a) not initialized, b) treated as non-null in more than one place, then result in all non-initialized fields being marked @SuppressWarnings(...)? Even when many could otherwise be marked @Nullable? It seems like the auto-annotator should be able to consider each uninitialized field as a separate error...
To test this, could we have an example analogous to f1 but without the explicit dereferences and make sure that is actually determined to be @Nullable?
Or am I missing a different reason why adding @Nullable to f1 or f3 doesn't remove the initialization error for that field?
| if (!error.getRegion().isOnField()) { | ||
| return false; | ||
| } | ||
| if (error.messageType.equals("PASS_NULLABLE") && error.getRegion().isOnField()) { |
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.
Isn't the second condition already guaranteed by the check right above? (i.e. at this point error.getRegion().isOnField() is known to be true or we would have hit the early return on line 295)
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 makes sense, removed it. Thank you c3b2464
lazaroclapp
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.
One small test case request and then this looks ready to me!
| false)); | ||
| Assert.assertEquals( | ||
| expectedAnnotations, Set.copyOf(coreTestHelper.getConfig().log.getInjectedAnnotations())); | ||
| } |
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.
👍
| Bar f1; | ||
| Bar f2, f3; | ||
| @Nullable Bar nullableBar; | ||
| Bar f4 = nullableBar.process(new Object()); |
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.
New test case looks great to me! Could we still add Bar f5; without initialization or dereference to this single constructor test case and make sure it's marked as @Nullable. Just for the sake of testing all cases. If I understood the explanation correctly, then I fully expect it to work and then we can land 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.
Sure, please find it 970a2ab
lazaroclapp
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.
Looks good to land if/when CI passes! 🚀
This PR is built upon #91 and #74(#91 and #74 is landed)CI
Please note that CI will fail for this PR as it requires release ofNullAway:0.10.5.NullAway:0.10.5is released and CI should pass.This PR adds
@SuppressWarnings("NullAway")on fields that cannot get annotated as@Nullabledue to their impacts and a@Nullablevalue is used as the initializer.