Skip to content

Commit 499ede7

Browse files
committed
Iterate on wording; fix minor errors.
Incorporates review from Brian Lanier <[email protected]>.
1 parent efae604 commit 499ede7

File tree

1 file changed

+54
-41
lines changed

1 file changed

+54
-41
lines changed

TSPL.docc/LanguageGuide/Macros.md

Lines changed: 54 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# Macros
22

3-
Transform code at compile time to generate repetitive code.
3+
Use macros to generate code at compile time.
44

55
Macros transform your source code when you compile it,
66
letting you avoid writing repetitive code by hand.
77
During compilation,
8-
Swift expands macros before building your code as usual.
8+
Swift expands any macros in your code before building your code as usual.
99

1010
![A diagram showing an overview of macro expansion. On the left, a stylized representation of Swift code. On the right, the same code with several lines added by the macro.](macro-expansion)
1111

@@ -23,7 +23,7 @@ if the macro's implementation encounters an error when expanding that macro,
2323
the compiler treats this as a compilation error.
2424
These guarantees make it easier to reason about code that uses macros,
2525
and they make it easier to identify issues
26-
like using macro incorrectly
26+
like using a macro incorrectly
2727
or a macro implementation that has a bug.
2828

2929
Swift has two kinds of macros:
@@ -46,19 +46,26 @@ and you write any arguments to the macro in parentheses after its name.
4646
For example:
4747

4848
```swift
49-
let currentFunctionName = #function
50-
#warning("Something's wrong")
49+
func myFunction() {
50+
print("Currently running \(#function)")
51+
#warning("Something's wrong")
52+
}
5153
```
5254

5355
In the first line,
54-
`#function` calls the `function` macro from the Swift standard library.
56+
`#function` calls the [`function`][] macro from the Swift standard library.
5557
When you compile this code,
5658
Swift calls that macro's implementation,
5759
which replaces `#function` with the name of the current function.
60+
When you run this code and call `myFunction()`,
61+
it prints `Currently running myFunction()`.
5862
In the second line,
59-
`#warning` calls another macro from the standard library
63+
`#warning` calls the [`warning(_:)`][] macro from the Swift standard library
6064
to produce a custom compile-time warning.
6165

66+
[`function`]: https://developer.apple.com/documentation/swift/function
67+
[`warning(_:)`]: https://developer.apple.comdocumentation/swift/warning(_:)
68+
6269
Freestanding macros can produce a value, like `#function` does,
6370
or they can perform an action at compile time, like `#warning` does.
6471
<!-- SE-0397: or they can generate new declarations. -->
@@ -144,20 +151,20 @@ comes from the `@OptionSet` macro.
144151
The version of `SundaeToppings`
145152
that uses a macro to generate all of the static variables
146153
is easier to read and easier to maintain
147-
than the manual-coded version, earlier.
154+
than the manually coded version, earlier.
148155

149156
## Macro Declarations
150157

151158
In most Swift code,
152-
you implement a symbol, like a function or type,
153-
and there's no separate declaration.
159+
when you implement a symbol, like a function or type,
160+
there's no separate declaration.
154161
However, for macros, the declaration and implementation are separate.
155-
A macro's implementation contains the code
156-
that expands the macro by generating Swift code,
157-
and its declaration
158-
as well as the macro's name, the parameters it takes,
162+
A macro's declaration contains its name,
163+
the parameters it takes,
159164
where it can be used,
160165
and what kind of code it generates.
166+
A macro's implementation contains the code
167+
that expands the macro by generating Swift code.
161168

162169
You introduce a macro declaration with the `macro` keyword.
163170
For example,
@@ -170,18 +177,18 @@ public macro OptionSet<RawType>() =
170177
```
171178

172179
The first line
173-
specifies the macro's name, `OptionSet`,
174-
and the arguments that takes, in this case none.
180+
specifies the macro's name and its arguments ---
181+
the name is `OptionSet`, and it doesn't take any arguments.
175182
The second line
176183
uses the `#externalMacro(module:type:)` macro from the Swift standard library
177-
to tell Swift where the macro's implementation is.
184+
to tell Swift where the macro's implementation is located.
178185
In this case,
179186
the `SwiftMacros` module
180187
contains a type named `OptionSetMacro`,
181188
which implements the `@OptionSet` macro.
182189

183190
Because `OptionSet` is an attached macro,
184-
its name uses upper camel case
191+
its name uses upper camel case,
185192
like the names for structures and classes.
186193
Freestanding macros have lower camel case names,
187194
like the names for variables and functions.
@@ -198,7 +205,7 @@ and the kinds of code the macro can generate.
198205
Every macro has one or more roles,
199206
which you write as part of the attributes
200207
at the beginning of the macro declaration.
201-
Here's a partial declaration of `@OptionSet`,
208+
Here's a bit more of the declaration for `@OptionSet`,
202209
including the attributes for its roles:
203210

204211
```swift
@@ -230,6 +237,13 @@ public macro line<T: ExpressibleByIntegerLiteral>() -> T =
230237
/* ... location of the macro implementation... */
231238
```
232239

240+
<!--
241+
Elided the implementation of #line above
242+
because it's a compiler built-in:
243+
244+
public macro line<T: ExpressibleByIntegerLiteral>() -> T = Builtin.LineMacro
245+
-->
246+
233247
The `#line` macro above has the `expression` role.
234248
An expression macro produces a value,
235249
or performs a compile-time action like generating a warning.
@@ -261,22 +275,24 @@ the macro declaration lists them explicitly.
261275
The macro declaration also includes `arbitrary` after the list of names,
262276
allowing the macro to generate declarations
263277
whose names aren't known until you use the macro.
264-
In this case,
265-
for each case in the private `Options` enumeration,
266-
`@OptionSet` generates a corresponding declaration with the same name.
278+
For example,
279+
when the `@OptionSet` macro is applied to the `SundaeToppings` above,
280+
it generates type properties that correspond to the enumeration cases,
281+
`nuts`, `cherry`, and `fudge`.
267282

