Skip to content

Commit 4e6f213

Browse files
authored
Fix incorrect discovery of effectful keywords in the lexical context. (#1482)
This PR fixes a bug in our code that discovers effectful keywords like `try` when expanding `#expect()`. ### Checklist: - [x] Code and documentation should follow the style of the [Style Guide](https://github.com/apple/swift-testing/blob/main/Documentation/StyleGuide.md). - [x] If public symbols are renamed or modified, DocC references should be updated.
1 parent cc6b359 commit 4e6f213

3 files changed

Lines changed: 38 additions & 19 deletions

File tree

Sources/TestingMacros/Support/ConditionArgumentParsing.swift

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -507,23 +507,32 @@ private func _parseCondition(from expr: ExprSyntax, for macro: some Freestanding
507507

508508
// MARK: -
509509

510-
/// Parse a condition argument from an arbitrary expression.
511-
///
512-
/// - Parameters:
513-
/// - expr: The condition expression to parse.
514-
/// - macro: The macro expression being expanded.
515-
/// - context: The macro context in which the expression is being parsed.
516-
///
517-
/// - Returns: An instance of ``Condition`` describing `expr`.
518-
func parseCondition(from expr: ExprSyntax, for macro: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext) -> Condition {
519-
// If the condition involves the `unsafe`, `try`, or `await` keywords, assume
520-
// we cannot expand it.
521-
let effectKeywordsToApply = findEffectKeywords(in: expr).union(findEffectKeywords(in: context))
522-
guard effectKeywordsToApply.intersection([.unsafe, .try, .await]).isEmpty else {
523-
return Condition(expression: expr)
524-
}
510+
extension ConditionMacro {
511+
/// Parse a condition argument from an arbitrary expression.
512+
///
513+
/// - Parameters:
514+
/// - expr: The condition expression to parse.
515+
/// - macro: The macro expression being expanded.
516+
/// - context: The macro context in which the expression is being parsed.
517+
///
518+
/// - Returns: An instance of ``Condition`` describing `expr`.
519+
static func parseCondition(from expr: ExprSyntax, for macro: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext) -> Condition {
520+
// If the condition involves the `unsafe`, `try`, or `await` keywords, assume
521+
// we cannot expand it.
522+
let effectKeywordsFromNode = findEffectKeywords(in: expr)
523+
guard effectKeywordsFromNode.intersection([.unsafe, .try, .await]).isEmpty else {
524+
return Condition(expression: expr)
525+
}
526+
let effectKeywordsInLexicalContext = findEffectKeywords(in: context)
527+
guard effectKeywordsInLexicalContext.intersection([.unsafe, .await]).isEmpty else {
528+
return Condition(expression: expr)
529+
}
530+
if !isThrowing && effectKeywordsInLexicalContext.contains(.try) {
531+
return Condition(expression: expr)
532+
}
525533

526-
_diagnoseTrivialBooleanValue(from: expr, for: macro, in: context)
527-
let result = _parseCondition(from: expr, for: macro, in: context)
528-
return result
534+
_diagnoseTrivialBooleanValue(from: expr, for: macro, in: context)
535+
let result = _parseCondition(from: expr, for: macro, in: context)
536+
return result
537+
}
529538
}

Sources/TestingMacros/Support/EffectfulExpressionHandling.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ func findEffectKeywords(in node: some SyntaxProtocol) -> Set<Keyword> {
106106
/// - Returns: A set of effectful keywords such as `await` that are present in
107107
/// `context` and would apply to an expression macro during its expansion.
108108
func findEffectKeywords(in context: some MacroExpansionContext) -> Set<Keyword> {
109-
let result = context.lexicalContext.reversed().lazy
109+
// The lexical context is ordered from innermost to outermost, and we are most
110+
// interested in keywords near the macro expansion, so don't use reversed().
111+
let result = context.lexicalContext.lazy
110112
.prefix { _continueKind(for: $0) == .visitChildren }
111113
.compactMap { $0.as(ExprSyntax.self) }
112114
.compactMap(_effectKeyword(for:))

Tests/TestingTests/MiscellaneousTests.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,14 @@ struct MultiLineSuite {
272272
_ = try #require(x?[...].last)
273273
}
274274

275+
extension Bool {
276+
func throwingValue() throws -> Bool { self }
277+
}
278+
279+
@Test(.hidden) func `Effectful keywords are found in the lexical context of an expression macro`() throws {
280+
try #expect(true.throwingValue())
281+
}
282+
275283
@Suite("Miscellaneous tests")
276284
struct MiscellaneousTests {
277285
@Test("Free function's name")

0 commit comments

Comments
 (0)