20#include "clang/ASTMatchers/ASTMatchFinder.h"
21#include "clang/Basic/Diagnostic.h"
22#include "clang/Basic/DiagnosticIDs.h"
23#include "clang/Basic/FileManager.h"
24#include "clang/Frontend/CompilerInstance.h"
25#include "clang/Lex/Preprocessor.h"
26#include "clang/Lex/PreprocessorOptions.h"
27#include "clang/Tooling/Core/Diagnostic.h"
28#include "llvm/ADT/DenseSet.h"
29#include "llvm/ADT/STLExtras.h"
30#include "llvm/ADT/SmallString.h"
31#include "llvm/Support/MemoryBuffer.h"
32#include "llvm/Support/Path.h"
43#define CLANG_TIDY_DISABLE_STATIC_ANALYZER_CHECKS
47#if CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS
64class TidyDiagnosticGroups {
70 llvm::DenseSet<unsigned> Exceptions;
73 TidyDiagnosticGroups(llvm::StringRef
Checks) {
74 constexpr llvm::StringLiteral Prefix =
"clang-diagnostic-";
75 llvm::StringRef Check;
82 bool Enable = !Check.consume_front(
"-");
83 bool Glob = Check.consume_back(
"*");
87 if (Prefix.starts_with(Check)) {
95 if (Default == Enable || !Check.consume_front(Prefix))
97 if (
auto Group = DiagnosticIDs::getGroupForWarningOption(Check))
98 Exceptions.insert(
static_cast<unsigned>(*Group));
102 bool operator()(diag::Group Group)
const {
103 return Exceptions.contains(
static_cast<unsigned>(Group)) ? !Default
113void applyWarningOptions(llvm::ArrayRef<std::string> ExtraArgs,
114 llvm::function_ref<
bool(diag::Group)> EnabledGroups,
115 DiagnosticsEngine &Diags) {
116 for (llvm::StringRef Group : ExtraArgs) {
119 llvm::SmallVector<diag::kind> Members;
120 if (!Group.consume_front(
"-W") || Group.empty())
122 bool Enable = !Group.consume_front(
"no-");
123 if (Diags.getDiagnosticIDs()->getDiagnosticsInGroup(
124 diag::Flavor::WarningOrError, Group, Members))
130 bool NeedsWerrorExclusion =
false;
131 for (diag::kind ID : Members) {
133 if (Diags.getDiagnosticLevel(ID, SourceLocation()) >=
134 DiagnosticsEngine::Warning)
136 auto DiagGroup = Diags.getDiagnosticIDs()->getGroupForDiag(ID);
137 if (!DiagGroup || !EnabledGroups(*DiagGroup))
139 Diags.setSeverity(ID, diag::Severity::Warning, SourceLocation());
140 NeedsWerrorExclusion |= Diags.getWarningsAsErrors();
142 Diags.setSeverity(ID, diag::Severity::Ignored, SourceLocation());
145 if (NeedsWerrorExclusion) {
149 Diags.setDiagnosticGroupWarningAsError(Group,
false);
154tidy::ClangTidyCheckFactories
155filterFastChecks(
const tidy::ClangTidyCheckFactories &
All,
160 tidy::ClangTidyCheckFactories Fast;
161 for (
const auto &Factory :
All)
163 Fast.registerCheckFactory(Factory.first(), Factory.second);
169 explicit TidyASTListener(std::shared_ptr<const TidyProvider> OptionsProvider)
170 : Provider(std::move(OptionsProvider)) {}
172 void beforeBeginSourceFile(CompilerInstance &CI)
override {
175 const auto &Inputs = CI.getFrontendOpts().Inputs;
176 if (Inputs.empty() || !Inputs.front().isFile()) {
177 elog(
"Cannot configure clang-tidy without a main-file input");
180 llvm::SmallString<256>
Path(Inputs.front().getFile());
182 for (
const auto &Remap :
183 llvm::reverse(CI.getPreprocessorOpts().RemappedFileBuffers)) {
184 if (Remap.first == Inputs.front().getFile()) {
185 Path = Remap.second->getBufferIdentifier();
191 if (!llvm::sys::path::is_absolute(
Path))
192 CI.getFileManager().makeAbsolutePath(
Path,
true);
193 if (!llvm::sys::path::is_absolute(
Path)) {
194 elog(
"Cannot resolve clang-tidy input path: {0}",
Path);
197 Filename =
Path.str().str();
198 trace::Span Tracer(
"ClangTidyOpts");
200 dlog(
"ClangTidy configuration for file {0}: {1}", Filename,
221 TidyDiagnosticGroups EnabledGroups(Options->Checks ? *Options->Checks
222 : llvm::StringRef());
223 if (Options->ExtraArgsBefore)
224 applyWarningOptions(*Options->ExtraArgsBefore, EnabledGroups,
225 CI.getDiagnostics());
226 if (Options->ExtraArgs)
227 applyWarningOptions(*Options->ExtraArgs, EnabledGroups,
228 CI.getDiagnostics());
231 void beforePPCallbacks(CompilerInstance &CI)
override {
243 trace::Span Tracer(
"ClangTidyInit");
244 static const auto *AllFactories = [] {
245 auto *Factories =
new tidy::ClangTidyCheckFactories;
246 for (
const auto &Entry : tidy::ClangTidyModuleRegistry::entries())
247 Entry.instantiate()->addCheckFactories(*Factories);
251 std::make_unique<tidy::DefaultOptionsProvider>(
252 tidy::ClangTidyGlobalOptions(), std::move(*Options)),
258 Context->setDiagnosticsEngine(
nullptr, &CI.getDiagnostics());
259 Context->setASTContext(&CI.getASTContext());
260 Context->setCurrentFile(Filename);
261 Context->setSelfContainedDiags(
true);
262 tidy::ClangTidyCheckFactories Factories = *AllFactories;
263#if CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS
264 if (Context->canExperimentalCustomChecks() &&
268 static std::mutex CustomChecksMu;
269 std::lock_guard<std::mutex> Lock(CustomChecksMu);
273 auto FastFactories = filterFastChecks(
275 Checks = FastFactories.createChecksForLanguage(&*Context);
277 Preprocessor *PP = &CI.getPreprocessor();
278 for (
const auto &Check : Checks) {
279 Check->registerPPCallbacks(CI.getSourceManager(), PP, PP);
280 Check->registerMatchers(&Finder);
284 void afterExecute(CompilerInstance &CI)
override {
289 trace::Span Tracer(
"ClangTidyMatch");
290 Finder.matchAST(CI.getASTContext());
293 void sawDiagnostic(
const clang::Diagnostic &
Info, clangd::Diag &D)
override {
297 std::string CheckName = Context->getCheckName(
Info.getID());
298 if (CheckName.empty())
302 D.Severity = DiagnosticsEngine::Ignored;
312 bool InsideMainFile =
313 Info.hasSourceManager() &&
315 llvm::SmallVector<tooling::Diagnostic, 1> SuppressionErrors;
316 if (InsideMainFile &&
317 Context->shouldSuppressDiagnostic(
D.Severity,
Info, SuppressionErrors,
322 D.Severity = DiagnosticsEngine::Ignored;
325 if (!Context->getOptions().SystemHeaders.value_or(
false) &&
326 Info.hasSourceManager() &&
327 Info.getSourceManager().isInSystemMacro(
Info.getLocation())) {
328 D.Severity = DiagnosticsEngine::Ignored;
332 if (
D.Severity == DiagnosticsEngine::Warning &&
333 Context->treatAsError(CheckName))
334 D.Severity = DiagnosticsEngine::Error;
338 if (!Context->isCompilerDiagnostic(
Info.getID())) {
339 D.Name = std::move(CheckName);
344 void finalizeDiagnostic(clangd::Diag &D)
override {
347 !Context->isCompilerDiagnostic(
D.ID)) {
348 auto Name = Context->getCheckName(
D.ID);
350 D.Name = std::move(Name);
358 auto CleanMessage = [&](std::string &Message) {
359 llvm::StringRef Rest(Message);
360 if (Rest.consume_back(
"]") && Rest.consume_back(
D.Name) &&
361 Rest.consume_back(
" ["))
362 Message.resize(Rest.size());
364 CleanMessage(
D.Message);
365 for (
auto &Note :
D.Notes)
366 CleanMessage(Note.Message);
367 for (
auto &
Fix :
D.Fixes)
368 CleanMessage(
Fix.Message);
369 if (llvm::StringRef(
D.Name).starts_with(
"misc-unused-") &&
372 if (llvm::StringRef(
D.Name).starts_with(
"modernize-") &&
378 std::shared_ptr<const TidyProvider> Provider;
379 std::string Filename;
380 std::optional<tidy::ClangTidyOptions> Options;
382 std::optional<tidy::ClangTidyContext> Context;
383 std::vector<std::unique_ptr<tidy::ClangTidyCheck>> Checks;
384 ast_matchers::MatchFinder Finder;
389std::unique_ptr<FeatureModule::ASTListener>
391 std::lock_guard<std::mutex> Lock(ProviderMu);
394 return std::make_unique<TidyASTListener>(Provider);
397void ClangTidyFeatureModule::setProvider(
TidyProvider OptionsProvider) {
398 std::shared_ptr<const TidyProvider> Replacement;
401 std::make_shared<const TidyProvider>(std::move(OptionsProvider));
406 std::shared_ptr<const TidyProvider> &OptionsProvider) {
407 std::lock_guard<std::mutex> Lock(ProviderMu);
408 Provider.swap(OptionsProvider);
static cl::opt< bool > Fix("fix", desc(R"(
Apply suggested fixes. Without -fix-errors
clang-tidy will bail out if any compilation
errors were found.
)"), cl::init(false), cl::cat(ClangTidyCategory))
static cl::opt< std::string > Checks("checks", desc(R"(
Comma-separated list of globs with optional '-'
prefix. Globs are processed in order of
appearance in the list. Globs without '-'
prefix add checks with matching names to the
set, globs with the '-' prefix remove checks
with matching names from the set of enabled
checks. This option's value is appended to the
value of the 'Checks' option in .clang-tidy
file, if any.
)"), cl::init(""), cl::cat(ClangTidyCategory))
void swapProvider(std::shared_ptr< const TidyProvider > &OptionsProvider)
Swap provider snapshots, e.g.
std::unique_ptr< ASTListener > astListeners() override
Can be called asynchronously before building an AST.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
llvm::unique_function< void(tidy::ClangTidyOptions &, llvm::StringRef) const > TidyProvider
A factory to modify a tidy::ClangTidyOptions.
@ Info
An information message.
bool isInsideMainFile(SourceLocation Loc, const SourceManager &SM)
Returns true iff Loc is inside the main file.
std::optional< bool > isFastTidyCheck(llvm::StringRef Check)
Returns if Check is known-fast, known-slow, or its speed is unknown.
tidy::ClangTidyOptions getTidyOptionsForFile(TidyProviderRef Provider, llvm::StringRef Filename)
std::string Path
A typedef to represent a file path.
@ Deprecated
Deprecated or obsolete code.
@ Unnecessary
Unused or unnecessary code.
void elog(const char *Fmt, Ts &&... Vals)
void(* RegisterCustomChecks)(const ClangTidyOptions &O, ClangTidyCheckFactories &Factories)
std::string configurationAsText(const ClangTidyOptions &Options)
Serializes configuration to a YAML-encoded string.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
FastCheckPolicy FastCheckFilter
static const Config & current()
Returns the Config of the current Context, or an empty configuration.
struct clang::clangd::Config::@343034053122374337352226322054223376344037116252::@107156241027253143221327255130274177352007274355 ClangTidy
Configures what clang-tidy checks to run and options to use with them.
bool ExperimentalCustomChecks
Extension point that allows modules to observe and modify an AST build.