[Diagnostics] Correctly diagnose situations when immutable value is passed to inout parameter

Currently the note is going to point to the "callee" but that is
incorrect when the failure is related to an argument of a call.

Detect this situation in `RValueTreatedAsLValueFailure::diagnoseAsNote`
and produce a correct note.

Resolves: rdar://150689994
(cherry picked from commit 6bbc101a98)
This commit is contained in:
Pavel Yaskevich
2025-06-05 22:26:19 -07:00
parent 3ca6228246
commit a9e5a8a07a
3 changed files with 45 additions and 1 deletions

View File

@@ -5136,6 +5136,9 @@ ERROR(assignment_bang_has_immutable_subcomponent,none,
NOTE(candidate_is_not_assignable,none,
"candidate is not assignable: %kind0",
(const ValueDecl *))
NOTE(candidate_expects_inout_argument,none,
"candidate expects in-out value for parameter #%0 but argument is immutable",
(unsigned))
NOTE(change_to_mutating,none,
"mark %select{method|%1}0 'mutating' to make 'self' mutable",

View File

@@ -2049,11 +2049,24 @@ bool RValueTreatedAsLValueFailure::diagnoseAsError() {
}
bool RValueTreatedAsLValueFailure::diagnoseAsNote() {
auto overload = getCalleeOverloadChoiceIfAvailable(getLocator());
auto *locator = getLocator();
auto overload = getCalleeOverloadChoiceIfAvailable(locator);
if (!(overload && overload->choice.isDecl()))
return false;
auto *decl = overload->choice.getDecl();
if (locator->isLastElement<LocatorPathElt::ArgumentAttribute>()) {
auto argConv = locator->findLast<LocatorPathElt::ApplyArgToParam>();
if (!argConv)
return false;
emitDiagnosticAt(decl, diag::candidate_expects_inout_argument,
argConv->getParamIdx() + 1);
return true;
}
emitDiagnosticAt(decl, diag::candidate_is_not_assignable, decl);
return true;
}

View File

@@ -349,3 +349,31 @@ do {
}
}
}
do {
struct S {
let x: Int
var y: Int
}
func overloaded(_: String) {}
// expected-note@-1 3 {{candidate expects value of type 'String' for parameter #1 (got 'Int')}}
func overloaded(_: inout Int) {}
// expected-note@-1 3 {{candidate expects in-out value for parameter #1 but argument is immutable}}
func testImmutable(s: S) {
overloaded(s.x) // expected-error {{no exact matches in call to local function 'overloaded'}}
}
func testMutable(s: inout S) {
overloaded(s.x) // expected-error {{no exact matches in call to local function 'overloaded'}}
}
func testImmutableBase(s: S) {
overloaded(s.y) // expected-error {{no exact matches in call to local function 'overloaded'}}
}
func testMissingAddressOf(s: inout S) {
overloaded(s.y) // expected-error {{passing value of type 'Int' to an inout parameter requires explicit '&'}}
}
}