内置的 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
,获取所使用的专业化,并获取 那里有加糖的类型?
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
本身 是 不规范;它所指的类型通常是规范的。 (我认为 唯一的例外是基础类型也是依赖的。) 还有其他方法可以做到这一点吗?
模板 ID(专业化名称)
foo<size_t>
内 表达式
foo<size_t>()
是
DeclRefExpr
谁
template_arguments()
请参阅 size_t
(这在 AST 树转储中并不明显; 我使用了我自己的工具 打印更多细节来调查这一点)和 它的
getDecl()
指向实例化(树转储确实显示 那个)。因此,给定一个使用
SubstTemplateTypeParmType
的实例化 一种可能很麻烦的方法:
DeclRefExpr
的
getDecl()
指向该实例。
size_t
,并报告是否为。