-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang] Fix-it hint for ++this
-> ++*this
when deref is modifiable
#94159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13273,6 +13273,23 @@ enum { | |
ConstUnknown, // Keep as last element | ||
}; | ||
|
||
static void MaybeSuggestDerefFixIt(Sema &S, const Expr *E, SourceLocation Loc) { | ||
ExprResult Deref; | ||
Expr *TE = const_cast<Expr *>(E); | ||
{ | ||
Sema::TentativeAnalysisScope Trap(S); | ||
Deref = S.ActOnUnaryOp(S.getCurScope(), Loc, tok::star, TE); | ||
} | ||
if (Deref.isUsable() && | ||
Deref.get()->isModifiableLvalue(S.Context, &Loc) == Expr::MLV_Valid && | ||
!E->getType()->isObjCObjectPointerType()) { | ||
S.Diag(E->getBeginLoc(), | ||
diag::note_typecheck_add_deref_star_not_modifiable_lvalue) | ||
<< E->getSourceRange() | ||
<< FixItHint::CreateInsertion(E->getBeginLoc(), "*"); | ||
Comment on lines
+13286
to
+13289
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shouldn't be suggesting adding a const int *const p;
const int *const q;
p = q; ... we should not suggest a dereference -- the problem is clearly that I suspect we simply don't have enough information to do this from within There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah, my idea was that by trying to build a |
||
} | ||
} | ||
|
||
/// Emit the "read-only variable not assignable" error and print notes to give | ||
/// more information about why the variable is not assignable, such as pointing | ||
/// 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, | |
if (!DiagnosticEmitted) { | ||
S.Diag(Loc, diag::err_typecheck_assign_const) | ||
<< ExprRange << ConstVariable << VD << VD->getType(); | ||
MaybeSuggestDerefFixIt(S, E, Loc); | ||
DiagnosticEmitted = true; | ||
} | ||
S.Diag(VD->getLocation(), diag::note_typecheck_assign_const) | ||
|
@@ -13587,10 +13605,12 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { | |
SourceRange Assign; | ||
if (Loc != OrigLoc) | ||
Assign = SourceRange(OrigLoc, OrigLoc); | ||
if (NeedType) | ||
if (NeedType) { | ||
S.Diag(Loc, DiagID) << E->getType() << E->getSourceRange() << Assign; | ||
else | ||
} else { | ||
S.Diag(Loc, DiagID) << E->getSourceRange() << Assign; | ||
MaybeSuggestDerefFixIt(S, E, Loc); | ||
} | ||
return true; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s | ||
|
||
Rajveer100 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
struct S { | ||
void f() { | ||
++this; // expected-error {{expression is not assignable}} | ||
// expected-note@-1 {{add '*' to dereference it}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a bad diagnostic change: adding the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That’s true. Looking at this again, I’m candidly a bit confused that this seemed fine to me at the time... |
||
} | ||
|
||
void g() const { | ||
++this; // expected-error {{expression is not assignable}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Likewise. |
||
} | ||
}; | ||
|
||
void f(int* a, int* const b, const int* const c, __UINTPTR_TYPE__ d) { | ||
// expected-note@-1 {{variable 'b' declared const here}} | ||
// expected-note@-2 {{variable 'c' declared const here}} | ||
(int*)d = 4; // expected-error {{assignment to cast is illegal, lvalue casts are not supported}} | ||
// expected-note@-1 {{add '*' to dereference it}} | ||
|
||
++a; | ||
++b; // expected-error {{cannot assign to variable 'b' with const-qualified type 'int *const'}} | ||
// expected-note@-1 {{add '*' to dereference it}} | ||
++c; // expected-error {{cannot assign to variable 'c' with const-qualified type 'const int *const'}} | ||
|
||
reinterpret_cast<int*>(42) += 3; // expected-error {{expression is not assignable}} | ||
// expected-note@-1 {{add '*' to dereference it}} | ||
|
||
const int x = 42; | ||
(const_cast<int*>(&x)) += 3; // expected-error {{expression is not assignable}} | ||
// expected-note@-1 {{add '*' to dereference it}} | ||
} | ||
|
||
template <typename T> | ||
void f(T& t) { | ||
// expected-note@* 2 {{variable 't' declared const here}} | ||
++t; | ||
// expected-error@-1 {{cannot assign to variable 't' with const-qualified type 'int *const &'}} | ||
// expected-error@-2 {{cannot assign to variable 't' with const-qualified type 'const int *const &'}} | ||
// expected-note@-3 {{add '*' to dereference it}} | ||
} | ||
|
||
void g() { | ||
int* a; | ||
int* const b = a; | ||
const int* const c = a; | ||
f(a); | ||
f(b); // expected-note {{in instantiation of function template specialization 'f<int *const>' requested here}} | ||
f(c); // expected-note {{in instantiation of function template specialization 'f<const int *const>' requested here}} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,8 +65,10 @@ void test4(void) { | |
|
||
void test5(int *X, float *P) { | ||
(float*)X = P; // expected-error {{assignment to cast is illegal, lvalue casts are not supported}} | ||
// expected-note@-1 {{add '*' to dereference it}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a diagnostic regression. |
||
#define FOO ((float*) X) | ||
FOO = P; // expected-error {{assignment to cast is illegal, lvalue casts are not supported}} | ||
// expected-note@-1 {{add '*' to dereference it}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a diagnostic regression. |
||
} | ||
|
||
void test6(void) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not really feasible to use a
TentativeAnalysisScope
for this. Here are three reasons:ActOnUnaryOp
produces an "immediate context" diagnostic (the common case), such as a warning, then the note you produce below will be attached to that diagnostic instead of to the original diagnostic, so your note will be suppressed.ActOnUnaryOp
triggers template instantiation, thenTentativeAnalysisScope
will not suppress diagnostics outside of the immediate context, potentially leading to additional spurious diagnostics being produced.CheckForModifiableLValue
in the future), then the diagnostics produced by (2) would lead to rejecting a valid program.Checking for an overloaded
operator*
seems overly complicated here, so I think the right thing to test here is just whether the expression has pointer type, and the pointee type is an object type (not void or a function type).