Skip to content

[clang][Sema] Suggest/Hint Standard Library Include File #146227

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,7 @@ Improvements to Clang's diagnostics
diagnostics. This fixes a bunch of `bool` being printed as `_Bool`, and also
a bunch of HLSL types being printed as their C++ equivalents.
- Clang now consistently quotes expressions in diagnostics.
- Clang now suggests including standard library headers when encountering standard types.
- When printing types for diagnostics, clang now doesn't suppress the scopes of
template arguments contained within nested names.
- The ``-Wshift-bool`` warning has been added to warn about shifting a boolean. (#GH28334)
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -5990,6 +5990,12 @@ def err_template_expansion_into_fixed_list : Error<
"template|concept}0">;
def note_parameter_type : Note<
"parameter of type %0 is declared here">;
def note_standard_lib_include_suggestion : Note<
"'%1%2' is defined in %0; did you mean to include it?">;
def note_standard_lib_version : Note<
"'%0%1' is a %enum_select<StandardVersion>{%CPlusPlus11{c++11}|%CPlusPlus14{c++14}|"
"%CPlusPlus17{c++17}|%CPlusPlus20{c++20}|%CPlusPlus23{c++23}|"
"%CPlusPlus26{c++26}|%C99{c99}|%C11{c11}}2 feature">;

// C++11 Variadic Templates
def err_template_param_pack_default_arg : Error<
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -3613,6 +3613,16 @@ class Sema final : public SemaBase {
ParsedType &SuggestedType,
bool IsTemplateName = false);

// Try to suggest the missing standard library include files
//
// \param SymbolName the symbol name like 'cout'
// \param SourceLocation the location of the note being emitted
// \param Namespace the namespace that must end with ::, so like 'std::'
void NoteStandardIncludes(StringRef SymbolName, SourceLocation IILoc,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really a fan of this interface. Unless it is particularly easy, we shouldn't be forcing the inclusion of the '::' in this interface.

Copy link
Contributor Author

@Mr-Anyone Mr-Anyone Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, the interface in clangToolingInclusionsStdlib requires the inclusion of ::, so Namesace+SymbolName gives you the full name. I am fine with removing the inclusion of :: at the end. Should I still change it?

StringRef Namespace);
void NoteStandardIncludes(StringRef SymbolName, SourceLocation IILoc,
const CXXScopeSpec *SS);

/// Attempt to behave like MSVC in situations where lookup of an unqualified
/// type name has failed in a dependent context. In these situations, we
/// automatically form a DependentTypeName that will retry lookup in a related
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Tooling/Inclusions/StandardLibrary.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#ifndef LLVM_CLANG_TOOLING_INCLUSIONS_STANDARDLIBRARY_H
#define LLVM_CLANG_TOOLING_INCLUSIONS_STANDARDLIBRARY_H

#include "clang/Basic/LangStandard.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/StringRef.h"
Expand All @@ -29,7 +30,6 @@ class NamespaceDecl;
class DeclContext;
namespace tooling {
namespace stdlib {

class Symbol;
enum class Lang { C = 0, CXX, LastValue = CXX };

Expand Down Expand Up @@ -85,6 +85,7 @@ class Symbol {
std::optional<Header> header() const;
// Some symbols may be provided by multiple headers.
llvm::SmallVector<Header> headers() const;
std::optional<LangFeatures> version() const;

private:
Symbol(unsigned ID, Lang Language) : ID(ID), Language(Language) {}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,5 @@ add_clang_library(clangSema
clangEdit
clangLex
clangSupport
clangToolingInclusionsStdlib
)
70 changes: 70 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,13 @@
#include "clang/Sema/SemaSwift.h"
#include "clang/Sema/SemaWasm.h"
#include "clang/Sema/Template.h"
#include "clang/Tooling/Inclusions/StandardLibrary.h"
#include "llvm/ADT/STLForwardCompat.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Frontend/HLSL/HLSLRootSignature.h"
#include "llvm/Support/SaveAndRestore.h"
#include "llvm/TargetParser/Triple.h"
#include <algorithm>
Expand Down Expand Up @@ -804,6 +807,72 @@ void Sema::DiagnoseUnknownTypeName(IdentifierInfo *&II,
assert(SS && SS->isInvalid() &&
"Invalid scope specifier has already been diagnosed");
}

// don't note standard include files for OpenCL and Objective C
if ((getLangOpts().CPlusPlus || getLangOpts().C99) && !getLangOpts().OpenCL &&
!getLangOpts().ObjC)
NoteStandardIncludes(II->getName(), IILoc, SS);
}

static unsigned int GetLangFeatureDiagNum(LangFeatures Feature) {
switch (Feature) {
case C99:
return diag::StandardVersion::C99;
case C11:
return diag::StandardVersion::C11;
case CPlusPlus11:
return diag::StandardVersion::CPlusPlus11;
case CPlusPlus14:
return diag::StandardVersion::CPlusPlus14;
case CPlusPlus17:
return diag::StandardVersion::CPlusPlus17;
case CPlusPlus20:
return diag::StandardVersion::CPlusPlus20;
case CPlusPlus23:
return diag::StandardVersion::CPlusPlus23;
case CPlusPlus26:
return diag::StandardVersion::CPlusPlus26;
default:
llvm_unreachable("Invalid LangFeatures options.");
}
}

void Sema::NoteStandardIncludes(StringRef SymbolName, SourceLocation IILoc,
StringRef Namespace) {
using clang::tooling::stdlib::Lang;

llvm::StringRef HeaderName = "";
tooling::stdlib::Lang LangOption = tooling::stdlib::Lang::C;
if (getLangOpts().CPlusPlus)
LangOption = clang::tooling::stdlib::Lang::CXX;

if (auto StdSym =
tooling::stdlib::Symbol::named(Namespace, SymbolName, LangOption)) {
if (auto Header = StdSym->header()) {
HeaderName = Header->name();
Diag(IILoc, diag::note_standard_lib_include_suggestion)
<< HeaderName << Namespace << SymbolName;

// Noting the C/C++ version as well
if (auto LangVersion = StdSym->version()) {
Diag(IILoc, diag::note_standard_lib_version)
<< Namespace << SymbolName << GetLangFeatureDiagNum(*LangVersion);
}
}
}
}

void Sema::NoteStandardIncludes(StringRef SymbolName, SourceLocation IILoc,
const CXXScopeSpec *SS) {
std::string Namespace = "";
if (SS) {
llvm::raw_string_ostream Stream(Namespace);
if (SS->isValid())
SS->getScopeRep()->dump(Stream);
Stream.flush();
}

NoteStandardIncludes(SymbolName, IILoc, Namespace);
}

/// Determine whether the given result set contains either a type name
Expand Down Expand Up @@ -16783,6 +16852,7 @@ NamedDecl *Sema::ImplicitlyDefineFunction(SourceLocation Loc,
}

Diag(Loc, diag_id) << &II;
NoteStandardIncludes(II.getName(), Loc, "");
if (Corrected) {
// If the correction is going to suggest an implicitly defined function,
// skip the correction as not being a particularly good idea.
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2658,11 +2658,17 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R,
if (!SS.isEmpty()) {
Diag(R.getNameLoc(), diag::err_no_member)
<< Name << computeDeclContext(SS, false) << NameRange;
NoteStandardIncludes(Name.getAsString(), R.getNameLoc(), &SS);
return true;
}

// Give up, we can't recover.
Diag(R.getNameLoc(), diagnostic) << Name << NameRange;

// don't note standard include files for OpenCL and Objective C
if ((getLangOpts().CPlusPlus || getLangOpts().C99) && !getLangOpts().OpenCL &&
!getLangOpts().ObjC)
NoteStandardIncludes(Name.getAsString(), R.getNameLoc(), /*Namespace=*/"");
return true;
}

Expand Down
Loading
Loading