Skip to content

Improve the diagnostics for a bad exit test capture. #1146

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 12, 2025

Conversation

grynspan
Copy link
Contributor

@grynspan grynspan commented Jun 10, 2025

This PR improves the diagnostics presented at compile time when an exit test captures an unsupported value. For example, given the following (bad) exit test:

struct NonCodableValue {}

let x = NonCodableValue()
await #expect(processExitsWith: .success) { [x = x as NonCodableValue] in
  _ = x
}

We currently get diagnostics of the form:

🛑 Global function '__checkClosureCall(identifiedBy:encodingCapturedValues:processExitsWith:observing:performing:expression:comments:isRequired:isolation:sourceLocation:)' requires that 'NonCodableValue' conform to 'Decodable'
🛑 Global function '__checkClosureCall(identifiedBy:encodingCapturedValues:processExitsWith:observing:performing:expression:comments:isRequired:isolation:sourceLocation:)' requires that 'NonCodableValue' conform to 'Encodable'
⚠️ No 'async' operations occur within 'await' expression

None of which actually tell the developer (clearly) what's wrong. With this PR, we instead get:

🛑 Type of captured value 'x' must conform to 'Sendable' and 'Codable' (from macro '__capturedValue')

Much better! The diagnostic is attributed to the temporary file containing the expansion of #expect() rather than to the original source file, but I've opened an issue against swift-syntax with a fix in mind. Even with the misattribution, this diagnostic is still an improvement, yeah?

Note

Exit test value capture remains an experimental feature.

Checklist:

  • Code and documentation should follow the style of the Style Guide.
  • If public symbols are renamed or modified, DocC references should be updated.

This PR improves the diagnostics presented at compile time when an exit test captures an unsupported value. For example, given the following (bad) exit test:

```swift
struct NonCodableValue {}

let x = NonCodableValue()
await #expect(processExitsWith: .success) { [x = x as NonCodableValue] in
  _ = x
}
```

We currently get diagnostics of the form:

> 🛑 Global function '__checkClosureCall(identifiedBy:encodingCapturedValues:processExitsWith:observing:performing:expression:comments:isRequired:isolation:sourceLocation:)' requires that 'ExitTestTests.NonCodableValue' conform to 'Decodable'
> 🛑 Global function '__checkClosureCall(identifiedBy:encodingCapturedValues:processExitsWith:observing:performing:expression:comments:isRequired:isolation:sourceLocation:)' requires that 'ExitTestTests.NonCodableValue' conform to 'Encodable'
> ⚠️ No 'async' operations occur within 'await' expression

None of which actually tell the developer (clearly) what's wrong. With this PR, we instead get:

> 🛑 Type of captured value 'x' must conform to 'Sendable' and 'Codable' (from macro '__capturedValue')

Much better! The diagnostic is attributed to the temporary file containing the expansion of `#expect()` rather than to the original source file, but I've opened [an issue](swiftlang/swift-syntax#3085) against swift-syntax with a fix in mind. Even with the misattribution, this diagnostic is still an improvement, yeah?

> [!NOTE]
> Exit test value capture remains an experimental feature.
@grynspan grynspan added this to the Swift 6.x milestone Jun 10, 2025
@grynspan grynspan self-assigned this Jun 10, 2025
@grynspan grynspan added enhancement New feature or request exit-tests ☠️ Work related to exit tests parameterized-testing Related to parameterized testing functionality macros 🔭 Related to Swift macros such as @Test or #expect labels Jun 10, 2025
@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan grynspan merged commit eac4833 into main Jun 12, 2025
3 checks passed
@grynspan grynspan deleted the jgrynspan/improve-bad-capture-diagnostics branch June 12, 2025 23:35
grynspan added a commit that referenced this pull request Jun 13, 2025
…t test. (#1130)

This PR adds the ability to infer the type of a parameter of a function
or closure that encloses an exit test. For example, `x` here:

```swift
func f(x: Int) async {
  await #expect(processExitsWith: .failure) { [x] in
    ...
  }
}
```

This inference still fails if a parameter is shadowed by a variable with
an incompatible type; we still need something like `decltype()` to solve
for such cases. We emit a custom diagnostic of the form "🛑 Type of
captured value 'x' is ambiguous" if the inferred type of the captured
value doesn't match what the compiler thinks it is (see #1146). Still,
being able to capture `@Test` function arguments with minimal ceremony
is helpful:

```swift
@test(arguments: 0 ..< 100) func f(i: Int) async {
  await #expect(exitsWith: .failure) { [i] in
    ...
  }
}
```

Also type inference for literals because "why not?"

> [!NOTE]
> Exit test value capture remains an experimental feature.

### 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request exit-tests ☠️ Work related to exit tests macros 🔭 Related to Swift macros such as @Test or #expect parameterized-testing Related to parameterized testing functionality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants