Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configure expression folding by argument of @SwiftSyntaxRule macro #5255

Merged
merged 1 commit into from
Oct 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import SwiftSyntax

@Fold
@SwiftSyntaxRule
@SwiftSyntaxRule(foldExpressions: true)
struct LegacyMultipleRule: OptInRule, ConfigurationProviderRule {
var configuration = SeverityConfiguration<Self>(.warning)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import SwiftSyntax

@Fold
@SwiftSyntaxRule
@SwiftSyntaxRule(foldExpressions: true)
struct IdenticalOperandsRule: ConfigurationProviderRule, OptInRule {
var configuration = SeverityConfiguration<Self>(.warning)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import SwiftSyntax

@Fold
@SwiftSyntaxRule
@SwiftSyntaxRule(foldExpressions: true)
struct ContainsOverFirstNotNilRule: OptInRule, ConfigurationProviderRule {
var configuration = SeverityConfiguration<Self>(.warning)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import SwiftSyntax

@Fold
@SwiftSyntaxRule
@SwiftSyntaxRule(foldExpressions: true)
struct ContainsOverRangeNilComparisonRule: OptInRule, ConfigurationProviderRule {
var configuration = SeverityConfiguration<Self>(.warning)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import SwiftSyntax

@Fold
struct EmptyCountRule: ConfigurationProviderRule, OptInRule, SwiftSyntaxRule {
var configuration = EmptyCountConfiguration()

Expand Down Expand Up @@ -37,6 +36,10 @@ struct EmptyCountRule: ConfigurationProviderRule, OptInRule, SwiftSyntaxRule {
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
Visitor(onlyAfterDot: configuration.onlyAfterDot)
}

func preprocess(file: SwiftLintFile) -> SourceFileSyntax? {
file.foldedSyntaxTree
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might be able to find a solution for this explicit implementation as well. Configurations need to be passed to the visitor somehow automatically.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's done with #5275.

}

private extension EmptyCountRule {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import SwiftSyntax

@Fold
@SwiftSyntaxRule
@SwiftSyntaxRule(foldExpressions: true)
struct ShorthandOperatorRule: ConfigurationProviderRule {
var configuration = SeverityConfiguration<Self>(.error)

Expand Down
14 changes: 4 additions & 10 deletions Source/SwiftLintCore/Helpers/Macros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,10 @@ public macro MakeAcceptableByConfigurationElement() = #externalMacro(
)

/// Macro that adds a conformance to the `SwiftSyntaxRule` protocol and a default `makeVisitor(file:)` implementation
/// that creates a visitor defined in the same file.
@attached(extension, conformances: SwiftSyntaxRule, names: named(makeVisitor(file:)))
public macro SwiftSyntaxRule() = #externalMacro(
/// that creates a visitor defined in the same file. It also adds an implementation of `preprocess(file:)` which folds
/// expressions if the `foldExpressions` argument is set.
@attached(extension, conformances: SwiftSyntaxRule, names: named(makeVisitor(file:)), named(preprocess(file:)))
public macro SwiftSyntaxRule(foldExpressions: Bool = false) = #externalMacro(
module: "SwiftLintCoreMacros",
type: "SwiftSyntaxRule"
)

/// Macro that preprocesses the file by folding its operators before processing it for rule violations.
@attached(extension, names: named(preprocess(file:)))
public macro Fold() = #externalMacro(
module: "SwiftLintCoreMacros",
type: "Fold"
)
22 changes: 0 additions & 22 deletions Source/SwiftLintCoreMacros/Fold.swift

This file was deleted.

1 change: 0 additions & 1 deletion Source/SwiftLintCoreMacros/SwiftLintCoreMacros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import SwiftSyntaxMacros
struct SwiftLintCoreMacros: CompilerPlugin {
let providingMacros: [Macro.Type] = [
AutoApply.self,
Fold.self,
MakeAcceptableByConfigurationElement.self,
SwiftSyntaxRule.self
]
Expand Down
34 changes: 34 additions & 0 deletions Source/SwiftLintCoreMacros/SwiftSyntaxRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,42 @@ struct SwiftSyntaxRule: ExtensionMacro {
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
Visitor(viewMode: .sourceAccurate)
}
\(createFoldingPreprocessor(from: node.foldArgument))
}
""")
]
}

private static func createFoldingPreprocessor(from foldArgument: ExprSyntax?) -> DeclSyntax {
guard let foldArgument else {
return ""
}
if let booleanLiteral = foldArgument.as(BooleanLiteralExprSyntax.self)?.literal {
if booleanLiteral.text == "true" {
return """
func preprocess(file: SwiftLintFile) -> SourceFileSyntax? {
file.foldedSyntaxTree
}
"""
}
if booleanLiteral.text == "false" {
return ""
}
}
return """
func preprocess(file: SwiftLintFile) -> SourceFileSyntax? {
if \(foldArgument) { file.foldedSyntaxTree } else { nil }
}
"""
}
}

private extension AttributeSyntax {
var foldArgument: ExprSyntax? {
if case let .argumentList(args) = arguments, let first = args.first, first.label?.text == "foldExpressions" {
first.expression
} else {
nil
}
}
}
47 changes: 0 additions & 47 deletions Tests/MacroTests/MacroTests.swift

This file was deleted.

97 changes: 97 additions & 0 deletions Tests/MacroTests/SwiftSyntaxRuleTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
@testable import SwiftLintCoreMacros
import SwiftSyntaxMacrosTestSupport
import XCTest

private let macros = [
"SwiftSyntaxRule": SwiftSyntaxRule.self
]

final class SwiftSyntaxRuleTests: XCTestCase {
func testNoFoldArgument() {
assertMacroExpansion(
"""
@SwiftSyntaxRule
struct Hello {}
""",
expandedSource: """
struct Hello {}

extension Hello: SwiftSyntaxRule {
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
Visitor(viewMode: .sourceAccurate)
}

}
""",
macros: macros
)
}

func testFalseFoldArgument() {
assertMacroExpansion(
"""
@SwiftSyntaxRule(foldExpressions: false)
struct Hello {}
""",
expandedSource: """
struct Hello {}

extension Hello: SwiftSyntaxRule {
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
Visitor(viewMode: .sourceAccurate)
}

}
""",
macros: macros
)
}

func testTrueFoldArgument() {
assertMacroExpansion(
"""
@SwiftSyntaxRule(foldExpressions: true)
struct Hello {}
""",
expandedSource: """
struct Hello {}

extension Hello: SwiftSyntaxRule {
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
Visitor(viewMode: .sourceAccurate)
}
func preprocess(file: SwiftLintFile) -> SourceFileSyntax? {
file.foldedSyntaxTree
}
}
""",
macros: macros
)
}

func testArbitraryFoldArgument() {
assertMacroExpansion(
"""
@SwiftSyntaxRule(foldExpressions: variable)
struct Hello {}
""",
expandedSource: """
struct Hello {}

extension Hello: SwiftSyntaxRule {
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
Visitor(viewMode: .sourceAccurate)
}
func preprocess(file: SwiftLintFile) -> SourceFileSyntax? {
if variable {
file.foldedSyntaxTree
} else {
nil
}
}
}
""",
macros: macros
)
}
}