Skip to content

Commit 4558b50

Browse files
committed
Sema: New fix-its for RawRepresentable conversions
If the expression type is RawRepresentable with an associated RawValue type of T, and the contextual type is T, suggest adding a '.rawValue' accessor call. Also, suggest inserting the fixit in a few more cases, such as dictionary keys. This improves upon a previous patch which added a fix-it for the other direction: <55bf215> Fixes <rdar://problem/26470490>.
1 parent 75c5acb commit 4558b50

File tree

3 files changed

+121
-67
lines changed

3 files changed

+121
-67
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 109 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -3076,87 +3076,110 @@ bool FailureDiagnosis::diagnoseCalleeResultContextualConversionError() {
30763076
}
30773077

30783078

3079-
/// Return true if the conversion from fromType to toType is an invalid string
3080-
/// index operation.
3081-
static bool isIntegerToStringIndexConversion(Type fromType, Type toType,
3082-
ConstraintSystem *CS) {
3079+
/// Return true if the given type conforms to a known protocol type.
3080+
static bool isLiteralConvertibleType(Type fromType,
3081+
KnownProtocolKind kind,
3082+
ConstraintSystem *CS) {
30833083
auto integerType =
3084-
CS->TC.getProtocol(SourceLoc(),
3085-
KnownProtocolKind::IntegerLiteralConvertible);
3086-
if (!integerType) return false;
3084+
CS->TC.getProtocol(SourceLoc(), kind);
3085+
if (!integerType)
3086+
return false;
30873087

3088-
// If the from type is an integer type, and the to type is
3089-
// String.CharacterView.Index, then we found one.
30903088
if (CS->TC.conformsToProtocol(fromType, integerType, CS->DC,
30913089
ConformanceCheckFlags::InExpression)) {
3092-
if (toType->getCanonicalType().getString() == "String.CharacterView.Index")
3093-
return true;
3090+
return true;
30943091
}
30953092

30963093
return false;
30973094
}
30983095

3099-
/// Attempts to add a fixit to correct the compiler error.
3100-
///
3101-
/// Corrects the error is "cannot convert value of type <integer> to expected
3102-
/// argument type <RawRepresentable>", by adding a fixit that constructs the
3103-
/// raw-representable type from the value. This helps with SDK changes.
3104-
static void tryConversionFixit(InFlightDiagnostic &diag,
3105-
ConstraintSystem *CS,
3106-
Diag<Type, Type> diagID,
3107-
Type exprType,
3108-
Type contextualType,
3109-
Expr *expr) {
3110-
if (!CS || !expr || !exprType || !contextualType)
3111-
return;
3112-
if (diagID.ID != diag::cannot_convert_argument_value.ID)
3113-
return;
3114-
3115-
auto integerType =
3116-
CS->TC.getProtocol(SourceLoc(),
3117-
KnownProtocolKind::IntegerLiteralConvertible);
3118-
if (!integerType) return;
3119-
3120-
auto isIntegerType = [&](Type ty) -> bool {
3121-
return CS->TC.conformsToProtocol(exprType, integerType, CS->DC,
3122-
ConformanceCheckFlags::InExpression);
3123-
};
3124-
3125-
// When the error is "cannot convert value of type <integer> to expected
3126-
// argument type <RawRepresentable>", add a fixit that constructs the
3127-
// raw-representable type from the value.
3128-
3129-
if (!isIntegerType(exprType))
3130-
return;
3131-
3096+
/// Return true if the given type conforms to RawRepresentable, with an
3097+
/// underlying type conforming to the given known protocol.
3098+
static Type isRawRepresentable(Type fromType,
3099+
KnownProtocolKind kind,
3100+
ConstraintSystem *CS) {
31323101
auto rawReprType =
31333102
CS->TC.getProtocol(SourceLoc(), KnownProtocolKind::RawRepresentable);
3134-
if (!rawReprType) return;
3103+
if (!rawReprType)
3104+
return Type();
31353105

3136-
ProtocolConformance *rawReprConformance;
3137-
if (!CS->TC.conformsToProtocol(contextualType, rawReprType, CS->DC,
3106+
ProtocolConformance *conformance;
3107+
if (!CS->TC.conformsToProtocol(fromType, rawReprType, CS->DC,
31383108
ConformanceCheckFlags::InExpression,
3139-
&rawReprConformance))
3140-
return;
3109+
&conformance))
3110+
return Type();
31413111

3142-
Type rawTy = ProtocolConformance::getTypeWitnessByName(contextualType,
3143-
rawReprConformance,
3112+
Type rawTy = ProtocolConformance::getTypeWitnessByName(fromType,
3113+
conformance,
31443114
CS->getASTContext().getIdentifier("RawValue"),
3145-
&CS->TC);
3146-
if (!rawTy || !isIntegerType(rawTy))
3147-
return;
3115+
&CS->TC);
3116+
if (!rawTy || !isLiteralConvertibleType(rawTy, kind, CS))
3117+
return Type();
3118+
3119+
return rawTy;
3120+
}
3121+
3122+
/// Return true if the conversion from fromType to toType is an invalid string
3123+
/// index operation.
3124+
static bool isIntegerToStringIndexConversion(Type fromType, Type toType,
3125+
ConstraintSystem *CS) {
3126+
auto kind = KnownProtocolKind::IntegerLiteralConvertible;
3127+
return (isLiteralConvertibleType(fromType, kind, CS) &&
3128+
toType->getCanonicalType().getString() == "String.CharacterView.Index");
3129+
}
31483130

3149-
std::string convWrapBefore = contextualType.getString();
3150-
convWrapBefore += "(rawValue: ";
3151-
std::string convWrapAfter = ")";
3152-
if (rawTy->getCanonicalType() != exprType->getCanonicalType()) {
3153-
convWrapBefore += rawTy->getString();
3154-
convWrapBefore += "(";
3155-
convWrapAfter += ")";
3156-
}
3157-
SourceRange exprRange = expr->getSourceRange();
3158-
diag.fixItInsert(exprRange.Start, convWrapBefore);
3159-
diag.fixItInsertAfter(exprRange.End, convWrapAfter);
3131+
/// Attempts to add fix-its for these two mistakes:
3132+
///
3133+
/// - Passing an integer where a type conforming to RawRepresentable is
3134+
/// expected, by wrapping the expression in a call to the contextual
3135+
/// type's initializer
3136+
///
3137+
/// - Passing a type conforming to RawRepresentable where an integer is
3138+
/// expected, by wrapping the expression in a call to the rawValue
3139+
/// accessor
3140+
///
3141+
/// This helps migration with SDK changes.
3142+
static void tryRawRepresentableFixIts(InFlightDiagnostic &diag,
3143+
ConstraintSystem *CS,
3144+
Diag<Type, Type> diagID,
3145+
Type fromType,
3146+
Type toType,
3147+
KnownProtocolKind kind,
3148+
Expr *expr) {
3149+
if (isLiteralConvertibleType(fromType, kind, CS)) {
3150+
if (auto rawTy = isRawRepresentable(toType, kind, CS)) {
3151+
// Produce before/after strings like 'Result(rawValue: RawType(<expr>))'
3152+
// or just 'Result(rawValue: <expr>)'.
3153+
std::string convWrapBefore = toType.getString();
3154+
convWrapBefore += "(rawValue: ";
3155+
std::string convWrapAfter = ")";
3156+
if (rawTy->getCanonicalType() != fromType->getCanonicalType()) {
3157+
convWrapBefore += rawTy->getString();
3158+
convWrapBefore += "(";
3159+
convWrapAfter += ")";
3160+
}
3161+
SourceRange exprRange = expr->getSourceRange();
3162+
diag.fixItInsert(exprRange.Start, convWrapBefore);
3163+
diag.fixItInsertAfter(exprRange.End, convWrapAfter);
3164+
return;
3165+
}
3166+
}
3167+
3168+
if (auto rawTy = isRawRepresentable(fromType, kind, CS)) {
3169+
if (isLiteralConvertibleType(toType, kind, CS)) {
3170+
std::string convWrapBefore;
3171+
std::string convWrapAfter = ".rawValue";
3172+
if (rawTy->getCanonicalType() != toType->getCanonicalType()) {
3173+
convWrapBefore += rawTy->getString();
3174+
convWrapBefore += "(";
3175+
convWrapAfter += ")";
3176+
}
3177+
SourceRange exprRange = expr->getSourceRange();
3178+
diag.fixItInsert(exprRange.Start, convWrapBefore);
3179+
diag.fixItInsertAfter(exprRange.End, convWrapAfter);
3180+
return;
3181+
}
3182+
}
31603183
}
31613184

31623185
bool FailureDiagnosis::diagnoseContextualConversionError() {
@@ -3387,10 +3410,29 @@ bool FailureDiagnosis::diagnoseContextualConversionError() {
33873410
diagID = diag::noescape_functiontype_mismatch;
33883411
}
33893412

3390-
InFlightDiagnostic diag = diagnose(expr->getLoc(), diagID, exprType, contextualType);
3413+
InFlightDiagnostic diag = diagnose(expr->getLoc(), diagID,
3414+
exprType, contextualType);
33913415
diag.highlight(expr->getSourceRange());
3416+
33923417
// Attempt to add a fixit for the error.
3393-
tryConversionFixit(diag, CS, diagID, exprType, contextualType, expr);
3418+
switch (CS->getContextualTypePurpose()) {
3419+
case CTP_CallArgument:
3420+
case CTP_ArrayElement:
3421+
case CTP_DictionaryKey:
3422+
case CTP_DictionaryValue:
3423+
tryRawRepresentableFixIts(diag, CS, diagID, exprType, contextualType,
3424+
KnownProtocolKind::IntegerLiteralConvertible,
3425+
expr);
3426+
tryRawRepresentableFixIts(diag, CS, diagID, exprType, contextualType,
3427+
KnownProtocolKind::StringLiteralConvertible,
3428+
expr);
3429+
break;
3430+
3431+
default:
3432+
// FIXME: Other contextual conversions too?
3433+
break;
3434+
}
3435+
33943436
return true;
33953437
}
33963438

test/FixCode/fixits-apply.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ func testMask1(a: Int) {
4242
func testMask2(a: UInt64) {
4343
sendIt(a)
4444
}
45+
func testMask3(a: MyEventMask2) {
46+
testMask1(a: a)
47+
}
48+
func testMask4(a: MyEventMask2) {
49+
testMask2(a: a)
50+
}
4551

4652
func goo(var e : ErrorProtocol) {
4753
}

test/FixCode/fixits-apply.swift.result

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ func testMask1(a: Int) {
4242
func testMask2(a: UInt64) {
4343
sendIt(MyEventMask2(rawValue: a))
4444
}
45+
func testMask3(a: MyEventMask2) {
46+
testMask1(a: UInt64(a.rawValue))
47+
}
48+
func testMask4(a: MyEventMask2) {
49+
testMask2(a: a.rawValue)
50+
}
4551

4652
func goo(e : ErrorProtocol) {
4753
var e = e

0 commit comments

Comments
 (0)