Skip to content

Commit 5092ffd

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

File tree

7 files changed

+93
-2
lines changed

7 files changed

+93
-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: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13367,6 +13367,21 @@ static void DiagnoseConstAssignment(Sema &S, const Expr *E,
1336713367
if (!DiagnosticEmitted) {
1336813368
S.Diag(Loc, diag::err_typecheck_assign_const)
1336913369
<< ExprRange << ConstVariable << VD << VD->getType();
13370+
ExprResult Deref;
13371+
Expr *TE = const_cast<Expr *>(E);
13372+
{
13373+
Sema::TentativeAnalysisScope Trap(S);
13374+
Deref = S.ActOnUnaryOp(S.getCurScope(), Loc, tok::star, TE);
13375+
}
13376+
if (Deref.isUsable() &&
13377+
Deref.get()->isModifiableLvalue(S.Context, &Loc) ==
13378+
Expr::MLV_Valid &&
13379+
!E->getType()->isObjCObjectPointerType()) {
13380+
S.Diag(Loc, diag::note_typecheck_expression_not_modifiable_lvalue)
13381+
<< E->getSourceRange()
13382+
<< FixItHint::CreateInsertion(E->getBeginLoc(),
13383+
"*" + VD->getNameAsString());
13384+
}
1337013385
DiagnosticEmitted = true;
1337113386
}
1337213387
S.Diag(VD->getLocation(), diag::note_typecheck_assign_const)
@@ -13587,10 +13602,47 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) {
1358713602
SourceRange Assign;
1358813603
if (Loc != OrigLoc)
1358913604
Assign = SourceRange(OrigLoc, OrigLoc);
13590-
if (NeedType)
13605+
if (NeedType) {
1359113606
S.Diag(Loc, DiagID) << E->getType() << E->getSourceRange() << Assign;
13592-
else
13607+
} else {
13608+
ExprResult Deref;
13609+
{
13610+
Sema::TentativeAnalysisScope Trap(S);
13611+
Deref = S.ActOnUnaryOp(S.getCurScope(), Loc, tok::star, E);
13612+
}
1359313613
S.Diag(Loc, DiagID) << E->getSourceRange() << Assign;
13614+
if (Deref.isUsable() &&
13615+
Deref.get()->isModifiableLvalue(S.Context, &Loc) == Expr::MLV_Valid) {
13616+
std::string exprType;
13617+
switch (E->getStmtClass()) {
13618+
case Stmt::CStyleCastExprClass: {
13619+
const auto *CSC = cast<CStyleCastExpr>(E);
13620+
exprType = CSC->getType().getAsString();
13621+
break;
13622+
}
13623+
case Stmt::CXXConstCastExprClass: {
13624+
const auto *CXXCCE = cast<CXXConstCastExpr>(E);
13625+
exprType = CXXCCE->getTypeAsWritten().getAsString();
13626+
break;
13627+
}
13628+
case Stmt::CXXReinterpretCastExprClass: {
13629+
const auto *CXXRCE = cast<CXXReinterpretCastExpr>(E);
13630+
exprType = CXXRCE->getTypeAsWritten().getAsString();
13631+
break;
13632+
}
13633+
case Stmt::CXXThisExprClass: {
13634+
exprType = "this";
13635+
break;
13636+
}
13637+
default: {
13638+
}
13639+
}
13640+
S.Diag(Loc, diag::note_typecheck_expression_not_modifiable_lvalue)
13641+
<< E->getSourceRange() << Assign
13642+
<< FixItHint::CreateInsertion(E->getBeginLoc(),
13643+
"*(" + exprType + ")");
13644+
}
13645+
}
1359413646
return true;
1359513647
}
1359613648

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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ void h() {
1010
SEL* ps = &s;
1111

1212
@selector(dealloc) = s; // expected-error {{expression is not assignable}}
13+
// expected-note@-1 {{add '*' to dereference it}}
1314

1415
SEL* ps2 = &@selector(dealloc);
1516

0 commit comments

Comments
 (0)