Skip to content

Commit

Permalink
Update the AddBlocker implementation and clean up a few things
Browse files Browse the repository at this point in the history
  • Loading branch information
DougGregor committed Feb 1, 2023
1 parent d1e7fbe commit 3f134cc
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 74 deletions.
14 changes: 7 additions & 7 deletions MacroExamples/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,24 @@ func blockAdd() {
print(#addBlocker(x * y + z))
}

func produceWarning() {
func warningAndError() {
#myWarning("remember to pass a string literal here")
}
// Uncomment to get an error out of the macro.
// let text = "oops"
// #myWarning(text)

// Uncomment to get an error out of the macro.
// let text = "oops"
// #myWarning(text)
}

struct Font: ExpressibleByFontLiteral {
init(fontLiteralName: String, size: Int, weight: MacroExamplesLib.FontWeight) {
}
}

func testFontLiteral() {
let font: Font = #fontLiteral(name: "Comic Sans", size: 14, weight: .thin)
let _: Font = #fontLiteral(name: "Comic Sans", size: 14, weight: .thin)
}

testStringify()
blockAdd()
produceWarning()
warningAndError()
testFontLiteral()
121 changes: 56 additions & 65 deletions MacroExamplesPlugin/AddBlocker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,95 +13,86 @@ extension SimpleDiagnosticMessage: FixItMessage {
var fixItID: MessageID { diagnosticID }
}

class AddVisitor: SyntaxRewriter {
var diagnostics: [Diagnostic] = []
public struct AddBlocker: ExpressionMacro {
class AddVisitor: SyntaxRewriter {
var diagnostics: [Diagnostic] = []

override func visit(
_ node: InfixOperatorExprSyntax
) -> ExprSyntax {
if let binOp = node.operatorOperand.as(BinaryOperatorExprSyntax.self) {
if binOp.operatorToken.text == "+" {
let messageID = MessageID(domain: "silly", id: "addblock")
diagnostics.append(
Diagnostic(
node: Syntax(node.operatorOperand),
message: SimpleDiagnosticMessage(
message: "blocked an add; did you mean to subtract?",
diagnosticID: messageID,
severity: .warning
),
highlights: [
Syntax(node.leftOperand.with(\.leadingTrivia, []).with(\.trailingTrivia, [])),
Syntax(node.rightOperand.with(\.leadingTrivia, []).with(\.trailingTrivia, []))
],
fixIts: [
FixIt(
message: SimpleDiagnosticMessage(
message: "use '-'",
diagnosticID: messageID,
severity: .error
),
changes: [
FixIt.Change.replace(
oldNode: Syntax(binOp.operatorToken.with(\.leadingTrivia, []).with(\.trailingTrivia, [])),
newNode: Syntax(
TokenSyntax(
.binaryOperator("-"),
presence: .present
override func visit(
_ node: InfixOperatorExprSyntax
) -> ExprSyntax {
if let binOp = node.operatorOperand.as(BinaryOperatorExprSyntax.self) {
if binOp.operatorToken.text == "+" {
let messageID = MessageID(domain: "silly", id: "addblock")
diagnostics.append(
Diagnostic(
node: Syntax(node.operatorOperand),
message: SimpleDiagnosticMessage(
message: "blocked an add; did you mean to subtract?",
diagnosticID: messageID,
severity: .warning
),
highlights: [
Syntax(node.leftOperand),
Syntax(node.rightOperand)
],
fixIts: [
FixIt(
message: SimpleDiagnosticMessage(
message: "use '-'",
diagnosticID: messageID,
severity: .error
),
changes: [
FixIt.Change.replace(
oldNode: Syntax(binOp.operatorToken),
newNode: Syntax(
TokenSyntax(
.binaryOperator("-"),
leadingTrivia: binOp.operatorToken.leadingTrivia,
trailingTrivia: binOp.operatorToken.trailingTrivia,
presence: .present
)
)
)
)
]
),
]
]
),
]
)
)
)

return ExprSyntax(
node.with(
\.operatorOperand,
ExprSyntax(
binOp.with(
\.operatorToken,
binOp.operatorToken.withKind(.binaryOperator("-"))
return ExprSyntax(
node.with(
\.operatorOperand,
ExprSyntax(
binOp.with(
\.operatorToken,
binOp.operatorToken.withKind(.binaryOperator("-"))
)
)
)
)
)
)
}
}
}

return ExprSyntax(node)
return ExprSyntax(node)
}
}
}

public struct AddBlocker: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
guard let argument = node.argumentList.first?.expression else {
let messageID = MessageID(domain: "example", id: "missingArg")
throw SimpleDiagnosticMessage(message: "missing argument",
diagnosticID: messageID,
severity: .error)
fatalError("boom")
}

let opTable = OperatorTable.standardOperators
let foldedArgument = opTable.foldAll(argument) { error in
context.diagnose(error.asDiagnostic)
}

// Link the folded argument back into the tree.
let node = node.with(\.argumentList, node.argumentList.replacing(childAt: 0, with: node.argumentList.first!.with(\.expression, foldedArgument.as(ExprSyntax.self)!)))

let visitor = AddVisitor()
let result = visitor.visit(Syntax(node))

for diag in visitor.diagnostics {
context.diagnose(diag)
}

return result.as(MacroExpansionExprSyntax.self)!.argumentList.first!.expression
return result.asProtocol(FreestandingMacroExpansionSyntax.self)!.argumentList.first!.expression
}
}
4 changes: 2 additions & 2 deletions MacroExamplesPlugin/WarningMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,5 @@ enum CustomError: Error, CustomStringConvertible {
)

return "()"
}
}
}
}

0 comments on commit 3f134cc

Please sign in to comment.