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

Serialization of Type Change Suggestions for Type Violations. #517

Merged
merged 388 commits into from
Feb 9, 2022

Conversation

nimakarimipour
Copy link
Contributor

@nimakarimipour nimakarimipour commented Dec 15, 2021

Overview

This PR enables NullAway to serialize information on code locations to resolve the reporting errors if they can be resolved via a type change. This information can be used by other tools such as AutoFixer to have an understanding of the source code, code points where violations occurred, and possible solutions. In the example below, NullAway will report the error:
assigning @Nullable null to @Nonnull bar.

class Foo {
     Object bar = new Object();
     
     void baz(){
           this.bar = null;
     }
}

This PR enables NullAway to suggest bar type to be @Nullable to resolve the error and creates two output files. errors.tsv where all reporting errors are stored and fixes.tsv where all the type change suggestions are stored.

  1. errors.tsv: Every row represents a reporting error with the format: MESSAGE_TYPE, MESSAGE, ENCLOSING CLASS, ENCLOSING METHOD
    For instance: ASSIGN_FIELD_NULLABLE, assigning nullable null to field bar, Foo, baz
  2. fixes.tsv: Every row represents a type change suggestion with the format: LOCATION, MESSAGE_TYPE, ANNOT
    For instance: Field bar of Foo, ASSIGN_FIELD_NULLABLE, @Nullable

To activate the feature above the following flags must be passed to NullAway via ErrorProneFlags:

"-XepOpt:NullAway:SerializeFixMetadata=true"
"-XepOpt:NullAway:FixSerializationConfigPath=configpath"

When -XepOpt:NullAway:SerializeFixMetadata=true is observed in the flags, NullAway will look for a file at FixSerializationConfigPath to activate/deactivate serialization features.

Below is the FixMetadataConfigPath format:

<serialization>
    <suggest activation="true" enclosing="true"/>
    <output>
           "outputdir"
    </output>
    <annotation>
        <nullable>javax.annotation.Nullable</nullable>
        <nonnull>javax.annotation.Nonnull</nonnull>
    </annotation>
</serialization>

If -XepOpt:NullAway:SerializeFixMetadata=true is observed, NullAway will serialize all reporting errors to errors.tsv regardless of flag values in FixSerializationConfig.

Finding enclosing method/class for suggested fixes is costly and not always required, they can be controlled using enclosing attribute in the config file above. If any of the keys in the template config above is not found, the default value will be considered.

Please note, at the places where explicit @Nonnull is observed, NullAway will acknowledge that annotation and will not suggest any type change.

Implementation

All implemented classes reside in the fixserialization package. The goal of this PR is to create SuggestedFixInfo objects which are type change suggestions of elements in the source code. Each of these objects contains a single Location instance and an Annotation . Location instances target an element in the source code. Throughout this PR the only suggested annotation in SuggestedFixInfo instances is @Nullable.

The creation of SuggestedFixInfo objects is implemented through ErrorBuilder. The createErrorDescription signature is changed and now it accepts a fifth argument called nonNullTarget. If this parameter is non-null, the error involved a pseudo-assignment of a @Nullable expression into a @NonNull target, and this parameter is the Symbol for that target. Whenever ErrorBuilder is asked to create an ErrorDescription object, it also creates a SuggestedFixInfo targeting the parameter nonNullTarget to be @Nullable.

@nimakarimipour
Copy link
Contributor Author

nimakarimipour commented Jan 28, 2022

Hello @msridhar and @lazaroclapp,

I ran the new version on my benchmarks to make sure everything is working and the tool crashed, I fixed the bug and added a test case: e86e2a3. It seems methods and constructs have different values for ElementKind.

Lazaro, there are a lot of comments on this PR and the GitHub UI is not working really well, for your convenience I rewrote my responses to some of your comments, please find them below:

  1. Side Note on TSV: You mentioned that file names can have tabs inside but this is fine for now, please let me know of you prefer to address it now.
  2. Keeping the config file: In our last meeting we decided to keep the config file, therefore, I did not do anything for this comment.
  3. Make Nullable: You mentioned to make a field (outputdir at FixSerializationConfig) @Nullable for a reason which I did not understand, would you please give me more instructions what I have to do ?

Also you asked why we need to have a control flag on fix serialization, since fix serialization comes with a cost on performance, and in our diagnose phase where we rebuild the project per fix, we do not need to know the list of new fixes at every single build unless we are performing a deep search.

I believe this is ready for another review, sorry for the delay, I did not expect it to take this long :)

