2401llvm,clang转换器

什么是ClangTransformer

ClangTransformer是一个来编写C++诊断和转换代码的框架.它在clang工具链和LibTooling库之上创建,旨在隐藏clang原生低级库的大部分复杂性.
Transformer的核心抽象是,指定了如何把给定的转换模式更改为新形式重写规则.以下是你可用Transformer完成的一些任务示例:
1,警告MkX声明的函数,
2,把MkX声明函数名更改为MakeX,
3,对s串,把s.size()更改为Size(s),
4,对任意式e和叫m的方法,把e.child().m()折叠为e.m().

示例都有个共同点:它们标识待转换目标的模式,指定模式标识代码的编辑,且模式和编辑引用如s,em区间超过代码片公共变量.

第一个和第二个示例还指定了仅从语法中并不明显的模式约束,如"s是个串".即使它没有更改代码,"编辑"只是个空操作,第一例("warn...")也共享该形式.

Transformer帮助用户简洁指定此类规则,并轻松地在文件集合上本地执行它们,把它们应用至代码基的选中部分,甚至按正在应用的干净检查绑定它们.

谁是ClangTransformer的对象

ClangTransformer适合想要编写clang-tidy检查或编写工具以(大致)相同方式,修改大量C++文件的开发者.

开始

Transformer中的模式是用clangAST匹配器表示的.匹配器是一个描述clang抽象语法树(AST)的各个部分的组合器语言.

因为clangAST包含完整的类型信息(在单个翻译单元(TU)的限制区间内),因此这些模式甚至可编码AST节点类型属性的丰富约束.

这里,假设熟悉clangAST和相应的AST匹配器.

示例:检查风格名

假设有个禁止把函数"MkX"风格指南规则,且想要编写抓违反此规则行为的检查.可表示为Transformer重写规则:

