Author: Rajveer Singh Bharadwaj Date: 2024-06-13T08:54:13+02:00 New Revision: 9d7299fb84bcaafc6b7368dbe5ee6de29e71e655
URL: https://github.com/llvm/llvm-project/commit/9d7299fb84bcaafc6b7368dbe5ee6de29e71e655 DIFF: https://github.com/llvm/llvm-project/commit/9d7299fb84bcaafc6b7368dbe5ee6de29e71e655.diff LOG: [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 Added: clang/test/Sema/debug-93066.cpp Modified: clang/include/clang/Basic/DiagnosticSemaKinds.td clang/lib/Sema/SemaExpr.cpp clang/test/C/drs/dr1xx.c clang/test/Sema/exprs.c clang/test/Sema/va_arg_x86_32.c clang/test/SemaObjCXX/sel-address.mm Removed: ################################################################################ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 193eae3bc41d6..ab223f2b806d5 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8779,6 +8779,9 @@ def err_typecheck_incomplete_type_not_modifiable_lvalue : Error< def err_typecheck_lvalue_casts_not_supported : Error< "assignment to cast is illegal, lvalue casts are not supported">; +def note_typecheck_add_deref_star_not_modifiable_lvalue : Note< + "add '*' to dereference it">; + def err_typecheck_duplicate_vector_components_not_mlvalue : Error< "vector is not assignable (contains duplicate components)">; def err_block_decl_ref_not_modifiable_lvalue : Error< diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 76145f291887c..99a8704298314 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -13288,6 +13288,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(), "*"); + } +} + /// 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 @@ -13382,6 +13399,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) @@ -13602,10 +13620,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; } diff --git a/clang/test/C/drs/dr1xx.c b/clang/test/C/drs/dr1xx.c index 47538e44428c3..20e953b2c20ac 100644 --- a/clang/test/C/drs/dr1xx.c +++ b/clang/test/C/drs/dr1xx.c @@ -296,6 +296,7 @@ void dr126(void) { */ *object = 12; /* ok */ ++object; /* expected-error {{cannot assign to variable 'object' with const-qualified type 'const IP' (aka 'int *const')}} */ + /* expected-note@-1 {{add '*' to dereference it}} */ } /* WG14 DR128: yes diff --git a/clang/test/Sema/debug-93066.cpp b/clang/test/Sema/debug-93066.cpp new file mode 100644 index 0000000000000..e64d378e6f90d --- /dev/null +++ b/clang/test/Sema/debug-93066.cpp @@ -0,0 +1,49 @@ +// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s + +struct S { + void f() { + ++this; // expected-error {{expression is not assignable}} + // expected-note@-1 {{add '*' to dereference it}} + } + + void g() const { + ++this; // expected-error {{expression is not assignable}} + } +}; + +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}} +} diff --git a/clang/test/Sema/exprs.c b/clang/test/Sema/exprs.c index 3203d961dd0a4..a5b7268f4f527 100644 --- a/clang/test/Sema/exprs.c +++ b/clang/test/Sema/exprs.c @@ -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}} #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}} } void test6(void) { diff --git a/clang/test/Sema/va_arg_x86_32.c b/clang/test/Sema/va_arg_x86_32.c index 86a6a8881f54b..55d21f787a6f0 100644 --- a/clang/test/Sema/va_arg_x86_32.c +++ b/clang/test/Sema/va_arg_x86_32.c @@ -2,5 +2,6 @@ int a(void) { __builtin_va_arg((char*)0, int); // expected-error {{expression is not assignable}} + // expected-note@-1 {{add '*' to dereference it}} __builtin_va_arg((void*){0}, int); // expected-error {{first argument to 'va_arg' is of type 'void *'}} } diff --git a/clang/test/SemaObjCXX/sel-address.mm b/clang/test/SemaObjCXX/sel-address.mm index e5661af341691..d69f7055035d6 100644 --- a/clang/test/SemaObjCXX/sel-address.mm +++ b/clang/test/SemaObjCXX/sel-address.mm @@ -9,7 +9,20 @@ void h() { SEL s = @selector(dealloc); SEL* ps = &s; + /* + FIXME: https://github.com/llvm/llvm-project/pull/94159 + + TLDR; This is about inserting '*' to deref. + + 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. + + Therefore, this syntax is not valid for selectors in Objective-C. + */ @selector(dealloc) = s; // expected-error {{expression is not assignable}} + // expected-note@-1 {{add '*' to dereference it}} SEL* ps2 = &@selector(dealloc); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits