Skip to content

Commit 54a4615

Browse files
committed
[Diagnostics] Do not offer a mutating fix-it if we have a mutating protocol requirement
Do not offer a mutating fix-it if we have a mutating protocol requirement and we're assigning to it from an implicitly nonmutating setter inside a protocol extension
1 parent 0c0a726 commit 54a4615

File tree

4 files changed

+37
-8
lines changed

4 files changed

+37
-8
lines changed

include/swift/AST/Decl.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5131,7 +5131,8 @@ class VarDecl : public AbstractStorageDecl {
51315131
/// If this is a simple 'let' constant, emit a note with a fixit indicating
51325132
/// that it can be rewritten to a 'var'. This is used in situations where the
51335133
/// compiler detects obvious attempts to mutate a constant.
5134-
void emitLetToVarNoteIfSimple(DeclContext *UseDC) const;
5134+
void emitLetToVarNoteIfSimple(DeclContext *UseDC,
5135+
ValueDecl *Anchor = nullptr) const;
51355136

51365137
/// Returns true if the name is the self identifier and is implicit.
51375138
bool isSelfParameter() const;

lib/AST/Decl.cpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5689,7 +5689,8 @@ ObjCSelector VarDecl::getDefaultObjCSetterSelector(ASTContext &ctx,
56895689
/// If this is a simple 'let' constant, emit a note with a fixit indicating
56905690
/// that it can be rewritten to a 'var'. This is used in situations where the
56915691
/// compiler detects obvious attempts to mutate a constant.
5692-
void VarDecl::emitLetToVarNoteIfSimple(DeclContext *UseDC) const {
5692+
void VarDecl::emitLetToVarNoteIfSimple(DeclContext *UseDC,
5693+
ValueDecl *Anchor) const {
56935694
// If it isn't a 'let', don't touch it.
56945695
if (!isLet()) return;
56955696

@@ -5703,12 +5704,31 @@ void VarDecl::emitLetToVarNoteIfSimple(DeclContext *UseDC) const {
57035704
if (FD && !FD->isMutating() && !FD->isImplicit() && FD->isInstanceMember()&&
57045705
!FD->getDeclContext()->getDeclaredInterfaceType()
57055706
->hasReferenceSemantics()) {
5706-
// Do not suggest the fix it in implicit getters
5707+
// Do not suggest the fix-it in implicit getters
57075708
if (auto AD = dyn_cast<AccessorDecl>(FD)) {
57085709
if (AD->isGetter() && !AD->getAccessorKeywordLoc().isValid())
57095710
return;
5711+
5712+
// Do not suggest the fix-it if we have an implicitly nonmutating
5713+
// setter in a protocol extension and we're assigning to a mutating
5714+
// protocol requirement.
5715+
if (Anchor && Anchor->isProtocolRequirement() && isa<VarDecl>(Anchor)) {
5716+
auto requirementVar = cast<VarDecl>(Anchor);
5717+
auto innermostTyCtx = AD->getInnermostTypeContext();
5718+
bool isRequirementSetterMutating = requirementVar->isSetterMutating();
5719+
bool isProtoExtension =
5720+
innermostTyCtx
5721+
? innermostTyCtx->getExtendedProtocolDecl() != nullptr
5722+
: false;
5723+
bool isImplicitlyNonMutatingSetter =
5724+
AD->isSetter() && AD->isNonMutating() &&
5725+
!AD->getAttrs().hasAttribute<NonMutatingAttr>();
5726+
if (isRequirementSetterMutating && isProtoExtension &&
5727+
isImplicitlyNonMutatingSetter)
5728+
return;
5729+
}
57105730
}
5711-
5731+
57125732
auto &d = getASTContext().Diags;
57135733
d.diagnose(FD->getFuncLoc(), diag::change_to_mutating,
57145734
isa<AccessorDecl>(FD))

lib/Sema/CSDiagnostics.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,9 +1468,18 @@ bool AssignmentFailure::diagnoseAsError() {
14681468
}
14691469
}
14701470

1471-
// If this is a simple variable marked with a 'let', emit a note to fixit
1472-
// hint it to 'var'.
1473-
VD->emitLetToVarNoteIfSimple(DC);
1471+
// If this is a simple variable marked with a 'let', emit a note to change
1472+
// it to 'var'.
1473+
//
1474+
// We also need a reference to the overload for the anchor, because it's
1475+
// possible that we're assigning to a mutating protocol property from an
1476+
// implicitly nonmutating setter in a protocol extension. In that case,
1477+
// we want to drop the fix-it to add 'mutating' as it's gonna re-trigger
1478+
// this error.
1479+
auto overload =
1480+
getConstraintSystem().findSelectedOverloadFor(getAnchor());
1481+
auto anchor = overload ? overload->Choice.getDeclOrNull() : nullptr;
1482+
VD->emitLetToVarNoteIfSimple(DC, anchor);
14741483
return true;
14751484
}
14761485

test/decl/ext/extensions.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ extension DoesNotImposeClassReq_2 where Self : AnyObject {
151151
var wrappingProperty: String {
152152
get { property }
153153
set { property = newValue } // expected-error {{cannot assign to property: 'self' is immutable}}
154-
// expected-note@-1 {{mark accessor 'mutating' to make 'self' mutable}}
155154
}
156155
}
157156

0 commit comments

Comments
 (0)