blob: f497fd749afcb3a19d1118671e58f4d44f33e8f1 [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"
Roman Lebedev12152512018-05-08 13:14:21 +000021#include "ClangTidyProfiling.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000022#include "clang/AST/ASTConsumer.h"
23#include "clang/AST/ASTContext.h"
24#include "clang/AST/Decl.h"
25#include "clang/ASTMatchers/ASTMatchFinder.h"
Alexander Kornienko98d33912016-10-17 17:25:02 +000026#include "clang/Format/Format.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000027#include "clang/Frontend/ASTConsumers.h"
28#include "clang/Frontend/CompilerInstance.h"
29#include "clang/Frontend/FrontendActions.h"
Alexander Kornienko38d81b42014-03-27 10:24:11 +000030#include "clang/Frontend/FrontendDiagnostic.h"
Alexander Kornienko175fefb2014-01-03 09:31:57 +000031#include "clang/Frontend/MultiplexConsumer.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000032#include "clang/Frontend/TextDiagnosticPrinter.h"
Chandler Carruth85e6e872014-01-07 20:05:01 +000033#include "clang/Lex/PPCallbacks.h"
34#include "clang/Lex/Preprocessor.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000035#include "clang/Rewrite/Frontend/FixItRewriter.h"
36#include "clang/Rewrite/Frontend/FrontendActions.h"
Benjamin Kramer8ff8fdf2016-07-18 19:21:22 +000037#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
Alexander Kornienkod1199cb2014-01-03 17:24:20 +000038#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
Alexander Kornienko563de792017-01-03 14:36:13 +000039#include "clang/Tooling/DiagnosticsYaml.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000040#include "clang/Tooling/Refactoring.h"
Benjamin Kramerfb98b742014-09-04 10:31:23 +000041#include "clang/Tooling/ReplacementsYaml.h"
Chandler Carruth85e6e872014-01-07 20:05:01 +000042#include "clang/Tooling/Tooling.h"
Alexander Kornienko54461eb2014-02-06 14:50:10 +000043#include "llvm/Support/Process.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000044#include "llvm/Support/Signals.h"
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +000045#include <algorithm>
Alexander Kornienko38d81b42014-03-27 10:24:11 +000046#include <utility>
Daniel Jasperd07c8402013-07-29 08:19:24 +000047
48using namespace clang::ast_matchers;
49using namespace clang::driver;
50using namespace clang::tooling;
51using namespace llvm;
52
John Brawn4d79ec72016-08-05 11:01:08 +000053LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry)
NAKAMURA Takumi71b982b2014-07-03 14:12:47 +000054
Daniel Jasperd07c8402013-07-29 08:19:24 +000055namespace clang {
56namespace tidy {
Alexander Kornienko175fefb2014-01-03 09:31:57 +000057
Daniel Jasperd07c8402013-07-29 08:19:24 +000058namespace {
Alexander Kornienko54461eb2014-02-06 14:50:10 +000059static const char *AnalyzerCheckNamePrefix = "clang-analyzer-";
Manuel Klimek814f9bd2013-11-14 15:49:44 +000060
Alexander Kornienko54461eb2014-02-06 14:50:10 +000061class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer {
62public:
63 AnalyzerDiagnosticConsumer(ClangTidyContext &Context) : Context(Context) {}
64
Alexander Kornienkocb9272f2014-02-27 13:14:51 +000065 void FlushDiagnosticsImpl(std::vector<const ento::PathDiagnostic *> &Diags,
Craig Toppera3dbe842014-03-02 10:20:11 +000066 FilesMade *filesMade) override {
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +000067 for (const ento::PathDiagnostic *PD : Diags) {
Alexander Kornienkod1afc702014-02-12 09:52:07 +000068 SmallString<64> CheckName(AnalyzerCheckNamePrefix);
69 CheckName += PD->getCheckName();
Alexander Kornienko95cd50f2014-03-06 13:24:28 +000070 Context.diag(CheckName, PD->getLocation().asLocation(),
71 PD->getShortDescription())
72 << PD->path.back()->getRanges();
Alexander Kornienko54461eb2014-02-06 14:50:10 +000073
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +000074 for (const auto &DiagPiece :
75 PD->path.flatten(/*ShouldFlattenMacros=*/true)) {
Alexander Kornienko95cd50f2014-03-06 13:24:28 +000076 Context.diag(CheckName, DiagPiece->getLocation().asLocation(),
77 DiagPiece->getString(), DiagnosticIDs::Note)
78 << DiagPiece->getRanges();
Alexander Kornienko54461eb2014-02-06 14:50:10 +000079 }
80 }
81 }
82
Craig Toppera3dbe842014-03-02 10:20:11 +000083 StringRef getName() const override { return "ClangTidyDiags"; }
84 bool supportsLogicalOpControlFlow() const override { return true; }
85 bool supportsCrossFileDiagnostics() const override { return true; }
Alexander Kornienko54461eb2014-02-06 14:50:10 +000086
87private:
88 ClangTidyContext &Context;
Alexander Kornienko54461eb2014-02-06 14:50:10 +000089};
90
Alexander Kornienko38d81b42014-03-27 10:24:11 +000091class ErrorReporter {
92public:
Ilya Biryukova67b3662018-01-23 12:31:06 +000093 ErrorReporter(ClangTidyContext &Context, bool ApplyFixes,
94 llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS)
95 : Files(FileSystemOptions(), BaseFS), DiagOpts(new DiagnosticOptions()),
Alexander Kornienko38d81b42014-03-27 10:24:11 +000096 DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)),
97 Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
98 DiagPrinter),
Alexander Kornienko25613202017-04-06 13:41:29 +000099 SourceMgr(Diags, Files), Context(Context), ApplyFixes(ApplyFixes),
100 TotalFixes(0), AppliedFixes(0), WarningsAsErrors(0) {
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000101 DiagOpts->ShowColors = llvm::sys::Process::StandardOutHasColors();
102 DiagPrinter->BeginSourceFile(LangOpts);
103 }
104
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000105 SourceManager &getSourceManager() { return SourceMgr; }
Haojian Wuf7692a22016-02-26 09:19:33 +0000106
Alexander Kornienko742790c2014-07-02 15:05:04 +0000107 void reportDiagnostic(const ClangTidyError &Error) {
Alexander Kornienko563de792017-01-03 14:36:13 +0000108 const tooling::DiagnosticMessage &Message = Error.Message;
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000109 SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
110 // Contains a pair for each attempted fix: location and whether the fix was
111 // applied successfully.
112 SmallVector<std::pair<SourceLocation, bool>, 4> FixLocations;
113 {
Alexander Kornienko742790c2014-07-02 15:05:04 +0000114 auto Level = static_cast<DiagnosticsEngine::Level>(Error.DiagLevel);
Alexander Kornienko563de792017-01-03 14:36:13 +0000115 std::string Name = Error.DiagnosticName;
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000116 if (Error.IsWarningAsError) {
117 Name += ",-warnings-as-errors";
118 Level = DiagnosticsEngine::Error;
119 WarningsAsErrors++;
120 }
Alexander Kornienko58fe57a2015-11-16 13:06:15 +0000121 auto Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0 [%1]"))
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000122 << Message.Message << Name;
Eric Liu7e544522016-08-09 07:54:49 +0000123 for (const auto &FileAndReplacements : Error.Fix) {
Alexander Kornienko98d33912016-10-17 17:25:02 +0000124 for (const auto &Repl : FileAndReplacements.second) {
Eric Liu7e544522016-08-09 07:54:49 +0000125 // Retrieve the source range for applicable fixes. Macro definitions
126 // on the command line have locations in a virtual buffer and don't
127 // have valid file paths and are therefore not applicable.
128 SourceRange Range;
129 SourceLocation FixLoc;
Alexander Kornienko98d33912016-10-17 17:25:02 +0000130 ++TotalFixes;
131 bool CanBeApplied = false;
132 if (Repl.isApplicable()) {
133 SmallString<128> FixAbsoluteFilePath = Repl.getFilePath();
Eric Liu7e544522016-08-09 07:54:49 +0000134 Files.makeAbsolutePath(FixAbsoluteFilePath);
Alexander Kornienko98d33912016-10-17 17:25:02 +0000135 if (ApplyFixes) {
136 tooling::Replacement R(FixAbsoluteFilePath, Repl.getOffset(),
137 Repl.getLength(),
138 Repl.getReplacementText());
139 Replacements &Replacements = FileReplacements[R.getFilePath()];
140 llvm::Error Err = Replacements.add(R);
141 if (Err) {
142 // FIXME: Implement better conflict handling.
143 llvm::errs() << "Trying to resolve conflict: "
144 << llvm::toString(std::move(Err)) << "\n";
145 unsigned NewOffset =
146 Replacements.getShiftedCodePosition(R.getOffset());
147 unsigned NewLength = Replacements.getShiftedCodePosition(
148 R.getOffset() + R.getLength()) -
149 NewOffset;
150 if (NewLength == R.getLength()) {
151 R = Replacement(R.getFilePath(), NewOffset, NewLength,
152 R.getReplacementText());
153 Replacements = Replacements.merge(tooling::Replacements(R));
154 CanBeApplied = true;
155 ++AppliedFixes;
156 } else {
157 llvm::errs()
158 << "Can't resolve conflict, skipping the replacement.\n";
159 }
160
161 } else {
162 CanBeApplied = true;
163 ++AppliedFixes;
164 }
165 }
166 FixLoc = getLocation(FixAbsoluteFilePath, Repl.getOffset());
Eric Liu7e544522016-08-09 07:54:49 +0000167 SourceLocation FixEndLoc =
Alexander Kornienko98d33912016-10-17 17:25:02 +0000168 FixLoc.getLocWithOffset(Repl.getLength());
Eric Liu7e544522016-08-09 07:54:49 +0000169 Range = SourceRange(FixLoc, FixEndLoc);
Alexander Kornienko98d33912016-10-17 17:25:02 +0000170 Diag << FixItHint::CreateReplacement(Range,
171 Repl.getReplacementText());
Eric Liu7e544522016-08-09 07:54:49 +0000172 }
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000173
Alexander Kornienko98d33912016-10-17 17:25:02 +0000174 if (ApplyFixes)
175 FixLocations.push_back(std::make_pair(FixLoc, CanBeApplied));
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000176 }
177 }
178 }
179 for (auto Fix : FixLocations) {
180 Diags.Report(Fix.first, Fix.second ? diag::note_fixit_applied
181 : diag::note_fixit_failed);
182 }
Alexander Kornienko563de792017-01-03 14:36:13 +0000183 for (const auto &Note : Error.Notes)
Alexander Kornienko742790c2014-07-02 15:05:04 +0000184 reportNote(Note);
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000185 }
186
187 void Finish() {
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000188 if (ApplyFixes && TotalFixes > 0) {
Alexander Kornienko98d33912016-10-17 17:25:02 +0000189 Rewriter Rewrite(SourceMgr, LangOpts);
190 for (const auto &FileAndReplacements : FileReplacements) {
191 StringRef File = FileAndReplacements.first();
192 llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
193 SourceMgr.getFileManager().getBufferForFile(File);
194 if (!Buffer) {
195 llvm::errs() << "Can't get buffer for file " << File << ": "
196 << Buffer.getError().message() << "\n";
197 // FIXME: Maybe don't apply fixes for other files as well.
198 continue;
199 }
200 StringRef Code = Buffer.get()->getBuffer();
Alexander Kornienko25613202017-04-06 13:41:29 +0000201 auto Style = format::getStyle(
202 *Context.getOptionsForFile(File).FormatStyle, File, "none");
Antonio Maiorano0d7d9c22017-01-17 00:13:32 +0000203 if (!Style) {
204 llvm::errs() << llvm::toString(Style.takeError()) << "\n";
205 continue;
206 }
Alexander Kornienko17a4b232017-03-03 11:16:34 +0000207 llvm::Expected<tooling::Replacements> Replacements =
Alexander Kornienko98d33912016-10-17 17:25:02 +0000208 format::cleanupAroundReplacements(Code, FileAndReplacements.second,
Antonio Maiorano0d7d9c22017-01-17 00:13:32 +0000209 *Style);
Alexander Kornienko17a4b232017-03-03 11:16:34 +0000210 if (!Replacements) {
211 llvm::errs() << llvm::toString(Replacements.takeError()) << "\n";
Alexander Kornienko98d33912016-10-17 17:25:02 +0000212 continue;
213 }
Alexander Kornienko17a4b232017-03-03 11:16:34 +0000214 if (llvm::Expected<tooling::Replacements> FormattedReplacements =
215 format::formatReplacements(Code, *Replacements, *Style)) {
216 Replacements = std::move(FormattedReplacements);
217 if (!Replacements)
218 llvm_unreachable("!Replacements");
219 } else {
220 llvm::errs() << llvm::toString(FormattedReplacements.takeError())
221 << ". Skipping formatting.\n";
222 }
223 if (!tooling::applyAllReplacements(Replacements.get(), Rewrite)) {
Alexander Kornienko98d33912016-10-17 17:25:02 +0000224 llvm::errs() << "Can't apply replacements for file " << File << "\n";
225 }
226 }
227 if (Rewrite.overwriteChangedFiles()) {
228 llvm::errs() << "clang-tidy failed to apply suggested fixes.\n";
229 } else {
230 llvm::errs() << "clang-tidy applied " << AppliedFixes << " of "
231 << TotalFixes << " suggested fixes.\n";
232 }
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000233 }
234 }
235
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000236 unsigned getWarningsAsErrorsCount() const { return WarningsAsErrors; }
237
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000238private:
239 SourceLocation getLocation(StringRef FilePath, unsigned Offset) {
240 if (FilePath.empty())
241 return SourceLocation();
242
243 const FileEntry *File = SourceMgr.getFileManager().getFile(FilePath);
Chih-Hung Hsieh90fccec2017-04-06 20:19:26 +0000244 FileID ID = SourceMgr.getOrCreateFileID(File, SrcMgr::C_User);
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000245 return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
246 }
247
Alexander Kornienko563de792017-01-03 14:36:13 +0000248 void reportNote(const tooling::DiagnosticMessage &Message) {
Alexander Kornienko742790c2014-07-02 15:05:04 +0000249 SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
Alexander Kornienko563de792017-01-03 14:36:13 +0000250 Diags.Report(Loc, Diags.getCustomDiagID(DiagnosticsEngine::Note, "%0"))
Alexander Kornienko742790c2014-07-02 15:05:04 +0000251 << Message.Message;
252 }
253
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000254 FileManager Files;
255 LangOptions LangOpts; // FIXME: use langopts from each original file
256 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
257 DiagnosticConsumer *DiagPrinter;
258 DiagnosticsEngine Diags;
259 SourceManager SourceMgr;
Alexander Kornienko98d33912016-10-17 17:25:02 +0000260 llvm::StringMap<Replacements> FileReplacements;
Alexander Kornienko25613202017-04-06 13:41:29 +0000261 ClangTidyContext &Context;
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000262 bool ApplyFixes;
NAKAMURA Takumi4dd18132014-03-27 14:53:37 +0000263 unsigned TotalFixes;
264 unsigned AppliedFixes;
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000265 unsigned WarningsAsErrors;
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000266};
267
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000268class ClangTidyASTConsumer : public MultiplexConsumer {
269public:
David Blaikie680c4c82014-08-10 19:56:59 +0000270 ClangTidyASTConsumer(std::vector<std::unique_ptr<ASTConsumer>> Consumers,
Roman Lebedev12152512018-05-08 13:14:21 +0000271 std::unique_ptr<ClangTidyProfiling> Profiling,
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000272 std::unique_ptr<ast_matchers::MatchFinder> Finder,
273 std::vector<std::unique_ptr<ClangTidyCheck>> Checks)
Roman Lebedev12152512018-05-08 13:14:21 +0000274 : MultiplexConsumer(std::move(Consumers)),
275 Profiling(std::move(Profiling)), Finder(std::move(Finder)),
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000276 Checks(std::move(Checks)) {}
277
278private:
Roman Lebedev12152512018-05-08 13:14:21 +0000279 // Destructor order matters! Profiling must be destructed last.
280 // Or at least after Finder.
281 std::unique_ptr<ClangTidyProfiling> Profiling;
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000282 std::unique_ptr<ast_matchers::MatchFinder> Finder;
283 std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
284};
285
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000286} // namespace
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000287
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000288ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000289 ClangTidyContext &Context)
290 : Context(Context), CheckFactories(new ClangTidyCheckFactories) {
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000291 for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
292 E = ClangTidyModuleRegistry::end();
293 I != E; ++I) {
Ahmed Charles6a2dc5c2014-03-09 09:24:40 +0000294 std::unique_ptr<ClangTidyModule> Module(I->instantiate());
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000295 Module->addCheckFactories(*CheckFactories);
296 }
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000297}
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000298
Gabor Horvath34383212015-03-11 17:25:22 +0000299static void setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts,
300 AnalyzerOptionsRef AnalyzerOptions) {
301 StringRef AnalyzerPrefix(AnalyzerCheckNamePrefix);
302 for (const auto &Opt : Opts.CheckOptions) {
303 StringRef OptName(Opt.first);
304 if (!OptName.startswith(AnalyzerPrefix))
305 continue;
306 AnalyzerOptions->Config[OptName.substr(AnalyzerPrefix.size())] = Opt.second;
307 }
308}
309
Alexander Kornienko179e89f2016-11-08 07:43:42 +0000310typedef std::vector<std::pair<std::string, bool>> CheckersList;
311
Alexander Kornienko85a92c32018-05-17 14:04:27 +0000312static CheckersList getCheckersControlList(ClangTidyContext &Context,
313 bool IncludeExperimental) {
Alexander Kornienko179e89f2016-11-08 07:43:42 +0000314 CheckersList List;
315
316 const auto &RegisteredCheckers =
Alexander Kornienko85a92c32018-05-17 14:04:27 +0000317 AnalyzerOptions::getRegisteredCheckers(IncludeExperimental);
Alexander Kornienko179e89f2016-11-08 07:43:42 +0000318 bool AnalyzerChecksEnabled = false;
319 for (StringRef CheckName : RegisteredCheckers) {
320 std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
Alexander Kornienko21375182017-05-17 14:39:47 +0000321 AnalyzerChecksEnabled |= Context.isCheckEnabled(ClangTidyCheckName);
Alexander Kornienko179e89f2016-11-08 07:43:42 +0000322 }
323
324 if (!AnalyzerChecksEnabled)
325 return List;
326
327 // List all static analyzer checkers that our filter enables.
328 //
329 // Always add all core checkers if any other static analyzer check is enabled.
330 // This is currently necessary, as other path sensitive checks rely on the
331 // core checkers.
332 for (StringRef CheckName : RegisteredCheckers) {
333 std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
334
Alexander Kornienko21375182017-05-17 14:39:47 +0000335 if (CheckName.startswith("core") ||
336 Context.isCheckEnabled(ClangTidyCheckName)) {
Alexander Kornienko179e89f2016-11-08 07:43:42 +0000337 List.emplace_back(CheckName, true);
Alexander Kornienko21375182017-05-17 14:39:47 +0000338 }
Alexander Kornienko179e89f2016-11-08 07:43:42 +0000339 }
340 return List;
341}
342
David Blaikie680c4c82014-08-10 19:56:59 +0000343std::unique_ptr<clang::ASTConsumer>
344ClangTidyASTConsumerFactory::CreateASTConsumer(
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000345 clang::CompilerInstance &Compiler, StringRef File) {
346 // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
347 // modify Compiler.
348 Context.setSourceManager(&Compiler.getSourceManager());
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000349 Context.setCurrentFile(File);
Alexander Kornienkoad216882014-07-14 14:10:03 +0000350 Context.setASTContext(&Compiler.getASTContext());
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000351
Haojian Wuf7692a22016-02-26 09:19:33 +0000352 auto WorkingDir = Compiler.getSourceManager()
353 .getFileManager()
354 .getVirtualFileSystem()
355 ->getCurrentWorkingDirectory();
356 if (WorkingDir)
357 Context.setCurrentBuildDirectory(WorkingDir.get());
358
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000359 std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000360 CheckFactories->createChecks(&Context, Checks);
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000361
Samuel Benzaquenaedd9942014-10-23 17:23:20 +0000362 ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;
Roman Lebedev12152512018-05-08 13:14:21 +0000363
364 std::unique_ptr<ClangTidyProfiling> Profiling;
365 if (Context.getEnableProfiling()) {
Roman Lebedeva87f1d02018-06-06 15:07:51 +0000366 Profiling = llvm::make_unique<ClangTidyProfiling>(
367 Context.getProfileStorageParams());
Roman Lebedev12152512018-05-08 13:14:21 +0000368 FinderOptions.CheckProfiling.emplace(Profiling->Records);
369 }
Samuel Benzaquenaedd9942014-10-23 17:23:20 +0000370
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000371 std::unique_ptr<ast_matchers::MatchFinder> Finder(
Samuel Benzaquenaedd9942014-10-23 17:23:20 +0000372 new ast_matchers::MatchFinder(std::move(FinderOptions)));
373
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000374 for (auto &Check : Checks) {
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000375 Check->registerMatchers(&*Finder);
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000376 Check->registerPPCallbacks(Compiler);
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000377 }
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000378
David Blaikie680c4c82014-08-10 19:56:59 +0000379 std::vector<std::unique_ptr<ASTConsumer>> Consumers;
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000380 if (!Checks.empty())
381 Consumers.push_back(Finder->newASTConsumer());
Alexander Kornienko298b3822014-02-13 16:10:47 +0000382
Alex McCarthyfec08c72014-04-30 14:09:24 +0000383 AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts();
Alexander Kornienko85a92c32018-05-17 14:04:27 +0000384 AnalyzerOptions->CheckersControlList =
385 getCheckersControlList(Context, Context.canEnableAnalyzerAlphaCheckers());
Alex McCarthyfec08c72014-04-30 14:09:24 +0000386 if (!AnalyzerOptions->CheckersControlList.empty()) {
Gabor Horvath34383212015-03-11 17:25:22 +0000387 setStaticAnalyzerCheckerOpts(Context.getOptions(), AnalyzerOptions);
Alex McCarthyfec08c72014-04-30 14:09:24 +0000388 AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel;
389 AnalyzerOptions->AnalysisDiagOpt = PD_NONE;
390 AnalyzerOptions->AnalyzeNestedBlocks = true;
391 AnalyzerOptions->eagerlyAssumeBinOpBifurcation = true;
David Blaikie680c4c82014-08-10 19:56:59 +0000392 std::unique_ptr<ento::AnalysisASTConsumer> AnalysisConsumer =
Ted Kremenek4d1692f2014-08-27 15:14:47 +0000393 ento::CreateAnalysisConsumer(Compiler);
Alexander Kornienko298b3822014-02-13 16:10:47 +0000394 AnalysisConsumer->AddDiagnosticConsumer(
395 new AnalyzerDiagnosticConsumer(Context));
David Blaikie680c4c82014-08-10 19:56:59 +0000396 Consumers.push_back(std::move(AnalysisConsumer));
Alexander Kornienko298b3822014-02-13 16:10:47 +0000397 }
David Blaikie680c4c82014-08-10 19:56:59 +0000398 return llvm::make_unique<ClangTidyASTConsumer>(
Roman Lebedev12152512018-05-08 13:14:21 +0000399 std::move(Consumers), std::move(Profiling), std::move(Finder),
400 std::move(Checks));
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000401}
402
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000403std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000404 std::vector<std::string> CheckNames;
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000405 for (const auto &CheckFactory : *CheckFactories) {
Alexander Kornienko21375182017-05-17 14:39:47 +0000406 if (Context.isCheckEnabled(CheckFactory.first))
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000407 CheckNames.push_back(CheckFactory.first);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000408 }
409
Alexander Kornienko85a92c32018-05-17 14:04:27 +0000410 for (const auto &AnalyzerCheck : getCheckersControlList(
411 Context, Context.canEnableAnalyzerAlphaCheckers()))
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000412 CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000413
414 std::sort(CheckNames.begin(), CheckNames.end());
415 return CheckNames;
416}
417
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000418ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() {
419 ClangTidyOptions::OptionMap Options;
420 std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
421 CheckFactories->createChecks(&Context, Checks);
422 for (const auto &Check : Checks)
423 Check->storeOptions(Options);
424 return Options;
425}
426
Peter Collingbourneb17a3b32014-03-02 23:34:48 +0000427DiagnosticBuilder ClangTidyCheck::diag(SourceLocation Loc, StringRef Message,
428 DiagnosticIDs::Level Level) {
429 return Context->diag(CheckName, Loc, Message, Level);
Alexander Kornienko41bfe8d2014-01-13 10:50:51 +0000430}
431
Daniel Jasperd07c8402013-07-29 08:19:24 +0000432void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) {
433 Context->setSourceManager(Result.SourceManager);
434 check(Result);
435}
436
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000437OptionsView::OptionsView(StringRef CheckName,
438 const ClangTidyOptions::OptionMap &CheckOptions)
439 : NamePrefix(CheckName.str() + "."), CheckOptions(CheckOptions) {}
440
Haojian Wuc2d75772016-02-05 11:23:59 +0000441std::string OptionsView::get(StringRef LocalName, StringRef Default) const {
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000442 const auto &Iter = CheckOptions.find(NamePrefix + LocalName.str());
443 if (Iter != CheckOptions.end())
444 return Iter->second;
445 return Default;
446}
447
Haojian Wuc2d75772016-02-05 11:23:59 +0000448std::string OptionsView::getLocalOrGlobal(StringRef LocalName,
449 StringRef Default) const {
450 auto Iter = CheckOptions.find(NamePrefix + LocalName.str());
451 if (Iter != CheckOptions.end())
452 return Iter->second;
453 // Fallback to global setting, if present.
454 Iter = CheckOptions.find(LocalName.str());
455 if (Iter != CheckOptions.end())
456 return Iter->second;
457 return Default;
458}
459
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000460void OptionsView::store(ClangTidyOptions::OptionMap &Options,
461 StringRef LocalName, StringRef Value) const {
462 Options[NamePrefix + LocalName.str()] = Value;
463}
464
465void OptionsView::store(ClangTidyOptions::OptionMap &Options,
466 StringRef LocalName, int64_t Value) const {
467 store(Options, LocalName, llvm::itostr(Value));
Alexander Kornienko41bfe8d2014-01-13 10:50:51 +0000468}
469
Alexander Kornienko85a92c32018-05-17 14:04:27 +0000470std::vector<std::string>
471getCheckNames(const ClangTidyOptions &Options,
472 bool AllowEnablingAnalyzerAlphaCheckers) {
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000473 clang::tidy::ClangTidyContext Context(
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000474 llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
Alexander Kornienko85a92c32018-05-17 14:04:27 +0000475 Options),
476 AllowEnablingAnalyzerAlphaCheckers);
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000477 ClangTidyASTConsumerFactory Factory(Context);
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000478 return Factory.getCheckNames();
479}
480
Alexander Kornienko85a92c32018-05-17 14:04:27 +0000481ClangTidyOptions::OptionMap
482getCheckOptions(const ClangTidyOptions &Options,
483 bool AllowEnablingAnalyzerAlphaCheckers) {
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000484 clang::tidy::ClangTidyContext Context(
485 llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
Alexander Kornienko85a92c32018-05-17 14:04:27 +0000486 Options),
487 AllowEnablingAnalyzerAlphaCheckers);
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000488 ClangTidyASTConsumerFactory Factory(Context);
489 return Factory.getCheckOptions();
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000490}
491
Alexander Kornienko25613202017-04-06 13:41:29 +0000492void runClangTidy(clang::tidy::ClangTidyContext &Context,
493 const CompilationDatabase &Compilations,
Ilya Biryukova67b3662018-01-23 12:31:06 +0000494 ArrayRef<std::string> InputFiles,
495 llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS,
Roman Lebedeva87f1d02018-06-06 15:07:51 +0000496 bool EnableCheckProfile, llvm::StringRef StoreCheckProfile) {
Ilya Biryukova67b3662018-01-23 12:31:06 +0000497 ClangTool Tool(Compilations, InputFiles,
498 std::make_shared<PCHContainerOperations>(), BaseFS);
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000499
500 // Add extra arguments passed by the clang-tidy command-line.
501 ArgumentsAdjuster PerFileExtraArgumentsInserter =
502 [&Context](const CommandLineArguments &Args, StringRef Filename) {
503 ClangTidyOptions Opts = Context.getOptionsForFile(Filename);
Alexander Kornienko42fd75e2016-08-23 14:13:31 +0000504 CommandLineArguments AdjustedArgs = Args;
505 if (Opts.ExtraArgsBefore) {
506 auto I = AdjustedArgs.begin();
507 if (I != AdjustedArgs.end() && !StringRef(*I).startswith("-"))
508 ++I; // Skip compiler binary name, if it is there.
509 AdjustedArgs.insert(I, Opts.ExtraArgsBefore->begin(),
510 Opts.ExtraArgsBefore->end());
511 }
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000512 if (Opts.ExtraArgs)
513 AdjustedArgs.insert(AdjustedArgs.end(), Opts.ExtraArgs->begin(),
514 Opts.ExtraArgs->end());
515 return AdjustedArgs;
516 };
517
518 // Remove plugins arguments.
519 ArgumentsAdjuster PluginArgumentsRemover =
Malcolm Parsons3c3ae0e2017-01-13 18:56:04 +0000520 [](const CommandLineArguments &Args, StringRef Filename) {
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000521 CommandLineArguments AdjustedArgs;
522 for (size_t I = 0, E = Args.size(); I < E; ++I) {
523 if (I + 4 < Args.size() && Args[I] == "-Xclang" &&
524 (Args[I + 1] == "-load" || Args[I + 1] == "-add-plugin" ||
525 StringRef(Args[I + 1]).startswith("-plugin-arg-")) &&
526 Args[I + 2] == "-Xclang") {
527 I += 3;
528 } else
529 AdjustedArgs.push_back(Args[I]);
530 }
531 return AdjustedArgs;
532 };
533
Alexander Kornienko64956b52015-11-09 16:28:11 +0000534 Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter);
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000535 Tool.appendArgumentsAdjuster(PluginArgumentsRemover);
Roman Lebedev12152512018-05-08 13:14:21 +0000536 Context.setEnableProfiling(EnableCheckProfile);
Roman Lebedeva87f1d02018-06-06 15:07:51 +0000537 Context.setProfileStoragePrefix(StoreCheckProfile);
Samuel Benzaquenaedd9942014-10-23 17:23:20 +0000538
Daniel Jasperd07c8402013-07-29 08:19:24 +0000539 ClangTidyDiagnosticConsumer DiagConsumer(Context);
Manuel Klimek814f9bd2013-11-14 15:49:44 +0000540
541 Tool.setDiagnosticConsumer(&DiagConsumer);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000542
543 class ActionFactory : public FrontendActionFactory {
544 public:
Benjamin Kramer6e914242014-07-24 10:23:33 +0000545 ActionFactory(ClangTidyContext &Context) : ConsumerFactory(Context) {}
Roman Lebedev12b40742018-02-27 15:54:41 +0000546 FrontendAction *create() override { return new Action(&ConsumerFactory); }
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000547
Zinovy Nisbeca7682018-05-03 18:26:39 +0000548 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
549 FileManager *Files,
550 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
551 DiagnosticConsumer *DiagConsumer) override {
552 // Explicitly set ProgramAction to RunAnalysis to make the preprocessor
553 // define __clang_analyzer__ macro. The frontend analyzer action will not
554 // be called here.
555 Invocation->getFrontendOpts().ProgramAction = frontend::RunAnalysis;
556 return FrontendActionFactory::runInvocation(
557 Invocation, Files, PCHContainerOps, DiagConsumer);
558 }
559
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000560 private:
561 class Action : public ASTFrontendAction {
562 public:
563 Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {}
David Blaikie680c4c82014-08-10 19:56:59 +0000564 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
565 StringRef File) override {
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000566 return Factory->CreateASTConsumer(Compiler, File);
567 }
568
569 private:
570 ClangTidyASTConsumerFactory *Factory;
571 };
572
Benjamin Kramer6e914242014-07-24 10:23:33 +0000573 ClangTidyASTConsumerFactory ConsumerFactory;
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000574 };
575
Benjamin Kramer6e914242014-07-24 10:23:33 +0000576 ActionFactory Factory(Context);
577 Tool.run(&Factory);
Manuel Klimek814f9bd2013-11-14 15:49:44 +0000578}
579
Alexander Kornienko25613202017-04-06 13:41:29 +0000580void handleErrors(ClangTidyContext &Context, bool Fix,
Ilya Biryukova67b3662018-01-23 12:31:06 +0000581 unsigned &WarningsAsErrorsCount,
582 llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS) {
583 ErrorReporter Reporter(Context, Fix, BaseFS);
Haojian Wuf7692a22016-02-26 09:19:33 +0000584 vfs::FileSystem &FileSystem =
585 *Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
586 auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();
587 if (!InitialWorkingDir)
588 llvm::report_fatal_error("Cannot get current working path.");
589
Alexander Kornienko25613202017-04-06 13:41:29 +0000590 for (const ClangTidyError &Error : Context.getErrors()) {
Haojian Wuf7692a22016-02-26 09:19:33 +0000591 if (!Error.BuildDirectory.empty()) {
592 // By default, the working directory of file system is the current
593 // clang-tidy running directory.
594 //
595 // Change the directory to the one used during the analysis.
596 FileSystem.setCurrentWorkingDirectory(Error.BuildDirectory);
597 }
Alexander Kornienko742790c2014-07-02 15:05:04 +0000598 Reporter.reportDiagnostic(Error);
Haojian Wuf7692a22016-02-26 09:19:33 +0000599 // Return to the initial directory to correctly resolve next Error.
600 FileSystem.setCurrentWorkingDirectory(InitialWorkingDir.get());
601 }
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000602 Reporter.Finish();
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000603 WarningsAsErrorsCount += Reporter.getWarningsAsErrorsCount();
Daniel Jasperd07c8402013-07-29 08:19:24 +0000604}
605
Alexander Kornienko563de792017-01-03 14:36:13 +0000606void exportReplacements(const llvm::StringRef MainFilePath,
607 const std::vector<ClangTidyError> &Errors,
Benjamin Kramerfb98b742014-09-04 10:31:23 +0000608 raw_ostream &OS) {
Alexander Kornienko563de792017-01-03 14:36:13 +0000609 TranslationUnitDiagnostics TUD;
610 TUD.MainSourceFile = MainFilePath;
611 for (const auto &Error : Errors) {
612 tooling::Diagnostic Diag = Error;
613 TUD.Diagnostics.insert(TUD.Diagnostics.end(), Diag);
Eric Liu7e544522016-08-09 07:54:49 +0000614 }
Benjamin Kramerfb98b742014-09-04 10:31:23 +0000615
616 yaml::Output YAML(OS);
Alexander Kornienko563de792017-01-03 14:36:13 +0000617 YAML << TUD;
Benjamin Kramerfb98b742014-09-04 10:31:23 +0000618}
619
Daniel Jasperd07c8402013-07-29 08:19:24 +0000620} // namespace tidy
621} // namespace clang