Skip to content

Commit 5a95228

Browse files
authored
Suppress warning about #require(nonOptional) in some cases. (#947)
When using `try #require()` to unwrap an optional value, we emit a compile-time warning if the value is not actually optional. However, there is a bug in the type checker (swiftlang/swift#79202) that triggers a false positive when downcasting an object (of `class` type), e.g.: ```swift class Animal {} class Duck: Animal {} let beast: Animal = Duck() let definitelyADuck = try #require(beast as? Duck) // ⚠️ '#require(_:_:)' is redundant because 'beast as? Duck' never equals 'nil' ``` This change suppresses the warning we emit if the expression contains certain syntax tokens (namely `?`, `nil`, or `Optional`) on the assumption that their presence means the test author is expecting an optional value and we've hit a false positive. ### 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 6a49142 commit 5a95228

File tree

2 files changed

+33
-0
lines changed

2 files changed

+33
-0
lines changed

Sources/TestingMacros/ConditionMacro.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,24 @@ public struct NonOptionalRequireMacro: RefinedConditionMacro {
324324
in context: some MacroExpansionContext
325325
) throws -> ExprSyntax {
326326
if let argument = macro.arguments.first {
327+
#if !SWT_FIXED_137943258
328+
// Silence this warning if we see a token (`?`, `nil`, or "Optional") that
329+
// might indicate the test author expects the expression is optional.
330+
let tokenKindsIndicatingOptionality: [TokenKind] = [
331+
.infixQuestionMark,
332+
.postfixQuestionMark,
333+
.keyword(.nil),
334+
.identifier("Optional")
335+
]
336+
let looksOptional = argument.tokens(viewMode: .sourceAccurate).lazy
337+
.map(\.tokenKind)
338+
.contains(where: tokenKindsIndicatingOptionality.contains)
339+
if !looksOptional {
340+
context.diagnose(.nonOptionalRequireIsRedundant(argument.expression, in: macro))
341+
}
342+
#else
327343
context.diagnose(.nonOptionalRequireIsRedundant(argument.expression, in: macro))
344+
#endif
328345
}
329346

330347
// Perform the normal macro expansion for #require().

Tests/TestingMacrosTests/ConditionMacroTests.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,22 @@ struct ConditionMacroTests {
352352
#expect(diagnostic.message.contains("is redundant"))
353353
}
354354

355+
#if !SWT_FIXED_137943258
356+
@Test(
357+
"#require(optional value mistyped as non-optional) diagnostic is suppressed",
358+
.bug("https://github.com/swiftlang/swift/issues/79202"),
359+
arguments: [
360+
"#requireNonOptional(expression as? T)",
361+
"#requireNonOptional(expression as Optional<T>)",
362+
"#requireNonOptional(expression ?? nil)",
363+
]
364+
)
365+
func requireNonOptionalDiagnosticSuppressed(input: String) throws {
366+
let (_, diagnostics) = try parse(input)
367+
#expect(diagnostics.isEmpty)
368+
}
369+
#endif
370+
355371
@Test("#require(throws: Never.self) produces a diagnostic",
356372
arguments: [
357373
"#requireThrows(throws: Swift.Never.self)",

0 commit comments

Comments
 (0)