-
Notifications
You must be signed in to change notification settings - Fork 291
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
refactor: streamline mayBeNullExpr flow #753
Changes from all commits
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 |
---|---|---|
|
@@ -2197,18 +2197,13 @@ private boolean mayBeNullExpr(VisitorState state, ExpressionTree expr) { | |
// obviously not null | ||
return false; | ||
} | ||
if (expr.getKind() == Tree.Kind.NULL_LITERAL) { | ||
// obviously null, so same as for above literals we return early without consulting handlers | ||
return true; | ||
} | ||
// the logic here is to avoid doing dataflow analysis whenever possible | ||
Symbol exprSymbol = ASTHelpers.getSymbol(expr); | ||
boolean exprMayBeNull; | ||
// return early for expressions that no handler overrides and will not need dataflow analysis | ||
switch (expr.getKind()) { | ||
case NULL_LITERAL: | ||
// obviously null | ||
return true; | ||
case ARRAY_ACCESS: | ||
// unsound! we cannot check for nullness of array contents yet | ||
exprMayBeNull = false; | ||
break; | ||
case NEW_CLASS: | ||
case NEW_ARRAY: | ||
// for string concatenation, auto-boxing | ||
|
@@ -2258,29 +2253,35 @@ private boolean mayBeNullExpr(VisitorState state, ExpressionTree expr) { | |
case RIGHT_SHIFT: | ||
case UNSIGNED_RIGHT_SHIFT: | ||
// clearly not null | ||
exprMayBeNull = false; | ||
return false; | ||
default: | ||
break; | ||
} | ||
// the logic here is to avoid doing dataflow analysis whenever possible | ||
Symbol exprSymbol = ASTHelpers.getSymbol(expr); | ||
boolean exprMayBeNull; | ||
switch (expr.getKind()) { | ||
case MEMBER_SELECT: | ||
if (exprSymbol == null) { | ||
throw new IllegalStateException( | ||
"unexpected null symbol for dereference expression " + state.getSourceForNode(expr)); | ||
} | ||
exprMayBeNull = mayBeNullFieldAccess(state, expr, exprSymbol); | ||
exprMayBeNull = | ||
NullabilityUtil.mayBeNullFieldFromType(exprSymbol, config, codeAnnotationInfo); | ||
break; | ||
case IDENTIFIER: | ||
if (exprSymbol == null) { | ||
throw new IllegalStateException( | ||
"unexpected null symbol for identifier " + state.getSourceForNode(expr)); | ||
} | ||
if (exprSymbol.getKind().equals(ElementKind.FIELD)) { | ||
// Special case: mayBeNullFieldAccess runs handler.onOverrideMayBeNullExpr before | ||
// dataflow. | ||
return mayBeNullFieldAccess(state, expr, exprSymbol); | ||
if (exprSymbol.getKind() == ElementKind.FIELD) { | ||
exprMayBeNull = | ||
NullabilityUtil.mayBeNullFieldFromType(exprSymbol, config, codeAnnotationInfo); | ||
} else { | ||
// Check handler.onOverrideMayBeNullExpr before dataflow. | ||
exprMayBeNull = handler.onOverrideMayBeNullExpr(this, expr, exprSymbol, state, true); | ||
return exprMayBeNull ? nullnessFromDataflow(state, expr) : false; | ||
// rely on dataflow analysis for local variables | ||
exprMayBeNull = true; | ||
} | ||
break; | ||
case METHOD_INVOCATION: | ||
if (!(exprSymbol instanceof Symbol.MethodSymbol)) { | ||
throw new IllegalStateException( | ||
|
@@ -2289,36 +2290,30 @@ private boolean mayBeNullExpr(VisitorState state, ExpressionTree expr) { | |
+ " for method invocation " | ||
+ state.getSourceForNode(expr)); | ||
} | ||
// Special case: mayBeNullMethodCall runs handler.onOverrideMayBeNullExpr before dataflow. | ||
return mayBeNullMethodCall(state, expr, (Symbol.MethodSymbol) exprSymbol); | ||
exprMayBeNull = mayBeNullMethodCall((Symbol.MethodSymbol) exprSymbol); | ||
break; | ||
case CONDITIONAL_EXPRESSION: | ||
case ASSIGNMENT: | ||
exprMayBeNull = nullnessFromDataflow(state, expr); | ||
exprMayBeNull = true; | ||
break; | ||
default: | ||
// match switch expressions by comparing strings, so the code compiles on JDK versions < 12 | ||
if (expr.getKind().name().equals("SWITCH_EXPRESSION")) { | ||
exprMayBeNull = nullnessFromDataflow(state, expr); | ||
exprMayBeNull = true; | ||
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. note that here and above, we used to first run dataflow and only then called the handler overrides while now we would be doing it the other way round. 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, agreed, running dataflow last uniformly is what we want. 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 do, under some circumstances, want handlers to override the results from the dataflow analysis, but I think this should in all cases be done by using one of the handler methods that run as part of the dataflow analysis itself, not the AST-node taking methods... (Edit: to clarify, I am agreeing with the uniform ordering here) |
||
} else { | ||
throw new RuntimeException( | ||
"whoops, better handle " + expr.getKind() + " " + state.getSourceForNode(expr)); | ||
} | ||
} | ||
exprMayBeNull = handler.onOverrideMayBeNullExpr(this, expr, exprSymbol, state, exprMayBeNull); | ||
return exprMayBeNull; | ||
return exprMayBeNull && nullnessFromDataflow(state, expr); | ||
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. together with the |
||
} | ||
|
||
private boolean mayBeNullMethodCall( | ||
VisitorState state, ExpressionTree expr, Symbol.MethodSymbol exprSymbol) { | ||
boolean exprMayBeNull = true; | ||
private boolean mayBeNullMethodCall(Symbol.MethodSymbol exprSymbol) { | ||
if (codeAnnotationInfo.isSymbolUnannotated(exprSymbol, config)) { | ||
exprMayBeNull = false; | ||
} | ||
if (!Nullness.hasNullableAnnotation(exprSymbol, config)) { | ||
exprMayBeNull = false; | ||
return false; | ||
} | ||
exprMayBeNull = handler.onOverrideMayBeNullExpr(this, expr, exprSymbol, state, exprMayBeNull); | ||
return exprMayBeNull ? nullnessFromDataflow(state, expr) : false; | ||
return Nullness.hasNullableAnnotation(exprSymbol, config); | ||
} | ||
|
||
public boolean nullnessFromDataflow(VisitorState state, ExpressionTree expr) { | ||
|
@@ -2336,15 +2331,6 @@ public AccessPathNullnessAnalysis getNullnessAnalysis(VisitorState state) { | |
return AccessPathNullnessAnalysis.instance(state, nonAnnotatedMethod, config, this.handler); | ||
} | ||
|
||
private boolean mayBeNullFieldAccess(VisitorState state, ExpressionTree expr, Symbol exprSymbol) { | ||
boolean exprMayBeNull = true; | ||
if (!NullabilityUtil.mayBeNullFieldFromType(exprSymbol, config, codeAnnotationInfo)) { | ||
exprMayBeNull = false; | ||
} | ||
exprMayBeNull = handler.onOverrideMayBeNullExpr(this, expr, exprSymbol, state, exprMayBeNull); | ||
return exprMayBeNull ? nullnessFromDataflow(state, expr) : false; | ||
} | ||
|
||
private Description matchDereference( | ||
ExpressionTree baseExpression, ExpressionTree derefExpression, VisitorState state) { | ||
Symbol baseExpressionSymbol = ASTHelpers.getSymbol(baseExpression); | ||
|
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.
Add a comment here saying we rely on inference / dataflow analysis for local variables
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, please check if it is as you had mind.
also i tried putting answers to the questions in the PR description, wdyt?
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.
LGTM. Will take another look later in the day and then likely land this