如何在Clang Tidy中实现变量与typedef或其底层类型的匹配检查
Got it, let's solve this problem properly. Since Clang Tidy doesn't ship with a ready-made rule for enforcing Hungarian naming tied to specific types (and their underlying typedefs), we'll build a custom checker using Clang's AST (Abstract Syntax Tree) analysis. This will fully automate the check, so you don't have to waste time manually verifying naming compliance.
Core Approach
Our checker needs to do two key things:
- Resolve a variable's canonical type (stripping away typedefs/aliases to get the raw underlying type, e.g.,
uint16_tbecomesunsigned short) - Validate that the variable's name starts with the correct prefix defined by your team's rules (e.g.,
lforuint32_t/unsigned int,wforuint16_t/unsigned short)
Step-by-Step Implementation
1. Set Up Your Environment
First, make sure you have these installed:
- Clang/LLVM development package (version 12+ recommended for stable APIs)
- CMake (to compile the custom checker)
2. Write the Custom Checker Code
Create a new C++ file (e.g., HungarianNamingCheck.cpp) with the following code. It inherits from Clang Tidy's base check class and implements the AST matching logic:
#include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Tidy/ClangTidy.h" #include "clang/Tidy/ClangTidyCheck.h" #include "clang/Tidy/ClangTidyModule.h" #include "clang/Tidy/ClangTidyModuleRegistry.h" using namespace clang; using namespace clang::ast_matchers; using namespace clang::tidy; class HungarianNamingCheck : public ClangTidyCheck { public: HungarianNamingCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} void registerMatchers(MatchFinder *Finder) override { // Match all variable declarations (adjust to exclude params/globals if needed) Finder->addMatcher(varDecl().bind("var"), this); } void check(const MatchFinder::MatchResult &Result) override { const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var"); if (!Var || Var->isInvalidDecl()) return; // Get the canonical type (removes typedefs/aliases to reveal underlying type) QualType CanonicalType = Var->getType().getCanonicalType(); std::string TypeStr = CanonicalType.getAsString(); std::string VarName = Var->getNameAsString(); // Define your team's naming rules: prefix -> list of acceptable types (typedef + underlying) const std::map<std::string, std::vector<std::string>> NamingRules = { {"l", {"uint32_t", "unsigned int"}}, {"w", {"uint16_t", "unsigned short"}}, {"b", {"uint8_t", "unsigned char"}}, {"d", {"double"}}, {"f", {"float"}}, // Add more rules here to match your team's conventions }; // Check if the variable's type matches any rule for (const auto &[Prefix, TargetTypes] : NamingRules) { if (std::find(TargetTypes.begin(), TargetTypes.end(), TypeStr) != TargetTypes.end()) { // Verify the variable starts with the correct prefix if (VarName.substr(0, Prefix.size()) != Prefix) { diag(Var->getLocation(), "Variable name violates Hungarian naming: %0 type requires prefix '%1'") << TypeStr << Prefix; // Optional: Auto-fix by adding the missing prefix auto Fix = FixItHint::CreateInsertion(Var->getLocation(), Prefix); diag(Var->getLocation(), "Auto-add prefix '%0'", DiagnosticIDs::Note) << Prefix << Fix; } return; // Exit once we find the matching rule } } // Optional: Warn about types without defined naming rules diag(Var->getLocation(), "No Hungarian naming rule defined for type %0", DiagnosticIDs::Note) << TypeStr; } }; // Register the checker as a Clang Tidy module class HungarianNamingModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck<HungarianNamingCheck>( "misc-hungarian-naming-check"); } }; static ClangTidyModuleRegistry::Add<HungarianNamingModule> X("hungarian-naming-module", "Adds Hungarian naming convention checker"); // Entry point for the plugin (if compiling as a standalone module) extern "C" LLVM_ATTRIBUTE_WEAK clang::tidy::ClangTidyModule * clangTidyModuleFactory() { return new HungarianNamingModule(); }
3. Compile the Checker
Create a CMakeLists.txt to build the checker as a Clang Tidy plugin:
cmake_minimum_required(VERSION 3.16) project(HungarianNamingCheck) find_package(LLVM REQUIRED CONFIG) find_package(Clang REQUIRED CONFIG) include_directories(${LLVM_INCLUDE_DIRS} ${CLANG_INCLUDE_DIRS}) add_definitions(${LLVM_DEFINITIONS}) add_library(HungarianNamingCheck MODULE HungarianNamingCheck.cpp) # Link against required Clang/LLVM libraries target_link_libraries(HungarianNamingCheck PRIVATE clangAST clangASTMatchers clangTidy clangTidyCore clangTooling ) # Ensure the plugin is output with the correct name and no prefix set_target_properties(HungarianNamingCheck PROPERTIES OUTPUT_NAME "HungarianNamingCheck" PREFIX "" )
Compile with these commands:
mkdir build && cd build cmake .. -DCMAKE_CXX_COMPILER=clang++ -DLLVM_DIR=/path/to/llvm/lib/cmake/llvm make
(Replace /path/to/llvm with the actual path to your LLVM installation.)
4. Use the Checker in Your Workflow
Add the checker to your .clang-tidy configuration file:
Checks: - misc-hungarian-naming-check # Optional: Add configuration options here if you extend the checker later
Run Clang Tidy with the custom plugin:
clang-tidy -p build path/to/your/source/file.cpp --load build/HungarianNamingCheck.so
Key Details to Note
- Canonical Type Handling: Using
getCanonicalType()ensures we recognize bothuint16_tand its underlying typeunsigned shortas the same, which is exactly what you need to cover typedefs and their raw types. - Auto-Fix: The code includes an auto-fix that adds the missing prefix to non-compliant variable names. You can enable/disable this by commenting out the
FixItHintlines. - Rule Customization: The
NamingRulesmap is easy to modify—just add new prefix-type pairs to match your team's specific conventions. - Scope Control: If you only want to check specific variables (like
my_var), update the matcher inregisterMatcherstovarDecl(hasName("my_var")).bind("var"), or extend the checker to accept a list of target variables via configuration.
This setup will fully automate your team's Hungarian naming checks, so you can stop wasting time manually enforcing the rule and focus on writing code.
内容的提问来源于stack exchange,提问作者Ryan B

