[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
This commit is contained in:
Suyash Srijan
2019-09-06 23:40:07 +01:00
parent 0c0a72677c
commit 54a4615e6f
4 changed files with 37 additions and 8 deletions

View File

@@ -5689,7 +5689,8 @@ ObjCSelector VarDecl::getDefaultObjCSetterSelector(ASTContext &ctx,
/// If this is a simple 'let' constant, emit a note with a fixit indicating
/// that it can be rewritten to a 'var'. This is used in situations where the
/// compiler detects obvious attempts to mutate a constant.
void VarDecl::emitLetToVarNoteIfSimple(DeclContext *UseDC) const {
void VarDecl::emitLetToVarNoteIfSimple(DeclContext *UseDC,
ValueDecl *Anchor) const {
// If it isn't a 'let', don't touch it.
if (!isLet()) return;
@@ -5703,12 +5704,31 @@ void VarDecl::emitLetToVarNoteIfSimple(DeclContext *UseDC) const {
if (FD && !FD->isMutating() && !FD->isImplicit() && FD->isInstanceMember()&&
!FD->getDeclContext()->getDeclaredInterfaceType()
->hasReferenceSemantics()) {
// Do not suggest the fix it in implicit getters
// Do not suggest the fix-it in implicit getters
if (auto AD = dyn_cast<AccessorDecl>(FD)) {
if (AD->isGetter() && !AD->getAccessorKeywordLoc().isValid())
return;
// Do not suggest the fix-it if we have an implicitly nonmutating
// setter in a protocol extension and we're assigning to a mutating
// protocol requirement.
if (Anchor && Anchor->isProtocolRequirement() && isa<VarDecl>(Anchor)) {
auto requirementVar = cast<VarDecl>(Anchor);
auto innermostTyCtx = AD->getInnermostTypeContext();
bool isRequirementSetterMutating = requirementVar->isSetterMutating();
bool isProtoExtension =
innermostTyCtx
? innermostTyCtx->getExtendedProtocolDecl() != nullptr
: false;
bool isImplicitlyNonMutatingSetter =
AD->isSetter() && AD->isNonMutating() &&
!AD->getAttrs().hasAttribute<NonMutatingAttr>();
if (isRequirementSetterMutating && isProtoExtension &&
isImplicitlyNonMutatingSetter)
return;
}
}
auto &d = getASTContext().Diags;
d.diagnose(FD->getFuncLoc(), diag::change_to_mutating,
isa<AccessorDecl>(FD))