Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,8 @@ class AstCreator(
fileContent.foreach(fileNode.content(_))
val namespaceBlock = globalNamespaceBlock()
methodAstParentStack.push(namespaceBlock)
val astForFakeMethod =
astInFakeMethod(namespaceBlock.fullName, parserResult.filename, parserResult.ast)
val ast = Ast(fileNode).withChild(Ast(namespaceBlock).withChild(astForFakeMethod))
val astForFakeMethod = astInFakeMethod(namespaceBlock.fullName, parserResult.filename, parserResult.ast)
val ast = Ast(fileNode).withChild(Ast(namespaceBlock).withChild(astForFakeMethod))
Ast.storeInDiffGraph(ast, diffGraph)
scope.createVariableReferenceLinks(diffGraph, parserResult.filename)
diffGraph
Expand Down Expand Up @@ -125,4 +124,5 @@ class AstCreator(
case _ => shortenCode(new String(code)).stripLineEnd
}
}

}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import io.joern.x2cpg.datastructures.VariableScopeManager
import io.joern.x2cpg.frontendspecific.swiftsrc2cpg.Defines
import io.joern.x2cpg.{Ast, ValidationMode}
import io.shiftleft.codepropertygraph.generated.*
import io.shiftleft.codepropertygraph.generated.nodes.{ExpressionNew, NewCall}
import io.shiftleft.codepropertygraph.generated.nodes.{ExpressionNew, NewCall, NewControlStructure}

import scala.annotation.{tailrec, unused}

Expand Down Expand Up @@ -498,15 +498,114 @@ trait AstForExprSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
}

private def astForIfExprSyntax(node: IfExprSyntax): Ast = {
val code = this.code(node)
val ifNode = controlStructureNode(node, ControlStructureTypes.IF, code)
val conditionAstRaw = astForNode(node.conditions)
val conditionAst = conditionAstRaw.root match {
case Some(_) => conditionAstRaw
case None => blockAst(blockNode(node.conditions), List.empty)
}
val thenAst = astForNode(node.body)
val elseAst = node.elseBody.map(astForNode)
val code = this.code(node)
val ifNode = controlStructureNode(node, ControlStructureTypes.IF, code)

handleOptionalBindingConditions(
node.conditions.children,
onAllSimple = simpleBindings => astForIfLetExprSyntax(node, ifNode, simpleBindings, node.body, node.elseBody),
onMixed = (simpleBindings, tupleBindings) =>
astForIfLetExprSyntaxMixed(node, ifNode, simpleBindings, tupleBindings, node.body, node.elseBody),
onPartial = (simpleBindings, tupleBindings, otherConditions) =>
astForIfLetExprSyntaxPartial(
node,
ifNode,
simpleBindings,
tupleBindings,
otherConditions,
node.body,
node.elseBody
),
onStandard = () => {
val conditionAst = astForNode(node.conditions)
val thenAst = astForNode(node.body)
val elseAst = node.elseBody.map(astForNode)
ifThenElseAst(ifNode, Option(conditionAst), thenAst, elseAst)
}
)
}

/** Handles Swift optional binding (if-let) constructs.
*
* De-sugars `if let baz = foo() { body }` into:
*
* Condition: { (<tmp>0 = foo()) != nil }
*
* Then block: { let baz = <tmp>0; body }
*
* For multiple bindings `if let a = foo(), let b = bar() { body }`:
*
* Condition: { (<tmp>0 = foo()) != nil && (<tmp>1 = bar()) != nil }
*
* Then block: { a = <tmp>0; b = <tmp>1; body }
Comment thread
max-leuthaeuser marked this conversation as resolved.
*
* For mixed cases with/without initializers `if let a = foo(), let b { body }`:
*
* Condition: { (<tmp>0 = foo()) != nil && b != nil }
*
* Then block: { a = <tmp>0; body }
*/
private def astForIfLetExprSyntax(
node: IfExprSyntax,
ifNode: NewControlStructure,
optionalBindings: Seq[OptionalBindingConditionSyntax],
thenBody: CodeBlockSyntax,
elseBody: Option[IfExprSyntax | CodeBlockSyntax]
): Ast = {
val bindingInfos = collectBindingInfos(optionalBindings)
val conditionAst = buildOptionalBindingCondition(node, bindingInfos)
val thenAst = buildBodyWithUnwrapping(thenBody, thenBody.statements.children, bindingInfos)
val elseAst = elseBody.map(astForNode)

ifThenElseAst(ifNode, Option(conditionAst), thenAst, elseAst)
}

/** Handles mixed optional binding constructs with both simple and tuple patterns.
*
* De-sugars `if let a = foo(), let (b, c) = bar() { body }` into:
*
* Condition: { <tmp>0 = foo(); <tmp>0 != nil }
*
* Then block: { let a = <tmp>0; let (b, c) = bar(); body }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how is the tuple assignment desugared?

Copy link
Copy Markdown
Contributor Author

@max-leuthaeuser max-leuthaeuser May 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

*/
private def astForIfLetExprSyntaxMixed(
node: IfExprSyntax,
ifNode: NewControlStructure,
simpleBindings: Seq[OptionalBindingConditionSyntax],
tupleBindings: Seq[OptionalBindingConditionSyntax],
thenBody: CodeBlockSyntax,
elseBody: Option[IfExprSyntax | CodeBlockSyntax]
): Ast = {
val bindingInfos = collectBindingInfos(simpleBindings)
val conditionAst = buildOptionalBindingCondition(node, bindingInfos)
val thenAst = buildBodyWithUnwrapping(thenBody, tupleBindings ++ thenBody.statements.children, bindingInfos)
val elseAst = elseBody.map(astForNode)

ifThenElseAst(ifNode, Option(conditionAst), thenAst, elseAst)
}

