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
)
58 namespace clang::tidy
{
61 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
62 static const char *AnalyzerCheckNamePrefix
= "clang-analyzer-";
64 class AnalyzerDiagnosticConsumer
: public ento::PathDiagnosticConsumer
{
66 AnalyzerDiagnosticConsumer(ClangTidyContext
&Context
) : Context(Context
) {}
68 void FlushDiagnosticsImpl(std::vector
<const ento::PathDiagnostic
*> &Diags
,
69 FilesMade
*FilesMade
) override
{
70 for (const ento::PathDiagnostic
*PD
: Diags
) {
71 SmallString
<64> CheckName(AnalyzerCheckNamePrefix
);
72 CheckName
+= PD
->getCheckerName();
73 Context
.diag(CheckName
, PD
->getLocation().asLocation(),
74 PD
->getShortDescription())
75 << PD
->path
.back()->getRanges();
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();
86 StringRef
getName() const override
{ return "ClangTidyDiags"; }
87 bool supportsLogicalOpControlFlow() const override
{ return true; }
88 bool supportsCrossFileDiagnostics() const override
{ return true; }
91 ClangTidyContext
&Context
;
93 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
97 ErrorReporter(ClangTidyContext
&Context
, FixBehaviour ApplyFixes
,
98 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> BaseFS
)
99 : Files(FileSystemOptions(), std::move(BaseFS
)),
100 DiagOpts(new DiagnosticOptions()),
101 DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts
)),
102 Diags(IntrusiveRefCntPtr
<DiagnosticIDs
>(new DiagnosticIDs
), &*DiagOpts
,
104 SourceMgr(Diags
, Files
), Context(Context
), ApplyFixes(ApplyFixes
) {
105 DiagOpts
->ShowColors
= Context
.getOptions().UseColor
.value_or(
106 llvm::sys::Process::StandardOutHasColors());
107 DiagPrinter
->BeginSourceFile(LangOpts
);
108 if (DiagOpts
->ShowColors
&& !llvm::sys::Process::StandardOutIsDisplayed()) {
109 llvm::sys::Process::UseANSIEscapeCodes(true);
113 SourceManager
&getSourceManager() { return SourceMgr
; }
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
;
122 auto Level
= static_cast<DiagnosticsEngine::Level
>(Error
.DiagLevel
);
123 std::string Name
= Error
.DiagnosticName
;
124 if (!Error
.EnabledDiagnosticAliases
.empty())
125 Name
+= "," + llvm::join(Error
.EnabledDiagnosticAliases
, ",");
126 if (Error
.IsWarningAsError
) {
127 Name
+= ",-warnings-as-errors";
128 Level
= DiagnosticsEngine::Error
;
131 auto Diag
= Diags
.Report(Loc
, Diags
.getCustomDiagID(Level
, "%0 [%1]"))
132 << Message
.Message
<< Name
;
133 for (const FileByteRange
&FBR
: Error
.Message
.Ranges
)
134 Diag
<< getRange(FBR
);
135 // FIXME: explore options to support interactive fix selection.
136 const llvm::StringMap
<Replacements
> *ChosenFix
= nullptr;
137 if (ApplyFixes
!= FB_NoFix
&&
138 (ChosenFix
= getFixIt(Error
, ApplyFixes
== FB_FixNotes
))) {
139 for (const auto &FileAndReplacements
: *ChosenFix
) {
140 for (const auto &Repl
: FileAndReplacements
.second
) {
142 bool CanBeApplied
= false;
143 if (!Repl
.isApplicable())
145 SourceLocation FixLoc
;
146 SmallString
<128> FixAbsoluteFilePath
= Repl
.getFilePath();
147 Files
.makeAbsolutePath(FixAbsoluteFilePath
);
148 tooling::Replacement
R(FixAbsoluteFilePath
, Repl
.getOffset(),
149 Repl
.getLength(), Repl
.getReplacementText());
150 Replacements
&Replacements
= FileReplacements
[R
.getFilePath()];
151 llvm::Error Err
= Replacements
.add(R
);
153 // FIXME: Implement better conflict handling.
154 llvm::errs() << "Trying to resolve conflict: "
155 << llvm::toString(std::move(Err
)) << "\n";
157 Replacements
.getShiftedCodePosition(R
.getOffset());
158 unsigned NewLength
= Replacements
.getShiftedCodePosition(
159 R
.getOffset() + R
.getLength()) -
161 if (NewLength
== R
.getLength()) {
162 R
= Replacement(R
.getFilePath(), NewOffset
, NewLength
,
163 R
.getReplacementText());
164 Replacements
= Replacements
.merge(tooling::Replacements(R
));
169 << "Can't resolve conflict, skipping the replacement.\n";
175 FixLoc
= getLocation(FixAbsoluteFilePath
, Repl
.getOffset());
176 FixLocations
.push_back(std::make_pair(FixLoc
, CanBeApplied
));
180 reportFix(Diag
, Error
.Message
.Fix
);
182 for (auto Fix
: FixLocations
) {
183 Diags
.Report(Fix
.first
, Fix
.second
? diag::note_fixit_applied
184 : diag::note_fixit_failed
);
186 for (const auto &Note
: Error
.Notes
)
191 if (TotalFixes
> 0) {
192 Rewriter
Rewrite(SourceMgr
, LangOpts
);
193 for (const auto &FileAndReplacements
: FileReplacements
) {
194 StringRef File
= FileAndReplacements
.first();
195 llvm::ErrorOr
<std::unique_ptr
<MemoryBuffer
>> Buffer
=
196 SourceMgr
.getFileManager().getBufferForFile(File
);
198 llvm::errs() << "Can't get buffer for file " << File
<< ": "
199 << Buffer
.getError().message() << "\n";
200 // FIXME: Maybe don't apply fixes for other files as well.
203 StringRef Code
= Buffer
.get()->getBuffer();
204 auto Style
= format::getStyle(
205 *Context
.getOptionsForFile(File
).FormatStyle
, File
, "none");
207 llvm::errs() << llvm::toString(Style
.takeError()) << "\n";
210 llvm::Expected
<tooling::Replacements
> Replacements
=
211 format::cleanupAroundReplacements(Code
, FileAndReplacements
.second
,
214 llvm::errs() << llvm::toString(Replacements
.takeError()) << "\n";
217 if (llvm::Expected
<tooling::Replacements
> FormattedReplacements
=
218 format::formatReplacements(Code
, *Replacements
, *Style
)) {
219 Replacements
= std::move(FormattedReplacements
);
221 llvm_unreachable("!Replacements");
223 llvm::errs() << llvm::toString(FormattedReplacements
.takeError())
224 << ". Skipping formatting.\n";
226 if (!tooling::applyAllReplacements(Replacements
.get(), Rewrite
)) {
227 llvm::errs() << "Can't apply replacements for file " << File
<< "\n";
230 if (Rewrite
.overwriteChangedFiles()) {
231 llvm::errs() << "clang-tidy failed to apply suggested fixes.\n";
233 llvm::errs() << "clang-tidy applied " << AppliedFixes
<< " of "
234 << TotalFixes
<< " suggested fixes.\n";
239 unsigned getWarningsAsErrorsCount() const { return WarningsAsErrors
; }
242 SourceLocation
getLocation(StringRef FilePath
, unsigned Offset
) {
243 if (FilePath
.empty())
246 auto File
= SourceMgr
.getFileManager().getFile(FilePath
);
250 FileID ID
= SourceMgr
.getOrCreateFileID(*File
, SrcMgr::C_User
);
251 return SourceMgr
.getLocForStartOfFile(ID
).getLocWithOffset(Offset
);
254 void reportFix(const DiagnosticBuilder
&Diag
,
255 const llvm::StringMap
<Replacements
> &Fix
) {
256 for (const auto &FileAndReplacements
: Fix
) {
257 for (const auto &Repl
: FileAndReplacements
.second
) {
258 if (!Repl
.isApplicable())
261 FBR
.FilePath
= Repl
.getFilePath().str();
262 FBR
.FileOffset
= Repl
.getOffset();
263 FBR
.Length
= Repl
.getLength();
265 Diag
<< FixItHint::CreateReplacement(getRange(FBR
),
266 Repl
.getReplacementText());
271 void reportNote(const tooling::DiagnosticMessage
&Message
) {
272 SourceLocation Loc
= getLocation(Message
.FilePath
, Message
.FileOffset
);
274 Diags
.Report(Loc
, Diags
.getCustomDiagID(DiagnosticsEngine::Note
, "%0"))
276 for (const FileByteRange
&FBR
: Message
.Ranges
)
277 Diag
<< getRange(FBR
);
278 reportFix(Diag
, Message
.Fix
);
281 CharSourceRange
getRange(const FileByteRange
&Range
) {
282 SmallString
<128> AbsoluteFilePath
{Range
.FilePath
};
283 Files
.makeAbsolutePath(AbsoluteFilePath
);
284 SourceLocation BeginLoc
= getLocation(AbsoluteFilePath
, Range
.FileOffset
);
285 SourceLocation EndLoc
= BeginLoc
.getLocWithOffset(Range
.Length
);
286 // Retrieve the source range for applicable highlights and fixes. Macro
287 // definition on the command line have locations in a virtual buffer and
288 // don't have valid file paths and are therefore not applicable.
289 return CharSourceRange::getCharRange(BeginLoc
, EndLoc
);
293 LangOptions LangOpts
; // FIXME: use langopts from each original file
294 IntrusiveRefCntPtr
<DiagnosticOptions
> DiagOpts
;
295 DiagnosticConsumer
*DiagPrinter
;
296 DiagnosticsEngine Diags
;
297 SourceManager SourceMgr
;
298 llvm::StringMap
<Replacements
> FileReplacements
;
299 ClangTidyContext
&Context
;
300 FixBehaviour ApplyFixes
;
301 unsigned TotalFixes
= 0U;
302 unsigned AppliedFixes
= 0U;
303 unsigned WarningsAsErrors
= 0U;
306 class ClangTidyASTConsumer
: public MultiplexConsumer
{
308 ClangTidyASTConsumer(std::vector
<std::unique_ptr
<ASTConsumer
>> Consumers
,
309 std::unique_ptr
<ClangTidyProfiling
> Profiling
,
310 std::unique_ptr
<ast_matchers::MatchFinder
> Finder
,
311 std::vector
<std::unique_ptr
<ClangTidyCheck
>> Checks
)
312 : MultiplexConsumer(std::move(Consumers
)),
313 Profiling(std::move(Profiling
)), Finder(std::move(Finder
)),
314 Checks(std::move(Checks
)) {}
317 // Destructor order matters! Profiling must be destructed last.
318 // Or at least after Finder.
319 std::unique_ptr
<ClangTidyProfiling
> Profiling
;
320 std::unique_ptr
<ast_matchers::MatchFinder
> Finder
;
321 std::vector
<std::unique_ptr
<ClangTidyCheck
>> Checks
;
326 ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
327 ClangTidyContext
&Context
,
328 IntrusiveRefCntPtr
<llvm::vfs::OverlayFileSystem
> OverlayFS
)
329 : Context(Context
), OverlayFS(std::move(OverlayFS
)),
330 CheckFactories(new ClangTidyCheckFactories
) {
331 for (ClangTidyModuleRegistry::entry E
: ClangTidyModuleRegistry::entries()) {
332 std::unique_ptr
<ClangTidyModule
> Module
= E
.instantiate();
333 Module
->addCheckFactories(*CheckFactories
);
337 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
339 setStaticAnalyzerCheckerOpts(const ClangTidyOptions
&Opts
,
340 clang::AnalyzerOptions
&AnalyzerOptions
) {
341 StringRef
AnalyzerPrefix(AnalyzerCheckNamePrefix
);
342 for (const auto &Opt
: Opts
.CheckOptions
) {
343 StringRef
OptName(Opt
.getKey());
344 if (!OptName
.consume_front(AnalyzerPrefix
))
346 // Analyzer options are always local options so we can ignore priority.
347 AnalyzerOptions
.Config
[OptName
] = Opt
.getValue().Value
;
351 using CheckersList
= std::vector
<std::pair
<std::string
, bool>>;
353 static CheckersList
getAnalyzerCheckersAndPackages(ClangTidyContext
&Context
,
354 bool IncludeExperimental
) {
357 const auto &RegisteredCheckers
=
358 AnalyzerOptions::getRegisteredCheckers(IncludeExperimental
);
359 bool AnalyzerChecksEnabled
= false;
360 for (StringRef CheckName
: RegisteredCheckers
) {
361 std::string
ClangTidyCheckName((AnalyzerCheckNamePrefix
+ CheckName
).str());
362 AnalyzerChecksEnabled
|= Context
.isCheckEnabled(ClangTidyCheckName
);
365 if (!AnalyzerChecksEnabled
)
368 // List all static analyzer checkers that our filter enables.
370 // Always add all core checkers if any other static analyzer check is enabled.
371 // This is currently necessary, as other path sensitive checks rely on the
373 for (StringRef CheckName
: RegisteredCheckers
) {
374 std::string
ClangTidyCheckName((AnalyzerCheckNamePrefix
+ CheckName
).str());
376 if (CheckName
.startswith("core") ||
377 Context
.isCheckEnabled(ClangTidyCheckName
)) {
378 List
.emplace_back(std::string(CheckName
), true);
383 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
385 std::unique_ptr
<clang::ASTConsumer
>
386 ClangTidyASTConsumerFactory::createASTConsumer(
387 clang::CompilerInstance
&Compiler
, StringRef File
) {
388 // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
390 SourceManager
*SM
= &Compiler
.getSourceManager();
391 Context
.setSourceManager(SM
);
392 Context
.setCurrentFile(File
);
393 Context
.setASTContext(&Compiler
.getASTContext());
395 auto WorkingDir
= Compiler
.getSourceManager()
397 .getVirtualFileSystem()
398 .getCurrentWorkingDirectory();
400 Context
.setCurrentBuildDirectory(WorkingDir
.get());
402 std::vector
<std::unique_ptr
<ClangTidyCheck
>> Checks
=
403 CheckFactories
->createChecksForLanguage(&Context
);
405 ast_matchers::MatchFinder::MatchFinderOptions FinderOptions
;
407 std::unique_ptr
<ClangTidyProfiling
> Profiling
;
408 if (Context
.getEnableProfiling()) {
409 Profiling
= std::make_unique
<ClangTidyProfiling
>(
410 Context
.getProfileStorageParams());
411 FinderOptions
.CheckProfiling
.emplace(Profiling
->Records
);
414 std::unique_ptr
<ast_matchers::MatchFinder
> Finder(
415 new ast_matchers::MatchFinder(std::move(FinderOptions
)));
417 Preprocessor
*PP
= &Compiler
.getPreprocessor();
418 Preprocessor
*ModuleExpanderPP
= PP
;
420 if (Context
.canEnableModuleHeadersParsing() &&
421 Context
.getLangOpts().Modules
&& OverlayFS
!= nullptr) {
422 auto ModuleExpander
= std::make_unique
<ExpandModularHeadersPPCallbacks
>(
423 &Compiler
, OverlayFS
);
424 ModuleExpanderPP
= ModuleExpander
->getPreprocessor();
425 PP
->addPPCallbacks(std::move(ModuleExpander
));
428 for (auto &Check
: Checks
) {
429 Check
->registerMatchers(&*Finder
);
430 Check
->registerPPCallbacks(*SM
, PP
, ModuleExpanderPP
);
433 std::vector
<std::unique_ptr
<ASTConsumer
>> Consumers
;
435 Consumers
.push_back(Finder
->newASTConsumer());
437 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
438 AnalyzerOptions
&AnalyzerOptions
= Compiler
.getAnalyzerOpts();
439 AnalyzerOptions
.CheckersAndPackages
= getAnalyzerCheckersAndPackages(
440 Context
, Context
.canEnableAnalyzerAlphaCheckers());
441 if (!AnalyzerOptions
.CheckersAndPackages
.empty()) {
442 setStaticAnalyzerCheckerOpts(Context
.getOptions(), AnalyzerOptions
);
443 AnalyzerOptions
.AnalysisDiagOpt
= PD_NONE
;
444 AnalyzerOptions
.eagerlyAssumeBinOpBifurcation
= true;
445 std::unique_ptr
<ento::AnalysisASTConsumer
> AnalysisConsumer
=
446 ento::CreateAnalysisConsumer(Compiler
);
447 AnalysisConsumer
->AddDiagnosticConsumer(
448 new AnalyzerDiagnosticConsumer(Context
));
449 Consumers
.push_back(std::move(AnalysisConsumer
));
451 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
452 return std::make_unique
<ClangTidyASTConsumer
>(
453 std::move(Consumers
), std::move(Profiling
), std::move(Finder
),
457 std::vector
<std::string
> ClangTidyASTConsumerFactory::getCheckNames() {
458 std::vector
<std::string
> CheckNames
;
459 for (const auto &CheckFactory
: *CheckFactories
) {
460 if (Context
.isCheckEnabled(CheckFactory
.getKey()))
461 CheckNames
.emplace_back(CheckFactory
.getKey());
464 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
465 for (const auto &AnalyzerCheck
: getAnalyzerCheckersAndPackages(
466 Context
, Context
.canEnableAnalyzerAlphaCheckers()))
467 CheckNames
.push_back(AnalyzerCheckNamePrefix
+ AnalyzerCheck
.first
);
468 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
470 llvm::sort(CheckNames
);
474 ClangTidyOptions::OptionMap
ClangTidyASTConsumerFactory::getCheckOptions() {
475 ClangTidyOptions::OptionMap Options
;
476 std::vector
<std::unique_ptr
<ClangTidyCheck
>> Checks
=
477 CheckFactories
->createChecks(&Context
);
478 for (const auto &Check
: Checks
)
479 Check
->storeOptions(Options
);
483 std::vector
<std::string
>
484 getCheckNames(const ClangTidyOptions
&Options
,
485 bool AllowEnablingAnalyzerAlphaCheckers
) {
486 clang::tidy::ClangTidyContext
Context(
487 std::make_unique
<DefaultOptionsProvider
>(ClangTidyGlobalOptions(),
489 AllowEnablingAnalyzerAlphaCheckers
);
490 ClangTidyASTConsumerFactory
Factory(Context
);
491 return Factory
.getCheckNames();
494 ClangTidyOptions::OptionMap
495 getCheckOptions(const ClangTidyOptions
&Options
,
496 bool AllowEnablingAnalyzerAlphaCheckers
) {
497 clang::tidy::ClangTidyContext
Context(
498 std::make_unique
<DefaultOptionsProvider
>(ClangTidyGlobalOptions(),
500 AllowEnablingAnalyzerAlphaCheckers
);
501 ClangTidyDiagnosticConsumer
DiagConsumer(Context
);
502 DiagnosticsEngine
DE(llvm::makeIntrusiveRefCnt
<DiagnosticIDs
>(),
503 llvm::makeIntrusiveRefCnt
<DiagnosticOptions
>(),
504 &DiagConsumer
, /*ShouldOwnClient=*/false);
505 Context
.setDiagnosticsEngine(&DE
);
506 ClangTidyASTConsumerFactory
Factory(Context
);
507 return Factory
.getCheckOptions();
510 std::vector
<ClangTidyError
>
511 runClangTidy(clang::tidy::ClangTidyContext
&Context
,
512 const CompilationDatabase
&Compilations
,
513 ArrayRef
<std::string
> InputFiles
,
514 llvm::IntrusiveRefCntPtr
<llvm::vfs::OverlayFileSystem
> BaseFS
,
515 bool ApplyAnyFix
, bool EnableCheckProfile
,
516 llvm::StringRef StoreCheckProfile
) {
517 ClangTool
Tool(Compilations
, InputFiles
,
518 std::make_shared
<PCHContainerOperations
>(), BaseFS
);
520 // Add extra arguments passed by the clang-tidy command-line.
521 ArgumentsAdjuster PerFileExtraArgumentsInserter
=
522 [&Context
](const CommandLineArguments
&Args
, StringRef Filename
) {
523 ClangTidyOptions Opts
= Context
.getOptionsForFile(Filename
);
524 CommandLineArguments AdjustedArgs
= Args
;
525 if (Opts
.ExtraArgsBefore
) {
526 auto I
= AdjustedArgs
.begin();
527 if (I
!= AdjustedArgs
.end() && !StringRef(*I
).startswith("-"))
528 ++I
; // Skip compiler binary name, if it is there.
529 AdjustedArgs
.insert(I
, Opts
.ExtraArgsBefore
->begin(),
530 Opts
.ExtraArgsBefore
->end());
533 AdjustedArgs
.insert(AdjustedArgs
.end(), Opts
.ExtraArgs
->begin(),
534 Opts
.ExtraArgs
->end());
538 Tool
.appendArgumentsAdjuster(PerFileExtraArgumentsInserter
);
539 Tool
.appendArgumentsAdjuster(getStripPluginsAdjuster());
540 Context
.setEnableProfiling(EnableCheckProfile
);
541 Context
.setProfileStoragePrefix(StoreCheckProfile
);
543 ClangTidyDiagnosticConsumer
DiagConsumer(Context
, nullptr, true, ApplyAnyFix
);
544 DiagnosticsEngine
DE(new DiagnosticIDs(), new DiagnosticOptions(),
545 &DiagConsumer
, /*ShouldOwnClient=*/false);
546 Context
.setDiagnosticsEngine(&DE
);
547 Tool
.setDiagnosticConsumer(&DiagConsumer
);
549 class ActionFactory
: public FrontendActionFactory
{
551 ActionFactory(ClangTidyContext
&Context
,
552 IntrusiveRefCntPtr
<llvm::vfs::OverlayFileSystem
> BaseFS
)
553 : ConsumerFactory(Context
, std::move(BaseFS
)) {}
554 std::unique_ptr
<FrontendAction
> create() override
{
555 return std::make_unique
<Action
>(&ConsumerFactory
);
558 bool runInvocation(std::shared_ptr
<CompilerInvocation
> Invocation
,
560 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
,
561 DiagnosticConsumer
*DiagConsumer
) override
{
562 // Explicitly ask to define __clang_analyzer__ macro.
563 Invocation
->getPreprocessorOpts().SetUpStaticAnalyzer
= true;
564 return FrontendActionFactory::runInvocation(
565 Invocation
, Files
, PCHContainerOps
, DiagConsumer
);
569 class Action
: public ASTFrontendAction
{
571 Action(ClangTidyASTConsumerFactory
*Factory
) : Factory(Factory
) {}
572 std::unique_ptr
<ASTConsumer
> CreateASTConsumer(CompilerInstance
&Compiler
,
573 StringRef File
) override
{
574 return Factory
->createASTConsumer(Compiler
, File
);
578 ClangTidyASTConsumerFactory
*Factory
;
581 ClangTidyASTConsumerFactory ConsumerFactory
;
584 ActionFactory
Factory(Context
, std::move(BaseFS
));
586 return DiagConsumer
.take();
589 void handleErrors(llvm::ArrayRef
<ClangTidyError
> Errors
,
590 ClangTidyContext
&Context
, FixBehaviour Fix
,
591 unsigned &WarningsAsErrorsCount
,
592 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> BaseFS
) {
593 ErrorReporter
Reporter(Context
, Fix
, std::move(BaseFS
));
594 llvm::vfs::FileSystem
&FileSystem
=
595 Reporter
.getSourceManager().getFileManager().getVirtualFileSystem();
596 auto InitialWorkingDir
= FileSystem
.getCurrentWorkingDirectory();
597 if (!InitialWorkingDir
)
598 llvm::report_fatal_error("Cannot get current working path.");
600 for (const ClangTidyError
&Error
: Errors
) {
601 if (!Error
.BuildDirectory
.empty()) {
602 // By default, the working directory of file system is the current
603 // clang-tidy running directory.
605 // Change the directory to the one used during the analysis.
606 FileSystem
.setCurrentWorkingDirectory(Error
.BuildDirectory
);
608 Reporter
.reportDiagnostic(Error
);
609 // Return to the initial directory to correctly resolve next Error.
610 FileSystem
.setCurrentWorkingDirectory(InitialWorkingDir
.get());
613 WarningsAsErrorsCount
+= Reporter
.getWarningsAsErrorsCount();
616 void exportReplacements(const llvm::StringRef MainFilePath
,
617 const std::vector
<ClangTidyError
> &Errors
,
619 TranslationUnitDiagnostics TUD
;
620 TUD
.MainSourceFile
= std::string(MainFilePath
);
621 for (const auto &Error
: Errors
) {
622 tooling::Diagnostic Diag
= Error
;
623 if (Error
.IsWarningAsError
)
624 Diag
.DiagLevel
= tooling::Diagnostic::Error
;
625 TUD
.Diagnostics
.insert(TUD
.Diagnostics
.end(), Diag
);
628 yaml::Output
YAML(OS
);
633 getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers
) {
634 NamesAndOptions Result
;
635 ClangTidyOptions Opts
;
637 clang::tidy::ClangTidyContext
Context(
638 std::make_unique
<DefaultOptionsProvider
>(ClangTidyGlobalOptions(), Opts
),
639 AllowEnablingAnalyzerAlphaCheckers
);
640 ClangTidyCheckFactories Factories
;
641 for (const ClangTidyModuleRegistry::entry
&Module
:
642 ClangTidyModuleRegistry::entries()) {
643 Module
.instantiate()->addCheckFactories(Factories
);
646 for (const auto &Factory
: Factories
)
647 Result
.Names
.insert(Factory
.getKey());
649 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
650 SmallString
<64> Buffer(AnalyzerCheckNamePrefix
);
651 size_t DefSize
= Buffer
.size();
652 for (const auto &AnalyzerCheck
: AnalyzerOptions::getRegisteredCheckers(
653 AllowEnablingAnalyzerAlphaCheckers
)) {
654 Buffer
.truncate(DefSize
);
655 Buffer
.append(AnalyzerCheck
);
656 Result
.Names
.insert(Buffer
);
658 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
660 Context
.setOptionsCollector(&Result
.Options
);
661 for (const auto &Factory
: Factories
) {
662 Factory
.getValue()(Factory
.getKey(), &Context
);
667 } // namespace clang::tidy