blob: e465c6cbbfd2fa4264eedb87d406907f8044f899 [file] [log] [blame]
Daniel Jasperd07c8402013-07-29 08:19:241//===--- 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:2425#include "clang/Frontend/ASTConsumers.h"
26#include "clang/Frontend/CompilerInstance.h"
27#include "clang/Frontend/FrontendActions.h"
Alexander Kornienko175fefb2014-01-03 09:31:5728#include "clang/Frontend/MultiplexConsumer.h"
Daniel Jasperd07c8402013-07-29 08:19:2429#include "clang/Frontend/TextDiagnosticPrinter.h"
Chandler Carruth85e6e872014-01-07 20:05:0130#include "clang/Lex/PPCallbacks.h"
31#include "clang/Lex/Preprocessor.h"
Daniel Jasperd07c8402013-07-29 08:19:2432#include "clang/Rewrite/Frontend/FixItRewriter.h"
33#include "clang/Rewrite/Frontend/FrontendActions.h"
Alexander Kornienkod1199cb2014-01-03 17:24:2034#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
Daniel Jasperd07c8402013-07-29 08:19:2435#include "clang/Tooling/Refactoring.h"
Chandler Carruth85e6e872014-01-07 20:05:0136#include "clang/Tooling/Tooling.h"
Daniel Jasperd07c8402013-07-29 08:19:2437#include "llvm/Support/Path.h"
38#include "llvm/Support/Signals.h"
Alexander Kornienkofb9e92b2013-12-19 19:57:0539#include <algorithm>
Daniel Jasperd07c8402013-07-29 08:19:2440#include <vector>
Daniel Jasperd07c8402013-07-29 08:19:2441
42using namespace clang::ast_matchers;
43using namespace clang::driver;
44using namespace clang::tooling;
45using namespace llvm;
46
47namespace clang {
48namespace tidy {
Alexander Kornienko175fefb2014-01-03 09:31:5749
Daniel Jasperd07c8402013-07-29 08:19:2450namespace {
Alexander Kornienko175fefb2014-01-03 09:31:5751static const char *AnalyzerCheckerNamePrefix = "clang-analyzer-";
Manuel Klimek814f9bd2013-11-14 15:49:4452
Alexander Kornienkofb9e92b2013-12-19 19:57:0553static StringRef StaticAnalyzerCheckers[] = {
54#define GET_CHECKERS
55#define CHECKER(FULLNAME, CLASS, DESCFILE, HELPTEXT, GROUPINDEX, HIDDEN) \
56 FULLNAME,
NAKAMURA Takumi321b7d32014-01-03 10:24:5157#include "../../../lib/StaticAnalyzer/Checkers/Checkers.inc"
Alexander Kornienkofb9e92b2013-12-19 19:57:0558#undef CHECKER
59#undef GET_CHECKERS
60};
61
Alexander Kornienko175fefb2014-01-03 09:31:5762} // namespace
Alexander Kornienkofb9e92b2013-12-19 19:57:0563
Alexander Kornienko175fefb2014-01-03 09:31:5764ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
65 StringRef EnableChecksRegex, StringRef DisableChecksRegex,
66 ClangTidyContext &Context)
67 : Filter(EnableChecksRegex, DisableChecksRegex), Context(Context),
68 CheckFactories(new ClangTidyCheckFactories) {
69 for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
70 E = ClangTidyModuleRegistry::end();
71 I != E; ++I) {
72 OwningPtr<ClangTidyModule> Module(I->instantiate());
73 Module->addCheckFactories(*CheckFactories);
74 }
Alexander Kornienkofb9e92b2013-12-19 19:57:0575
Alexander Kornienko175fefb2014-01-03 09:31:5776 CheckFactories->createChecks(Filter, Checks);
Alexander Kornienkofb9e92b2013-12-19 19:57:0577
Alexander Kornienko175fefb2014-01-03 09:31:5778 for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(),
79 E = Checks.end();
80 I != E; ++I) {
81 (*I)->setContext(&Context);
82 (*I)->registerMatchers(&Finder);
83 }
84}
Alexander Kornienkofb9e92b2013-12-19 19:57:0585
Alexander Kornienko175fefb2014-01-03 09:31:5786ClangTidyASTConsumerFactory::~ClangTidyASTConsumerFactory() {
87 for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(),
88 E = Checks.end();
89 I != E; ++I)
90 delete *I;
91}
Alexander Kornienkofb9e92b2013-12-19 19:57:0592
Alexander Kornienko175fefb2014-01-03 09:31:5793clang::ASTConsumer *ClangTidyASTConsumerFactory::CreateASTConsumer(
94 clang::CompilerInstance &Compiler, StringRef File) {
95 // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
96 // modify Compiler.
97 Context.setSourceManager(&Compiler.getSourceManager());
98 for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(),
99 E = Checks.end();
100 I != E; ++I)
101 (*I)->registerPPCallbacks(Compiler);
102
103 AnalyzerOptionsRef Options = Compiler.getAnalyzerOpts();
104 Options->CheckersControlList = getCheckersControlList();
105 Options->AnalysisStoreOpt = RegionStoreModel;
106 Options->AnalysisDiagOpt = PD_TEXT;
107 Options->AnalyzeNestedBlocks = true;
108 Options->eagerlyAssumeBinOpBifurcation = true;
109 ASTConsumer *Consumers[] = {
110 Finder.newASTConsumer(),
111 ento::CreateAnalysisConsumer(Compiler.getPreprocessor(),
112 Compiler.getFrontendOpts().OutputFile, Options,
113 Compiler.getFrontendOpts().Plugins)
114 };
115 return new MultiplexConsumer(Consumers);
116}
117
118std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
119 std::vector<std::string> CheckNames;
120 for (ClangTidyCheckFactories::FactoryMap::const_iterator
121 I = CheckFactories->begin(),
122 E = CheckFactories->end();
123 I != E; ++I) {
124 if (Filter.IsCheckEnabled(I->first))
125 CheckNames.push_back(I->first);
126 }
127
128 CheckersList AnalyzerChecks = getCheckersControlList();
129 for (CheckersList::const_iterator I = AnalyzerChecks.begin(),
130 E = AnalyzerChecks.end();
131 I != E; ++I)
132 CheckNames.push_back(AnalyzerCheckerNamePrefix + I->first);
133
134 std::sort(CheckNames.begin(), CheckNames.end());
135 return CheckNames;
136}
137
138ClangTidyASTConsumerFactory::CheckersList
139ClangTidyASTConsumerFactory::getCheckersControlList() {
140 CheckersList List;
141 ArrayRef<StringRef> Checkers(StaticAnalyzerCheckers);
142
143 bool AnalyzerChecksEnabled = false;
144 for (unsigned i = 0; i < Checkers.size(); ++i) {
145 std::string Checker((AnalyzerCheckerNamePrefix + Checkers[i]).str());
146 AnalyzerChecksEnabled |=
147 Filter.IsCheckEnabled(Checker) && !Checkers[i].startswith("debug");
148 }
149
150 if (AnalyzerChecksEnabled) {
151 // Run our regex against all possible static analyzer checkers. Note that
152 // debug checkers print values / run programs to visualize the CFG and are
153 // thus not applicable to clang-tidy in general.
154 //
Alexander Kornienkofb9e92b2013-12-19 19:57:05155 // Always add all core checkers if any other static analyzer checks are
Alexander Kornienko175fefb2014-01-03 09:31:57156 // enabled. This is currently necessary, as other path sensitive checks
157 // rely on the core checkers.
Alexander Kornienkofb9e92b2013-12-19 19:57:05158 for (unsigned i = 0; i < Checkers.size(); ++i) {
159 std::string Checker((AnalyzerCheckerNamePrefix + Checkers[i]).str());
160
161 if (Checkers[i].startswith("core") ||
162 (!Checkers[i].startswith("debug") && Filter.IsCheckEnabled(Checker)))
163 List.push_back(std::make_pair(Checkers[i], true));
164 }
165 }
Alexander Kornienko175fefb2014-01-03 09:31:57166 return List;
167}
Daniel Jasperd07c8402013-07-29 08:19:24168
Alexander Kornienkofb9e92b2013-12-19 19:57:05169ChecksFilter::ChecksFilter(StringRef EnableChecksRegex,
170 StringRef DisableChecksRegex)
171 : EnableChecks(EnableChecksRegex), DisableChecks(DisableChecksRegex) {}
172
173bool ChecksFilter::IsCheckEnabled(StringRef Name) {
174 return EnableChecks.match(Name) && !DisableChecks.match(Name);
175}
176
Manuel Klimek814f9bd2013-11-14 15:49:44177ClangTidyMessage::ClangTidyMessage(StringRef Message) : Message(Message) {}
178
179ClangTidyMessage::ClangTidyMessage(StringRef Message,
180 const SourceManager &Sources,
181 SourceLocation Loc)
182 : Message(Message) {
Daniel Jasperd07c8402013-07-29 08:19:24183 FilePath = Sources.getFilename(Loc);
184 FileOffset = Sources.getFileOffset(Loc);
185}
186
Manuel Klimek814f9bd2013-11-14 15:49:44187ClangTidyError::ClangTidyError(const ClangTidyMessage &Message)
188 : Message(Message) {}
189
Daniel Jasperd07c8402013-07-29 08:19:24190DiagnosticBuilder ClangTidyContext::Diag(SourceLocation Loc,
191 StringRef Message) {
192 return DiagEngine->Report(
193 Loc, DiagEngine->getCustomDiagID(DiagnosticsEngine::Warning, Message));
194}
195
196void ClangTidyContext::setDiagnosticsEngine(DiagnosticsEngine *Engine) {
197 DiagEngine = Engine;
198}
199
200void ClangTidyContext::setSourceManager(SourceManager *SourceMgr) {
201 DiagEngine->setSourceManager(SourceMgr);
202}
203
204/// \brief Store a \c ClangTidyError.
205void ClangTidyContext::storeError(const ClangTidyError &Error) {
206 Errors->push_back(Error);
207}
208
209void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) {
210 Context->setSourceManager(Result.SourceManager);
211 check(Result);
212}
213
Alexander Kornienkofb9e92b2013-12-19 19:57:05214std::vector<std::string> getCheckNames(StringRef EnableChecksRegex,
215 StringRef DisableChecksRegex) {
216 SmallVector<ClangTidyError, 8> Errors;
217 clang::tidy::ClangTidyContext Context(&Errors);
Alexander Kornienko175fefb2014-01-03 09:31:57218 ClangTidyASTConsumerFactory Factory(EnableChecksRegex, DisableChecksRegex,
219 Context);
Alexander Kornienkofb9e92b2013-12-19 19:57:05220 return Factory.getCheckNames();
221}
222
223void runClangTidy(StringRef EnableChecksRegex, StringRef DisableChecksRegex,
Daniel Jasperd07c8402013-07-29 08:19:24224 const tooling::CompilationDatabase &Compilations,
225 ArrayRef<std::string> Ranges,
226 SmallVectorImpl<ClangTidyError> *Errors) {
227 // FIXME: Ranges are currently full files. Support selecting specific
228 // (line-)ranges.
229 ClangTool Tool(Compilations, Ranges);
230 clang::tidy::ClangTidyContext Context(Errors);
231 ClangTidyDiagnosticConsumer DiagConsumer(Context);
Manuel Klimek814f9bd2013-11-14 15:49:44232
233 Tool.setDiagnosticConsumer(&DiagConsumer);
Alexander Kornienko175fefb2014-01-03 09:31:57234
235 class ActionFactory : public FrontendActionFactory {
236 public:
237 ActionFactory(ClangTidyASTConsumerFactory *ConsumerFactory)
238 : ConsumerFactory(ConsumerFactory) {}
239 FrontendAction *create() LLVM_OVERRIDE {
240 return new Action(ConsumerFactory);
241 }
242
243 private:
244 class Action : public ASTFrontendAction {
245 public:
246 Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {}
247 ASTConsumer *CreateASTConsumer(CompilerInstance &Compiler,
248 StringRef File) LLVM_OVERRIDE {
249 return Factory->CreateASTConsumer(Compiler, File);
250 }
251
252 private:
253 ClangTidyASTConsumerFactory *Factory;
254 };
255
256 ClangTidyASTConsumerFactory *ConsumerFactory;
257 };
258
259 Tool.run(new ActionFactory(new ClangTidyASTConsumerFactory(
260 EnableChecksRegex, DisableChecksRegex, Context)));
Manuel Klimek814f9bd2013-11-14 15:49:44261}
262
263static void reportDiagnostic(const ClangTidyMessage &Message,
264 SourceManager &SourceMgr,
265 DiagnosticsEngine::Level Level,
266 DiagnosticsEngine &Diags) {
267 SourceLocation Loc;
268 if (!Message.FilePath.empty()) {
269 const FileEntry *File =
270 SourceMgr.getFileManager().getFile(Message.FilePath);
271 FileID ID = SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User);
272 Loc = SourceMgr.getLocForStartOfFile(ID);
273 Loc = Loc.getLocWithOffset(Message.FileOffset);
Daniel Jasperd07c8402013-07-29 08:19:24274 }
Manuel Klimek814f9bd2013-11-14 15:49:44275 Diags.Report(Loc, Diags.getCustomDiagID(Level, Message.Message));
Daniel Jasperd07c8402013-07-29 08:19:24276}
277
278void handleErrors(SmallVectorImpl<ClangTidyError> &Errors, bool Fix) {
279 FileManager Files((FileSystemOptions()));
280 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
281 DiagnosticConsumer *DiagPrinter =
282 new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts);
283 DiagnosticsEngine Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
284 &*DiagOpts, DiagPrinter);
285 DiagPrinter->BeginSourceFile(LangOptions());
286 SourceManager SourceMgr(Diags, Files);
287 Rewriter Rewrite(SourceMgr, LangOptions());
288 for (SmallVectorImpl<ClangTidyError>::iterator I = Errors.begin(),
289 E = Errors.end();
290 I != E; ++I) {
Manuel Klimek814f9bd2013-11-14 15:49:44291 reportDiagnostic(I->Message, SourceMgr, DiagnosticsEngine::Warning, Diags);
292 for (unsigned i = 0, e = I->Notes.size(); i != e; ++i) {
293 reportDiagnostic(I->Notes[i], SourceMgr, DiagnosticsEngine::Note, Diags);
294 }
Daniel Jasperd07c8402013-07-29 08:19:24295 tooling::applyAllReplacements(I->Fix, Rewrite);
296 }
297 // FIXME: Run clang-format on changes.
298 if (Fix)
299 Rewrite.overwriteChangedFiles();
300}
301
Daniel Jasperd07c8402013-07-29 08:19:24302} // namespace tidy
303} // namespace clang