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
52 using namespace clang::driver;
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(
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,
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; }
118 const tooling::DiagnosticMessage &
Message =
Error.Message;
122 SmallVector<std::pair<SourceLocation, bool>, 4> FixLocations;
124 auto Level =
static_cast<DiagnosticsEngine::Level
>(
Error.DiagLevel);
126 if (!
Error.EnabledDiagnosticAliases.empty())
128 if (
Error.IsWarningAsError) {
129 Name +=
",-warnings-as-errors";
133 auto Diag =
Diags.Report(
Loc,
Diags.getCustomDiagID(Level,
"%0 [%1]"))
135 for (
const FileByteRange &FBR :
Error.Message.Ranges)
136 Diag << getRange(FBR);
138 const llvm::StringMap<Replacements> *ChosenFix;
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);
156 llvm::errs() <<
"Trying to resolve conflict: "
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) {
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";
205 StringRef
Code = Buffer.get()->getBuffer();
206 auto Style = format::getStyle(
207 *Context.getOptionsForFile(File).FormatStyle, File,
"none");
212 llvm::Expected<tooling::Replacements> Replacements =
213 format::cleanupAroundReplacements(
Code, FileAndReplacements.second,
219 if (llvm::Expected<tooling::Replacements> FormattedReplacements =
220 format::formatReplacements(
Code, *Replacements, *Style)) {
221 Replacements = std::move(FormattedReplacements);
223 llvm_unreachable(
"!Replacements");
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";
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);
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) {
276 Diags.Report(
Loc,
Diags.getCustomDiagID(DiagnosticsEngine::Note,
"%0"))
278 for (
const FileByteRange &FBR :
Message.Ranges)
279 Diag << getRange(FBR);
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);
291 return CharSourceRange::getCharRange(BeginLoc, EndLoc);
296 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
298 DiagnosticsEngine
Diags;
300 llvm::StringMap<Replacements> FileReplacements;
301 ClangTidyContext &Context;
304 unsigned AppliedFixes;
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)
315 Profiling(std::move(Profiling)), Finder(std::move(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(
330 IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS)
331 : Context(Context), OverlayFS(std::move(OverlayFS)),
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
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))
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)
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>
389 clang::CompilerInstance &Compiler, StringRef File) {
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->createChecksForLanguage(&Context);
407 ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;
409 std::unique_ptr<ClangTidyProfiling> Profiling;
410 if (Context.getEnableProfiling()) {
411 Profiling = std::make_unique<ClangTidyProfiling>(
412 Context.getProfileStorageParams());
413 FinderOptions.CheckProfiling.emplace(Profiling->Records);
416 std::unique_ptr<ast_matchers::MatchFinder> Finder(
417 new ast_matchers::MatchFinder(std::move(FinderOptions)));
419 Preprocessor *
PP = &Compiler.getPreprocessor();
420 Preprocessor *ModuleExpanderPP =
PP;
422 if (Context.getLangOpts().Modules && OverlayFS !=
nullptr) {
423 auto ModuleExpander = std::make_unique<ExpandModularHeadersPPCallbacks>(
424 &Compiler, OverlayFS);
425 ModuleExpanderPP = ModuleExpander->getPreprocessor();
426 PP->addPPCallbacks(std::move(ModuleExpander));
429 for (
auto &Check :
Checks) {
430 Check->registerMatchers(&*Finder);
431 Check->registerPPCallbacks(*SM,
PP, ModuleExpanderPP);
434 std::vector<std::unique_ptr<ASTConsumer>> Consumers;
436 Consumers.push_back(Finder->newASTConsumer());
438 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
439 AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts();
440 AnalyzerOptions->CheckersAndPackages = getAnalyzerCheckersAndPackages(
441 Context, Context.canEnableAnalyzerAlphaCheckers());
442 if (!AnalyzerOptions->CheckersAndPackages.empty()) {
443 setStaticAnalyzerCheckerOpts(Context.getOptions(), *AnalyzerOptions);
444 AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel;
445 AnalyzerOptions->AnalysisDiagOpt = PD_NONE;
446 AnalyzerOptions->AnalyzeNestedBlocks =
true;
447 AnalyzerOptions->eagerlyAssumeBinOpBifurcation =
true;
448 std::unique_ptr<ento::AnalysisASTConsumer> AnalysisConsumer =
449 ento::CreateAnalysisConsumer(Compiler);
450 AnalysisConsumer->AddDiagnosticConsumer(
451 new AnalyzerDiagnosticConsumer(Context));
452 Consumers.push_back(std::move(AnalysisConsumer));
454 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
455 return std::make_unique<ClangTidyASTConsumer>(
456 std::move(Consumers), std::move(Profiling), std::move(Finder),
461 std::vector<std::string> CheckNames;
462 for (
const auto &CheckFactory : *CheckFactories) {
463 if (Context.isCheckEnabled(CheckFactory.getKey()))
464 CheckNames.emplace_back(CheckFactory.getKey());
467 #if CLANG_TIDY_ENABLE_STATIC_ANALYZER
468 for (
const auto &AnalyzerCheck : getAnalyzerCheckersAndPackages(
469 Context, Context.canEnableAnalyzerAlphaCheckers()))
470 CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first);
471 #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
473 llvm::sort(CheckNames);
479 std::vector<std::unique_ptr<ClangTidyCheck>>
Checks =
480 CheckFactories->createChecks(&Context);
481 for (
const auto &Check :
Checks)
482 Check->storeOptions(Options);
486 std::vector<std::string>
508 std::vector<ClangTidyError>
510 const CompilationDatabase &Compilations,
511 ArrayRef<std::string> InputFiles,
512 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS,
515 ClangTool Tool(Compilations, InputFiles,
516 std::make_shared<PCHContainerOperations>(), BaseFS);
519 ArgumentsAdjuster PerFileExtraArgumentsInserter =
520 [&Context](
const CommandLineArguments &
Args, StringRef
Filename) {
522 CommandLineArguments AdjustedArgs =
Args;
523 if (Opts.ExtraArgsBefore) {
524 auto I = AdjustedArgs.begin();
525 if (I != AdjustedArgs.end() && !StringRef(*I).startswith(
"-"))
527 AdjustedArgs.insert(I, Opts.ExtraArgsBefore->begin(),
528 Opts.ExtraArgsBefore->end());
531 AdjustedArgs.insert(AdjustedArgs.end(), Opts.ExtraArgs->begin(),
532 Opts.ExtraArgs->end());
536 Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter);
537 Tool.appendArgumentsAdjuster(getStripPluginsAdjuster());
542 DiagnosticsEngine DE(
new DiagnosticIDs(),
new DiagnosticOptions(),
543 &DiagConsumer,
false);
544 Context.setDiagnosticsEngine(&DE);
545 Tool.setDiagnosticConsumer(&DiagConsumer);
547 class ActionFactory :
public FrontendActionFactory {
550 IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS)
551 : ConsumerFactory(Context, std::move(BaseFS)) {}
552 std::unique_ptr<FrontendAction> create()
override {
553 return std::make_unique<Action>(&ConsumerFactory);
556 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
558 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
561 Invocation->getPreprocessorOpts().SetUpStaticAnalyzer =
true;
562 return FrontendActionFactory::runInvocation(
563 Invocation,
Files, PCHContainerOps, DiagConsumer);
567 class Action :
public ASTFrontendAction {
570 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
571 StringRef File)
override {
582 ActionFactory Factory(Context, std::move(BaseFS));
584 return DiagConsumer.
take();
589 unsigned &WarningsAsErrorsCount,
590 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS) {
591 ErrorReporter Reporter(Context,
Fix, std::move(BaseFS));
592 llvm::vfs::FileSystem &FileSystem =
593 Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
594 auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();
595 if (!InitialWorkingDir)
596 llvm::report_fatal_error(
"Cannot get current working path.");
599 if (!Error.BuildDirectory.empty()) {
604 FileSystem.setCurrentWorkingDirectory(Error.BuildDirectory);
606 Reporter.reportDiagnostic(Error);
608 FileSystem.setCurrentWorkingDirectory(InitialWorkingDir.get());
611 WarningsAsErrorsCount += Reporter.getWarningsAsErrorsCount();
615 const std::vector<ClangTidyError> &Errors,
617 TranslationUnitDiagnostics TUD;
618 TUD.MainSourceFile = std::string(MainFilePath);
619 for (
const auto &Error : Errors) {
621 TUD.Diagnostics.insert(TUD.Diagnostics.end(), Diag);