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
Added Optional emptiness handler #278
Changes from 1 commit
fd0fadd
884528b
f639ed2
c7bf3d0
a155e0e
a521ff0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -365,7 +365,8 @@ private void updateEnvironmentMapping(Tree tree, VisitorState state) { | |
// from the nested scope, so the program point doesn't matter | ||
// 2. we keep info on all locals rather than just effectively final ones for simplicity | ||
EnclosingEnvironmentNullness.instance(state.context) | ||
.addEnvironmentMapping(tree, analysis.getLocalVarInfoBefore(state.getPath(), state)); | ||
.addEnvironmentMapping( | ||
tree, analysis.getLocalVarInfoBefore(state.getPath(), state, handler)); | ||
} | ||
|
||
private Symbol.MethodSymbol getSymbolOfSuperConstructor( | ||
|
@@ -1932,21 +1933,16 @@ private Description matchDereference( | |
return Description.NO_MATCH; | ||
} | ||
if (mayBeNullExpr(state, baseExpression)) { | ||
String message; | ||
if (handler.checkIfOptionalGetCall(baseExpression, state)) { | ||
final int exprStringSize = baseExpression.toString().length(); | ||
// Name of the optional is extracted from the expression | ||
message = | ||
"Optional " | ||
+ baseExpression.toString().substring(0, exprStringSize - 6) | ||
+ " can be empty, dereferenced get() call on it"; | ||
} else { | ||
message = "dereferenced expression " + baseExpression.toString() + " is @Nullable"; | ||
} | ||
final String message = | ||
"dereferenced expression " + baseExpression.toString() + " is @Nullable"; | ||
ErrorMessage errorMessage = new ErrorMessage(MessageTypes.DEREFERENCE_NULLABLE, message); | ||
|
||
handler.getErrorMessage(baseExpression, state, errorMessage); | ||
|
||
return createErrorDescriptionForNullAssignment( | ||
MessageTypes.DEREFERENCE_NULLABLE, | ||
errorMessage.messageType, | ||
derefExpression, | ||
message, | ||
errorMessage.message, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should probably change all the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, definitely. |
||
baseExpression, | ||
state.getPath()); | ||
} | ||
|
@@ -2346,7 +2342,23 @@ public enum MessageTypes { | |
UNBOX_NULLABLE, | ||
NONNULL_FIELD_READ_BEFORE_INIT, | ||
ANNOTATION_VALUE_INVALID, | ||
CAST_TO_NONNULL_ARG_NONNULL; | ||
CAST_TO_NONNULL_ARG_NONNULL, | ||
GET_ON_EMPTY_OPTIONAL; | ||
} | ||
|
||
public static class ErrorMessage { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Like this. But, can we extract this and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍Done |
||
MessageTypes messageType; | ||
String message; | ||
|
||
ErrorMessage(MessageTypes messageType, String message) { | ||
this.messageType = messageType; | ||
this.message = message; | ||
} | ||
|
||
public void updateErrorMessage(MessageTypes messageType, String message) { | ||
this.messageType = messageType; | ||
this.message = message; | ||
} | ||
} | ||
|
||
@AutoValue | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,15 +21,12 @@ | |
import com.google.common.collect.ImmutableList; | ||
import com.google.errorprone.VisitorState; | ||
import com.sun.source.util.TreePath; | ||
import com.sun.tools.javac.code.Symbol; | ||
import com.sun.tools.javac.code.Type; | ||
import com.sun.tools.javac.util.Context; | ||
import com.uber.nullaway.Config; | ||
import com.uber.nullaway.Nullness; | ||
import com.uber.nullaway.handlers.Handler; | ||
import java.util.Collections; | ||
import java.util.LinkedHashSet; | ||
import java.util.Optional; | ||
import java.util.Set; | ||
import java.util.function.Predicate; | ||
import javax.annotation.Nullable; | ||
|
@@ -160,7 +157,7 @@ public Set<Element> getNonnullStaticFieldsBefore(TreePath path, Context context) | |
* @param state visitor state | ||
* @return nullness info for local variables just before the node | ||
*/ | ||
public NullnessStore getLocalVarInfoBefore(TreePath path, VisitorState state) { | ||
public NullnessStore getLocalVarInfoBefore(TreePath path, VisitorState state, Handler handler) { | ||
NullnessStore store = dataFlow.resultBefore(path, state.context, nullnessPropagation); | ||
if (store == null) { | ||
return NullnessStore.empty(); | ||
|
@@ -176,30 +173,14 @@ public NullnessStore getLocalVarInfoBefore(TreePath path, VisitorState state) { | |
} | ||
} | ||
|
||
// a filter for Optional get() call | ||
if (ap.getElements().size() == 1) { | ||
AccessPath.Root root = ap.getRoot(); | ||
if (!root.isReceiver() && (ap.getElements().get(0) instanceof Symbol.MethodSymbol)) { | ||
final Element e = root.getVarElement(); | ||
final Symbol.MethodSymbol g = (Symbol.MethodSymbol) ap.getElements().get(0); | ||
final Optional<Type> tbaseType = | ||
Optional.ofNullable(state.getTypeFromString(OPTIONAL_PATH)) | ||
.map(state.getTypes()::erasure); | ||
return e.getKind().equals(ElementKind.LOCAL_VARIABLE) | ||
&& g.getSimpleName().toString().equals("get") | ||
&& g.getParameters().length() == 0 | ||
&& tbaseType.isPresent() | ||
&& state.getTypes().isSubtype(g.owner.type, tbaseType.get()); | ||
} | ||
} | ||
return false; | ||
return handler.filterApForLocalVarInfoBefore(ap, state); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure the name is super clear. But can't also think of a better one. @msridhar ? (This definitely is a better design, though :) ) p.s. See #278 (comment) for further discussion. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍Done |
||
}); | ||
} | ||
|
||
/** | ||
* @param path tree path of static method, or initializer block | ||
* @param context Javac context | ||
* @return fields guaranteed to be nonnull at exit of static method (or initializer block) | ||
* @return fields guaranteed to be nonnull at exit of static method (or initialize r block) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo? |
||
*/ | ||
public Set<Element> getNonnullStaticFieldsAtExit(TreePath path, Context context) { | ||
NullnessStore nullnessResult = dataFlow.finalResult(path, context, nullnessPropagation); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,6 +36,7 @@ | |
import com.sun.tools.javac.util.Context; | ||
import com.uber.nullaway.NullAway; | ||
import com.uber.nullaway.Nullness; | ||
import com.uber.nullaway.dataflow.AccessPath; | ||
import com.uber.nullaway.dataflow.AccessPathNullnessPropagation; | ||
import com.uber.nullaway.dataflow.NullnessStore; | ||
import java.util.List; | ||
|
@@ -275,9 +276,21 @@ void onDataflowVisitLambdaResultExpression( | |
* | ||
* @param expr The AST node for the expression being matched. | ||
* @param state The current visitor state. | ||
* @return If the method call in the expression is to Optional.get(). | ||
* @param errorMessage error message string and type of the error wrapped in {@link | ||
* com.uber.nullaway.NullAway.ErrorMessage}. | ||
*/ | ||
boolean checkIfOptionalGetCall(ExpressionTree expr, VisitorState state); | ||
void getErrorMessage(ExpressionTree expr, VisitorState state, NullAway.ErrorMessage errorMessage); | ||
|
||
/** | ||
* Called when the store access paths are filtered for local variable information before an | ||
* expression. | ||
* | ||
* @param accessPath The access path that needs to be checked if filtered. | ||
* @param state The current visitor state. | ||
* @return true if the accesspath should be filtered to be included in the local variable | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "should be filtered to be included" doesn't easily parse in my mind. Are we excluding or including? (e.g. is this a whitelist or a blacklist?). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After reading the rest of the code more carefully, I'd go with "@return true if the nullability information for this accesspath should be treated as part of the surrounding context when processing a lambda expression or anonymous class declaration" I'd also rename If we do rename that, we can have this method be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with @lazaroclapp's comments and suggested names 😄 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍Done |
||
* information. | ||
*/ | ||
boolean filterApForLocalVarInfoBefore(AccessPath accessPath, VisitorState state); | ||
|
||
/** | ||
* A three value enum for handlers implementing onDataflowVisitMethodInvocation to communicate | ||
|
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.
If this is changing the error message text, rather than retrieving a new copy, it shouldn't be called
get[...]
. How aboutHandler.onPrepareErrorMessage
?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.
done 👍