Skip to content

Commit bf11465

Browse files
committed
[clang] Fix-it hint for ++this -> ++*this when deref is modifiable
Resolves #93066
1 parent 1430405 commit bf11465

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
@@ -8777,6 +8777,9 @@ def err_typecheck_incomplete_type_not_modifiable_lvalue : Error<
87778777
def err_typecheck_lvalue_casts_not_supported : Error<
87788778
"assignment to cast is illegal, lvalue casts are not supported">;
87798779

8780+
def note_typecheck_add_deref_star_not_modifiable_lvalue : Note<
8781+
"add '*' to dereference it">;
8782+
87808783
def err_typecheck_duplicate_vector_components_not_mlvalue : Error<
87818784
"vector is not assignable (contains duplicate components)">;
87828785
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
@@ -13273,6 +13273,23 @@ enum {
1327313273
ConstUnknown, // Keep as last element
1327413274
};
1327513275

13276+
static void MaybeSuggestDerefFixIt(Sema &S, const Expr *E, SourceLocation Loc) {
13277+
ExprResult Deref;
13278+
Expr *TE = const_cast<Expr *>(E);
13279+
{
13280+
Sema::TentativeAnalysisScope Trap(S);
13281+
Deref = S.ActOnUnaryOp(S.getCurScope(), Loc, tok::star, TE);
13282+
}
13283+
if (Deref.isUsable() &&
13284+
Deref.get()->isModifiableLvalue(S.Context, &Loc) == Expr::MLV_Valid &&
13285+
!E->getType()->isObjCObjectPointerType()) {
13286+
S.Diag(E->getBeginLoc(),
13287+
diag::note_typecheck_add_deref_star_not_modifiable_lvalue)
13288+
<< E->getSourceRange()
13289+
<< FixItHint::CreateInsertion(E->getBeginLoc(), "*");
13290+
}
13291+
}
13292+
1327613293
/// Emit the "read-only variable not assignable" error and print notes to give
1327713294
/// more information about why the variable is not assignable, such as pointing
1327813295
/// to the declaration of a const variable, showing that a method is const, or
@@ -13367,6 +13384,7 @@ static void DiagnoseConstAssignment(Sema &S, const Expr *E,
1336713384
if (!DiagnosticEmitted) {
1336813385
S.Diag(Loc, diag::err_typecheck_assign_const)
1336913386
<< ExprRange << ConstVariable << VD << VD->getType();
13387+
MaybeSuggestDerefFixIt(S, E, Loc);
1337013388
DiagnosticEmitted = true;
1337113389
}
1337213390
S.Diag(VD->getLocation(), diag::note_typecheck_assign_const)
@@ -13587,10 +13605,12 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) {
1358713605
SourceRange Assign;
1358813606
if (Loc != OrigLoc)
1358913607
Assign = SourceRange(OrigLoc, OrigLoc);
13590-
if (NeedType)
13608+
if (NeedType) {
1359113609
S.Diag(Loc, DiagID) << E->getType() << E->getSourceRange() << Assign;
13592-
else
13610+
} else {
1359313611
S.Diag(Loc, DiagID) << E->getSourceRange() << Assign;
13612+
MaybeSuggestDerefFixIt(S, E, Loc);
13613+
}
1359413614
return true;
1359513615
}
1359613616

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)