reapply "[clang-tidy] support query based custom check" (#159547)
reapply #131804 and #159289 Fixed cmake link issue. --------- Co-authored-by: DeNiCoN <denicon1234@gmail.com> Co-authored-by: Baranov Victor <bar.victor.2002@gmail.com>
This commit is contained in:
parent
2654b511fe
commit
584af2f89e
@ -5,6 +5,8 @@ include(GNUInstallDirs)
|
||||
|
||||
option(CLANG_TIDY_ENABLE_STATIC_ANALYZER
|
||||
"Include static analyzer checks in clang-tidy" ON)
|
||||
option(CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS
|
||||
"Enable query-based custom checks in clang-tidy" ON)
|
||||
|
||||
if(CLANG_INCLUDE_TESTS)
|
||||
umbrella_lit_testsuite_begin(check-clang-tools)
|
||||
|
||||
@ -58,6 +58,7 @@ add_subdirectory(bugprone)
|
||||
add_subdirectory(cert)
|
||||
add_subdirectory(concurrency)
|
||||
add_subdirectory(cppcoreguidelines)
|
||||
add_subdirectory(custom)
|
||||
add_subdirectory(darwin)
|
||||
add_subdirectory(fuchsia)
|
||||
add_subdirectory(google)
|
||||
@ -101,6 +102,10 @@ set(ALL_CLANG_TIDY_CHECKS
|
||||
clangTidyReadabilityModule
|
||||
clangTidyZirconModule
|
||||
)
|
||||
|
||||
if(CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS)
|
||||
list(APPEND ALL_CLANG_TIDY_CHECKS clangTidyCustomModule)
|
||||
endif()
|
||||
if(CLANG_TIDY_ENABLE_STATIC_ANALYZER)
|
||||
list(APPEND ALL_CLANG_TIDY_CHECKS clangTidyMPIModule)
|
||||
endif()
|
||||
|
||||
@ -53,6 +53,13 @@ LLVM_INSTANTIATE_REGISTRY(clang::tidy::ClangTidyModuleRegistry)
|
||||
|
||||
namespace clang::tidy {
|
||||
|
||||
#if CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS
|
||||
namespace custom {
|
||||
void (*RegisterCustomChecks)(const ClangTidyOptions &O,
|
||||
ClangTidyCheckFactories &Factories) = nullptr;
|
||||
} // namespace custom
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
#if CLANG_TIDY_ENABLE_STATIC_ANALYZER
|
||||
#define ANALYZER_CHECK_NAME_PREFIX "clang-analyzer-"
|
||||
@ -342,6 +349,10 @@ ClangTidyASTConsumerFactory::ClangTidyASTConsumerFactory(
|
||||
IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFS)
|
||||
: Context(Context), OverlayFS(std::move(OverlayFS)),
|
||||
CheckFactories(new ClangTidyCheckFactories) {
|
||||
#if CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS
|
||||
if (Context.canExperimentalCustomChecks() && custom::RegisterCustomChecks)
|
||||
custom::RegisterCustomChecks(Context.getOptions(), *CheckFactories);
|
||||
#endif
|
||||
for (ClangTidyModuleRegistry::entry E : ClangTidyModuleRegistry::entries()) {
|
||||
std::unique_ptr<ClangTidyModule> Module = E.instantiate();
|
||||
Module->addCheckFactories(*CheckFactories);
|
||||
@ -411,7 +422,10 @@ ClangTidyASTConsumerFactory::createASTConsumer(
|
||||
.getCurrentWorkingDirectory();
|
||||
if (WorkingDir)
|
||||
Context.setCurrentBuildDirectory(WorkingDir.get());
|
||||
|
||||
#if CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS
|
||||
if (Context.canExperimentalCustomChecks() && custom::RegisterCustomChecks)
|
||||
custom::RegisterCustomChecks(Context.getOptions(), *CheckFactories);
|
||||
#endif
|
||||
std::vector<std::unique_ptr<ClangTidyCheck>> Checks =
|
||||
CheckFactories->createChecksForLanguage(&Context);
|
||||
|
||||
@ -497,13 +511,13 @@ ClangTidyOptions::OptionMap ClangTidyASTConsumerFactory::getCheckOptions() {
|
||||
return Options;
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
getCheckNames(const ClangTidyOptions &Options,
|
||||
bool AllowEnablingAnalyzerAlphaCheckers) {
|
||||
std::vector<std::string> getCheckNames(const ClangTidyOptions &Options,
|
||||
bool AllowEnablingAnalyzerAlphaCheckers,
|
||||
bool ExperimentalCustomChecks) {
|
||||
clang::tidy::ClangTidyContext Context(
|
||||
std::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
|
||||
Options),
|
||||
AllowEnablingAnalyzerAlphaCheckers);
|
||||
AllowEnablingAnalyzerAlphaCheckers, false, ExperimentalCustomChecks);
|
||||
ClangTidyASTConsumerFactory Factory(Context);
|
||||
return Factory.getCheckNames();
|
||||
}
|
||||
@ -524,11 +538,12 @@ void filterCheckOptions(ClangTidyOptions &Options,
|
||||
|
||||
ClangTidyOptions::OptionMap
|
||||
getCheckOptions(const ClangTidyOptions &Options,
|
||||
bool AllowEnablingAnalyzerAlphaCheckers) {
|
||||
bool AllowEnablingAnalyzerAlphaCheckers,
|
||||
bool ExperimentalCustomChecks) {
|
||||
clang::tidy::ClangTidyContext Context(
|
||||
std::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(),
|
||||
Options),
|
||||
AllowEnablingAnalyzerAlphaCheckers);
|
||||
AllowEnablingAnalyzerAlphaCheckers, false, ExperimentalCustomChecks);
|
||||
ClangTidyDiagnosticConsumer DiagConsumer(Context);
|
||||
auto DiagOpts = std::make_unique<DiagnosticOptions>();
|
||||
DiagnosticsEngine DE(llvm::makeIntrusiveRefCnt<DiagnosticIDs>(), *DiagOpts,
|
||||
@ -665,15 +680,19 @@ void exportReplacements(const llvm::StringRef MainFilePath,
|
||||
YAML << TUD;
|
||||
}
|
||||
|
||||
ChecksAndOptions
|
||||
getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers) {
|
||||
ChecksAndOptions getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers,
|
||||
bool ExperimentalCustomChecks) {
|
||||
ChecksAndOptions Result;
|
||||
ClangTidyOptions Opts;
|
||||
Opts.Checks = "*";
|
||||
clang::tidy::ClangTidyContext Context(
|
||||
std::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(), Opts),
|
||||
AllowEnablingAnalyzerAlphaCheckers);
|
||||
AllowEnablingAnalyzerAlphaCheckers, false, ExperimentalCustomChecks);
|
||||
ClangTidyCheckFactories Factories;
|
||||
#if CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS
|
||||
if (ExperimentalCustomChecks && custom::RegisterCustomChecks)
|
||||
custom::RegisterCustomChecks(Context.getOptions(), Factories);
|
||||
#endif
|
||||
for (const ClangTidyModuleRegistry::entry &Module :
|
||||
ClangTidyModuleRegistry::entries()) {
|
||||
Module.instantiate()->addCheckFactories(Factories);
|
||||
|
||||
@ -56,15 +56,16 @@ private:
|
||||
/// Fills the list of check names that are enabled when the provided
|
||||
/// filters are applied.
|
||||
std::vector<std::string> getCheckNames(const ClangTidyOptions &Options,
|
||||
bool AllowEnablingAnalyzerAlphaCheckers);
|
||||
bool AllowEnablingAnalyzerAlphaCheckers,
|
||||
bool ExperimentalCustomChecks);
|
||||
|
||||
struct ChecksAndOptions {
|
||||
llvm::StringSet<> Checks;
|
||||
llvm::StringSet<> Options;
|
||||
};
|
||||
|
||||
ChecksAndOptions
|
||||
getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers = true);
|
||||
ChecksAndOptions getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers,
|
||||
bool ExperimentalCustomChecks);
|
||||
|
||||
/// Returns the effective check-specific options.
|
||||
///
|
||||
@ -74,7 +75,8 @@ getAllChecksAndOptions(bool AllowEnablingAnalyzerAlphaCheckers = true);
|
||||
/// Options.
|
||||
ClangTidyOptions::OptionMap
|
||||
getCheckOptions(const ClangTidyOptions &Options,
|
||||
bool AllowEnablingAnalyzerAlphaCheckers);
|
||||
bool AllowEnablingAnalyzerAlphaCheckers,
|
||||
bool ExperimentalCustomChecks);
|
||||
|
||||
/// Filters CheckOptions in \p Options to only include options specified in
|
||||
/// the \p EnabledChecks which is a sorted vector.
|
||||
@ -125,6 +127,10 @@ void exportReplacements(StringRef MainFilePath,
|
||||
const std::vector<ClangTidyError> &Errors,
|
||||
raw_ostream &OS);
|
||||
|
||||
namespace custom {
|
||||
extern void (*RegisterCustomChecks)(const ClangTidyOptions &O,
|
||||
ClangTidyCheckFactories &Factories);
|
||||
} // namespace custom
|
||||
} // end namespace tidy
|
||||
} // end namespace clang
|
||||
|
||||
|
||||
@ -160,11 +160,12 @@ ClangTidyError::ClangTidyError(StringRef CheckName,
|
||||
|
||||
ClangTidyContext::ClangTidyContext(
|
||||
std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
|
||||
bool AllowEnablingAnalyzerAlphaCheckers, bool EnableModuleHeadersParsing)
|
||||
bool AllowEnablingAnalyzerAlphaCheckers, bool EnableModuleHeadersParsing,
|
||||
bool ExperimentalCustomChecks)
|
||||
: OptionsProvider(std::move(OptionsProvider)),
|
||||
|
||||
AllowEnablingAnalyzerAlphaCheckers(AllowEnablingAnalyzerAlphaCheckers),
|
||||
EnableModuleHeadersParsing(EnableModuleHeadersParsing) {
|
||||
EnableModuleHeadersParsing(EnableModuleHeadersParsing),
|
||||
ExperimentalCustomChecks(ExperimentalCustomChecks) {
|
||||
// Before the first translation unit we can get errors related to command-line
|
||||
// parsing, use dummy string for the file name in this case.
|
||||
setCurrentFile("dummy");
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
#include "llvm/ADT/StringSet.h"
|
||||
#include "llvm/Support/Regex.h"
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
namespace clang {
|
||||
|
||||
@ -68,10 +69,13 @@ struct ClangTidyStats {
|
||||
/// \endcode
|
||||
class ClangTidyContext {
|
||||
public:
|
||||
ClangTidyContext(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider)
|
||||
: ClangTidyContext(std::move(OptionsProvider), false, false, false) {}
|
||||
/// Initializes \c ClangTidyContext instance.
|
||||
ClangTidyContext(std::unique_ptr<ClangTidyOptionsProvider> OptionsProvider,
|
||||
bool AllowEnablingAnalyzerAlphaCheckers = false,
|
||||
bool EnableModuleHeadersParsing = false);
|
||||
bool AllowEnablingAnalyzerAlphaCheckers,
|
||||
bool EnableModuleHeadersParsing,
|
||||
bool ExperimentalCustomChecks);
|
||||
/// Sets the DiagnosticsEngine that diag() will emit diagnostics to.
|
||||
// FIXME: this is required initialization, and should be a constructor param.
|
||||
// Fix the context -> diag engine -> consumer -> context initialization cycle.
|
||||
@ -210,6 +214,10 @@ public:
|
||||
return EnableModuleHeadersParsing;
|
||||
}
|
||||
|
||||
// whether experimental custom checks can be enabled.
|
||||
// enabled with `--experimental-custom-checks`
|
||||
bool canExperimentalCustomChecks() const { return ExperimentalCustomChecks; }
|
||||
|
||||
void setSelfContainedDiags(bool Value) { SelfContainedDiags = Value; }
|
||||
|
||||
bool areDiagsSelfContained() const { return SelfContainedDiags; }
|
||||
@ -258,6 +266,7 @@ private:
|
||||
|
||||
bool AllowEnablingAnalyzerAlphaCheckers;
|
||||
bool EnableModuleHeadersParsing;
|
||||
bool ExperimentalCustomChecks;
|
||||
|
||||
bool SelfContainedDiags = false;
|
||||
|
||||
|
||||
@ -54,6 +54,13 @@ extern volatile int CppCoreGuidelinesModuleAnchorSource;
|
||||
static int LLVM_ATTRIBUTE_UNUSED CppCoreGuidelinesModuleAnchorDestination =
|
||||
CppCoreGuidelinesModuleAnchorSource;
|
||||
|
||||
#if CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS
|
||||
// This anchor is used to force the linker to link the CustomModule.
|
||||
extern volatile int CustomModuleAnchorSource;
|
||||
static int LLVM_ATTRIBUTE_UNUSED CustomModuleAnchorDestination =
|
||||
CustomModuleAnchorSource;
|
||||
#endif
|
||||
|
||||
// This anchor is used to force the linker to link the DarwinModule.
|
||||
extern volatile int DarwinModuleAnchorSource;
|
||||
static int LLVM_ATTRIBUTE_UNUSED DarwinModuleAnchorDestination =
|
||||
|
||||
@ -62,6 +62,8 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
void eraseCheck(llvm::StringRef CheckName) { Factories.erase(CheckName); }
|
||||
|
||||
/// Create instances of checks that are enabled.
|
||||
std::vector<std::unique_ptr<ClangTidyCheck>>
|
||||
createChecks(ClangTidyContext *Context) const;
|
||||
|
||||
@ -8,8 +8,10 @@
|
||||
|
||||
#include "ClangTidyOptions.h"
|
||||
#include "ClangTidyModuleRegistry.h"
|
||||
#include "clang/Basic/DiagnosticIDs.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/ErrorOr.h"
|
||||
#include "llvm/Support/MemoryBufferRef.h"
|
||||
@ -129,6 +131,51 @@ void yamlize(IO &IO, ClangTidyOptions::OptionMap &Val, bool,
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct MultiLineString {
|
||||
std::string &S;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
template <> struct BlockScalarTraits<MultiLineString> {
|
||||
static void output(const MultiLineString &S, void *Ctxt, raw_ostream &OS) {
|
||||
OS << S.S;
|
||||
}
|
||||
static StringRef input(StringRef Str, void *Ctxt, MultiLineString &S) {
|
||||
S.S = Str;
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct ScalarEnumerationTraits<clang::DiagnosticIDs::Level> {
|
||||
static void enumeration(IO &IO, clang::DiagnosticIDs::Level &Level) {
|
||||
IO.enumCase(Level, "Warning", clang::DiagnosticIDs::Level::Warning);
|
||||
IO.enumCase(Level, "Note", clang::DiagnosticIDs::Level::Note);
|
||||
}
|
||||
};
|
||||
template <> struct SequenceElementTraits<ClangTidyOptions::CustomCheckDiag> {
|
||||
static const bool flow = false;
|
||||
};
|
||||
template <> struct MappingTraits<ClangTidyOptions::CustomCheckDiag> {
|
||||
static void mapping(IO &IO, ClangTidyOptions::CustomCheckDiag &D) {
|
||||
IO.mapRequired("BindName", D.BindName);
|
||||
MultiLineString MLS{D.Message};
|
||||
IO.mapRequired("Message", MLS);
|
||||
IO.mapOptional("Level", D.Level);
|
||||
}
|
||||
};
|
||||
template <> struct SequenceElementTraits<ClangTidyOptions::CustomCheckValue> {
|
||||
static const bool flow = false;
|
||||
};
|
||||
template <> struct MappingTraits<ClangTidyOptions::CustomCheckValue> {
|
||||
static void mapping(IO &IO, ClangTidyOptions::CustomCheckValue &V) {
|
||||
IO.mapRequired("Name", V.Name);
|
||||
MultiLineString MLS{V.Query};
|
||||
IO.mapRequired("Query", MLS);
|
||||
IO.mapRequired("Diagnostic", V.Diags);
|
||||
}
|
||||
};
|
||||
|
||||
struct ChecksVariant {
|
||||
std::optional<std::string> AsString;
|
||||
std::optional<std::vector<std::string>> AsVector;
|
||||
@ -184,6 +231,7 @@ template <> struct MappingTraits<ClangTidyOptions> {
|
||||
IO.mapOptional("InheritParentConfig", Options.InheritParentConfig);
|
||||
IO.mapOptional("UseColor", Options.UseColor);
|
||||
IO.mapOptional("SystemHeaders", Options.SystemHeaders);
|
||||
IO.mapOptional("CustomChecks", Options.CustomChecks);
|
||||
}
|
||||
};
|
||||
|
||||
@ -245,7 +293,8 @@ ClangTidyOptions &ClangTidyOptions::mergeWith(const ClangTidyOptions &Other,
|
||||
overrideValue(UseColor, Other.UseColor);
|
||||
mergeVectors(ExtraArgs, Other.ExtraArgs);
|
||||
mergeVectors(ExtraArgsBefore, Other.ExtraArgsBefore);
|
||||
|
||||
// FIXME: how to handle duplicate names check?
|
||||
mergeVectors(CustomChecks, Other.CustomChecks);
|
||||
for (const auto &KeyValue : Other.CheckOptions) {
|
||||
CheckOptions.insert_or_assign(
|
||||
KeyValue.getKey(),
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYOPTIONS_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CLANGTIDYOPTIONS_H
|
||||
|
||||
#include "clang/Basic/DiagnosticIDs.h"
|
||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
@ -129,6 +130,19 @@ struct ClangTidyOptions {
|
||||
/// Key-value mapping used to store check-specific options.
|
||||
OptionMap CheckOptions;
|
||||
|
||||
struct CustomCheckDiag {
|
||||
std::string BindName;
|
||||
std::string Message;
|
||||
std::optional<DiagnosticIDs::Level> Level;
|
||||
};
|
||||
struct CustomCheckValue {
|
||||
std::string Name;
|
||||
std::string Query;
|
||||
llvm::SmallVector<CustomCheckDiag> Diags;
|
||||
};
|
||||
using CustomCheckValueList = llvm::SmallVector<CustomCheckValue>;
|
||||
std::optional<CustomCheckValueList> CustomChecks;
|
||||
|
||||
using ArgList = std::vector<std::string>;
|
||||
|
||||
/// Add extra compilation arguments to the end of the list.
|
||||
|
||||
@ -6,5 +6,6 @@
|
||||
#define CLANG_TIDY_CONFIG_H
|
||||
|
||||
#cmakedefine01 CLANG_TIDY_ENABLE_STATIC_ANALYZER
|
||||
#cmakedefine01 CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS
|
||||
|
||||
#endif
|
||||
|
||||
38
clang-tools-extra/clang-tidy/custom/CMakeLists.txt
Normal file
38
clang-tools-extra/clang-tidy/custom/CMakeLists.txt
Normal file
@ -0,0 +1,38 @@
|
||||
if(CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS)
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
FrontendOpenMP
|
||||
support
|
||||
)
|
||||
|
||||
add_clang_library(clangTidyCustomModule STATIC
|
||||
CustomTidyModule.cpp
|
||||
QueryCheck.cpp
|
||||
|
||||
LINK_LIBS
|
||||
clangTidy
|
||||
clangTidyBugproneModule
|
||||
clangTidyMiscModule
|
||||
clangTidyModernizeModule
|
||||
clangTidyPerformanceModule
|
||||
clangTidyReadabilityModule
|
||||
clangTidyUtils
|
||||
|
||||
DEPENDS
|
||||
omp_gen
|
||||
ClangDriverOptions
|
||||
)
|
||||
|
||||
clang_target_link_libraries(clangTidyCustomModule
|
||||
PRIVATE
|
||||
clangAnalysis
|
||||
clangAST
|
||||
clangASTMatchers
|
||||
clangBasic
|
||||
clangDynamicASTMatchers
|
||||
clangFrontend
|
||||
clangLex
|
||||
clangQuery
|
||||
clangSerialization
|
||||
clangTooling
|
||||
)
|
||||
endif()
|
||||
59
clang-tools-extra/clang-tidy/custom/CustomTidyModule.cpp
Normal file
59
clang-tools-extra/clang-tidy/custom/CustomTidyModule.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
#include "../ClangTidy.h"
|
||||
#include "../ClangTidyModule.h"
|
||||
#include "../ClangTidyModuleRegistry.h"
|
||||
#include "../ClangTidyOptions.h"
|
||||
#include "QueryCheck.h"
|
||||
#include "llvm/ADT/SmallSet.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
namespace clang::tidy {
|
||||
namespace custom {
|
||||
|
||||
class CustomModule : public ClangTidyModule {
|
||||
public:
|
||||
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {}
|
||||
};
|
||||
|
||||
// We need to register the checks more flexibly than builtin modules. The checks
|
||||
// will changed dynamically when switching to different source file.
|
||||
extern void registerCustomChecks(const ClangTidyOptions &Options,
|
||||
ClangTidyCheckFactories &Factories) {
|
||||
static llvm::SmallSet<llvm::SmallString<32>, 8> CustomCheckNames{};
|
||||
if (!Options.CustomChecks.has_value() || Options.CustomChecks->empty())
|
||||
return;
|
||||
for (const llvm::SmallString<32> &Name : CustomCheckNames)
|
||||
Factories.eraseCheck(Name);
|
||||
for (const ClangTidyOptions::CustomCheckValue &V :
|
||||
Options.CustomChecks.value()) {
|
||||
llvm::SmallString<32> Name = llvm::StringRef{"custom-" + V.Name};
|
||||
Factories.registerCheckFactory(
|
||||
// add custom- prefix to avoid conflicts with builtin checks
|
||||
Name, [&V](llvm::StringRef Name, ClangTidyContext *Context) {
|
||||
return std::make_unique<custom::QueryCheck>(Name, V, Context);
|
||||
});
|
||||
CustomCheckNames.insert(std::move(Name));
|
||||
}
|
||||
}
|
||||
|
||||
struct CustomChecksRegisterInitializer {
|
||||
CustomChecksRegisterInitializer() noexcept {
|
||||
RegisterCustomChecks = &custom::registerCustomChecks;
|
||||
}
|
||||
};
|
||||
|
||||
static CustomChecksRegisterInitializer Init{};
|
||||
|
||||
} // namespace custom
|
||||
|
||||
// Register the CustomTidyModule using this statically initialized variable.
|
||||
static ClangTidyModuleRegistry::Add<custom::CustomModule>
|
||||
X("custom-module", "Adds custom query lint checks.");
|
||||
|
||||
// This anchor is used to force the linker to link in the generated object file
|
||||
// and thus register the AlteraModule.
|
||||
volatile int CustomModuleAnchorSource = 0; // NOLINT (misc-use-internal-linkage)
|
||||
|
||||
} // namespace clang::tidy
|
||||
146
clang-tools-extra/clang-tidy/custom/QueryCheck.cpp
Normal file
146
clang-tools-extra/clang-tidy/custom/QueryCheck.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
//===--- QueryCheck.cpp - clang-tidy --------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "QueryCheck.h"
|
||||
#include "../../clang-query/Query.h"
|
||||
#include "../../clang-query/QueryParser.h"
|
||||
#include "clang/ASTMatchers/ASTMatchers.h"
|
||||
#include "clang/ASTMatchers/Dynamic/VariantValue.h"
|
||||
#include "clang/Basic/DiagnosticIDs.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <string>
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
namespace clang::tidy::custom {
|
||||
|
||||
static void emitConfigurationDiag(ClangTidyContext *Context, StringRef Message,
|
||||
StringRef CheckName) {
|
||||
Context->configurationDiag("%0 in '%1'", DiagnosticIDs::Warning)
|
||||
<< Message << CheckName;
|
||||
}
|
||||
|
||||
static SmallVector<ast_matchers::dynamic::DynTypedMatcher>
|
||||
parseQuery(const ClangTidyOptions::CustomCheckValue &V,
|
||||
ClangTidyContext *Context) {
|
||||
SmallVector<ast_matchers::dynamic::DynTypedMatcher> Matchers{};
|
||||
clang::query::QuerySession QS({});
|
||||
llvm::StringRef QueryStringRef{V.Query};
|
||||
while (!QueryStringRef.empty()) {
|
||||
query::QueryRef Q = query::QueryParser::parse(QueryStringRef, QS);
|
||||
switch (Q->Kind) {
|
||||
case query::QK_Match: {
|
||||
const auto &MatchQuery = llvm::cast<query::MatchQuery>(*Q);
|
||||
Matchers.push_back(MatchQuery.Matcher);
|
||||
break;
|
||||
}
|
||||
case query::QK_Let: {
|
||||
const auto &LetQuery = llvm::cast<query::LetQuery>(*Q);
|
||||
LetQuery.run(llvm::errs(), QS);
|
||||
break;
|
||||
}
|
||||
case query::QK_NoOp: {
|
||||
const auto &NoOpQuery = llvm::cast<query::NoOpQuery>(*Q);
|
||||
NoOpQuery.run(llvm::errs(), QS);
|
||||
break;
|
||||
}
|
||||
case query::QK_Invalid: {
|
||||
const auto &InvalidQuery = llvm::cast<query::InvalidQuery>(*Q);
|
||||
emitConfigurationDiag(Context, InvalidQuery.ErrStr, V.Name);
|
||||
return {};
|
||||
}
|
||||
// FIXME: TODO
|
||||
case query::QK_File: {
|
||||
emitConfigurationDiag(Context, "unsupported query kind 'File'", V.Name);
|
||||
return {};
|
||||
}
|
||||
case query::QK_DisableOutputKind: {
|
||||
emitConfigurationDiag(
|
||||
Context, "unsupported query kind 'DisableOutputKind'", V.Name);
|
||||
return {};
|
||||
}
|
||||
case query::QK_EnableOutputKind: {
|
||||
emitConfigurationDiag(
|
||||
Context, "unsupported query kind 'EnableOutputKind'", V.Name);
|
||||
return {};
|
||||
}
|
||||
case query::QK_SetOutputKind: {
|
||||
emitConfigurationDiag(Context, "unsupported query kind 'SetOutputKind'",
|
||||
V.Name);
|
||||
return {};
|
||||
}
|
||||
case query::QK_SetTraversalKind: {
|
||||
emitConfigurationDiag(
|
||||
Context, "unsupported query kind 'SetTraversalKind'", V.Name);
|
||||
return {};
|
||||
}
|
||||
case query::QK_SetBool: {
|
||||
emitConfigurationDiag(Context, "unsupported query kind 'SetBool'",
|
||||
V.Name);
|
||||
return {};
|
||||
}
|
||||
case query::QK_Help: {
|
||||
emitConfigurationDiag(Context, "unsupported query kind 'Help'", V.Name);
|
||||
return {};
|
||||
}
|
||||
case query::QK_Quit: {
|
||||
emitConfigurationDiag(Context, "unsupported query kind 'Quit'", V.Name);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
QueryStringRef = Q->RemainingContent;
|
||||
}
|
||||
return Matchers;
|
||||
}
|
||||
|
||||
QueryCheck::QueryCheck(llvm::StringRef Name,
|
||||
const ClangTidyOptions::CustomCheckValue &V,
|
||||
ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {
|
||||
for (const ClangTidyOptions::CustomCheckDiag &D : V.Diags) {
|
||||
auto DiagnosticIdIt =
|
||||
Diags
|
||||
.try_emplace(D.Level.value_or(DiagnosticIDs::Warning),
|
||||
llvm::StringMap<llvm::SmallVector<std::string>>{})
|
||||
.first;
|
||||
auto DiagMessageIt =
|
||||
DiagnosticIdIt->getSecond()
|
||||
.try_emplace(D.BindName, llvm::SmallVector<std::string>{})
|
||||
.first;
|
||||
DiagMessageIt->second.emplace_back(D.Message);
|
||||
}
|
||||
Matchers = parseQuery(V, Context);
|
||||
}
|
||||
|
||||
void QueryCheck::registerMatchers(MatchFinder *Finder) {
|
||||
for (const ast_matchers::dynamic::DynTypedMatcher &M : Matchers)
|
||||
Finder->addDynamicMatcher(M, this);
|
||||
}
|
||||
|
||||
void QueryCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
auto Emit = [this](const DiagMaps &DiagMaps, const std::string &BindName,
|
||||
const DynTypedNode &Node, DiagnosticIDs::Level Level) {
|
||||
DiagMaps::const_iterator DiagMapIt = DiagMaps.find(Level);
|
||||
if (DiagMapIt == DiagMaps.end())
|
||||
return;
|
||||
const BindNameMapToDiagMessage &BindNameMap = DiagMapIt->second;
|
||||
BindNameMapToDiagMessage::const_iterator BindNameMapIt =
|
||||
BindNameMap.find(BindName);
|
||||
if (BindNameMapIt == BindNameMap.end())
|
||||
return;
|
||||
for (const std::string &Message : BindNameMapIt->second)
|
||||
diag(Node.getSourceRange().getBegin(), Message, Level);
|
||||
};
|
||||
for (const auto &[Name, Node] : Result.Nodes.getMap())
|
||||
Emit(Diags, Name, Node, DiagnosticIDs::Warning);
|
||||
// place Note last, otherwise it will not be emitted
|
||||
for (const auto &[Name, Node] : Result.Nodes.getMap())
|
||||
Emit(Diags, Name, Node, DiagnosticIDs::Note);
|
||||
}
|
||||
} // namespace clang::tidy::custom
|
||||
41
clang-tools-extra/clang-tidy/custom/QueryCheck.h
Normal file
41
clang-tools-extra/clang-tidy/custom/QueryCheck.h
Normal file
@ -0,0 +1,41 @@
|
||||
//===--- QueryCheck.h - clang-tidy ------------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CUSTOM_QUERYCHECK_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CUSTOM_QUERYCHECK_H
|
||||
|
||||
#include "../ClangTidyCheck.h"
|
||||
#include "clang/ASTMatchers/Dynamic/VariantValue.h"
|
||||
#include "clang/Basic/DiagnosticIDs.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
|
||||
namespace clang::tidy::custom {
|
||||
|
||||
/// Implement of Clang-Query based check.
|
||||
/// Not directly visible to users.
|
||||
class QueryCheck : public ClangTidyCheck {
|
||||
public:
|
||||
QueryCheck(llvm::StringRef Name, const ClangTidyOptions::CustomCheckValue &V,
|
||||
ClangTidyContext *Context);
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
|
||||
private:
|
||||
llvm::SmallVector<ast_matchers::dynamic::DynTypedMatcher> Matchers;
|
||||
using BindNameMapToDiagMessage =
|
||||
llvm::StringMap<llvm::SmallVector<std::string>>;
|
||||
using DiagMaps =
|
||||
llvm::DenseMap<DiagnosticIDs::Level, BindNameMapToDiagMessage>;
|
||||
DiagMaps Diags;
|
||||
};
|
||||
|
||||
} // namespace clang::tidy::custom
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CUSTOM_QUERYCHECK_H
|
||||
@ -60,6 +60,8 @@ Configuration files:
|
||||
Checks - Same as '--checks'. Additionally, the list of
|
||||
globs can be specified as a list instead of a
|
||||
string.
|
||||
CustomChecks - Array of user defined checks based on
|
||||
Clang-Query syntax.
|
||||
ExcludeHeaderFilterRegex - Same as '--exclude-header-filter'.
|
||||
ExtraArgs - Same as '--extra-arg'.
|
||||
ExtraArgsBefore - Same as '--extra-arg-before'.
|
||||
@ -344,6 +346,15 @@ all of the checks.
|
||||
)"),
|
||||
cl::init(false), cl::cat(ClangTidyCategory));
|
||||
|
||||
static cl::opt<bool> ExperimentalCustomChecks("experimental-custom-checks",
|
||||
desc(R"(
|
||||
Enable experimental clang-query based
|
||||
custom checks.
|
||||
see https://clang.llvm.org/extra/clang-tidy/QueryBasedCustomChecks.html.
|
||||
)"),
|
||||
cl::init(false),
|
||||
cl::cat(ClangTidyCategory));
|
||||
|
||||
namespace clang::tidy {
|
||||
|
||||
static void printStats(const ClangTidyStats &Stats) {
|
||||
@ -631,7 +642,8 @@ int clangTidyMain(int argc, const char **argv) {
|
||||
ClangTidyOptions EffectiveOptions = OptionsProvider->getOptions(FilePath);
|
||||
|
||||
std::vector<std::string> EnabledChecks =
|
||||
getCheckNames(EffectiveOptions, AllowEnablingAnalyzerAlphaCheckers);
|
||||
getCheckNames(EffectiveOptions, AllowEnablingAnalyzerAlphaCheckers,
|
||||
ExperimentalCustomChecks);
|
||||
|
||||
if (ExplainConfig) {
|
||||
// FIXME: Show other ClangTidyOptions' fields, like ExtraArg.
|
||||
@ -663,7 +675,8 @@ int clangTidyMain(int argc, const char **argv) {
|
||||
|
||||
if (DumpConfig) {
|
||||
EffectiveOptions.CheckOptions =
|
||||
getCheckOptions(EffectiveOptions, AllowEnablingAnalyzerAlphaCheckers);
|
||||
getCheckOptions(EffectiveOptions, AllowEnablingAnalyzerAlphaCheckers,
|
||||
ExperimentalCustomChecks);
|
||||
ClangTidyOptions OptionsToDump =
|
||||
ClangTidyOptions::getDefaults().merge(EffectiveOptions, 0);
|
||||
filterCheckOptions(OptionsToDump, EnabledChecks);
|
||||
@ -674,8 +687,8 @@ int clangTidyMain(int argc, const char **argv) {
|
||||
if (VerifyConfig) {
|
||||
std::vector<ClangTidyOptionsProvider::OptionsSource> RawOptions =
|
||||
OptionsProvider->getRawOptions(FileName);
|
||||
ChecksAndOptions Valid =
|
||||
getAllChecksAndOptions(AllowEnablingAnalyzerAlphaCheckers);
|
||||
ChecksAndOptions Valid = getAllChecksAndOptions(
|
||||
AllowEnablingAnalyzerAlphaCheckers, ExperimentalCustomChecks);
|
||||
bool AnyInvalid = false;
|
||||
for (const auto &[Opts, Source] : RawOptions) {
|
||||
if (Opts.Checks)
|
||||
@ -712,9 +725,9 @@ int clangTidyMain(int argc, const char **argv) {
|
||||
llvm::InitializeAllTargetMCs();
|
||||
llvm::InitializeAllAsmParsers();
|
||||
|
||||
ClangTidyContext Context(std::move(OwningOptionsProvider),
|
||||
AllowEnablingAnalyzerAlphaCheckers,
|
||||
EnableModuleHeadersParsing);
|
||||
ClangTidyContext Context(
|
||||
std::move(OwningOptionsProvider), AllowEnablingAnalyzerAlphaCheckers,
|
||||
EnableModuleHeadersParsing, ExperimentalCustomChecks);
|
||||
std::vector<ClangTidyError> Errors =
|
||||
runClangTidy(Context, OptionsParser->getCompilations(), PathList, BaseFS,
|
||||
FixNotes, EnableCheckProfile, ProfilePrefix, Quiet);
|
||||
|
||||
@ -127,6 +127,10 @@ Improvements to clang-tidy
|
||||
by default, greatly improving performance. This behavior is disabled if the
|
||||
`SystemHeaders` option is enabled.
|
||||
|
||||
- :program:`clang-tidy` now supports query based custom checks by `CustomChecks`
|
||||
configuration option.
|
||||
:doc:`Query Based Custom Check Document <clang-tidy/QueryBasedCustomChecks>`
|
||||
|
||||
- The :program:`run-clang-tidy.py` and :program:`clang-tidy-diff.py` scripts
|
||||
now run checks in parallel by default using all available hardware threads.
|
||||
Both scripts display the number of threads being used in their output.
|
||||
|
||||
@ -33,6 +33,9 @@ If CMake is configured with ``CLANG_TIDY_ENABLE_STATIC_ANALYZER=NO``,
|
||||
:program:`clang-tidy` will not be built with support for the
|
||||
``clang-analyzer-*`` checks or the ``mpi-*`` checks.
|
||||
|
||||
If CMake is configured with ``CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS=NO``,
|
||||
:program:`clang-tidy` will not be built with support for query based checks.
|
||||
|
||||
|
||||
.. _AST Matchers: https://clang.llvm.org/docs/LibASTMatchers.html
|
||||
.. _PPCallbacks: https://clang.llvm.org/doxygen/classclang_1_1PPCallbacks.html
|
||||
|
||||
82
clang-tools-extra/docs/clang-tidy/QueryBasedCustomChecks.rst
Normal file
82
clang-tools-extra/docs/clang-tidy/QueryBasedCustomChecks.rst
Normal file
@ -0,0 +1,82 @@
|
||||
====================================
|
||||
Query Based Custom Clang-Tidy Checks
|
||||
====================================
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
This page provides examples of how to add query based custom checks for
|
||||
:program:`clang-tidy`.
|
||||
|
||||
Custom checks are based on :program:`clang-query` syntax. Every custom checks
|
||||
will be registered in `custom` module to avoid name conflict. They can be
|
||||
enabled or disabled by the checks option like the built-in checks.
|
||||
|
||||
Custom checks support inheritance from parent configurations like other
|
||||
configuration items.
|
||||
|
||||
Goals: easy to write, cross platform, multiple versions supported toolkit for
|
||||
custom clang-tidy rules.
|
||||
Non-Goals: complex checks, performance, fix-its, etc.
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
`CustomChecks` is a list of custom checks. Each check must contain
|
||||
- Name: check name can be used in `-checks` option.
|
||||
- Query: `clang-query` string
|
||||
- Diagnostic: list of diagnostics to be reported.
|
||||
- BindName: name of the node to be bound in `Query`.
|
||||
- Message: message to be reported.
|
||||
- Level: severity of the diagnostic, the possible values are `Note`, `Warning`.
|
||||
|
||||
`CustomChecks` can be configured by `Checks` option in the configuration file.
|
||||
|
||||
Example
|
||||
=======
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Checks: -*,custom-call-main-function
|
||||
CustomChecks:
|
||||
- Name: call-main-function
|
||||
Query: |
|
||||
match callExpr(
|
||||
callee(
|
||||
functionDecl(isMain()).bind("fn")
|
||||
)
|
||||
).bind("callee")
|
||||
Diagnostic:
|
||||
- BindName: fn
|
||||
Message: main function.
|
||||
Level: Note
|
||||
- BindName: callee
|
||||
Message: call to main function.
|
||||
Level: Warning
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
int main(); // note: main function.
|
||||
|
||||
void bar() {
|
||||
main(); // warning: call to main function. [custom-call-main-function]
|
||||
}
|
||||
|
||||
Matters Need Attention
|
||||
======================
|
||||
|
||||
This feature needs to be explicitly enabled by `--experimental-custom-checks`
|
||||
because it is currently in the experimental stage. Welcome to submit any
|
||||
suggestions in the `link <https://discourse.llvm.org/t/support-query-based-clang-tidy-external-check/85331>`_.
|
||||
|
||||
During the experimental stage, the required configuration structure of this
|
||||
feature may be changed in the future. Future changes will be as
|
||||
forward-compatible as possible, but this is not a guarantee.
|
||||
|
||||
In subsequent versions, including non-experimental stage, the query statements
|
||||
will change at any time. The essence of :program:`clang-query` is to parse the
|
||||
query string and dynamically generate the corresponding AST matcher.
|
||||
Therefore, its functionality is entirely dependent on the functions provided by
|
||||
the AST matcher library.
|
||||
The ast matcher will change along with the changes in the clang AST.
|
||||
Please refer to `ast matcher reference <https://clang.llvm.org/docs/LibASTMatchersReference.html>`_.
|
||||
@ -10,6 +10,7 @@ See also:
|
||||
:maxdepth: 1
|
||||
|
||||
List of Clang-Tidy Checks <checks/list>
|
||||
Query Based Custom Clang-Tidy Checks <QueryBasedCustomChecks>
|
||||
Clang-tidy IDE/Editor Integrations <Integrations>
|
||||
Getting Involved <Contributing>
|
||||
External Clang-Tidy Examples <ExternalClang-TidyExamples>
|
||||
@ -304,6 +305,8 @@ An overview of all the command-line options:
|
||||
Checks - Same as '--checks'. Additionally, the list of
|
||||
globs can be specified as a list instead of a
|
||||
string.
|
||||
CustomChecks - List of user defined checks based on
|
||||
Clang-Query syntax.
|
||||
ExcludeHeaderFilterRegex - Same as '--exclude-header-filter'.
|
||||
ExtraArgs - Same as '--extra-arg'.
|
||||
ExtraArgsBefore - Same as '--extra-arg-before'.
|
||||
|
||||
@ -209,6 +209,7 @@ class CheckRunner:
|
||||
args = (
|
||||
[
|
||||
"clang-tidy",
|
||||
"--experimental-custom-checks",
|
||||
self.temp_file_name,
|
||||
]
|
||||
+ [
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
CustomChecks:
|
||||
- Name: test-diag-level
|
||||
Query: |
|
||||
match varDecl(
|
||||
hasType(asString("long")),
|
||||
hasTypeLoc(typeLoc().bind("long"))
|
||||
).bind("decl")
|
||||
Diagnostic:
|
||||
- BindName: long
|
||||
Message: use 'int' instead of 'long'
|
||||
Level: Warning
|
||||
- BindName: decl
|
||||
Message: declaration of 'long'
|
||||
Level: Note
|
||||
- Name: test-let-bind
|
||||
Query: |
|
||||
let expr varDecl(isStaticStorageClass()).bind("vd")
|
||||
match expr
|
||||
Diagnostic:
|
||||
- BindName: vd
|
||||
Message: find static variable
|
||||
Level: Warning
|
||||
@ -0,0 +1,25 @@
|
||||
CustomChecks:
|
||||
- Name: test-let-bind-invalid-1
|
||||
Query: |
|
||||
let expr varDecl(isStaticStorageClass()).bind("vd")
|
||||
match expr
|
||||
set output print
|
||||
Diagnostic:
|
||||
- BindName: vd
|
||||
Message: find static variable
|
||||
Level: Warning
|
||||
- Name: test-let-bind-invalid-2
|
||||
Query: |
|
||||
match varDeclInvalid(isStaticStorageClass()).bind("vd")
|
||||
Diagnostic:
|
||||
- BindName: vd
|
||||
Message: find static variable
|
||||
Level: Warning
|
||||
- Name: test-let-bind-valid
|
||||
Query: |
|
||||
let expr varDecl(isStaticStorageClass()).bind("vd")
|
||||
match expr
|
||||
Diagnostic:
|
||||
- BindName: vd
|
||||
Message: find static variable
|
||||
Level: Warning
|
||||
@ -0,0 +1,7 @@
|
||||
// RUN: %check_clang_tidy %s custom-* %t --config-file=%S/Inputs/incorrect-clang-tidy.yml
|
||||
|
||||
// CHECK-MESSAGES: warning: 1:1: Matcher not found: varDeclInvalid in 'test-let-bind-invalid-2' [clang-tidy-config]
|
||||
// CHECK-MESSAGES: warning: unsupported query kind 'SetOutputKind' in 'test-let-bind-invalid-1' [clang-tidy-config]
|
||||
|
||||
static int S;
|
||||
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: find static variable [custom-test-let-bind-valid]
|
||||
@ -0,0 +1,5 @@
|
||||
// RUN: %check_clang_tidy %s custom-test-let-bind %t --config-file=%S/Inputs/clang-tidy.yml
|
||||
|
||||
extern long E;
|
||||
static int S;
|
||||
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: find static variable [custom-test-let-bind]
|
||||
@ -0,0 +1,7 @@
|
||||
// RUN: %check_clang_tidy %s custom-* %t --config-file=%S/Inputs/clang-tidy.yml
|
||||
|
||||
extern long E;
|
||||
// CHECK-MESSAGES: [[@LINE-1]]:8: warning: use 'int' instead of 'long' [custom-test-diag-level]
|
||||
// CHECK-MESSAGES: [[@LINE-2]]:1: note: declaration of 'long'
|
||||
static int S;
|
||||
// CHECK-MESSAGES: [[@LINE-1]]:1: warning: find static variable [custom-test-let-bind]
|
||||
@ -0,0 +1,8 @@
|
||||
InheritParentConfig: true
|
||||
CustomChecks:
|
||||
- Name: function-decl
|
||||
Query: match functionDecl().bind("func")
|
||||
Diagnostic:
|
||||
- BindName: func
|
||||
Message: find function decl
|
||||
Level: Warning
|
||||
@ -0,0 +1 @@
|
||||
InheritParentConfig: false
|
||||
@ -0,0 +1,11 @@
|
||||
CustomChecks:
|
||||
- Name: avoid-long-type
|
||||
Query: |
|
||||
match varDecl(
|
||||
hasType(asString("long")),
|
||||
hasTypeLoc(typeLoc().bind("long"))
|
||||
)
|
||||
Diagnostic:
|
||||
- BindName: long
|
||||
Message: use 'int' instead of 'long' override
|
||||
Level: Warning
|
||||
@ -0,0 +1,11 @@
|
||||
CustomChecks:
|
||||
- Name: avoid-long-type
|
||||
Query: |
|
||||
match varDecl(
|
||||
hasType(asString("long")),
|
||||
hasTypeLoc(typeLoc().bind("long"))
|
||||
)
|
||||
Diagnostic:
|
||||
- BindName: long
|
||||
Message: use 'int' instead of 'long'
|
||||
Level: Warning
|
||||
@ -0,0 +1,44 @@
|
||||
version: 0
|
||||
roots:
|
||||
- name: OUT_DIR
|
||||
type: directory
|
||||
contents:
|
||||
- name: .clang-tidy
|
||||
type: file
|
||||
external-contents: INPUT_DIR/root-clang-tidy.yml
|
||||
- name: main.cpp
|
||||
type: file
|
||||
external-contents: MAIN_FILE
|
||||
- name: subdir
|
||||
type: directory
|
||||
contents:
|
||||
- name: main.cpp
|
||||
type: file
|
||||
external-contents: MAIN_FILE
|
||||
- name: subdir-override
|
||||
type: directory
|
||||
contents:
|
||||
- name: main.cpp
|
||||
type: file
|
||||
external-contents: MAIN_FILE
|
||||
- name: .clang-tidy
|
||||
type: file
|
||||
external-contents: INPUT_DIR/override-clang-tidy.yml
|
||||
- name: subdir-empty
|
||||
type: directory
|
||||
contents:
|
||||
- name: main.cpp
|
||||
type: file
|
||||
external-contents: MAIN_FILE
|
||||
- name: .clang-tidy
|
||||
type: file
|
||||
external-contents: INPUT_DIR/empty-clang-tidy.yml
|
||||
- name: subdir-append
|
||||
type: directory
|
||||
contents:
|
||||
- name: main.cpp
|
||||
type: file
|
||||
external-contents: MAIN_FILE
|
||||
- name: .clang-tidy
|
||||
type: file
|
||||
external-contents: INPUT_DIR/append-clang-tidy.yml
|
||||
@ -0,0 +1,14 @@
|
||||
// RUN: sed -e "s:INPUT_DIR:%S/Inputs/custom-query-check:g" -e "s:OUT_DIR:%t:g" -e "s:MAIN_FILE:%s:g" %S/Inputs/custom-query-check/vfsoverlay.yaml > %t.yaml
|
||||
// RUN: clang-tidy --allow-no-checks %t/main.cpp -checks='-*,custom-*' -vfsoverlay %t.yaml | FileCheck %s --check-prefix=CHECK
|
||||
// RUN: clang-tidy --allow-no-checks %t/main.cpp -checks='-*,custom-*' -vfsoverlay %t.yaml --list-checks | FileCheck %s --check-prefix=LIST-CHECK
|
||||
// REQUIRES: shell
|
||||
|
||||
|
||||
long V;
|
||||
// CHECK: No checks enabled.
|
||||
|
||||
void f();
|
||||
// CHECK-SUB-DIR-APPEND: [[@LINE-1]]:1: warning: find function decl [custom-function-decl]
|
||||
|
||||
// LIST-CHECK: Enabled checks:
|
||||
// LIST-CHECK-EMPTY:
|
||||
@ -0,0 +1,45 @@
|
||||
// RUN: sed -e "s:INPUT_DIR:%S/Inputs/custom-query-check:g" -e "s:OUT_DIR:%t:g" -e "s:MAIN_FILE:%s:g" %S/Inputs/custom-query-check/vfsoverlay.yaml > %t.yaml
|
||||
// RUN: clang-tidy --experimental-custom-checks %t/main.cpp -checks='-*,custom-*' -vfsoverlay %t.yaml | FileCheck %s --check-prefix=CHECK-SAME-DIR
|
||||
// RUN: clang-tidy --experimental-custom-checks %t/subdir/main.cpp -checks='-*,custom-*' -vfsoverlay %t.yaml | FileCheck %s --check-prefix=CHECK-SUB-DIR-BASE
|
||||
// RUN: clang-tidy --experimental-custom-checks %t/subdir-override/main.cpp -checks='-*,custom-*' -vfsoverlay %t.yaml | FileCheck %s --check-prefix=CHECK-SUB-DIR-OVERRIDE
|
||||
// RUN: clang-tidy --experimental-custom-checks %t/subdir-empty/main.cpp -checks='-*,custom-*' -vfsoverlay %t.yaml --allow-no-checks | FileCheck %s --check-prefix=CHECK-SUB-DIR-EMPTY
|
||||
// RUN: clang-tidy --experimental-custom-checks %t/subdir-append/main.cpp -checks='-*,custom-*' -vfsoverlay %t.yaml | FileCheck %s --check-prefix=CHECK-SUB-DIR-APPEND
|
||||
// RUN: clang-tidy --experimental-custom-checks %t/subdir-append/main.cpp -checks='-*,custom-*' -vfsoverlay %t.yaml --list-checks | FileCheck %s --check-prefix=LIST-CHECK
|
||||
// RUN: clang-tidy --experimental-custom-checks %t/subdir-append/main.cpp -checks='-*,custom-*' -vfsoverlay %t.yaml --dump-config | FileCheck %s --check-prefix=DUMP-CONFIG
|
||||
// REQUIRES: shell
|
||||
|
||||
|
||||
long V;
|
||||
// CHECK-SAME-DIR: [[@LINE-1]]:1: warning: use 'int' instead of 'long' [custom-avoid-long-type]
|
||||
// CHECK-SUB-DIR-BASE: [[@LINE-2]]:1: warning: use 'int' instead of 'long' [custom-avoid-long-type]
|
||||
// CHECK-SUB-DIR-OVERRIDE: [[@LINE-3]]:1: warning: use 'int' instead of 'long' override [custom-avoid-long-type]
|
||||
// CHECK-SUB-DIR-EMPTY: No checks enabled.
|
||||
// CHECK-SUB-DIR-APPEND: [[@LINE-5]]:1: warning: use 'int' instead of 'long' [custom-avoid-long-type]
|
||||
|
||||
void f();
|
||||
// CHECK-SUB-DIR-APPEND: [[@LINE-1]]:1: warning: find function decl [custom-function-decl]
|
||||
|
||||
// LIST-CHECK: Enabled checks:
|
||||
// LIST-CHECK: custom-avoid-long-type
|
||||
// LIST-CHECK: custom-function-decl
|
||||
|
||||
// DUMP-CONFIG: CustomChecks:
|
||||
// DUMP-CONFIG: - Name: avoid-long-type
|
||||
// DUMP-CONFIG: Query: |
|
||||
// DUMP-CONFIG: match varDecl(
|
||||
// DUMP-CONFIG: hasType(asString("long")),
|
||||
// DUMP-CONFIG: hasTypeLoc(typeLoc().bind("long"))
|
||||
// DUMP-CONFIG: )
|
||||
// DUMP-CONFIG: Diagnostic:
|
||||
// DUMP-CONFIG: - BindName: long
|
||||
// DUMP-CONFIG: Message: |
|
||||
// DUMP-CONFIG: use 'int' instead of 'long'
|
||||
// DUMP-CONFIG: Level: Warning
|
||||
// DUMP-CONFIG: - Name: function-decl
|
||||
// DUMP-CONFIG: Query: |
|
||||
// DUMP-CONFIG: match functionDecl().bind("func")
|
||||
// DUMP-CONFIG: Diagnostic:
|
||||
// DUMP-CONFIG: - BindName: func
|
||||
// DUMP-CONFIG: Message: |
|
||||
// DUMP-CONFIG: find function decl
|
||||
// DUMP-CONFIG: Level: Warning
|
||||
@ -54,6 +54,7 @@ target_link_libraries(ClangTidyTests
|
||||
PRIVATE
|
||||
clangTidy
|
||||
clangTidyAndroidModule
|
||||
clangTidyCustomModule
|
||||
clangTidyGoogleModule
|
||||
clangTidyMiscModule
|
||||
clangTidyLLVMModule
|
||||
|
||||
@ -94,7 +94,8 @@ runCheckOnCode(StringRef Code, std::vector<ClangTidyError> *Errors = nullptr,
|
||||
ClangTidyOptions Options = ExtraOptions;
|
||||
Options.Checks = "*";
|
||||
ClangTidyContext Context(std::make_unique<DefaultOptionsProvider>(
|
||||
ClangTidyGlobalOptions(), Options));
|
||||
ClangTidyGlobalOptions(), Options),
|
||||
false, false, false);
|
||||
ClangTidyDiagnosticConsumer DiagConsumer(Context);
|
||||
auto DiagOpts = std::make_unique<DiagnosticOptions>();
|
||||
DiagnosticsEngine DE(DiagnosticIDs::create(), *DiagOpts, &DiagConsumer,
|
||||
|
||||
@ -33,14 +33,17 @@ config_setting(
|
||||
expand_template(
|
||||
name = "config",
|
||||
out = "clang-tidy-config.h",
|
||||
substitutions = select({
|
||||
":static_analyzer_enabled": {
|
||||
"#cmakedefine01 CLANG_TIDY_ENABLE_STATIC_ANALYZER": "#define CLANG_TIDY_ENABLE_STATIC_ANALYZER 1",
|
||||
},
|
||||
"//conditions:default": {
|
||||
"#cmakedefine01 CLANG_TIDY_ENABLE_STATIC_ANALYZER": "#define CLANG_TIDY_ENABLE_STATIC_ANALYZER 0",
|
||||
},
|
||||
}),
|
||||
substitutions =
|
||||
{
|
||||
"#cmakedefine01 CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS": "#define CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKS 0",
|
||||
} | select({
|
||||
":static_analyzer_enabled": {
|
||||
"#cmakedefine01 CLANG_TIDY_ENABLE_STATIC_ANALYZER": "#define CLANG_TIDY_ENABLE_STATIC_ANALYZER 1",
|
||||
},
|
||||
"//conditions:default": {
|
||||
"#cmakedefine01 CLANG_TIDY_ENABLE_STATIC_ANALYZER": "#define CLANG_TIDY_ENABLE_STATIC_ANALYZER 0",
|
||||
},
|
||||
}),
|
||||
template = "clang-tidy-config.h.cmake",
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user