> For the complete documentation index, see [llms.txt](https://swift.unicorndonkeys.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://swift.unicorndonkeys.com/guida-al-linguaggio/macro.md).

# Macro (Macros)

Usa le macro per generare codice in fase di compilazione.

Le macro trasformano il tuo codice sorgente quando lo compili, permettendoti di evitare di scrivere a mano codice ripetitivo. Durante la compilazione, Swift espande tutte le macro presenti nel tuo codice prima di compilare il codice come di consueto.

![Un diagramma che mostra una panoramica dell'espansione delle macro.  A sinistra, una rappresentazione stilizzata di codice Swift.  A destra, lo stesso codice con diverse righe aggiunte dalla macro.](https://3581893755-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FNzatREGwodw9Z93LQYJb%2Fuploads%2FxG4BfrYq5MhZRYxRhZua%2Fmacro-expansion%402x.png?alt=media)

Espandere una macro è sempre un'operazione additiva: le macro aggiungono nuovo codice, ma non eliminano né modificano mai il codice esistente.

Sia l'input di una macro sia l'output dell'espansione della macro vengono controllati per garantire che siano codice Swift sintatticamente valido. Allo stesso modo, i valori che passi a una macro e i valori nel codice generato da una macro vengono controllati per garantire che abbiano i tipi corretti. Inoltre, se l'implementazione della macro incontra un errore durante l'espansione di quella macro, il compilatore lo tratta come un errore di compilazione. Queste garanzie rendono più facile ragionare sul codice che usa le macro, e rendono più facile individuare problemi come l'uso scorretto di una macro o un'implementazione di una macro che contiene un bug.

Swift ha due tipi di macro:

* Le *macro autonome* (freestanding) compaiono da sole, senza essere associate a una dichiarazione.
* Le *macro associate* (attached) modificano la dichiarazione a cui sono associate.

Chiami le macro associate e le macro autonome in modo leggermente diverso, ma entrambe seguono lo stesso modello di espansione delle macro, e le implementi entrambe usando lo stesso approccio. Le sezioni seguenti descrivono entrambi i tipi di macro in modo più dettagliato.

## Macro autonome <a href="#macro-autonome" id="macro-autonome"></a>

Per chiamare una macro autonoma, scrivi un cancelletto (`#`) prima del suo nome, e scrivi gli eventuali argomenti della macro tra parentesi dopo il suo nome. Per esempio:

```swift
func myFunction() {
    print("Currently running \(#function)")
    #warning("Something's wrong")
}
```

Nella prima riga, `#function` chiama la macro [`function()`](https://developer.apple.com/documentation/swift/function\(\)) della libreria standard di Swift. Quando compili questo codice, Swift chiama l'implementazione di quella macro, che sostituisce `#function` con il nome della funzione corrente. Quando esegui questo codice e chiami `myFunction()`, esso stampa "Currently running myFunction()". Nella seconda riga, `#warning` chiama la macro [`warning(_:)`](https://developer.apple.com/documentation/swift/warning\(_:\)) della libreria standard di Swift per produrre un avviso personalizzato in fase di compilazione.

Le macro autonome possono produrre un valore, come fa `#function`, oppure possono eseguire un'azione in fase di compilazione, come fa `#warning`.

## Macro associate <a href="#macro-associate" id="macro-associate"></a>

Per chiamare una macro associata, scrivi una chiocciola (`@`) prima del suo nome, e scrivi gli eventuali argomenti della macro tra parentesi dopo il suo nome.

Le macro associate modificano la dichiarazione a cui sono associate. Aggiungono codice a quella dichiarazione, come la definizione di un nuovo metodo o l'aggiunta della conformità a un protocollo.

Per esempio, considera il codice seguente che non usa macro:

```swift
struct SundaeToppings: OptionSet {
    let rawValue: Int
    static let nuts = SundaeToppings(rawValue: 1 << 0)
    static let cherry = SundaeToppings(rawValue: 1 << 1)
    static let fudge = SundaeToppings(rawValue: 1 << 2)
}
```

In questo codice, ciascuna delle opzioni dell'option set `SundaeToppings` include una chiamata all'inizializzatore, il che è ripetitivo e manuale. Sarebbe facile commettere un errore quando si aggiunge una nuova opzione, come digitare il numero sbagliato alla fine della riga.

Ecco invece una versione di questo codice che usa una macro:

```swift
@OptionSet<Int>
struct SundaeToppings {
    private enum Options: Int {
        case nuts
        case cherry
        case fudge
    }
}
```

Questa versione di `SundaeToppings` chiama una macro `@OptionSet`. La macro legge l'elenco dei casi nell'enumerazione privata, genera l'elenco delle costanti per ciascuna opzione, e aggiunge una conformità al protocollo [`OptionSet`](https://developer.apple.com/documentation/swift/optionset).

Per confronto, ecco come appare la versione espansa della macro `@OptionSet`. Non sei tu a scrivere questo codice, e lo vedresti solo se chiedessi specificamente a Swift di mostrare l'espansione della macro.

```swift
struct SundaeToppings {
    private enum Options: Int {
        case nuts
        case cherry
        case fudge
    }

    typealias RawValue = Int
    var rawValue: RawValue
    init() { self.rawValue = 0 }
    init(rawValue: RawValue) { self.rawValue = rawValue }
    static let nuts: Self = Self(rawValue: 1 << Options.nuts.rawValue)
    static let cherry: Self = Self(rawValue: 1 << Options.cherry.rawValue)
    static let fudge: Self = Self(rawValue: 1 << Options.fudge.rawValue)
}
extension SundaeToppings: OptionSet { }
```

Tutto il codice dopo l'enumerazione privata proviene dalla macro `@OptionSet`. La versione di `SundaeToppings` che usa una macro per generare tutte le variabili statiche è più facile da leggere e più facile da mantenere rispetto alla versione scritta a mano vista in precedenza.

## Dichiarazioni di macro <a href="#dichiarazioni-di-macro" id="dichiarazioni-di-macro"></a>

Nella maggior parte del codice Swift, quando implementi un simbolo, come una funzione o un tipo, non esiste una dichiarazione separata. Per le macro, invece, dichiarazione e implementazione sono separate. La dichiarazione di una macro contiene il suo nome, i parametri che accetta, dove può essere usata, e che tipo di codice genera. L'implementazione di una macro contiene il codice che espande la macro generando codice Swift.

Introduci la dichiarazione di una macro con la parola chiave `macro`. Per esempio, ecco una parte della dichiarazione della macro `@OptionSet` usata nell'esempio precedente:

```swift
public macro OptionSet<RawType>() =
        #externalMacro(module: "SwiftMacros", type: "OptionSetMacro")
```

La prima riga specifica il nome della macro e i suoi argomenti — il nome è `OptionSet`, e non accetta alcun argomento. La seconda riga usa la macro [`externalMacro(module:type:)`](https://developer.apple.com/documentation/swift/externalmacro\(module:type:\)) della libreria standard di Swift per dire a Swift dove si trova l'implementazione della macro. In questo caso, il modulo `SwiftMacros` contiene un tipo chiamato `OptionSetMacro`, che implementa la macro `@OptionSet`.

Poiché `OptionSet` è una macro associata, il suo nome usa l'upper camel case, come i nomi di strutture e classi. Le macro autonome hanno nomi in lower camel case, come i nomi di variabili e funzioni.

{% hint style="info" %}
**Nota:** Le macro sono sempre dichiarate come `public`. Poiché il codice che dichiara una macro si trova in un modulo diverso da quello del codice che usa quella macro, non esiste alcun punto in cui potresti applicare una macro non pubblica.
{% endhint %}

La dichiarazione di una macro definisce i *ruoli* della macro — i punti del codice sorgente in cui quella macro può essere chiamata, e i tipi di codice che la macro può generare. Ogni macro ha uno o più ruoli, che scrivi come parte degli attributi all'inizio della dichiarazione della macro. Ecco un'altra parte della dichiarazione di `@OptionSet`, compresi gli attributi per i suoi ruoli:

```swift
@attached(member)
@attached(extension, conformances: OptionSet)
public macro OptionSet<RawType>() =
        #externalMacro(module: "SwiftMacros", type: "OptionSetMacro")
```

L'attributo `@attached` compare due volte in questa dichiarazione, una volta per ciascun ruolo della macro. Il primo uso, `@attached(member)`, indica che la macro aggiunge nuovi membri al tipo a cui la applichi. La macro `@OptionSet` aggiunge un inizializzatore `init(rawValue:)` richiesto dal protocollo `OptionSet`, oltre ad alcuni membri aggiuntivi. Il secondo uso, `@attached(extension, conformances: OptionSet)`, ti dice che `@OptionSet` aggiunge la conformità al protocollo `OptionSet`. La macro `@OptionSet` estende il tipo a cui applichi la macro, per aggiungere la conformità al protocollo `OptionSet`.

Per una macro autonoma, scrivi l'attributo `@freestanding` per specificarne il ruolo:

```swift
@freestanding(expression)
public macro line<T: ExpressibleByIntegerLiteral>() -> T =
        /* ... posizione dell'implementazione della macro... */
```

La macro `#line` qui sopra ha il ruolo `expression`. Una macro di espressione produce un valore, oppure esegue un'azione in fase di compilazione, come generare un avviso.

Oltre al ruolo della macro, la dichiarazione di una macro fornisce informazioni sui nomi dei simboli che la macro genera. Quando la dichiarazione di una macro fornisce un elenco di nomi, è garantito che produca solo dichiarazioni che usano quei nomi, il che ti aiuta a comprendere il codice generato e a farne il debug. Ecco la dichiarazione completa di `@OptionSet`:

```swift
@attached(member, names: named(RawValue), named(rawValue),
        named(`init`), arbitrary)
@attached(extension, conformances: OptionSet)
public macro OptionSet<RawType>() =
        #externalMacro(module: "SwiftMacros", type: "OptionSetMacro")
```

Nella dichiarazione qui sopra, la macro `@attached(member)` include argomenti dopo l'etichetta `names:` per ciascuno dei simboli generati dalla macro `@OptionSet`. La macro aggiunge dichiarazioni per i simboli chiamati `RawValue`, `rawValue` e `init` — poiché questi nomi sono noti in anticipo, la dichiarazione della macro li elenca esplicitamente.

La dichiarazione della macro include anche `arbitrary` dopo l'elenco dei nomi, consentendo alla macro di generare dichiarazioni i cui nomi non sono noti finché non usi la macro. Per esempio, quando la macro `@OptionSet` viene applicata a `SundaeToppings` vista sopra, genera proprietà di tipo che corrispondono ai casi dell'enumerazione, `nuts`, `cherry` e `fudge`.

Per maggiori informazioni, compreso un elenco completo dei ruoli delle macro, vedi [attached](/riferimento-del-linguaggio/attributi.md#attached) e [freestanding](/riferimento-del-linguaggio/attributi.md#freestanding) in [Attributi](/riferimento-del-linguaggio/attributi.md).

## Espansione delle macro <a href="#espansione-delle-macro" id="espansione-delle-macro"></a>

Quando compila codice Swift che usa le macro, il compilatore chiama l'implementazione delle macro per espanderle.

![Diagramma che mostra le quattro fasi dell'espansione delle macro.  L'input è codice sorgente Swift.  Questo diventa un albero, che rappresenta la struttura del codice.  L'implementazione della macro aggiunge rami all'albero.  Il risultato è codice sorgente Swift con codice aggiuntivo.](https://3581893755-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FNzatREGwodw9Z93LQYJb%2Fuploads%2Fr6g8H6DEh6I5RnwzfeNE%2Fmacro-expansion-full%402x.png?alt=media)

Nello specifico, Swift espande le macro nel modo seguente:

1. Il compilatore legge il codice, creando una rappresentazione in memoria della sintassi.
2. Il compilatore invia una parte della rappresentazione in memoria all'implementazione della macro, che espande la macro.
3. Il compilatore sostituisce la chiamata alla macro con la sua forma espansa.
4. Il compilatore prosegue con la compilazione, usando il codice sorgente espanso.

Per esaminare le fasi specifiche, considera quanto segue:

```swift
let magicNumber = #fourCharacterCode("ABCD")
```

La macro `#fourCharacterCode` accetta una stringa lunga quattro caratteri e restituisce un intero senza segno a 32 bit che corrisponde ai valori ASCII della stringa uniti insieme. Alcuni formati di file usano interi come questo per identificare i dati perché sono compatti ma comunque leggibili in un debugger. La sezione [Implementare una macro](/guida-al-linguaggio/macro.md#implementare-una-macro) più avanti mostra come implementare questa macro.

Per espandere le macro nel codice qui sopra, il compilatore legge il file Swift e crea una rappresentazione in memoria di quel codice nota come *albero sintattico astratto* (abstract syntax tree), o AST. L'AST rende esplicita la struttura del codice, il che rende più facile scrivere codice che interagisce con quella struttura — come un compilatore o l'implementazione di una macro. Ecco una rappresentazione dell'AST per il codice qui sopra, leggermente semplificata omettendo alcuni dettagli aggiuntivi:

![Un diagramma ad albero, con una costante come elemento radice.  La costante ha un nome, magic number, e un valore.  Il valore della costante è una chiamata a una macro.  La chiamata alla macro ha un nome, fourCharacterCode, e degli argomenti.  L'argomento è un letterale stringa, ABCD.](https://3581893755-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FNzatREGwodw9Z93LQYJb%2Fuploads%2FUmalGIHcvpWNkdCNp4Dd%2Fmacro-ast-original%402x.png?alt=media)

Il diagramma qui sopra mostra come la struttura di questo codice è rappresentata in memoria. Ogni elemento dell'AST corrisponde a una parte del codice sorgente. L'elemento dell'AST "Constant declaration" ha sotto di sé due elementi figli, che rappresentano le due parti di una dichiarazione di costante: il suo nome e il suo valore. L'elemento "Macro call" ha elementi figli che rappresentano il nome della macro e l'elenco degli argomenti passati alla macro.

Come parte della costruzione di questo AST, il compilatore verifica che il codice sorgente sia Swift valido. Per esempio, `#fourCharacterCode` accetta un solo argomento, che deve essere una stringa. Se provassi a passare un argomento intero, o dimenticassi le virgolette (`"`) alla fine del letterale stringa, otterresti un errore in questo punto del processo.

Il compilatore trova i punti del codice in cui chiami una macro, e carica il file binario esterno che implementa quelle macro. Per ogni chiamata a una macro, il compilatore passa una parte dell'AST all'implementazione di quella macro. Ecco una rappresentazione di quell'AST parziale:

![Un diagramma ad albero, con una chiamata a una macro come elemento radice.  La chiamata alla macro ha un nome, fourCharacterCode, e degli argomenti.  L'argomento è un letterale stringa, ABCD.](https://3581893755-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FNzatREGwodw9Z93LQYJb%2Fuploads%2F5mjAqcnLz3Bf08hvl5nI%2Fmacro-ast-input%402x.png?alt=media)

L'implementazione della macro `#fourCharacterCode` legge questo AST parziale come input quando espande la macro. L'implementazione di una macro opera solo sull'AST parziale che riceve come input, il che significa che una macro si espande sempre nello stesso modo indipendentemente dal codice che la precede e la segue. Questa limitazione aiuta a rendere l'espansione delle macro più facile da comprendere, e aiuta il tuo codice a compilare più velocemente perché Swift può evitare di espandere le macro che non sono cambiate. Swift aiuta gli autori delle macro a evitare di leggere accidentalmente altri input limitando il codice che implementa le macro:

* L'AST passato all'implementazione di una macro contiene solo gli elementi dell'AST che rappresentano la macro, e non il codice che la precede o la segue.
* L'implementazione della macro viene eseguita in un ambiente isolato (sandbox) che le impedisce di accedere al file system o alla rete.

Oltre a queste salvaguardie, l'autore della macro è responsabile di non leggere né modificare nulla al di fuori degli input della macro. Per esempio, l'espansione di una macro non deve dipendere dall'ora corrente.

L'implementazione di `#fourCharacterCode` genera un nuovo AST contenente il codice espanso. Ecco ciò che quel codice restituisce al compilatore:

![Un diagramma ad albero con il letterale intero 1145258561 di tipo UInt32.](https://3581893755-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FNzatREGwodw9Z93LQYJb%2Fuploads%2F6ZHiluFcbabU9WljqxqZ%2Fmacro-ast-output%402x.png?alt=media)

Quando il compilatore riceve questa espansione, sostituisce l'elemento dell'AST che contiene la chiamata alla macro con l'elemento che contiene l'espansione della macro. Dopo l'espansione delle macro, il compilatore verifica di nuovo che il programma sia ancora Swift sintatticamente valido e che tutti i tipi siano corretti. Questo produce un AST finale che può essere compilato come di consueto:

![Un diagramma ad albero, con una costante come elemento radice.  La costante ha un nome, magic number, e un valore.  Il valore della costante è il letterale intero 1145258561 di tipo UInt32.](https://3581893755-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FNzatREGwodw9Z93LQYJb%2Fuploads%2FGMmzXWnxKdVrlns1zG3j%2Fmacro-ast-result%402x.png?alt=media)

Questo AST corrisponde a codice Swift come questo:

```swift
let magicNumber = 1145258561 as UInt32
```

In questo esempio, il codice sorgente di input contiene una sola macro, ma un programma reale potrebbe avere diverse istanze della stessa macro e diverse chiamate a macro differenti. Il compilatore espande le macro una alla volta.

Se una macro compare all'interno di un'altra, viene espansa per prima la macro esterna — questo permette alla macro esterna di modificare la macro interna prima che questa venga espansa.

## Implementare una macro <a href="#implementare-una-macro" id="implementare-una-macro"></a>

Per implementare una macro, crei due componenti: un tipo che esegue l'espansione della macro, e una libreria che dichiara la macro per esporla come API. Queste parti vengono compilate separatamente dal codice che usa la macro, anche se stai sviluppando la macro e i suoi client insieme, perché l'implementazione della macro viene eseguita come parte della compilazione dei client della macro.

Per creare una nuova macro usando Swift Package Manager, esegui `swift package init --type macro` — questo crea diversi file, tra cui un modello per l'implementazione e la dichiarazione di una macro.

Per aggiungere macro a un progetto esistente, modifica l'inizio del tuo file `Package.swift` come segue:

* Imposta una versione degli strumenti Swift pari a 5.9 o successiva nel commento `swift-tools-version`.
* Importa il modulo `CompilerPluginSupport`.
* Includi macOS 10.15 come deployment target minimo nell'elenco `platforms`.

Il codice seguente mostra l'inizio di un file `Package.swift` di esempio.

```swift
// swift-tools-version: 5.9

import PackageDescription
import CompilerPluginSupport

let package = Package(
    name: "MyPackage",
    platforms: [ .iOS(.v17), .macOS(.v13)],
    // ...
)
```

Successivamente, aggiungi un target per l'implementazione della macro e un target per la libreria della macro al tuo file `Package.swift` esistente. Per esempio, puoi aggiungere qualcosa di simile a quanto segue, cambiando i nomi in modo che corrispondano al tuo progetto:

```swift
targets: [
    // Implementazione della macro che esegue le trasformazioni del sorgente.
    .macro(
        name: "MyProjectMacros",
        dependencies: [
            .product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
            .product(name: "SwiftCompilerPlugin", package: "swift-syntax")
        ]
    ),

    // Libreria che espone una macro come parte della sua API.
    .target(name: "MyProject", dependencies: ["MyProjectMacros"]),
]
```

Il codice qui sopra definisce due target: `MyProjectMacros` contiene l'implementazione delle macro, e `MyProject` rende disponibili quelle macro.

L'implementazione di una macro usa il modulo [SwiftSyntax](https://github.com/swiftlang/swift-syntax) per interagire con il codice Swift in modo strutturato, usando un AST. Se hai creato un nuovo package di macro con Swift Package Manager, il file `Package.swift` generato include automaticamente una dipendenza da SwiftSyntax. Se stai aggiungendo macro a un progetto esistente, aggiungi una dipendenza da SwiftSyntax nel tuo file `Package.swift`:

```swift
dependencies: [
    .package(url: "https://github.com/swiftlang/swift-syntax", from: "509.0.0")
],
```

A seconda del ruolo della macro, esiste un protocollo corrispondente di SwiftSyntax a cui l'implementazione della macro è conforme. Per esempio, considera `#fourCharacterCode` della sezione precedente. Ecco una struttura che implementa quella macro:

```swift
import SwiftSyntax
import SwiftSyntaxMacros

public struct FourCharacterCode: ExpressionMacro {
    public static func expansion(
        of node: some FreestandingMacroExpansionSyntax,
        in context: some MacroExpansionContext
    ) throws -> ExprSyntax {
        guard let argument = node.argumentList.first?.expression,
              let segments = argument.as(StringLiteralExprSyntax.self)?.segments,
              segments.count == 1,
              case .stringSegment(let literalSegment)? = segments.first
        else {
            throw CustomError.message("Need a static string")
        }

        let string = literalSegment.content.text
        guard let result = fourCharacterCode(for: string) else {
            throw CustomError.message("Invalid four-character code")
        }

        return "\(raw: result) as UInt32"
    }
}

private func fourCharacterCode(for characters: String) -> UInt32? {
    guard characters.count == 4 else { return nil }

    var result: UInt32 = 0
    for character in characters {
        result = result << 8
        guard let asciiValue = character.asciiValue else { return nil }
        result += UInt32(asciiValue)
    }
    return result
}
enum CustomError: Error { case message(String) }
```

Se stai aggiungendo questa macro a un progetto Swift Package Manager esistente, aggiungi un tipo che funga da punto di ingresso per il target della macro e che elenchi le macro definite dal target:

```swift
import SwiftCompilerPlugin

@main
struct MyProjectMacros: CompilerPlugin {
    var providingMacros: [Macro.Type] = [FourCharacterCode.self]
}
```

La macro `#fourCharacterCode` è una macro autonoma che produce un'espressione, quindi il tipo `FourCharacterCode` che la implementa è conforme al protocollo `ExpressionMacro`. Il protocollo `ExpressionMacro` ha un solo requisito, un metodo `expansion(of:in:)` che espande l'AST. Per l'elenco dei ruoli delle macro e dei corrispondenti protocolli di SwiftSyntax, vedi [attached](/riferimento-del-linguaggio/attributi.md#attached) e [freestanding](/riferimento-del-linguaggio/attributi.md#freestanding) in [Attributi](/riferimento-del-linguaggio/attributi.md).

Per espandere la macro `#fourCharacterCode`, Swift invia l'AST del codice che usa questa macro alla libreria che contiene l'implementazione della macro. All'interno della libreria, Swift chiama `FourCharacterCode.expansion(of:in:)`, passando l'AST e il contesto come argomenti al metodo. L'implementazione di `expansion(of:in:)` trova la stringa passata come argomento a `#fourCharacterCode` e calcola il corrispondente valore letterale intero senza segno a 32 bit.

Nell'esempio qui sopra, il primo blocco `guard` estrae il letterale stringa dall'AST, assegnando quell'elemento dell'AST a `literalSegment`. Il secondo blocco `guard` chiama la funzione privata `fourCharacterCode(for:)`. Entrambi questi blocchi lanciano un errore se la macro viene usata in modo scorretto — il messaggio di errore diventa un errore del compilatore nel punto della chiamata malformata. Per esempio, se provi a chiamare la macro come `#fourCharacterCode("AB" + "CD")` il compilatore mostra l'errore "Need a static string".

Il metodo `expansion(of:in:)` restituisce un'istanza di `ExprSyntax`, un tipo di SwiftSyntax che rappresenta un'espressione in un AST. Poiché questo tipo è conforme al protocollo `StringLiteralConvertible`, l'implementazione della macro usa un letterale stringa come sintassi leggera per creare il proprio risultato. Tutti i tipi di SwiftSyntax che restituisci dall'implementazione di una macro sono conformi a `StringLiteralConvertible`, quindi puoi usare questo approccio quando implementi qualsiasi tipo di macro.

## Sviluppare le macro e farne il debug <a href="#sviluppare-le-macro-e-farne-il-debug" id="sviluppare-le-macro-e-farne-il-debug"></a>

Le macro si prestano bene allo sviluppo guidato dai test: trasformano un AST in un altro AST senza dipendere da alcuno stato esterno, e senza apportare modifiche ad alcuno stato esterno. Inoltre, puoi creare nodi sintattici a partire da un letterale stringa, il che semplifica la preparazione dell'input per un test. Puoi anche leggere la proprietà `description` di un AST per ottenere una stringa da confrontare con un valore atteso. Per esempio, ecco un test della macro `#fourCharacterCode` delle sezioni precedenti:

```swift
let source: SourceFileSyntax =
    """
    let abcd = #fourCharacterCode("ABCD")
    """

let file = BasicMacroExpansionContext.KnownSourceFile(
    moduleName: "MyModule",
    fullFilePath: "test.swift"
)

let context = BasicMacroExpansionContext(sourceFiles: [source: file])

let transformedSF = source.expand(
    macros:["fourCharacterCode": FourCharacterCode.self],
    in: context
)

let expectedDescription =
    """
    let abcd = 1145258561 as UInt32
    """

precondition(transformedSF.description == expectedDescription)
```

L'esempio qui sopra verifica la macro usando una precondizione, ma potresti invece usare un framework di test.
