Skip to content

Rust: Improve CFG #17498

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

Merged
merged 28 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
67a06cb
Rust: Support loop in CFG
paldepind Sep 12, 2024
3dc517c
Rust: Handle absence of else branch in if expression in CFG
paldepind Sep 12, 2024
e1f2fa8
Rust: Support break and continue in loops
paldepind Sep 12, 2024
c821ec2
Rust: CFG edge for return in functions
paldepind Sep 12, 2024
f73680b
Rust: Handle short-circuiting logical binary operators
paldepind Sep 12, 2024
b979df6
Rust: Handle functions correctly through scope in CFG
paldepind Sep 13, 2024
1a85dfd
Rust: Loops propagate CFG return completions but captures continue an…
paldepind Sep 13, 2024
61aad2e
Rust: Sort CFG trees and add scope for closures
paldepind Sep 13, 2024
9061536
Rust: Make logical operator pre order nodes and eliminate impossible …
paldepind Sep 13, 2024
afa4e79
Rust: Add support for more AST nodes to CFG contruction
paldepind Sep 13, 2024
c62c397
Merge branch 'main' into rust-improve-cfg
paldepind Sep 16, 2024
04aa7b4
Rust: Add support in CFG for various simple AST nodes
paldepind Sep 16, 2024
a935bde
Rust: CFG for match expressions
paldepind Sep 16, 2024
20e9687
Rust: Handle let statements with pattern and else branch in CFG
paldepind Sep 17, 2024
22edece
Rust: Add CFG construction for `if let` expressions
paldepind Sep 17, 2024
581d0c5
Rust: Handle more AST nodes in the CFG
paldepind Sep 17, 2024
6e868c2
Rust: CFG edges for `break` and `continue` with labels
paldepind Sep 17, 2024
73a430b
Merge branch 'main' into rust-improve-cfg
paldepind Sep 17, 2024
7a369f8
Rust: Update CFG test and expected output
paldepind Sep 17, 2024
6a5a505
Rust: Address QL suggestions for CFG implementation
paldepind Sep 18, 2024
c18c35d
Merge branch 'main' into rust-improve-cfg
paldepind Sep 18, 2024
dd25b3e
Rust: Don't use macro in test and add documentation string
paldepind Sep 18, 2024
db351bd
Rust: Align test output with CI
paldepind Sep 18, 2024
6f555f3
Merge branch 'main' into rust-improve-cfg
paldepind Sep 18, 2024
bbf5902
Rust: Tweak imports
paldepind Sep 18, 2024
2511986
Rust: Address PR review comments
paldepind Sep 19, 2024
db9f5fd
Rust: Handle nested if expressions, address review comments
paldepind Sep 19, 2024
19697b9
Merge branch 'main' into rust-improve-cfg
paldepind Sep 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 97 additions & 1 deletion rust/ql/lib/codeql/rust/controlflow/internal/Completion.qll
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ private import SuccessorType
private newtype TCompletion =
TSimpleCompletion() or
TBooleanCompletion(Boolean b) or
TMatchCompletion(Boolean isMatch) or
TLoopCompletion(TLoopJumpType kind, TLabelType label) {
label = TNoLabel()
or
kind = TBreakJump() and label = TLabel(any(BreakExpr b).getLifetime().getText())
or
kind = TContinueJump() and label = TLabel(any(ContinueExpr b).getLifetime().getText())
} or
TReturnCompletion()

/** A completion of a statement or an expression. */
Expand Down Expand Up @@ -51,6 +59,10 @@ abstract class ConditionalCompletion extends NormalCompletion {
/** Gets the Boolean value of this conditional completion. */
final boolean getValue() { result = value }

final predicate succeeded() { value = true }

final predicate failed() { value = false }

/** Gets the dual completion. */
abstract ConditionalCompletion getDual();
}
Expand All @@ -62,7 +74,34 @@ abstract class ConditionalCompletion extends NormalCompletion {
class BooleanCompletion extends ConditionalCompletion, TBooleanCompletion {
BooleanCompletion() { this = TBooleanCompletion(value) }

override predicate isValidForSpecific(AstNode e) { e = any(IfExpr c).getCondition() }
override predicate isValidForSpecific(AstNode e) {
e = any(IfExpr c).getCondition()
or
any(MatchArm arm).getGuard() = e
or
exists(BinaryExpr expr |
expr.getOperatorName() = ["&&", "||"] and
e = expr.getLhs()
)
or
exists(Expr parent | this.isValidForSpecific(parent) |
parent =
any(PrefixExpr expr |
expr.getOperatorName() = "!" and
e = expr.getExpr()
)
or
parent =
any(BinaryExpr expr |
expr.getOperatorName() = ["&&", "||"] and
e = expr.getRhs()
)
or
parent = any(IfExpr ie | e = [ie.getThen(), ie.getElse()])
or
parent = any(BlockExpr be | e = be.getStmtList().getTailExpr())
)
}

/** Gets the dual Boolean completion. */
override BooleanCompletion getDual() { result = TBooleanCompletion(value.booleanNot()) }
Expand All @@ -72,6 +111,63 @@ class BooleanCompletion extends ConditionalCompletion, TBooleanCompletion {
override string toString() { result = "boolean(" + value + ")" }
}

/**
* A completion that represents the result of a pattern match.
*/
class MatchCompletion extends TMatchCompletion, ConditionalCompletion {
MatchCompletion() { this = TMatchCompletion(value) }

override predicate isValidForSpecific(AstNode e) { e instanceof Pat }

override MatchSuccessor getAMatchingSuccessorType() { result.getValue() = value }

/** Gets the dual match completion. */
override MatchCompletion getDual() { result = TMatchCompletion(value.booleanNot()) }

override string toString() { result = "match(" + value + ")" }
}

/**
* A completion that represents a break or a continue.
*/
class LoopJumpCompletion extends TLoopCompletion, Completion {
override LoopJumpSuccessor getAMatchingSuccessorType() {
result = TLoopSuccessor(this.getKind(), this.getLabelType())
}

final TLoopJumpType getKind() { this = TLoopCompletion(result, _) }

final TLabelType getLabelType() { this = TLoopCompletion(_, result) }

final predicate hasLabel() { this.getLabelType() = TLabel(_) }

final string getLabelName() { TLabel(result) = this.getLabelType() }

final predicate isContinue() { this.getKind() = TContinueJump() }

final predicate isBreak() { this.getKind() = TBreakJump() }

override predicate isValidForSpecific(AstNode e) {
this.isBreak() and
e instanceof BreakExpr and
(
not e.(BreakExpr).hasLifetime() and not this.hasLabel()
or
e.(BreakExpr).getLifetime().getText() = this.getLabelName()
)
or
this.isContinue() and
e instanceof ContinueExpr and
(
not e.(ContinueExpr).hasLifetime() and not this.hasLabel()
or
e.(ContinueExpr).getLifetime().getText() = this.getLabelName()
)
}

override string toString() { result = this.getAMatchingSuccessorType().toString() }
}

/**
* A completion that represents a return.
*/
Expand Down
Loading
Loading