llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Jongmyeong Choi (jongmyeong-choi) <details> <summary>Changes</summary> When a “use of undeclared identifier” error happens for a function listed in StdSymbolMap, emit a note telling the user which header to include. Because nested-name-specifier errors occur before the function name is known, perform this lookup later in isCXXDeclarationSpecifier() after a parse failure. --- Full diff: https://github.com/llvm/llvm-project/pull/140247.diff 12 Files Affected: - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+1) - (modified) clang/lib/Parse/ParseTentative.cpp (+30) - (modified) clang/lib/Sema/CMakeLists.txt (+2-1) - (modified) clang/lib/Sema/SemaExpr.cpp (+17) - (modified) clang/test/CXX/dcl.decl/dcl.decomp/p3.cpp (+9-3) - (modified) clang/test/CXX/drs/cwg3xx.cpp (+2) - (modified) clang/test/Modules/implicit-declared-allocation-functions.cppm (+6-6) - (modified) clang/test/Modules/macro-reexport.cpp (+3-3) - (modified) clang/test/OpenMP/allocate_modifiers_messages.cpp (+1) - (modified) clang/test/Sema/builtin-setjmp.c (+6-4) - (added) clang/test/Sema/note-suggest-header.cpp (+10) - (modified) clang/test/SemaCXX/no-implicit-builtin-decls.cpp (+1-1) ``````````diff diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 604bb56d28252..bbc8e56175665 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5984,6 +5984,7 @@ def err_unexpected_typedef : Error< def err_unexpected_namespace : Error< "unexpected namespace name %0: expected expression">; def err_undeclared_var_use : Error<"use of undeclared identifier %0">; +def note_include_for_declaration : Note<"perhaps `#include %0` is needed?">; def ext_undeclared_unqual_id_with_dependent_base : ExtWarn< "use of undeclared identifier %0; " "unqualified lookup into dependent bases of class template %1 is a Microsoft extension">, diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index fcd76c75c9bfb..3cb46cbc4b665 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -14,6 +14,8 @@ #include "clang/Basic/DiagnosticParse.h" #include "clang/Parse/Parser.h" #include "clang/Sema/ParsedTemplate.h" +#include "clang/Tooling/Inclusions/StandardLibrary.h" + using namespace clang; /// isCXXDeclarationStatement - C++-specialized function that disambiguates @@ -1442,6 +1444,34 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename, // If annotation failed, assume it's a non-type. // FIXME: If this happens due to an undeclared identifier, treat it as // ambiguous. + + if (Tok.is(tok::annot_cxxscope)) { + CXXScopeSpec SS; + Actions.RestoreNestedNameSpecifierAnnotation( + Tok.getAnnotationValue(), Tok.getAnnotationRange(), SS); + auto Next = NextToken(); + if (SS.isInvalid() && Next.is(tok::identifier)) { + auto SymbolName = Next.getIdentifierInfo()->getName(); + SourceRange SR = Tok.getAnnotationRange(); + CharSourceRange CSR = CharSourceRange::getCharRange( + SR.getBegin(), SR.getEnd().getLocWithOffset(2)); + StringRef ScopeText = Lexer::getSourceText(CSR, PP.getSourceManager(), + PP.getLangOpts()); + + auto header = [](llvm::StringRef symbolName, llvm::StringRef scope) { + for (const auto &symbol : clang::tooling::stdlib::Symbol::all()) { + if (symbol.name() == symbolName && symbol.scope() == scope) + return symbol.header()->name(); + } + return llvm::StringRef(); + }(SymbolName, ScopeText); + + if (header.size() > 0) { + Diag(Next.getLocation(), diag::note_include_for_declaration) + << header; + } + } + } if (Tok.is(tok::identifier)) return TPResult::False; } diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 4b87004e4b8ea..de084d01cc553 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -114,4 +114,5 @@ add_clang_library(clangSema clangEdit clangLex clangSupport - ) + clangToolingInclusionsStdlib +) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index d72d97addfac2..9c12eea2083b8 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -58,6 +58,7 @@ #include "clang/Sema/SemaOpenMP.h" #include "clang/Sema/SemaPseudoObject.h" #include "clang/Sema/Template.h" +#include "clang/Tooling/Inclusions/StandardLibrary.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/STLForwardCompat.h" #include "llvm/ADT/StringExtras.h" @@ -2647,6 +2648,22 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, // Give up, we can't recover. Diag(R.getNameLoc(), diagnostic) << Name << NameRange; + + llvm::errs() << "cheese ##1) name's kind : " << Name.getAsString() << "\n"; + auto header = [](const std::string &symbolName) { + for (const auto &symbol : clang::tooling::stdlib::Symbol::all()) { + if (symbol.header() && symbol.name() == symbolName) + return symbol.header()->name(); + } + return llvm::StringRef(); + }(Name.getAsString()); + + llvm::errs() << "cheese ##2) header : " << header << "\n"; + bool IsFunctionContext = !Args.empty() || ExplicitTemplateArgs != nullptr; + if (diagnostic == diag::err_undeclared_var_use && header.size() > 0 && + IsFunctionContext) { + Diag(R.getNameLoc(), diag::note_include_for_declaration) << header; + } return true; } diff --git a/clang/test/CXX/dcl.decl/dcl.decomp/p3.cpp b/clang/test/CXX/dcl.decl/dcl.decomp/p3.cpp index ce5eefc6bfdb4..60b66aac9e5ca 100644 --- a/clang/test/CXX/dcl.decl/dcl.decomp/p3.cpp +++ b/clang/test/CXX/dcl.decl/dcl.decomp/p3.cpp @@ -26,14 +26,18 @@ void no_get_1() { auto [a0, a1] = A(); // expected-error {{decomposes into 3 elements}} auto [b0, b1] = B(); // expected-error {{decomposes into 3 elements}} } - auto [a0, a1, a2] = A(); // expected-error {{undeclared identifier 'get'}} expected-note {{in implicit initialization of binding declaration 'a0'}} + auto [a0, a1, a2] = A(); // expected-error {{undeclared identifier 'get'}} \ + // expected-note {{perhaps `#include <ranges>` is needed?}} \ + // expected-note {{in implicit initialization of binding declaration 'a0'}} } int get(A); void no_get_2() { // FIXME: This diagnostic is not great. - auto [a0, a1, a2] = A(); // expected-error {{undeclared identifier 'get'}} expected-note {{in implicit initialization of binding declaration 'a0'}} + auto [a0, a1, a2] = A(); // expected-error {{undeclared identifier 'get'}} \ + // expected-note {{perhaps `#include <ranges>` is needed?}} \ + // expected-note {{in implicit initialization of binding declaration 'a0'}} } template<int> float &get(A); // expected-note 2 {{no known conversion}} @@ -172,7 +176,9 @@ template<int> int get(ADL::X); template<> struct std::tuple_size<ADL::X> { static const int value = 1; }; template<> struct std::tuple_element<0, ADL::X> { typedef int type; }; void adl_only_bad() { - auto [x] = ADL::X(); // expected-error {{undeclared identifier 'get'}} expected-note {{in implicit init}} + auto [x] = ADL::X(); // expected-error {{undeclared identifier 'get'}} \ + // expected-note {{perhaps `#include <ranges>` is needed?}} \ + // expected-note {{in implicit init}} } template<typename ElemType, typename GetTypeLV, typename GetTypeRV> diff --git a/clang/test/CXX/drs/cwg3xx.cpp b/clang/test/CXX/drs/cwg3xx.cpp index 8b035cf6f2370..c94c478d8e3ac 100644 --- a/clang/test/CXX/drs/cwg3xx.cpp +++ b/clang/test/CXX/drs/cwg3xx.cpp @@ -1474,6 +1474,7 @@ namespace cwg387 { // cwg387: 2.8 a = gcd(a, b); b = gcd(3, 4); // expected-error@-1 {{use of undeclared identifier 'gcd'}} + // expected-note@-2 {{perhaps `#include <numeric>` is needed?}} } } @@ -1489,6 +1490,7 @@ namespace cwg387 { // cwg387: 2.8 a = gcd(a, b); b = gcd(3, 4); // expected-error@-1 {{use of undeclared identifier 'gcd'}} + // expected-note@-2 {{perhaps `#include <numeric>` is needed?}} } } } // namespace cwg387 diff --git a/clang/test/Modules/implicit-declared-allocation-functions.cppm b/clang/test/Modules/implicit-declared-allocation-functions.cppm index b378a1d1365ee..5507e19fda83d 100644 --- a/clang/test/Modules/implicit-declared-allocation-functions.cppm +++ b/clang/test/Modules/implicit-declared-allocation-functions.cppm @@ -17,14 +17,14 @@ export void alloc_wrapper() { // std::align_val_t is ill-formed unless a standard library declaration // ([cstddef.syn], [new.syn], [std.modules]) of that name precedes // ([basic.lookup.general]) the use of that name. - void *b = ::operator new((std::size_t)32); // expected-error {{use of undeclared identifier 'std'}} - void *c = ::operator new((std::size_t)32, // expected-error {{use of undeclared identifier 'std'}} - (std::align_val_t)64); // expected-error {{use of undeclared identifier 'std'}} + void *b = ::operator new((std::size_t)32); // expected-error {{use of undeclared identifier 'std'}} expected-note {{perhaps `#include <cstddef>` is needed?}} + void *c = ::operator new((std::size_t)32, // expected-error {{use of undeclared identifier 'std'}} expected-note {{perhaps `#include <cstddef>` is needed?}} + (std::align_val_t)64); // expected-error {{use of undeclared identifier 'std'}} expected-note {{perhaps `#include <new>` is needed?}} ::operator delete(a); - ::operator delete(b, (std::size_t)32); // expected-error {{use of undeclared identifier 'std'}} - ::operator delete(c, (std::size_t)32, // expected-error {{use of undeclared identifier 'std'}} - (std::align_val_t)64); // expected-error {{use of undeclared identifier 'std'}} + ::operator delete(b, (std::size_t)32); // expected-error {{use of undeclared identifier 'std'}} expected-note {{perhaps `#include <cstddef>` is needed?}} + ::operator delete(c, (std::size_t)32, // expected-error {{use of undeclared identifier 'std'}} expected-note {{perhaps `#include <cstddef>` is needed?}} + (std::align_val_t)64); // expected-error {{use of undeclared identifier 'std'}} expected-note {{perhaps `#include <new>` is needed?}} } //--- new diff --git a/clang/test/Modules/macro-reexport.cpp b/clang/test/Modules/macro-reexport.cpp index 4e825a07bd803..145b2e9c5d01b 100644 --- a/clang/test/Modules/macro-reexport.cpp +++ b/clang/test/Modules/macro-reexport.cpp @@ -21,13 +21,13 @@ #include "f1.h" void f() { return assert(true); } // expected-error {{undeclared identifier 'd'}} #include "e2.h" // undefines d1's macro -void g() { return assert(true); } // expected-error {{undeclared identifier 'assert'}} +void g() { return assert(true); } // expected-error {{undeclared identifier 'assert'}} expected-note {{perhaps `#include <cassert>` is needed?}} #elif defined(D1) #include "e1.h" // undefines c1's macro but not d1's macro #include "d1.h" void f() { return assert(true); } // expected-error {{undeclared identifier 'd'}} #include "e2.h" // undefines d1's macro -void g() { return assert(true); } // expected-error {{undeclared identifier 'assert'}} +void g() { return assert(true); } // expected-error {{undeclared identifier 'assert'}} expected-note {{perhaps `#include <cassert>` is needed?}} #elif defined(D2) #include "d2.h" void f() { return assert(true); } // expected-error {{undeclared identifier 'b'}} @@ -35,5 +35,5 @@ void f() { return assert(true); } // expected-error {{undeclared identifier 'b'} // e2 undefines d1's macro, which overrides c1's macro. #include "e2.h" #include "c1.h" -void f() { return assert(true); } // expected-error {{undeclared identifier 'assert'}} +void f() { return assert(true); } // expected-error {{undeclared identifier 'assert'}} expected-note {{perhaps `#include <cassert>` is needed?}} #endif diff --git a/clang/test/OpenMP/allocate_modifiers_messages.cpp b/clang/test/OpenMP/allocate_modifiers_messages.cpp index 6867e78a89ee9..2e6fa37096973 100644 --- a/clang/test/OpenMP/allocate_modifiers_messages.cpp +++ b/clang/test/OpenMP/allocate_modifiers_messages.cpp @@ -108,6 +108,7 @@ int main() { #pragma omp scope private(b) allocate(align // expected-error@+1 {{duplicate modifier 'align' in 'allocate' clause}} #pragma omp scope private(a) allocate(align(8), align(4) : a) + // expected-note@+6 {{perhaps `#include <memory>` is needed?}} // expected-error@+5 {{use of undeclared identifier 'align'}} // expected-error@+4 {{expected ',' or ')' in 'allocate' clause}} // expected-error@+3 {{expected ')'}} diff --git a/clang/test/Sema/builtin-setjmp.c b/clang/test/Sema/builtin-setjmp.c index a71f87162612d..3369468a4a79d 100644 --- a/clang/test/Sema/builtin-setjmp.c +++ b/clang/test/Sema/builtin-setjmp.c @@ -35,11 +35,13 @@ void use(void) { setjmp(0); #if NO_SETJMP // cxx-error@-2 {{undeclared identifier 'setjmp'}} - // c-error@-3 {{call to undeclared function 'setjmp'; ISO C99 and later do not support implicit function declarations}} + // cxx-note@-3 {{perhaps `#include <csetjmp>` is needed?}} + // c-error@-4 {{call to undeclared function 'setjmp'; ISO C99 and later do not support implicit function declarations}} #elif ONLY_JMP_BUF - // cxx-error@-5 {{undeclared identifier 'setjmp'}} - // c-error@-6 {{call to undeclared library function 'setjmp' with type 'int (jmp_buf)' (aka 'int (int *)'); ISO C99 and later do not support implicit function declarations}} - // c-note@-7 {{include the header <setjmp.h> or explicitly provide a declaration for 'setjmp'}} + // cxx-error@-6 {{undeclared identifier 'setjmp'}} + // cxx-note@-7 {{perhaps `#include <csetjmp>` is needed?}} + // c-error@-8 {{call to undeclared library function 'setjmp' with type 'int (jmp_buf)' (aka 'int (int *)'); ISO C99 and later do not support implicit function declarations}} + // c-note@-9 {{include the header <setjmp.h> or explicitly provide a declaration for 'setjmp'}} #else // cxx-no-diagnostics #endif diff --git a/clang/test/Sema/note-suggest-header.cpp b/clang/test/Sema/note-suggest-header.cpp new file mode 100644 index 0000000000000..7444763224903 --- /dev/null +++ b/clang/test/Sema/note-suggest-header.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +void f(void) { + int_val2 = 0; // expected-error{{use of undeclared identifier}} + sin(0); // expected-error{{use of undeclared identifier 'sin'}} \ + // expected-note{{perhaps `#include <cmath>` is needed?}} + + std::cout << "Hello world\n"; // expected-error{{use of undeclared identifier 'std'}} \ + // expected-note{{perhaps `#include <iostream>` is needed?}} +} \ No newline at end of file diff --git a/clang/test/SemaCXX/no-implicit-builtin-decls.cpp b/clang/test/SemaCXX/no-implicit-builtin-decls.cpp index d82f7f18bffe0..d848c9740dc6e 100644 --- a/clang/test/SemaCXX/no-implicit-builtin-decls.cpp +++ b/clang/test/SemaCXX/no-implicit-builtin-decls.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s void f() { - void *p = malloc(sizeof(int) * 10); // expected-error{{use of undeclared identifier 'malloc'}} + void *p = malloc(sizeof(int) * 10); // expected-error{{use of undeclared identifier 'malloc'}} expected-note {{perhaps `#include <cstdlib>` is needed?}} } int malloc(double); `````````` </details> https://github.com/llvm/llvm-project/pull/140247 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits