Skip to content

Commit 1a01836

Browse files
committed
[clang][Sema] Suggest/Hint Standard Library Include File
Clang now tries to suggest and hint standard library include file by emitting the following. ``` 4 | std::unordered_map<std::string, std::string> map; | ^~~~~~~~~~~~~ main.cpp:4:10: note: maybe try to include <unordered_map>; 'std::unordered_map' is defined in <unordered_map> main.cpp:4:10: note: 'std::unordered_map' is a c++11 feature main.cpp:4:42: note: maybe try to include <string>; 'std::string' is defined in <string> ``` This works for both C and C++. fixes #120388
1 parent 58b939a commit 1a01836

26 files changed

+5218
-2895
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ Improvements to Clang's diagnostics
470470
diagnostics. This fixes a bunch of `bool` being printed as `_Bool`, and also
471471
a bunch of HLSL types being printed as their C++ equivalents.
472472
- Clang now consistently quotes expressions in diagnostics.
473+
- Clang now suggest standard library include path and its associated C++ or C language version.
473474
- When printing types for diagnostics, clang now doesn't suppress the scopes of
474475
template arguments contained within nested names.
475476
- The ``-Wshift-bool`` warning has been added to warn about shifting a boolean. (#GH28334)

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5978,6 +5978,10 @@ def err_template_expansion_into_fixed_list : Error<
59785978
"template|concept}0">;
59795979
def note_parameter_type : Note<
59805980
"parameter of type %0 is declared here">;
5981+
def note_standard_lib_include_suggestion : Note<
5982+
"maybe try to include %0; '%1' is defined in %0">;
5983+
def note_standard_lib_version : Note<
5984+
"'%0' is a %1 feature">;
59815985

59825986
// C++11 Variadic Templates
59835987
def err_template_param_pack_default_arg : Error<

clang/include/clang/Sema/Sema.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3611,6 +3611,16 @@ class Sema final : public SemaBase {
36113611
ParsedType &SuggestedType,
36123612
bool IsTemplateName = false);
36133613

3614+
// Try to suggest the missing standard library include files
3615+
//
3616+
// \param SymbolName the symbol name like 'cout'
3617+
// \param SourceLocation the location of the note being emitted
3618+
// \param Namespace the namespace that must end with ::, so like 'std::'
3619+
void NoteStandardIncludes(StringRef SymbolName, SourceLocation IILoc,
3620+
StringRef Namespace);
3621+
void NoteStandardIncludes(StringRef SymbolName, SourceLocation IILoc,
3622+
const CXXScopeSpec *SS);
3623+
36143624
/// Attempt to behave like MSVC in situations where lookup of an unqualified
36153625
/// type name has failed in a dependent context. In these situations, we
36163626
/// automatically form a DependentTypeName that will retry lookup in a related

clang/include/clang/Tooling/Inclusions/StandardLibrary.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,27 @@ class NamespaceDecl;
2929
class DeclContext;
3030
namespace tooling {
3131
namespace stdlib {
32+
enum Version {
33+
Unknown,
34+
35+
// c++ versions
36+
CPlusPlusStart,
37+
CPlusPlus11,
38+
CPlusPlus14,
39+
CPlusPlus17,
40+
CPlusPlus20,
41+
CPlusPlus23,
42+
CPlusPlus26,
43+
CPlusPlusEnd,
44+
45+
// c version
46+
CStart,
47+
C99,
48+
C11,
49+
CEnd
50+
};
51+
52+
llvm::StringRef GetAsString(Version Ver);
3253

3354
class Symbol;
3455
enum class Lang { C = 0, CXX, LastValue = CXX };
@@ -85,6 +106,7 @@ class Symbol {
85106
std::optional<Header> header() const;
86107
// Some symbols may be provided by multiple headers.
87108
llvm::SmallVector<Header> headers() const;
109+
Version version() const;
88110

89111
private:
90112
Symbol(unsigned ID, Lang Language) : ID(ID), Language(Language) {}

clang/lib/Parse/ParseExprCXX.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,11 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
226226
HasScopeSpecifier = true;
227227
}
228228

229+
// If `FailedNestedNameBuilding` is true, attempt to suggest the standard
230+
// include file corresponding to the current `FullNamespace` and symbol.
231+
std::string FullNamespace = "";
232+
bool FailedNesatedNameBuilding = false;
233+
229234
// Preferred type might change when parsing qualifiers, we need the original.
230235
auto SavedType = PreferredType;
231236
while (true) {
@@ -454,6 +459,9 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
454459
// We have an identifier followed by a '::'. Lookup this name
455460
// as the name in a nested-name-specifier.
456461
Token Identifier = Tok;
462+
FullNamespace += Identifier.getIdentifierInfo()->getName();
463+
FullNamespace += "::";
464+
457465
SourceLocation IdLoc = ConsumeToken();
458466
assert(Tok.isOneOf(tok::coloncolon, tok::colon) &&
459467
"NextToken() not working properly!");
@@ -465,6 +473,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
465473
if (Actions.ActOnCXXNestedNameSpecifier(
466474
getCurScope(), IdInfo, EnteringContext, SS, CorrectionFlagPtr,
467475
OnlyNamespace)) {
476+
FailedNesatedNameBuilding = true;
468477
// Identifier is not recognized as a nested name, but we can have
469478
// mistyped '::' instead of ':'.
470479
if (CorrectionFlagPtr && IsCorrectedToColon) {
@@ -554,6 +563,11 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
554563
break;
555564
}
556565

566+
if (FailedNesatedNameBuilding && Tok.getKind() == tok::identifier) {
567+
Actions.NoteStandardIncludes(Tok.getIdentifierInfo()->getName(),
568+
Tok.getLocation(), FullNamespace);
569+
}
570+
557571
// Even if we didn't see any pieces of a nested-name-specifier, we
558572
// still check whether there is a tilde in this position, which
559573
// indicates a potential pseudo-destructor.

clang/lib/Sema/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,5 @@ add_clang_library(clangSema
115115
clangEdit
116116
clangLex
117117
clangSupport
118+
clangToolingInclusionsStdlib
118119
)

clang/lib/Sema/SemaDecl.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,12 @@
5858
#include "clang/Sema/SemaSwift.h"
5959
#include "clang/Sema/SemaWasm.h"
6060
#include "clang/Sema/Template.h"
61+
#include "clang/Tooling/Inclusions/StandardLibrary.h"
6162
#include "llvm/ADT/STLForwardCompat.h"
6263
#include "llvm/ADT/SmallPtrSet.h"
6364
#include "llvm/ADT/SmallString.h"
6465
#include "llvm/ADT/StringExtras.h"
66+
#include "llvm/ADT/StringSwitch.h"
6567
#include "llvm/Frontend/HLSL/HLSLRootSignature.h"
6668
#include "llvm/Support/SaveAndRestore.h"
6769
#include "llvm/TargetParser/Triple.h"
@@ -805,6 +807,52 @@ void Sema::DiagnoseUnknownTypeName(IdentifierInfo *&II,
805807
assert(SS && SS->isInvalid() &&
806808
"Invalid scope specifier has already been diagnosed");
807809
}
810+
811+
// don't note standard include files for OpenCL and Objective C
812+
if ((getLangOpts().CPlusPlus || getLangOpts().C99) && !getLangOpts().OpenCL &&
813+
!getLangOpts().ObjC)
814+
NoteStandardIncludes(II->getName(), IILoc, SS);
815+
}
816+
817+
void Sema::NoteStandardIncludes(StringRef SymbolName, SourceLocation IILoc,
818+
StringRef Namespace) {
819+
using clang::tooling::stdlib::Lang;
820+
821+
llvm::StringRef HeaderName = "";
822+
tooling::stdlib::Lang LangOption = tooling::stdlib::Lang::C;
823+
if (getLangOpts().CPlusPlus)
824+
LangOption = clang::tooling::stdlib::Lang::CXX;
825+
826+
if (auto StdSym =
827+
tooling::stdlib::Symbol::named(Namespace, SymbolName, LangOption)) {
828+
if (auto Header = StdSym->header()) {
829+
HeaderName = Header->name();
830+
Diag(IILoc, diag::note_standard_lib_include_suggestion)
831+
<< HeaderName << (Namespace + SymbolName).str();
832+
833+
// Noting the C/C++ version as well
834+
if (StdSym->version() != tooling::stdlib::Unknown) {
835+
llvm::StringRef CPlusPlusVersion =
836+
tooling::stdlib::GetAsString(StdSym->version());
837+
838+
Diag(IILoc, diag::note_standard_lib_version)
839+
<< (Namespace + SymbolName).str() << CPlusPlusVersion;
840+
}
841+
}
842+
}
843+
}
844+
845+
void Sema::NoteStandardIncludes(StringRef SymbolName, SourceLocation IILoc,
846+
const CXXScopeSpec *SS) {
847+
std::string Namespace = "";
848+
if (SS) {
849+
llvm::raw_string_ostream Stream(Namespace);
850+
if (SS->isValid())
851+
SS->getScopeRep()->dump(Stream);
852+
Stream.flush();
853+
}
854+
855+
NoteStandardIncludes(SymbolName, IILoc, Namespace);
808856
}
809857

810858
/// Determine whether the given result set contains either a type name
@@ -16784,6 +16832,7 @@ NamedDecl *Sema::ImplicitlyDefineFunction(SourceLocation Loc,
1678416832
}
1678516833

1678616834
Diag(Loc, diag_id) << &II;
16835+
NoteStandardIncludes(II.getName(), Loc, "");
1678716836
if (Corrected) {
1678816837
// If the correction is going to suggest an implicitly defined function,
1678916838
// skip the correction as not being a particularly good idea.

clang/lib/Sema/SemaExpr.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2660,11 +2660,17 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
26602660
if (!SS.isEmpty()) {
26612661
Diag(R.getNameLoc(), diag::err_no_member)
26622662
<< Name << computeDeclContext(SS, false) << NameRange;
2663+
NoteStandardIncludes(Name.getAsString(), R.getNameLoc(), &SS);
26632664
return true;
26642665
}
26652666

26662667
// Give up, we can't recover.
26672668
Diag(R.getNameLoc(), diagnostic) << Name << NameRange;
2669+
2670+
// don't note standard include files for OpenCL and Objective C
2671+
if ((getLangOpts().CPlusPlus || getLangOpts().C99) && !getLangOpts().OpenCL &&
2672+
!getLangOpts().ObjC)
2673+
NoteStandardIncludes(Name.getAsString(), R.getNameLoc(), /*Namespace=*/"");
26682674
return true;
26692675
}
26702676

0 commit comments

Comments
 (0)