-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Rust: Adopt shared flow summaries library #18130
Conversation
1000 + i | ||
} | ||
|
||
fn sink(s: i64) { |
Check notice
Code scanning / CodeQL
Unused variable Note test
} | ||
|
||
// has a flow model | ||
fn identity(i: i64) -> i64 { |
Check notice
Code scanning / CodeQL
Unused variable Note test
} | ||
|
||
// has a flow model | ||
fn get_var_pos(e: MyPosEnum) -> i64 { |
Check notice
Code scanning / CodeQL
Unused variable Note test
} | ||
|
||
// has a flow model | ||
fn set_var_pos(i: i64) -> MyPosEnum { |
Check notice
Code scanning / CodeQL
Unused variable Note test
} | ||
|
||
// has a flow model | ||
fn get_var_field(e: MyFieldEnum) -> i64 { |
Check notice
Code scanning / CodeQL
Unused variable Note test
} | ||
|
||
// has a flow model | ||
fn set_var_field(i: i64) -> MyFieldEnum { |
Check notice
Code scanning / CodeQL
Unused variable Note test
result.asLibraryCallable() = this.getSummarizedCallable() | ||
} | ||
|
||
override EmptyLocation getLocation() { any() } |
Check warning
Code scanning / CodeQL
Override with unmentioned parameter Warning
result
( | ||
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) | ||
or | ||
exists(SsaImpl::DefinitionExt def, boolean isUseStep | |
Check warning
Code scanning / CodeQL
Omittable 'exists' variable Warning
in this argument
5812246
to
93367bb
Compare
06355a0
to
3bb1ae0
Compare
7c47ade
to
e6e4f92
Compare
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.
Looks really great. Super nice to get flow through unwrap
.
I've left a few nitpick comments.
@@ -490,6 +638,13 @@ private module Aliases { | |||
class ContentSetAlias = ContentSet; | |||
} | |||
|
|||
pragma[nomagic] | |||
Resolvable getCallResolvable(CallExprBase call) { |
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.
This duplicates the private getCallResolvable
on CallExprBase
. I think we should make that public or remove the duplication some other way.
private module Summaries { | ||
private import codeql.rust.Frameworks | ||
|
||
// TODO: Used models-as-data when it's available |
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.
// TODO: Used models-as-data when it's available | |
// TODO: Use models-as-data when it's available |
exists(SsaImpl::DefinitionExt def, boolean isUseStep | | ||
SsaFlow::localFlowStep(def, nodeFrom, nodeTo, isUseStep) | ||
| | ||
isUseStep = false | ||
or | ||
isUseStep = true and | ||
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _) | ||
) |
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.
A bit shorter:
exists(SsaImpl::DefinitionExt def, boolean isUseStep | | |
SsaFlow::localFlowStep(def, nodeFrom, nodeTo, isUseStep) | |
| | |
isUseStep = false | |
or | |
isUseStep = true and | |
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _) | |
) | |
exists(boolean isUseStep | SsaFlow::localFlowStep(_, nodeFrom, nodeTo, isUseStep) | | |
isUseStep = true | |
implies | |
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _) | |
) |
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.
While it preserves semantics, it will introduce unnecessary tuple duplication. a implies b
is syntactic sugar for not a or b
, so the rewrite becomes
SsaFlow::localFlowStep(def, nodeFrom, nodeTo, isUseStep) and
not (isUseStep = true)
or
SsaFlow::localFlowStep(def, nodeFrom, nodeTo, isUseStep) and
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _)
Note how isUseStep = true
is not part of the last disjunct.
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.
This is a bit hard for me to wrap my head around. If isUseStep = true
is not the case then the first disjunct will always hold, so I would've thought that adding it to the last disjunct wouldn't make a difference. But I guess that's only the "preserves semantics" part.
Is it something along the lines of: For a disjunction P or Q
all the tuples satisfying P
will be created, then all tuples satisfying Q
will be created, and only then will they be joined together (and de-duplicated). With my rewrite Q
could be a larger stream of tuples overlapping with P
, and hence the result will be the same but more de-duplication is going on?
I guess turning def
into _
is still applicable.
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 it something along the lines of: For a disjunction P or Q all the tuples satisfying P will be created, then all tuples satisfying Q will be created, and only then will they be joined together (and de-duplicated). With my rewrite Q could be a larger stream of tuples overlapping with P, and hence the result will be the same but more de-duplication is going on?
Exactly.
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.
I oversaw the _
part; that is certainly valid, let's do it follow-up.
@@ -126,6 +126,11 @@ fn option_pattern_match_unqualified() { | |||
} | |||
} | |||
|
|||
fn option_unwrap() { | |||
let s1 = Some(source(19)); | |||
sink(s1.unwrap()); // $ hasValueFlow=19 |
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.
Nice! 😎
final class ReturnNode extends ExprNode { | ||
ReturnNode() { this.getCfgNode().getASuccessor() instanceof AnnotatedExitCfgNode } | ||
abstract class ReturnNode extends Node { | ||
abstract ReturnKind getKind(); |
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.
Do we think we'll get additional return kinds for Rust? If not, could we leave this as it was before and remove kind
in getCall
, etc?
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.
Who knows what Future Rust brings :-) If you prefer, I can change it back.
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.
:D My preference would be to await
the Rust future and just keep it simple for now without carrying around the return kind, but it obviously doesn't matter much.
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.
Actually, given that all the other comments are addressed I think that we should just move forward and merge instead of changing this back.
result = pos.getPosition().toString() | ||
or | ||
pos.isSelf() and | ||
result = "self" |
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.
This is the same as the toString
implementation on ParameterPosition
.
result = pos.getPosition().toString() | |
or | |
pos.isSelf() and | |
result = "self" | |
result = pos.toString() |
@@ -148,6 +173,26 @@ module Node { | |||
override Location getLocation() { none() } | |||
} | |||
|
|||
/** A data-flow node used to model flow summaries. */ |
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.
/** A data-flow node used to model flow summaries. */ | |
/** A data flow node used to model flow summaries. */ |
override CfgScope getCfgScope() { result = PostUpdateNode.super.getCfgScope() } | ||
|
||
final override Location getLocation() { result = n.getLocation() } | ||
override EmptyLocation getLocation() { result = PostUpdateNode.super.getLocation() } |
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.
I think the overloading is a bit convoluted here. PostUpdateNode.super.getLocation
ends up calling getLocation
on the pre
node which is a FlowSummaryNode
whose getLocation
returns and empty location. Why not just call FlowSummaryNode.super.getLocation
directly? And in that case, we don't need the getCfgScope
and getLocation
inside PostUpdateNode
. These two can be moved to ArgumentPostUpdateNode
and then these two overrides can just be deleted.
e6e4f92
to
52dc79e
Compare
Introduces the class
SummarizedCallable
for defining flow summaries in QL. Models-as-data will be added follow-up.