Skip to content

Commit 1a9ef88

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

File tree

7 files changed

+67
-2
lines changed

7 files changed

+67
-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_expression_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: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13273,6 +13273,22 @@ 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(Loc, diag::note_typecheck_expression_not_modifiable_lvalue)
13287+
<< E->getSourceRange()
13288+
<< FixItHint::CreateInsertion(E->getBeginLoc(), "*");
13289+
}
13290+
}
13291+
1327613292
/// Emit the "read-only variable not assignable" error and print notes to give
1327713293
/// more information about why the variable is not assignable, such as pointing
1327813294
/// to the declaration of a const variable, showing that a method is const, or
@@ -13367,6 +13383,7 @@ static void DiagnoseConstAssignment(Sema &S, const Expr *E,
1336713383
if (!DiagnosticEmitted) {
1336813384
S.Diag(Loc, diag::err_typecheck_assign_const)
1336913385
<< ExprRange << ConstVariable << VD << VD->getType();
13386+
MaybeSuggestDerefFixIt(S, E, Loc);
1337013387
DiagnosticEmitted = true;
1337113388
}
1337213389
S.Diag(VD->getLocation(), diag::note_typecheck_assign_const)
@@ -13587,10 +13604,12 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) {
1358713604
SourceRange Assign;
1358813605
if (Loc != OrigLoc)
1358913606
Assign = SourceRange(OrigLoc, OrigLoc);
13590-
if (NeedType)
13607+
if (NeedType) {
1359113608
S.Diag(Loc, DiagID) << E->getType() << E->getSourceRange() << Assign;
13592-
else
13609+
} else {
1359313610
S.Diag(Loc, DiagID) << E->getSourceRange() << Assign;
13611+
MaybeSuggestDerefFixIt(S, E, Loc);
13612+
}
1359413613
return true;
1359513614
}
1359613615

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: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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+
(int*)d = 4; // expected-error {{assignment to cast is illegal, lvalue casts are not supported}}
16+
// expected-note@-1 {{add '*' to dereference it}}
17+
18+
++a;
19+
++b; // expected-error {{cannot assign to variable 'b' with const-qualified type 'int *const'}}
20+
// expected-note@-1 {{add '*' to dereference it}}
21+
// expected-note@* {{variable 'b' declared const here}}
22+
++c; // expected-error {{cannot assign to variable 'c' with const-qualified type 'const int *const'}}
23+
// expected-note@* {{variable 'c' declared const here}}
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+
}

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: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,15 @@ 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+
This would assign the value of s to the SEL object pointed to by @selector(dealloc). However, in Objective-C, selectors are not pointers, they are special compile-time constructs representing method names, and they are immutable, so you cannot assign values to them.
16+
17+
Therefore, this syntax is not valid for selectors in Objective-C.
18+
*/
1219
@selector(dealloc) = s; // expected-error {{expression is not assignable}}
20+
// expected-note@-1 {{add '*' to dereference it}}
1321

1422
SEL* ps2 = &@selector(dealloc);
1523

0 commit comments

Comments
 (0)