Skip to content

Commit 1d7ba1f

Browse files
authored
Merge pull request #1095 from stmontgomery/main-6.2-merge
Merge 'main' branch to 'release/6.2'
2 parents 63d4ef2 + 954303c commit 1d7ba1f

File tree

7 files changed

+118
-6
lines changed

7 files changed

+118
-6
lines changed

CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@
88
# See https://swift.org/CONTRIBUTORS.txt for Swift project authors
99
#
1010

11-
* @stmontgomery @grynspan @briancroom @SeanROlszewski @suzannaratcliff
11+
* @stmontgomery @grynspan @briancroom @suzannaratcliff

Documentation/ABI/TestContent.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,10 @@ record's kind is a 32-bit unsigned value. The following kinds are defined:
100100
| `0x00000000` | – | Reserved (**do not use**) |
101101
| `0x74657374` | `'test'` | Test or suite declaration |
102102
| `0x65786974` | `'exit'` | Exit test |
103+
| `0x706c6179` | `'play'` | [Playground](https://github.com/apple/swift-play-experimental) |
103104

104-
<!-- When adding cases to this enumeration, be sure to also update the
105-
corresponding enumeration in TestContentGeneration.swift. -->
105+
<!-- The kind values listed in this table should be a superset of the cases in
106+
the `TestContentKind` enumeration. -->
106107

107108
If a test content record's `kind` field equals `0x00000000`, the values of all
108109
other fields in that record are undefined.

Sources/Testing/Events/Recorder/Event.ConsoleOutputRecorder.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ extension Event.ConsoleOutputRecorder {
323323
// text instead of just the symbol. Details may be multi-line messages,
324324
// so split the message on newlines and indent all lines to align them
325325
// to the indentation provided by the symbol.
326-
var lines = message.stringValue.split(whereSeparator: \.isNewline)
326+
var lines = message.stringValue.split(omittingEmptySubsequences: false, whereSeparator: \.isNewline)
327327
lines = CollectionOfOne(lines[0]) + lines.dropFirst().map { line in
328328
"\(padding) \(line)"
329329
}

Sources/TestingMacros/ConditionMacro.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,17 @@ extension ConditionMacro {
157157
expandedFunctionName = conditionArgument.expandedFunctionName
158158
}
159159

160-
// Capture any comments as well (either in source or as a macro argument.)
160+
// Capture any comments as well -- either in source, preceding the
161+
// expression macro or one of its lexical context nodes, or as an argument
162+
// to the macro.
161163
let commentsArrayExpr = ArrayExprSyntax {
164+
// Lexical context is ordered innermost-to-outermost, so reverse it to
165+
// maintain the expected order.
166+
for lexicalSyntaxNode in context.lexicalContext.trailingEffectExpressions.reversed() {
167+
for commentTraitExpr in createCommentTraitExprs(for: lexicalSyntaxNode) {
168+
ArrayElementSyntax(expression: commentTraitExpr)
169+
}
170+
}
162171
for commentTraitExpr in createCommentTraitExprs(for: macro) {
163172
ArrayElementSyntax(expression: commentTraitExpr)
164173
}

Sources/TestingMacros/Support/EffectfulExpressionHandling.swift

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import SwiftSyntax
1212
import SwiftSyntaxBuilder
1313
import SwiftSyntaxMacros
1414

15-
// MARK: - Finding effect keywords
15+
// MARK: - Finding effect keywords and expressions
1616

1717
/// A syntax visitor class that looks for effectful keywords in a given
1818
/// expression.
@@ -69,6 +69,21 @@ func findEffectKeywords(in node: some SyntaxProtocol, context: some MacroExpansi
6969
return effectFinder.effectKeywords
7070
}
7171

72+
extension BidirectionalCollection<Syntax> {
73+
/// The suffix of syntax nodes in this collection which are effectful
74+
/// expressions, such as those for `try` or `await`.
75+
var trailingEffectExpressions: some Collection<Syntax> {
76+
reversed()
77+
.prefix { node in
78+
// This could be simplified if/when swift-syntax introduces a protocol
79+
// which all effectful expression syntax node types conform to.
80+
// See https://github.com/swiftlang/swift-syntax/issues/3040
81+
node.is(TryExprSyntax.self) || node.is(AwaitExprSyntax.self) || node.is(UnsafeExprSyntax.self)
82+
}
83+
.reversed()
84+
}
85+
}
86+
7287
// MARK: - Inserting effect keywords/thunks
7388

7489
/// Make a function call expression to an effectful thunk function provided by
@@ -111,7 +126,13 @@ func applyEffectfulKeywords(_ effectfulKeywords: Set<Keyword>, to expr: some Exp
111126

112127
let needAwait = effectfulKeywords.contains(.await) && !expr.is(AwaitExprSyntax.self)
113128
let needTry = effectfulKeywords.contains(.try) && !expr.is(TryExprSyntax.self)
129+
130+
// The 'unsafe' keyword was introduced in 6.2 as part of SE-0458. Older
131+
// toolchains are not aware of it, so avoid emitting expressions involving
132+
// that keyword when the macro has been built using an older toolchain.
133+
#if compiler(>=6.2)
114134
let needUnsafe = effectfulKeywords.contains(.unsafe) && !expr.is(UnsafeExprSyntax.self)
135+
#endif
115136

116137
// First, add thunk function calls.
117138
if needAwait {
@@ -120,9 +141,11 @@ func applyEffectfulKeywords(_ effectfulKeywords: Set<Keyword>, to expr: some Exp
120141
if needTry {
121142
expr = _makeCallToEffectfulThunk(.identifier("__requiringTry"), passing: expr)
122143
}
144+
#if compiler(>=6.2)
123145
if needUnsafe {
124146
expr = _makeCallToEffectfulThunk(.identifier("__requiringUnsafe"), passing: expr)
125147
}
148+
#endif
126149

127150
// Then add keyword expressions. (We do this separately so we end up writing
128151
// `try await __r(__r(self))` instead of `try __r(await __r(self))` which is
@@ -143,6 +166,7 @@ func applyEffectfulKeywords(_ effectfulKeywords: Set<Keyword>, to expr: some Exp
143166
)
144167
)
145168
}
169+
#if compiler(>=6.2)
146170
if needUnsafe {
147171
expr = ExprSyntax(
148172
UnsafeExprSyntax(
@@ -151,6 +175,7 @@ func applyEffectfulKeywords(_ effectfulKeywords: Set<Keyword>, to expr: some Exp
151175
)
152176
)
153177
}
178+
#endif
154179

155180
expr.leadingTrivia = originalExpr.leadingTrivia
156181
expr.trailingTrivia = originalExpr.trailingTrivia

Tests/TestingMacrosTests/ConditionMacroTests.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,59 @@ struct ConditionMacroTests {
240240
// Capture me
241241
Testing.__checkValue(try x(), expression: .__fromSyntaxNode("try x()"), comments: [.__line("// Capture me")], isRequired: false, sourceLocation: Testing.SourceLocation.__here()).__expected()
242242
""",
243+
244+
"""
245+
// Capture me
246+
try #expect(x)
247+
""":
248+
"""
249+
// Capture me
250+
try Testing.__checkValue(x, expression: .__fromSyntaxNode("x"), comments: [.__line("// Capture me")], isRequired: false, sourceLocation: Testing.SourceLocation.__here()).__expected()
251+
""",
252+
253+
"""
254+
// Capture me
255+
await #expect(x)
256+
""":
257+
"""
258+
// Capture me
259+
await Testing.__checkValue(x, expression: .__fromSyntaxNode("x"), comments: [.__line("// Capture me")], isRequired: false, sourceLocation: Testing.SourceLocation.__here()).__expected()
260+
""",
261+
262+
"""
263+
// Ignore me
264+
265+
// Comment for try
266+
try
267+
// Comment for await
268+
await
269+
// Comment for expect
270+
#expect(x)
271+
""":
272+
"""
273+
// Comment for try
274+
try
275+
// Comment for await
276+
await
277+
// Comment for expect
278+
Testing.__checkValue(x, expression: .__fromSyntaxNode("x"), comments: [.__line("// Comment for try"), .__line("// Comment for await"), .__line("// Comment for expect")], isRequired: false, sourceLocation: Testing.SourceLocation.__here()).__expected()
279+
""",
280+
281+
"""
282+
// Ignore me
283+
func example() {
284+
// Capture me
285+
#expect(x())
286+
}
287+
""":
288+
"""
289+
func example() {
290+
// Capture me
291+
Testing.__checkFunctionCall((), calling: { _ in
292+
x()
293+
}, expression: .__fromFunctionCall(nil, "x"), comments: [.__line("// Capture me")], isRequired: false, sourceLocation: Testing.SourceLocation.__here()).__expected()
294+
}
295+
""",
243296
]
244297
)
245298
func commentCapture(input: String, expectedOutput: String) throws {

Tests/TestingTests/EventRecorderTests.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,30 @@ struct EventRecorderTests {
257257
}
258258
#endif
259259

260+
@Test(
261+
"Uncommonly-formatted comments",
262+
.bug("rdar://149482060"),
263+
arguments: [
264+
"", // Empty string
265+
"\n\n\n", // Only newlines
266+
"\nFoo\n\nBar\n\n\nBaz\n", // Newlines interspersed with non-empty strings
267+
]
268+
)
269+
func uncommonComments(text: String) async throws {
270+
let stream = Stream()
271+
272+
var configuration = Configuration()
273+
configuration.eventHandlingOptions.isWarningIssueRecordedEventEnabled = true
274+
let eventRecorder = Event.ConsoleOutputRecorder(writingUsing: stream.write)
275+
configuration.eventHandler = { event, context in
276+
eventRecorder.record(event, in: context)
277+
}
278+
279+
await Test {
280+
Issue.record(Comment(rawValue: text) /* empty */)
281+
}.run(configuration: configuration)
282+
}
283+
260284
@available(_regexAPI, *)
261285
@Test("Issue counts are omitted on a successful test")
262286
func issueCountOmittedForPassingTest() async throws {

0 commit comments

Comments
 (0)