为什么 Clang 的解析器会为同一类型生成不同的 Type 对象?

Why are distinct Type objects being generated by Clang's parser for the same type?

我有以下代码片段,我正在使用 Clang 的 API 解析我正在编写的工具:

namespace NS
{
    struct X
    {
    };
}

struct Y
{
    NS::X foo();
};

I use a (type that derives from) RecursiveASTVisitor to visit the AST。当 VisitCXXRecordDecl() 函数被调用时,我可以获得一个指向声明的 Type 对象的指针:

bool VisitCXXRecordDecl(CXXRecordDecl* const decl)
{
    auto declared_type = decl->getTypeForDecl();
    // ...
}

类似地,当 VisitCXXMethodDecl() 函数被调用时,我可以获得一个指向函数的 return Type 的指针,如下所示:

bool VisitCXXMethodDecl(CXXMethodDecl* const func) 
{
    auto return_type = func->getReturnType().getTypePtr();
    // ...
}

令我惊讶的是,上面两个函数中的变量declared_typereturn_type并没有指向同一个Type对象。

现在我知道 规范类型 ,事实上,如果我在 VisitCXXMethodDecl() 中编写以下内容,我将获得指向同一个 Type 对象的指针declared_type 指向:

auto canonical_type = return_type->getCanonicalTypeInternal().getTypePtr();
// Now `canonical_type` holds a pointer to the same Type object as the
// `declared_type` variable inside VisitCXXRecordDecl().

但是,我认为一个类型只有在涉及类型别名时才具有不同于自身的规范类型(至少这是我从 the Doxygen docs and the CFE Internals Manual 中收集到的)。我不确定这里创建两个 Type 对象的原因是什么,这让我相信我不了解 Type 对象和规范类型在 Clang 设计中的作用。

这是因为 clang 如何解释函数的 return 类型

NS::X foo();
^^^^^

According to the documentation,一个ElaboratedType

represents a type that was referred to using an elaborated type keyword, e.g., struct S, or via a qualified name, e.g., N::M::type, or both.

这意味着 declared_type 指向一个 RecordType 对象,但 return_type 实际上指向一个 ElaboratedType.

如果您已将此源代码传递给访问者:

namespace NS
{
    struct X
    {
    };
}

using namespace NS;
// struct NS::X -> heavily sugar'd
// NS::X -> sugar'd
// struct X -> sugar'd
// X -> not sugar'd
struct Y
{
    X foo();
};

你会收到所有三个指针的相同地址。

此区别是设计使然,与符号的可见性无关。