-
Notifications
You must be signed in to change notification settings - Fork 299
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
Fix error inside Lombok generated code for @Nullable @Builder.Default #765
Conversation
…uilder.Default fields
@msridhar Let me know if you want me to split this between steps 1 (LibraryModelsHandler and core NullAway.java changes) and 2 (adding the LombokHandler and corresponding test case). Also, I am 50/50 on whether or not we should always ignore |
Pull Request Test Coverage Report for Build #1097
💛 - Coveralls |
@@ -66,7 +66,7 @@ public ImmutableSetMultimap<MethodRef, Integer> nullImpliesNullParameters() { | |||
@Override | |||
public ImmutableSet<MethodRef> nullableReturns() { | |||
return ImmutableSet.of( | |||
methodRef("com.uber.Foo", "bar()"), | |||
methodRef("com.uber.AnnotatedWithModels", "returnsNullFromModel()"), |
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.
Renamed this to avoid clashing with other tests using the common name Foo.bar()
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 couple comments / questions. In particular I'm not sure I understand the changes to library model handling.
Also:
In addition to this, we can suggest a fix upstream in Lombok to propagate @nullable to these
$default$ methods when present on the field, but this would not obviate the need for this PR, since we are keeping compatibility with multiple older Lombok releases internally.
I think we should do this; I'll bet the Lombok maintainers would want to fix.
? Nullness.NULLABLE | ||
: Nullness.NONNULL; | ||
} | ||
return handler.onOverrideMethodInvocationReturnNullability( |
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.
Small thought: I wonder if we should rename the handler method onOverrideMethodReturnNullability
? Seems like we are using now at more than just invocations.
} else if (optLibraryModels.hasNullableReturn(methodSymbol, state.getTypes(), !isAnnotated)) { | ||
return NULLABLE; |
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.
Is this fixing a separate bug unrelated to Lombok?
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 consistency. Without this change library models specifying an annotated method is @Nullable
will be ignored while analyzing the return statements inside such methods. In theory this also changes behavior for overriding, but the error will manifest only in the case where we have a @Nullable
model for a method which overriding either a) a method in annotated code or, b) a third-party method with a @NonNull
return as a library model. Guess this is probably worth some internal testing just in case I am missing some subtlety about our existing library models...
@@ -36,4 +36,5 @@ public class LombokDTO { | |||
private String field; | |||
@Builder.Default private String fieldWithDefault = "Default"; | |||
@Nullable private String nullableField; | |||
@Nullable @Builder.Default private String fieldWithNullDefault = 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.
So before this PR, this line would result in a NullAway error? Just want to make sure we have the right NullAway config to test the fix 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, though the error would be reported at the class level, since it actually happens in a new generated method of LombokDTO
:
/test-java-lib-lombok/src/main/java/com/uber/lombok/LombokDTO.java:29: error: [NullAway] returning @Nullable expression from method with @NonNull return type
@Builder
^
(see http://t.uber.com/nullaway )
Without the Lombok handler.
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.
Yikes, glad we're getting rid of this awful error message!
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.
Now LGTM
@@ -36,4 +36,5 @@ public class LombokDTO { | |||
private String field; | |||
@Builder.Default private String fieldWithDefault = "Default"; | |||
@Nullable private String nullableField; | |||
@Nullable @Builder.Default private String fieldWithNullDefault = 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.
Yikes, glad we're getting rid of this awful error message!
…tFuture (#771) We need this due to our incomplete support for generics, and the fact that #765 fixes a previous false negative which would cause our `@NonNull` model for Guava's `Function` to be ignored when determining the correct overriding of the functional interface by a lambda. After the fix, however, code such as: ``` FluentFuture .from(...) .transform(s -> { if(...) {...} else { return null; } }, executor); ``` will fail with an error about `Function::apply` having a `@NonNull` result. Where nullability should actually depend on the parameter `T` of the `ListenableFuture<T>` in question. Unfortunately, usage of this futures API is common internally for us, and our generics support is far from fully supporting this in a sound manner, so this diff introduces a (hopefully temporary) workaround, in the form of handler for the Futures/FluentFuture Guava APIs: This works by special casing the return nullability of `com.google.common.base.Function` and `com.google.common.util.concurrent.AsyncFunction` to be e.g. `Function<@nullable T>` whenever these functional interfaces are implemented as a lambda expression passed to a list of specific methods of `com.google.common.util.concurrent.FluentFuture` or `com.google.common.util.concurrent.Futures`. This is unsound, but permits the code example above to pass NullAway checking, while strictly reducing the safety hole of NullAway versions prior to PR #765.
When given code such as:
Lombok internally generates the following method:
which does not propagate
@Nullable
to the method's return type!While this method is marked as
@Generated
code and@SuppressWarnings("all")
, that does not suppress NullAway under all configurations. In fact, we sometimes want to check generated code (setters/getters for auto-annotation, for example), so just counting all Lombok Generated code as unannotated is not always the desired behavior (it's optional behavior, enabled by theTreatGeneratedAsUnannotated=true
flag).Instead, we want to internally and implicitly propagate the
@Nullable
annotation fromfieldWithNullDefault
to the generated$default$fieldWithNullDefault()
.We do this in two steps:
return [nullable expression];
should result in a NullAway error.$default$foo()
methods and looking at the nullability offoo
to determine if the method should also be@Nullable
.In addition to this, we can suggest a fix upstream in Lombok to propagate
@Nullable
to these$default$
methods when present on the field, but this would not obviate the need for this PR, since we are keeping compatibility with multiple older Lombok releases internally.Edit: Also, note that coverage on
LombokHandler
is bad in terms of unit tests, but that's because coveralls doesn't counttest-java-lib-lombok/
as a test case. We need to build with Lombok to exercise most of this handler, so we can't really exercise it in unit tests (vs integration) without significantly artificial workarounds (i.e. manually replicating the Lombok-generated code).