c++ - Clang - 获取 SubstTemplateTypeParm 完整模板信息

标签 c++ windows clang llvm-clang

我正在遍历 clang AST,但是在遍历包含 clang::SubstTemplateTypeParmType 的 AST 声明的类型信息时,我无法获取所需的信息。

给定 clang 工具的以下最小输入代码

#include <map>

template <typename K, typename V> using Map = std::map<K, V>;
using String = std::string;
using ParameterMap = Map<String, String>;

ParameterMap someFunc();

当通过 ParameterMap 递归时的类型,clang 说第一个 Map参数 arg,String , 是一个 clang::SubstTemplateTypeParmType .如果我尝试进一步递归以获取有关 String 的更多信息,通过脱糖或获取替换类型(下面的代码),基础类型是 Type::Record , 并且是 std::basic_string .这对我来说是出乎意料的,因为我希望底层类型是模板特化,类似于 basic_string<const char*, std::char_traits<const char*>, std::allocator<const char*>>。 .由于节点是一条记录,我可以得到 map 包含一个 std::basic_string ,但无法获取basic_string的模板信息.如何获取 basic_string 的模板特化信息在这种情况下?

case Type::SubstTemplateTypeParm:
{
    auto substTemplateType = qualType->getAs<SubstTemplateTypeParmType>();
    walkType(substTemplateType->getReplacementType());
    return;
}

我了解发布最小可运行代码示例的要求,如下所示。然而,这需要 clang 工具依赖性,没有预构建,因此插入和运行并不容易。

路径是硬编码的,因此需要根据您的本地设置进行更新。这是 compile_commands 文件,它也有 3 个更新路径。编译器路径,最后两次是文件路径。

[
{"directory":"F:/git/minRepro/","command":"\"C:/Program Files (x86)/compilers/clang.exe\" -Wall -isystem -g -std=c++14 -Wno-format -Wno-unneeded-internal-declaration -Werror F:/git/minRepro/exampleSource.cpp","file":"F:/git/minRepro/exampleSource.cpp"}
]

代码:

#pragma comment(lib,"Version.lib")

#include <clang/Tooling/JSONCompilationDatabase.h>
#include <clang/Tooling/Tooling.h>
#include <clang/Frontend/FrontendAction.h>
#include <clang/Sema/SemaConsumer.h>
#include <clang/AST/Type.h>
#include <clang/AST/TemplateName.h>
#include <clang/AST/Decl.h>
#include <clang/AST/DeclTemplate.h>
#include <clang/Frontend/CompilerInstance.h>

#include <iostream>
#include <cstdlib>
#include <cassert>
#include <vector>
#include <string>

class AstWalker : public clang::SemaConsumer
{
public:
    AstWalker(clang::ASTContext& context)
        : m_context(context)
    {}

    virtual void HandleTranslationUnit(clang::ASTContext& context)
    {
        using namespace clang;

        for (auto declaration : context.getTranslationUnitDecl()->decls())
        {
            const auto&sm = m_context.getSourceManager();

            if (!declaration->getBeginLoc().isValid())
                continue;

            // Only walk declarations from our file.
            if (!sm.isInMainFile(sm.getSpellingLoc(declaration->getBeginLoc())))
                continue;

            // Find functions, and inspect their return type.
            auto nodeKind = declaration->getKind();
            if (nodeKind == Decl::Function)
            {
                auto funcDecl = cast<FunctionDecl>(declaration);

                // Check for and ignore built-in functions.
                if (funcDecl->getBuiltinID() != 0)
                    break;

                walkType(funcDecl->getReturnType());
                break;
            }
        }
    }

