Skip to content

Commit

Permalink
Fail with diagnostic on non-boolean macro arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
SimplyDanny committed Oct 19, 2023
1 parent 3ce2d60 commit b87467f
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 34 deletions.
1 change: 1 addition & 0 deletions Source/SwiftLintCoreMacros/SwiftLintCoreMacros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ enum SwiftLintCoreMacroError: String, DiagnosticMessage {
case notStruct = "Attribute can only be applied to structs"
case notEnum = "Attribute can only be applied to enums"
case noStringRawType = "Attribute can only be applied to enums with a 'String' raw type"
case noBooleanLiteral = "Macro argument must be a boolean literal"

var message: String {
rawValue
Expand Down
72 changes: 40 additions & 32 deletions Source/SwiftLintCoreMacros/SwiftSyntaxRule.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros

enum SwiftSyntaxRule: ExtensionMacro {
Expand All @@ -9,49 +10,52 @@ enum SwiftSyntaxRule: ExtensionMacro {
conformingTo protocols: [TypeSyntax],
in context: some MacroExpansionContext
) throws -> [ExtensionDeclSyntax] {
return [
[
try ExtensionDeclSyntax("""
extension \(type): SwiftSyntaxRule {
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor<ConfigurationType> {
Visitor(configuration: configuration, file: file)
}
}
"""),
try createFoldingPreprocessor(type: type, foldArgument: node.foldArgument),
try createRewriter(type: type, rewriterArgument: node.explicitRewriterArgument)
"""
),
try makeExtension(dependingOn: node.foldArgument, in: context, with: """
extension \(type) {
func preprocess(file: SwiftLintFile) -> SourceFileSyntax? {
file.foldedSyntaxTree
}
}
"""
),
try makeExtension(dependingOn: node.explicitRewriterArgument, in: context, with: """
extension \(type): SwiftSyntaxCorrectableRule {
func makeRewriter(file: SwiftLintFile) -> (some ViolationsSyntaxRewriter)? {
Rewriter(
locationConverter: file.locationConverter,
disabledRegions: disabledRegions(file: file)
)
}
}
"""
)
].compactMap { $0 }
}

private static func createFoldingPreprocessor(
type: some TypeSyntaxProtocol,
foldArgument: ExprSyntax?
private static func makeExtension(
dependingOn argument: ExprSyntax?,
in context: some MacroExpansionContext,
with content: SyntaxNodeString
) throws -> ExtensionDeclSyntax? {
guard foldArgument.isTrueLiteral else {
return nil
}
return try ExtensionDeclSyntax("""
extension \(type) {
func preprocess(file: SwiftLintFile) -> SourceFileSyntax? {
file.foldedSyntaxTree
if let argument {
if argument.isBooleanLiteral {
if argument.isTrueLiteral {
return try ExtensionDeclSyntax(content)
}
} else {
context.diagnose(SwiftLintCoreMacroError.noBooleanLiteral.diagnose(at: argument))
}
""")
}

private static func createRewriter(
type: some TypeSyntaxProtocol,
rewriterArgument: ExprSyntax?
) throws -> ExtensionDeclSyntax? {
guard rewriterArgument.isTrueLiteral else {
return nil
}
return try ExtensionDeclSyntax("""
extension \(type): SwiftSyntaxCorrectableRule {
func makeRewriter(file: SwiftLintFile) -> (some ViolationsSyntaxRewriter)? {
Rewriter(locationConverter: file.locationConverter, disabledRegions: disabledRegions(file: file))
}
}
""")
return nil
}
}

Expand All @@ -73,8 +77,12 @@ private extension AttributeSyntax {
}
}

private extension ExprSyntax? {
private extension ExprSyntax {
var isBooleanLiteral: Bool {
`is`(BooleanLiteralExprSyntax.self)
}

var isTrueLiteral: Bool {
self?.as(BooleanLiteralExprSyntax.self)?.literal.text == "true"
`as`(BooleanLiteralExprSyntax.self)?.literal.text == "true"
}
}
11 changes: 9 additions & 2 deletions Tests/MacroTests/SwiftSyntaxRuleTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ final class SwiftSyntaxRuleTests: XCTestCase {
extension Hello: SwiftSyntaxCorrectableRule {
func makeRewriter(file: SwiftLintFile) -> (some ViolationsSyntaxRewriter)? {
Rewriter(locationConverter: file.locationConverter, disabledRegions: disabledRegions(file: file))
Rewriter(
locationConverter: file.locationConverter,
disabledRegions: disabledRegions(file: file)
)
}
}
""",
Expand All @@ -77,7 +80,7 @@ final class SwiftSyntaxRuleTests: XCTestCase {
}

func testArbitraryArguments() {
// Silently fail because the macro definition explicitly requires a bool arguments.
// Fail with a diagnostic because the macro definition explicitly requires bool arguments.
assertMacroExpansion(
"""
@SwiftSyntaxRule(foldExpressions: variable, explicitRewriter: variable)
Expand All @@ -92,6 +95,10 @@ final class SwiftSyntaxRuleTests: XCTestCase {
}
}
""",
diagnostics: [
DiagnosticSpec(message: SwiftLintCoreMacroError.noBooleanLiteral.message, line: 1, column: 35),
DiagnosticSpec(message: SwiftLintCoreMacroError.noBooleanLiteral.message, line: 1, column: 63)
],
macros: macros
)
}
Expand Down

0 comments on commit b87467f

Please sign in to comment.