Skip to content

Commit a162fa4

Browse files
committed
Add Expr.init(literal:)
Adds an `Expr.init(literal:)` which functions similarly to `appendInterpolation(literal:format:)` but works outside of interpolations. Also rewrite and reformat related documentation.
1 parent 2b3a28f commit a162fa4

File tree

2 files changed

+87
-19
lines changed

2 files changed

+87
-19
lines changed

Sources/SwiftSyntaxBuilder/ConvenienceInitializers.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,42 @@ extension DictionaryExpr {
9090
}
9191
}
9292

93+
// MARK: - Expr
94+
95+
extension Expr {
96+
/// Returns a syntax tree for an expression that represents the value of the
97+
/// provided instance. For example, passing an `Array<String>` will result in
98+
/// an array literal containing string literals:
99+
///
100+
/// let arrayExpr = Expr(literal: ["a", "b", "c"])
101+
/// // `arrayExpr` is a syntax tree like `["a", "b", "c"]`
102+
///
103+
/// This initializer is compatible with types that conform to
104+
/// ``ExpressibleByLiteralSyntax``. These include:
105+
///
106+
/// * `String` and `Substring`
107+
/// * `Int` and other integer types
108+
/// * `Double` and other floating-point types
109+
/// * `Bool`
110+
/// * `Array` and `Set` of conforming elements
111+
/// * `Dictionary` and `KeyValuePairs` of conforming keys and values
112+
/// * `Optional` of conforming wrapped value
113+
///
114+
/// Conformances will generally handle edge cases sensibly: `String` will
115+
/// use raw literals and escapes as needed, `Optional` will wrap a nested
116+
/// `nil` in `.some`, `Double` wil represent special values like infinities
117+
/// as code sequences like `.infinity`, etc. `Set` and `Dictionary` sort
118+
/// thier elements to improve stability.
119+
///
120+
/// Because of that intelligent behavior, this initializer is not guaranteed
121+
/// to produce a literal as the outermost syntax node, or even to have a
122+
/// literal anywhere in its syntax tree. Use a convenience initializer on a
123+
/// specific type if you need that exact type in the syntax tree.
124+
public init<Literal: ExpressibleByLiteralSyntax>(literal: Literal) {
125+
self.init(fromProtocol: literal.makeLiteralSyntax())
126+
}
127+
}
128+
93129
// MARK: - FloatLiteralExprSyntax
94130

95131
extension FloatLiteralExprSyntax: ExpressibleByFloatLiteral {

Sources/SwiftSyntaxBuilder/Syntax+StringInterpolation.swift

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -110,26 +110,26 @@ extension SyntaxStringInterpolation: StringInterpolationProtocol {
110110
self.appendInterpolation(buildable.formatted(using: format))
111111
}
112112

113+
/// Interpolates a literal or similar expression syntax equivalent to `value`.
114+
///
115+
/// - SeeAlso: ``Expr.init(literal:)``
113116
public mutating func appendInterpolation<Literal: ExpressibleByLiteralSyntax>(
114117
literal value: Literal,
115118
format: BasicFormat = BasicFormat()
116119
) {
117-
self.appendInterpolation(
118-
ExprSyntax(fromProtocol: value.makeLiteralSyntax()),
119-
format: format
120-
)
120+
self.appendInterpolation(Expr(literal: value), format: format)
121121
}
122122

123123
// This overload is technically redundant with the previous one, except that
124124
// it silences a warning about interpolating Optionals.
125+
/// Interpolates a literal or similar expression syntax equivalent to `value`.
126+
///
127+
/// - SeeAlso: ``Expr.init(literal:)``
125128
public mutating func appendInterpolation<Literal: ExpressibleByLiteralSyntax>(
126129
literal value: Literal?,
127130
format: BasicFormat = BasicFormat()
128131
) {
129-
self.appendInterpolation(
130-
ExprSyntax(fromProtocol: value.makeLiteralSyntax()),
131-
format: format
132-
)
132+
self.appendInterpolation(Expr(literal: value), format: format)
133133
}
134134
}
135135

