Skip to content

Commit 9d7299f

Browse files
authored
[Clang] Add fix-it hint to insert * when a modifiable lvalue is required (#94159)
This adds a fix-it hint in situations where a modifiable lvalue is expected, but a prvalue or non-modifiable lvalue of pointer type was found, so long as dereferencing the pointer would result in a modifiable lvalue. Resolves #93066
1 parent 54b5c76 commit 9d7299f

File tree

7 files changed

+91
-2
lines changed

7 files changed

+91
-2
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8779,6 +8779,9 @@ def err_typecheck_incomplete_type_not_modifiable_lvalue : Error<
87798779
def err_typecheck_lvalue_casts_not_supported : Error<
87808780
"assignment to cast is illegal, lvalue casts are not supported">;
87818781

8782+
def note_typecheck_add_deref_star_not_modifiable_lvalue : Note<
8783+
"add '*' to dereference it">;
8784+
87828785
def err_typecheck_duplicate_vector_components_not_mlvalue : Error<
87838786
"vector is not assignable (contains duplicate components)">;
87848787
def err_block_decl_ref_not_modifiable_lvalue : Error<

clang/lib/Sema/SemaExpr.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13288,6 +13288,23 @@ enum {
1328813288
ConstUnknown, // Keep as last element
1328913289
};
1329013290

13291+
static void MaybeSuggestDerefFixIt(Sema &S, const Expr *E, SourceLocation Loc) {
13292+
ExprResult Deref;
13293+
Expr *TE = const_cast<Expr *>(E);
13294+
{
13295+
Sema::TentativeAnalysisScope Trap(S);
13296+
Deref = S.ActOnUnaryOp(S.getCurScope(), Loc, tok::star, TE);
13297+
}
13298+
if (Deref.isUsable() &&
13299+
Deref.get()->isModifiableLvalue(S.Context, &Loc) == Expr::MLV_Valid &&
13300+
!E->getType()->isObjCObjectPointerType()) {
13301+
S.Diag(E->getBeginLoc(),
13302+
diag::note_typecheck_add_deref_star_not_modifiable_lvalue)
13303+
<< E->getSourceRange()
13304+
<< FixItHint::CreateInsertion(E->getBeginLoc(), "*");
13305+
}
13306+
}
13307+
1329113308
/// Emit the "read-only variable not assignable" error and print notes to give
1329213309
/// more information about why the variable is not assignable, such as pointing
1329313310
/// to the declaration of a const variable, showing that a method is const, or
@@ -13382,6 +13399,7 @@ static void DiagnoseConstAssignment(Sema &S, const Expr *E,
1338213399
if (!DiagnosticEmitted) {
1338313400
S.Diag(Loc, diag::err_typecheck_assign_const)
1338413401
<< ExprRange << ConstVariable << VD << VD->getType();
13402+
MaybeSuggestDerefFixIt(S, E, Loc);
1338513403
DiagnosticEmitted = true;
1338613404
}
1338713405
S.Diag(VD->getLocation(), diag::note_typecheck_assign_const)
@@ -13602,10 +13620,12 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) {
1360213620
SourceRange Assign;
1360313621
if (Loc != OrigLoc)
1360413622
Assign = SourceRange(OrigLoc, OrigLoc);
13605-
if (NeedType)
13623+
if (NeedType) {
1360613624
S.Diag(Loc, DiagID) << E->getType() << E->getSourceRange() << Assign;
13607-
else
13625+
} else {
1360813626
S.Diag(Loc, DiagID) << E->getSourceRange() << Assign;
13627+
MaybeSuggestDerefFixIt(S, E, Loc);
13628+
}
1360913629
return true;
1361013630
}
1361113631

clang/test/C/drs/dr1xx.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ void dr126(void) {
296296
*/
297297
*object = 12; /* ok */
298298
++object; /* expected-error {{cannot assign to variable 'object' with const-qualified type 'const IP' (aka 'int *const')}} */
299+
/* expected-note@-1 {{add '*' to dereference it}} */
299300
}
300301

301302
/* WG14 DR128: yes

clang/test/Sema/debug-93066.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s
2+
3+
struct S {
4+
void f() {
5+
++this; // expected-error {{expression is not assignable}}
6+
// expected-note@-1 {{add '*' to dereference it}}
7+
}
8+
9+
void g() const {
10+
++this; // expected-error {{expression is not assignable}}
11+
}
12+
};
13+
14+
void f(int* a, int* const b, const int* const c, __UINTPTR_TYPE__ d) {
15+
// expected-note@-1 {{variable 'b' declared const here}}
16+
// expected-note@-2 {{variable 'c' declared const here}}
17+
(int*)d = 4; // expected-error {{assignment to cast is illegal, lvalue casts are not supported}}
18+
// expected-note@-1 {{add '*' to dereference it}}
19+
20+
++a;
21+
++b; // expected-error {{cannot assign to variable 'b' with const-qualified type 'int *const'}}
22+
// expected-note@-1 {{add '*' to dereference it}}
23+
++c; // expected-error {{cannot assign to variable 'c' with const-qualified type 'const int *const'}}
24+
25+
reinterpret_cast<int*>(42) += 3; // expected-error {{expression is not assignable}}
26+
// expected-note@-1 {{add '*' to dereference it}}
27+
28+
const int x = 42;
29+
(const_cast<int*>(&x)) += 3; // expected-error {{expression is not assignable}}
30+
// expected-note@-1 {{add '*' to dereference it}}
31+
}
32+
33+
template <typename T>
34+
void f(T& t) {
35+
// expected-note@* 2 {{variable 't' declared const here}}
36+
++t;
37+
// expected-error@-1 {{cannot assign to variable 't' with const-qualified type 'int *const &'}}
38+
// expected-error@-2 {{cannot assign to variable 't' with const-qualified type 'const int *const &'}}
39+
// expected-note@-3 {{add '*' to dereference it}}
40+
}
41+
42+
void g() {
43+
int* a;
44+
int* const b = a;
45+
const int* const c = a;
46+
f(a);
47+
f(b); // expected-note {{in instantiation of function template specialization 'f<int *const>' requested here}}
48+
f(c); // expected-note {{in instantiation of function template specialization 'f<const int *const>' requested here}}
49+
}

clang/test/Sema/exprs.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,10 @@ void test4(void) {
6565

6666
void test5(int *X, float *P) {
6767
(float*)X = P; // expected-error {{assignment to cast is illegal, lvalue casts are not supported}}
68+
// expected-note@-1 {{add '*' to dereference it}}
6869
#define FOO ((float*) X)
6970
FOO = P; // expected-error {{assignment to cast is illegal, lvalue casts are not supported}}
71+
// expected-note@-1 {{add '*' to dereference it}}
7072
}
7173

7274
void test6(void) {

clang/test/Sema/va_arg_x86_32.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22

33
int a(void) {
44
__builtin_va_arg((char*)0, int); // expected-error {{expression is not assignable}}
5+
// expected-note@-1 {{add '*' to dereference it}}
56
__builtin_va_arg((void*){0}, int); // expected-error {{first argument to 'va_arg' is of type 'void *'}}
67
}

clang/test/SemaObjCXX/sel-address.mm

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,20 @@ void h() {
99
SEL s = @selector(dealloc);
1010
SEL* ps = &s;
1111

12+
/*
13+
FIXME: https://github.com/llvm/llvm-project/pull/94159
14+
15+
TLDR; This is about inserting '*' to deref.
16+
17+
This would assign the value of s to the SEL object pointed to by
18+
@selector(dealloc). However, in Objective-C, selectors are not pointers,
19+
they are special compile-time constructs representing method names, and
20+
they are immutable, so you cannot assign values to them.
21+
22+
Therefore, this syntax is not valid for selectors in Objective-C.
23+
*/
1224
@selector(dealloc) = s; // expected-error {{expression is not assignable}}
25+
// expected-note@-1 {{add '*' to dereference it}}
1326

1427
SEL* ps2 = &@selector(dealloc);
1528

0 commit comments

Comments
 (0)