21 #include "clang/AST/ASTConsumer.h"
22 #include "clang/AST/ASTContext.h"
23 #include "clang/AST/Decl.h"
24 #include "clang/ASTMatchers/ASTMatchFinder.h"
25 #include "clang/Format/Format.h"
26 #include "clang/Frontend/ASTConsumers.h"
27 #include "clang/Frontend/CompilerInstance.h"
28 #include "clang/Frontend/FrontendActions.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/Rewrite/Frontend/FixItRewriter.h"
35 #include "clang/Rewrite/Frontend/FrontendActions.h"
36 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
37 #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.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"
43 #include "llvm/Support/Signals.h"
47 using namespace clang::ast_matchers;
48 using namespace clang::driver;
49 using namespace clang::tooling;
58 static const char *AnalyzerCheckNamePrefix =
"clang-analyzer-";
60 class AnalyzerDiagnosticConsumer :
public ento::PathDiagnosticConsumer {
62 AnalyzerDiagnosticConsumer(ClangTidyContext &
Context) : Context(Context) {}
64 void FlushDiagnosticsImpl(std::vector<const ento::PathDiagnostic *> &
Diags,
65 FilesMade *filesMade)
override {
66 for (
const ento::PathDiagnostic *PD : Diags) {
67 SmallString<64> CheckName(AnalyzerCheckNamePrefix);
68 CheckName += PD->getCheckName();
69 Context.diag(CheckName, PD->getLocation().asLocation(),
70 PD->getShortDescription())
71 << PD->path.back()->getRanges();
73 for (
const auto &DiagPiece :
74 PD->path.flatten(
true)) {
75 Context.diag(CheckName, DiagPiece->getLocation().asLocation(),
76 DiagPiece->getString(), DiagnosticIDs::Note)
77 << DiagPiece->getRanges();
82 StringRef getName()
const override {
return "ClangTidyDiags"; }
83 bool supportsLogicalOpControlFlow()
const override {
return true; }
84 bool supportsCrossFileDiagnostics()
const override {
return true; }
93 :
Files(FileSystemOptions()),
DiagOpts(
new DiagnosticOptions()),
95 Diags(IntrusiveRefCntPtr<DiagnosticIDs>(
new DiagnosticIDs), &*
DiagOpts,
99 DiagOpts->ShowColors = llvm::sys::Process::StandardOutHasColors();
103 SourceManager &getSourceManager() {
return SourceMgr; }
105 void reportDiagnostic(
const ClangTidyError &Error) {
106 const tooling::DiagnosticMessage &
Message = Error.Message;
107 SourceLocation
Loc = getLocation(Message.FilePath, Message.FileOffset);
110 SmallVector<std::pair<SourceLocation, bool>, 4> FixLocations;
112 auto Level =
static_cast<DiagnosticsEngine::Level
>(Error.DiagLevel);
113 std::string
Name = Error.DiagnosticName;
114 if (Error.IsWarningAsError) {
115 Name +=
",-warnings-as-errors";
116 Level = DiagnosticsEngine::Error;
119 auto Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level,
"%0 [%1]"))
120 << Message.Message << Name;
121 for (
const auto &FileAndReplacements : Error.Fix) {
122 for (
const auto &Repl : FileAndReplacements.second) {
127 SourceLocation FixLoc;
129 bool CanBeApplied =
false;
130 if (Repl.isApplicable()) {
131 SmallString<128> FixAbsoluteFilePath = Repl.getFilePath();
132 Files.makeAbsolutePath(FixAbsoluteFilePath);
134 tooling::Replacement R(FixAbsoluteFilePath, Repl.getOffset(),
136 Repl.getReplacementText());
138 llvm::Error Err = Replacements.add(R);
141 llvm::errs() <<
"Trying to resolve conflict: "
144 Replacements.getShiftedCodePosition(R.getOffset());
145 unsigned NewLength = Replacements.getShiftedCodePosition(
146 R.getOffset() + R.getLength()) -
148 if (NewLength == R.getLength()) {
149 R = Replacement(R.getFilePath(), NewOffset, NewLength,
150 R.getReplacementText());
151 Replacements = Replacements.merge(tooling::Replacements(R));
156 <<
"Can't resolve conflict, skipping the replacement.\n";
164 FixLoc = getLocation(FixAbsoluteFilePath, Repl.getOffset());
165 SourceLocation FixEndLoc =
166 FixLoc.getLocWithOffset(Repl.getLength());
167 Range = SourceRange(FixLoc, FixEndLoc);
168 Diag << FixItHint::CreateReplacement(Range,
169 Repl.getReplacementText());
173 FixLocations.push_back(std::make_pair(FixLoc, CanBeApplied));
177 for (
auto Fix : FixLocations) {
178 Diags.Report(
Fix.first,
Fix.second ? diag::note_fixit_applied
179 : diag::note_fixit_failed);
181 for (
const auto &Note : Error.Notes)
190 StringRef
File = FileAndReplacements.first();
191 llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
192 SourceMgr.getFileManager().getBufferForFile(File);
194 llvm::errs() <<
"Can't get buffer for file " << File <<
": "
195 << Buffer.getError().message() <<
"\n";
199 StringRef Code = Buffer.get()->getBuffer();
201 llvm::Expected<Replacements> CleanReplacements =
202 format::cleanupAroundReplacements(Code, FileAndReplacements.second,
204 if (!CleanReplacements) {
205 llvm::errs() <<
llvm::toString(CleanReplacements.takeError()) <<
"\n";
209 llvm::errs() <<
"Can't apply replacements for file " << File <<
"\n";
212 if (Rewrite.overwriteChangedFiles()) {
213 llvm::errs() <<
"clang-tidy failed to apply suggested fixes.\n";
215 llvm::errs() <<
"clang-tidy applied " <<
AppliedFixes <<
" of "
224 SourceLocation getLocation(StringRef FilePath,
unsigned Offset) {
225 if (FilePath.empty())
226 return SourceLocation();
228 const FileEntry *File =
SourceMgr.getFileManager().getFile(FilePath);
229 FileID ID =
SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User);
230 return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
233 void reportNote(
const tooling::DiagnosticMessage &Message) {
234 SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
235 Diags.Report(Loc, Diags.getCustomDiagID(DiagnosticsEngine::Note,
"%0"))
255 ClangTidyASTConsumer(std::vector<std::unique_ptr<ASTConsumer>> Consumers,
256 std::unique_ptr<ast_matchers::MatchFinder>
Finder,
257 std::vector<std::unique_ptr<ClangTidyCheck>>
Checks)
262 std::unique_ptr<ast_matchers::MatchFinder>
Finder;
263 std::vector<std::unique_ptr<ClangTidyCheck>>
Checks;
268 ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
271 for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
272 E = ClangTidyModuleRegistry::end();
274 std::unique_ptr<ClangTidyModule> Module(I->instantiate());
275 Module->addCheckFactories(*CheckFactories);
280 AnalyzerOptionsRef AnalyzerOptions) {
281 StringRef AnalyzerPrefix(AnalyzerCheckNamePrefix);
283 StringRef OptName(Opt.first);
284 if (!OptName.startswith(AnalyzerPrefix))
286 AnalyzerOptions->Config[OptName.substr(AnalyzerPrefix.size())] = Opt.second;
295 const auto &RegisteredCheckers =
296 AnalyzerOptions::getRegisteredCheckers(
false);
297 bool AnalyzerChecksEnabled =
false;
298 for (StringRef CheckName : RegisteredCheckers) {
299 std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
300 AnalyzerChecksEnabled |= Filter.
contains(ClangTidyCheckName);
303 if (!AnalyzerChecksEnabled)
311 for (StringRef CheckName : RegisteredCheckers) {
312 std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
314 if (CheckName.startswith(
"core") || Filter.
contains(ClangTidyCheckName))
315 List.emplace_back(CheckName,
true);
320 std::unique_ptr<clang::ASTConsumer>
322 clang::CompilerInstance &Compiler, StringRef File) {
329 auto WorkingDir = Compiler.getSourceManager()
331 .getVirtualFileSystem()
332 ->getCurrentWorkingDirectory();
336 std::vector<std::unique_ptr<ClangTidyCheck>>
Checks;
337 CheckFactories->createChecks(&Context, Checks);
339 ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;
341 FinderOptions.CheckProfiling.emplace(P->Records);
343 std::unique_ptr<ast_matchers::MatchFinder>
Finder(
344 new ast_matchers::MatchFinder(std::move(FinderOptions)));
346 for (
auto &
Check : Checks) {
347 Check->registerMatchers(&*Finder);
348 Check->registerPPCallbacks(Compiler);
351 std::vector<std::unique_ptr<ASTConsumer>> Consumers;
353 Consumers.push_back(Finder->newASTConsumer());
355 AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts();
358 AnalyzerOptions->Config[
"cfg-temporary-dtors"] =
363 if (!AnalyzerOptions->CheckersControlList.empty()) {
365 AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel;
366 AnalyzerOptions->AnalysisDiagOpt = PD_NONE;
367 AnalyzerOptions->AnalyzeNestedBlocks =
true;
368 AnalyzerOptions->eagerlyAssumeBinOpBifurcation =
true;
369 std::unique_ptr<ento::AnalysisASTConsumer> AnalysisConsumer =
370 ento::CreateAnalysisConsumer(Compiler);
371 AnalysisConsumer->AddDiagnosticConsumer(
372 new AnalyzerDiagnosticConsumer(Context));
373 Consumers.push_back(std::move(AnalysisConsumer));
375 return llvm::make_unique<ClangTidyASTConsumer>(
376 std::move(Consumers), std::move(Finder), std::move(Checks));
380 std::vector<std::string> CheckNames;
382 for (
const auto &CheckFactory : *CheckFactories) {
383 if (Filter.
contains(CheckFactory.first))
384 CheckNames.push_back(CheckFactory.first);
388 CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first);
390 std::sort(CheckNames.begin(), CheckNames.end());
396 std::vector<std::unique_ptr<ClangTidyCheck>>
Checks;
397 CheckFactories->createChecks(&Context, Checks);
398 for (
const auto &
Check : Checks)
399 Check->storeOptions(Options);
404 DiagnosticIDs::Level Level) {
405 return Context->
diag(CheckName, Loc, Message, Level);
408 void ClangTidyCheck::run(
const ast_matchers::MatchFinder::MatchResult &
Result) {
415 : NamePrefix(CheckName.str() +
"."), CheckOptions(CheckOptions) {}
418 const auto &Iter = CheckOptions.find(NamePrefix + LocalName.str());
419 if (Iter != CheckOptions.end())
425 StringRef Default)
const {
426 auto Iter = CheckOptions.find(NamePrefix + LocalName.str());
427 if (Iter != CheckOptions.end())
430 Iter = CheckOptions.find(LocalName.str());
431 if (Iter != CheckOptions.end())
437 StringRef LocalName, StringRef Value)
const {
438 Options[NamePrefix + LocalName.str()] = Value;
442 StringRef LocalName, int64_t Value)
const {
443 store(Options, LocalName, llvm::itostr(Value));
463 runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
464 const CompilationDatabase &Compilations,
465 ArrayRef<std::string> InputFiles,
466 std::vector<ClangTidyError> *Errors,
ProfileData *Profile) {
467 ClangTool Tool(Compilations, InputFiles);
471 ArgumentsAdjuster PerFileExtraArgumentsInserter =
474 CommandLineArguments AdjustedArgs = Args;
476 auto I = AdjustedArgs.begin();
477 if (I != AdjustedArgs.end() && !StringRef(*I).startswith(
"-"))
483 AdjustedArgs.insert(AdjustedArgs.end(), Opts.
ExtraArgs->begin(),
489 ArgumentsAdjuster PluginArgumentsRemover =
491 CommandLineArguments AdjustedArgs;
492 for (
size_t I = 0, E = Args.size(); I < E; ++I) {
493 if (I + 4 < Args.size() && Args[I] ==
"-Xclang" &&
494 (Args[I + 1] ==
"-load" || Args[I + 1] ==
"-add-plugin" ||
495 StringRef(Args[I + 1]).startswith(
"-plugin-arg-")) &&
496 Args[I + 2] ==
"-Xclang") {
499 AdjustedArgs.push_back(Args[I]);
504 Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter);
505 Tool.appendArgumentsAdjuster(PluginArgumentsRemover);
511 Tool.setDiagnosticConsumer(&DiagConsumer);
516 FrontendAction *create()
override {
return new Action(&ConsumerFactory); }
519 class Action :
public ASTFrontendAction {
522 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
523 StringRef File)
override {
534 ActionFactory Factory(Context);
541 StringRef FormatStyle,
unsigned &WarningsAsErrorsCount) {
542 ErrorReporter Reporter(Fix, FormatStyle);
543 vfs::FileSystem &FileSystem =
544 *Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
545 auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();
546 if (!InitialWorkingDir)
547 llvm::report_fatal_error(
"Cannot get current working path.");
550 if (!Error.BuildDirectory.empty()) {
555 FileSystem.setCurrentWorkingDirectory(Error.BuildDirectory);
557 Reporter.reportDiagnostic(Error);
559 FileSystem.setCurrentWorkingDirectory(InitialWorkingDir.get());
562 WarningsAsErrorsCount += Reporter.getWarningsAsErrorsCount();
566 const std::vector<ClangTidyError> &Errors,
568 TranslationUnitDiagnostics TUD;
569 TUD.MainSourceFile = MainFilePath;
570 for (
const auto &Error : Errors) {
571 tooling::Diagnostic Diag = Error;
572 TUD.Diagnostics.insert(TUD.Diagnostics.end(), Diag);
575 yaml::Output YAML(OS);
SourceLocation Loc
'#' location in the include directive
std::vector< std::string > getCheckNames()
Get the list of enabled checks.
llvm::Optional< ArgList > ExtraArgs
Add extra compilation arguments to the end of the list.
Read-only set of strings represented as a list of positive and negative globs.
llvm::StringMap< Replacements > FileReplacements
std::unique_ptr< ast_matchers::MatchFinder > Finder
ClangTidyOptions::OptionMap getCheckOptions()
Get the union of options from all checks.
std::vector< std::unique_ptr< ClangTidyCheck > > Checks
bool contains(StringRef S)
Returns true if the pattern matches S.
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options)
Returns the effective check-specific options.
static const StringRef Message
Contains options for clang-tidy.
ProfileData * getCheckProfileData() const
A collection of ClangTidyCheckFactory instances.
const std::vector< ClangTidyError > & getErrors() const
Returns all collected errors.
OptionMap CheckOptions
Key-value mapping used to store check-specific options.
std::string get(StringRef LocalName, StringRef Default) const
Read a named option from the Context.
llvm::Optional< ArgList > ExtraArgsBefore
Add extra compilation arguments to the start of the list.
ClangTidyOptions getOptionsForFile(StringRef File) const
Returns options for File.
void setCurrentFile(StringRef File)
Should be called when starting to process new translation unit.
const ClangTidyOptions & getOptions() const
Returns options for CurrentFile.
std::string Filename
Filename as a string.
DiagnosticBuilder diag(StringRef CheckName, SourceLocation Loc, StringRef Message, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Report any errors detected using this method.
void setCheckProfileData(ProfileData *Profile)
Set the output struct for profile data.
ClangTidyStats runClangTidy(std::unique_ptr< ClangTidyOptionsProvider > OptionsProvider, const CompilationDatabase &Compilations, ArrayRef< std::string > InputFiles, std::vector< ClangTidyError > *Errors, ProfileData *Profile)
std::string getLocalOrGlobal(StringRef LocalName, StringRef Default) const
Read a named option from the Context.
void setASTContext(ASTContext *Context)
Sets ASTContext for the current translation unit.
A diagnostic consumer that turns each Diagnostic into a SourceManager-independent ClangTidyError...
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
std::map< std::string, std::string > OptionMap
std::vector< std::string > getCheckNames(const ClangTidyOptions &Options)
Fills the list of check names that are enabled when the provided filters are applied.
std::vector< std::pair< std::string, bool > > CheckersList
static void setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts, AnalyzerOptionsRef AnalyzerOptions)
unsigned WarningsAsErrors
void setSourceManager(SourceManager *SourceMgr)
Sets the SourceManager of the used DiagnosticsEngine.
OptionsView(StringRef CheckName, const ClangTidyOptions::OptionMap &CheckOptions)
Initializes the instance using CheckName + "." as a prefix.
llvm::Optional< bool > AnalyzeTemporaryDtors
Turns on temporary destructor-based analysis.
void exportReplacements(const llvm::StringRef MainFilePath, const std::vector< ClangTidyError > &Errors, raw_ostream &OS)
const ClangTidyStats & getStats() const
Returns ClangTidyStats containing issued and ignored diagnostic counters.
CharSourceRange Range
SourceRange for the file name.
A detected error complete with information to display diagnostic and automatic fix.
IntrusiveRefCntPtr< DiagnosticOptions > DiagOpts
ClangTidyContext & Context
static CheckersList getCheckersControlList(GlobList &Filter)
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
llvm::Registry< ClangTidyModule > ClangTidyModuleRegistry
std::unique_ptr< clang::ASTConsumer > CreateASTConsumer(clang::CompilerInstance &Compiler, StringRef File)
Returns an ASTConsumer that runs the specified clang-tidy checks.
static cl::opt< bool > Fix("fix", cl::desc(R"(
Apply suggested fixes. Without -fix-errors
clang-tidy will bail out if any compilation
errors were found.
)"), cl::init(false), cl::cat(ClangTidyCategory))
void setCurrentBuildDirectory(StringRef BuildDirectory)
Should be called when starting to process new translation unit.
bool applyAllReplacements(const std::vector< tooling::Replacement > &Replaces, Rewriter &Rewrite)
virtual void check(const ast_matchers::MatchFinder::MatchResult &Result)
ClangTidyChecks that register ASTMatchers should do the actual work in here.
GlobList & getChecksFilter()
Returns check filter for the CurrentFile.
Container for clang-tidy profiling data.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void handleErrors(const std::vector< ClangTidyError > &Errors, bool Fix, StringRef FormatStyle, unsigned &WarningsAsErrorsCount)
Displays the found Errors to the users.
DiagnosticConsumer * DiagPrinter