Skip to content

Commit 4d30bdb

Browse files
authored
Merge pull request #9994 from xedin/rdar-32431736
[Diagnostics] Add a fix-it for optional to raw representable type conversion
2 parents b419b4f + c94fe94 commit 4d30bdb

File tree

10 files changed

+62
-34
lines changed

10 files changed

+62
-34
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2024,6 +2024,8 @@ NOTE(enum_raw_value_incrementing_from_here,none,
20242024
"raw value auto-incremented from here",())
20252025
NOTE(enum_raw_value_incrementing_from_zero,none,
20262026
"raw value implicitly auto-incremented from zero",())
2027+
NOTE(construct_raw_representable_from_unwrapped_value,none,
2028+
"construct %0 from unwrapped %1 value", (Type, Type))
20272029

20282030
// Derived conformances
20292031

include/swift/AST/Expr.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -592,12 +592,12 @@ class alignas(8) Expr {
592592
/// a base class.
593593
bool isSuperExpr() const;
594594

595-
/// Returns true if directly appending a parameter list would be syntactically
596-
/// valid.
595+
/// Returns false if this expression needs to be wrapped in parens when
596+
/// used inside of a any postfix expression, true otherwise.
597597
///
598-
/// Good examples: foo.bar, baz().
599-
/// Bad examples:
600-
bool canAppendCallParentheses() const;
598+
/// \param appendingPostfixOperator if the expression being
599+
/// appended is a postfix operator like '!' or '?'.
600+
bool canAppendPostfixExpression(bool appendingPostfixOperator = false) const;
601601

602602
/// Returns true if this is an infix operator of some sort, including
603603
/// a builtin operator.

lib/AST/Expr.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ bool Expr::isSuperExpr() const {
655655
} while (true);
656656
}
657657

