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 "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/Preprocessor.h"
33#include "clang/Lex/PreprocessorOptions.h"
34#include "clang/Rewrite/Frontend/FixItRewriter.h"
35#include "clang/Tooling/Core/Diagnostic.h"
36#include "clang/Tooling/DiagnosticsYaml.h"
37#include "clang/Tooling/Refactoring.h"
38#include "clang/Tooling/Tooling.h"
39#include "llvm/Support/Process.h"
40#include <utility>
41
42#if CLANG_TIDY_ENABLE_STATIC_ANALYZER
43#include "clang/Analysis/PathDiagnostic.h"
44#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
45#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
46
47using namespace clang::ast_matchers;
48using namespace clang::driver;
49using namespace clang::tooling;
50using namespace llvm;
51
52LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry)
53
54namespace clang::tidy {
55
56namespace {
57#if CLANG_TIDY_ENABLE_STATIC_ANALYZER
58#define ANALYZER_CHECK_NAME_PREFIX "clang-analyzer-"
59static constexpr llvm::StringLiteral AnalyzerCheckNamePrefix =
60 ANALYZER_CHECK_NAME_PREFIX;
61
62class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer {
63public:
64 AnalyzerDiagnosticConsumer(ClangTidyContext &Context) : Context(Context) {}
65
66 void FlushDiagnosticsImpl(std::vector<const ento::PathDiagnostic *> &Diags,
67 FilesMade *FilesMade) override {
68 for (const ento::PathDiagnostic *PD : Diags) {
69 SmallString<64> CheckName(AnalyzerCheckNamePrefix);
70 CheckName += PD->getCheckerName();
71 Context.diag(CheckName, Loc: PD->getLocation().asLocation(),
72 Description: PD->getShortDescription())
73 << PD->path.back()->getRanges();
74
75 for (const auto &DiagPiece :
76 PD->path.flatten(/*ShouldFlattenMacros=*/true)) {
77 Context.diag(CheckName, Loc: DiagPiece->getLocation().asLocation(),
78 Description: DiagPiece->getString(), Level: DiagnosticIDs::Note)
79 << DiagPiece->getRanges();
80 }
81 }
82 }
83
84 StringRef getName() const override { return "ClangTidyDiags"; }
85 bool supportsLogicalOpControlFlow() const override { return true; }
86 bool supportsCrossFileDiagnostics() const override { return true; }
87
88private:
89 ClangTidyContext &Context;
90};
91#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
92
93class ErrorReporter {
94public:
95 ErrorReporter(ClangTidyContext &Context, FixBehaviour ApplyFixes,
96 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS)
97 : Files(FileSystemOptions(), std::move(BaseFS)),
98 DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), DiagOpts)),
99 Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), DiagOpts,
100 DiagPrinter),
101 SourceMgr(Diags, Files), Context(Context), ApplyFixes(ApplyFixes) {
102 DiagOpts.ShowColors = Context.getOptions().UseColor.value_or(
103 u: llvm::sys::Process::StandardOutHasColors());
104 DiagPrinter->BeginSourceFile(LangOpts);
105 if (DiagOpts.ShowColors && !llvm::sys::Process::StandardOutIsDisplayed()) {
106 llvm::sys::Process::UseANSIEscapeCodes(enable: true);
107 }
108 }
109
110 SourceManager &getSourceManager() { return SourceMgr; }
111
112 void reportDiagnostic(const ClangTidyError &Error) {
113 const tooling::DiagnosticMessage &Message = Error.Message;
114 SourceLocation Loc = getLocation(FilePath: Message.FilePath, Offset: Message.FileOffset);
115 // Contains a pair for each attempted fix: location and whether the fix was
116 // applied successfully.
117 SmallVector<std::pair<SourceLocation, bool>, 4> FixLocations;
118 {
119 auto Level = static_cast<DiagnosticsEngine::Level>(Error.DiagLevel);
120 std::string Name = Error.DiagnosticName;
121 if (!Error.EnabledDiagnosticAliases.empty())
122 Name += "," + llvm::join(R: Error.EnabledDiagnosticAliases, Separator: ",");
123 if (Error.IsWarningAsError) {
124 Name += ",-warnings-as-errors";
125 Level = DiagnosticsEngine::Error;
126 WarningsAsErrors++;
127 }
128 auto Diag = Diags.Report(Loc, DiagID: Diags.getCustomDiagID(L: Level, FormatString: "%0 [%1]"))
129 << Message.Message << Name;
130 for (const FileByteRange &FBR : Error.Message.Ranges)
131 Diag << getRange(Range: FBR);
132 // FIXME: explore options to support interactive fix selection.
133 const llvm::StringMap<Replacements> *ChosenFix = nullptr;
134 if (ApplyFixes != FB_NoFix &&
135 (ChosenFix = getFixIt(Diagnostic: Error, AnyFix: ApplyFixes == FB_FixNotes))) {
136 for (const auto &FileAndReplacements : *ChosenFix) {
137 for (const auto &Repl : FileAndReplacements.second) {
138 ++TotalFixes;
139 bool CanBeApplied = false;
140 if (!Repl.isApplicable())
141 continue;
142 SourceLocation FixLoc;
143 SmallString<128> FixAbsoluteFilePath = Repl.getFilePath();
144 Files.makeAbsolutePath(Path&: FixAbsoluteFilePath);
145 tooling::Replacement R(FixAbsoluteFilePath, Repl.getOffset(),
146 Repl.getLength(), Repl.getReplacementText());
147 auto &Entry = FileReplacements[R.getFilePath()];
148 Replacements &Replacements = Entry.Replaces;
149 llvm::Error Err = Replacements.add(R);
150 if (Err) {
151 // FIXME: Implement better conflict handling.
152 llvm::errs() << "Trying to resolve conflict: "
153 << llvm::toString(E: std::move(Err)) << "\n";
154 unsigned NewOffset =
155 Replacements.getShiftedCodePosition(Position: R.getOffset());
156 unsigned NewLength = Replacements.getShiftedCodePosition(
157 Position: R.getOffset() + R.getLength()) -
158 NewOffset;
159 if (NewLength == R.getLength()) {
160 R = Replacement(R.getFilePath(), NewOffset, NewLength,
161 R.getReplacementText());
162 Replacements = Replacements.merge(Replaces: tooling::Replacements(R));
163 CanBeApplied = true;
164 ++AppliedFixes;
165 } else {
166 llvm::errs()
167 << "Can't resolve conflict, skipping the replacement.\n";
168 }
169 } else {
170 CanBeApplied = true;
171 ++AppliedFixes;
172 }
173 FixLoc = getLocation(FilePath: FixAbsoluteFilePath, Offset: Repl.getOffset());
174 FixLocations.push_back(Elt: std::make_pair(x&: FixLoc, y&: CanBeApplied));
175 Entry.BuildDir = Error.BuildDirectory;
176 }
177 }
178 }
179 reportFix(Diag, Fix: Error.Message.Fix);
180 }
181 for (auto Fix : FixLocations) {
182 Diags.Report(Loc: Fix.first, DiagID: Fix.second ? diag::note_fixit_applied
183 : diag::note_fixit_failed);
184 }
185 for (const auto &Note : Error.Notes)
186 reportNote(Message: Note);
187 }
188
189 void finish() {
190 if (TotalFixes > 0) {
191 auto &VFS = Files.getVirtualFileSystem();
192 auto OriginalCWD = VFS.getCurrentWorkingDirectory();
193 bool AnyNotWritten = false;
194
195 for (const auto &FileAndReplacements : FileReplacements) {
196 Rewriter Rewrite(SourceMgr, LangOpts);
197 StringRef File = FileAndReplacements.first();
198 VFS.setCurrentWorkingDirectory(FileAndReplacements.second.BuildDir);
199 llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
200 SourceMgr.getFileManager().getBufferForFile(Filename: File);
201 if (!Buffer) {
202 llvm::errs() << "Can't get buffer for file " << File << ": "
203 << Buffer.getError().message() << "\n";
204 // FIXME: Maybe don't apply fixes for other files as well.
205 continue;
206 }
207 StringRef Code = Buffer.get()->getBuffer();
208 auto Style = format::getStyle(
209 StyleName: *Context.getOptionsForFile(File).FormatStyle, FileName: File, FallbackStyle: "none");
210 if (!Style) {
211 llvm::errs() << llvm::toString(E: Style.takeError()) << "\n";
212 continue;
213 }
214 llvm::Expected<tooling::Replacements> Replacements =
215 format::cleanupAroundReplacements(
216 Code, Replaces: FileAndReplacements.second.Replaces, Style: *Style);
217 if (!Replacements) {
218 llvm::errs() << llvm::toString(E: Replacements.takeError()) << "\n";
219 continue;
220 }
221 if (llvm::Expected<tooling::Replacements> FormattedReplacements =
222 format::formatReplacements(Code, Replaces: *Replacements, Style: *Style)) {
223 Replacements = std::move(FormattedReplacements);
224 if (!Replacements)
225 llvm_unreachable("!Replacements");
226 } else {
227 llvm::errs() << llvm::toString(E: FormattedReplacements.takeError())
228 << ". Skipping formatting.\n";
229 }
230 if (!tooling::applyAllReplacements(Replaces: Replacements.get(), Rewrite)) {
231 llvm::errs() << "Can't apply replacements for file " << File << "\n";
232 }
233 AnyNotWritten |= Rewrite.overwriteChangedFiles();
234 }
235
236 if (AnyNotWritten) {
237 llvm::errs() << "clang-tidy failed to apply suggested fixes.\n";
238 } else {
239 llvm::errs() << "clang-tidy applied " << AppliedFixes << " of "
240 << TotalFixes << " suggested fixes.\n";
241 }
242
243 if (OriginalCWD)
244 VFS.setCurrentWorkingDirectory(*OriginalCWD);
245 }
246 }
247
248 unsigned getWarningsAsErrorsCount() const { return WarningsAsErrors; }
249
250private:
251 SourceLocation getLocation(StringRef FilePath, unsigned Offset) {
252 if (FilePath.empty())
253 return {};
254
255 auto File = SourceMgr.getFileManager().getOptionalFileRef(Filename: FilePath);
256 if (!File)
257 return {};
258
259 FileID ID = SourceMgr.getOrCreateFileID(SourceFile: *File, FileCharacter: SrcMgr::C_User);
260 return SourceMgr.getLocForStartOfFile(FID: ID).getLocWithOffset(Offset);
261 }
262
263 void reportFix(const DiagnosticBuilder &Diag,
264 const llvm::StringMap<Replacements> &Fix) {
265 for (const auto &FileAndReplacements : Fix) {
266 for (const auto &Repl : FileAndReplacements.second) {
267 if (!Repl.isApplicable())
268 continue;
269 FileByteRange FBR;
270 FBR.FilePath = Repl.getFilePath().str();
271 FBR.FileOffset = Repl.getOffset();
272 FBR.Length = Repl.getLength();
273
274 Diag << FixItHint::CreateReplacement(RemoveRange: getRange(Range: FBR),
275 Code: Repl.getReplacementText());
276 }
277 }
278 }
279
280 void reportNote(const tooling::DiagnosticMessage &Message) {
281 SourceLocation Loc = getLocation(FilePath: Message.FilePath, Offset: Message.FileOffset);
282 auto Diag =
283 Diags.Report(Loc, DiagID: Diags.getCustomDiagID(L: DiagnosticsEngine::Note, FormatString: "%0"))
284 << Message.Message;
285 for (const FileByteRange &FBR : Message.Ranges)
286 Diag << getRange(Range: FBR);
287 reportFix(Diag, Fix: Message.Fix);
288 }
289
290 CharSourceRange getRange(const FileByteRange &Range) {
291 SmallString<128> AbsoluteFilePath{Range.FilePath};
292 Files.makeAbsolutePath(Path&: AbsoluteFilePath);
293 SourceLocation BeginLoc = getLocation(FilePath: AbsoluteFilePath, Offset: Range.FileOffset);
294 SourceLocation EndLoc = BeginLoc.getLocWithOffset(Offset: Range.Length);
295 // Retrieve the source range for applicable highlights and fixes. Macro
296 // definition on the command line have locations in a virtual buffer and
297 // don't have valid file paths and are therefore not applicable.
298 return CharSourceRange::getCharRange(B: BeginLoc, E: EndLoc);
299 }
300
301 struct ReplacementsWithBuildDir {
302 StringRef BuildDir;
303 Replacements Replaces;
304 };
305
306 FileManager Files;
307 LangOptions LangOpts; // FIXME: use langopts from each original file
308 DiagnosticOptions DiagOpts;
309 DiagnosticConsumer *DiagPrinter;
310 DiagnosticsEngine Diags;
311 SourceManager SourceMgr;
312 llvm::StringMap<ReplacementsWithBuildDir> FileReplacements;
313 ClangTidyContext &Context;
314 FixBehaviour ApplyFixes;
315 unsigned TotalFixes = 0U;
316 unsigned AppliedFixes = 0U;
317 unsigned WarningsAsErrors = 0U;
318};
319
320class ClangTidyASTConsumer : public MultiplexConsumer {
321public:
322 ClangTidyASTConsumer(std::vector<std::unique_ptr<ASTConsumer>> Consumers,
323 std::unique_ptr<ClangTidyProfiling> Profiling,
324 std::unique_ptr<ast_matchers::MatchFinder> Finder,
325 std::vector<std::unique_ptr<ClangTidyCheck>> Checks)
326 : MultiplexConsumer(std::move(Consumers)),
327 Profiling(std::move(Profiling)), Finder(std::move(Finder)),
328 Checks(std::move(Checks)) {}
329
330private:
331 // Destructor order matters! Profiling must be destructed last.
332 // Or at least after Finder.
333 std::unique_ptr<ClangTidyProfiling> Profiling;
334 std::unique_ptr<ast_matchers::MatchFinder> Finder;
335 std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
336 void anchor() override {};
337};
338
339} // namespace
340
341ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
342 ClangTidyContext &Context,
343 IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS)
344 : Context(Context), OverlayFS(std::move(OverlayFS)),
345 CheckFactories(new ClangTidyCheckFactories) {
346 for (ClangTidyModuleRegistry::entry E : ClangTidyModuleRegistry::entries()) {
347 std::unique_ptr<ClangTidyModule> Module = E.instantiate();
348 Module->addCheckFactories(CheckFactories&: *CheckFactories);
349 }
350}
351
352#if CLANG_TIDY_ENABLE_STATIC_ANALYZER
353static void
354setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts,
355 clang::AnalyzerOptions &AnalyzerOptions) {
356 for (const auto &Opt : Opts.CheckOptions) {
357 StringRef OptName(Opt.getKey());
358 if (!OptName.consume_front(Prefix: AnalyzerCheckNamePrefix))
359 continue;
360 // Analyzer options are always local options so we can ignore priority.
361 AnalyzerOptions.Config[OptName] = Opt.getValue().Value;
362 }
363}
364
365using CheckersList = std::vector<std::pair<std::string, bool>>;
366
367static CheckersList getAnalyzerCheckersAndPackages(ClangTidyContext &Context,
368 bool IncludeExperimental) {
369 CheckersList List;
370
371 const auto &RegisteredCheckers =
372 AnalyzerOptions::getRegisteredCheckers(IncludeExperimental);
373 const bool AnalyzerChecksEnabled =
374 llvm::any_of(Range: RegisteredCheckers, P: [&](StringRef CheckName) -> bool {
375 return Context.isCheckEnabled(
376 CheckName: (AnalyzerCheckNamePrefix + CheckName).str());
377 });
378
379 if (!AnalyzerChecksEnabled)
380 return List;
381
382 // List all static analyzer checkers that our filter enables.
383 //
384 // Always add all core checkers if any other static analyzer check is enabled.
385 // This is currently necessary, as other path sensitive checks rely on the
386 // core checkers.
387 for (StringRef CheckName : RegisteredCheckers) {
388 std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
389
390 if (CheckName.starts_with(Prefix: "core") ||
391 Context.isCheckEnabled(CheckName: ClangTidyCheckName)) {
392 List.emplace_back(args: std::string(CheckName), args: true);
393 }
394 }
395 return List;
396}
397#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
398
399std::unique_ptr<clang::ASTConsumer>
400ClangTidyASTConsumerFactory::createASTConsumer(
401 clang::CompilerInstance &Compiler, StringRef File) {
402 // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
403 // modify Compiler.
404 SourceManager *SM = &Compiler.getSourceManager();
405 Context.setSourceManager(SM);
406 Context.setCurrentFile(File);
407 Context.setASTContext(&Compiler.getASTContext());
408
409 auto WorkingDir = Compiler.getSourceManager()
410 .getFileManager()
411 .getVirtualFileSystem()
412 .getCurrentWorkingDirectory();
413 if (WorkingDir)
414 Context.setCurrentBuildDirectory(WorkingDir.get());
415
416 std::vector<std::unique_ptr<ClangTidyCheck>> Checks =
417 CheckFactories->createChecksForLanguage(Context: &Context);
418
419 ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;
420
421 std::unique_ptr<ClangTidyProfiling> Profiling;
422 if (Context.getEnableProfiling()) {
423 Profiling =
424 std::make_unique<ClangTidyProfiling>(args: Context.getProfileStorageParams());
425 FinderOptions.CheckProfiling.emplace(args&: Profiling->Records);
426 }
427
428 std::unique_ptr<ast_matchers::MatchFinder> Finder(
429 new ast_matchers::MatchFinder(std::move(FinderOptions)));
430
431 Preprocessor *PP = &Compiler.getPreprocessor();
432 Preprocessor *ModuleExpanderPP = PP;
433
434 if (Context.canEnableModuleHeadersParsing() &&
435 Context.getLangOpts().Modules && OverlayFS != nullptr) {
436 auto ModuleExpander =
437 std::make_unique<ExpandModularHeadersPPCallbacks>(args: &Compiler, args&: OverlayFS);
438 ModuleExpanderPP = ModuleExpander->getPreprocessor();
439 PP->addPPCallbacks(C: std::move(ModuleExpander));
440 }
441
442 for (auto &Check : Checks) {
443 Check->registerMatchers(Finder: &*Finder);
444 Check->registerPPCallbacks(SM: *SM, PP, ModuleExpanderPP);
445 }
446
447 std::vector<std::unique_ptr<ASTConsumer>> Consumers;
448 if (!Checks.empty())
449 Consumers.push_back(x: Finder->newASTConsumer());
450
451#if CLANG_TIDY_ENABLE_STATIC_ANALYZER
452 AnalyzerOptions &AnalyzerOptions = Compiler.getAnalyzerOpts();
453 AnalyzerOptions.CheckersAndPackages = getAnalyzerCheckersAndPackages(
454 Context, IncludeExperimental: Context.canEnableAnalyzerAlphaCheckers());
455 if (!AnalyzerOptions.CheckersAndPackages.empty()) {
456 setStaticAnalyzerCheckerOpts(Opts: Context.getOptions(), AnalyzerOptions);
457 AnalyzerOptions.AnalysisDiagOpt = PD_NONE;
458 std::unique_ptr<ento::AnalysisASTConsumer> AnalysisConsumer =
459 ento::CreateAnalysisConsumer(CI&: Compiler);
460 AnalysisConsumer->AddDiagnosticConsumer(
461 Consumer: std::make_unique<AnalyzerDiagnosticConsumer>(args&: Context));
462 Consumers.push_back(x: std::move(AnalysisConsumer));
463 }
464#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
465 return std::make_unique<ClangTidyASTConsumer>(
466 args: std::move(Consumers), args: std::move(Profiling), args: std::move(Finder),
467 args: std::move(Checks));
468}
469
470std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
471 std::vector<std::string> CheckNames;
472 for (const auto &CheckFactory : *CheckFactories) {
473 if (Context.isCheckEnabled(CheckName: CheckFactory.getKey()))
474 CheckNames.emplace_back(args: CheckFactory.getKey());
475 }
476
477#if CLANG_TIDY_ENABLE_STATIC_ANALYZER
478 for (const auto &AnalyzerCheck : getAnalyzerCheckersAndPackages(
479 Context, IncludeExperimental: Context.canEnableAnalyzerAlphaCheckers()))
480 CheckNames.emplace_back(
481 args: (AnalyzerCheckNamePrefix + AnalyzerCheck.first).str());
482#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
483
484 llvm::sort(C&: CheckNames);
485 return CheckNames;
486}
487
488ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() {
489 ClangTidyOptions::OptionMap Options;
490 std::vector<std::unique_ptr<ClangTidyCheck>> Checks =
491 CheckFactories->createChecks(Context: &Context);
492 for (const auto &Check : Checks)
493 Check->storeOptions(Options);
494 return Options;
495}
496
497std::vector<std::string>
498getCheckNames(const ClangTidyOptions &Options,
499 bool AllowEnablingAnalyzerAlphaCheckers) {
500 clang::tidy::ClangTidyContext Context(
501 std::make_unique<DefaultOptionsProvider>(args: ClangTidyGlobalOptions(),
502 args: Options),
503 AllowEnablingAnalyzerAlphaCheckers);
504 ClangTidyASTConsumerFactory Factory(Context);
505 return Factory.getCheckNames();
506}
507
508void filterCheckOptions(ClangTidyOptions &Options,
509 const std::vector<std::string> &EnabledChecks) {
510 ClangTidyOptions::OptionMap FilteredOptions;
511 for (const auto &[OptionName, Value] : Options.CheckOptions) {
512 const size_t CheckNameEndPos = OptionName.find(C: '.');
513 if (CheckNameEndPos == StringRef::npos)
514 continue;
515 const StringRef CheckName = OptionName.substr(Start: 0, N: CheckNameEndPos);
516 if (llvm::binary_search(Range: EnabledChecks, Value: CheckName))
517 FilteredOptions[OptionName] = Value;
518 }
519 Options.CheckOptions = std::move(FilteredOptions);
520}
521
522ClangTidyOptions::OptionMap
523getCheckOptions(const ClangTidyOptions &Options,
524 bool AllowEnablingAnalyzerAlphaCheckers) {
525 clang::tidy::ClangTidyContext Context(
526 std::make_unique<DefaultOptionsProvider>(args: ClangTidyGlobalOptions(),
527 args: Options),
528 AllowEnablingAnalyzerAlphaCheckers);
529 ClangTidyDiagnosticConsumer DiagConsumer(Context);
530 auto DiagOpts = std::make_unique<DiagnosticOptions>();
531 DiagnosticsEngine DE(llvm::makeIntrusiveRefCnt<DiagnosticIDs>(), *DiagOpts,
532 &DiagConsumer, /*ShouldOwnClient=*/false);
533 Context.setDiagnosticsEngine(DiagOpts: std::move(DiagOpts), DiagEngine: &DE);
534 ClangTidyASTConsumerFactory Factory(Context);
535 return Factory.getCheckOptions();
536}
537
538std::vector<ClangTidyError>
539runClangTidy(clang::tidy::ClangTidyContext &Context,
540 const CompilationDatabase &Compilations,
541 ArrayRef<std::string> InputFiles,
542 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS,
543 bool ApplyAnyFix, bool EnableCheckProfile,
544 llvm::StringRef StoreCheckProfile) {
545 ClangTool Tool(Compilations, InputFiles,
546 std::make_shared<PCHContainerOperations>(), BaseFS);
547
548 // Add extra arguments passed by the clang-tidy command-line.
549 ArgumentsAdjuster PerFileExtraArgumentsInserter =
550 [&Context](const CommandLineArguments &Args, StringRef Filename) {
551 ClangTidyOptions Opts = Context.getOptionsForFile(File: Filename);
552 CommandLineArguments AdjustedArgs = Args;
553 if (Opts.ExtraArgsBefore) {
554 auto I = AdjustedArgs.begin();
555 if (I != AdjustedArgs.end() && !StringRef(*I).starts_with(Prefix: "-"))
556 ++I; // Skip compiler binary name, if it is there.
557 AdjustedArgs.insert(position: I, first: Opts.ExtraArgsBefore->begin(),
558 last: Opts.ExtraArgsBefore->end());
559 }
560 if (Opts.ExtraArgs)
561 AdjustedArgs.insert(position: AdjustedArgs.end(), first: Opts.ExtraArgs->begin(),
562 last: Opts.ExtraArgs->end());
563 return AdjustedArgs;
564 };
565
566 Tool.appendArgumentsAdjuster(Adjuster: PerFileExtraArgumentsInserter);
567 Tool.appendArgumentsAdjuster(Adjuster: getStripPluginsAdjuster());
568 Context.setEnableProfiling(EnableCheckProfile);
569 Context.setProfileStoragePrefix(StoreCheckProfile);
570
571 ClangTidyDiagnosticConsumer DiagConsumer(Context, nullptr, true, ApplyAnyFix);
572 auto DiagOpts = std::make_unique<DiagnosticOptions>();
573 DiagnosticsEngine DE(new DiagnosticIDs(), *DiagOpts, &DiagConsumer,
574 /*ShouldOwnClient=*/false);
575 Context.setDiagnosticsEngine(DiagOpts: std::move(DiagOpts), DiagEngine: &DE);
576 Tool.setDiagnosticConsumer(&DiagConsumer);
577
578 class ActionFactory : public FrontendActionFactory {
579 public:
580 ActionFactory(ClangTidyContext &Context,
581 IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS)
582 : ConsumerFactory(Context, std::move(BaseFS)) {}
583 std::unique_ptr<FrontendAction> create() override {
584 return std::make_unique<Action>(args: &ConsumerFactory);
585 }
586
587 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
588 FileManager *Files,
589 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
590 DiagnosticConsumer *DiagConsumer) override {
591 // Explicitly ask to define __clang_analyzer__ macro.
592 Invocation->getPreprocessorOpts().SetUpStaticAnalyzer = true;
593 return FrontendActionFactory::runInvocation(
594 Invocation, Files, PCHContainerOps, DiagConsumer);
595 }
596
597 private:
598 class Action : public ASTFrontendAction {
599 public:
600 Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {}
601 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
602 StringRef File) override {
603 return Factory->createASTConsumer(Compiler, File);
604 }
605
606 private:
607 ClangTidyASTConsumerFactory *Factory;
608 };
609
610 ClangTidyASTConsumerFactory ConsumerFactory;
611 };
612
613 ActionFactory Factory(Context, std::move(BaseFS));
614 Tool.run(Action: &Factory);
615 return DiagConsumer.take();
616}
617
618void handleErrors(llvm::ArrayRef<ClangTidyError> Errors,
619 ClangTidyContext &Context, FixBehaviour Fix,
620 unsigned &WarningsAsErrorsCount,
621 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS) {
622 ErrorReporter Reporter(Context, Fix, std::move(BaseFS));
623 llvm::vfs::FileSystem &FileSystem =
624 Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
625 auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();
626 if (!InitialWorkingDir)
627 llvm::report_fatal_error(reason: "Cannot get current working path.");
628
629 for (const ClangTidyError &Error : Errors) {
630 if (!Error.BuildDirectory.empty()) {
631 // By default, the working directory of file system is the current
632 // clang-tidy running directory.
633 //
634 // Change the directory to the one used during the analysis.
635 FileSystem.setCurrentWorkingDirectory(Error.BuildDirectory);
636 }
637 Reporter.reportDiagnostic(Error);
638 // Return to the initial directory to correctly resolve next Error.
639 FileSystem.setCurrentWorkingDirectory(InitialWorkingDir.get());
640 }
641 Reporter.finish();
642 WarningsAsErrorsCount += Reporter.getWarningsAsErrorsCount();
643}
644
645void exportReplacements(const llvm::StringRef MainFilePath,
646 const std::vector<ClangTidyError> &Errors,
647 raw_ostream &OS) {
648 TranslationUnitDiagnostics TUD;
649 TUD.MainSourceFile = std::string(MainFilePath);
650 for (const auto &Error : Errors) {
651 tooling::Diagnostic Diag = Error;
652 if (Error.IsWarningAsError)
653 Diag.DiagLevel = tooling::Diagnostic::Error;
654 TUD.Diagnostics.insert(position: TUD.Diagnostics.end(), x: Diag);
655 }
656
657 yaml::Output YAML(OS);
658 YAML << TUD;
659}
660
661ChecksAndOptions
662getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers) {
663 ChecksAndOptions Result;
664 ClangTidyOptions Opts;
665 Opts.Checks = "*";
666 clang::tidy::ClangTidyContext Context(
667 std::make_unique<DefaultOptionsProvider>(args: ClangTidyGlobalOptions(), args&: Opts),
668 AllowEnablingAnalyzerAlphaCheckers);
669 ClangTidyCheckFactories Factories;
670 for (const ClangTidyModuleRegistry::entry &Module :
671 ClangTidyModuleRegistry::entries()) {
672 Module.instantiate()->addCheckFactories(CheckFactories&: Factories);
673 }
674
675 for (const auto &Factory : Factories)
676 Result.Checks.insert(key: Factory.getKey());
677
678#if CLANG_TIDY_ENABLE_STATIC_ANALYZER
679 SmallString<64> Buffer(AnalyzerCheckNamePrefix);
680 size_t DefSize = Buffer.size();
681 for (const auto &AnalyzerCheck : AnalyzerOptions::getRegisteredCheckers(
682 IncludeExperimental: AllowEnablingAnalyzerAlphaCheckers)) {
683 Buffer.truncate(N: DefSize);
684 Buffer.append(RHS: AnalyzerCheck);
685 Result.Checks.insert(key: Buffer);
686 }
687
688 static constexpr llvm::StringLiteral OptionNames[] = {
689#define GET_CHECKER_OPTIONS
690#define CHECKER_OPTION(TYPE, CHECKER, OPTION_NAME, DESCRIPTION, DEFAULT, \
691 RELEASE, HIDDEN) \
692 ANALYZER_CHECK_NAME_PREFIX CHECKER ":" OPTION_NAME,
693
694#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
695#undef CHECKER_OPTION
696#undef GET_CHECKER_OPTIONS
697 };
698
699 Result.Options.insert_range(R: OptionNames);
700#endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER
701
702 Context.setOptionsCollector(&Result.Options);
703 for (const auto &Factory : Factories) {
704 Factory.getValue()(Factory.getKey(), &Context);
705 }
706
707 return Result;
708}
709} // namespace clang::tidy
710

source code of clang-tools-extra/clang-tidy/ClangTidy.cpp