Skip to content

Commit

Permalink
better diagnosis
Browse files Browse the repository at this point in the history
  • Loading branch information
MahdiBM committed Nov 1, 2023
1 parent aab0877 commit 15ec0ea
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 49 deletions.
35 changes: 11 additions & 24 deletions Macros/PostgresRecordMacro/Diagnoser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import SwiftSyntax
import SwiftSyntaxMacros
import SwiftDiagnostics

struct Diagnoser<Context: MacroExpansionContext> {
let context: Context
struct Diagnoser {
let context: any MacroExpansionContext

static var shared: Diagnoser! = nil

func cannotConformToProtocol(
name: String,
Expand All @@ -16,12 +18,7 @@ struct Diagnoser<Context: MacroExpansionContext> {
position: old.position,
message: diagnosis.diagnosticMessage,
highlights: nil,
notes: [],
fixIt: .replace(
message: diagnosis.fixItMessage,
oldNode: old,
newNode: new
)
notes: []
))
}

Expand All @@ -32,11 +29,7 @@ struct Diagnoser<Context: MacroExpansionContext> {
position: node.position,
message: diagnosis.diagnosticMessage,
highlights: nil,
notes: [],
fixIt: .init(
message: diagnosis.fixItMessage,
changes: []
)
notes: []
))
}

Expand All @@ -47,11 +40,7 @@ struct Diagnoser<Context: MacroExpansionContext> {
position: node.position,
message: diagnosis.diagnosticMessage,
highlights: nil,
notes: [],
fixIt: .init(
message: diagnosis.fixItMessage,
changes: []
)
notes: []
))
}
}
Expand All @@ -69,9 +58,9 @@ private enum Diagnosis: Error {
case let .cannotConformToProtocol(proto):
return "Simultaneous conformance to '\(proto)' is not supported"
case let .unsupportedPattern(pattern):
return "Pattern of '\(pattern)' is unsupported. Please file and issue in at https://github.com/vapor/postgres-kit/issues"
return "Pattern of '\(pattern)' is unsupported. As a workaround, try to use a more common pattern. Please file and issue in at https://github.com/vapor/postgres-kit/issues"
case let .typeSyntaxNotFound(name):
return "Type declaration was not found for property '\(name)'."
return "Type declaration was not found for property '\(name)'. Please provide an explicit type"
}
}

Expand All @@ -91,10 +80,8 @@ private enum Diagnosis: Error {
switch parent {
case let .cannotConformToProtocol(proto):
return "Remove conformance to '\(proto)'"
case .unsupportedPattern:
return "As a workaround, try to use a more common pattern."
case .typeSyntaxNotFound:
return "Please provide an explicit type."
case .unsupportedPattern, .typeSyntaxNotFound:
return ""
}
}

Expand Down
3 changes: 3 additions & 0 deletions Macros/PostgresRecordMacro/MacroError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ import SwiftDiagnostics

enum MacroError: Error {
case isNotStruct
case vagueError
}