658-
bool Expr::canAppendCallParentheses() const {
658+
bool Expr::canAppendPostfixExpression(bool appendingPostfixOperator) const {
659659
switch (getKind()) {
660660
case ExprKind::Error:
661661
case ExprKind::CodeCompletion:
@@ -749,11 +749,13 @@ bool Expr::canAppendCallParentheses() const {
749749
return false;
750750

751751
case ExprKind::Call:
752-
case ExprKind::PostfixUnary:
753752
case ExprKind::DotSyntaxCall:
754753
case ExprKind::ConstructorRefCall:
755754
return true;
756755

756+
case ExprKind::PostfixUnary:
757+
return !appendingPostfixOperator;
758+
757759
case ExprKind::PrefixUnary:
758760
case ExprKind::Binary:
759761
return false;
@@ -784,7 +786,7 @@ bool Expr::canAppendCallParentheses() const {
784786
// Implicit conversion nodes have no syntax of their own; defer to the
785787
// subexpression.
786788
return cast<ImplicitConversionExpr>(this)->getSubExpr()
787-
->canAppendCallParentheses();
789+
->canAppendPostfixExpression(appendingPostfixOperator);
788790

789791
case ExprKind::ForcedCheckedCast:
790792
case ExprKind::ConditionalCheckedCast:

lib/Migrator/APIDiffMigratorPass.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker {
464464
auto *SelfExpr = AllArgs[0].ArgExp;
465465
if (auto *IOE = dyn_cast<InOutExpr>(SelfExpr))
466466
SelfExpr = IOE->getSubExpr();
467-
const bool NeedParen = !SelfExpr->canAppendCallParentheses();
467+
const bool NeedParen = !SelfExpr->canAppendPostfixExpression();
468468

469469
// Remove the global function name: "Foo(a, b..." to "a, b..."
470470
Editor.remove(CharSourceRange(SM, Call->getStartLoc(),

lib/Sema/CSApply.cpp

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7216,17 +7216,6 @@ static bool exprNeedsParensAfterAddingAs(TypeChecker &TC, DeclContext *DC,
72167216
return exprNeedsParensOutsideFollowingOperator(TC, DC, expr, rootExpr, asPG);
72177217
}
72187218

7219-
static bool exprNeedsParensInsidePostfixOperator(TypeChecker &TC,
7220-
DeclContext *DC,
7221-
Expr *expr) {
7222-
// Prefix and infix operators will bind outside of a postfix operator.
7223-
// Postfix operators will get token-merged with a new postfix operator.
7224-
return (isa<PrefixUnaryExpr>(expr) ||
7225-
isa<PostfixUnaryExpr>(expr) ||
7226-
isa<OptionalEvaluationExpr>(expr) ||
7227-
expr->isInfixOperator());
7228-
}
7229-
72307219
namespace {
72317220
class ExprWalker : public ASTWalker {
72327221
ExprRewriter &Rewriter;
@@ -7424,14 +7413,11 @@ bool ConstraintSystem::applySolutionFix(Expr *expr,
74247413
} else {
74257414
auto diag = TC.diagnose(affected->getLoc(),
74267415
diag::missing_unwrap_optional, type);
7427-
bool parensNeeded =
7428-
exprNeedsParensInsidePostfixOperator(TC, DC, affected);
7429-
7430-
if (parensNeeded) {
7416+
if (affected->canAppendPostfixExpression(true)) {
7417+
diag.fixItInsertAfter(affected->getEndLoc(), "!");
7418+
} else {
74317419
diag.fixItInsert(affected->getStartLoc(), "(")
74327420
.fixItInsertAfter(affected->getEndLoc(), ")!");
7433-
} else {
7434-
diag.fixItInsertAfter(affected->getEndLoc(), "!");
74357421
}
74367422
}
74377423
return true;

lib/Sema/CSDiag.cpp

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2790,7 +2790,7 @@ bool FailureDiagnosis::diagnoseConversionToBool(Expr *expr, Type exprType) {
27902790
// Technically we only need them if there's something in 'expr' with
27912791
// lower precedence than '!=', but the code actually comes out nicer
27922792
// in most cases with parens on anything non-trivial.
2793-
if (expr->canAppendCallParentheses()) {
2793+
if (expr->canAppendPostfixExpression()) {
27942794
prefix = prefix.drop_back();
27952795
suffix = suffix.drop_front();
27962796
}
@@ -2972,7 +2972,7 @@ bool FailureDiagnosis::diagnoseGeneralConversionFailure(Constraint *constraint){
29722972
DeclName(CS->TC.Context.getIdentifier("boolValue")));
29732973
if (!LookupResult.empty()) {
29742974
if (isa<VarDecl>(LookupResult.begin()->Decl)) {
2975-
if (anchor->canAppendCallParentheses())
2975+
if (anchor->canAppendPostfixExpression())
29762976
diagnose(anchor->getLoc(), diag::types_not_convertible_use_bool_value,
29772977
fromType, toType).fixItInsertAfter(anchor->getEndLoc(),
29782978
".boolValue");
@@ -3596,7 +3596,7 @@ static bool tryRawRepresentableFixIts(InFlightDiagnostic &diag,
35963596
if (fromTypeIsOptional && toTypeIsOptional) {
35973597
// Use optional's map function to convert conditionally, like so:
35983598
// expr.map{ T(rawValue: $0) }
3599-
bool needsParens = !expr->canAppendCallParentheses();
3599+
bool needsParens = !expr->canAppendPostfixExpression();
36003600
std::string mapCodeFix;
36013601
if (needsParens) {
36023602
diag.fixItInsert(exprRange.Start, "(");
@@ -3611,6 +3611,24 @@ static bool tryRawRepresentableFixIts(InFlightDiagnostic &diag,
36113611
} else if (!fromTypeIsOptional) {
36123612
diag.fixItInsert(exprRange.Start, convWrapBefore);
36133613
diag.fixItInsertAfter(exprRange.End, convWrapAfter);
3614+
} else {
3615+
SmallString<16> fixItBefore(convWrapBefore);
3616+
SmallString<16> fixItAfter;
3617+
3618+
if (!expr->canAppendPostfixExpression(true)) {
3619+
fixItBefore += "(";
3620+
fixItAfter = ")";
3621+
}
3622+
3623+
fixItAfter += "!" + convWrapAfter.str();
3624+
3625+
diag.flush();
3626+
CS->TC.diagnose(expr->getLoc(),
3627+
diag::construct_raw_representable_from_unwrapped_value,
3628+
toType, fromType)
3629+
.highlight(exprRange)
3630+
.fixItInsert(exprRange.Start, fixItBefore)
3631+
.fixItInsert(exprRange.End, fixItAfter);
36143632
}
36153633
};
36163634

lib/Sema/MiscDiagnostics.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E,
548548
auto diag = TC.diagnose(E->getEndLoc(),
549549
diag::warn_value_of_metatype_missing_self,
550550
E->getType()->getRValueInstanceType());
551-
if (E->canAppendCallParentheses()) {
551+
if (E->canAppendPostfixExpression()) {
552552
diag.fixItInsertAfter(E->getEndLoc(), ".self");
553553
} else {
554554
diag.fixItInsert(E->getStartLoc(), "(");
@@ -578,7 +578,7 @@ static void diagSyntacticUseRestrictions(TypeChecker &TC, const Expr *E,
578578

579579
// Add fix-it to insert ".self".
580580
auto diag = TC.diagnose(E->getEndLoc(), diag::add_self_to_type);
581-
if (E->canAppendCallParentheses()) {
581+
if (E->canAppendPostfixExpression()) {
582582
diag.fixItInsertAfter(E->getEndLoc(), ".self");
583583
} else {
584584
diag.fixItInsert(E->getStartLoc(), "(");
@@ -2190,7 +2190,7 @@ VarDeclUsageChecker::~VarDeclUsageChecker() {
21902190
if (isa<NamedPattern>(LP->getSubPattern())) {
21912191
auto initExpr = SC->getCond()[0].getInitializer();
21922192
if (initExpr->getStartLoc().isValid()) {
2193-
unsigned noParens = initExpr->canAppendCallParentheses();
2193+
unsigned noParens = initExpr->canAppendPostfixExpression();
21942194

21952195
// If the subexpr is an "as?" cast, we can rewrite it to
21962196
// be an "is" test.

lib/Sema/TypeCheckAvailability.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1623,7 +1623,7 @@ static void fixItAvailableAttrRename(TypeChecker &TC,
16231623
CharSourceRange selfExprRange =
16241624
Lexer::getCharSourceRangeFromSourceRange(sourceMgr,
16251625
selfExpr->getSourceRange());
1626-
bool needsParens = !selfExpr->canAppendCallParentheses();
1626+
bool needsParens = !selfExpr->canAppendPostfixExpression();
16271627

16281628
SmallString<64> selfReplace;
16291629
if (needsParens)

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3438,7 +3438,7 @@ CheckedCastKind TypeChecker::typeCheckCheckedCast(Type fromType,
34383438
diag.fixItReplace(SourceRange(diagLoc, diagToRange.End), "!= nil");
34393439

34403440
// Add parentheses if needed.
3441-
if (!fromExpr->canAppendCallParentheses()) {
3441+
if (!fromExpr->canAppendPostfixExpression()) {
34423442
diag.fixItInsert(fromExpr->getStartLoc(), "(");
34433443
diag.fixItInsertAfter(fromExpr->getEndLoc(), ")");
34443444
}

test/Sema/enum_raw_representable.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,23 @@ class Outer {
8383
case a
8484
}
8585
}
86+
87+
// rdar://problem/32431736 - Conversion fix-it from String to String raw value enum can't look through optionals
88+
89+
func rdar32431736() {
90+
enum E : String {
91+
case A = "A"
92+
case B = "B"
93+
}
94+
95+
let items1: [String] = ["A", "a"]
96+
let items2: [String]? = ["A"]
97+
98+
let myE1: E = items1.first
99+
// expected-error@-1 {{cannot convert value of type 'String?' to specified type 'E'}}
100+
// expected-note@-2 {{construct 'E' from unwrapped 'String' value}} {{17-17=E(rawValue: }} {{24-24=!)}}
101+
102+
let myE2: E = items2?.first
103+
// expected-error@-1 {{cannot convert value of type 'String?' to specified type 'E'}}
104+
// expected-note@-2 {{construct 'E' from unwrapped 'String' value}} {{17-17=E(rawValue: (}} {{25-25=)!)}}
105+
}

0 commit comments

Comments
 (0)