-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
AutoValue.Builder generated code is not correct for null-analysis #977
Comments
Thanks for the very detailed analysis! I think we'd be reluctant to add things to AutoValue just for Eclipse, but we are definitely interested in nullness analysis more generally, and the Eclipse annotations might be addressed as a special case of that. I'm surprised that the The question has arisen before, and I continue to claim that it is a bug for a nullness analyser not to know that the parameter to I like your suggestion for using local variables in the Meanwhile as a possible workaround for Eclipse it is apparently possible to disable warnings in generated code which might be appropriate until we have a better solution. |
I've spent a fair amount of time investigating this, partly because we might also want AutoValue code to be null-safe as part of the JSpecify project. However, making it work with Eclipse specifically seems as if it would be hard. In what follows, I used Eclipse JDT 4.19 for my experiments. By far the largest problem is that Eclipse takes it on itself to add a fictitious
(italics in the original). AutoValue faithfully copies type annotations everywhere the type is used, so this originally- But we run into lots of problems after that. The constructor parameters in the generated
This despite carefully copying the value in question into a local variable in order to prove to Eclipse that it can't be null. The code in question is essentially this:
You might think that Eclipse can't know that these methods never return null, but I get the same warnings if I replace I also get various warnings like this:
Well yes, but the nullness annotation is there because you added it, Eclipse. And I get this:
This kind of makes sense, since this is in a method that is (thanks the inserted annotation) So on the whole I think with the current state of the world it isn't feasible to have the |
I submitted this bug: |
While this may or maybe not a bug in JDT compiler.... So what if the example is changed with @AutoValue
public abstract class Animal {
@NonNull
public abstract String name();
@NonNull
public abstract List<@NonNull String> friends();
public abstract int numberOfLegs();
@NonNull
public static Builder builder() {
return new AutoValue_Animal.Builder();
}
@AutoValue.Builder
public abstract static class Builder {
@NonNull
public abstract Builder name(@NonNull String value);
@NonNull
public abstract Builder friends(@NonNull List<@NonNull String> value);
@NonNull
public abstract Builder numberOfLegs(int value);
@NonNull
public abstract Animal build();
}
} Now I would want the generated Build to look like this:
static final class Builder extends Animal.Builder {
private String name;
private List<@NonNull String> friends;
private Integer numberOfLegs;
Builder() {
}
@NonNull
@Override
public Animal.Builder name(@NonNull final String name) {
Objects.requireNonNull(name, "Null name");
this.name = name;
return this;
}
@NonNull
@Override
public Animal.Builder friends(@NonNull final List<@NonNull String> friends) {
Objects.requireNonNull(name, "Null friends");
this.friends = friends;
return this;
}
@NonNull
@Override
public Animal.Builder numberOfLegs(final int numberOfLegs) {
this.numberOfLegs = Integer.valueOf(numberOfLegs);
return this;
}
@NonNull
@Override
public Animal build() {
// prefix 'local' choosen by first testing if it collides with an existing element name
final String localname = name;
final List<@NonNull String> localfriends = friends;
final Integer localnumberOfLegs = numberOfLegs;
if ((localname == null) || (localfriends == null) || (localnumberOfLegs == null)) {
String missing = "";
if (localname == null) {
missing += " name";
}
if (localfriends == null) {
missing += " friends";
}
if (localnumberOfLegs == null) {
missing += " numberOfLegs";
}
throw new IllegalStateException("Missing required properties:" + missing);
}
return new AutoValue_Animal(
localname,
localfriends,
localnumberOfLegs.intValue());
}
} |
As part of my experimental work I did in fact do several of those things:
We can definitely do at least some of these, and probably will. But my impression after doing the existing experiments is that it's a lot of work just to get rid of warnings from generated code, which nobody should really care about anyway. |
Using AutoValue 1.7.4
Given the Animal example class from the documentation, with the added Eclipse JDT annotation @NonNullByDefault:
Now the AutoValue_Animal class is generated, but with errors.
Those are in the category "optional problems" and can be deactivated in the project settings. However, they might lead to other problems.
Problem 1:
The generated Builder fields:
Both should be @nullable instead
Problem 2:
All Builder method return types should be annotated as @nonnull
Problem 3:
If those would be annotated as @nullable, the Builder.build() must copy values to locals and make it possible for compiler flow analysis to verify the null-correctness. Hence I would suggest this generation pattern:
Note:
Using @AutoValue.CopyAnnotation does not solve the problem either.
As this has problems as well:
The text was updated successfully, but these errors were encountered: