xref: /llvm-project/clang-tools-extra/clang-tidy/ClangTidy.cpp (revision e5de2a2df4f983e1db83deaaa1d21457c922404f)
1 //===--- tools/extra/clang-tidy/ClangTidy.cpp - Clang tidy tool -----------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 ///  \file This file implements a clang-tidy tool.
10 ///
11 ///  This tool uses the Clang Tooling infrastructure, see
12 ///    http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
13 ///  for details on setting it up with LLVM source tree.
14 ///
15 //===----------------------------------------------------------------------===//
16 
17 #include "ClangTidy.h"
18 #include "ClangTidyCheck.h"
19 #include "ClangTidyDiagnosticConsumer.h"
20 #include "ClangTidyModuleRegistry.h"
21 #include "ClangTidyProfiling.h"
22 #include "ExpandModularHeadersPPCallbacks.h"
23 #include "clang-tidy-config.h"
24 #include "clang/AST/ASTConsumer.h"
25 #include "clang/ASTMatchers/ASTMatchFinder.h"
26 #include "clang/Format/Format.h"
27 #include "clang/Frontend/ASTConsumers.h"
28 #include "clang/Frontend/CompilerInstance.h"
29 #include "clang/Frontend/FrontendDiagnostic.h"
30 #include "clang/Frontend/MultiplexConsumer.h"
31 #include "clang/Frontend/TextDiagnosticPrinter.h"
32 #include "clang/Lex/PPCallbacks.h"
33 #include "clang/Lex/Preprocessor.h"
34 #include "clang/Lex/PreprocessorOptions.h"
35 #include "clang/Rewrite/Frontend/FixItRewriter.h"
36 #include "clang/Rewrite/Frontend/FrontendActions.h"
37 #include "clang/Tooling/Core/Diagnostic.h"
38 #include "clang/Tooling/DiagnosticsYaml.h"
39 #include "clang/Tooling/Refactoring.h"
40 #include "clang/Tooling/ReplacementsYaml.h"
41 #include "clang/Tooling/Tooling.h"
42 #include "llvm/Support/Process.h"
43 #include <algorithm>
44 #include <utility>
45 
46 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
47 #include "clang/Analysis/PathDiagnostic.h"
48 #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
49 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
50 
51 using namespace clang::ast_matchers;
52 using namespace clang::driver;
53 using namespace clang::tooling;
54 using namespace llvm;
55 
56 LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry)
57 
58 namespace clang::tidy {
59 
60 namespace {
61 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
62 static const char *AnalyzerCheckNamePrefix = "clang-analyzer-";
63 
64 class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer {
65 public:
66   AnalyzerDiagnosticConsumer(ClangTidyContext &Context) : Context(Context) {}
67 
68   void FlushDiagnosticsImpl(std::vector<const ento::PathDiagnostic *> &Diags,
69                             FilesMade *FilesMade) override {
70     for (const ento::PathDiagnostic *PD : Diags) {
71       SmallString<64> CheckName(AnalyzerCheckNamePrefix);
72       CheckName += PD->getCheckerName();
73       Context.diag(CheckName, PD->getLocation().asLocation(),
74                    PD->getShortDescription())
75           << PD->path.back()->getRanges();
76 
77       for (const auto &DiagPiece :
78            PD->path.flatten(/*ShouldFlattenMacros=*/true)) {
79         Context.diag(CheckName, DiagPiece->getLocation().asLocation(),
80                      DiagPiece->getString(), DiagnosticIDs::Note)
81             << DiagPiece->getRanges();
82       }
83     }
84   }
85 
86   StringRef getName() const override { return "ClangTidyDiags"; }
87   bool supportsLogicalOpControlFlow() const override { return true; }
88   bool supportsCrossFileDiagnostics() const override { return true; }
89 
90 private:
91   ClangTidyContext &Context;
92 };
93 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
94 
95 class ErrorReporter {
96 public:
97   ErrorReporter(ClangTidyContext &Context, FixBehaviour ApplyFixes,
98                 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS)
99       : Files(FileSystemOptions(), std::move(BaseFS)),
100         DiagOpts(new DiagnosticOptions()),
101         DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)),
102         Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
103               DiagPrinter),
104         SourceMgr(Diags, Files), Context(Context), ApplyFixes(ApplyFixes) {
105     DiagOpts->ShowColors = Context.getOptions().UseColor.value_or(
106         llvm::sys::Process::StandardOutHasColors());
107     DiagPrinter->BeginSourceFile(LangOpts);
108     if (DiagOpts->ShowColors && !llvm::sys::Process::StandardOutIsDisplayed()) {
109       llvm::sys::Process::UseANSIEscapeCodes(true);
110     }
111   }
112 
113   SourceManager &getSourceManager() { return SourceMgr; }
114 
115   void reportDiagnostic(const ClangTidyError &Error) {
116     const tooling::DiagnosticMessage &Message = Error.Message;
117     SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
118     // Contains a pair for each attempted fix: location and whether the fix was
119     // applied successfully.
120     SmallVector<std::pair<SourceLocation, bool>, 4> FixLocations;
121     {
122       auto Level = static_cast<DiagnosticsEngine::Level>(Error.DiagLevel);
123       std::string Name = Error.DiagnosticName;
124       if (!Error.EnabledDiagnosticAliases.empty())
125         Name += "," + llvm::join(Error.EnabledDiagnosticAliases, ",");
126       if (Error.IsWarningAsError) {
127         Name += ",-warnings-as-errors";
128         Level = DiagnosticsEngine::Error;
129         WarningsAsErrors++;
130       }
131       auto Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0 [%1]"))
132                   << Message.Message << Name;
133       for (const FileByteRange &FBR : Error.Message.Ranges)
134         Diag << getRange(FBR);
135       // FIXME: explore options to support interactive fix selection.
136       const llvm::StringMap<Replacements> *ChosenFix = nullptr;
137       if (ApplyFixes != FB_NoFix &&
138           (ChosenFix = getFixIt(Error, ApplyFixes == FB_FixNotes))) {
139         for (const auto &FileAndReplacements : *ChosenFix) {
140           for (const auto &Repl : FileAndReplacements.second) {
141             ++TotalFixes;
142             bool CanBeApplied = false;
143             if (!Repl.isApplicable())
144               continue;
145             SourceLocation FixLoc;
146             SmallString<128> FixAbsoluteFilePath = Repl.getFilePath();
147             Files.makeAbsolutePath(FixAbsoluteFilePath);
148             tooling::Replacement R(FixAbsoluteFilePath, Repl.getOffset(),
149                                    Repl.getLength(), Repl.getReplacementText());
150             auto &Entry = FileReplacements[R.getFilePath()];
151             Replacements &Replacements = Entry.Replaces;
152             llvm::Error Err = Replacements.add(R);
153             if (Err) {
154               // FIXME: Implement better conflict handling.
155               llvm::errs() << "Trying to resolve conflict: "
156                            << llvm::toString(std::move(Err)) << "\n";
157               unsigned NewOffset =
158                   Replacements.getShiftedCodePosition(R.getOffset());
159               unsigned NewLength = Replacements.getShiftedCodePosition(
160                                        R.getOffset() + R.getLength()) -
161                                    NewOffset;
162               if (NewLength == R.getLength()) {
163                 R = Replacement(R.getFilePath(), NewOffset, NewLength,
164                                 R.getReplacementText());
165                 Replacements = Replacements.merge(tooling::Replacements(R));
166                 CanBeApplied = true;
167                 ++AppliedFixes;
168               } else {
169                 llvm::errs()
170                     << "Can't resolve conflict, skipping the replacement.\n";
171               }
172             } else {
173               CanBeApplied = true;
174               ++AppliedFixes;
175             }
176             FixLoc = getLocation(FixAbsoluteFilePath, Repl.getOffset());
177             FixLocations.push_back(std::make_pair(FixLoc, CanBeApplied));
178             Entry.BuildDir = Error.BuildDirectory;
179           }
180         }
181       }
182       reportFix(Diag, Error.Message.Fix);
183     }
184     for (auto Fix : FixLocations) {
185       Diags.Report(Fix.first, Fix.second ? diag::note_fixit_applied
186                                          : diag::note_fixit_failed);
187     }
188     for (const auto &Note : Error.Notes)
189       reportNote(Note);
190   }
191 
192   void finish() {
193     if (TotalFixes > 0) {
194       auto &VFS = Files.getVirtualFileSystem();
195       auto OriginalCWD = VFS.getCurrentWorkingDirectory();
196       bool AnyNotWritten = false;
197 
198       for (const auto &FileAndReplacements : FileReplacements) {
199         Rewriter Rewrite(SourceMgr, LangOpts);
200         StringRef File = FileAndReplacements.first();
201         VFS.setCurrentWorkingDirectory(FileAndReplacements.second.BuildDir);
202         llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
203             SourceMgr.getFileManager().getBufferForFile(File);
204         if (!Buffer) {
205           llvm::errs() << "Can't get buffer for file " << File << ": "
206                        << Buffer.getError().message() << "\n";
207           // FIXME: Maybe don't apply fixes for other files as well.
208           continue;
209         }
210         StringRef Code = Buffer.get()->getBuffer();
211         auto Style = format::getStyle(
212             *Context.getOptionsForFile(File).FormatStyle, File, "none");
213         if (!Style) {
214           llvm::errs() << llvm::toString(Style.takeError()) << "\n";
215           continue;
216         }
217         llvm::Expected<tooling::Replacements> Replacements =
218             format::cleanupAroundReplacements(
219                 Code, FileAndReplacements.second.Replaces, *Style);
220         if (!Replacements) {
221           llvm::errs() << llvm::toString(Replacements.takeError()) << "\n";
222           continue;
223         }
224         if (llvm::Expected<tooling::Replacements> FormattedReplacements =
225                 format::formatReplacements(Code, *Replacements, *Style)) {
226           Replacements = std::move(FormattedReplacements);
227           if (!Replacements)
228             llvm_unreachable("!Replacements");
229         } else {
230           llvm::errs() << llvm::toString(FormattedReplacements.takeError())
231                        << ". Skipping formatting.\n";
232         }
233         if (!tooling::applyAllReplacements(Replacements.get(), Rewrite)) {
234           llvm::errs() << "Can't apply replacements for file " << File << "\n";
235         }
236         AnyNotWritten |= Rewrite.overwriteChangedFiles();
237       }
238 
239       if (AnyNotWritten) {
240         llvm::errs() << "clang-tidy failed to apply suggested fixes.\n";
241       } else {
242         llvm::errs() << "clang-tidy applied " << AppliedFixes << " of "
243                      << TotalFixes << " suggested fixes.\n";
244       }
245 
246       if (OriginalCWD)
247         VFS.setCurrentWorkingDirectory(*OriginalCWD);
248     }
249   }
250 
251   unsigned getWarningsAsErrorsCount() const { return WarningsAsErrors; }
252 
253 private:
254   SourceLocation getLocation(StringRef FilePath, unsigned Offset) {
255     if (FilePath.empty())
256       return {};
257 
258     auto File = SourceMgr.getFileManager().getOptionalFileRef(FilePath);
259     if (!File)
260       return {};
261 
262     FileID ID = SourceMgr.getOrCreateFileID(*File, SrcMgr::C_User);
263     return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
264   }
265 
266   void reportFix(const DiagnosticBuilder &Diag,
267                  const llvm::StringMap<Replacements> &Fix) {
268     for (const auto &FileAndReplacements : Fix) {
269       for (const auto &Repl : FileAndReplacements.second) {
270         if (!Repl.isApplicable())
271           continue;
272         FileByteRange FBR;
273         FBR.FilePath = Repl.getFilePath().str();
274         FBR.FileOffset = Repl.getOffset();
275         FBR.Length = Repl.getLength();
276 
277         Diag << FixItHint::CreateReplacement(getRange(FBR),
278                                              Repl.getReplacementText());
279       }
280     }
281   }
282 
283   void reportNote(const tooling::DiagnosticMessage &Message) {
284     SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
285     auto Diag =
286         Diags.Report(Loc, Diags.getCustomDiagID(DiagnosticsEngine::Note, "%0"))
287         << Message.Message;
288     for (const FileByteRange &FBR : Message.Ranges)
289       Diag << getRange(FBR);
290     reportFix(Diag, Message.Fix);
291   }
292 
293   CharSourceRange getRange(const FileByteRange &Range) {
294     SmallString<128> AbsoluteFilePath{Range.FilePath};
295     Files.makeAbsolutePath(AbsoluteFilePath);
296     SourceLocation BeginLoc = getLocation(AbsoluteFilePath, Range.FileOffset);
297     SourceLocation EndLoc = BeginLoc.getLocWithOffset(Range.Length);
298     // Retrieve the source range for applicable highlights and fixes. Macro
299     // definition on the command line have locations in a virtual buffer and
300     // don't have valid file paths and are therefore not applicable.
301     return CharSourceRange::getCharRange(BeginLoc, EndLoc);
302   }
303 
304   struct ReplacementsWithBuildDir {
305     StringRef BuildDir;
306     Replacements Replaces;
307   };
308 
309   FileManager Files;
310   LangOptions LangOpts; // FIXME: use langopts from each original file
311   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
312   DiagnosticConsumer *DiagPrinter;
313   DiagnosticsEngine Diags;
314   SourceManager SourceMgr;
315   llvm::StringMap<ReplacementsWithBuildDir> FileReplacements;
316   ClangTidyContext &Context;
317   FixBehaviour ApplyFixes;
318   unsigned TotalFixes = 0U;
319   unsigned AppliedFixes = 0U;
320   unsigned WarningsAsErrors = 0U;
321 };
322 
323 class ClangTidyASTConsumer : public MultiplexConsumer {
324 public:
325   ClangTidyASTConsumer(std::vector<std::unique_ptr<ASTConsumer>> Consumers,
326                        std::unique_ptr<ClangTidyProfiling> Profiling,
327                        std::unique_ptr<ast_matchers::MatchFinder> Finder,
328                        std::vector<std::unique_ptr<ClangTidyCheck>> Checks)
329       : MultiplexConsumer(std::move(Consumers)),
330         Profiling(std::move(Profiling)), Finder(std::move(Finder)),
331         Checks(std::move(Checks)) {}
332 
333 private:
334   // Destructor order matters! Profiling must be destructed last.
335   // Or at least after Finder.
336   std::unique_ptr<ClangTidyProfiling> Profiling;
337   std::unique_ptr<ast_matchers::MatchFinder> Finder;
338   std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
339   void anchor() override {};
340 };
341 
342 } // namespace
343 
344 ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
345     ClangTidyContext &Context,
346     IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS)
347     : Context(Context), OverlayFS(std::move(OverlayFS)),
348       CheckFactories(new ClangTidyCheckFactories) {
349   for (ClangTidyModuleRegistry::entry E : ClangTidyModuleRegistry::entries()) {
350     std::unique_ptr<ClangTidyModule> Module = E.instantiate();
351     Module->addCheckFactories(*CheckFactories);
352   }
353 }
354 
355 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
356 static void
357 setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts,
358                              clang::AnalyzerOptions &AnalyzerOptions) {
359   StringRef AnalyzerPrefix(AnalyzerCheckNamePrefix);
360   for (const auto &Opt : Opts.CheckOptions) {
361     StringRef OptName(Opt.getKey());
362     if (!OptName.consume_front(AnalyzerPrefix))
363       continue;
364     // Analyzer options are always local options so we can ignore priority.
365     AnalyzerOptions.Config[OptName] = Opt.getValue().Value;
366   }
367 }
368 
369 using CheckersList = std::vector<std::pair<std::string, bool>>;
370 
371 static CheckersList getAnalyzerCheckersAndPackages(ClangTidyContext &Context,
372                                                    bool IncludeExperimental) {
373   CheckersList List;
374 
375   const auto &RegisteredCheckers =
376       AnalyzerOptions::getRegisteredCheckers(IncludeExperimental);
377   const bool AnalyzerChecksEnabled =
378       llvm::any_of(RegisteredCheckers, [&](StringRef CheckName) -> bool {
379         return Context.isCheckEnabled(
380             (AnalyzerCheckNamePrefix + CheckName).str());
381       });
382 
383   if (!AnalyzerChecksEnabled)
384     return List;
385 
386   // List all static analyzer checkers that our filter enables.
387   //
388   // Always add all core checkers if any other static analyzer check is enabled.
389   // This is currently necessary, as other path sensitive checks rely on the
390   // core checkers.
391   for (StringRef CheckName : RegisteredCheckers) {
392     std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
393 
394     if (CheckName.starts_with("core") ||
395         Context.isCheckEnabled(ClangTidyCheckName)) {
396       List.emplace_back(std::string(CheckName), true);
397     }
398   }
399   return List;
400 }
401 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
402 
403 std::unique_ptr<clang::ASTConsumer>
404 ClangTidyASTConsumerFactory::createASTConsumer(
405     clang::CompilerInstance &Compiler, StringRef File) {
406   // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
407   // modify Compiler.
408   SourceManager *SM = &Compiler.getSourceManager();
409   Context.setSourceManager(SM);
410   Context.setCurrentFile(File);
411   Context.setASTContext(&Compiler.getASTContext());
412 
413   auto WorkingDir = Compiler.getSourceManager()
414                         .getFileManager()
415                         .getVirtualFileSystem()
416                         .getCurrentWorkingDirectory();
417   if (WorkingDir)
418     Context.setCurrentBuildDirectory(WorkingDir.get());
419 
420   std::vector<std::unique_ptr<ClangTidyCheck>> Checks =
421       CheckFactories->createChecksForLanguage(&Context);
422 
423   ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;
424 
425   std::unique_ptr<ClangTidyProfiling> Profiling;
426   if (Context.getEnableProfiling()) {
427     Profiling = std::make_unique<ClangTidyProfiling>(
428         Context.getProfileStorageParams());
429     FinderOptions.CheckProfiling.emplace(Profiling->Records);
430   }
431 
432   std::unique_ptr<ast_matchers::MatchFinder> Finder(
433       new ast_matchers::MatchFinder(std::move(FinderOptions)));
434 
435   Preprocessor *PP = &Compiler.getPreprocessor();
436   Preprocessor *ModuleExpanderPP = PP;
437 
438   if (Context.canEnableModuleHeadersParsing() &&
439       Context.getLangOpts().Modules && OverlayFS != nullptr) {
440     auto ModuleExpander = std::make_unique<ExpandModularHeadersPPCallbacks>(
441         &Compiler, OverlayFS);
442     ModuleExpanderPP = ModuleExpander->getPreprocessor();
443     PP->addPPCallbacks(std::move(ModuleExpander));
444   }
445 
446   for (auto &Check : Checks) {
447     Check->registerMatchers(&*Finder);
448     Check->registerPPCallbacks(*SM, PP, ModuleExpanderPP);
449   }
450 
451   std::vector<std::unique_ptr<ASTConsumer>> Consumers;
452   if (!Checks.empty())
453     Consumers.push_back(Finder->newASTConsumer());
454 
455 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
456   AnalyzerOptions &AnalyzerOptions = Compiler.getAnalyzerOpts();
457   AnalyzerOptions.CheckersAndPackages = getAnalyzerCheckersAndPackages(
458       Context, Context.canEnableAnalyzerAlphaCheckers());
459   if (!AnalyzerOptions.CheckersAndPackages.empty()) {
460     setStaticAnalyzerCheckerOpts(Context.getOptions(), AnalyzerOptions);
461     AnalyzerOptions.AnalysisDiagOpt = PD_NONE;
462     std::unique_ptr<ento::AnalysisASTConsumer> AnalysisConsumer =
463         ento::CreateAnalysisConsumer(Compiler);
464     AnalysisConsumer->AddDiagnosticConsumer(
465         new AnalyzerDiagnosticConsumer(Context));
466     Consumers.push_back(std::move(AnalysisConsumer));
467   }
468 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
469   return std::make_unique<ClangTidyASTConsumer>(
470       std::move(Consumers), std::move(Profiling), std::move(Finder),
471       std::move(Checks));
472 }
473 
474 std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
475   std::vector<std::string> CheckNames;
476   for (const auto &CheckFactory : *CheckFactories) {
477     if (Context.isCheckEnabled(CheckFactory.getKey()))
478       CheckNames.emplace_back(CheckFactory.getKey());
479   }
480 
481 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
482   for (const auto &AnalyzerCheck : getAnalyzerCheckersAndPackages(
483            Context, Context.canEnableAnalyzerAlphaCheckers()))
484     CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first);
485 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
486 
487   llvm::sort(CheckNames);
488   return CheckNames;
489 }
490 
491 ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() {
492   ClangTidyOptions::OptionMap Options;
493   std::vector<std::unique_ptr<ClangTidyCheck>> Checks =
494       CheckFactories->createChecks(&Context);
495   for (const auto &Check : Checks)
496     Check->storeOptions(Options);
497   return Options;
498 }
499 
500 std::vector<std::string>
501 getCheckNames(const ClangTidyOptions &Options,
502               bool AllowEnablingAnalyzerAlphaCheckers) {
503   clang::tidy::ClangTidyContext Context(
504       std::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
505                                                 Options),
506       AllowEnablingAnalyzerAlphaCheckers);
507   ClangTidyASTConsumerFactory Factory(Context);
508   return Factory.getCheckNames();
509 }
510 
511 ClangTidyOptions::OptionMap
512 getCheckOptions(const ClangTidyOptions &Options,
513                 bool AllowEnablingAnalyzerAlphaCheckers) {
514   clang::tidy::ClangTidyContext Context(
515       std::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
516                                                 Options),
517       AllowEnablingAnalyzerAlphaCheckers);
518   ClangTidyDiagnosticConsumer DiagConsumer(Context);
519   DiagnosticsEngine DE(llvm::makeIntrusiveRefCnt<DiagnosticIDs>(),
520                        llvm::makeIntrusiveRefCnt<DiagnosticOptions>(),
521                        &DiagConsumer, /*ShouldOwnClient=*/false);
522   Context.setDiagnosticsEngine(&DE);
523   ClangTidyASTConsumerFactory Factory(Context);
524   return Factory.getCheckOptions();
525 }
526 
527 std::vector<ClangTidyError>
528 runClangTidy(clang::tidy::ClangTidyContext &Context,
529              const CompilationDatabase &Compilations,
530              ArrayRef<std::string> InputFiles,
531              llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS,
532              bool ApplyAnyFix, bool EnableCheckProfile,
533              llvm::StringRef StoreCheckProfile) {
534   ClangTool Tool(Compilations, InputFiles,
535                  std::make_shared<PCHContainerOperations>(), BaseFS);
536 
537   // Add extra arguments passed by the clang-tidy command-line.
538   ArgumentsAdjuster PerFileExtraArgumentsInserter =
539       [&Context](const CommandLineArguments &Args, StringRef Filename) {
540         ClangTidyOptions Opts = Context.getOptionsForFile(Filename);
541         CommandLineArguments AdjustedArgs = Args;
542         if (Opts.ExtraArgsBefore) {
543           auto I = AdjustedArgs.begin();
544           if (I != AdjustedArgs.end() && !StringRef(*I).starts_with("-"))
545             ++I; // Skip compiler binary name, if it is there.
546           AdjustedArgs.insert(I, Opts.ExtraArgsBefore->begin(),
547                               Opts.ExtraArgsBefore->end());
548         }
549         if (Opts.ExtraArgs)
550           AdjustedArgs.insert(AdjustedArgs.end(), Opts.ExtraArgs->begin(),
551                               Opts.ExtraArgs->end());
552         return AdjustedArgs;
553       };
554 
555   Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter);
556   Tool.appendArgumentsAdjuster(getStripPluginsAdjuster());
557   Context.setEnableProfiling(EnableCheckProfile);
558   Context.setProfileStoragePrefix(StoreCheckProfile);
559 
560   ClangTidyDiagnosticConsumer DiagConsumer(Context, nullptr, true, ApplyAnyFix);
561   DiagnosticsEngine DE(new DiagnosticIDs(), new DiagnosticOptions(),
562                        &DiagConsumer, /*ShouldOwnClient=*/false);
563   Context.setDiagnosticsEngine(&DE);
564   Tool.setDiagnosticConsumer(&DiagConsumer);
565 
566   class ActionFactory : public FrontendActionFactory {
567   public:
568     ActionFactory(ClangTidyContext &Context,
569                   IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS)
570         : ConsumerFactory(Context, std::move(BaseFS)) {}
571     std::unique_ptr<FrontendAction> create() override {
572       return std::make_unique<Action>(&ConsumerFactory);
573     }
574 
575     bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
576                        FileManager *Files,
577                        std::shared_ptr<PCHContainerOperations> PCHContainerOps,
578                        DiagnosticConsumer *DiagConsumer) override {
579       // Explicitly ask to define __clang_analyzer__ macro.
580       Invocation->getPreprocessorOpts().SetUpStaticAnalyzer = true;
581       return FrontendActionFactory::runInvocation(
582           Invocation, Files, PCHContainerOps, DiagConsumer);
583     }
584 
585   private:
586     class Action : public ASTFrontendAction {
587     public:
588       Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {}
589       std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
590                                                      StringRef File) override {
591         return Factory->createASTConsumer(Compiler, File);
592       }
593 
594     private:
595       ClangTidyASTConsumerFactory *Factory;
596     };
597 
598     ClangTidyASTConsumerFactory ConsumerFactory;
599   };
600 
601   ActionFactory Factory(Context, std::move(BaseFS));
602   Tool.run(&Factory);
603   return DiagConsumer.take();
604 }
605 
606 void handleErrors(llvm::ArrayRef<ClangTidyError> Errors,
607                   ClangTidyContext &Context, FixBehaviour Fix,
608                   unsigned &WarningsAsErrorsCount,
609                   llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS) {
610   ErrorReporter Reporter(Context, Fix, std::move(BaseFS));
611   llvm::vfs::FileSystem &FileSystem =
612       Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
613   auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();
614   if (!InitialWorkingDir)
615     llvm::report_fatal_error("Cannot get current working path.");
616 
617   for (const ClangTidyError &Error : Errors) {
618     if (!Error.BuildDirectory.empty()) {
619       // By default, the working directory of file system is the current
620       // clang-tidy running directory.
621       //
622       // Change the directory to the one used during the analysis.
623       FileSystem.setCurrentWorkingDirectory(Error.BuildDirectory);
624     }
625     Reporter.reportDiagnostic(Error);
626     // Return to the initial directory to correctly resolve next Error.
627     FileSystem.setCurrentWorkingDirectory(InitialWorkingDir.get());
628   }
629   Reporter.finish();
630   WarningsAsErrorsCount += Reporter.getWarningsAsErrorsCount();
631 }
632 
633 void exportReplacements(const llvm::StringRef MainFilePath,
634                         const std::vector<ClangTidyError> &Errors,
635                         raw_ostream &OS) {
636   TranslationUnitDiagnostics TUD;
637   TUD.MainSourceFile = std::string(MainFilePath);
638   for (const auto &Error : Errors) {
639     tooling::Diagnostic Diag = Error;
640     if (Error.IsWarningAsError)
641       Diag.DiagLevel = tooling::Diagnostic::Error;
642     TUD.Diagnostics.insert(TUD.Diagnostics.end(), Diag);
643   }
644 
645   yaml::Output YAML(OS);
646   YAML << TUD;
647 }
648 
649 ChecksAndOptions
650 getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers) {
651   ChecksAndOptions Result;
652   ClangTidyOptions Opts;
653   Opts.Checks = "*";
654   clang::tidy::ClangTidyContext Context(
655       std::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(), Opts),
656       AllowEnablingAnalyzerAlphaCheckers);
657   ClangTidyCheckFactories Factories;
658   for (const ClangTidyModuleRegistry::entry &Module :
659        ClangTidyModuleRegistry::entries()) {
660     Module.instantiate()->addCheckFactories(Factories);
661   }
662 
663   for (const auto &Factory : Factories)
664     Result.Checks.insert(Factory.getKey());
665 
666 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
667   SmallString<64> Buffer(AnalyzerCheckNamePrefix);
668   size_t DefSize = Buffer.size();
669   for (const auto &AnalyzerCheck : AnalyzerOptions::getRegisteredCheckers(
670            AllowEnablingAnalyzerAlphaCheckers)) {
671     Buffer.truncate(DefSize);
672     Buffer.append(AnalyzerCheck);
673     Result.Checks.insert(Buffer);
674   }
675   for (std::string OptionName : {
676 #define GET_CHECKER_OPTIONS
677 #define CHECKER_OPTION(TYPE, CHECKER, OPTION_NAME, DESCRIPTION, DEFAULT,       \
678                        RELEASE, HIDDEN)                                        \
679   Twine(AnalyzerCheckNamePrefix).concat(CHECKER ":" OPTION_NAME).str(),
680 
681 #include "clang/StaticAnalyzer/Checkers/Checkers.inc"
682 #undef CHECKER_OPTION
683 #undef GET_CHECKER_OPTIONS
684        }) {
685     Result.Options.insert(OptionName);
686   }
687 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
688 
689   Context.setOptionsCollector(&Result.Options);
690   for (const auto &Factory : Factories) {
691     Factory.getValue()(Factory.getKey(), &Context);
692   }
693 
694   return Result;
695 }
696 } // namespace clang::tidy
697