blob: 65c10f4cfefda98fa525b034c5f8c8bcc9f9a74c [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"
Alexander Kornienkod1199cb2014-01-03 17:24:20 +000035#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000036#include "clang/Tooling/Refactoring.h"
Chandler Carruth85e6e872014-01-07 20:05:01 +000037#include "clang/Tooling/Tooling.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000038#include "llvm/Support/Path.h"
Alexander Kornienko54461eb2014-02-06 14:50:10 +000039#include "llvm/Support/Process.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000040#include "llvm/Support/Signals.h"
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +000041#include <algorithm>
Alexander Kornienko38d81b42014-03-27 10:24:11 +000042#include <utility>
Daniel Jasperd07c8402013-07-29 08:19:24 +000043
44using namespace clang::ast_matchers;
45using namespace clang::driver;
46using namespace clang::tooling;
47using namespace llvm;
48
49namespace clang {
50namespace tidy {
Alexander Kornienko175fefb2014-01-03 09:31:57 +000051
Daniel Jasperd07c8402013-07-29 08:19:24 +000052namespace {
Alexander Kornienko54461eb2014-02-06 14:50:10 +000053static const char *AnalyzerCheckNamePrefix = "clang-analyzer-";
Manuel Klimek814f9bd2013-11-14 15:49:44 +000054
Alexander Kornienko54461eb2014-02-06 14:50:10 +000055static StringRef StaticAnalyzerChecks[] = {
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +000056#define GET_CHECKERS
57#define CHECKER(FULLNAME, CLASS, DESCFILE, HELPTEXT, GROUPINDEX, HIDDEN) \
58 FULLNAME,
NAKAMURA Takumi321b7d32014-01-03 10:24:51 +000059#include "../../../lib/StaticAnalyzer/Checkers/Checkers.inc"
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +000060#undef CHECKER
61#undef GET_CHECKERS
62};
63
Alexander Kornienko54461eb2014-02-06 14:50:10 +000064class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer {
65public:
66 AnalyzerDiagnosticConsumer(ClangTidyContext &Context) : Context(Context) {}
67
Alexander Kornienkocb9272f2014-02-27 13:14:51 +000068 void FlushDiagnosticsImpl(std::vector<const ento::PathDiagnostic *> &Diags,
Craig Toppera3dbe842014-03-02 10:20:11 +000069 FilesMade *filesMade) override {
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +000070 for (const ento::PathDiagnostic *PD : Diags) {
Alexander Kornienkod1afc702014-02-12 09:52:07 +000071 SmallString<64> CheckName(AnalyzerCheckNamePrefix);
72 CheckName += PD->getCheckName();
Alexander Kornienko95cd50f2014-03-06 13:24:28 +000073 Context.diag(CheckName, PD->getLocation().asLocation(),
74 PD->getShortDescription())
75 << PD->path.back()->getRanges();
Alexander Kornienko54461eb2014-02-06 14:50:10 +000076
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +000077 for (const auto &DiagPiece :
78 PD->path.flatten(/*ShouldFlattenMacros=*/true)) {
Alexander Kornienko95cd50f2014-03-06 13:24:28 +000079 Context.diag(CheckName, DiagPiece->getLocation().asLocation(),
80 DiagPiece->getString(), DiagnosticIDs::Note)
81 << DiagPiece->getRanges();
Alexander Kornienko54461eb2014-02-06 14:50:10 +000082 }
83 }
84 }
85
Craig Toppera3dbe842014-03-02 10:20:11 +000086 StringRef getName() const override { return "ClangTidyDiags"; }
87 bool supportsLogicalOpControlFlow() const override { return true; }
88 bool supportsCrossFileDiagnostics() const override { return true; }
Alexander Kornienko54461eb2014-02-06 14:50:10 +000089
90private:
91 ClangTidyContext &Context;
Alexander Kornienko54461eb2014-02-06 14:50:10 +000092};
93
Alexander Kornienko38d81b42014-03-27 10:24:11 +000094class ErrorReporter {
95public:
96 ErrorReporter(bool ApplyFixes)
97 : Files(FileSystemOptions()), DiagOpts(new DiagnosticOptions()),
98 DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)),
99 Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
100 DiagPrinter),
101 SourceMgr(Diags, Files), Rewrite(SourceMgr, LangOpts),
NAKAMURA Takumi4dd18132014-03-27 14:53:37 +0000102 ApplyFixes(ApplyFixes), TotalFixes(0), AppliedFixes(0) {
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000103 DiagOpts->ShowColors = llvm::sys::Process::StandardOutHasColors();
104 DiagPrinter->BeginSourceFile(LangOpts);
105 }
106
107 void reportDiagnostic(const ClangTidyMessage &Message,
108 DiagnosticsEngine::Level Level,
109 const tooling::Replacements *Fixes = nullptr) {
110 SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
111 // Contains a pair for each attempted fix: location and whether the fix was
112 // applied successfully.
113 SmallVector<std::pair<SourceLocation, bool>, 4> FixLocations;
114 {
115 DiagnosticBuilder Diag =
116 Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0"))
117 << Message.Message;
118 if (Fixes != NULL) {
119 for (const tooling::Replacement &Fix : *Fixes) {
120 SourceLocation FixLoc =
121 getLocation(Fix.getFilePath(), Fix.getOffset());
122 SourceLocation FixEndLoc = FixLoc.getLocWithOffset(Fix.getLength());
123 Diag << FixItHint::CreateReplacement(SourceRange(FixLoc, FixEndLoc),
124 Fix.getReplacementText());
125 ++TotalFixes;
126 if (ApplyFixes) {
127 bool Success = Fix.isApplicable() && Fix.apply(Rewrite);
128 if (Success)
129 ++AppliedFixes;
130 FixLocations.push_back(std::make_pair(FixLoc, Success));
131 }
132 }
133 }
134 }
135 for (auto Fix : FixLocations) {
136 Diags.Report(Fix.first, Fix.second ? diag::note_fixit_applied
137 : diag::note_fixit_failed);
138 }
139 }
140
141 void Finish() {
142 // FIXME: Run clang-format on changes.
143 if (ApplyFixes && TotalFixes > 0) {
144 llvm::errs() << "clang-tidy applied " << AppliedFixes << " of "
145 << TotalFixes << " suggested fixes.\n";
146 Rewrite.overwriteChangedFiles();
147 }
148 }
149
150private:
151 SourceLocation getLocation(StringRef FilePath, unsigned Offset) {
152 if (FilePath.empty())
153 return SourceLocation();
154
155 const FileEntry *File = SourceMgr.getFileManager().getFile(FilePath);
156 FileID ID = SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User);
157 return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
158 }
159
160 FileManager Files;
161 LangOptions LangOpts; // FIXME: use langopts from each original file
162 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
163 DiagnosticConsumer *DiagPrinter;
164 DiagnosticsEngine Diags;
165 SourceManager SourceMgr;
166 Rewriter Rewrite;
167 bool ApplyFixes;
NAKAMURA Takumi4dd18132014-03-27 14:53:37 +0000168 unsigned TotalFixes;
169 unsigned AppliedFixes;
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000170};
171
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000172} // namespace
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000173
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000174ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
Alex McCarthyfec08c72014-04-30 14:09:24 +0000175 ClangTidyContext &Context, const ClangTidyOptions &Options)
176 : Context(Context), CheckFactories(new ClangTidyCheckFactories),
177 Options(Options) {
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000178 for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
179 E = ClangTidyModuleRegistry::end();
180 I != E; ++I) {
Ahmed Charles6a2dc5c2014-03-09 09:24:40 +0000181 std::unique_ptr<ClangTidyModule> Module(I->instantiate());
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000182 Module->addCheckFactories(*CheckFactories);
183 }
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000184
Alexander Kornienko09952d22014-03-20 09:38:22 +0000185 CheckFactories->createChecks(Context.getChecksFilter(), Checks);
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000186
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000187 for (ClangTidyCheck *Check : Checks) {
188 Check->setContext(&Context);
189 Check->registerMatchers(&Finder);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000190 }
191}
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000192
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000193ClangTidyASTConsumerFactory::~ClangTidyASTConsumerFactory() {
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000194 for (ClangTidyCheck *Check : Checks)
195 delete Check;
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000196}
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000197
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000198clang::ASTConsumer *ClangTidyASTConsumerFactory::CreateASTConsumer(
199 clang::CompilerInstance &Compiler, StringRef File) {
200 // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
201 // modify Compiler.
202 Context.setSourceManager(&Compiler.getSourceManager());
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000203 for (ClangTidyCheck *Check : Checks)
204 Check->registerPPCallbacks(Compiler);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000205
Alexander Kornienko298b3822014-02-13 16:10:47 +0000206 SmallVector<ASTConsumer *, 2> Consumers;
Alexander Kornienkod68aa4c2014-02-13 16:29:39 +0000207 if (!CheckFactories->empty())
Alexander Kornienko298b3822014-02-13 16:10:47 +0000208 Consumers.push_back(Finder.newASTConsumer());
209
Alex McCarthyfec08c72014-04-30 14:09:24 +0000210 AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts();
211 // FIXME: Remove this option once clang's cfg-temporary-dtors option defaults
212 // to true.
213 AnalyzerOptions->Config["cfg-temporary-dtors"] =
214 Options.AnalyzeTemporaryDtors ? "true" : "false";
215
216 AnalyzerOptions->CheckersControlList = getCheckersControlList();
217 if (!AnalyzerOptions->CheckersControlList.empty()) {
218 AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel;
219 AnalyzerOptions->AnalysisDiagOpt = PD_NONE;
220 AnalyzerOptions->AnalyzeNestedBlocks = true;
221 AnalyzerOptions->eagerlyAssumeBinOpBifurcation = true;
Alexander Kornienko298b3822014-02-13 16:10:47 +0000222 ento::AnalysisASTConsumer *AnalysisConsumer = ento::CreateAnalysisConsumer(
223 Compiler.getPreprocessor(), Compiler.getFrontendOpts().OutputFile,
Alex McCarthyfec08c72014-04-30 14:09:24 +0000224 AnalyzerOptions, Compiler.getFrontendOpts().Plugins);
Alexander Kornienko298b3822014-02-13 16:10:47 +0000225 AnalysisConsumer->AddDiagnosticConsumer(
226 new AnalyzerDiagnosticConsumer(Context));
227 Consumers.push_back(AnalysisConsumer);
228 }
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000229 return new MultiplexConsumer(Consumers);
230}
231
232std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
233 std::vector<std::string> CheckNames;
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000234 for (const auto &CheckFactory : *CheckFactories) {
Alexander Kornienko09952d22014-03-20 09:38:22 +0000235 if (Context.getChecksFilter().isCheckEnabled(CheckFactory.first))
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000236 CheckNames.push_back(CheckFactory.first);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000237 }
238
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000239 for (const auto &AnalyzerCheck : getCheckersControlList())
240 CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000241
242 std::sort(CheckNames.begin(), CheckNames.end());
243 return CheckNames;
244}
245
246ClangTidyASTConsumerFactory::CheckersList
247ClangTidyASTConsumerFactory::getCheckersControlList() {
248 CheckersList List;
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000249
250 bool AnalyzerChecksEnabled = false;
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000251 for (StringRef CheckName : StaticAnalyzerChecks) {
252 std::string Checker((AnalyzerCheckNamePrefix + CheckName).str());
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000253 AnalyzerChecksEnabled |=
Alexander Kornienko09952d22014-03-20 09:38:22 +0000254 Context.getChecksFilter().isCheckEnabled(Checker) &&
255 !CheckName.startswith("debug");
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000256 }
257
258 if (AnalyzerChecksEnabled) {
259 // Run our regex against all possible static analyzer checkers. Note that
260 // debug checkers print values / run programs to visualize the CFG and are
261 // thus not applicable to clang-tidy in general.
262 //
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000263 // Always add all core checkers if any other static analyzer checks are
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000264 // enabled. This is currently necessary, as other path sensitive checks
265 // rely on the core checkers.
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000266 for (StringRef CheckName : StaticAnalyzerChecks) {
267 std::string Checker((AnalyzerCheckNamePrefix + CheckName).str());
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000268
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000269 if (CheckName.startswith("core") ||
Alexander Kornienko09952d22014-03-20 09:38:22 +0000270 (!CheckName.startswith("debug") &&
271 Context.getChecksFilter().isCheckEnabled(Checker)))
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000272 List.push_back(std::make_pair(CheckName, true));
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000273 }
274 }
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000275 return List;
276}
Daniel Jasperd07c8402013-07-29 08:19:24 +0000277
Peter Collingbourneb17a3b32014-03-02 23:34:48 +0000278DiagnosticBuilder ClangTidyCheck::diag(SourceLocation Loc, StringRef Message,
279 DiagnosticIDs::Level Level) {
280 return Context->diag(CheckName, Loc, Message, Level);
Alexander Kornienko41bfe8d2014-01-13 10:50:51 +0000281}
282
Daniel Jasperd07c8402013-07-29 08:19:24 +0000283void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) {
284 Context->setSourceManager(Result.SourceManager);
285 check(Result);
286}
287
Alexander Kornienko41bfe8d2014-01-13 10:50:51 +0000288void ClangTidyCheck::setName(StringRef Name) {
289 assert(CheckName.empty());
290 CheckName = Name.str();
291}
292
Alexander Kornienko33a9bcc2014-04-29 15:20:10 +0000293std::vector<std::string> getCheckNames(const ClangTidyOptions &Options) {
Alexander Kornienko826b5ad2014-05-09 12:24:09 +0000294 clang::tidy::ClangTidyContext Context(Options);
Alex McCarthyfec08c72014-04-30 14:09:24 +0000295 ClangTidyASTConsumerFactory Factory(Context, Options);
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000296 return Factory.getCheckNames();
297}
298
Alexander Kornienko5d174542014-05-07 09:06:53 +0000299ClangTidyStats runClangTidy(const ClangTidyOptions &Options,
300 const tooling::CompilationDatabase &Compilations,
Alexander Kornienko1e1ad5c2014-05-28 15:21:14 +0000301 ArrayRef<std::string> InputFiles,
Alexander Kornienko826b5ad2014-05-09 12:24:09 +0000302 std::vector<ClangTidyError> *Errors) {
Alexander Kornienko1e1ad5c2014-05-28 15:21:14 +0000303 ClangTool Tool(Compilations, InputFiles);
Alexander Kornienko826b5ad2014-05-09 12:24:09 +0000304 clang::tidy::ClangTidyContext Context(Options);
Daniel Jasperd07c8402013-07-29 08:19:24 +0000305 ClangTidyDiagnosticConsumer DiagConsumer(Context);
Manuel Klimek814f9bd2013-11-14 15:49:44 +0000306
307 Tool.setDiagnosticConsumer(&DiagConsumer);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000308
309 class ActionFactory : public FrontendActionFactory {
310 public:
311 ActionFactory(ClangTidyASTConsumerFactory *ConsumerFactory)
312 : ConsumerFactory(ConsumerFactory) {}
Alexander Kornienko21f3b772014-03-05 13:01:24 +0000313 FrontendAction *create() override { return new Action(ConsumerFactory); }
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000314
315 private:
316 class Action : public ASTFrontendAction {
317 public:
318 Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {}
319 ASTConsumer *CreateASTConsumer(CompilerInstance &Compiler,
Craig Toppera3dbe842014-03-02 10:20:11 +0000320 StringRef File) override {
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000321 return Factory->CreateASTConsumer(Compiler, File);
322 }
323
324 private:
325 ClangTidyASTConsumerFactory *Factory;
326 };
327
328 ClangTidyASTConsumerFactory *ConsumerFactory;
329 };
330
Alex McCarthyfec08c72014-04-30 14:09:24 +0000331 Tool.run(new ActionFactory(new ClangTidyASTConsumerFactory(Context, Options)));
Alexander Kornienko826b5ad2014-05-09 12:24:09 +0000332 *Errors = Context.getErrors();
Alexander Kornienko5d174542014-05-07 09:06:53 +0000333 return Context.getStats();
Manuel Klimek814f9bd2013-11-14 15:49:44 +0000334}
335
Alexander Kornienko826b5ad2014-05-09 12:24:09 +0000336void handleErrors(const std::vector<ClangTidyError> &Errors, bool Fix) {
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000337 ErrorReporter Reporter(Fix);
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000338 for (const ClangTidyError &Error : Errors) {
Alexander Kornienko348cae82014-06-02 20:44:32 +0000339 Reporter.reportDiagnostic(
340 Error.Message, static_cast<DiagnosticsEngine::Level>(Error.DiagLevel),
341 &Error.Fix);
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000342 for (const ClangTidyMessage &Note : Error.Notes)
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000343 Reporter.reportDiagnostic(Note, DiagnosticsEngine::Note);
Daniel Jasperd07c8402013-07-29 08:19:24 +0000344 }
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000345 Reporter.Finish();
Daniel Jasperd07c8402013-07-29 08:19:24 +0000346}
347
Daniel Jasperd07c8402013-07-29 08:19:24 +0000348} // namespace tidy
349} // namespace clang