From 35a055251aecea31f6a5ee2ce2fb8569f19a53b1 Mon Sep 17 00:00:00 2001 From: Liam Nichols Date: Mon, 15 Jul 2024 18:45:04 +0200 Subject: [PATCH 1/3] Add spec to assert expected typeName when using nested type that shadows global type --- .../Parsing/FileParser_MethodsSpec.swift | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/SourceryTests/Parsing/FileParser_MethodsSpec.swift b/SourceryTests/Parsing/FileParser_MethodsSpec.swift index 4ba9fa5e2..751a46f96 100644 --- a/SourceryTests/Parsing/FileParser_MethodsSpec.swift +++ b/SourceryTests/Parsing/FileParser_MethodsSpec.swift @@ -284,6 +284,38 @@ class FileParserMethodsSpec: QuickSpec { ])) } + it("extracts correct typeName when a nested type shadows a global type") { + let code = """ + protocol Foo { + } + + class Bar { + struct Foo { + } + + func doSomething(with foo: Foo) { + } + } + """ + + let fooProtocol = Protocol(name: "Foo") + let fooStruct = Struct(name: "Foo") + let barClass = Class( + name: "Bar", + methods: [ + Method(name: "doSomething(with foo: Bar.Foo)", selectorName: "doSomething(with:)", parameters: [ + MethodParameter(argumentLabel: "with", name: "foo", index: 0, typeName: TypeName("Bar.Foo"), type: fooStruct) + ], definedInTypeName: TypeName("Bar")) + ], + containedTypes: [ + fooStruct + ] + ) + + let result = parse(code) + expect(result).to(equal([fooProtocol, barClass, fooStruct])) + } + context("given parameter default value") { it("extracts simple default value") { expect(parse("class Foo { func foo(a: Int? = nil) {} }")).to(equal([ From e6bf15e732ae2b6685471388d339f41564162fbb Mon Sep 17 00:00:00 2001 From: Liam Nichols Date: Mon, 15 Jul 2024 18:52:47 +0200 Subject: [PATCH 2/3] Update MethodParameter to correctly resolve typeName to local type when initialising from syntax tree --- .../SwiftSyntax/AST/Method+SwiftSyntax.swift | 7 ++++--- .../AST/MethodParameter+SwiftSyntax.swift | 18 +++++++++++++++--- .../Parsing/SwiftSyntax/AST/Signature.swift | 16 ++++++++++++---- .../AST/Subscript+SwiftSyntax.swift | 2 +- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/Method+SwiftSyntax.swift b/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/Method+SwiftSyntax.swift index 79857275f..03a6feec5 100644 --- a/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/Method+SwiftSyntax.swift +++ b/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/Method+SwiftSyntax.swift @@ -9,7 +9,7 @@ extension SourceryMethod { parent: parent, identifier: node.name.text.trimmed, typeName: typeName, - signature: Signature(node.signature, annotationsParser: annotationsParser), + signature: Signature(node.signature, annotationsParser: annotationsParser, parent: parent), modifiers: node.modifiers, attributes: node.attributes, genericParameterClause: node.genericParameterClause, @@ -30,7 +30,8 @@ extension SourceryMethod { output: nil, asyncKeyword: nil, throwsOrRethrowsKeyword: signature.effectSpecifiers?.throwsSpecifier?.description.trimmed, - annotationsParser: annotationsParser + annotationsParser: annotationsParser, + parent: parent ), modifiers: node.modifiers, attributes: node.attributes, @@ -46,7 +47,7 @@ extension SourceryMethod { parent: parent, identifier: "deinit", typeName: typeName, - signature: Signature(parameters: nil, output: nil, asyncKeyword: nil, throwsOrRethrowsKeyword: nil, annotationsParser: annotationsParser), + signature: Signature(parameters: nil, output: nil, asyncKeyword: nil, throwsOrRethrowsKeyword: nil, annotationsParser: annotationsParser, parent: parent), modifiers: node.modifiers, attributes: node.attributes, genericParameterClause: nil, diff --git a/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/MethodParameter+SwiftSyntax.swift b/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/MethodParameter+SwiftSyntax.swift index 6980619f3..c369cc2e6 100644 --- a/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/MethodParameter+SwiftSyntax.swift +++ b/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/MethodParameter+SwiftSyntax.swift @@ -2,12 +2,24 @@ import SwiftSyntax import SourceryRuntime extension MethodParameter { - convenience init(_ node: FunctionParameterSyntax, index: Int, annotationsParser: AnnotationsParser) { + convenience init(_ node: FunctionParameterSyntax, index: Int, annotationsParser: AnnotationsParser, parent: Type?) { let firstName = node.firstName.text.trimmed.nilIfNotValidParameterName - let typeName = TypeName(node.type) + let isVisitingTypeSourceryProtocol = parent is SourceryProtocol let specifiers = TypeName.specifiers(from: node.type) - + + // NOTE: This matches implementation in Variable+SwiftSyntax.swift + // TODO: Walk up the `parent` in the event that there are multiple levels of nested types + var typeName = TypeName(node.type) + if !isVisitingTypeSourceryProtocol { + // we are in a custom type, which may contain other types + // in order to assign correct type to the variable, we need to match + // all of the contained types against the variable type + if let matchingContainedType = parent?.containedTypes.first(where: { $0.localName == typeName.name }) { + typeName = TypeName(matchingContainedType.name) + } + } + if specifiers.isInOut { // TODO: TBR typeName.name = "inout \(typeName.name)" diff --git a/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/Signature.swift b/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/Signature.swift index f015cbc16..86710bbb2 100644 --- a/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/Signature.swift +++ b/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/Signature.swift @@ -14,20 +14,28 @@ public struct Signature { /// The `throws` or `rethrows` keyword, if any. public let throwsOrRethrowsKeyword: String? - public init(_ node: FunctionSignatureSyntax, annotationsParser: AnnotationsParser) { + public init(_ node: FunctionSignatureSyntax, annotationsParser: AnnotationsParser, parent: Type?) { self.init(parameters: node.parameterClause.parameters, output: node.returnClause.map { TypeName($0.type) }, asyncKeyword: node.effectSpecifiers?.asyncSpecifier?.text, throwsOrRethrowsKeyword: node.effectSpecifiers?.throwsSpecifier?.description.trimmed, - annotationsParser: annotationsParser + annotationsParser: annotationsParser, + parent: parent ) } - public init(parameters: FunctionParameterListSyntax?, output: TypeName?, asyncKeyword: String?, throwsOrRethrowsKeyword: String?, annotationsParser: AnnotationsParser) { + public init( + parameters: FunctionParameterListSyntax?, + output: TypeName?, + asyncKeyword: String?, + throwsOrRethrowsKeyword: String?, + annotationsParser: AnnotationsParser, + parent: Type? + ) { var methodParameters: [MethodParameter] = [] if let parameters { for (idx, param) in parameters.enumerated() { - methodParameters.append(MethodParameter(param, index: idx, annotationsParser: annotationsParser)) + methodParameters.append(MethodParameter(param, index: idx, annotationsParser: annotationsParser, parent: parent)) } } input = methodParameters diff --git a/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/Subscript+SwiftSyntax.swift b/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/Subscript+SwiftSyntax.swift index e1e555821..f55dbbda9 100644 --- a/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/Subscript+SwiftSyntax.swift +++ b/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/Subscript+SwiftSyntax.swift @@ -85,7 +85,7 @@ extension Subscript { var parameters: [MethodParameter] = [] for (idx, param) in node.parameterClause.parameters.enumerated() { - parameters.append(MethodParameter(param, index: idx, annotationsParser: annotationsParser)) + parameters.append(MethodParameter(param, index: idx, annotationsParser: annotationsParser, parent: parent)) } self.init( parameters: parameters, From 8b65136a69c0f6fb91a3ab0a05fc7591b61b24d5 Mon Sep 17 00:00:00 2001 From: Liam Nichols Date: Mon, 15 Jul 2024 19:11:06 +0200 Subject: [PATCH 3/3] Apply same fix for return type --- .../Parsing/SwiftSyntax/AST/Signature.swift | 16 +++++++++++++++- .../Parsing/FileParser_MethodsSpec.swift | 4 ++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/Signature.swift b/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/Signature.swift index 86710bbb2..e932e67ba 100644 --- a/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/Signature.swift +++ b/SourceryFramework/Sources/Parsing/SwiftSyntax/AST/Signature.swift @@ -15,8 +15,22 @@ public struct Signature { public let throwsOrRethrowsKeyword: String? public init(_ node: FunctionSignatureSyntax, annotationsParser: AnnotationsParser, parent: Type?) { + let isVisitingTypeSourceryProtocol = parent is SourceryProtocol + + // NOTE: This matches implementation in Variable+SwiftSyntax.swift + // TODO: Walk up the `parent` in the event that there are multiple levels of nested types + var returnTypeName = node.returnClause.map { TypeName($0.type) } + if !isVisitingTypeSourceryProtocol { + // we are in a custom type, which may contain other types + // in order to assign correct type to the variable, we need to match + // all of the contained types against the variable type + if let matchingContainedType = parent?.containedTypes.first(where: { $0.localName == returnTypeName?.name }) { + returnTypeName = TypeName(matchingContainedType.name) + } + } + self.init(parameters: node.parameterClause.parameters, - output: node.returnClause.map { TypeName($0.type) }, + output: returnTypeName, asyncKeyword: node.effectSpecifiers?.asyncSpecifier?.text, throwsOrRethrowsKeyword: node.effectSpecifiers?.throwsSpecifier?.description.trimmed, annotationsParser: annotationsParser, diff --git a/SourceryTests/Parsing/FileParser_MethodsSpec.swift b/SourceryTests/Parsing/FileParser_MethodsSpec.swift index 751a46f96..06df1329a 100644 --- a/SourceryTests/Parsing/FileParser_MethodsSpec.swift +++ b/SourceryTests/Parsing/FileParser_MethodsSpec.swift @@ -293,7 +293,7 @@ class FileParserMethodsSpec: QuickSpec { struct Foo { } - func doSomething(with foo: Foo) { + func doSomething(with foo: Foo) -> Foo { } } """ @@ -305,7 +305,7 @@ class FileParserMethodsSpec: QuickSpec { methods: [ Method(name: "doSomething(with foo: Bar.Foo)", selectorName: "doSomething(with:)", parameters: [ MethodParameter(argumentLabel: "with", name: "foo", index: 0, typeName: TypeName("Bar.Foo"), type: fooStruct) - ], definedInTypeName: TypeName("Bar")) + ], returnTypeName: TypeName("Bar.Foo"), definedInTypeName: TypeName("Bar")) ], containedTypes: [ fooStruct