@@ -157,23 +157,55 @@ enum SyntaxStringInterpolationError: Error, CustomStringConvertible {
157157
}
158158
}
159159

160-
/// A Swift type whose value can be represented directly in source code by a Swift literal.
160+
/// A Swift type whose value can be represented directly in source code by a
161+
/// Swift literal.
162+
///
163+
/// Conforming types do not *contain* Swift source code; rather, they can be
164+
/// *expressed* in Swift source code, and this protocol can be used to get
165+
/// whatever source code would do that. For example, `String` is
166+
/// `ExpressibleByLiteralSyntax` but `StringLiteralExprSyntax` is not.
167+
///
168+
/// This protocol is usually not used directly. Instead, conforming types can
169+
/// be turned into syntax trees using ``Expr.init(literal:)``:
161170
///
162-
/// Conforming types do not *contain* Swift source code; rather, they can be *expressed* in Swift source code, and this protocol can be used to get whatever source code would do that. For example, `String` is `ExpressibleByLiteralSyntax` but `StringLiteralExprSyntax` is not.
171+
/// let expr2 = Expr(literal: [0+1, 1+1, 2+1])
172+
/// // `expr2` is a syntax tree for `[1, 2, 3]`.
163173
///
164-
/// Conforming types can be interpolated into a Swift source code literal with the syntax `\(literal: <value>)`:
174+
/// Or interpolated into a Swift source code literal with the syntax
175+
/// `\(literal: <value>)`:
165176
///
166-
/// let greeting = "Hello, world!"
167-
/// let expr1 = ExprSyntax("print(\(literal: greeting))")
168-
/// // `expr1` is a syntax tree for `print("Hello, world!")`
177+
/// let greeting = "Hello, world!"
178+
/// let expr1 = ExprSyntax("print(\(literal: greeting))")
179+
/// // `expr1` is a syntax tree for `print("Hello, world!")`
169180
///
170-
/// Note that quote marks are automatically added around the contents; you don't have to write them yourself. The conformance will automatically ensure the contents are correctly escaped, possibly by using raw literals or other language features:
181+
/// Note that quote marks are automatically added around the contents of string
182+
/// literals; you don't have to write them yourself. The conformance for
183+
/// `String` will automatically ensure the contents are correctly escaped,
184+
/// possibly by using raw literals or other language features:
171185
///
172-
/// let msPath = "c:\\windows\\system32"
173-
/// let expr2 = ExprSyntax("open(\(literal: msPath))")
174-
/// // `expr2` might be a syntax tree for `open(#"c:\windows\system32"#)`
175-
/// // or for `open("c:\\windows\\system32")`.
186+
/// let msPath = "c:\\windows\\system32"
187+
/// let expr3 = ExprSyntax("open(\(literal: msPath))")
188+
/// // `expr3` might be a syntax tree for `open(#"c:\windows\system32"#)`
189+
/// // or for `open("c:\\windows\\system32")`.
190+
///
191+
/// Other conformances have similar intelligent behaviors: floating-point types
192+
/// produce correct syntax trees for infinities and NaNs, nested optionals
193+
/// produce `.some(nil)` where appropriate, etc.
176194
public protocol ExpressibleByLiteralSyntax {
195+
/// Returns a syntax tree that represents the value of this instance.
196+
///
197+
/// This method is usually not called directly. Instead, conforming types can
198+
/// be turned into syntax trees using ``Expr.init(literal:)``:
199+
///
200+
/// let expr2 = Expr(literal: [0+1, 1+1, 2+1])
201+
/// // `expr2` is a syntax tree for `[1, 2, 3]`.
202+
///
203+
/// Or interpolated into a Swift source code literal with the syntax
204+
/// `\(literal: <value>)`:
205+
///
206+
/// let greeting = "Hello, world!"
207+
/// let expr1 = ExprSyntax("print(\(literal: greeting))")
208+
/// // `expr1` is a syntax tree for `print("Hello, world!")`
177209
func makeLiteralSyntax() -> ExprSyntaxProtocol
178210
}
179211

0 commit comments

Comments
 (0)