-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
[clang][Sema] Add diagnostic note for reference of function-like macros requiring without parentheses #123495
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-clang Author: Prashanth (StarOne01) ChangesThis PR enhances the Clang diagnostics to provide better guidance when function-like macros are used without parentheses. Additionally, it updates the relevant test cases to reflect these changes (#123038 ). Full diff: https://github.com/llvm/llvm-project/pull/123495.diff 3 Files Affected:
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index db54312ad965e8..37a9a67ac1efba 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5936,6 +5936,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 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 ae40895980d90a..44bab3e47233cb 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -2509,6 +2509,20 @@ 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) {
diff --git a/clang/test/Preprocessor/macro_with_initializer_list.cpp b/clang/test/Preprocessor/macro_with_initializer_list.cpp
index 40f53164b263d9..cc7dae0b5a3e00 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());
|
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.
This still needs tests and a release note.
clang/lib/Sema/SemaExpr.cpp
Outdated
@@ -2509,6 +2509,20 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, | |||
DC = DC->getLookupParent(); | |||
} | |||
|
|||
// Check whether a similar function-like macro exists and suggest it |
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.
Instead of doing this here before typo correction, the note that this pr adds should probably just be emitted right after the error we emit at the very end of this function (or after the one right before that we emit if the SS isn’t empty).
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.
I did try it, but in this case the function returns here itself which is before giving up, maybe i could put the note inside this conditional?
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.
I did try it, but in this case the function returns here itself
I'm not sure I understand what it is the problem with this return? It should happen when a typo replacement has been found, which seems not the case for the originally reported case, see https://godbolt.org/z/br9n6eGsT .
Could you please elaborate a bit more?
Because that
Instead of doing this here before typo correction, the note that this pr adds should probably just be emitted right after the error we emit at the very end of this function
Does seem like a correct soluion.
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.
Hey @Fznamznon, thanks for asking, let me explain myself.
If i understand it right, that particular conditional statement should get executed for this:
int main(){
int var1 = 63;
int out = var;
}
and should be skipped for:
int main(){
int var1 = 63;
int out = notFound;
}
but the problem is that the conditional statement gets executed for both the cases, so when i put the new check somewhere below it, the control never reaches the new check.
please correct me if i'm off somewhere :)
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.
Hmm, that sounds weird because I don’t think we should be doing any typo correction of notFound
to var1
here. That said, I’m not too familiar w/ typo correction myself.
@@ -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?}} |
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.
Er, this note being emitted here is ... not great because we are using it (or at least trying to use it) as a function-like macro below—to be fair though, putting an init list in a function-like macro currently already leads to a flood of errors because we can’t recover from it too well, so if this is the only case that causes a false positive then it’s not that much of an issue imo.
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.
Yeah, tests seem to be missing
clang/lib/Sema/SemaExpr.cpp
Outdated
@@ -2509,6 +2509,20 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, | |||
DC = DC->getLookupParent(); | |||
} | |||
|
|||
// Check whether a similar function-like macro exists and suggest it |
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.
I did try it, but in this case the function returns here itself
I'm not sure I understand what it is the problem with this return? It should happen when a typo replacement has been found, which seems not the case for the originally reported case, see https://godbolt.org/z/br9n6eGsT .
Could you please elaborate a bit more?
Because that
Instead of doing this here before typo correction, the note that this pr adds should probably just be emitted right after the error we emit at the very end of this function
Does seem like a correct soluion.
Is there any other tests we would need to include? |
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.
The tests are in the wrong directory (you’ve added them to a file in test/Preprocessor
, but this is a Sema change so the tests should probably be in test/Sema
).
Additionally, a test involving typo correction would probably be a good idea:
#define FOO1()
void f() {
int FOO;
int x = FOO1; // Typo correction to 'FOO' instead of note about 'FOO1()'.
}
Thanks for the correction @Sirraide , yeah I forgot that area, will add. |
Hey @Sirraide , inorder to include the test, we would need to figure out what's going on with the |
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.
Adding to the current comments.
|
||
#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(...)'}} |
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.
The addition of this diagnostic here when the diagnostic below is error: too many arguments provided to function-like macro invocation
seems not very helpful and too verbose.
I did find what was wrong When we try to correct typos, we seem to do it by looking over all the identifiers in the Context which includes the typo itself! // clang/lib/Sema/SemaLookup.cpp
for (const auto &I : Context.Idents)
Consumer->FoundName(I.getKey()); as the typo and the correction (the typo too) are identical, a correction is added.
To avoid this, i tried to check for identical identifiers, but in some cases identical identifiers are a necessary correction too for example when:
because of this ambiguity, the i'm lost rn, any help with fixing it would be appreciated! |
… new behavior and Format the changes
…ut parentheses Co-authored-by: Sirraide <[email protected]>
…arify usage and improve error messages
…ut parentheses Co-authored-by: Mariya Podchishchaeva <[email protected]>
…nitializer list tests
a7cb0e3
to
eac145a
Compare
Do you have commit access for LLVM? If not you might indeed not be able to. |
…function-like macros
No, i don't have commit access. |
I've removed the ones added by your merge. |
I’m not sure if this is the case, but my theory is that it’s specifically creating a new commit on merge that does that. After resolving conflicts, you have to run |
Hmm, I don’t know too much about typo correction so I’m probably not the best person to look into that. |
Could you tag someone who works on this? |
clang/lib/Sema/SemaExpr.cpp
Outdated
if (MI && MI->isFunctionLike()) { | ||
SemaRef.Diag(TypoLoc, | ||
diag::err_undeclared_var_use_suggest_func_like_macro) | ||
<< II->getName(); |
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.
<< II->getName(); | |
<< II; |
This will ensure the name is properly quoted by the diagnostics engine.
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.
Tried it, but gives 'FOO1' is defined as an object-like macro; did you mean ''FOO1'(...)'?
so reverted back.
@AaronBallman could you help me with this? I do understand the delayed typo thing but how is that being decided? also, I couldn't find that documented. |
c0ed9df
to
d463086
Compare
…alizer list tests
d463086
to
cc50469
Compare
Done! (Sorry for force pushes 😅, git suddenly decided to act weird). |
This PR enhances the Clang diagnostics to provide better guidance when function-like macros are used without parentheses. Additionally, it updates the relevant test cases to reflect these changes (#123038 ).