ClangTidy.cpp 26 KB

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