Skip to content

Commit b88715b

Browse files
fix for review comment
1 parent 863ecd2 commit b88715b

File tree

7 files changed

+108
-31
lines changed

7 files changed

+108
-31
lines changed

joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstCreatorHelper.scala

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -164,22 +164,42 @@ trait AstCreatorHelper(implicit withSchemaValidation: ValidationMode) { this: As
164164

165165
protected def astForIdentifier(node: SwiftNode): Ast = {
166166
val identifierName = code(node)
167-
dynamicInstanceTypeStack.headOption match {
168-
case Some(InstanceTypeStackElement(tpe, members)) if members.contains(identifierName) =>
169-
val selfNode = identifierNode(node, "self", "self", tpe)
170-
scope.addVariableReference("self", selfNode, selfNode.typeFullName, EvaluationStrategies.BY_REFERENCE)
171-
fieldAccessAst(node, node, Ast(selfNode), s"self.$identifierName", identifierName, tpe)
172-
case _ =>
173-
val identNode = identifierNode(node, identifierName)
174-
val variableOption = scope.lookupVariable(identifierName)
175-
val tpe = variableOption match {
176-
case Some((_, variableTypeName)) if variableTypeName != Defines.Any => variableTypeName
177-
case None if identNode.typeFullName != Defines.Any => identNode.typeFullName
178-
case _ => Defines.Any
179-
}
180-
identNode.typeFullName = tpe
181-
scope.addVariableReference(identifierName, identNode, tpe, EvaluationStrategies.BY_REFERENCE)
182-
Ast(identNode)
167+
// scoping rules for self access:
168+
// 1) if identifier is in local method scope it shadows everything else:
169+
if (scope.variableIsInMethodScope(identifierName)) {
170+
val identNode = identifierNode(node, identifierName)
171+
val variableOption = scope.lookupVariable(identifierName)
172+
val tpe = variableOption match {
173+
case Some((_, variableTypeName)) if variableTypeName != Defines.Any => variableTypeName
174+
case None if identNode.typeFullName != Defines.Any => identNode.typeFullName
175+
case _ => Defines.Any
176+
}
177+
identNode.typeFullName = tpe
178+
scope.addVariableReference(identifierName, identNode, tpe, EvaluationStrategies.BY_REFERENCE)
179+
Ast(identNode)
180+
} else if (
181+
dynamicInstanceTypeStack.headOption.exists { case InstanceTypeStackElement(tpe, members) =>
182+
members.contains(identifierName)
183+
}
184+
) {
185+
// 2) we found it as member of the surrounding type decl:
186+
// (Swift does not allow to access any member / function of the outer class instance)
187+
val tpe = dynamicInstanceTypeStack.head.name
188+
val selfNode = identifierNode(node, "self", "self", tpe)
189+
scope.addVariableReference("self", selfNode, selfNode.typeFullName, EvaluationStrategies.BY_REFERENCE)
190+
fieldAccessAst(node, node, Ast(selfNode), s"self.$identifierName", identifierName, tpe)
191+
} else {
192+
// 3) default handling, it must come from the outer scope somewhere
193+
val identNode = identifierNode(node, identifierName)
194+
val variableOption = scope.lookupVariable(identifierName)
195+
val tpe = variableOption match {
196+
case Some((_, variableTypeName)) if variableTypeName != Defines.Any => variableTypeName
197+
case None if identNode.typeFullName != Defines.Any => identNode.typeFullName
198+
case _ => Defines.Any
199+
}
200+
identNode.typeFullName = tpe
201+
scope.addVariableReference(identifierName, identNode, tpe, EvaluationStrategies.BY_REFERENCE)
202+
Ast(identNode)
183203
}
184204
}
185205

joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstForExprSyntaxCreator.scala

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,9 @@ trait AstForExprSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
211211
val args = callExpr.arguments.children.map(astForNode) ++ trailingClosureAsts ++ additionalTrailingClosuresAsts
212212

213213
val callExprCode = code(callExpr)
214-
val callCode = if (callExprCode.startsWith(".") || callExprCode.contains("#if ")) {
214+
val callCode = if (callExprCode.startsWith(".")) {
215+
s"${codeOf(baseAst.root.get)}$callExprCode"
216+
} else if (callExprCode.contains("#if ")) {
215217
s"${codeOf(baseAst.root.get)}(${code(callExpr.arguments)})"
216218
} else callExprCode
217219
val callNode_ = callNode(
@@ -237,7 +239,18 @@ trait AstForExprSyntaxCreator(implicit withSchemaValidation: ValidationMode) {
237239
// TODO: extend the GsonTypeInfoReader to query for information whether
238240
// the call is a call to a static function and generate a proper static call here
239241
callee match {
242+
case m: MemberAccessExprSyntax if m.base.isEmpty || code(m.base.get) == "self" =>
243+
// referencing implicit self
244+
val selfTpe = typeHintForSelfExpression().headOption.getOrElse(Defines.Any)
245+
val selfNode = identifierNode(node, "self", "self", selfTpe)
246+
scope.addVariableReference("self", selfNode, selfTpe, EvaluationStrategies.BY_REFERENCE)
247+
handleCallNodeArgs(node, Ast(selfNode), code(m.declName.baseName))
248+
case m: MemberAccessExprSyntax if m.base.exists(_.isInstanceOf[DeclReferenceExprSyntax]) =>
249+
// simple base
250+
val memberCode = code(m.declName)
251+
handleCallNodeArgs(node, astForNode(m.base.get), memberCode)
240252
case m: MemberAccessExprSyntax =>
253+
// call-chain / complex base
241254
val memberCode = code(m.declName)
242255
handleCallNodeArgs(node, astForMemberAccessExprSyntax(m), memberCode)
243256
case other if isRefToClosure(node, other) =>

joern-cli/frontends/swiftsrc2cpg/src/main/scala/io/joern/swiftsrc2cpg/astcreation/AstNodeBuilder.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ trait AstNodeBuilder(implicit withSchemaValidation: ValidationMode) { this: AstC
153153
t
154154
}
155155
.getOrElse(name match {
156-
case "this" | "self" | "Self" => typeHintForSelfExpression().headOption.getOrElse(Defines.Any)
157-
case _ => Defines.Any
156+
case "self" | "Self" => typeHintForSelfExpression().headOption.getOrElse(Defines.Any)
157+
case _ => Defines.Any
158158
})
159159
identifierNode(node, name, name, tpe)
160160
}

joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/AsyncTests.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ class AsyncTests extends AstSwiftSrc2CpgSuite {
112112
"let <wildcard>0 = await asyncGlobal1()",
113113
"let <wildcard>1 = myFuture.await()",
114114
"let myFuture = MyFuture()",
115-
"myFuture.await",
116115
"myFuture.await()"
117116
)
118117
}

joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/CallTests.scala

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class CallTests extends SwiftCompilerSrc2CpgSuite {
1818
| func main() {
1919
| foo()
2020
| self.bar()
21+
| other.method()
2122
| }
2223
|}
2324
|""".stripMargin
@@ -33,10 +34,16 @@ class CallTests extends SwiftCompilerSrc2CpgSuite {
3334
val List(barCall) = cpg.call.nameExact("bar").l
3435
barCall.methodFullName shouldBe x2cpg.Defines.DynamicCallUnknownFullName
3536
barCall.signature shouldBe ""
36-
val List(barCallReceiverCall) = barCall.receiver.fieldAccess.l
37-
barCallReceiverCall.argument(2).asInstanceOf[FieldIdentifier].canonicalName shouldBe "bar"
38-
barCallReceiverCall.argument(1).asInstanceOf[Identifier].name shouldBe "self"
39-
barCallReceiverCall.argument(1).asInstanceOf[Identifier].typeFullName shouldBe "Sources/main.swift:<global>.Foo"
37+
val List(barCallReceiverCall) = barCall.receiver.isIdentifier.l
38+
barCallReceiverCall.name shouldBe "self"
39+
barCallReceiverCall.typeFullName shouldBe "Sources/main.swift:<global>.Foo"
40+
41+
val List(methodCall) = cpg.call.nameExact("method").l
42+
methodCall.methodFullName shouldBe x2cpg.Defines.DynamicCallUnknownFullName
43+
methodCall.signature shouldBe ""
44+
val List(methodCallReceiverCall) = methodCall.receiver.isIdentifier.l
45+
methodCallReceiverCall.name shouldBe "other"
46+
methodCallReceiverCall.typeFullName shouldBe "ANY"
4047
}
4148

4249
"be correct for simple calls with compiler support" in {
@@ -63,11 +70,9 @@ class CallTests extends SwiftCompilerSrc2CpgSuite {
6370
val List(barCall) = cpg.call.nameExact("bar").l
6471
barCall.methodFullName shouldBe "SwiftTest.Foo.bar:()->Swift.String"
6572
barCall.signature shouldBe "()->Swift.String"
66-
val List(barCallReceiverCall) = barCall.receiver.fieldAccess.l
67-
barCallReceiverCall.typeFullName shouldBe "Swift.Function<()->Swift.String>"
68-
barCallReceiverCall.argument(2).asInstanceOf[FieldIdentifier].canonicalName shouldBe "bar"
69-
barCallReceiverCall.argument(1).asInstanceOf[Identifier].name shouldBe "self"
70-
barCallReceiverCall.argument(1).asInstanceOf[Identifier].typeFullName shouldBe "SwiftTest.Foo"
73+
val List(barCallReceiverCall) = barCall.receiver.isIdentifier.l
74+
barCallReceiverCall.name shouldBe "self"
75+
barCallReceiverCall.typeFullName shouldBe "SwiftTest.Foo"
7176
}
7277

7378
}

joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/DirectiveTests.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class DirectiveTests extends AstSwiftSrc2CpgSuite {
6161
|.baz()
6262
|#endif
6363
|""".stripMargin).withConfig(Config(Set("CONFIG1")))
64-
cpg.call.code.l shouldBe List("foo.bar()", "foo.bar")
64+
cpg.call.code.l shouldBe List("foo.bar()")
6565
}
6666

6767
"testConfigExpression7 (call behind define with trailing call)" in {
@@ -74,7 +74,7 @@ class DirectiveTests extends AstSwiftSrc2CpgSuite {
7474
|#endif
7575
|.oneMore(x: 1)
7676
|""".stripMargin).withConfig(Config(Set("CONFIG1")))
77-
cpg.call.code.l shouldBe List("foo.bar().oneMore(x: 1)", "foo.bar().oneMore", "foo.bar()", "foo.bar")
77+
cpg.call.code.l shouldBe List("foo.bar().oneMore(x: 1)", "foo.bar().oneMore", "foo.bar()")
7878
}
7979

8080
"testSourceLocation1" ignore {

joern-cli/frontends/swiftsrc2cpg/src/test/scala/io/joern/swiftsrc2cpg/passes/ast/SimpleAstCreationPassTest.scala

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,46 @@ class SimpleAstCreationPassTest extends AstSwiftSrc2CpgSuite {
236236
}
237237
}
238238

239+
"have correct structure for implicit self access with shadowing a member in class" in {
240+
val cpg = code("""
241+
|class Foo {
242+
| let f: String = "f"
243+
| func bar() {
244+
| let f: Int = 1
245+
| handleF(f)
246+
| }
247+
|}
248+
|""".stripMargin)
249+
cpg.typeDecl.nameExact("Foo").member.name.l shouldBe List("f")
250+
inside(cpg.call.nameExact("handleF").argument(1).l) { case List(id: Identifier) =>
251+
id.name shouldBe "f"
252+
id.typeFullName shouldBe Defines.Int
253+
val fLocal = id._localViaRefOut.get
254+
fLocal.name shouldBe "f"
255+
fLocal.typeFullName shouldBe Defines.Int
256+
}
257+
}
258+
259+
"have correct structure for implicit self access with shadowing a variable from outer scope" in {
260+
val cpg = code("""
261+
|let f: String = "f"
262+
|class Foo {
263+
| let f: String = "f"
264+
| func bar() {
265+
| handleF(f)
266+
| }
267+
|}
268+
|""".stripMargin)
269+
cpg.typeDecl.nameExact("Foo").member.name.l shouldBe List("f")
270+
val List(bAccess) = cpg.call.nameExact("handleF").argument.fieldAccess.l
271+
bAccess.code shouldBe "self.f"
272+
inside(bAccess.argument.l) { case List(id: Identifier, fieldName: FieldIdentifier) =>
273+
id.name shouldBe "self"
274+
id.typeFullName shouldBe "Test0.swift:<global>.Foo"
275+
fieldName.canonicalName shouldBe "f"
276+
}
277+
}
278+
239279
"have correct structure for implicit self access except for variable in closure" in {
240280
val cpg = code("""
241281
|class Foo {

0 commit comments

Comments
 (0)