Skip to content
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

Add @NullUnmarked on constructors #133

Merged
merged 9 commits into from
Feb 15, 2023
Merged

Conversation

nimakarimipour
Copy link
Member

@nimakarimipour nimakarimipour commented Feb 10, 2023

This PR fixes a bug which was causing annotator to only inject @NullUnmarked annotations on methods. This PR fixes this issue and enables Annotator to add @NullUnmarked annotation for errors enclosed by constructors.

Copy link
Member

@msridhar msridhar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One minor comment but overall this LGTM!

Comment on lines 232 to 233
System.out.println(
remainingErrors.size() + " errors remaining. Applying suppression annotations.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this print supposed to be part of this PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@msridhar Yes, I was planning to add all changes to error suppression in this PR, but it is already divided into two PRs. I can add this in a follow up PR.

Copy link
Collaborator

@lazaroclapp lazaroclapp left a 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, but I'd expect at least one test case to go with this feature/fix. Do we have a good way to test these @NullUnmarked injections now?

@nimakarimipour
Copy link
Member Author

nimakarimipour commented Feb 13, 2023

@msridhar @lazaroclapp I noticed a problem in a followup PR for this PR and updated this one. Would you please take a look at the changes.

The plan for remaining error suppression is as below:

  1. Add @NullUnmarked on the containing method where the error is reported except for initialization errors.
  2. Add SuppressWarings("NullAway.Init") for initialization errors on the field not on the constructors.
  3. Add SuppressWarings("NullAway") for errors in the initializer expression for fields on the initialized field except for passing nullable as parameters.
  4. For passing nullable errors in the initializer expression for fields, annotate the invoked method as @NullUnmarked.
  5. Add NullUnmarked at the class level for errors in the static initialization blocks
  6. Add NullUnmarked at the class level if the error is still unresolved after step 1 to 4.

Prior to the changes I made recently, we were adding @NullUnmarked on the constructors for uninitialized fields. The latest changes also conforms to version 1.3.6-alpha-5.

Copy link
Member

@msridhar msridhar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM though I agree with @lazaroclapp that we should have a test here

@nimakarimipour
Copy link
Member Author

@msridhar @lazaroclapp Please find the update test case here 5f25f1b. This unit test with this change would have been failed without changes in this PR.

new OnMethod("Main.java", "test.Main", "Main(java.lang.Object,java.lang.Object)"),
"org.jspecify.nullness.NullUnmarked"));
Assert.assertEquals(
expectedAnnotations, coreTestHelper.getConfig().log.getInjectedAnnotations());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I think this test logic is correct. But also, why are we piggy-backing testing a new feature/behavior onto an existing test, rather than creating one specifically for this change?

Copy link
Member Author

@nimakarimipour nimakarimipour Feb 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lazaroclapp Yes, recently I have been incorporating some new tests cases in the existing ones. The reason behind that is that the current CI is rather a heavy process due to the unit tests architecture. Each unit test is converted to a standalone gradle project and that makes each unit test somewhat heavy. We currently have 53 unit tests and even though the procedure is parallelized ( to the number of cores), in my laptop with 8 cores it takes more than 10 minutes. We are planning to have a CI that runs all tests with all optimization disabled and that should take even more. I think it worths keeping CI light and for new features that we would like to test if:

  1. The code pattern is very small (in this case just a constructor)
  2. The code pattern is not so different from other existing tests.
  3. The selected unit tests to be embedded is testing the similar behavior with new feature.
  4. Does not add too much noise to the selected test case.

We may be able to avoid adding a new test case and just include it in existing ones. Please let me know your thoughts on this and if this is a good approach. Thank you very much.

Copy link
Collaborator

@lazaroclapp lazaroclapp Feb 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I understand the infrastructure limitations now. I still think generally speaking such "combined" unit tests are a bad practice: a unit test, properly speaking, should be the smallest possible piece of code required to test a specific functionality in isolation. Given arbitrary engineering resources, I think it would be desirable to change the testing infra so that test cases are either much faster or this "combination" happens transparently from the point of view of a user running tests.

That said, that's obviously a massive undertaking (not to mention one which introduces its own complexities and potential errors while debugging tests), and this is a project maintained by one person, so it seems reasonable to combine test cases to some degree as above. I just wonder if that's only delaying the issue, though, as we add more and more test code (whether fully new or modified)

cc: @msridhar for any thoughts on this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the explanation and I agree this is not a good practice. We should definitely try to come up with a solution in future to resolve this. I have been planing to redesign test architecture (postponed to after submission deadline).
The main issue is that there are calls to build tasks in the test module that creates a series of .tsv files. This is the bottleneck of testing that makes the process heavy and slow. I was thinking to mock this but this (the call to subprocess that builds the target project) will require defining a series of mocked outputs that might making a new unit tests too difficult as it happens multiple times with different data for each unit tests.

@nimakarimipour
Copy link
Member Author

@msridhar @lazaroclapp Thank you very much for the review. I will land this now. We can continue the discussion in #137

@nimakarimipour nimakarimipour merged commit 73f58fa into master Feb 15, 2023
@nimakarimipour nimakarimipour deleted the nimak/resolve-remaining branch February 15, 2023 02:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants