11 #include "../clang-tidy/ClangTidyDiagnosticConsumer.h" 12 #include "../clang-tidy/ClangTidyModuleRegistry.h" 18 #include "clang/AST/ASTContext.h" 19 #include "clang/Basic/LangOptions.h" 20 #include "clang/Frontend/CompilerInstance.h" 21 #include "clang/Frontend/CompilerInvocation.h" 22 #include "clang/Frontend/FrontendActions.h" 23 #include "clang/Frontend/Utils.h" 24 #include "clang/Index/IndexDataConsumer.h" 25 #include "clang/Index/IndexingAction.h" 26 #include "clang/Lex/Lexer.h" 27 #include "clang/Lex/MacroInfo.h" 28 #include "clang/Lex/Preprocessor.h" 29 #include "clang/Lex/PreprocessorOptions.h" 30 #include "clang/Sema/Sema.h" 31 #include "clang/Serialization/ASTWriter.h" 32 #include "clang/Tooling/CompilationDatabase.h" 33 #include "llvm/ADT/ArrayRef.h" 34 #include "llvm/ADT/SmallString.h" 35 #include "llvm/ADT/SmallVector.h" 36 #include "llvm/Support/raw_ostream.h" 43 bool compileCommandsAreEqual(
const tooling::CompileCommand &LHS,
44 const tooling::CompileCommand &RHS) {
46 return LHS.Directory == RHS.Directory && LHS.Filename == RHS.Filename &&
47 llvm::makeArrayRef(LHS.CommandLine).equals(RHS.CommandLine);
50 template <
class T> std::size_t getUsedBytes(
const std::vector<T> &Vec) {
51 return Vec.capacity() *
sizeof(T);
54 class DeclTrackingASTConsumer :
public ASTConsumer {
56 DeclTrackingASTConsumer(std::vector<Decl *> &TopLevelDecls)
57 : TopLevelDecls(TopLevelDecls) {}
59 bool HandleTopLevelDecl(DeclGroupRef DG)
override {
61 if (
D->isFromASTFile())
65 if (isa<ObjCMethodDecl>(
D))
68 TopLevelDecls.push_back(
D);
74 std::vector<Decl *> &TopLevelDecls;
79 std::vector<Decl *> takeTopLevelDecls() {
return std::move(TopLevelDecls); }
82 std::unique_ptr<ASTConsumer>
83 CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile)
override {
84 return llvm::make_unique<DeclTrackingASTConsumer>( TopLevelDecls);
88 std::vector<Decl *> TopLevelDecls;
91 class CppFilePreambleCallbacks :
public PreambleCallbacks {
94 : File(File), ParsedCallback(ParsedCallback) {}
96 IncludeStructure takeIncludes() {
return std::move(Includes); }
98 void AfterExecute(CompilerInstance &CI)
override {
101 trace::Span Tracer(
"Running PreambleCallback");
102 ParsedCallback(CI.getASTContext(), CI.getPreprocessorPtr());
105 void BeforeExecute(CompilerInstance &CI)
override {
106 SourceMgr = &CI.getSourceManager();
109 std::unique_ptr<PPCallbacks> createPPCallbacks()
override {
110 assert(SourceMgr &&
"SourceMgr must be set at this point");
117 IncludeStructure Includes;
118 SourceManager *SourceMgr =
nullptr;
132 static void attach(
const IncludeStructure &Includes,
133 CompilerInstance &Clang) {
134 auto &PP = Clang.getPreprocessor();
135 auto *ExistingCallbacks = PP.getPPCallbacks();
136 PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(
137 new ReplayPreamble(Includes, ExistingCallbacks,
138 Clang.getSourceManager(), PP, Clang.getLangOpts())));
141 assert(PP.getPPCallbacks() != ExistingCallbacks &&
142 "Expected chaining implementation");
146 ReplayPreamble(
const IncludeStructure &Includes,
PPCallbacks *Delegate,
147 const SourceManager &SM, Preprocessor &PP,
148 const LangOptions &LangOpts)
149 : Includes(Includes), Delegate(Delegate), SM(SM), PP(PP),
150 LangOpts(LangOpts) {}
167 void FileChanged(SourceLocation
Loc, FileChangeReason Reason,
168 SrcMgr::CharacteristicKind
Kind, FileID PrevFID)
override {
170 if (Reason == FileChangeReason::ExitFile &&
171 SM.getBuffer(PrevFID)->getBufferIdentifier() ==
"<built-in>")
176 for (
const auto &Inc : Includes.MainFileIncludes) {
177 const FileEntry *
File =
nullptr;
178 if (Inc.Resolved !=
"")
179 File = SM.getFileManager().getFile(Inc.Resolved);
181 llvm::StringRef WrittenFilename =
182 llvm::StringRef(Inc.Written).drop_front().drop_back();
183 bool Angled = llvm::StringRef(Inc.Written).startswith(
"<");
186 llvm::StringRef Src = SM.getBufferData(SM.getMainFileID());
187 Lexer RawLexer(SM.getLocForStartOfFile(SM.getMainFileID()), LangOpts,
188 Src.begin(), Src.begin() + Inc.HashOffset, Src.end());
189 Token HashTok, IncludeTok, FilenameTok;
190 RawLexer.LexFromRawLexer(HashTok);
191 assert(HashTok.getKind() == tok::hash);
192 RawLexer.setParsingPreprocessorDirective(
true);
193 RawLexer.LexFromRawLexer(IncludeTok);
194 IdentifierInfo *II = PP.getIdentifierInfo(IncludeTok.getRawIdentifier());
195 IncludeTok.setIdentifierInfo(II);
196 IncludeTok.setKind(II->getTokenID());
197 RawLexer.LexIncludeFilename(FilenameTok);
199 Delegate->InclusionDirective(
200 HashTok.getLocation(), IncludeTok, WrittenFilename, Angled,
201 CharSourceRange::getCharRange(FilenameTok.getLocation(),
202 FilenameTok.getEndLoc()),
203 File,
"SearchPath",
"RelPath",
nullptr, Inc.FileKind);
205 Delegate->FileSkipped(*File, FilenameTok, Inc.FileKind);
207 llvm::SmallString<1> UnusedRecovery;
208 Delegate->FileNotFound(WrittenFilename, UnusedRecovery);
213 const IncludeStructure &Includes;
215 const SourceManager &SM;
217 const LangOptions &LangOpts;
223 AST.
getASTContext().getTranslationUnitDecl()->dump(OS,
true);
226 llvm::Optional<ParsedAST>
228 std::shared_ptr<const PreambleData>
Preamble,
229 std::unique_ptr<llvm::MemoryBuffer> Buffer,
230 std::shared_ptr<PCHContainerOperations>
PCHs,
231 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
VFS) {
235 CI->getFrontendOpts().DisableFree =
false;
236 const PrecompiledPreamble *PreamblePCH =
237 Preamble ? &Preamble->Preamble :
nullptr;
242 std::move(PCHs), std::move(VFS), ASTDiags);
246 auto Action = llvm::make_unique<ClangdFrontendAction>();
247 const FrontendInputFile &MainInput = Clang->getFrontendOpts().Inputs[0];
248 if (!
Action->BeginSourceFile(*Clang, MainInput)) {
249 log(
"BeginSourceFile() failed when building AST for {0}",
250 MainInput.getFile());
260 std::vector<std::unique_ptr<tidy::ClangTidyCheck>> CTChecks;
261 ast_matchers::MatchFinder CTFinder;
262 llvm::Optional<tidy::ClangTidyContext> CTContext;
266 for (
const auto &E : tidy::ClangTidyModuleRegistry::entries())
267 E.instantiate()->addCheckFactories(CTFactories);
272 CTOpts.Checks =
"bugprone-sizeof-expression," 273 "bugprone-macro-repeated-side-effects," 274 "modernize-deprecated-headers";
275 CTContext.emplace(llvm::make_unique<tidy::DefaultOptionsProvider>(
277 CTContext->setDiagnosticsEngine(&Clang->getDiagnostics());
278 CTContext->setASTContext(&Clang->getASTContext());
279 CTContext->setCurrentFile(MainInput.getFile());
280 CTFactories.
createChecks(CTContext.getPointer(), CTChecks);
281 for (
const auto &Check : CTChecks) {
284 Check->registerPPCallbacks(*Clang);
285 Check->registerMatchers(&CTFinder);
294 ReplayPreamble::attach(Includes, *Clang);
298 Clang->getPreprocessor().addPPCallbacks(
302 log(
"Execute() failed when building AST for {0}", MainInput.getFile());
304 std::vector<Decl *> ParsedDecls =
Action->takeTopLevelDecls();
306 Clang->getASTContext().setTraversalScope(ParsedDecls);
311 CTFinder.matchAST(Clang->getASTContext());
322 Clang->getPreprocessor().EndSourceFile();
324 std::vector<Diag> Diags = ASTDiags.
take();
327 Diags.insert(Diags.begin(), Preamble->Diags.begin(), Preamble->Diags.end());
328 return ParsedAST(std::move(Preamble), std::move(Clang), std::move(
Action),
329 std::move(ParsedDecls), std::move(Diags),
330 std::move(Includes));
341 auto PP = Clang->getPreprocessorPtr();
342 Clang->setPreprocessor(
nullptr);
351 return Clang->getASTContext();
357 return Clang->getPreprocessorPtr();
361 return Clang->getPreprocessor();
365 return LocalTopLevelDecls;
371 auto &
AST = getASTContext();
375 clangd::getUsedBytes(LocalTopLevelDecls) + clangd::getUsedBytes(Diags);
381 Total +=
AST.getASTAllocatedMemory();
382 Total +=
AST.getSideTableAllocatedMemory();
383 Total +=
AST.Idents.getAllocator().getTotalMemory();
384 Total +=
AST.Selectors.getTotalMemory();
386 Total +=
AST.getSourceManager().getContentCacheSize();
387 Total +=
AST.getSourceManager().getDataStructureSizes();
388 Total +=
AST.getSourceManager().getMemoryBufferSizes().malloc_bytes;
390 if (ExternalASTSource *Ext =
AST.getExternalSource())
391 Total += Ext->getMemoryBufferSizes().malloc_bytes;
393 const Preprocessor &PP = getPreprocessor();
394 Total += PP.getTotalMemory();
395 if (PreprocessingRecord *PRec = PP.getPreprocessingRecord())
396 Total += PRec->getTotalMemory();
397 Total += PP.getHeaderSearchInfo().getTotalMemory();
408 std::unique_ptr<PreambleFileStatusCache> StatCache)
409 : Preamble(std::move(Preamble)), Diags(std::move(Diags)),
410 Includes(std::move(Includes)), StatCache(std::move(StatCache)) {}
413 std::unique_ptr<CompilerInstance> Clang,
414 std::unique_ptr<FrontendAction>
Action,
415 std::vector<Decl *> LocalTopLevelDecls,
417 :
Preamble(std::move(Preamble)), Clang(std::move(Clang)),
418 Action(std::move(Action)),
Diags(std::move(Diags)),
419 LocalTopLevelDecls(std::move(LocalTopLevelDecls)),
422 assert(this->Action);
425 std::unique_ptr<CompilerInvocation>
427 std::vector<const char *> ArgStrs;
429 ArgStrs.push_back(S.c_str());
431 if (Inputs.
FS->setCurrentWorkingDirectory(Inputs.
CompileCommand.Directory)) {
432 log(
"Couldn't set working directory when creating compiler invocation.");
440 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> CommandLineDiagsEngine =
441 CompilerInstance::createDiagnostics(
new DiagnosticOptions,
442 &IgnoreDiagnostics,
false);
443 std::unique_ptr<CompilerInvocation> CI = createInvocationFromCommandLine(
444 ArgStrs, CommandLineDiagsEngine, Inputs.
FS);
448 CI->getFrontendOpts().DisableFree =
false;
449 CI->getLangOpts()->CommentOpts.ParseAllComments =
true;
453 std::shared_ptr<const PreambleData>
455 std::shared_ptr<const PreambleData> OldPreamble,
456 const tooling::CompileCommand &OldCompileCommand,
458 std::shared_ptr<PCHContainerOperations>
PCHs,
bool StoreInMemory,
462 auto ContentsBuffer = llvm::MemoryBuffer::getMemBuffer(Inputs.
Contents);
464 ComputePreambleBounds(*CI.getLangOpts(), ContentsBuffer.get(), 0);
467 compileCommandsAreEqual(Inputs.
CompileCommand, OldCompileCommand) &&
468 OldPreamble->Preamble.CanReuse(CI, ContentsBuffer.get(), Bounds,
470 vlog(
"Reusing preamble for file {0}", llvm::Twine(FileName));
473 vlog(
"Preamble for file {0} cannot be reused. Attempting to rebuild it.",
479 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> PreambleDiagsEngine =
480 CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(),
481 &PreambleDiagnostics,
false);
485 assert(!CI.getFrontendOpts().SkipFunctionBodies);
486 CI.getFrontendOpts().SkipFunctionBodies =
true;
489 CI.getPreprocessorOpts().WriteCommentListToPCH =
false;
491 CppFilePreambleCallbacks SerializedDeclsCollector(FileName, PreambleCallback);
492 if (Inputs.
FS->setCurrentWorkingDirectory(Inputs.
CompileCommand.Directory)) {
493 log(
"Couldn't set working directory when building the preamble.");
498 llvm::SmallString<32> AbsFileName(FileName);
499 Inputs.
FS->makeAbsolute(AbsFileName);
500 auto StatCache = llvm::make_unique<PreambleFileStatusCache>(AbsFileName);
501 auto BuiltPreamble = PrecompiledPreamble::Build(
502 CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine,
504 SerializedDeclsCollector);
508 CI.getFrontendOpts().SkipFunctionBodies =
false;
511 vlog(
"Built preamble of size {0} for file {1}", BuiltPreamble->getSize(),
513 return std::make_shared<PreambleData>(
514 std::move(*BuiltPreamble), PreambleDiagnostics.
take(),
515 SerializedDeclsCollector.takeIncludes(), std::move(
StatCache));
517 elog(
"Could not build a preamble for file {0}", FileName);
522 llvm::Optional<ParsedAST>
525 std::shared_ptr<const PreambleData> Preamble,
526 std::shared_ptr<PCHContainerOperations>
PCHs) {
530 auto VFS = Inputs.
FS;
531 if (Preamble && Preamble->StatCache)
532 VFS = Preamble->StatCache->getConsumingFS(std::move(
VFS));
534 log(
"Couldn't set working directory when building the preamble.");
541 llvm::MemoryBuffer::getMemBufferCopy(Inputs.
Contents),
542 PCHs, std::move(
VFS));
548 const SourceManager &SourceMgr = AST.getSourceManager();
551 log(
"getBeginningOfIdentifier: {0}", Offset.takeError());
552 return SourceLocation();
564 SourceLocation InputLoc = SourceMgr.getComposedLoc(FID, *Offset);
566 return SourceMgr.getMacroArgExpandedLocation(InputLoc);
567 SourceLocation Before = SourceMgr.getComposedLoc(FID, *Offset - 1);
569 Before = Lexer::GetBeginningOfToken(Before, SourceMgr, AST.getLangOpts());
571 if (Before.isValid() &&
572 !Lexer::getRawToken(Before, Tok, SourceMgr, AST.getLangOpts(),
false) &&
573 Tok.is(tok::raw_identifier))
574 return SourceMgr.getMacroArgExpandedLocation(Before);
575 return SourceMgr.getMacroArgExpandedLocation(InputLoc);
581 #define LINK_TIDY_MODULE(X) \ 582 extern volatile int X##ModuleAnchorSource; \ 583 static int LLVM_ATTRIBUTE_UNUSED X##ModuleAnchorDestination = \ 584 X##ModuleAnchorSource 602 #undef LINK_TIDY_MODULE SourceLocation Loc
'#' location in the include directive
StoreDiags collects the diagnostics that can later be reported by clangd.
ParsedAST & operator=(ParsedAST &&Other)
std::vector< Diag > take()
Preprocessor & getPreprocessor()
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS
llvm::Optional< ParsedAST > buildAST(PathRef FileName, std::unique_ptr< CompilerInvocation > Invocation, const ParseInputs &Inputs, std::shared_ptr< const PreambleData > Preamble, std::shared_ptr< PCHContainerOperations > PCHs)
Build an AST from provided user inputs.
void dumpAST(ParsedAST &AST, llvm::raw_ostream &OS)
For testing/debugging purposes.
llvm::StringRef PathRef
A typedef to represent a ref to file path.
static llvm::Optional< ParsedAST > build(std::unique_ptr< clang::CompilerInvocation > CI, std::shared_ptr< const PreambleData > Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, std::shared_ptr< PCHContainerOperations > PCHs, IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS)
Attempts to run Clang and store parsed AST.
ArrayRef< Decl * > getLocalTopLevelDecls()
This function returns top-level decls present in the main file of the AST.
Documents should not be synced at all.
A collection of ClangTidyCheckFactory instances.
void vlog(const char *Fmt, Ts &&... Vals)
void elog(const char *Fmt, Ts &&... Vals)
std::function< void(ASTContext &, std::shared_ptr< clang::Preprocessor >)> PreambleParsedCallback
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 esitmated size of the AST and the accessory structures, in bytes.
const IncludeStructure & getIncludeStructure() const
std::unique_ptr< PreambleFileStatusCache > StatCache
#define LINK_TIDY_MODULE(X)
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
llvm::unique_function< void()> Action
void createChecks(ClangTidyContext *Context, std::vector< std::unique_ptr< ClangTidyCheck >> &Checks)
Create instances of all checks matching CheckRegexString and store them in Checks.
void log(const char *Fmt, Ts &&... Vals)
std::shared_ptr< Preprocessor > getPreprocessorPtr()
std::unique_ptr< CompilerInvocation > buildCompilerInvocation(const ParseInputs &Inputs)
Builds compiler invocation that could be used to build AST or preamble.
PreambleData(PrecompiledPreamble Preamble, std::vector< Diag > Diags, IncludeStructure Includes, std::unique_ptr< PreambleFileStatusCache > StatCache)
std::shared_ptr< const PreambleData > buildPreamble(PathRef FileName, CompilerInvocation &CI, std::shared_ptr< const PreambleData > OldPreamble, const tooling::CompileCommand &OldCompileCommand, const ParseInputs &Inputs, std::shared_ptr< PCHContainerOperations > PCHs, bool StoreInMemory, PreambleParsedCallback PreambleCallback)
Rebuild the preamble for the new inputs unless the old one can be reused.
Stores and provides access to parsed AST.
const PreambleData * Preamble
SourceLocation getBeginningOfIdentifier(ParsedAST &Unit, const Position &Pos, const FileID FID)
Get the beginning SourceLocation at a specified Pos.
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++ -*-===//
std::shared_ptr< PCHContainerOperations > PCHs
void EndSourceFile() override
std::vector< Diag > Diags
PrecompiledPreamble Preamble
static ClangTidyOptions getDefaults()
These options are used for all settings that haven't been overridden by the OptionsProvider.
IncludeStructure Includes
std::unique_ptr< CompilerInstance > prepareCompilerInstance(std::unique_ptr< clang::CompilerInvocation > CI, const PrecompiledPreamble *Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, std::shared_ptr< PCHContainerOperations > PCHs, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, DiagnosticConsumer &DiagsClient)
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)
const std::vector< Diag > & getDiagnostics() const