Skip to content

Commit

Permalink
#34: Check for empty path and provide meaningful error message; remov…
Browse files Browse the repository at this point in the history
…e unnecessary generic type error message/fixit (will never be seen)
  • Loading branch information
tdeleon committed Jan 4, 2024
1 parent 00838d7 commit df65808
Show file tree
Hide file tree
Showing 3 changed files with 11 additions and 65 deletions.
30 changes: 5 additions & 25 deletions Sources/RelaxMacros/APIEndpointMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,50 +22,30 @@ public struct APIEndpointMacro: ExtensionMacro {
.arguments?.as(LabeledExprListSyntax.self)?
.first?.expression.as(StringLiteralExprSyntax.self)?
.segments.trimmedDescription,
!path.isEmpty
!(path.trimmingCharacters(in: .whitespacesAndNewlines)).isEmpty
else {
let error = Diagnostic(node: node, message: RelaxMacroDiagnostic.invalidPath)
context.diagnose(error)
return []
}

guard let parent = node.attributeName.as(IdentifierTypeSyntax.self)?
.genericArgumentClause?.as(GenericArgumentClauseSyntax.self)?
.arguments
.description else {
let fixIt = FixIt.replace(
message: RelaxFixItMessage.missingParent,
oldNode: Syntax(node),
newNode: FixItRewriter().visit(node)
)
let error = Diagnostic(node: node, message: RelaxMacroDiagnostic.missingParent, fixIt: fixIt)
context.diagnose(error)

return []
}

let decl: DeclSyntax =
"""
extension \(type.trimmed): Endpoint {
typealias Parent = \(raw: parent)
static let path: String = \"\(raw: path)\"
}
"""

guard let extensionDecl = decl.as(ExtensionDeclSyntax.self) else { return [] }

return [extensionDecl]
}

final private class FixItRewriter: SyntaxRewriter {
override func visitAny(_ node: Syntax) -> Syntax? {
guard let attributeName = node.as(IdentifierTypeSyntax.self),
attributeName.genericArgumentClause == nil
else { return node }
let listSyntax = GenericArgumentListSyntax {
GenericArgumentSyntax(argument: TypeSyntax(stringLiteral: "<#APIComponent#>"))
}
let placeholder = GenericArgumentClauseSyntax(arguments: listSyntax)
guard let genericDecl = placeholder.as(GenericArgumentClauseSyntax.self) else { return node }
var newAttributeName = attributeName
newAttributeName.genericArgumentClause = genericDecl
return newAttributeName.as(Syntax.self)
}
}
}
21 changes: 1 addition & 20 deletions Sources/RelaxMacros/RelaxMacroDiagnostic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import SwiftDiagnostics
enum RelaxMacroDiagnostic: String, DiagnosticMessage {
case invalidBaseURL
case invalidPath
case missingParent

var severity: DiagnosticSeverity { .error }

Expand All @@ -19,29 +18,11 @@ enum RelaxMacroDiagnostic: String, DiagnosticMessage {
case .invalidBaseURL:
"The base URL is invalid."
case .invalidPath:
"The path is invalid."
case .missingParent:
"The parent APIComponent must be specified as a generic argument."
"The path must not be empty."
}
}

var diagnosticID: MessageID {
MessageID(domain: "RelaxMacros", id: rawValue)
}
}

enum RelaxFixItMessage: String, FixItMessage {
case missingParent
var message: String {
switch self {
case .missingParent:
"Add generic argument 'APIComponent'"
}
}

var fixItID: SwiftDiagnostics.MessageID {
MessageID(domain: "RelaxMacros", id: rawValue)
}


}
25 changes: 5 additions & 20 deletions Tests/RelaxMacrosTests/APIEndpointMacroTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,40 +42,25 @@ final class APIEndpointMacroTests: XCTestCase {
#endif
}

func testAPIEndpointNoParent() throws {
func testAPIEndpointInvalidPath() throws {
#if canImport(RelaxMacros)
assertMacroExpansion(
"""
@APIEndpoint("path")
@APIEndpoint<Parent>("")
enum TestService {
}
""",
expandedSource: """
enum TestService {
}
""",
diagnostics: [
DiagnosticSpec(
message: RelaxMacroDiagnostic.missingParent.message,
line: 1,
column: 1,
fixIts: [
FixItSpec(message: RelaxFixItMessage.missingParent.message)
]
)
],
diagnostics: [.init(message: RelaxMacroDiagnostic.invalidPath.message, line: 1, column: 1)],
macros: testMacros
)
#else
throw XCTSkip("macros are only supported when running tests for the host platform")
#endif
}

func testAPIEndpointInvalidPath() throws {
#if canImport(RelaxMacros)

assertMacroExpansion(
"""
@APIEndpoint<Parent>("")
@APIEndpoint<Parent>(" ")
enum TestService {
}
""",
Expand Down

0 comments on commit df65808

Please sign in to comment.