LLVM Clang类名格式检验插件

LLVM Clang编译器编写的Xcode进行代码类名格式检验插件

一.源码

#include <iostream>
#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
 
using namespace clang;
using namespace std;
using namespace llvm;
using namespace clang::ast_matchers;
 
namespace WSHPlugin {
    class MJHandler : public MatchFinder::MatchCallback {
    private:
        CompilerInstance &ci;
        
    public:
        MJHandler(CompilerInstance &ci) :ci(ci) {}
        
        void run(const MatchFinder::MatchResult &Result) {
            if (const ObjCInterfaceDecl *decl = Result.Nodes.getNodeAs<ObjCInterfaceDecl>("ObjCInterfaceDecl")) {
                size_t pos = decl->getName().find('_');
                if (pos != StringRef::npos) {
                    DiagnosticsEngine &D = ci.getDiagnostics();
                    SourceLocation loc = decl->getLocation().getLocWithOffset(pos);
                    D.Report(loc, D.getCustomDiagID(DiagnosticsEngine::Error, "温馨提示:类名中不能带有下划线"));
                }
            }
        }
    };
    
    class MJASTConsumer: public ASTConsumer {
    private:
        MatchFinder matcher;
        MJHandler handler;
        
    public:
        MJASTConsumer(CompilerInstance &ci) :handler(ci) {
            matcher.addMatcher(objcInterfaceDecl().bind("ObjCInterfaceDecl"), &handler);
        }
        
        void HandleTranslationUnit(ASTContext &context) {
            matcher.matchAST(context);
        }
    };
    
    class MJASTAction: public PluginASTAction {
    public:
        unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &ci, StringRef iFile) {
            return unique_ptr<MJASTConsumer> (new MJASTConsumer(ci));
        }
        
        bool ParseArgs(const CompilerInstance &ci, const vector<string> &args) {
            return true;
        }
    };
}
 
static FrontendPluginRegistry::Add<WSHPlugin::MJASTAction>
X("WSHPlugin", "The WSHPlugin is my first clang-plugin.");

二.源代码分析

因为,LLVM是C++开发环境,因此代码都是通过C++来编写的。
1.导入头文件
#include <iostream>
#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendPluginRegistry.h"

导入头文件Clang头文件,Clang是C,C++,Objective-C语言的前端开发编辑器。在Clang命令中我们可以看见程序变异中间有个缓解是通过生成树阶段,在程序编译生成树阶段,我们进行插件的书写。因此,导入除了iostream的C++头文件外,我们还需要导入AST生成树的头文件。
2.命名空间
using namespace clang;
using namespace std;
using namespace llvm;
using namespace clang::ast_matchers;

命名空间使用,是用来简化C++代码可以直接名命名空间内的成员和方法。
namespace WSHPlugin {...}
3.注册一个插件
static FrontendPluginRegistry::Add<WSHPlugin::MJASTAction>
X("WSHPlugin", "The WSHPlugin is my first clang-plugin.");

(1)设置一个static静态方法,说明当调用这段代码语句时候,内存只加载一次。FrontendPluginRegistry(Frontend Plugin Registry – 前端插件注册);
(2)调用FrontendPluginRegistry的Add方法,把WSHPlugin命名空间下的MJASAction放入参数–表示一个生成树的行为。传入的参数是“框架的名称”–"WSHPlugin"和“框架的描述” – “The WSHPlugin is my first clang-plugin.”;
(3)Add<…> 后面说明是函数模板,根据模板传入参数;

4.创建一个Handler类 – 管理者类
    class MJHandler : public MatchFinder::MatchCallback {
    private:
        CompilerInstance &ci;
        
    public:
        MJHandler(CompilerInstance &ci) :ci(ci) {}
        
        void run(const MatchFinder::MatchResult &Result) {
            if (const ObjCInterfaceDecl *decl = Result.Nodes.getNodeAs<ObjCInterfaceDecl>("ObjCInterfaceDecl")) {
                size_t pos = decl->getName().find('_');
                if (pos != StringRef::npos) {
                    DiagnosticsEngine &D = ci.getDiagnostics();
                    SourceLocation loc = decl->getLocation().getLocWithOffset(pos);
                    D.Report(loc, D.getCustomDiagID(DiagnosticsEngine::Error, "温馨提示:类名中不能带有下划线"));
                }
            }
        }
    };
(1)MJHandler继承MatchFinder下的MatchCallback回掉类;
(2)定义一个完成编译的实例对象ci;
(3)创建一个类的构造器,但是什么都不做;
(4)定义一个运行时候的方法,传入的参数是const类型(防止被修改,更加安全)MatchFinder::MatchResult的结果对象;
(5) 判断条件
if (const ObjCInterfaceDecl *decl = Result.Nodes.getNodeAs<ObjCInterfaceDecl>("ObjCInterfaceDecl")) {
    size_t pos = decl->getName().find('_');
    if (pos != StringRef::npos) {
        ...
    }
}
(6) Diagnostics – 诊断 && DiagnosticsEngine – 诊断引擎【通过完成编译实例的对象ci获得诊断信息】;
DiagnosticsEngine &D = ci.getDiagnostics();
(7) SourceLocation loc – 通过定位错误警告和诊断信息返回源码的位置,从而确定是呐一行代码出现了错误,并且记录下来他的错误信息;
SourceLocation loc = decl->getLocation().getLocWithOffset(pos);
(8) 通过把保存下来的位置信息和需要显示的温馨提示内容传给Report()函数,实现在对应位置上进行提示错误信息(DiagnosticsEngine::Error – 诊断引擎下的错误Error)【D.getCustomDiagID – 获得对话框的ID信息】;
D.Report(loc, D.getCustomDiagID(DiagnosticsEngine::Error, "温馨提示:类名中不能带有下划线"));

5.创建第二个类MJASTConsumer – 生成树的用户模式
 class MJASTConsumer: public ASTConsumer {
    private:
        MatchFinder matcher;
        MJHandler handler;
        
    public:
        MJASTConsumer(CompilerInstance &ci) :handler(ci) {
            matcher.addMatcher(objcInterfaceDecl().bind("ObjCInterfaceDecl"), &handler);
        }
        
        void HandleTranslationUnit(ASTContext &context) {
            matcher.matchAST(context);
        }
    };
(1)MJASTConsumer继承自ASTConsumer,通过MatchFinder和MJHandler创建两个成员;
(2)设计一个构造器;

6.创建第三个类MJASTAction – AST动作
class MJASTAction: public PluginASTAction {
    public:
        unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &ci, StringRef iFile) {
            return unique_ptr<MJASTConsumer> (new MJASTConsumer(ci));
        }
        
        bool ParseArgs(const CompilerInstance &ci, const vector<string> &args) {
            return true;
        }
    };

unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &ci, StringRef iFile) {
            return unique_ptr<MJASTConsumer> (new MJASTConsumer(ci));
        }
创建了一个AST用户ASTConsumer,并且返回该用户

 bool ParseArgs(const CompilerInstance &ci, const vector<string> &args) {
            return true;
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值