Skip to content

Commit

Permalink
Added support for omitting the default parameter/initialiser for opti…
Browse files Browse the repository at this point in the history
…onal types.
  • Loading branch information
bok- committed Dec 13, 2024
1 parent 2c183af commit 0ad4f0f
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 19 deletions.
64 changes: 45 additions & 19 deletions Sources/VexilMacros/FlagMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//

import SwiftDiagnostics
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros
Expand All @@ -33,15 +34,15 @@ public struct FlagMacro {
/// Create a FlagMacro from the given attribute/declaration
init(node: AttributeSyntax, declaration: some DeclSyntaxProtocol, context: some MacroExpansionContext) throws {
guard node.attributeName.as(IdentifierTypeSyntax.self)?.name.text == "Flag" else {
throw Diagnostic.notFlagMacro
throw DiagnosticsError(diagnostics: [ .init(node: node, message: Diagnostic.notFlagMacro) ])
}
guard let arguments = node.arguments else {
throw Diagnostic.missingArguments
throw DiagnosticsError(diagnostics: [ .init(node: node, message: Diagnostic.missingArguments) ])
}

// Description can have an explicit or omitted label
guard let description = arguments.descriptionArgument else {
throw Diagnostic.missingDescription
throw DiagnosticsError(diagnostics: [ .init(node: node, message: Diagnostic.missingDescription) ])
}

guard
Expand All @@ -51,11 +52,16 @@ public struct FlagMacro {
let type = binding.typeAnnotation?.type ?? binding.inferredType,
binding.accessorBlock == nil
else {
throw Diagnostic.onlySimpleVariableSupported
throw DiagnosticsError(diagnostics: [ .init(node: node, message: Diagnostic.onlySimpleVariableSupported) ])
}

guard let defaultExprSyntax = arguments[label: "default"]?.expression ?? binding.initializer?.value else {
throw Diagnostic.missingDefaultValue
var defaultExprSyntax: ExprSyntax
if let defaultExpr = arguments[label: "default"]?.expression ?? binding.initializer?.value {
defaultExprSyntax = defaultExpr
} else if binding.typeAnnotation?.type.is(OptionalTypeSyntax.self) == true {
defaultExprSyntax = ExprSyntax(NilLiteralExprSyntax())
} else {
throw DiagnosticsError(diagnostics: [ .init(node: node, message: Diagnostic.missingDefaultValue) ])
}

let strategy = KeyStrategy(exprSyntax: arguments[label: "keyStrategy"]?.expression) ?? .default
Expand Down Expand Up @@ -123,18 +129,14 @@ extension FlagMacro: AccessorMacro {
providingAccessorsOf declaration: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [AccessorDeclSyntax] {
do {
let macro = try FlagMacro(node: node, declaration: declaration, context: context)
return [
"""
get {
\(macro.makeLookupExpression())
}
""",
]
} catch {
return []
}
let macro = try FlagMacro(node: node, declaration: declaration, context: context)
return [
"""
get {
\(macro.makeLookupExpression())
}
""",
]
}

}
Expand Down Expand Up @@ -177,12 +179,36 @@ extension FlagMacro: PeerMacro {

extension FlagMacro {

enum Diagnostic: Error {
enum Diagnostic: DiagnosticMessage {
case notFlagMacro
case missingArguments
case missingDefaultValue
case missingDescription
case onlySimpleVariableSupported

var message: String {
switch self {
case .notFlagMacro: "This is not a @Flag macro..?"
case .missingArguments: "Required arguments have not been provided."
case .missingDefaultValue: "Could not infer the default value. Initialise the property or set the default: parameter."
case .missingDescription: "Description parameter missing."
case .onlySimpleVariableSupported: "Only simple single-binding properties supported."
}
}

var diagnosticID: MessageID {
switch self {
case .notFlagMacro: MessageID(domain: "com.unsignedapps.vexil.flagMacro", id: "notFlagMacro")
case .missingArguments: MessageID(domain: "com.unsignedapps.vexil.flagMacro", id: "missingArguments")
case .missingDefaultValue: MessageID(domain: "com.unsignedapps.vexil.flagMacro", id: "missingDefaultValue")
case .missingDescription: MessageID(domain: "com.unsignedapps.vexil.flagMacro", id: "missingDescription")
case .onlySimpleVariableSupported: MessageID(domain: "com.unsignedapps.vexil.flagMacro", id: "onlySimpleVariableSupported")
}
}

var severity: DiagnosticSeverity {
.error
}
}

}
Expand Down
35 changes: 35 additions & 0 deletions Tests/VexilMacroTests/FlagMacroTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,41 @@ final class FlagMacroTests: XCTestCase {
)
}

func testExpandsOptional() throws {
assertMacroExpansion(
"""
struct TestFlags {
@Flag(description: "meow")
var testProperty: Bool?
}
""",
expandedSource:
"""
struct TestFlags {
var testProperty: Bool? {
get {
_flagLookup.value(for: _flagKeyPath.append(.automatic("test-property"))) ?? nil
}
}
var $testProperty: FlagWigwag<Bool?> {
FlagWigwag(
keyPath: _flagKeyPath.append(.automatic("test-property")),
name: nil,
defaultValue: nil,
description: "meow",
displayOption: .default,
lookup: _flagLookup
)
}
}
""",
macros: [
"Flag": FlagMacro.self,
]
)
}


// MARK: - Property Initialisation Tests

Expand Down

0 comments on commit 0ad4f0f

Please sign in to comment.