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 auto &Entry
= FileReplacements
[R
.getFilePath()];
151 Replacements
&Replacements
= Entry
.Replaces
;
152 llvm::Error Err
= Replacements
.add(R
);
154 // FIXME: Implement better conflict handling.
155 llvm::errs() << "Trying to resolve conflict: "
156 << llvm::toString(std::move(Err
)) << "\n";
158 Replacements
.getShiftedCodePosition(R
.getOffset());
159 unsigned NewLength
= Replacements
.getShiftedCodePosition(
160 R
.getOffset() + R
.getLength()) -
162 if (NewLength
== R
.getLength()) {
163 R
= Replacement(R
.getFilePath(), NewOffset
, NewLength
,
164 R
.getReplacementText());
165 Replacements
= Replacements
.merge(tooling::Replacements(R
));
170 << "Can't resolve conflict, skipping the replacement.\n";
176 FixLoc
= getLocation(FixAbsoluteFilePath
, Repl
.getOffset());
177 FixLocations
.push_back(std::make_pair(FixLoc
, CanBeApplied
));
178 Entry
.BuildDir
= Error
.BuildDirectory
;
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 auto &VFS
= Files
.getVirtualFileSystem();
195 auto OriginalCWD
= VFS
.getCurrentWorkingDirectory();
196 bool AnyNotWritten
= false;
198 for (const auto &FileAndReplacements
: FileReplacements
) {
199 Rewriter
Rewrite(SourceMgr
, LangOpts
);
200 StringRef File
= FileAndReplacements
.first();
201 VFS
.setCurrentWorkingDirectory(FileAndReplacements
.second
.BuildDir
);
202 llvm::ErrorOr
<std::unique_ptr
<MemoryBuffer
>> Buffer
=
203 SourceMgr
.getFileManager().getBufferForFile(File
);
205 llvm::errs() << "Can't get buffer for file " << File
<< ": "
206 << Buffer
.getError().message() << "\n";
207 // FIXME: Maybe don't apply fixes for other files as well.
210 StringRef Code
= Buffer
.get()->getBuffer();
211 auto Style
= format::getStyle(
212 *Context
.getOptionsForFile(File
).FormatStyle
, File
, "none");
214 llvm::errs() << llvm::toString(Style
.takeError()) << "\n";
217 llvm::Expected
<tooling::Replacements
> Replacements
=
218 format::cleanupAroundReplacements(
219 Code
, FileAndReplacements
.second
.Replaces
, *Style
);
221 llvm::errs() << llvm::toString(Replacements
.takeError()) << "\n";
224 if (llvm::Expected
<tooling::Replacements
> FormattedReplacements
=
225 format::formatReplacements(Code
, *Replacements
, *Style
)) {
226 Replacements
= std::move(FormattedReplacements
);
228 llvm_unreachable("!Replacements");
230 llvm::errs() << llvm::toString(FormattedReplacements
.takeError())
231 << ". Skipping formatting.\n";
233 if (!tooling::applyAllReplacements(Replacements
.get(), Rewrite
)) {
234 llvm::errs() << "Can't apply replacements for file " << File
<< "\n";
236 AnyNotWritten
|= Rewrite
.overwriteChangedFiles();
240 llvm::errs() << "clang-tidy failed to apply suggested fixes.\n";
242 llvm::errs() << "clang-tidy applied " << AppliedFixes
<< " of "
243 << TotalFixes
<< " suggested fixes.\n";
247 VFS
.setCurrentWorkingDirectory(*OriginalCWD
);
251 unsigned getWarningsAsErrorsCount() const { return WarningsAsErrors
; }
254 SourceLocation
getLocation(StringRef FilePath
, unsigned Offset
) {
255 if (FilePath
.empty())
258 auto File
= SourceMgr
.getFileManager().getOptionalFileRef(FilePath
);
262 FileID ID
= SourceMgr
.getOrCreateFileID(*File
, SrcMgr::C_User
);
263 return SourceMgr
.getLocForStartOfFile(ID
).getLocWithOffset(Offset
);
266 void reportFix(const DiagnosticBuilder
&Diag
,
267 const llvm::StringMap
<Replacements
> &Fix
) {
268 for (const auto &FileAndReplacements
: Fix
) {
269 for (const auto &Repl
: FileAndReplacements
.second
) {
270 if (!Repl
.isApplicable())
273 FBR
.FilePath
= Repl
.getFilePath().str();
274 FBR
.FileOffset
= Repl
.getOffset();
275 FBR
.Length
= Repl
.getLength();
277 Diag
<< FixItHint::CreateReplacement(getRange(FBR
),
278 Repl
.getReplacementText());
283 void reportNote(const tooling::DiagnosticMessage
&Message
) {
284 SourceLocation Loc
= getLocation(Message
.FilePath
, Message
.FileOffset
);
286 Diags
.Report(Loc
, Diags
.getCustomDiagID(DiagnosticsEngine::Note
, "%0"))
288 for (const FileByteRange
&FBR
: Message
.Ranges
)
289 Diag
<< getRange(FBR
);
290 reportFix(Diag
, Message
.Fix
);
293 CharSourceRange
getRange(const FileByteRange
&Range
) {
294 SmallString
<128> AbsoluteFilePath
{Range
.FilePath
};
295 Files
.makeAbsolutePath(AbsoluteFilePath
);
296 SourceLocation BeginLoc
= getLocation(AbsoluteFilePath
, Range
.FileOffset
);
297 SourceLocation EndLoc
= BeginLoc
.getLocWithOffset(Range
.Length
);
298 // Retrieve the source range for applicable highlights and fixes. Macro
299 // definition on the command line have locations in a virtual buffer and
300 // don't have valid file paths and are therefore not applicable.
301 return CharSourceRange::getCharRange(BeginLoc
, EndLoc
);
304 struct ReplacementsWithBuildDir
{
306 Replacements Replaces
;
310 LangOptions LangOpts
; // FIXME: use langopts from each original file
311 IntrusiveRefCntPtr
<DiagnosticOptions
> DiagOpts
;
312 DiagnosticConsumer
*DiagPrinter
;
313 DiagnosticsEngine Diags
;
314 SourceManager SourceMgr
;
315 llvm::StringMap
<ReplacementsWithBuildDir
> FileReplacements
;
316 ClangTidyContext
&Context
;
317 FixBehaviour ApplyFixes
;
318 unsigned TotalFixes
= 0U;
319 unsigned AppliedFixes
= 0U;
320 unsigned WarningsAsErrors
= 0U;
323 class ClangTidyASTConsumer
: public MultiplexConsumer
{
325 ClangTidyASTConsumer(std::vector
<std::unique_ptr
<ASTConsumer
>> Consumers
,
326 std::unique_ptr
<ClangTidyProfiling
> Profiling
,
327 std::unique_ptr
<ast_matchers::MatchFinder
> Finder
,
328 std::vector
<std::unique_ptr
<ClangTidyCheck
>> Checks
)
329 : MultiplexConsumer(std::move(Consumers
)),
330 Profiling(std::move(Profiling
)), Finder(std::move(Finder
)),
331 Checks(std::move(Checks
)) {}
334 // Destructor order matters! Profiling must be destructed last.
335 // Or at least after Finder.
336 std::unique_ptr
<ClangTidyProfiling
> Profiling
;
337 std::unique_ptr
<ast_matchers::MatchFinder
> Finder
;
338 std::vector
<std::unique_ptr
<ClangTidyCheck
>> Checks
;
339 void anchor() override
{};
344 ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
345 ClangTidyContext
&Context
,
346 IntrusiveRefCntPtr
<llvm::vfs::OverlayFileSystem
> OverlayFS
)
347 : Context(Context
), OverlayFS(std::move(OverlayFS
)),
348 CheckFactories(new ClangTidyCheckFactories
) {
349 for (ClangTidyModuleRegistry::entry E
: ClangTidyModuleRegistry::entries()) {
350 std::unique_ptr
<ClangTidyModule
> Module
= E
.instantiate();
351 Module
->addCheckFactories(*CheckFactories
);
355 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
357 setStaticAnalyzerCheckerOpts(const ClangTidyOptions
&Opts
,
358 clang::AnalyzerOptions
&AnalyzerOptions
) {
359 StringRef
AnalyzerPrefix(AnalyzerCheckNamePrefix
);
360 for (const auto &Opt
: Opts
.CheckOptions
) {
361 StringRef
OptName(Opt
.getKey());
362 if (!OptName
.consume_front(AnalyzerPrefix
))
364 // Analyzer options are always local options so we can ignore priority.
365 AnalyzerOptions
.Config
[OptName
] = Opt
.getValue().Value
;
369 using CheckersList
= std::vector
<std::pair
<std::string
, bool>>;
371 static CheckersList
getAnalyzerCheckersAndPackages(ClangTidyContext
&Context
,
372 bool IncludeExperimental
) {
375 const auto &RegisteredCheckers
=
376 AnalyzerOptions::getRegisteredCheckers(IncludeExperimental
);
377 const bool AnalyzerChecksEnabled
=
378 llvm::any_of(RegisteredCheckers
, [&](StringRef CheckName
) -> bool {
379 return Context
.isCheckEnabled(
380 (AnalyzerCheckNamePrefix
+ CheckName
).str());
383 if (!AnalyzerChecksEnabled
)
386 // List all static analyzer checkers that our filter enables.
388 // Always add all core checkers if any other static analyzer check is enabled.
389 // This is currently necessary, as other path sensitive checks rely on the
391 for (StringRef CheckName
: RegisteredCheckers
) {
392 std::string
ClangTidyCheckName((AnalyzerCheckNamePrefix
+ CheckName
).str());
394 if (CheckName
.starts_with("core") ||
395 Context
.isCheckEnabled(ClangTidyCheckName
)) {
396 List
.emplace_back(std::string(CheckName
), true);
401 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
403 std::unique_ptr
<clang::ASTConsumer
>
404 ClangTidyASTConsumerFactory::createASTConsumer(
405 clang::CompilerInstance
&Compiler
, StringRef File
) {
406 // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
408 SourceManager
*SM
= &Compiler
.getSourceManager();
409 Context
.setSourceManager(SM
);
410 Context
.setCurrentFile(File
);
411 Context
.setASTContext(&Compiler
.getASTContext());
413 auto WorkingDir
= Compiler
.getSourceManager()
415 .getVirtualFileSystem()
416 .getCurrentWorkingDirectory();
418 Context
.setCurrentBuildDirectory(WorkingDir
.get());
420 std::vector
<std::unique_ptr
<ClangTidyCheck
>> Checks
=
421 CheckFactories
->createChecksForLanguage(&Context
);
423 ast_matchers::MatchFinder::MatchFinderOptions FinderOptions
;
425 std::unique_ptr
<ClangTidyProfiling
> Profiling
;
426 if (Context
.getEnableProfiling()) {
427 Profiling
= std::make_unique
<ClangTidyProfiling
>(
428 Context
.getProfileStorageParams());
429 FinderOptions
.CheckProfiling
.emplace(Profiling
->Records
);
432 std::unique_ptr
<ast_matchers::MatchFinder
> Finder(
433 new ast_matchers::MatchFinder(std::move(FinderOptions
)));
435 Preprocessor
*PP
= &Compiler
.getPreprocessor();
436 Preprocessor
*ModuleExpanderPP
= PP
;
438 if (Context
.canEnableModuleHeadersParsing() &&
439 Context
.getLangOpts().Modules
&& OverlayFS
!= nullptr) {
440 auto ModuleExpander
= std::make_unique
<ExpandModularHeadersPPCallbacks
>(
441 &Compiler
, OverlayFS
);
442 ModuleExpanderPP
= ModuleExpander
->getPreprocessor();
443 PP
->addPPCallbacks(std::move(ModuleExpander
));
446 for (auto &Check
: Checks
) {
447 Check
->registerMatchers(&*Finder
);
448 Check
->registerPPCallbacks(*SM
, PP
, ModuleExpanderPP
);
451 std::vector
<std::unique_ptr
<ASTConsumer
>> Consumers
;
453 Consumers
.push_back(Finder
->newASTConsumer());
455 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
456 AnalyzerOptions
&AnalyzerOptions
= Compiler
.getAnalyzerOpts();
457 AnalyzerOptions
.CheckersAndPackages
= getAnalyzerCheckersAndPackages(
458 Context
, Context
.canEnableAnalyzerAlphaCheckers());
459 if (!AnalyzerOptions
.CheckersAndPackages
.empty()) {
460 setStaticAnalyzerCheckerOpts(Context
.getOptions(), AnalyzerOptions
);
461 AnalyzerOptions
.AnalysisDiagOpt
= PD_NONE
;
462 std::unique_ptr
<ento::AnalysisASTConsumer
> AnalysisConsumer
=
463 ento::CreateAnalysisConsumer(Compiler
);
464 AnalysisConsumer
->AddDiagnosticConsumer(
465 new AnalyzerDiagnosticConsumer(Context
));
466 Consumers
.push_back(std::move(AnalysisConsumer
));
468 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
469 return std::make_unique
<ClangTidyASTConsumer
>(
470 std::move(Consumers
), std::move(Profiling
), std::move(Finder
),
474 std::vector
<std::string
> ClangTidyASTConsumerFactory::getCheckNames() {
475 std::vector
<std::string
> CheckNames
;
476 for (const auto &CheckFactory
: *CheckFactories
) {
477 if (Context
.isCheckEnabled(CheckFactory
.getKey()))
478 CheckNames
.emplace_back(CheckFactory
.getKey());
481 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
482 for (const auto &AnalyzerCheck
: getAnalyzerCheckersAndPackages(
483 Context
, Context
.canEnableAnalyzerAlphaCheckers()))
484 CheckNames
.push_back(AnalyzerCheckNamePrefix
+ AnalyzerCheck
.first
);
485 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
487 llvm::sort(CheckNames
);
491 ClangTidyOptions::OptionMap
ClangTidyASTConsumerFactory::getCheckOptions() {
492 ClangTidyOptions::OptionMap Options
;
493 std::vector
<std::unique_ptr
<ClangTidyCheck
>> Checks
=
494 CheckFactories
->createChecks(&Context
);
495 for (const auto &Check
: Checks
)
496 Check
->storeOptions(Options
);
500 std::vector
<std::string
>
501 getCheckNames(const ClangTidyOptions
&Options
,
502 bool AllowEnablingAnalyzerAlphaCheckers
) {
503 clang::tidy::ClangTidyContext
Context(
504 std::make_unique
<DefaultOptionsProvider
>(ClangTidyGlobalOptions(),
506 AllowEnablingAnalyzerAlphaCheckers
);
507 ClangTidyASTConsumerFactory
Factory(Context
);
508 return Factory
.getCheckNames();
511 ClangTidyOptions::OptionMap
512 getCheckOptions(const ClangTidyOptions
&Options
,
513 bool AllowEnablingAnalyzerAlphaCheckers
) {
514 clang::tidy::ClangTidyContext
Context(
515 std::make_unique
<DefaultOptionsProvider
>(ClangTidyGlobalOptions(),
517 AllowEnablingAnalyzerAlphaCheckers
);
518 ClangTidyDiagnosticConsumer
DiagConsumer(Context
);
519 DiagnosticsEngine
DE(llvm::makeIntrusiveRefCnt
<DiagnosticIDs
>(),
520 llvm::makeIntrusiveRefCnt
<DiagnosticOptions
>(),
521 &DiagConsumer
, /*ShouldOwnClient=*/false);
522 Context
.setDiagnosticsEngine(&DE
);
523 ClangTidyASTConsumerFactory
Factory(Context
);
524 return Factory
.getCheckOptions();
527 std::vector
<ClangTidyError
>
528 runClangTidy(clang::tidy::ClangTidyContext
&Context
,
529 const CompilationDatabase
&Compilations
,
530 ArrayRef
<std::string
> InputFiles
,
531 llvm::IntrusiveRefCntPtr
<llvm::vfs::OverlayFileSystem
> BaseFS
,
532 bool ApplyAnyFix
, bool EnableCheckProfile
,
533 llvm::StringRef StoreCheckProfile
) {
534 ClangTool
Tool(Compilations
, InputFiles
,
535 std::make_shared
<PCHContainerOperations
>(), BaseFS
);
537 // Add extra arguments passed by the clang-tidy command-line.
538 ArgumentsAdjuster PerFileExtraArgumentsInserter
=
539 [&Context
](const CommandLineArguments
&Args
, StringRef Filename
) {
540 ClangTidyOptions Opts
= Context
.getOptionsForFile(Filename
);
541 CommandLineArguments AdjustedArgs
= Args
;
542 if (Opts
.ExtraArgsBefore
) {
543 auto I
= AdjustedArgs
.begin();
544 if (I
!= AdjustedArgs
.end() && !StringRef(*I
).starts_with("-"))
545 ++I
; // Skip compiler binary name, if it is there.
546 AdjustedArgs
.insert(I
, Opts
.ExtraArgsBefore
->begin(),
547 Opts
.ExtraArgsBefore
->end());
550 AdjustedArgs
.insert(AdjustedArgs
.end(), Opts
.ExtraArgs
->begin(),
551 Opts
.ExtraArgs
->end());
555 Tool
.appendArgumentsAdjuster(PerFileExtraArgumentsInserter
);
556 Tool
.appendArgumentsAdjuster(getStripPluginsAdjuster());
557 Context
.setEnableProfiling(EnableCheckProfile
);
558 Context
.setProfileStoragePrefix(StoreCheckProfile
);
560 ClangTidyDiagnosticConsumer
DiagConsumer(Context
, nullptr, true, ApplyAnyFix
);
561 DiagnosticsEngine
DE(new DiagnosticIDs(), new DiagnosticOptions(),
562 &DiagConsumer
, /*ShouldOwnClient=*/false);
563 Context
.setDiagnosticsEngine(&DE
);
564 Tool
.setDiagnosticConsumer(&DiagConsumer
);
566 class ActionFactory
: public FrontendActionFactory
{
568 ActionFactory(ClangTidyContext
&Context
,
569 IntrusiveRefCntPtr
<llvm::vfs::OverlayFileSystem
> BaseFS
)
570 : ConsumerFactory(Context
, std::move(BaseFS
)) {}
571 std::unique_ptr
<FrontendAction
> create() override
{
572 return std::make_unique
<Action
>(&ConsumerFactory
);
575 bool runInvocation(std::shared_ptr
<CompilerInvocation
> Invocation
,
577 std::shared_ptr
<PCHContainerOperations
> PCHContainerOps
,
578 DiagnosticConsumer
*DiagConsumer
) override
{
579 // Explicitly ask to define __clang_analyzer__ macro.
580 Invocation
->getPreprocessorOpts().SetUpStaticAnalyzer
= true;
581 return FrontendActionFactory::runInvocation(
582 Invocation
, Files
, PCHContainerOps
, DiagConsumer
);
586 class Action
: public ASTFrontendAction
{
588 Action(ClangTidyASTConsumerFactory
*Factory
) : Factory(Factory
) {}
589 std::unique_ptr
<ASTConsumer
> CreateASTConsumer(CompilerInstance
&Compiler
,
590 StringRef File
) override
{
591 return Factory
->createASTConsumer(Compiler
, File
);
595 ClangTidyASTConsumerFactory
*Factory
;
598 ClangTidyASTConsumerFactory ConsumerFactory
;
601 ActionFactory
Factory(Context
, std::move(BaseFS
));
603 return DiagConsumer
.take();
606 void handleErrors(llvm::ArrayRef
<ClangTidyError
> Errors
,
607 ClangTidyContext
&Context
, FixBehaviour Fix
,
608 unsigned &WarningsAsErrorsCount
,
609 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> BaseFS
) {
610 ErrorReporter
Reporter(Context
, Fix
, std::move(BaseFS
));
611 llvm::vfs::FileSystem
&FileSystem
=
612 Reporter
.getSourceManager().getFileManager().getVirtualFileSystem();
613 auto InitialWorkingDir
= FileSystem
.getCurrentWorkingDirectory();
614 if (!InitialWorkingDir
)
615 llvm::report_fatal_error("Cannot get current working path.");
617 for (const ClangTidyError
&Error
: Errors
) {
618 if (!Error
.BuildDirectory
.empty()) {
619 // By default, the working directory of file system is the current
620 // clang-tidy running directory.
622 // Change the directory to the one used during the analysis.
623 FileSystem
.setCurrentWorkingDirectory(Error
.BuildDirectory
);
625 Reporter
.reportDiagnostic(Error
);
626 // Return to the initial directory to correctly resolve next Error.
627 FileSystem
.setCurrentWorkingDirectory(InitialWorkingDir
.get());
630 WarningsAsErrorsCount
+= Reporter
.getWarningsAsErrorsCount();
633 void exportReplacements(const llvm::StringRef MainFilePath
,
634 const std::vector
<ClangTidyError
> &Errors
,
636 TranslationUnitDiagnostics TUD
;
637 TUD
.MainSourceFile
= std::string(MainFilePath
);
638 for (const auto &Error
: Errors
) {
639 tooling::Diagnostic Diag
= Error
;
640 if (Error
.IsWarningAsError
)
641 Diag
.DiagLevel
= tooling::Diagnostic::Error
;
642 TUD
.Diagnostics
.insert(TUD
.Diagnostics
.end(), Diag
);
645 yaml::Output
YAML(OS
);
650 getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers
) {
651 NamesAndOptions Result
;
652 ClangTidyOptions Opts
;
654 clang::tidy::ClangTidyContext
Context(
655 std::make_unique
<DefaultOptionsProvider
>(ClangTidyGlobalOptions(), Opts
),
656 AllowEnablingAnalyzerAlphaCheckers
);
657 ClangTidyCheckFactories Factories
;
658 for (const ClangTidyModuleRegistry::entry
&Module
:
659 ClangTidyModuleRegistry::entries()) {
660 Module
.instantiate()->addCheckFactories(Factories
);
663 for (const auto &Factory
: Factories
)
664 Result
.Names
.insert(Factory
.getKey());
666 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
667 SmallString
<64> Buffer(AnalyzerCheckNamePrefix
);
668 size_t DefSize
= Buffer
.size();
669 for (const auto &AnalyzerCheck
: AnalyzerOptions::getRegisteredCheckers(
670 AllowEnablingAnalyzerAlphaCheckers
)) {
671 Buffer
.truncate(DefSize
);
672 Buffer
.append(AnalyzerCheck
);
673 Result
.Names
.insert(Buffer
);
675 for (std::string OptionName
: {
676 #define GET_CHECKER_OPTIONS
677 #define CHECKER_OPTION(TYPE, CHECKER, OPTION_NAME, DESCRIPTION, DEFAULT, \
679 Twine(AnalyzerCheckNamePrefix).concat(CHECKER ":" OPTION_NAME).str(),
681 #include "clang/StaticAnalyzer/Checkers/Checkers.inc"
682 #undef CHECKER_OPTION
683 #undef GET_CHECKER_OPTIONS
685 Result
.Options
.insert(OptionName
);
687 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
689 Context
.setOptionsCollector(&Result
.Options
);
690 for (const auto &Factory
: Factories
) {
691 Factory
.getValue()(Factory
.getKey(), &Context
);
696 } // namespace clang::tidy