    void walkType(const clang::QualType& qualType)
    {
        using namespace clang;

        auto classType = qualType->getTypeClass();
        switch (classType)
        {
        case Type::Typedef:
        {
            auto typedefType = qualType->getAs<TypedefType>();
            walkType(typedefType->desugar());
            return;
        }
        case Type::TemplateSpecialization:
        {
            auto templateSpecialization = qualType->getAs<TemplateSpecializationType>();
            if (templateSpecialization->isTypeAlias())
            {
                walkType(templateSpecialization->getAliasedType());
                return;
            }

            std::string templateType = templateSpecialization->getTemplateName().getAsTemplateDecl()->getQualifiedNameAsString();
            std::cout << templateType << "<";

            auto numArgs = templateSpecialization->getNumArgs();
            for (unsigned int i = 0; i < numArgs; ++i)
            {
                if (i > 0)
                    std::cout << ", ";

                const clang::TemplateArgument& templateArg = templateSpecialization->getArg(i);
                if (templateArg.getKind() == clang::TemplateArgument::ArgKind::Type)
                {
                    walkType(templateArg.getAsType());
                }
            }

            std::cout << ">";
            return;
        }
        case Type::Record:
        {
            const auto record = qualType->getAs<RecordType>();
            std::string recordQualifiedName = record->getAsRecordDecl()->getQualifiedNameAsString();
            std::cout << recordQualifiedName;
            return;
        }
        case Type::Elaborated:
        {
            auto elaboratedType = qualType->getAs<ElaboratedType>();
            walkType(elaboratedType->desugar());
            return;
        }
        case Type::SubstTemplateTypeParm:
        {
            auto substTemplateType = qualType->getAs<SubstTemplateTypeParmType>();
            walkType(substTemplateType->desugar());
            //Also tried getReplacementType.
            //walkType(substTemplateType->getReplacementType());
            return;
        }
        }
    }

private:
    clang::ASTContext& m_context;
};

class ExampleAction : public clang::ASTFrontendAction
{
public:
    ExampleAction() {}

    virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(clang::CompilerInstance& compiler, llvm::StringRef inFile)
    {
        return std::unique_ptr<clang::ASTConsumer>(new AstWalker(compiler.getASTContext()));
    }
};

int main(int argc, char **argv)
{
    // Create the compilation database.
    std::string errorOut;
    std::unique_ptr<clang::tooling::JSONCompilationDatabase> compilationDatabase = clang::tooling::JSONCompilationDatabase::loadFromFile("F:/git/minRepro/compile_commands.json", errorOut, clang::tooling::JSONCommandLineSyntax::AutoDetect);

    if (compilationDatabase == nullptr || !errorOut.empty())
    {
        std::cout << "[Error] Failed to load compilation database. Error=" << errorOut.c_str() << std::endl;
        return false;
    }

    std::vector<std::string> headerFiles;
    headerFiles.push_back("F:/git/minRepro/exampleSource.cpp");
    clang::tooling::ClangTool tool(*compilationDatabase, llvm::ArrayRef<std::string>(headerFiles));

    auto toolResult = tool.run(clang::tooling::newFrontendActionFactory<ExampleAction>().get());
    if (toolResult == 1)
    {
        std::cout << "[Error] Error occurred.  Check log.  Aborting.\n";
        assert(false);
        return false;
    }
}

最佳答案

在某些情况下,模板信息嵌套在给定 RecordTypeClassTemplateSpecializationDecl 中。您可以通过将 RecordDecl 转换为 ClassTemplateSpecializationDecl 来读取它。

const auto record = qualType->getAs<clang::RecordType>();
auto classTemplateSpecialization = llvm::dyn_cast<clang::ClassTemplateSpecializationDecl>(record->getAsRecordDecl());
if (classTemplateSpecialization)
{
    const auto& args = classTemplateSpecialization->getTemplateArgs();
    for (unsigned int i = 0; i < args.size(); ++i)
    {
        const clang::TemplateArgument& templateArg = args[i];
        // Read arg info as needed.
    }   
}
else
{
    // Not a template specialization.
}

关于c++ - Clang - 获取 SubstTemplateTypeParm 完整模板信息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56894327/

相关文章:

c++ - 程序不会运行非常大的数字

c++ - 一个源文件更改导致在安装 Visual 2013 后在 Visual 2010 上重建完整项目

c - 如何在 Windows 中检索线程的起始地址?

c++ - Clang 编译器使用运算符 T* 构造对象时出错

clang - clang 如何检查重定义?

c++ - 当 Matlab 中仅接受 Int32Ptr 时,以字符串形式返回错误号

c++ - C99 风格的 VLA 有哪些技术缺点?

windows - 在远程计算机上查找RAM的大小

c++ - 如何从剪贴板中清除指定格式的数据?

c++ - 在 llvm 中用 Undef 值替换对删除指令的所有使用?