diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..de27119 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +# editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true +insert_final_newline = true +end_of_line = lf diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 386c826..89f323e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,39 +18,46 @@ env: jobs: macos: - name: macOS - runs-on: macos-13 + name: macOS (Swift ${{ matrix.swift }}) + runs-on: macos-15 strategy: + fail-fast: false matrix: - swift-syntax-version: - [ - "509.0.0..<510.0.0", - "510.0.0..<511.0.0", - "511.0.0..<601.0.0", - "601.0.0..<602.0.0", - ] - + platform: + - macOS + swift: + - "6.0" + - "6.1" + - "6.2" steps: - - uses: actions/checkout@v4 - - name: Select Xcode 15 - run: sudo xcode-select -s /Applications/Xcode_15.0.app - - name: Set SWIFT_SYNTAX_VERSION environment variable - run: echo "SWIFT_SYNTAX_VERSION=${{ matrix.swift-syntax-version }}" >> $GITHUB_ENV - - name: Resolve Dependencies - run: swift package resolve - - name: Run tests - run: swift test + - name: Git Checkout + uses: actions/checkout@v5 + + - name: Run Tests + uses: mxcl/xcodebuild@v3 + with: + platform: ${{ matrix.platform }} + swift: ~${{ matrix.swift }} + action: test + verbosity: xcbeautify linux: - name: Linux + name: Linux (Swift ${{ matrix.swift }}) runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + swift: + - "6.0" + - "6.1" + - "6.2" + container: + image: swift:${{ matrix.swift }} steps: - - name: Install Swift - uses: swift-actions/setup-swift@v2 - with: - swift-version: "6.0.2" - - uses: actions/checkout@v4 - - name: Run tests + - name: Git Checkout + uses: actions/checkout@v5 + + - name: Run Tests run: swift test # NB: 5.9 snapshot unavailable, wait for release diff --git a/Makefile b/Makefile deleted file mode 100644 index 320b0b6..0000000 --- a/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -default: test - -test: test-swift - -test-swift: - swift test --parallel - -# Remove build artifacts while tolerating SourceKit-locked files -clean: - @echo "Cleaning build artifacts..." - @rm -rf .build/* 2>&1 | grep -v "Permission denied" || true - @echo "Checking remaining build artifacts..." - @if [ -d .build ]; then \ - ls -R .build 2>/dev/null || echo "Unable to list some directories"; \ - else \ - echo "Build directory completely removed"; \ - fi - @echo "Build artifacts cleaned (ensure that any remaining files listed above won't affect new builds, e.g. SourceKit files)" - -test-swift-syntax-versions: - @for version in \ - "509.0.0..<510.0.0" \ - "510.0.0..<511.0.0" \ - "511.0.0..<601.0.0"; \ - do \ - echo "\n## Testing SwiftSyntax version $$version"; \ - $(MAKE) clean; \ - SWIFT_SYNTAX_VERSION="$$version" $(MAKE) test-swift || exit 1; \ - done - -format: - swift format \ - --ignore-unparsable-files \ - --in-place \ - --recursive \ - ./Package.swift ./Sources ./Tests - -.PHONY: default test test-swift clean test-swift-syntax-versions format diff --git a/Package.resolved b/Package.resolved index 41575b3..381d768 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,4 +1,5 @@ { + "originHash" : "a8fbbbecac50c00f4999452280b608b2e7c91e6f6d2aaf6a029ec0f8cc6dd8b8", "pins" : [ { "identity" : "swift-custom-dump", @@ -9,13 +10,22 @@ "version" : "1.3.3" } }, + { + "identity" : "swift-macro-testing", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-macro-testing", + "state" : { + "revision" : "9ab11325daa51c7c5c10fcf16c92bac906717c7e", + "version" : "0.6.4" + } + }, { "identity" : "swift-snapshot-testing", "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/swift-snapshot-testing", "state" : { - "revision" : "37230a37e83f1b7023be08e1b1a2603fcb1567fb", - "version" : "1.18.4" + "revision" : "a8b7c5e0ed33d8ab8887d1654d9b59f2cbad529b", + "version" : "1.18.7" } }, { @@ -23,8 +33,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swiftlang/swift-syntax", "state" : { - "revision" : "f99ae8aa18f0cf0d53481901f88a0991dc3bd4a2", - "version" : "601.0.1" + "revision" : "4799286537280063c85a32f09884cfbca301b1a1", + "version" : "602.0.0" } }, { @@ -32,10 +42,10 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/pointfreeco/xctest-dynamic-overlay", "state" : { - "revision" : "39de59b2d47f7ef3ca88a039dff3084688fe27f4", - "version" : "1.5.2" + "revision" : "4c27acf5394b645b70d8ba19dc249c0472d5f618", + "version" : "1.7.0" } } ], - "version" : 2 + "version" : 3 } diff --git a/Package.swift b/Package.swift index 222709c..9e8862e 100644 --- a/Package.swift +++ b/Package.swift @@ -1,11 +1,10 @@ -// swift-tools-version: 5.9 +// swift-tools-version: 6.0 import CompilerPluginSupport -import Foundation import PackageDescription let package = Package( - name: "MemberwiseInit", + name: "swift-memberwise-init-macro", platforms: [ .iOS(.v13), .macOS(.v10_15), @@ -23,15 +22,8 @@ let package = Package( ), ], dependencies: [ - .package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.18.1"), - //.conditionalPackage(url: "https://github.com/swiftlang/swift-syntax", envVar: "SWIFT_SYNTAX_VERSION", default: "509.0.0..<510.0.0") - //.conditionalPackage(url: "https://github.com/swiftlang/swift-syntax", envVar: "SWIFT_SYNTAX_VERSION", default: "510.0.0..<511.0.0") - //.conditionalPackage(url: "https://github.com/swiftlang/swift-syntax", envVar: "SWIFT_SYNTAX_VERSION", default: "511.0.0..<601.0.0-prerelease") - .conditionalPackage( - url: "https://github.com/swiftlang/swift-syntax", - envVar: "SWIFT_SYNTAX_VERSION", - default: "509.0.0..<602.0.0" - ), + .package(url: "https://github.com/pointfreeco/swift-macro-testing", from: "0.6.0"), + .package(url: "https://github.com/swiftlang/swift-syntax", "600.0.0"..<"603.0.0"), ], targets: [ .macro( @@ -53,78 +45,9 @@ let package = Package( name: "MemberwiseInitTests", dependencies: [ "MemberwiseInitMacros", - "MacroTesting", - .product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"), - ] - ), - .target( - name: "MacroTesting", - dependencies: [ - .product(name: "InlineSnapshotTesting", package: "swift-snapshot-testing"), - .product(name: "SwiftDiagnostics", package: "swift-syntax"), - .product(name: "SwiftOperators", package: "swift-syntax"), - .product(name: "SwiftParserDiagnostics", package: "swift-syntax"), - .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), - .product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"), - ] - ), - .testTarget( - name: "MacroTestingTests", - dependencies: [ - "MacroTesting" + .product(name: "MacroTesting", package: "swift-macro-testing"), + .product(name: "SwiftCompilerPlugin", package: "swift-syntax"), ] ), ] ) - -extension Package.Dependency { - /// Creates a dependency based on an environment variable or a default version range. - /// - /// This function allows dynamically setting the version range of a package dependency via an environment variable. - /// If the environment variable is not set, it falls back to a specified default version range. - /// - /// - Parameters: - /// - url: The URL of the package repository. - /// - envVar: The name of the environment variable that contains the version range. - /// - versionExpression: The default version range in case the environment variable is not set. - /// Example format: `"509.0.0..<511.0.0"` or `"509.0.0...510.0.0"`. - /// - Returns: A `Package.Dependency` configured with the specified or default version range. - /// - Throws: A fatal error if the version expression format is invalid or the range operator is unsupported. - /// - static func conditionalPackage( - url: String, - envVar: String, - default versionExpression: String - ) -> Package.Dependency { - let versionRangeString = ProcessInfo.processInfo.environment[envVar] ?? versionExpression - let (lower, op, upper) = parseVersionExpression(from: versionRangeString) - if op == "..<" { - return .package(url: url, lower.. (Version, String, Version) { - let rangeOperators = ["..<", "..."] - for op in rangeOperators { - if expression.contains(op) { - let parts = expression.split(separator: op, maxSplits: 1, omittingEmptySubsequences: true) - .map(String.init) - guard - parts.count == 2, - let lower = Version(parts[0]), - let upper = Version(parts[1]) - else { - fatalError("Invalid version expression format: \(expression)") - } - return (lower, op, upper) - } - } - fatalError("No valid range operator found in expression: \(expression)") - } -} diff --git a/README.md b/README.md index 62e0657..e616866 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ A Swift Macro for enhanced automatic memberwise initializers, greatly reducing m Informed by explicit developer cues, MemberwiseInit can more often automatically provide your intended memberwise `init`, while maintaining a safe-by-default standard in line with [Swift’s memberwise initializers][swifts-memberwise-init]. > [!IMPORTANT] -> `@MemberwiseInit` is a Swift Macro requiring **swift-tools-version: 5.9** or later (**Xcode 15** onwards). +> `@MemberwiseInit` is a Swift Macro requiring **swift-tools-version: 6.0** or later (**Xcode 16** onwards). * [Quick start](#quick-start) * [Quick reference](#quick-reference) diff --git a/Sources/MacroTesting/AssertMacro.swift b/Sources/MacroTesting/AssertMacro.swift deleted file mode 100644 index 8699834..0000000 --- a/Sources/MacroTesting/AssertMacro.swift +++ /dev/null @@ -1,897 +0,0 @@ -import InlineSnapshotTesting -@_spi(Internals) import SnapshotTesting -import SwiftDiagnostics -import SwiftOperators -import SwiftParser -import SwiftParserDiagnostics -import SwiftSyntax -import SwiftSyntaxMacroExpansion -import SwiftSyntaxMacros -import XCTest - -#if canImport(Testing) - import Testing -#endif - -//let compilableUsages = { -// assertMacro { "" } -// assertMacro { "" } expansion: { "" } -// assertMacro { "" } expansion: { "" } diagnostics: { "" } -// assertMacro { "" } expansion: { "" } diagnostics: { "" } fixes: { "" } -// assertMacro { "" } diagnostics: { "" } -// assertMacro { "" } diagnostics: { "" } fixes: { "" } -// -// // technically allowed by signature -// assertMacro { "" } fixes: { "" } -// -// // old usage -// assertMacro { "" } diagnostics: { "" } expansion: { "" } -// assertMacro { "" } fixes: { "" } expansion: { "" } -// assertMacro { "" } diagnostics: { "" } fixes: { "" } expansion: { "" } -// -// return false -//}() - -// Allow previous usage to compile during migration. -// Fail any test where `expansion` is given after `diagnostics`/`fixes`. -@_disfavoredOverload -public func assertMacro( - _ macros: [String: Macro.Type]? = nil, - indentationWidth: Trivia? = nil, - record: SnapshotTestingConfiguration.Record? = nil, - of originalSource: () throws -> String, - diagnostics diagnosedSource: (() -> String)? = nil, - fixes fixedSource: (() -> String)? = nil, - expansion expandedSource: (() -> String)? = nil, - fileID: StaticString = #fileID, - file filePath: StaticString = #filePath, - function: StaticString = #function, - line: UInt = #line, - column: UInt = #column -) { - if expandedSource != nil, - diagnosedSource != nil || fixedSource != nil - { - recordIssue( - "`expansion` must come before both `diagnostics` and `fixes`", - fileID: fileID, - filePath: filePath, - line: line, - column: column - ) - return - } - assertMacro( - macros, - indentationWidth: indentationWidth, - record: record, - of: originalSource, - expansion: expandedSource, - diagnostics: diagnosedSource, - fixes: fixedSource, - fileID: fileID, - file: filePath, - function: function, - line: line, - column: column - ) -} - -/// Asserts that a given Swift source string matches an expected string with all macros expanded. -/// -/// To write a macro assertion, you simply pass the mapping of macros to expand along with the -/// source code that should be expanded: -/// -/// ```swift -/// func testMacro() { -/// assertMacro(["stringify": StringifyMacro.self]) { -/// """ -/// #stringify(a + b) -/// """ -/// } -/// } -/// ``` -/// -/// When this test is run, the result of the expansion is automatically written to the test file, -/// inlined, as a trailing argument: -/// -/// ```swift -/// func testMacro() { -/// assertMacro(["stringify": StringifyMacro.self]) { -/// """ -/// #stringify(a + b) -/// """ -/// } expansion: { -/// """ -/// (a + b, "a + b") -/// """ -/// } -/// } -/// ``` -/// -/// If the expansion fails, diagnostics are inlined instead: -/// -/// ```swift -/// assertMacro(["MetaEnum": MetaEnumMacro.self]) { -/// """ -/// @MetaEnum struct Cell { -/// let integer: Int -/// let text: String -/// let boolean: Bool -/// } -/// """ -/// } diagnostics: { -/// """ -/// @MetaEnum struct Cell { -/// ┬──────── -/// ╰─ 🛑 '@MetaEnum' can only be attached to an enum, not a struct -/// let integer: Int -/// let text: String -/// let boolean: Bool -/// } -/// """ -/// } -/// ``` -/// -/// > Tip: Use ``withMacroTesting(indentationWidth:isRecording:macros:operation:)-5id9j`` in your -/// > test case's `invokeTest` to avoid the repetitive work of passing the macro mapping to every -/// > `assertMacro`: -/// > -/// > ```swift -/// > override func invokeTest() { -/// > // By wrapping each test with macro testing configuration... -/// > withMacroTesting(macros: ["stringify": StringifyMacro.self]) { -/// > super.invokeTest() -/// > } -/// > } -/// > -/// > func testMacro() { -/// > assertMacro { // ...we can omit it from the assertion. -/// > """ -/// > #stringify(a + b) -/// > """ -/// > } expansion: { -/// > """ -/// > (a + b, "a + b") -/// > """ -/// > } -/// > } -/// > ``` -/// -/// - Parameters: -/// - macros: The macros to expand in the original source string. Required, either implicitly via -/// ``withMacroTesting(indentationWidth:isRecording:macros:operation:)-5id9j``, or explicitly -/// via this parameter. -/// - applyFixIts: Whether to assert the application of fix-its. Defaults to `true`. -/// - indentationWidth: The `Trivia` for setting indentation during macro expansion -/// (e.g., `.spaces(2)`). Defaults to the original source's indentation if unspecified. If the -/// original source lacks indentation, it defaults to `.spaces(4)`. -/// - isRecording: Always records new snapshots when enabled. -/// - originalSource: A string of Swift source code. -/// - diagnosedSource: Swift source code annotated with expected diagnostics. -/// - fixedSource: Swift source code with expected fix-its applied. -/// - expandedSource: Expected Swift source string with macros expanded. -/// - file: The file where the assertion occurs. The default is the filename of the test case -/// where you call this function. -/// - function: The function where the assertion occurs. The default is the name of the test -/// method where you call this function. -/// - line: The line where the assertion occurs. The default is the line number where you call -/// this function. -/// - column: The column where the assertion occurs. The default is the column where you call this -/// function. -public func assertMacro( - _ macros: [String: Macro.Type]? = nil, - applyFixIts: Bool = true, - indentationWidth: Trivia? = nil, - record: SnapshotTestingConfiguration.Record? = nil, - of originalSource: () throws -> String, - expansion expandedSource: (() -> String)? = nil, - diagnostics diagnosedSource: (() -> String)? = nil, - fixes fixedSource: (() -> String)? = nil, - fileID: StaticString = #fileID, - file filePath: StaticString = #filePath, - function: StaticString = #function, - line: UInt = #line, - column: UInt = #column -) { - var indentationWidth = - indentationWidth - ?? MacroTestingConfiguration.current.indentationWidth - var macros = - macros - ?? MacroTestingConfiguration.current.macros - var record = - record - ?? SnapshotTestingConfiguration.current?.record - #if canImport(Testing) - indentationWidth = - indentationWidth - ?? Test.current?.indentationWidth - macros = - macros - ?? Test.current?.macros - record = - record - ?? Test.current?.record - #endif - withMacroTesting(indentationWidth: indentationWidth, record: record, macros: macros) { - #if canImport(Testing) - let macros = macros ?? MacroTestingConfiguration.current.macros ?? Test.current?.macros - #else - let macros = macros ?? MacroTestingConfiguration.current.macros - #endif - guard let macros, !macros.isEmpty else { - recordIssue( - """ - No macros configured for this assertion. Pass a mapping to this function, e.g.: - - assertMacro(["stringify": StringifyMacro.self]) { … } - - Or wrap your assertion using 'withMacroTesting', e.g. in 'invokeTest': - - class StringifyMacroTests: XCTestCase { - override func invokeTest() { - withMacroTesting(macros: ["stringify": StringifyMacro.self]) { - super.invokeTest() - } - } - … - } - """, - fileID: fileID, - filePath: filePath, - line: line, - column: column - ) - return - } - - do { - var origSourceFile = Parser.parse(source: try originalSource()) - if let foldedSourceFile = try OperatorTable.standardOperators.foldAll(origSourceFile) - .as( - SourceFileSyntax.self - ) - { - origSourceFile = foldedSourceFile - } - - let origDiagnostics = ParseDiagnosticsGenerator.diagnostics(for: origSourceFile) - let indentationWidth = - indentationWidth - ?? MacroTestingConfiguration.current.indentationWidth - ?? Trivia( - stringLiteral: String( - SourceLocationConverter(fileName: "-", tree: origSourceFile).sourceLines - .first(where: { $0.first?.isWhitespace == true && $0 != "\n" })? - .prefix(while: { $0.isWhitespace }) - ?? " " - ) - ) - - let context = BasicMacroExpansionContext( - sourceFiles: [ - origSourceFile: .init(moduleName: "TestModule", fullFilePath: "Test.swift") - ] - ) - #if canImport(SwiftSyntax600) - let expandedSourceFile = origSourceFile.expand( - macros: macros, - contextGenerator: { syntax in - BasicMacroExpansionContext( - sharingWith: context, lexicalContext: syntax.allMacroLexicalContexts()) - }, - indentationWidth: indentationWidth - ) - #else - let expandedSourceFile = origSourceFile.expand( - macros: macros, - in: context, - indentationWidth: indentationWidth - ) - #endif - - var offset = 0 - - func anchor(_ diag: Diagnostic) -> Diagnostic { - let location = context.location( - for: diag.position, anchoredAt: diag.node, fileName: "") - return Diagnostic( - node: diag.node, - position: AbsolutePosition(utf8Offset: location.offset), - message: diag.diagMessage, - highlights: diag.highlights, - notes: diag.notes, - fixIts: diag.fixIts - ) - } - - // TODO: write a test where didExpand returns false - // For now, covered in MemberwiseInitTests.testAppliedToEnum_FailsWithDiagnostic - var didExpand: Bool { - let removingWhere: (AttributeSyntax) -> Bool = { - guard let name = $0.attributeName.as(IdentifierTypeSyntax.self)?.name.text - else { return false } - return macros.keys.contains(name) - } - - #if canImport(SwiftSyntax600) - let remover = MacroTesting.AttributeRemover600(removingWhere: removingWhere) - #else - let remover = MacroTesting.AttributeRemover509(removingWhere: removingWhere) - #endif - - let removedSource = remover.rewrite(origSourceFile) - - return expandedSourceFile.description.trimmingCharacters(in: .newlines) - != removedSource.description.trimmingCharacters(in: .newlines) - } - - if didExpand { - offset += 1 - assertInlineSnapshot( - of: expandedSourceFile.description.trimmingCharacters(in: .newlines), - as: ._lines, - message: """ - Expanded output (\(newPrefix)) differed from expected output (\(oldPrefix)). \ - Difference: … - """, - syntaxDescriptor: InlineSnapshotSyntaxDescriptor( - deprecatedTrailingClosureLabels: ["matches"], - trailingClosureLabel: "expansion", - trailingClosureOffset: offset - ), - matches: expandedSource, - fileID: fileID, - file: filePath, - function: function, - line: line, - column: column - ) - } else if expandedSource != nil { - offset += 1 - assertInlineSnapshot( - of: nil, - as: ._lines, - message: """ - Expanded output (\(newPrefix)) differed from expected output (\(oldPrefix)). \ - Difference: … - """, - syntaxDescriptor: InlineSnapshotSyntaxDescriptor( - deprecatedTrailingClosureLabels: ["matches"], - trailingClosureLabel: "expansion", - trailingClosureOffset: offset - ), - matches: expandedSource, - fileID: fileID, - file: filePath, - function: function, - line: line, - column: column - ) - } - - let allDiagnostics: [Diagnostic] = origDiagnostics + context.diagnostics - if !allDiagnostics.isEmpty || diagnosedSource != nil { - offset += 1 - - let converter = SourceLocationConverter(fileName: "-", tree: origSourceFile) - let lineCount = converter.location(for: origSourceFile.endPosition).line - let diagnostics = - DiagnosticsFormatter - .annotatedSource( - tree: origSourceFile, - diags: allDiagnostics.map(anchor), - context: context, - contextSize: lineCount - ) - .description - .replacingOccurrences( - of: #"(^|\n) *\d* +│ "#, with: "$1", options: .regularExpression - ) - .trimmingCharacters(in: .newlines) - - assertInlineSnapshot( - of: diagnostics, - as: ._lines, - message: """ - Diagnostic output (\(newPrefix)) differed from expected output (\(oldPrefix)). \ - Difference: … - """, - syntaxDescriptor: InlineSnapshotSyntaxDescriptor( - deprecatedTrailingClosureLabels: ["matches"], - trailingClosureLabel: "diagnostics", - trailingClosureOffset: offset - ), - matches: diagnosedSource, - file: filePath, - function: function, - line: line, - column: column - ) - } else if diagnosedSource != nil { - offset += 1 - assertInlineSnapshot( - of: nil, - as: ._lines, - message: """ - Diagnostic output (\(newPrefix)) differed from expected output (\(oldPrefix)). \ - Difference: … - """, - syntaxDescriptor: InlineSnapshotSyntaxDescriptor( - deprecatedTrailingClosureLabels: ["matches"], - trailingClosureLabel: "diagnostics", - trailingClosureOffset: offset - ), - matches: diagnosedSource, - fileID: fileID, - file: filePath, - function: function, - line: line, - column: column - ) - } - - if applyFixIts && !allDiagnostics.isEmpty - && allDiagnostics.contains(where: { !$0.fixIts.isEmpty }) - { - offset += 1 - - let diagnostics = allDiagnostics.filter { !$0.fixIts.isEmpty } - - // NB: Only one of the fix-its can be applied at a time--they can be exclustive, e.g. two - // options for how to resolve an issue. - var fixedSourceLines = [String]() - for (diagnosticIndex, diagnostic) in diagnostics.enumerated() { - let diagnosticsString = - DiagnosticsFormatter - .annotatedSource( - tree: origSourceFile, - diags: [diagnostic].map(anchor), - context: context, - contextSize: 0 - ) - .description - .replacingOccurrences( - of: #"(^|\n) *\d* +│ "#, with: "$1", options: .regularExpression - ) - .trimmingCharacters(in: .newlines) - - let lines = diagnosticsString.split(separator: "\n") - let extraLeadingWhitespace = - lines.first?.prefix(while: \.isWhitespace).count ?? 0 - var fixIts = diagnostic.fixIts - for (lineNumber, line) in lines.enumerated() { - if line.first(where: { !$0.isWhitespace }) != "✏️" { - let line = String(line.dropFirst(extraLeadingWhitespace)) - - if diagnosticIndex > 0 && lineNumber == 0 { - fixedSourceLines.append("") - } - fixedSourceLines.append(line) - continue - } - let line = String(line.drop(while: \.isWhitespace)) - - let fixIt = fixIts.removeFirst() - - let edits = fixIt.changes - .map { $0.edit(in: context) } - - var fixedSourceFile = Parser.parse( - source: FixItApplier.apply( - edits: edits, - to: origSourceFile - ) - .description - ) - - if let foldedSourceFile = try OperatorTable.standardOperators.foldAll( - fixedSourceFile - ) - .as(SourceFileSyntax.self) { - fixedSourceFile = foldedSourceFile - } - - fixedSourceLines.append("") - fixedSourceLines.append(line) - fixedSourceLines.append(fixedSourceFile.description) - } - } - - assertInlineSnapshot( - of: fixedSourceLines.joined(separator: "\n").trimmingCharacters(in: .newlines), - as: ._lines, - message: """ - Fixed output (\(newPrefix)) differed from expected output (\(oldPrefix)). \ - Difference: … - """, - syntaxDescriptor: InlineSnapshotSyntaxDescriptor( - trailingClosureLabel: "fixes", - trailingClosureOffset: offset - ), - matches: fixedSource, - file: filePath, - function: function, - line: line, - column: column - ) - } else if fixedSource != nil { - offset += 1 - assertInlineSnapshot( - of: nil, - as: ._lines, - message: """ - Fixed output (\(newPrefix)) differed from expected output (\(oldPrefix)). \ - Difference: … - """, - syntaxDescriptor: InlineSnapshotSyntaxDescriptor( - trailingClosureLabel: "fixes", - trailingClosureOffset: offset - ), - matches: fixedSource, - fileID: fileID, - file: filePath, - function: function, - line: line, - column: column - ) - } - } catch { - recordIssue( - "Threw error: \(error)", - fileID: fileID, - filePath: filePath, - line: line, - column: column - ) - } - } -} - -// From: https://github.com/swiftlang/swift-syntax/blob/601.0.1/Sources/SwiftSyntaxMacrosGenericTestSupport/Assertions.swift -extension FixIt.Change { - /// Returns the edit for this change, translating positions from detached nodes - /// to the corresponding locations in the original source file based on - /// `expansionContext`. - /// - /// - SeeAlso: `FixIt.Change.edit` - fileprivate func edit(in expansionContext: BasicMacroExpansionContext) -> SourceEdit { - switch self { - case .replace(let oldNode, let newNode): - let start = expansionContext.position(of: oldNode.position, anchoredAt: oldNode) - let end = expansionContext.position(of: oldNode.endPosition, anchoredAt: oldNode) - return SourceEdit( - range: start.. AbsolutePosition { - let location = self.location(for: position, anchoredAt: Syntax(node), fileName: "") - return AbsolutePosition(utf8Offset: location.offset) - } -} - -/// Asserts that a given Swift source string matches an expected string with all macros expanded. -/// -/// See ``assertMacro(_:indentationWidth:record:of:diagnostics:fixes:expansion:file:function:line:column:)-pkfi`` -/// for more details. -/// -/// - Parameters: -/// - macros: The macros to expand in the original source string. Required, either implicitly via -/// ``withMacroTesting(indentationWidth:isRecording:macros:operation:)-5id9j``, or explicitly -/// via this parameter. -/// - indentationWidth: The `Trivia` for setting indentation during macro expansion -/// (e.g., `.spaces(2)`). Defaults to the original source's indentation if unspecified. If the -/// original source lacks indentation, it defaults to `.spaces(4)`. -/// - isRecording: Always records new snapshots when enabled. -/// - originalSource: A string of Swift source code. -/// - diagnosedSource: Swift source code annotated with expected diagnostics. -/// - fixedSource: Swift source code with expected fix-its applied. -/// - expandedSource: Expected Swift source string with macros expanded. -/// - file: The file where the assertion occurs. The default is the filename of the test case -/// where you call this function. -/// - function: The function where the assertion occurs. The default is the name of the test -/// method where you call this function. -/// - line: The line where the assertion occurs. The default is the line number where you call -/// this function. -/// - column: The column where the assertion occurs. The default is the column where you call this -/// function. -public func assertMacro( - _ macros: [Macro.Type], - indentationWidth: Trivia? = nil, - record: SnapshotTestingConfiguration.Record? = nil, - of originalSource: () throws -> String, - expansion expandedSource: (() -> String)? = nil, - diagnostics diagnosedSource: (() -> String)? = nil, - fixes fixedSource: (() -> String)? = nil, - fileID: StaticString = #fileID, - file filePath: StaticString = #filePath, - function: StaticString = #function, - line: UInt = #line, - column: UInt = #column -) { - assertMacro( - Dictionary(macros: macros), - indentationWidth: indentationWidth, - record: record, - of: originalSource, - expansion: expandedSource, - diagnostics: diagnosedSource, - fixes: fixedSource, - fileID: fileID, - file: filePath, - function: function, - line: line, - column: column - ) -} - -/// Customizes `assertMacro` for the duration of an operation. -/// -/// Use this operation to customize how the `assertMacro` behaves in a test. It is most convenient -/// to use this tool to wrap `invokeTest` in a `XCTestCase` subclass so that the configuration -/// applies to every test method. -/// -/// For example, to specify which macros will be expanded during an assertion for an entire test -/// case you can do the following: -/// -/// ```swift -/// class StringifyTests: XCTestCase { -/// override func invokeTest() { -/// withMacroTesting(macros: [StringifyMacro.self]) { -/// super.invokeTest() -/// } -/// } -/// } -/// ``` -/// -/// And to re-record all macro expansions in a test case you can do the following: -/// -/// ```swift -/// class StringifyTests: XCTestCase { -/// override func invokeTest() { -/// withMacroTesting(isRecording: true, macros: [StringifyMacro.self]) { -/// super.invokeTest() -/// } -/// } -/// } -/// ``` -/// -/// - Parameters: -/// - indentationWidth: The `Trivia` for setting indentation during macro expansion -/// (e.g., `.spaces(2)`). Defaults to the original source's indentation if unspecified. If the -/// original source lacks indentation, it defaults to `.spaces(4)`. -/// - isRecording: Determines if a new macro expansion will be recorded. -/// - macros: Specifies the macros to be expanded in the input Swift source string. -/// - operation: The operation to run with the configuration updated. -public func withMacroTesting( - indentationWidth: Trivia? = nil, - record: SnapshotTestingConfiguration.Record? = nil, - macros: [String: Macro.Type]? = nil, - operation: () async throws -> R -) async rethrows -> R { - var configuration = MacroTestingConfiguration.current - if let indentationWidth { configuration.indentationWidth = indentationWidth } - if let macros { configuration.macros = macros } - return try await withSnapshotTesting(record: record) { - try await MacroTestingConfiguration.$current.withValue(configuration) { - try await operation() - } - } -} - -/// Customizes `assertMacro` for the duration of an operation. -/// -/// See ``withMacroTesting(indentationWidth:isRecording:macros:operation:)-5id9j`` for -/// more details. -/// -/// - Parameters: -/// - indentationWidth: The `Trivia` for setting indentation during macro expansion -/// (e.g., `.spaces(2)`). Defaults to the original source's indentation if unspecified. If the -/// original source lacks indentation, it defaults to `.spaces(4)`. -/// - isRecording: Determines if a new macro expansion will be recorded. -/// - macros: Specifies the macros to be expanded in the input Swift source string. -/// - operation: The operation to run with the configuration updated. -public func withMacroTesting( - indentationWidth: Trivia? = nil, - record: SnapshotTestingConfiguration.Record? = nil, - macros: [String: Macro.Type]? = nil, - operation: () throws -> R -) rethrows -> R { - var configuration = MacroTestingConfiguration.current - if let indentationWidth { configuration.indentationWidth = indentationWidth } - if let macros { configuration.macros = macros } - return try withSnapshotTesting(record: record) { - try MacroTestingConfiguration.$current.withValue(configuration) { - try operation() - } - } -} - -/// Customizes `assertMacro` for the duration of an operation. -/// -/// See ``withMacroTesting(indentationWidth:isRecording:macros:operation:)-5id9j`` for -/// more details. -/// -/// - Parameters: -/// - indentationWidth: The `Trivia` for setting indentation during macro expansion -/// (e.g., `.spaces(2)`). Defaults to the original source's indentation if unspecified. If the -/// original source lacks indentation, it defaults to `.spaces(4)`. -/// - isRecording: Determines if a new macro expansion will be recorded. -/// - macros: Specifies the macros to be expanded in the input Swift source string. -/// - operation: The operation to run with the configuration updated. -public func withMacroTesting( - indentationWidth: Trivia? = nil, - record: SnapshotTestingConfiguration.Record? = nil, - macros: [Macro.Type], - operation: () async throws -> R -) async rethrows -> R { - try await withMacroTesting( - indentationWidth: indentationWidth, - record: record, - macros: Dictionary(macros: macros), - operation: operation - ) -} - -/// Customizes `assertMacro` for the duration of an operation. -/// -/// See ``withMacroTesting(indentationWidth:isRecording:macros:operation:)-5id9j`` for -/// more details. -/// -/// - Parameters: -/// - indentationWidth: The `Trivia` for setting indentation during macro expansion -/// (e.g., `.spaces(2)`). Defaults to the original source's indentation if unspecified. If the -/// original source lacks indentation, it defaults to `.spaces(4)`. -/// - isRecording: Determines if a new macro expansion will be recorded. -/// - macros: Specifies the macros to be expanded in the input Swift source string. -/// - operation: The operation to run with the configuration updated. -public func withMacroTesting( - indentationWidth: Trivia? = nil, - record: SnapshotTestingConfiguration.Record? = nil, - macros: [Macro.Type], - operation: () throws -> R -) rethrows -> R { - try withMacroTesting( - indentationWidth: indentationWidth, - record: record, - macros: Dictionary(macros: macros), - operation: operation - ) -} - -extension Snapshotting where Value == String, Format == String { - fileprivate static let _lines = Snapshotting( - pathExtension: "txt", - diffing: Diffing( - toData: { Data($0.utf8) }, - fromData: { String(decoding: $0, as: UTF8.self) } - ) { old, new in - guard old != new else { return nil } - - let newLines = new.split(separator: "\n", omittingEmptySubsequences: false) - - let oldLines = old.split(separator: "\n", omittingEmptySubsequences: false) - let difference = newLines.difference(from: oldLines) - - var result = "" - - var insertions = [Int: Substring]() - var removals = [Int: Substring]() - - for change in difference { - switch change { - case let .insert(offset, element, _): - insertions[offset] = element - case let .remove(offset, element, _): - removals[offset] = element - } - } - - var oldLine = 0 - var newLine = 0 - - while oldLine < oldLines.count || newLine < newLines.count { - if let removal = removals[oldLine] { - result += "\(oldPrefix) \(removal)\n" - oldLine += 1 - } else if let insertion = insertions[newLine] { - result += "\(newPrefix) \(insertion)\n" - newLine += 1 - } else { - result += "\(prefix) \(oldLines[oldLine])\n" - oldLine += 1 - newLine += 1 - } - } - - let attachment = XCTAttachment( - data: Data(result.utf8), - uniformTypeIdentifier: "public.patch-file" - ) - return (result, [attachment]) - } - ) -} - -internal func macroName(className: String, isExpression: Bool) -> String { - var name = - className - .replacingOccurrences(of: "Macro$", with: "", options: .regularExpression) - if !name.isEmpty, isExpression { - var prefix = name.prefix(while: \.isUppercase) - if prefix.count > 1, name[prefix.endIndex...].first?.isLowercase == true { - prefix.removeLast() - } - name.replaceSubrange(prefix.startIndex.. Bool - - var triviaToAttachToNextToken: Trivia = Trivia() - - init(removingWhere predicate: @escaping (AttributeSyntax) -> Bool) { - self.predicate = predicate - } - - override func visit(_ node: AttributeListSyntax) -> AttributeListSyntax { - var filteredAttributes: [AttributeListSyntax.Element] = [] - for case .attribute(let attribute) in node { - if self.predicate(attribute) { - var leadingTrivia = node.leadingTrivia - if let lastNewline = leadingTrivia.pieces.lastIndex(where: { $0.isNewline }), - leadingTrivia.pieces[lastNewline...].allSatisfy(\.isWhitespace), - node.trailingTrivia.isEmpty, - node.nextToken(viewMode: .sourceAccurate)?.leadingTrivia.first?.isNewline ?? false - { - // If the attribute is on its own line based on the following conditions, - // remove the newline from it so we don’t end up with an empty line - // - Trailing trivia ends with a newline followed by arbitrary number of spaces or tabs - // - There is no trailing trivia and the next token starts on a new line - leadingTrivia = Trivia(pieces: leadingTrivia.pieces[.. TokenSyntax { - if !triviaToAttachToNextToken.isEmpty { - defer { triviaToAttachToNextToken = Trivia() } - return token.with(\.leadingTrivia, triviaToAttachToNextToken + token.leadingTrivia) - } else { - return token - } - } -} diff --git a/Sources/MacroTesting/Internal/AttributeRemover600.swift b/Sources/MacroTesting/Internal/AttributeRemover600.swift deleted file mode 100644 index e347761..0000000 --- a/Sources/MacroTesting/Internal/AttributeRemover600.swift +++ /dev/null @@ -1,114 +0,0 @@ -import SwiftSyntax - -public class AttributeRemover600: SyntaxRewriter { - let predicate: (AttributeSyntax) -> Bool - - var triviaToAttachToNextToken: Trivia = Trivia() - - /// Initializes an attribute remover with a given predicate to determine which attributes to remove. - /// - /// - Parameter predicate: A closure that determines whether a given `AttributeSyntax` should be removed. - /// If this closure returns `true` for an attribute, that attribute will be removed. - public init(removingWhere predicate: @escaping (AttributeSyntax) -> Bool) { - self.predicate = predicate - } - - public override func visit(_ node: AttributeListSyntax) -> AttributeListSyntax { - var filteredAttributes: [AttributeListSyntax.Element] = [] - for case .attribute(let attribute) in node { - if self.predicate(attribute) { - var leadingTrivia = attribute.leadingTrivia - - // Don't leave behind an empty line when the attribute being removed is on its own line, - // based on the following conditions: - // - Leading trivia ends with a newline followed by arbitrary number of spaces or tabs - // - All leading trivia pieces after the last newline are just whitespace, ensuring - // there are no comments or other non-whitespace characters on the same line - // preceding the attribute. - // - There is no trailing trivia and the next token has leading trivia. - if let lastNewline = leadingTrivia.pieces.lastIndex(where: \.isNewline), - leadingTrivia.pieces[lastNewline...].allSatisfy(\.isWhitespace), - attribute.trailingTrivia.isEmpty, - let nextToken = attribute.nextToken(viewMode: .sourceAccurate), - !nextToken.leadingTrivia.isEmpty - { - leadingTrivia = Trivia(pieces: leadingTrivia.pieces[.. TokenSyntax { - return prependAndClearAccumulatedTrivia(to: token) - } - - /// Prepends the accumulated trivia to the given node's leading trivia. - /// - /// To preserve correct formatting after attribute removal, this function reassigns - /// significant trivia accumulated from removed attributes to the provided subsequent node. - /// Once attached, the accumulated trivia is cleared. - /// - /// - Parameter node: The syntax node receiving the accumulated trivia. - /// - Returns: The modified syntax node with the prepended trivia. - private func prependAndClearAccumulatedTrivia(to syntaxNode: T) -> T { - defer { triviaToAttachToNextToken = Trivia() } - return syntaxNode.with(\.leadingTrivia, triviaToAttachToNextToken + syntaxNode.leadingTrivia) - } -} - -extension Trivia { - fileprivate func trimmingPrefix( - while predicate: (TriviaPiece) -> Bool - ) -> Trivia { - Trivia(pieces: self.drop(while: predicate)) - } - - fileprivate func trimmingSuffix( - while predicate: (TriviaPiece) -> Bool - ) -> Trivia { - Trivia( - pieces: self[...] - .reversed() - .drop(while: predicate) - .reversed() - ) - } - - fileprivate var startsWithNewline: Bool { - self.first?.isNewline ?? false - } -} diff --git a/Sources/MacroTesting/Internal/Deprecations.swift b/Sources/MacroTesting/Internal/Deprecations.swift deleted file mode 100644 index 33bc739..0000000 --- a/Sources/MacroTesting/Internal/Deprecations.swift +++ /dev/null @@ -1,235 +0,0 @@ -//import InlineSnapshotTesting -//@_spi(Internals) import SnapshotTesting -//import SwiftDiagnostics -//import SwiftOperators -//import SwiftParser -//import SwiftParserDiagnostics -//import SwiftSyntax -//import SwiftSyntaxMacroExpansion -//import SwiftSyntaxMacros -//import XCTest -// -//// MARK: Deprecated after 0.4.2 -// -//@available(iOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)") -//@available( -// macOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)" -//) -//@available(tvOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)") -//@available( -// visionOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)" -//) -//@available( -// watchOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)" -//) -//@_disfavoredOverload -//public func withMacroTesting( -// indentationWidth: Trivia? = nil, -// isRecording: Bool? = nil, -// macros: [String: Macro.Type]? = nil, -// operation: () async throws -> R -//) async rethrows { -// var configuration = MacroTestingConfiguration.current -// if let indentationWidth { configuration.indentationWidth = indentationWidth } -// let record: SnapshotTestingConfiguration.Record? = isRecording.map { $0 ? .all : .missing } -// if let macros { configuration.macros = macros } -// _ = try await withSnapshotTesting(record: record) { -// try await MacroTestingConfiguration.$current.withValue(configuration) { -// try await operation() -// } -// } -//} -// -//@available(iOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)") -//@available( -// macOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)" -//) -//@available(tvOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)") -//@available( -// visionOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)" -//) -//@available( -// watchOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)" -//) -//@_disfavoredOverload -//public func withMacroTesting( -// indentationWidth: Trivia? = nil, -// isRecording: Bool? = nil, -// macros: [String: Macro.Type]? = nil, -// operation: () throws -> R -//) rethrows { -// var configuration = MacroTestingConfiguration.current -// if let indentationWidth { configuration.indentationWidth = indentationWidth } -// let record: SnapshotTestingConfiguration.Record? = isRecording.map { $0 ? .all : .missing } -// if let macros { configuration.macros = macros } -// _ = try withSnapshotTesting(record: record) { -// try MacroTestingConfiguration.$current.withValue(configuration) { -// try operation() -// } -// } -//} -// -//@available(iOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)") -//@available( -// macOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)" -//) -//@available(tvOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)") -//@available( -// visionOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)" -//) -//@available( -// watchOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)" -//) -//@_disfavoredOverload -//public func withMacroTesting( -// indentationWidth: Trivia? = nil, -// isRecording: Bool? = nil, -// macros: [Macro.Type], -// operation: () async throws -> R -//) async rethrows { -// try await withMacroTesting( -// indentationWidth: indentationWidth, -// isRecording: isRecording, -// macros: Dictionary(macros: macros), -// operation: operation -// ) -//} -// -//@available(iOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)") -//@available( -// macOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)" -//) -//@available(tvOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)") -//@available( -// visionOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)" -//) -//@available( -// watchOS, deprecated, renamed: "withMacroTesting(indentationWidth:record:macros:operation:)" -//) -//@_disfavoredOverload -//public func withMacroTesting( -// indentationWidth: Trivia? = nil, -// isRecording: Bool? = nil, -// macros: [Macro.Type], -// operation: () throws -> R -//) rethrows { -// try withMacroTesting( -// indentationWidth: indentationWidth, -// isRecording: isRecording, -// macros: Dictionary(macros: macros), -// operation: operation -// ) -//} -// -//// MARK: Deprecated after 0.1.0 -// -//@available(*, deprecated, message: "Re-record this assertion") -//public func assertMacro( -// _ macros: [String: Macro.Type]? = nil, -// record isRecording: Bool? = nil, -// of originalSource: () throws -> String, -// matches expandedOrDiagnosedSource: () -> String, -// fileID: StaticString = #fileID, -// file filePath: StaticString = #filePath, -// function: StaticString = #function, -// line: UInt = #line, -// column: UInt = #column -//) { -// guard isRecording ?? (SnapshotTestingConfiguration.current?.record == .all) else { -// recordIssue( -// "Re-record this assertion", -// fileID: fileID, -// filePath: filePath, -// line: line, -// column: column -// ) -// return -// } -// assertMacro( -// macros, -// record: true, -// of: originalSource, -// file: filePath, -// function: function, -// line: line, -// column: column -// ) -//} -// -//@available(*, deprecated, message: "Re-record this assertion") -//public func assertMacro( -// _ macros: [Macro.Type], -// record isRecording: Bool? = nil, -// of originalSource: () throws -> String, -// matches expandedOrDiagnosedSource: () -> String, -// fileID: StaticString = #fileID, -// file filePath: StaticString = #filePath, -// function: StaticString = #function, -// line: UInt = #line, -// column: UInt = #column -//) { -// assertMacro( -// Dictionary(macros: macros), -// record: isRecording, -// of: originalSource, -// matches: expandedOrDiagnosedSource, -// fileID: fileID, -// file: filePath, -// function: function, -// line: line, -// column: column -// ) -//} -// -//@available( -// *, deprecated, message: "Delete 'applyFixIts' and 'matches' and re-record this assertion" -//) -//public func assertMacro( -// _ macros: [String: Macro.Type]? = nil, -// applyFixIts: Bool, -// record isRecording: Bool? = nil, -// of originalSource: () throws -> String, -// matches expandedOrDiagnosedSource: () -> String, -// fileID: StaticString = #fileID, -// file filePath: StaticString = #filePath, -// function: StaticString = #function, -// line: UInt = #line, -// column: UInt = #column -//) { -// recordIssue( -// "Delete 'matches' and re-record this assertion", -// fileID: fileID, -// filePath: filePath, -// line: line, -// column: column -// ) -//} -// -//@available( -// *, deprecated, message: "Delete 'applyFixIts' and 'matches' and re-record this assertion" -//) -//public func assertMacro( -// _ macros: [Macro.Type], -// applyFixIts: Bool, -// record isRecording: Bool? = nil, -// of originalSource: () throws -> String, -// matches expandedOrDiagnosedSource: () -> String, -// fileID: StaticString = #fileID, -// file filePath: StaticString = #filePath, -// function: StaticString = #function, -// line: UInt = #line, -// column: UInt = #column -//) { -// assertMacro( -// Dictionary(macros: macros), -// applyFixIts: applyFixIts, -// record: isRecording, -// of: originalSource, -// matches: expandedOrDiagnosedSource, -// fileID: fileID, -// file: filePath, -// function: function, -// line: line, -// column: column -// ) -//} diff --git a/Sources/MacroTesting/Internal/Diagnostic+UnderlineHighlights.swift b/Sources/MacroTesting/Internal/Diagnostic+UnderlineHighlights.swift deleted file mode 100644 index e216045..0000000 --- a/Sources/MacroTesting/Internal/Diagnostic+UnderlineHighlights.swift +++ /dev/null @@ -1,44 +0,0 @@ -import SwiftDiagnostics -import SwiftSyntax -import SwiftSyntaxMacroExpansion - -extension Array where Element == Diagnostic { - func underlineHighlights( - sourceString: String, - lineNumber: Int, - column: Int, - context: BasicMacroExpansionContext - ) -> String? { - let (highlightColumns, highlightLineLength) = self.reduce( - into: (highlightColumns: Set(), highlightLineLength: column + 1) - ) { partialResult, diag in - for highlight in diag.highlights { - let startLocation = context.location( - for: highlight.positionAfterSkippingLeadingTrivia, anchoredAt: diag.node, fileName: "" - ) - let endLocation = context.location( - for: highlight.endPositionBeforeTrailingTrivia, anchoredAt: diag.node, fileName: "" - ) - guard - startLocation.line == lineNumber, - startLocation.line == endLocation.line, - sourceString.contains(diag.node.trimmedDescription) - else { continue } - partialResult.highlightColumns.formUnion(startLocation.column.. String, - fileID: StaticString, - filePath: StaticString, - line: UInt, - column: UInt -) { - #if canImport(Testing) - if Test.current != nil { - Issue.record( - Comment(rawValue: message()), - sourceLocation: SourceLocation( - fileID: fileID.description, - filePath: filePath.description, - line: Int(line), - column: Int(column) - ) - ) - } else { - XCTFail(message(), file: filePath, line: line) - } - #else - XCTFail(message(), file: filePath, line: line) - #endif -} diff --git a/Sources/MacroTesting/MacrosTestTrait.swift b/Sources/MacroTesting/MacrosTestTrait.swift deleted file mode 100644 index fad9e28..0000000 --- a/Sources/MacroTesting/MacrosTestTrait.swift +++ /dev/null @@ -1,65 +0,0 @@ -#if canImport(Testing) - import SnapshotTesting - import SwiftSyntax - import SwiftSyntaxMacros - import Testing - - @_spi(Experimental) - extension Trait where Self == _MacrosTestTrait { - /// Configure snapshot testing in a suite or test. - /// - /// - Parameters: - /// - record: The record mode of the test. - /// - diffTool: The diff tool to use in failure messages. - public static func macros( - indentationWidth: Trivia? = nil, - record: SnapshotTestingConfiguration.Record? = nil, - macros: [String: Macro.Type]? = nil - ) -> Self { - _MacrosTestTrait( - configuration: MacroTestingConfiguration( - indentationWidth: indentationWidth, - macros: macros - ), - record: record - ) - } - } - - /// A type representing the configuration of snapshot testing. - @_spi(Experimental) - public struct _MacrosTestTrait: SuiteTrait, TestTrait { - public let isRecursive = true - let configuration: MacroTestingConfiguration - let record: SnapshotTestingConfiguration.Record? - } - - extension Test { - var indentationWidth: Trivia? { - for trait in traits.reversed() { - if let indentationWidth = (trait as? _MacrosTestTrait)?.configuration.indentationWidth { - return indentationWidth - } - } - return nil - } - - var macros: [String: Macro.Type]? { - for trait in traits.reversed() { - if let macros = (trait as? _MacrosTestTrait)?.configuration.macros { - return macros - } - } - return nil - } - - var record: SnapshotTestingConfiguration.Record? { - for trait in traits.reversed() { - if let macros = (trait as? _MacrosTestTrait)?.record { - return macros - } - } - return nil - } - } -#endif diff --git a/Sources/MacroTesting/SwiftDiagnostics/DiagnosticsFormatter.swift b/Sources/MacroTesting/SwiftDiagnostics/DiagnosticsFormatter.swift deleted file mode 100644 index 99b88e3..0000000 --- a/Sources/MacroTesting/SwiftDiagnostics/DiagnosticsFormatter.swift +++ /dev/null @@ -1,456 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftDiagnostics -import SwiftSyntax -import SwiftSyntaxMacroExpansion - -extension Sequence where Element == Range { - /// Given a set of ranges that are sorted in order of nondecreasing lower - /// bound, merge any overlapping ranges to produce a sequence of - /// nonoverlapping ranges. - fileprivate func mergingOverlappingRanges() -> [Range] { - var result: [Range] = [] - - var prior: Range? = nil - for range in self { - // If this is the first range we've seen, note it as the prior and - // continue. - guard let priorRange = prior else { - prior = range - continue - } - - // If the ranges overlap, expand the prior range. - precondition(priorRange.lowerBound <= range.lowerBound) - if priorRange.overlaps(range) { - let lower = priorRange.lowerBound - let upper = Swift.max(priorRange.upperBound, range.upperBound) - prior = lower.. String { - let formatter = DiagnosticsFormatter(contextSize: contextSize, colorize: colorize) - return formatter.annotatedSource(tree: tree, diags: diags, context: context) - } - - /// Colorize the given source line by applying highlights from diagnostics. - private func colorizeSourceLine( - _ annotatedLine: AnnotatedSourceLine, - lineNumber: Int, - tree: some SyntaxProtocol, - sourceLocationConverter slc: SourceLocationConverter - ) -> String { - guard colorize, !annotatedLine.diagnostics.isEmpty else { - return annotatedLine.sourceString - } - - // Compute the set of highlight ranges that land on this line. These - // are column ranges, sorted in order of increasing starting column, and - // with overlapping ranges merged. - let highlightRanges: [Range] = annotatedLine.diagnostics.map { - $0.highlights - }.joined().compactMap { (highlight) -> Range? in - if highlight.root != Syntax(tree) { - return nil - } - - let startLoc = highlight.startLocation(converter: slc, afterLeadingTrivia: true) - let startLine = startLoc.line - - // Find the starting column. - let startColumn: Int - if startLine < lineNumber { - startColumn = 1 - } else if startLine == lineNumber { - startColumn = startLoc.column - } else { - return nil - } - - // Find the ending column. - let endLoc = highlight.endLocation(converter: slc, afterTrailingTrivia: false) - let endLine = endLoc.line - - let endColumn: Int - if endLine > lineNumber { - endColumn = annotatedLine.sourceString.count - } else if endLine == lineNumber { - endColumn = endLoc.column - } else { - return nil - } - - if startColumn == endColumn { - return nil - } - - return startColumn..] = highlightRanges.map { highlightRange in - let startIndex = sourceStringUTF8.index( - sourceStringUTF8.startIndex, offsetBy: highlightRange.lowerBound - 1) - let endIndex = sourceStringUTF8.index(startIndex, offsetBy: highlightRange.count) - return startIndex.. String { - let slc = - sourceLocationConverter ?? SourceLocationConverter(fileName: fileName ?? "", tree: tree) - - // First, we need to put each line and its diagnostics together - var annotatedSourceLines = [AnnotatedSourceLine]() - - for (sourceLineIndex, sourceLine) in slc.sourceLines.enumerated() { - let diagsForLine = diags.filter { diag in - return diag.location(converter: slc).line == (sourceLineIndex + 1) - } - let suffixText = suffixTexts.compactMap { (position, text) in - if slc.location(for: position).line == (sourceLineIndex + 1) { - return text - } - - return nil - }.joined() - - annotatedSourceLines.append( - AnnotatedSourceLine( - diagnostics: diagsForLine, sourceString: sourceLine, suffixText: suffixText)) - } - - // Only lines with diagnostic messages should be printed, but including some context - let rangesToPrint = annotatedSourceLines.enumerated().compactMap { - (lineIndex, sourceLine) -> Range? in - let lineNumber = lineIndex + 1 - if !sourceLine.isFreeOfAnnotations { - return Range( - uncheckedBounds: (lower: lineNumber - contextSize, upper: lineNumber + contextSize + 1)) - } - return nil - } - - var annotatedSource = "" - - // If there was a filename, add it first. - if let fileName { - let header = colorizeBufferOutline("===") - let firstLine = - 1 - + (annotatedSourceLines.enumerated().first { (lineIndex, sourceLine) in - !sourceLine.isFreeOfAnnotations - }?.offset ?? 0) - - annotatedSource.append("\(indentString)\(header) \(fileName):\(firstLine) \(header)\n") - } - - /// Keep track if a line missing char should be printed - var hasLineBeenSkipped = false - - let maxNumberOfDigits = String(annotatedSourceLines.count).count - - for (lineIndex, annotatedLine) in annotatedSourceLines.enumerated() { - let lineNumber = lineIndex + 1 - guard - rangesToPrint.contains(where: { range in - range.contains(lineNumber) - }) - else { - hasLineBeenSkipped = true - continue - } - - // line numbers should be right aligned - let lineNumberString = String(lineNumber) - let leadingSpaces = String(repeating: " ", count: maxNumberOfDigits - lineNumberString.count) - let linePrefix = "\(leadingSpaces)\(colorizeBufferOutline("\(lineNumberString) │")) " - - // If necessary, print a line that indicates that there was lines skipped in the source code - if hasLineBeenSkipped && !annotatedSource.isEmpty { - let lineMissingInfoLine = - indentString + String(repeating: " ", count: maxNumberOfDigits) - + " \(colorizeBufferOutline("┆"))" - annotatedSource.append("\(lineMissingInfoLine)\n") - } - hasLineBeenSkipped = false - - // add indentation - annotatedSource.append(indentString) - - // print the source line - annotatedSource.append(linePrefix) - annotatedSource.append( - colorizeSourceLine( - annotatedLine, - lineNumber: lineNumber, - tree: tree, - sourceLocationConverter: slc - ) - ) - - // If the line did not end with \n (e.g. the last line), append it manually - if annotatedSource.last != "\n" { - annotatedSource.append("\n") - } - - let columnsWithDiagnostics = Set( - annotatedLine.diagnostics.map { $0.location(converter: slc).column }) - let diagsPerColumn = Dictionary(grouping: annotatedLine.diagnostics) { diag in - diag.location(converter: slc).column - }.sorted { lhs, rhs in - lhs.key > rhs.key - } - - let preMessagePrefix = - indentString + String(repeating: " ", count: maxNumberOfDigits) + " " - + colorizeBufferOutline("│") - for (column, diags) in diagsPerColumn { - // compute the string that is shown before each message - var preMessage = preMessagePrefix - for c in 0.. String { - return annotatedSource( - fileName: nil, - tree: tree, - diags: diags, - context: context, - indentString: "", - suffixTexts: [:] - ) - } - - /// Annotates the given ``DiagnosticMessage`` with an appropriate ANSI color code (if the value of the `colorize` - /// property is `true`) and returns the result as a printable string. - private func colorizeIfRequested(_ message: DiagnosticMessage) -> String { - switch message.severity { - case .error: - let annotation = ANSIAnnotation(color: .red, trait: .bold) - return colorizeIfRequested("🛑 \(message.message)", annotation: annotation) - - case .warning: - let color = ANSIAnnotation(color: .yellow) - let prefix = colorizeIfRequested("⚠️ ", annotation: color.withTrait(.bold)) - - return prefix + colorizeIfRequested(message.message, annotation: color) - default: - let color = ANSIAnnotation(color: .default, trait: .bold) - let prefix = colorizeIfRequested("ℹ️ ", annotation: color) - return prefix + message.message - } - } - - /// Apply the given color and trait to the specified text, when we are - /// supposed to color the output. - private func colorizeIfRequested( - _ text: String, - annotation: ANSIAnnotation - ) -> String { - guard colorize, !text.isEmpty else { - return text - } - - return annotation.applied(to: text) - } - - /// Colorize for the buffer outline and line numbers. - func colorizeBufferOutline(_ text: String) -> String { - colorizeIfRequested(text, annotation: .bufferOutline) - } -} - -struct ANSIAnnotation { - enum Color: UInt8 { - case normal = 0 - case black = 30 - case red = 31 - case green = 32 - case yellow = 33 - case blue = 34 - case magenta = 35 - case cyan = 36 - case white = 37 - case `default` = 39 - } - - enum Trait: UInt8 { - case normal = 0 - case bold = 1 - case underline = 4 - } - - var color: Color - var trait: Trait - - /// The textual representation of the annotation. - var code: String { - "\u{001B}[\(trait.rawValue);\(color.rawValue)m" - } - - init(color: Color, trait: Trait = .normal) { - self.color = color - self.trait = trait - } - - func withTrait(_ trait: Trait) -> Self { - return ANSIAnnotation(color: self.color, trait: trait) - } - - func applied(to message: String) -> String { - // Resetting after the message ensures that we don't color unintended lines in the output - return "\(code)\(message)\(ANSIAnnotation.normal.code)" - } - - /// The "normal" or "reset" ANSI code used to unset any previously added annotation. - static var normal: ANSIAnnotation { - self.init(color: .normal, trait: .normal) - } - - /// Annotation used for the outline and line numbers of a buffer. - static var bufferOutline: ANSIAnnotation { - ANSIAnnotation(color: .cyan, trait: .normal) - } - - /// Annotation used for highlighting source text. - static var sourceHighlight: ANSIAnnotation { - ANSIAnnotation(color: .default, trait: .underline) - } -} diff --git a/Sources/MacroTesting/SwiftSyntax/SourceEdit.swift b/Sources/MacroTesting/SwiftSyntax/SourceEdit.swift deleted file mode 100644 index 50852b7..0000000 --- a/Sources/MacroTesting/SwiftSyntax/SourceEdit.swift +++ /dev/null @@ -1,74 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftSyntax - -/// A textual edit to the original source represented by a range and a -/// replacement. -public struct SourceEdit: Equatable { - /// The half-open range that this edit applies to. - public let range: Range - /// The text to replace the original range with. Empty for a deletion. - public let replacement: String - - /// Length of the original source range that this edit applies to. Zero if - /// this is an addition. - public var length: SourceLength { - return SourceLength(utf8Length: range.lowerBound.utf8Offset - range.upperBound.utf8Offset) - } - - /// Create an edit to replace `range` in the original source with - /// `replacement`. - public init(range: Range, replacement: String) { - self.range = range - self.replacement = replacement - } - - /// Convenience function to create a textual addition after the given node - /// and its trivia. - public static func insert(_ newText: String, after node: some SyntaxProtocol) -> SourceEdit { - return SourceEdit(range: node.endPosition.. SourceEdit { - return SourceEdit(range: node.position.. SourceEdit { - return SourceEdit(range: node.position.. SourceEdit { - return SourceEdit(range: node.position.. String { - // let messages = messages ?? diagnostics.compactMap { $0.fixIts.first?.message.message } - // - // let edits = - // diagnostics - // .flatMap(\.fixIts) - // .filter { messages.contains($0.message.message) } - // .flatMap(\.edits) - // - // return self.apply(edits: edits, to: tree) - // } - - /// Apply the given edits to the syntax tree. - /// - /// - Parameters: - /// - edits: The edits to apply to the syntax tree - /// - tree: he syntax tree to which the edits should be applied. - /// - Returns: A `String` representation of the modified syntax tree after applying the edits. - public static func apply( - edits: [SourceEdit], - to tree: any SyntaxProtocol - ) -> String { - var edits = edits - var source = tree.description - - while let edit = edits.first { - edits = Array(edits.dropFirst()) - - let startIndex = source.utf8.index(source.utf8.startIndex, offsetBy: edit.startUtf8Offset) - let endIndex = source.utf8.index(source.utf8.startIndex, offsetBy: edit.endUtf8Offset) - - source.replaceSubrange(startIndex.. SourceEdit? in - if remainingEdit.replacementRange.overlaps(edit.replacementRange) { - // The edit overlaps with the previous edit. We can't apply both - // without conflicts. Apply the one that's listed first and drop the - // later edit. - return nil - } - - // If the remaining edit starts after or at the end of the edit that we just applied, - // shift it by the current edit's difference in length. - if edit.endUtf8Offset <= remainingEdit.startUtf8Offset { - let startPosition = AbsolutePosition( - utf8Offset: remainingEdit.startUtf8Offset - edit.replacementRange.count - + edit.replacementLength) - let endPosition = AbsolutePosition( - utf8Offset: remainingEdit.endUtf8Offset - edit.replacementRange.count - + edit.replacementLength) - return SourceEdit( - range: startPosition.. { - return startUtf8Offset.. [Diagnostic] { - return diagnoseDotEscaping(decl) -} - -private func diagnoseDotEscaping(_ decl: D) -> [Diagnostic] { - guard let decl = decl.as(StructDeclSyntax.self) else { return [] } - - return decl.memberBlock.members.compactMap { element -> Diagnostic? in - guard - let configuration = element.decl - .as(VariableDeclSyntax.self)? - .customConfigurationArguments - else { return nil } - - let dotEscapingIndex = configuration.firstIndex( - where: { - $0.expression - .as(MemberAccessExprSyntax.self)? - .declName.baseName.text == "escaping" - } - ) - guard let dotEscapingIndex else { return nil } - - let newIndex = - configuration.firstIndex(where: { $0.label?.text == "label" }) - .map { configuration.index(before: $0) } - ?? configuration.endIndex - - let newEscaping = LabeledExprSyntax( - label: .identifier("escaping"), - colon: .colonToken(trailingTrivia: .space), - expression: BooleanLiteralExprSyntax(booleanLiteral: true), - trailingComma: newIndex != configuration.endIndex ? .commaToken(trailingTrivia: .space) : nil - ) - - var newConfiguration = configuration - newConfiguration.remove(at: dotEscapingIndex) - newConfiguration.insert(newEscaping, at: newIndex) - - return Diagnostic( - node: configuration, - message: MacroExpansionWarningMessage( - """ - @Init(.escaping) is deprecated - """ - ), - fixIt: FixIt( - message: MacroExpansionFixItMessage( - "Replace '@Init(.escaping)' with '@Init(escaping: true)'" - ), - changes: [ - FixIt.Change.replace( - oldNode: Syntax(configuration), - newNode: Syntax(newConfiguration) - ) - ] - ) - ) - } -} diff --git a/Sources/MemberwiseInitMacros/Macros/Support/ExprTypeInference.swift b/Sources/MemberwiseInitMacros/Macros/Support/ExprTypeInference.swift index d5aea19..3bde4cf 100644 --- a/Sources/MemberwiseInitMacros/Macros/Support/ExprTypeInference.swift +++ b/Sources/MemberwiseInitMacros/Macros/Support/ExprTypeInference.swift @@ -1,10 +1,6 @@ import SwiftOperators -#if canImport(SwiftSyntax600) - @_spi(ExperimentalLanguageFeatures) import SwiftSyntax -#else - import SwiftSyntax -#endif +@_spi(ExperimentalLanguageFeatures) import SwiftSyntax // Potential future enhancements: // - .ternaryExpr having "then" and "else" expressions as inferrable types @@ -221,8 +217,8 @@ extension ExprSyntax { case .infixOperatorExpr: guard let infixOperatorExpr = self.as(InfixOperatorExprSyntax.self), - let lhsType = infixOperatorExpr.leftOperand.as(ExprSyntax.self)?.inferredType, - let rhsType = infixOperatorExpr.rightOperand.as(ExprSyntax.self)?.inferredType, + let lhsType = infixOperatorExpr.leftOperand.inferredType, + let rhsType = infixOperatorExpr.rightOperand.inferredType, let operation = InfixOperator(rawValue: infixOperatorExpr.operator.trimmedDescription), let inferredType = resultTypeOfInfixOperation( lhs: lhsType, @@ -261,95 +257,7 @@ extension ExprSyntax { else { return nil } return .tuple(elementTypes) - #if canImport(SwiftSyntax510) - case .thenStmt: - return nil - #endif - - #if !canImport(SwiftSyntax600) - case .canImportExpr, .canImportVersionInfo: - return nil - #endif - - #if canImport(SwiftSyntax600) - case ._canImportExpr, - ._canImportVersionInfo, - .doExpr, - .lifetimeSpecifierArgumentList, - .lifetimeSpecifierArgument, - .lifetimeTypeSpecifier, - .simpleTypeSpecifier, - .throwsClause, - .typeSpecifierList: - return nil - #endif - - case .token, .accessorBlock, .accessorDeclList, .accessorDecl, .accessorEffectSpecifiers, - .accessorParameters, .actorDecl, .arrayElementList, .arrayElement, .arrayType, .arrowExpr, - .assignmentExpr, .associatedTypeDecl, .attributeList, .attribute, .attributedType, - .availabilityArgumentList, .availabilityArgument, .availabilityCondition, - .availabilityLabeledArgument, .awaitExpr, .backDeployedAttributeArguments, - .binaryOperatorExpr, .borrowExpr, .breakStmt, - .catchClauseList, .catchClause, .catchItemList, .catchItem, .classDecl, .classRestrictionType, - .closureCaptureClause, .closureCaptureList, .closureCaptureSpecifier, .closureCapture, - .closureExpr, .closureParameterClause, .closureParameterList, .closureParameter, - .closureShorthandParameterList, .closureShorthandParameter, .closureSignature, - .codeBlockItemList, .codeBlockItem, .codeBlock, .compositionTypeElementList, - .compositionTypeElement, .compositionType, .conditionElementList, .conditionElement, - .conformanceRequirement, .consumeExpr, .continueStmt, .conventionAttributeArguments, - .conventionWitnessMethodAttributeArguments, .copyExpr, .declModifierDetail, .declModifierList, - .declModifier, .declNameArgumentList, .declNameArgument, .declNameArguments, - .declReferenceExpr, .deferStmt, .deinitializerDecl, .deinitializerEffectSpecifiers, - .derivativeAttributeArguments, .designatedTypeList, .designatedType, .dictionaryElementList, - .dictionaryElement, .dictionaryType, .differentiabilityArgumentList, - .differentiabilityArgument, .differentiabilityArguments, - .differentiabilityWithRespectToArgument, .differentiableAttributeArguments, - .discardAssignmentExpr, .discardStmt, .doStmt, .documentationAttributeArgumentList, - .documentationAttributeArgument, .dynamicReplacementAttributeArguments, - .editorPlaceholderDecl, .editorPlaceholderExpr, .effectsAttributeArgumentList, .enumCaseDecl, - .enumCaseElementList, .enumCaseElement, .enumCaseParameterClause, .enumCaseParameterList, - .enumCaseParameter, .enumDecl, .exposeAttributeArguments, .exprList, .expressionPattern, - .expressionSegment, .expressionStmt, .extensionDecl, .fallThroughStmt, .forStmt, - .forceUnwrapExpr, .functionDecl, .functionEffectSpecifiers, .functionParameterClause, - .functionParameterList, .functionParameter, .functionSignature, .functionType, - .genericArgumentClause, .genericArgumentList, .genericArgument, .genericParameterClause, - .genericParameterList, .genericParameter, .genericRequirementList, .genericRequirement, - .genericSpecializationExpr, .genericWhereClause, .guardStmt, .identifierPattern, - .identifierType, .ifConfigClauseList, .ifConfigClause, .ifConfigDecl, .ifExpr, - .implementsAttributeArguments, .implicitlyUnwrappedOptionalType, .importDecl, - .importPathComponentList, .importPathComponent, .inOutExpr, .inheritanceClause, - .inheritedTypeList, .inheritedType, .initializerClause, .initializerDecl, .isExpr, - .isTypePattern, .keyPathComponentList, .keyPathComponent, .keyPathExpr, - .keyPathOptionalComponent, .keyPathPropertyComponent, .keyPathSubscriptComponent, - .labeledExprList, .labeledExpr, .labeledSpecializeArgument, .labeledStmt, .layoutRequirement, - .macroDecl, .macroExpansionDecl, .macroExpansionExpr, .matchingPatternCondition, - .memberAccessExpr, .memberBlockItemList, .memberBlockItem, .memberBlock, .memberType, - .metatypeType, .missingDecl, .missingExpr, .missingPattern, .missingStmt, .missing, - .missingType, .multipleTrailingClosureElementList, .multipleTrailingClosureElement, - .namedOpaqueReturnType, .nilLiteralExpr, .objCSelectorPieceList, .objCSelectorPiece, - .opaqueReturnTypeOfAttributeArguments, .operatorDecl, .operatorPrecedenceAndTypes, - .optionalBindingCondition, .optionalChainingExpr, .optionalType, - .originallyDefinedInAttributeArguments, .packElementExpr, .packElementType, - .packExpansionExpr, .packExpansionType, .patternBindingList, .patternBinding, .patternExpr, - .platformVersionItemList, .platformVersionItem, .platformVersion, .postfixIfConfigExpr, - .postfixOperatorExpr, .poundSourceLocationArguments, .poundSourceLocation, - .precedenceGroupAssignment, .precedenceGroupAssociativity, .precedenceGroupAttributeList, - .precedenceGroupDecl, .precedenceGroupNameList, .precedenceGroupName, - .precedenceGroupRelation, .primaryAssociatedTypeClause, .primaryAssociatedTypeList, - .primaryAssociatedType, .protocolDecl, .regexLiteralExpr, .repeatStmt, .returnClause, - .returnStmt, .sameTypeRequirement, .someOrAnyType, .sourceFile, - .specializeAttributeArgumentList, .specializeAvailabilityArgument, - .specializeTargetFunctionArgument, .stringSegment, .structDecl, .subscriptCallExpr, - .subscriptDecl, .superExpr, .suppressedType, .switchCaseItemList, .switchCaseItem, - .switchCaseLabel, .switchCaseList, .switchCase, .switchDefaultLabel, .switchExpr, - .ternaryExpr, .throwStmt, .tryExpr, .tuplePatternElementList, .tuplePatternElement, - .tuplePattern, .tupleTypeElementList, .tupleTypeElement, .tupleType, .typeAliasDecl, - .typeAnnotation, .typeEffectSpecifiers, .typeExpr, .typeInitializerClause, - .unavailableFromAsyncAttributeArguments, .underscorePrivateAttributeArguments, - .unexpectedNodes, .unresolvedAsExpr, .unresolvedIsExpr, .unresolvedTernaryExpr, - .valueBindingPattern, .variableDecl, .versionComponentList, .versionComponent, .versionTuple, - .whereClause, .whileStmt, .wildcardPattern, .yieldStmt, .yieldedExpressionList, - .yieldedExpression, .yieldedExpressionsClause: + default: return nil } } diff --git a/Sources/MemberwiseInitMacros/Macros/Support/SyntaxHelpers.swift b/Sources/MemberwiseInitMacros/Macros/Support/SyntaxHelpers.swift index 7a317fe..71fd682 100644 --- a/Sources/MemberwiseInitMacros/Macros/Support/SyntaxHelpers.swift +++ b/Sources/MemberwiseInitMacros/Macros/Support/SyntaxHelpers.swift @@ -63,7 +63,7 @@ extension VariableDeclSyntax { var isComputedProperty: Bool { guard self.bindings.count == 1, - let binding = self.bindings.first?.as(PatternBindingSyntax.self) + let binding = self.bindings.first else { return false } return self.bindingSpecifier.tokenKind == .keyword(.var) && binding.isComputedProperty diff --git a/Sources/MemberwiseInitMacros/Macros/UncheckedMemberwiseInitMacro.swift b/Sources/MemberwiseInitMacros/Macros/UncheckedMemberwiseInitMacro.swift index eca75f1..5fa07e7 100644 --- a/Sources/MemberwiseInitMacros/Macros/UncheckedMemberwiseInitMacro.swift +++ b/Sources/MemberwiseInitMacros/Macros/UncheckedMemberwiseInitMacro.swift @@ -6,12 +6,12 @@ import SwiftSyntaxMacroExpansion import SwiftSyntaxMacros public struct UncheckedMemberwiseInitMacro: MemberMacro { - public static func expansion( + public static func expansion( of node: AttributeSyntax, - providingMembersOf decl: D, - in context: C - ) throws -> [SwiftSyntax.DeclSyntax] - where D: DeclGroupSyntax, C: MacroExpansionContext { + providingMembersOf decl: some DeclGroupSyntax, + conformingTo protocols: [TypeSyntax], + in context: some MacroExpansionContext + ) throws -> [DeclSyntax] { guard [SwiftSyntax.SyntaxKind.classDecl, .structDecl, .actorDecl].contains(decl.kind) else { throw MacroExpansionErrorMessage( """ diff --git a/Tests/MacroTestingTests/AddAsyncTests.swift b/Tests/MacroTestingTests/AddAsyncTests.swift deleted file mode 100644 index d29a0a2..0000000 --- a/Tests/MacroTestingTests/AddAsyncTests.swift +++ /dev/null @@ -1,168 +0,0 @@ -import MacroTesting -import XCTest - -final class AddAsyncMacroTests: BaseTestCase { - override func invokeTest() { - withMacroTesting(macros: [AddAsyncMacro.self]) { - super.invokeTest() - } - } - - func testExpansionTransformsFunctionWithResultCompletionToAsyncThrows() { - #if canImport(SwiftSyntax600) - assertMacro { - #""" - @AddAsync - func c(a: Int, for b: String, _ value: Double, completionBlock: @escaping (Result) -> Void) -> Void { - completionBlock(.success("a: \(a), b: \(b), value: \(value)")) - } - """# - } expansion: { - #""" - func c(a: Int, for b: String, _ value: Double, completionBlock: @escaping (Result) -> Void) -> Void { - completionBlock(.success("a: \(a), b: \(b), value: \(value)")) - } - - func c(a: Int, for b: String, _ value: Double) async throws -> String { - try await withCheckedThrowingContinuation { continuation in - c(a: a, for: b, value) { returnValue in - - switch returnValue { - case .success(let value): - continuation.resume(returning: value) - case .failure(let error): - continuation.resume(throwing: error) - } - } - } - - } - """# - } - #else - assertMacro { - #""" - @AddAsync - func c(a: Int, for b: String, _ value: Double, completionBlock: @escaping (Result) -> Void) -> Void { - completionBlock(.success("a: \(a), b: \(b), value: \(value)")) - } - """# - } expansion: { - #""" - func c(a: Int, for b: String, _ value: Double, completionBlock: @escaping (Result) -> Void) -> Void { - completionBlock(.success("a: \(a), b: \(b), value: \(value)")) - } - - func c(a: Int, for b: String, _ value: Double) async throws -> String { - try await withCheckedThrowingContinuation { continuation in - c(a: a, for: b, value) { returnValue in - - switch returnValue { - case .success(let value): - continuation.resume(returning: value) - case .failure(let error): - continuation.resume(throwing: error) - } - } - } - } - """# - } - #endif - } - - func testExpansionTransformsFunctionWithBoolCompletionToAsync() { - #if canImport(SwiftSyntax600) - assertMacro { - """ - @AddAsync - func d(a: Int, for b: String, _ value: Double, completionBlock: @escaping (Bool) -> Void) -> Void { - completionBlock(true) - } - """ - } expansion: { - """ - func d(a: Int, for b: String, _ value: Double, completionBlock: @escaping (Bool) -> Void) -> Void { - completionBlock(true) - } - - func d(a: Int, for b: String, _ value: Double) async -> Bool { - await withCheckedContinuation { continuation in - d(a: a, for: b, value) { returnValue in - - continuation.resume(returning: returnValue) - } - } - - } - """ - } - #else - assertMacro { - """ - @AddAsync - func d(a: Int, for b: String, _ value: Double, completionBlock: @escaping (Bool) -> Void) -> Void { - completionBlock(true) - } - """ - } expansion: { - """ - func d(a: Int, for b: String, _ value: Double, completionBlock: @escaping (Bool) -> Void) -> Void { - completionBlock(true) - } - - func d(a: Int, for b: String, _ value: Double) async -> Bool { - await withCheckedContinuation { continuation in - d(a: a, for: b, value) { returnValue in - - continuation.resume(returning: returnValue) - } - } - } - """ - } - #endif - } - - func testExpansionOnStoredPropertyEmitsError() { - assertMacro { - """ - struct Test { - @AddAsync - var name: String - } - """ - } diagnostics: { - """ - struct Test { - @AddAsync - ┬──────── - ╰─ 🛑 @addAsync only works on functions - var name: String - } - """ - } - } - - func testExpansionOnAsyncFunctionEmitsError() { - assertMacro { - """ - struct Test { - @AddAsync - async func sayHello() { - } - } - """ - } diagnostics: { - """ - struct Test { - @AddAsync - ┬──────── - ╰─ 🛑 @addAsync requires an function that returns void - async func sayHello() { - } - } - """ - } - } -} diff --git a/Tests/MacroTestingTests/AddBlockerTests.swift b/Tests/MacroTestingTests/AddBlockerTests.swift deleted file mode 100644 index f2f088a..0000000 --- a/Tests/MacroTestingTests/AddBlockerTests.swift +++ /dev/null @@ -1,50 +0,0 @@ -import MacroTesting -import XCTest - -final class AddBlockerTests: BaseTestCase { - override func invokeTest() { - withMacroTesting(macros: [AddBlocker.self]) { - super.invokeTest() - } - } - - func testExpansionTransformsAdditionToSubtractionAndEmitsWarning() { - assertMacro { - """ - #addBlocker(x * y + z) - """ - } expansion: { - """ - x * y - z - """ - } diagnostics: { - """ - #addBlocker(x * y + z) - ───── ┬ ─ - ╰─ ⚠️ blocked an add; did you mean to subtract? - ✏️ use '-' - """ - } fixes: { - """ - #addBlocker(x * y + z) - ───── ┬ ─ - ╰─ ⚠️ blocked an add; did you mean to subtract? - - ✏️ use '-' - #addBlocker(x * y - z) - """ - } - } - - func testExpansionPreservesSubtraction() { - assertMacro { - """ - #addBlocker(x * y - z) - """ - } expansion: { - """ - x * y - z - """ - } - } -} diff --git a/Tests/MacroTestingTests/AddCompletionHandlerTests.swift b/Tests/MacroTestingTests/AddCompletionHandlerTests.swift deleted file mode 100644 index 5ac1943..0000000 --- a/Tests/MacroTestingTests/AddCompletionHandlerTests.swift +++ /dev/null @@ -1,116 +0,0 @@ -import MacroTesting -import XCTest - -final class AddCompletionHandlerTests: BaseTestCase { - override func invokeTest() { - withMacroTesting(macros: [AddCompletionHandlerMacro.self]) { - super.invokeTest() - } - } - - func testExpansionTransformsAsyncFunctionToCompletion() { - #if canImport(SwiftSyntax600) - assertMacro { - """ - @AddCompletionHandler - func f(a: Int, for b: String, _ value: Double) async -> String { - return b - } - """ - } expansion: { - """ - func f(a: Int, for b: String, _ value: Double) async -> String { - return b - } - - func f(a: Int, for b: String, _ value: Double, completionHandler: @escaping (String) -> Void) { - Task { - completionHandler(await f(a: a, for: b, value)) - } - - } - """ - } - #else - assertMacro { - """ - @AddCompletionHandler - func f(a: Int, for b: String, _ value: Double) async -> String { - return b - } - """ - } expansion: { - """ - func f(a: Int, for b: String, _ value: Double) async -> String { - return b - } - - func f(a: Int, for b: String, _ value: Double, completionHandler: @escaping (String) -> Void) { - Task { - completionHandler(await f(a: a, for: b, value)) - } - } - """ - } - #endif - } - - func testExpansionOnStoredPropertyEmitsError() { - assertMacro { - """ - struct Test { - @AddCompletionHandler - var value: Int - } - """ - } diagnostics: { - """ - struct Test { - @AddCompletionHandler - ┬──────────────────── - ╰─ 🛑 @addCompletionHandler only works on functions - var value: Int - } - """ - } - } - - func testExpansionOnNonAsyncFunctionEmitsErrorWithFixItSuggestion() { - assertMacro { - """ - struct Test { - @AddCompletionHandler - func fetchData() -> String { - return "Hello, World!" - } - } - """ - } diagnostics: { - """ - struct Test { - @AddCompletionHandler - func fetchData() -> String { - ┬─── - ╰─ 🛑 can only add a completion-handler variant to an 'async' function - ✏️ add 'async' - return "Hello, World!" - } - } - """ - } fixes: { - """ - func fetchData() -> String { - ┬─── - ╰─ 🛑 can only add a completion-handler variant to an 'async' function - - ✏️ add 'async' - struct Test { - @AddCompletionHandler - func fetchData() async-> String { - return "Hello, World!" - } - } - """ - } - } -} diff --git a/Tests/MacroTestingTests/AssertMacroTests.swift b/Tests/MacroTestingTests/AssertMacroTests.swift deleted file mode 100644 index 37bfa77..0000000 --- a/Tests/MacroTestingTests/AssertMacroTests.swift +++ /dev/null @@ -1,33 +0,0 @@ -import MacroTesting -import XCTest - -final class AssertMacroTests: BaseTestCase { - #if os(iOS) || os(macOS) || os(tvOS) || os(visionOS) || os(watchOS) - func testMacrosRequired() { - XCTExpectFailure { - assertMacro { - """ - #forgotToConfigure() - """ - } - } issueMatcher: { - $0.compactDescription == """ - failed - No macros configured for this assertion. Pass a mapping to this function, e.g.: - - assertMacro(["stringify": StringifyMacro.self]) { … } - - Or wrap your assertion using 'withMacroTesting', e.g. in 'invokeTest': - - class StringifyMacroTests: XCTestCase { - override func invokeTest() { - withMacroTesting(macros: ["stringify": StringifyMacro.self]) { - super.invokeTest() - } - } - … - } - """ - } - } - #endif -} diff --git a/Tests/MacroTestingTests/BaseTestCase.swift b/Tests/MacroTestingTests/BaseTestCase.swift deleted file mode 100644 index e079126..0000000 --- a/Tests/MacroTestingTests/BaseTestCase.swift +++ /dev/null @@ -1,12 +0,0 @@ -import MacroTesting -import XCTest - -class BaseTestCase: XCTestCase { - override func invokeTest() { - withMacroTesting( - record: .missing - ) { - super.invokeTest() - } - } -} diff --git a/Tests/MacroTestingTests/CaseDetectionMacroTests.swift b/Tests/MacroTestingTests/CaseDetectionMacroTests.swift deleted file mode 100644 index 79190b2..0000000 --- a/Tests/MacroTestingTests/CaseDetectionMacroTests.swift +++ /dev/null @@ -1,45 +0,0 @@ -import MacroTesting -import XCTest - -final class CaseDetectionMacroTests: BaseTestCase { - override func invokeTest() { - withMacroTesting(macros: [CaseDetectionMacro.self]) { - super.invokeTest() - } - } - - func testExpansionAddsComputedProperties() { - assertMacro { - """ - @CaseDetection - enum Animal { - case dog - case cat(curious: Bool) - } - """ - } expansion: { - """ - enum Animal { - case dog - case cat(curious: Bool) - - var isDog: Bool { - if case .dog = self { - return true - } - - return false - } - - var isCat: Bool { - if case .cat = self { - return true - } - - return false - } - } - """ - } - } -} diff --git a/Tests/MacroTestingTests/CustomCodableMacroTests.swift b/Tests/MacroTestingTests/CustomCodableMacroTests.swift deleted file mode 100644 index 651a567..0000000 --- a/Tests/MacroTestingTests/CustomCodableMacroTests.swift +++ /dev/null @@ -1,62 +0,0 @@ -import MacroTesting -import XCTest - -final class CustomCodableMacroTests: BaseTestCase { - override func invokeTest() { - withMacroTesting(macros: [CodableKey.self, CustomCodable.self]) { - super.invokeTest() - } - } - - func testExpansionAddsDefaultCodingKeys() { - assertMacro { - """ - @CustomCodable - struct Person { - let name: String - let age: Int - } - """ - } expansion: { - """ - struct Person { - let name: String - let age: Int - - enum CodingKeys: String, CodingKey { - case name - case age - } - } - """ - } - } - - func testExpansionWithCodableKeyAddsCustomCodingKeys() { - assertMacro { - """ - @CustomCodable - struct Person { - let name: String - @CodableKey("user_age") let age: Int - - func randomFunction() {} - } - """ - } expansion: { - """ - struct Person { - let name: String - let age: Int - - func randomFunction() {} - - enum CodingKeys: String, CodingKey { - case name - case age = "user_age" - } - } - """ - } - } -} diff --git a/Tests/MacroTestingTests/DefaultFatalErrorImplementationMacroTests.swift b/Tests/MacroTestingTests/DefaultFatalErrorImplementationMacroTests.swift deleted file mode 100644 index 7771de8..0000000 --- a/Tests/MacroTestingTests/DefaultFatalErrorImplementationMacroTests.swift +++ /dev/null @@ -1,65 +0,0 @@ -import MacroTesting -import XCTest - -final class DefaultFatalErrorImplementationMacroTests: BaseTestCase { - override func invokeTest() { - withMacroTesting( - macros: ["defaultFatalErrorImplementation": DefaultFatalErrorImplementationMacro.self] - ) { - super.invokeTest() - } - } - - func testExpansionWhenAttachedToProtocolExpandsCorrectly() { - assertMacro { - """ - @defaultFatalErrorImplementation - protocol MyProtocol { - func foo() - func bar() -> Int - } - """ - } expansion: { - """ - protocol MyProtocol { - func foo() - func bar() -> Int - } - - extension MyProtocol { - func foo() { - fatalError("whoops 😅") - } - func bar() -> Int { - fatalError("whoops 😅") - } - } - """ - } - } - - func testExpansionWhenNotAttachedToProtocolProducesDiagnostic() { - assertMacro { - """ - @defaultFatalErrorImplementation - class MyClass {} - """ - } diagnostics: { - """ - @defaultFatalErrorImplementation - ┬─────────────────────────────── - ╰─ 🛑 Macro `defaultFatalErrorImplementation` can only be applied to a protocol - class MyClass {} - """ - } - } - - func testExpansionWhenAttachedToEmptyProtocolDoesNotAddExtension() { - assertMacro { - """ - @defaultFatalErrorImplementation - protocol EmptyProtocol {} - """ - } - } -} diff --git a/Tests/MacroTestingTests/DiagnosticsAndFixitsEmitterMacroTests.swift b/Tests/MacroTestingTests/DiagnosticsAndFixitsEmitterMacroTests.swift deleted file mode 100644 index af7e724..0000000 --- a/Tests/MacroTestingTests/DiagnosticsAndFixitsEmitterMacroTests.swift +++ /dev/null @@ -1,55 +0,0 @@ -import MacroTesting -import XCTest - -final class DiagnosticsAndFixitsEmitterMacroTests: BaseTestCase { - override func invokeTest() { - withMacroTesting(macros: [DiagnosticsAndFixitsEmitterMacro.self]) { - super.invokeTest() - } - } - - func testExpansionEmitsDiagnosticsAndFixits() { - assertMacro { - """ - @DiagnosticsAndFixitsEmitter - struct FooBar { - let foo: Foo - let bar: Bar - } - """ - } diagnostics: { - """ - @DiagnosticsAndFixitsEmitter - ┬────────────────────────── - ├─ ⚠️ This is the first diagnostic. - │ ✏️ This is the first fix-it. - │ ✏️ This is the second fix-it. - ╰─ ℹ️ This is the second diagnostic, it's a note. - struct FooBar { - let foo: Foo - let bar: Bar - } - """ - } fixes: { - """ - @DiagnosticsAndFixitsEmitter - ┬────────────────────────── - ╰─ ⚠️ This is the first diagnostic. - - ✏️ This is the first fix-it. - @DiagnosticsAndFixitsEmitter - struct FooBar { - let foo: Foo - let bar: Bar - } - - ✏️ This is the second fix-it. - @DiagnosticsAndFixitsEmitter - struct FooBar { - let foo: Foo - let bar: Bar - } - """ - } - } -} diff --git a/Tests/MacroTestingTests/DictionaryStorageMacroTests.swift b/Tests/MacroTestingTests/DictionaryStorageMacroTests.swift deleted file mode 100644 index 0b11b30..0000000 --- a/Tests/MacroTestingTests/DictionaryStorageMacroTests.swift +++ /dev/null @@ -1,141 +0,0 @@ -import MacroTesting -import XCTest - -final class DictionaryStorageMacroTests: BaseTestCase { - override func invokeTest() { - withMacroTesting( - macros: [ - DictionaryStorageMacro.self, - DictionaryStoragePropertyMacro.self, - ] - ) { - super.invokeTest() - } - } - - func testExpansionConvertsStoredProperties() { - #if canImport(SwiftSyntax510) - assertMacro { - """ - @DictionaryStorage - struct Point { - var x: Int = 1 - var y: Int = 2 - } - """ - } expansion: { - """ - struct Point { - var x: Int { - get { - _storage["x", default: 1] as! Int - } - set { - _storage["x"] = newValue - } - } - var y: Int { - get { - _storage["y", default: 2] as! Int - } - set { - _storage["y"] = newValue - } - } - - var _storage: [String: Any] = [:] - } - """ - } - #elseif canImport(SwiftSyntax509) - assertMacro { - """ - @DictionaryStorage - struct Point { - var x: Int = 1 - var y: Int = 2 - } - """ - } expansion: { - """ - struct Point { - var x: Int = 1 { - get { - _storage["x", default: 1] as! Int - } - set { - _storage["x"] = newValue - } - } - var y: Int = 2 { - get { - _storage["y", default: 2] as! Int - } - set { - _storage["y"] = newValue - } - } - - var _storage: [String: Any] = [:] - } - """ - } - #endif - } - - func testExpansionWithoutInitializersEmitsError() { - assertMacro { - """ - @DictionaryStorage - class Point { - let x: Int - let y: Int - } - """ - } expansion: { - """ - class Point { - let x: Int - let y: Int - - var _storage: [String: Any] = [:] - } - """ - } diagnostics: { - """ - @DictionaryStorage - class Point { - let x: Int - ╰─ 🛑 stored property must have an initializer - let y: Int - ╰─ 🛑 stored property must have an initializer - } - """ - } - } - - func testExpansionIgnoresComputedProperties() { - assertMacro { - """ - @DictionaryStorage - struct Test { - var value: Int { - get { return 0 } - set {} - } - } - """ - } expansion: { - """ - struct Test { - var value: Int { - get { return 0 } - set {} - } - - var _storage: [String: Any] = [:] - } - """ - } - } -} diff --git a/Tests/MacroTestingTests/EntryMacroTests.swift b/Tests/MacroTestingTests/EntryMacroTests.swift deleted file mode 100644 index e441d9b..0000000 --- a/Tests/MacroTestingTests/EntryMacroTests.swift +++ /dev/null @@ -1,54 +0,0 @@ -#if canImport(SwiftSyntax600) - import MacroTesting - import XCTest - - final class EntryMacroTests: BaseTestCase { - override func invokeTest() { - withMacroTesting( - macros: [ - EntryMacro.self - ] - ) { - super.invokeTest() - } - } - - func testWithinEnvironmentValues() { - assertMacro { - """ - extension EnvironmentValues { - @Entry var x: String = "" - } - """ - } expansion: { - """ - extension EnvironmentValues { - var x: String { - get { - fatalError() - } - } - } - """ - } - } - - func testNotWithinEnvironmentValues() { - assertMacro { - """ - extension String { - @Entry var x: String = "" - } - """ - } diagnostics: { - """ - extension String { - @Entry var x: String = "" - ┬───── - ╰─ 🛑 '@Entry' macro can only attach to var declarations inside extensions of EnvironmentValues - } - """ - } - } - } -#endif diff --git a/Tests/MacroTestingTests/EquatableExtensionMacroTests.swift b/Tests/MacroTestingTests/EquatableExtensionMacroTests.swift deleted file mode 100644 index ee87d79..0000000 --- a/Tests/MacroTestingTests/EquatableExtensionMacroTests.swift +++ /dev/null @@ -1,32 +0,0 @@ -import MacroTesting -import XCTest - -final class EquatableExtensionMacroTests: XCTestCase { - override func invokeTest() { - withMacroTesting(macros: ["equatable": EquatableExtensionMacro.self]) { - super.invokeTest() - } - } - - func testExpansionAddsExtensionWithEquatableConformance() { - assertMacro { - """ - @equatable - final public class Message { - let text: String - let sender: String - } - """ - } expansion: { - """ - final public class Message { - let text: String - let sender: String - } - - extension Message: Equatable { - } - """ - } - } -} diff --git a/Tests/MacroTestingTests/FixItTests.swift b/Tests/MacroTestingTests/FixItTests.swift deleted file mode 100644 index bb85763..0000000 --- a/Tests/MacroTestingTests/FixItTests.swift +++ /dev/null @@ -1,93 +0,0 @@ -import MacroTesting -import SwiftDiagnostics -import SwiftSyntax -import SwiftSyntaxBuilder -import SwiftSyntaxMacros -import XCTest - -private enum ReplaceFirstMemberMacro: MemberMacro { - public static func expansion( - of node: AttributeSyntax, - providingMembersOf declaration: some DeclGroupSyntax, - in context: some MacroExpansionContext - ) throws -> [DeclSyntax] { - guard - let nodeToReplace = declaration.memberBlock.members.first, - let newNode = try? MemberBlockItemSyntax( - decl: VariableDeclSyntax(SyntaxNodeString(stringLiteral: "\n let oye: Oye")) - ) - else { return [] } - - context.diagnose( - Diagnostic( - node: node.attributeName, - message: SimpleDiagnosticMessage( - message: "First member needs to be replaced", - diagnosticID: MessageID(domain: "domain", id: "diagnostic2"), - severity: .warning - ), - fixIts: [ - FixIt( - message: SimpleDiagnosticMessage( - message: "Replace the first member", - diagnosticID: MessageID(domain: "domain", id: "fixit1"), - severity: .error - ), - changes: [ - .replace(oldNode: Syntax(nodeToReplace), newNode: Syntax(newNode)) - ] - ) - ] - ) - ) - - return [] - } -} - -final class FixItTests: BaseTestCase { - override func invokeTest() { - withMacroTesting(macros: [ReplaceFirstMemberMacro.self]) { - super.invokeTest() - } - } - - func testReplaceFirstMember() { - assertMacro { - """ - @ReplaceFirstMember - struct FooBar { - let foo: Foo - let bar: Bar - let baz: Baz - } - """ - } diagnostics: { - """ - @ReplaceFirstMember - ┬───────────────── - ╰─ ⚠️ First member needs to be replaced - ✏️ Replace the first member - struct FooBar { - let foo: Foo - let bar: Bar - let baz: Baz - } - """ - } fixes: { - """ - @ReplaceFirstMember - ┬───────────────── - ╰─ ⚠️ First member needs to be replaced - - ✏️ Replace the first member - @ReplaceFirstMember - struct FooBar { - let oye: Oye - let bar: Bar - let baz: Baz - } - """ - } - } -} diff --git a/Tests/MacroTestingTests/FontLiteralMacroTests.swift b/Tests/MacroTestingTests/FontLiteralMacroTests.swift deleted file mode 100644 index 53739f3..0000000 --- a/Tests/MacroTestingTests/FontLiteralMacroTests.swift +++ /dev/null @@ -1,34 +0,0 @@ -import MacroTesting -import XCTest - -final class FontLiteralMacroTests: BaseTestCase { - override func invokeTest() { - withMacroTesting(macros: [FontLiteralMacro.self]) { - super.invokeTest() - } - } - - func testExpansionWithNamedArguments() { - assertMacro { - """ - #fontLiteral(name: "Comic Sans", size: 14, weight: .thin) - """ - } expansion: { - """ - .init(fontLiteralName: "Comic Sans", size: 14, weight: .thin) - """ - } - } - - func testExpansionWithUnlabeledFirstArgument() { - assertMacro { - """ - #fontLiteral("Copperplate Gothic", size: 69, weight: .bold) - """ - } expansion: { - """ - .init(fontLiteralName: "Copperplate Gothic", size: 69, weight: .bold) - """ - } - } -} diff --git a/Tests/MacroTestingTests/FuncUniqueMacroTests.swift b/Tests/MacroTestingTests/FuncUniqueMacroTests.swift deleted file mode 100644 index d8fb6fd..0000000 --- a/Tests/MacroTestingTests/FuncUniqueMacroTests.swift +++ /dev/null @@ -1,27 +0,0 @@ -import MacroTesting -import XCTest - -final class FuncUniqueMacroTests: BaseTestCase { - override func invokeTest() { - withMacroTesting( - macros: [FuncUniqueMacro.self] - ) { - super.invokeTest() - } - } - - func testExpansionCreatesDeclarationWithUniqueFunction() { - assertMacro { - """ - #FuncUnique() - """ - } expansion: { - """ - class MyClass { - func __macro_local_6uniquefMu_() { - } - } - """ - } - } -} diff --git a/Tests/MacroTestingTests/IndentationWidthTests.swift b/Tests/MacroTestingTests/IndentationWidthTests.swift deleted file mode 100644 index fdad4a0..0000000 --- a/Tests/MacroTestingTests/IndentationWidthTests.swift +++ /dev/null @@ -1,116 +0,0 @@ -import MacroTesting -import SwiftSyntax -import SwiftSyntaxMacros -import XCTest - -private struct AddMemberMacro: MemberMacro { - static func expansion( - of node: AttributeSyntax, - providingMembersOf declaration: some DeclGroupSyntax, - in context: some MacroExpansionContext - ) throws -> [DeclSyntax] { - return ["let v: T"] - } -} - -final class IndentationWidthTests: XCTestCase { - func testExpansionAddsMemberUsingDetectedIndentation() { - assertMacro([AddMemberMacro.self]) { - """ - @AddMember - struct S { - let w: T - } - """ - } expansion: { - """ - struct S { - let w: T - - let v: T - } - """ - } - } - - func testExpansionAddsMemberToEmptyStructUsingDefaultIndentation() { - assertMacro([AddMemberMacro.self]) { - """ - @AddMember - struct S { - } - """ - } expansion: { - """ - struct S { - - let v: T - } - """ - } - } - - func testExpansionAddsMemberToEmptyStructUsingTwoSpaceIndentation() { - assertMacro( - [AddMemberMacro.self], - indentationWidth: .spaces(2) - ) { - """ - @AddMember - struct S { - } - """ - } expansion: { - """ - struct S { - - let v: T - } - """ - } - } - - func testExpansionAddsMemberToEmptyStructUsingTwoSpaceIndentation_withMacroTesting() { - withMacroTesting( - indentationWidth: .spaces(2), - macros: [AddMemberMacro.self] - ) { - assertMacro { - """ - @AddMember - struct S { - } - """ - } expansion: { - """ - struct S { - - let v: T - } - """ - } - } - } - - func testExpansionAddsMemberUsingMistchedIndentation() { - assertMacro( - [AddMemberMacro.self], - indentationWidth: .spaces(4) - ) { - """ - @AddMember - struct S { - let w: T - } - """ - } expansion: { - """ - struct S { - let w: T - - let v: T - } - """ - } - } -} diff --git a/Tests/MacroTestingTests/MacroAttributeDiagnosticTests.swift b/Tests/MacroTestingTests/MacroAttributeDiagnosticTests.swift deleted file mode 100644 index 959e7ee..0000000 --- a/Tests/MacroTestingTests/MacroAttributeDiagnosticTests.swift +++ /dev/null @@ -1,384 +0,0 @@ -//// Come back and check this work after two PRs land or are otherwise resolved: -//// - Update FixItApplier -//// - Multi-FixIt support -// -//import MacroTesting -//import SwiftDiagnostics -//import SwiftSyntax -//import SwiftSyntaxBuilder -//import SwiftSyntaxMacros -//import SwiftSyntaxMacrosTestSupport -//import XCTest -// -//private struct Index { -// private static var index = 0 -// -// static var next: Int { -// defer { index += 1 } -// return index -// } -//} -// -//extension Diagnostic { -// fileprivate static func error(on node: AttributeSyntax, fixIts: [FixIt] = []) -> Self { -// let index = Index.next -// return Diagnostic( -// node: node.attributeName, -// message: SimpleDiagnosticMessage( -// message: "This is an error diagnostic (#\(index)).", -// diagnosticID: MessageID(domain: "domain", id: "diagnostic\(index)"), -// severity: .error -// ), -// fixIts: fixIts -// ) -// } -// -// fileprivate static func note(on node: AttributeSyntax) -> Self { -// let index = Index.next -// return Diagnostic( -// node: node.attributeName, -// message: SimpleDiagnosticMessage( -// message: "This is a note diagnostic (#\(index)).", -// diagnosticID: MessageID(domain: "domain", id: "diagnostic(#\(index)"), -// severity: .note -// ) -// ) -// } -// -// fileprivate static func warning(on node: AttributeSyntax, fixIts: [FixIt] = []) -> Self { -// let index = Index.next -// return Diagnostic( -// node: node.attributeName, -// message: SimpleDiagnosticMessage( -// message: "This is a warning diagnostic (#\(index)).", -// diagnosticID: MessageID(domain: "domain", id: "diagnostic\(index)"), -// severity: .warning -// ), -// fixIts: fixIts -// ) -// } -// -//} -// -//extension FixIt { -// fileprivate static func error(on node: Syntax) -> Self { -// let index = Index.next -// return FixIt( -// message: SimpleDiagnosticMessage( -// message: "This is a fix-it (#\(index)).", -// diagnosticID: MessageID(domain: "domain", id: "fixit#\(index)"), -// severity: .error -// ), -// changes: [ -// .replace(oldNode: Syntax(node), newNode: Syntax(node)) // no-op -// ] -// ) -// } -// -// fileprivate static func error(on node: SyntaxProtocol) -> Self { -// error(on: Syntax(node)) -// } -//} -// -//final class DiagnosticTests: XCTestCase { -// func testDiagnosticFixit() { -// enum TestMacro: MemberMacro { -// public static func expansion( -// of node: AttributeSyntax, -// providingMembersOf declaration: some DeclGroupSyntax, -// in context: some MacroExpansionContext -// ) throws -> [DeclSyntax] { -// context.diagnose( -// .error( -// on: node, -// fixIts: [.error(on: node)] // 👈 will crash with a fixit -// // fixIts: [.error(on: declaration)] // 👈 also crashes -// ) -// ) -// -// return [] -// } -// } -// -// // 🛑 🛑 🛑 When given an "expansion" and macro emits a fixit, `assertMacro` will crash. -// assertMacro([TestMacro.self]) { -// """ -// @Test -// struct S {} -// """ -// } expansion: { -// """ -// struct S {} -// """ -// } -// } -// -// func testDiagnosticFixit2() { -// enum TestMacro: MemberMacro { -// public static func expansion( -// of node: AttributeSyntax, -// providingMembersOf declaration: some DeclGroupSyntax, -// in context: some MacroExpansionContext -// ) throws -> [DeclSyntax] { -// context.diagnose( -// .error( -// on: node, -// fixIts: [.error(on: node)] // 👈 fixit -// ) -// ) -// -// return [] -// } -// } -// -// // 🛑 `assertMacro` expands with a blank "expansion" string instead of containing "struct S {}". -// assertMacro([TestMacro.self]) { -// """ -// @Test -// struct S {} -// """ -// // } diagnostics: { -// // """ -// // @Test -// // ┬─── -// // ╰─ 🛑 This is an error diagnostic (#1). -// // ✏️ This is a fix-it (#0). -// // struct S {} -// // """ -// // } fixes: { -// // """ -// // @Test -// // """ -// // } expansion: { -// // """ -// // -// // """ -// } -// } -// -// func testDiagnosticFixitNote() { -// enum TestMacro: MemberMacro { -// public static func expansion( -// of node: AttributeSyntax, -// providingMembersOf declaration: some DeclGroupSyntax, -// in context: some MacroExpansionContext -// ) throws -> [DeclSyntax] { -// context.diagnose( -// .error( -// on: node, -// fixIts: [.error(on: node)] // 👈 fixit -// ) -// ) -// context.diagnose( -// .note(on: node) // 👈 note -// ) -// -// return [] -// } -// } -// -// // `assertMacro` expands with just "diagnostics", omitting "fixes" and "expansion". -// assertMacro([TestMacro.self]) { -// """ -// @Test -// struct S {} -// """ -// // } diagnostics: { -// // """ -// // @Test -// // ┬─── -// // ├─ 🛑 This is an error diagnostic (#1). -// // │ ✏️ This is a fix-it (#0). -// // ╰─ ℹ️ This is a note diagnostic (#2). -// // struct S {} -// // """ -// } -// } -// -// // This test match the emissions of `DiagnosticsAndFixitsEmitterMacro`, and demonstrates -// // that `assertMacro` behaves the same with this test setup. -// func testMirrorDiagnosticsAndFixitsEmitterMacro() { -// enum TestMacro: MemberMacro { -// public static func expansion( -// of node: AttributeSyntax, -// providingMembersOf declaration: some DeclGroupSyntax, -// in context: some MacroExpansionContext -// ) throws -> [DeclSyntax] { -// context.diagnose( -// .warning( // change this to `.error` to only get "diagnostics" instead of "diagnostics" and "expansion". Either way, no "fixes". -// on: node, -// fixIts: [ -// .error(on: node), -// .error(on: node), -// ] -// ) -// ) -// context.diagnose( -// .note(on: node) -// ) -// -// return [] -// } -// } -// -// assertMacro([TestMacro.self]) { -// """ -// @Test -// struct S {} -// """ -// // } diagnostics: { -// // """ -// // @Test -// // ┬─── -// // ├─ ⚠️ This is a warning diagnostic (#2). -// // │ ✏️ This is a fix-it (#0). -// // │ ✏️ This is a fix-it (#1). -// // ╰─ ℹ️ This is a note diagnostic (#3). -// // struct S {} -// // """ -// // } expansion: { -// // """ -// // struct S {} -// // """ -// } -// } -// -// // The actual DiagnosticsAndFixitsEmitterMacro, for comparison to above. -// func testDiagnosticsAndFixitsEmitterMacro() { -// assertMacro([DiagnosticsAndFixitsEmitterMacro.self]) { -// """ -// @DiagnosticsAndFixitsEmitter -// struct S {} -// """ -// // } diagnostics: { -// // """ -// // @DiagnosticsAndFixitsEmitter -// // ┬────────────────────────── -// // ├─ ⚠️ This is the first diagnostic. -// // │ ✏️ This is the first fix-it. -// // │ ✏️ This is the second fix-it. -// // ╰─ ℹ️ This is the second diagnostic, it's a note. -// // struct S {} -// // """ -// // } expansion: { -// // """ -// // struct S {} -// // """ -// } -// } -// -// // `SwiftSyntaxMacrosTestSupport.assertMacroExpansion` works as expected. -// // (The compiler/Xcode also work as expected.) -// func testDiagnosticFixit_assertMacroExpansion() { -// enum TestMacro: MemberMacro { -// public static func expansion( -// of node: AttributeSyntax, -// providingMembersOf declaration: some DeclGroupSyntax, -// in context: some MacroExpansionContext -// ) throws -> [DeclSyntax] { -// context.diagnose( -// .error( -// on: node, -// fixIts: [.error(on: node)] // 👈 fixit -// ) -// ) -// -// return [] -// } -// } -// -// assertMacroExpansion( -// """ -// @Test -// struct S {} -// """, -// expandedSource: """ -// struct S {} -// """, -// diagnostics: [ -// DiagnosticSpec( -// message: "This is an error diagnostic (#1).", -// line: 1, -// column: 2, -// fixIts: [ -// FixItSpec(message: "This is a fix-it (#0).") -// ] -// ) -// ], -// macros: [ -// "Test": TestMacro.self -// ] -// ) -// } -// -// // This test shows awkward but expected behavior, given the bug that the "expansion" is empty. -// func testDiagnostic() { -// enum TestMacro: MemberMacro { -// public static func expansion( -// of node: AttributeSyntax, -// providingMembersOf declaration: some DeclGroupSyntax, -// in context: some MacroExpansionContext -// ) throws -> [DeclSyntax] { -// context.diagnose( -// .error(on: node) // 👈 no fixit -// ) -// -// return [] -// } -// } -// -// // Bug: Expanding `assertMacro` omits the "expansion". -// assertMacro([TestMacro.self]) { -// """ -// @Test -// struct S {} -// """ -// // } diagnostics: { -// // """ -// // @Test -// // ┬─── -// // ╰─ 🛑 This is an error diagnostic (#0). -// // struct S {} -// // """ -// } -// -// // `assertMacro` expands as expected when given the correct "expansion". -// assertMacro([TestMacro.self]) { -// """ -// @Test -// struct S {} -// """ -// // } diagnostics: { -// // """ -// // @Test -// // ┬─── -// // ╰─ 🛑 This is an error diagnostic (#1). -// // struct S {} -// // """ -// } expansion: { -// """ -// struct S {} -// """ -// } -// -// // 🛑 `assertMacro` test failure: : 'failed - Expected macro expansion, but there was none' -// assertMacro([TestMacro.self]) { -// """ -// @Test -// struct S {} -// """ -// } diagnostics: { -// """ -// @Test -// ┬─── -// ╰─ 🛑 This is an error diagnostic (#0). -// struct S {} -// """ -// } expansion: { -// """ -// struct S {} -// """ -// } -// } -// -//} diff --git a/Tests/MacroTestingTests/MacroAttributeDiagnosticTests2.swift b/Tests/MacroTestingTests/MacroAttributeDiagnosticTests2.swift deleted file mode 100644 index 55e24c7..0000000 --- a/Tests/MacroTestingTests/MacroAttributeDiagnosticTests2.swift +++ /dev/null @@ -1,281 +0,0 @@ -//// Come back and check this work after two PRs land or are otherwise resolved: -//// - Update FixItApplier -//// - Multi-FixIt support - -//import MacroTesting -//import SwiftDiagnostics -//import SwiftSyntax -//import SwiftSyntaxBuilder -//import SwiftSyntaxMacros -//import SwiftSyntaxMacrosTestSupport -//import XCTest -// -//// TODO: i am here -//// This doesn't actually have to do with the diagnostic being on the AttributeSyntax node, as I originally though. -//// This has to do with the combination of diagnostics/types/severities. -//// For example, adding a "note" changes the behavior, but not completely. -//// It actually fixes the crash, but the expansion is still wrong. -//// -//// Need to show permutations/combinations: -//// - Diagnostic -//// - Diagnostic, FixIt -//// - Diagnostic, FixIt, Note -// -//// Change tests to this style, to ease undertanding of what's being tested -///* -//enum TestMacro: MemberMacro { -// public static func expansion( -// of node: AttributeSyntax, -// providingMembersOf declaration: some DeclGroupSyntax, -// in context: some MacroExpansionContext -// ) throws -> [DeclSyntax] { -//// context.diagnose(.error) -//// context.diagnose(.errorWithFixit) -//// context.diagnose(.errorWithFixit + .note) -//// context.diagnose(.warning) -//// context.diagnose(.warningWithFixit) -//// context.diagnose(.warningWithFixit + .note) -// -// // context.diagnose(.error) -// // context.diagnose(.error(fixit: .error) + .note) -// // context.diagnose(.error(fixit: .error)) -// // context.diagnose(.error(fixit: .warning)) -// // context.diagnose(.error(fixit: .warning) + .note) -// -// // context.diagnose(.warning) -// // context.diagnose(.warning(fixit: .error) + .note) -// // context.diagnose(.warning(fixit: .error)) -// // context.diagnose(.warning(fixit: .warning)) -// // context.diagnose(.warning(fixit: .warning) + .note) -// -// return [] -// } -//} -//*/ -// -//enum MacroAttributeDiagnosticEmitterMacro2: MemberMacro { -// public static func expansion( -// of node: AttributeSyntax, -// providingMembersOf declaration: some DeclGroupSyntax, -// in context: some MacroExpansionContext -// ) throws -> [DeclSyntax] { -// context.diagnose( -// Diagnostic( -// node: node.attributeName, -// message: SimpleDiagnosticMessage( -// message: "This is the first diagnostic.", -// diagnosticID: MessageID(domain: "domain", id: "diagnostic1"), -// severity: .error -// ) -// ) -// ) -// -// return [] -// } -//} -// -//enum MacroAttributeDiagnosticAndFixitEmitterMacro2: MemberMacro { -// public static func expansion( -// of node: AttributeSyntax, -// providingMembersOf declaration: some DeclGroupSyntax, -// in context: some MacroExpansionContext -// ) throws -> [DeclSyntax] { -// let firstFixIt = FixIt( -// message: SimpleDiagnosticMessage( -// message: "This is the first fix-it.", -// diagnosticID: MessageID(domain: "domain", id: "fixit"), -// severity: .warning -// ), -// changes: [ -// // .replace(oldNode: Syntax(declaration), newNode: Syntax(declaration)) // no-op, testMacroAttributeDiagnosticAndFixitEmitter still crashes -// .replace(oldNode: Syntax(node), newNode: Syntax(node)) // no-op -// ] -// ) -// -// context.diagnose( -// Diagnostic( -// node: node.attributeName, -// message: SimpleDiagnosticMessage( -// message: "This is the first diagnostic.", -// diagnosticID: MessageID(domain: "domain", id: "diagnostic2"), -// severity: .error -// ), -// fixIts: [firstFixIt] -// ) -// ) -// // context.diagnose( -// // Diagnostic( -// // node: node.attributeName, -// // message: SimpleDiagnosticMessage( -// // message: "This is the second diagnostic, it's a note.", -// // diagnosticID: MessageID(domain: "domain", id: "diagnostic3"), -// // severity: .note))) -// -// return [] -// } -//} -// -//final class MacroAttributeDiagnosticTests2: XCTestCase { -// // No fixit, with prexisting "expansion": no crash. -// // See next `testMacroAttributeDiagnosticEmitter_After` for expanded `assertMacro`. -// func testMacroAttributeDiagnosticEmitter_Before() { -// assertMacro([MacroAttributeDiagnosticEmitterMacro2.self]) { -// """ -// @MacroAttributeDiagnosticEmitter -// struct S { -// } -// """ -// } expansion: { -// """ -// struct S { -// } -// """ -// } -// } -// -// // Correctly expanded `assertMacro` from above `testMacroAttributeDiagnosticEmitter_Before`. -// // But, this test fails: 'failed - Expected macro expansion, but there was none' -// func testMacroAttributeDiagnosticEmitter_After() { -// assertMacro([MacroAttributeDiagnosticEmitterMacro2.self]) { -// """ -// @MacroAttributeDiagnosticEmitter -// struct S { -// } -// """ -// } diagnostics: { -// """ -// @MacroAttributeDiagnosticEmitter -// ┬────────────────────────────── -// ╰─ 🛑 This is the first diagnostic. -// struct S { -// } -// """ -// } expansion: { -// """ -// struct S { -// } -// """ -// } -// } -// -// // 🛑 🛑 🛑 Will crash -// // Has fixit and existing "expansion", so it will crash. -// func testMacroAttributeDiagnosticAndFixitEmitter() { -// assertMacro([MacroAttributeDiagnosticAndFixitEmitterMacro2.self]) { -// """ -// @MacroAttributeDiagnosticAndFixitEmitter -// struct S { -// } -// """ -// } expansion: { -// """ -// struct S { -// } -// """ -// } -// } -// -// // TODO: This assertMacro expands correctly, despite preexisting expansion ...wha? -// // And after it expands correctly, it passes. -// // This must be a huge clue. -// // Maybe because it has not diagnostics of "error" severity? I don't think so? -// // DiagnosticsAndFixitsEmitterMacro -// func testMacroAttributeDiagnosticAndFixitEmitterASDF() { -// assertMacro([DiagnosticsAndFixitsEmitterMacro.self]) { -// """ -// @DiagnosticsAndFixitsEmitter -// struct S { -// } -// """ -// // } diagnostics: { -// // """ -// // @DiagnosticsAndFixitsEmitter -// // ┬────────────────────────── -// // ├─ ⚠️ This is the first diagnostic. -// // │ ✏️ This is the first fix-it. -// // │ ✏️ This is the second fix-it. -// // ╰─ ℹ️ This is the second diagnostic, it's a note. -// // struct S { -// // } -// // """ -// } expansion: { -// """ -// struct S { -// } -// """ -// } -// } -// -// // Here, `assertMacro` expands but is wrong: "expansion" is blank. -// // * See next `testMacroAttributeDiagnosticAndFixitEmitter_After` for expanded `assertMacro`. -// func testMacroAttributeDiagnosticAndFixitEmitter_Before() { -// assertMacro([MacroAttributeDiagnosticAndFixitEmitterMacro2.self]) { -// """ -// @MacroAttributeDiagnosticAndFixitEmitter -// struct S { -// } -// """ -// } -// } -// -// // Incorrectly expanded `assertMacro` from above `testMacroAttributeDiagnosticAndFixitEmitter_Before`. -// // * In this state, the test doesn't crash (and passes). -// // * If you manually add the correct non-blank "expansion", the test will fail because it -// // differs from blank but it won't crash. -// func testMacroAttributeDiagnosticAndFixitEmitter_After() { -// assertMacro([MacroAttributeDiagnosticAndFixitEmitterMacro2.self]) { -// """ -// @MacroAttributeDiagnosticAndFixitEmitter -// struct S { -// } -// """ -// } diagnostics: { -// """ -// @MacroAttributeDiagnosticAndFixitEmitter -// ┬────────────────────────────────────── -// ╰─ 🛑 This is the first diagnostic. -// ✏️ This is the first fix-it. -// struct S { -// } -// """ -// } fixes: { -// """ -// @MacroAttributeDiagnosticAndFixitEmitter -// """ -// } expansion: { -// """ -// -// """ -// } -// } -// -// // `SwiftSyntaxMacrosTestSupport.assertMacroExpansion` works as expected. -// // (The compiler/Xcode also work as expected.) -// func testMacroAttributeDiagnosticAndFixitEmitter_assertMacroExpansion() { -// assertMacroExpansion( -// """ -// @MacroAttributeDiagnosticAndFixitEmitter -// struct S { -// } -// """, -// expandedSource: """ -// struct S { -// } -// """, -// diagnostics: [ -// DiagnosticSpec( -// message: "This is the first diagnostic.", -// line: 1, -// column: 2, -// fixIts: [ -// FixItSpec(message: "This is the first fix-it.") -// ] -// ) -// ], -// macros: [ -// "MacroAttributeDiagnosticAndFixitEmitter": MacroAttributeDiagnosticAndFixitEmitterMacro2 -// .self -// ] -// ) -// } -//} diff --git a/Tests/MacroTestingTests/MacroExamples/AddAsyncMacro.swift b/Tests/MacroTestingTests/MacroExamples/AddAsyncMacro.swift deleted file mode 100644 index 5603852..0000000 --- a/Tests/MacroTestingTests/MacroExamples/AddAsyncMacro.swift +++ /dev/null @@ -1,168 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftSyntax -import SwiftSyntaxMacros - -extension SyntaxCollection { - mutating func removeLast() { - self.remove(at: self.index(before: self.endIndex)) - } -} - -public struct AddAsyncMacro: PeerMacro { - public static func expansion< - Context: MacroExpansionContext, - Declaration: DeclSyntaxProtocol - >( - of node: AttributeSyntax, - providingPeersOf declaration: Declaration, - in context: Context - ) throws -> [DeclSyntax] { - - // Only on functions at the moment. - guard var funcDecl = declaration.as(FunctionDeclSyntax.self) else { - throw CustomError.message("@addAsync only works on functions") - } - - // This only makes sense for non async functions. - if funcDecl.signature.effectSpecifiers?.asyncSpecifier != nil { - throw CustomError.message( - "@addAsync requires an non async function" - ) - } - - // This only makes sense void functions - if funcDecl.signature.returnClause?.type.as(IdentifierTypeSyntax.self)?.name.text != "Void" { - throw CustomError.message( - "@addAsync requires an function that returns void" - ) - } - - // Requires a completion handler block as last parameter - guard - let completionHandlerParameterAttribute = funcDecl.signature.parameterClause.parameters.last? - .type.as(AttributedTypeSyntax.self), - let completionHandlerParameter = completionHandlerParameterAttribute.baseType.as( - FunctionTypeSyntax.self) - else { - throw CustomError.message( - "@addAsync requires an function that has a completion handler as last parameter" - ) - } - - // Completion handler needs to return Void - if completionHandlerParameter.returnClause.type.as(IdentifierTypeSyntax.self)?.name.text - != "Void" - { - throw CustomError.message( - "@addAsync requires an function that has a completion handler that returns Void" - ) - } - - let returnType = completionHandlerParameter.parameters.first?.type - - let isResultReturn = returnType?.children(viewMode: .all).first?.description == "Result" - let successReturnType = - isResultReturn - ? returnType!.as(IdentifierTypeSyntax.self)!.genericArgumentClause?.arguments.first! - .argumentCompat600 - : returnType - - // Remove completionHandler and comma from the previous parameter - var newParameterList = funcDecl.signature.parameterClause.parameters - newParameterList.removeLast() - var newParameterListLastParameter = newParameterList.last! - newParameterList.removeLast() - newParameterListLastParameter.trailingTrivia = [] - newParameterListLastParameter.trailingComma = nil - newParameterList.append(newParameterListLastParameter) - - // Drop the @addAsync attribute from the new declaration. - let newAttributeList = funcDecl.attributes.filter { - guard case let .attribute(attribute) = $0, - let attributeType = attribute.attributeName.as(IdentifierTypeSyntax.self), - let nodeType = node.attributeName.as(IdentifierTypeSyntax.self) - else { - return true - } - - return attributeType.name.text != nodeType.name.text - } - - let callArguments: [String] = newParameterList.map { param in - let argName = param.secondName ?? param.firstName - - let paramName = param.firstName - if paramName.text != "_" { - return "\(paramName.text): \(argName.text)" - } - - return "\(argName.text)" - } - - let switchBody: ExprSyntax = - """ - switch returnValue { - case .success(let value): - continuation.resume(returning: value) - case .failure(let error): - continuation.resume(throwing: error) - } - """ - - let newBody: ExprSyntax = - """ - - \(raw: isResultReturn ? "try await withCheckedThrowingContinuation { continuation in" : "await withCheckedContinuation { continuation in") - \(raw: funcDecl.name)(\(raw: callArguments.joined(separator: ", "))) { \(raw: returnType != nil ? "returnValue in" : "") - - \(raw: isResultReturn ? switchBody : "continuation.resume(returning: \(raw: returnType != nil ? "returnValue" : "()"))") - } - } - - """ - - // add async - funcDecl.signature.effectSpecifiers = FunctionEffectSpecifiersSyntax( - leadingTrivia: .space, - asyncSpecifier: .keyword(.async), - throwsSpecifier: isResultReturn ? .keyword(.throws) : nil - ) - - // add result type - if let successReturnType { - funcDecl.signature.returnClause = ReturnClauseSyntax( - leadingTrivia: .space, type: successReturnType.with(\.leadingTrivia, .space)) - } else { - funcDecl.signature.returnClause = nil - } - - // drop completion handler - funcDecl.signature.parameterClause.parameters = newParameterList - funcDecl.signature.parameterClause.trailingTrivia = [] - - funcDecl.body = CodeBlockSyntax( - leftBrace: .leftBraceToken(leadingTrivia: .space), - statements: CodeBlockItemListSyntax( - [CodeBlockItemSyntax(item: .expr(newBody))] - ), - rightBrace: .rightBraceToken(leadingTrivia: .newline) - ) - - funcDecl.attributes = newAttributeList - - funcDecl.leadingTrivia = .newlines(2) - - return [DeclSyntax(funcDecl)] - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/AddBlocker.swift b/Tests/MacroTestingTests/MacroExamples/AddBlocker.swift deleted file mode 100644 index 9778ef5..0000000 --- a/Tests/MacroTestingTests/MacroExamples/AddBlocker.swift +++ /dev/null @@ -1,98 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftDiagnostics -import SwiftOperators -import SwiftSyntax -import SwiftSyntaxMacros - -/// Implementation of the `addBlocker` macro, which demonstrates how to -/// produce detailed diagnostics from a macro implementation for an utterly -/// silly task: warning about every "add" (binary +) in the argument, with a -/// Fix-It that changes it to a "-". -public struct AddBlocker: ExpressionMacro { - class AddVisitor: SyntaxRewriter { - var diagnostics: [Diagnostic] = [] - - override func visit( - _ node: InfixOperatorExprSyntax - ) -> ExprSyntax { - // Identify any infix operator + in the tree. - if var binOp = node.operator.as(BinaryOperatorExprSyntax.self) { - if binOp.operator.text == "+" { - // Form the warning - let messageID = MessageID(domain: "silly", id: "addblock") - diagnostics.append( - Diagnostic( - // Where the warning should go (on the "+"). - node: Syntax(node.operator), - // The warning message and severity. - message: SimpleDiagnosticMessage( - message: "blocked an add; did you mean to subtract?", - diagnosticID: messageID, - severity: .warning - ), - // Highlight the left and right sides of the `+`. - highlights: [ - Syntax(node.leftOperand), - Syntax(node.rightOperand), - ], - fixIts: [ - // Fix-It to replace the '+' with a '-'. - FixIt( - message: SimpleDiagnosticMessage( - message: "use '-'", - diagnosticID: messageID, - severity: .error - ), - changes: [ - FixIt.Change.replace( - oldNode: Syntax(binOp.operator), - newNode: Syntax( - TokenSyntax( - .binaryOperator("-"), - leadingTrivia: binOp.operator.leadingTrivia, - trailingTrivia: binOp.operator.trailingTrivia, - presence: .present - ) - ) - ) - ] - ) - ] - ) - ) - - binOp.operator.tokenKind = .binaryOperator("-") - - return ExprSyntax(node.with(\.operator, ExprSyntax(binOp))) - } - } - - return ExprSyntax(node) - } - } - - public static func expansion( - of node: some FreestandingMacroExpansionSyntax, - in context: some MacroExpansionContext - ) throws -> ExprSyntax { - let visitor = AddVisitor() - let result = visitor.rewrite(Syntax(node)) - - for diag in visitor.diagnostics { - context.diagnose(diag) - } - - return result.asProtocol(FreestandingMacroExpansionSyntax.self)!.arguments.first!.expression - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/AddCompletionHandlerMacro.swift b/Tests/MacroTestingTests/MacroExamples/AddCompletionHandlerMacro.swift deleted file mode 100644 index 0691f23..0000000 --- a/Tests/MacroTestingTests/MacroExamples/AddCompletionHandlerMacro.swift +++ /dev/null @@ -1,165 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftDiagnostics -import SwiftSyntax -import SwiftSyntaxMacros - -public struct AddCompletionHandlerMacro: PeerMacro { - public static func expansion< - Context: MacroExpansionContext, - Declaration: DeclSyntaxProtocol - >( - of node: AttributeSyntax, - providingPeersOf declaration: Declaration, - in context: Context - ) throws -> [DeclSyntax] { - // Only on functions at the moment. We could handle initializers as well - // with a bit of work. - guard var funcDecl = declaration.as(FunctionDeclSyntax.self) else { - throw CustomError.message("@addCompletionHandler only works on functions") - } - - // This only makes sense for async functions. - if funcDecl.signature.effectSpecifiers?.asyncSpecifier == nil { - var newEffects: FunctionEffectSpecifiersSyntax - if let existingEffects = funcDecl.signature.effectSpecifiers { - newEffects = existingEffects - newEffects.asyncSpecifier = .keyword(.async) - } else { - newEffects = FunctionEffectSpecifiersSyntax(asyncSpecifier: .keyword(.async)) - } - - var newSignature = funcDecl.signature - newSignature.effectSpecifiers = newEffects - let messageID = MessageID(domain: "MacroExamples", id: "MissingAsync") - - let diag = Diagnostic( - // Where the error should go (on the "+"). - node: Syntax(funcDecl.funcKeyword), - // The warning message and severity. - message: SimpleDiagnosticMessage( - message: "can only add a completion-handler variant to an 'async' function", - diagnosticID: messageID, - severity: .error - ), - fixIts: [ - // Fix-It to replace the '+' with a '-'. - FixIt( - message: SimpleDiagnosticMessage( - message: "add 'async'", - diagnosticID: messageID, - severity: .error - ), - changes: [ - FixIt.Change.replace( - oldNode: Syntax(funcDecl.signature), - newNode: Syntax(newSignature) - ) - ] - ) - ] - ) - - context.diagnose(diag) - return [] - } - - // Form the completion handler parameter. - var resultType = funcDecl.signature.returnClause?.type - resultType?.leadingTrivia = [] - resultType?.trailingTrivia = [] - - let completionHandlerParam = - FunctionParameterSyntax( - firstName: .identifier("completionHandler"), - colon: .colonToken(trailingTrivia: .space), - type: "@escaping (\(resultType ?? "")) -> Void" as TypeSyntax - ) - - // Add the completion handler parameter to the parameter list. - let parameterList = funcDecl.signature.parameterClause.parameters - var newParameterList = parameterList - if var lastParam = parameterList.last { - // We need to add a trailing comma to the preceding list. - newParameterList.removeLast() - lastParam.trailingComma = .commaToken(trailingTrivia: .space) - newParameterList += [ - lastParam, - completionHandlerParam, - ] - } else { - newParameterList.append(completionHandlerParam) - } - - let callArguments: [String] = parameterList.map { param in - let argName = param.secondName ?? param.firstName - - let paramName = param.firstName - if paramName.text != "_" { - return "\(paramName.text): \(argName.text)" - } - - return "\(argName.text)" - } - - let call: ExprSyntax = - "\(funcDecl.name)(\(raw: callArguments.joined(separator: ", ")))" - - // FIXME: We should make CodeBlockSyntax ExpressibleByStringInterpolation, - // so that the full body could go here. - let newBody: ExprSyntax = - """ - - Task { - completionHandler(await \(call)) - } - - """ - - // Drop the @addCompletionHandler attribute from the new declaration. - let newAttributeList = funcDecl.attributes.filter { - guard case let .attribute(attribute) = $0, - let attributeType = attribute.attributeName.as(IdentifierTypeSyntax.self), - let nodeType = node.attributeName.as(IdentifierTypeSyntax.self) - else { - return true - } - - return attributeType.name.text != nodeType.name.text - } - - // drop async - funcDecl.signature.effectSpecifiers?.asyncSpecifier = nil - - // drop result type - funcDecl.signature.returnClause = nil - - // add completion handler parameter - funcDecl.signature.parameterClause.parameters = newParameterList - funcDecl.signature.parameterClause.trailingTrivia = [] - - funcDecl.body = CodeBlockSyntax( - leftBrace: .leftBraceToken(leadingTrivia: .space), - statements: CodeBlockItemListSyntax( - [CodeBlockItemSyntax(item: .expr(newBody))] - ), - rightBrace: .rightBraceToken(leadingTrivia: .newline) - ) - - funcDecl.attributes = newAttributeList - - funcDecl.leadingTrivia = .newlines(2) - - return [DeclSyntax(funcDecl)] - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/CaseDetectionMacro.swift b/Tests/MacroTestingTests/MacroExamples/CaseDetectionMacro.swift deleted file mode 100644 index 4e9e184..0000000 --- a/Tests/MacroTestingTests/MacroExamples/CaseDetectionMacro.swift +++ /dev/null @@ -1,49 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftSyntax -import SwiftSyntaxMacros - -public enum CaseDetectionMacro: MemberMacro { - public static func expansion( - of node: AttributeSyntax, - providingMembersOf declaration: some DeclGroupSyntax, - in context: some MacroExpansionContext - ) throws -> [DeclSyntax] { - declaration.memberBlock.members - .compactMap { $0.decl.as(EnumCaseDeclSyntax.self) } - .map { $0.elements.first!.name } - .map { ($0, $0.initialUppercased) } - .map { original, uppercased in - """ - var is\(raw: uppercased): Bool { - if case .\(raw: original) = self { - return true - } - - return false - } - """ - } - } -} - -extension TokenSyntax { - fileprivate var initialUppercased: String { - let name = self.text - guard let initial = name.first else { - return name - } - - return "\(initial.uppercased())\(name.dropFirst())" - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/CustomCodable.swift b/Tests/MacroTestingTests/MacroExamples/CustomCodable.swift deleted file mode 100644 index a3b1d6a..0000000 --- a/Tests/MacroTestingTests/MacroExamples/CustomCodable.swift +++ /dev/null @@ -1,69 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftSyntax -import SwiftSyntaxMacros - -public enum CustomCodable: MemberMacro { - public static func expansion( - of node: AttributeSyntax, - providingMembersOf declaration: some DeclGroupSyntax, - in context: some MacroExpansionContext - ) throws -> [DeclSyntax] { - let memberList = declaration.memberBlock.members - - let cases = memberList.compactMap({ member -> String? in - // is a property - guard - let propertyName = member.decl.as(VariableDeclSyntax.self)?.bindings.first?.pattern.as( - IdentifierPatternSyntax.self)?.identifier.text - else { - return nil - } - - // if it has a CodableKey macro on it - if let customKeyMacro = member.decl.as(VariableDeclSyntax.self)?.attributes.first(where: { - element in - element.as(AttributeSyntax.self)?.attributeName.as(IdentifierTypeSyntax.self)?.description - == "CodableKey" - }) { - - // Uses the value in the Macro - let customKeyValue = customKeyMacro.as(AttributeSyntax.self)!.arguments!.as( - LabeledExprListSyntax.self)!.first!.expression - - return "case \(propertyName) = \(customKeyValue)" - } else { - return "case \(propertyName)" - } - }) - - let codingKeys: DeclSyntax = """ - enum CodingKeys: String, CodingKey { - \(raw: cases.joined(separator: "\n")) - } - """ - - return [codingKeys] - } -} - -public struct CodableKey: PeerMacro { - public static func expansion( - of node: AttributeSyntax, - providingPeersOf declaration: some DeclSyntaxProtocol, - in context: some MacroExpansionContext - ) throws -> [DeclSyntax] { - // Does nothing, used only to decorate members with data - return [] - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/DefaultFatalErrorImplementationMacro.swift b/Tests/MacroTestingTests/MacroExamples/DefaultFatalErrorImplementationMacro.swift deleted file mode 100644 index 401cf22..0000000 --- a/Tests/MacroTestingTests/MacroExamples/DefaultFatalErrorImplementationMacro.swift +++ /dev/null @@ -1,73 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftDiagnostics -import SwiftSyntax -import SwiftSyntaxBuilder -import SwiftSyntaxMacros - -/// Provides default `fatalError` implementations for protocol methods. -/// -/// This macro generates extensions that add default `fatalError` implementations -/// for each method in the protocol it is attached to. -public enum DefaultFatalErrorImplementationMacro: ExtensionMacro { - - /// Unique identifier for messages related to this macro. - private static let messageID = MessageID( - domain: "MacroExamples", id: "ProtocolDefaultImplementation") - - /// Generates extension for the protocol to which this macro is attached. - public static func expansion( - of node: AttributeSyntax, - attachedTo declaration: some DeclGroupSyntax, - providingExtensionsOf type: some TypeSyntaxProtocol, - conformingTo protocols: [TypeSyntax], - in context: some MacroExpansionContext - ) throws -> [ExtensionDeclSyntax] { - - // Validate that the macro is being applied to a protocol declaration - guard let protocolDecl = declaration.as(ProtocolDeclSyntax.self) else { - throw SimpleDiagnosticMessage( - message: "Macro `defaultFatalErrorImplementation` can only be applied to a protocol", - diagnosticID: messageID, - severity: .error - ) - } - - // Extract all the methods from the protocol and assign default implementations - let methods = protocolDecl.memberBlock.members - .map(\.decl) - .compactMap { declaration -> FunctionDeclSyntax? in - guard var function = declaration.as(FunctionDeclSyntax.self) else { - return nil - } - function.body = CodeBlockSyntax { - ExprSyntax(#"fatalError("whoops 😅")"#) - } - return function - } - - // Don't generate an extension if there are no methods - if methods.isEmpty { - return [] - } - - // Generate the extension containing the default implementations - let extensionDecl = ExtensionDeclSyntax(extendedType: type) { - for method in methods { - MemberBlockItemSyntax(decl: method) - } - } - - return [extensionDecl] - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/Diagnostics.swift b/Tests/MacroTestingTests/MacroExamples/Diagnostics.swift deleted file mode 100644 index 83a08df..0000000 --- a/Tests/MacroTestingTests/MacroExamples/Diagnostics.swift +++ /dev/null @@ -1,35 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftDiagnostics -import SwiftSyntax - -struct SimpleDiagnosticMessage: DiagnosticMessage, Error { - let message: String - let diagnosticID: MessageID - let severity: DiagnosticSeverity -} - -extension SimpleDiagnosticMessage: FixItMessage { - var fixItID: MessageID { diagnosticID } -} - -enum CustomError: Error, CustomStringConvertible { - case message(String) - - var description: String { - switch self { - case .message(let text): - return text - } - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/DiagnosticsAndFixitsEmitterMacro.swift b/Tests/MacroTestingTests/MacroExamples/DiagnosticsAndFixitsEmitterMacro.swift deleted file mode 100644 index 753f803..0000000 --- a/Tests/MacroTestingTests/MacroExamples/DiagnosticsAndFixitsEmitterMacro.swift +++ /dev/null @@ -1,61 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftDiagnostics -import SwiftSyntax -import SwiftSyntaxBuilder -import SwiftSyntaxMacros - -/// Emits two diagnostics, the first of which is a warning and has two fix-its, and -/// the second is a note and has no fix-its. -public enum DiagnosticsAndFixitsEmitterMacro: MemberMacro { - public static func expansion( - of node: AttributeSyntax, - providingMembersOf declaration: some DeclGroupSyntax, - in context: some MacroExpansionContext - ) throws -> [DeclSyntax] { - let firstFixIt = FixIt( - message: SimpleDiagnosticMessage( - message: "This is the first fix-it.", - diagnosticID: MessageID(domain: "domain", id: "fixit1"), - severity: .error), - changes: [ - .replace(oldNode: Syntax(node), newNode: Syntax(node)) // no-op - ]) - let secondFixIt = FixIt( - message: SimpleDiagnosticMessage( - message: "This is the second fix-it.", - diagnosticID: MessageID(domain: "domain", id: "fixit2"), - severity: .error), - changes: [ - .replace(oldNode: Syntax(node), newNode: Syntax(node)) // no-op - ]) - - context.diagnose( - Diagnostic( - node: node.attributeName, - message: SimpleDiagnosticMessage( - message: "This is the first diagnostic.", - diagnosticID: MessageID(domain: "domain", id: "diagnostic2"), - severity: .warning), - fixIts: [firstFixIt, secondFixIt])) - context.diagnose( - Diagnostic( - node: node.attributeName, - message: SimpleDiagnosticMessage( - message: "This is the second diagnostic, it's a note.", - diagnosticID: MessageID(domain: "domain", id: "diagnostic2"), - severity: .note))) - - return [] - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/DictionaryIndirectionMacro.swift b/Tests/MacroTestingTests/MacroExamples/DictionaryIndirectionMacro.swift deleted file mode 100644 index ccd3651..0000000 --- a/Tests/MacroTestingTests/MacroExamples/DictionaryIndirectionMacro.swift +++ /dev/null @@ -1,92 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftSyntax -import SwiftSyntaxMacros - -public struct DictionaryStorageMacro {} - -extension DictionaryStorageMacro: MemberMacro { - public static func expansion( - of node: AttributeSyntax, - providingMembersOf declaration: some DeclGroupSyntax, - in context: some MacroExpansionContext - ) throws -> [DeclSyntax] { - return ["\n var _storage: [String: Any] = [:]"] - } -} - -extension DictionaryStorageMacro: MemberAttributeMacro { - public static func expansion( - of node: AttributeSyntax, - attachedTo declaration: some DeclGroupSyntax, - providingAttributesFor member: some DeclSyntaxProtocol, - in context: some MacroExpansionContext - ) throws -> [AttributeSyntax] { - guard let property = member.as(VariableDeclSyntax.self), - property.isStoredProperty - else { - return [] - } - - return [ - AttributeSyntax( - leadingTrivia: [.newlines(1), .spaces(2)], - attributeName: IdentifierTypeSyntax( - name: .identifier("DictionaryStorageProperty") - ) - ) - ] - } -} - -public struct DictionaryStoragePropertyMacro: AccessorMacro { - public static func expansion< - Context: MacroExpansionContext, - Declaration: DeclSyntaxProtocol - >( - of node: AttributeSyntax, - providingAccessorsOf declaration: Declaration, - in context: Context - ) throws -> [AccessorDeclSyntax] { - guard let varDecl = declaration.as(VariableDeclSyntax.self), - let binding = varDecl.bindings.first, - let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier, - binding.accessorBlock == nil, - let type = binding.typeAnnotation?.type - else { - return [] - } - - // Ignore the "_storage" variable. - if identifier.text == "_storage" { - return [] - } - - guard let defaultValue = binding.initializer?.value else { - throw CustomError.message("stored property must have an initializer") - } - - return [ - """ - get { - _storage[\(literal: identifier.text), default: \(defaultValue)] as! \(type) - } - """, - """ - set { - _storage[\(literal: identifier.text)] = newValue - } - """, - ] - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/EntryMacro.swift b/Tests/MacroTestingTests/MacroExamples/EntryMacro.swift deleted file mode 100644 index a1f12e9..0000000 --- a/Tests/MacroTestingTests/MacroExamples/EntryMacro.swift +++ /dev/null @@ -1,30 +0,0 @@ -#if canImport(SwiftSyntax600) - import SwiftSyntax - import SwiftSyntaxMacros - - // Not complete, just enough to unit test lexical context. - public struct EntryMacro: AccessorMacro { - public static func expansion( - of node: AttributeSyntax, - providingAccessorsOf declaration: some DeclSyntaxProtocol, - in context: some MacroExpansionContext - ) throws -> [AccessorDeclSyntax] { - let isInEnvironmentValues = context.lexicalContext.contains { lexicalContext in - lexicalContext.as(ExtensionDeclSyntax.self)?.extendedType.trimmedDescription - == "EnvironmentValues" - } - - guard isInEnvironmentValues else { - throw MacroExpansionErrorMessage( - "'@Entry' macro can only attach to var declarations inside extensions of EnvironmentValues" - ) - } - - return [ - AccessorDeclSyntax(accessorSpecifier: .keyword(.get)) { - "fatalError()" - } - ] - } - } -#endif diff --git a/Tests/MacroTestingTests/MacroExamples/EquatableExtensionMacro.swift b/Tests/MacroTestingTests/MacroExamples/EquatableExtensionMacro.swift deleted file mode 100644 index f13cf98..0000000 --- a/Tests/MacroTestingTests/MacroExamples/EquatableExtensionMacro.swift +++ /dev/null @@ -1,28 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftSyntax -import SwiftSyntaxMacros - -public enum EquatableExtensionMacro: ExtensionMacro { - public static func expansion( - of node: AttributeSyntax, - attachedTo declaration: some DeclGroupSyntax, - providingExtensionsOf type: some TypeSyntaxProtocol, - conformingTo protocols: [TypeSyntax], - in context: some MacroExpansionContext - ) throws -> [ExtensionDeclSyntax] { - let equatableExtension = try ExtensionDeclSyntax("extension \(type.trimmed): Equatable {}") - - return [equatableExtension] - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/FontLiteralMacro.swift b/Tests/MacroTestingTests/MacroExamples/FontLiteralMacro.swift deleted file mode 100644 index 8afbad6..0000000 --- a/Tests/MacroTestingTests/MacroExamples/FontLiteralMacro.swift +++ /dev/null @@ -1,46 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftSyntax -import SwiftSyntaxMacros - -/// Implementation of the `#fontLiteral` macro, which is similar in spirit -/// to the built-in expressions `#colorLiteral`, `#imageLiteral`, etc., but in -/// a small macro. -public enum FontLiteralMacro: ExpressionMacro { - public static func expansion( - of node: some FreestandingMacroExpansionSyntax, - in context: some MacroExpansionContext - ) throws -> ExprSyntax { - let argList = replaceFirstLabel( - of: node.arguments, - with: "fontLiteralName" - ) - return ".init(\(argList))" - } -} - -/// Replace the label of the first element in the tuple with the given -/// new label. -private func replaceFirstLabel( - of tuple: LabeledExprListSyntax, - with newLabel: String -) -> LabeledExprListSyntax { - if tuple.isEmpty { - return tuple - } - - var tuple = tuple - tuple[tuple.startIndex].label = .identifier(newLabel) - tuple[tuple.startIndex].colon = .colonToken() - return tuple -} diff --git a/Tests/MacroTestingTests/MacroExamples/FuncUniqueMacro.swift b/Tests/MacroTestingTests/MacroExamples/FuncUniqueMacro.swift deleted file mode 100644 index 9f495c9..0000000 --- a/Tests/MacroTestingTests/MacroExamples/FuncUniqueMacro.swift +++ /dev/null @@ -1,32 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftSyntax -import SwiftSyntaxBuilder -import SwiftSyntaxMacros - -/// Func With unique name. -public enum FuncUniqueMacro: DeclarationMacro { - public static func expansion( - of node: some FreestandingMacroExpansionSyntax, - in context: some MacroExpansionContext - ) throws -> [DeclSyntax] { - let name = context.makeUniqueName("unique") - return [ - """ - class MyClass { - func \(name)() {} - } - """ - ] - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/MemberDeprecatedMacro.swift b/Tests/MacroTestingTests/MacroExamples/MemberDeprecatedMacro.swift deleted file mode 100644 index 27f7418..0000000 --- a/Tests/MacroTestingTests/MacroExamples/MemberDeprecatedMacro.swift +++ /dev/null @@ -1,26 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftSyntax -import SwiftSyntaxMacros - -/// Add '@available(*, deprecated)' to members. -public enum MemberDeprecatedMacro: MemberAttributeMacro { - public static func expansion( - of node: AttributeSyntax, - attachedTo declaration: some DeclGroupSyntax, - providingAttributesFor member: some DeclSyntaxProtocol, - in context: some MacroExpansionContext - ) throws -> [AttributeSyntax] { - return ["@available(*, deprecated)"] - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/MemberwiseInitMacro.swift b/Tests/MacroTestingTests/MacroExamples/MemberwiseInitMacro.swift deleted file mode 100644 index 0c525e9..0000000 --- a/Tests/MacroTestingTests/MacroExamples/MemberwiseInitMacro.swift +++ /dev/null @@ -1,1987 +0,0 @@ -//import Foundation -////import SwiftCompilerPlugin -//import SwiftDiagnostics -//import SwiftOperators -//import SwiftSyntax -//import SwiftSyntaxBuilder -//import SwiftSyntaxMacroExpansion -//import SwiftSyntaxMacros -// -//public struct InitMacro: PeerMacro { -// public static func expansion( -// of node: SwiftSyntax.AttributeSyntax, -// providingPeersOf declaration: some SwiftSyntax.DeclSyntaxProtocol, -// in context: some SwiftSyntaxMacros.MacroExpansionContext -// ) throws -> [SwiftSyntax.DeclSyntax] { -// return [] -// } -//} -// -//public struct MemberwiseInitMacro: MemberMacro { -// public static func expansion( -// of node: AttributeSyntax, -// providingMembersOf decl: D, -// in context: C -// ) throws -> [SwiftSyntax.DeclSyntax] -// where D: DeclGroupSyntax, C: MacroExpansionContext { -// guard [SwiftSyntax.SyntaxKind.classDecl, .structDecl, .actorDecl].contains(decl.kind) else { -// throw MacroExpansionErrorMessage( -// """ -// @MemberwiseInit can only be attached to a struct, class, or actor; \ -// not to \(decl.descriptiveDeclKind(withArticle: true)). -// """ -// ) -// } -// -// deprecationDiagnostics(node: node, declaration: decl) -// .forEach(context.diagnose) -// -// let configuredAccessLevel: AccessLevelModifier? = extractConfiguredAccessLevel(from: node) -// let optionalsDefaultNil: Bool? = -// extractLabeledBoolArgument("_optionalsDefaultNil", from: node) -// let deunderscoreParameters: Bool = -// extractLabeledBoolArgument("_deunderscoreParameters", from: node) ?? false -// -// let accessLevel = configuredAccessLevel ?? .internal -// let (properties, diagnostics) = try collectMemberPropertiesAndDiagnostics( -// from: decl.memberBlock.members, -// targetAccessLevel: accessLevel -// ) -// diagnostics.forEach { context.diagnose($0) } -// -// func formatParameters() -> String { -// guard !properties.isEmpty else { return "" } -// -// return "\n" -// + properties -// .map { property in -// formatParameter( -// for: property, -// considering: properties, -// deunderscoreParameters: deunderscoreParameters, -// optionalsDefaultNil: optionalsDefaultNil -// ?? defaultOptionalsDefaultNil( -// for: property.keywordToken, -// initAccessLevel: accessLevel -// ) -// ) -// } -// .joined(separator: ",\n") -// + "\n" -// } -// -// let formattedInitSignature = "\n\(accessLevel) init(\(formatParameters()))" -// return [ -// DeclSyntax( -// try InitializerDeclSyntax(SyntaxNodeString(stringLiteral: formattedInitSignature)) { -// CodeBlockItemListSyntax( -// properties -// .map { property in -// CodeBlockItemSyntax( -// stringLiteral: formatInitializerAssignmentStatement( -// for: property, -// considering: properties, -// deunderscoreParameters: deunderscoreParameters -// ) -// ) -// } -// ) -// } -// ) -// ] -// } -// -// private static func extractConfiguredAccessLevel( -// from node: AttributeSyntax -// ) -> AccessLevelModifier? { -// guard let arguments = node.arguments?.as(LabeledExprListSyntax.self) -// else { return nil } -// -// // NB: Search for the first argument whose name matches an access level name -// for labeledExprSyntax in arguments { -// if let identifier = labeledExprSyntax.expression.as(MemberAccessExprSyntax.self)?.declName, -// let accessLevel = AccessLevelModifier(rawValue: identifier.baseName.trimmedDescription) -// { -// return accessLevel -// } -// } -// -// return nil -// } -// -// private static func extractLabeledBoolArgument( -// _ label: String, -// from node: AttributeSyntax -// ) -> Bool? { -// guard let arguments = node.arguments?.as(LabeledExprListSyntax.self) -// else { return nil } -// -// let argument = arguments.filter { labeledExprSyntax in -// labeledExprSyntax.label?.text == label -// }.first -// -// guard let argument else { return nil } -// return argument.expression.as(BooleanLiteralExprSyntax.self)?.literal.text == "true" -// } -// -// private static func collectMemberPropertiesAndDiagnostics( -// from memberBlockItemList: MemberBlockItemListSyntax, -// targetAccessLevel: AccessLevelModifier -// ) throws -> ([MemberProperty], [Diagnostic]) { -// let (variables, variableDiagnostics) = collectMemberVariables( -// from: memberBlockItemList, -// targetAccessLevel: targetAccessLevel -// ) -// -// let bindings = collectPropertyBindings(variables: variables) -// let bindingDiagnostics = customInitLabelDiagnosticsFor(bindings: bindings) -// -// var (properties, memberDiagnostics) = collectMemberProperties(bindings: bindings) -// memberDiagnostics += customInitLabelDiagnosticsFor(properties: properties) -// -// return (properties, variableDiagnostics + bindingDiagnostics + memberDiagnostics) -// } -// -// private static func collectMemberVariables( -// from memberBlockItemList: MemberBlockItemListSyntax, -// targetAccessLevel: AccessLevelModifier -// ) -> ([MemberVariable], [Diagnostic]) { -// memberBlockItemList -// .reduce( -// into: ( -// variables: [MemberVariable](), -// diagnostics: [Diagnostic]() -// ) -// ) { acc, member in -// guard -// let variable = member.decl.as(VariableDeclSyntax.self), -// variable.attributes.isEmpty || variable.hasCustomConfigurationAttribute, -// !variable.isComputedProperty -// else { return } -// -// if let diagnostics = diagnoseMultipleConfigurations(variable: variable) { -// acc.diagnostics += diagnostics -// return -// } -// -// let customSettings = extractVariableCustomSettings(from: variable) -// if let customSettings, customSettings.ignore { -// return -// } -// -// let diagnostics = diagnoseVariableDecl( -// customSettings: customSettings, -// variable: variable, -// targetAccessLevel: targetAccessLevel -// ) -// guard diagnostics.isEmpty else { -// acc.diagnostics += diagnostics -// return -// } -// -// guard variable.modifiersExclude([.static, .lazy]) else { return } -// -// acc.variables.append( -// MemberVariable( -// customSettings: customSettings, -// syntax: variable -// ) -// ) -// } -// } -// -// private static func collectPropertyBindings(variables: [MemberVariable]) -> [PropertyBinding] { -// variables.flatMap { variable -> [PropertyBinding] in -// variable.bindings -// .reversed() -// .reduce( -// into: ( -// bindings: [PropertyBinding](), -// typeFromTrailingBinding: TypeSyntax?.none -// ) -// ) { acc, binding in -// acc.bindings.append( -// PropertyBinding( -// typeFromTrailingBinding: acc.typeFromTrailingBinding, -// syntax: binding, -// variable: variable -// ) -// ) -// acc.typeFromTrailingBinding = -// binding.typeAnnotation?.type ?? acc.typeFromTrailingBinding -// } -// .bindings -// .reversed() -// } -// } -// -// private static func collectMemberProperties( -// bindings: [PropertyBinding] -// ) -> ( -// members: [MemberProperty], -// diagnostics: [Diagnostic] -// ) { -// bindings.reduce( -// into: ( -// members: [MemberProperty](), -// diagnostics: [Diagnostic]() -// ) -// ) { acc, propertyBinding in -// if propertyBinding.isInitializedLet { -// return -// } -// -// if propertyBinding.isInitializedVarWithoutType { -// acc.diagnostics.append( -// propertyBinding.diagnostic( -// MacroExpansionErrorMessage("@MemberwiseInit requires a type annotation.") // TODO: fix-it -// ) -// ) -// return -// } -// if propertyBinding.isTuplePattern { -// acc.diagnostics.append( -// propertyBinding.diagnostic( -// MacroExpansionErrorMessage( -// """ -// @MemberwiseInit does not support tuple destructuring for property declarations. \ -// Use multiple declarations instead. -// """ -// ) -// ) -// ) -// -// return -// } -// -// guard -// let name = propertyBinding.name, -// let effectiveType = propertyBinding.effectiveType -// else { return } -// -// let newProperty = MemberProperty( -// accessLevel: propertyBinding.variable.accessLevel, -// customSettings: propertyBinding.variable.customSettings, -// initializerValue: propertyBinding.initializerValue, -// keywordToken: propertyBinding.variable.keywordToken, -// name: name, -// type: effectiveType.trimmed -// ) -// acc.members.append(newProperty) -// } -// } -// -// private static func extractVariableCustomSettings( -// from variable: VariableDeclSyntax -// ) -> VariableCustomSettings? { -// guard let customConfigurationAttribute = variable.customConfigurationAttribute else { -// return nil -// } -// -// let customConfiguration = variable.customConfigurationArguments -// -// let configuredValues = -// customConfiguration?.compactMap { -// $0.expression.as(MemberAccessExprSyntax.self)?.declName.baseName.trimmedDescription -// } -// -// let configuredAccessLevel = -// configuredValues? -// .compactMap(AccessLevelModifier.init(rawValue:)) -// .first -// -// let configuredAssignee: VariableCustomSettings.Assignee? = -// (customConfigurationAttribute.isInitWrapper ? .wrapper : nil) -// ?? customConfiguration? -// .firstWhereLabel("assignee")? -// .expression -// .trimmedStringLiteral -// .map(VariableCustomSettings.Assignee.raw) -// -// let configuredForceEscaping = -// (customConfiguration? -// .firstWhereLabel("escaping")? -// .expression -// .as(BooleanLiteralExprSyntax.self)? -// .literal -// .text == "true") -// || configuredValues?.contains("escaping") ?? false // Deprecated; remove in 1.0 -// -// let configuredIgnore = configuredValues?.contains("ignore") ?? false -// -// let configuredDefault = -// customConfiguration? -// .firstWhereLabel("default")? -// .expression -// .trimmedDescription -// -// let configuredLabel = -// customConfiguration? -// .firstWhereLabel("label")? -// .expression -// .trimmedStringLiteral -// -// let configuredType = -// customConfiguration? -// .firstWhereLabel("type")? -// .expression -// .trimmedDescription -// -// // TODO: Is it possible for invalid type syntax to be provided for an `Any.Type` parameter? -// // NB: All expressions satisfying the `Any.Type` parameter type are parsable to TypeSyntax. -// let configuredTypeSyntax = -// configuredType.map(TypeSyntax.init(stringLiteral:)) -// -// return VariableCustomSettings( -// accessLevel: configuredAccessLevel, -// assignee: configuredAssignee, -// defaultValue: configuredDefault, -// forceEscaping: configuredForceEscaping, -// ignore: configuredIgnore, -// label: configuredLabel, -// type: configuredTypeSyntax, -// _syntaxNode: customConfigurationAttribute -// ) -// } -// -// private static func defaultOptionalsDefaultNil( -// for bindingKeyword: TokenKind, -// initAccessLevel: AccessLevelModifier -// ) -> Bool { -// guard bindingKeyword == .keyword(.var) else { return false } -// return switch initAccessLevel { -// case .private, .fileprivate, .internal: -// true -// case .package, .public, .open: -// false -// } -// } -// -// private static func formatParameter( -// for property: MemberProperty, -// considering allProperties: [MemberProperty], -// deunderscoreParameters: Bool, -// optionalsDefaultNil: Bool -// ) -> String { -// let defaultValue = -// property.initializerValue.map { " = \($0.description)" } -// ?? property.customSettings?.defaultValue.map { " = \($0.description)" } -// ?? (optionalsDefaultNil && property.type.isOptionalType ? " = nil" : "") -// -// let escaping = -// (property.customSettings?.forceEscaping ?? false || property.type.isFunctionType) -// ? "@escaping " : "" -// -// let label = property.initParameterLabel( -// considering: allProperties, deunderscoreParameters: deunderscoreParameters) -// -// let parameterName = property.initParameterName( -// considering: allProperties, deunderscoreParameters: deunderscoreParameters) -// -// return "\(label)\(parameterName): \(escaping)\(property.type.description)\(defaultValue)" -// } -// -// private static func formatInitializerAssignmentStatement( -// for property: MemberProperty, -// considering allProperties: [MemberProperty], -// deunderscoreParameters: Bool -// ) -> String { -// let assignee = -// switch property.customSettings?.assignee { -// case .none: -// "self.\(property.name)" -// case .wrapper: -// "self._\(property.name)" -// case let .raw(assignee): -// assignee -// } -// -// let parameterName = property.initParameterName( -// considering: allProperties, -// deunderscoreParameters: deunderscoreParameters -// ) -// return "\(assignee) = \(parameterName)" -// } -//} -// -//// Modified from IanKeen's MacroKit: -//// https://github.com/IanKeen/MacroKit/blob/main/Sources/MacroKitMacros/Support/AccessLevelSyntax.swift -//// -//// Modifications: -//// - Declarations can have multiple access level modifiers, e.g. `public private(set)` -//// I'm still not dealing with the "detail" (`set`) in any way -//// - Apply Swift's rules concering default access when no access level is explicitly stated (it's not always `internal`): -//// https://docs.swift.org/swift-book/documentation/the-swift-programming-language/accesscontrol#Custom-Types -//// - Added missing DeclGroupSyntax kinds -// -//// TODO: Rules for local DeclGroup's nested within a function definition -//// TODO: Rules for local access functions? e.g. `func foo() { func bar() { … } … }` -// -//enum AccessLevelModifier: String, Comparable, CaseIterable, Sendable { -// case `private` -// case `fileprivate` -// case `internal` -// case `package` -// case `public` -// case `open` -// -// var keyword: Keyword { -// switch self { -// case .private: return .private -// case .fileprivate: return .fileprivate -// case .internal: return .internal -// case .package: return .package -// case .public: return .public -// case .open: return .open -// } -// } -// -// static func < (lhs: AccessLevelModifier, rhs: AccessLevelModifier) -> Bool { -// let lhs = Self.allCases.firstIndex(of: lhs)! -// let rhs = Self.allCases.firstIndex(of: rhs)! -// return lhs < rhs -// } -//} -// -//public protocol AccessLevelSyntax { -// var parent: Syntax? { get } -// var modifiers: DeclModifierListSyntax { get set } -//} -// -//extension AccessLevelSyntax { -// var accessLevelModifiers: [AccessLevelModifier]? { -// get { -// let accessLevels = modifiers.lazy.compactMap { AccessLevelModifier(rawValue: $0.name.text) } -// return accessLevels.isEmpty ? nil : Array(accessLevels) -// } -// set { -// guard let newModifiers = newValue else { -// modifiers = [] -// return -// } -// let newModifierKeywords = newModifiers.map { DeclModifierSyntax(name: .keyword($0.keyword)) } -// let filteredModifiers = modifiers.filter { -// AccessLevelModifier(rawValue: $0.name.text) == nil -// } -// modifiers = filteredModifiers + newModifierKeywords -// } -// } -//} -// -//protocol DeclGroupAccessLevelSyntax: AccessLevelSyntax { -//} -//extension DeclGroupAccessLevelSyntax { -// public var accessLevel: AccessLevelModifier { -// self.accessLevelModifiers?.first ?? .internal -// } -//} -// -//extension ActorDeclSyntax: DeclGroupAccessLevelSyntax {} -//extension ClassDeclSyntax: DeclGroupAccessLevelSyntax {} -//extension EnumDeclSyntax: DeclGroupAccessLevelSyntax {} -//extension StructDeclSyntax: DeclGroupAccessLevelSyntax {} -// -//// NB: MemberwiseInit doesn't need this on FunctionDeclSyntax extension -////extension FunctionDeclSyntax: AccessLevelSyntax { -//// public var accessLevel: AccessLevelModifier { -//// get { -//// // a decl (function, variable) can -//// if let formalModifier = self.accessLevelModifiers?.first { -//// return formalModifier -//// } -//// -//// guard let parent = self.parent else { return .internal } -//// -//// if let parent = parent as? DeclGroupSyntax { -//// return [parent.declAccessLevel, .internal].min()! -//// } else { -//// return .internal -//// } -//// } -//// } -////} -// -//extension VariableDeclSyntax: AccessLevelSyntax { -// var accessLevel: AccessLevelModifier { -// // TODO: assuming the least access of the modifiers may not be correct, but it suits the special case of MemberwiseInit -// // maybe this is generally okay, since the "set" detail must be given less access than then get? either way, this needs to be made clearer -// self.accessLevelModifiers?.min() ?? inferDefaultAccessLevel(node: self._syntaxNode) -// } -//} -// -//private func inferDefaultAccessLevel(node: Syntax?) -> AccessLevelModifier { -// guard let node else { return .internal } -// guard let decl = node.asProtocol(DeclGroupSyntax.self) else { -// return inferDefaultAccessLevel(node: node.parent) -// } -// -// return [decl.declAccessLevel, .internal].min()! -//} -// -//// NB: This extension is sugar to avoid user needing to first cast to a specific kind of decl group syntax -//extension DeclGroupSyntax { -// var declAccessLevel: AccessLevelModifier { -// (self as? DeclGroupAccessLevelSyntax)!.accessLevel -// } -//} -// -///// Removes attributes from a syntax tree while maintaining their surrounding trivia. -//public class AttributeRemover: SyntaxRewriter { -// let predicate: (AttributeSyntax) -> Bool -// -// var triviaToAttachToNextToken: Trivia = Trivia() -// -// /// Initializes an attribute remover with a given predicate to determine which attributes to remove. -// /// -// /// - Parameter predicate: A closure that determines whether a given `AttributeSyntax` should be removed. -// /// If this closure returns `true` for an attribute, that attribute will be removed. -// public init(removingWhere predicate: @escaping (AttributeSyntax) -> Bool) { -// self.predicate = predicate -// } -// -// public override func visit(_ nodeList: AttributeListSyntax) -> AttributeListSyntax { -// var filteredAttributes: [AttributeListSyntax.Element] = [] -// -// for node in nodeList { -// switch node { -// case .attribute(let attribute): -// guard self.predicate(attribute) else { -// filteredAttributes.append(.attribute(prependAndClearAccumulatedTrivia(to: attribute))) -// continue -// } -// -// var leadingTrivia = attribute.leadingTrivia -// -// // Don't leave behind an empty line when the attribute being removed is on its own line, -// // based on the following conditions: -// // - Leading trivia ends with a newline followed by arbitrary number of spaces or tabs -// // - All leading trivia pieces after the last newline are just whitespace, ensuring -// // there are no comments or other non-whitespace characters on the same line -// // preceding the attribute. -// // - There is no trailing trivia and the next token has leading trivia. -// if let lastNewline = leadingTrivia.pieces.lastIndex(where: \.isNewline), -// leadingTrivia.pieces[lastNewline...].allSatisfy(\.isWhitespace), -// attribute.trailingTrivia.isEmpty, -// let nextToken = attribute.nextToken(viewMode: .sourceAccurate), -// !nextToken.leadingTrivia.isEmpty -// { -// leadingTrivia = Trivia(pieces: leadingTrivia.pieces[.. TokenSyntax { -// return prependAndClearAccumulatedTrivia(to: token) -// } -// -// /// Prepends the accumulated trivia to the given node's leading trivia. -// /// -// /// To preserve correct formatting after attribute removal, this function reassigns -// /// significant trivia accumulated from removed attributes to the provided subsequent node. -// /// Once attached, the accumulated trivia is cleared. -// /// -// /// - Parameter node: The syntax node receiving the accumulated trivia. -// /// - Returns: The modified syntax node with the prepended trivia. -// private func prependAndClearAccumulatedTrivia(to syntaxNode: T) -> T { -// defer { self.triviaToAttachToNextToken = Trivia() } -// return syntaxNode.with( -// \.leadingTrivia, self.triviaToAttachToNextToken + syntaxNode.leadingTrivia) -// } -//} -// -//extension Trivia { -// fileprivate func trimmingPrefix( -// while predicate: (TriviaPiece) -> Bool -// ) -> Trivia { -// Trivia(pieces: self.drop(while: predicate)) -// } -// -// fileprivate func trimmingSuffix( -// while predicate: (TriviaPiece) -> Bool -// ) -> Trivia { -// Trivia( -// pieces: self[...] -// .reversed() -// .drop(while: predicate) -// .reversed() -// ) -// } -// -// fileprivate var startsWithNewline: Bool { -// self.first?.isNewline ?? false -// } -//} -// -//extension VariableDeclSyntax { -// var customConfigurationAttributes: [AttributeSyntax] { -// self.attributes -// .compactMap { $0.as(AttributeSyntax.self) } -// .filter { -// ["Init", "InitWrapper", "InitRaw"].contains($0.attributeName.trimmedDescription) -// } -// } -// -// var customConfigurationAttribute: AttributeSyntax? { -// self.customConfigurationAttributes.first -// } -// -// var hasNonConfigurationAttributes: Bool { -// self.attributes.filter { attribute in -// switch attribute { -// case .attribute: -// true -// case .ifConfigDecl: -// false -// } -// }.count != self.customConfigurationAttributes.count -// } -// -// var hasCustomConfigurationAttribute: Bool { -// !self.customConfigurationAttributes.isEmpty -// } -// -// var customConfigurationArguments: LabeledExprListSyntax? { -// self.customConfigurationAttribute? -// .arguments? -// .as(LabeledExprListSyntax.self) -// } -// -// func hasSoleArgument(_ label: String) -> Bool { -// guard let arguments = self.customConfigurationArguments else { return false } -// return arguments.count == 1 && arguments.first?.label?.text == label -// } -// -// func includesArgument(_ label: String) -> Bool { -// guard let arguments = self.customConfigurationArguments else { return false } -// return arguments.first(where: { $0.label?.text == label }) != nil -// } -//} -// -//extension LabeledExprListSyntax { -// func firstWhereLabel(_ label: String) -> Element? { -// first(where: { $0.label?.text == label }) -// } -//} -// -//extension AttributeSyntax { -// var isInitWrapper: Bool { -// self.attributeName.as(IdentifierTypeSyntax.self)?.name.text == "InitWrapper" -// } -//} -// -//func deprecationDiagnostics( -// node: AttributeSyntax, -// declaration decl: some DeclGroupSyntax -//) -> [Diagnostic] { -// return diagnoseDotEscaping(decl) -//} -// -//private func diagnoseDotEscaping(_ decl: D) -> [Diagnostic] { -// guard let decl = decl.as(StructDeclSyntax.self) else { return [] } -// -// return decl.memberBlock.members.compactMap { element -> Diagnostic? in -// guard -// let configuration = element.decl -// .as(VariableDeclSyntax.self)? -// .customConfigurationArguments -// else { return nil } -// -// let dotEscapingIndex = configuration.firstIndex( -// where: { -// $0.expression -// .as(MemberAccessExprSyntax.self)? -// .declName.baseName.text == "escaping" -// } -// ) -// guard let dotEscapingIndex else { return nil } -// -// let newIndex = -// configuration.firstIndex(where: { $0.label?.text == "label" }) -// .map { configuration.index(before: $0) } -// ?? configuration.endIndex -// -// let newEscaping = LabeledExprSyntax( -// label: .identifier("escaping"), -// colon: .colonToken(trailingTrivia: .space), -// expression: BooleanLiteralExprSyntax(booleanLiteral: true), -// trailingComma: newIndex != configuration.endIndex ? .commaToken(trailingTrivia: .space) : nil -// ) -// -// var newConfiguration = configuration -// newConfiguration.remove(at: dotEscapingIndex) -// newConfiguration.insert(newEscaping, at: newIndex) -// -// return Diagnostic( -// node: configuration, -// message: MacroExpansionWarningMessage( -// """ -// @Init(.escaping) is deprecated -// """ -// ), -// fixIt: FixIt( -// message: MacroExpansionFixItMessage( -// "Replace '@Init(.escaping)' with '@Init(escaping: true)'" -// ), -// changes: [ -// FixIt.Change.replace( -// oldNode: Syntax(configuration), -// newNode: Syntax(newConfiguration) -// ) -// ] -// ) -// ) -// } -//} -// -//// MARK: - Diagnose VariableDeclSyntax -// -//func diagnoseMultipleConfigurations(variable: VariableDeclSyntax) -> [Diagnostic]? { -// guard variable.customConfigurationAttributes.count > 1 else { return nil } -// -// return variable.customConfigurationAttributes.dropFirst().map { attribute in -// Diagnostic( -// node: attribute, -// message: MacroExpansionErrorMessage( -// "Multiple @Init configurations are not supported by @MemberwiseInit" -// ) -// ) -// } -//} -// -//func diagnoseVariableDecl( -// customSettings: VariableCustomSettings?, -// variable: VariableDeclSyntax, -// targetAccessLevel: AccessLevelModifier -//) -> [Diagnostic] { -// let customSettingsDiagnostics = -// customSettings.map { settings in -// if let diagnostic = diagnoseInitOnInitializedLet(customSettings: settings, variable: variable) -// { -// return [diagnostic] -// } -// -// if let diagnostic = diagnoseMemberModifiers(customSettings: settings, variable: variable) { -// return [diagnostic] -// } -// -// return [ -// diagnoseVariableLabel(customSettings: settings, variable: variable), -// diagnoseDefaultValueAppliedToMultipleBindings( -// customSettings: settings, -// variable: variable -// ) -// ?? diagnoseDefaultValueAppliedToInitialized(customSettings: settings, variable: variable), -// ].compactMap { $0 } -// } ?? [Diagnostic]() -// -// let accessibilityDiagnostics = [ -// diagnoseAccessibilityLeak( -// customSettings: customSettings, -// variable: variable, -// targetAccessLevel: targetAccessLevel -// ) -// ].compactMap { $0 } -// -// return customSettingsDiagnostics + accessibilityDiagnostics -//} -// -//private func diagnoseInitOnInitializedLet( -// customSettings: VariableCustomSettings, -// variable: VariableDeclSyntax -//) -> Diagnostic? { -// guard -// variable.isLet, -// variable.isFullyInitialized -// else { return nil } -// -// let fixIts = [ -// variable.fixItRemoveCustomInit, -// variable.fixItRemoveInitializer, -// ].compactMap { $0 } -// -// var diagnosticMessage: DiagnosticMessage { -// let attributeName = customSettings.customAttributeName -// -// let message = "@\(attributeName) can't be applied to already initialized constant" -// -// // @InitWrapper and @InitRaw can be errors instead of warnings since they haven't seen release. -// if attributeName != "Init" { -// return MacroExpansionErrorMessage(message) -// } -// // @Init(default:) hasn't seen release, so any misuses that include "default" can be an error. -// if variable.includesArgument("default") { -// return MacroExpansionErrorMessage(message) -// } -// -// // TODO: For 1.0, @Init can also be an error -// // Conservatively, make @Init be a warning to tolerate uses relying on @Init being silently ignored. -// return MacroExpansionWarningMessage(message) -// } -// -// return customSettings.diagnosticOnDefault(diagnosticMessage, fixIts: fixIts) -//} -// -//private func diagnoseMemberModifiers( -// customSettings: VariableCustomSettings, -// variable: VariableDeclSyntax -//) -> Diagnostic? { -// let attributeName = customSettings.customAttributeName -// -// if let modifier = variable.firstModifierWhere(keyword: .static) { -// return Diagnostic( -// node: modifier, -// message: MacroExpansionWarningMessage( -// "@\(attributeName) can't be applied to 'static' members"), -// fixIts: [variable.fixItRemoveCustomInit].compactMap { $0 } -// ) -// } -// -// if let modifier = variable.firstModifierWhere(keyword: .lazy) { -// return Diagnostic( -// node: modifier, -// message: MacroExpansionWarningMessage("@\(attributeName) can't be applied to 'lazy' members"), -// fixIts: [variable.fixItRemoveCustomInit].compactMap { $0 } -// ) -// } -// -// return nil -//} -// -//private func diagnoseVariableLabel( -// customSettings: VariableCustomSettings, -// variable: VariableDeclSyntax -//) -> Diagnostic? { -// if let label = customSettings.label, -// label != "_", -// variable.bindings.count > 1 -// { -// return customSettings.diagnosticOnLabel( -// MacroExpansionErrorMessage("Custom 'label' can't be applied to multiple bindings") -// ) -// } -// -// if customSettings.label?.isInvalidSwiftLabel ?? false { -// return customSettings.diagnosticOnLabelValue(MacroExpansionErrorMessage("Invalid label value")) -// } -// -// return nil -//} -// -//private func diagnoseDefaultValueAppliedToMultipleBindings( -// customSettings: VariableCustomSettings, -// variable: VariableDeclSyntax -//) -> Diagnostic? { -// guard -// let defaultValue = customSettings.defaultValue, -// variable.bindings.count > 1 -// else { return nil } -// -// let fixIts = [ -// determineRemoveDefaultFixIt(variable: variable, defaultValue: defaultValue), -// determineRemoveCustomInitFixIt(variable: variable), -// ].compactMap { $0 } -// -// return customSettings.diagnosticOnDefault( -// MacroExpansionErrorMessage("Custom 'default' can't be applied to multiple bindings"), -// fixIts: fixIts -// ) -//} -// -//private func diagnoseDefaultValueAppliedToInitialized( -// customSettings: VariableCustomSettings, -// variable: VariableDeclSyntax -//) -> Diagnostic? { -// guard -// let defaultValue = customSettings.defaultValue, -// variable.isFullyInitialized -// else { return nil } -// -// let fixIts = [ -// determineRemoveDefaultFixIt(variable: variable, defaultValue: defaultValue), -// determineRemoveCustomInitFixIt(variable: variable), -// variable.fixItRemoveInitializer, -// ].compactMap { $0 } -// -// return customSettings.diagnosticOnDefault( -// MacroExpansionErrorMessage("Custom 'default' can't be applied to already initialized variable"), -// fixIts: fixIts -// ) -//} -// -//private func determineRemoveDefaultFixIt( -// variable: VariableDeclSyntax, -// defaultValue: String -//) -> FixIt? { -// let shouldRemoveDefault = -// variable.isVar -// && (!variable.hasSoleArgument("default") || variable.hasNonConfigurationAttributes) -// || variable.bindings.count > 1 && !variable.hasSoleArgument("default") -// -// return shouldRemoveDefault ? variable.fixItRemoveDefault(defaultValue: defaultValue) : nil -//} -// -//private func determineRemoveCustomInitFixIt( -// variable: VariableDeclSyntax -//) -> FixIt? { -// let shouldRemoveCustomInit = -// !variable.hasNonConfigurationAttributes && variable.hasSoleArgument("default") -// -// return shouldRemoveCustomInit ? variable.fixItRemoveCustomInit : nil -//} -// -//private func diagnoseAccessibilityLeak( -// customSettings: VariableCustomSettings?, -// variable: VariableDeclSyntax, -// targetAccessLevel: AccessLevelModifier -//) -> Diagnostic? { -// let effectiveAccessLevel = customSettings?.accessLevel ?? variable.accessLevel -// -// guard -// targetAccessLevel > effectiveAccessLevel, -// !variable.isFullyInitializedLet -// else { return nil } -// -// let customAccess = variable.customConfigurationArguments? -// .first? -// .expression -// .as(MemberAccessExprSyntax.self) -// -// let targetNode = -// customAccess?._syntaxNode -// ?? (variable.modifiers.isEmpty ? variable._syntaxNode : variable.modifiers._syntaxNode) -// -// var fixWithCustomInitAccess: FixIt? { -// var customAttribute = -// variable.customConfigurationAttribute ?? AttributeSyntax(stringLiteral: "@Init()") -// -// var newArguments = -// customAttribute.arguments? -// .as(LabeledExprListSyntax.self) ?? LabeledExprListSyntax() -// -// let argumentExpr = LabeledExprSyntax( -// label: nil, -// expression: MemberAccessExprSyntax(name: TokenSyntax(stringLiteral: "\(targetAccessLevel)")) -// ) -// if customAccess != nil { -// newArguments = [argumentExpr] + newArguments.dropFirst() -// } else { -// newArguments = [argumentExpr] + newArguments -// } -// customAttribute.arguments = .argumentList(newArguments) -// -// let leadingTrivia = variable.leadingTrivia -// customAttribute.leadingTrivia = leadingTrivia -// -// var newVariable = variable -// newVariable.leadingTrivia = .space -// newVariable.attributes = [.attribute(customAttribute)] -// -// return FixIt( -// message: MacroExpansionFixItMessage("Add '\(customAttribute.trimmedDescription)'"), -// changes: [ -// FixIt.Change.replace( -// oldNode: Syntax(variable), newNode: Syntax(newVariable) -// ) -// ] -// ) -// } -// -// var fixWithAccessModifier: FixIt? { -// var newVariable = variable -// -// newVariable.leadingTrivia = Trivia() -// newVariable.accessLevelModifiers = [targetAccessLevel] -// -// var modifier = newVariable.modifiers.first! -// // modifier.leadingTrivia = variable.leadingTrivia // TODO: This can include comments that we don't want to move -// if let lastNewline = variable.leadingTrivia.pieces.lastIndex(where: \.isNewline), -// variable.leadingTrivia.pieces[lastNewline...].allSatisfy(\.isWhitespace) -// { -// modifier.leadingTrivia = Trivia(pieces: variable.leadingTrivia.pieces[lastNewline...]) -// } -// -// modifier.trailingTrivia = .space -// newVariable.modifiers = [modifier] -// -// // `let value = 0` — Add 'public' access level -// // `private let value = 0` — Replace 'private' access with 'public' -// // `public private(set) var value = 0` (var not let!) -// // TODO: @MemberwiseInit(.internal) but `public private(set) var value = 0` should yield `public internal(set) var value = 0` -// -// let message = -// if !variable.modifiers.isEmpty { -// "Replace '\(variable.modifiers.trimmedDescription)' access with '\(targetAccessLevel)'" -// } else { -// "Add '\(targetAccessLevel)' access level" -// } -// -// return FixIt( -// message: MacroExpansionFixItMessage(message), -// changes: [ -// FixIt.Change.replace( -// oldNode: Syntax(variable), newNode: Syntax(newVariable) -// ) -// ] -// ) -// } -// -// var fixWithCustomInitIgnore: FixIt? { -// var customAttribute = -// variable.customConfigurationAttribute ?? AttributeSyntax(stringLiteral: "@Init()") -// -// var newArguments = -// customAttribute.arguments? -// .as(LabeledExprListSyntax.self) ?? LabeledExprListSyntax() -// -// let argumentExpr = LabeledExprSyntax( -// label: nil, -// expression: MemberAccessExprSyntax(name: TokenSyntax(stringLiteral: "ignore")) -// ) -// newArguments = [argumentExpr] -// customAttribute.arguments = .argumentList(newArguments) -// -// let leadingTrivia = variable.leadingTrivia -// customAttribute.leadingTrivia = leadingTrivia -// -// var newVariable = variable -// newVariable.leadingTrivia = .space -// newVariable.attributes = [.attribute(customAttribute)] -// -// // TODO: `private var x, y: Int` -// let message = -// if variable.isFullyInitialized { -// "Add '\(customAttribute.trimmedDescription)'" -// } else { -// "Add '\(customAttribute.trimmedDescription)' and an initializer" -// } -// -// // TODO: it would be more correct to "carry" the type annotation backward -// newVariable.bindings = PatternBindingListSyntax( -// newVariable.bindings.map { patternBinding in -// guard patternBinding.initializer == nil else { return patternBinding } -// -// var newBinding = patternBinding -// newBinding.initializer = InitializerClauseSyntax( -// equal: .equalToken(leadingTrivia: .space, trailingTrivia: .space), -// value: EditorPlaceholderExprSyntax( -// placeholder: TokenSyntax(stringLiteral: "\u{3C}#value#\u{3E}") -// ) -// ) -// -// // I think it's okay to not add a type annotation. It will only be missing on multiple bindings, -// // and there are many valid ways to initialize a memeber without a type annotation, especially -// // while being ignored by MemberwiseInit (literals and full inference from the type system). -// // newBinding.typeAnnotation = patternBinding.typeAnnotation -// // ?? TypeAnnotationSyntax( -// // colon: .colonToken(trailingTrivia: .space), -// // type: MissingTypeSyntax(placeholder: TokenSyntax(stringLiteral: "\u{3C}#Type#\u{3E}")) -// // .as(TypeSyntax.self)! -// // ) -// return newBinding -// } -// ) -// -// return FixIt( -// message: MacroExpansionFixItMessage(message), -// changes: [ -// FixIt.Change.replace( -// oldNode: Syntax(variable), newNode: Syntax(newVariable) -// ) -// ] -// ) -// } -// -// // TODO: fix to add/replace `@Init(.[targetAccessLevel])` -// // TODO: fix to add/change access level modifier to match targetAccessLevel -// // TODO: fix to add `@Init(.ignore)` and `= ` (if not already assigned) -// let fixIts: [FixIt] = [ -// fixWithCustomInitAccess, -// fixWithAccessModifier, -// fixWithCustomInitIgnore, -// ].compactMap { $0 } -// -// return Diagnostic( -// node: targetNode, -// message: MacroExpansionErrorMessage( -// """ -// @MemberwiseInit(.\(targetAccessLevel)) would leak access to '\(effectiveAccessLevel)' property -// """ -// ), -// fixIts: fixIts -// ) -//} -// -//// MARK: - Diagnose [PropertyBinding] and [MemberProperty] -// -//func customInitLabelDiagnosticsFor(bindings: [PropertyBinding]) -> [Diagnostic] { -// var diagnostics: [Diagnostic] = [] -// -// let customLabeledBindings = bindings.filter { -// $0.variable.customSettings?.label != nil -// } -// -// // Diagnose custom label conflicts with another custom label -// var seenCustomLabels: Set = [] -// for binding in customLabeledBindings { -// guard -// let customSettings = binding.variable.customSettings, -// let label = customSettings.label, -// label != "_" -// else { continue } -// defer { seenCustomLabels.insert(label) } -// if seenCustomLabels.contains(label) { -// diagnostics.append( -// customSettings.diagnosticOnLabelValue( -// MacroExpansionErrorMessage("Label '\(label)' conflicts with another label") -// ) -// ) -// } -// } -// -// return diagnostics -//} -// -//func customInitLabelDiagnosticsFor(properties: [MemberProperty]) -> [Diagnostic] { -// var diagnostics: [Diagnostic] = [] -// -// let propertiesByName = Dictionary(grouping: properties, by: { $0.name }) -// -// // Diagnose custom label conflicts with a property -// for property in properties { -// guard -// let propertyCustomSettings = property.customSettings, -// let label = propertyCustomSettings.label, -// let duplicates = propertiesByName[label], -// duplicates.contains(where: { $0 != property }) -// else { continue } -// -// diagnostics.append( -// propertyCustomSettings.diagnosticOnLabelValue( -// MacroExpansionErrorMessage("Label '\(label)' conflicts with a property name") -// ) -// ) -// } -// -// return diagnostics -//} -// -//// MARK: Fix-its -// -//extension VariableDeclSyntax { -// func fixItRemoveDefault(defaultValue: String) -> FixIt? { -// guard -// let customAttribute = self.customConfigurationAttribute, -// let arguments = self.customConfigurationArguments -// else { return nil } -// -// var newAttribute = customAttribute -// let newArguments = arguments.filter { $0.label?.text != "default" } -// newAttribute.arguments = newArguments.as(AttributeSyntax.Arguments.self) -// if newArguments.count == 0 { -// newAttribute.leftParen = nil -// newAttribute.rightParen = nil -// } -// -// return FixIt( -// message: MacroExpansionFixItMessage("Remove 'default: \(defaultValue)'"), -// changes: [ -// FixIt.Change.replace( -// oldNode: Syntax(customAttribute), -// newNode: Syntax(newAttribute)) -// ] -// ) -// } -// -// var fixItRemoveCustomInit: FixIt? { -// guard let customAttribute = self.customConfigurationAttribute else { return nil } -// -// let newVariable = AttributeRemover( -// removingWhere: { -// ["Init", "InitWrapper", "InitRaw"].contains($0.attributeName.trimmedDescription) -// } -// ).rewrite(self) -// -// return FixIt( -// message: MacroExpansionFixItMessage("Remove '\(customAttribute.trimmedDescription)'"), -// changes: [ -// FixIt.Change.replace( -// oldNode: Syntax(self), newNode: Syntax(newVariable) -// ) -// ] -// ) -// } -// -// var fixItRemoveInitializer: FixIt? { -// guard -// self.bindings.count == 1, -// let firstBinding = self.bindings.first, -// let firstBindingInitializer = firstBinding.initializer -// else { return nil } -// -// var newFirstBinding = firstBinding.with(\.initializer, nil) -// -// if firstBinding.typeAnnotation == nil { -// let inferredTypeSyntax = firstBindingInitializer.value.inferredTypeSyntax -// -// newFirstBinding.typeAnnotation = TypeAnnotationSyntax( -// colon: .colonToken(trailingTrivia: .space), -// type: inferredTypeSyntax -// ?? MissingTypeSyntax(placeholder: TokenSyntax(stringLiteral: "\u{3C}#Type#\u{3E}")) -// .as(TypeSyntax.self)! -// ) -// newFirstBinding.pattern = newFirstBinding.pattern.trimmed -// } -// -// var newNode = self.detached -// newNode.bindings = .init(arrayLiteral: newFirstBinding) -// -// return FixIt( -// message: MacroExpansionFixItMessage( -// "Remove '\(firstBindingInitializer.trimmedDescription)'" -// ), -// changes: [ -// FixIt.Change.replace( -// oldNode: Syntax(self), newNode: Syntax(newNode) -// ) -// ] -// ) -// } -//} -// -//// Potential future enhancements: -//// - .ternaryExpr having "then" and "else" expressions as inferrable types -//// - Consider: .isExpr, switchExpr, .tryExpr, .closureExpr -// -//extension ExprSyntax { -// var inferredTypeSyntax: TypeSyntax? { -// self.inferredType?.typeSyntax -// } -//} -// -//private indirect enum ExprInferrableType: Equatable, CustomStringConvertible { -// case array(ExprInferrableType) -// case arrayTypeInitializer(elementType: String) -// case `as`(type: String) -// case bool -// case closedRange(ExprInferrableType) -// case dictionary(key: ExprInferrableType, value: ExprInferrableType) -// case dictionaryTypeInitializer(keyType: String, valueType: String) -// case double -// case int -// case range(ExprInferrableType) -// case string -// case tuple([ExprInferrableType]) -// -// var description: String { -// switch self { -// case .array(let elementType): -// return "[\(elementType.description)]" -// -// case .arrayTypeInitializer(let elementType): -// return "[\(elementType)]" -// -// case .as(let type): -// return type -// -// case .bool: -// return "Bool" -// -// case .closedRange(let containedType): -// return "ClosedRange<\(containedType.description)>" -// -// case .dictionary(let keyType, let valueType): -// // NB: swift-format prefers `[Key: Value]`, but Xcode uses `[Key : Value]`. -// return "[\(keyType.description): \(valueType.description)]" -// -// case .dictionaryTypeInitializer(let keyType, let valueType): -// return "[\(keyType): \(valueType)]" -// -// case .double: -// return "Double" -// -// case .int: -// return "Int" -// -// case .range(let containedType): -// return "Range<\(containedType.description)>" -// -// case .string: -// return "String" -// -// case .tuple(let elementTypes): -// let typeDescriptions = elementTypes.map(\.description).joined(separator: ", ") -// return "(\(typeDescriptions))" -// } -// } -// -// var unwrapSingleElementTuple: ExprInferrableType? { -// guard -// case let .tuple(elementTypes) = self, -// elementTypes.count == 1 -// else { return nil } -// return elementTypes.first -// } -// -// var typeSyntax: TypeSyntax { -// TypeSyntax(stringLiteral: self.description) -// } -//} -// -//enum InfixOperator { -// enum ArithmeticOperator: String { -// case addition = "+" -// case subtraction = "-" -// case multiplication = "*" -// case division = "/" -// case modulo = "%" -// } -// -// enum BitwiseOperator: String { -// case bitwiseAnd = "&" -// case bitwiseOr = "|" -// case bitwiseXor = "^" -// case bitwiseShiftLeft = "<<" -// case bitwiseShiftRight = ">>" -// } -// -// enum LogicalOperator: String { -// case equality = "==" -// case inequality = "!=" -// case lessThan = "<" -// case greaterThan = ">" -// case lessThanOrEqual = "<=" -// case greaterThanOrEqual = ">=" -// case logicalAnd = "&&" -// case logicalOr = "||" -// } -// -// enum RangeOperator: String { -// case closedRange = "..." -// case halfOpenRange = "..<" -// } -// -// case arithmetic(ArithmeticOperator) -// case bitwise(BitwiseOperator) -// case logical(LogicalOperator) -// case range(RangeOperator) -// -// init?(rawValue: String) { -// let type: Self? = -// if let arithmeticOp = ArithmeticOperator(rawValue: rawValue) { -// .arithmetic(arithmeticOp) -// } else if let bitwiseOp = BitwiseOperator(rawValue: rawValue) { -// .bitwise(bitwiseOp) -// } else if let logicalOp = LogicalOperator(rawValue: rawValue) { -// .logical(logicalOp) -// } else if let rangeOp = RangeOperator(rawValue: rawValue) { -// .range(rangeOp) -// } else { -// nil -// } -// guard let type else { return nil } -// self = type -// } -//} -// -//extension ExprSyntax { -// private var inferredType: ExprInferrableType? { -// switch self.kind { -// case .arrayExpr: -// guard let arrayExpr = self.as(ArrayExprSyntax.self) else { return nil } -// -// let elementTypes = arrayExpr.elements.compactMap { $0.expression.inferredType } -// guard -// elementTypes.count == arrayExpr.elements.count, -// let firstType = elementTypes.first, -// let inferredArrayType = elementTypes.dropFirst().reduce(firstType, { commonType($0, $1) }) -// else { return nil } -// return .array(inferredArrayType) -// -// case .asExpr: -// guard let asExpr = self.as(AsExprSyntax.self) else { return nil } -// return .as(type: asExpr.type.trimmedDescription) -// -// case .booleanLiteralExpr: -// return .bool -// -// case .dictionaryExpr: -// guard let dictionaryExpr = self.as(DictionaryExprSyntax.self) else { return nil } -// -// let keyValuePairs = -// dictionaryExpr.content -// .as(DictionaryElementListSyntax.self)? -// .compactMap { ($0.key.inferredType, $0.value.inferredType) } -// ?? [] -// -// guard !keyValuePairs.isEmpty else { return nil } -// -// let initialKeyTypes = keyValuePairs.map(\.0) -// let initialValueTypes = keyValuePairs.map(\.1) -// -// guard -// let firstKeyType = initialKeyTypes.first, -// let firstValueType = initialValueTypes.first, -// let inferredKeyType = initialKeyTypes.dropFirst().reduce( -// firstKeyType, { commonType($0, $1) }), -// let inferredValueType = initialValueTypes.dropFirst().reduce( -// firstValueType, { commonType($0, $1) }) -// else { return nil } -// -// return .dictionary(key: inferredKeyType, value: inferredValueType) -// -// case .floatLiteralExpr: -// return .double -// -// case .functionCallExpr: -// guard let functionCallExpr = self.as(FunctionCallExprSyntax.self) else { return nil } -// -// // NB: `[Type]()` -// if let arrayExpr = functionCallExpr.calledExpression.as(ArrayExprSyntax.self) { -// let typeString = arrayExpr.elements -// .first? -// .expression -// .as(DeclReferenceExprSyntax.self)? -// .baseName -// .trimmedDescription -// guard let typeString else { return nil } -// return .arrayTypeInitializer(elementType: typeString) -// } -// -// // NB: `[KeyType : ValueType]()` -// if let dictionaryExpr = functionCallExpr.calledExpression.as(DictionaryExprSyntax.self) { -// guard let type = dictionaryExpr.content.as(DictionaryElementListSyntax.self)?.first -// else { return nil } -// -// return .dictionaryTypeInitializer( -// keyType: type.key.trimmedDescription, -// valueType: type.value.trimmedDescription -// ) -// } -// -// return nil -// -// case .infixOperatorExpr: -// guard -// let infixOperatorExpr = self.as(InfixOperatorExprSyntax.self), -// let lhsType = infixOperatorExpr.leftOperand.as(ExprSyntax.self)?.inferredType, -// let rhsType = infixOperatorExpr.rightOperand.as(ExprSyntax.self)?.inferredType, -// let operation = InfixOperator(rawValue: infixOperatorExpr.operator.trimmedDescription), -// let inferredType = resultTypeOfInfixOperation( -// lhs: lhsType, -// rhs: rhsType, -// operation: operation -// ) -// else { return nil } -// return inferredType -// -// case .integerLiteralExpr: -// return .int -// -// case .prefixOperatorExpr: -// guard -// let prefixOperatorExpr = self.as(PrefixOperatorExprSyntax.self) -// else { return nil } -// return prefixOperatorExpr.expression.inferredType -// -// case .sequenceExpr: -// // NB: SwiftSyntax 509.0.2 represents `1 + 2 + 3` as a tree of InfixOperatorExprSyntax -// // values, but Swift 5.9.0 represents it as SequenceExprSyntax. -// guard -// let sequenceExpr = self.as(SequenceExprSyntax.self), -// let foldedExpr = try? OperatorTable.standardOperators.foldSingle(sequenceExpr) -// else { return nil } -// return foldedExpr.inferredType -// -// case .stringLiteralExpr, .simpleStringLiteralExpr, .simpleStringLiteralSegmentList, -// .stringLiteralSegmentList: -// return .string -// -// case .tupleExpr: -// guard let tupleExpr = self.as(TupleExprSyntax.self) else { return nil } -// let elementTypes = tupleExpr.elements.compactMap { $0.expression.inferredType } -// guard elementTypes.count == tupleExpr.elements.count -// else { return nil } -// return .tuple(elementTypes) -// -// case .token, .accessorBlock, .accessorDeclList, .accessorDecl, .accessorEffectSpecifiers, -// .accessorParameters, .actorDecl, .arrayElementList, .arrayElement, .arrayType, .arrowExpr, -// .assignmentExpr, .associatedTypeDecl, .attributeList, .attribute, .attributedType, -// .availabilityArgumentList, .availabilityArgument, .availabilityCondition, -// .availabilityLabeledArgument, .awaitExpr, .backDeployedAttributeArguments, -// .binaryOperatorExpr, .borrowExpr, .breakStmt, .canImportExpr, .canImportVersionInfo, -// .catchClauseList, .catchClause, .catchItemList, .catchItem, .classDecl, .classRestrictionType, -// .closureCaptureClause, .closureCaptureList, .closureCaptureSpecifier, .closureCapture, -// .closureExpr, .closureParameterClause, .closureParameterList, .closureParameter, -// .closureShorthandParameterList, .closureShorthandParameter, .closureSignature, -// .codeBlockItemList, .codeBlockItem, .codeBlock, .compositionTypeElementList, -// .compositionTypeElement, .compositionType, .conditionElementList, .conditionElement, -// .conformanceRequirement, .consumeExpr, .continueStmt, .conventionAttributeArguments, -// .conventionWitnessMethodAttributeArguments, .copyExpr, .declModifierDetail, .declModifierList, -// .declModifier, .declNameArgumentList, .declNameArgument, .declNameArguments, -// .declReferenceExpr, .deferStmt, .deinitializerDecl, .deinitializerEffectSpecifiers, -// .derivativeAttributeArguments, .designatedTypeList, .designatedType, .dictionaryElementList, -// .dictionaryElement, .dictionaryType, .differentiabilityArgumentList, -// .differentiabilityArgument, .differentiabilityArguments, -// .differentiabilityWithRespectToArgument, .differentiableAttributeArguments, -// .discardAssignmentExpr, .discardStmt, .doStmt, .documentationAttributeArgumentList, -// .documentationAttributeArgument, .dynamicReplacementAttributeArguments, -// .editorPlaceholderDecl, .editorPlaceholderExpr, .effectsAttributeArgumentList, .enumCaseDecl, -// .enumCaseElementList, .enumCaseElement, .enumCaseParameterClause, .enumCaseParameterList, -// .enumCaseParameter, .enumDecl, .exposeAttributeArguments, .exprList, .expressionPattern, -// .expressionSegment, .expressionStmt, .extensionDecl, .fallThroughStmt, .forStmt, -// .forceUnwrapExpr, .functionDecl, .functionEffectSpecifiers, .functionParameterClause, -// .functionParameterList, .functionParameter, .functionSignature, .functionType, -// .genericArgumentClause, .genericArgumentList, .genericArgument, .genericParameterClause, -// .genericParameterList, .genericParameter, .genericRequirementList, .genericRequirement, -// .genericSpecializationExpr, .genericWhereClause, .guardStmt, .identifierPattern, -// .identifierType, .ifConfigClauseList, .ifConfigClause, .ifConfigDecl, .ifExpr, -// .implementsAttributeArguments, .implicitlyUnwrappedOptionalType, .importDecl, -// .importPathComponentList, .importPathComponent, .inOutExpr, .inheritanceClause, -// .inheritedTypeList, .inheritedType, .initializerClause, .initializerDecl, .isExpr, -// .isTypePattern, .keyPathComponentList, .keyPathComponent, .keyPathExpr, -// .keyPathOptionalComponent, .keyPathPropertyComponent, .keyPathSubscriptComponent, -// .labeledExprList, .labeledExpr, .labeledSpecializeArgument, .labeledStmt, .layoutRequirement, -// .macroDecl, .macroExpansionDecl, .macroExpansionExpr, .matchingPatternCondition, -// .memberAccessExpr, .memberBlockItemList, .memberBlockItem, .memberBlock, .memberType, -// .metatypeType, .missingDecl, .missingExpr, .missingPattern, .missingStmt, .missing, -// .missingType, .multipleTrailingClosureElementList, .multipleTrailingClosureElement, -// .namedOpaqueReturnType, .nilLiteralExpr, .objCSelectorPieceList, .objCSelectorPiece, -// .opaqueReturnTypeOfAttributeArguments, .operatorDecl, .operatorPrecedenceAndTypes, -// .optionalBindingCondition, .optionalChainingExpr, .optionalType, -// .originallyDefinedInAttributeArguments, .packElementExpr, .packElementType, -// .packExpansionExpr, .packExpansionType, .patternBindingList, .patternBinding, .patternExpr, -// .platformVersionItemList, .platformVersionItem, .platformVersion, .postfixIfConfigExpr, -// .postfixOperatorExpr, .poundSourceLocationArguments, .poundSourceLocation, -// .precedenceGroupAssignment, .precedenceGroupAssociativity, .precedenceGroupAttributeList, -// .precedenceGroupDecl, .precedenceGroupNameList, .precedenceGroupName, -// .precedenceGroupRelation, .primaryAssociatedTypeClause, .primaryAssociatedTypeList, -// .primaryAssociatedType, .protocolDecl, .regexLiteralExpr, .repeatStmt, .returnClause, -// .returnStmt, .sameTypeRequirement, .someOrAnyType, .sourceFile, -// .specializeAttributeArgumentList, .specializeAvailabilityArgument, -// .specializeTargetFunctionArgument, .stringSegment, .structDecl, .subscriptCallExpr, -// .subscriptDecl, .superExpr, .suppressedType, .switchCaseItemList, .switchCaseItem, -// .switchCaseLabel, .switchCaseList, .switchCase, .switchDefaultLabel, .switchExpr, -// .ternaryExpr, .throwStmt, .tryExpr, .tuplePatternElementList, .tuplePatternElement, -// .tuplePattern, .tupleTypeElementList, .tupleTypeElement, .tupleType, .typeAliasDecl, -// .typeAnnotation, .typeEffectSpecifiers, .typeExpr, .typeInitializerClause, -// .unavailableFromAsyncAttributeArguments, .underscorePrivateAttributeArguments, -// .unexpectedNodes, .unresolvedAsExpr, .unresolvedIsExpr, .unresolvedTernaryExpr, -// .valueBindingPattern, .variableDecl, .versionComponentList, .versionComponent, .versionTuple, -// .whereClause, .whileStmt, .wildcardPattern, .yieldStmt, .yieldedExpressionList, -// .yieldedExpression, .yieldedExpressionsClause: -// return nil -// } -// } -//} -// -//private func commonType( -// _ first: ExprInferrableType?, -// _ second: ExprInferrableType? -//) -> ExprInferrableType? { -// guard let firstType = first, let secondType = second else { return nil } -// -// switch (firstType, secondType) { -// case (.as(let firstElementType), .as(let secondElementType)): -// return firstElementType == secondElementType ? firstType : nil -// -// case (.int, .double), (.double, .int): -// return .double -// -// case (.int, .int): -// return .int -// -// case (.double, .double): -// return .double -// -// case (.string, .string): -// return .string -// -// case (.bool, .bool): -// return .bool -// -// case (.array(let firstElementType), .array(let secondElementType)): -// if let commonElementType = commonType(firstElementType, secondElementType) { -// return .array(commonElementType) -// } -// -// case ( -// .dictionary(let firstKeyType, let firstValueType), -// .dictionary(let secondKeyType, let secondValueType) -// ): -// if let commonKeyType = commonType(firstKeyType, secondKeyType), -// let commonValueType = commonType(firstValueType, secondValueType) -// { -// return .dictionary(key: commonKeyType, value: commonValueType) -// } -// -// case (.closedRange(let firstContainedType), .closedRange(let secondContainedType)): -// if let commonContainedType = commonType(firstContainedType, secondContainedType) { -// return .closedRange(commonContainedType) -// } -// -// case (.range(let firstContainedType), .range(let secondContainedType)): -// if let commonContainedType = commonType(firstContainedType, secondContainedType) { -// return .range(commonContainedType) -// } -// -// default: -// return nil -// } -// -// return nil -//} -// -//private func resultTypeOfInfixOperation( -// lhs: ExprInferrableType, -// rhs: ExprInferrableType, -// operation: InfixOperator -//) -> ExprInferrableType? { -// let lhsType = lhs.unwrapSingleElementTuple ?? lhs -// let rhsType = rhs.unwrapSingleElementTuple ?? rhs -// -// switch operation { -// case .logical(_): -// return .bool -// -// case .arithmetic(let op): -// switch op { -// case .addition, .subtraction, .multiplication, .division: -// return commonType(lhsType, rhsType) -// -// case .modulo: -// return (lhsType, rhsType) == (.int, .int) ? .int : nil -// } -// -// case .range(let op): -// guard let type = commonType(lhsType, rhsType) else { return nil } -// return switch op { -// case .closedRange: -// ExprInferrableType.closedRange(type) -// -// case .halfOpenRange: -// .range(type) -// } -// -// case .bitwise(_): -// guard (lhsType, rhsType) == (.int, .int) else { return nil } -// return .int -// } -//} -// -//struct VariableCustomSettings: Equatable { -// enum Assignee: Equatable { -// case wrapper -// case raw(String) -// } -// -// let accessLevel: AccessLevelModifier? -// let assignee: Assignee? -// let defaultValue: String? -// let forceEscaping: Bool -// let ignore: Bool -// let label: String? -// let type: TypeSyntax? -// let _syntaxNode: AttributeSyntax -// -// var customAttributeName: String { -// self._syntaxNode.attributeName.trimmedDescription -// } -// -// func diagnosticOnDefault(_ message: DiagnosticMessage, fixIts: [FixIt] = []) -> Diagnostic { -// let labelNode = self._syntaxNode -// .arguments? -// .as(LabeledExprListSyntax.self)? -// .firstWhereLabel("default") -// -// return diagnostic(node: labelNode ?? self._syntaxNode, message: message, fixIts: fixIts) -// } -// -// func diagnosticOnLabel(_ message: DiagnosticMessage, fixIts: [FixIt] = []) -> Diagnostic { -// let labelNode = self._syntaxNode -// .arguments? -// .as(LabeledExprListSyntax.self)? -// .firstWhereLabel("label") -// -// return diagnostic(node: labelNode ?? self._syntaxNode, message: message, fixIts: fixIts) -// } -// -// func diagnosticOnLabelValue(_ message: DiagnosticMessage) -> Diagnostic { -// let labelValueNode = self._syntaxNode -// .arguments? -// .as(LabeledExprListSyntax.self)? -// .firstWhereLabel("label")? -// .expression -// -// return diagnostic(node: labelValueNode ?? self._syntaxNode, message: message) -// } -// -// private func diagnostic( -// node: any SyntaxProtocol, -// message: DiagnosticMessage, -// fixIts: [FixIt] = [] -// ) -> Diagnostic { -// Diagnostic(node: node, message: message, fixIts: fixIts) -// } -//} -// -//struct PropertyBinding { -// let typeFromTrailingBinding: TypeSyntax? -// let syntax: PatternBindingSyntax -// let variable: MemberVariable -// -// var effectiveType: TypeSyntax? { -// variable.customSettings?.type -// ?? self.syntax.typeAnnotation?.type -// ?? self.syntax.initializer?.value.inferredTypeSyntax -// ?? self.typeFromTrailingBinding -// } -// -// var initializerValue: ExprSyntax? { -// self.syntax.initializer?.trimmed.value -// } -// -// var isTuplePattern: Bool { -// self.syntax.pattern.isTuplePattern -// } -// -// var name: String? { -// self.syntax.pattern.as(IdentifierPatternSyntax.self)?.identifier.text -// } -// -// var isInitializedVarWithoutType: Bool { -// self.initializerValue != nil -// && self.variable.keywordToken == .keyword(.var) -// && self.effectiveType == nil -// && self.initializerValue?.inferredTypeSyntax == nil -// } -// -// var isInitializedLet: Bool { -// self.initializerValue != nil && self.variable.keywordToken == .keyword(.let) -// } -// -// func diagnostic(_ message: DiagnosticMessage) -> Diagnostic { -// Diagnostic(node: self.syntax._syntaxNode, message: message) -// } -//} -// -//struct MemberVariable { -// let customSettings: VariableCustomSettings? -// let syntax: VariableDeclSyntax -// -// var accessLevel: AccessLevelModifier { -// self.syntax.accessLevel -// } -// -// var bindings: PatternBindingListSyntax { -// self.syntax.bindings -// } -// -// var keywordToken: TokenKind { -// self.syntax.bindingSpecifier.tokenKind -// } -//} -// -//struct MemberProperty: Equatable { -// let accessLevel: AccessLevelModifier -// let customSettings: VariableCustomSettings? -// let initializerValue: ExprSyntax? -// let keywordToken: TokenKind -// let name: String -// let type: TypeSyntax -// -// func initParameterLabel( -// considering allProperties: [MemberProperty], -// deunderscoreParameters: Bool -// ) -> String { -// guard -// let customSettings = self.customSettings, -// customSettings.label -// != self.initParameterName( -// considering: allProperties, -// deunderscoreParameters: deunderscoreParameters -// ) -// else { return "" } -// -// return customSettings.label.map { "\($0) " } ?? "" -// } -// -// func initParameterName( -// considering allProperties: [MemberProperty], -// deunderscoreParameters: Bool -// ) -> String { -// guard -// self.customSettings?.label == nil, -// deunderscoreParameters -// else { return self.name } -// -// let potentialName = self.name.hasPrefix("_") ? String(name.dropFirst()) : self.name -// return allProperties.contains(where: { $0.name == potentialName }) ? self.name : potentialName -// } -//} -// -//extension String { -// var isValidSwiftLabel: Bool { -// let pattern = #"^[_a-zA-Z][_a-zA-Z0-9]*$"# -// let regex = try! NSRegularExpression(pattern: pattern) -// let range = NSRange(self.startIndex.. Bool { -// return !self.modifiers.containsAny(of: keywords.map { TokenSyntax.keyword($0) }) -// } -// -// func firstModifierWhere(keyword: Keyword) -> DeclModifierSyntax? { -// let keywordText = TokenSyntax.keyword(keyword).text -// return self.modifiers.first { modifier in -// modifier.name.text == keywordText -// } -// } -//} -// -//extension DeclModifierListSyntax { -// fileprivate func containsAny(of tokens: [TokenSyntax]) -> Bool { -// return self.contains { modifier in -// tokens.contains { $0.text == modifier.name.text } -// } -// } -//} -// -//extension PatternBindingSyntax { -// var isComputedProperty: Bool { -// guard let accessors = self.accessorBlock?.accessors else { return false } -// -// switch accessors { -// case .accessors(let accessors): -// let tokenKinds = accessors.compactMap { $0.accessorSpecifier.tokenKind } -// let propertyObservers: [TokenKind] = [.keyword(.didSet), .keyword(.willSet)] -// -// return !tokenKinds.allSatisfy(propertyObservers.contains) -// -// case .getter(_): -// return true -// } -// } -//} -// -//extension TypeSyntax { -// var isFunctionType: Bool { -// // NB: Check for `FunctionTypeSyntax` directly or when wrapped within `AttributedTypeSyntax`, -// // e.g., `@Sendable () -> Void`. -// return self.is(FunctionTypeSyntax.self) -// || (self.as(AttributedTypeSyntax.self)?.baseType.is(FunctionTypeSyntax.self) ?? false) -// } -//} -// -//extension TypeSyntax { -// var isOptionalType: Bool { -// self.as(OptionalTypeSyntax.self) != nil -// } -//} -// -//extension PatternSyntax { -// var isTuplePattern: Bool { -// self.as(TuplePatternSyntax.self) != nil -// } -//} -// -//extension VariableDeclSyntax { -// var isComputedProperty: Bool { -// guard -// self.bindings.count == 1, -// let binding = self.bindings.first?.as(PatternBindingSyntax.self) -// else { return false } -// -// return self.bindingSpecifier.tokenKind == .keyword(.var) && binding.isComputedProperty -// } -// -// var isFullyInitialized: Bool { -// self.bindings.allSatisfy { $0.initializer != nil } -// } -// -// var isFullyInitializedLet: Bool { -// self.isLet && self.isFullyInitialized -// } -// -// var isLet: Bool { -// self.bindingSpecifier.tokenKind == .keyword(.let) -// } -// -// var isVar: Bool { -// self.bindingSpecifier.tokenKind == .keyword(.var) -// } -//} -// -//extension ExprSyntax { -// var trimmedStringLiteral: String? { -// self.as(StringLiteralExprSyntax.self)? -// .segments -// .trimmedDescription -// .trimmingCharacters(in: .whitespacesAndNewlines) -// } -//} -// -////extension DeclGroupSyntax { -//// func descriptiveDeclKind(withArticle article: Bool = false) -> String { -//// switch self { -//// case is ActorDeclSyntax: -//// return article ? "an actor" : "actor" -//// case is ClassDeclSyntax: -//// return article ? "a class" : "class" -//// case is ExtensionDeclSyntax: -//// return article ? "an extension" : "extension" -//// case is ProtocolDeclSyntax: -//// return article ? "a protocol" : "protocol" -//// case is StructDeclSyntax: -//// return article ? "a struct" : "struct" -//// case is EnumDeclSyntax: -//// return article ? "an enum" : "enum" -//// default: -//// return "`\(self.kind)`" -//// } -//// } -////} diff --git a/Tests/MacroTestingTests/MacroExamples/MetaEnumMacro.swift b/Tests/MacroTestingTests/MacroExamples/MetaEnumMacro.swift deleted file mode 100644 index 22ac266..0000000 --- a/Tests/MacroTestingTests/MacroExamples/MetaEnumMacro.swift +++ /dev/null @@ -1,158 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftDiagnostics -import SwiftSyntax -import SwiftSyntaxBuilder -import SwiftSyntaxMacros - -public struct MetaEnumMacro { - let parentTypeName: TokenSyntax - let childCases: [EnumCaseElementSyntax] - let access: DeclModifierListSyntax.Element? - let parentParamName: TokenSyntax - - init( - node: AttributeSyntax, declaration: some DeclGroupSyntax, context: some MacroExpansionContext - ) throws { - guard let enumDecl = declaration.as(EnumDeclSyntax.self) else { - throw DiagnosticsError(diagnostics: [ - CaseMacroDiagnostic.notAnEnum(declaration).diagnose(at: Syntax(node)) - ]) - } - - parentTypeName = enumDecl.name.with(\.trailingTrivia, []) - - access = enumDecl.modifiers.first(where: \.isNeededAccessLevelModifier) - - childCases = enumDecl.caseElements.map { parentCase in - parentCase.with(\.parameterClause, nil) - } - - parentParamName = context.makeUniqueName("parent") - } - - func makeMetaEnum() -> DeclSyntax { - // FIXME: Why does this need to be a string to make trailing trivia work properly? - let caseDecls = - childCases - .map { childCase in - " case \(childCase.name)" - } - .joined(separator: "\n") - - return """ - \(access)enum Meta { - \(raw: caseDecls) - \(makeMetaInit()) - } - """ - } - - func makeMetaInit() -> DeclSyntax { - // FIXME: Why does this need to be a string to make trailing trivia work properly? - let caseStatements = - childCases - .map { childCase in - """ - case .\(childCase.name): - self = .\(childCase.name) - """ - } - .joined(separator: "\n") - - return """ - \(access)init(_ \(parentParamName): \(parentTypeName)) { - switch \(parentParamName) { - \(raw: caseStatements) - } - } - """ - } -} - -extension MetaEnumMacro: MemberMacro { - public static func expansion( - of node: AttributeSyntax, - providingMembersOf declaration: some DeclGroupSyntax, - in context: some MacroExpansionContext - ) throws -> [DeclSyntax] { - let macro = try MetaEnumMacro(node: node, declaration: declaration, context: context) - - return [macro.makeMetaEnum()] - } -} - -extension EnumDeclSyntax { - var caseElements: [EnumCaseElementSyntax] { - memberBlock.members.flatMap { member in - guard let caseDecl = member.decl.as(EnumCaseDeclSyntax.self) else { - return [EnumCaseElementSyntax]() - } - - return Array(caseDecl.elements) - } - } -} - -enum CaseMacroDiagnostic { - case notAnEnum(DeclGroupSyntax) -} - -extension CaseMacroDiagnostic: DiagnosticMessage { - var message: String { - switch self { - case .notAnEnum(let decl): - return - "'@MetaEnum' can only be attached to an enum, not \(decl.descriptiveDeclKind(withArticle: true))" - } - } - - var diagnosticID: MessageID { - switch self { - case .notAnEnum: - return MessageID(domain: "MetaEnumDiagnostic", id: "notAnEnum") - } - } - - var severity: DiagnosticSeverity { - switch self { - case .notAnEnum: - return .error - } - } - - func diagnose(at node: Syntax) -> Diagnostic { - Diagnostic(node: node, message: self) - } -} - -extension DeclGroupSyntax { - func descriptiveDeclKind(withArticle article: Bool = false) -> String { - switch self { - case is ActorDeclSyntax: - return article ? "an actor" : "actor" - case is ClassDeclSyntax: - return article ? "a class" : "class" - case is ExtensionDeclSyntax: - return article ? "an extension" : "extension" - case is ProtocolDeclSyntax: - return article ? "a protocol" : "protocol" - case is StructDeclSyntax: - return article ? "a struct" : "struct" - case is EnumDeclSyntax: - return article ? "an enum" : "enum" - default: - fatalError("Unknown DeclGroupSyntax") - } - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/NewTypeMacro.swift b/Tests/MacroTestingTests/MacroExamples/NewTypeMacro.swift deleted file mode 100644 index 41e8cdd..0000000 --- a/Tests/MacroTestingTests/MacroExamples/NewTypeMacro.swift +++ /dev/null @@ -1,71 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftSyntax -import SwiftSyntaxBuilder -import SwiftSyntaxMacros - -public enum NewTypeMacro {} - -extension NewTypeMacro: MemberMacro { - public static func expansion( - of node: AttributeSyntax, - providingMembersOf declaration: Declaration, - in context: Context - ) throws -> [DeclSyntax] where Declaration: DeclGroupSyntax, Context: MacroExpansionContext { - do { - guard - case .argumentList(let arguments) = node.arguments, - arguments.count == 1, - let memberAccessExn = arguments.first? - .expression.as(MemberAccessExprSyntax.self), - let rawType = memberAccessExn.base?.as(DeclReferenceExprSyntax.self) - else { - throw CustomError.message( - #"@NewType requires the raw type as an argument, in the form "RawType.self"."#) - } - - guard let declaration = declaration.as(StructDeclSyntax.self) else { - throw CustomError.message("@NewType can only be applied to a struct declarations.") - } - - let access = declaration.modifiers.first(where: \.isNeededAccessLevelModifier) - - return [ - "\(access)typealias RawValue = \(rawType)", - "\(access)var rawValue: RawValue", - "\(access)init(_ rawValue: RawValue) { self.rawValue = rawValue }", - ] - } catch { - print("--------------- throwing \(error)") - throw error - } - } -} - -extension DeclModifierSyntax { - var isNeededAccessLevelModifier: Bool { - switch self.name.tokenKind { - case .keyword(.public): return true - default: return false - } - } -} - -extension SyntaxStringInterpolation { - // It would be nice for SwiftSyntaxBuilder to provide this out-of-the-box. - mutating func appendInterpolation(_ node: Node?) { - if let node { - appendInterpolation(node) - } - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/ObservableMacro.swift b/Tests/MacroTestingTests/MacroExamples/ObservableMacro.swift deleted file mode 100644 index bbaac5f..0000000 --- a/Tests/MacroTestingTests/MacroExamples/ObservableMacro.swift +++ /dev/null @@ -1,173 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftSyntax -import SwiftSyntaxMacros - -extension DeclSyntaxProtocol { - fileprivate var isObservableStoredProperty: Bool { - if let property = self.as(VariableDeclSyntax.self), - let binding = property.bindings.first, - let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier, - identifier.text != "_registrar", identifier.text != "_storage", - binding.accessorBlock == nil - { - return true - } - - return false - } -} - -public struct ObservableMacro: MemberMacro, MemberAttributeMacro { - - // MARK: - MemberMacro - - public static func expansion( - of node: AttributeSyntax, - providingMembersOf declaration: some DeclGroupSyntax, - in context: some MacroExpansionContext - ) throws -> [DeclSyntax] { - guard let identified = declaration.asProtocol(NamedDeclSyntax.self) else { - return [] - } - - let parentName = identified.name - - let registrar: DeclSyntax = - """ - let _registrar = ObservationRegistrar<\(parentName)>() - """ - - let addObserver: DeclSyntax = - """ - public nonisolated func addObserver(_ observer: some Observer<\(parentName)>) { - _registrar.addObserver(observer) - } - """ - - let removeObserver: DeclSyntax = - """ - public nonisolated func removeObserver(_ observer: some Observer<\(parentName)>) { - _registrar.removeObserver(observer) - } - """ - - let withTransaction: DeclSyntax = - """ - private func withTransaction(_ apply: () throws -> T) rethrows -> T { - _registrar.beginAccess() - defer { _registrar.endAccess() } - return try apply() - } - """ - - let memberList = declaration.memberBlock.members.filter { - $0.decl.isObservableStoredProperty - } - - let storageStruct: DeclSyntax = - """ - private struct Storage { - \(memberList) - } - """ - - let storage: DeclSyntax = - """ - private var _storage = Storage() - """ - - return [ - registrar, - addObserver, - removeObserver, - withTransaction, - storageStruct, - storage, - ] - } - - // MARK: - MemberAttributeMacro - - public static func expansion( - of node: AttributeSyntax, - attachedTo declaration: some DeclGroupSyntax, - providingAttributesFor member: some DeclSyntaxProtocol, - in context: some MacroExpansionContext - ) throws -> [SwiftSyntax.AttributeSyntax] { - guard member.isObservableStoredProperty else { - return [] - } - - return [ - AttributeSyntax( - attributeName: IdentifierTypeSyntax( - name: .identifier("ObservableProperty") - ) - ) - ] - } - -} - -extension ObservableMacro: ExtensionMacro { - public static func expansion( - of node: AttributeSyntax, - attachedTo declaration: some DeclGroupSyntax, - providingExtensionsOf type: some TypeSyntaxProtocol, - conformingTo protocols: [TypeSyntax], - in context: some MacroExpansionContext - ) throws -> [ExtensionDeclSyntax] { - [try ExtensionDeclSyntax("extension \(type): Observable {}")] - } -} - -public struct ObservablePropertyMacro: AccessorMacro { - public static func expansion( - of node: AttributeSyntax, - providingAccessorsOf declaration: some DeclSyntaxProtocol, - in context: some MacroExpansionContext - ) throws -> [AccessorDeclSyntax] { - guard let property = declaration.as(VariableDeclSyntax.self), - let binding = property.bindings.first, - let identifier = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier, - binding.accessorBlock == nil - else { - return [] - } - - let getAccessor: AccessorDeclSyntax = - """ - get { - _registrar.beginAccess(\\.\(identifier)) - defer { _registrar.endAccess() } - return _storage.\(identifier) - } - """ - - let setAccessor: AccessorDeclSyntax = - """ - set { - _registrar.beginAccess(\\.\(identifier)) - _registrar.register(observable: self, willSet: \\.\(identifier), to: newValue) - defer { - _registrar.register(observable: self, didSet: \\.\(identifier)) - _registrar.endAccess() - } - _storage.\(identifier) = newValue - } - """ - - return [getAccessor, setAccessor] - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/OptionSetMacro.swift b/Tests/MacroTestingTests/MacroExamples/OptionSetMacro.swift deleted file mode 100644 index 49c7b3e..0000000 --- a/Tests/MacroTestingTests/MacroExamples/OptionSetMacro.swift +++ /dev/null @@ -1,207 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftDiagnostics -import SwiftSyntax -import SwiftSyntaxBuilder -import SwiftSyntaxMacros - -enum OptionSetMacroDiagnostic { - case requiresStruct - case requiresStringLiteral(String) - case requiresOptionsEnum(String) - case requiresOptionsEnumRawType -} - -extension OptionSetMacroDiagnostic: DiagnosticMessage { - func diagnose(at node: some SyntaxProtocol) -> Diagnostic { - Diagnostic(node: Syntax(node), message: self) - } - - var message: String { - switch self { - case .requiresStruct: - return "'OptionSet' macro can only be applied to a struct" - - case .requiresStringLiteral(let name): - return "'OptionSet' macro argument \(name) must be a string literal" - - case .requiresOptionsEnum(let name): - return "'OptionSet' macro requires nested options enum '\(name)'" - - case .requiresOptionsEnumRawType: - return "'OptionSet' macro requires a raw type" - } - } - - var severity: DiagnosticSeverity { .error } - - var diagnosticID: MessageID { - MessageID(domain: "Swift", id: "OptionSet.\(self)") - } -} - -/// The label used for the OptionSet macro argument that provides the name of -/// the nested options enum. -private let optionsEnumNameArgumentLabel = "optionsName" - -/// The default name used for the nested "Options" enum. This should -/// eventually be overridable. -private let defaultOptionsEnumName = "Options" - -extension LabeledExprListSyntax { - /// Retrieve the first element with the given label. - func first(labeled name: String) -> Element? { - return first { element in - if let label = element.label, label.text == name { - return true - } - - return false - } - } -} - -public struct OptionSetMacro { - /// Decodes the arguments to the macro expansion. - /// - /// - Returns: the important arguments used by the various roles of this - /// macro inhabits, or nil if an error occurred. - static func decodeExpansion( - of attribute: AttributeSyntax, - attachedTo decl: some DeclGroupSyntax, - in context: some MacroExpansionContext - ) -> (StructDeclSyntax, EnumDeclSyntax, TypeSyntax)? { - // Determine the name of the options enum. - let optionsEnumName: String - if case let .argumentList(arguments) = attribute.arguments, - let optionEnumNameArg = arguments.first(labeled: optionsEnumNameArgumentLabel) - { - // We have a options name; make sure it is a string literal. - guard let stringLiteral = optionEnumNameArg.expression.as(StringLiteralExprSyntax.self), - stringLiteral.segments.count == 1, - case let .stringSegment(optionsEnumNameString)? = stringLiteral.segments.first - else { - context.diagnose( - OptionSetMacroDiagnostic.requiresStringLiteral(optionsEnumNameArgumentLabel).diagnose( - at: optionEnumNameArg.expression)) - return nil - } - - optionsEnumName = optionsEnumNameString.content.text - } else { - optionsEnumName = defaultOptionsEnumName - } - - // Only apply to structs. - guard let structDecl = decl.as(StructDeclSyntax.self) else { - context.diagnose(OptionSetMacroDiagnostic.requiresStruct.diagnose(at: decl)) - return nil - } - - // Find the option enum within the struct. - guard - let optionsEnum = decl.memberBlock.members.compactMap({ member in - if let enumDecl = member.decl.as(EnumDeclSyntax.self), - enumDecl.name.text == optionsEnumName - { - return enumDecl - } - - return nil - }).first - else { - context.diagnose( - OptionSetMacroDiagnostic.requiresOptionsEnum(optionsEnumName).diagnose(at: decl)) - return nil - } - - // Retrieve the raw type from the attribute. - guard - let genericArgs = attribute.attributeName.as(IdentifierTypeSyntax.self)? - .genericArgumentClause, - let rawType = genericArgs.arguments.first?.argumentCompat600 - else { - context.diagnose(OptionSetMacroDiagnostic.requiresOptionsEnumRawType.diagnose(at: attribute)) - return nil - } - - return (structDecl, optionsEnum, rawType) - } -} - -extension OptionSetMacro: ExtensionMacro { - public static func expansion( - of node: AttributeSyntax, - attachedTo declaration: some DeclGroupSyntax, - providingExtensionsOf type: some TypeSyntaxProtocol, - conformingTo protocols: [TypeSyntax], - in context: some MacroExpansionContext - ) throws -> [ExtensionDeclSyntax] { - // Decode the expansion arguments. - guard let (structDecl, _, _) = decodeExpansion(of: node, attachedTo: declaration, in: context) - else { - return [] - } - - // If there is an explicit conformance to OptionSet already, don't add one. - if let inheritedTypes = structDecl.inheritanceClause?.inheritedTypes, - inheritedTypes.contains(where: { inherited in inherited.type.trimmedDescription == "OptionSet" - }) - { - return [] - } - - return [try ExtensionDeclSyntax("extension \(type): OptionSet {}")] - } -} - -extension OptionSetMacro: MemberMacro { - public static func expansion( - of attribute: AttributeSyntax, - providingMembersOf decl: some DeclGroupSyntax, - in context: some MacroExpansionContext - ) throws -> [DeclSyntax] { - // Decode the expansion arguments. - guard - let (_, optionsEnum, rawType) = decodeExpansion(of: attribute, attachedTo: decl, in: context) - else { - return [] - } - - // Find all of the case elements. - let caseElements = optionsEnum.memberBlock.members.flatMap { member in - guard let caseDecl = member.decl.as(EnumCaseDeclSyntax.self) else { - return [EnumCaseElementSyntax]() - } - - return Array(caseDecl.elements) - } - - // Dig out the access control keyword we need. - let access = decl.modifiers.first(where: \.isNeededAccessLevelModifier) - - let staticVars = caseElements.map { (element) -> DeclSyntax in - """ - \(access) static let \(element.name): Self = - Self(rawValue: 1 << \(optionsEnum.name).\(element.name).rawValue) - """ - } - - return [ - "\(access)typealias RawValue = \(rawType)", - "\(access)var rawValue: RawValue", - "\(access)init() { self.rawValue = 0 }", - "\(access)init(rawValue: RawValue) { self.rawValue = rawValue }", - ] + staticVars - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/PeerValueWithSuffixMacro.swift b/Tests/MacroTestingTests/MacroExamples/PeerValueWithSuffixMacro.swift deleted file mode 100644 index 1e0aa91..0000000 --- a/Tests/MacroTestingTests/MacroExamples/PeerValueWithSuffixMacro.swift +++ /dev/null @@ -1,28 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftSyntax -import SwiftSyntaxMacros - -/// Peer 'var' with the name suffixed with '_peer'. -public enum PeerValueWithSuffixNameMacro: PeerMacro { - public static func expansion( - of node: AttributeSyntax, - providingPeersOf declaration: some DeclSyntaxProtocol, - in context: some MacroExpansionContext - ) throws -> [DeclSyntax] { - guard let identified = declaration.asProtocol(NamedDeclSyntax.self) else { - return [] - } - return ["var \(raw: identified.name.text)_peer: Int { 1 }"] - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/StringifyMacro.swift b/Tests/MacroTestingTests/MacroExamples/StringifyMacro.swift deleted file mode 100644 index 5d0d7c8..0000000 --- a/Tests/MacroTestingTests/MacroExamples/StringifyMacro.swift +++ /dev/null @@ -1,37 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftSyntax -import SwiftSyntaxBuilder -import SwiftSyntaxMacros - -/// Implementation of the `stringify` macro, which takes an expression -/// of any type and produces a tuple containing the value of that expression -/// and the source code that produced the value. For example -/// -/// #stringify(x + y) -/// -/// will expand to -/// -/// (x + y, "x + y") -public enum StringifyMacro: ExpressionMacro { - public static func expansion( - of node: some FreestandingMacroExpansionSyntax, - in context: some MacroExpansionContext - ) -> ExprSyntax { - guard let argument = node.arguments.first?.expression else { - fatalError("compiler bug: the macro does not have any arguments") - } - - return "(\(argument), \(literal: argument.description))" - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/SwiftSyntaxCompatibilityExtensions.swift b/Tests/MacroTestingTests/MacroExamples/SwiftSyntaxCompatibilityExtensions.swift deleted file mode 100644 index 909cba7..0000000 --- a/Tests/MacroTestingTests/MacroExamples/SwiftSyntaxCompatibilityExtensions.swift +++ /dev/null @@ -1,11 +0,0 @@ -import SwiftSyntax -import SwiftSyntaxMacros - -#if !canImport(SwiftSyntax510) && canImport(SwiftSyntax509) - extension FreestandingMacroExpansionSyntax { - var arguments: LabeledExprListSyntax { - get { self.argumentList } - set { self.argumentList = newValue } - } - } -#endif diff --git a/Tests/MacroTestingTests/MacroExamples/URLMacro.swift b/Tests/MacroTestingTests/MacroExamples/URLMacro.swift deleted file mode 100644 index c811c05..0000000 --- a/Tests/MacroTestingTests/MacroExamples/URLMacro.swift +++ /dev/null @@ -1,38 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import Foundation -import SwiftSyntax -import SwiftSyntaxMacros - -/// Creates a non-optional URL from a static string. The string is checked to -/// be valid during compile time. -public enum URLMacro: ExpressionMacro { - public static func expansion( - of node: some FreestandingMacroExpansionSyntax, - in context: some MacroExpansionContext - ) throws -> ExprSyntax { - guard let argument = node.arguments.first?.expression, - let segments = argument.as(StringLiteralExprSyntax.self)?.segments, - segments.count == 1, - case .stringSegment(let literalSegment)? = segments.first - else { - throw CustomError.message("#URL requires a static string literal") - } - - guard URL(string: literalSegment.content.text) != nil else { - throw CustomError.message("malformed url: \(argument)") - } - - return "URL(string: \(argument))!" - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/WarningMacro.swift b/Tests/MacroTestingTests/MacroExamples/WarningMacro.swift deleted file mode 100644 index 9f2163c..0000000 --- a/Tests/MacroTestingTests/MacroExamples/WarningMacro.swift +++ /dev/null @@ -1,46 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftDiagnostics -import SwiftSyntax -import SwiftSyntaxMacros - -/// Implementation of the `myWarning` macro, which mimics the behavior of the -/// built-in `#warning`. -public enum WarningMacro: ExpressionMacro { - public static func expansion( - of node: some FreestandingMacroExpansionSyntax, - in context: some MacroExpansionContext - ) throws -> ExprSyntax { - guard let firstElement = node.arguments.first, - let stringLiteral = firstElement.expression - .as(StringLiteralExprSyntax.self), - stringLiteral.segments.count == 1, - case let .stringSegment(messageString)? = stringLiteral.segments.first - else { - throw CustomError.message("#myWarning macro requires a string literal") - } - - context.diagnose( - Diagnostic( - node: Syntax(node), - message: SimpleDiagnosticMessage( - message: messageString.content.description, - diagnosticID: MessageID(domain: "test123", id: "error"), - severity: .warning - ) - ) - ) - - return "()" - } -} diff --git a/Tests/MacroTestingTests/MacroExamples/WrapStoredPropertiesMacro.swift b/Tests/MacroTestingTests/MacroExamples/WrapStoredPropertiesMacro.swift deleted file mode 100644 index a28fb21..0000000 --- a/Tests/MacroTestingTests/MacroExamples/WrapStoredPropertiesMacro.swift +++ /dev/null @@ -1,111 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import SwiftSyntax -import SwiftSyntaxBuilder -import SwiftSyntaxMacros - -/// Implementation of the `wrapStoredProperties` macro, which can be -/// used to apply an attribute to all of the stored properties of a type. -/// -/// This macro demonstrates member-attribute macros, which allow an attribute -/// written on a type or extension to apply attributes to the members -/// declared within that type or extension. -public struct WrapStoredPropertiesMacro: MemberAttributeMacro { - public static func expansion< - Declaration: DeclGroupSyntax, - Context: MacroExpansionContext - >( - of node: AttributeSyntax, - attachedTo decl: Declaration, - providingAttributesFor member: some DeclSyntaxProtocol, - in context: Context - ) throws -> [AttributeSyntax] { - guard let property = member.as(VariableDeclSyntax.self), - property.isStoredProperty - else { - return [] - } - - guard case let .argumentList(arguments) = node.arguments, - let firstElement = arguments.first, - let stringLiteral = firstElement.expression - .as(StringLiteralExprSyntax.self), - stringLiteral.segments.count == 1, - case let .stringSegment(wrapperName)? = stringLiteral.segments.first - else { - throw CustomError.message( - "macro requires a string literal containing the name of an attribute") - } - - return [ - AttributeSyntax( - leadingTrivia: [.newlines(1), .spaces(2)], - attributeName: IdentifierTypeSyntax( - name: .identifier(wrapperName.content.text) - ) - ) - ] - } -} - -extension VariableDeclSyntax { - /// Determine whether this variable has the syntax of a stored property. - /// - /// This syntactic check cannot account for semantic adjustments due to, - /// e.g., accessor macros or property wrappers. - var isStoredProperty: Bool { - if bindings.count != 1 { - return false - } - - let binding = bindings.first! - switch binding.accessorBlock?.accessors { - case .none: - return true - - case .accessors(let accessors): - for accessor in accessors { - switch accessor.accessorSpecifier.tokenKind { - case .keyword(.willSet), .keyword(.didSet): - // Observers can occur on a stored property. - break - - default: - // Other accessors make it a computed property. - return false - } - } - - return true - - case .getter: - return false - } - } -} - -extension DeclGroupSyntax { - /// Enumerate the stored properties that syntactically occur in this - /// declaration. - func storedProperties() -> [VariableDeclSyntax] { - return memberBlock.members.compactMap { member in - guard let variable = member.decl.as(VariableDeclSyntax.self), - variable.isStoredProperty - else { - return nil - } - - return variable - } - } -} diff --git a/Tests/MacroTestingTests/MacroNameTests.swift b/Tests/MacroTestingTests/MacroNameTests.swift deleted file mode 100644 index f73b760..0000000 --- a/Tests/MacroTestingTests/MacroNameTests.swift +++ /dev/null @@ -1,48 +0,0 @@ -import XCTest - -@testable import MacroTesting - -final class MacroNameTests: BaseTestCase { - func testBasics() { - XCTAssertEqual( - macroName(className: "AddAsyncHandler", isExpression: false), - "AddAsyncHandler" - ) - XCTAssertEqual( - macroName(className: "AddAsyncHandlerMacro", isExpression: false), - "AddAsyncHandler" - ) - XCTAssertEqual( - macroName(className: "URL", isExpression: false), - "URL" - ) - XCTAssertEqual( - macroName(className: "URLMacro", isExpression: false), - "URL" - ) - XCTAssertEqual( - macroName(className: "URL", isExpression: true), - "url" - ) - XCTAssertEqual( - macroName(className: "URLMacro", isExpression: true), - "url" - ) - XCTAssertEqual( - macroName(className: "URLComponents", isExpression: true), - "urlComponents" - ) - XCTAssertEqual( - macroName(className: "URLComponentsMacro", isExpression: true), - "urlComponents" - ) - XCTAssertEqual( - macroName(className: "FontLiteral", isExpression: true), - "fontLiteral" - ) - XCTAssertEqual( - macroName(className: "FontLiteralMacro", isExpression: true), - "fontLiteral" - ) - } -} diff --git a/Tests/MacroTestingTests/MemberDeprecatedMacroTests.swift b/Tests/MacroTestingTests/MemberDeprecatedMacroTests.swift deleted file mode 100644 index b99527c..0000000 --- a/Tests/MacroTestingTests/MemberDeprecatedMacroTests.swift +++ /dev/null @@ -1,42 +0,0 @@ -import MacroTesting -import XCTest - -final class MemberDepreacatedMacroTests: XCTestCase { - override func invokeTest() { - withMacroTesting(macros: ["memberDeprecated": MemberDeprecatedMacro.self]) { - super.invokeTest() - } - } - - func testExpansionMarksMembersAsDeprecated() { - assertMacro { - """ - @memberDeprecated - public struct SomeStruct { - typealias MacroName = String - - public var oldProperty: Int = 420 - - func oldMethod() { - print("This is an old method.") - } - } - """ - } expansion: { - """ - public struct SomeStruct { - @available(*, deprecated) - typealias MacroName = String - @available(*, deprecated) - - public var oldProperty: Int = 420 - @available(*, deprecated) - - func oldMethod() { - print("This is an old method.") - } - } - """ - } - } -} diff --git a/Tests/MacroTestingTests/MetaEnumMacroTests.swift b/Tests/MacroTestingTests/MetaEnumMacroTests.swift deleted file mode 100644 index f12c0d4..0000000 --- a/Tests/MacroTestingTests/MetaEnumMacroTests.swift +++ /dev/null @@ -1,109 +0,0 @@ -import MacroTesting -import XCTest - -final class MetaEnumMacroTests: BaseTestCase { - override func invokeTest() { - withMacroTesting(macros: [MetaEnumMacro.self]) { - super.invokeTest() - } - } - - func testExpansionAddsNestedMetaEnum() { - assertMacro { - """ - @MetaEnum enum Cell { - case integer(Int) - case text(String) - case boolean(Bool) - case null - } - """ - } expansion: { - """ - enum Cell { - case integer(Int) - case text(String) - case boolean(Bool) - case null - - enum Meta { - case integer - case text - case boolean - case null - init(_ __macro_local_6parentfMu_: Cell) { - switch __macro_local_6parentfMu_ { - case .integer: - self = .integer - case .text: - self = .text - case .boolean: - self = .boolean - case .null: - self = .null - } - } - } - } - """ - } - } - - func testExpansionAddsPublicNestedMetaEnum() { - assertMacro { - """ - @MetaEnum public enum Cell { - case integer(Int) - case text(String) - case boolean(Bool) - } - """ - } expansion: { - """ - public enum Cell { - case integer(Int) - case text(String) - case boolean(Bool) - - public enum Meta { - case integer - case text - case boolean - public init(_ __macro_local_6parentfMu_: Cell) { - switch __macro_local_6parentfMu_ { - case .integer: - self = .integer - case .text: - self = .text - case .boolean: - self = .boolean - } - } - } - } - """ - } - } - - func testExpansionOnStructEmitsError() { - assertMacro { - """ - @MetaEnum struct Cell { - let integer: Int - let text: String - let boolean: Bool - } - """ - } diagnostics: { - """ - @MetaEnum struct Cell { - ┬──────── - ╰─ 🛑 '@MetaEnum' can only be attached to an enum, not a struct - let integer: Int - let text: String - let boolean: Bool - } - """ - } - } -} diff --git a/Tests/MacroTestingTests/NewTypeMacroTests.swift b/Tests/MacroTestingTests/NewTypeMacroTests.swift deleted file mode 100644 index cf9c0e2..0000000 --- a/Tests/MacroTestingTests/NewTypeMacroTests.swift +++ /dev/null @@ -1,92 +0,0 @@ -import MacroTesting -import XCTest - -final class NewTypeMacroTests: BaseTestCase { - override func invokeTest() { - withMacroTesting(macros: [NewTypeMacro.self]) { - super.invokeTest() - } - } - - func testExpansionAddsStringRawType() { - assertMacro { - """ - @NewType(String.self) - struct Username { - } - """ - } expansion: { - """ - struct Username { - - typealias RawValue = String - - var rawValue: RawValue - - init(_ rawValue: RawValue) { - self.rawValue = rawValue - } - } - """ - } - } - - func testExpansionWithPublicAddsPublicStringRawType() { - assertMacro { - """ - @NewType(String.self) - public struct MyString { - } - """ - } expansion: { - """ - public struct MyString { - - public typealias RawValue = String - - public var rawValue: RawValue - - public init(_ rawValue: RawValue) { - self.rawValue = rawValue - } - } - """ - } - } - - func testExpansionOnClassEmitsError() { - assertMacro { - """ - @NewType(Int.self) - class NotAUsername { - } - """ - } diagnostics: { - """ - @NewType(Int.self) - ┬───────────────── - ╰─ 🛑 @NewType can only be applied to a struct declarations. - class NotAUsername { - } - """ - } - } - - func testExpansionWithMissingRawTypeEmitsError() { - assertMacro { - """ - @NewType - struct NoRawType { - } - """ - } diagnostics: { - """ - @NewType - ┬─────── - ╰─ 🛑 @NewType requires the raw type as an argument, in the form "RawType.self". - struct NoRawType { - } - """ - } - } -} diff --git a/Tests/MacroTestingTests/ObservableMacroTests.swift b/Tests/MacroTestingTests/ObservableMacroTests.swift deleted file mode 100644 index 8c896d1..0000000 --- a/Tests/MacroTestingTests/ObservableMacroTests.swift +++ /dev/null @@ -1,249 +0,0 @@ -import MacroTesting -import XCTest - -final class ObservableMacroTests: XCTestCase { - override func invokeTest() { - withMacroTesting( - macros: [ - "Observable": ObservableMacro.self, - "ObservableProperty": ObservablePropertyMacro.self, - ] - ) { - super.invokeTest() - } - } - - func testExpansion() { - #if !canImport(SwiftSyntax510) - assertMacro { - """ - @Observable - final class Dog { - var name: String? - var treat: Treat? - - var isHappy: Bool = true - - init() {} - - func bark() { - print("bork bork") - } - } - """ - } expansion: { - #""" - final class Dog { - var name: String? { - get { - _registrar.beginAccess(\.name) - defer { - _registrar.endAccess() - } - return _storage.name - } - set { - _registrar.beginAccess(\.name) - _registrar.register(observable: self, willSet: \.name, to: newValue) - defer { - _registrar.register(observable: self, didSet: \.name) - _registrar.endAccess() - } - _storage.name = newValue - } - } - var treat: Treat? { - get { - _registrar.beginAccess(\.treat) - defer { - _registrar.endAccess() - } - return _storage.treat - } - set { - _registrar.beginAccess(\.treat) - _registrar.register(observable: self, willSet: \.treat, to: newValue) - defer { - _registrar.register(observable: self, didSet: \.treat) - _registrar.endAccess() - } - _storage.treat = newValue - } - } - - var isHappy: Bool = true { - get { - _registrar.beginAccess(\.isHappy) - defer { - _registrar.endAccess() - } - return _storage.isHappy - } - set { - _registrar.beginAccess(\.isHappy) - _registrar.register(observable: self, willSet: \.isHappy, to: newValue) - defer { - _registrar.register(observable: self, didSet: \.isHappy) - _registrar.endAccess() - } - _storage.isHappy = newValue - } - } - - init() {} - - func bark() { - print("bork bork") - } - - let _registrar = ObservationRegistrar() - - public nonisolated func addObserver(_ observer: some Observer) { - _registrar.addObserver(observer) - } - - public nonisolated func removeObserver(_ observer: some Observer) { - _registrar.removeObserver(observer) - } - - private func withTransaction(_ apply: () throws -> T) rethrows -> T { - _registrar.beginAccess() - defer { - _registrar.endAccess() - } - return try apply() - } - - private struct Storage { - - var name: String? - var treat: Treat? - - var isHappy: Bool = true - } - - private var _storage = Storage() - } - - extension Dog: Observable { - } - """# - } - #else - assertMacro { - """ - @Observable - final class Dog { - var name: String? - var treat: Treat? - - var isHappy: Bool = true - - init() {} - - func bark() { - print("bork bork") - } - } - """ - } expansion: { - #""" - final class Dog { - var name: String? { - get { - _registrar.beginAccess(\.name) - defer { - _registrar.endAccess() - } - return _storage.name - } - set { - _registrar.beginAccess(\.name) - _registrar.register(observable: self, willSet: \.name, to: newValue) - defer { - _registrar.register(observable: self, didSet: \.name) - _registrar.endAccess() - } - _storage.name = newValue - } - } - var treat: Treat? { - get { - _registrar.beginAccess(\.treat) - defer { - _registrar.endAccess() - } - return _storage.treat - } - set { - _registrar.beginAccess(\.treat) - _registrar.register(observable: self, willSet: \.treat, to: newValue) - defer { - _registrar.register(observable: self, didSet: \.treat) - _registrar.endAccess() - } - _storage.treat = newValue - } - } - - var isHappy: Bool { - get { - _registrar.beginAccess(\.isHappy) - defer { - _registrar.endAccess() - } - return _storage.isHappy - } - set { - _registrar.beginAccess(\.isHappy) - _registrar.register(observable: self, willSet: \.isHappy, to: newValue) - defer { - _registrar.register(observable: self, didSet: \.isHappy) - _registrar.endAccess() - } - _storage.isHappy = newValue - } - } - - init() {} - - func bark() { - print("bork bork") - } - - let _registrar = ObservationRegistrar() - - public nonisolated func addObserver(_ observer: some Observer) { - _registrar.addObserver(observer) - } - - public nonisolated func removeObserver(_ observer: some Observer) { - _registrar.removeObserver(observer) - } - - private func withTransaction(_ apply: () throws -> T) rethrows -> T { - _registrar.beginAccess() - defer { - _registrar.endAccess() - } - return try apply() - } - - private struct Storage { - - var name: String? - var treat: Treat? - - var isHappy: Bool = true - } - - private var _storage = Storage() - } - - extension Dog: Observable { - } - """# - } - #endif - } -} diff --git a/Tests/MacroTestingTests/OptionSetMacroTests.swift b/Tests/MacroTestingTests/OptionSetMacroTests.swift deleted file mode 100644 index 5947f66..0000000 --- a/Tests/MacroTestingTests/OptionSetMacroTests.swift +++ /dev/null @@ -1,279 +0,0 @@ -import MacroTesting -import XCTest - -final class OptionSetMacroTests: BaseTestCase { - override func invokeTest() { - withMacroTesting(macros: ["MyOptionSet": OptionSetMacro.self]) { - super.invokeTest() - } - } - - func testExpansionOnStructWithNestedEnumAndStatics() { - #if canImport(SwiftSyntax600) - assertMacro { - """ - @MyOptionSet - struct ShippingOptions { - private enum Options: Int { - case nextDay - case secondDay - case priority - case standard - } - - static let express: ShippingOptions = [.nextDay, .secondDay] - static let all: ShippingOptions = [.express, .priority, .standard] - } - """ - } expansion: { - """ - struct ShippingOptions { - private enum Options: Int { - case nextDay - case secondDay - case priority - case standard - } - - static let express: ShippingOptions = [.nextDay, .secondDay] - static let all: ShippingOptions = [.express, .priority, .standard] - - typealias RawValue = UInt8 - - var rawValue: RawValue - - init() { - self.rawValue = 0 - } - - init(rawValue: RawValue) { - self.rawValue = rawValue - } - - static let nextDay: Self = - Self(rawValue: 1 << Options.nextDay.rawValue) - - static let secondDay: Self = - Self(rawValue: 1 << Options.secondDay.rawValue) - - static let priority: Self = - Self(rawValue: 1 << Options.priority.rawValue) - - static let standard: Self = - Self(rawValue: 1 << Options.standard.rawValue) - } - - extension ShippingOptions: OptionSet { - } - """ - } - #else - assertMacro { - """ - @MyOptionSet - struct ShippingOptions { - private enum Options: Int { - case nextDay - case secondDay - case priority - case standard - } - - static let express: ShippingOptions = [.nextDay, .secondDay] - static let all: ShippingOptions = [.express, .priority, .standard] - } - """ - } expansion: { - """ - struct ShippingOptions { - private enum Options: Int { - case nextDay - case secondDay - case priority - case standard - } - - static let express: ShippingOptions = [.nextDay, .secondDay] - static let all: ShippingOptions = [.express, .priority, .standard] - - typealias RawValue = UInt8 - - var rawValue: RawValue - - init() { - self.rawValue = 0 - } - - init(rawValue: RawValue) { - self.rawValue = rawValue - } - - static let nextDay: Self = - Self (rawValue: 1 << Options.nextDay.rawValue) - - static let secondDay: Self = - Self (rawValue: 1 << Options.secondDay.rawValue) - - static let priority: Self = - Self (rawValue: 1 << Options.priority.rawValue) - - static let standard: Self = - Self (rawValue: 1 << Options.standard.rawValue) - } - - extension ShippingOptions: OptionSet { - } - """ - } - #endif - } - - func testExpansionOnPublicStructWithExplicitOptionSetConformance() { - #if canImport(SwiftSyntax600) - assertMacro { - """ - @MyOptionSet - public struct ShippingOptions: OptionSet { - private enum Options: Int { - case nextDay - case standard - } - } - """ - } expansion: { - """ - public struct ShippingOptions: OptionSet { - private enum Options: Int { - case nextDay - case standard - } - - public typealias RawValue = UInt8 - - public var rawValue: RawValue - - public init() { - self.rawValue = 0 - } - - public init(rawValue: RawValue) { - self.rawValue = rawValue - } - - public static let nextDay: Self = - Self(rawValue: 1 << Options.nextDay.rawValue) - - public static let standard: Self = - Self(rawValue: 1 << Options.standard.rawValue) - } - """ - } - #else - assertMacro { - """ - @MyOptionSet - public struct ShippingOptions: OptionSet { - private enum Options: Int { - case nextDay - case standard - } - } - """ - } expansion: { - """ - public struct ShippingOptions: OptionSet { - private enum Options: Int { - case nextDay - case standard - } - - public typealias RawValue = UInt8 - - public var rawValue: RawValue - - public init() { - self.rawValue = 0 - } - - public init(rawValue: RawValue) { - self.rawValue = rawValue - } - - public static let nextDay: Self = - Self (rawValue: 1 << Options.nextDay.rawValue) - - public static let standard: Self = - Self (rawValue: 1 << Options.standard.rawValue) - } - """ - } - #endif - } - - func testExpansionFailsOnEnumType() { - assertMacro { - """ - @MyOptionSet - enum Animal { - case dog - } - """ - } diagnostics: { - """ - @MyOptionSet - ├─ 🛑 'OptionSet' macro can only be applied to a struct - ╰─ 🛑 'OptionSet' macro can only be applied to a struct - enum Animal { - case dog - } - """ - } - } - - func testExpansionFailsWithoutNestedOptionsEnum() { - assertMacro { - """ - @MyOptionSet - struct ShippingOptions { - static let express: ShippingOptions = [.nextDay, .secondDay] - static let all: ShippingOptions = [.express, .priority, .standard] - } - """ - } diagnostics: { - """ - @MyOptionSet - ├─ 🛑 'OptionSet' macro requires nested options enum 'Options' - ╰─ 🛑 'OptionSet' macro requires nested options enum 'Options' - struct ShippingOptions { - static let express: ShippingOptions = [.nextDay, .secondDay] - static let all: ShippingOptions = [.express, .priority, .standard] - } - """ - } - } - - func testExpansionFailsWithoutSpecifiedRawType() { - assertMacro { - """ - @MyOptionSet - struct ShippingOptions { - private enum Options: Int { - case nextDay - } - } - """ - } diagnostics: { - """ - @MyOptionSet - ┬─────────── - ├─ 🛑 'OptionSet' macro requires a raw type - ╰─ 🛑 'OptionSet' macro requires a raw type - struct ShippingOptions { - private enum Options: Int { - case nextDay - } - } - """ - } - } -} diff --git a/Tests/MacroTestingTests/PeerValueWithSuffixMacroTests.swift b/Tests/MacroTestingTests/PeerValueWithSuffixMacroTests.swift deleted file mode 100644 index e244b60..0000000 --- a/Tests/MacroTestingTests/PeerValueWithSuffixMacroTests.swift +++ /dev/null @@ -1,57 +0,0 @@ -import MacroTesting -import XCTest - -final class PeerValueWithSuffixNameMacroTests: XCTestCase { - override func invokeTest() { - withMacroTesting(macros: [PeerValueWithSuffixNameMacro.self]) { - super.invokeTest() - } - } - - func testExpansionAddsPeerValueToPrivateActor() { - assertMacro { - """ - @PeerValueWithSuffixName - private actor Counter { - var value = 0 - } - """ - } expansion: { - """ - private actor Counter { - var value = 0 - } - - var Counter_peer: Int { - 1 - } - """ - } - } - - func testExpansionAddsPeerValueToFunction() { - assertMacro { - """ - @PeerValueWithSuffixName - func someFunction() {} - """ - } expansion: { - """ - func someFunction() {} - - var someFunction_peer: Int { - 1 - } - """ - } - } - - func testExpansionIgnoresVariables() { - assertMacro { - """ - @PeerValueWithSuffixName - var someVariable: Int - """ - } - } -} diff --git a/Tests/MacroTestingTests/StringifyMacroTests.swift b/Tests/MacroTestingTests/StringifyMacroTests.swift deleted file mode 100644 index 834890c..0000000 --- a/Tests/MacroTestingTests/StringifyMacroTests.swift +++ /dev/null @@ -1,34 +0,0 @@ -import MacroTesting -import XCTest - -final class StringifyMacroTests: BaseTestCase { - override func invokeTest() { - withMacroTesting(macros: [StringifyMacro.self]) { - super.invokeTest() - } - } - - func testExpansionWithBasicArithmeticExpression() { - assertMacro { - """ - let a = #stringify(x + y) - """ - } expansion: { - """ - let a = (x + y, "x + y") - """ - } - } - - func testExpansionWithStringInterpolation() { - assertMacro { - #""" - let b = #stringify("Hello, \(name)") - """# - } expansion: { - #""" - let b = ("Hello, \(name)", #""Hello, \(name)""#) - """# - } - } -} diff --git a/Tests/MacroTestingTests/SwiftSyntaxCompatibilityHelpers.swift b/Tests/MacroTestingTests/SwiftSyntaxCompatibilityHelpers.swift deleted file mode 100644 index 6a2ae82..0000000 --- a/Tests/MacroTestingTests/SwiftSyntaxCompatibilityHelpers.swift +++ /dev/null @@ -1,11 +0,0 @@ -import SwiftSyntax - -extension GenericArgumentSyntax { - var argumentCompat600: TypeSyntax? { - #if canImport(SwiftSyntax601) - argument.as(TypeSyntax.self) - #else - argument - #endif - } -} diff --git a/Tests/MacroTestingTests/SwiftTestingTests.swift b/Tests/MacroTestingTests/SwiftTestingTests.swift deleted file mode 100644 index df857db..0000000 --- a/Tests/MacroTestingTests/SwiftTestingTests.swift +++ /dev/null @@ -1,55 +0,0 @@ -#if canImport(Testing) - @_spi(Experimental) import MacroTesting - import Testing - - @Suite( - .macros( - //record: .failed, - macros: ["URL": URLMacro.self] - ) - ) - struct URLMacroSwiftTestingTests { - @Test - func expansionWithMalformedURLEmitsError() { - assertMacro { - """ - let invalid = #URL("https://not a url.com") - """ - } diagnostics: { - """ - let invalid = #URL("https://not a url.com") - ┬──────────────────────────── - ╰─ 🛑 malformed url: "https://not a url.com" - """ - } - } - - @Test - func expansionWithStringInterpolationEmitsError() { - assertMacro { - #""" - #URL("https://\(domain)/api/path") - """# - } diagnostics: { - #""" - #URL("https://\(domain)/api/path") - ┬───────────────────────────────── - ╰─ 🛑 #URL requires a static string literal - """# - } - } - - @Test - func expansionWithValidURL() { - assertMacro { - """ - let valid = #URL("https://swift.org/") - """ - } expansion: { - """ - let valid = URL(string: "https://swift.org/")! - """ - } - } - } -#endif diff --git a/Tests/MacroTestingTests/URLMacroTests.swift b/Tests/MacroTestingTests/URLMacroTests.swift deleted file mode 100644 index 5a1bcf0..0000000 --- a/Tests/MacroTestingTests/URLMacroTests.swift +++ /dev/null @@ -1,50 +0,0 @@ -import MacroTesting -import XCTest - -final class URLMacroTests: BaseTestCase { - override func invokeTest() { - withMacroTesting(macros: ["URL": URLMacro.self]) { - super.invokeTest() - } - } - - func testExpansionWithMalformedURLEmitsError() { - assertMacro { - """ - let invalid = #URL("https://not a url.com") - """ - } diagnostics: { - """ - let invalid = #URL("https://not a url.com") - ┬──────────────────────────── - ╰─ 🛑 malformed url: "https://not a url.com" - """ - } - } - - func testExpansionWithStringInterpolationEmitsError() { - assertMacro { - #""" - #URL("https://\(domain)/api/path") - """# - } diagnostics: { - #""" - #URL("https://\(domain)/api/path") - ┬───────────────────────────────── - ╰─ 🛑 #URL requires a static string literal - """# - } - } - - func testExpansionWithValidURL() { - assertMacro { - """ - let valid = #URL("https://swift.org/") - """ - } expansion: { - """ - let valid = URL(string: "https://swift.org/")! - """ - } - } -} diff --git a/Tests/MacroTestingTests/WarningMacroTests.swift b/Tests/MacroTestingTests/WarningMacroTests.swift deleted file mode 100644 index 2590c9c..0000000 --- a/Tests/MacroTestingTests/WarningMacroTests.swift +++ /dev/null @@ -1,56 +0,0 @@ -import MacroTesting -import XCTest - -final class WarningMacroTests: BaseTestCase { - override func invokeTest() { - withMacroTesting(macros: ["myWarning": WarningMacro.self]) { - super.invokeTest() - } - } - - func testExpansionWithValidStringLiteralEmitsWarning() { - assertMacro { - """ - #myWarning("This is a warning") - """ - } expansion: { - """ - () - """ - } diagnostics: { - """ - #myWarning("This is a warning") - ┬────────────────────────────── - ╰─ ⚠️ This is a warning - """ - } - } - - func testExpansionWithInvalidExpressionEmitsError() { - assertMacro { - """ - #myWarning(42) - """ - } diagnostics: { - """ - #myWarning(42) - ┬───────────── - ╰─ 🛑 #myWarning macro requires a string literal - """ - } - } - - func testExpansionWithStringInterpolationEmitsError() { - assertMacro { - #""" - #myWarning("Say hello \(number) times!") - """# - } diagnostics: { - #""" - #myWarning("Say hello \(number) times!") - ┬─────────────────────────────────────── - ╰─ 🛑 #myWarning macro requires a string literal - """# - } - } -} diff --git a/Tests/MacroTestingTests/WrapStoredPropertiesMacroTests.swift b/Tests/MacroTestingTests/WrapStoredPropertiesMacroTests.swift deleted file mode 100644 index 2911b63..0000000 --- a/Tests/MacroTestingTests/WrapStoredPropertiesMacroTests.swift +++ /dev/null @@ -1,80 +0,0 @@ -import MacroTesting -import XCTest - -final class WrapStoredPropertiesMacroTests: BaseTestCase { - override func invokeTest() { - withMacroTesting(macros: ["wrapStoredProperties": WrapStoredPropertiesMacro.self]) { - super.invokeTest() - } - } - - func testExpansionAddsPublished() { - assertMacro { - """ - @wrapStoredProperties("Published") - struct Test { - var value: Int - } - """ - } expansion: { - """ - struct Test { - @Published - var value: Int - } - """ - } - } - - func testExpansionAddsDeprecationAttribute() { - assertMacro { - """ - @wrapStoredProperties(#"available(*, deprecated, message: "hands off my data")"#) - struct Test { - var value: Int - } - """ - } expansion: { - """ - struct Test { - @available(*, deprecated, message: "hands off my data") - var value: Int - } - """ - } - } - - func testExpansionIgnoresComputedProperty() { - assertMacro { - """ - @wrapStoredProperties("Published") - struct Test { - var value: Int { - get { return 0 } - set {} - } - } - """ - } - } - - func testExpansionWithInvalidAttributeEmitsError() { - assertMacro { - """ - @wrapStoredProperties(12) - struct Test { - var value: Int - } - """ - } diagnostics: { - """ - @wrapStoredProperties(12) - ┬──────────────────────── - ╰─ 🛑 macro requires a string literal containing the name of an attribute - struct Test { - var value: Int - } - """ - } - } -} diff --git a/Tests/MemberwiseInitTests/CustomInitDefaultTests.swift b/Tests/MemberwiseInitTests/CustomInitDefaultTests.swift index a23e10a..ac301cb 100644 --- a/Tests/MemberwiseInitTests/CustomInitDefaultTests.swift +++ b/Tests/MemberwiseInitTests/CustomInitDefaultTests.swift @@ -6,9 +6,9 @@ import XCTest final class CustomInitDefaultTests: XCTestCase { override func invokeTest() { - // NB: Waiting for swift-macro-testing PR to support explicit indentationWidth: https://github.com/pointfreeco/swift-macro-testing/pull/8 withMacroTesting( - //indentationWidth: .spaces(2), + indentationWidth: .spaces(2), + record: .missing, macros: [ "MemberwiseInit": MemberwiseInitMacro.self, "InitRaw": InitMacro.self, @@ -73,15 +73,6 @@ final class CustomInitDefaultTests: XCTestCase { @Init(default: 42) let number = 0 } """ - } expansion: { - """ - struct S { - @Init(default: 42) let number = 0 - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -95,20 +86,18 @@ final class CustomInitDefaultTests: XCTestCase { """ } fixes: { """ - @Init(default: 42) let number = 0 - ┬────────── - ╰─ 🛑 @Init can't be applied to already initialized constant - - ✏️ Remove '@Init(default: 42)' @MemberwiseInit struct S { let number = 0 } - - ✏️ Remove '= 0' - @MemberwiseInit + """ + } expansion: { + """ struct S { - @Init(default: 42) let number: Int + let number = 0 + + internal init() { + } } """ } @@ -122,15 +111,6 @@ final class CustomInitDefaultTests: XCTestCase { @Init(default: 42, label: "_") let number = 0 } """ - } expansion: { - """ - struct S { - @Init(default: 42, label: "_") let number = 0 - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -144,20 +124,18 @@ final class CustomInitDefaultTests: XCTestCase { """ } fixes: { """ - @Init(default: 42, label: "_") let number = 0 - ┬─────────── - ╰─ 🛑 @Init can't be applied to already initialized constant - - ✏️ Remove '@Init(default: 42, label: "_")' @MemberwiseInit struct S { let number = 0 } - - ✏️ Remove '= 0' - @MemberwiseInit + """ + } expansion: { + """ struct S { - @Init(default: 42, label: "_") let number: Int + let number = 0 + + internal init() { + } } """ } @@ -171,15 +149,6 @@ final class CustomInitDefaultTests: XCTestCase { @Init(default: 42) var number = 0 } """ - } expansion: { - """ - struct S { - @Init(default: 42) var number = 0 - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -193,20 +162,21 @@ final class CustomInitDefaultTests: XCTestCase { """ } fixes: { """ - @Init(default: 42) var number = 0 - ┬────────── - ╰─ 🛑 Custom 'default' can't be applied to already initialized variable - - ✏️ Remove '@Init(default: 42)' @MemberwiseInit struct S { var number = 0 } - - ✏️ Remove '= 0' - @MemberwiseInit + """ + } expansion: { + """ struct S { - @Init(default: 42) var number: Int + var number = 0 + + internal init( + number: Int = 0 + ) { + self.number = number + } } """ } @@ -220,15 +190,6 @@ final class CustomInitDefaultTests: XCTestCase { @Binding @Init(default: 42) let number = 0 } """ - } expansion: { - """ - struct S { - @Binding @Init(default: 42) let number = 0 - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -242,20 +203,18 @@ final class CustomInitDefaultTests: XCTestCase { """ } fixes: { """ - @Binding @Init(default: 42) let number = 0 - ┬────────── - ╰─ 🛑 @Init can't be applied to already initialized constant - - ✏️ Remove '@Init(default: 42)' @MemberwiseInit struct S { @Binding let number = 0 } - - ✏️ Remove '= 0' - @MemberwiseInit + """ + } expansion: { + """ struct S { - @Binding @Init(default: 42) let number: Int + @Binding let number = 0 + + internal init() { + } } """ } @@ -269,15 +228,6 @@ final class CustomInitDefaultTests: XCTestCase { @Binding @Init(default: T.q) let number = T.t } """ - } expansion: { - """ - struct S { - @Binding @Init(default: T.q) let number = T.t - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -291,20 +241,18 @@ final class CustomInitDefaultTests: XCTestCase { """ } fixes: { """ - @Binding @Init(default: T.q) let number = T.t - ┬─────────── - ╰─ 🛑 @Init can't be applied to already initialized constant - - ✏️ Remove '@Init(default: T.q)' @MemberwiseInit struct S { @Binding let number = T.t } - - ✏️ Remove '= T.t' - @MemberwiseInit + """ + } expansion: { + """ struct S { - @Binding @Init(default: T.q) let number: <#Type#> + @Binding let number = T.t + + internal init() { + } } """ } @@ -318,15 +266,6 @@ final class CustomInitDefaultTests: XCTestCase { @Binding @Init(default: 42) var number = 0 } """ - } expansion: { - """ - struct S { - @Binding @Init(default: 42) var number = 0 - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -340,20 +279,21 @@ final class CustomInitDefaultTests: XCTestCase { """ } fixes: { """ - @Binding @Init(default: 42) var number = 0 - ┬────────── - ╰─ 🛑 Custom 'default' can't be applied to already initialized variable - - ✏️ Remove 'default: 42' @MemberwiseInit struct S { @Binding @Init var number = 0 } - - ✏️ Remove '= 0' - @MemberwiseInit + """ + } expansion: { + """ struct S { - @Binding @Init(default: 42) var number: Int + @Binding @Init var number = 0 + + internal init( + number: Int = 0 + ) { + self.number = number + } } """ } @@ -367,15 +307,6 @@ final class CustomInitDefaultTests: XCTestCase { @Binding @Init(default: T.q) var number = T.t } """ - } expansion: { - """ - struct S { - @Binding @Init(default: T.q) var number = T.t - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -389,23 +320,12 @@ final class CustomInitDefaultTests: XCTestCase { """ } fixes: { """ - @Binding @Init(default: T.q) var number = T.t - ┬─────────── - ╰─ 🛑 Custom 'default' can't be applied to already initialized variable - - ✏️ Remove 'default: T.q' @MemberwiseInit struct S { @Binding @Init var number = T.t } - - ✏️ Remove '= T.t' - @MemberwiseInit - struct S { - @Binding @Init(default: T.q) var number: <#Type#> - } """ - } + } } // TODO: This test doesn't fit perfectly here because it touches on label @@ -417,15 +337,6 @@ final class CustomInitDefaultTests: XCTestCase { @Init(default: 42, label: "_") var number = 0 } """ - } expansion: { - """ - struct S { - @Init(default: 42, label: "_") var number = 0 - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -439,20 +350,21 @@ final class CustomInitDefaultTests: XCTestCase { """ } fixes: { """ - @Init(default: 42, label: "_") var number = 0 - ┬─────────── - ╰─ 🛑 Custom 'default' can't be applied to already initialized variable - - ✏️ Remove 'default: 42' @MemberwiseInit struct S { @Init(label: "_") var number = 0 } - - ✏️ Remove '= 0' - @MemberwiseInit + """ + } expansion: { + """ struct S { - @Init(default: 42, label: "_") var number: Int + @Init(label: "_") var number = 0 + + internal init( + _ number: Int = 0 + ) { + self.number = number + } } """ } @@ -466,15 +378,6 @@ final class CustomInitDefaultTests: XCTestCase { @Init(default: 42) let x, y: Int } """ - } expansion: { - """ - struct S { - @Init(default: 42) let x, y: Int - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -487,16 +390,25 @@ final class CustomInitDefaultTests: XCTestCase { """ } fixes: { """ - @Init(default: 42) let x, y: Int - ┬────────── - ╰─ 🛑 Custom 'default' can't be applied to multiple bindings - - ✏️ Remove '@Init(default: 42)' @MemberwiseInit struct S { let x, y: Int } """ + } expansion: { + """ + struct S { + let x, y: Int + + internal init( + x: Int, + y: Int + ) { + self.x = x + self.y = y + } + } + """ } // } expansion: { // """ @@ -522,15 +434,6 @@ final class CustomInitDefaultTests: XCTestCase { @Init(default: 42) var x, y: Int } """ - } expansion: { - """ - struct S { - @Init(default: 42) var x, y: Int - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -543,16 +446,25 @@ final class CustomInitDefaultTests: XCTestCase { """ } fixes: { """ - @Init(default: 42) var x, y: Int - ┬────────── - ╰─ 🛑 Custom 'default' can't be applied to multiple bindings - - ✏️ Remove '@Init(default: 42)' @MemberwiseInit struct S { var x, y: Int } """ + } expansion: { + """ + struct S { + var x, y: Int + + internal init( + x: Int, + y: Int + ) { + self.x = x + self.y = y + } + } + """ } // } expansion: { // """ @@ -578,15 +490,6 @@ final class CustomInitDefaultTests: XCTestCase { @Init(default: 42) let x = 0, y: Int } """ - } expansion: { - """ - struct S { - @Init(default: 42) let x = 0, y: Int - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -599,16 +502,23 @@ final class CustomInitDefaultTests: XCTestCase { """ } fixes: { """ - @Init(default: 42) let x = 0, y: Int - ┬────────── - ╰─ 🛑 Custom 'default' can't be applied to multiple bindings - - ✏️ Remove '@Init(default: 42)' @MemberwiseInit struct S { let x = 0, y: Int } """ + } expansion: { + """ + struct S { + let x = 0, y: Int + + internal init( + y: Int + ) { + self.y = y + } + } + """ } // } expansion: { // """ @@ -632,15 +542,6 @@ final class CustomInitDefaultTests: XCTestCase { @Init(default: 42) var x = 0, y: Int } """ - } expansion: { - """ - struct S { - @Init(default: 42) var x = 0, y: Int - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -653,16 +554,25 @@ final class CustomInitDefaultTests: XCTestCase { """ } fixes: { """ - @Init(default: 42) var x = 0, y: Int - ┬────────── - ╰─ 🛑 Custom 'default' can't be applied to multiple bindings - - ✏️ Remove '@Init(default: 42)' @MemberwiseInit struct S { var x = 0, y: Int } """ + } expansion: { + """ + struct S { + var x = 0, y: Int + + internal init( + x: Int = 0, + y: Int + ) { + self.x = x + self.y = y + } + } + """ } // } expansion: { // """ @@ -688,15 +598,6 @@ final class CustomInitDefaultTests: XCTestCase { @Init(default: 42) let x: Int, isOn: Bool } """ - } expansion: { - """ - struct S { - @Init(default: 42) let x: Int, isOn: Bool - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -709,16 +610,25 @@ final class CustomInitDefaultTests: XCTestCase { """ } fixes: { """ - @Init(default: 42) let x: Int, isOn: Bool - ┬────────── - ╰─ 🛑 Custom 'default' can't be applied to multiple bindings - - ✏️ Remove '@Init(default: 42)' @MemberwiseInit struct S { let x: Int, isOn: Bool } """ + } expansion: { + """ + struct S { + let x: Int, isOn: Bool + + internal init( + x: Int, + isOn: Bool + ) { + self.x = x + self.isOn = isOn + } + } + """ } // } expansion: { // """ diff --git a/Tests/MemberwiseInitTests/CustomInitRawTests.swift b/Tests/MemberwiseInitTests/CustomInitRawTests.swift index dc5f149..7f405e3 100644 --- a/Tests/MemberwiseInitTests/CustomInitRawTests.swift +++ b/Tests/MemberwiseInitTests/CustomInitRawTests.swift @@ -7,6 +7,7 @@ final class CustomInitRawTests: XCTestCase { override func invokeTest() { withMacroTesting( indentationWidth: .spaces(2), + record: .missing, macros: [ "MemberwiseInit": MemberwiseInitMacro.self, "InitRaw": InitMacro.self, @@ -70,15 +71,6 @@ final class CustomInitRawTests: XCTestCase { @InitRaw(default: 42) let number = 0 } """ - } expansion: { - """ - struct S { - let number = 0 - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -92,20 +84,18 @@ final class CustomInitRawTests: XCTestCase { """ } fixes: { """ - @InitRaw(default: 42) let number = 0 - ┬────────── - ╰─ 🛑 @InitRaw can't be applied to already initialized constant - - ✏️ Remove '@InitRaw(default: 42)' @MemberwiseInit struct S { let number = 0 } - - ✏️ Remove '= 0' - @MemberwiseInit + """ + } expansion: { + """ struct S { - @InitRaw(default: 42) let number: Int + let number = 0 + + internal init() { + } } """ } diff --git a/Tests/MemberwiseInitTests/CustomInitTests.swift b/Tests/MemberwiseInitTests/CustomInitTests.swift index f78ab2e..a590748 100644 --- a/Tests/MemberwiseInitTests/CustomInitTests.swift +++ b/Tests/MemberwiseInitTests/CustomInitTests.swift @@ -4,9 +4,9 @@ import XCTest final class CustomInitTests: XCTestCase { override func invokeTest() { - // NB: Waiting for swift-macro-testing PR to support explicit indentationWidth: https://github.com/pointfreeco/swift-macro-testing/pull/8 withMacroTesting( - //indentationWidth: .spaces(2), + indentationWidth: .spaces(2), + record: .missing, macros: [ "MemberwiseInit": MemberwiseInitMacro.self, "InitRaw": InitMacro.self, @@ -25,15 +25,6 @@ final class CustomInitTests: XCTestCase { @Init let number = 42 } """ - } expansion: { - """ - struct S { - @Init let number = 42 - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -47,20 +38,18 @@ final class CustomInitTests: XCTestCase { """ } fixes: { """ - @Init let number = 42 - ┬──── - ╰─ ⚠️ @Init can't be applied to already initialized constant - - ✏️ Remove '@Init' @MemberwiseInit struct S { let number = 42 } - - ✏️ Remove '= 42' - @MemberwiseInit + """ + } expansion: { + """ struct S { - @Init let number: Int + let number = 42 + + internal init() { + } } """ } @@ -103,15 +92,6 @@ final class CustomInitTests: XCTestCase { @Init static var staticNumber: Int } """ - } expansion: { - """ - struct S { - @Init static var staticNumber: Int - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -124,16 +104,20 @@ final class CustomInitTests: XCTestCase { """ } fixes: { """ - @Init static var staticNumber: Int - ┬───── - ╰─ ⚠️ @Init can't be applied to 'static' members - - ✏️ Remove '@Init' @MemberwiseInit struct S { static var staticNumber: Int } """ + } expansion: { + """ + struct S { + static var staticNumber: Int + + internal init() { + } + } + """ } } @@ -148,17 +132,6 @@ final class CustomInitTests: XCTestCase { }() } """ - } expansion: { - """ - struct S { - @Init(default: 0) lazy var lazyNumber: Int = { - return 2 * 2 - }() - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -173,11 +146,6 @@ final class CustomInitTests: XCTestCase { """ } fixes: { """ - @Init(default: 0) lazy var lazyNumber: Int = { - ┬─── - ╰─ ⚠️ @Init can't be applied to 'lazy' members - - ✏️ Remove '@Init(default: 0)' @MemberwiseInit struct S { lazy var lazyNumber: Int = { @@ -185,6 +153,17 @@ final class CustomInitTests: XCTestCase { }() } """ + } expansion: { + """ + struct S { + lazy var lazyNumber: Int = { + return 2 * 2 + }() + + internal init() { + } + } + """ } } @@ -199,15 +178,6 @@ final class CustomInitTests: XCTestCase { @Init lazy static var value = 0 } """ - } expansion: { - """ - struct B { - @Init lazy static var value = 0 - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -220,16 +190,20 @@ final class CustomInitTests: XCTestCase { """ } fixes: { """ - @Init lazy static var value = 0 - ┬───── - ╰─ ⚠️ @Init can't be applied to 'static' members - - ✏️ Remove '@Init' @MemberwiseInit struct B { lazy static var value = 0 } """ + } expansion: { + """ + struct B { + lazy static var value = 0 + + internal init() { + } + } + """ } } } diff --git a/Tests/MemberwiseInitTests/CustomInitWrapperTests.swift b/Tests/MemberwiseInitTests/CustomInitWrapperTests.swift index 38e9849..41402bb 100644 --- a/Tests/MemberwiseInitTests/CustomInitWrapperTests.swift +++ b/Tests/MemberwiseInitTests/CustomInitWrapperTests.swift @@ -7,6 +7,7 @@ final class CustomInitWrapperTests: XCTestCase { override func invokeTest() { withMacroTesting( indentationWidth: .spaces(2), + record: .missing, macros: [ "MemberwiseInit": MemberwiseInitMacro.self, "Init": InitMacro.self, diff --git a/Tests/MemberwiseInitTests/InvalidSyntaxTests.swift b/Tests/MemberwiseInitTests/InvalidSyntaxTests.swift index b63d946..c7a9041 100644 --- a/Tests/MemberwiseInitTests/InvalidSyntaxTests.swift +++ b/Tests/MemberwiseInitTests/InvalidSyntaxTests.swift @@ -4,9 +4,9 @@ import XCTest final class InvalidSyntaxTests: XCTestCase { override func invokeTest() { - // NB: Waiting for swift-macro-testing PR to support explicit indentationWidth: https://github.com/pointfreeco/swift-macro-testing/pull/8 withMacroTesting( - //indentationWidth: .spaces(2), + indentationWidth: .spaces(2), + record: .missing, macros: [ "MemberwiseInit": MemberwiseInitMacro.self, "InitRaw": InitMacro.self, @@ -77,21 +77,6 @@ final class InvalidSyntaxTests: XCTestCase { let x: T } """ - } expansion: { - """ - struct S { - @Init(label: "x") let x: T - let x: T - - internal init( - x: T, - x: T - ) { - self.x = x - self.x = x - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -102,6 +87,6 @@ final class InvalidSyntaxTests: XCTestCase { let x: T } """ - } + } } } diff --git a/Tests/MemberwiseInitTests/LayeredDiagnosticsTests.swift b/Tests/MemberwiseInitTests/LayeredDiagnosticsTests.swift index c39206e..b90df52 100644 --- a/Tests/MemberwiseInitTests/LayeredDiagnosticsTests.swift +++ b/Tests/MemberwiseInitTests/LayeredDiagnosticsTests.swift @@ -4,9 +4,9 @@ import XCTest final class LayeredDiagnosticsTests: XCTestCase { override func invokeTest() { - // NB: Waiting for swift-macro-testing PR to support explicit indentationWidth: https://github.com/pointfreeco/swift-macro-testing/pull/8 withMacroTesting( - //indentationWidth: .spaces(2), + indentationWidth: .spaces(2), + record: .missing, macros: [ "MemberwiseInit": MemberwiseInitMacro.self, "InitRaw": InitMacro.self, @@ -24,15 +24,6 @@ final class LayeredDiagnosticsTests: XCTestCase { @Init(label: "$foo") let x, y: T } """ - } expansion: { - """ - struct S { - @Init(label: "$foo") let x, y: T - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -42,7 +33,7 @@ final class LayeredDiagnosticsTests: XCTestCase { ╰─ 🛑 Custom 'label' can't be applied to multiple bindings } """ - } + } } func testInvalidLabelAndDefaultOnMultipleBindings() { @@ -53,15 +44,6 @@ final class LayeredDiagnosticsTests: XCTestCase { @Init(default: 0, label: "$foo") let x, y: T } """ - } expansion: { - """ - struct S { - @Init(default: 0, label: "$foo") let x, y: T - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -74,18 +56,6 @@ final class LayeredDiagnosticsTests: XCTestCase { ✏️ Remove 'default: 0' } """ - } fixes: { - """ - @Init(default: 0, label: "$foo") let x, y: T - ┬────────── - ╰─ 🛑 Custom 'default' can't be applied to multiple bindings - - ✏️ Remove 'default: 0' - @MemberwiseInit - struct S { - @Init(label: "$foo") let x, y: T - } - """ } } @@ -97,15 +67,6 @@ final class LayeredDiagnosticsTests: XCTestCase { @Init(default: 0, label: "$foo") private let x, y: T } """ - } expansion: { - """ - struct S { - @Init(default: 0, label: "$foo") private let x, y: T - - public init() { - } - } - """ } diagnostics: { """ @MemberwiseInit(.public) @@ -123,40 +84,6 @@ final class LayeredDiagnosticsTests: XCTestCase { ✏️ Remove 'default: 0' } """ - } fixes: { - """ - @Init(default: 0, label: "$foo") private let x, y: T - ┬────────── - ╰─ 🛑 Custom 'default' can't be applied to multiple bindings - - ✏️ Remove 'default: 0' - @MemberwiseInit(.public) - struct S { - @Init(label: "$foo") private let x, y: T - } - - @Init(default: 0, label: "$foo") private let x, y: T - ┬────── - ╰─ 🛑 @MemberwiseInit(.public) would leak access to 'private' property - - ✏️ Add '@Init(.public)' - @MemberwiseInit(.public) - struct S { - @Init(.public, default: 0, label: "$foo") private let x, y: T - } - - ✏️ Replace 'private' access with 'public' - @MemberwiseInit(.public) - struct S { - @Init(default: 0, label: "$foo") public let x, y: T - } - - ✏️ Add '@Init(.ignore)' and an initializer - @MemberwiseInit(.public) - struct S { - @Init(.ignore) private let x = <#value#>, y: T = <#value#> - } - """ } } @@ -169,16 +96,6 @@ final class LayeredDiagnosticsTests: XCTestCase { @Init(label: "foo") let y: T } """ - } expansion: { - """ - struct S { - @Init(label: "foo") let x: T - @Init(label: "foo") let y: T - - public init() { - } - } - """ } diagnostics: { """ @MemberwiseInit(.public) @@ -199,55 +116,11 @@ final class LayeredDiagnosticsTests: XCTestCase { """ } fixes: { """ - @Init(label: "foo") let x: T - ┬─────────────────────────── - ╰─ 🛑 @MemberwiseInit(.public) would leak access to 'internal' property - - ✏️ Add '@Init(.public)' @MemberwiseInit(.public) struct S { @Init(.public, label: "foo") let x: T - @Init(label: "foo") let y: T - } - - ✏️ Add 'public' access level - @MemberwiseInit(.public) - struct S { - @Init(label: "foo") public let x: T - @Init(label: "foo") let y: T - } - - ✏️ Add '@Init(.ignore)' and an initializer - @MemberwiseInit(.public) - struct S { - @Init(.ignore) let x: T = <#value#> - @Init(label: "foo") let y: T - } - - @Init(label: "foo") let y: T - ┬─────────────────────────── - ╰─ 🛑 @MemberwiseInit(.public) would leak access to 'internal' property - - ✏️ Add '@Init(.public)' - @MemberwiseInit(.public) - struct S { - @Init(label: "foo") let x: T @Init(.public, label: "foo") let y: T } - - ✏️ Add 'public' access level - @MemberwiseInit(.public) - struct S { - @Init(label: "foo") let x: T - @Init(label: "foo") public let y: T - } - - ✏️ Add '@Init(.ignore)' and an initializer - @MemberwiseInit(.public) - struct S { - @Init(label: "foo") let x: T - @Init(.ignore) let y: T = <#value#> - } """ } } @@ -261,16 +134,6 @@ final class LayeredDiagnosticsTests: XCTestCase { let y: T } """ - } expansion: { - """ - struct S { - @Init(label: "y") let x: T - let y: T - - public init() { - } - } - """ } diagnostics: { """ @MemberwiseInit(.public) @@ -291,55 +154,11 @@ final class LayeredDiagnosticsTests: XCTestCase { """ } fixes: { """ - @Init(label: "y") let x: T - ┬───────────────────────── - ╰─ 🛑 @MemberwiseInit(.public) would leak access to 'internal' property - - ✏️ Add '@Init(.public)' @MemberwiseInit(.public) struct S { @Init(.public, label: "y") let x: T - let y: T - } - - ✏️ Add 'public' access level - @MemberwiseInit(.public) - struct S { - @Init(label: "y") public let x: T - let y: T - } - - ✏️ Add '@Init(.ignore)' and an initializer - @MemberwiseInit(.public) - struct S { - @Init(.ignore) let x: T = <#value#> - let y: T - } - - let y: T - ┬─────── - ╰─ 🛑 @MemberwiseInit(.public) would leak access to 'internal' property - - ✏️ Add '@Init(.public)' - @MemberwiseInit(.public) - struct S { - @Init(label: "y") let x: T @Init(.public) let y: T } - - ✏️ Add 'public' access level - @MemberwiseInit(.public) - struct S { - @Init(label: "y") let x: T - public let y: T - } - - ✏️ Add '@Init(.ignore)' and an initializer - @MemberwiseInit(.public) - struct S { - @Init(label: "y") let x: T - @Init(.ignore) let y: T = <#value#> - } """ } } diff --git a/Tests/MemberwiseInitTests/MemberwiseInitAccessLevelTests.swift b/Tests/MemberwiseInitTests/MemberwiseInitAccessLevelTests.swift index e5dbe6f..63bff4e 100644 --- a/Tests/MemberwiseInitTests/MemberwiseInitAccessLevelTests.swift +++ b/Tests/MemberwiseInitTests/MemberwiseInitAccessLevelTests.swift @@ -8,10 +8,9 @@ import XCTest final class MemberwiseInitAccessLevelTests: XCTestCase { override func invokeTest() { - // NB: Waiting for swift-macro-testing PR to support explicit indentationWidth: https://github.com/pointfreeco/swift-macro-testing/pull/8 withMacroTesting( - //indentationWidth: .spaces(2), - //isRecording: true, + indentationWidth: .spaces(2), + record: .missing, macros: [ "MemberwiseInit": MemberwiseInitMacro.self, "Init": InitMacro.self, @@ -22,7 +21,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PrivateStruct_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) private struct S { @@ -45,7 +44,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PrivateStruct_InitPrivate_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) private struct S { @@ -68,7 +67,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PrivateStruct_InitInternal_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) private struct S { @@ -91,7 +90,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PrivateStruct_InitPublic_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) private struct S { @@ -114,7 +113,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PrivateStruct_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) private struct S { @@ -137,7 +136,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PrivateStruct_InitPrivate_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) private struct S { @@ -160,7 +159,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PrivateStruct_InitInternal_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) private struct S { @@ -183,7 +182,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PrivateStruct_InitPublic_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) private struct S { @@ -206,7 +205,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PrivateStruct_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) private struct S { @@ -229,7 +228,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PrivateStruct_InitPrivate_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) private struct S { @@ -252,7 +251,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PrivateStruct_InitInternal_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) private struct S { @@ -275,7 +274,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PrivateStruct_InitPublic_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) private struct S { @@ -298,7 +297,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PrivateStruct_NoProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) private struct S { @@ -308,15 +307,15 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { """ private struct S { - private init() { - } + private init() { + } } """ } } func testMemberwiseInitPrivate_DefaultStruct_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) struct S { @@ -339,7 +338,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_DefaultStruct_InitPrivate_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) struct S { @@ -362,7 +361,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_DefaultStruct_InitInternal_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) struct S { @@ -385,7 +384,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_DefaultStruct_InitPublic_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) struct S { @@ -408,7 +407,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_DefaultStruct_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) struct S { @@ -431,7 +430,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_DefaultStruct_InitPrivate_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) struct S { @@ -454,7 +453,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_DefaultStruct_InitInternal_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) struct S { @@ -477,7 +476,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_DefaultStruct_InitPublic_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) struct S { @@ -500,7 +499,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_DefaultStruct_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) struct S { @@ -523,7 +522,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_DefaultStruct_InitPrivate_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) struct S { @@ -546,7 +545,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_DefaultStruct_InitInternal_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) struct S { @@ -569,7 +568,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_DefaultStruct_InitPublic_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) struct S { @@ -592,7 +591,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_DefaultStruct_NoProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) struct S { @@ -602,15 +601,15 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { """ struct S { - private init() { - } + private init() { + } } """ } } func testMemberwiseInitPrivate_PublicStruct_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) public struct S { @@ -633,7 +632,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PublicStruct_InitPrivate_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) public struct S { @@ -656,7 +655,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PublicStruct_InitInternal_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) public struct S { @@ -679,7 +678,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PublicStruct_InitPublic_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) public struct S { @@ -702,7 +701,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PublicStruct_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) public struct S { @@ -725,7 +724,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PublicStruct_InitPrivate_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) public struct S { @@ -748,7 +747,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PublicStruct_InitInternal_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) public struct S { @@ -771,7 +770,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PublicStruct_InitPublic_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) public struct S { @@ -794,7 +793,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PublicStruct_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) public struct S { @@ -817,7 +816,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PublicStruct_InitPrivate_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) public struct S { @@ -840,7 +839,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PublicStruct_InitInternal_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) public struct S { @@ -863,7 +862,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PublicStruct_InitPublic_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) public struct S { @@ -886,7 +885,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPrivate_PublicStruct_NoProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.private) public struct S { @@ -896,30 +895,21 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { """ public struct S { - private init() { - } + private init() { + } } """ } } func testMemberwiseInitDefault_PrivateStruct_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit private struct S { private let v: T } """ - } expansion: { - """ - private struct S { - private let v: T - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -932,15 +922,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitDefault_PrivateStruct_InitPrivate_PrivateProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit private struct S { - @Init(.private) private let v: T + @Init(.internal) private let v: T } """ } expansion: { @@ -948,10 +934,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { private struct S { private let v: T - internal init() { + internal init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitDefault_PrivateStruct_InitPrivate_PrivateProperty() { + assertMacro { + """ + @MemberwiseInit + private struct S { + @Init(.private) private let v: T + } + """ } diagnostics: { """ @MemberwiseInit @@ -963,11 +963,30 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ + } fixes: { + """ + @MemberwiseInit + private struct S { + @Init(.internal, ) private let v: T + } + """ + } expansion: { + """ + private struct S { + private let v: T + + internal init( + v: T + ) { + self.v = v + } + } + """ } } func testMemberwiseInitDefault_PrivateStruct_InitInternal_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit private struct S { @@ -990,7 +1009,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_PrivateStruct_InitPublic_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit private struct S { @@ -1013,22 +1032,13 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_PrivateStruct_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit private struct S { let v: T } """ - } expansion: { - """ - private struct S { - let v: T - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -1041,15 +1051,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitDefault_PrivateStruct_InitPrivate_DefaultProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit private struct S { - @Init(.private) let v: T + @Init(.internal) let v: T } """ } expansion: { @@ -1057,10 +1063,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { private struct S { let v: T - internal init() { + internal init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitDefault_PrivateStruct_InitPrivate_DefaultProperty() { + assertMacro { + """ + @MemberwiseInit + private struct S { + @Init(.private) let v: T + } + """ } diagnostics: { """ @MemberwiseInit @@ -1072,11 +1092,30 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ + } fixes: { + """ + @MemberwiseInit + private struct S { + @Init(.internal, ) let v: T + } + """ + } expansion: { + """ + private struct S { + let v: T + + internal init( + v: T + ) { + self.v = v + } + } + """ } } func testMemberwiseInitDefault_PrivateStruct_InitInternal_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit private struct S { @@ -1099,7 +1138,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_PrivateStruct_InitPublic_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit private struct S { @@ -1122,7 +1161,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_PrivateStruct_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit private struct S { @@ -1145,22 +1184,13 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_PrivateStruct_InitPrivate_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit private struct S { @Init(.private) public let v: T } """ - } expansion: { - """ - private struct S { - public let v: T - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -1172,11 +1202,30 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ + } fixes: { + """ + @MemberwiseInit + private struct S { + @Init(.internal, ) public let v: T + } + """ + } expansion: { + """ + private struct S { + public let v: T + + internal init( + v: T + ) { + self.v = v + } + } + """ } } func testMemberwiseInitDefault_PrivateStruct_InitInternal_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit private struct S { @@ -1199,7 +1248,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_PrivateStruct_InitPublic_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit private struct S { @@ -1222,7 +1271,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_PrivateStruct_NoProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit private struct S { @@ -1232,30 +1281,21 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { """ private struct S { - internal init() { - } + internal init() { + } } """ } } func testMemberwiseInitDefault_DefaultStruct_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit struct S { private let v: T } """ - } expansion: { - """ - struct S { - private let v: T - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -1268,15 +1308,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitDefault_DefaultStruct_InitPrivate_PrivateProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit struct S { - @Init(.private) private let v: T + @Init(.internal) private let v: T } """ } expansion: { @@ -1284,10 +1320,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { struct S { private let v: T - internal init() { + internal init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitDefault_DefaultStruct_InitPrivate_PrivateProperty() { + assertMacro { + """ + @MemberwiseInit + struct S { + @Init(.private) private let v: T + } + """ } diagnostics: { """ @MemberwiseInit @@ -1299,11 +1349,30 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ + } fixes: { + """ + @MemberwiseInit + struct S { + @Init(.internal, ) private let v: T + } + """ + } expansion: { + """ + struct S { + private let v: T + + internal init( + v: T + ) { + self.v = v + } + } + """ } } func testMemberwiseInitDefault_DefaultStruct_InitInternal_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit struct S { @@ -1326,7 +1395,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_DefaultStruct_InitPublic_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit struct S { @@ -1349,7 +1418,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_DefaultStruct_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit struct S { @@ -1372,22 +1441,13 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_DefaultStruct_InitPrivate_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit struct S { @Init(.private) let v: T } """ - } expansion: { - """ - struct S { - let v: T - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -1399,11 +1459,30 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ + } fixes: { + """ + @MemberwiseInit + struct S { + @Init(.internal, ) let v: T + } + """ + } expansion: { + """ + struct S { + let v: T + + internal init( + v: T + ) { + self.v = v + } + } + """ } } func testMemberwiseInitDefault_DefaultStruct_InitInternal_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit struct S { @@ -1426,7 +1505,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_DefaultStruct_InitPublic_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit struct S { @@ -1449,7 +1528,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_DefaultStruct_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit struct S { @@ -1472,22 +1551,13 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_DefaultStruct_InitPrivate_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit struct S { @Init(.private) public let v: T } """ - } expansion: { - """ - struct S { - public let v: T - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -1499,11 +1569,30 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ + } fixes: { + """ + @MemberwiseInit + struct S { + @Init(.internal, ) public let v: T + } + """ + } expansion: { + """ + struct S { + public let v: T + + internal init( + v: T + ) { + self.v = v + } + } + """ } } func testMemberwiseInitDefault_DefaultStruct_InitInternal_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit struct S { @@ -1526,7 +1615,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_DefaultStruct_InitPublic_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit struct S { @@ -1549,7 +1638,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_DefaultStruct_NoProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit struct S { @@ -1559,30 +1648,21 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { """ struct S { - internal init() { - } + internal init() { + } } """ } } func testMemberwiseInitDefault_PublicStruct_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit public struct S { private let v: T } """ - } expansion: { - """ - public struct S { - private let v: T - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -1595,15 +1675,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitDefault_PublicStruct_InitPrivate_PrivateProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit public struct S { - @Init(.private) private let v: T + @Init(.internal) private let v: T } """ } expansion: { @@ -1611,10 +1687,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { public struct S { private let v: T - internal init() { + internal init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitDefault_PublicStruct_InitPrivate_PrivateProperty() { + assertMacro { + """ + @MemberwiseInit + public struct S { + @Init(.private) private let v: T + } + """ } diagnostics: { """ @MemberwiseInit @@ -1626,11 +1716,30 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ + } fixes: { + """ + @MemberwiseInit + public struct S { + @Init(.internal, ) private let v: T + } + """ + } expansion: { + """ + public struct S { + private let v: T + + internal init( + v: T + ) { + self.v = v + } + } + """ } } func testMemberwiseInitDefault_PublicStruct_InitInternal_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit public struct S { @@ -1653,7 +1762,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_PublicStruct_InitPublic_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit public struct S { @@ -1676,7 +1785,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_PublicStruct_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit public struct S { @@ -1699,22 +1808,13 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_PublicStruct_InitPrivate_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit public struct S { @Init(.private) let v: T } """ - } expansion: { - """ - public struct S { - let v: T - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -1726,11 +1826,30 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ + } fixes: { + """ + @MemberwiseInit + public struct S { + @Init(.internal, ) let v: T + } + """ + } expansion: { + """ + public struct S { + let v: T + + internal init( + v: T + ) { + self.v = v + } + } + """ } } func testMemberwiseInitDefault_PublicStruct_InitInternal_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit public struct S { @@ -1753,7 +1872,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_PublicStruct_InitPublic_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit public struct S { @@ -1776,7 +1895,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_PublicStruct_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit public struct S { @@ -1799,22 +1918,13 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_PublicStruct_InitPrivate_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit public struct S { @Init(.private) public let v: T } """ - } expansion: { - """ - public struct S { - public let v: T - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -1826,11 +1936,30 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ + } fixes: { + """ + @MemberwiseInit + public struct S { + @Init(.internal, ) public let v: T + } + """ + } expansion: { + """ + public struct S { + public let v: T + + internal init( + v: T + ) { + self.v = v + } + } + """ } } func testMemberwiseInitDefault_PublicStruct_InitInternal_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit public struct S { @@ -1853,7 +1982,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_PublicStruct_InitPublic_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit public struct S { @@ -1876,7 +2005,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitDefault_PublicStruct_NoProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit public struct S { @@ -1886,30 +2015,21 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { """ public struct S { - internal init() { - } + internal init() { + } } """ } } func testMemberwiseInitPublic_PrivateStruct_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.public) private struct S { private let v: T } """ - } expansion: { - """ - private struct S { - private let v: T - - public init() { - } - } - """ } diagnostics: { """ @MemberwiseInit(.public) @@ -1922,15 +2042,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitPublic_PrivateStruct_InitPrivate_PrivateProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit(.public) private struct S { - @Init(.private) private let v: T + @Init(.public) private let v: T } """ } expansion: { @@ -1938,10 +2054,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { private struct S { private let v: T - public init() { + public init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitPublic_PrivateStruct_InitPrivate_PrivateProperty() { + assertMacro { + """ + @MemberwiseInit(.public) + private struct S { + @Init(.private) private let v: T + } + """ } diagnostics: { """ @MemberwiseInit(.public) @@ -1953,15 +2083,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitPublic_PrivateStruct_InitInternal_PrivateProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit(.public) private struct S { - @Init(.internal) private let v: T + @Init(.public, ) private let v: T } """ } expansion: { @@ -1969,10 +2095,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { private struct S { private let v: T - public init() { + public init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitPublic_PrivateStruct_InitInternal_PrivateProperty() { + assertMacro { + """ + @MemberwiseInit(.public) + private struct S { + @Init(.internal) private let v: T + } + """ } diagnostics: { """ @MemberwiseInit(.public) @@ -1984,15 +2124,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitPublic_PrivateStruct_InitPublic_PrivateProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit(.public) private struct S { - @Init(.public) private let v: T + @Init(.public, ) private let v: T } """ } expansion: { @@ -2010,23 +2146,37 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } } - func testMemberwiseInitPublic_PrivateStruct_DefaultProperty() { - assertMacro(applyFixIts: false) { + func testMemberwiseInitPublic_PrivateStruct_InitPublic_PrivateProperty() { + assertMacro { """ @MemberwiseInit(.public) private struct S { - let v: T + @Init(.public) private let v: T } """ } expansion: { """ private struct S { - let v: T + private let v: T - public init() { + public init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitPublic_PrivateStruct_DefaultProperty() { + assertMacro { + """ + @MemberwiseInit(.public) + private struct S { + let v: T + } + """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2039,15 +2189,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitPublic_PrivateStruct_InitPrivate_DefaultProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit(.public) private struct S { - @Init(.private) let v: T + @Init(.public) let v: T } """ } expansion: { @@ -2055,10 +2201,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { private struct S { let v: T - public init() { + public init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitPublic_PrivateStruct_InitPrivate_DefaultProperty() { + assertMacro { + """ + @MemberwiseInit(.public) + private struct S { + @Init(.private) let v: T + } + """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2070,15 +2230,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitPublic_PrivateStruct_InitInternal_DefaultProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit(.public) private struct S { - @Init(.internal) let v: T + @Init(.public, ) let v: T } """ } expansion: { @@ -2086,10 +2242,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { private struct S { let v: T - public init() { + public init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitPublic_PrivateStruct_InitInternal_DefaultProperty() { + assertMacro { + """ + @MemberwiseInit(.public) + private struct S { + @Init(.internal) let v: T + } + """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2101,11 +2271,30 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ + } fixes: { + """ + @MemberwiseInit(.public) + private struct S { + @Init(.public, ) let v: T + } + """ + } expansion: { + """ + private struct S { + let v: T + + public init( + v: T + ) { + self.v = v + } + } + """ } } func testMemberwiseInitPublic_PrivateStruct_InitPublic_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.public) private struct S { @@ -2128,7 +2317,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPublic_PrivateStruct_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.public) private struct S { @@ -2151,22 +2340,13 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPublic_PrivateStruct_InitPrivate_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.public) private struct S { @Init(.private) public let v: T } """ - } expansion: { - """ - private struct S { - public let v: T - - public init() { - } - } - """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2178,15 +2358,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitPublic_PrivateStruct_InitInternal_PublicProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit(.public) private struct S { - @Init(.internal) public let v: T + @Init(.public, ) public let v: T } """ } expansion: { @@ -2194,10 +2370,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { private struct S { public let v: T - public init() { + public init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitPublic_PrivateStruct_InitInternal_PublicProperty() { + assertMacro { + """ + @MemberwiseInit(.public) + private struct S { + @Init(.internal) public let v: T + } + """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2209,11 +2399,30 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ + } fixes: { + """ + @MemberwiseInit(.public) + private struct S { + @Init(.public, ) public let v: T + } + """ + } expansion: { + """ + private struct S { + public let v: T + + public init( + v: T + ) { + self.v = v + } + } + """ } } func testMemberwiseInitPublic_PrivateStruct_InitPublic_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.public) private struct S { @@ -2236,7 +2445,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPublic_PrivateStruct_NoProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.public) private struct S { @@ -2246,33 +2455,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { """ private struct S { - public init() { - } + public init() { + } } """ } } func testMemberwiseInitPublic_DefaultStruct_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.public) struct S { private let v: T } """ - } expansion: { + } diagnostics: { """ - struct S { - private let v: T - - public init() { - } - } - """ - } diagnostics: { - """ - @MemberwiseInit(.public) + @MemberwiseInit(.public) struct S { private let v: T ┬────── @@ -2282,15 +2482,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitPublic_DefaultStruct_InitPrivate_PrivateProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit(.public) struct S { - @Init(.private) private let v: T + @Init(.public) private let v: T } """ } expansion: { @@ -2298,10 +2494,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { struct S { private let v: T - public init() { + public init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitPublic_DefaultStruct_InitPrivate_PrivateProperty() { + assertMacro { + """ + @MemberwiseInit(.public) + struct S { + @Init(.private) private let v: T + } + """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2313,15 +2523,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitPublic_DefaultStruct_InitInternal_PrivateProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit(.public) struct S { - @Init(.internal) private let v: T + @Init(.public, ) private let v: T } """ } expansion: { @@ -2329,10 +2535,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { struct S { private let v: T - public init() { + public init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitPublic_DefaultStruct_InitInternal_PrivateProperty() { + assertMacro { + """ + @MemberwiseInit(.public) + struct S { + @Init(.internal) private let v: T + } + """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2344,15 +2564,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitPublic_DefaultStruct_InitPublic_PrivateProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit(.public) struct S { - @Init(.public) private let v: T + @Init(.public, ) private let v: T } """ } expansion: { @@ -2370,23 +2586,37 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } } - func testMemberwiseInitPublic_DefaultStruct_DefaultProperty() { - assertMacro(applyFixIts: false) { + func testMemberwiseInitPublic_DefaultStruct_InitPublic_PrivateProperty() { + assertMacro { """ @MemberwiseInit(.public) struct S { - let v: T + @Init(.public) private let v: T } """ } expansion: { """ struct S { - let v: T + private let v: T - public init() { + public init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitPublic_DefaultStruct_DefaultProperty() { + assertMacro { + """ + @MemberwiseInit(.public) + struct S { + let v: T + } + """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2399,15 +2629,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitPublic_DefaultStruct_InitPrivate_DefaultProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit(.public) struct S { - @Init(.private) let v: T + @Init(.public) let v: T } """ } expansion: { @@ -2415,10 +2641,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { struct S { let v: T - public init() { + public init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitPublic_DefaultStruct_InitPrivate_DefaultProperty() { + assertMacro { + """ + @MemberwiseInit(.public) + struct S { + @Init(.private) let v: T + } + """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2430,15 +2670,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitPublic_DefaultStruct_InitInternal_DefaultProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit(.public) struct S { - @Init(.internal) let v: T + @Init(.public, ) let v: T } """ } expansion: { @@ -2446,10 +2682,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { struct S { let v: T - public init() { + public init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitPublic_DefaultStruct_InitInternal_DefaultProperty() { + assertMacro { + """ + @MemberwiseInit(.public) + struct S { + @Init(.internal) let v: T + } + """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2461,11 +2711,30 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ + } fixes: { + """ + @MemberwiseInit(.public) + struct S { + @Init(.public, ) let v: T + } + """ + } expansion: { + """ + struct S { + let v: T + + public init( + v: T + ) { + self.v = v + } + } + """ } } func testMemberwiseInitPublic_DefaultStruct_InitPublic_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.public) struct S { @@ -2488,7 +2757,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPublic_DefaultStruct_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.public) struct S { @@ -2511,22 +2780,13 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPublic_DefaultStruct_InitPrivate_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.public) struct S { @Init(.private) public let v: T } """ - } expansion: { - """ - struct S { - public let v: T - - public init() { - } - } - """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2538,15 +2798,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitPublic_DefaultStruct_InitInternal_PublicProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit(.public) struct S { - @Init(.internal) public let v: T + @Init(.public, ) public let v: T } """ } expansion: { @@ -2554,10 +2810,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { struct S { public let v: T - public init() { + public init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitPublic_DefaultStruct_InitInternal_PublicProperty() { + assertMacro { + """ + @MemberwiseInit(.public) + struct S { + @Init(.internal) public let v: T + } + """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2569,11 +2839,30 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ + } fixes: { + """ + @MemberwiseInit(.public) + struct S { + @Init(.public, ) public let v: T + } + """ + } expansion: { + """ + struct S { + public let v: T + + public init( + v: T + ) { + self.v = v + } + } + """ } } func testMemberwiseInitPublic_DefaultStruct_InitPublic_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.public) struct S { @@ -2596,7 +2885,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPublic_DefaultStruct_NoProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.public) struct S { @@ -2606,30 +2895,21 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { """ struct S { - public init() { - } + public init() { + } } """ } } func testMemberwiseInitPublic_PublicStruct_PrivateProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.public) public struct S { private let v: T } """ - } expansion: { - """ - public struct S { - private let v: T - - public init() { - } - } - """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2642,15 +2922,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitPublic_PublicStruct_InitPrivate_PrivateProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit(.public) public struct S { - @Init(.private) private let v: T + @Init(.public) private let v: T } """ } expansion: { @@ -2658,10 +2934,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { public struct S { private let v: T - public init() { + public init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitPublic_PublicStruct_InitPrivate_PrivateProperty() { + assertMacro { + """ + @MemberwiseInit(.public) + public struct S { + @Init(.private) private let v: T + } + """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2673,15 +2963,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitPublic_PublicStruct_InitInternal_PrivateProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit(.public) public struct S { - @Init(.internal) private let v: T + @Init(.public, ) private let v: T } """ } expansion: { @@ -2689,10 +2975,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { public struct S { private let v: T - public init() { + public init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitPublic_PublicStruct_InitInternal_PrivateProperty() { + assertMacro { + """ + @MemberwiseInit(.public) + public struct S { + @Init(.internal) private let v: T + } + """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2704,15 +3004,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitPublic_PublicStruct_InitPublic_PrivateProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit(.public) public struct S { - @Init(.public) private let v: T + @Init(.public, ) private let v: T } """ } expansion: { @@ -2730,23 +3026,37 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } } - func testMemberwiseInitPublic_PublicStruct_DefaultProperty() { - assertMacro(applyFixIts: false) { + func testMemberwiseInitPublic_PublicStruct_InitPublic_PrivateProperty() { + assertMacro { """ @MemberwiseInit(.public) public struct S { - let v: T + @Init(.public) private let v: T } """ } expansion: { """ public struct S { - let v: T + private let v: T - public init() { + public init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitPublic_PublicStruct_DefaultProperty() { + assertMacro { + """ + @MemberwiseInit(.public) + public struct S { + let v: T + } + """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2759,15 +3069,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitPublic_PublicStruct_InitPrivate_DefaultProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit(.public) public struct S { - @Init(.private) let v: T + @Init(.public) let v: T } """ } expansion: { @@ -2775,10 +3081,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { public struct S { let v: T - public init() { + public init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitPublic_PublicStruct_InitPrivate_DefaultProperty() { + assertMacro { + """ + @MemberwiseInit(.public) + public struct S { + @Init(.private) let v: T + } + """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2790,15 +3110,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitPublic_PublicStruct_InitInternal_DefaultProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit(.public) public struct S { - @Init(.internal) let v: T + @Init(.public, ) let v: T } """ } expansion: { @@ -2806,10 +3122,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { public struct S { let v: T - public init() { + public init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitPublic_PublicStruct_InitInternal_DefaultProperty() { + assertMacro { + """ + @MemberwiseInit(.public) + public struct S { + @Init(.internal) let v: T + } + """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2821,11 +3151,30 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ + } fixes: { + """ + @MemberwiseInit(.public) + public struct S { + @Init(.public, ) let v: T + } + """ + } expansion: { + """ + public struct S { + let v: T + + public init( + v: T + ) { + self.v = v + } + } + """ } } func testMemberwiseInitPublic_PublicStruct_InitPublic_DefaultProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.public) public struct S { @@ -2848,7 +3197,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPublic_PublicStruct_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.public) public struct S { @@ -2871,22 +3220,13 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPublic_PublicStruct_InitPrivate_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.public) public struct S { @Init(.private) public let v: T } """ - } expansion: { - """ - public struct S { - public let v: T - - public init() { - } - } - """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2898,15 +3238,11 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ - } - } - - func testMemberwiseInitPublic_PublicStruct_InitInternal_PublicProperty() { - assertMacro(applyFixIts: false) { + } fixes: { """ @MemberwiseInit(.public) public struct S { - @Init(.internal) public let v: T + @Init(.public, ) public let v: T } """ } expansion: { @@ -2914,10 +3250,24 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { public struct S { public let v: T - public init() { + public init( + v: T + ) { + self.v = v } } """ + } + } + + func testMemberwiseInitPublic_PublicStruct_InitInternal_PublicProperty() { + assertMacro { + """ + @MemberwiseInit(.public) + public struct S { + @Init(.internal) public let v: T + } + """ } diagnostics: { """ @MemberwiseInit(.public) @@ -2929,11 +3279,30 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ + } fixes: { + """ + @MemberwiseInit(.public) + public struct S { + @Init(.public, ) public let v: T + } + """ + } expansion: { + """ + public struct S { + public let v: T + + public init( + v: T + ) { + self.v = v + } + } + """ } } func testMemberwiseInitPublic_PublicStruct_InitPublic_PublicProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.public) public struct S { @@ -2956,7 +3325,7 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { } func testMemberwiseInitPublic_PublicStruct_NoProperty() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit(.public) public struct S { @@ -2966,8 +3335,8 @@ final class MemberwiseInitAccessLevelTests: XCTestCase { """ public struct S { - public init() { - } + public init() { + } } """ } diff --git a/Tests/MemberwiseInitTests/MemberwiseInitDeprecationTests.swift b/Tests/MemberwiseInitTests/MemberwiseInitDeprecationTests.swift deleted file mode 100644 index 3d43972..0000000 --- a/Tests/MemberwiseInitTests/MemberwiseInitDeprecationTests.swift +++ /dev/null @@ -1,204 +0,0 @@ -import MacroTesting -import MemberwiseInitMacros -import SwiftSyntaxMacros -import SwiftSyntaxMacrosTestSupport -import XCTest - -final class MemberwiseInitDeprecationTests: XCTestCase { - override func invokeTest() { - // NB: Waiting for swift-macro-testing PR to support explicit indentationWidth: https://github.com/pointfreeco/swift-macro-testing/pull/8 - withMacroTesting( - //indentationWidth: .spaces(2), - macros: [ - "MemberwiseInit": MemberwiseInitMacro.self, - "Init": InitMacro.self, - ] - ) { - super.invokeTest() - } - } - - // Deprecated; remove in 1.0 - func testDotEscaping1() { - assertMacro { - """ - @MemberwiseInit - struct S { - @Init(.escaping) var value: T - } - """ - } expansion: { - """ - struct S { - var value: T - - internal init( - value: @escaping T - ) { - self.value = value - } - } - """ - } diagnostics: { - """ - @MemberwiseInit - struct S { - @Init(.escaping) var value: T - ┬──────── - ╰─ ⚠️ @Init(.escaping) is deprecated - ✏️ Replace '@Init(.escaping)' with '@Init(escaping: true)' - } - """ - } fixes: { - """ - @Init(.escaping) var value: T - ┬──────── - ╰─ ⚠️ @Init(.escaping) is deprecated - - ✏️ Replace '@Init(.escaping)' with '@Init(escaping: true)' - @MemberwiseInit - struct S { - @Init(escaping: true) var value: T - } - """ - } - } - - // Deprecated; remove in 1.0 - func testDotEscaping2() { - assertMacro { - """ - @MemberwiseInit - struct S { - @Init(.public, .escaping) var value: T - } - """ - } expansion: { - """ - struct S { - var value: T - - internal init( - value: @escaping T - ) { - self.value = value - } - } - """ - } diagnostics: { - """ - @MemberwiseInit - struct S { - @Init(.public, .escaping) var value: T - ┬───────────────── - ╰─ ⚠️ @Init(.escaping) is deprecated - ✏️ Replace '@Init(.escaping)' with '@Init(escaping: true)' - } - """ - } fixes: { - """ - @Init(.public, .escaping) var value: T - ┬───────────────── - ╰─ ⚠️ @Init(.escaping) is deprecated - - ✏️ Replace '@Init(.escaping)' with '@Init(escaping: true)' - @MemberwiseInit - struct S { - @Init(.public, escaping: true) var value: T - } - """ - } - } - - // Deprecated; remove in 1.0 - func testDotEscaping3() { - assertMacro { - """ - @MemberwiseInit - struct S { - @Init(.escaping, label: "_") var value: T - } - """ - } expansion: { - """ - struct S { - var value: T - - internal init( - _ value: @escaping T - ) { - self.value = value - } - } - """ - } diagnostics: { - """ - @MemberwiseInit - struct S { - @Init(.escaping, label: "_") var value: T - ┬──────────────────── - ╰─ ⚠️ @Init(.escaping) is deprecated - ✏️ Replace '@Init(.escaping)' with '@Init(escaping: true)' - } - """ - } fixes: { - """ - @Init(.escaping, label: "_") var value: T - ┬──────────────────── - ╰─ ⚠️ @Init(.escaping) is deprecated - - ✏️ Replace '@Init(.escaping)' with '@Init(escaping: true)' - @MemberwiseInit - struct S { - @Init(escaping: true, label: "_") var value: T - } - """ - } - } - - // Deprecated; remove in 1.0 - func testDotEscaping4() { - assertMacro { - """ - @MemberwiseInit - struct S { - @Init(.public, .escaping, label: "_") var value: T - } - """ - } expansion: { - """ - struct S { - var value: T - - internal init( - _ value: @escaping T - ) { - self.value = value - } - } - """ - } diagnostics: { - """ - @MemberwiseInit - struct S { - @Init(.public, .escaping, label: "_") var value: T - ┬───────────────────────────── - ╰─ ⚠️ @Init(.escaping) is deprecated - ✏️ Replace '@Init(.escaping)' with '@Init(escaping: true)' - } - """ - } fixes: { - """ - @Init(.public, .escaping, label: "_") var value: T - ┬───────────────────────────── - ╰─ ⚠️ @Init(.escaping) is deprecated - - ✏️ Replace '@Init(.escaping)' with '@Init(escaping: true)' - @MemberwiseInit - struct S { - @Init(.public, escaping: true, label: "_") var value: T - } - """ - } - } -} diff --git a/Tests/MemberwiseInitTests/MemberwiseInitInferredTypeTests.swift b/Tests/MemberwiseInitTests/MemberwiseInitInferredTypeTests.swift index 4bf6d69..2c821ac 100644 --- a/Tests/MemberwiseInitTests/MemberwiseInitInferredTypeTests.swift +++ b/Tests/MemberwiseInitTests/MemberwiseInitInferredTypeTests.swift @@ -6,6 +6,8 @@ import XCTest final class MemberwiseInitInferredTypeTests: XCTestCase { override func invokeTest() { withMacroTesting( + indentationWidth: .spaces(2), + record: .missing, macros: [ "MemberwiseInit": MemberwiseInitMacro.self, "Init": InitMacro.self, @@ -26,16 +28,6 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { var stepsToday = number } """ - } expansion: { - """ - let number = 0 - public struct Pedometer { - var stepsToday = number - - internal init() { - } - } - """ } diagnostics: { """ let number = 0 @@ -46,7 +38,7 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { ╰─ 🛑 @MemberwiseInit requires a type annotation. } """ - } + } } func testBooleanLiterals() { @@ -438,15 +430,6 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { var array = [1 as Int, 2 as Double] } """## - } expansion: { - """ - struct S { - var array = [1 as Int, 2 as Double] - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -456,7 +439,7 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { ╰─ 🛑 @MemberwiseInit requires a type annotation. } """ - } + } } func testNestedArrayLiteralPromotedToDouble() { @@ -491,16 +474,6 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { var array = [1, number, 3] } """## - } expansion: { - """ - let number = 2 - public struct S { - var array = [1, number, 3] - - internal init() { - } - } - """ } diagnostics: { """ let number = 2 @@ -511,75 +484,43 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { ╰─ 🛑 @MemberwiseInit requires a type annotation. } """ - } + } } // NB: Xcode and SwiftSyntax prefer `[T] ()`, but swift-format prefers `[T]()`. // The node is copied unchanged from the property declaration and SwiftSyntax is adding trivia. func testArrayWithExplicitTypeInitializer() { - #if canImport(SwiftSyntax600) - assertMacro { - ##""" - @MemberwiseInit - public struct S { - var array = [String]() - } - """## - } expansion: { - """ - public struct S { - var array = [String]() - - internal init( - array: [String] = [String]() - ) { - self.array = array - } - } - """ - } - #else - assertMacro { - ##""" - @MemberwiseInit - public struct S { - var array = [String]() - } - """## - } expansion: { - """ - public struct S { - var array = [String]() - - internal init( - array: [String] = [String] () - ) { - self.array = array - } - } - """ - } - #endif - } - - // FIXME: Diagnostic is excessive on already invalid syntax, but we can only detect special cases. - func testRaggedLiteralArray_FailsWithDiagnostic() { assertMacro { ##""" @MemberwiseInit public struct S { - var array = [1, "foo", 3] + var array = [String]() } """## } expansion: { """ public struct S { - var array = [1, "foo", 3] + var array = [String]() - internal init() { + internal init( + array: [String] = [String]() + ) { + self.array = array } } """ + } + } + + // FIXME: Diagnostic is excessive on already invalid syntax, but we can only detect special cases. + func testRaggedLiteralArray_FailsWithDiagnostic() { + assertMacro { + ##""" + @MemberwiseInit + public struct S { + var array = [1, "foo", 3] + } + """## } diagnostics: { """ @MemberwiseInit @@ -589,7 +530,7 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { ╰─ 🛑 @MemberwiseInit requires a type annotation. } """ - } + } } func testRaggedLiteralArrayWithAsAny() { @@ -650,17 +591,6 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { var dictionary = ["key1": foo, "key2": bar] } """## - } expansion: { - """ - let foo = "foo" - let bar = "bar" - public struct S { - var dictionary = ["key1": foo, "key2": bar] - - internal init() { - } - } - """ } diagnostics: { """ let foo = "foo" @@ -672,7 +602,7 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { ╰─ 🛑 @MemberwiseInit requires a type annotation. } """ - } + } } func testDictionary_NonLiteralKeysLiteralValues_FailsWithDiagnostic() { @@ -684,16 +614,6 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { var dictionary = ["foo": 1, bar: 2] } """## - } expansion: { - """ - let bar = "bar" - public struct S { - var dictionary = ["foo": 1, bar: 2] - - internal init() { - } - } - """ } diagnostics: { """ let bar = "bar" @@ -704,7 +624,7 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { ╰─ 🛑 @MemberwiseInit requires a type annotation. } """ - } + } } func testDictionaryLiteralPromotedToDoubleDouble() { @@ -763,15 +683,6 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { var array = [1 as Int: 2 as Double, 1.0: 2] } """## - } expansion: { - """ - struct S { - var array = [1 as Int: 2 as Double, 1.0: 2] - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -781,56 +692,33 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { ╰─ 🛑 @MemberwiseInit requires a type annotation. } """ - } + } } // NB: Xcode and SwiftSyntax prefer `[K : V] ()`, but swift-format prefers `[K : V]()`. // The node is copied unchanged from the property declaration and SwiftSyntax is adding trivia. // I tried detaching the syntax node, to no effect. func testDictionaryWithExplicitTypeInitializer() { - #if canImport(SwiftSyntax600) - assertMacro { - ##""" - @MemberwiseInit - public struct S { - var dictionary = [String: Int]() - } - """## - } expansion: { - """ - public struct S { - var dictionary = [String: Int]() - - internal init( - dictionary: [String: Int] = [String: Int]() - ) { - self.dictionary = dictionary - } - } - """ - } - #else - assertMacro { - ##""" - @MemberwiseInit - public struct S { - var dictionary = [String: Int]() - } - """## - } expansion: { - """ - public struct S { - var dictionary = [String: Int]() - - internal init( - dictionary: [String: Int] = [String: Int] () - ) { - self.dictionary = dictionary - } + assertMacro { + ##""" + @MemberwiseInit + public struct S { + var dictionary = [String: Int]() + } + """## + } expansion: { + """ + public struct S { + var dictionary = [String: Int]() + + internal init( + dictionary: [String: Int] = [String: Int]() + ) { + self.dictionary = dictionary } - """ } - #endif + """ + } } // FIXME: Diagnostic is excessive on already invalid syntax. @@ -843,15 +731,6 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { var dictionary = ["foo": 1, 3: "bar"] } """## - } expansion: { - """ - public struct S { - var dictionary = ["foo": 1, 3: "bar"] - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -861,7 +740,7 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { ╰─ 🛑 @MemberwiseInit requires a type annotation. } """ - } + } } func testRaggedLiteralDictionaryWithAsAnyHashableAny() { @@ -921,16 +800,6 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { var tuple = (1, name, true) } """## - } expansion: { - """ - let name = "Blob" - public struct S { - var tuple = (1, name, true) - - internal init() { - } - } - """ } diagnostics: { """ let name = "Blob" @@ -941,7 +810,7 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { ╰─ 🛑 @MemberwiseInit requires a type annotation. } """ - } + } } func testNonLiteralTupleAs() { @@ -1154,16 +1023,6 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { var range = start...5 } """## - } expansion: { - """ - let start = 0 - public struct S { - var range = start...5 - - internal init() { - } - } - """ } diagnostics: { """ let start = 0 @@ -1174,7 +1033,7 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { ╰─ 🛑 @MemberwiseInit requires a type annotation. } """ - } + } } func testBitwiseInfixOpertors() { @@ -1225,15 +1084,6 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { var bitwiseAnd = 0b1010 & 1.0 } """## - } expansion: { - """ - public struct S { - var bitwiseAnd = 0b1010 & 1.0 - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -1243,7 +1093,7 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { ╰─ 🛑 @MemberwiseInit requires a type annotation. } """ - } + } } func testBooleanInfixOperators() { @@ -1345,15 +1195,6 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { var modulo = 10 % 3.0 } """## - } expansion: { - """ - public struct S { - var modulo = 10 % 3.0 - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -1363,7 +1204,7 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { ╰─ 🛑 @MemberwiseInit requires a type annotation. } """ - } + } } // NB: Unannotated floating-point literals default to `Double` in Swift. @@ -1648,3 +1489,4 @@ final class MemberwiseInitInferredTypeTests: XCTestCase { } } + diff --git a/Tests/MemberwiseInitTests/MemberwiseInitTests.swift b/Tests/MemberwiseInitTests/MemberwiseInitTests.swift index 7a61e6b..effd101 100644 --- a/Tests/MemberwiseInitTests/MemberwiseInitTests.swift +++ b/Tests/MemberwiseInitTests/MemberwiseInitTests.swift @@ -8,9 +8,9 @@ import XCTest final class MemberwiseInitTests: XCTestCase { override func invokeTest() { - // NB: Waiting for swift-macro-testing PR to support explicit indentationWidth: https://github.com/pointfreeco/swift-macro-testing/pull/8 withMacroTesting( - //indentationWidth: .spaces(2), + indentationWidth: .spaces(2), + record: .missing, macros: [ "MemberwiseInit": MemberwiseInitMacro.self, "Init": InitMacro.self, @@ -34,8 +34,8 @@ final class MemberwiseInitTests: XCTestCase { """ struct Person { - internal init() { - } + internal init() { + } } """ } @@ -53,8 +53,8 @@ final class MemberwiseInitTests: XCTestCase { """ public struct Person { - internal init() { - } + internal init() { + } } """ } @@ -137,15 +137,6 @@ final class MemberwiseInitTests: XCTestCase { @Init let name = "Earth" } """ - } expansion: { - """ - struct Earth { - let name = "Earth" - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -159,20 +150,18 @@ final class MemberwiseInitTests: XCTestCase { """ } fixes: { """ - @Init let name = "Earth" - ┬──── - ╰─ ⚠️ @Init can't be applied to already initialized constant - - ✏️ Remove '@Init' @MemberwiseInit struct Earth { let name = "Earth" } - - ✏️ Remove '= "Earth"' - @MemberwiseInit + """ + } expansion: { + """ struct Earth { - @Init let name: String + let name = "Earth" + + internal init() { + } } """ } @@ -486,15 +475,6 @@ final class MemberwiseInitTests: XCTestCase { let (x, y): (Int, Int) } """ - } expansion: { - """ - struct Point2D { - let (x, y): (Int, Int) - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -504,7 +484,7 @@ final class MemberwiseInitTests: XCTestCase { ╰─ 🛑 @MemberwiseInit does not support tuple destructuring for property declarations. Use multiple declarations instead. } """ - } + } } func testVarDestructuredTupleWithInitializer_FailsNotSupported() { @@ -515,15 +495,6 @@ final class MemberwiseInitTests: XCTestCase { var (x, y): (Int, Int) = (0, 0) } """ - } expansion: { - """ - struct Point2D { - var (x, y): (Int, Int) = (0, 0) - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -533,7 +504,7 @@ final class MemberwiseInitTests: XCTestCase { ╰─ 🛑 @MemberwiseInit does not support tuple destructuring for property declarations. Use multiple declarations instead. } """ - } + } } // MARK: - Test enum and extension @@ -869,133 +840,49 @@ final class MemberwiseInitTests: XCTestCase { } func testInitAndInit_FailsWithDiagnostic() { - #if canImport(SwiftSyntax600) - assertMacro { - """ - @MemberwiseInit - struct S { - @Init @Init - let value: T - } - """ - } expansion: { - """ - struct S { - - let value: T - - internal init() { - } - } - """ - } diagnostics: { - """ - @MemberwiseInit - struct S { - @Init @Init - ┬──── - ╰─ 🛑 Multiple @Init configurations are not supported by @MemberwiseInit - let value: T - } - """ + assertMacro { + """ + @MemberwiseInit + struct S { + @Init @Init + let value: T } - #else - assertMacro { - """ - @MemberwiseInit - struct S { - @Init @Init - let value: T - } - """ - } expansion: { - """ - struct S { - let value: T - - internal init() { - } - } - """ - } diagnostics: { - """ - @MemberwiseInit - struct S { - @Init @Init - ┬──── - ╰─ 🛑 Multiple @Init configurations are not supported by @MemberwiseInit - let value: T - } - """ + """ + } diagnostics: { + """ + @MemberwiseInit + struct S { + @Init @Init + ┬──── + ╰─ 🛑 Multiple @Init configurations are not supported by @MemberwiseInit + let value: T } - #endif + """ + } } func testInitInitWrapperInitRaw_FailsWithDiagnostics() { - #if canImport(SwiftSyntax600) - assertMacro { - """ - @MemberwiseInit - struct S { - @Init @InitWrapper @InitRaw - let value: T - } - """ - } expansion: { - """ - struct S { - @InitWrapper @InitRaw - let value: T - - internal init() { - } - } - """ - } diagnostics: { - """ - @MemberwiseInit - struct S { - @Init @InitWrapper @InitRaw - ┬─────── - │ ╰─ 🛑 Multiple @Init configurations are not supported by @MemberwiseInit - ┬─────────── - ╰─ 🛑 Multiple @Init configurations are not supported by @MemberwiseInit - let value: T - } - """ - } - #else - assertMacro { - """ - @MemberwiseInit - struct S { - @Init @InitWrapper @InitRaw - let value: T - } - """ - } expansion: { - """ - struct S {@InitWrapper @InitRaw - let value: T - - internal init() { - } - } - """ - } diagnostics: { - """ - @MemberwiseInit - struct S { - @Init @InitWrapper @InitRaw - ┬─────── - │ ╰─ 🛑 Multiple @Init configurations are not supported by @MemberwiseInit - ┬─────────── - ╰─ 🛑 Multiple @Init configurations are not supported by @MemberwiseInit - let value: T - } - """ + assertMacro { + """ + @MemberwiseInit + struct S { + @Init @InitWrapper @InitRaw + let value: T + } + """ + } diagnostics: { + """ + @MemberwiseInit + struct S { + @Init @InitWrapper @InitRaw + ┬─────── + │ ╰─ 🛑 Multiple @Init configurations are not supported by @MemberwiseInit + ┬─────────── + ╰─ 🛑 Multiple @Init configurations are not supported by @MemberwiseInit + let value: T } - #endif + """ + } } // MARK: - Test invalid syntax @@ -1077,38 +964,38 @@ final class MemberwiseInitTests: XCTestCase { """ private struct Person { - internal init() { - } + internal init() { + } } fileprivate struct Person { - internal init() { - } + internal init() { + } } struct Person { - internal init() { - } + internal init() { + } } internal struct Person { - internal init() { - } + internal init() { + } } package struct Person { - internal init() { - } + internal init() { + } } public struct Person { - internal init() { - } + internal init() { + } } open class Person { - internal init() { - } + internal init() { + } } """ } @@ -1117,7 +1004,7 @@ final class MemberwiseInitTests: XCTestCase { // NB: This is almost covered by the exhaustive AccessLevelTests. This test touches on all the // access levels (instead of a meaningful few). func testDefaultInitAccessLevels_FailsWithDiagnotics() { - assertMacro(applyFixIts: false) { + assertMacro { """ @MemberwiseInit private struct Person { @@ -1139,33 +1026,6 @@ final class MemberwiseInitTests: XCTestCase { fileprivate let name: String } """ - } expansion: { - """ - private struct Person { - private let name: String - - internal init() { - } - } - fileprivate struct Person { - private let name: String - - internal init() { - } - } - struct Person { - fileprivate let name: String - - internal init() { - } - } - internal struct Person { - fileprivate let name: String - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -1208,6 +1068,67 @@ final class MemberwiseInitTests: XCTestCase { ✏️ Add '@Init(.ignore)' and an initializer } """ + } fixes: { + """ + @MemberwiseInit + private struct Person { + @Init(.internal) private let name: String + } + + @MemberwiseInit + fileprivate struct Person { + @Init(.internal) private let name: String + } + + @MemberwiseInit + struct Person { + @Init(.internal) fileprivate let name: String + } + + @MemberwiseInit + internal struct Person { + @Init(.internal) fileprivate let name: String + } + """ + } expansion: { + """ + private struct Person { + private let name: String + + internal init( + name: String + ) { + self.name = name + } + } + fileprivate struct Person { + private let name: String + + internal init( + name: String + ) { + self.name = name + } + } + struct Person { + fileprivate let name: String + + internal init( + name: String + ) { + self.name = name + } + } + internal struct Person { + fileprivate let name: String + + internal init( + name: String + ) { + self.name = name + } + } + """ } } @@ -1273,19 +1194,6 @@ final class MemberwiseInitTests: XCTestCase { var lastName: String } """ - } expansion: { - """ - public struct Person { - public var firstName = "Foo" - var lastName: String - - public init( - firstName: String = "Foo" - ) { - self.firstName = firstName - } - } - """ } diagnostics: { """ @MemberwiseInit(.public) @@ -1301,29 +1209,25 @@ final class MemberwiseInitTests: XCTestCase { """ } fixes: { """ - var lastName: String - ┬─────────────────── - ╰─ 🛑 @MemberwiseInit(.public) would leak access to 'internal' property - - ✏️ Add '@Init(.public)' @MemberwiseInit(.public) public struct Person { public var firstName = "Foo" @Init(.public) var lastName: String } - - ✏️ Add 'public' access level - @MemberwiseInit(.public) + """ + } expansion: { + """ public struct Person { public var firstName = "Foo" - public var lastName: String - } + var lastName: String - ✏️ Add '@Init(.ignore)' and an initializer - @MemberwiseInit(.public) - public struct Person { - public var firstName = "Foo" - @Init(.ignore) var lastName: String = <#value#> + public init( + firstName: String = "Foo", + lastName: String + ) { + self.firstName = firstName + self.lastName = lastName + } } """ } @@ -1361,19 +1265,6 @@ final class MemberwiseInitTests: XCTestCase { fileprivate let lastName: String } """ - } expansion: { - """ - public struct Person { - public let firstName: String - fileprivate let lastName: String - - internal init( - firstName: String - ) { - self.firstName = firstName - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -1389,35 +1280,31 @@ final class MemberwiseInitTests: XCTestCase { """ } fixes: { """ - fileprivate let lastName: String - ┬────────── - ╰─ 🛑 @MemberwiseInit(.internal) would leak access to 'fileprivate' property - - ✏️ Add '@Init(.internal)' @MemberwiseInit public struct Person { public let firstName: String @Init(.internal) fileprivate let lastName: String } - - ✏️ Replace 'fileprivate' access with 'internal' - @MemberwiseInit - public struct Person { - public let firstName: String - internal let lastName: String - } - - ✏️ Add '@Init(.ignore)' and an initializer - @MemberwiseInit + """ + } expansion: { + """ public struct Person { public let firstName: String - @Init(.ignore) fileprivate let lastName: String = <#value#> - } - """ - } - } + fileprivate let lastName: String - // TODO: rename to _FailsWithDiagnostic + internal init( + firstName: String, + lastName: String + ) { + self.firstName = firstName + self.lastName = lastName + } + } + """ + } + } + + // TODO: rename to _FailsWithDiagnostic func testPublicStruct_PublicAndPrivateProperty_PrivateInit() { assertMacro { """ @@ -1427,19 +1314,6 @@ final class MemberwiseInitTests: XCTestCase { private let lastName: String } """ - } expansion: { - """ - public struct Person { - public let firstName: String - private let lastName: String - - internal init( - firstName: String - ) { - self.firstName = firstName - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -1455,29 +1329,25 @@ final class MemberwiseInitTests: XCTestCase { """ } fixes: { """ - private let lastName: String - ┬────── - ╰─ 🛑 @MemberwiseInit(.internal) would leak access to 'private' property - - ✏️ Add '@Init(.internal)' @MemberwiseInit public struct Person { public let firstName: String @Init(.internal) private let lastName: String } - - ✏️ Replace 'private' access with 'internal' - @MemberwiseInit + """ + } expansion: { + """ public struct Person { public let firstName: String - internal let lastName: String - } + private let lastName: String - ✏️ Add '@Init(.ignore)' and an initializer - @MemberwiseInit - public struct Person { - public let firstName: String - @Init(.ignore) private let lastName: String = <#value#> + internal init( + firstName: String, + lastName: String + ) { + self.firstName = firstName + self.lastName = lastName + } } """ } @@ -1492,19 +1362,6 @@ final class MemberwiseInitTests: XCTestCase { private let lastName: String } """ - } expansion: { - """ - struct Person { - public let firstName: String - private let lastName: String - - internal init( - firstName: String - ) { - self.firstName = firstName - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -1520,29 +1377,25 @@ final class MemberwiseInitTests: XCTestCase { """ } fixes: { """ - private let lastName: String - ┬────── - ╰─ 🛑 @MemberwiseInit(.internal) would leak access to 'private' property - - ✏️ Add '@Init(.internal)' @MemberwiseInit struct Person { public let firstName: String @Init(.internal) private let lastName: String } - - ✏️ Replace 'private' access with 'internal' - @MemberwiseInit + """ + } expansion: { + """ struct Person { public let firstName: String - internal let lastName: String - } + private let lastName: String - ✏️ Add '@Init(.ignore)' and an initializer - @MemberwiseInit - struct Person { - public let firstName: String - @Init(.ignore) private let lastName: String = <#value#> + internal init( + firstName: String, + lastName: String + ) { + self.firstName = firstName + self.lastName = lastName + } } """ } @@ -1556,15 +1409,6 @@ final class MemberwiseInitTests: XCTestCase { @Init let v: T } """ - } expansion: { - """ - public struct S { - let v: T - - public init() { - } - } - """ } diagnostics: { """ @MemberwiseInit(.public) @@ -1579,26 +1423,21 @@ final class MemberwiseInitTests: XCTestCase { """ } fixes: { """ - @Init let v: T - ┬───────────── - ╰─ 🛑 @MemberwiseInit(.public) would leak access to 'internal' property - - ✏️ Add '@Init(.public)' @MemberwiseInit(.public) public struct S { @Init(.public) let v: T } - - ✏️ Add 'public' access level - @MemberwiseInit(.public) + """ + } expansion: { + """ public struct S { - @Init public let v: T - } + let v: T - ✏️ Add '@Init(.ignore)' and an initializer - @MemberwiseInit(.public) - public struct S { - @Init(.ignore) let v: T = <#value#> + public init( + v: T + ) { + self.v = v + } } """ } @@ -1612,15 +1451,6 @@ final class MemberwiseInitTests: XCTestCase { @Init private let v: T } """ - } expansion: { - """ - public struct S { - private let v: T - - public init() { - } - } - """ } diagnostics: { """ @MemberwiseInit(.public) @@ -1635,26 +1465,21 @@ final class MemberwiseInitTests: XCTestCase { """ } fixes: { """ - @Init private let v: T - ┬────── - ╰─ 🛑 @MemberwiseInit(.public) would leak access to 'private' property - - ✏️ Add '@Init(.public)' @MemberwiseInit(.public) public struct S { @Init(.public) private let v: T } - - ✏️ Replace 'private' access with 'public' - @MemberwiseInit(.public) + """ + } expansion: { + """ public struct S { - @Init public let v: T - } + private let v: T - ✏️ Add '@Init(.ignore)' and an initializer - @MemberwiseInit(.public) - public struct S { - @Init(.ignore) private let v: T = <#value#> + public init( + v: T + ) { + self.v = v + } } """ } @@ -1668,15 +1493,6 @@ final class MemberwiseInitTests: XCTestCase { @Init(.private, label: "_") let v: T } """ - } expansion: { - """ - public struct S { - let v: T - - public init() { - } - } - """ } diagnostics: { """ @MemberwiseInit(.public) @@ -1690,20 +1506,21 @@ final class MemberwiseInitTests: XCTestCase { """ } fixes: { """ - @Init(.private, label: "_") let v: T - ┬─────── - ╰─ 🛑 @MemberwiseInit(.public) would leak access to 'private' property - - ✏️ Add '@Init(.public)' @MemberwiseInit(.public) public struct S { @Init(.public, label: "_") let v: T } - - ✏️ Add '@Init(.ignore)' and an initializer - @MemberwiseInit(.public) + """ + } expansion: { + """ public struct S { - @Init(.ignore) let v: T = <#value#> + let v: T + + public init( + _ v: T + ) { + self.v = v + } } """ } @@ -1717,15 +1534,6 @@ final class MemberwiseInitTests: XCTestCase { @Init(label: "_") private let v: T } """ - } expansion: { - """ - public struct S { - private let v: T - - public init() { - } - } - """ } diagnostics: { """ @MemberwiseInit(.public) @@ -1740,26 +1548,21 @@ final class MemberwiseInitTests: XCTestCase { """ } fixes: { """ - @Init(label: "_") private let v: T - ┬────── - ╰─ 🛑 @MemberwiseInit(.public) would leak access to 'private' property - - ✏️ Add '@Init(.public)' @MemberwiseInit(.public) public struct S { @Init(.public, label: "_") private let v: T } - - ✏️ Replace 'private' access with 'public' - @MemberwiseInit(.public) + """ + } expansion: { + """ public struct S { - @Init(label: "_") public let v: T - } + private let v: T - ✏️ Add '@Init(.ignore)' and an initializer - @MemberwiseInit(.public) - public struct S { - @Init(.ignore) private let v: T = <#value#> + public init( + _ v: T + ) { + self.v = v + } } """ } @@ -1817,8 +1620,8 @@ final class MemberwiseInitTests: XCTestCase { """ public final class Person { - internal init() { - } + internal init() { + } } """ } @@ -1855,15 +1658,6 @@ final class MemberwiseInitTests: XCTestCase { public private(set) var stepsToday: Int } """ - } expansion: { - """ - struct Pedometer { - public private(set) var stepsToday: Int - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -1878,26 +1672,21 @@ final class MemberwiseInitTests: XCTestCase { """ } fixes: { """ - public private(set) var stepsToday: Int - ┬────────────────── - ╰─ 🛑 @MemberwiseInit(.internal) would leak access to 'private' property - - ✏️ Add '@Init(.internal)' @MemberwiseInit struct Pedometer { @Init(.internal) public private(set) var stepsToday: Int } - - ✏️ Replace 'public private(set)' access with 'internal' - @MemberwiseInit + """ + } expansion: { + """ struct Pedometer { - public internal(set) var stepsToday: Int - } + public private(set) var stepsToday: Int - ✏️ Add '@Init(.ignore)' and an initializer - @MemberwiseInit - struct Pedometer { - @Init(.ignore) public private(set) var stepsToday: Int = <#value#> + internal init( + stepsToday: Int + ) { + self.stepsToday = stepsToday + } } """ } @@ -1911,15 +1700,6 @@ final class MemberwiseInitTests: XCTestCase { private var stepsToday: Int = 0 } """ - } expansion: { - """ - struct Pedometer { - private var stepsToday: Int = 0 - - public init() { - } - } - """ } diagnostics: { """ @MemberwiseInit(.public) @@ -1934,26 +1714,21 @@ final class MemberwiseInitTests: XCTestCase { """ } fixes: { """ - private var stepsToday: Int = 0 - ┬────── - ╰─ 🛑 @MemberwiseInit(.public) would leak access to 'private' property - - ✏️ Add '@Init(.public)' @MemberwiseInit(.public) struct Pedometer { @Init(.public) private var stepsToday: Int = 0 } - - ✏️ Replace 'private' access with 'public' - @MemberwiseInit(.public) + """ + } expansion: { + """ struct Pedometer { - public var stepsToday: Int = 0 - } + private var stepsToday: Int = 0 - ✏️ Add '@Init(.ignore)' - @MemberwiseInit(.public) - struct Pedometer { - @Init(.ignore) private var stepsToday: Int = 0 + public init( + stepsToday: Int = 0 + ) { + self.stepsToday = stepsToday + } } """ } @@ -1969,17 +1744,6 @@ final class MemberwiseInitTests: XCTestCase { } } """ - } expansion: { - """ - struct S { - private struct T { - let v: Int - - internal init() { - } - } - } - """ } diagnostics: { """ struct S { @@ -1996,31 +1760,24 @@ final class MemberwiseInitTests: XCTestCase { """ } fixes: { """ - let v: Int - ┬───────── - ╰─ 🛑 @MemberwiseInit(.internal) would leak access to 'private' property - - ✏️ Add '@Init(.internal)' struct S { @MemberwiseInit(.internal) private struct T { @Init(.internal) let v: Int } } - - ✏️ Add 'internal' access level + """ + } expansion: { + """ struct S { - @MemberwiseInit(.internal) private struct T { - internal let v: Int - } - } + let v: Int - ✏️ Add '@Init(.ignore)' and an initializer - struct S { - @MemberwiseInit(.internal) - private struct T { - @Init(.ignore) let v: Int = <#value#> + internal init( + v: Int + ) { + self.v = v + } } } """ @@ -2035,15 +1792,6 @@ final class MemberwiseInitTests: XCTestCase { private var x, y: Int } """ - } expansion: { - """ - public struct S { - private var x, y: Int - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -2058,29 +1806,12 @@ final class MemberwiseInitTests: XCTestCase { """ } fixes: { """ - private var x, y: Int - ┬────── - ╰─ 🛑 @MemberwiseInit(.internal) would leak access to 'private' property - - ✏️ Add '@Init(.internal)' @MemberwiseInit public struct S { @Init(.internal) private var x, y: Int } - - ✏️ Replace 'private' access with 'internal' - @MemberwiseInit - public struct S { - internal var x, y: Int - } - - ✏️ Add '@Init(.ignore)' and an initializer - @MemberwiseInit - public struct S { - @Init(.ignore) private var x = <#value#>, y: Int = <#value#> - } """ - } + } } // MARK: - Test macro parameters @@ -2114,186 +1845,69 @@ final class MemberwiseInitTests: XCTestCase { // TODO: regress MemberwiseInit to match swift-syntax 5.10 limitation func testCustomInitEscapingWithMultipleBindings() { - #if canImport(SwiftSyntax510) - assertMacro { - """ - @MemberwiseInit - struct S { - @Init(escaping: true) - let v, r: T - } - """ - } expansion: { - """ - struct S { - let v, r: T - - internal init( - v: @escaping T, - r: @escaping T - ) { - self.v = v - self.r = r - } - } - """ - } diagnostics: { - """ - @MemberwiseInit - struct S { - @Init(escaping: true) - ┬──────────────────── - ╰─ 🛑 peer macro can only be applied to a single variable - let v, r: T - } - """ + assertMacro { + """ + @MemberwiseInit + struct S { + @Init(escaping: true) + let v, r: T } - #elseif canImport(SwfitSyntax509) - assertMacro { - """ - @MemberwiseInit - struct S { - @Init(escaping: true) - let v, r: T - } - """ - } expansion: { - """ - struct S { - let v, r: T - - internal init( - v: @escaping T, - r: @escaping T - ) { - self.v = v - self.r = r - } - } - """ + """ + } diagnostics: { + """ + @MemberwiseInit + struct S { + @Init(escaping: true) + ┬──────────────────── + ╰─ 🛑 peer macro can only be applied to a single variable + let v, r: T } - #endif + """ + } } // TODO: Consider regressing MemberwiseInit to match swift-syntax 5.10 limitation (or leave redundant error, but that has a fix-it) func testCustomLabelWithMultipleBindings_FailsWithDiagnostic() { - #if canImport(SwiftSyntax510) - assertMacro { - """ - @MemberwiseInit(.public) - public struct Person { - @Init(label: "with") public let firstName, lastName: String - } - """ - } expansion: { - """ - public struct Person { - public let firstName, lastName: String - - public init() { - } - } - """ - } diagnostics: { - """ - @MemberwiseInit(.public) - public struct Person { - @Init(label: "with") public let firstName, lastName: String - ┬──────────── - │ ╰─ 🛑 Custom 'label' can't be applied to multiple bindings - ┬─────────────────── - ╰─ 🛑 peer macro can only be applied to a single variable - } - """ - } - #elseif canImport(SwfitSyntax509) - assertMacro { - """ - @MemberwiseInit(.public) - public struct Person { - @Init(label: "with") public let firstName, lastName: String - } - """ - } expansion: { - """ - public struct Person { - public let firstName, lastName: String - - public init() { - } - } - """ - } diagnostics: { - """ - @MemberwiseInit(.public) - public struct Person { - @Init(label: "with") public let firstName, lastName: String - ┬──────────── - ╰─ 🛑 Custom 'label' can't be applied to multiple bindings - } - """ + assertMacro { + """ + @MemberwiseInit(.public) + public struct Person { + @Init(label: "with") public let firstName, lastName: String + } + """ + } diagnostics: { + """ + @MemberwiseInit(.public) + public struct Person { + @Init(label: "with") public let firstName, lastName: String + ┬──────────── + │ ╰─ 🛑 Custom 'label' can't be applied to multiple bindings + ┬─────────────────── + ╰─ 🛑 peer macro can only be applied to a single variable } - #endif + """ + } } // TODO: Consider regressing MemberwiseInit to match swift-syntax 5.10 limitation (or leave redundant error, but that has a fix-it) func testLabellessCustomInitForMultipleBindings() { - #if canImport(SwiftSyntax510) - assertMacro { - """ - @MemberwiseInit(.public) - public struct Person { - @Init(label: "_") public let firstName, lastName: String - } - """ - } expansion: { - """ - public struct Person { - public let firstName, lastName: String - - public init( - _ firstName: String, - _ lastName: String - ) { - self.firstName = firstName - self.lastName = lastName - } - } - """ - } diagnostics: { - """ - @MemberwiseInit(.public) - public struct Person { - @Init(label: "_") public let firstName, lastName: String - ┬──────────────── - ╰─ 🛑 peer macro can only be applied to a single variable - } - """ - } - #elseif canImport(SwfitSyntax509) - assertMacro { - """ - @MemberwiseInit(.public) - public struct Person { - @Init(label: "_") public let firstName, lastName: String - } - """ - } expansion: { - """ - public struct Person { - public let firstName, lastName: String - - public init( - _ firstName: String, - _ lastName: String - ) { - self.firstName = firstName - self.lastName = lastName - } - } - """ + assertMacro { + """ + @MemberwiseInit(.public) + public struct Person { + @Init(label: "_") public let firstName, lastName: String + } + """ + } diagnostics: { + """ + @MemberwiseInit(.public) + public struct Person { + @Init(label: "_") public let firstName, lastName: String + ┬──────────────── + ╰─ 🛑 peer macro can only be applied to a single variable } - #endif + """ + } } func testCustomInitIgnore() { @@ -2623,15 +2237,6 @@ final class MemberwiseInitTests: XCTestCase { @Init(label: "1foo") let name: String } """ - } expansion: { - """ - struct Person { - let name: String - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -2641,7 +2246,7 @@ final class MemberwiseInitTests: XCTestCase { ╰─ 🛑 Invalid label value } """ - } + } } func testCustomInitLabelHavingMultipleLines_FailsWithDiagnostic() { @@ -2655,15 +2260,6 @@ final class MemberwiseInitTests: XCTestCase { """) let name: String } """# - } expansion: { - """ - struct Person { - let name: String - - internal init() { - } - } - """ } diagnostics: { #""" @MemberwiseInit @@ -2675,7 +2271,7 @@ final class MemberwiseInitTests: XCTestCase { """) let name: String } """# - } + } } func testCustomInitLabelHavingSingleLineUsingMultilineSyntax() { @@ -2712,21 +2308,6 @@ final class MemberwiseInitTests: XCTestCase { let b: String } """ - } expansion: { - """ - struct S { - let a: String - let b: String - - internal init( - b a: String, - b: String - ) { - self.a = a - self.b = b - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -2737,7 +2318,7 @@ final class MemberwiseInitTests: XCTestCase { let b: String } """ - } + } } func testCustomInitLabel_WhenConflictingPropertyIsIgnored() { @@ -2774,21 +2355,6 @@ final class MemberwiseInitTests: XCTestCase { @Init(label: "z") let b: String } """ - } expansion: { - """ - struct S { - let a: String - let b: String - - internal init( - z a: String, - z b: String - ) { - self.a = a - self.b = b - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -2799,7 +2365,7 @@ final class MemberwiseInitTests: XCTestCase { ╰─ 🛑 Label 'z' conflicts with another label } """ - } + } } func testCustomInitLabelConflictsWithMultipleOtherLabels_FailsWithDiagnostic() { @@ -2812,24 +2378,6 @@ final class MemberwiseInitTests: XCTestCase { @Init(label: "z") let c: String } """ - } expansion: { - """ - struct S { - let a: String - let b: String - let c: String - - internal init( - z a: String, - z b: String, - z c: String - ) { - self.a = a - self.b = b - self.c = c - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -2843,7 +2391,7 @@ final class MemberwiseInitTests: XCTestCase { ╰─ 🛑 Label 'z' conflicts with another label } """ - } + } } // MARK: - Test _optionalsDefaultNil (experimental) diff --git a/Tests/MemberwiseInitTests/ReadmeTests.swift b/Tests/MemberwiseInitTests/ReadmeTests.swift index 8b57ff0..d3aa176 100644 --- a/Tests/MemberwiseInitTests/ReadmeTests.swift +++ b/Tests/MemberwiseInitTests/ReadmeTests.swift @@ -7,6 +7,7 @@ final class ReadmeTests: XCTestCase { override func invokeTest() { withMacroTesting( indentationWidth: .spaces(2), + record: .missing, macros: [ "MemberwiseInit": MemberwiseInitMacro.self, "Init": InitMacro.self, @@ -27,19 +28,6 @@ final class ReadmeTests: XCTestCase { private var age: Int? = nil } """ - } expansion: { - """ - public struct Person { - public let name: String - private var age: Int? = nil - - public init( - name: String - ) { - self.name = name - } - } - """ } diagnostics: { """ @MemberwiseInit(.public) @@ -55,83 +43,53 @@ final class ReadmeTests: XCTestCase { """ } fixes: { """ - private var age: Int? = nil - ┬────── - ╰─ 🛑 @MemberwiseInit(.public) would leak access to 'private' property - - ✏️ Add '@Init(.public)' @MemberwiseInit(.public) public struct Person { public let name: String @Init(.public) private var age: Int? = nil } - - ✏️ Replace 'private' access with 'public' - @MemberwiseInit(.public) + """ + } expansion: { + """ public struct Person { public let name: String - public var age: Int? = nil - } + private var age: Int? = nil - ✏️ Add '@Init(.ignore)' - @MemberwiseInit(.public) - public struct Person { - public let name: String - @Init(.ignore) private var age: Int? = nil + public init( + name: String, + age: Int? = nil + ) { + self.name = name + self.age = age + } } """ } } func testIgnoreAge() { - #if canImport(SwiftSyntax600) - assertMacro { - """ - @MemberwiseInit(.public) - public struct Person { - public let name: String - @Init(.ignore) private var age: Int? = nil - } - """ - } expansion: { - """ - public struct Person { - public let name: String - private var age: Int? = nil - - public init( - name: String - ) { - self.name = name - } - } - """ - } - #else - assertMacro { - """ - @MemberwiseInit(.public) - public struct Person { - public let name: String - @Init(.ignore) private var age: Int? = nil - } - """ - } expansion: { - """ - - public struct Person { - public let name: String - private var age: Int? = nil + assertMacro { + """ + @MemberwiseInit(.public) + public struct Person { + public let name: String + @Init(.ignore) private var age: Int? = nil + } + """ + } expansion: { + """ + public struct Person { + public let name: String + private var age: Int? = nil public init( name: String ) { self.name = name } - } - """ - } - #endif + } + """ + } } func testExposeAgePublically() { @@ -162,60 +120,31 @@ final class ReadmeTests: XCTestCase { } func testBinding() { - #if canImport(SwiftSyntax600) - assertMacro { - """ - @MemberwiseInit - struct CounterView: View { - @InitWrapper(type: Binding.self) - @Binding var isOn: Bool - - var body: some View { EmptyView() } - } - """ - } expansion: { - """ - struct CounterView: View { - @Binding var isOn: Bool - - var body: some View { EmptyView() } + assertMacro { + """ + @MemberwiseInit + struct CounterView: View { + @InitWrapper(type: Binding.self) + @Binding var isOn: Bool - internal init( - isOn: Binding - ) { - self._isOn = isOn - } - } - """ + var body: some View { EmptyView() } } - #else - assertMacro { - """ - @MemberwiseInit - struct CounterView: View { - @InitWrapper(type: Binding.self) - @Binding var isOn: Bool - - var body: some View { EmptyView() } - } - """ - } expansion: { - """ - struct CounterView: View { - @Binding - var isOn: Bool + """ + } expansion: { + """ + struct CounterView: View { + @Binding var isOn: Bool - var body: some View { EmptyView() } + var body: some View { EmptyView() } - internal init( - isOn: Binding - ) { - self._isOn = isOn - } + internal init( + isOn: Binding + ) { + self._isOn = isOn } - """ } - #endif + """ + } } func testLabelessParmeters() { @@ -418,124 +347,63 @@ final class ReadmeTests: XCTestCase { """ } - #if canImport(SwiftSyntax600) - assertMacro { - """ - import SwiftUI - @MemberwiseInit(.internal) - struct MyView: View { - @Init @State var isOn: Bool // 👈 `@Init` - - var body: some View { EmptyView() } - } - """ - } expansion: { - """ - import SwiftUI - struct MyView: View { - @State var isOn: Bool // 👈 `@Init` - - var body: some View { EmptyView() } + assertMacro { + """ + import SwiftUI + @MemberwiseInit(.internal) + struct MyView: View { + @Init @State var isOn: Bool // 👈 `@Init` - internal init( - isOn: Bool - ) { - self.isOn = isOn - } - } - """ + var body: some View { EmptyView() } } - #else - assertMacro { - """ - import SwiftUI - @MemberwiseInit(.internal) - struct MyView: View { - @Init @State var isOn: Bool // 👈 `@Init` - - var body: some View { EmptyView() } - } - """ - } expansion: { - """ - import SwiftUI - struct MyView: View {@State - var isOn: Bool // 👈 `@Init` + """ + } expansion: { + """ + import SwiftUI + struct MyView: View { + @State var isOn: Bool // 👈 `@Init` - var body: some View { EmptyView() } + var body: some View { EmptyView() } - internal init( - isOn: Bool - ) { - self.isOn = isOn - } + internal init( + isOn: Bool + ) { + self.isOn = isOn } - """ } - #endif + """ + } } func testSupportForPropertyWrappers() { - #if canImport(SwiftSyntax600) - assertMacro { - """ - import SwiftUI - - @MemberwiseInit - struct CounterView: View { - @InitWrapper(type: Binding.self) - @Binding var count: Int - - var body: some View { EmptyView() } - } - """ - } expansion: { - """ - import SwiftUI - struct CounterView: View { - @Binding var count: Int + assertMacro { + """ + import SwiftUI - var body: some View { EmptyView() } + @MemberwiseInit + struct CounterView: View { + @InitWrapper(type: Binding.self) + @Binding var count: Int - internal init( - count: Binding - ) { - self._count = count - } - } - """ + var body: some View { EmptyView() } } - #else - assertMacro { - """ - import SwiftUI + """ + } expansion: { + """ + import SwiftUI + struct CounterView: View { + @Binding var count: Int - @MemberwiseInit - struct CounterView: View { - @InitWrapper(type: Binding.self) - @Binding var count: Int + var body: some View { EmptyView() } - var body: some View { EmptyView() } - } - """ - } expansion: { - """ - import SwiftUI - struct CounterView: View { - @Binding - var count: Int - - var body: some View { EmptyView() } - - internal init( - count: Binding - ) { - self._count = count - } + internal init( + count: Binding + ) { + self._count = count } - """ } - #endif + """ + } } func testAutomaticEscapingForClosureTypes() { @@ -695,15 +563,6 @@ final class ReadmeTests: XCTestCase { let (x, y): (Int, Int) } """ - } expansion: { - """ - struct Point2D { - let (x, y): (Int, Int) - - internal init() { - } - } - """ } diagnostics: { """ @MemberwiseInit @@ -713,7 +572,7 @@ final class ReadmeTests: XCTestCase { ╰─ 🛑 @MemberwiseInit does not support tuple destructuring for property declarations. Use multiple declarations instead. } """ - } + } } func testBackground() { @@ -769,19 +628,6 @@ final class ReadmeTests: XCTestCase { private var age: Int? // 👈 `private` } """ - } expansion: { - """ - public struct Person { - public let name: String - private var age: Int? // 👈 `private` - - public init( - name: String - ) { - self.name = name - } - } - """ } diagnostics: { """ @MemberwiseInit(.public) @@ -797,29 +643,25 @@ final class ReadmeTests: XCTestCase { """ } fixes: { """ - private var age: Int? // 👈 `private` - ┬────── - ╰─ 🛑 @MemberwiseInit(.public) would leak access to 'private' property - - ✏️ Add '@Init(.public)' @MemberwiseInit(.public) public struct Person { public let name: String @Init(.public) private var age: Int? // 👈 `private` } - - ✏️ Replace 'private' access with 'public' - @MemberwiseInit(.public) + """ + } expansion: { + """ public struct Person { public let name: String - public var age: Int? // 👈 `private` - } + private var age: Int? // 👈 `private` - ✏️ Add '@Init(.ignore)' and an initializer - @MemberwiseInit(.public) - public struct Person { - public let name: String - @Init(.ignore) private var age: Int? // 👈 `private` = <#value#> + public init( + name: String, + age: Int? + ) { + self.name = name + self.age = age + } } """ } diff --git a/Tests/MemberwiseInitTests/UncheckedMemberwiseInitTests.swift b/Tests/MemberwiseInitTests/UncheckedMemberwiseInitTests.swift index 8ed0355..c054f06 100644 --- a/Tests/MemberwiseInitTests/UncheckedMemberwiseInitTests.swift +++ b/Tests/MemberwiseInitTests/UncheckedMemberwiseInitTests.swift @@ -6,6 +6,8 @@ import XCTest final class UncheckedMemberwiseInitTests: XCTestCase { override func invokeTest() { withMacroTesting( + indentationWidth: .spaces(2), + record: .missing, macros: [ "_UncheckedMemberwiseInit": UncheckedMemberwiseInitMacro.self ] @@ -408,15 +410,15 @@ final class UncheckedMemberwiseInitTests: XCTestCase { let onFailure: @MainActor @Sendable (Error) -> Void @Init(escaping: true) var customEscaping: CompletionHandler - internal init( - onSuccess: @escaping (Data) -> Void, - onFailure: @escaping @MainActor @Sendable (Error) -> Void, - customEscaping: @escaping CompletionHandler - ) { - self.onSuccess = onSuccess - self.onFailure = onFailure - self.customEscaping = customEscaping - } + internal init( + onSuccess: @escaping (Data) -> Void, + onFailure: @escaping @MainActor @Sendable (Error) -> Void, + customEscaping: @escaping CompletionHandler + ) { + self.onSuccess = onSuccess + self.onFailure = onFailure + self.customEscaping = customEscaping + } } """ } diff --git a/bin/generate_access_level_tests.sh b/bin/generate_access_level_tests.sh index d988f09..956aa0f 100755 --- a/bin/generate_access_level_tests.sh +++ b/bin/generate_access_level_tests.sh @@ -89,7 +89,7 @@ capitalize() { test_name="${test_name//__/_}" echo " func $test_name() {" - echo " assertMacro(applyFixIts: false) {" + echo " assertMacro {" echo " \"\"\"" echo " @MemberwiseInit${memberwise_str:+($memberwise_str)}" echo " ${struct_str}${struct_str:+ }struct S {"