blob: d24489756251407352fd2ba1fe89cfe6e7bad796 [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 Kornienko175fefb2014-01-03 09:31:57 +000028#include "clang/Frontend/MultiplexConsumer.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000029#include "clang/Frontend/TextDiagnosticPrinter.h"
Chandler Carruth85e6e872014-01-07 20:05:01 +000030#include "clang/Lex/PPCallbacks.h"
31#include "clang/Lex/Preprocessor.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000032#include "clang/Rewrite/Frontend/FixItRewriter.h"
33#include "clang/Rewrite/Frontend/FrontendActions.h"
Alexander Kornienkod1199cb2014-01-03 17:24:20 +000034#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000035#include "clang/Tooling/Refactoring.h"
Chandler Carruth85e6e872014-01-07 20:05:01 +000036#include "clang/Tooling/Tooling.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000037#include "llvm/Support/Path.h"
Alexander Kornienko54461eb2014-02-06 14:50:10 +000038#include "llvm/Support/Process.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000039#include "llvm/Support/Signals.h"
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +000040#include <algorithm>
Daniel Jasperd07c8402013-07-29 08:19:24 +000041#include <vector>
42
43using namespace clang::ast_matchers;
44using namespace clang::driver;
45using namespace clang::tooling;
46using namespace llvm;
47
48namespace clang {
49namespace tidy {
Alexander Kornienko175fefb2014-01-03 09:31:57 +000050
Daniel Jasperd07c8402013-07-29 08:19:24 +000051namespace {
Alexander Kornienko54461eb2014-02-06 14:50:10 +000052static const char *AnalyzerCheckNamePrefix = "clang-analyzer-";
Manuel Klimek814f9bd2013-11-14 15:49:44 +000053
Alexander Kornienko54461eb2014-02-06 14:50:10 +000054static StringRef StaticAnalyzerChecks[] = {
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +000055#define GET_CHECKERS
56#define CHECKER(FULLNAME, CLASS, DESCFILE, HELPTEXT, GROUPINDEX, HIDDEN) \
57 FULLNAME,
NAKAMURA Takumi321b7d32014-01-03 10:24:51 +000058#include "../../../lib/StaticAnalyzer/Checkers/Checkers.inc"
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +000059#undef CHECKER
60#undef GET_CHECKERS
61};
62
Alexander Kornienko54461eb2014-02-06 14:50:10 +000063class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer {
64public:
65 AnalyzerDiagnosticConsumer(ClangTidyContext &Context) : Context(Context) {}
66
Alexander Kornienkocb9272f2014-02-27 13:14:51 +000067 void FlushDiagnosticsImpl(std::vector<const ento::PathDiagnostic *> &Diags,
Craig Toppera3dbe842014-03-02 10:20:11 +000068 FilesMade *filesMade) override {
Alexander Kornienko54461eb2014-02-06 14:50:10 +000069 for (std::vector<const ento::PathDiagnostic *>::iterator I = Diags.begin(),
70 E = Diags.end();
71 I != E; ++I) {
72 const ento::PathDiagnostic *PD = *I;
Alexander Kornienkod1afc702014-02-12 09:52:07 +000073 SmallString<64> CheckName(AnalyzerCheckNamePrefix);
74 CheckName += PD->getCheckName();
Alexander Kornienko54461eb2014-02-06 14:50:10 +000075 addRanges(Context.diag(CheckName, PD->getLocation().asLocation(),
76 PD->getShortDescription()),
77 PD->path.back()->getRanges());
78
79 ento::PathPieces FlatPath =
80 PD->path.flatten(/*ShouldFlattenMacros=*/true);
81 for (ento::PathPieces::const_iterator PI = FlatPath.begin(),
82 PE = FlatPath.end();
83 PI != PE; ++PI) {
84 addRanges(Context.diag(CheckName, (*PI)->getLocation().asLocation(),
85 (*PI)->getString(), DiagnosticIDs::Note),
86 (*PI)->getRanges());
87 }
88 }
89 }
90
Craig Toppera3dbe842014-03-02 10:20:11 +000091 StringRef getName() const override { return "ClangTidyDiags"; }
92 bool supportsLogicalOpControlFlow() const override { return true; }
93 bool supportsCrossFileDiagnostics() const override { return true; }
Alexander Kornienko54461eb2014-02-06 14:50:10 +000094
95private:
96 ClangTidyContext &Context;
97
98 // FIXME: Convert to operator<<(DiagnosticBuilder&, ArrayRef<SourceRange>).
99 static const DiagnosticBuilder &addRanges(const DiagnosticBuilder &DB,
100 ArrayRef<SourceRange> Ranges) {
101 for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end();
102 I != E; ++I)
103 DB << *I;
104 return DB;
105 }
106};
107
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000108} // namespace
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000109
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000110ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
111 StringRef EnableChecksRegex, StringRef DisableChecksRegex,
112 ClangTidyContext &Context)
113 : Filter(EnableChecksRegex, DisableChecksRegex), Context(Context),
114 CheckFactories(new ClangTidyCheckFactories) {
115 for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
116 E = ClangTidyModuleRegistry::end();
117 I != E; ++I) {
118 OwningPtr<ClangTidyModule> Module(I->instantiate());
119 Module->addCheckFactories(*CheckFactories);
120 }
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000121
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000122 CheckFactories->createChecks(Filter, Checks);
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000123
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000124 for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(),
125 E = Checks.end();
126 I != E; ++I) {
127 (*I)->setContext(&Context);
128 (*I)->registerMatchers(&Finder);
129 }
130}
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000131
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000132ClangTidyASTConsumerFactory::~ClangTidyASTConsumerFactory() {
133 for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(),
134 E = Checks.end();
135 I != E; ++I)
136 delete *I;
137}
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000138
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000139clang::ASTConsumer *ClangTidyASTConsumerFactory::CreateASTConsumer(
140 clang::CompilerInstance &Compiler, StringRef File) {
141 // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
142 // modify Compiler.
143 Context.setSourceManager(&Compiler.getSourceManager());
144 for (SmallVectorImpl<ClangTidyCheck *>::iterator I = Checks.begin(),
145 E = Checks.end();
146 I != E; ++I)
147 (*I)->registerPPCallbacks(Compiler);
148
Alexander Kornienko298b3822014-02-13 16:10:47 +0000149 SmallVector<ASTConsumer *, 2> Consumers;
Alexander Kornienkod68aa4c2014-02-13 16:29:39 +0000150 if (!CheckFactories->empty())
Alexander Kornienko298b3822014-02-13 16:10:47 +0000151 Consumers.push_back(Finder.newASTConsumer());
152
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000153 AnalyzerOptionsRef Options = Compiler.getAnalyzerOpts();
154 Options->CheckersControlList = getCheckersControlList();
Alexander Kornienko298b3822014-02-13 16:10:47 +0000155 if (!Options->CheckersControlList.empty()) {
156 Options->AnalysisStoreOpt = RegionStoreModel;
157 Options->AnalysisDiagOpt = PD_NONE;
158 Options->AnalyzeNestedBlocks = true;
159 Options->eagerlyAssumeBinOpBifurcation = true;
160 ento::AnalysisASTConsumer *AnalysisConsumer = ento::CreateAnalysisConsumer(
161 Compiler.getPreprocessor(), Compiler.getFrontendOpts().OutputFile,
162 Options, Compiler.getFrontendOpts().Plugins);
163 AnalysisConsumer->AddDiagnosticConsumer(
164 new AnalyzerDiagnosticConsumer(Context));
165 Consumers.push_back(AnalysisConsumer);
166 }
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000167 return new MultiplexConsumer(Consumers);
168}
169
170std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
171 std::vector<std::string> CheckNames;
172 for (ClangTidyCheckFactories::FactoryMap::const_iterator
173 I = CheckFactories->begin(),
174 E = CheckFactories->end();
175 I != E; ++I) {
176 if (Filter.IsCheckEnabled(I->first))
177 CheckNames.push_back(I->first);
178 }
179
180 CheckersList AnalyzerChecks = getCheckersControlList();
181 for (CheckersList::const_iterator I = AnalyzerChecks.begin(),
182 E = AnalyzerChecks.end();
183 I != E; ++I)
Alexander Kornienko54461eb2014-02-06 14:50:10 +0000184 CheckNames.push_back(AnalyzerCheckNamePrefix + I->first);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000185
186 std::sort(CheckNames.begin(), CheckNames.end());
187 return CheckNames;
188}
189
190ClangTidyASTConsumerFactory::CheckersList
191ClangTidyASTConsumerFactory::getCheckersControlList() {
192 CheckersList List;
Alexander Kornienko54461eb2014-02-06 14:50:10 +0000193 ArrayRef<StringRef> Checks(StaticAnalyzerChecks);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000194
195 bool AnalyzerChecksEnabled = false;
Alexander Kornienko54461eb2014-02-06 14:50:10 +0000196 for (unsigned i = 0; i < Checks.size(); ++i) {
197 std::string Checker((AnalyzerCheckNamePrefix + Checks[i]).str());
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000198 AnalyzerChecksEnabled |=
Alexander Kornienko54461eb2014-02-06 14:50:10 +0000199 Filter.IsCheckEnabled(Checker) && !Checks[i].startswith("debug");
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000200 }
201
202 if (AnalyzerChecksEnabled) {
203 // Run our regex against all possible static analyzer checkers. Note that
204 // debug checkers print values / run programs to visualize the CFG and are
205 // thus not applicable to clang-tidy in general.
206 //
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000207 // Always add all core checkers if any other static analyzer checks are
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000208 // enabled. This is currently necessary, as other path sensitive checks
209 // rely on the core checkers.
Alexander Kornienko54461eb2014-02-06 14:50:10 +0000210 for (unsigned i = 0; i < Checks.size(); ++i) {
211 std::string Checker((AnalyzerCheckNamePrefix + Checks[i]).str());
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000212
Alexander Kornienko54461eb2014-02-06 14:50:10 +0000213 if (Checks[i].startswith("core") ||
214 (!Checks[i].startswith("debug") && Filter.IsCheckEnabled(Checker)))
215 List.push_back(std::make_pair(Checks[i], true));
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000216 }
217 }
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000218 return List;
219}
Daniel Jasperd07c8402013-07-29 08:19:24 +0000220
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000221ChecksFilter::ChecksFilter(StringRef EnableChecksRegex,
222 StringRef DisableChecksRegex)
223 : EnableChecks(EnableChecksRegex), DisableChecks(DisableChecksRegex) {}
224
225bool ChecksFilter::IsCheckEnabled(StringRef Name) {
226 return EnableChecks.match(Name) && !DisableChecks.match(Name);
227}
228
Peter Collingbourneb17a3b32014-03-02 23:34:48 +0000229DiagnosticBuilder ClangTidyCheck::diag(SourceLocation Loc, StringRef Message,
230 DiagnosticIDs::Level Level) {
231 return Context->diag(CheckName, Loc, Message, Level);
Alexander Kornienko41bfe8d2014-01-13 10:50:51 +0000232}
233
Daniel Jasperd07c8402013-07-29 08:19:24 +0000234void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) {
235 Context->setSourceManager(Result.SourceManager);
236 check(Result);
237}
238
Alexander Kornienko41bfe8d2014-01-13 10:50:51 +0000239void ClangTidyCheck::setName(StringRef Name) {
240 assert(CheckName.empty());
241 CheckName = Name.str();
242}
243
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000244std::vector<std::string> getCheckNames(StringRef EnableChecksRegex,
245 StringRef DisableChecksRegex) {
246 SmallVector<ClangTidyError, 8> Errors;
247 clang::tidy::ClangTidyContext Context(&Errors);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000248 ClangTidyASTConsumerFactory Factory(EnableChecksRegex, DisableChecksRegex,
249 Context);
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000250 return Factory.getCheckNames();
251}
252
253void runClangTidy(StringRef EnableChecksRegex, StringRef DisableChecksRegex,
Daniel Jasperd07c8402013-07-29 08:19:24 +0000254 const tooling::CompilationDatabase &Compilations,
255 ArrayRef<std::string> Ranges,
256 SmallVectorImpl<ClangTidyError> *Errors) {
257 // FIXME: Ranges are currently full files. Support selecting specific
258 // (line-)ranges.
259 ClangTool Tool(Compilations, Ranges);
260 clang::tidy::ClangTidyContext Context(Errors);
261 ClangTidyDiagnosticConsumer DiagConsumer(Context);
Manuel Klimek814f9bd2013-11-14 15:49:44 +0000262
263 Tool.setDiagnosticConsumer(&DiagConsumer);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000264
265 class ActionFactory : public FrontendActionFactory {
266 public:
267 ActionFactory(ClangTidyASTConsumerFactory *ConsumerFactory)
268 : ConsumerFactory(ConsumerFactory) {}
Craig Toppera3dbe842014-03-02 10:20:11 +0000269 FrontendAction *create() override {
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000270 return new Action(ConsumerFactory);
271 }
272
273 private:
274 class Action : public ASTFrontendAction {
275 public:
276 Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {}
277 ASTConsumer *CreateASTConsumer(CompilerInstance &Compiler,
Craig Toppera3dbe842014-03-02 10:20:11 +0000278 StringRef File) override {
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000279 return Factory->CreateASTConsumer(Compiler, File);
280 }
281
282 private:
283 ClangTidyASTConsumerFactory *Factory;
284 };
285
286 ClangTidyASTConsumerFactory *ConsumerFactory;
287 };
288
289 Tool.run(new ActionFactory(new ClangTidyASTConsumerFactory(
290 EnableChecksRegex, DisableChecksRegex, Context)));
Manuel Klimek814f9bd2013-11-14 15:49:44 +0000291}
292
Alexander Kornienko54461eb2014-02-06 14:50:10 +0000293static SourceLocation getLocation(SourceManager &SourceMgr, StringRef FilePath,
294 unsigned Offset) {
295 if (FilePath.empty())
296 return SourceLocation();
297
298 const FileEntry *File = SourceMgr.getFileManager().getFile(FilePath);
299 FileID ID = SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User);
300 return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
301}
302
Manuel Klimek814f9bd2013-11-14 15:49:44 +0000303static void reportDiagnostic(const ClangTidyMessage &Message,
304 SourceManager &SourceMgr,
305 DiagnosticsEngine::Level Level,
Alexander Kornienko41bfe8d2014-01-13 10:50:51 +0000306 DiagnosticsEngine &Diags,
Alexander Kornienko54461eb2014-02-06 14:50:10 +0000307 tooling::Replacements *Fixes = NULL) {
308 SourceLocation Loc =
309 getLocation(SourceMgr, Message.FilePath, Message.FileOffset);
310 DiagnosticBuilder Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0"))
311 << Message.Message;
312 if (Fixes != NULL) {
313 for (tooling::Replacements::const_iterator I = Fixes->begin(),
314 E = Fixes->end();
315 I != E; ++I) {
316 SourceLocation FixLoc =
317 getLocation(SourceMgr, I->getFilePath(), I->getOffset());
318 Diag << FixItHint::CreateReplacement(
319 SourceRange(FixLoc, FixLoc.getLocWithOffset(I->getLength())),
320 I->getReplacementText());
321 }
Daniel Jasperd07c8402013-07-29 08:19:24 +0000322 }
Daniel Jasperd07c8402013-07-29 08:19:24 +0000323}
324
325void handleErrors(SmallVectorImpl<ClangTidyError> &Errors, bool Fix) {
326 FileManager Files((FileSystemOptions()));
Nick Lewyckyccf8e292014-02-06 22:57:16 +0000327 LangOptions LangOpts; // FIXME: use langopts from each original file
Daniel Jasperd07c8402013-07-29 08:19:24 +0000328 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
Alexander Kornienko54461eb2014-02-06 14:50:10 +0000329 DiagOpts->ShowColors = llvm::sys::Process::StandardErrHasColors();
Daniel Jasperd07c8402013-07-29 08:19:24 +0000330 DiagnosticConsumer *DiagPrinter =
331 new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts);
332 DiagnosticsEngine Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
333 &*DiagOpts, DiagPrinter);
Nick Lewyckyccf8e292014-02-06 22:57:16 +0000334 DiagPrinter->BeginSourceFile(LangOpts);
Daniel Jasperd07c8402013-07-29 08:19:24 +0000335 SourceManager SourceMgr(Diags, Files);
Nick Lewyckyccf8e292014-02-06 22:57:16 +0000336 Rewriter Rewrite(SourceMgr, LangOpts);
Daniel Jasperd07c8402013-07-29 08:19:24 +0000337 for (SmallVectorImpl<ClangTidyError>::iterator I = Errors.begin(),
338 E = Errors.end();
339 I != E; ++I) {
Alexander Kornienko41bfe8d2014-01-13 10:50:51 +0000340 reportDiagnostic(I->Message, SourceMgr, DiagnosticsEngine::Warning, Diags,
Alexander Kornienko54461eb2014-02-06 14:50:10 +0000341 &I->Fix);
Manuel Klimek814f9bd2013-11-14 15:49:44 +0000342 for (unsigned i = 0, e = I->Notes.size(); i != e; ++i) {
343 reportDiagnostic(I->Notes[i], SourceMgr, DiagnosticsEngine::Note, Diags);
344 }
Daniel Jasperd07c8402013-07-29 08:19:24 +0000345 tooling::applyAllReplacements(I->Fix, Rewrite);
346 }
347 // FIXME: Run clang-format on changes.
348 if (Fix)
349 Rewrite.overwriteChangedFiles();
350}
351
Daniel Jasperd07c8402013-07-29 08:19:24 +0000352} // namespace tidy
353} // namespace clang