blob: d8f2212d7b82d47cbba4f5c47df892310cf039a5 [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"
Alexander Kornienko98d33912016-10-17 17:25:02 +000025#include "clang/Format/Format.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000026#include "clang/Frontend/ASTConsumers.h"
27#include "clang/Frontend/CompilerInstance.h"
28#include "clang/Frontend/FrontendActions.h"
Alexander Kornienko38d81b42014-03-27 10:24:11 +000029#include "clang/Frontend/FrontendDiagnostic.h"
Alexander Kornienko175fefb2014-01-03 09:31:57 +000030#include "clang/Frontend/MultiplexConsumer.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000031#include "clang/Frontend/TextDiagnosticPrinter.h"
Chandler Carruth85e6e872014-01-07 20:05:01 +000032#include "clang/Lex/PPCallbacks.h"
33#include "clang/Lex/Preprocessor.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000034#include "clang/Rewrite/Frontend/FixItRewriter.h"
35#include "clang/Rewrite/Frontend/FrontendActions.h"
Benjamin Kramer8ff8fdf2016-07-18 19:21:22 +000036#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
Alexander Kornienkod1199cb2014-01-03 17:24:20 +000037#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
Alexander Kornienko563de792017-01-03 14:36:13 +000038#include "clang/Tooling/DiagnosticsYaml.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000039#include "clang/Tooling/Refactoring.h"
Benjamin Kramerfb98b742014-09-04 10:31:23 +000040#include "clang/Tooling/ReplacementsYaml.h"
Chandler Carruth85e6e872014-01-07 20:05:01 +000041#include "clang/Tooling/Tooling.h"
Alexander Kornienko54461eb2014-02-06 14:50:10 +000042#include "llvm/Support/Process.h"
Daniel Jasperd07c8402013-07-29 08:19:24 +000043#include "llvm/Support/Signals.h"
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +000044#include <algorithm>
Alexander Kornienko38d81b42014-03-27 10:24:11 +000045#include <utility>
Daniel Jasperd07c8402013-07-29 08:19:24 +000046
47using namespace clang::ast_matchers;
48using namespace clang::driver;
49using namespace clang::tooling;
50using namespace llvm;
51
John Brawn4d79ec72016-08-05 11:01:08 +000052LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry)
NAKAMURA Takumi71b982b2014-07-03 14:12:47 +000053
Daniel Jasperd07c8402013-07-29 08:19:24 +000054namespace clang {
55namespace tidy {
Alexander Kornienko175fefb2014-01-03 09:31:57 +000056
Daniel Jasperd07c8402013-07-29 08:19:24 +000057namespace {
Alexander Kornienko54461eb2014-02-06 14:50:10 +000058static const char *AnalyzerCheckNamePrefix = "clang-analyzer-";
Manuel Klimek814f9bd2013-11-14 15:49:44 +000059
Alexander Kornienko54461eb2014-02-06 14:50:10 +000060class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer {
61public:
62 AnalyzerDiagnosticConsumer(ClangTidyContext &Context) : Context(Context) {}
63
Alexander Kornienkocb9272f2014-02-27 13:14:51 +000064 void FlushDiagnosticsImpl(std::vector<const ento::PathDiagnostic *> &Diags,
Craig Toppera3dbe842014-03-02 10:20:11 +000065 FilesMade *filesMade) override {
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +000066 for (const ento::PathDiagnostic *PD : Diags) {
Alexander Kornienkod1afc702014-02-12 09:52:07 +000067 SmallString<64> CheckName(AnalyzerCheckNamePrefix);
68 CheckName += PD->getCheckName();
Alexander Kornienko95cd50f2014-03-06 13:24:28 +000069 Context.diag(CheckName, PD->getLocation().asLocation(),
70 PD->getShortDescription())
71 << PD->path.back()->getRanges();
Alexander Kornienko54461eb2014-02-06 14:50:10 +000072
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +000073 for (const auto &DiagPiece :
74 PD->path.flatten(/*ShouldFlattenMacros=*/true)) {
Alexander Kornienko95cd50f2014-03-06 13:24:28 +000075 Context.diag(CheckName, DiagPiece->getLocation().asLocation(),
76 DiagPiece->getString(), DiagnosticIDs::Note)
77 << DiagPiece->getRanges();
Alexander Kornienko54461eb2014-02-06 14:50:10 +000078 }
79 }
80 }
81
Craig Toppera3dbe842014-03-02 10:20:11 +000082 StringRef getName() const override { return "ClangTidyDiags"; }
83 bool supportsLogicalOpControlFlow() const override { return true; }
84 bool supportsCrossFileDiagnostics() const override { return true; }
Alexander Kornienko54461eb2014-02-06 14:50:10 +000085
86private:
87 ClangTidyContext &Context;
Alexander Kornienko54461eb2014-02-06 14:50:10 +000088};
89
Alexander Kornienko38d81b42014-03-27 10:24:11 +000090class ErrorReporter {
91public:
Alexander Kornienko25613202017-04-06 13:41:29 +000092 ErrorReporter(ClangTidyContext &Context, bool ApplyFixes)
Alexander Kornienko38d81b42014-03-27 10:24:11 +000093 : Files(FileSystemOptions()), DiagOpts(new DiagnosticOptions()),
94 DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)),
95 Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
96 DiagPrinter),
Alexander Kornienko25613202017-04-06 13:41:29 +000097 SourceMgr(Diags, Files), Context(Context), ApplyFixes(ApplyFixes),
98 TotalFixes(0), AppliedFixes(0), WarningsAsErrors(0) {
Alexander Kornienko38d81b42014-03-27 10:24:11 +000099 DiagOpts->ShowColors = llvm::sys::Process::StandardOutHasColors();
100 DiagPrinter->BeginSourceFile(LangOpts);
101 }
102
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000103 SourceManager &getSourceManager() { return SourceMgr; }
Haojian Wuf7692a22016-02-26 09:19:33 +0000104
Alexander Kornienko742790c2014-07-02 15:05:04 +0000105 void reportDiagnostic(const ClangTidyError &Error) {
Alexander Kornienko563de792017-01-03 14:36:13 +0000106 const tooling::DiagnosticMessage &Message = Error.Message;
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000107 SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
108 // Contains a pair for each attempted fix: location and whether the fix was
109 // applied successfully.
110 SmallVector<std::pair<SourceLocation, bool>, 4> FixLocations;
111 {
Alexander Kornienko742790c2014-07-02 15:05:04 +0000112 auto Level = static_cast<DiagnosticsEngine::Level>(Error.DiagLevel);
Alexander Kornienko563de792017-01-03 14:36:13 +0000113 std::string Name = Error.DiagnosticName;
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000114 if (Error.IsWarningAsError) {
115 Name += ",-warnings-as-errors";
116 Level = DiagnosticsEngine::Error;
117 WarningsAsErrors++;
118 }
Alexander Kornienko58fe57a2015-11-16 13:06:15 +0000119 auto Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0 [%1]"))
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000120 << Message.Message << Name;
Eric Liu7e544522016-08-09 07:54:49 +0000121 for (const auto &FileAndReplacements : Error.Fix) {
Alexander Kornienko98d33912016-10-17 17:25:02 +0000122 for (const auto &Repl : FileAndReplacements.second) {
Eric Liu7e544522016-08-09 07:54:49 +0000123 // Retrieve the source range for applicable fixes. Macro definitions
124 // on the command line have locations in a virtual buffer and don't
125 // have valid file paths and are therefore not applicable.
126 SourceRange Range;
127 SourceLocation FixLoc;
Alexander Kornienko98d33912016-10-17 17:25:02 +0000128 ++TotalFixes;
129 bool CanBeApplied = false;
130 if (Repl.isApplicable()) {
131 SmallString<128> FixAbsoluteFilePath = Repl.getFilePath();
Eric Liu7e544522016-08-09 07:54:49 +0000132 Files.makeAbsolutePath(FixAbsoluteFilePath);
Alexander Kornienko98d33912016-10-17 17:25:02 +0000133 if (ApplyFixes) {
134 tooling::Replacement R(FixAbsoluteFilePath, Repl.getOffset(),
135 Repl.getLength(),
136 Repl.getReplacementText());
137 Replacements &Replacements = FileReplacements[R.getFilePath()];
138 llvm::Error Err = Replacements.add(R);
139 if (Err) {
140 // FIXME: Implement better conflict handling.
141 llvm::errs() << "Trying to resolve conflict: "
142 << llvm::toString(std::move(Err)) << "\n";
143 unsigned NewOffset =
144 Replacements.getShiftedCodePosition(R.getOffset());
145 unsigned NewLength = Replacements.getShiftedCodePosition(
146 R.getOffset() + R.getLength()) -
147 NewOffset;
148 if (NewLength == R.getLength()) {
149 R = Replacement(R.getFilePath(), NewOffset, NewLength,
150 R.getReplacementText());
151 Replacements = Replacements.merge(tooling::Replacements(R));
152 CanBeApplied = true;
153 ++AppliedFixes;
154 } else {
155 llvm::errs()
156 << "Can't resolve conflict, skipping the replacement.\n";
157 }
158
159 } else {
160 CanBeApplied = true;
161 ++AppliedFixes;
162 }
163 }
164 FixLoc = getLocation(FixAbsoluteFilePath, Repl.getOffset());
Eric Liu7e544522016-08-09 07:54:49 +0000165 SourceLocation FixEndLoc =
Alexander Kornienko98d33912016-10-17 17:25:02 +0000166 FixLoc.getLocWithOffset(Repl.getLength());
Eric Liu7e544522016-08-09 07:54:49 +0000167 Range = SourceRange(FixLoc, FixEndLoc);
Alexander Kornienko98d33912016-10-17 17:25:02 +0000168 Diag << FixItHint::CreateReplacement(Range,
169 Repl.getReplacementText());
Eric Liu7e544522016-08-09 07:54:49 +0000170 }
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000171
Alexander Kornienko98d33912016-10-17 17:25:02 +0000172 if (ApplyFixes)
173 FixLocations.push_back(std::make_pair(FixLoc, CanBeApplied));
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000174 }
175 }
176 }
177 for (auto Fix : FixLocations) {
178 Diags.Report(Fix.first, Fix.second ? diag::note_fixit_applied
179 : diag::note_fixit_failed);
180 }
Alexander Kornienko563de792017-01-03 14:36:13 +0000181 for (const auto &Note : Error.Notes)
Alexander Kornienko742790c2014-07-02 15:05:04 +0000182 reportNote(Note);
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000183 }
184
185 void Finish() {
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000186 if (ApplyFixes && TotalFixes > 0) {
Alexander Kornienko98d33912016-10-17 17:25:02 +0000187 Rewriter Rewrite(SourceMgr, LangOpts);
188 for (const auto &FileAndReplacements : FileReplacements) {
189 StringRef File = FileAndReplacements.first();
190 llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
191 SourceMgr.getFileManager().getBufferForFile(File);
192 if (!Buffer) {
193 llvm::errs() << "Can't get buffer for file " << File << ": "
194 << Buffer.getError().message() << "\n";
195 // FIXME: Maybe don't apply fixes for other files as well.
196 continue;
197 }
198 StringRef Code = Buffer.get()->getBuffer();
Alexander Kornienko25613202017-04-06 13:41:29 +0000199 auto Style = format::getStyle(
200 *Context.getOptionsForFile(File).FormatStyle, File, "none");
Antonio Maiorano0d7d9c22017-01-17 00:13:32 +0000201 if (!Style) {
202 llvm::errs() << llvm::toString(Style.takeError()) << "\n";
203 continue;
204 }
Alexander Kornienko17a4b232017-03-03 11:16:34 +0000205 llvm::Expected<tooling::Replacements> Replacements =
Alexander Kornienko98d33912016-10-17 17:25:02 +0000206 format::cleanupAroundReplacements(Code, FileAndReplacements.second,
Antonio Maiorano0d7d9c22017-01-17 00:13:32 +0000207 *Style);
Alexander Kornienko17a4b232017-03-03 11:16:34 +0000208 if (!Replacements) {
209 llvm::errs() << llvm::toString(Replacements.takeError()) << "\n";
Alexander Kornienko98d33912016-10-17 17:25:02 +0000210 continue;
211 }
Alexander Kornienko17a4b232017-03-03 11:16:34 +0000212 if (llvm::Expected<tooling::Replacements> FormattedReplacements =
213 format::formatReplacements(Code, *Replacements, *Style)) {
214 Replacements = std::move(FormattedReplacements);
215 if (!Replacements)
216 llvm_unreachable("!Replacements");
217 } else {
218 llvm::errs() << llvm::toString(FormattedReplacements.takeError())
219 << ". Skipping formatting.\n";
220 }
221 if (!tooling::applyAllReplacements(Replacements.get(), Rewrite)) {
Alexander Kornienko98d33912016-10-17 17:25:02 +0000222 llvm::errs() << "Can't apply replacements for file " << File << "\n";
223 }
224 }
225 if (Rewrite.overwriteChangedFiles()) {
226 llvm::errs() << "clang-tidy failed to apply suggested fixes.\n";
227 } else {
228 llvm::errs() << "clang-tidy applied " << AppliedFixes << " of "
229 << TotalFixes << " suggested fixes.\n";
230 }
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000231 }
232 }
233
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000234 unsigned getWarningsAsErrorsCount() const { return WarningsAsErrors; }
235
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000236private:
237 SourceLocation getLocation(StringRef FilePath, unsigned Offset) {
238 if (FilePath.empty())
239 return SourceLocation();
240
241 const FileEntry *File = SourceMgr.getFileManager().getFile(FilePath);
Chih-Hung Hsiehb7b6c902017-03-31 01:11:11 +0000242 FileID ID = SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User);
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000243 return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
244 }
245
Alexander Kornienko563de792017-01-03 14:36:13 +0000246 void reportNote(const tooling::DiagnosticMessage &Message) {
Alexander Kornienko742790c2014-07-02 15:05:04 +0000247 SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
Alexander Kornienko563de792017-01-03 14:36:13 +0000248 Diags.Report(Loc, Diags.getCustomDiagID(DiagnosticsEngine::Note, "%0"))
Alexander Kornienko742790c2014-07-02 15:05:04 +0000249 << Message.Message;
250 }
251
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000252 FileManager Files;
253 LangOptions LangOpts; // FIXME: use langopts from each original file
254 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
255 DiagnosticConsumer *DiagPrinter;
256 DiagnosticsEngine Diags;
257 SourceManager SourceMgr;
Alexander Kornienko98d33912016-10-17 17:25:02 +0000258 llvm::StringMap<Replacements> FileReplacements;
Alexander Kornienko25613202017-04-06 13:41:29 +0000259 ClangTidyContext &Context;
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000260 bool ApplyFixes;
NAKAMURA Takumi4dd18132014-03-27 14:53:37 +0000261 unsigned TotalFixes;
262 unsigned AppliedFixes;
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000263 unsigned WarningsAsErrors;
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000264};
265
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000266class ClangTidyASTConsumer : public MultiplexConsumer {
267public:
David Blaikie680c4c82014-08-10 19:56:59 +0000268 ClangTidyASTConsumer(std::vector<std::unique_ptr<ASTConsumer>> Consumers,
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000269 std::unique_ptr<ast_matchers::MatchFinder> Finder,
270 std::vector<std::unique_ptr<ClangTidyCheck>> Checks)
David Blaikie680c4c82014-08-10 19:56:59 +0000271 : MultiplexConsumer(std::move(Consumers)), Finder(std::move(Finder)),
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000272 Checks(std::move(Checks)) {}
273
274private:
275 std::unique_ptr<ast_matchers::MatchFinder> Finder;
276 std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
277};
278
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000279} // namespace
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000280
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000281ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000282 ClangTidyContext &Context)
283 : Context(Context), CheckFactories(new ClangTidyCheckFactories) {
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000284 for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
285 E = ClangTidyModuleRegistry::end();
286 I != E; ++I) {
Ahmed Charles6a2dc5c2014-03-09 09:24:40 +0000287 std::unique_ptr<ClangTidyModule> Module(I->instantiate());
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000288 Module->addCheckFactories(*CheckFactories);
289 }
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000290}
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000291
Gabor Horvath34383212015-03-11 17:25:22 +0000292static void setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts,
293 AnalyzerOptionsRef AnalyzerOptions) {
294 StringRef AnalyzerPrefix(AnalyzerCheckNamePrefix);
295 for (const auto &Opt : Opts.CheckOptions) {
296 StringRef OptName(Opt.first);
297 if (!OptName.startswith(AnalyzerPrefix))
298 continue;
299 AnalyzerOptions->Config[OptName.substr(AnalyzerPrefix.size())] = Opt.second;
300 }
301}
302
Alexander Kornienko179e89f2016-11-08 07:43:42 +0000303typedef std::vector<std::pair<std::string, bool>> CheckersList;
304
305static CheckersList getCheckersControlList(GlobList &Filter) {
306 CheckersList List;
307
308 const auto &RegisteredCheckers =
309 AnalyzerOptions::getRegisteredCheckers(/*IncludeExperimental=*/false);
310 bool AnalyzerChecksEnabled = false;
311 for (StringRef CheckName : RegisteredCheckers) {
312 std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
313 AnalyzerChecksEnabled |= Filter.contains(ClangTidyCheckName);
314 }
315
316 if (!AnalyzerChecksEnabled)
317 return List;
318
319 // List all static analyzer checkers that our filter enables.
320 //
321 // Always add all core checkers if any other static analyzer check is enabled.
322 // This is currently necessary, as other path sensitive checks rely on the
323 // core checkers.
324 for (StringRef CheckName : RegisteredCheckers) {
325 std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
326
327 if (CheckName.startswith("core") || Filter.contains(ClangTidyCheckName))
328 List.emplace_back(CheckName, true);
329 }
330 return List;
331}
332
David Blaikie680c4c82014-08-10 19:56:59 +0000333std::unique_ptr<clang::ASTConsumer>
334ClangTidyASTConsumerFactory::CreateASTConsumer(
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000335 clang::CompilerInstance &Compiler, StringRef File) {
336 // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
337 // modify Compiler.
338 Context.setSourceManager(&Compiler.getSourceManager());
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000339 Context.setCurrentFile(File);
Alexander Kornienkoad216882014-07-14 14:10:03 +0000340 Context.setASTContext(&Compiler.getASTContext());
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000341
Haojian Wuf7692a22016-02-26 09:19:33 +0000342 auto WorkingDir = Compiler.getSourceManager()
343 .getFileManager()
344 .getVirtualFileSystem()
345 ->getCurrentWorkingDirectory();
346 if (WorkingDir)
347 Context.setCurrentBuildDirectory(WorkingDir.get());
348
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000349 std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000350 CheckFactories->createChecks(&Context, Checks);
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000351
Samuel Benzaquenaedd9942014-10-23 17:23:20 +0000352 ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;
353 if (auto *P = Context.getCheckProfileData())
354 FinderOptions.CheckProfiling.emplace(P->Records);
355
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000356 std::unique_ptr<ast_matchers::MatchFinder> Finder(
Samuel Benzaquenaedd9942014-10-23 17:23:20 +0000357 new ast_matchers::MatchFinder(std::move(FinderOptions)));
358
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000359 for (auto &Check : Checks) {
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000360 Check->registerMatchers(&*Finder);
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000361 Check->registerPPCallbacks(Compiler);
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000362 }
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000363
David Blaikie680c4c82014-08-10 19:56:59 +0000364 std::vector<std::unique_ptr<ASTConsumer>> Consumers;
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000365 if (!Checks.empty())
366 Consumers.push_back(Finder->newASTConsumer());
Alexander Kornienko298b3822014-02-13 16:10:47 +0000367
Alex McCarthyfec08c72014-04-30 14:09:24 +0000368 AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts();
369 // FIXME: Remove this option once clang's cfg-temporary-dtors option defaults
370 // to true.
371 AnalyzerOptions->Config["cfg-temporary-dtors"] =
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000372 Context.getOptions().AnalyzeTemporaryDtors ? "true" : "false";
Alex McCarthyfec08c72014-04-30 14:09:24 +0000373
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000374 GlobList &Filter = Context.getChecksFilter();
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000375 AnalyzerOptions->CheckersControlList = getCheckersControlList(Filter);
Alex McCarthyfec08c72014-04-30 14:09:24 +0000376 if (!AnalyzerOptions->CheckersControlList.empty()) {
Gabor Horvath34383212015-03-11 17:25:22 +0000377 setStaticAnalyzerCheckerOpts(Context.getOptions(), AnalyzerOptions);
Alex McCarthyfec08c72014-04-30 14:09:24 +0000378 AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel;
379 AnalyzerOptions->AnalysisDiagOpt = PD_NONE;
380 AnalyzerOptions->AnalyzeNestedBlocks = true;
381 AnalyzerOptions->eagerlyAssumeBinOpBifurcation = true;
David Blaikie680c4c82014-08-10 19:56:59 +0000382 std::unique_ptr<ento::AnalysisASTConsumer> AnalysisConsumer =
Ted Kremenek4d1692f2014-08-27 15:14:47 +0000383 ento::CreateAnalysisConsumer(Compiler);
Alexander Kornienko298b3822014-02-13 16:10:47 +0000384 AnalysisConsumer->AddDiagnosticConsumer(
385 new AnalyzerDiagnosticConsumer(Context));
David Blaikie680c4c82014-08-10 19:56:59 +0000386 Consumers.push_back(std::move(AnalysisConsumer));
Alexander Kornienko298b3822014-02-13 16:10:47 +0000387 }
David Blaikie680c4c82014-08-10 19:56:59 +0000388 return llvm::make_unique<ClangTidyASTConsumer>(
389 std::move(Consumers), std::move(Finder), std::move(Checks));
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000390}
391
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000392std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000393 std::vector<std::string> CheckNames;
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000394 GlobList &Filter = Context.getChecksFilter();
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000395 for (const auto &CheckFactory : *CheckFactories) {
Alexander Kornienkob3d331d2014-08-06 11:49:10 +0000396 if (Filter.contains(CheckFactory.first))
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000397 CheckNames.push_back(CheckFactory.first);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000398 }
399
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000400 for (const auto &AnalyzerCheck : getCheckersControlList(Filter))
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000401 CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000402
403 std::sort(CheckNames.begin(), CheckNames.end());
404 return CheckNames;
405}
406
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000407ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() {
408 ClangTidyOptions::OptionMap Options;
409 std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
410 CheckFactories->createChecks(&Context, Checks);
411 for (const auto &Check : Checks)
412 Check->storeOptions(Options);
413 return Options;
414}
415
Peter Collingbourneb17a3b32014-03-02 23:34:48 +0000416DiagnosticBuilder ClangTidyCheck::diag(SourceLocation Loc, StringRef Message,
417 DiagnosticIDs::Level Level) {
418 return Context->diag(CheckName, Loc, Message, Level);
Alexander Kornienko41bfe8d2014-01-13 10:50:51 +0000419}
420
Daniel Jasperd07c8402013-07-29 08:19:24 +0000421void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) {
422 Context->setSourceManager(Result.SourceManager);
423 check(Result);
424}
425
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000426OptionsView::OptionsView(StringRef CheckName,
427 const ClangTidyOptions::OptionMap &CheckOptions)
428 : NamePrefix(CheckName.str() + "."), CheckOptions(CheckOptions) {}
429
Haojian Wuc2d75772016-02-05 11:23:59 +0000430std::string OptionsView::get(StringRef LocalName, StringRef Default) const {
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000431 const auto &Iter = CheckOptions.find(NamePrefix + LocalName.str());
432 if (Iter != CheckOptions.end())
433 return Iter->second;
434 return Default;
435}
436
Haojian Wuc2d75772016-02-05 11:23:59 +0000437std::string OptionsView::getLocalOrGlobal(StringRef LocalName,
438 StringRef Default) const {
439 auto Iter = CheckOptions.find(NamePrefix + LocalName.str());
440 if (Iter != CheckOptions.end())
441 return Iter->second;
442 // Fallback to global setting, if present.
443 Iter = CheckOptions.find(LocalName.str());
444 if (Iter != CheckOptions.end())
445 return Iter->second;
446 return Default;
447}
448
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000449void OptionsView::store(ClangTidyOptions::OptionMap &Options,
450 StringRef LocalName, StringRef Value) const {
451 Options[NamePrefix + LocalName.str()] = Value;
452}
453
454void OptionsView::store(ClangTidyOptions::OptionMap &Options,
455 StringRef LocalName, int64_t Value) const {
456 store(Options, LocalName, llvm::itostr(Value));
Alexander Kornienko41bfe8d2014-01-13 10:50:51 +0000457}
458
Alexander Kornienko33a9bcc2014-04-29 15:20:10 +0000459std::vector<std::string> getCheckNames(const ClangTidyOptions &Options) {
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000460 clang::tidy::ClangTidyContext Context(
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000461 llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
462 Options));
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000463 ClangTidyASTConsumerFactory Factory(Context);
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000464 return Factory.getCheckNames();
465}
466
467ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options) {
468 clang::tidy::ClangTidyContext Context(
469 llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
470 Options));
471 ClangTidyASTConsumerFactory Factory(Context);
472 return Factory.getCheckOptions();
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000473}
474
Alexander Kornienko25613202017-04-06 13:41:29 +0000475void runClangTidy(clang::tidy::ClangTidyContext &Context,
476 const CompilationDatabase &Compilations,
477 ArrayRef<std::string> InputFiles, ProfileData *Profile) {
Alexander Kornienko1e1ad5c2014-05-28 15:21:14 +0000478 ClangTool Tool(Compilations, InputFiles);
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000479
480 // Add extra arguments passed by the clang-tidy command-line.
481 ArgumentsAdjuster PerFileExtraArgumentsInserter =
482 [&Context](const CommandLineArguments &Args, StringRef Filename) {
483 ClangTidyOptions Opts = Context.getOptionsForFile(Filename);
Alexander Kornienko42fd75e2016-08-23 14:13:31 +0000484 CommandLineArguments AdjustedArgs = Args;
485 if (Opts.ExtraArgsBefore) {
486 auto I = AdjustedArgs.begin();
487 if (I != AdjustedArgs.end() && !StringRef(*I).startswith("-"))
488 ++I; // Skip compiler binary name, if it is there.
489 AdjustedArgs.insert(I, Opts.ExtraArgsBefore->begin(),
490 Opts.ExtraArgsBefore->end());
491 }
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000492 if (Opts.ExtraArgs)
493 AdjustedArgs.insert(AdjustedArgs.end(), Opts.ExtraArgs->begin(),
494 Opts.ExtraArgs->end());
495 return AdjustedArgs;
496 };
497
498 // Remove plugins arguments.
499 ArgumentsAdjuster PluginArgumentsRemover =
Malcolm Parsons3c3ae0e2017-01-13 18:56:04 +0000500 [](const CommandLineArguments &Args, StringRef Filename) {
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000501 CommandLineArguments AdjustedArgs;
502 for (size_t I = 0, E = Args.size(); I < E; ++I) {
503 if (I + 4 < Args.size() && Args[I] == "-Xclang" &&
504 (Args[I + 1] == "-load" || Args[I + 1] == "-add-plugin" ||
505 StringRef(Args[I + 1]).startswith("-plugin-arg-")) &&
506 Args[I + 2] == "-Xclang") {
507 I += 3;
508 } else
509 AdjustedArgs.push_back(Args[I]);
510 }
511 return AdjustedArgs;
512 };
513
Alexander Kornienko64956b52015-11-09 16:28:11 +0000514 Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter);
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000515 Tool.appendArgumentsAdjuster(PluginArgumentsRemover);
Samuel Benzaquenaedd9942014-10-23 17:23:20 +0000516 if (Profile)
517 Context.setCheckProfileData(Profile);
518
Daniel Jasperd07c8402013-07-29 08:19:24 +0000519 ClangTidyDiagnosticConsumer DiagConsumer(Context);
Manuel Klimek814f9bd2013-11-14 15:49:44 +0000520
521 Tool.setDiagnosticConsumer(&DiagConsumer);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000522
523 class ActionFactory : public FrontendActionFactory {
524 public:
Benjamin Kramer6e914242014-07-24 10:23:33 +0000525 ActionFactory(ClangTidyContext &Context) : ConsumerFactory(Context) {}
526 FrontendAction *create() override { return new Action(&ConsumerFactory); }
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000527
528 private:
529 class Action : public ASTFrontendAction {
530 public:
531 Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {}
David Blaikie680c4c82014-08-10 19:56:59 +0000532 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
533 StringRef File) override {
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000534 return Factory->CreateASTConsumer(Compiler, File);
535 }
536
537 private:
538 ClangTidyASTConsumerFactory *Factory;
539 };
540
Benjamin Kramer6e914242014-07-24 10:23:33 +0000541 ClangTidyASTConsumerFactory ConsumerFactory;
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000542 };
543
Benjamin Kramer6e914242014-07-24 10:23:33 +0000544 ActionFactory Factory(Context);
545 Tool.run(&Factory);
Manuel Klimek814f9bd2013-11-14 15:49:44 +0000546}
547
Alexander Kornienko25613202017-04-06 13:41:29 +0000548void handleErrors(ClangTidyContext &Context, bool Fix,
549 unsigned &WarningsAsErrorsCount) {
550 ErrorReporter Reporter(Context, Fix);
Haojian Wuf7692a22016-02-26 09:19:33 +0000551 vfs::FileSystem &FileSystem =
552 *Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
553 auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();
554 if (!InitialWorkingDir)
555 llvm::report_fatal_error("Cannot get current working path.");
556
Alexander Kornienko25613202017-04-06 13:41:29 +0000557 for (const ClangTidyError &Error : Context.getErrors()) {
Haojian Wuf7692a22016-02-26 09:19:33 +0000558 if (!Error.BuildDirectory.empty()) {
559 // By default, the working directory of file system is the current
560 // clang-tidy running directory.
561 //
562 // Change the directory to the one used during the analysis.
563 FileSystem.setCurrentWorkingDirectory(Error.BuildDirectory);
564 }
Alexander Kornienko742790c2014-07-02 15:05:04 +0000565 Reporter.reportDiagnostic(Error);
Haojian Wuf7692a22016-02-26 09:19:33 +0000566 // Return to the initial directory to correctly resolve next Error.
567 FileSystem.setCurrentWorkingDirectory(InitialWorkingDir.get());
568 }
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000569 Reporter.Finish();
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000570 WarningsAsErrorsCount += Reporter.getWarningsAsErrorsCount();
Daniel Jasperd07c8402013-07-29 08:19:24 +0000571}
572
Alexander Kornienko563de792017-01-03 14:36:13 +0000573void exportReplacements(const llvm::StringRef MainFilePath,
574 const std::vector<ClangTidyError> &Errors,
Benjamin Kramerfb98b742014-09-04 10:31:23 +0000575 raw_ostream &OS) {
Alexander Kornienko563de792017-01-03 14:36:13 +0000576 TranslationUnitDiagnostics TUD;
577 TUD.MainSourceFile = MainFilePath;
578 for (const auto &Error : Errors) {
579 tooling::Diagnostic Diag = Error;
580 TUD.Diagnostics.insert(TUD.Diagnostics.end(), Diag);
Eric Liu7e544522016-08-09 07:54:49 +0000581 }
Benjamin Kramerfb98b742014-09-04 10:31:23 +0000582
583 yaml::Output YAML(OS);
Alexander Kornienko563de792017-01-03 14:36:13 +0000584 YAML << TUD;
Benjamin Kramerfb98b742014-09-04 10:31:23 +0000585}
586
Daniel Jasperd07c8402013-07-29 08:19:24 +0000587} // namespace tidy
588} // namespace clang