10 #include "../clang-tidy/ClangTidyDiagnosticConsumer.h" 11 #include "../clang-tidy/ClangTidyModuleRegistry.h" 22 #include "clang/AST/ASTContext.h" 23 #include "clang/AST/Decl.h" 24 #include "clang/Basic/LangOptions.h" 25 #include "clang/Basic/SourceLocation.h" 26 #include "clang/Basic/SourceManager.h" 27 #include "clang/Basic/TokenKinds.h" 28 #include "clang/Frontend/CompilerInstance.h" 29 #include "clang/Frontend/CompilerInvocation.h" 30 #include "clang/Frontend/FrontendActions.h" 31 #include "clang/Frontend/Utils.h" 32 #include "clang/Index/IndexDataConsumer.h" 33 #include "clang/Index/IndexingAction.h" 34 #include "clang/Lex/Lexer.h" 35 #include "clang/Lex/MacroInfo.h" 36 #include "clang/Lex/PPCallbacks.h" 37 #include "clang/Lex/Preprocessor.h" 38 #include "clang/Lex/PreprocessorOptions.h" 39 #include "clang/Sema/Sema.h" 40 #include "clang/Serialization/ASTWriter.h" 41 #include "clang/Serialization/PCHContainerOperations.h" 42 #include "clang/Tooling/CompilationDatabase.h" 43 #include "clang/Tooling/Syntax/Tokens.h" 44 #include "llvm/ADT/ArrayRef.h" 45 #include "llvm/ADT/STLExtras.h" 46 #include "llvm/ADT/SmallString.h" 47 #include "llvm/ADT/SmallVector.h" 48 #include "llvm/Support/raw_ostream.h" 54 #define CLANG_TIDY_DISABLE_STATIC_ANALYZER_CHECKS 55 #include "../clang-tidy/ClangTidyForceLinker.h" 61 template <
class T> std::size_t getUsedBytes(
const std::vector<T> &Vec) {
62 return Vec.capacity() *
sizeof(T);
65 class DeclTrackingASTConsumer :
public ASTConsumer {
67 DeclTrackingASTConsumer(std::vector<Decl *> &TopLevelDecls)
68 : TopLevelDecls(TopLevelDecls) {}
70 bool HandleTopLevelDecl(DeclGroupRef DG)
override {
72 auto &SM = D->getASTContext().getSourceManager();
75 if (
const NamedDecl *ND = dyn_cast<NamedDecl>(D))
80 if (isa<ObjCMethodDecl>(D))
83 TopLevelDecls.push_back(D);
89 std::vector<Decl *> &TopLevelDecls;
94 std::vector<Decl *> takeTopLevelDecls() {
return std::move(TopLevelDecls); }
97 std::unique_ptr<ASTConsumer>
98 CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile)
override {
99 return std::make_unique<DeclTrackingASTConsumer>( TopLevelDecls);
103 std::vector<Decl *> TopLevelDecls;
117 static void attach(
const IncludeStructure &Includes,
118 CompilerInstance &Clang) {
119 auto &PP = Clang.getPreprocessor();
120 auto *ExistingCallbacks = PP.getPPCallbacks();
122 if (!ExistingCallbacks)
124 PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(
125 new ReplayPreamble(Includes, ExistingCallbacks,
126 Clang.getSourceManager(), PP, Clang.getLangOpts())));
129 assert(PP.getPPCallbacks() != ExistingCallbacks &&
130 "Expected chaining implementation");
134 ReplayPreamble(
const IncludeStructure &Includes,
PPCallbacks *Delegate,
135 const SourceManager &SM, Preprocessor &PP,
136 const LangOptions &LangOpts)
137 : Includes(Includes), Delegate(Delegate), SM(SM), PP(PP),
138 LangOpts(LangOpts) {}
155 void FileChanged(SourceLocation
Loc, FileChangeReason Reason,
156 SrcMgr::CharacteristicKind
Kind, FileID PrevFID)
override {
158 if (Reason == FileChangeReason::ExitFile &&
159 SM.getBuffer(PrevFID)->getBufferIdentifier() ==
"<built-in>")
164 for (
const auto &Inc : Includes.MainFileIncludes) {
165 const FileEntry *
File =
nullptr;
166 if (Inc.Resolved !=
"")
167 if (
auto FE = SM.getFileManager().getFile(Inc.Resolved))
170 llvm::StringRef WrittenFilename =
171 llvm::StringRef(Inc.Written).drop_front().drop_back();
172 bool Angled = llvm::StringRef(Inc.Written).startswith(
"<");
175 llvm::StringRef
Src = SM.getBufferData(SM.getMainFileID());
176 Lexer RawLexer(SM.getLocForStartOfFile(SM.getMainFileID()), LangOpts,
177 Src.begin(), Src.begin() + Inc.HashOffset, Src.end());
178 Token HashTok, IncludeTok, FilenameTok;
179 RawLexer.LexFromRawLexer(HashTok);
180 assert(HashTok.getKind() == tok::hash);
181 RawLexer.setParsingPreprocessorDirective(
true);
182 RawLexer.LexFromRawLexer(IncludeTok);
183 IdentifierInfo *II = PP.getIdentifierInfo(IncludeTok.getRawIdentifier());
184 IncludeTok.setIdentifierInfo(II);
185 IncludeTok.setKind(II->getTokenID());
186 RawLexer.LexIncludeFilename(FilenameTok);
188 Delegate->InclusionDirective(
189 HashTok.getLocation(), IncludeTok, WrittenFilename, Angled,
190 CharSourceRange::getCharRange(FilenameTok.getLocation(),
191 FilenameTok.getEndLoc()),
192 File,
"SearchPath",
"RelPath",
nullptr, Inc.FileKind);
195 Delegate->FileSkipped(FileEntryRef(File->getName(), *
File), FilenameTok,
198 llvm::SmallString<1> UnusedRecovery;
199 Delegate->FileNotFound(WrittenFilename, UnusedRecovery);
204 const IncludeStructure &Includes;
206 const SourceManager &SM;
208 const LangOptions &LangOpts;
214 AST.
getASTContext().getTranslationUnitDecl()->dump(OS,
true);
217 llvm::Optional<ParsedAST>
219 llvm::ArrayRef<Diag> CompilerInvocationDiags,
220 std::shared_ptr<const PreambleData>
Preamble,
221 std::unique_ptr<llvm::MemoryBuffer> Buffer,
222 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
VFS,
227 CI->getFrontendOpts().DisableFree =
false;
228 const PrecompiledPreamble *PreamblePCH =
229 Preamble ? &Preamble->Preamble :
nullptr;
232 std::string Content = Buffer->getBuffer();
233 std::string
Filename = Buffer->getBufferIdentifier();
236 std::move(Buffer), VFS, ASTDiags);
240 auto Action = std::make_unique<ClangdFrontendAction>();
241 const FrontendInputFile &MainInput = Clang->getFrontendOpts().Inputs[0];
242 if (!
Action->BeginSourceFile(*Clang, MainInput)) {
243 log(
"BeginSourceFile() failed when building AST for {0}",
244 MainInput.getFile());
254 std::vector<std::unique_ptr<tidy::ClangTidyCheck>> CTChecks;
255 ast_matchers::MatchFinder CTFinder;
256 llvm::Optional<tidy::ClangTidyContext> CTContext;
259 dlog(
"ClangTidy configuration for file {0}: {1}", Filename,
262 for (
const auto &
E : tidy::ClangTidyModuleRegistry::entries())
263 E.instantiate()->addCheckFactories(CTFactories);
264 CTContext.emplace(std::make_unique<tidy::DefaultOptionsProvider>(
266 CTContext->setDiagnosticsEngine(&Clang->getDiagnostics());
267 CTContext->setASTContext(&Clang->getASTContext());
268 CTContext->setCurrentFile(Filename);
269 CTChecks = CTFactories.
createChecks(CTContext.getPointer());
271 const clang::Diagnostic &
Info) {
273 std::string CheckName = CTContext->getCheckName(Info.getID());
274 bool IsClangTidyDiag = !CheckName.empty();
275 if (IsClangTidyDiag) {
280 CTContext->treatAsError(CheckName)) {
289 bool IsInsideMainFile =
290 Info.hasSourceManager() &&
293 DiagLevel, Info, *CTContext,
295 return DiagnosticsEngine::Ignored;
301 Preprocessor *PP = &Clang->getPreprocessor();
302 for (
const auto &Check : CTChecks) {
305 Check->registerPPCallbacks(Clang->getSourceManager(), PP, PP);
306 Check->registerMatchers(&CTFinder);
312 llvm::Optional<IncludeFixer> FixIncludes;
313 auto BuildDir = VFS->getCurrentWorkingDirectory();
316 auto Inserter = std::make_shared<IncludeInserter>(
317 Filename, Content, Style, BuildDir.get(),
318 &Clang->getPreprocessor().getHeaderSearchInfo());
320 for (
const auto &Inc : Preamble->Includes.MainFileIncludes)
321 Inserter->addExisting(Inc);
323 FixIncludes.emplace(Filename, Inserter, *Index,
325 ASTDiags.
contributeFixes([&FixIncludes](DiagnosticsEngine::Level DiagLevl,
326 const clang::Diagnostic &
Info) {
327 return FixIncludes->fix(DiagLevl, Info);
329 Clang->setExternalSemaSource(FixIncludes->unresolvedNameRecorder());
337 ReplayPreamble::attach(Includes, *Clang);
341 Clang->getPreprocessor().addPPCallbacks(
347 Macros = Preamble->Macros;
348 Clang->getPreprocessor().addPPCallbacks(
349 std::make_unique<CollectMainFileMacros>(Clang->getSourceManager(),
350 Clang->getLangOpts(), Macros));
356 CanonIncludes = Preamble->CanonIncludes;
359 std::unique_ptr<CommentHandler> IWYUHandler =
361 Clang->getPreprocessor().addCommentHandler(IWYUHandler.get());
364 syntax::TokenCollector CollectTokens(Clang->getPreprocessor());
367 log(
"Execute() failed when building AST for {0}: {1}", MainInput.getFile(),
373 syntax::TokenBuffer Tokens = std::move(CollectTokens).consume();
374 std::vector<Decl *> ParsedDecls =
Action->takeTopLevelDecls();
376 Clang->getASTContext().setTraversalScope(ParsedDecls);
381 CTFinder.matchAST(Clang->getASTContext());
392 Clang->getPreprocessor().EndSourceFile();
394 std::vector<Diag> Diags = CompilerInvocationDiags;
397 Diags.insert(Diags.end(), Preamble->Diags.begin(), Preamble->Diags.end());
400 std::vector<Diag> D = ASTDiags.
take(CTContext.getPointer());
401 Diags.insert(Diags.end(), D.begin(), D.end());
403 return ParsedAST(std::move(Preamble), std::move(Clang), std::move(
Action),
404 std::move(Tokens), std::move(Macros), std::move(ParsedDecls),
405 std::move(Diags), std::move(Includes),
406 std::move(CanonIncludes));
417 auto PP = Clang->getPreprocessorPtr();
418 Clang->setPreprocessor(
nullptr);
427 return Clang->getASTContext();
433 return Clang->getPreprocessorPtr();
437 return Clang->getPreprocessor();
441 return LocalTopLevelDecls;
449 auto &
AST = getASTContext();
453 clangd::getUsedBytes(LocalTopLevelDecls) + clangd::getUsedBytes(Diags);
459 Total +=
AST.getASTAllocatedMemory();
460 Total +=
AST.getSideTableAllocatedMemory();
461 Total +=
AST.Idents.getAllocator().getTotalMemory();
462 Total +=
AST.Selectors.getTotalMemory();
464 Total +=
AST.getSourceManager().getContentCacheSize();
465 Total +=
AST.getSourceManager().getDataStructureSizes();
466 Total +=
AST.getSourceManager().getMemoryBufferSizes().malloc_bytes;
468 if (ExternalASTSource *Ext =
AST.getExternalSource())
469 Total += Ext->getMemoryBufferSizes().malloc_bytes;
471 const Preprocessor &PP = getPreprocessor();
472 Total += PP.getTotalMemory();
473 if (PreprocessingRecord *PRec = PP.getPreprocessingRecord())
474 Total += PRec->getTotalMemory();
475 Total += PP.getHeaderSearchInfo().getTotalMemory();
485 return CanonIncludes;
489 std::unique_ptr<CompilerInstance> Clang,
490 std::unique_ptr<FrontendAction>
Action,
492 std::vector<Decl *> LocalTopLevelDecls,
495 :
Preamble(std::move(Preamble)), Clang(std::move(Clang)),
496 Action(std::move(Action)), Tokens(std::move(Tokens)),
497 Macros(std::move(Macros)), Diags(std::move(Diags)),
498 LocalTopLevelDecls(std::move(LocalTopLevelDecls)),
499 Includes(std::move(Includes)), CanonIncludes(std::move(CanonIncludes)) {
501 assert(this->Action);
504 llvm::Optional<ParsedAST>
506 llvm::ArrayRef<Diag> CompilerInvocationDiags,
508 std::shared_ptr<const PreambleData> Preamble) {
512 auto VFS = Inputs.
FS;
513 if (Preamble && Preamble->StatCache)
514 VFS = Preamble->StatCache->getConsumingFS(std::move(
VFS));
516 log(
"Couldn't set working directory when building the preamble.");
522 std::make_unique<CompilerInvocation>(*Invocation),
523 CompilerInvocationDiags, Preamble,
524 llvm::MemoryBuffer::getMemBufferCopy(Inputs.
Contents, FileName),
std::unique_ptr< CommentHandler > collectIWYUHeaderMaps(CanonicalIncludes *Includes)
Returns a CommentHandler that parses pragma comment on include files to determine when we should incl...
SourceLocation Loc
'#' location in the include directive
const FunctionDecl * Decl
StoreDiags collects the diagnostics that can later be reported by clangd.
ParsedAST & operator=(ParsedAST &&Other)
llvm::Optional< ParsedAST > buildAST(PathRef FileName, std::unique_ptr< CompilerInvocation > Invocation, llvm::ArrayRef< Diag > CompilerInvocationDiags, const ParseInputs &Inputs, std::shared_ptr< const PreambleData > Preamble)
Build an AST from provided user inputs.
Preprocessor & getPreprocessor()
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS
void dumpAST(ParsedAST &AST, llvm::raw_ostream &OS)
For testing/debugging purposes.
void contributeFixes(DiagFixer Fixer)
If set, possibly adds fixes for diagnostics using Fixer.
Interface for symbol indexes that can be used for searching or matching symbols among a set of symbol...
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
bool SuggestMissingIncludes
llvm::StringRef PathRef
A typedef to represent a ref to file path.
ArrayRef< Decl * > getLocalTopLevelDecls()
This function returns top-level decls present in the main file of the AST.
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)
Documents should not be synced at all.
tidy::ClangTidyOptions ClangTidyOpts
A collection of ClangTidyCheckFactory instances.
ASTContext & getASTContext()
Note that the returned ast will not contain decls from the preamble that were not deserialized during...
std::size_t getUsedBytes() const
Returns the estimated size of the AST and the accessory structures, in bytes.
Maps a definition location onto an #include file, based on a set of filename rules.
void addSystemHeadersMapping(const LangOptions &Language)
Adds mapping for system headers and some special symbols (e.g.
const CanonicalIncludes & getCanonicalIncludes() const
const IncludeStructure & getIncludeStructure() const
std::string Filename
Filename as a string.
llvm::unique_function< void()> Action
std::string configurationAsText(const ClangTidyOptions &Options)
Serializes configuration to a YAML-encoded string.
void log(const char *Fmt, Ts &&... Vals)
static const char * toString(OffsetEncoding OE)
std::shared_ptr< Preprocessor > getPreprocessorPtr()
bool isImplicitTemplateInstantiation(const NamedDecl *D)
Indicates if D is a template instantiation implicitly generated by the compiler, e.g.
format::FormatStyle getFormatStyleForFile(llvm::StringRef File, llvm::StringRef Content, llvm::vfs::FileSystem *FS)
Choose the clang-format style we should apply to a certain file.
Stores and provides access to parsed AST.
const MainFileMacros & getMacros() const
Gets all macro references (definition, expansions) present in the main file, including those in the p...
const PreambleData * Preamble
std::unique_ptr< PPCallbacks > collectIncludeStructureCallback(const SourceManager &SM, IncludeStructure *Out)
Returns a PPCallback that visits all inclusions in the main file.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
bool shouldSuppressDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info, ClangTidyContext &Context, bool CheckMacroExpansion)
Check whether a given diagnostic should be suppressed due to the presence of a "NOLINT" suppression c...
void EndSourceFile() override
static llvm::Optional< ParsedAST > build(std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, const SymbolIndex *Index, const ParseOptions &Opts)
Attempts to run Clang and store parsed AST.
void setLevelAdjuster(LevelAdjuster Adjuster)
If set, this allows the client of this class to adjust the level of diagnostics, such as promoting wa...
Records an event whose duration is the lifetime of the Span object.
#define SPAN_ATTACH(S, Name, Expr)
Attach a key-value pair to a Span event.
ParsedAST(ParsedAST &&Other)
std::vector< Diag > take(const clang::tidy::ClangTidyContext *Tidy=nullptr)
const std::vector< Diag > & getDiagnostics() const
std::vector< std::unique_ptr< ClangTidyCheck > > createChecks(ClangTidyContext *Context)
Create instances of checks that are enabled.
const SymbolIndex * Index