ClangChecker扩展

ClangCheckerExpand

  1. 在已有的检查器包——alpha.core中添加自定义的检查器,那么在Checkers.td文件中添加如下内容:

    def MainCallChecker : Checker<"MainCall">,
      HelpText<"Check for calls to main">,
      Documentation<NotDocumented>;
    

    修改后的内容如下:

    let ParentPackage = CoreAlpha in {
    
    // 省略 ...
    
    def MainCallChecker : Checker<"MainCall">,
      HelpText<"Check for calls to main">,
      Documentation<NotDocumented>;
    
    } // end "alpha.core"
    

    注:

    • Checkers.td文件位于clang/include/clang/StaticAnalyzer/Checkers/目录中。
    • clang -cc1 -analyzer-checker-helpclang -cc1 -analyzer-checker-help-alpha等命令所显示的检查器列表来源于Checkers.td文件。
    • def MainCallChecker,表示检查器的注册名称为MainCallChecker
    • "MainCall",表示检查器的名称为MainCall。也就是说,可以通过-analyzer-checker=alpha.core.MainCall标志来启用该检查器。
    • HelpText选项,用于指定该检查器对应的描述。从而,在执行类似于-help命令时显示。
    • Documentation选项,用于指定检查器文档的URI地址。

    需要注意的是, 修改的Checkers.td文件在重新编译安装 Clang 后才生效。

  2. 添加检查器的实现代码

clang/lib/StaticAnalyzer/Checkers/目录中新建MainCallChecker.cpp文件,内容如下:

#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"

using namespace clang;
using namespace ento;

namespace {

class MainCallChecker : public Checker<check::PreCall> {
public:
  void checkPreCall(const CallEvent &Call, CheckerContext &Ctx) const;

private:
  mutable std::unique_ptr<BugType> BT;
};

} // anonymous namespace

void MainCallChecker::checkPreCall(const CallEvent &Call,
                                   CheckerContext &Ctx) const {
  if (const IdentifierInfo *II = Call.getCalleeIdentifier()) {
    if (II->isStr("main")) {
      if (!BT) {
        BT.reset(new BugType(this, "Call to main", "Example checker"));
      }
      ExplodedNode *Node = Ctx.generateErrorNode();
      auto Report
        = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), Node);
      Ctx.emitReport(std::move(Report));
    }
  }
}

void ento::registerMainCallChecker(CheckerManager &Mgr) {
  Mgr.registerChecker<MainCallChecker>();
}

bool ento::shouldRegisterMainCallChecker(const CheckerManager &mgr) {
  return true;
}

注:

  • 新建的源文件名称不必是MainCallChecker.cpp,也可以是其他名称。
  • 检查器的类名不必与在Checkers.td文件中定义的检查器的注册名称保持一致。
  • void ento::registerXXX(CheckerManager &Mgr)中的XXX必须与在Checkers.td文件中定义的检查器的注册名称保持一致。
  • Mgr.registerChecker();中的XXX必须与检查器的类名保持一致。
  • ento::shouldRegisterXXX(const CheckerManager &mgr)中的XXX必须与在Checkers.td文件中定义的检查器的注册名称保持一致。
  1. 修改 CMakeLists.txt

    clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt文件中添加如下内容:

    MainCallChecker.cpp
    

    注:MainCallChecker.cpp即上文中新建的用于存放检查器实现代码的源文件。

    修改后的内容如下:

    add_clang_library( # 省略 … MainCallChecker.cpp # 省略 …)

  2. 编译安装 Clang

    省略

  3. 查看自定义的检查器

    clang -cc1 -analyzer-checker-help-alpha | grep MainCall  alpha.core.MainCall      (Enable only for development!) Check for calls to main
    

    从上面的输出可以看出:

    • alpha.core.MainCall,即我们自定义的检查器的完整名称。
    • Check for calls to main,即我们在Checkers.td文件中为自定义的检查器所添加的描述。
  4. 编写测试用例 Example_Test.c

    typedef int (*main_t)(int, char **);
    int main(int argc, char **argv) {
      main_t foo = main;
      int exit_code = foo(argc, argv);   // actually calls main()!
      return exit_code;
    }
    

    上述测试程序存在这样的错误:通过函数指针变量foo调用main函数。

  5. 运行自定义的检查器

    clang --analyze -Xanalyzer -analyzer-checker=alpha.core.MainCall Example_Test.c
    

    output

    Example_Test.c:4:19: warning: Call to main [alpha.core.MainCall]
      int exit_code = foo(argc, argv);   // actually calls main()!
                      ^~~~~~~~~~~~~~~
    1 warning generated.
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值