makeRule(functionDecl(hasName("MkX").bind("fun"), noopEdit(node("fun")),
  cat("函数禁止使用名''MkX'';请重命名"));

makeRule是用来生成重写规则首选函数.它需要三个参数:模式,编辑和(可选)解释说明.

示例中,(functionDecl(...))模式标识MkX函数的声明.因为只是在诊断问题,而不是建议修复,因此编辑无操作的.

但是,它包含诊断消息的重点:node("fun")表示用绑定到"fun"AST节点的源区间关联消息;

本例中,即错误命名的函数声明.最后,使用cat来构建一条解释更改的消息.

注意,makeRule的结果是一般不必关心的clang::transformer::RewriteRule类型的值.

示例:重命名函数

现在,扩展该示例到一个转换;即,上面的第二个示例:

makeRule(declRefExpr(to(functionDecl(hasName("MkX")))),
     changeTo(cat("MakeX")),
     cat("MkX has been renamed MakeX"));

此例中,(declRefExpr(...))模式,标识引用MkX函数,而不是声明自身,如上例所示.

编辑(changeTo(...))表示把匹配模式的代码更改"MakeX"文本.最后,再次用cat来构建一条解释更改的消息.

以下是此规则把的一些示例更改:

源语言 结果
X x=MkX(3); X x=MakeX(3);
CallFactory(MkX,3); CallFactory(MakeX,3);
auto f=MkX; auto f=MakeX;

示例:方法到函数

接着,编写一个把调用方法替换为(自由)函数调用的规则,应用至原始方法调用的目标对象.即,把s.size()更改为Size(s):

从忽略s类型的更简单更改开始.也即,修改叫"size"的调用方法:

llvm::StringRef s = "str";
makeRule(
  cxxMemberCallExpr(
    on(expr().bind(s)),
    callee(cxxMethodDecl(hasName("size")))),
  changeTo(cat("Size(", node(s), ")")),
   cat("已弃用''size''方法,可用`''Size''"`自由函数));

用绑定调用方法目标到s的给定的AST匹配器来表达该模式.对编辑,再次使用changeTo,但这次从用cat组成的多个部分构建项.

项的第二部分node(s),在AST中找到匹配规则模式的项时,选择与绑定的sAST节点相关的源码.

node(s)构造一个,在cat中使用时,指示应在该点的输出中插入所选源码RangeSelector.

现在,可能不想重写所有调用"size"方法,只重写调用std::string.只需细化匹配器即可实现此更改.其余规则不变:

llvm::StringRef s = "str";
makeRule(
  cxxMemberCallExpr(
    on(expr(hasType(namedDecl(hasName("std::string"))))
      .bind(s)),
    callee(cxxMethodDecl(hasName("size")))),
  changeTo(cat("Size(", node(s), ")")),
   cat("已弃用''size''方法,可用`''Size''"`自由函数));

示例:重写调用方法

此例中,删除在串调用中的"中间"调用方法.如,如果要折叠子结构到其父结构中,则可能会这样.

llvm::StringRef e = "expr", m = "member";
auto child_call = cxxMemberCallExpr(on(expr().bind(e)), callee(cxxMethodDecl(hasName("child"))));
makeRule(cxxMemberCallExpr(on(child_call), callee(memberExpr().bind(m)),
     changeTo(cat(e, ".", member(m), "()"))),
     cat("`child` accessor is being removed; call ", member(m), " directly on parent"));

这条规则并不是想要的:它会把my_object.child().foo()重写为my_object.foo(),但它也会把my_ptr->child().foo()重写为my_ptr.foo(),这不是期望意图.
可通过在child_call的定义中使用not(isArrow())来限制模式来解决该问题.

然而,想通过指针重写调用.
为此,提供了访问(access)组合器来智能构造字段/方法访问.示例中,成员权限表示为:

access(e, cat(member(m)))

第一个参数指定要访问的对象,第二个参数指定字段/方法名的描述.本例中,指定应从复制方法名,即,是m成员的源区间.为了构造调用方法,在cat中使用以下:

cat(access(e, cat(member(m))), "()")

引用:区间,模具,编辑,规则

上例仅演示了重写规则基础.提到的每个元素都有更多可用的构造器:区间选择器,模板,编辑和规则.这里,依次简要回顾每个内容,并引用源头文件以取最新信息.

不过,首先,澄清哪些重写规则是真正的重写.

重写AST为文本

在解释重写规则内容时,提到了"代码",但代码既可表示为原始源文本,也可表示为抽象语法树.则,是哪一个.

最好,可重写输入AST为新的AST,但clangAST不太适合该转换.因此,妥协了:用AST项来表达的模式及绑定的名字,但用源码文本来表达变化.

设计了Transformer的语言,来沟通这两个表示,以尽量减少用户对源码位置和其他低级语法细节理解的需要.

区间选择器

Transformer提供了一个描述源区间的小型API:RangeSelector组合器.这些区间经常用来指定受编辑影响的源码,及在构造新文本时,来提取源码.

大致上,有两个区间组合器:一个根据AST选择源区间,另一个按新区间合并现有区间.

如,node选择特定AST节点跨越的源区间,而after选择紧跟参数区间之后的(空)区间.

因此,after(node("id"))是紧跟在绑定到idAST节点之后的空区间.

RangeSelectors完整集合,见头文件clang/Tooling/Transformer/RangeSelector.h这里

模具

Transformer提供了大量且不断增长运算器来构建输出.上面,演示了构建模板核心功能的cat.它需要一系列参数,有三种:

1,直接复制到输出中的原始文本.
2,Selector:用指示待复制到输出源文本区间RangeSelector指定.
3,Builder:从参数构造代码片的操作.如,上面看到的访问(access)函数.

(一般)由模具表示这些不同类型的数据.cat直接按参数取textRangeSelectors,而不是要求使用构建器构造它们;其他构建器是显式构造的.

一般,模具会根据匹配结果生成文本.因此,不仅限于生成源码,与在重写调用方法示例中一样,还可用来生成引用,匹配代码(命名)元素的诊断消息.

Stencil类型的更多细节,见clang/Tooling/Transformer/Stencil.h头文件.

编辑

Transformer支持其他形式的编辑.首先,在changeTo中,可用前面看到的相同RangeSelector指定要替换代码特定部分.

如,可如下更改函数声明中的函数名:

makeRule(functionDecl(hasName("bad")).bind(f),
     changeTo(name(f), cat("good")),
     cat("bad is now good"));

还为插入和删除提供了更简单编辑原语:insertBefore,insertAfterremove.
可在clang/Tooling/Transformer/RewriteRule.h头文件中找到.

不限制每个找到的匹配一次编辑.有时,需要多次编辑每场比赛.如,假设想要交换函数调用的两个参数.

为此,提供了一个接受编辑列表,而不仅是一个编辑makeRule重载.示例如下:

makeRule(callExpr(...),
{
   changeTo(node(arg0), cat(node(arg2))),
 changeTo(node(arg2), cat(node(arg0)))},
cat("swap the first and third arguments of the call"));

EditGenerators(高级)

目前,看到的特定编辑都是ASTEdit类或此类列表的实例.但是,并非可按ASTEdits表示所有编辑.因此,还支持编辑生成器的非常通用的签名:

using EditGenerator = MatchConsumer<llvm::SmallVector<Edit, 1>>;

也即,EditGenerator是映射MatchResult到一组编辑或失败的函数.此签名支持匹配结果非常通用的计算形式.

Transformer提供了许多处理EditGenerators的函数,最明显的是变平EditGenerators,如列表变平.完整列表,见clang/Tooling/Transformer/RewriteRule.h头文件.

规则

还可用applyFirst编写多个规则,而不仅是在规则中编辑:它按有序选择组合规则列表,而Transformer应用模式匹配第一个规则,忽略后面列表中的其他规则.

如果匹配器独立的,则顺序不重要.此时,applyFirst只是组合规则组一个.

applyFirst的好处是,对某些问题,因为这些模式不需要显式排除列表的早期模式,它允许用户更简洁地表述列表中的后续规则.

如,考虑一组重写复合语句的规则,其中一个规则处理空复合语句的大小写,另一个规则处理非空复合语句大小写.

使用applyFirst,这些规则可紧凑地表示为:

applyFirst({
   
  makeRule(compoundStmt(statementCountIs(0)).bind("empty"), ...),
  makeRule(compoundStmt().bind("non-empty"),...)
})

第二条规则不需要显式指定复合语句为非空语句,它遵循applyFirst中的规则位置.对更复杂的示例,会大大提高代码可读性.

有时,修改代码,可能需要包含特定头文件.为此,用户可使用addInclude修改规则,来指定include指令.

文档,见clang/Tooling/Transformer/RewriteRule.h头文件.

clang-tidy检查使用RewriteRule

Transformer支持按clang-tidy这里来执行重写规则,类为clang::tidy::utils::TransformerClangTidyCheck.
在定义中按需要最少的代码来设计.如,给定MyCheckAsRewriteRule规则,可如下定义干净检查:

class MyCheck : public TransformerClangTidyCheck {
   
 public:
  MyCheck(StringRef Name, ClangTidyContext *Context)
      : TransformerClangTidyCheck(MyCheckAsRewriteRule, Name, Context) {
   }
};

TransformerClangTidyCheck根据你的规则规范,实现虚registerMatcherscheck方法,因此无需自己实现.

如果要根据语言选项和/或clang-tidy配置来配置规则,则可按取这些为参数并(可选)返回RewriteRule的函数来表示.

如,对由原始名目标参数化的重命名方法规则很有用.细节,见clang-tools-extra/clang-tidy/utils/TransformerClangTidyCheck.h,这里

参考

了解clangAST及其匹配器的一个好地方是clang网站上的介绍:
1,ClangAST简介.
2,匹配ClangAST.
3,AST匹配器参考.

相关推荐

  1. 2401llvm,clang转换器

    2024-01-26 12:04:02       50 阅读
  2. 2401vim,vim标号

    2024-01-26 12:04:02       43 阅读
  3. HDOJ 2041

    2024-01-26 12:04:02       46 阅读
  4. 2401编辑器,好插件

    2024-01-26 12:04:02       71 阅读
  5. 2401C++,C++自动注册

    2024-01-26 12:04:02       62 阅读
  6. 2401llvm,clang的libtooling

    2024-01-26 12:04:02       67 阅读
  7. 2401llvm,clang插件

    2024-01-26 12:04:02       57 阅读
  8. 2401cmake,学习cmake1

    2024-01-26 12:04:02       60 阅读
  9. 2401cmake,学习cmake4

    2024-01-26 12:04:02       53 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-01-26 12:04:02       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-01-26 12:04:02       106 阅读
  3. 在Django里面运行非项目文件

    2024-01-26 12:04:02       87 阅读
  4. Python语言-面向对象

    2024-01-26 12:04:02       96 阅读

热门阅读

  1. 2401llvm,clang插件

    2024-01-26 12:04:02       57 阅读
  2. conda常用命令总结

    2024-01-26 12:04:02       60 阅读
  3. ·策略模式

    2024-01-26 12:04:02       53 阅读
  4. conda使用,pip使用

    2024-01-26 12:04:02       58 阅读
  5. 解决linux下wps缺失字体的问题

    2024-01-26 12:04:02       62 阅读
  6. 低代码开发助力业务效能高速提升

    2024-01-26 12:04:02       61 阅读
  7. C++(2) 结构体和动态数组的实现

    2024-01-26 12:04:02       46 阅读
  8. ubuntu运行指定的py环境

    2024-01-26 12:04:02       53 阅读
  9. 04 约数

    04 约数

    2024-01-26 12:04:02      48 阅读
  10. kotlin sum 与 sumOf

    2024-01-26 12:04:02       60 阅读
  11. Android.bp 语

    2024-01-26 12:04:02       59 阅读
  12. kotlin中的初始化问题纪录

    2024-01-26 12:04:02       52 阅读
  13. 大厂程序员成长路径

    2024-01-26 12:04:02       58 阅读