在 varDecl() 中匹配模板参数的加糖 QualType

问题描述 投票:0回答:1

背景

内置的 C++ 类型没有定义大小(只有最小大小),所以我们 要求所有整数类型使用显式定义的 typedef,因此大小为 所有平台、编译器和 CPU 架构都相同。 孤独者 例外是

size_t
,考虑到它是常见的返回类型 类似
std::vector::size()
的操作。

因此,我们正在寻找通过 clang-tidy 检测何时使用

size_t
的方法 在 x86 架构下的 MSVC 上导致“缩小转换”警告。

问题

给定一个这样的函数:

template <typename T>
void foo()
{
  int64_t i{0};
  T v = i;
}

我试图区分

varDecl
v
何时是通过调用创建的
foo<size_t>()
,而不是没有问题的
foo<uint64_t>()

编译器资源管理器中上述内容的 AST 转储:

|-FunctionTemplateDecl <line:4:1, line:9:1> line:5:6 foo | |-TemplateTypeParmDecl <line:4:11, col:20> col:20 referenced typename depth 0 index 0 T | |-FunctionDecl <line:5:1, line:9:1> line:5:6 foo 'void ()' | | `-CompoundStmt <line:6:1, line:9:1> | | |-DeclStmt <line:7:3, col:16> | | | `-VarDecl <col:3, col:15> col:12 referenced v 'uint64_t':'unsigned long' listinit | | | `-InitListExpr <col:13, col:15> 'uint64_t':'unsigned long' | | | `-ImplicitCastExpr <col:14> 'uint64_t':'unsigned long' <IntegralCast> | | | `-IntegerLiteral <col:14> 'int' 3 | | `-DeclStmt <line:8:3, col:12> | | `-VarDecl <col:3, col:11> col:5 var 'T' cinit | | `-DeclRefExpr <col:11> 'uint64_t':'unsigned long' lvalue Var 0xcf7fc88 'v' 'uint64_t':'unsigned long' | `-FunctionDecl <line:5:1, line:9:1> line:5:6 used foo 'void ()' implicit_instantiation | |-TemplateArgument type 'unsigned long' | | `-BuiltinType 'unsigned long' | `-CompoundStmt <line:6:1, line:9:1> | |-DeclStmt <line:7:3, col:16> | | `-VarDecl <col:3, col:15> col:12 used v 'uint64_t':'unsigned long' listinit | | `-InitListExpr <col:13, col:15> 'uint64_t':'unsigned long' | | `-ImplicitCastExpr <col:14> 'uint64_t':'unsigned long' <IntegralCast> | | `-IntegerLiteral <col:14> 'int' 3 | `-DeclStmt <line:8:3, col:12> | `-VarDecl <col:3, col:11> col:5 var 'unsigned long' cinit | `-ImplicitCastExpr <col:11> 'uint64_t':'unsigned long' <LValueToRValue> | `-DeclRefExpr <col:11> 'uint64_t':'unsigned long' lvalue Var 0xcf8a3a8 'v' 'uint64_t':'unsigned long'
我尝试过的事情

我可以看到下面有一个函数模板的实例化

FunctionTemplateDecl

节点,它看起来像
substTemplateTypeParmType()
hasReplacementType()
 是获得该类型的有希望的组合
varDecl
,就像这样:

m varDecl( hasType(substTemplateTypeParmType( hasReplacementType(qualType()) )) )
...但是,此时替换类型已经“脱糖”了,并且
丢失 

size_t

 声明名称。  尽管文档确实如此
SubstTemplateTypeParmType
 状态:

它们仅用于记录类型最初是作为模板类型参数编写的;因此它们从来都不是规范的。

...该类具体有

isSugared()

desugar()
 方法,其中
让我相信这种类型应该是加糖的
size_t
 typedef。

(也许我错误地假设完全脱糖的类型是“规范”类型?)

我在问什么

我的假设是否正确,

SubstTemplateTypeParmType

应该返回
还是加糖的类型?

有没有办法从

varDecl

 遍历,回到
FunctionTemplateDecl
,获取所使用的专业化,并获取
那里有加糖的类型?

c++ clang clang-tidy clang-ast-matchers
1个回答
0
投票
模板专业化使用规范类型

据我了解,你的目标是区分专业化

foo<uint64_t>

 来自专业化 
foo<size_t>
,当目标
平台使用 
unsigned long
 来表示 
uint64_t
size_t

这不可能以直接的方式实现,因为两者都是名称 对于同一实体;例如,它们在以下位置具有相同的地址: 记忆。 与大多数(全部?)C++ 编译器一样,Clang 的表示形式是 模板特化使用引用规范的模板参数 类型,其中规范类型是其选择的代表 语义等价类(这里是内置类型

unsigned long

)。

问题中的 AST 转储准确地描述了这一点:

| `-VarDecl <col:3, col:11> col:5 var 'unsigned long' cinit ^^^^^^^^^^^^^
在实例化体内,

var

的类型只是(a
SubstTemplateTypeParmType
指的是)
unsigned long
。  事实
专业化参数最初拼写为 
size_t
 已经消失
当实例化机器开始运行时。  最后,
AST 匹配器无法检测到缺失的信息。

(注意:问题中的代码与问题中的代码略有不同 用于创建 AST 转储。 在问题代码中,该变量是 称为

v

,而创建转储时则称为 
var
。)

但是

SubstTemplateTypeParmType
不规范?

没错,但这只是意味着

SubstTemplateTypeParmType

 
本身 是 不规范;它所指的类型通常是规范的。 (我认为 唯一的例外是基础类型也是依赖的。)

还有其他方法可以做到这一点吗?

是的,但可能不适用于 AST 匹配器。

模板 ID(专业化名称)

foo<size_t>

 内
表达式 
foo<size_t>()
DeclRefExpr
template_arguments()

请参阅 
size_t
 (这在 AST 树转储中并不明显;
我使用了
我自己的工具 打印更多细节来调查这一点)和 它的 getDecl()
 指向实例化(树转储确实显示
那个)。

因此,给定一个使用

SubstTemplateTypeParmType

 的实例化
一种可能很麻烦的方法:

  • 搜索翻译单元中所有

    DeclRefExpr

    getDecl()
    指向该实例。

  • 对于每一个,检查其模板参数以获取带有 正确的索引。

  • 检查该参数是否为

    size_t

    ,并报告是否为。

使用 Clang 可以相当简单地完成此过程 C++ API。 我怀疑单独使用 AST 匹配器无法完成,但我 认为这还取决于您如何识别潜在的 麻烦的类型用法。

© www.soinside.com 2019 - 2024. All rights reserved.