mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[6.2][stdlib] Allow a default for optional interpolations (#81360)
Cherry-pick of #80547 for the 6.2 release branch. --- Explanation: This cherry picks the implementation of SE-0477 to add a string interpolation method with a `default:` parameter for optional interpolation values. Main Branch PR: https://github.com/swiftlang/swift/pull/80547 Risk: Low. Reviewed By: @stephentyrone Resolves: rdar://150865613 Testing: New tests for the string interpolations and fix-its.
This commit is contained in:
@@ -4973,6 +4973,8 @@ NOTE(iuo_to_any_coercion_note_func_result,none,
|
||||
(const ValueDecl *))
|
||||
NOTE(default_optional_to_any,none,
|
||||
"provide a default value to avoid this warning", ())
|
||||
NOTE(default_optional_parameter,none,
|
||||
"use a default value parameter to avoid this warning", ())
|
||||
NOTE(force_optional_to_any,none,
|
||||
"force-unwrap the value to avoid this warning", ())
|
||||
NOTE(silence_optional_to_any,none,
|
||||
|
||||
@@ -5604,7 +5604,7 @@ static void diagnoseUnintendedOptionalBehavior(const Expr *E,
|
||||
segment->getCalledValue(/*skipFunctionConversions=*/true), kind))
|
||||
if (auto firstArg =
|
||||
getFirstArgIfUnintendedInterpolation(segment->getArgs(), kind))
|
||||
diagnoseUnintendedInterpolation(firstArg, kind);
|
||||
diagnoseUnintendedInterpolation(segment, firstArg, kind);
|
||||
}
|
||||
|
||||
bool interpolationWouldBeUnintended(ConcreteDeclRef appendMethod,
|
||||
@@ -5670,13 +5670,40 @@ static void diagnoseUnintendedOptionalBehavior(const Expr *E,
|
||||
return firstArg;
|
||||
}
|
||||
|
||||
void diagnoseUnintendedInterpolation(Expr * arg, UnintendedInterpolationKind kind) {
|
||||
std::string baseInterpolationTypeName(CallExpr *segment) {
|
||||
if (auto selfApplyExpr = dyn_cast<SelfApplyExpr>(segment->getFn())) {
|
||||
auto baseType = selfApplyExpr->getBase()->getType();
|
||||
return baseType->getWithoutSpecifierType()->getString();
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
void diagnoseUnintendedInterpolation(CallExpr *segment,
|
||||
Expr * arg,
|
||||
UnintendedInterpolationKind kind) {
|
||||
Ctx.Diags
|
||||
.diagnose(arg->getStartLoc(),
|
||||
diag::debug_description_in_string_interpolation_segment,
|
||||
(bool)kind)
|
||||
.highlight(arg->getSourceRange());
|
||||
|
||||
if (kind == UnintendedInterpolationKind::Optional) {
|
||||
auto wrappedArgType = arg->getType()->getRValueType()->getOptionalObjectType();
|
||||
auto baseTypeName = baseInterpolationTypeName(segment);
|
||||
|
||||
// Suggest using a default value parameter, but only for non-string values
|
||||
// when the base interpolation type is the default.
|
||||
if (!wrappedArgType->isString() && baseTypeName == "DefaultStringInterpolation")
|
||||
Ctx.Diags.diagnose(arg->getLoc(), diag::default_optional_parameter)
|
||||
.highlight(arg->getSourceRange())
|
||||
.fixItInsertAfter(arg->getEndLoc(), ", default: <#default value#>");
|
||||
|
||||
// Suggest providing a default value using the nil-coalescing operator.
|
||||
Ctx.Diags.diagnose(arg->getLoc(), diag::default_optional_to_any)
|
||||
.highlight(arg->getSourceRange())
|
||||
.fixItInsertAfter(arg->getEndLoc(), " ?? <#default value#>");
|
||||
}
|
||||
|
||||
// Suggest 'String(describing: <expr>)'.
|
||||
auto argStart = arg->getStartLoc();
|
||||
Ctx.Diags
|
||||
@@ -5686,13 +5713,6 @@ static void diagnoseUnintendedOptionalBehavior(const Expr *E,
|
||||
.highlight(arg->getSourceRange())
|
||||
.fixItInsert(argStart, "String(describing: ")
|
||||
.fixItInsertAfter(arg->getEndLoc(), ")");
|
||||
|
||||
if (kind == UnintendedInterpolationKind::Optional) {
|
||||
// Suggest inserting a default value.
|
||||
Ctx.Diags.diagnose(arg->getLoc(), diag::default_optional_to_any)
|
||||
.highlight(arg->getSourceRange())
|
||||
.fixItInsertAfter(arg->getEndLoc(), " ?? <#default value#>");
|
||||
}
|
||||
}
|
||||
|
||||
PreWalkResult<Expr *> walkToExprPre(Expr *E) override {
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
/// Represents a string literal with interpolations while it is being built up.
|
||||
/// Represents a string literal with interpolations while it's being built up.
|
||||
///
|
||||
/// Do not create an instance of this type directly. It is used by the compiler
|
||||
/// when you create a string using string interpolation. Instead, use string
|
||||
/// interpolation to create a new string by including values, literals,
|
||||
/// You don't need to create an instance of this type directly. It's used by the
|
||||
/// compiler when you create a string using string interpolation. Instead, use
|
||||
/// string interpolation to create a new string by including values, literals,
|
||||
/// variables, or expressions enclosed in parentheses, prefixed by a
|
||||
/// backslash (`\(`...`)`).
|
||||
///
|
||||
@@ -68,8 +68,8 @@ public struct DefaultStringInterpolation: StringInterpolationProtocol, Sendable
|
||||
/// Creates a string interpolation with storage pre-sized for a literal
|
||||
/// with the indicated attributes.
|
||||
///
|
||||
/// Do not call this initializer directly. It is used by the compiler when
|
||||
/// interpreting string interpolations.
|
||||
/// You don't need to call this initializer directly. It's used by the
|
||||
/// compiler when interpreting string interpolations.
|
||||
@inlinable
|
||||
public init(literalCapacity: Int, interpolationCount: Int) {
|
||||
let capacityPerInterpolation = 2
|
||||
@@ -80,8 +80,8 @@ public struct DefaultStringInterpolation: StringInterpolationProtocol, Sendable
|
||||
|
||||
/// Appends a literal segment of a string interpolation.
|
||||
///
|
||||
/// Do not call this method directly. It is used by the compiler when
|
||||
/// interpreting string interpolations.
|
||||
/// You don't need to call this method directly. It's used by the compiler
|
||||
/// when interpreting string interpolations.
|
||||
@inlinable
|
||||
public mutating func appendLiteral(_ literal: String) {
|
||||
literal.write(to: &self)
|
||||
@@ -90,8 +90,8 @@ public struct DefaultStringInterpolation: StringInterpolationProtocol, Sendable
|
||||
/// Interpolates the given value's textual representation into the
|
||||
/// string literal being created.
|
||||
///
|
||||
/// Do not call this method directly. It is used by the compiler when
|
||||
/// interpreting string interpolations. Instead, use string
|
||||
/// You don't need to call this method directly. It's used by the compiler
|
||||
/// when interpreting string interpolations. Instead, use string
|
||||
/// interpolation to create a new string by including values, literals,
|
||||
/// variables, or expressions enclosed in parentheses, prefixed by a
|
||||
/// backslash (`\(`...`)`).
|
||||
@@ -114,8 +114,8 @@ public struct DefaultStringInterpolation: StringInterpolationProtocol, Sendable
|
||||
/// Interpolates the given value's textual representation into the
|
||||
/// string literal being created.
|
||||
///
|
||||
/// Do not call this method directly. It is used by the compiler when
|
||||
/// interpreting string interpolations. Instead, use string
|
||||
/// You don't need to call this method directly. It's used by the compiler
|
||||
/// when interpreting string interpolations. Instead, use string
|
||||
/// interpolation to create a new string by including values, literals,
|
||||
/// variables, or expressions enclosed in parentheses, prefixed by a
|
||||
/// backslash (`\(`...`)`).
|
||||
@@ -136,8 +136,8 @@ public struct DefaultStringInterpolation: StringInterpolationProtocol, Sendable
|
||||
/// Interpolates the given value's textual representation into the
|
||||
/// string literal being created.
|
||||
///
|
||||
/// Do not call this method directly. It is used by the compiler when
|
||||
/// interpreting string interpolations. Instead, use string
|
||||
/// You don't need to call this method directly. It's used by the compiler
|
||||
/// when interpreting string interpolations. Instead, use string
|
||||
/// interpolation to create a new string by including values, literals,
|
||||
/// variables, or expressions enclosed in parentheses, prefixed by a
|
||||
/// backslash (`\(`...`)`).
|
||||
@@ -160,8 +160,8 @@ public struct DefaultStringInterpolation: StringInterpolationProtocol, Sendable
|
||||
/// Interpolates the given value's textual representation into the
|
||||
/// string literal being created.
|
||||
///
|
||||
/// Do not call this method directly. It is used by the compiler when
|
||||
/// interpreting string interpolations. Instead, use string
|
||||
/// You don't need to call this method directly. It's used by the compiler
|
||||
/// when interpreting string interpolations. Instead, use string
|
||||
/// interpolation to create a new string by including values, literals,
|
||||
/// variables, or expressions enclosed in parentheses, prefixed by a
|
||||
/// backslash (`\(`...`)`).
|
||||
@@ -197,6 +197,128 @@ public struct DefaultStringInterpolation: StringInterpolationProtocol, Sendable
|
||||
}
|
||||
}
|
||||
|
||||
extension DefaultStringInterpolation {
|
||||
/// 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's used by the compiler
|
||||
/// when interpreting string interpolations where you provide a `default`
|
||||
/// parameter. For example, the following code implicitly calls this method,
|
||||
/// using the value of the `default` parameter when `value` is `nil`:
|
||||
///
|
||||
/// var age: Int? = 48
|
||||
/// print("Your age is \(age, default: "unknown")")
|
||||
/// // Prints: Your age is 48
|
||||
/// age = nil
|
||||
/// print("Your age is \(age, default: "unknown")")
|
||||
/// // Prints: Your age is unknown
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - value: The value to include in a string interpolation, if non-`nil`.
|
||||
/// - default: The string to include if `value` is `nil`.
|
||||
@_alwaysEmitIntoClient
|
||||
public mutating func appendInterpolation<T>(
|
||||
_ value: T?,
|
||||
default: @autoclosure () -> some StringProtocol
|
||||
) where T: TextOutputStreamable, T: CustomStringConvertible {
|
||||
if let value {
|
||||
self.appendInterpolation(value)
|
||||
} else {
|
||||
self.appendInterpolation(`default`())
|
||||
}
|
||||
}
|
||||
|
||||
/// 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's used by the compiler
|
||||
/// when interpreting string interpolations where you provide a `default`
|
||||
/// parameter. For example, the following code implicitly calls this method,
|
||||
/// using the value of the `default` parameter when `value` is `nil`:
|
||||
///
|
||||
/// var age: Int? = 48
|
||||
/// print("Your age is \(age, default: "unknown")")
|
||||
/// // Prints: Your age is 48
|
||||
/// age = nil
|
||||
/// print("Your age is \(age, default: "unknown")")
|
||||
/// // Prints: Your age is unknown
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - value: The value to include in a string interpolation, if non-`nil`.
|
||||
/// - default: The string to include if `value` is `nil`.
|
||||
@_alwaysEmitIntoClient
|
||||
public mutating func appendInterpolation<T>(
|
||||
_ value: T?,
|
||||
default: @autoclosure () -> some StringProtocol
|
||||
) where T: TextOutputStreamable {
|
||||
if let value {
|
||||
self.appendInterpolation(value)
|
||||
} else {
|
||||
self.appendInterpolation(`default`())
|
||||
}
|
||||
}
|
||||
|
||||
/// 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's used by the compiler
|
||||
/// when interpreting string interpolations where you provide a `default`
|
||||
/// parameter. For example, the following code implicitly calls this method,
|
||||
/// using the value of the `default` parameter when `value` is `nil`:
|
||||
///
|
||||
/// var age: Int? = 48
|
||||
/// print("Your age is \(age, default: "unknown")")
|
||||
/// // Prints: Your age is 48
|
||||
/// age = nil
|
||||
/// print("Your age is \(age, default: "unknown")")
|
||||
/// // Prints: Your age is unknown
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - value: The value to include in a string interpolation, if non-`nil`.
|
||||
/// - default: The string to include if `value` is `nil`.
|
||||
@_alwaysEmitIntoClient
|
||||
public mutating func appendInterpolation<T>(
|
||||
_ value: T?,
|
||||
default: @autoclosure () -> some StringProtocol
|
||||
) where T: CustomStringConvertible {
|
||||
if let value {
|
||||
self.appendInterpolation(value)
|
||||
} else {
|
||||
self.appendInterpolation(`default`())
|
||||
}
|
||||
}
|
||||
|
||||
/// 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's used by the compiler
|
||||
/// when interpreting string interpolations where you provide a `default`
|
||||
/// parameter. For example, the following code implicitly calls this method,
|
||||
/// using the value of the `default` parameter when `value` is `nil`:
|
||||
///
|
||||
/// var age: Int? = 48
|
||||
/// print("Your age is \(age, default: "unknown")")
|
||||
/// // Prints: Your age is 48
|
||||
/// age = nil
|
||||
/// print("Your age is \(age, default: "unknown")")
|
||||
/// // Prints: Your age is unknown
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - value: The value to include in a string interpolation, if non-`nil`.
|
||||
/// - default: The string to include if `value` is `nil`.
|
||||
@_alwaysEmitIntoClient
|
||||
public mutating func appendInterpolation<T>(
|
||||
_ value: T?,
|
||||
default: @autoclosure () -> some StringProtocol
|
||||
) {
|
||||
if let value {
|
||||
self.appendInterpolation(value)
|
||||
} else {
|
||||
self.appendInterpolation(`default`())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension DefaultStringInterpolation: CustomStringConvertible {
|
||||
@inlinable
|
||||
public var description: String {
|
||||
@@ -220,9 +342,9 @@ extension DefaultStringInterpolation: TextOutputStream {
|
||||
extension String {
|
||||
/// Creates a new instance from an interpolated string literal.
|
||||
///
|
||||
/// Do not call this initializer directly. It is used by the compiler when
|
||||
/// you create a string using string interpolation. Instead, use string
|
||||
/// interpolation to create a new string by including values, literals,
|
||||
/// You don't need to call this initializer directly. It's used by the
|
||||
/// compiler when you create a string using string interpolation. Instead, use
|
||||
/// string interpolation to create a new string by including values, literals,
|
||||
/// variables, or expressions enclosed in parentheses, prefixed by a
|
||||
/// backslash (`\(`...`)`).
|
||||
///
|
||||
@@ -244,9 +366,9 @@ extension String {
|
||||
extension Substring {
|
||||
/// Creates a new instance from an interpolated string literal.
|
||||
///
|
||||
/// Do not call this initializer directly. It is used by the compiler when
|
||||
/// you create a string using string interpolation. Instead, use string
|
||||
/// interpolation to create a new string by including values, literals,
|
||||
/// You don't need to call this initializer directly. It's used by the
|
||||
/// compiler when you create a string using string interpolation. Instead, use
|
||||
/// string interpolation to create a new string by including values, literals,
|
||||
/// variables, or expressions enclosed in parentheses, prefixed by a
|
||||
/// backslash (`\(`...`)`).
|
||||
///
|
||||
|
||||
@@ -38,8 +38,9 @@ func rdar29691909(o: AnyObject) -> Any? {
|
||||
|
||||
func rdar29907555(_ value: Any!) -> String {
|
||||
return "\(value)" // expected-warning {{string interpolation produces a debug description for an optional value; did you mean to make this explicit?}}
|
||||
// expected-note@-1 {{use 'String(describing:)' to silence this warning}}
|
||||
// expected-note@-1 {{use a default value parameter to avoid this warning}}
|
||||
// expected-note@-2 {{provide a default value to avoid this warning}}
|
||||
// expected-note@-3 {{use 'String(describing:)' to silence this warning}}
|
||||
}
|
||||
|
||||
// https://github.com/apple/swift/issues/46300
|
||||
|
||||
@@ -295,10 +295,10 @@ func resyncParserB14() {}
|
||||
var stringInterp = "\(#^STRING_INTERP_3?check=STRING_INTERP^#)"
|
||||
_ = "" + "\(#^STRING_INTERP_4?check=STRING_INTERP^#)" + ""
|
||||
// STRING_INTERP-DAG: Decl[InstanceMethod]/CurrNominal/Flair[ArgLabels]/IsSystem: ['(']{#(value): any Any.Type#}[')'][#Void#];
|
||||
// STRING_INTERP-DAG: Decl[Struct]/CurrModule: FooStruct[#FooStruct#]; name=FooStruct
|
||||
// STRING_INTERP-DAG: Decl[FreeFunction]/CurrModule/TypeRelation[Invalid]: fooFunc1()[#Void#];
|
||||
// STRING_INTERP-DAG: Decl[FreeFunction]/CurrModule: optStr()[#String?#];
|
||||
// STRING_INTERP-DAG: Decl[GlobalVar]/Local: fooObject[#FooStruct#];
|
||||
// STRING_INTERP-DAG: Decl[Struct]/CurrModule/TypeRelation[Convertible]: FooStruct[#FooStruct#]; name=FooStruct
|
||||
// STRING_INTERP-DAG: Decl[FreeFunction]/CurrModule/TypeRelation[Convertible]: fooFunc1[#() -> ()#]; name=fooFunc1
|
||||
// STRING_INTERP-DAG: Decl[FreeFunction]/CurrModule/TypeRelation[Convertible]: optStr()[#String?#]; name=optStr()
|
||||
// STRING_INTERP-DAG: Decl[GlobalVar]/Local/TypeRelation[Convertible]: fooObject[#FooStruct#]; name=fooObject
|
||||
func resyncParserC1() {}
|
||||
|
||||
// FOR_COLLECTION-NOT: forIndex
|
||||
|
||||
@@ -27,18 +27,18 @@ protocol FooProtocol {
|
||||
typealias FooTypealias = Int
|
||||
|
||||
// Function parameter
|
||||
// COMMON-DAG: Decl[LocalVar]/Local: fooParam[#FooStruct#]{{; name=.+$}}
|
||||
// COMMON-DAG: Decl[LocalVar]/Local{{(/TypeRelation\[Convertible\])?}}: fooParam[#FooStruct#]; name=fooParam
|
||||
// Global completions
|
||||
// COMMON-DAG: Decl[Struct]/CurrModule: FooStruct[#FooStruct#]{{; name=.+$}}
|
||||
// COMMON-DAG: Decl[Enum]/CurrModule: FooEnum[#FooEnum#]{{; name=.+$}}
|
||||
// COMMON-DAG: Decl[Class]/CurrModule: FooClass[#FooClass#]{{; name=.+$}}
|
||||
// COMMON-DAG: Decl[Protocol]/CurrModule/Flair[RareType]: FooProtocol[#FooProtocol#]{{; name=.+$}}
|
||||
// COMMON-DAG: Decl[Struct]/CurrModule{{(/TypeRelation\[Convertible\])?}}: FooStruct[#FooStruct#]{{; name=.+$}}
|
||||
// COMMON-DAG: Decl[Enum]/CurrModule{{(/TypeRelation\[Convertible\])?}}: FooEnum[#FooEnum#]{{; name=.+$}}
|
||||
// COMMON-DAG: Decl[Class]/CurrModule{{(/TypeRelation\[Convertible\])?}}: FooClass[#FooClass#]{{; name=.+$}}
|
||||
// COMMON-DAG: Decl[Protocol]/CurrModule/Flair[RareType]{{(/TypeRelation\[Convertible\])?}}: FooProtocol[#FooProtocol#]{{; name=.+$}}
|
||||
// COMMON-DAG: Decl[TypeAlias]/CurrModule{{(/TypeRelation\[Convertible\])?}}: FooTypealias[#Int#]{{; name=.+$}}
|
||||
// COMMON-DAG: Decl[GlobalVar]/CurrModule: fooObject[#FooStruct#]{{; name=.+$}}
|
||||
// COMMON-DAG: Decl[GlobalVar]/CurrModule{{(/TypeRelation\[Convertible\])?}}: fooObject[#FooStruct#]{{; name=.+$}}
|
||||
// COMMON-DAG: Keyword[try]/None: try{{; name=.+$}}
|
||||
// COMMON-DAG: Literal[Boolean]/None{{(/TypeRelation\[Convertible\])?}}: true[#Bool#]{{; name=.+$}}
|
||||
// COMMON-DAG: Literal[Boolean]/None{{(/TypeRelation\[Convertible\])?}}: false[#Bool#]{{; name=.+$}}
|
||||
// COMMON-DAG: Literal[Nil]/None: nil{{; name=.+$}}
|
||||
// COMMON-DAG: Literal[Nil]/None{{(/TypeRelation\[Convertible\])?}}: nil{{.*; name=.+$}}
|
||||
// COMMON-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem{{(/TypeRelation\[Convertible\])?}}: Int8[#Int8#]{{; name=.+$}}
|
||||
// COMMON-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem{{(/TypeRelation\[Convertible\])?}}: Int16[#Int16#]{{; name=.+$}}
|
||||
// COMMON-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem{{(/TypeRelation\[Convertible\])?}}: Int32[#Int32#]{{; name=.+$}}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
// GENERICPARAM: Decl[GenericTypeParam]/Local: Self[#Self#];
|
||||
|
||||
// STATICSELF: Keyword[Self]/CurrNominal: Self[#S#];
|
||||
// STATICSELF: Keyword[Self]/CurrNominal{{(/TypeRelation\[Convertible\])?}}: Self[#S#];
|
||||
|
||||
// DYNAMICSELF: Keyword[Self]/CurrNominal: Self[#Self#];
|
||||
// DYNAMICSELF: Keyword[Self]/CurrNominal{{(/TypeRelation\[Convertible\])?}}: Self[#Self#];
|
||||
|
||||
func freeFunc() {
|
||||
#^GLOBAL_BODY_EXPR?check=NOSELF^#
|
||||
|
||||
@@ -1123,13 +1123,13 @@ func testInterpolatedString1() {
|
||||
|
||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: lazyInstanceVar[#Int#]{{; name=.+$}}
|
||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: instanceVar[#Int#]{{; name=.+$}}
|
||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: instanceFunc0()[#Void#]{{; name=.+$}}
|
||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}}
|
||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: instanceFunc2({#(a): Int#}, {#b: &Double#})[#Void#]{{; name=.+$}}
|
||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: instanceFunc3({#(a): Int#}, {#(Float, Double)#})[#Void#]{{; name=.+$}}
|
||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: instanceFunc4({#(a): Int?#}, {#b: Int!#}, {#c: &Int?#}, {#d: &Int!#})[#Void#]{{; name=.+$}}
|
||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc5()[#Int?#]{{; name=.+$}}
|
||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc6()[#Int!#]{{; name=.+$}}
|
||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Convertible]: instanceFunc0[#() -> ()#]; name=instanceFunc0
|
||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Convertible]: instanceFunc1(_:)[#(Int) -> ()#]; name=instanceFunc1(_:)
|
||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Convertible]: instanceFunc2(_:b:)[#(Int, inout Double) -> ()#]; name=instanceFunc2(_:b:)
|
||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Convertible]: instanceFunc3(_:_:)[#(Int, (Float, Double)) -> ()#]; name=instanceFunc3(_:_:)
|
||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Convertible]: instanceFunc4(_:b:c:d:)[#(Int?, Int?, inout Int?, inout Int?) -> ()#]; name=instanceFunc4(_:b:c:d:)
|
||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Convertible]: instanceFunc5()[#Int?#]; name=instanceFunc5()
|
||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Convertible]: instanceFunc6()[#Int!#]; name=instanceFunc6()
|
||||
|
||||
//===--- Check protocol extensions
|
||||
|
||||
|
||||
@@ -196,21 +196,35 @@ extension DefaultStringInterpolation {
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that provides a custom `StringInterpolation` type.
|
||||
struct CustomInterpolation: ExpressibleByStringInterpolation {
|
||||
struct StringInterpolation: StringInterpolationProtocol {
|
||||
init(literalCapacity: Int, interpolationCount: Int) {}
|
||||
mutating func appendLiteral(_ literal: String) {}
|
||||
mutating func appendInterpolation<T>(_ interp: T) {}
|
||||
}
|
||||
init(stringInterpolation: StringInterpolation) {}
|
||||
init(stringLiteral: String) {}
|
||||
}
|
||||
|
||||
func warnOptionalInStringInterpolationSegment(_ o : Int?) {
|
||||
print("Always some, Always some, Always some: \(o)")
|
||||
// expected-warning@-1 {{string interpolation produces a debug description for an optional value; did you mean to make this explicit?}}
|
||||
// expected-note@-2 {{use 'String(describing:)' to silence this warning}} {{51-51=String(describing: }} {{52-52=)}}
|
||||
// expected-note@-2 {{use a default value parameter to avoid this warning}} {{52-52=, default: <#default value#>}}
|
||||
// expected-note@-3 {{provide a default value to avoid this warning}} {{52-52= ?? <#default value#>}}
|
||||
// expected-note@-4 {{use 'String(describing:)' to silence this warning}} {{51-51=String(describing: }} {{52-52=)}}
|
||||
var i: Int? = o
|
||||
print("Always some, Always some, Always some: \(i)")
|
||||
// expected-warning@-1 {{string interpolation produces a debug description for an optional value; did you mean to make this explicit?}}
|
||||
// expected-note@-2 {{use 'String(describing:)' to silence this warning}} {{51-51=String(describing: }} {{52-52=)}}
|
||||
// expected-note@-2 {{use a default value parameter to avoid this warning}} {{52-52=, default: <#default value#>}}
|
||||
// expected-note@-3 {{provide a default value to avoid this warning}} {{52-52= ?? <#default value#>}}
|
||||
// expected-note@-4 {{use 'String(describing:)' to silence this warning}} {{51-51=String(describing: }} {{52-52=)}}
|
||||
i = nil
|
||||
print("Always some, Always some, Always some: \(o.map { $0 + 1 })")
|
||||
// expected-warning@-1 {{string interpolation produces a debug description for an optional value; did you mean to make this explicit?}}
|
||||
// expected-note@-2 {{use 'String(describing:)' to silence this warning}} {{51-51=String(describing: }} {{67-67=)}}
|
||||
// expected-note@-2 {{use a default value parameter to avoid this warning}} {{67-67=, default: <#default value#>}}
|
||||
// expected-note@-3 {{provide a default value to avoid this warning}} {{67-67= ?? <#default value#>}}
|
||||
// expected-note@-4 {{use 'String(describing:)' to silence this warning}} {{51-51=String(describing: }} {{67-67=)}}
|
||||
|
||||
print("Always some, Always some, Always some: \(o as Int?)") // No warning
|
||||
print("Always some, Always some, Always some: \(o.debugDescription)") // No warning.
|
||||
@@ -221,8 +235,24 @@ func warnOptionalInStringInterpolationSegment(_ o : Int?) {
|
||||
|
||||
print("Always some, Always some, Always some: \(ooST)")
|
||||
// expected-warning@-1 {{string interpolation produces a debug description for an optional value; did you mean to make this explicit?}}
|
||||
// expected-note@-2 {{use 'String(describing:)' to silence this warning}} {{51-51=String(describing: }} {{55-55=)}}
|
||||
// expected-note@-2 {{use a default value parameter to avoid this warning}} {{55-55=, default: <#default value#>}}
|
||||
// expected-note@-3 {{provide a default value to avoid this warning}} {{55-55= ?? <#default value#>}}
|
||||
// expected-note@-4 {{use 'String(describing:)' to silence this warning}} {{51-51=String(describing: }} {{55-55=)}}
|
||||
|
||||
// Don't provide a `\(_:default:)` fix-it for optional strings; the `??`
|
||||
// operator is the recommended warning resolution.
|
||||
let s: String? = o.map(String.init)
|
||||
print("Always some, Always some, Always some: \(s)")
|
||||
// expected-warning@-1 {{string interpolation produces a debug description for an optional value; did you mean to make this explicit?}}
|
||||
// expected-note@-2 {{provide a default value to avoid this warning}} {{52-52= ?? <#default value#>}}
|
||||
// expected-note@-3 {{use 'String(describing:)' to silence this warning}} {{51-51=String(describing: }} {{52-52=)}}
|
||||
|
||||
// Don't provide a `\(_:default:)` fix-it for interpolations that don't resolve
|
||||
// to strings.
|
||||
let _: CustomInterpolation = "Always some, Always some, Always some: \(o)"
|
||||
// expected-warning@-1 {{string interpolation produces a debug description for an optional value; did you mean to make this explicit?}}
|
||||
// expected-note@-2 {{provide a default value to avoid this warning}} {{75-75= ?? <#default value#>}}
|
||||
// expected-note@-3 {{use 'String(describing:)' to silence this warning}} {{74-74=String(describing: }} {{75-75=)}}
|
||||
}
|
||||
|
||||
// Make sure we don't double diagnose
|
||||
|
||||
@@ -92,6 +92,11 @@ PrintTests.test("StringInterpolation") {
|
||||
|
||||
let s2 = "aaa\(1)bbb\(2 as Any)"
|
||||
expectEqual("aaa1bbb2", s2)
|
||||
|
||||
let x: Int? = 1
|
||||
let y: Int? = nil
|
||||
let s3 = "aaa\(x, default: "NONE")bbb\(y, default: "NONE")"
|
||||
expectEqual("aaa1bbbNONE", s3)
|
||||
}
|
||||
|
||||
PrintTests.test("SubstringInterpolation") {
|
||||
@@ -100,6 +105,11 @@ PrintTests.test("SubstringInterpolation") {
|
||||
|
||||
let s2 = "aaa\(1)bbb\(2 as Any)" as Substring
|
||||
expectEqual("aaa1bbb2", s2)
|
||||
|
||||
let x: Int? = 1
|
||||
let y: Int? = nil
|
||||
let s3 = "aaa\(x, default: "NONE")bbb\(y, default: "NONE")" as Substring
|
||||
expectEqual("aaa1bbbNONE", s3)
|
||||
}
|
||||
|
||||
PrintTests.test("CustomStringInterpolation") {
|
||||
@@ -116,6 +126,11 @@ PrintTests.test("AutoCustomStringInterpolation") {
|
||||
|
||||
let s2 = ("aaa\(1)bbb\(2 as Any)" as MySimpleString).value
|
||||
expectEqual("aaa1bbb2", s2)
|
||||
|
||||
let x: Int? = 1
|
||||
let y: Int? = nil
|
||||
let s3 = ("aaa\(x, default: "NONE")bbb\(y, default: "NONE")" as MySimpleString).value
|
||||
expectEqual("aaa1bbbNONE", s3)
|
||||
}
|
||||
|
||||
PrintTests.test("CustomStringInterpolationExtra") {
|
||||
|
||||
Reference in New Issue
Block a user