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