blob: 59ee60466535a13a58148ca7ce72baaee82321fa [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:
Ilya Biryukova67b3662018-01-23 12:31:06 +000092 ErrorReporter(ClangTidyContext &Context, bool ApplyFixes,
93 llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS)
94 : Files(FileSystemOptions(), BaseFS), DiagOpts(new DiagnosticOptions()),
Alexander Kornienko38d81b42014-03-27 10:24:11 +000095 DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)),
96 Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
97 DiagPrinter),
Alexander Kornienko25613202017-04-06 13:41:29 +000098 SourceMgr(Diags, Files), Context(Context), ApplyFixes(ApplyFixes),
99 TotalFixes(0), AppliedFixes(0), WarningsAsErrors(0) {
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000100 DiagOpts->ShowColors = llvm::sys::Process::StandardOutHasColors();
101 DiagPrinter->BeginSourceFile(LangOpts);
102 }
103
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000104 SourceManager &getSourceManager() { return SourceMgr; }
Haojian Wuf7692a22016-02-26 09:19:33 +0000105
Alexander Kornienko742790c2014-07-02 15:05:04 +0000106 void reportDiagnostic(const ClangTidyError &Error) {
Alexander Kornienko563de792017-01-03 14:36:13 +0000107 const tooling::DiagnosticMessage &Message = Error.Message;
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000108 SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
109 // Contains a pair for each attempted fix: location and whether the fix was
110 // applied successfully.
111 SmallVector<std::pair<SourceLocation, bool>, 4> FixLocations;
112 {
Alexander Kornienko742790c2014-07-02 15:05:04 +0000113 auto Level = static_cast<DiagnosticsEngine::Level>(Error.DiagLevel);
Alexander Kornienko563de792017-01-03 14:36:13 +0000114 std::string Name = Error.DiagnosticName;
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000115 if (Error.IsWarningAsError) {
116 Name += ",-warnings-as-errors";
117 Level = DiagnosticsEngine::Error;
118 WarningsAsErrors++;
119 }
Alexander Kornienko58fe57a2015-11-16 13:06:15 +0000120 auto Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0 [%1]"))
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000121 << Message.Message << Name;
Eric Liu7e544522016-08-09 07:54:49 +0000122 for (const auto &FileAndReplacements : Error.Fix) {
Alexander Kornienko98d33912016-10-17 17:25:02 +0000123 for (const auto &Repl : FileAndReplacements.second) {
Eric Liu7e544522016-08-09 07:54:49 +0000124 // Retrieve the source range for applicable fixes. Macro definitions
125 // on the command line have locations in a virtual buffer and don't
126 // have valid file paths and are therefore not applicable.
127 SourceRange Range;
128 SourceLocation FixLoc;
Alexander Kornienko98d33912016-10-17 17:25:02 +0000129 ++TotalFixes;
130 bool CanBeApplied = false;
131 if (Repl.isApplicable()) {
132 SmallString<128> FixAbsoluteFilePath = Repl.getFilePath();
Eric Liu7e544522016-08-09 07:54:49 +0000133 Files.makeAbsolutePath(FixAbsoluteFilePath);
Alexander Kornienko98d33912016-10-17 17:25:02 +0000134 if (ApplyFixes) {
135 tooling::Replacement R(FixAbsoluteFilePath, Repl.getOffset(),
136 Repl.getLength(),
137 Repl.getReplacementText());
138 Replacements &Replacements = FileReplacements[R.getFilePath()];
139 llvm::Error Err = Replacements.add(R);
140 if (Err) {
141 // FIXME: Implement better conflict handling.
142 llvm::errs() << "Trying to resolve conflict: "
143 << llvm::toString(std::move(Err)) << "\n";
144 unsigned NewOffset =
145 Replacements.getShiftedCodePosition(R.getOffset());
146 unsigned NewLength = Replacements.getShiftedCodePosition(
147 R.getOffset() + R.getLength()) -
148 NewOffset;
149 if (NewLength == R.getLength()) {
150 R = Replacement(R.getFilePath(), NewOffset, NewLength,
151 R.getReplacementText());
152 Replacements = Replacements.merge(tooling::Replacements(R));
153 CanBeApplied = true;
154 ++AppliedFixes;
155 } else {
156 llvm::errs()
157 << "Can't resolve conflict, skipping the replacement.\n";
158 }
159
160 } else {
161 CanBeApplied = true;
162 ++AppliedFixes;
163 }
164 }
165 FixLoc = getLocation(FixAbsoluteFilePath, Repl.getOffset());
Eric Liu7e544522016-08-09 07:54:49 +0000166 SourceLocation FixEndLoc =
Alexander Kornienko98d33912016-10-17 17:25:02 +0000167 FixLoc.getLocWithOffset(Repl.getLength());
Eric Liu7e544522016-08-09 07:54:49 +0000168 Range = SourceRange(FixLoc, FixEndLoc);
Alexander Kornienko98d33912016-10-17 17:25:02 +0000169 Diag << FixItHint::CreateReplacement(Range,
170 Repl.getReplacementText());
Eric Liu7e544522016-08-09 07:54:49 +0000171 }
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000172
Alexander Kornienko98d33912016-10-17 17:25:02 +0000173 if (ApplyFixes)
174 FixLocations.push_back(std::make_pair(FixLoc, CanBeApplied));
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000175 }
176 }
177 }
178 for (auto Fix : FixLocations) {
179 Diags.Report(Fix.first, Fix.second ? diag::note_fixit_applied
180 : diag::note_fixit_failed);
181 }
Alexander Kornienko563de792017-01-03 14:36:13 +0000182 for (const auto &Note : Error.Notes)
Alexander Kornienko742790c2014-07-02 15:05:04 +0000183 reportNote(Note);
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000184 }
185
186 void Finish() {
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000187 if (ApplyFixes && TotalFixes > 0) {
Alexander Kornienko98d33912016-10-17 17:25:02 +0000188 Rewriter Rewrite(SourceMgr, LangOpts);
189 for (const auto &FileAndReplacements : FileReplacements) {
190 StringRef File = FileAndReplacements.first();
191 llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer =
192 SourceMgr.getFileManager().getBufferForFile(File);
193 if (!Buffer) {
194 llvm::errs() << "Can't get buffer for file " << File << ": "
195 << Buffer.getError().message() << "\n";
196 // FIXME: Maybe don't apply fixes for other files as well.
197 continue;
198 }
199 StringRef Code = Buffer.get()->getBuffer();
Alexander Kornienko25613202017-04-06 13:41:29 +0000200 auto Style = format::getStyle(
201 *Context.getOptionsForFile(File).FormatStyle, File, "none");
Antonio Maiorano0d7d9c22017-01-17 00:13:32 +0000202 if (!Style) {
203 llvm::errs() << llvm::toString(Style.takeError()) << "\n";
204 continue;
205 }
Alexander Kornienko17a4b232017-03-03 11:16:34 +0000206 llvm::Expected<tooling::Replacements> Replacements =
Alexander Kornienko98d33912016-10-17 17:25:02 +0000207 format::cleanupAroundReplacements(Code, FileAndReplacements.second,
Antonio Maiorano0d7d9c22017-01-17 00:13:32 +0000208 *Style);
Alexander Kornienko17a4b232017-03-03 11:16:34 +0000209 if (!Replacements) {
210 llvm::errs() << llvm::toString(Replacements.takeError()) << "\n";
Alexander Kornienko98d33912016-10-17 17:25:02 +0000211 continue;
212 }
Alexander Kornienko17a4b232017-03-03 11:16:34 +0000213 if (llvm::Expected<tooling::Replacements> FormattedReplacements =
214 format::formatReplacements(Code, *Replacements, *Style)) {
215 Replacements = std::move(FormattedReplacements);
216 if (!Replacements)
217 llvm_unreachable("!Replacements");
218 } else {
219 llvm::errs() << llvm::toString(FormattedReplacements.takeError())
220 << ". Skipping formatting.\n";
221 }
222 if (!tooling::applyAllReplacements(Replacements.get(), Rewrite)) {
Alexander Kornienko98d33912016-10-17 17:25:02 +0000223 llvm::errs() << "Can't apply replacements for file " << File << "\n";
224 }
225 }
226 if (Rewrite.overwriteChangedFiles()) {
227 llvm::errs() << "clang-tidy failed to apply suggested fixes.\n";
228 } else {
229 llvm::errs() << "clang-tidy applied " << AppliedFixes << " of "
230 << TotalFixes << " suggested fixes.\n";
231 }
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000232 }
233 }
234
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000235 unsigned getWarningsAsErrorsCount() const { return WarningsAsErrors; }
236
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000237private:
238 SourceLocation getLocation(StringRef FilePath, unsigned Offset) {
239 if (FilePath.empty())
240 return SourceLocation();
241
242 const FileEntry *File = SourceMgr.getFileManager().getFile(FilePath);
Chih-Hung Hsieh90fccec2017-04-06 20:19:26 +0000243 FileID ID = SourceMgr.getOrCreateFileID(File, SrcMgr::C_User);
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000244 return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset);
245 }
246
Alexander Kornienko563de792017-01-03 14:36:13 +0000247 void reportNote(const tooling::DiagnosticMessage &Message) {
Alexander Kornienko742790c2014-07-02 15:05:04 +0000248 SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset);
Alexander Kornienko563de792017-01-03 14:36:13 +0000249 Diags.Report(Loc, Diags.getCustomDiagID(DiagnosticsEngine::Note, "%0"))
Alexander Kornienko742790c2014-07-02 15:05:04 +0000250 << Message.Message;
251 }
252
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000253 FileManager Files;
254 LangOptions LangOpts; // FIXME: use langopts from each original file
255 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
256 DiagnosticConsumer *DiagPrinter;
257 DiagnosticsEngine Diags;
258 SourceManager SourceMgr;
Alexander Kornienko98d33912016-10-17 17:25:02 +0000259 llvm::StringMap<Replacements> FileReplacements;
Alexander Kornienko25613202017-04-06 13:41:29 +0000260 ClangTidyContext &Context;
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000261 bool ApplyFixes;
NAKAMURA Takumi4dd18132014-03-27 14:53:37 +0000262 unsigned TotalFixes;
263 unsigned AppliedFixes;
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000264 unsigned WarningsAsErrors;
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000265};
266
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000267class ClangTidyASTConsumer : public MultiplexConsumer {
268public:
David Blaikie680c4c82014-08-10 19:56:59 +0000269 ClangTidyASTConsumer(std::vector<std::unique_ptr<ASTConsumer>> Consumers,
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000270 std::unique_ptr<ast_matchers::MatchFinder> Finder,
271 std::vector<std::unique_ptr<ClangTidyCheck>> Checks)
David Blaikie680c4c82014-08-10 19:56:59 +0000272 : MultiplexConsumer(std::move(Consumers)), Finder(std::move(Finder)),
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000273 Checks(std::move(Checks)) {}
274
275private:
276 std::unique_ptr<ast_matchers::MatchFinder> Finder;
277 std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
278};
279
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000280} // namespace
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000281
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000282ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000283 ClangTidyContext &Context)
284 : Context(Context), CheckFactories(new ClangTidyCheckFactories) {
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000285 for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
286 E = ClangTidyModuleRegistry::end();
287 I != E; ++I) {
Ahmed Charles6a2dc5c2014-03-09 09:24:40 +0000288 std::unique_ptr<ClangTidyModule> Module(I->instantiate());
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000289 Module->addCheckFactories(*CheckFactories);
290 }
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000291}
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000292
Gabor Horvath34383212015-03-11 17:25:22 +0000293static void setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts,
294 AnalyzerOptionsRef AnalyzerOptions) {
295 StringRef AnalyzerPrefix(AnalyzerCheckNamePrefix);
296 for (const auto &Opt : Opts.CheckOptions) {
297 StringRef OptName(Opt.first);
298 if (!OptName.startswith(AnalyzerPrefix))
299 continue;
300 AnalyzerOptions->Config[OptName.substr(AnalyzerPrefix.size())] = Opt.second;
301 }
302}
303
Alexander Kornienko179e89f2016-11-08 07:43:42 +0000304typedef std::vector<std::pair<std::string, bool>> CheckersList;
305
Alexander Kornienko21375182017-05-17 14:39:47 +0000306static CheckersList getCheckersControlList(ClangTidyContext &Context) {
Alexander Kornienko179e89f2016-11-08 07:43:42 +0000307 CheckersList List;
308
309 const auto &RegisteredCheckers =
310 AnalyzerOptions::getRegisteredCheckers(/*IncludeExperimental=*/false);
311 bool AnalyzerChecksEnabled = false;
312 for (StringRef CheckName : RegisteredCheckers) {
313 std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
Alexander Kornienko21375182017-05-17 14:39:47 +0000314 AnalyzerChecksEnabled |= Context.isCheckEnabled(ClangTidyCheckName);
Alexander Kornienko179e89f2016-11-08 07:43:42 +0000315 }
316
317 if (!AnalyzerChecksEnabled)
318 return List;
319
320 // List all static analyzer checkers that our filter enables.
321 //
322 // Always add all core checkers if any other static analyzer check is enabled.
323 // This is currently necessary, as other path sensitive checks rely on the
324 // core checkers.
325 for (StringRef CheckName : RegisteredCheckers) {
326 std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str());
327
Alexander Kornienko21375182017-05-17 14:39:47 +0000328 if (CheckName.startswith("core") ||
329 Context.isCheckEnabled(ClangTidyCheckName)) {
Alexander Kornienko179e89f2016-11-08 07:43:42 +0000330 List.emplace_back(CheckName, true);
Alexander Kornienko21375182017-05-17 14:39:47 +0000331 }
Alexander Kornienko179e89f2016-11-08 07:43:42 +0000332 }
333 return List;
334}
335
David Blaikie680c4c82014-08-10 19:56:59 +0000336std::unique_ptr<clang::ASTConsumer>
337ClangTidyASTConsumerFactory::CreateASTConsumer(
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000338 clang::CompilerInstance &Compiler, StringRef File) {
339 // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't
340 // modify Compiler.
341 Context.setSourceManager(&Compiler.getSourceManager());
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000342 Context.setCurrentFile(File);
Alexander Kornienkoad216882014-07-14 14:10:03 +0000343 Context.setASTContext(&Compiler.getASTContext());
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000344
Haojian Wuf7692a22016-02-26 09:19:33 +0000345 auto WorkingDir = Compiler.getSourceManager()
346 .getFileManager()
347 .getVirtualFileSystem()
348 ->getCurrentWorkingDirectory();
349 if (WorkingDir)
350 Context.setCurrentBuildDirectory(WorkingDir.get());
351
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000352 std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000353 CheckFactories->createChecks(&Context, Checks);
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000354
Samuel Benzaquenaedd9942014-10-23 17:23:20 +0000355 ast_matchers::MatchFinder::MatchFinderOptions FinderOptions;
356 if (auto *P = Context.getCheckProfileData())
357 FinderOptions.CheckProfiling.emplace(P->Records);
358
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000359 std::unique_ptr<ast_matchers::MatchFinder> Finder(
Samuel Benzaquenaedd9942014-10-23 17:23:20 +0000360 new ast_matchers::MatchFinder(std::move(FinderOptions)));
361
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000362 for (auto &Check : Checks) {
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000363 Check->registerMatchers(&*Finder);
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000364 Check->registerPPCallbacks(Compiler);
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000365 }
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000366
David Blaikie680c4c82014-08-10 19:56:59 +0000367 std::vector<std::unique_ptr<ASTConsumer>> Consumers;
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000368 if (!Checks.empty())
369 Consumers.push_back(Finder->newASTConsumer());
Alexander Kornienko298b3822014-02-13 16:10:47 +0000370
Alex McCarthyfec08c72014-04-30 14:09:24 +0000371 AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts();
Alexander Kornienko21375182017-05-17 14:39:47 +0000372 AnalyzerOptions->CheckersControlList = getCheckersControlList(Context);
Alex McCarthyfec08c72014-04-30 14:09:24 +0000373 if (!AnalyzerOptions->CheckersControlList.empty()) {
Gabor Horvath34383212015-03-11 17:25:22 +0000374 setStaticAnalyzerCheckerOpts(Context.getOptions(), AnalyzerOptions);
Alex McCarthyfec08c72014-04-30 14:09:24 +0000375 AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel;
376 AnalyzerOptions->AnalysisDiagOpt = PD_NONE;
377 AnalyzerOptions->AnalyzeNestedBlocks = true;
378 AnalyzerOptions->eagerlyAssumeBinOpBifurcation = true;
David Blaikie680c4c82014-08-10 19:56:59 +0000379 std::unique_ptr<ento::AnalysisASTConsumer> AnalysisConsumer =
Ted Kremenek4d1692f2014-08-27 15:14:47 +0000380 ento::CreateAnalysisConsumer(Compiler);
Alexander Kornienko298b3822014-02-13 16:10:47 +0000381 AnalysisConsumer->AddDiagnosticConsumer(
382 new AnalyzerDiagnosticConsumer(Context));
David Blaikie680c4c82014-08-10 19:56:59 +0000383 Consumers.push_back(std::move(AnalysisConsumer));
Alexander Kornienko298b3822014-02-13 16:10:47 +0000384 }
David Blaikie680c4c82014-08-10 19:56:59 +0000385 return llvm::make_unique<ClangTidyASTConsumer>(
386 std::move(Consumers), std::move(Finder), std::move(Checks));
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000387}
388
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000389std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() {
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000390 std::vector<std::string> CheckNames;
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000391 for (const auto &CheckFactory : *CheckFactories) {
Alexander Kornienko21375182017-05-17 14:39:47 +0000392 if (Context.isCheckEnabled(CheckFactory.first))
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000393 CheckNames.push_back(CheckFactory.first);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000394 }
395
Alexander Kornienko21375182017-05-17 14:39:47 +0000396 for (const auto &AnalyzerCheck : getCheckersControlList(Context))
Alexander Kornienkodf1e3cb2014-03-06 10:17:46 +0000397 CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000398
399 std::sort(CheckNames.begin(), CheckNames.end());
400 return CheckNames;
401}
402
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000403ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() {
404 ClangTidyOptions::OptionMap Options;
405 std::vector<std::unique_ptr<ClangTidyCheck>> Checks;
406 CheckFactories->createChecks(&Context, Checks);
407 for (const auto &Check : Checks)
408 Check->storeOptions(Options);
409 return Options;
410}
411
Peter Collingbourneb17a3b32014-03-02 23:34:48 +0000412DiagnosticBuilder ClangTidyCheck::diag(SourceLocation Loc, StringRef Message,
413 DiagnosticIDs::Level Level) {
414 return Context->diag(CheckName, Loc, Message, Level);
Alexander Kornienko41bfe8d2014-01-13 10:50:51 +0000415}
416
Daniel Jasperd07c8402013-07-29 08:19:24 +0000417void ClangTidyCheck::run(const ast_matchers::MatchFinder::MatchResult &Result) {
418 Context->setSourceManager(Result.SourceManager);
419 check(Result);
420}
421
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000422OptionsView::OptionsView(StringRef CheckName,
423 const ClangTidyOptions::OptionMap &CheckOptions)
424 : NamePrefix(CheckName.str() + "."), CheckOptions(CheckOptions) {}
425
Haojian Wuc2d75772016-02-05 11:23:59 +0000426std::string OptionsView::get(StringRef LocalName, StringRef Default) const {
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000427 const auto &Iter = CheckOptions.find(NamePrefix + LocalName.str());
428 if (Iter != CheckOptions.end())
429 return Iter->second;
430 return Default;
431}
432
Haojian Wuc2d75772016-02-05 11:23:59 +0000433std::string OptionsView::getLocalOrGlobal(StringRef LocalName,
434 StringRef Default) const {
435 auto Iter = CheckOptions.find(NamePrefix + LocalName.str());
436 if (Iter != CheckOptions.end())
437 return Iter->second;
438 // Fallback to global setting, if present.
439 Iter = CheckOptions.find(LocalName.str());
440 if (Iter != CheckOptions.end())
441 return Iter->second;
442 return Default;
443}
444
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000445void OptionsView::store(ClangTidyOptions::OptionMap &Options,
446 StringRef LocalName, StringRef Value) const {
447 Options[NamePrefix + LocalName.str()] = Value;
448}
449
450void OptionsView::store(ClangTidyOptions::OptionMap &Options,
451 StringRef LocalName, int64_t Value) const {
452 store(Options, LocalName, llvm::itostr(Value));
Alexander Kornienko41bfe8d2014-01-13 10:50:51 +0000453}
454
Alexander Kornienko33a9bcc2014-04-29 15:20:10 +0000455std::vector<std::string> getCheckNames(const ClangTidyOptions &Options) {
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000456 clang::tidy::ClangTidyContext Context(
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000457 llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
458 Options));
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000459 ClangTidyASTConsumerFactory Factory(Context);
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000460 return Factory.getCheckNames();
461}
462
463ClangTidyOptions::OptionMap getCheckOptions(const ClangTidyOptions &Options) {
464 clang::tidy::ClangTidyContext Context(
465 llvm::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
466 Options));
467 ClangTidyASTConsumerFactory Factory(Context);
468 return Factory.getCheckOptions();
Alexander Kornienkofb9e92b2013-12-19 19:57:05 +0000469}
470
Alexander Kornienko25613202017-04-06 13:41:29 +0000471void runClangTidy(clang::tidy::ClangTidyContext &Context,
472 const CompilationDatabase &Compilations,
Ilya Biryukova67b3662018-01-23 12:31:06 +0000473 ArrayRef<std::string> InputFiles,
474 llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS,
475 ProfileData *Profile) {
476 ClangTool Tool(Compilations, InputFiles,
477 std::make_shared<PCHContainerOperations>(), BaseFS);
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000478
479 // Add extra arguments passed by the clang-tidy command-line.
480 ArgumentsAdjuster PerFileExtraArgumentsInserter =
481 [&Context](const CommandLineArguments &Args, StringRef Filename) {
482 ClangTidyOptions Opts = Context.getOptionsForFile(Filename);
Alexander Kornienko42fd75e2016-08-23 14:13:31 +0000483 CommandLineArguments AdjustedArgs = Args;
484 if (Opts.ExtraArgsBefore) {
485 auto I = AdjustedArgs.begin();
486 if (I != AdjustedArgs.end() && !StringRef(*I).startswith("-"))
487 ++I; // Skip compiler binary name, if it is there.
488 AdjustedArgs.insert(I, Opts.ExtraArgsBefore->begin(),
489 Opts.ExtraArgsBefore->end());
490 }
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000491 if (Opts.ExtraArgs)
492 AdjustedArgs.insert(AdjustedArgs.end(), Opts.ExtraArgs->begin(),
493 Opts.ExtraArgs->end());
494 return AdjustedArgs;
495 };
496
497 // Remove plugins arguments.
498 ArgumentsAdjuster PluginArgumentsRemover =
Malcolm Parsons3c3ae0e2017-01-13 18:56:04 +0000499 [](const CommandLineArguments &Args, StringRef Filename) {
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000500 CommandLineArguments AdjustedArgs;
501 for (size_t I = 0, E = Args.size(); I < E; ++I) {
502 if (I + 4 < Args.size() && Args[I] == "-Xclang" &&
503 (Args[I + 1] == "-load" || Args[I + 1] == "-add-plugin" ||
504 StringRef(Args[I + 1]).startswith("-plugin-arg-")) &&
505 Args[I + 2] == "-Xclang") {
506 I += 3;
507 } else
508 AdjustedArgs.push_back(Args[I]);
509 }
510 return AdjustedArgs;
511 };
512
Alexander Kornienko64956b52015-11-09 16:28:11 +0000513 Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter);
Etienne Bergeron4eaeace2016-04-06 14:07:51 +0000514 Tool.appendArgumentsAdjuster(PluginArgumentsRemover);
Samuel Benzaquenaedd9942014-10-23 17:23:20 +0000515 if (Profile)
516 Context.setCheckProfileData(Profile);
517
Daniel Jasperd07c8402013-07-29 08:19:24 +0000518 ClangTidyDiagnosticConsumer DiagConsumer(Context);
Manuel Klimek814f9bd2013-11-14 15:49:44 +0000519
520 Tool.setDiagnosticConsumer(&DiagConsumer);
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000521
522 class ActionFactory : public FrontendActionFactory {
523 public:
Benjamin Kramer6e914242014-07-24 10:23:33 +0000524 ActionFactory(ClangTidyContext &Context) : ConsumerFactory(Context) {}
Roman Lebedev12b40742018-02-27 15:54:41 +0000525 FrontendAction *create() override { return new Action(&ConsumerFactory); }
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000526
Zinovy Nisbeca7682018-05-03 18:26:39 +0000527 bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
528 FileManager *Files,
529 std::shared_ptr<PCHContainerOperations> PCHContainerOps,
530 DiagnosticConsumer *DiagConsumer) override {
531 // Explicitly set ProgramAction to RunAnalysis to make the preprocessor
532 // define __clang_analyzer__ macro. The frontend analyzer action will not
533 // be called here.
534 Invocation->getFrontendOpts().ProgramAction = frontend::RunAnalysis;
535 return FrontendActionFactory::runInvocation(
536 Invocation, Files, PCHContainerOps, DiagConsumer);
537 }
538
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000539 private:
540 class Action : public ASTFrontendAction {
541 public:
542 Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {}
David Blaikie680c4c82014-08-10 19:56:59 +0000543 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
544 StringRef File) override {
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000545 return Factory->CreateASTConsumer(Compiler, File);
546 }
547
548 private:
549 ClangTidyASTConsumerFactory *Factory;
550 };
551
Benjamin Kramer6e914242014-07-24 10:23:33 +0000552 ClangTidyASTConsumerFactory ConsumerFactory;
Alexander Kornienko175fefb2014-01-03 09:31:57 +0000553 };
554
Benjamin Kramer6e914242014-07-24 10:23:33 +0000555 ActionFactory Factory(Context);
556 Tool.run(&Factory);
Manuel Klimek814f9bd2013-11-14 15:49:44 +0000557}
558
Alexander Kornienko25613202017-04-06 13:41:29 +0000559void handleErrors(ClangTidyContext &Context, bool Fix,
Ilya Biryukova67b3662018-01-23 12:31:06 +0000560 unsigned &WarningsAsErrorsCount,
561 llvm::IntrusiveRefCntPtr<vfs::FileSystem> BaseFS) {
562 ErrorReporter Reporter(Context, Fix, BaseFS);
Haojian Wuf7692a22016-02-26 09:19:33 +0000563 vfs::FileSystem &FileSystem =
564 *Reporter.getSourceManager().getFileManager().getVirtualFileSystem();
565 auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory();
566 if (!InitialWorkingDir)
567 llvm::report_fatal_error("Cannot get current working path.");
568
Alexander Kornienko25613202017-04-06 13:41:29 +0000569 for (const ClangTidyError &Error : Context.getErrors()) {
Haojian Wuf7692a22016-02-26 09:19:33 +0000570 if (!Error.BuildDirectory.empty()) {
571 // By default, the working directory of file system is the current
572 // clang-tidy running directory.
573 //
574 // Change the directory to the one used during the analysis.
575 FileSystem.setCurrentWorkingDirectory(Error.BuildDirectory);
576 }
Alexander Kornienko742790c2014-07-02 15:05:04 +0000577 Reporter.reportDiagnostic(Error);
Haojian Wuf7692a22016-02-26 09:19:33 +0000578 // Return to the initial directory to correctly resolve next Error.
579 FileSystem.setCurrentWorkingDirectory(InitialWorkingDir.get());
580 }
Alexander Kornienko38d81b42014-03-27 10:24:11 +0000581 Reporter.Finish();
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000582 WarningsAsErrorsCount += Reporter.getWarningsAsErrorsCount();
Daniel Jasperd07c8402013-07-29 08:19:24 +0000583}
584
Alexander Kornienko563de792017-01-03 14:36:13 +0000585void exportReplacements(const llvm::StringRef MainFilePath,
586 const std::vector<ClangTidyError> &Errors,
Benjamin Kramerfb98b742014-09-04 10:31:23 +0000587 raw_ostream &OS) {
Alexander Kornienko563de792017-01-03 14:36:13 +0000588 TranslationUnitDiagnostics TUD;
589 TUD.MainSourceFile = MainFilePath;
590 for (const auto &Error : Errors) {
591 tooling::Diagnostic Diag = Error;
592 TUD.Diagnostics.insert(TUD.Diagnostics.end(), Diag);
Eric Liu7e544522016-08-09 07:54:49 +0000593 }
Benjamin Kramerfb98b742014-09-04 10:31:23 +0000594
595 yaml::Output YAML(OS);
Alexander Kornienko563de792017-01-03 14:36:13 +0000596 YAML << TUD;
Benjamin Kramerfb98b742014-09-04 10:31:23 +0000597}
598
Daniel Jasperd07c8402013-07-29 08:19:24 +0000599} // namespace tidy
600} // namespace clang