Daniel Jasper | d07c840 | 2013-07-29 08:19:24 | [diff] [blame] | 1 | //===--- tools/extra/clang-tidy/ClangTidy.cpp - Clang tidy tool -----------===// |
| 2 | // |
Chandler Carruth | 2946cd7 | 2019-01-19 08:50:56 | [diff] [blame] | 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
Daniel Jasper | d07c840 | 2013-07-29 08:19:24 | [diff] [blame] | 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | /// |
| 9 | /// \file This file implements a clang-tidy tool. |
| 10 | /// |
| 11 | /// This tool uses the Clang Tooling infrastructure, see |
| 12 | /// http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html |
| 13 | /// for details on setting it up with LLVM source tree. |
| 14 | /// |
| 15 | //===----------------------------------------------------------------------===// |
| 16 | |
| 17 | #include "ClangTidy.h" |
Nathan James | 860aefd | 2020-06-29 15:05:51 | [diff] [blame] | 18 | #include "ClangTidyCheck.h" |
Daniel Jasper | d07c840 | 2013-07-29 08:19:24 | [diff] [blame] | 19 | #include "ClangTidyDiagnosticConsumer.h" |
| 20 | #include "ClangTidyModuleRegistry.h" |
Roman Lebedev | 1215251 | 2018-05-08 13:14:21 | [diff] [blame] | 21 | #include "ClangTidyProfiling.h" |
Alexander Kornienko | bbc89dc | 2019-03-22 13:42:48 | [diff] [blame] | 22 | #include "ExpandModularHeadersPPCallbacks.h" |
Nico Weber | 33c9dbb | 2020-09-03 23:37:29 | [diff] [blame] | 23 | #include "clang-tidy-config.h" |
Daniel Jasper | d07c840 | 2013-07-29 08:19:24 | [diff] [blame] | 24 | #include "clang/AST/ASTConsumer.h" |
Daniel Jasper | d07c840 | 2013-07-29 08:19:24 | [diff] [blame] | 25 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
Alexander Kornienko | 98d3391 | 2016-10-17 17:25:02 | [diff] [blame] | 26 | #include "clang/Format/Format.h" |
Daniel Jasper | d07c840 | 2013-07-29 08:19:24 | [diff] [blame] | 27 | #include "clang/Frontend/ASTConsumers.h" |
| 28 | #include "clang/Frontend/CompilerInstance.h" |
Alexander Kornienko | 38d81b4 | 2014-03-27 10:24:11 | [diff] [blame] | 29 | #include "clang/Frontend/FrontendDiagnostic.h" |
Alexander Kornienko | 175fefb | 2014-01-03 09:31:57 | [diff] [blame] | 30 | #include "clang/Frontend/MultiplexConsumer.h" |
Daniel Jasper | d07c840 | 2013-07-29 08:19:24 | [diff] [blame] | 31 | #include "clang/Frontend/TextDiagnosticPrinter.h" |
Chandler Carruth | 85e6e87 | 2014-01-07 20:05:01 | [diff] [blame] | 32 | #include "clang/Lex/PPCallbacks.h" |
| 33 | #include "clang/Lex/Preprocessor.h" |
Jan Korous | c5d14b5 | 2019-10-14 20:15:01 | [diff] [blame] | 34 | #include "clang/Lex/PreprocessorOptions.h" |
Daniel Jasper | d07c840 | 2013-07-29 08:19:24 | [diff] [blame] | 35 | #include "clang/Rewrite/Frontend/FixItRewriter.h" |
| 36 | #include "clang/Rewrite/Frontend/FrontendActions.h" |
Haojian Wu | f2879d8 | 2019-04-17 12:53:59 | [diff] [blame] | 37 | #include "clang/Tooling/Core/Diagnostic.h" |
Alexander Kornienko | 563de79 | 2017-01-03 14:36:13 | [diff] [blame] | 38 | #include "clang/Tooling/DiagnosticsYaml.h" |
Daniel Jasper | d07c840 | 2013-07-29 08:19:24 | [diff] [blame] | 39 | #include "clang/Tooling/Refactoring.h" |
Benjamin Kramer | fb98b74 | 2014-09-04 10:31:23 | [diff] [blame] | 40 | #include "clang/Tooling/ReplacementsYaml.h" |
Chandler Carruth | 85e6e87 | 2014-01-07 20:05:01 | [diff] [blame] | 41 | #include "clang/Tooling/Tooling.h" |
Alexander Kornienko | 54461eb | 2014-02-06 14:50:10 | [diff] [blame] | 42 | #include "llvm/Support/Process.h" |
Alexander Kornienko | fb9e92b | 2013-12-19 19:57:05 | [diff] [blame] | 43 | #include <algorithm> |
Alexander Kornienko | 38d81b4 | 2014-03-27 10:24:11 | [diff] [blame] | 44 | #include <utility> |
Daniel Jasper | d07c840 | 2013-07-29 08:19:24 | [diff] [blame] | 45 | |
Nico Weber | 33c9dbb | 2020-09-03 23:37:29 | [diff] [blame] | 46 | #if CLANG_TIDY_ENABLE_STATIC_ANALYZER |
Artem Dergachev | f0bb45f | 2019-09-11 20:54:27 | [diff] [blame] | 47 | #include "clang/Analysis/PathDiagnostic.h" |
| 48 | #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" |
Nico Weber | 33c9dbb | 2020-09-03 23:37:29 | [diff] [blame] | 49 | #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER |
Artem Dergachev | f0bb45f | 2019-09-11 20:54:27 | [diff] [blame] | 50 | |
Daniel Jasper | d07c840 | 2013-07-29 08:19:24 | [diff] [blame] | 51 | using namespace clang::ast_matchers; |
| 52 | using namespace clang::driver; |
| 53 | using namespace clang::tooling; |
| 54 | using namespace llvm; |
| 55 | |
John Brawn | 4d79ec7 | 2016-08-05 11:01:08 | [diff] [blame] | 56 | LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry) |
NAKAMURA Takumi | 71b982b | 2014-07-03 14:12:47 | [diff] [blame] | 57 | |
Daniel Jasper | d07c840 | 2013-07-29 08:19:24 | [diff] [blame] | 58 | namespace clang { |
| 59 | namespace tidy { |
Alexander Kornienko | 175fefb | 2014-01-03 09:31:57 | [diff] [blame] | 60 | |
Daniel Jasper | d07c840 | 2013-07-29 08:19:24 | [diff] [blame] | 61 | namespace { |
Nico Weber | 33c9dbb | 2020-09-03 23:37:29 | [diff] [blame] | 62 | #if CLANG_TIDY_ENABLE_STATIC_ANALYZER |
Alexander Kornienko | 54461eb | 2014-02-06 14:50:10 | [diff] [blame] | 63 | static const char *AnalyzerCheckNamePrefix = "clang-analyzer-"; |
Manuel Klimek | 814f9bd | 2013-11-14 15:49:44 | [diff] [blame] | 64 | |
Alexander Kornienko | 54461eb | 2014-02-06 14:50:10 | [diff] [blame] | 65 | class AnalyzerDiagnosticConsumer : public ento::PathDiagnosticConsumer { |
| 66 | public: |
| 67 | AnalyzerDiagnosticConsumer(ClangTidyContext &Context) : Context(Context) {} |
| 68 | |
Alexander Kornienko | cb9272f | 2014-02-27 13:14:51 | [diff] [blame] | 69 | void FlushDiagnosticsImpl(std::vector<const ento::PathDiagnostic *> &Diags, |
Alexander Kornienko | ab2d3ce | 2021-01-28 23:49:53 | [diff] [blame] | 70 | FilesMade *FilesMade) override { |
Alexander Kornienko | df1e3cb | 2014-03-06 10:17:46 | [diff] [blame] | 71 | for (const ento::PathDiagnostic *PD : Diags) { |
Alexander Kornienko | d1afc70 | 2014-02-12 09:52:07 | [diff] [blame] | 72 | SmallString<64> CheckName(AnalyzerCheckNamePrefix); |
Tim Shen | 75e963e | 2019-09-12 21:18:44 | [diff] [blame] | 73 | CheckName += PD->getCheckerName(); |
Alexander Kornienko | 95cd50f | 2014-03-06 13:24:28 | [diff] [blame] | 74 | Context.diag(CheckName, PD->getLocation().asLocation(), |
| 75 | PD->getShortDescription()) |
| 76 | << PD->path.back()->getRanges(); |
Alexander Kornienko | 54461eb | 2014-02-06 14:50:10 | [diff] [blame] | 77 | |
Alexander Kornienko | df1e3cb | 2014-03-06 10:17:46 | [diff] [blame] | 78 | for (const auto &DiagPiece : |
| 79 | PD->path.flatten(/*ShouldFlattenMacros=*/true)) { |
Alexander Kornienko | 95cd50f | 2014-03-06 13:24:28 | [diff] [blame] | 80 | Context.diag(CheckName, DiagPiece->getLocation().asLocation(), |
| 81 | DiagPiece->getString(), DiagnosticIDs::Note) |
| 82 | << DiagPiece->getRanges(); |
Alexander Kornienko | 54461eb | 2014-02-06 14:50:10 | [diff] [blame] | 83 | } |
| 84 | } |
| 85 | } |
| 86 | |
Craig Topper | a3dbe84 | 2014-03-02 10:20:11 | [diff] [blame] | 87 | StringRef getName() const override { return "ClangTidyDiags"; } |
| 88 | bool supportsLogicalOpControlFlow() const override { return true; } |
| 89 | bool supportsCrossFileDiagnostics() const override { return true; } |
Alexander Kornienko | 54461eb | 2014-02-06 14:50:10 | [diff] [blame] | 90 | |
| 91 | private: |
| 92 | ClangTidyContext &Context; |
Alexander Kornienko | 54461eb | 2014-02-06 14:50:10 | [diff] [blame] | 93 | }; |
Nico Weber | 33c9dbb | 2020-09-03 23:37:29 | [diff] [blame] | 94 | #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER |
Alexander Kornienko | 54461eb | 2014-02-06 14:50:10 | [diff] [blame] | 95 | |
Alexander Kornienko | 38d81b4 | 2014-03-27 10:24:11 | [diff] [blame] | 96 | class ErrorReporter { |
| 97 | public: |
Nathan James | abbe9e2 | 2021-03-01 22:07:09 | [diff] [blame] | 98 | ErrorReporter(ClangTidyContext &Context, FixBehaviour ApplyFixes, |
Jonas Devlieghere | fc51490 | 2018-10-10 13:27:25 | [diff] [blame] | 99 | llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS) |
Nathan James | 64badec | 2020-12-17 14:09:08 | [diff] [blame] | 100 | : Files(FileSystemOptions(), std::move(BaseFS)), |
| 101 | DiagOpts(new DiagnosticOptions()), |
Alexander Kornienko | 38d81b4 | 2014-03-27 10:24:11 | [diff] [blame] | 102 | DiagPrinter(new TextDiagnosticPrinter(llvm::outs(), &*DiagOpts)), |
| 103 | Diags(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, |
| 104 | DiagPrinter), |
Alexander Kornienko | 2561320 | 2017-04-06 13:41:29 | [diff] [blame] | 105 | SourceMgr(Diags, Files), Context(Context), ApplyFixes(ApplyFixes), |
| 106 | TotalFixes(0), AppliedFixes(0), WarningsAsErrors(0) { |
hyd-dev | d9b8aad | 2020-06-18 15:12:36 | [diff] [blame] | 107 | DiagOpts->ShowColors = Context.getOptions().UseColor.getValueOr( |
| 108 | llvm::sys::Process::StandardOutHasColors()); |
Alexander Kornienko | 38d81b4 | 2014-03-27 10:24:11 | [diff] [blame] | 109 | DiagPrinter->BeginSourceFile(LangOpts); |
David Sanders | d915d40 | 2020-10-31 14:36:42 | [diff] [blame] | 110 | if (DiagOpts->ShowColors && !llvm::sys::Process::StandardOutIsDisplayed()) { |
| 111 | llvm::sys::Process::UseANSIEscapeCodes(true); |
| 112 | } |
Alexander Kornienko | 38d81b4 | 2014-03-27 10:24:11 | [diff] [blame] | 113 | } |
| 114 | |
Etienne Bergeron | 4eaeace | 2016-04-06 14:07:51 | [diff] [blame] | 115 | SourceManager &getSourceManager() { return SourceMgr; } |
Haojian Wu | f7692a2 | 2016-02-26 09:19:33 | [diff] [blame] | 116 | |
Alexander Kornienko | 742790c | 2014-07-02 15:05:04 | [diff] [blame] | 117 | void reportDiagnostic(const ClangTidyError &Error) { |
Alexander Kornienko | 563de79 | 2017-01-03 14:36:13 | [diff] [blame] | 118 | const tooling::DiagnosticMessage &Message = Error.Message; |
Alexander Kornienko | 38d81b4 | 2014-03-27 10:24:11 | [diff] [blame] | 119 | SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset); |
| 120 | // Contains a pair for each attempted fix: location and whether the fix was |
| 121 | // applied successfully. |
| 122 | SmallVector<std::pair<SourceLocation, bool>, 4> FixLocations; |
| 123 | { |
Alexander Kornienko | 742790c | 2014-07-02 15:05:04 | [diff] [blame] | 124 | auto Level = static_cast<DiagnosticsEngine::Level>(Error.DiagLevel); |
Alexander Kornienko | 563de79 | 2017-01-03 14:36:13 | [diff] [blame] | 125 | std::string Name = Error.DiagnosticName; |
Daniel | af4f2eb | 2020-06-19 19:40:03 | [diff] [blame] | 126 | if (!Error.EnabledDiagnosticAliases.empty()) |
| 127 | Name += "," + llvm::join(Error.EnabledDiagnosticAliases, ","); |
Jonathan Roelofs | d60388a | 2016-01-13 17:36:41 | [diff] [blame] | 128 | if (Error.IsWarningAsError) { |
| 129 | Name += ",-warnings-as-errors"; |
| 130 | Level = DiagnosticsEngine::Error; |
| 131 | WarningsAsErrors++; |
| 132 | } |
Alexander Kornienko | 58fe57a | 2015-11-16 13:06:15 | [diff] [blame] | 133 | auto Diag = Diags.Report(Loc, Diags.getCustomDiagID(Level, "%0 [%1]")) |
Jonathan Roelofs | d60388a | 2016-01-13 17:36:41 | [diff] [blame] | 134 | << Message.Message << Name; |
Whisperity | 3b677b8 | 2021-03-15 16:06:03 | [diff] [blame] | 135 | for (const FileByteRange &FBR : Error.Message.Ranges) |
| 136 | Diag << getRange(FBR); |
Haojian Wu | f2879d8 | 2019-04-17 12:53:59 | [diff] [blame] | 137 | // FIXME: explore options to support interactive fix selection. |
Nathan James | abbe9e2 | 2021-03-01 22:07:09 | [diff] [blame] | 138 | const llvm::StringMap<Replacements> *ChosenFix; |
| 139 | if (ApplyFixes != FB_NoFix && |
| 140 | (ChosenFix = getFixIt(Error, ApplyFixes == FB_FixNotes))) { |
Haojian Wu | f2879d8 | 2019-04-17 12:53:59 | [diff] [blame] | 141 | for (const auto &FileAndReplacements : *ChosenFix) { |
| 142 | for (const auto &Repl : FileAndReplacements.second) { |
| 143 | ++TotalFixes; |
| 144 | bool CanBeApplied = false; |
Haojian Wu | 8bbbd31 | 2019-04-18 14:18:14 | [diff] [blame] | 145 | if (!Repl.isApplicable()) |
| 146 | continue; |
| 147 | SourceLocation FixLoc; |
| 148 | SmallString<128> FixAbsoluteFilePath = Repl.getFilePath(); |
| 149 | Files.makeAbsolutePath(FixAbsoluteFilePath); |
| 150 | tooling::Replacement R(FixAbsoluteFilePath, Repl.getOffset(), |
| 151 | Repl.getLength(), Repl.getReplacementText()); |
| 152 | Replacements &Replacements = FileReplacements[R.getFilePath()]; |
| 153 | llvm::Error Err = Replacements.add(R); |
| 154 | if (Err) { |
| 155 | // FIXME: Implement better conflict handling. |
| 156 | llvm::errs() << "Trying to resolve conflict: " |
| 157 | << llvm::toString(std::move(Err)) << "\n"; |
| 158 | unsigned NewOffset = |
| 159 | Replacements.getShiftedCodePosition(R.getOffset()); |
| 160 | unsigned NewLength = Replacements.getShiftedCodePosition( |
| 161 | R.getOffset() + R.getLength()) - |
| 162 | NewOffset; |
| 163 | if (NewLength == R.getLength()) { |
| 164 | R = Replacement(R.getFilePath(), NewOffset, NewLength, |
| 165 | R.getReplacementText()); |
| 166 | Replacements = Replacements.merge(tooling::Replacements(R)); |
Alexander Kornienko | 98d3391 | 2016-10-17 17:25:02 | [diff] [blame] | 167 | CanBeApplied = true; |
| 168 | ++AppliedFixes; |
Haojian Wu | 8bbbd31 | 2019-04-18 14:18:14 | [diff] [blame] | 169 | } else { |
| 170 | llvm::errs() |
| 171 | << "Can't resolve conflict, skipping the replacement.\n"; |
Alexander Kornienko | 98d3391 | 2016-10-17 17:25:02 | [diff] [blame] | 172 | } |
Haojian Wu | 8bbbd31 | 2019-04-18 14:18:14 | [diff] [blame] | 173 | } else { |
| 174 | CanBeApplied = true; |
| 175 | ++AppliedFixes; |
Alexander Kornienko | 98d3391 | 2016-10-17 17:25:02 | [diff] [blame] | 176 | } |
Haojian Wu | 8bbbd31 | 2019-04-18 14:18:14 | [diff] [blame] | 177 | FixLoc = getLocation(FixAbsoluteFilePath, Repl.getOffset()); |
| 178 | FixLocations.push_back(std::make_pair(FixLoc, CanBeApplied)); |
Eric Liu | 7e54452 | 2016-08-09 07:54:49 | [diff] [blame] | 179 | } |
Alexander Kornienko | 38d81b4 | 2014-03-27 10:24:11 | [diff] [blame] | 180 | } |
| 181 | } |
Haojian Wu | f2879d8 | 2019-04-17 12:53:59 | [diff] [blame] | 182 | reportFix(Diag, Error.Message.Fix); |
Alexander Kornienko | 38d81b4 | 2014-03-27 10:24:11 | [diff] [blame] | 183 | } |
| 184 | for (auto Fix : FixLocations) { |
| 185 | Diags.Report(Fix.first, Fix.second ? diag::note_fixit_applied |
| 186 | : diag::note_fixit_failed); |
| 187 | } |
Alexander Kornienko | 563de79 | 2017-01-03 14:36:13 | [diff] [blame] | 188 | for (const auto &Note : Error.Notes) |
Alexander Kornienko | 742790c | 2014-07-02 15:05:04 | [diff] [blame] | 189 | reportNote(Note); |
Alexander Kornienko | 38d81b4 | 2014-03-27 10:24:11 | [diff] [blame] | 190 | } |
| 191 | |
Alexander Kornienko | ab2d3ce | 2021-01-28 23:49:53 | [diff] [blame] | 192 | void finish() { |
Nathan James | abbe9e2 | 2021-03-01 22:07:09 | [diff] [blame] | 193 | if (TotalFixes > 0) { |
Alexander Kornienko | 98d3391 | 2016-10-17 17:25:02 | [diff] [blame] | 194 | Rewriter Rewrite(SourceMgr, LangOpts); |
| 195 | for (const auto &FileAndReplacements : FileReplacements) { |
| 196 | StringRef File = FileAndReplacements.first(); |
| 197 | llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> Buffer = |
| 198 | SourceMgr.getFileManager().getBufferForFile(File); |
| 199 | if (!Buffer) { |
| 200 | llvm::errs() << "Can't get buffer for file " << File << ": " |
| 201 | << Buffer.getError().message() << "\n"; |
| 202 | // FIXME: Maybe don't apply fixes for other files as well. |
| 203 | continue; |
| 204 | } |
| 205 | StringRef Code = Buffer.get()->getBuffer(); |
Alexander Kornienko | 2561320 | 2017-04-06 13:41:29 | [diff] [blame] | 206 | auto Style = format::getStyle( |
| 207 | *Context.getOptionsForFile(File).FormatStyle, File, "none"); |
Antonio Maiorano | 0d7d9c2 | 2017-01-17 00:13:32 | [diff] [blame] | 208 | if (!Style) { |
| 209 | llvm::errs() << llvm::toString(Style.takeError()) << "\n"; |
| 210 | continue; |
| 211 | } |
Alexander Kornienko | 17a4b23 | 2017-03-03 11:16:34 | [diff] [blame] | 212 | llvm::Expected<tooling::Replacements> Replacements = |
Alexander Kornienko | 98d3391 | 2016-10-17 17:25:02 | [diff] [blame] | 213 | format::cleanupAroundReplacements(Code, FileAndReplacements.second, |
Antonio Maiorano | 0d7d9c2 | 2017-01-17 00:13:32 | [diff] [blame] | 214 | *Style); |
Alexander Kornienko | 17a4b23 | 2017-03-03 11:16:34 | [diff] [blame] | 215 | if (!Replacements) { |
| 216 | llvm::errs() << llvm::toString(Replacements.takeError()) << "\n"; |
Alexander Kornienko | 98d3391 | 2016-10-17 17:25:02 | [diff] [blame] | 217 | continue; |
| 218 | } |
Alexander Kornienko | 17a4b23 | 2017-03-03 11:16:34 | [diff] [blame] | 219 | if (llvm::Expected<tooling::Replacements> FormattedReplacements = |
| 220 | format::formatReplacements(Code, *Replacements, *Style)) { |
| 221 | Replacements = std::move(FormattedReplacements); |
| 222 | if (!Replacements) |
| 223 | llvm_unreachable("!Replacements"); |
| 224 | } else { |
| 225 | llvm::errs() << llvm::toString(FormattedReplacements.takeError()) |
| 226 | << ". Skipping formatting.\n"; |
| 227 | } |
| 228 | if (!tooling::applyAllReplacements(Replacements.get(), Rewrite)) { |
Alexander Kornienko | 98d3391 | 2016-10-17 17:25:02 | [diff] [blame] | 229 | llvm::errs() << "Can't apply replacements for file " << File << "\n"; |
| 230 | } |
| 231 | } |
| 232 | if (Rewrite.overwriteChangedFiles()) { |
| 233 | llvm::errs() << "clang-tidy failed to apply suggested fixes.\n"; |
| 234 | } else { |
| 235 | llvm::errs() << "clang-tidy applied " << AppliedFixes << " of " |
| 236 | << TotalFixes << " suggested fixes.\n"; |
| 237 | } |
Alexander Kornienko | 38d81b4 | 2014-03-27 10:24:11 | [diff] [blame] | 238 | } |
| 239 | } |
| 240 | |
Jonathan Roelofs | d60388a | 2016-01-13 17:36:41 | [diff] [blame] | 241 | unsigned getWarningsAsErrorsCount() const { return WarningsAsErrors; } |
| 242 | |
Alexander Kornienko | 38d81b4 | 2014-03-27 10:24:11 | [diff] [blame] | 243 | private: |
| 244 | SourceLocation getLocation(StringRef FilePath, unsigned Offset) { |
| 245 | if (FilePath.empty()) |
| 246 | return SourceLocation(); |
| 247 | |
Harlan Haskins | a02f857 | 2019-08-01 21:32:01 | [diff] [blame] | 248 | auto File = SourceMgr.getFileManager().getFile(FilePath); |
| 249 | if (!File) |
| 250 | return SourceLocation(); |
| 251 | |
| 252 | FileID ID = SourceMgr.getOrCreateFileID(*File, SrcMgr::C_User); |
Alexander Kornienko | 38d81b4 | 2014-03-27 10:24:11 | [diff] [blame] | 253 | return SourceMgr.getLocForStartOfFile(ID).getLocWithOffset(Offset); |
| 254 | } |
| 255 | |
Haojian Wu | f2879d8 | 2019-04-17 12:53:59 | [diff] [blame] | 256 | void reportFix(const DiagnosticBuilder &Diag, |
| 257 | const llvm::StringMap<Replacements> &Fix) { |
| 258 | for (const auto &FileAndReplacements : Fix) { |
| 259 | for (const auto &Repl : FileAndReplacements.second) { |
| 260 | if (!Repl.isApplicable()) |
| 261 | continue; |
Whisperity | 3b677b8 | 2021-03-15 16:06:03 | [diff] [blame] | 262 | FileByteRange FBR; |
| 263 | FBR.FilePath = Repl.getFilePath().str(); |
| 264 | FBR.FileOffset = Repl.getOffset(); |
| 265 | FBR.Length = Repl.getLength(); |
| 266 | |
| 267 | Diag << FixItHint::CreateReplacement(getRange(FBR), |
| 268 | Repl.getReplacementText()); |
Haojian Wu | f2879d8 | 2019-04-17 12:53:59 | [diff] [blame] | 269 | } |
| 270 | } |
| 271 | } |
| 272 | |
Alexander Kornienko | 563de79 | 2017-01-03 14:36:13 | [diff] [blame] | 273 | void reportNote(const tooling::DiagnosticMessage &Message) { |
Alexander Kornienko | 742790c | 2014-07-02 15:05:04 | [diff] [blame] | 274 | SourceLocation Loc = getLocation(Message.FilePath, Message.FileOffset); |
Haojian Wu | f2879d8 | 2019-04-17 12:53:59 | [diff] [blame] | 275 | auto Diag = |
| 276 | Diags.Report(Loc, Diags.getCustomDiagID(DiagnosticsEngine::Note, "%0")) |
Alexander Kornienko | 742790c | 2014-07-02 15:05:04 | [diff] [blame] | 277 | << Message.Message; |
Whisperity | 3b677b8 | 2021-03-15 16:06:03 | [diff] [blame] | 278 | for (const FileByteRange &FBR : Message.Ranges) |
| 279 | Diag << getRange(FBR); |
Haojian Wu | f2879d8 | 2019-04-17 12:53:59 | [diff] [blame] | 280 | reportFix(Diag, Message.Fix); |
Alexander Kornienko | 742790c | 2014-07-02 15:05:04 | [diff] [blame] | 281 | } |
| 282 | |
Whisperity | 3b677b8 | 2021-03-15 16:06:03 | [diff] [blame] | 283 | CharSourceRange getRange(const FileByteRange &Range) { |
| 284 | SmallString<128> AbsoluteFilePath{Range.FilePath}; |
| 285 | Files.makeAbsolutePath(AbsoluteFilePath); |
| 286 | SourceLocation BeginLoc = getLocation(AbsoluteFilePath, Range.FileOffset); |
| 287 | SourceLocation EndLoc = BeginLoc.getLocWithOffset(Range.Length); |
| 288 | // Retrieve the source range for applicable highlights and fixes. Macro |
| 289 | // definition on the command line have locations in a virtual buffer and |
| 290 | // don't have valid file paths and are therefore not applicable. |
| 291 | return CharSourceRange::getCharRange(BeginLoc, EndLoc); |
| 292 | } |
| 293 | |
Alexander Kornienko | 38d81b4 | 2014-03-27 10:24:11 | [diff] [blame] | 294 | FileManager Files; |
| 295 | LangOptions LangOpts; // FIXME: use langopts from each original file |
| 296 | IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; |
| 297 | DiagnosticConsumer *DiagPrinter; |
| 298 | DiagnosticsEngine Diags; |
| 299 | SourceManager SourceMgr; |
Alexander Kornienko | 98d3391 | 2016-10-17 17:25:02 | [diff] [blame] | 300 | llvm::StringMap<Replacements> FileReplacements; |
Alexander Kornienko | 2561320 | 2017-04-06 13:41:29 | [diff] [blame] | 301 | ClangTidyContext &Context; |
Nathan James | abbe9e2 | 2021-03-01 22:07:09 | [diff] [blame] | 302 | FixBehaviour ApplyFixes; |
NAKAMURA Takumi | 4dd1813 | 2014-03-27 14:53:37 | [diff] [blame] | 303 | unsigned TotalFixes; |
| 304 | unsigned AppliedFixes; |
Jonathan Roelofs | d60388a | 2016-01-13 17:36:41 | [diff] [blame] | 305 | unsigned WarningsAsErrors; |
Alexander Kornienko | 38d81b4 | 2014-03-27 10:24:11 | [diff] [blame] | 306 | }; |
| 307 | |
Alexander Kornienko | a469522 | 2014-06-05 13:31:45 | [diff] [blame] | 308 | class ClangTidyASTConsumer : public MultiplexConsumer { |
| 309 | public: |
David Blaikie | 680c4c8 | 2014-08-10 19:56:59 | [diff] [blame] | 310 | ClangTidyASTConsumer(std::vector<std::unique_ptr<ASTConsumer>> Consumers, |
Roman Lebedev | 1215251 | 2018-05-08 13:14:21 | [diff] [blame] | 311 | std::unique_ptr<ClangTidyProfiling> Profiling, |
Alexander Kornienko | a469522 | 2014-06-05 13:31:45 | [diff] [blame] | 312 | std::unique_ptr<ast_matchers::MatchFinder> Finder, |
| 313 | std::vector<std::unique_ptr<ClangTidyCheck>> Checks) |
Roman Lebedev | 1215251 | 2018-05-08 13:14:21 | [diff] [blame] | 314 | : MultiplexConsumer(std::move(Consumers)), |
| 315 | Profiling(std::move(Profiling)), Finder(std::move(Finder)), |
Alexander Kornienko | a469522 | 2014-06-05 13:31:45 | [diff] [blame] | 316 | Checks(std::move(Checks)) {} |
| 317 | |
| 318 | private: |
Roman Lebedev | 1215251 | 2018-05-08 13:14:21 | [diff] [blame] | 319 | // Destructor order matters! Profiling must be destructed last. |
| 320 | // Or at least after Finder. |
| 321 | std::unique_ptr<ClangTidyProfiling> Profiling; |
Alexander Kornienko | a469522 | 2014-06-05 13:31:45 | [diff] [blame] | 322 | std::unique_ptr<ast_matchers::MatchFinder> Finder; |
| 323 | std::vector<std::unique_ptr<ClangTidyCheck>> Checks; |
| 324 | }; |
| 325 | |
Alexander Kornienko | 175fefb | 2014-01-03 09:31:57 | [diff] [blame] | 326 | } // namespace |
Alexander Kornienko | fb9e92b | 2013-12-19 19:57:05 | [diff] [blame] | 327 | |
Alexander Kornienko | 175fefb | 2014-01-03 09:31:57 | [diff] [blame] | 328 | ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory( |
Alexander Kornienko | bbc89dc | 2019-03-22 13:42:48 | [diff] [blame] | 329 | ClangTidyContext &Context, |
| 330 | IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS) |
Nathan James | 64badec | 2020-12-17 14:09:08 | [diff] [blame] | 331 | : Context(Context), OverlayFS(std::move(OverlayFS)), |
Alexander Kornienko | bbc89dc | 2019-03-22 13:42:48 | [diff] [blame] | 332 | CheckFactories(new ClangTidyCheckFactories) { |
Alexander Kornienko | aa0e92e | 2019-11-22 11:22:40 | [diff] [blame] | 333 | for (ClangTidyModuleRegistry::entry E : ClangTidyModuleRegistry::entries()) { |
| 334 | std::unique_ptr<ClangTidyModule> Module = E.instantiate(); |
Alexander Kornienko | 175fefb | 2014-01-03 09:31:57 | [diff] [blame] | 335 | Module->addCheckFactories(*CheckFactories); |
| 336 | } |
Alexander Kornienko | 175fefb | 2014-01-03 09:31:57 | [diff] [blame] | 337 | } |
Alexander Kornienko | fb9e92b | 2013-12-19 19:57:05 | [diff] [blame] | 338 | |
Nico Weber | 33c9dbb | 2020-09-03 23:37:29 | [diff] [blame] | 339 | #if CLANG_TIDY_ENABLE_STATIC_ANALYZER |
Nathan James | 64badec | 2020-12-17 14:09:08 | [diff] [blame] | 340 | static void |
| 341 | setStaticAnalyzerCheckerOpts(const ClangTidyOptions &Opts, |
| 342 | clang::AnalyzerOptions &AnalyzerOptions) { |
Gabor Horvath | 3438321 | 2015-03-11 17:25:22 | [diff] [blame] | 343 | StringRef AnalyzerPrefix(AnalyzerCheckNamePrefix); |
| 344 | for (const auto &Opt : Opts.CheckOptions) { |
Nathan James | 45a720a | 2020-07-30 09:31:12 | [diff] [blame] | 345 | StringRef OptName(Opt.getKey()); |
| 346 | if (!OptName.consume_front(AnalyzerPrefix)) |
Gabor Horvath | 3438321 | 2015-03-11 17:25:22 | [diff] [blame] | 347 | continue; |
Dmitry Polukhin | cb1ee34 | 2020-04-01 09:08:53 | [diff] [blame] | 348 | // Analyzer options are always local options so we can ignore priority. |
Nathan James | 64badec | 2020-12-17 14:09:08 | [diff] [blame] | 349 | AnalyzerOptions.Config[OptName] = Opt.getValue().Value; |
Gabor Horvath | 3438321 | 2015-03-11 17:25:22 | [diff] [blame] | 350 | } |
| 351 | } |
| 352 | |
Alexander Kornienko | 179e89f | 2016-11-08 07:43:42 | [diff] [blame] | 353 | typedef std::vector<std::pair<std::string, bool>> CheckersList; |
| 354 | |
Csaba Dabis | a079a42 | 2019-08-16 01:53:14 | [diff] [blame] | 355 | static CheckersList getAnalyzerCheckersAndPackages(ClangTidyContext &Context, |
| 356 | bool IncludeExperimental) { |
Alexander Kornienko | 179e89f | 2016-11-08 07:43:42 | [diff] [blame] | 357 | CheckersList List; |
| 358 | |
| 359 | const auto &RegisteredCheckers = |
Alexander Kornienko | 85a92c3 | 2018-05-17 14:04:27 | [diff] [blame] | 360 | AnalyzerOptions::getRegisteredCheckers(IncludeExperimental); |
Alexander Kornienko | 179e89f | 2016-11-08 07:43:42 | [diff] [blame] | 361 | bool AnalyzerChecksEnabled = false; |
| 362 | for (StringRef CheckName : RegisteredCheckers) { |
| 363 | std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str()); |
Alexander Kornienko | 2137518 | 2017-05-17 14:39:47 | [diff] [blame] | 364 | AnalyzerChecksEnabled |= Context.isCheckEnabled(ClangTidyCheckName); |
Alexander Kornienko | 179e89f | 2016-11-08 07:43:42 | [diff] [blame] | 365 | } |
| 366 | |
| 367 | if (!AnalyzerChecksEnabled) |
| 368 | return List; |
| 369 | |
| 370 | // List all static analyzer checkers that our filter enables. |
| 371 | // |
| 372 | // Always add all core checkers if any other static analyzer check is enabled. |
| 373 | // This is currently necessary, as other path sensitive checks rely on the |
| 374 | // core checkers. |
| 375 | for (StringRef CheckName : RegisteredCheckers) { |
| 376 | std::string ClangTidyCheckName((AnalyzerCheckNamePrefix + CheckName).str()); |
| 377 | |
Alexander Kornienko | 2137518 | 2017-05-17 14:39:47 | [diff] [blame] | 378 | if (CheckName.startswith("core") || |
| 379 | Context.isCheckEnabled(ClangTidyCheckName)) { |
Mikael Holmén | 2103e08 | 2020-01-29 09:32:04 | [diff] [blame] | 380 | List.emplace_back(std::string(CheckName), true); |
Alexander Kornienko | 2137518 | 2017-05-17 14:39:47 | [diff] [blame] | 381 | } |
Alexander Kornienko | 179e89f | 2016-11-08 07:43:42 | [diff] [blame] | 382 | } |
| 383 | return List; |
| 384 | } |
Nico Weber | 33c9dbb | 2020-09-03 23:37:29 | [diff] [blame] | 385 | #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER |
Alexander Kornienko | 179e89f | 2016-11-08 07:43:42 | [diff] [blame] | 386 | |
David Blaikie | 680c4c8 | 2014-08-10 19:56:59 | [diff] [blame] | 387 | std::unique_ptr<clang::ASTConsumer> |
| 388 | ClangTidyASTConsumerFactory::CreateASTConsumer( |
Alexander Kornienko | 175fefb | 2014-01-03 09:31:57 | [diff] [blame] | 389 | clang::CompilerInstance &Compiler, StringRef File) { |
| 390 | // FIXME: Move this to a separate method, so that CreateASTConsumer doesn't |
| 391 | // modify Compiler. |
Alexander Kornienko | bbc89dc | 2019-03-22 13:42:48 | [diff] [blame] | 392 | SourceManager *SM = &Compiler.getSourceManager(); |
| 393 | Context.setSourceManager(SM); |
Alexander Kornienko | a469522 | 2014-06-05 13:31:45 | [diff] [blame] | 394 | Context.setCurrentFile(File); |
Alexander Kornienko | ad21688 | 2014-07-14 14:10:03 | [diff] [blame] | 395 | Context.setASTContext(&Compiler.getASTContext()); |
Alexander Kornienko | a469522 | 2014-06-05 13:31:45 | [diff] [blame] | 396 | |
Haojian Wu | f7692a2 | 2016-02-26 09:19:33 | [diff] [blame] | 397 | auto WorkingDir = Compiler.getSourceManager() |
| 398 | .getFileManager() |
| 399 | .getVirtualFileSystem() |
Duncan P. N. Exon Smith | db8a742 | 2019-03-26 22:32:06 | [diff] [blame] | 400 | .getCurrentWorkingDirectory(); |
Haojian Wu | f7692a2 | 2016-02-26 09:19:33 | [diff] [blame] | 401 | if (WorkingDir) |
| 402 | Context.setCurrentBuildDirectory(WorkingDir.get()); |
| 403 | |
Dmitri Gribenko | bb7a9dc | 2019-09-26 13:55:01 | [diff] [blame] | 404 | std::vector<std::unique_ptr<ClangTidyCheck>> Checks = |
| 405 | CheckFactories->createChecks(&Context); |
Alexander Kornienko | a469522 | 2014-06-05 13:31:45 | [diff] [blame] | 406 | |
Nathan James | 3bca861 | 2021-03-05 15:35:24 | [diff] [blame] | 407 | llvm::erase_if(Checks, [&](std::unique_ptr<ClangTidyCheck> &Check) { |
| 408 | return !Check->isLanguageVersionSupported(Context.getLangOpts()); |
| 409 | }); |
| 410 | |
Samuel Benzaquen | aedd994 | 2014-10-23 17:23:20 | [diff] [blame] | 411 | ast_matchers::MatchFinder::MatchFinderOptions FinderOptions; |
Roman Lebedev | 1215251 | 2018-05-08 13:14:21 | [diff] [blame] | 412 | |
| 413 | std::unique_ptr<ClangTidyProfiling> Profiling; |
| 414 | if (Context.getEnableProfiling()) { |
Jonas Devlieghere | 1c705d9 | 2019-08-14 23:52:23 | [diff] [blame] | 415 | Profiling = std::make_unique<ClangTidyProfiling>( |
Roman Lebedev | a87f1d0 | 2018-06-06 15:07:51 | [diff] [blame] | 416 | Context.getProfileStorageParams()); |
Roman Lebedev | 1215251 | 2018-05-08 13:14:21 | [diff] [blame] | 417 | FinderOptions.CheckProfiling.emplace(Profiling->Records); |
| 418 | } |
Samuel Benzaquen | aedd994 | 2014-10-23 17:23:20 | [diff] [blame] | 419 | |
Alexander Kornienko | a469522 | 2014-06-05 13:31:45 | [diff] [blame] | 420 | std::unique_ptr<ast_matchers::MatchFinder> Finder( |
Samuel Benzaquen | aedd994 | 2014-10-23 17:23:20 | [diff] [blame] | 421 | new ast_matchers::MatchFinder(std::move(FinderOptions))); |
| 422 | |
Alexander Kornienko | bbc89dc | 2019-03-22 13:42:48 | [diff] [blame] | 423 | Preprocessor *PP = &Compiler.getPreprocessor(); |
| 424 | Preprocessor *ModuleExpanderPP = PP; |
| 425 | |
| 426 | if (Context.getLangOpts().Modules && OverlayFS != nullptr) { |
Jonas Devlieghere | 1c705d9 | 2019-08-14 23:52:23 | [diff] [blame] | 427 | auto ModuleExpander = std::make_unique<ExpandModularHeadersPPCallbacks>( |
Alexander Kornienko | bbc89dc | 2019-03-22 13:42:48 | [diff] [blame] | 428 | &Compiler, OverlayFS); |
| 429 | ModuleExpanderPP = ModuleExpander->getPreprocessor(); |
| 430 | PP->addPPCallbacks(std::move(ModuleExpander)); |
| 431 | } |
| 432 | |
Alexander Kornienko | a469522 | 2014-06-05 13:31:45 | [diff] [blame] | 433 | for (auto &Check : Checks) { |
Alexander Kornienko | a469522 | 2014-06-05 13:31:45 | [diff] [blame] | 434 | Check->registerMatchers(&*Finder); |
Alexander Kornienko | bbc89dc | 2019-03-22 13:42:48 | [diff] [blame] | 435 | Check->registerPPCallbacks(*SM, PP, ModuleExpanderPP); |
Alexander Kornienko | a469522 | 2014-06-05 13:31:45 | [diff] [blame] | 436 | } |
Alexander Kornienko | 175fefb | 2014-01-03 09:31:57 | [diff] [blame] | 437 | |
David Blaikie | 680c4c8 | 2014-08-10 19:56:59 | [diff] [blame] | 438 | std::vector<std::unique_ptr<ASTConsumer>> Consumers; |
Alexander Kornienko | a469522 | 2014-06-05 13:31:45 | [diff] [blame] | 439 | if (!Checks.empty()) |
| 440 | Consumers.push_back(Finder->newASTConsumer()); |
Alexander Kornienko | 298b382 | 2014-02-13 16:10:47 | [diff] [blame] | 441 | |
Nico Weber | 33c9dbb | 2020-09-03 23:37:29 | [diff] [blame] | 442 | #if CLANG_TIDY_ENABLE_STATIC_ANALYZER |
Alex McCarthy | fec08c7 | 2014-04-30 14:09:24 | [diff] [blame] | 443 | AnalyzerOptionsRef AnalyzerOptions = Compiler.getAnalyzerOpts(); |
Csaba Dabis | a079a42 | 2019-08-16 01:53:14 | [diff] [blame] | 444 | AnalyzerOptions->CheckersAndPackages = getAnalyzerCheckersAndPackages( |
| 445 | Context, Context.canEnableAnalyzerAlphaCheckers()); |
| 446 | if (!AnalyzerOptions->CheckersAndPackages.empty()) { |
Nathan James | 64badec | 2020-12-17 14:09:08 | [diff] [blame] | 447 | setStaticAnalyzerCheckerOpts(Context.getOptions(), *AnalyzerOptions); |
Alex McCarthy | fec08c7 | 2014-04-30 14:09:24 | [diff] [blame] | 448 | AnalyzerOptions->AnalysisStoreOpt = RegionStoreModel; |
| 449 | AnalyzerOptions->AnalysisDiagOpt = PD_NONE; |
| 450 | AnalyzerOptions->AnalyzeNestedBlocks = true; |
| 451 | AnalyzerOptions->eagerlyAssumeBinOpBifurcation = true; |
David Blaikie | 680c4c8 | 2014-08-10 19:56:59 | [diff] [blame] | 452 | std::unique_ptr<ento::AnalysisASTConsumer> AnalysisConsumer = |
Ted Kremenek | 4d1692f | 2014-08-27 15:14:47 | [diff] [blame] | 453 | ento::CreateAnalysisConsumer(Compiler); |
Alexander Kornienko | 298b382 | 2014-02-13 16:10:47 | [diff] [blame] | 454 | AnalysisConsumer->AddDiagnosticConsumer( |
| 455 | new AnalyzerDiagnosticConsumer(Context)); |
David Blaikie | 680c4c8 | 2014-08-10 19:56:59 | [diff] [blame] | 456 | Consumers.push_back(std::move(AnalysisConsumer)); |
Alexander Kornienko | 298b382 | 2014-02-13 16:10:47 | [diff] [blame] | 457 | } |
Nico Weber | 33c9dbb | 2020-09-03 23:37:29 | [diff] [blame] | 458 | #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER |
Jonas Devlieghere | 1c705d9 | 2019-08-14 23:52:23 | [diff] [blame] | 459 | return std::make_unique<ClangTidyASTConsumer>( |
Roman Lebedev | 1215251 | 2018-05-08 13:14:21 | [diff] [blame] | 460 | std::move(Consumers), std::move(Profiling), std::move(Finder), |
| 461 | std::move(Checks)); |
Alexander Kornienko | 175fefb | 2014-01-03 09:31:57 | [diff] [blame] | 462 | } |
| 463 | |
Alexander Kornienko | 6e0cbc8 | 2014-09-12 08:53:36 | [diff] [blame] | 464 | std::vector<std::string> ClangTidyASTConsumerFactory::getCheckNames() { |
Alexander Kornienko | 175fefb | 2014-01-03 09:31:57 | [diff] [blame] | 465 | std::vector<std::string> CheckNames; |
Alexander Kornienko | df1e3cb | 2014-03-06 10:17:46 | [diff] [blame] | 466 | for (const auto &CheckFactory : *CheckFactories) { |
Nathan James | c23ae3f | 2020-07-30 21:57:32 | [diff] [blame] | 467 | if (Context.isCheckEnabled(CheckFactory.getKey())) |
| 468 | CheckNames.emplace_back(CheckFactory.getKey()); |
Alexander Kornienko | 175fefb | 2014-01-03 09:31:57 | [diff] [blame] | 469 | } |
| 470 | |
Nico Weber | 33c9dbb | 2020-09-03 23:37:29 | [diff] [blame] | 471 | #if CLANG_TIDY_ENABLE_STATIC_ANALYZER |
Csaba Dabis | a079a42 | 2019-08-16 01:53:14 | [diff] [blame] | 472 | for (const auto &AnalyzerCheck : getAnalyzerCheckersAndPackages( |
Alexander Kornienko | 85a92c3 | 2018-05-17 14:04:27 | [diff] [blame] | 473 | Context, Context.canEnableAnalyzerAlphaCheckers())) |
Alexander Kornienko | df1e3cb | 2014-03-06 10:17:46 | [diff] [blame] | 474 | CheckNames.push_back(AnalyzerCheckNamePrefix + AnalyzerCheck.first); |
Nico Weber | 33c9dbb | 2020-09-03 23:37:29 | [diff] [blame] | 475 | #endif // CLANG_TIDY_ENABLE_STATIC_ANALYZER |
Alexander Kornienko | 175fefb | 2014-01-03 09:31:57 | [diff] [blame] | 476 | |
Benjamin Kramer | 4065e92 | 2020-03-28 18:19:55 | [diff] [blame] | 477 | llvm::sort(CheckNames); |
Alexander Kornienko | 175fefb | 2014-01-03 09:31:57 | [diff] [blame] | 478 | return CheckNames; |
| 479 | } |
| 480 | |
Alexander Kornienko | 6e0cbc8 | 2014-09-12 08:53:36 | [diff] [blame] | 481 | ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() { |
| 482 | ClangTidyOptions::OptionMap Options; |
Dmitri Gribenko | bb7a9dc | 2019-09-26 13:55:01 | [diff] [blame] | 483 | std::vector<std::unique_ptr<ClangTidyCheck>> Checks = |
| 484 | CheckFactories->createChecks(&Context); |
Alexander Kornienko | 6e0cbc8 | 2014-09-12 08:53:36 | [diff] [blame] | 485 | for (const auto &Check : Checks) |
| 486 | Check->storeOptions(Options); |
| 487 | return Options; |
| 488 | } |
| 489 | |
Alexander Kornienko | 85a92c3 | 2018-05-17 14:04:27 | [diff] [blame] | 490 | std::vector<std::string> |
| 491 | getCheckNames(const ClangTidyOptions &Options, |
| 492 | bool AllowEnablingAnalyzerAlphaCheckers) { |
Alexander Kornienko | a469522 | 2014-06-05 13:31:45 | [diff] [blame] | 493 | clang::tidy::ClangTidyContext Context( |
Jonas Devlieghere | 1c705d9 | 2019-08-14 23:52:23 | [diff] [blame] | 494 | std::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(), |
Alexander Kornienko | 85a92c3 | 2018-05-17 14:04:27 | [diff] [blame] | 495 | Options), |
| 496 | AllowEnablingAnalyzerAlphaCheckers); |
Alexander Kornienko | a469522 | 2014-06-05 13:31:45 | [diff] [blame] | 497 | ClangTidyASTConsumerFactory Factory(Context); |
Alexander Kornienko | 6e0cbc8 | 2014-09-12 08:53:36 | [diff] [blame] | 498 | return Factory.getCheckNames(); |
| 499 | } |
| 500 | |
Alexander Kornienko | 85a92c3 | 2018-05-17 14:04:27 | [diff] [blame] | 501 | ClangTidyOptions::OptionMap |
| 502 | getCheckOptions(const ClangTidyOptions &Options, |
| 503 | bool AllowEnablingAnalyzerAlphaCheckers) { |
Alexander Kornienko | 6e0cbc8 | 2014-09-12 08:53:36 | [diff] [blame] | 504 | clang::tidy::ClangTidyContext Context( |
Jonas Devlieghere | 1c705d9 | 2019-08-14 23:52:23 | [diff] [blame] | 505 | std::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(), |
Alexander Kornienko | 85a92c3 | 2018-05-17 14:04:27 | [diff] [blame] | 506 | Options), |
| 507 | AllowEnablingAnalyzerAlphaCheckers); |
Alexander Kornienko | 6e0cbc8 | 2014-09-12 08:53:36 | [diff] [blame] | 508 | ClangTidyASTConsumerFactory Factory(Context); |
| 509 | return Factory.getCheckOptions(); |
Alexander Kornienko | fb9e92b | 2013-12-19 19:57:05 | [diff] [blame] | 510 | } |
| 511 | |
Sam McCall | cb50e23 | 2018-11-02 10:01:59 | [diff] [blame] | 512 | std::vector<ClangTidyError> |
| 513 | runClangTidy(clang::tidy::ClangTidyContext &Context, |
| 514 | const CompilationDatabase &Compilations, |
| 515 | ArrayRef<std::string> InputFiles, |
Alexander Kornienko | bbc89dc | 2019-03-22 13:42:48 | [diff] [blame] | 516 | llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS, |
Nathan James | abbe9e2 | 2021-03-01 22:07:09 | [diff] [blame] | 517 | bool ApplyAnyFix, bool EnableCheckProfile, |
| 518 | llvm::StringRef StoreCheckProfile) { |
Ilya Biryukov | a67b366 | 2018-01-23 12:31:06 | [diff] [blame] | 519 | ClangTool Tool(Compilations, InputFiles, |
| 520 | std::make_shared<PCHContainerOperations>(), BaseFS); |
Etienne Bergeron | 4eaeace | 2016-04-06 14:07:51 | [diff] [blame] | 521 | |
| 522 | // Add extra arguments passed by the clang-tidy command-line. |
| 523 | ArgumentsAdjuster PerFileExtraArgumentsInserter = |
| 524 | [&Context](const CommandLineArguments &Args, StringRef Filename) { |
| 525 | ClangTidyOptions Opts = Context.getOptionsForFile(Filename); |
Alexander Kornienko | 42fd75e | 2016-08-23 14:13:31 | [diff] [blame] | 526 | CommandLineArguments AdjustedArgs = Args; |
| 527 | if (Opts.ExtraArgsBefore) { |
| 528 | auto I = AdjustedArgs.begin(); |
| 529 | if (I != AdjustedArgs.end() && !StringRef(*I).startswith("-")) |
| 530 | ++I; // Skip compiler binary name, if it is there. |
| 531 | AdjustedArgs.insert(I, Opts.ExtraArgsBefore->begin(), |
| 532 | Opts.ExtraArgsBefore->end()); |
| 533 | } |
Etienne Bergeron | 4eaeace | 2016-04-06 14:07:51 | [diff] [blame] | 534 | if (Opts.ExtraArgs) |
| 535 | AdjustedArgs.insert(AdjustedArgs.end(), Opts.ExtraArgs->begin(), |
| 536 | Opts.ExtraArgs->end()); |
| 537 | return AdjustedArgs; |
| 538 | }; |
| 539 | |
Alexander Kornienko | 64956b5 | 2015-11-09 16:28:11 | [diff] [blame] | 540 | Tool.appendArgumentsAdjuster(PerFileExtraArgumentsInserter); |
Kadir Cetinkaya | fd29980 | 2019-01-21 10:10:18 | [diff] [blame] | 541 | Tool.appendArgumentsAdjuster(getStripPluginsAdjuster()); |
Roman Lebedev | 1215251 | 2018-05-08 13:14:21 | [diff] [blame] | 542 | Context.setEnableProfiling(EnableCheckProfile); |
Roman Lebedev | a87f1d0 | 2018-06-06 15:07:51 | [diff] [blame] | 543 | Context.setProfileStoragePrefix(StoreCheckProfile); |
Samuel Benzaquen | aedd994 | 2014-10-23 17:23:20 | [diff] [blame] | 544 | |
Nathan James | abbe9e2 | 2021-03-01 22:07:09 | [diff] [blame] | 545 | ClangTidyDiagnosticConsumer DiagConsumer(Context, nullptr, true, ApplyAnyFix); |
Sam McCall | 3c1f490 | 2018-11-08 17:42:16 | [diff] [blame] | 546 | DiagnosticsEngine DE(new DiagnosticIDs(), new DiagnosticOptions(), |
| 547 | &DiagConsumer, /*ShouldOwnClient=*/false); |
| 548 | Context.setDiagnosticsEngine(&DE); |
Manuel Klimek | 814f9bd | 2013-11-14 15:49:44 | [diff] [blame] | 549 | Tool.setDiagnosticConsumer(&DiagConsumer); |
Alexander Kornienko | 175fefb | 2014-01-03 09:31:57 | [diff] [blame] | 550 | |
| 551 | class ActionFactory : public FrontendActionFactory { |
| 552 | public: |
Alexander Kornienko | bbc89dc | 2019-03-22 13:42:48 | [diff] [blame] | 553 | ActionFactory(ClangTidyContext &Context, |
| 554 | IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> BaseFS) |
Nathan James | 64badec | 2020-12-17 14:09:08 | [diff] [blame] | 555 | : ConsumerFactory(Context, std::move(BaseFS)) {} |
Dmitri Gribenko | 9074521 | 2019-08-29 16:38:36 | [diff] [blame] | 556 | std::unique_ptr<FrontendAction> create() override { |
| 557 | return std::make_unique<Action>(&ConsumerFactory); |
| 558 | } |
Alexander Kornienko | 175fefb | 2014-01-03 09:31:57 | [diff] [blame] | 559 | |
Zinovy Nis | beca768 | 2018-05-03 18:26:39 | [diff] [blame] | 560 | bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation, |
| 561 | FileManager *Files, |
| 562 | std::shared_ptr<PCHContainerOperations> PCHContainerOps, |
| 563 | DiagnosticConsumer *DiagConsumer) override { |
Jan Korous | c5d14b5 | 2019-10-14 20:15:01 | [diff] [blame] | 564 | // Explicitly ask to define __clang_analyzer__ macro. |
| 565 | Invocation->getPreprocessorOpts().SetUpStaticAnalyzer = true; |
Zinovy Nis | beca768 | 2018-05-03 18:26:39 | [diff] [blame] | 566 | return FrontendActionFactory::runInvocation( |
| 567 | Invocation, Files, PCHContainerOps, DiagConsumer); |
| 568 | } |
| 569 | |
Alexander Kornienko | 175fefb | 2014-01-03 09:31:57 | [diff] [blame] | 570 | private: |
| 571 | class Action : public ASTFrontendAction { |
| 572 | public: |
| 573 | Action(ClangTidyASTConsumerFactory *Factory) : Factory(Factory) {} |
David Blaikie | 680c4c8 | 2014-08-10 19:56:59 | [diff] [blame] | 574 | std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, |
| 575 | StringRef File) override { |
Alexander Kornienko | 175fefb | 2014-01-03 09:31:57 | [diff] [blame] | 576 | return Factory->CreateASTConsumer(Compiler, File); |
| 577 | } |
| 578 | |
| 579 | private: |
| 580 | ClangTidyASTConsumerFactory *Factory; |
| 581 | }; |
| 582 | |
Benjamin Kramer | 6e91424 | 2014-07-24 10:23:33 | [diff] [blame] | 583 | ClangTidyASTConsumerFactory ConsumerFactory; |
Alexander Kornienko | 175fefb | 2014-01-03 09:31:57 | [diff] [blame] | 584 | }; |
| 585 | |
Nathan James | 64badec | 2020-12-17 14:09:08 | [diff] [blame] | 586 | ActionFactory Factory(Context, std::move(BaseFS)); |
Benjamin Kramer | 6e91424 | 2014-07-24 10:23:33 | [diff] [blame] | 587 | Tool.run(&Factory); |
Sam McCall | cb50e23 | 2018-11-02 10:01:59 | [diff] [blame] | 588 | return DiagConsumer.take(); |
Manuel Klimek | 814f9bd | 2013-11-14 15:49:44 | [diff] [blame] | 589 | } |
| 590 | |
Sam McCall | cb50e23 | 2018-11-02 10:01:59 | [diff] [blame] | 591 | void handleErrors(llvm::ArrayRef<ClangTidyError> Errors, |
Nathan James | abbe9e2 | 2021-03-01 22:07:09 | [diff] [blame] | 592 | ClangTidyContext &Context, FixBehaviour Fix, |
Ilya Biryukov | a67b366 | 2018-01-23 12:31:06 | [diff] [blame] | 593 | unsigned &WarningsAsErrorsCount, |
Jonas Devlieghere | fc51490 | 2018-10-10 13:27:25 | [diff] [blame] | 594 | llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS) { |
Nathan James | 64badec | 2020-12-17 14:09:08 | [diff] [blame] | 595 | ErrorReporter Reporter(Context, Fix, std::move(BaseFS)); |
Jonas Devlieghere | fc51490 | 2018-10-10 13:27:25 | [diff] [blame] | 596 | llvm::vfs::FileSystem &FileSystem = |
Duncan P. N. Exon Smith | db8a742 | 2019-03-26 22:32:06 | [diff] [blame] | 597 | Reporter.getSourceManager().getFileManager().getVirtualFileSystem(); |
Haojian Wu | f7692a2 | 2016-02-26 09:19:33 | [diff] [blame] | 598 | auto InitialWorkingDir = FileSystem.getCurrentWorkingDirectory(); |
| 599 | if (!InitialWorkingDir) |
| 600 | llvm::report_fatal_error("Cannot get current working path."); |
| 601 | |
Sam McCall | cb50e23 | 2018-11-02 10:01:59 | [diff] [blame] | 602 | for (const ClangTidyError &Error : Errors) { |
Haojian Wu | f7692a2 | 2016-02-26 09:19:33 | [diff] [blame] | 603 | if (!Error.BuildDirectory.empty()) { |
| 604 | // By default, the working directory of file system is the current |
| 605 | // clang-tidy running directory. |
| 606 | // |
| 607 | // Change the directory to the one used during the analysis. |
| 608 | FileSystem.setCurrentWorkingDirectory(Error.BuildDirectory); |
| 609 | } |
Alexander Kornienko | 742790c | 2014-07-02 15:05:04 | [diff] [blame] | 610 | Reporter.reportDiagnostic(Error); |
Haojian Wu | f7692a2 | 2016-02-26 09:19:33 | [diff] [blame] | 611 | // Return to the initial directory to correctly resolve next Error. |
| 612 | FileSystem.setCurrentWorkingDirectory(InitialWorkingDir.get()); |
| 613 | } |
Alexander Kornienko | ab2d3ce | 2021-01-28 23:49:53 | [diff] [blame] | 614 | Reporter.finish(); |
Jonathan Roelofs | d60388a | 2016-01-13 17:36:41 | [diff] [blame] | 615 | WarningsAsErrorsCount += Reporter.getWarningsAsErrorsCount(); |
Daniel Jasper | d07c840 | 2013-07-29 08:19:24 | [diff] [blame] | 616 | } |
| 617 | |
Alexander Kornienko | 563de79 | 2017-01-03 14:36:13 | [diff] [blame] | 618 | void exportReplacements(const llvm::StringRef MainFilePath, |
| 619 | const std::vector<ClangTidyError> &Errors, |
Benjamin Kramer | fb98b74 | 2014-09-04 10:31:23 | [diff] [blame] | 620 | raw_ostream &OS) { |
Alexander Kornienko | 563de79 | 2017-01-03 14:36:13 | [diff] [blame] | 621 | TranslationUnitDiagnostics TUD; |
Benjamin Kramer | adcd026 | 2020-01-28 19:23:46 | [diff] [blame] | 622 | TUD.MainSourceFile = std::string(MainFilePath); |
Alexander Kornienko | 563de79 | 2017-01-03 14:36:13 | [diff] [blame] | 623 | for (const auto &Error : Errors) { |
| 624 | tooling::Diagnostic Diag = Error; |
| 625 | TUD.Diagnostics.insert(TUD.Diagnostics.end(), Diag); |
Eric Liu | 7e54452 | 2016-08-09 07:54:49 | [diff] [blame] | 626 | } |
Benjamin Kramer | fb98b74 | 2014-09-04 10:31:23 | [diff] [blame] | 627 | |
| 628 | yaml::Output YAML(OS); |
Alexander Kornienko | 563de79 | 2017-01-03 14:36:13 | [diff] [blame] | 629 | YAML << TUD; |
Benjamin Kramer | fb98b74 | 2014-09-04 10:31:23 | [diff] [blame] | 630 | } |
| 631 | |
Daniel Jasper | d07c840 | 2013-07-29 08:19:24 | [diff] [blame] | 632 | } // namespace tidy |
| 633 | } // namespace clang |