@lazaroclapp
Copy link
Collaborator

Doing another pass over the PR right now, but went over the responses to comments first, and they generally look good to me.

Regarding the three questions in the summary:

  1. Side Note on TSV: You mentioned that file names can have tabs inside but this is fine for now, please let me know of you prefer to address it now.

Fine to leave for later. Maybe worth a comment in the code noting the potential issue, but filenames with [TAB] should be rare.

  1. Keeping the config file: In our last meeting we decided to keep the config file, therefore, I did not do anything for this comment.

Agreed!

  1. Make Nullable: You mentioned to make a field (outputdir at FixSerializationConfig) @Nullable for a reason which I did not understand, would you please give me more instructions what I have to do ?

Responded inline. But, basically outputdir is not initialized in the constructor for the builder, so it should be an @Nullable field, and presumably checked to be non-null at build, throwing some sort of exception related to incorrect object construction if it is or just a Preconditions.checkNotNull(...) if this will only ever be used internally within NullAway.

Also you asked why we need to have a control flag on fix serialization, since fix serialization comes with a cost on performance, and in our diagnose phase where we rebuild the project per fix, we do not need to know the list of new fixes at every single build unless we are performing a deep search.

Makes sense to me!

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.

This is basically ready. One issue with the output from tests that I would really want fixed and the rest are a couple nits and questions. Hopefully, this should be a much easier review to address than the last one :)

nullaway/src/main/java/com/uber/nullaway/Config.java Outdated Show resolved Hide resolved
import com.uber.nullaway.fixserialization.location.FixLocation;
import com.uber.nullaway.fixserialization.out.ErrorInfo;
import com.uber.nullaway.fixserialization.out.SuggestedFixInfo;
import com.uber.nullaway.fixserialization.SerializationHandler;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Again, probably a different name, but love how much cleaner this is in terms of changes to core NullAway! :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Renamed to SerializationService, please let me know if you still think I should rename it to something else.

.map(
element -> ASTHelpers.getSymbol(getTreesInstance(state).getTree(element)))
.collect(Collectors.toList()))
.build();
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think I said this before, but ImmutableList also has a collector, so no need to generate the intermediate mutable list and copy the elements. See my response comment to the original change here.

"-XepOpt:NullAway:SerializeFixMetadata=true",
"-XepOpt:NullAway:FixSerializationConfigPath=" + configPath))
.addSourceLines(
"com/uber/android/Super.java",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why Super.java?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

" return h;",
" }",
" Object test_param(@Nullable String o) {",
" // BUG: Diagnostic contains: passing @Nullable",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Match indentation of the following line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

"package com.uber;",
"import java.util.ArrayList;",
"public class Child extends Super<String>{",
" public void newSideEffect(ArrayList<String> op) {",
" // BUG: Diagnostic contains: passing @Nullable",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Match indentation of the following line.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@nimakarimipour
Copy link
Contributor Author

Hello @lazaroclapp, thank you very much for your comments. This is ready for another review, with best regards.

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.

LGTM. Consider fixing the one typo below before landing, but otherwise this looks ready to me! 🎉 🚀

if (output.size() != 0) {
errorMessage
.append(output.size())
.append(" unexpected fix(s) suggestions were found:")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Doesn't need the (s), "fix suggestions" is already plural.

@nimakarimipour
Copy link
Contributor Author

@msridhar and @lazaroclapp Thank you very much for your comments, this was super educating for me.

@msridhar msridhar merged commit 9dbdd65 into uber:master Feb 9, 2022
@nimakarimipour nimakarimipour deleted the fix_suggest branch March 21, 2022 23:56
lazaroclapp pushed a commit that referenced this pull request Jul 14, 2022
Following GH-517 If `serializeFixMetadata` is active, `NullAway` will serialize suggested type changes (currently just adding `@Nullable`). In the `FixSerializationConfig` file we can set a custom annotation that `NullAway` will use in the output serializations. 

However, this process can be simplified by just asking `NullAway` to serialize type change locations with a default annotation name, any tool processing this output can use their own preferred annotation.

After this PR the following entity will be removed from the config `.xml` file:
```xml
<annotation>
       <nonnull> ... </nonnull>
       <nullable> ... </nullable>
</annotation
```
And a sample out below:
```
Field bar of Foo, ASSIGN_FIELD_NULLABLE, @nullable
```

Will be changed to simply:
```
Field bar of Foo, ASSIGN_FIELD_NULLABLE, nullable
```
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.

4 participants