21#include "clang-include-cleaner/Record.h"
26#include "clang/AST/ASTContext.h"
27#include "clang/AST/Decl.h"
28#include "clang/AST/DeclGroup.h"
29#include "clang/AST/ExternalASTSource.h"
30#include "clang/Basic/Diagnostic.h"
31#include "clang/Basic/DiagnosticIDs.h"
32#include "clang/Basic/DiagnosticSema.h"
33#include "clang/Basic/FileEntry.h"
34#include "clang/Basic/LLVM.h"
35#include "clang/Basic/LangOptions.h"
36#include "clang/Basic/SourceLocation.h"
37#include "clang/Basic/SourceManager.h"
38#include "clang/Basic/TokenKinds.h"
39#include "clang/Frontend/CompilerInstance.h"
40#include "clang/Frontend/CompilerInvocation.h"
41#include "clang/Frontend/FrontendActions.h"
42#include "clang/Frontend/FrontendOptions.h"
43#include "clang/Frontend/PrecompiledPreamble.h"
44#include "clang/Lex/Lexer.h"
45#include "clang/Lex/PPCallbacks.h"
46#include "clang/Lex/PreprocessingRecord.h"
47#include "clang/Lex/Preprocessor.h"
48#include "clang/Sema/HeuristicResolver.h"
49#include "clang/Serialization/ASTWriter.h"
50#include "clang/Tooling/CompilationDatabase.h"
51#include "clang/Tooling/Syntax/Tokens.h"
52#include "llvm/ADT/ArrayRef.h"
53#include "llvm/ADT/DenseMap.h"
54#include "llvm/ADT/STLExtras.h"
55#include "llvm/ADT/STLFunctionalExtras.h"
56#include "llvm/ADT/SmallVector.h"
57#include "llvm/ADT/StringRef.h"
58#include "llvm/Support/Error.h"
59#include "llvm/Support/MemoryBuffer.h"
73template <
class T> std::size_t getUsedBytes(
const std::vector<T> &Vec) {
74 return Vec.capacity() *
sizeof(
T);
77class DeclTrackingASTConsumer :
public ASTConsumer {
79 DeclTrackingASTConsumer(std::vector<Decl *> &TopLevelDecls)
80 : TopLevelDecls(TopLevelDecls) {}
82 bool HandleTopLevelDecl(DeclGroupRef DG)
override {
84 auto &SM =
D->getASTContext().getSourceManager();
87 if (
const NamedDecl *ND = dyn_cast<NamedDecl>(D))
92 if (isa<ObjCMethodDecl>(D))
95 TopLevelDecls.push_back(D);
101 std::vector<Decl *> &TopLevelDecls;
104class ClangdFrontendAction :
public SyntaxOnlyAction {
106 std::vector<Decl *> takeTopLevelDecls() {
return std::move(TopLevelDecls); }
109 std::unique_ptr<ASTConsumer>
110 CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile)
override {
111 return std::make_unique<DeclTrackingASTConsumer>( TopLevelDecls);
115 std::vector<Decl *> TopLevelDecls;
124class ReplayPreamble :
private PPCallbacks {
129 static void attach(std::vector<Inclusion> Includes,
130 const MainFileMacros Macros, CompilerInstance &Clang,
131 const PreambleBounds &PB) {
132 auto &PP = Clang.getPreprocessor();
133 auto *ExistingCallbacks = PP.getPPCallbacks();
135 if (!ExistingCallbacks)
137 PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(
new ReplayPreamble(
138 std::move(Includes), std::move(Macros), ExistingCallbacks,
139 Clang.getSourceManager(), PP, Clang.getLangOpts(), PB)));
142 assert(PP.getPPCallbacks() != ExistingCallbacks &&
143 "Expected chaining implementation");
147 ReplayPreamble(std::vector<Inclusion> Includes, MainFileMacros Macros,
148 PPCallbacks *Delegate,
const SourceManager &SM,
149 Preprocessor &PP,
const LangOptions &LangOpts,
150 const PreambleBounds &PB)
151 : Includes(std::move(Includes)), Macros(std::move(Macros)),
152 Delegate(Delegate), SM(SM), PP(PP) {
155 MainFileTokens = syntax::tokenize(
156 syntax::FileRange(SM.getMainFileID(), 0, PB.Size), SM, LangOpts);
174 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
175 SrcMgr::CharacteristicKind Kind, FileID PrevFID)
override {
177 if (Reason == FileChangeReason::ExitFile &&
178 SM.getBufferOrFake(PrevFID).getBufferIdentifier() ==
"<built-in>")
185 for (
const auto &[SID, Refs] : Macros.MacroRefs) {
186 for (
const auto &Ref : Refs) {
187 if (!Ref.IsDefinition)
189 auto Loc = SM.getComposedLoc(SM.getMainFileID(), Ref.StartOffset);
191 if (Lexer::getRawToken(Loc, Tok, SM, PP.getLangOpts(),
false))
193 if (
auto *II = PP.getIdentifierInfo(Tok.getRawIdentifier())) {
194 Tok.setIdentifierInfo(II);
195 Tok.setKind(tok::identifier);
196 if (
auto *
MD = PP.getLocalMacroDirective(II))
197 Delegate->MacroDefined(Tok,
MD);
201 for (
const auto &Inc : Includes) {
202 OptionalFileEntryRef
File;
203 if (Inc.Resolved !=
"")
204 File = expectedToOptional(SM.getFileManager().getFileRef(Inc.Resolved));
207 auto HashLoc = SM.getComposedLoc(SM.getMainFileID(), Inc.HashOffset);
208 auto HashTok = llvm::partition_point(MainFileTokens,
209 [&HashLoc](
const syntax::Token &T) {
210 return T.location() < HashLoc;
212 assert(HashTok != MainFileTokens.end() && HashTok->kind() == tok::hash);
214 auto IncludeTok = std::next(HashTok);
215 assert(IncludeTok != MainFileTokens.end());
217 auto FileTok = std::next(IncludeTok);
218 assert(FileTok != MainFileTokens.end());
222 Token SynthesizedIncludeTok;
223 SynthesizedIncludeTok.startToken();
224 SynthesizedIncludeTok.setLocation(IncludeTok->location());
225 SynthesizedIncludeTok.setLength(IncludeTok->length());
226 SynthesizedIncludeTok.setKind(tok::raw_identifier);
227 SynthesizedIncludeTok.setRawIdentifierData(IncludeTok->text(SM).data());
228 PP.LookUpIdentifierInfo(SynthesizedIncludeTok);
231 Token SynthesizedFilenameTok;
232 SynthesizedFilenameTok.startToken();
233 SynthesizedFilenameTok.setLocation(FileTok->location());
238 SynthesizedFilenameTok.setLength(Inc.Written.length());
239 SynthesizedFilenameTok.setKind(tok::header_name);
240 SynthesizedFilenameTok.setLiteralData(Inc.Written.data());
242 llvm::StringRef WrittenFilename =
243 llvm::StringRef(Inc.Written).drop_front().drop_back();
244 Delegate->InclusionDirective(
245 HashTok->location(), SynthesizedIncludeTok, WrittenFilename,
246 Inc.Written.front() ==
'<',
247 syntax::FileRange(SM, SynthesizedFilenameTok.getLocation(),
248 SynthesizedFilenameTok.getEndLoc())
250 File,
"SearchPath",
"RelPath",
251 nullptr,
false, Inc.FileKind);
253 Delegate->FileSkipped(*
File, SynthesizedFilenameTok, Inc.FileKind);
257 const std::vector<Inclusion> Includes;
258 const MainFileMacros Macros;
259 PPCallbacks *Delegate;
260 const SourceManager &SM;
262 std::vector<syntax::Token> MainFileTokens;
265std::vector<Diag> getIncludeCleanerDiags(
ParsedAST &
AST, llvm::StringRef Code,
268 if (Cfg.Diagnostics.SuppressAll)
270 bool SuppressMissing =
271 Cfg.Diagnostics.Suppress.contains(
"missing-includes") ||
273 bool SuppressUnused =
274 Cfg.Diagnostics.Suppress.contains(
"unused-includes") ||
276 if (SuppressMissing && SuppressUnused)
279 AST, Cfg.Diagnostics.Includes.AnalyzeAngledIncludes);
281 Findings.MissingIncludes.clear();
283 Findings.UnusedIncludes.clear();
285 AST, Code, Findings, TFS, Cfg.Diagnostics.Includes.IgnoreHeader,
286 Cfg.Style.AngledHeaders, Cfg.Style.QuotedHeaders);
291std::optional<ParsedAST>
293 std::unique_ptr<clang::CompilerInvocation> CI,
294 llvm::ArrayRef<Diag> CompilerInvocationDiags,
295 std::shared_ptr<const PreambleData> Preamble) {
301 if (Preamble && Preamble->StatCache)
302 VFS = Preamble->StatCache->getConsumingFS(std::move(VFS));
306 if (CI->getFrontendOpts().Inputs.size() > 0) {
307 auto Lang = CI->getFrontendOpts().Inputs[0].getKind().getLanguage();
308 if (Lang == Language::Asm || Lang == Language::LLVM_IR) {
309 elog(
"Clangd does not support assembly or IR source files");
316 CI->getFrontendOpts().DisableFree =
false;
317 const PrecompiledPreamble *PreamblePCH =
318 Preamble ? &Preamble->Preamble :
nullptr;
322 CI->getLangOpts().DelayedTemplateParsing =
false;
324 std::vector<std::unique_ptr<FeatureModule::ASTListener>> ASTListeners;
327 if (
auto Listener = M.astListeners())
328 ASTListeners.emplace_back(std::move(Listener));
334 for (
const auto &L : ASTListeners)
335 L->sawDiagnostic(D,
Diag);
338 for (
const auto &L : ASTListeners)
339 L->finalizeDiagnostic(
Diag);
344 if (Preamble && Preamble->RequiredModules)
345 Preamble->RequiredModules->adjustHeaderSearchOptions(
346 CI->getHeaderSearchOpts());
348 std::optional<PreamblePatch> Patch;
359 llvm::MemoryBuffer::getMemBufferCopy(Inputs.
Contents, Filename), VFS,
365 std::vector<Diag> Diags(ASTDiags.
take());
366 elog(
"Failed to prepare a compiler instance: {0}",
371 auto Action = std::make_unique<ClangdFrontendAction>();
372 for (
const auto &L : ASTListeners)
373 L->beforeBeginSourceFile(*Clang);
374 const FrontendInputFile &MainInput = Clang->getFrontendOpts().Inputs[0];
375 if (!Action->BeginSourceFile(*Clang, MainInput)) {
376 elog(
"BeginSourceFile() failed when building AST for {0}",
377 MainInput.getFile());
385 if (Preamble && Preamble->MainIsIncludeGuarded) {
386 const SourceManager &SM = Clang->getSourceManager();
387 OptionalFileEntryRef MainFE = SM.getFileEntryRefForID(SM.getMainFileID());
388 Clang->getPreprocessor().getHeaderSearchInfo().MarkFileIncludeOnce(*MainFE);
392 auto BuildDir = VFS->getCurrentWorkingDirectory();
393 std::optional<IncludeFixer> FixIncludes;
394 llvm::DenseMap<diag::kind, DiagnosticsEngine::Level> OverriddenSeverity;
400 for (
auto ID : {diag::ext_implicit_function_decl_c99,
401 diag::ext_implicit_lib_function_decl,
402 diag::ext_implicit_lib_function_decl_c99,
403 diag::warn_implicit_function_decl}) {
404 OverriddenSeverity.try_emplace(
405 ID, Clang->getDiagnostics().getDiagnosticLevel(ID, SourceLocation()));
406 Clang->getDiagnostics().setSeverity(ID, diag::Severity::Error,
410 [&](DiagnosticsEngine::Level Level,
const clang::Diagnostic &
Info) {
411 auto It = OverriddenSeverity.find(
Info.getID());
412 return It == OverriddenSeverity.end() ? Level : It->second;
417 if (Inputs.
Index && !BuildDir.getError()) {
420 auto Inserter = std::make_shared<IncludeInserter>(
421 Filename, Inputs.
Contents, Style, BuildDir.get(),
422 &Clang->getPreprocessor().getHeaderSearchInfo(),
424 ArrayRef<Inclusion> MainFileIncludes;
426 MainFileIncludes = Preamble->Includes.MainFileIncludes;
427 for (
const auto &Inc : Preamble->Includes.MainFileIncludes)
428 Inserter->addExisting(Inc);
435 MainFileIncludes, {})
437 FixIncludes.emplace(Filename, Inserter, *Inputs.
Index,
439 ASTDiags.
contributeFixes([&FixIncludes](DiagnosticsEngine::Level Level,
440 const clang::Diagnostic &
Info) {
441 return FixIncludes->fix(Level,
Info);
443 Clang->setExternalSemaSource(FixIncludes->unresolvedNameRecorder());
447 for (
const auto &L : ASTListeners)
448 L->beforePPCallbacks(*Clang);
451 include_cleaner::PragmaIncludes PI;
454 Includes = Preamble->Includes;
455 Includes.MainFileIncludes = Patch->preambleIncludes();
457 ReplayPreamble::attach(Patch->preambleIncludes(), Patch->mainFileMacros(),
458 *Clang, Patch->modifiedBounds());
459 PI = *Preamble->Pragmas;
471 std::vector<PragmaMark> Marks;
473 Macros = Patch->mainFileMacros();
474 Marks = Patch->marks();
476 auto &PP = Clang->getPreprocessor();
477 auto MacroCollector = std::make_unique<CollectMainFileMacros>(PP, Macros);
478 auto *MacroCollectorPtr = MacroCollector.get();
479 PP.addPPCallbacks(std::move(MacroCollector));
488 syntax::TokenCollector CollectTokens(PP);
493 for (
const auto &L : ASTListeners)
494 L->beforeExecute(*Clang);
496 if (llvm::Error Err = Action->Execute())
497 log(
"Execute() failed when building AST for {0}: {1}", MainInput.getFile(),
502 MacroCollectorPtr->doneParse();
507 syntax::TokenBuffer Tokens = std::move(CollectTokens).consume();
509 Tokens.indexExpandedTokens();
510 std::vector<Decl *> ParsedDecls = Action->takeTopLevelDecls();
512 Clang->getASTContext().setTraversalScope(ParsedDecls);
513 for (
const auto &L : ASTListeners)
514 L->afterExecute(*Clang);
526 std::vector<Diag> Diags = CompilerInvocationDiags;
531 llvm::append_range(Diags, Patch->patchedDiags());
534 std::vector<Diag> D = ASTDiags.
take();
535 Diags.insert(Diags.end(), D.begin(), D.end());
538 std::move(Clang), std::move(Action), std::move(Tokens),
539 std::move(Macros), std::move(Marks), std::move(ParsedDecls),
540 std::move(Diags), std::move(Includes), std::move(PI));
541 llvm::move(getIncludeCleanerDiags(Result, Inputs.
Contents, *Inputs.
TFS),
542 std::back_inserter(Result.Diags));
543 return std::move(Result);
554 auto PP = Clang->getPreprocessorPtr();
555 Clang->setPreprocessor(
nullptr);
556 Action->EndSourceFile();
564 return Clang->getASTContext();
572 return Clang->getPreprocessorPtr();
576 return Clang->getPreprocessor();
580 return LocalTopLevelDecls;
584 return LocalTopLevelDecls;
595 clangd::getUsedBytes(LocalTopLevelDecls) + clangd::getUsedBytes(Diags);
601 Total +=
AST.getASTAllocatedMemory();
602 Total +=
AST.getSideTableAllocatedMemory();
603 Total +=
AST.Idents.getAllocator().getTotalMemory();
604 Total +=
AST.Selectors.getTotalMemory();
606 Total +=
AST.getSourceManager().getContentCacheSize();
607 Total +=
AST.getSourceManager().getDataStructureSizes();
608 Total +=
AST.getSourceManager().getMemoryBufferSizes().malloc_bytes;
610 if (ExternalASTSource *Ext =
AST.getExternalSource())
611 Total += Ext->getMemoryBufferSizes().malloc_bytes;
614 Total += PP.getTotalMemory();
615 if (PreprocessingRecord *PRec = PP.getPreprocessingRecord())
616 Total += PRec->getTotalMemory();
617 Total += PP.getHeaderSearchInfo().getTotalMemory();
627 std::shared_ptr<const PreambleData>
Preamble,
628 std::unique_ptr<CompilerInstance> Clang,
629 std::unique_ptr<FrontendAction> Action,
631 std::vector<PragmaMark> Marks,
632 std::vector<Decl *> LocalTopLevelDecls,
634 include_cleaner::PragmaIncludes PI)
636 Clang(std::
move(Clang)), Action(std::
move(Action)),
637 Tokens(std::
move(Tokens)), Macros(std::
move(Macros)),
638 Marks(std::
move(Marks)), Diags(std::
move(Diags)),
639 LocalTopLevelDecls(std::
move(LocalTopLevelDecls)),
640 Includes(std::
move(Includes)), PI(std::
move(PI)),
641 Resolver(std::make_unique<HeuristicResolver>(getASTContext())) {
643 assert(this->Action);
653 return llvm::StringRef(Preamble->Version);
Include Cleaner is clangd functionality for providing diagnostics for misuse of transitive headers an...
static GeneratorRegistry::Add< MDGenerator > MD(MDGenerator::Format, "Generator for Markdown output.")
#define SPAN_ATTACH(S, Name, Expr)
Attach a key-value pair to a Span event.
void collect(const CompilerInstance &CI)
Stores and provides access to parsed AST.
std::size_t getUsedBytes() const
Returns the estimated size of the AST and the accessory structures, in bytes.
std::optional< llvm::StringRef > preambleVersion() const
Returns the version of the ParseInputs used to build Preamble part of this AST.
const include_cleaner::PragmaIncludes & getPragmaIncludes() const
Returns the PramaIncludes for preamble + main file includes.
const std::vector< PragmaMark > & getMarks() const
Gets all pragma marks in the main file.
ASTContext & getASTContext()
Note that the returned ast will not contain decls from the preamble that were not deserialized during...
static std::optional< ParsedAST > build(llvm::StringRef Filename, const ParseInputs &Inputs, std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble)
Attempts to run Clang and store the parsed AST.
llvm::ArrayRef< Diag > getDiagnostics() const
std::shared_ptr< Preprocessor > getPreprocessorPtr()
Preprocessor & getPreprocessor()
ArrayRef< Decl * > getLocalTopLevelDecls()
This function returns top-level decls present in the main file of the AST.
ParsedAST & operator=(ParsedAST &&Other)
const IncludeStructure & getIncludeStructure() const
ParsedAST(ParsedAST &&Other)
const MainFileMacros & getMacros() const
Gets all macro references (definition, expansions) present in the main file, including those in the p...
static PreamblePatch createFullPatch(llvm::StringRef FileName, const ParseInputs &Modified, const PreambleData &Baseline)
Builds a patch that contains new PP directives introduced to the preamble section of Modified compare...
StoreDiags collects the diagnostics that can later be reported by clangd.
void contributeFixes(DiagFixer Fixer)
If set, possibly adds fixes for diagnostics using Fixer.
void setDiagFinalizer(DiagFinalizer F)
Invokes a callback after notes and fixes have been attached.
void setLevelAdjuster(LevelAdjuster Adjuster)
If set, this allows the client of this class to adjust the level of diagnostics, such as promoting wa...
void setDiagCallback(DiagCallback CB)
Invokes a callback when a main diagnostic is first formed, before notes and fixes are attached.
void EndSourceFile() override
std::vector< Diag > take()
Wrapper for vfs::FileSystem for use in multithreaded programs like clangd.
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > view(std::nullopt_t CWD) const
Obtain a vfs::FileSystem with an arbitrary initial working directory.
Records an event whose duration is the lifetime of the Span object.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
@ Info
An information message.
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
static const char * toString(OffsetEncoding OE)
std::unique_ptr< PPCallbacks > collectPragmaMarksCallback(const SourceManager &SM, std::vector< PragmaMark > &Out)
Collect all pragma marks from the main file.
Symbol::IncludeDirective preferredIncludeDirective(llvm::StringRef FileName, const LangOptions &LangOpts, ArrayRef< Inclusion > MainFileIncludes, ArrayRef< const Decl * > TopLevelDecls)
Infer the include directive to use for the given FileName.
std::unique_ptr< CompilerInstance > prepareCompilerInstance(std::unique_ptr< clang::CompilerInvocation > CI, const PrecompiledPreamble *Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, DiagnosticConsumer &DiagsClient)
void log(const char *Fmt, Ts &&... Vals)
IncludeCleanerFindings computeIncludeCleanerFindings(ParsedAST &AST, bool AnalyzeAngledIncludes)
llvm::StringRef PathRef
A typedef to represent a ref to file path.
bool isImplicitTemplateInstantiation(const NamedDecl *D)
Indicates if D is a template instantiation implicitly generated by the compiler, e....
std::vector< Diag > issueIncludeCleanerDiagnostics(ParsedAST &AST, llvm::StringRef Code, const IncludeCleanerFindings &Findings, const ThreadsafeFS &TFS, HeaderFilter IgnoreHeaders, HeaderFilter AngledHeaders, HeaderFilter QuotedHeaders)
void elog(const char *Fmt, Ts &&... Vals)
format::FormatStyle getFormatStyleForFile(llvm::StringRef File, llvm::StringRef Content, const ThreadsafeFS &TFS, bool FormatFile)
Choose the clang-format style we should apply to a certain file.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Settings that express user/project preferences and control clangd behavior.
struct clang::clangd::Config::@314053012031341203055315320366267371313202370174 Style
Style of the codebase.
static const Config & current()
Returns the Config of the current Context, or an empty configuration.
std::vector< std::function< bool(llvm::StringRef)> > QuotedHeaders
std::vector< std::function< bool(llvm::StringRef)> > AngledHeaders
Contains basic information about a diagnostic.
A top-level diagnostic that may have Notes and Fixes.
@ Include
#include "header.h"