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 *))
|
(const ValueDecl *))
|
||||||
NOTE(default_optional_to_any,none,
|
NOTE(default_optional_to_any,none,
|
||||||
"provide a default value to avoid this warning", ())
|
"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,
|
NOTE(force_optional_to_any,none,
|
||||||
"force-unwrap the value to avoid this warning", ())
|
"force-unwrap the value to avoid this warning", ())
|
||||||
NOTE(silence_optional_to_any,none,
|
NOTE(silence_optional_to_any,none,
|
||||||
|
|||||||
@@ -5604,7 +5604,7 @@ static void diagnoseUnintendedOptionalBehavior(const Expr *E,
|
|||||||
segment->getCalledValue(/*skipFunctionConversions=*/true), kind))
|
segment->getCalledValue(/*skipFunctionConversions=*/true), kind))
|
||||||
if (auto firstArg =
|
if (auto firstArg =
|
||||||
getFirstArgIfUnintendedInterpolation(segment->getArgs(), kind))
|
getFirstArgIfUnintendedInterpolation(segment->getArgs(), kind))
|
||||||
diagnoseUnintendedInterpolation(firstArg, kind);
|
diagnoseUnintendedInterpolation(segment, firstArg, kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool interpolationWouldBeUnintended(ConcreteDeclRef appendMethod,
|
bool interpolationWouldBeUnintended(ConcreteDeclRef appendMethod,
|
||||||
@@ -5670,13 +5670,40 @@ static void diagnoseUnintendedOptionalBehavior(const Expr *E,
|
|||||||
return firstArg;
|
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
|
Ctx.Diags
|
||||||
.diagnose(arg->getStartLoc(),
|
.diagnose(arg->getStartLoc(),
|
||||||
diag::debug_description_in_string_interpolation_segment,
|
diag::debug_description_in_string_interpolation_segment,
|
||||||
(bool)kind)
|
(bool)kind)
|
||||||
.highlight(arg->getSourceRange());
|
.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>)'.
|
// Suggest 'String(describing: <expr>)'.
|
||||||
auto argStart = arg->getStartLoc();
|
auto argStart = arg->getStartLoc();
|
||||||
Ctx.Diags
|
Ctx.Diags
|
||||||
@@ -5686,13 +5713,6 @@ static void diagnoseUnintendedOptionalBehavior(const Expr *E,
|
|||||||
.highlight(arg->getSourceRange())
|
.highlight(arg->getSourceRange())
|
||||||
.fixItInsert(argStart, "String(describing: ")
|
.fixItInsert(argStart, "String(describing: ")
|
||||||
.fixItInsertAfter(arg->getEndLoc(), ")");
|
.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 {
|
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
|
/// You don't need to create an instance of this type directly. It's used by the
|
||||||
/// when you create a string using string interpolation. Instead, use string
|
/// compiler when you create a string using string interpolation. Instead, use
|
||||||
/// interpolation to create a new string by including values, literals,
|
/// string interpolation to create a new string by including values, literals,
|
||||||
/// variables, or expressions enclosed in parentheses, prefixed by a
|
/// variables, or expressions enclosed in parentheses, prefixed by a
|
||||||
/// backslash (`\(`...`)`).
|
/// backslash (`\(`...`)`).
|
||||||
///
|
///
|
||||||
@@ -68,8 +68,8 @@ public struct DefaultStringInterpolation: StringInterpolationProtocol, Sendable
|
|||||||
/// Creates a string interpolation with storage pre-sized for a literal
|
/// Creates a string interpolation with storage pre-sized for a literal
|
||||||
/// with the indicated attributes.
|
/// with the indicated attributes.
|
||||||
///
|
///
|
||||||
/// Do not call this initializer directly. It is used by the compiler when
|
/// You don't need to call this initializer directly. It's used by the
|
||||||
/// interpreting string interpolations.
|
/// compiler when interpreting string interpolations.
|
||||||
@inlinable
|
@inlinable
|
||||||
public init(literalCapacity: Int, interpolationCount: Int) {
|
public init(literalCapacity: Int, interpolationCount: Int) {
|
||||||
let capacityPerInterpolation = 2
|
let capacityPerInterpolation = 2
|
||||||
@@ -80,8 +80,8 @@ public struct DefaultStringInterpolation: StringInterpolationProtocol, Sendable
|
|||||||
|
|
||||||
/// Appends a literal segment of a string interpolation.
|
/// Appends a literal segment of a string interpolation.
|
||||||
///
|
///
|
||||||
/// Do not call this method directly. It is used by the compiler when
|
/// You don't need to call this method directly. It's used by the compiler
|
||||||
/// interpreting string interpolations.
|
/// when interpreting string interpolations.
|
||||||
@inlinable
|
@inlinable
|
||||||
public mutating func appendLiteral(_ literal: String) {
|
public mutating func appendLiteral(_ literal: String) {
|
||||||
literal.write(to: &self)
|
literal.write(to: &self)
|
||||||
@@ -90,8 +90,8 @@ public struct DefaultStringInterpolation: StringInterpolationProtocol, Sendable
|
|||||||
/// Interpolates the given value's textual representation into the
|
/// Interpolates the given value's textual representation into the
|
||||||
/// string literal being created.
|
/// string literal being created.
|
||||||
///
|
///
|
||||||
/// Do not call this method directly. It is used by the compiler when
|
/// You don't need to call this method directly. It's used by the compiler
|
||||||
/// interpreting string interpolations. Instead, use string
|
/// when interpreting string interpolations. Instead, use string
|
||||||
/// interpolation to create a new string by including values, literals,
|
/// interpolation to create a new string by including values, literals,
|
||||||
/// variables, or expressions enclosed in parentheses, prefixed by a
|
/// variables, or expressions enclosed in parentheses, prefixed by a
|
||||||
/// backslash (`\(`...`)`).
|
/// backslash (`\(`...`)`).
|
||||||
@@ -114,8 +114,8 @@ public struct DefaultStringInterpolation: StringInterpolationProtocol, Sendable
|
|||||||
/// Interpolates the given value's textual representation into the
|
/// Interpolates the given value's textual representation into the
|
||||||
/// string literal being created.
|
/// string literal being created.
|
||||||
///
|
///
|
||||||
/// Do not call this method directly. It is used by the compiler when
|
/// You don't need to call this method directly. It's used by the compiler
|
||||||
/// interpreting string interpolations. Instead, use string
|
/// when interpreting string interpolations. Instead, use string
|
||||||
/// interpolation to create a new string by including values, literals,
|
/// interpolation to create a new string by including values, literals,
|
||||||
/// variables, or expressions enclosed in parentheses, prefixed by a
|
/// variables, or expressions enclosed in parentheses, prefixed by a
|
||||||
/// backslash (`\(`...`)`).
|
/// backslash (`\(`...`)`).
|
||||||
@@ -136,8 +136,8 @@ public struct DefaultStringInterpolation: StringInterpolationProtocol, Sendable
|
|||||||
/// Interpolates the given value's textual representation into the
|
/// Interpolates the given value's textual representation into the
|
||||||
/// string literal being created.
|
/// string literal being created.
|
||||||
///
|
///
|
||||||
/// Do not call this method directly. It is used by the compiler when
|
/// You don't need to call this method directly. It's used by the compiler
|
||||||
/// interpreting string interpolations. Instead, use string
|
/// when interpreting string interpolations. Instead, use string
|
||||||
/// interpolation to create a new string by including values, literals,
|
/// interpolation to create a new string by including values, literals,
|
||||||
/// variables, or expressions enclosed in parentheses, prefixed by a
|
/// variables, or expressions enclosed in parentheses, prefixed by a
|
||||||
/// backslash (`\(`...`)`).
|
/// backslash (`\(`...`)`).
|
||||||
@@ -160,8 +160,8 @@ public struct DefaultStringInterpolation: StringInterpolationProtocol, Sendable
|
|||||||
/// Interpolates the given value's textual representation into the
|
/// Interpolates the given value's textual representation into the
|
||||||
/// string literal being created.
|
/// string literal being created.
|
||||||
///
|
///
|
||||||
/// Do not call this method directly. It is used by the compiler when
|
/// You don't need to call this method directly. It's used by the compiler
|
||||||
/// interpreting string interpolations. Instead, use string
|
/// when interpreting string interpolations. Instead, use string
|
||||||
/// interpolation to create a new string by including values, literals,
|
/// interpolation to create a new string by including values, literals,
|
||||||
/// variables, or expressions enclosed in parentheses, prefixed by a
|
/// variables, or expressions enclosed in parentheses, prefixed by a
|
||||||
/// backslash (`\(`...`)`).
|
/// 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 {
|
extension DefaultStringInterpolation: CustomStringConvertible {
|
||||||
@inlinable
|
@inlinable
|
||||||
public var description: String {
|
public var description: String {
|
||||||
@@ -220,9 +342,9 @@ extension DefaultStringInterpolation: TextOutputStream {
|
|||||||
extension String {
|
extension String {
|
||||||
/// Creates a new instance from an interpolated string literal.
|
/// Creates a new instance from an interpolated string literal.
|
||||||
///
|
///
|
||||||
/// Do not call this initializer directly. It is used by the compiler when
|
/// You don't need to call this initializer directly. It's used by the
|
||||||
/// you create a string using string interpolation. Instead, use string
|
/// compiler when you create a string using string interpolation. Instead, use
|
||||||
/// interpolation to create a new string by including values, literals,
|
/// string interpolation to create a new string by including values, literals,
|
||||||
/// variables, or expressions enclosed in parentheses, prefixed by a
|
/// variables, or expressions enclosed in parentheses, prefixed by a
|
||||||
/// backslash (`\(`...`)`).
|
/// backslash (`\(`...`)`).
|
||||||
///
|
///
|
||||||
@@ -244,9 +366,9 @@ extension String {
|
|||||||
extension Substring {
|
extension Substring {
|
||||||
/// Creates a new instance from an interpolated string literal.
|
/// Creates a new instance from an interpolated string literal.
|
||||||
///
|
///
|
||||||
/// Do not call this initializer directly. It is used by the compiler when
|
/// You don't need to call this initializer directly. It's used by the
|
||||||
/// you create a string using string interpolation. Instead, use string
|
/// compiler when you create a string using string interpolation. Instead, use
|
||||||
/// interpolation to create a new string by including values, literals,
|
/// string interpolation to create a new string by including values, literals,
|
||||||
/// variables, or expressions enclosed in parentheses, prefixed by a
|
/// variables, or expressions enclosed in parentheses, prefixed by a
|
||||||
/// backslash (`\(`...`)`).
|
/// backslash (`\(`...`)`).
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -38,8 +38,9 @@ func rdar29691909(o: AnyObject) -> Any? {
|
|||||||
|
|
||||||
func rdar29907555(_ value: Any!) -> String {
|
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?}}
|
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@-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
|
// https://github.com/apple/swift/issues/46300
|
||||||
|
|||||||
@@ -295,10 +295,10 @@ func resyncParserB14() {}
|
|||||||
var stringInterp = "\(#^STRING_INTERP_3?check=STRING_INTERP^#)"
|
var stringInterp = "\(#^STRING_INTERP_3?check=STRING_INTERP^#)"
|
||||||
_ = "" + "\(#^STRING_INTERP_4?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[InstanceMethod]/CurrNominal/Flair[ArgLabels]/IsSystem: ['(']{#(value): any Any.Type#}[')'][#Void#];
|
||||||
// STRING_INTERP-DAG: Decl[Struct]/CurrModule: FooStruct[#FooStruct#]; name=FooStruct
|
// STRING_INTERP-DAG: Decl[Struct]/CurrModule/TypeRelation[Convertible]: FooStruct[#FooStruct#]; name=FooStruct
|
||||||
// STRING_INTERP-DAG: Decl[FreeFunction]/CurrModule/TypeRelation[Invalid]: fooFunc1()[#Void#];
|
// STRING_INTERP-DAG: Decl[FreeFunction]/CurrModule/TypeRelation[Convertible]: fooFunc1[#() -> ()#]; name=fooFunc1
|
||||||
// STRING_INTERP-DAG: Decl[FreeFunction]/CurrModule: optStr()[#String?#];
|
// STRING_INTERP-DAG: Decl[FreeFunction]/CurrModule/TypeRelation[Convertible]: optStr()[#String?#]; name=optStr()
|
||||||
// STRING_INTERP-DAG: Decl[GlobalVar]/Local: fooObject[#FooStruct#];
|
// STRING_INTERP-DAG: Decl[GlobalVar]/Local/TypeRelation[Convertible]: fooObject[#FooStruct#]; name=fooObject
|
||||||
func resyncParserC1() {}
|
func resyncParserC1() {}
|
||||||
|
|
||||||
// FOR_COLLECTION-NOT: forIndex
|
// FOR_COLLECTION-NOT: forIndex
|
||||||
|
|||||||
@@ -27,18 +27,18 @@ protocol FooProtocol {
|
|||||||
typealias FooTypealias = Int
|
typealias FooTypealias = Int
|
||||||
|
|
||||||
// Function parameter
|
// Function parameter
|
||||||
// COMMON-DAG: Decl[LocalVar]/Local: fooParam[#FooStruct#]{{; name=.+$}}
|
// COMMON-DAG: Decl[LocalVar]/Local{{(/TypeRelation\[Convertible\])?}}: fooParam[#FooStruct#]; name=fooParam
|
||||||
// Global completions
|
// Global completions
|
||||||
// COMMON-DAG: Decl[Struct]/CurrModule: FooStruct[#FooStruct#]{{; name=.+$}}
|
// COMMON-DAG: Decl[Struct]/CurrModule{{(/TypeRelation\[Convertible\])?}}: FooStruct[#FooStruct#]{{; name=.+$}}
|
||||||
// COMMON-DAG: Decl[Enum]/CurrModule: FooEnum[#FooEnum#]{{; name=.+$}}
|
// COMMON-DAG: Decl[Enum]/CurrModule{{(/TypeRelation\[Convertible\])?}}: FooEnum[#FooEnum#]{{; name=.+$}}
|
||||||
// COMMON-DAG: Decl[Class]/CurrModule: FooClass[#FooClass#]{{; name=.+$}}
|
// COMMON-DAG: Decl[Class]/CurrModule{{(/TypeRelation\[Convertible\])?}}: FooClass[#FooClass#]{{; name=.+$}}
|
||||||
// COMMON-DAG: Decl[Protocol]/CurrModule/Flair[RareType]: FooProtocol[#FooProtocol#]{{; 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[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: Keyword[try]/None: try{{; name=.+$}}
|
||||||
// COMMON-DAG: Literal[Boolean]/None{{(/TypeRelation\[Convertible\])?}}: true[#Bool#]{{; name=.+$}}
|
// COMMON-DAG: Literal[Boolean]/None{{(/TypeRelation\[Convertible\])?}}: true[#Bool#]{{; name=.+$}}
|
||||||
// COMMON-DAG: Literal[Boolean]/None{{(/TypeRelation\[Convertible\])?}}: false[#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\])?}}: Int8[#Int8#]{{; name=.+$}}
|
||||||
// COMMON-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem{{(/TypeRelation\[Convertible\])?}}: Int16[#Int16#]{{; 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=.+$}}
|
// COMMON-DAG: Decl[Struct]/OtherModule[Swift]/IsSystem{{(/TypeRelation\[Convertible\])?}}: Int32[#Int32#]{{; name=.+$}}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
// GENERICPARAM: Decl[GenericTypeParam]/Local: Self[#Self#];
|
// 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() {
|
func freeFunc() {
|
||||||
#^GLOBAL_BODY_EXPR?check=NOSELF^#
|
#^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]: lazyInstanceVar[#Int#]{{; name=.+$}}
|
||||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Convertible]: instanceVar[#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[Convertible]: instanceFunc0[#() -> ()#]; name=instanceFunc0
|
||||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: instanceFunc1({#(a): Int#})[#Void#]{{; name=.+$}}
|
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Convertible]: instanceFunc1(_:)[#(Int) -> ()#]; name=instanceFunc1(_:)
|
||||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: instanceFunc2({#(a): Int#}, {#b: &Double#})[#Void#]{{; name=.+$}}
|
// 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[Invalid]: instanceFunc3({#(a): Int#}, {#(Float, Double)#})[#Void#]{{; name=.+$}}
|
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Convertible]: instanceFunc3(_:_:)[#(Int, (Float, Double)) -> ()#]; name=instanceFunc3(_:_:)
|
||||||
// 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/TypeRelation[Convertible]: instanceFunc4(_:b:c:d:)[#(Int?, Int?, inout Int?, inout Int?) -> ()#]; name=instanceFunc4(_:b:c:d:)
|
||||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc5()[#Int?#]{{; name=.+$}}
|
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Convertible]: instanceFunc5()[#Int?#]; name=instanceFunc5()
|
||||||
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal: instanceFunc6()[#Int!#]{{; name=.+$}}
|
// INTERPOLATED_STRING_1-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Convertible]: instanceFunc6()[#Int!#]; name=instanceFunc6()
|
||||||
|
|
||||||
//===--- Check protocol extensions
|
//===--- 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?) {
|
func warnOptionalInStringInterpolationSegment(_ o : Int?) {
|
||||||
print("Always some, Always some, Always some: \(o)")
|
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-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@-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
|
var i: Int? = o
|
||||||
print("Always some, Always some, Always some: \(i)")
|
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-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@-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
|
i = nil
|
||||||
print("Always some, Always some, Always some: \(o.map { $0 + 1 })")
|
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-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@-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 as Int?)") // No warning
|
||||||
print("Always some, Always some, Always some: \(o.debugDescription)") // 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)")
|
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-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@-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
|
// Make sure we don't double diagnose
|
||||||
|
|||||||
@@ -92,6 +92,11 @@ PrintTests.test("StringInterpolation") {
|
|||||||
|
|
||||||
let s2 = "aaa\(1)bbb\(2 as Any)"
|
let s2 = "aaa\(1)bbb\(2 as Any)"
|
||||||
expectEqual("aaa1bbb2", s2)
|
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") {
|
PrintTests.test("SubstringInterpolation") {
|
||||||
@@ -100,6 +105,11 @@ PrintTests.test("SubstringInterpolation") {
|
|||||||
|
|
||||||
let s2 = "aaa\(1)bbb\(2 as Any)" as Substring
|
let s2 = "aaa\(1)bbb\(2 as Any)" as Substring
|
||||||
expectEqual("aaa1bbb2", s2)
|
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") {
|
PrintTests.test("CustomStringInterpolation") {
|
||||||
@@ -116,6 +126,11 @@ PrintTests.test("AutoCustomStringInterpolation") {
|
|||||||
|
|
||||||
let s2 = ("aaa\(1)bbb\(2 as Any)" as MySimpleString).value
|
let s2 = ("aaa\(1)bbb\(2 as Any)" as MySimpleString).value
|
||||||
expectEqual("aaa1bbb2", s2)
|
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") {
|
PrintTests.test("CustomStringInterpolationExtra") {
|
||||||
|
|||||||
Reference in New Issue
Block a user