Skip to content

Commit b615db4

Browse files
authored
[CSDiagnostics] A couple of fixes for the "self." immutability… (#28288)
[CSDiagnostics] A couple of fixes for the "self." immutability fix-it
2 parents 1d76c9d + 6d6feb6 commit b615db4

File tree

3 files changed

+105
-37
lines changed

3 files changed

+105
-37
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3424,9 +3424,9 @@ ERROR(assignment_bang_has_immutable_subcomponent,none,
34243424
NOTE(change_to_mutating,none,
34253425
"mark %select{method|accessor}0 'mutating' to make 'self' mutable",
34263426
(bool))
3427-
NOTE(masked_instance_variable,none,
3428-
"add explicit 'self.' to refer to mutable property of %0",
3429-
(Type))
3427+
NOTE(masked_mutable_property,none,
3428+
"add explicit '%0' to refer to mutable %1 of %2",
3429+
(StringRef, DescriptiveDeclKind, Type))
34303430

34313431
ERROR(assignment_let_property_delegating_init,none,
34323432
"'let' property %0 may not be initialized directly; use "

lib/Sema/CSDiagnostics.cpp

Lines changed: 62 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1555,9 +1555,9 @@ bool AssignmentFailure::diagnoseAsError() {
15551555

15561556
// Walk through the destination expression, resolving what the problem is. If
15571557
// we find a node in the lvalue path that is problematic, this returns it.
1558-
auto immInfo = resolveImmutableBase(DestExpr);
1559-
1560-
Optional<OverloadChoice> choice = immInfo.second;
1558+
Expr *immutableExpr;
1559+
Optional<OverloadChoice> choice;
1560+
std::tie(immutableExpr, choice) = resolveImmutableBase(DestExpr);
15611561

15621562
// Attempt diagnostics based on the overload choice.
15631563
if (choice.hasValue()) {
@@ -1571,17 +1571,17 @@ bool AssignmentFailure::diagnoseAsError() {
15711571

15721572
if (!choice->isDecl()) {
15731573
if (choice->getKind() == OverloadChoiceKind::KeyPathApplication &&
1574-
!isa<ApplyExpr>(immInfo.first)) {
1574+
!isa<ApplyExpr>(immutableExpr)) {
15751575
std::string message = "key path is read-only";
1576-
if (auto *SE = dyn_cast<SubscriptExpr>(immInfo.first)) {
1576+
if (auto *SE = dyn_cast<SubscriptExpr>(immutableExpr)) {
15771577
if (auto *DRE = dyn_cast<DeclRefExpr>(getKeyPathArgument(SE))) {
15781578
auto identifier = DRE->getDecl()->getBaseName().getIdentifier();
15791579
message =
15801580
"'" + identifier.str().str() + "' is a read-only key path";
15811581
}
15821582
}
15831583
emitDiagnostic(Loc, DeclDiagnostic, message)
1584-
.highlight(immInfo.first->getSourceRange());
1584+
.highlight(immutableExpr->getSourceRange());
15851585
return true;
15861586
}
15871587
return false;
@@ -1595,7 +1595,7 @@ bool AssignmentFailure::diagnoseAsError() {
15951595
message += VD->getName().str().str();
15961596
message += "'";
15971597

1598-
auto type = getType(immInfo.first);
1598+
auto type = getType(immutableExpr);
15991599

16001600
if (isKnownKeyPathType(type))
16011601
message += " is read-only";
@@ -1614,26 +1614,54 @@ bool AssignmentFailure::diagnoseAsError() {
16141614
}
16151615

16161616
emitDiagnostic(Loc, DeclDiagnostic, message)
1617-
.highlight(immInfo.first->getSourceRange());
1617+
.highlight(immutableExpr->getSourceRange());
16181618

1619-
// If there is a masked instance variable of the same type, emit a
1620-
// note to fixit prepend a 'self.'.
1619+
// If there is a masked property of the same type, emit a
1620+
// note to fixit prepend a 'self.' or 'Type.'.
16211621
if (auto typeContext = DC->getInnermostTypeContext()) {
1622-
UnqualifiedLookup lookup(VD->getFullName(), typeContext);
1623-
for (auto &result : lookup.Results) {
1624-
const VarDecl *typeVar = dyn_cast<VarDecl>(result.getValueDecl());
1625-
if (typeVar && typeVar != VD && typeVar->isSettable(DC) &&
1626-
typeVar->isSetterAccessibleFrom(DC) &&
1627-
typeVar->getType()->isEqual(VD->getType())) {
1628-
// But not in its own accessor.
1629-
auto AD =
1630-
dyn_cast_or_null<AccessorDecl>(DC->getInnermostMethodContext());
1631-
if (!AD || AD->getStorage() != typeVar) {
1632-
emitDiagnostic(Loc, diag::masked_instance_variable,
1633-
typeContext->getSelfTypeInContext())
1634-
.fixItInsert(Loc, "self.");
1635-
}
1622+
SmallVector<ValueDecl *, 2> results;
1623+
DC->lookupQualified(typeContext->getSelfNominalTypeDecl(),
1624+
VD->getFullName(),
1625+
NL_QualifiedDefault | NL_RemoveNonVisible, results);
1626+
1627+
auto foundProperty = llvm::find_if(results, [&](ValueDecl *decl) {
1628+
// We're looking for a settable property that is the same type as the
1629+
// var we found.
1630+
auto *var = dyn_cast<VarDecl>(decl);
1631+
if (!var || var == VD)
1632+
return false;
1633+
1634+
if (!var->isSettable(DC) || !var->isSetterAccessibleFrom(DC))
1635+
return false;
1636+
1637+
if (!var->getType()->isEqual(VD->getType()))
1638+
return false;
1639+
1640+
// Don't suggest a property if we're in one of its accessors.
1641+
auto *methodDC = DC->getInnermostMethodContext();
1642+
if (auto *AD = dyn_cast_or_null<AccessorDecl>(methodDC))
1643+
if (AD->getStorage() == var)
1644+
return false;
1645+
1646+
return true;
1647+
});
1648+
1649+
if (foundProperty != results.end()) {
1650+
auto startLoc = immutableExpr->getStartLoc();
1651+
auto *property = *foundProperty;
1652+
auto selfTy = typeContext->getSelfTypeInContext();
1653+
1654+
// If we found an instance property, suggest inserting "self.",
1655+
// otherwise suggest "Type." for a static property.
1656+
std::string fixItText;
1657+
if (property->isInstanceMember()) {
1658+
fixItText = "self.";
1659+
} else {
1660+
fixItText = selfTy->getString() + ".";
16361661
}
1662+
emitDiagnostic(startLoc, diag::masked_mutable_property,
1663+
fixItText, property->getDescriptiveKind(), selfTy)
1664+
.fixItInsert(startLoc, fixItText);
16371665
}
16381666
}
16391667

@@ -1654,7 +1682,7 @@ bool AssignmentFailure::diagnoseAsError() {
16541682
message = "subscript is immutable";
16551683

16561684
emitDiagnostic(Loc, DeclDiagnostic, message)
1657-
.highlight(immInfo.first->getSourceRange());
1685+
.highlight(immutableExpr->getSourceRange());
16581686
return true;
16591687
}
16601688

@@ -1676,7 +1704,7 @@ bool AssignmentFailure::diagnoseAsError() {
16761704
message += " is not settable";
16771705

16781706
emitDiagnostic(Loc, diagID, message)
1679-
.highlight(immInfo.first->getSourceRange());
1707+
.highlight(immutableExpr->getSourceRange());
16801708
return true;
16811709
}
16821710
}
@@ -1686,21 +1714,21 @@ bool AssignmentFailure::diagnoseAsError() {
16861714

16871715
// If a keypath was the problem but wasn't resolved into a vardecl
16881716
// it is ambiguous or unable to be used for setting.
1689-
if (auto *KPE = dyn_cast_or_null<KeyPathExpr>(immInfo.first)) {
1717+
if (auto *KPE = dyn_cast_or_null<KeyPathExpr>(immutableExpr)) {
16901718
emitDiagnostic(Loc, DeclDiagnostic, "immutable key path")
16911719
.highlight(KPE->getSourceRange());
16921720
return true;
16931721
}
16941722

1695-
if (auto LE = dyn_cast<LiteralExpr>(immInfo.first)) {
1723+
if (auto LE = dyn_cast<LiteralExpr>(immutableExpr)) {
16961724
emitDiagnostic(Loc, DeclDiagnostic, "literals are not mutable")
16971725
.highlight(LE->getSourceRange());
16981726
return true;
16991727
}
17001728

17011729
// If the expression is the result of a call, it is an rvalue, not a mutable
17021730
// lvalue.
1703-
if (auto *AE = dyn_cast<ApplyExpr>(immInfo.first)) {
1731+
if (auto *AE = dyn_cast<ApplyExpr>(immutableExpr)) {
17041732
// Handle literals, which are a call to the conversion function.
17051733
auto argsTuple =
17061734
dyn_cast<TupleExpr>(AE->getArg()->getSemanticsProvidingExpr());
@@ -1733,22 +1761,22 @@ bool AssignmentFailure::diagnoseAsError() {
17331761
return true;
17341762
}
17351763

1736-
if (auto contextualType = cs.getContextualType(immInfo.first)) {
1764+
if (auto contextualType = cs.getContextualType(immutableExpr)) {
17371765
Type neededType = contextualType->getInOutObjectType();
1738-
Type actualType = getType(immInfo.first)->getInOutObjectType();
1766+
Type actualType = getType(immutableExpr)->getInOutObjectType();
17391767
if (!neededType->isEqual(actualType)) {
17401768
if (DeclDiagnostic.ID != diag::cannot_pass_rvalue_inout_subelement.ID) {
17411769
emitDiagnostic(Loc, DeclDiagnostic,
17421770
"implicit conversion from '" + actualType->getString() +
17431771
"' to '" + neededType->getString() +
17441772
"' requires a temporary")
1745-
.highlight(immInfo.first->getSourceRange());
1773+
.highlight(immutableExpr->getSourceRange());
17461774
}
17471775
return true;
17481776
}
17491777
}
17501778

1751-
if (auto IE = dyn_cast<IfExpr>(immInfo.first)) {
1779+
if (auto IE = dyn_cast<IfExpr>(immutableExpr)) {
17521780
emitDiagnostic(Loc, DeclDiagnostic,
17531781
"result of conditional operator '? :' is never mutable")
17541782
.highlight(IE->getQuestionLoc())
@@ -1757,7 +1785,7 @@ bool AssignmentFailure::diagnoseAsError() {
17571785
}
17581786

17591787
emitDiagnostic(Loc, TypeDiagnostic, getType(DestExpr))
1760-
.highlight(immInfo.first->getSourceRange());
1788+
.highlight(immutableExpr->getSourceRange());
17611789
return true;
17621790
}
17631791

test/Sema/immutability.swift

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,3 +701,43 @@ extension JustAProtocol {
701701
name = "World" // expected-error {{cannot assign to property: 'self' is immutable}}
702702
}
703703
}
704+
705+
struct S {
706+
var x = 0
707+
static var y = 0
708+
709+
struct Nested {
710+
func foo() {
711+
// SR-11786: Make sure we don't offer the 'self.' fix-it here.
712+
let x = 0 // expected-note {{change 'let' to 'var' to make it mutable}}
713+
x += 1 // expected-error {{left side of mutating operator isn't mutable: 'x' is a 'let' constant}}
714+
}
715+
}
716+
717+
func bar() {
718+
// SR-11787: Make sure we insert "self." in the right location.
719+
let x = 0 // expected-note 3{{change 'let' to 'var' to make it mutable}}
720+
x += 1 // expected-error {{left side of mutating operator isn't mutable: 'x' is a 'let' constant}}
721+
// expected-note@-1 {{add explicit 'self.' to refer to mutable property of 'S'}} {{5-5=self.}}
722+
723+
(try x) += 1 // expected-error {{left side of mutating operator isn't mutable: 'x' is a 'let' constant}}
724+
// expected-note@-1 {{add explicit 'self.' to refer to mutable property of 'S'}} {{10-10=self.}}
725+
726+
x = 1 // expected-error {{cannot assign to value: 'x' is a 'let' constant}}
727+
// expected-note@-1 {{add explicit 'self.' to refer to mutable property of 'S'}} {{5-5=self.}}
728+
729+
// SR-11788: Insert "Type." for a static property.
730+
let y = 0 // expected-note {{change 'let' to 'var' to make it mutable}}
731+
y += 1 // expected-error {{left side of mutating operator isn't mutable: 'y' is a 'let' constant}}
732+
// expected-note@-1 {{add explicit 'S.' to refer to mutable static property of 'S'}} {{5-5=S.}}
733+
}
734+
}
735+
736+
struct S2<T> {
737+
static var y: Int { get { 0 } set {} }
738+
func foo() {
739+
let y = 0 // expected-note {{change 'let' to 'var' to make it mutable}}
740+
y += 1 // expected-error {{left side of mutating operator isn't mutable: 'y' is a 'let' constant}}
741+
// expected-note@-1 {{add explicit 'S2<T>.' to refer to mutable static property of 'S2<T>'}} {{5-5=S2<T>.}}
742+
}
743+
}

0 commit comments

Comments
 (0)