From ea6c9ca9cffbf4327fc7bab85e37e2a3c2c0f0cd Mon Sep 17 00:00:00 2001 From: Prashanth Date: Sun, 19 Jan 2025 07:28:24 +0530 Subject: [PATCH 01/12] [clang][Sema] Add diagnostic note for function-like macros requiring parentheses --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 ++ clang/lib/Sema/SemaExpr.cpp | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index bcae9e9f30093..d9c8fcc66acf2 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5961,6 +5961,8 @@ def err_fold_expression_limit_exceeded: Error< "instantiating fold expression with %0 arguments exceeded expression nesting " "limit of %1">, DefaultFatal, NoSFINAE; +def note_function_like_macro_requires_parens : Note< + "'%0' exists, but as a function-like macro; perhaps, did you forget the parentheses?">; def err_unexpected_typedef : Error< "unexpected type name %0: expected expression">; def err_unexpected_namespace : Error< diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 3cd4010740d19..dff03ac31ef2a 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -2522,6 +2522,19 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, DC = DC->getLookupParent(); } + // Check whether a similar function-like macro exists and suggest it + if (IdentifierInfo *II = Name.getAsIdentifierInfo()) { + if (II->hasMacroDefinition()) { + MacroInfo *MI = PP.getMacroInfo(II); + if (MI && MI->isFunctionLike()) { + Diag( R.getNameLoc() ,diag::err_undeclared_var_use) << II->getName(); + Diag(MI->getDefinitionLoc(), diag::note_function_like_macro_requires_parens) + << II->getName(); + return true; + } + } + } + // We didn't find anything, so try to correct for a typo. TypoCorrection Corrected; if (S && Out) { @@ -2632,7 +2645,7 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, << SS.getRange(); return true; } - + // Give up, we can't recover. Diag(R.getNameLoc(), diagnostic) << Name; return true; From c274178a0ce3a510eaaa9390b277b860bcc5a2a9 Mon Sep 17 00:00:00 2001 From: Prashanth Date: Sun, 19 Jan 2025 08:30:39 +0530 Subject: [PATCH 02/12] [clang][Tests] Modify tests for function-like macros according to the new behavior and Format the changes --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 5 +++-- clang/lib/Sema/SemaExpr.cpp | 7 ++++--- clang/test/Preprocessor/macro_with_initializer_list.cpp | 6 ++++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d9c8fcc66acf2..697b92d325240 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5961,8 +5961,9 @@ def err_fold_expression_limit_exceeded: Error< "instantiating fold expression with %0 arguments exceeded expression nesting " "limit of %1">, DefaultFatal, NoSFINAE; -def note_function_like_macro_requires_parens : Note< - "'%0' exists, but as a function-like macro; perhaps, did you forget the parentheses?">; +def note_function_like_macro_requires_parens + : Note<"'%0' exists, but as a function-like macro; perhaps, did you forget " + "the parentheses?">; def err_unexpected_typedef : Error< "unexpected type name %0: expected expression">; def err_unexpected_namespace : Error< diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index dff03ac31ef2a..da894dd3a6d6a 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -2527,8 +2527,9 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, if (II->hasMacroDefinition()) { MacroInfo *MI = PP.getMacroInfo(II); if (MI && MI->isFunctionLike()) { - Diag( R.getNameLoc() ,diag::err_undeclared_var_use) << II->getName(); - Diag(MI->getDefinitionLoc(), diag::note_function_like_macro_requires_parens) + Diag(R.getNameLoc(), diag::err_undeclared_var_use) << II->getName(); + Diag(MI->getDefinitionLoc(), + diag::note_function_like_macro_requires_parens) << II->getName(); return true; } @@ -2645,7 +2646,7 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, << SS.getRange(); return true; } - + // Give up, we can't recover. Diag(R.getNameLoc(), diagnostic) << Name; return true; diff --git a/clang/test/Preprocessor/macro_with_initializer_list.cpp b/clang/test/Preprocessor/macro_with_initializer_list.cpp index 40f53164b263d..cc7dae0b5a3e0 100644 --- a/clang/test/Preprocessor/macro_with_initializer_list.cpp +++ b/clang/test/Preprocessor/macro_with_initializer_list.cpp @@ -134,6 +134,7 @@ void test_NE() { // CHECK: fix-it:"{{.*}}macro_with_initializer_list.cpp":{110:32-110:32}:")" #define INIT(var, init) Foo var = init; // expected-note 3{{defined here}} +// expected-note@-1 2{{'INIT' exists, but as a function-like macro; perhaps, did you forget the parentheses?}} // Can't use an initializer list as a macro argument. The commas in the list // will be interpretted as argument separaters and adding parenthesis will // make it no longer an initializer list. @@ -159,12 +160,13 @@ void test() { // expected-note@-3 {{cannot use initializer list at the beginning of a macro argument}} } -// CHECK: fix-it:"{{.*}}macro_with_initializer_list.cpp":{145:11-145:11}:"(" -// CHECK: fix-it:"{{.*}}macro_with_initializer_list.cpp":{145:23-145:23}:")" +// CHECK: fix-it:"{{.*}}macro_with_initializer_list.cpp":{146:11-146:11}:"(" +// CHECK: fix-it:"{{.*}}macro_with_initializer_list.cpp":{146:23-146:23}:")" #define M(name,a,b,c,d,e,f,g,h,i,j,k,l) \ Foo name = a + b + c + d + e + f + g + h + i + j + k + l; // expected-note@-2 2{{defined here}} +// expected-note@-3 {{'M' exists, but as a function-like macro; perhaps, did you forget the parentheses?}} void test2() { M(F1, Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo()); From 33e001ee6e852f630fd1b69bea424c6e7166078b Mon Sep 17 00:00:00 2001 From: Prashanth Date: Mon, 20 Jan 2025 13:34:48 +0530 Subject: [PATCH 03/12] Change the note for reference of function-like macros requiring without parentheses Co-authored-by: Sirraide --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 697b92d325240..6d3a065077d4b 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5962,8 +5962,7 @@ def err_fold_expression_limit_exceeded: Error< "limit of %1">, DefaultFatal, NoSFINAE; def note_function_like_macro_requires_parens - : Note<"'%0' exists, but as a function-like macro; perhaps, did you forget " - "the parentheses?">; + : Note<"'%0' is defined here as a function-like macro; did you mean to write '%0(...)'">; def err_unexpected_typedef : Error< "unexpected type name %0: expected expression">; def err_unexpected_namespace : Error< From 5aa4654ffe3f7499faf6f8abc7dbdf3468a8dcb9 Mon Sep 17 00:00:00 2001 From: Prashanth Date: Tue, 21 Jan 2025 16:20:10 +0530 Subject: [PATCH 04/12] [clang][Tests] Update diagnostic tests for function-like macros to clarify usage and improve error messages --- .../Preprocessor/macro_with_initializer_list.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/clang/test/Preprocessor/macro_with_initializer_list.cpp b/clang/test/Preprocessor/macro_with_initializer_list.cpp index cc7dae0b5a3e0..f7f645cce1180 100644 --- a/clang/test/Preprocessor/macro_with_initializer_list.cpp +++ b/clang/test/Preprocessor/macro_with_initializer_list.cpp @@ -133,8 +133,8 @@ void test_NE() { // CHECK: fix-it:"{{.*}}macro_with_initializer_list.cpp":{110:9-110:9}:"(" // CHECK: fix-it:"{{.*}}macro_with_initializer_list.cpp":{110:32-110:32}:")" -#define INIT(var, init) Foo var = init; // expected-note 3{{defined here}} -// expected-note@-1 2{{'INIT' exists, but as a function-like macro; perhaps, did you forget the parentheses?}} +#define INIT(var, init) Foo var = init; // expected-note 3{{macro 'INIT' defined here}} +// expected-note@-1 2{{'INIT' is defined here as a function-like macro; did you mean to write 'INIT(...)'}} // Can't use an initializer list as a macro argument. The commas in the list // will be interpretted as argument separaters and adding parenthesis will // make it no longer an initializer list. @@ -166,7 +166,7 @@ void test() { #define M(name,a,b,c,d,e,f,g,h,i,j,k,l) \ Foo name = a + b + c + d + e + f + g + h + i + j + k + l; // expected-note@-2 2{{defined here}} -// expected-note@-3 {{'M' exists, but as a function-like macro; perhaps, did you forget the parentheses?}} +// expected-note@-3 {{'M' is defined here as a function-like macro; did you mean to write 'M(...)'}} void test2() { M(F1, Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo()); @@ -182,3 +182,11 @@ void test2() { // expected-error@-3 {{use of undeclared identifier}} // expected-note@-4 {{cannot use initializer list at the beginning of a macro argument}} } + +#define LIM() 10 +// expected-note@-1 {{'LIM' is defined here as a function-like macro; did you mean to write 'LIM(...)'}} + +void test3() { + int iter = LIM; + // expected-error@-1 {{use of undeclared identifier LIM}} +} \ No newline at end of file From bd0848786ffb70e6f06b2b93b1b2fae46478484c Mon Sep 17 00:00:00 2001 From: Prashanth Date: Tue, 21 Jan 2025 16:22:39 +0530 Subject: [PATCH 05/12] Change the note for reference of function-like macros requiring without parentheses Co-authored-by: Mariya Podchishchaeva --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 6d3a065077d4b..34ed18e518773 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5962,7 +5962,7 @@ def err_fold_expression_limit_exceeded: Error< "limit of %1">, DefaultFatal, NoSFINAE; def note_function_like_macro_requires_parens - : Note<"'%0' is defined here as a function-like macro; did you mean to write '%0(...)'">; + : Note<"'%0' is defined here as a function-like macro; did you mean '%0(...)'">; def err_unexpected_typedef : Error< "unexpected type name %0: expected expression">; def err_unexpected_namespace : Error< From 6ef338ab4c2dd123397327fb3de7f1057643fa49 Mon Sep 17 00:00:00 2001 From: Prashanth Date: Tue, 21 Jan 2025 17:03:21 +0530 Subject: [PATCH 06/12] [clang][Tests] Update diagnostic tests for function-like macros for the updated note --- clang/test/Preprocessor/macro_with_initializer_list.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/test/Preprocessor/macro_with_initializer_list.cpp b/clang/test/Preprocessor/macro_with_initializer_list.cpp index f7f645cce1180..cf1d137eadcbe 100644 --- a/clang/test/Preprocessor/macro_with_initializer_list.cpp +++ b/clang/test/Preprocessor/macro_with_initializer_list.cpp @@ -134,7 +134,7 @@ void test_NE() { // CHECK: fix-it:"{{.*}}macro_with_initializer_list.cpp":{110:32-110:32}:")" #define INIT(var, init) Foo var = init; // expected-note 3{{macro 'INIT' defined here}} -// expected-note@-1 2{{'INIT' is defined here as a function-like macro; did you mean to write 'INIT(...)'}} +// expected-note@-1 2{{'INIT' is defined here as a function-like macro; did you mean 'INIT(...)'}} // Can't use an initializer list as a macro argument. The commas in the list // will be interpretted as argument separaters and adding parenthesis will // make it no longer an initializer list. @@ -166,7 +166,7 @@ void test() { #define M(name,a,b,c,d,e,f,g,h,i,j,k,l) \ Foo name = a + b + c + d + e + f + g + h + i + j + k + l; // expected-note@-2 2{{defined here}} -// expected-note@-3 {{'M' is defined here as a function-like macro; did you mean to write 'M(...)'}} +// expected-note@-3 {{'M' is defined here as a function-like macro; did you mean 'M(...)'}} void test2() { M(F1, Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo()); @@ -184,7 +184,7 @@ void test2() { } #define LIM() 10 -// expected-note@-1 {{'LIM' is defined here as a function-like macro; did you mean to write 'LIM(...)'}} +// expected-note@-1 {{'LIM' is defined here as a function-like macro; did you mean 'LIM(...)'}} void test3() { int iter = LIM; From 094887dbe3a521ad4733cd37cbfb5313bbb0dc4e Mon Sep 17 00:00:00 2001 From: Prashanth Date: Sun, 9 Feb 2025 14:20:36 +0530 Subject: [PATCH 07/12] [clang][Sema] Improve diagnostics for undeclared function-like macros --- .../clang/Basic/DiagnosticSemaKinds.td | 4 +- clang/lib/Sema/SemaExpr.cpp | 43 ++++++++++++------- .../macro_with_initializer_list.cpp | 14 +----- clang/test/Sema/typo-correction.c | 22 ++++++++++ 4 files changed, 55 insertions(+), 28 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 34ed18e518773..33a3bfe4ff646 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5962,7 +5962,7 @@ def err_fold_expression_limit_exceeded: Error< "limit of %1">, DefaultFatal, NoSFINAE; def note_function_like_macro_requires_parens - : Note<"'%0' is defined here as a function-like macro; did you mean '%0(...)'">; + : Note<"'%0' defined here as a function-like macro">; def err_unexpected_typedef : Error< "unexpected type name %0: expected expression">; def err_unexpected_namespace : Error< @@ -10857,6 +10857,8 @@ def err_undeclared_use_suggest : Error< "use of undeclared %0; did you mean %1?">; def err_undeclared_var_use_suggest : Error< "use of undeclared identifier %0; did you mean %1?">; +def err_undeclared_var_use_suggest_func_like_macro + : Error<"use of undeclared identifier %0; did you mean %0(...)?">; def err_no_template : Error<"no template named %0">; def err_no_template_suggest : Error<"no template named %0; did you mean %1?">; def err_no_member_template : Error<"no template named %0 in %1">; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index da894dd3a6d6a..27c2cf65762e3 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -2347,6 +2347,27 @@ Sema::BuildDeclRefExpr(ValueDecl *D, QualType Ty, ExprValueKind VK, return E; } +// Check whether a similar function-like macro exists and suggest it +static bool isFunctionLikeMacro(const DeclarationName &Name, Sema &SemaRef, + const SourceLocation &TypoLoc) { + + if (IdentifierInfo *II = Name.getAsIdentifierInfo()) { + if (II->hasMacroDefinition()) { + MacroInfo *MI = SemaRef.PP.getMacroInfo(II); + if (MI && MI->isFunctionLike()) { + SemaRef.Diag(TypoLoc, + diag::err_undeclared_var_use_suggest_func_like_macro) + << II->getName(); + SemaRef.Diag(MI->getDefinitionLoc(), + diag::note_function_like_macro_requires_parens) + << II->getName(); + return true; + } + } + } + return false; +} + void Sema::DecomposeUnqualifiedId(const UnqualifiedId &Id, TemplateArgumentListInfo &Buffer, @@ -2382,8 +2403,11 @@ static void emitEmptyLookupTypoDiagnostic( if (Ctx) SemaRef.Diag(TypoLoc, diag::err_no_member) << Typo << Ctx << SS.getRange(); - else + else { + if (isFunctionLikeMacro(Typo, SemaRef, TypoLoc)) + return; SemaRef.Diag(TypoLoc, DiagnosticID) << Typo; + } return; } @@ -2522,20 +2546,6 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, DC = DC->getLookupParent(); } - // Check whether a similar function-like macro exists and suggest it - if (IdentifierInfo *II = Name.getAsIdentifierInfo()) { - if (II->hasMacroDefinition()) { - MacroInfo *MI = PP.getMacroInfo(II); - if (MI && MI->isFunctionLike()) { - Diag(R.getNameLoc(), diag::err_undeclared_var_use) << II->getName(); - Diag(MI->getDefinitionLoc(), - diag::note_function_like_macro_requires_parens) - << II->getName(); - return true; - } - } - } - // We didn't find anything, so try to correct for a typo. TypoCorrection Corrected; if (S && Out) { @@ -2638,6 +2648,9 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, } R.clear(); + if (isFunctionLikeMacro(Name, SemaRef, R.getNameLoc())) + return true; + // Emit a special diagnostic for failed member lookups. // FIXME: computing the declaration context might fail here (?) if (!SS.isEmpty()) { diff --git a/clang/test/Preprocessor/macro_with_initializer_list.cpp b/clang/test/Preprocessor/macro_with_initializer_list.cpp index cf1d137eadcbe..5f66971622ce7 100644 --- a/clang/test/Preprocessor/macro_with_initializer_list.cpp +++ b/clang/test/Preprocessor/macro_with_initializer_list.cpp @@ -134,7 +134,6 @@ void test_NE() { // CHECK: fix-it:"{{.*}}macro_with_initializer_list.cpp":{110:32-110:32}:")" #define INIT(var, init) Foo var = init; // expected-note 3{{macro 'INIT' defined here}} -// expected-note@-1 2{{'INIT' is defined here as a function-like macro; did you mean 'INIT(...)'}} // Can't use an initializer list as a macro argument. The commas in the list // will be interpretted as argument separaters and adding parenthesis will // make it no longer an initializer list. @@ -160,13 +159,12 @@ void test() { // expected-note@-3 {{cannot use initializer list at the beginning of a macro argument}} } -// CHECK: fix-it:"{{.*}}macro_with_initializer_list.cpp":{146:11-146:11}:"(" -// CHECK: fix-it:"{{.*}}macro_with_initializer_list.cpp":{146:23-146:23}:")" +// CHECK: fix-it:"{{.*}}macro_with_initializer_list.cpp":{145:11-145:11}:"(" +// CHECK: fix-it:"{{.*}}macro_with_initializer_list.cpp":{145:23-145:23}:")" #define M(name,a,b,c,d,e,f,g,h,i,j,k,l) \ Foo name = a + b + c + d + e + f + g + h + i + j + k + l; // expected-note@-2 2{{defined here}} -// expected-note@-3 {{'M' is defined here as a function-like macro; did you mean 'M(...)'}} void test2() { M(F1, Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo()); @@ -181,12 +179,4 @@ void test2() { // expected-error@-2 {{too many arguments provided}} // expected-error@-3 {{use of undeclared identifier}} // expected-note@-4 {{cannot use initializer list at the beginning of a macro argument}} -} - -#define LIM() 10 -// expected-note@-1 {{'LIM' is defined here as a function-like macro; did you mean 'LIM(...)'}} - -void test3() { - int iter = LIM; - // expected-error@-1 {{use of undeclared identifier LIM}} } \ No newline at end of file diff --git a/clang/test/Sema/typo-correction.c b/clang/test/Sema/typo-correction.c index 4157207a9ac42..3efd35538dd58 100644 --- a/clang/test/Sema/typo-correction.c +++ b/clang/test/Sema/typo-correction.c @@ -114,3 +114,25 @@ void PR40286_3(int the_value) { void PR40286_4(int the_value) { // expected-note {{'the_value' declared here}} PR40286_h(the_value, the_value, the_walue); // expected-error {{use of undeclared identifier 'the_walue'; did you mean 'the_value'?}} } + +#define FOO1() 10 +// expected-note@-1 4 {{'FOO1' defined here as a function-like macro}} + +int x = FOO1; // expected-error {{use of undeclared identifier FOO1; did you mean FOO1(...)?}} + +void test3() { + int iter = FOO1; + // expected-error@-1 {{use of undeclared identifier FOO1; did you mean FOO1(...)?}} +} + +void bar(int); + +void test4() { + int FOO; // expected-note {{'FOO' declared here}} + int x = FOO1; // expected-error {{use of undeclared identifier 'FOO1'; did you mean 'FOO'?}} +} + +void test5() { + FOO1 + 1; // expected-error {{use of undeclared identifier FOO1; did you mean FOO1(...)?}} + bar(FOO1); // expected-error {{use of undeclared identifier FOO1; did you mean FOO1(...)?}} +} From eac145ae3c3de06573cc7cbef40fe5436606e089 Mon Sep 17 00:00:00 2001 From: Prashanth Date: Sun, 9 Feb 2025 14:50:49 +0530 Subject: [PATCH 08/12] [clang][Tests] Enhance diagnostic notes for function-like macros in initializer list tests --- clang/test/Preprocessor/macro_with_initializer_list.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/clang/test/Preprocessor/macro_with_initializer_list.cpp b/clang/test/Preprocessor/macro_with_initializer_list.cpp index 5f66971622ce7..dab60e60b14a1 100644 --- a/clang/test/Preprocessor/macro_with_initializer_list.cpp +++ b/clang/test/Preprocessor/macro_with_initializer_list.cpp @@ -134,6 +134,7 @@ void test_NE() { // CHECK: fix-it:"{{.*}}macro_with_initializer_list.cpp":{110:32-110:32}:")" #define INIT(var, init) Foo var = init; // expected-note 3{{macro 'INIT' defined here}} +// expected-note@-1 2{{'INIT' defined here as a function-like macro}} // Can't use an initializer list as a macro argument. The commas in the list // will be interpretted as argument separaters and adding parenthesis will // make it no longer an initializer list. @@ -159,12 +160,13 @@ void test() { // expected-note@-3 {{cannot use initializer list at the beginning of a macro argument}} } -// CHECK: fix-it:"{{.*}}macro_with_initializer_list.cpp":{145:11-145:11}:"(" -// CHECK: fix-it:"{{.*}}macro_with_initializer_list.cpp":{145:23-145:23}:")" +// CHECK: fix-it:"{{.*}}macro_with_initializer_list.cpp":{146:11-146:11}:"(" +// CHECK: fix-it:"{{.*}}macro_with_initializer_list.cpp":{146:23-146:23}:")" #define M(name,a,b,c,d,e,f,g,h,i,j,k,l) \ Foo name = a + b + c + d + e + f + g + h + i + j + k + l; // expected-note@-2 2{{defined here}} +// expected-note@-3 {{'M' defined here as a function-like macro}} void test2() { M(F1, Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo(), Foo()); @@ -179,4 +181,4 @@ void test2() { // expected-error@-2 {{too many arguments provided}} // expected-error@-3 {{use of undeclared identifier}} // expected-note@-4 {{cannot use initializer list at the beginning of a macro argument}} -} \ No newline at end of file +} From e7404fb8509f7b2e80782cea27986ee610b69b20 Mon Sep 17 00:00:00 2001 From: Prashanth Date: Sun, 9 Feb 2025 20:28:12 +0530 Subject: [PATCH 09/12] [clang][Docs] Add release note for diagnosing missing parentheses in function-like macros --- clang/docs/ReleaseNotes.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 50d3bbbc97e91..a469e5a97a4ac 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -128,6 +128,7 @@ Improvements to Clang's diagnostics which are supposed to only exist once per program, but may get duplicated when built into a shared library. - Fixed a bug where Clang's Analysis did not correctly model the destructor behavior of ``union`` members (#GH119415). +- Clang now provides a diagnostic note for ``function-like macros`` that are missing the required parentheses. Improvements to Clang's time-trace ---------------------------------- From e4d2186162e5383d787450c07d81f08d527a429d Mon Sep 17 00:00:00 2001 From: Prashanth Date: Wed, 12 Feb 2025 09:52:23 +0530 Subject: [PATCH 10/12] [clang][Sema] Improve diagnostics for function-like macros and update related notes --- clang/docs/ReleaseNotes.rst | 5 +++-- .../include/clang/Basic/DiagnosticSemaKinds.td | 4 ++-- clang/lib/Sema/SemaExpr.cpp | 18 +++++++++--------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index a469e5a97a4ac..c37c3d586de7b 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -128,8 +128,9 @@ Improvements to Clang's diagnostics which are supposed to only exist once per program, but may get duplicated when built into a shared library. - Fixed a bug where Clang's Analysis did not correctly model the destructor behavior of ``union`` members (#GH119415). -- Clang now provides a diagnostic note for ``function-like macros`` that are missing the required parentheses. - +- Clang now provides a diagnostic note for function-like macros that are + missing the required parentheses (#GH123038). + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 33a3bfe4ff646..e74020f8c8a0e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10857,8 +10857,8 @@ def err_undeclared_use_suggest : Error< "use of undeclared %0; did you mean %1?">; def err_undeclared_var_use_suggest : Error< "use of undeclared identifier %0; did you mean %1?">; -def err_undeclared_var_use_suggest_func_like_macro - : Error<"use of undeclared identifier %0; did you mean %0(...)?">; +def err_undeclared_var_use_suggest_func_like_macro : Error< + "%0 is defined as an object-like macro; did you mean '%0(...)'?">; def err_no_template : Error<"no template named %0">; def err_no_template_suggest : Error<"no template named %0; did you mean %1?">; def err_no_member_template : Error<"no template named %0 in %1">; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 27c2cf65762e3..26150bcb34967 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -2347,9 +2347,10 @@ Sema::BuildDeclRefExpr(ValueDecl *D, QualType Ty, ExprValueKind VK, return E; } -// Check whether a similar function-like macro exists and suggest it -static bool isFunctionLikeMacro(const DeclarationName &Name, Sema &SemaRef, - const SourceLocation &TypoLoc) { +// Diagnose when a macro cannot be expanded because it's a function-like macro +// being used as an object-like macro. Returns true if a diagnostic is emitted. +static bool diagnoseFunctionLikeMacro(Sema &SemaRef, DeclarationName Name, + SourceLocation TypoLoc) { if (IdentifierInfo *II = Name.getAsIdentifierInfo()) { if (II->hasMacroDefinition()) { @@ -2357,7 +2358,7 @@ static bool isFunctionLikeMacro(const DeclarationName &Name, Sema &SemaRef, if (MI && MI->isFunctionLike()) { SemaRef.Diag(TypoLoc, diag::err_undeclared_var_use_suggest_func_like_macro) - << II->getName(); + << II; SemaRef.Diag(MI->getDefinitionLoc(), diag::note_function_like_macro_requires_parens) << II->getName(); @@ -2403,11 +2404,10 @@ static void emitEmptyLookupTypoDiagnostic( if (Ctx) SemaRef.Diag(TypoLoc, diag::err_no_member) << Typo << Ctx << SS.getRange(); - else { - if (isFunctionLikeMacro(Typo, SemaRef, TypoLoc)) - return; + else if (diagnoseFunctionLikeMacro(SemaRef, Typo, TypoLoc)) + return; + else SemaRef.Diag(TypoLoc, DiagnosticID) << Typo; - } return; } @@ -2648,7 +2648,7 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, } R.clear(); - if (isFunctionLikeMacro(Name, SemaRef, R.getNameLoc())) + if (diagnoseFunctionLikeMacro(SemaRef, Name, R.getNameLoc())) return true; // Emit a special diagnostic for failed member lookups. From cc50469f3fbbe3d9cb877386abad68b59a84c816 Mon Sep 17 00:00:00 2001 From: Prashanth Date: Wed, 12 Feb 2025 16:17:08 +0530 Subject: [PATCH 11/12] [clang][Sema] Update diagnostic tests for object-like macros in initializer list tests --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/lib/Sema/SemaExpr.cpp | 2 +- clang/test/Preprocessor/macro_with_initializer_list.cpp | 6 +++--- clang/test/Sema/typo-correction.c | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index e74020f8c8a0e..bafc2308f522f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10858,7 +10858,7 @@ def err_undeclared_use_suggest : Error< def err_undeclared_var_use_suggest : Error< "use of undeclared identifier %0; did you mean %1?">; def err_undeclared_var_use_suggest_func_like_macro : Error< - "%0 is defined as an object-like macro; did you mean '%0(...)'?">; + "'%0' is defined as an object-like macro; did you mean '%0(...)'?">; def err_no_template : Error<"no template named %0">; def err_no_template_suggest : Error<"no template named %0; did you mean %1?">; def err_no_member_template : Error<"no template named %0 in %1">; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 26150bcb34967..c3b5c705178a1 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -2358,7 +2358,7 @@ static bool diagnoseFunctionLikeMacro(Sema &SemaRef, DeclarationName Name, if (MI && MI->isFunctionLike()) { SemaRef.Diag(TypoLoc, diag::err_undeclared_var_use_suggest_func_like_macro) - << II; + << II ->getName(); SemaRef.Diag(MI->getDefinitionLoc(), diag::note_function_like_macro_requires_parens) << II->getName(); diff --git a/clang/test/Preprocessor/macro_with_initializer_list.cpp b/clang/test/Preprocessor/macro_with_initializer_list.cpp index dab60e60b14a1..d9e653106d4a1 100644 --- a/clang/test/Preprocessor/macro_with_initializer_list.cpp +++ b/clang/test/Preprocessor/macro_with_initializer_list.cpp @@ -150,13 +150,13 @@ void test() { // Can't be fixed by parentheses. INIT(e, {1, 2, 3}); // expected-error@-1 {{too many arguments provided}} - // expected-error@-2 {{use of undeclared identifier}} + // expected-error@-2 {{'INIT' is defined as an object-like macro; did you mean 'INIT(...)'?}} // expected-note@-3 {{cannot use initializer list at the beginning of a macro argument}} // Can't be fixed by parentheses. INIT(e, {1, 2, 3} + {1, 2, 3}); // expected-error@-1 {{too many arguments provided}} - // expected-error@-2 {{use of undeclared identifier}} + // expected-error@-2 {{'INIT' is defined as an object-like macro; did you mean 'INIT(...)'?}} // expected-note@-3 {{cannot use initializer list at the beginning of a macro argument}} } @@ -179,6 +179,6 @@ void test2() { M(F3, {1,2,3}, {1,2,3}, {1,2,3}, {1,2,3}, {1,2,3}, {1,2,3}, {1,2,3}, {1,2,3}, {1,2,3}, {1,2,3}, {1,2,3}, {1,2,3}); // expected-error@-2 {{too many arguments provided}} - // expected-error@-3 {{use of undeclared identifier}} + // expected-error@-3 {{'M' is defined as an object-like macro; did you mean 'M(...)'?}} // expected-note@-4 {{cannot use initializer list at the beginning of a macro argument}} } diff --git a/clang/test/Sema/typo-correction.c b/clang/test/Sema/typo-correction.c index 3efd35538dd58..e16ad78583223 100644 --- a/clang/test/Sema/typo-correction.c +++ b/clang/test/Sema/typo-correction.c @@ -118,11 +118,11 @@ void PR40286_4(int the_value) { // expected-note {{'the_value' declared here}} #define FOO1() 10 // expected-note@-1 4 {{'FOO1' defined here as a function-like macro}} -int x = FOO1; // expected-error {{use of undeclared identifier FOO1; did you mean FOO1(...)?}} +int x = FOO1; // expected-error {{'FOO1' is defined as an object-like macro; did you mean 'FOO1(...)'?}} void test3() { int iter = FOO1; - // expected-error@-1 {{use of undeclared identifier FOO1; did you mean FOO1(...)?}} + // expected-error@-1 {{'FOO1' is defined as an object-like macro; did you mean 'FOO1(...)'?}} } void bar(int); @@ -133,6 +133,6 @@ void test4() { } void test5() { - FOO1 + 1; // expected-error {{use of undeclared identifier FOO1; did you mean FOO1(...)?}} - bar(FOO1); // expected-error {{use of undeclared identifier FOO1; did you mean FOO1(...)?}} + FOO1 + 1; // expected-error {{'FOO1' is defined as an object-like macro; did you mean 'FOO1(...)'?}} + bar(FOO1); // expected-error {{'FOO1' is defined as an object-like macro; did you mean 'FOO1'(...)'?}} } From 11adc1721c743cdd709d3e230e25fcf525d0a98b Mon Sep 17 00:00:00 2001 From: Prashanth Date: Wed, 12 Feb 2025 18:04:00 +0530 Subject: [PATCH 12/12] [clang][test]fix overlooked testcase --- clang/test/Sema/typo-correction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Sema/typo-correction.c b/clang/test/Sema/typo-correction.c index e16ad78583223..e000ecdf234e3 100644 --- a/clang/test/Sema/typo-correction.c +++ b/clang/test/Sema/typo-correction.c @@ -134,5 +134,5 @@ void test4() { void test5() { FOO1 + 1; // expected-error {{'FOO1' is defined as an object-like macro; did you mean 'FOO1(...)'?}} - bar(FOO1); // expected-error {{'FOO1' is defined as an object-like macro; did you mean 'FOO1'(...)'?}} + bar(FOO1); // expected-error {{'FOO1' is defined as an object-like macro; did you mean 'FOO1(...)'?}} }