[swiftsrc2cpg] Implement desugaring for optional binding syntax#6006
[swiftsrc2cpg] Implement desugaring for optional binding syntax#6006max-leuthaeuser wants to merge 5 commits into
Conversation
fb6adcf to
1bfc3f1
Compare
1bfc3f1 to
d8c72f5
Compare
| * | ||
| * Condition: { <tmp>0 = foo(); <tmp>0 != nil } | ||
| * | ||
| * Then block: { let a = <tmp>0; let (b, c) = bar(); body } |
There was a problem hiding this comment.
how is the tuple assignment desugared?
There was a problem hiding this comment.
This is not done with full coverage there. That needs an additional PR.
There is special tuple de-sugaring, but only in condition/pattern-matching contexts like if case, guard case, and switch, where tuple subjects get de-sugared into tmp variables plus .0, .1, etc. accesses. That de-sugaring is in astsForBindingTuplePattern / astsForBindingTupleExpr.
For optional tuple pattern bindings here we can reuse it mostly (store in tmp and do the nil comparison; locals and .0, .1, etc. accesses in then block).
f5e2872 to
0a65640
Compare
maltek
left a comment
There was a problem hiding this comment.
I'm not happy with some of the tests. They are so fuzzy. E.g. AvailabilityQueryTests is using contains and not be empty all over the place instead of complete matches that'd tell us exactly what's there and what isn't there. And some of the queries in there are also structured similarly imprecise - e.g. we know that nilCheck has an assignment argument, but we don't know if it has any other arguments.
(It's not just this test though, some of the others are similar.)
|
Ok, I'll overhaul them (tbh they were bad already before this PR). |
Are we talking about the same file? I can not find any "not be empty" assertion there.
|
| val List(condition) = guardIf.condition.l | ||
| condition.code shouldBe "i % 2 == 0" | ||
|
|
||
| val List(thenPrint, thenAssign) = guardIf.whenTrue.astChildren.isCall.l |
There was a problem hiding this comment.
generally, I think it's better to write such tests like this instead:
val List(thenPrint: Call, thenAssign: Call) = guardIf.whenTrue.astChildren.lThat way, any potential children which are not calls aren't swept under the rug.
(Depending on the warning levels of the project, inside might be required.)
There was a problem hiding this comment.
Yeah, warning level do not allow this implicit type narrowing.
Rewriting everything to inside matchers is something thats needs to be addressed separately as the current test style with .isFoo traversals is scattered across the whole test base here.
Add support for optional binding constructs in if/while/guard statements:
If-let and While-let:
if let a = foo() { ... }if let a { ... }(checks existing variable)if let a = foo(), let b = bar() { ... }if let a = foo(), let b { ... }if let (a, b) = foo() { ... }if let a = foo(), let (b, c) = bar() { ... }while let item = iterator.next() { ... }Guard-let:
guard let a = foo() else { exit }guard let a else { exit }(checks existing variable)guard let a = foo(), let b = bar() else { exit }guard let a = foo(), let b else { exit }guard let a = foo(), flag else { exit }Desugaring strategy:
astsForBlockElements(AstCreatorHelper.scala) because guard's then block includes all subsequent statements in the function, not just an explicit blockDesugaring examples:
if let a = foo() { body }desugars to:if let a = foo(), let b = bar() { body }desugars to:This ensures proper short-circuit evaluation:
bar()is only called iffoo()returns non-nil.if let a { body }desugars to:guard let a = foo() else { exit }desugars to:guard let a = foo(), let b else { exit }desugars to:This does no type "unwrapping" (i.e., Optional[T] -> T).