diff --git a/Sources/TestingMacros/Support/ConditionArgumentParsing.swift b/Sources/TestingMacros/Support/ConditionArgumentParsing.swift index d59d7600d..30f0cd430 100644 --- a/Sources/TestingMacros/Support/ConditionArgumentParsing.swift +++ b/Sources/TestingMacros/Support/ConditionArgumentParsing.swift @@ -312,17 +312,6 @@ private func _exprFromOptionalChainedExpr(_ expr: some ExprSyntaxProtocol) -> (E /// /// - Returns: An instance of ``Condition`` describing `expr`. private func _parseCondition(from expr: FunctionCallExprSyntax, for macro: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext) -> Condition { - // If the member function call involves the `try` or `await` keywords, assume - // we cannot expand it. This check cannot handle expressions like - // `try #expect(a.b(c))` where `b()` is throwing because the `try` keyword - // is outside the macro expansion. SEE: rdar://109470248 - let containsTryOrAwait = expr.tokens(viewMode: .sourceAccurate).lazy - .map(\.tokenKind) - .contains { $0 == .keyword(.try) || $0 == .keyword(.await) } - if containsTryOrAwait { - return Condition(expression: expr) - } - // We do not support function calls with trailing closures because the // transform required to forward them requires more information than is // available solely from the syntax tree. @@ -477,6 +466,22 @@ private func _parseCondition(negating expr: ExprSyntax, isParenthetical: Bool, f /// /// - Returns: An instance of ``Condition`` describing `expr`. private func _parseCondition(from expr: ExprSyntax, for macro: some FreestandingMacroExpansionSyntax, in context: some MacroExpansionContext) -> Condition { + // Handle closures with a single expression in them (e.g. { $0.foo() }) + if let closureExpr = expr.as(ClosureExprSyntax.self) { + return _parseCondition(from: closureExpr, for: macro, in: context) + } + + // If the condition involves the `try` or `await` keywords, assume we cannot + // expand it. This check cannot handle expressions like + // `try #expect(a.b(c))` where `b()` is throwing because the `try` keyword is + // outside the macro expansion. SEE: rdar://109470248 + let containsTryOrAwait = expr.tokens(viewMode: .sourceAccurate).lazy + .map(\.tokenKind) + .contains { $0 == .keyword(.try) || $0 == .keyword(.await) } + if containsTryOrAwait { + return Condition(expression: expr) + } + if let infixOperator = expr.as(InfixOperatorExprSyntax.self), let op = infixOperator.operator.as(BinaryOperatorExprSyntax.self) { return _parseCondition(from: expr, leftOperand: infixOperator.leftOperand, operator: op, rightOperand: infixOperator.rightOperand, for: macro, in: context) @@ -489,11 +494,6 @@ private func _parseCondition(from expr: ExprSyntax, for macro: some Freestanding return _parseCondition(from: asExpr, for: macro, in: context) } - // Handle closures with a single expression in them (e.g. { $0.foo() }) - if let closureExpr = expr.as(ClosureExprSyntax.self) { - return _parseCondition(from: closureExpr, for: macro, in: context) - } - // Handle function calls and member accesses. if let functionCallExpr = expr.as(FunctionCallExprSyntax.self) { return _parseCondition(from: functionCallExpr, for: macro, in: context) diff --git a/Tests/TestingTests/MiscellaneousTests.swift b/Tests/TestingTests/MiscellaneousTests.swift index 33980cf38..32a86df6b 100644 --- a/Tests/TestingTests/MiscellaneousTests.swift +++ b/Tests/TestingTests/MiscellaneousTests.swift @@ -123,6 +123,15 @@ struct SendableTests: Sendable { @Test(.hidden, arguments: FixtureData.stringReturningClosureArray) func parameterizedAcceptingFunction(f: @Sendable () -> String) {} + + @Test(.hidden) func throwKeywordInExpectation() throws { + #expect(UInt() == UInt(try #require(Int("0")))) + } + + @Test(.hidden) func asyncKeywordInExpectation() async { + func f() async -> Int { 0 } + #expect(UInt() == UInt(await f())) + } } @Suite("Named Sendable test type", .hidden)