You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Clang Tidy中实现变量与typedef或其底层类型的匹配检查

实现Clang Tidy自定义检查:自动验证变量类型与匈牙利命名匹配

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:

  1. Resolve a variable's canonical type (stripping away typedefs/aliases to get the raw underlying type, e.g., uint16_t becomes unsigned short)
  2. Validate that the variable's name starts with the correct prefix defined by your team's rules (e.g., l for uint32_t/unsigned int, w for uint16_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 both uint16_t and its underlying type unsigned short as 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 FixItHint lines.
  • Rule Customization: The NamingRules map 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 in registerMatchers to varDecl(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:49:48