1 //===--- tools/extra/clang-tidy/ClangTidy.cpp - Clang tidy tool -----------===//
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
7 //===----------------------------------------------------------------------===//
9 /// \file This file implements a clang-tidy tool.
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.
15 //===----------------------------------------------------------------------===//
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"
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
51 using namespace clang::ast_matchers
;
52 using namespace clang::driver
;
53 using namespace clang::tooling
;
56 LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry
)
62 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
63 static const char *AnalyzerCheckNamePrefix
= "clang-analyzer-";
65 class AnalyzerDiagnosticConsumer
: public ento::PathDiagnosticConsumer
{
67 AnalyzerDiagnosticConsumer(ClangTidyContext
&Context
) : Context(Context
) {}
69 void FlushDiagnosticsImpl(std::vector
<const ento::PathDiagnostic
*> &Diags
,
70 FilesMade
*FilesMade
) override
{
71 for (const ento::PathDiagnostic
*PD
: Diags
) {
72 SmallString
<64> CheckName(AnalyzerCheckNamePrefix
);
73 CheckName
+= PD
->getCheckerName();
74 Context
.diag(CheckName
, PD
->getLocation().asLocation(),
75 PD
->getShortDescription())
76 << PD
->path
.back()->getRanges();
78 for (const auto &DiagPiece
:
79 PD
->path
.flatten(/*ShouldFlattenMacros=*/true)) {
80 Context
.diag(CheckName
, DiagPiece
->getLocation().asLocation(),
81 DiagPiece
->getString(), DiagnosticIDs::Note
)
82 << DiagPiece
->getRanges();
87 StringRef
getName() const override
{ return "ClangTidyDiags"; }
88 bool supportsLogicalOpControlFlow() const override
{ return true; }
89 bool supportsCrossFileDiagnostics() const override
{ return true; }
92 ClangTidyContext
&Context
;
94 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
98 ErrorReporter(ClangTidyContext
&Context
, FixBehaviour ApplyFixes
,
99 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> BaseFS
)
100 : Files(FileSystemOptions(), std::move(BaseFS
)),
101 DiagOpts(new DiagnosticOptions()),
102 DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts
)),
103 Diags(IntrusiveRefCntPtr
<DiagnosticIDs
>(new DiagnosticIDs
), &*DiagOpts
,
105 SourceMgr(Diags
, Files
), Context(Context
), ApplyFixes(ApplyFixes
),
106 TotalFixes(0), AppliedFixes(0), WarningsAsErrors(0) {
107 DiagOpts
->ShowColors
= Context
.getOptions().UseColor
.getValueOr(
108 llvm::sys::Process::StandardOutHasColors());
109 DiagPrinter
->BeginSourceFile(LangOpts
);
110 if (DiagOpts
->ShowColors
&& !llvm::sys::Process::StandardOutIsDisplayed()) {
111 llvm::sys::Process::UseANSIEscapeCodes(true);
115 SourceManager
&getSourceManager() { return SourceMgr
; }
117 void reportDiagnostic(const ClangTidyError
&Error
) {
118 const tooling::DiagnosticMessage
&Message
= Error
.Message
;
119 SourceLocation Loc
= getLocation(Message
.FilePath
, Message
.FileOffset
);
120 // Contains a pair for each attempted fix: location and whether the fix was
121 // applied successfully.
122 SmallVector
<std::pair
<SourceLocation
, bool>, 4> FixLocations
;
124 auto Level
= static_cast<DiagnosticsEngine::Level
>(Error
.DiagLevel
);
125 std::string Name
= Error
.DiagnosticName
;
126 if (!Error
.EnabledDiagnosticAliases
.empty())
127 Name
+= "," + llvm::join(Error
.EnabledDiagnosticAliases
, ",");
128 if (Error
.IsWarningAsError
) {
129 Name
+= ",-warnings-as-errors";
130 Level
= DiagnosticsEngine::Error
;
133 auto Diag
= Diags
.Report(Loc
, Diags
.getCustomDiagID(Level
, "%0 [%1]"))
134 << Message
.Message
<< Name
;
135 for (const FileByteRange
&FBR
: Error
.Message
.Ranges
)
136 Diag
<< getRange(FBR
);
137 // FIXME: explore options to support interactive fix selection.
138 const llvm::StringMap
<Replacements
> *ChosenFix
;
139 if (ApplyFixes
!= FB_NoFix
&&
140 (ChosenFix
= getFixIt(Error
, ApplyFixes
== FB_FixNotes
))) {
141 for (const auto &FileAndReplacements
: *ChosenFix
) {
142 for (const auto &Repl
: FileAndReplacements
.second
) {
144 bool CanBeApplied
= false;
145 if (!Repl
.isApplicable())
147 SourceLocation FixLoc
;
148 SmallString
<128> FixAbsoluteFilePath
= Repl
.getFilePath();
149 Files
.makeAbsolutePath(FixAbsoluteFilePath
);
150 tooling::Replacement
R(FixAbsoluteFilePath
, Repl
.getOffset(),
151 Repl
.getLength(), Repl
.getReplacementText());
152 Replacements
&Replacements
= FileReplacements
[R
.getFilePath()];
153 llvm::Error Err
= Replacements
.add(R
);
155 // FIXME: Implement better conflict handling.
156 llvm::errs() << "Trying to resolve conflict: "
157 << llvm::toString(std::move(Err
)) << "\n";
159 Replacements
.getShiftedCodePosition(R
.getOffset());
160 unsigned NewLength
= Replacements
.getShiftedCodePosition(
161 R
.getOffset() + R
.getLength()) -
163 if (NewLength
== R
.getLength()) {
164 R
= Replacement(R
.getFilePath(), NewOffset
, NewLength
,
165 R
.getReplacementText());
166 Replacements
= Replacements
.merge(tooling::Replacements(R
));
171 << "Can't resolve conflict, skipping the replacement.\n";
177 FixLoc
= getLocation(FixAbsoluteFilePath
, Repl
.getOffset());
178 FixLocations
.push_back(std::make_pair(FixLoc
, CanBeApplied
));
182 reportFix(Diag
, Error
.Message
.Fix
);
184 for (auto Fix
: FixLocations
) {
185 Diags
.Report(Fix
.first
, Fix
.second
? diag::note_fixit_applied
186 : diag::note_fixit_failed
);
188 for (const auto &Note
: Error
.Notes
)
193 if (TotalFixes
> 0) {
194 Rewriter
Rewrite(SourceMgr
, LangOpts
);
195 for (const auto &FileAndReplacements
: FileReplacements
) {
196 StringRef File
= FileAndReplacements
.first();
197 llvm::ErrorOr
<std::unique_ptr
<MemoryBuffer
>> Buffer
=
198 SourceMgr
.getFileManager().getBufferForFile(File
);
200 llvm::errs() << "Can't get buffer for file " << File
<< ": "
201 << Buffer
.getError().message() << "\n";
202 // FIXME: Maybe don't apply fixes for other files as well.
205 StringRef Code
= Buffer
.get()->getBuffer();
206 auto Style
= format::getStyle(
207 *Context
.getOptionsForFile(File
).FormatStyle
, File
, "none");
209 llvm::errs() << llvm::toString(Style
.takeError()) << "\n";
212 llvm::Expected
<tooling::Replacements
> Replacements
=
213 format::cleanupAroundReplacements(Code
, FileAndReplacements
.second
,
216 llvm::errs() << llvm::toString(Replacements
.takeError()) << "\n";
219 if (llvm::Expected
<tooling::Replacements
> FormattedReplacements
=
220 format::formatReplacements(Code
, *Replacements
, *Style
)) {
221 Replacements
= std::move(FormattedReplacements
);
223 llvm_unreachable("!Replacements");
225 llvm::errs() << llvm::toString(FormattedReplacements
.takeError())
226 << ". Skipping formatting.\n";
228 if (!tooling::applyAllReplacements(Replacements
.get(), Rewrite
)) {
229 llvm::errs() << "Can't apply replacements for file " << File
<< "\n";
232 if (Rewrite
.overwriteChangedFiles()) {
233 llvm::errs() << "clang-tidy failed to apply suggested fixes.\n";
235 llvm::errs() << "clang-tidy applied " << AppliedFixes
<< " of "
236 << TotalFixes
<< " suggested fixes.\n";
241 unsigned getWarningsAsErrorsCount() const { return WarningsAsErrors
; }
244 SourceLocation
getLocation(StringRef FilePath
, unsigned Offset
) {
245 if (FilePath
.empty())
246 return SourceLocation();
248 auto File
= SourceMgr
.getFileManager().getFile(FilePath
);
250 return SourceLocation();
252 FileID ID
= SourceMgr
.getOrCreateFileID(*File
, SrcMgr::C_User
);
253 return SourceMgr
.getLocForStartOfFile(ID
).getLocWithOffset(Offset
);
256 void reportFix(const DiagnosticBuilder
&Diag
,
257 const llvm::StringMap
<Replacements
> &Fix
) {
258 for (const auto &FileAndReplacements
: Fix
) {
259 for (const auto &Repl
: FileAndReplacements
.second
) {
260 if (!Repl
.isApplicable())
263 FBR
.FilePath
= Repl
.getFilePath().str();
264 FBR
.FileOffset
= Repl
.getOffset();
265 FBR
.Length
= Repl
.getLength();
267 Diag
<< FixItHint::CreateReplacement(getRange(FBR
),
268 Repl
.getReplacementText());
273 void reportNote(const tooling::DiagnosticMessage
&Message
) {
274 SourceLocation Loc
= getLocation(Message
.FilePath
, Message
.FileOffset
);
276 Diags
.Report(Loc
, Diags
.getCustomDiagID(DiagnosticsEngine::Note
, "%0"))
278 for (const FileByteRange
&FBR
: Message
.Ranges
)
279 Diag
<< getRange(FBR
);
280 reportFix(Diag
, Message
.Fix
);
283 CharSourceRange
getRange(const FileByteRange
&Range
) {
284 SmallString
<128> AbsoluteFilePath
{Range
.FilePath
};
285 Files
.makeAbsolutePath(AbsoluteFilePath
);
286 SourceLocation BeginLoc
= getLocation(AbsoluteFilePath
, Range
.FileOffset
);
287 SourceLocation EndLoc
= BeginLoc
.getLocWithOffset(Range
.Length
);
288 // Retrieve the source range for applicable highlights and fixes. Macro
289 // definition on the command line have locations in a virtual buffer and
290 // don't have valid file paths and are therefore not applicable.
291 return CharSourceRange::getCharRange(BeginLoc
, EndLoc
);
295 LangOptions LangOpts
; // FIXME: use langopts from each original file
296 IntrusiveRefCntPtr
<DiagnosticOptions
> DiagOpts
;
297 DiagnosticConsumer
*DiagPrinter
;
298 DiagnosticsEngine Diags
;
299 SourceManager SourceMgr
;
300 llvm::StringMap
<Replacements
> FileReplacements
;
301 ClangTidyContext
&Context
;
302 FixBehaviour ApplyFixes
;
304 unsigned AppliedFixes
;
305 unsigned WarningsAsErrors
;
308 class ClangTidyASTConsumer
: public MultiplexConsumer
{
310 ClangTidyASTConsumer(std::vector
<std::unique_ptr
<ASTConsumer
>> Consumers
,
311 std::unique_ptr
<ClangTidyProfiling
> Profiling
,
312 std::unique_ptr
<ast_matchers::MatchFinder
> Finder
,
313 std::vector
<std::unique_ptr
<ClangTidyCheck
>> Checks
)
314 : MultiplexConsumer(std::move(Consumers
)),
315 Profiling(std::move(Profiling
)), Finder(std::move(Finder
)),
316 Checks(std::move(Checks
)) {}
319 // Destructor order matters! Profiling must be destructed last.
320 // Or at least after Finder.
321 std::unique_ptr
<ClangTidyProfiling
> Profiling
;
322 std::unique_ptr
<ast_matchers::MatchFinder
> Finder
;
323 std::vector
<std::unique_ptr
<ClangTidyCheck
>> Checks
;
328 ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
329 ClangTidyContext
&Context
,
330 IntrusiveRefCntPtr
<llvm::vfs::OverlayFileSystem
> OverlayFS
)
331 : Context(Context
), OverlayFS(std::move(OverlayFS
)),
332 CheckFactories(new ClangTidyCheckFactories
) {
333 for (ClangTidyModuleRegistry::entry E
: ClangTidyModuleRegistry::entries()) {
334 std::unique_ptr
<ClangTidyModule
> Module
= E
.instantiate();
335 Module
->addCheckFactories(*CheckFactories
);
339 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
341 setStaticAnalyzerCheckerOpts(const ClangTidyOptions
&Opts
,
342 clang::AnalyzerOptions
&AnalyzerOptions
) {
343 StringRef
AnalyzerPrefix(AnalyzerCheckNamePrefix
);
344 for (const auto &Opt
: Opts
.CheckOptions
) {
345 StringRef
OptName(Opt
.getKey());
346 if (!OptName
.consume_front(AnalyzerPrefix
))
348 // Analyzer options are always local options so we can ignore priority.
349 AnalyzerOptions
.Config
[OptName
] = Opt
.getValue().Value
;
353 typedef std::vector
<std::pair
<std::string
, bool>> CheckersList
;
355 static CheckersList
getAnalyzerCheckersAndPackages(ClangTidyContext
&Context
,
356 bool IncludeExperimental
) {
359 const auto &RegisteredCheckers
=
360 AnalyzerOptions::getRegisteredCheckers(IncludeExperimental
);
361 bool AnalyzerChecksEnabled
= false;
362 for (StringRef CheckName
: RegisteredCheckers
) {
363 std::string
ClangTidyCheckName((AnalyzerCheckNamePrefix
+ CheckName
).str());
364 AnalyzerChecksEnabled
|= Context
.isCheckEnabled(ClangTidyCheckName
);
367 if (!AnalyzerChecksEnabled
)
370 // List all static analyzer checkers that our filter enables.
372 // Always add all core checkers if any other static analyzer check is enabled.
373 // This is currently necessary, as other path sensitive checks rely on the
375 for (StringRef CheckName
: RegisteredCheckers
) {
376 std::string
ClangTidyCheckName((AnalyzerCheckNamePrefix
+ CheckName
).str());
378 if (CheckName
.startswith("core") ||
379 Context
.isCheckEnabled(ClangTidyCheckName
)) {
380 List
.emplace_back(std::string(CheckName
), true);
385 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
387 std::unique_ptr
<clang::ASTConsumer
>
388 ClangTidyASTConsumerFactory::createASTConsumer(
389 clang::CompilerInstance
&Compiler
, StringRef File
) {
390 // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
392 SourceManager
*SM
= &Compiler
.getSourceManager();
393 Context
.setSourceManager(SM
);
394 Context
.setCurrentFile(File
);
395 Context
.setASTContext(&Compiler
.getASTContext());
397 auto WorkingDir
= Compiler
.getSourceManager()
399 .getVirtualFileSystem()
400 .getCurrentWorkingDirectory();
402 Context
.setCurrentBuildDirectory(WorkingDir
.get());
404 std::vector
<std::unique_ptr
<ClangTidyCheck
>> Checks
=
405 CheckFactories
->createChecks(&Context
);
407 llvm::erase_if(Checks
, [&](std::unique_ptr
<ClangTidyCheck
> &Check
) {
408 return !Check
->isLanguageVersionSupported(Context
.getLangOpts());
411 ast_matchers::MatchFinder::MatchFinderOptions FinderOptions
;
413 std::unique_ptr
<ClangTidyProfiling
> Profiling
;
414 if (Context
.getEnableProfiling()) {
415 Profiling
= std::make_unique
<ClangTidyProfiling
>(
416 Context
.getProfileStorageParams());
417 FinderOptions
.CheckProfiling
.emplace(Profiling
->Records
);
420 std::unique_ptr
<ast_matchers::MatchFinder
> Finder(
421 new ast_matchers::MatchFinder(std::move(FinderOptions
)));
423 Preprocessor
*PP
= &Compiler
.getPreprocessor();
424 Preprocessor
*ModuleExpanderPP
= PP
;
426 if (Context
.getLangOpts().Modules
&& OverlayFS
!= nullptr) {
427 auto ModuleExpander
= std::make_unique
<ExpandModularHeadersPPCallbacks
>(
428 &Compiler
, OverlayFS
);
429 ModuleExpanderPP
= ModuleExpander
->getPreprocessor();
430 PP
->addPPCallbacks(std::move(ModuleExpander
));
433 for (auto &Check
: Checks
) {
434 Check
->registerMatchers(&*Finder
);
435 Check
->registerPPCallbacks(*SM
, PP
, ModuleExpanderPP
);
438 std::vector
<std::unique_ptr
<ASTConsumer
>> Consumers
;
440 Consumers
.push_back(Finder
->newASTConsumer());
442 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
443 AnalyzerOptionsRef AnalyzerOptions
= Compiler
.getAnalyzerOpts();
444 AnalyzerOptions
->CheckersAndPackages
= getAnalyzerCheckersAndPackages(
445 Context
, Context
.canEnableAnalyzerAlphaCheckers());
446 if (!AnalyzerOptions
->CheckersAndPackages
.empty()) {
447 setStaticAnalyzerCheckerOpts(Context
.getOptions(), *AnalyzerOptions
);
448 AnalyzerOptions
->AnalysisStoreOpt
= RegionStoreModel
;
449 AnalyzerOptions
->AnalysisDiagOpt
= PD_NONE
;
450 AnalyzerOptions
->AnalyzeNestedBlocks
= true;
451 AnalyzerOptions
->eagerlyAssumeBinOpBifurcation
= true;
452 std::unique_ptr
<ento::AnalysisASTConsumer
> AnalysisConsumer
=
453 ento::CreateAnalysisConsumer(Compiler
);
454 AnalysisConsumer
->AddDiagnosticConsumer(
455 new AnalyzerDiagnosticConsumer(Context
));
456 Consumers
.push_back(std::move(AnalysisConsumer
));
458 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
459 return std::make_unique
<ClangTidyASTConsumer
>(
460 std::move(Consumers
), std::move(Profiling
), std::move(Finder
),
464 std::vector
<std::string
> ClangTidyASTConsumerFactory::getCheckNames() {
465 std::vector
<std::string
> CheckNames
;
466 for (const auto &CheckFactory
: *CheckFactories
) {
467 if (Context
.isCheckEnabled(CheckFactory
.getKey()))
468 CheckNames
.emplace_back(CheckFactory
.getKey());
471 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
472 for (const auto &AnalyzerCheck
: getAnalyzerCheckersAndPackages(
473 Context
, Context
.canEnableAnalyzerAlphaCheckers()))
474 CheckNames
.push_back(AnalyzerCheckNamePrefix
+ AnalyzerCheck
.first
);
475 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
477 llvm::sort(CheckNames
);
481 ClangTidyOptions::OptionMap
ClangTidyASTConsumerFactory::getCheckOptions() {
482 ClangTidyOptions::OptionMap Options
;
483 std::vector
<std::unique_ptr
<ClangTidyCheck
>> Checks
=
484 CheckFactories
->createChecks(&Context
);
485 for (const auto &Check
: Checks
)
486 Check
->storeOptions(Options
);
490 std::vector
<std::string
>
491 getCheckNames(const ClangTidyOptions
&Options
,
492 bool AllowEnablingAnalyzerAlphaCheckers
) {
493 clang::tidy::ClangTidyContext
Context(
494 std::make_unique
<DefaultOptionsProvider
>(ClangTidyGlobalOptions(),
496 AllowEnablingAnalyzerAlphaCheckers
);
497 ClangTidyASTConsumerFactory
Factory(Context
);
498 return Factory
.getCheckNames();
501 ClangTidyOptions::OptionMap
502 getCheckOptions(const ClangTidyOptions
&Options
,
503 bool AllowEnablingAnalyzerAlphaCheckers
) {
504 clang::tidy::ClangTidyContext
Context(
505 std::make_unique
<DefaultOptionsProvider
>(ClangTidyGlobalOptions(),
507 AllowEnablingAnalyzerAlphaCheckers
);
508 ClangTidyASTConsumerFactory
Factory(Context
);
509 return Factory
.getCheckOptions();
512 std::vector
<ClangTidyError
>
513 runClangTidy(clang::tidy::ClangTidyContext
&Context
,
514 const CompilationDatabase
&Compilations
,
515 ArrayRef
<std::string
> InputFiles
,
516 llvm::IntrusiveRefCntPtr
<llvm::vfs::OverlayFileSystem
> BaseFS
,
517 bool ApplyAnyFix
, bool EnableCheckProfile
,
518 llvm::StringRef StoreCheckProfile
) {
519 ClangTool
Tool(Compilations
, InputFiles
,
520 std::make_shared
<PCHContainerOperations
>(), BaseFS
);
522 // Add extra arguments passed by the clang-tidy command-line.
523 ArgumentsAdjuster PerFileExtraArgumentsInserter
=
524 [&Context
](const CommandLineArguments
&Args
, StringRef Filename
) {
525 ClangTidyOptions Opts
= Context
.getOptionsForFile(Filename
);
526 CommandLineArguments AdjustedArgs
= Args
;
527 if (Opts
.ExtraArgsBefore
) {
528 auto I
= AdjustedArgs
.begin();
529 if (I
!= AdjustedArgs
.end() && !StringRef(*I
).startswith("-"))
530 ++I
; // Skip compiler binary name, if it is there.
531 AdjustedArgs
.insert(I
, Opts
.ExtraArgsBefore
->begin(),
532 Opts
.ExtraArgsBefore
->end());
535 AdjustedArgs
.insert(AdjustedArgs
.end(), Opts
.ExtraArgs
->begin(),
536 Opts
.ExtraArgs
->end());
540 Tool
.appendArgumentsAdjuster(PerFileExtraArgumentsInserter
);
541 Tool
.appendArgumentsAdjuster(getStripPluginsAdjuster());
542 Context
.setEnableProfiling(EnableCheckProfile
);
543 Context
.setProfileStoragePrefix(StoreCheckProfile
);
545 ClangTidyDiagnosticConsumer
DiagConsumer(Context
, nullptr, true, ApplyAnyFix
);
546 DiagnosticsEngine
DE(new DiagnosticIDs(), new DiagnosticOptions(),
547 &DiagConsumer
, /*ShouldOwnClient=*/false);
548 Context
.setDiagnosticsEngine(&DE
);
549 Tool
.setDiagnosticConsumer(&DiagConsumer
);
551 class ActionFactory
: public FrontendActionFactory
{
553 ActionFactory(ClangTidyContext
&Context
,
554 IntrusiveRefCntPtr
<llvm::vfs::OverlayFileSystem
> BaseFS
)
555 : ConsumerFactory(Context
, std::move(BaseFS
)) {}
556 std::unique_ptr
<FrontendAction
> create() override
{
557 return std::make_unique
<Action
>(&ConsumerFactory
);
560 bool runInvocation(std::shared_ptr
<CompilerInvocation
> Invocation
,
562 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
,
563 DiagnosticConsumer
*DiagConsumer
) override
{
564 // Explicitly ask to define __clang_analyzer__ macro.
565 Invocation
->getPreprocessorOpts().SetUpStaticAnalyzer
= true;
566 return FrontendActionFactory::runInvocation(
567 Invocation
, Files
, PCHContainerOps
, DiagConsumer
);
571 class Action
: public ASTFrontendAction
{
573 Action(ClangTidyASTConsumerFactory
*Factory
) : Factory(Factory
) {}
574 std::unique_ptr
<ASTConsumer
> CreateASTConsumer(CompilerInstance
&Compiler
,
575 StringRef File
) override
{
576 return Factory
->createASTConsumer(Compiler
, File
);
580 ClangTidyASTConsumerFactory
*Factory
;
583 ClangTidyASTConsumerFactory ConsumerFactory
;
586 ActionFactory
Factory(Context
, std::move(BaseFS
));
588 return DiagConsumer
.take();
591 void handleErrors(llvm::ArrayRef
<ClangTidyError
> Errors
,
592 ClangTidyContext
&Context
, FixBehaviour Fix
,
593 unsigned &WarningsAsErrorsCount
,
594 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> BaseFS
) {
595 ErrorReporter
Reporter(Context
, Fix
, std::move(BaseFS
));
596 llvm::vfs::FileSystem
&FileSystem
=
597 Reporter
.getSourceManager().getFileManager().getVirtualFileSystem();
598 auto InitialWorkingDir
= FileSystem
.getCurrentWorkingDirectory();
599 if (!InitialWorkingDir
)
600 llvm::report_fatal_error("Cannot get current working path.");
602 for (const ClangTidyError
&Error
: Errors
) {
603 if (!Error
.BuildDirectory
.empty()) {
604 // By default, the working directory of file system is the current
605 // clang-tidy running directory.
607 // Change the directory to the one used during the analysis.
608 FileSystem
.setCurrentWorkingDirectory(Error
.BuildDirectory
);
610 Reporter
.reportDiagnostic(Error
);
611 // Return to the initial directory to correctly resolve next Error.
612 FileSystem
.setCurrentWorkingDirectory(InitialWorkingDir
.get());
615 WarningsAsErrorsCount
+= Reporter
.getWarningsAsErrorsCount();
618 void exportReplacements(const llvm::StringRef MainFilePath
,
619 const std::vector
<ClangTidyError
> &Errors
,
621 TranslationUnitDiagnostics TUD
;
622 TUD
.MainSourceFile
= std::string(MainFilePath
);
623 for (const auto &Error
: Errors
) {
624 tooling::Diagnostic Diag
= Error
;
625 TUD
.Diagnostics
.insert(TUD
.Diagnostics
.end(), Diag
);
628 yaml::Output
YAML(OS
);