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