Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix unexpected expansion #7

Merged
merged 5 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-snapshot-testing",
"state" : {
"revision" : "506b6052384d8e97a4bb16fe8680325351c23c64",
"version" : "1.14.0"
"branch" : "fix-unexpected-expansion",
"revision" : "3771c88abc02ed159650f3ddfbed64ffd1c33ec7"
}
},
{
Expand Down
5 changes: 4 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/apple/swift-syntax.git", from: "509.0.0"),
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.14.0"),
.package(
url: "https://github.com/pointfreeco/swift-snapshot-testing",
branch: "fix-unexpected-expansion"
),
],
targets: [
.target(
Expand Down
4 changes: 1 addition & 3 deletions Sources/MacroTesting/AssertMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,7 @@ public func assertMacro(
)
}

if allDiagnostics.filter({ $0.diagMessage.severity == .error }).isEmpty
|| expandedSource != nil
{
if allDiagnostics.filter({ $0.diagMessage.severity == .error }).isEmpty {
offset += 1
assertInlineSnapshot(
of: expandedSourceFile.description.trimmingCharacters(in: .newlines),
Expand Down
27 changes: 7 additions & 20 deletions Tests/MacroTestingTests/MacroExamples/AddAsyncMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,30 +42,23 @@ public struct AddAsyncMacro: PeerMacro {
}

// This only makes sense void functions
if funcDecl.signature.returnClause?.type.with(\.leadingTrivia, []).with(\.trailingTrivia, [])
.description != "Void"
{
if funcDecl.signature.returnClause?.type.with(\.leadingTrivia, []).with(\.trailingTrivia, []).description != "Void" {
throw CustomError.message(
"@addAsync requires an function that returns void"
)
}

// Requires a completion handler block as last parameter
guard
let completionHandlerParameterAttribute = funcDecl.signature.parameterClause.parameters.last?
.type.as(AttributedTypeSyntax.self),
let completionHandlerParameter = completionHandlerParameterAttribute.baseType.as(
FunctionTypeSyntax.self)
guard let completionHandlerParameterAttribute = funcDecl.signature.parameterClause.parameters.last?.type.as(AttributedTypeSyntax.self),
let completionHandlerParameter = completionHandlerParameterAttribute.baseType.as(FunctionTypeSyntax.self)
else {
throw CustomError.message(
"@addAsync requires an function that has a completion handler as last parameter"
)
}

// Completion handler needs to return Void
if completionHandlerParameter.returnClause.type.with(\.leadingTrivia, []).with(
\.trailingTrivia, []
).description != "Void" {
if completionHandlerParameter.returnClause.type.with(\.leadingTrivia, []).with(\.trailingTrivia, []).description != "Void" {
throw CustomError.message(
"@addAsync requires an function that has a completion handler that returns Void"
)
Expand All @@ -74,18 +67,14 @@ public struct AddAsyncMacro: PeerMacro {
let returnType = completionHandlerParameter.parameters.first?.type

let isResultReturn = returnType?.children(viewMode: .all).first?.description == "Result"
let successReturnType =
isResultReturn
? returnType!.as(IdentifierTypeSyntax.self)!.genericArgumentClause?.arguments.first!.argument
: returnType
let successReturnType = isResultReturn ? returnType!.as(IdentifierTypeSyntax.self)!.genericArgumentClause?.arguments.first!.argument : returnType

// Remove completionHandler and comma from the previous parameter
var newParameterList = funcDecl.signature.parameterClause.parameters
newParameterList.removeLast()
let newParameterListLastParameter = newParameterList.last!
newParameterList.removeLast()
newParameterList.append(
newParameterListLastParameter.with(\.trailingTrivia, []).with(\.trailingComma, nil))
newParameterList.append(newParameterListLastParameter.with(\.trailingTrivia, []).with(\.trailingComma, nil))

// Drop the @addAsync attribute from the new declaration.
let newAttributeList = funcDecl.attributes.filter {
Expand Down Expand Up @@ -147,9 +136,7 @@ public struct AddAsyncMacro: PeerMacro {
)
.with(
\.returnClause,
successReturnType != nil
? ReturnClauseSyntax(
leadingTrivia: .space, type: successReturnType!.with(\.leadingTrivia, .space)) : nil
successReturnType != nil ? ReturnClauseSyntax(leadingTrivia: .space, type: successReturnType!.with(\.leadingTrivia, .space)) : nil
) // add result type
.with(
\.parameterClause,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ public struct AddCompletionHandlerMacro: PeerMacro {
}

// Form the completion handler parameter.
let resultType: TypeSyntax? = funcDecl.signature.returnClause?.type.with(\.leadingTrivia, [])
.with(\.trailingTrivia, [])
let resultType: TypeSyntax? = funcDecl.signature.returnClause?.type.with(\.leadingTrivia, []).with(\.trailingTrivia, [])

let completionHandlerParam =
FunctionParameterSyntax(
Expand Down
12 changes: 4 additions & 8 deletions Tests/MacroTestingTests/MacroExamples/CustomCodable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,18 @@ public enum CustomCodable: MemberMacro {
let cases = memberList.compactMap({ member -> String? in
// is a property
guard
let propertyName = member.decl.as(VariableDeclSyntax.self)?.bindings.first?.pattern.as(
IdentifierPatternSyntax.self)?.identifier.text
let propertyName = member.decl.as(VariableDeclSyntax.self)?.bindings.first?.pattern.as(IdentifierPatternSyntax.self)?.identifier.text
else {
return nil
}

// if it has a CodableKey macro on it
if let customKeyMacro = member.decl.as(VariableDeclSyntax.self)?.attributes.first(where: {
element in
element.as(AttributeSyntax.self)?.attributeName.as(IdentifierTypeSyntax.self)?.description
== "CodableKey"
if let customKeyMacro = member.decl.as(VariableDeclSyntax.self)?.attributes.first(where: { element in
element.as(AttributeSyntax.self)?.attributeName.as(IdentifierTypeSyntax.self)?.description == "CodableKey"
}) {

// Uses the value in the Macro
let customKeyValue = customKeyMacro.as(AttributeSyntax.self)!.arguments!.as(
LabeledExprListSyntax.self)!.first!.expression
let customKeyValue = customKeyMacro.as(AttributeSyntax.self)!.arguments!.as(LabeledExprListSyntax.self)!.first!.expression

return "case \(propertyName) = \(customKeyValue)"
} else {
Expand Down
9 changes: 3 additions & 6 deletions Tests/MacroTestingTests/MacroExamples/MetaEnumMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ public struct MetaEnumMacro {
let access: DeclModifierListSyntax.Element?
let parentParamName: TokenSyntax

init(
node: AttributeSyntax, declaration: some DeclGroupSyntax, context: some MacroExpansionContext
) throws {
init(node: AttributeSyntax, declaration: some DeclGroupSyntax, context: some MacroExpansionContext) throws {
guard let enumDecl = declaration.as(EnumDeclSyntax.self) else {
throw DiagnosticsError(diagnostics: [
CaseMacroDiagnostic.notAnEnum(declaration).diagnose(at: Syntax(node))
Expand Down Expand Up @@ -96,7 +94,7 @@ extension EnumDeclSyntax {
var caseElements: [EnumCaseElementSyntax] {
memberBlock.members.flatMap { member in
guard let caseDecl = member.decl.as(EnumCaseDeclSyntax.self) else {
return [EnumCaseElementSyntax]()
return Array<EnumCaseElementSyntax>()
}

return Array(caseDecl.elements)
Expand All @@ -112,8 +110,7 @@ extension CaseMacroDiagnostic: DiagnosticMessage {
var message: String {
switch self {
case .notAnEnum(let decl):
return
"'@MetaEnum' can only be attached to an enum, not \(decl.descriptiveDeclKind(withArticle: true))"
return "'@MetaEnum' can only be attached to an enum, not \(decl.descriptiveDeclKind(withArticle: true))"
}
}

Expand Down
3 changes: 1 addition & 2 deletions Tests/MacroTestingTests/MacroExamples/NewTypeMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ extension NewTypeMacro: MemberMacro {
.expression.as(MemberAccessExprSyntax.self),
let rawType = memberAccessExn.base?.as(DeclReferenceExprSyntax.self)
else {
throw CustomError.message(
#"@NewType requires the raw type as an argument, in the form "RawType.self"."#)
throw CustomError.message(#"@NewType requires the raw type as an argument, in the form "RawType.self"."#)
}

guard let declaration = declaration.as(StructDeclSyntax.self) else {
Expand Down
25 changes: 9 additions & 16 deletions Tests/MacroTestingTests/MacroExamples/OptionSetMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,7 @@ public struct OptionSetMacro {
stringLiteral.segments.count == 1,
case let .stringSegment(optionsEnumNameString)? = stringLiteral.segments.first
else {
context.diagnose(
OptionSetMacroDiagnostic.requiresStringLiteral(optionsEnumNameArgumentLabel).diagnose(
at: optionEnumNameArg.expression))
context.diagnose(OptionSetMacroDiagnostic.requiresStringLiteral(optionsEnumNameArgumentLabel).diagnose(at: optionEnumNameArg.expression))
return nil
}

Expand All @@ -120,15 +118,12 @@ public struct OptionSetMacro {
return nil
}).first
else {
context.diagnose(
OptionSetMacroDiagnostic.requiresOptionsEnum(optionsEnumName).diagnose(at: decl))
context.diagnose(OptionSetMacroDiagnostic.requiresOptionsEnum(optionsEnumName).diagnose(at: decl))
return nil
}

// Retrieve the raw type from the attribute.
guard
let genericArgs = attribute.attributeName.as(IdentifierTypeSyntax.self)?
.genericArgumentClause,
guard let genericArgs = attribute.attributeName.as(IdentifierTypeSyntax.self)?.genericArgumentClause,
let rawType = genericArgs.arguments.first?.argument
else {
context.diagnose(OptionSetMacroDiagnostic.requiresOptionsEnumRawType.diagnose(at: attribute))
Expand All @@ -148,15 +143,15 @@ extension OptionSetMacro: ExtensionMacro {
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
// Decode the expansion arguments.
guard let (structDecl, _, _) = decodeExpansion(of: node, attachedTo: declaration, in: context)
else {
guard let (structDecl, _, _) = decodeExpansion(of: node, attachedTo: declaration, in: context) else {
return []
}

// If there is an explicit conformance to OptionSet already, don't add one.
if let inheritedTypes = structDecl.inheritanceClause?.inheritedTypes,
inheritedTypes.contains(where: { inherited in inherited.type.trimmedDescription == "OptionSet"
})
inheritedTypes.contains(
where: { inherited in inherited.type.trimmedDescription == "OptionSet"
)
{
return []
}
Expand All @@ -172,16 +167,14 @@ extension OptionSetMacro: MemberMacro {
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
// Decode the expansion arguments.
guard
let (_, optionsEnum, rawType) = decodeExpansion(of: attribute, attachedTo: decl, in: context)
else {
guard let (_, optionsEnum, rawType) = decodeExpansion(of: attribute, attachedTo: decl, in: context) else {
return []
}

// Find all of the case elements.
let caseElements = optionsEnum.memberBlock.members.flatMap { member in
guard let caseDecl = member.decl.as(EnumCaseDeclSyntax.self) else {
return [EnumCaseElementSyntax]()
return Array<EnumCaseElementSyntax>()
}

return Array(caseDecl.elements)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ public struct WrapStoredPropertiesMacro: MemberAttributeMacro {
stringLiteral.segments.count == 1,
case let .stringSegment(wrapperName)? = stringLiteral.segments.first
else {
throw CustomError.message(
"macro requires a string literal containing the name of an attribute")
throw CustomError.message("macro requires a string literal containing the name of an attribute")
}

return [
Expand Down