Clang - 获取 SubstTemplateTypeParm 完整模板信息
Clang - Getting SubstTemplateTypeParm full template information
我正在遍历 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;
}
}
在某些情况下,对于给定的 RecordType
,模板信息嵌套在 ClassTemplateSpecializationDecl
中。您可以通过将 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.
}
我正在遍历 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;
}
}
在某些情况下,对于给定的 RecordType
,模板信息嵌套在 ClassTemplateSpecializationDecl
中。您可以通过将 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.
}