extension MacroError: DiagnosticMessage {
var message: String {
switch self {
case .isNotStruct:
return "Only 'struct's are supported"
case .vagueError:
return "'PostgresRecord' macro expansion failed"
}
}

Expand Down
19 changes: 10 additions & 9 deletions Macros/PostgresRecordMacro/PostgresRecordMacroType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public enum PostgresRecordMacroType: ExtensionMacro {
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
if declaration.hasError { return [] }
let diagnoser = Diagnoser(context: context)
Diagnoser.shared = Diagnoser(context: context)

guard let structDecl = declaration.as(StructDeclSyntax.self) else {
throw MacroError.isNotStruct
Expand All @@ -24,7 +24,7 @@ public enum PostgresRecordMacroType: ExtensionMacro {
let proto = inheritedTypes[idx]
let name = proto.trimmedDescription
if forbiddenProtocols.contains(name) {
diagnoser.cannotConformToProtocol(
Diagnoser.shared.cannotConformToProtocol(
name: name,
old: structDecl,
new: structDecl.removingInheritedType(at: idx)
Expand All @@ -35,15 +35,16 @@ public enum PostgresRecordMacroType: ExtensionMacro {

let members = structDecl.memberBlock.members
let variableDecls = members.compactMap { $0.decl.as(VariableDeclSyntax.self) }
let variables = try variableDecls.flatMap {
try Variable.parse(from: $0, diagnoser: diagnoser)
}.filter {
!($0.isStatic || $0.isComputed)
}
let storedVariables = try variableDecls
.flatMap(Variable.parse(from:))
.filter { !($0.isStatic || $0.isComputed) }

let name = structDecl.name.trimmedDescription
let initializer = variables.makePostgresRecordInit(name: name, accessLevel: accessLevel)
let codingKeys = variables.makeCodingKeys(accessLevel: accessLevel)
let initializer = try storedVariables.makePostgresRecordInit(
name: name,
accessLevel: accessLevel
)
let codingKeys = storedVariables.makeCodingKeys(accessLevel: accessLevel)
let postgresRecord = try ExtensionDeclSyntax("""
extension \(raw: name): PostgresRecord {
\(raw: initializer)
Expand Down
22 changes: 18 additions & 4 deletions Macros/PostgresRecordMacro/Variable+init.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import SwiftSyntax

extension [Variable] {
func makePostgresRecordInit(name: String, accessLevel: String) -> String {
"""
func makePostgresRecordInit(name: String, accessLevel: String) throws -> String {
try """
\(accessLevel)init(
_from row: PostgresRow,
context: PostgresDecodingContext<some PostgresJSONDecoder>,
Expand All @@ -20,8 +20,22 @@ extension [Variable] {
"""
}

private func makeType() -> String {
"(\(self.map(\.type.description).joined(separator: ","))).self"
private func makeType() throws -> String {
let count = self.count
let variables = self.compactMap { variable -> Variable? in
if variable.type == nil {
Diagnoser.shared.typeSyntaxNotFound(name: variable.name, node: variable.binding)
return nil
} else {
return variable
}
}
/// Some variable had unexpected type.
if count != variables.count {
throw MacroError.vagueError
} else {
return "(\(variables.map(\.type!.description).joined(separator: ","))).self"
}
}

private func makeInitializations() -> String {
Expand Down
19 changes: 8 additions & 11 deletions Macros/PostgresRecordMacro/Variable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,32 @@ struct Variable {
let name: String
let isStatic: Bool
let isComputed: Bool
let type: ParsedType
let type: ParsedType?
let binding: PatternBindingSyntax

static func parse(
from element: VariableDeclSyntax,
diagnoser: Diagnoser<some MacroExpansionContext>
) throws -> [Variable] {
static func parse(from element: VariableDeclSyntax) throws -> [Variable] {
let isStatic = element.modifiers.contains(.static)
return try element.bindings.compactMap { binding -> Variable? in
let isComputed = binding.accessorBlock.map({ Self.isComputed($0.accessors) }) ?? false
guard let pattern = binding.pattern.as(IdentifierPatternSyntax.self) else {
diagnoser.unsupportedPattern(
Diagnoser.shared.unsupportedPattern(
binding.pattern.trimmedDescription,
node: binding.pattern
)
return nil
}
let name = pattern.identifier.trimmed.text

guard let typeSyntax = binding.typeAnnotation?.type else {
diagnoser.typeSyntaxNotFound(name: name, node: binding)
return nil
let type = try binding.typeAnnotation.map {
try ParsedType(syntax: $0.type)
}
let type = try ParsedType(syntax: typeSyntax)

return Variable(
name: name,
isStatic: isStatic,
isComputed: isComputed,
type: type
type: type,
binding: binding
)
}
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/PostgresKit/PostgresRecord.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public macro PostgresRecord() = #externalMacro(
@PostgresRecord
struct MyTable {
let int: Int
let string: String
let string = ""

static let name = ""
}

0 comments on commit 15ec0ea

Please sign in to comment.