-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[stdlib] Allow a default for optional interpolations #80547
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
[stdlib] Allow a default for optional interpolations #80547
Conversation
This adds an `appendInterpolation` overload to `DefaultStringInterpolation` that includes a parameter for providing a default string when the value to interpolate is `nil`. This allows this kind of usage: let age: Int? = nil print("Your age is \(age, default: "timeless")") // Prints "Your age is timeless" Includes an additional fixit when optional values are interpolated to use this `default:` parameter.
lib/Sema/MiscDiagnostics.cpp
Outdated
@@ -5688,6 +5688,11 @@ static void diagnoseUnintendedOptionalBehavior(const Expr *E, | |||
.fixItInsertAfter(arg->getEndLoc(), ")"); | |||
|
|||
if (kind == UnintendedInterpolationKind::Optional) { | |||
// Suggest using a default interpolation value parameter. | |||
Ctx.Diags.diagnose(arg->getLoc(), diag::default_optional_parameter) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@beccadax Do you know if it would be possible to only provide this fixit when the string interpolation type is DefaultStringInterpolation
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you had the caller extract the ConcreteDeclRef
and pass it in, then yes, that ought to be possible.
As an aside, I'm thinking we might not want to suggest both \(_:default:)
and ??
, as they mean nearly the same thing. Maybe ??
if it's an Optional<String>
and \(_:default:)
otherwise?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated the logic so that the new fixit is only provided for DefaultStringInterpolation
types, and isn't provided for optional strings, since it's the same functionality as the preferred ??
. I left in the ??
suggestion for other optional types, since there could be some types or situations where a default value of that same type still makes sense.
(I also moved the String(describing:)
fix-it to be the last suggestion, since it feels like "fix this warning" should be preferred over "silence this warning".)
lib/Sema/MiscDiagnostics.cpp
Outdated
@@ -5688,6 +5688,11 @@ static void diagnoseUnintendedOptionalBehavior(const Expr *E, | |||
.fixItInsertAfter(arg->getEndLoc(), ")"); | |||
|
|||
if (kind == UnintendedInterpolationKind::Optional) { | |||
// Suggest using a default interpolation value parameter. | |||
Ctx.Diags.diagnose(arg->getLoc(), diag::default_optional_parameter) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you had the caller extract the ConcreteDeclRef
and pass it in, then yes, that ought to be possible.
As an aside, I'm thinking we might not want to suggest both \(_:default:)
and ??
, as they mean nearly the same thing. Maybe ??
if it's an Optional<String>
and \(_:default:)
otherwise?
} | ||
|
||
@backDeployed(before: SwiftStdlib 6.2) | ||
@_disfavoredOverload |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of using @_disfavoredOverload
for these, you should be able to provide a fourth T: TextOutputStreamable, T: CustomStringConvertible
overload. That's how regular appendInterpolation(_:)
works. (The type checker really penalizes disfavored overloads, so it's usually better to avoid it if you can.)
Also, consider making these @inlinable
. When I originally implemented DefaultStringInterpolation
, I did some interpolation performance benchmarking (the test should still be in the benchmark suite) and found that inlining really matters for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I didn't realize! I thought I was helping out by only providing three overloads instead of four. 🙃 I'll look at adding @inlinable
, too. Thanks!
This fix-it suppresses the string interpolation warning, but doesn't provide any additional affordance for dealing with the problem, so it shouldn't be the first suggestion.
Modify fix-its so the new interpolation API is only recommended for non-strings (since `??` is a more fluent resolution for strings) and when used in a `DefaultStringInterpolation` type (since other interpolation types won't have this API).
@swift-ci Please smoke test |
@swift-ci Please smoke test |
@swift-ci Please smoke test |
@swift-ci Please smoke test |
@swift-ci Please build toolchain |
@swift-ci Please build macOS toolchain |
@swift-ci Please build toolchain macOS platform |
@swift-ci Please smoke test |
@swift-ci Please smoke test |
@swift-ci Please smoke test Linux platform |
1 similar comment
@swift-ci Please smoke test Linux platform |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good — left minor feedback and a suggestion.
/// Interpolates the given optional value's textual representation, or the | ||
/// specified default string, into the string literal being created. | ||
/// | ||
/// You don't need to call this method directly. It is used by the compiler |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and in the other places this phrasing appears, missing a contraction:
/// You don't need to call this method directly. It is used by the compiler | |
/// You don't need to call this method directly. It's used by the compiler |
/// | ||
/// You don't need to call this method directly. It is used by the compiler | ||
/// when interpreting string interpolations where you provide a `default` | ||
/// parameter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code listing would benefit from a sentence introducing it — that might also give you an earlier place to frame the fact that the default interpolation value is used when the value's nil
.
Changes in 3973a36 look good to me! |
/// - value: The value to include in a string interpolation, if non-`nil`. | ||
/// - default: The string to include if `value` is `nil`. | ||
@backDeployed(before: SwiftStdlib 6.2) | ||
@inlinable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason for @backDeployed
+@inlinable
instead of just @_alwaysEmitIntoClient
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seemed like the right set, presuming that @inlinable
has different behavior than @_alwaysEmit...
. Do we have guidance about which to prefer/when between inlinable and always-emit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are very simple functions and I'm unsure if we want to commit these as ABI. With AEIC, the stdlib doesn't take the ABI hit and clients who don't use it don't pay for it either.
@swift-ci Please smoke test |
@swift-ci Please smoke test Windows platform |
This adds an `appendInterpolation` overload to `DefaultStringInterpolation` that includes a parameter for providing a default string when the value to interpolate is `nil`. This allows this kind of usage: ```swift let age: Int? = nil print("Your age is \(age, default: "timeless")") // Prints "Your age is timeless" ``` The change includes an additional fixit when optional values are interpolated, with a suggestion to use this `default:` parameter.
This adds an
appendInterpolation
overload toDefaultStringInterpolation
that includes a parameter for providing a default string when the value to interpolate isnil
. This allows this kind of usage:Includes an additional fixit when optional values are interpolated to use this
default:
parameter.