Skip to content

Commit 5fa86c5

Browse files
authored
[rust2cpg] lower match guards. (#6223)
1 parent fceed89 commit 5fa86c5

2 files changed

Lines changed: 55 additions & 3 deletions

File tree

joern-cli/frontends/rust2cpg/src/main/scala/io/joern/rust2cpg/astcreation/RustVisitor.scala

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1775,15 +1775,24 @@ trait RustVisitor(implicit withSchemaValidation: ValidationMode) { this: AstCrea
17751775
Ast(blockNode(matchExpr)).withChildren(Seq(tmpLocalAst, tmpAssignAst, matchExprAst))
17761776
}
17771777

1778-
// TODO: handle guards.
17791778
private def lowerMatchArm(matchArm: MatchArm, tmpIdentAst: () => Ast): Seq[Ast] = {
17801779
val bindingAsts = createLocalsForBindings(collectPatternBindings(matchArm.pat)) ++ createAssignmentsForPattern(
17811780
matchArm.pat,
17821781
tmpIdentAst
17831782
)
1784-
val bodyAst = visitExpr(matchArm.expr)
1783+
val bodyAst = matchArm.matchGuard match {
1784+
case Some(matchGuard) =>
1785+
val conditionAst = visitExpr(matchGuard.expr)
1786+
ifThenElseAst(matchGuard, Some(conditionAst), visitExpr(matchArm.expr), None)
1787+
case None =>
1788+
visitExpr(matchArm.expr)
1789+
}
1790+
val caseCode = matchArm.matchGuard match {
1791+
case Some(matchGuard) => s"${code(matchArm.pat)} ${code(matchGuard)}"
1792+
case None => code(matchArm.pat)
1793+
}
17851794
val matchArmBlock = blockAst(blockNode(matchArm), (bindingAsts :+ bodyAst).toList)
1786-
val jumpTargetAst = Ast(jumpTargetNode(matchArm.pat, s"case ${code(matchArm.pat)}", code(matchArm.pat)))
1795+
val jumpTargetAst = Ast(jumpTargetNode(matchArm.pat, s"case $caseCode", caseCode))
17871796
Seq(jumpTargetAst, matchArmBlock)
17881797
}
17891798

joern-cli/frontends/rust2cpg/src/test/scala/io/joern/rust2cpg/passes/ast/MatchTests.scala

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,49 @@ class MatchTests extends Rust2CpgSuite(noSysRoot = true) {
112112
}
113113
}
114114

115+
"match with guards" should {
116+
val cpg = code("""
117+
|fn foo(x: (i32, i32)) {
118+
| match x {
119+
| (n, _) if n > 3 => bar(n),
120+
| _ if baz() => qux(),
121+
| _ => 0,
122+
| };
123+
|}
124+
|""".stripMargin)
125+
126+
"have correct jump target names" in {
127+
cpg.jumpTarget.sortBy(_.order).name.l shouldBe List("case (n, _) if n > 3", "case _ if baz()", "case _")
128+
}
129+
130+
"have correct locals" in {
131+
inside(cpg.local.sortBy(_.order).l) { case tmp :: nLocal :: Nil =>
132+
tmp.name shouldBe "<tmp>0"
133+
tmp.typeFullName shouldBe "(i32, i32)"
134+
nLocal.name shouldBe "n"
135+
nLocal.typeFullName shouldBe "i32"
136+
}
137+
}
138+
139+
"have correct local assignments" in {
140+
cpg.method.nameExact("foo").block.assignment.sortBy(_.order).code.l shouldBe List("<tmp>0 = x", "n = <tmp>0.0")
141+
}
142+
143+
"have correct if control structures" in {
144+
inside(cpg.ifBlock.sortBy(_.lineNumber).l) { case guard1 :: guard2 :: Nil =>
145+
guard1.code shouldBe "if n > 3"
146+
guard1.condition.code.l shouldBe List("n > 3")
147+
guard1.whenTrue.code.l shouldBe List("bar(n)")
148+
guard1.whenFalse shouldBe empty
149+
150+
guard2.code shouldBe "if baz()"
151+
guard2.condition.code.l shouldBe List("baz()")
152+
guard2.whenTrue.code.l shouldBe List("qux()")
153+
guard2.whenFalse shouldBe empty
154+
}
155+
}
156+
}
157+
115158
"match with an or-pattern case" should {
116159
val cpg = code("""
117160
|struct Point { x: i32, y: i32 }

0 commit comments

Comments
 (0)