268283
For more information,
269284
including a full list of macro roles,
270285
see <doc:Attributes#attached> and <doc:Attributes#freestanding>
271-
in <doc:Attributes>
286+
in <doc:Attributes>.
272287

273288
## Macro Expansion
274289

275-
As part of building Swift code that uses macros,
276-
the compiler and the macro's implementation
277-
pass that code back and forth to expand the macros.
290+
When building Swift code that uses macros,
291+
the compiler calls the macros' implementation to expand them.
278292

279-
Macros are expanded as follows:
293+
![Diagram showing the four steps of expanding macros. The input is Swift source code. This becomes a tree, representing the code's structure. The macro implementation adds branches to the tree. The result is Swift source with additional code.](macro-expansion-full)
294+
295+
Specifically, Swift expands macros in the following way:
280296

281297
1. The compiler reads the code,
282298
creating an in-memory representation of the syntax.
@@ -290,29 +306,25 @@ Macros are expanded as follows:
290306
1. The compiler continues with compilation,
291307
using the expanded source code.
292308

293-
These steps transform the code as shown below:
294-
295-
![Diagram showing the four steps of expanding macros. The input is Swift source code. This becomes a tree, representing the code's structure. The macro implementation adds branches to the tree. The result is Swift source with additional code.](macro-expansion-full)
296-
297309
To go through the specific steps, consider the following:
298310

299311
```
300312
let magicNumber = #fourCharacterCode("ABCD")
301313
```
302314

303315
The `#fourCharacterCode` macro takes a string that's four characters long
304-
and returns a unsigned 32-bit integer
316+
and returns an unsigned 32-bit integer
305317
that corresponds to the ASCII values in the string joined together.
306318
Some file formats use integers like this to identify data
307319
because they're compact but still readable in a debugger.
308-
The <doc:Macros#Implementing-Macros> section below
320+
The <doc:Macros#Implementing-a-Macro> section below
309321
shows how to implement this macro.
310322

311323
To expand the macros in the code above,
312324
the compiler reads the Swift file
313325
and creates an in-memory representation of that code
314326
known an as *abstract syntax tree*, or AST.
315-
The AST makes the code's meaning and structure explicit,
327+
The AST makes the code's structure explicit,
316328
which makes it easier to write code that interacts with that structure ---
317329
like a compiler or a macro implementation.
318330
Here's a representation of the AST for the code above,
@@ -322,15 +334,16 @@ slightly simplified by omitting some extra detail:
322334

323335
The diagram above shows how the structure of this code
324336
is represented in memory.
325-
Each of the elements in the AST
326-
corresponds to part of the source code's meaning.
337+
Each element in the AST
338+
corresponds to a part of the source code.
327339
The "Constant declaration" AST element
328340
has two child elements under it,
329341
which represent the two parts of a constant declaration:
330342
its name and its value.
331343
The "Macro call" element has child elements
332344
that represent the macro's name
333345
and the list of arguments being passed to the macro.
346+
334347
As part of constructing this AST,
335348
the compiler checks that the source code is valid Swift.
336349
For example, `#fourCharacterCode` takes a single argument,
@@ -370,7 +383,7 @@ by restricting the code that implements macros:
370383
In addition to these safeguards,
371384
the macro's author is responsible for not reading or modifying anything
372385
outside of the macro's inputs.
373-
For example, a macro's expansion can't depend on the current time of day.
386+
For example, a macro's expansion must not depend on the current time of day.
374387

375388
The implementation of `#fourCharacterCode`
376389
generates a new AST containing the expanded code.
@@ -469,7 +482,7 @@ and `MyProject` makes those macros available.
469482

470483
The implementation of a macro
471484
uses the [SwiftSyntax][] module to interact with Swift code
472-
in a structured way, using an abstract syntax trees (AST).
485+
in a structured way, using an AST.
473486
If you created a new macro package with Swift Package Manager,
474487
the generated `Package.swift` file
475488
automatically includes a dependency on SwiftSyntax.
@@ -538,7 +551,7 @@ conforms to the `ExpressionMacro` protocol.
538551
The `ExpressionMacro` protocol has one requirement,
539552
a `expansion(of:in:)` method that expands the AST.
540553
For the list of macro roles and their corresponding SwiftSystem protocols,
541-
see <doc:Attributes#attached> and <doc:Attributes:freestanding>
554+
see <doc:Attributes#attached> and <doc:Attributes#freestanding>
542555
in <doc:Attributes>
543556

544557
To expand the `#fourCharacterCode` macro,
@@ -646,9 +659,9 @@ https://github.com/apple/swift-syntax/blob/main/Sources/SwiftSyntaxBuilder/Synta
646659
## Developing and Debugging Macros
647660

648661
Macros are well suited to development using tests:
649-
they transform one AST into another AST
662+
They transform one AST into another AST
650663
without depending on any external state,
651-
and without making changes to any external state..
664+
and without making changes to any external state.
652665
In addition, you can create syntax nodes from a string literal,
653666
which simplifies setting up the input for a unit test.
654667
You can also read the `description` property of an AST

0 commit comments

Comments
 (0)