blob: f2ad5b2b9a830d110ec962cfc2ca5ca17d452f4c [file] [log] [blame]
Daniel Jasperd07c8402013-07-29 08:19:24 +00001//===--- tools/extra/clang-tidy/ClangTidy.cpp - Clang tidy tool -----------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9///
10/// \file This file implements a clang-tidy tool.
11///
12/// This tool uses the Clang Tooling infrastructure, see
13/// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html
14/// for details on setting it up with LLVM source tree.
15///
16//===----------------------------------------------------------------------===//
17
18#include "ClangTidy.h"
19#include "ClangTidyDiagnosticConsumer.h"
20#include "ClangTidyModuleRegistry.h"
21#include "clang/AST/ASTConsumer.h"
22#include "clang/AST/ASTContext.h"
23#include "clang/AST/Decl.h"
24#include "clang/ASTMatchers/ASTMatchFinder.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000025#include "clang/Frontend/ASTConsumers.h"
26#include "clang/Frontend/CompilerInstance.h"
27#include "clang/Frontend/FrontendActions.h"
Alexander Kornienko38d81b42014-03-27 10:24:11 +000028#include "clang/Frontend/FrontendDiagnostic.h"
Alexander Kornienko175fefb2014-01-03 09:31:57 +000029#include "clang/Frontend/MultiplexConsumer.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000030#include "clang/Frontend/TextDiagnosticPrinter.h"
Chandler Carruth85e6e872014-01-07 20:05:01 +000031#include "clang/Lex/PPCallbacks.h"
32#include "clang/Lex/Preprocessor.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000033#include "clang/Rewrite/Frontend/FixItRewriter.h"
34#include "clang/Rewrite/Frontend/FrontendActions.h"
Benjamin Kramer8ff8fdf2016-07-18 19:21:22 +000035#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
Alexander Kornienkod1199cb2014-01-03 17:24:20 +000036#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000037#include "clang/Tooling/Refactoring.h"
Benjamin Kramerfb98b742014-09-04 10:31:23 +000038#include "clang/Tooling/ReplacementsYaml.h"
Chandler Carruth85e6e872014-01-07 20:05:01 +000039#include "clang/Tooling/Tooling.h"
Alexander Kornienko54461eb2014-02-06 14:50:10 +000040#include "llvm/Support/Process.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000041#include "llvm/Support/Signals.h"
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +000042#include <algorithm>
Alexander Kornienko38d81b42014-03-27 10:24:11 +000043#include <utility>
Daniel Jasperd07c8402013-07-29 08:19:24 +000044
45using namespace clang::ast_matchers;
46using namespace clang::driver;
47using namespace clang::tooling;
48using namespace llvm;
49
John Brawn4d79ec72016-08-05 11:01:08 +000050LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry)
NAKAMURA Takumi71b982b2014-07-03 14:12:47 +000051
Daniel Jasperd07c8402013-07-29 08:19:24 +000052namespace clang {
53namespace tidy {
Alexander Kornienko175fefb2014-01-03 09:31:57 +000054
Daniel Jasperd07c8402013-07-29 08:19:24 +000055namespace {
Alexander Kornienko54461eb2014-02-06 14:50:10 +000056static const char *AnalyzerCheckNamePrefix = "clang-analyzer-";
Manuel Klimek814f9bd2013-11-14 15:49:44 +000057
Craig Topper45857d42015-10-18 05:14:41 +000058static const StringRef StaticAnalyzerChecks[] = {
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +000059#define GET_CHECKERS
60#define CHECKER(FULLNAME, CLASS, DESCFILE, HELPTEXT, GROUPINDEX, HIDDEN) \
61 FULLNAME,
Stephen Hines15643352016-04-28 01:42:12 +000062#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +000063#undef CHECKER
64#undef GET_CHECKERS
65};
66
Alexander Kornienko54461eb2014-02-06 14:50:10 +000067class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer {
68public:
69 AnalyzerDiagnosticConsumer(ClangTidyContext &Context) : Context(Context) {}
70
Alexander Kornienkocb9272f2014-02-27 13:14:51 +000071 void FlushDiagnosticsImpl(std::vector<const ento::PathDiagnostic *> &Diags,
Craig Toppera3dbe842014-03-02 10:20:11 +000072 FilesMade *filesMade) override {
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +000073 for (const ento::PathDiagnostic *PD : Diags) {
Alexander Kornienkod1afc702014-02-12 09:52:07 +000074 SmallString<64> CheckName(AnalyzerCheckNamePrefix);
75 CheckName += PD->getCheckName();
Alexander Kornienko95cd50f2014-03-06 13:24:28 +000076 Context.diag(CheckName, PD->getLocation().asLocation(),
77 PD->getShortDescription())
78 << PD->path.back()->getRanges();
Alexander Kornienko54461eb2014-02-06 14:50:10 +000079
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +000080 for (const auto &DiagPiece :
81 PD->path.flatten(/*ShouldFlattenMacros=*/true)) {
Alexander Kornienko95cd50f2014-03-06 13:24:28 +000082 Context.diag(CheckName, DiagPiece->getLocation().asLocation(),
83 DiagPiece->getString(), DiagnosticIDs::Note)
84 << DiagPiece->getRanges();
Alexander Kornienko54461eb2014-02-06 14:50:10 +000085 }
86 }
87 }
88
Craig Toppera3dbe842014-03-02 10:20:11 +000089 StringRef getName() const override { return "ClangTidyDiags"; }
90 bool supportsLogicalOpControlFlow() const override { return true; }
91 bool supportsCrossFileDiagnostics() const override { return true; }
Alexander Kornienko54461eb2014-02-06 14:50:10 +000092
93private:
94 ClangTidyContext &Context;
Alexander Kornienko54461eb2014-02-06 14:50:10 +000095};
96
Alexander Kornienko38d81b42014-03-27 10:24:11 +000097class ErrorReporter {
98public:
99 ErrorReporter(bool ApplyFixes)
100 : Files(FileSystemOptions()), DiagOpts(new DiagnosticOptions()),
101 DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)),
102 Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
103 DiagPrinter),
104 SourceMgr(Diags, Files), Rewrite(SourceMgr, LangOpts),
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000105 ApplyFixes(ApplyFixes), TotalFixes(0), AppliedFixes(0),
106 WarningsAsErrors(0) {
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000107 DiagOpts->ShowColors = llvm::sys::Process::StandardOutHasColors();
108 DiagPrinter->BeginSourceFile(LangOpts);
109 }
110
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000111 SourceManager &getSourceManager() { return SourceMgr; }
Haojian Wuf7692a22016-02-26 09:19:33 +0000112
Alexander Kornienko742790c2014-07-02 15:05:04 +0000113 void reportDiagnostic(const ClangTidyError &Error) {
114 const ClangTidyMessage &Message = Error.Message;
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000115 SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
116 // Contains a pair for each attempted fix: location and whether the fix was
117 // applied successfully.
118 SmallVector<std::pair<SourceLocation, bool>, 4> FixLocations;
119 {
Alexander Kornienko742790c2014-07-02 15:05:04 +0000120 auto Level = static_cast<DiagnosticsEngine::Level>(Error.DiagLevel);
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000121 std::string Name = Error.CheckName;
122 if (Error.IsWarningAsError) {
123 Name += ",-warnings-as-errors";
124 Level = DiagnosticsEngine::Error;
125 WarningsAsErrors++;
126 }
Alexander Kornienko58fe57a2015-11-16 13:06:15 +0000127 auto Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0 [%1]"))
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000128 << Message.Message << Name;
Eric Liu7e544522016-08-09 07:54:49 +0000129 for (const auto &FileAndReplacements : Error.Fix) {
130 for (const auto &Replacement : FileAndReplacements.second) {
131 // Retrieve the source range for applicable fixes. Macro definitions
132 // on the command line have locations in a virtual buffer and don't
133 // have valid file paths and are therefore not applicable.
134 SourceRange Range;
135 SourceLocation FixLoc;
136 if (Replacement.isApplicable()) {
137 SmallString<128> FixAbsoluteFilePath = Replacement.getFilePath();
138 Files.makeAbsolutePath(FixAbsoluteFilePath);
139 FixLoc = getLocation(FixAbsoluteFilePath, Replacement.getOffset());
140 SourceLocation FixEndLoc =
141 FixLoc.getLocWithOffset(Replacement.getLength());
142 Range = SourceRange(FixLoc, FixEndLoc);
143 Diag << FixItHint::CreateReplacement(
144 Range, Replacement.getReplacementText());
145 }
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000146
Eric Liu7e544522016-08-09 07:54:49 +0000147 ++TotalFixes;
148 if (ApplyFixes) {
149 bool Success =
150 Replacement.isApplicable() && Replacement.apply(Rewrite);
151 if (Success)
152 ++AppliedFixes;
153 FixLocations.push_back(std::make_pair(FixLoc, Success));
154 }
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000155 }
156 }
157 }
158 for (auto Fix : FixLocations) {
159 Diags.Report(Fix.first, Fix.second ? diag::note_fixit_applied
160 : diag::note_fixit_failed);
161 }
Alexander Kornienko742790c2014-07-02 15:05:04 +0000162 for (const ClangTidyMessage &Note : Error.Notes)
163 reportNote(Note);
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000164 }
165
166 void Finish() {
167 // FIXME: Run clang-format on changes.
168 if (ApplyFixes && TotalFixes > 0) {
169 llvm::errs() << "clang-tidy applied " << AppliedFixes << " of "
170 << TotalFixes << " suggested fixes.\n";
171 Rewrite.overwriteChangedFiles();
172 }
173 }
174
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000175 unsigned getWarningsAsErrorsCount() const { return WarningsAsErrors; }
176
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000177private:
178 SourceLocation getLocation(StringRef FilePath, unsigned Offset) {
179 if (FilePath.empty())
180 return SourceLocation();
181
182 const FileEntry *File = SourceMgr.getFileManager().getFile(FilePath);
183 FileID ID = SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User);
184 return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
185 }
186
Alexander Kornienko742790c2014-07-02 15:05:04 +0000187 void reportNote(const ClangTidyMessage &Message) {
188 SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
189 DiagnosticBuilder Diag =
190 Diags.Report(Loc, Diags.getCustomDiagID(DiagnosticsEngine::Note, "%0"))
191 << Message.Message;
192 }
193
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000194 FileManager Files;
195 LangOptions LangOpts; // FIXME: use langopts from each original file
196 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
197 DiagnosticConsumer *DiagPrinter;
198 DiagnosticsEngine Diags;
199 SourceManager SourceMgr;
200 Rewriter Rewrite;
201 bool ApplyFixes;
NAKAMURA Takumi4dd18132014-03-27 14:53:37 +0000202 unsigned TotalFixes;
203 unsigned AppliedFixes;
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000204 unsigned WarningsAsErrors;
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000205};
206
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000207class ClangTidyASTConsumer : public MultiplexConsumer {
208public:
David Blaikie680c4c82014-08-10 19:56:59 +0000209 ClangTidyASTConsumer(std::vector<std::unique_ptr<ASTConsumer>> Consumers,
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000210 std::unique_ptr<ast_matchers::MatchFinder> Finder,
211 std::vector<std::unique_ptr<ClangTidyCheck>> Checks)
David Blaikie680c4c82014-08-10 19:56:59 +0000212 : MultiplexConsumer(std::move(Consumers)), Finder(std::move(Finder)),
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000213 Checks(std::move(Checks)) {}
214
215private:
216 std::unique_ptr<ast_matchers::MatchFinder> Finder;
217 std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
218};
219
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000220} // namespace
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000221
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000222ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000223 ClangTidyContext &Context)
224 : Context(Context), CheckFactories(new ClangTidyCheckFactories) {
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000225 for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
226 E = ClangTidyModuleRegistry::end();
227 I != E; ++I) {
Ahmed Charles6a2dc5c2014-03-09 09:24:40 +0000228 std::unique_ptr<ClangTidyModule> Module(I->instantiate());
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000229 Module->addCheckFactories(*CheckFactories);
230 }
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000231}
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000232
Gabor Horvath34383212015-03-11 17:25:22 +0000233static void setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts,
234 AnalyzerOptionsRef AnalyzerOptions) {
235 StringRef AnalyzerPrefix(AnalyzerCheckNamePrefix);
236 for (const auto &Opt : Opts.CheckOptions) {
237 StringRef OptName(Opt.first);
238 if (!OptName.startswith(AnalyzerPrefix))
239 continue;
240 AnalyzerOptions->Config[OptName.substr(AnalyzerPrefix.size())] = Opt.second;
241 }
242}
243
David Blaikie680c4c82014-08-10 19:56:59 +0000244std::unique_ptr<clang::ASTConsumer>
245ClangTidyASTConsumerFactory::CreateASTConsumer(
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000246 clang::CompilerInstance &Compiler, StringRef File) {
247 // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
248 // modify Compiler.
249 Context.setSourceManager(&Compiler.getSourceManager());
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000250 Context.setCurrentFile(File);
Alexander Kornienkoad216882014-07-14 14:10:03 +0000251 Context.setASTContext(&Compiler.getASTContext());
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000252
Haojian Wuf7692a22016-02-26 09:19:33 +0000253 auto WorkingDir = Compiler.getSourceManager()
254 .getFileManager()
255 .getVirtualFileSystem()
256 ->getCurrentWorkingDirectory();
257 if (WorkingDir)
258 Context.setCurrentBuildDirectory(WorkingDir.get());
259
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000260 std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000261 CheckFactories->createChecks(&Context, Checks);
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000262
Samuel Benzaquenaedd9942014-10-23 17:23:20 +0000263 ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;
264 if (auto *P = Context.getCheckProfileData())
265 FinderOptions.CheckProfiling.emplace(P->Records);
266
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000267 std::unique_ptr<ast_matchers::MatchFinder> Finder(
Samuel Benzaquenaedd9942014-10-23 17:23:20 +0000268 new ast_matchers::MatchFinder(std::move(FinderOptions)));
269
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000270 for (auto &Check : Checks) {
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000271 Check->registerMatchers(&*Finder);
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000272 Check->registerPPCallbacks(Compiler);
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000273 }
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000274
David Blaikie680c4c82014-08-10 19:56:59 +0000275 std::vector<std::unique_ptr<ASTConsumer>> Consumers;
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000276 if (!Checks.empty())
277 Consumers.push_back(Finder->newASTConsumer());
Alexander Kornienko298b3822014-02-13 16:10:47 +0000278
Alex McCarthyfec08c72014-04-30 14:09:24 +0000279 AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts();
280 // FIXME: Remove this option once clang's cfg-temporary-dtors option defaults
281 // to true.
282 AnalyzerOptions->Config["cfg-temporary-dtors"] =
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000283 Context.getOptions().AnalyzeTemporaryDtors ? "true" : "false";
Alex McCarthyfec08c72014-04-30 14:09:24 +0000284
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000285 GlobList &Filter = Context.getChecksFilter();
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000286 AnalyzerOptions->CheckersControlList = getCheckersControlList(Filter);
Alex McCarthyfec08c72014-04-30 14:09:24 +0000287 if (!AnalyzerOptions->CheckersControlList.empty()) {
Gabor Horvath34383212015-03-11 17:25:22 +0000288 setStaticAnalyzerCheckerOpts(Context.getOptions(), AnalyzerOptions);
Alex McCarthyfec08c72014-04-30 14:09:24 +0000289 AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel;
290 AnalyzerOptions->AnalysisDiagOpt = PD_NONE;
291 AnalyzerOptions->AnalyzeNestedBlocks = true;
292 AnalyzerOptions->eagerlyAssumeBinOpBifurcation = true;
David Blaikie680c4c82014-08-10 19:56:59 +0000293 std::unique_ptr<ento::AnalysisASTConsumer> AnalysisConsumer =
Ted Kremenek4d1692f2014-08-27 15:14:47 +0000294 ento::CreateAnalysisConsumer(Compiler);
Alexander Kornienko298b3822014-02-13 16:10:47 +0000295 AnalysisConsumer->AddDiagnosticConsumer(
296 new AnalyzerDiagnosticConsumer(Context));
David Blaikie680c4c82014-08-10 19:56:59 +0000297 Consumers.push_back(std::move(AnalysisConsumer));
Alexander Kornienko298b3822014-02-13 16:10:47 +0000298 }
David Blaikie680c4c82014-08-10 19:56:59 +0000299 return llvm::make_unique<ClangTidyASTConsumer>(
300 std::move(Consumers), std::move(Finder), std::move(Checks));
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000301}
302
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000303std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000304 std::vector<std::string> CheckNames;
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000305 GlobList &Filter = Context.getChecksFilter();
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000306 for (const auto &CheckFactory : *CheckFactories) {
Alexander Kornienkob3d331d2014-08-06 11:49:10 +0000307 if (Filter.contains(CheckFactory.first))
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000308 CheckNames.push_back(CheckFactory.first);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000309 }
310
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000311 for (const auto &AnalyzerCheck : getCheckersControlList(Filter))
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000312 CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000313
314 std::sort(CheckNames.begin(), CheckNames.end());
315 return CheckNames;
316}
317
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000318ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() {
319 ClangTidyOptions::OptionMap Options;
320 std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
321 CheckFactories->createChecks(&Context, Checks);
322 for (const auto &Check : Checks)
323 Check->storeOptions(Options);
324 return Options;
325}
326
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000327ClangTidyASTConsumerFactory::CheckersList
Alexander Kornienkob3d331d2014-08-06 11:49:10 +0000328ClangTidyASTConsumerFactory::getCheckersControlList(GlobList &Filter) {
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000329 CheckersList List;
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000330
331 bool AnalyzerChecksEnabled = false;
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000332 for (StringRef CheckName : StaticAnalyzerChecks) {
333 std::string Checker((AnalyzerCheckNamePrefix + CheckName).str());
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000334 AnalyzerChecksEnabled =
335 AnalyzerChecksEnabled ||
Alexander Kornienkob3d331d2014-08-06 11:49:10 +0000336 (!CheckName.startswith("debug") && Filter.contains(Checker));
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000337 }
338
339 if (AnalyzerChecksEnabled) {
340 // Run our regex against all possible static analyzer checkers. Note that
341 // debug checkers print values / run programs to visualize the CFG and are
342 // thus not applicable to clang-tidy in general.
343 //
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000344 // Always add all core checkers if any other static analyzer checks are
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000345 // enabled. This is currently necessary, as other path sensitive checks
346 // rely on the core checkers.
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000347 for (StringRef CheckName : StaticAnalyzerChecks) {
348 std::string Checker((AnalyzerCheckNamePrefix + CheckName).str());
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000349
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000350 if (CheckName.startswith("core") ||
Alexander Kornienkob3d331d2014-08-06 11:49:10 +0000351 (!CheckName.startswith("debug") && Filter.contains(Checker)))
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000352 List.push_back(std::make_pair(CheckName, true));
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000353 }
354 }
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000355 return List;
356}
Daniel Jasperd07c8402013-07-29 08:19:24 +0000357
Peter Collingbourneb17a3b32014-03-02 23:34:48 +0000358DiagnosticBuilder ClangTidyCheck::diag(SourceLocation Loc, StringRef Message,
359 DiagnosticIDs::Level Level) {
360 return Context->diag(CheckName, Loc, Message, Level);
Alexander Kornienko41bfe8d2014-01-13 10:50:51 +0000361}
362
Daniel Jasperd07c8402013-07-29 08:19:24 +0000363void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) {
364 Context->setSourceManager(Result.SourceManager);
365 check(Result);
366}
367
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000368OptionsView::OptionsView(StringRef CheckName,
369 const ClangTidyOptions::OptionMap &CheckOptions)
370 : NamePrefix(CheckName.str() + "."), CheckOptions(CheckOptions) {}
371
Haojian Wuc2d75772016-02-05 11:23:59 +0000372std::string OptionsView::get(StringRef LocalName, StringRef Default) const {
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000373 const auto &Iter = CheckOptions.find(NamePrefix + LocalName.str());
374 if (Iter != CheckOptions.end())
375 return Iter->second;
376 return Default;
377}
378
Haojian Wuc2d75772016-02-05 11:23:59 +0000379std::string OptionsView::getLocalOrGlobal(StringRef LocalName,
380 StringRef Default) const {
381 auto Iter = CheckOptions.find(NamePrefix + LocalName.str());
382 if (Iter != CheckOptions.end())
383 return Iter->second;
384 // Fallback to global setting, if present.
385 Iter = CheckOptions.find(LocalName.str());
386 if (Iter != CheckOptions.end())
387 return Iter->second;
388 return Default;
389}
390
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000391void OptionsView::store(ClangTidyOptions::OptionMap &Options,
392 StringRef LocalName, StringRef Value) const {
393 Options[NamePrefix + LocalName.str()] = Value;
394}
395
396void OptionsView::store(ClangTidyOptions::OptionMap &Options,
397 StringRef LocalName, int64_t Value) const {
398 store(Options, LocalName, llvm::itostr(Value));
Alexander Kornienko41bfe8d2014-01-13 10:50:51 +0000399}
400
Alexander Kornienko33a9bcc2014-04-29 15:20:10 +0000401std::vector<std::string> getCheckNames(const ClangTidyOptions &Options) {
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000402 clang::tidy::ClangTidyContext Context(
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000403 llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
404 Options));
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000405 ClangTidyASTConsumerFactory Factory(Context);
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000406 return Factory.getCheckNames();
407}
408
409ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options) {
410 clang::tidy::ClangTidyContext Context(
411 llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
412 Options));
413 ClangTidyASTConsumerFactory Factory(Context);
414 return Factory.getCheckOptions();
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000415}
416
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000417ClangTidyStats
418runClangTidy(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
419 const tooling::CompilationDatabase &Compilations,
420 ArrayRef<std::string> InputFiles,
Samuel Benzaquenaedd9942014-10-23 17:23:20 +0000421 std::vector<ClangTidyError> *Errors, ProfileData *Profile) {
Alexander Kornienko1e1ad5c2014-05-28 15:21:14 +0000422 ClangTool Tool(Compilations, InputFiles);
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000423 clang::tidy::ClangTidyContext Context(std::move(OptionsProvider));
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000424
425 // Add extra arguments passed by the clang-tidy command-line.
426 ArgumentsAdjuster PerFileExtraArgumentsInserter =
427 [&Context](const CommandLineArguments &Args, StringRef Filename) {
428 ClangTidyOptions Opts = Context.getOptionsForFile(Filename);
429 CommandLineArguments AdjustedArgs;
430 if (Opts.ExtraArgsBefore)
431 AdjustedArgs = *Opts.ExtraArgsBefore;
432 AdjustedArgs.insert(AdjustedArgs.begin(), Args.begin(), Args.end());
433 if (Opts.ExtraArgs)
434 AdjustedArgs.insert(AdjustedArgs.end(), Opts.ExtraArgs->begin(),
435 Opts.ExtraArgs->end());
436 return AdjustedArgs;
437 };
438
439 // Remove plugins arguments.
440 ArgumentsAdjuster PluginArgumentsRemover =
441 [&Context](const CommandLineArguments &Args, StringRef Filename) {
442 CommandLineArguments AdjustedArgs;
443 for (size_t I = 0, E = Args.size(); I < E; ++I) {
444 if (I + 4 < Args.size() && Args[I] == "-Xclang" &&
445 (Args[I + 1] == "-load" || Args[I + 1] == "-add-plugin" ||
446 StringRef(Args[I + 1]).startswith("-plugin-arg-")) &&
447 Args[I + 2] == "-Xclang") {
448 I += 3;
449 } else
450 AdjustedArgs.push_back(Args[I]);
451 }
452 return AdjustedArgs;
453 };
454
Alexander Kornienko64956b52015-11-09 16:28:11 +0000455 Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter);
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000456 Tool.appendArgumentsAdjuster(PluginArgumentsRemover);
Samuel Benzaquenaedd9942014-10-23 17:23:20 +0000457 if (Profile)
458 Context.setCheckProfileData(Profile);
459
Daniel Jasperd07c8402013-07-29 08:19:24 +0000460 ClangTidyDiagnosticConsumer DiagConsumer(Context);
Manuel Klimek814f9bd2013-11-14 15:49:44 +0000461
462 Tool.setDiagnosticConsumer(&DiagConsumer);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000463
464 class ActionFactory : public FrontendActionFactory {
465 public:
Benjamin Kramer6e914242014-07-24 10:23:33 +0000466 ActionFactory(ClangTidyContext &Context) : ConsumerFactory(Context) {}
467 FrontendAction *create() override { return new Action(&ConsumerFactory); }
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000468
469 private:
470 class Action : public ASTFrontendAction {
471 public:
472 Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {}
David Blaikie680c4c82014-08-10 19:56:59 +0000473 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
474 StringRef File) override {
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000475 return Factory->CreateASTConsumer(Compiler, File);
476 }
477
478 private:
479 ClangTidyASTConsumerFactory *Factory;
480 };
481
Benjamin Kramer6e914242014-07-24 10:23:33 +0000482 ClangTidyASTConsumerFactory ConsumerFactory;
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000483 };
484
Benjamin Kramer6e914242014-07-24 10:23:33 +0000485 ActionFactory Factory(Context);
486 Tool.run(&Factory);
Alexander Kornienko826b5ad2014-05-09 12:24:09 +0000487 *Errors = Context.getErrors();
Alexander Kornienko5d174542014-05-07 09:06:53 +0000488 return Context.getStats();
Manuel Klimek814f9bd2013-11-14 15:49:44 +0000489}
490
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000491void handleErrors(const std::vector<ClangTidyError> &Errors, bool Fix,
492 unsigned &WarningsAsErrorsCount) {
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000493 ErrorReporter Reporter(Fix);
Haojian Wuf7692a22016-02-26 09:19:33 +0000494 vfs::FileSystem &FileSystem =
495 *Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
496 auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();
497 if (!InitialWorkingDir)
498 llvm::report_fatal_error("Cannot get current working path.");
499
500 for (const ClangTidyError &Error : Errors) {
501 if (!Error.BuildDirectory.empty()) {
502 // By default, the working directory of file system is the current
503 // clang-tidy running directory.
504 //
505 // Change the directory to the one used during the analysis.
506 FileSystem.setCurrentWorkingDirectory(Error.BuildDirectory);
507 }
Alexander Kornienko742790c2014-07-02 15:05:04 +0000508 Reporter.reportDiagnostic(Error);
Haojian Wuf7692a22016-02-26 09:19:33 +0000509 // Return to the initial directory to correctly resolve next Error.
510 FileSystem.setCurrentWorkingDirectory(InitialWorkingDir.get());
511 }
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000512 Reporter.Finish();
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000513 WarningsAsErrorsCount += Reporter.getWarningsAsErrorsCount();
Daniel Jasperd07c8402013-07-29 08:19:24 +0000514}
515
Benjamin Kramerfb98b742014-09-04 10:31:23 +0000516void exportReplacements(const std::vector<ClangTidyError> &Errors,
517 raw_ostream &OS) {
518 tooling::TranslationUnitReplacements TUR;
Eric Liu7e544522016-08-09 07:54:49 +0000519 for (const ClangTidyError &Error : Errors) {
520 for (const auto &FileAndFixes : Error.Fix)
521 TUR.Replacements.insert(TUR.Replacements.end(),
522 FileAndFixes.second.begin(),
523 FileAndFixes.second.end());
524 }
Benjamin Kramerfb98b742014-09-04 10:31:23 +0000525
526 yaml::Output YAML(OS);
527 YAML << TUR;
528}
529
Daniel Jasperd07c8402013-07-29 08:19:24 +0000530} // namespace tidy
531} // namespace clang