/** Handles partial optional binding desugaring with other conditions.
*
* De-sugars `if let a = foo(), #unavailable(...) { body }` into:
*
* Condition: { <tmp>0 = foo(); <tmp>0 != nil && #unavailable(...) }
*
* Then block: { let a = <tmp>0; body }
*/
private def astForIfLetExprSyntaxPartial(
node: IfExprSyntax,
ifNode: NewControlStructure,
simpleBindings: Seq[OptionalBindingConditionSyntax],
tupleBindings: Seq[OptionalBindingConditionSyntax],
otherConditions: Seq[ConditionElementSyntax],
thenBody: CodeBlockSyntax,
elseBody: Option[IfExprSyntax | CodeBlockSyntax]
): Ast = {
val bindingInfos = collectBindingInfos(simpleBindings)
val conditionAst = buildOptionalBindingCondition(node, bindingInfos, otherConditions)
val thenAst = buildBodyWithUnwrapping(thenBody, tupleBindings ++ thenBody.statements.children, bindingInfos)
val elseAst = elseBody.map(astForNode)

ifThenElseAst(ifNode, Option(conditionAst), thenAst, elseAst)
}

Expand Down Expand Up @@ -723,7 +822,7 @@ trait AstForExprSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
* identifier/field-identifier nodes so the resulting AST can be safely used as an argument without node-sharing
* issues.
*/
protected def createFieldAccessChain(baseName: String, fields: List[String], node: SwiftNode): Ast = {
private def createFieldAccessChain(baseName: String, fields: List[String], node: SwiftNode): Ast = {
val baseAst = Ast(identifierNode(node, baseName))
fields.foldLeft(baseAst) { (accAst, field) =>
createFieldAccessCallAst(node, accAst, fieldIdentifierNode(node, field, field))
Expand Down Expand Up @@ -826,7 +925,7 @@ trait AstForExprSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
*/

/** Creates an instanceOf check for an IsTypePatternSyntax against a subject field access. */
protected def astForIsTypePatternInTupleContext(
private def astForIsTypePatternInTupleContext(
isType: IsTypePatternSyntax,
subjectAst: Ast,
subjectCode: String,
Expand All @@ -843,7 +942,7 @@ trait AstForExprSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
}

/** Creates an equality check for an expression pattern against a subject field access. */
protected def astForExpressionPatternInTupleContext(
private def astForExpressionPatternInTupleContext(
ep: ExpressionPatternSyntax,
subjectAst: Ast,
subjectCode: String,
Expand All @@ -856,7 +955,7 @@ trait AstForExprSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
}

/** Creates a variable binding assignment for a pattern element against a subject field access. */
protected def astForBindingInTupleContext(
private def astForBindingInTupleContext(
varName: String,
subjectAst: Ast,
subjectCode: String,
Expand Down Expand Up @@ -903,7 +1002,7 @@ trait AstForExprSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
}

/** Determines whether an expression inside a tuple represents a binding (let/var pattern). */
protected def isBindingExpression(expr: ExprSyntax): Boolean = expr match {
private def isBindingExpression(expr: ExprSyntax): Boolean = expr match {
case p: PatternExprSyntax =>
p.pattern match {
case _: ValueBindingPatternSyntax => true
Expand All @@ -914,7 +1013,7 @@ trait AstForExprSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
}

/** Dispatches a PatternSyntax inside a tuple context to the appropriate de-sugaring. */
protected def astsForPatternInTupleContext(
private def astsForPatternInTupleContext(
pattern: PatternSyntax,
subjectAst: Ast,
subjectCode: String,
Expand Down Expand Up @@ -967,7 +1066,7 @@ trait AstForExprSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
* - `DeclReferenceExprSyntax` (`a` in `case let (a, b):`)
* - `PatternExprSyntax(ValueBindingPatternSyntax(IdentifierPatternSyntax))` (`var a` in `case (var a, var b):`)
*/
protected def extractBindingName(expr: ExprSyntax): String = {
private def extractBindingName(expr: ExprSyntax): String = {
expr match {
case d: DeclReferenceExprSyntax => code(d)
case p: PatternExprSyntax =>
Expand Down Expand Up @@ -1056,7 +1155,6 @@ trait AstForExprSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
val condIdentNode = identifierNode(node, subjectTmpName, subjectTmpName, Defines.Tuple)
scope.addVariableReference(subjectTmpName, condIdentNode, Defines.Tuple, EvaluationStrategies.BY_REFERENCE)
val condAst = Ast(condIdentNode)
setOrderExplicitly(condAst, 1)

val switchBlockNode = blockNode(node).order(2)
scope.pushNewBlockScope(switchBlockNode)
Expand All @@ -1073,15 +1171,10 @@ trait AstForExprSyntaxCreator(implicit withSchemaValidation: ValidationMode) {

blockAst(outerBlockNode, List(subjectAssignAst, switchAstResult))
} else {
// The semantics of switch statement children is partially defined by their order value.
// The blockAst must have order == 2. Only to avoid collision we set switchExpressionAst to 1
// because the semantics of it is already indicated via the condition edge.
val switchNode = controlStructureNode(node, ControlStructureTypes.SWITCH, code(node))

val switchNode = controlStructureNode(node, ControlStructureTypes.SWITCH, code(node))
val switchExpressionAst = astForNode(node.subject)
setOrderExplicitly(switchExpressionAst, 1)

val blockNode_ = blockNode(node).order(2)
val blockNode_ = blockNode(node)
scope.pushNewBlockScope(blockNode_)
localAstParentStack.push(blockNode_)
val casesAsts = cases.flatMap(astsForSwitchCase(_, None))
Expand Down
Loading
Loading