如何找到类模板成员函数定义的外层模板参数列表的SourceLocation?

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

我正在研究从以下源代码生成的 Clang AST:

template<class T>
struct my_class
{
  template<class U>
  void foo(U arg);
};

template<class V>
template<class W> void my_class<V>::foo(W arg)
{
}

int main()
{
  return 0;
}

我有一个

FunctionDecl
对应于第 9 行
foo
的定义。我想在第 8 行找到
SourceLocation
template<class V>

这可能吗?

我正在分析 AST,以便重写它。

c++ clang llvm abstract-syntax-tree clang-ast-matchers
1个回答
2
投票

“外部”模板参数列表与

DeclaratorDecl
, 并可以使用检索
getNumTemplateParameterLists()
getTemplateParameterList(unsigned)
方法。

这些方法没有任何文档,但是如果您查看

Decl.h
源码,可以看到这些方法访问了一个
QualifierInfo
结构,其中包含两个相关领域的文档。

然后,一旦我们得到相关的

TemplateParameterList
,
template
关键字和尖括号的位置可以是 通过调用
getTemplateLoc()
getLAngleLoc()
getRAngleLoc()
。各个参数的位置可用 从
NamedDecl
节点可通过
asArray()
访问。

因此,给定

FunctionDecl *functionDecl
,我们可以打印(比如说) 外部
template
关键字的位置如下:

  for (unsigned i=0; i < functionDecl->getNumTemplateParameterLists(); ++i) {
    clang::TemplateParameterList *paramList =
      functionDecl->getTemplateParameterList(i);

    cout << "outer template parameter list " << i << "\n"
         << "  TemplateLoc: "
         << paramList->getTemplateLoc().
              printToString(m_astContext.getSourceManager()) << "\n";
  }

一个相关的怪癖/细节:当

DeclaratorDecl
(它是
FunctionDecl
CXXMethodDecl
的子类)具有“外部”模板参数时,其
getBeginLoc()
方法将返回
template
关键字的位置第一个外层。如果您想获取声明开始位置after所有模板参数列表(内部和外部,尽管文档仅提到外部),请使用
DeclaratorDecl::getInnerLocStart()
。对于问题中的示例,它返回
void
关键字的位置(对于
foo
的两个声明)。

完整示例

这是演示该方法的完整程序:

// outer-tparams.cc
// Report "outer" template parameter lists.

#include "clang/AST/RecursiveASTVisitor.h"                 // clang::RecursiveASTVisitor
#include "clang/Basic/Diagnostic.h"                        // clang::DiagnosticsEngine
#include "clang/Basic/DiagnosticOptions.h"                 // clang::DiagnosticOptions
#include "clang/Frontend/ASTUnit.h"                        // clang::ASTUnit
#include "clang/Frontend/CompilerInstance.h"               // clang::CompilerInstance
#include "clang/Serialization/PCHContainerOperations.h"    // clang::PCHContainerOperations

#include "llvm/Support/raw_ostream.h"                      // llvm::raw_string_ostream

#include <iostream>                                        // std::cout
#include <sstream>                                         // std::ostringstream
#include <string>                                          // std::string

#include <assert.h>                                        // assert

// Convenient string construction.
#define stringb(stuff) \
  (static_cast<std::ostringstream&>(std::ostringstream() << stuff).str())

using clang::dyn_cast;

using std::cout;
using std::endl;
using std::string;


class Visitor : public clang::RecursiveASTVisitor<Visitor> {
public:      // data
  clang::ASTContext &m_astContext;

public:      // methods
  Visitor(clang::ASTUnit *astUnit)
    : m_astContext(astUnit->getASTContext())
  {}

  // Convenience methods to stringify some things.
  string locStr(clang::SourceLocation loc) const;
  string declLocStr(clang::Decl const *decl) const;
  string declNameLocStr(clang::NamedDecl const *decl) const;
  string templateParameterListStr(
    clang::TemplateParameterList const *paramList) const;

  // Visitor methods.
  bool VisitFunctionDecl(clang::FunctionDecl *functionDecl);

  // Kick off the traversal.
  void traverseTU();
};


string Visitor::locStr(clang::SourceLocation loc) const
{
  return loc.printToString(m_astContext.getSourceManager());
}

string Visitor::declLocStr(clang::Decl const *decl) const
{
  return locStr(decl->getLocation());
}

string Visitor::declNameLocStr(clang::NamedDecl const *decl) const
{
  return stringb(decl->getQualifiedNameAsString() <<
                 " declared at " << declLocStr(decl));
}


string Visitor::templateParameterListStr(
  clang::TemplateParameterList const *paramList) const
{
  string ret;
  llvm::raw_string_ostream rso(ret);

  clang::PrintingPolicy printingPolicy(m_astContext.getLangOpts());
  bool const omitTemplateKW = false;
  paramList->print(rso, m_astContext, printingPolicy, omitTemplateKW);

  // Annoyingly, the string has a trailing space.
  ret.erase(ret.find_last_not_of(" \t\n\r\f\v") + 1);

  return ret;
}


bool Visitor::VisitFunctionDecl(clang::FunctionDecl *functionDecl)
{
  cout << "FunctionDecl: " << declNameLocStr(functionDecl) << "\n";

  // Iterate over the "outer" template parameter lists, i.e., those
  // associated with qualifiers in the name of the declared entity, but
  // not directly associated with the entity itself.  (Parameters for
  // the entity itself are on the 'TemplateDecl' if it is a template.)
  for (unsigned i=0; i < functionDecl->getNumTemplateParameterLists(); ++i) {
    clang::TemplateParameterList *paramList =
      functionDecl->getTemplateParameterList(i);

    cout << "  outer template parameter list " << i << "\n"
         << "    syntax: \"" << templateParameterListStr(paramList) << "\"\n"
         << "    TemplateLoc: " << locStr(paramList->getTemplateLoc()) << "\n"
         << "    LAngleLoc: " << locStr(paramList->getLAngleLoc()) << "\n"
         << "    RAngleLoc: " << locStr(paramList->getRAngleLoc()) << "\n"
         ;
  }

  return true;
}


void Visitor::traverseTU()
{
  this->TraverseDecl(m_astContext.getTranslationUnitDecl());
}


int main(int argc, char const **argv)
{
  // Copy the arguments into a vector of char pointers since that is
  // what 'createInvocationFromCommandLine' wants.
  std::vector<char const *> commandLine;
  {
    // Path to the 'clang' binary that I am behaving like.  This path is
    // used to compute the location of compiler headers like stddef.h.
    commandLine.push_back(CLANG_LLVM_INSTALL_DIR "/bin/clang");

    for (int i = 1; i < argc; ++i) {
      commandLine.push_back(argv[i]);
    }
  }

  // Parse the command line options.
  std::shared_ptr<clang::CompilerInvocation> compilerInvocation(
    clang::createInvocation(llvm::ArrayRef(commandLine)));
  if (!compilerInvocation) {
    // Command line parsing errors have already been printed.
    return 2;
  }

  // Boilerplate.
  std::shared_ptr<clang::PCHContainerOperations> pchContainerOps(
    new clang::PCHContainerOperations());
  clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnosticsEngine(
    clang::CompilerInstance::createDiagnostics(
      &(compilerInvocation->getDiagnosticOpts())));

  // Run the Clang parser to produce an AST.
  std::unique_ptr<clang::ASTUnit> ast(
    clang::ASTUnit::LoadFromCompilerInvocationAction(
      compilerInvocation,
      pchContainerOps,
      diagnosticsEngine));

  if (ast == nullptr ||
      diagnosticsEngine->getNumErrors() > 0) {
    // Error messages have already been printed.
    return 2;
  }

  Visitor visitor(ast.get());
  visitor.traverseTU();

  return 0;
}


// EOF
# Makefile

# Default target.
all:
.PHONY: all


# ---- Configuration ----
# Installation directory from a binary distribution.
CLANG_LLVM_INSTALL_DIR = $(HOME)/opt/clang+llvm-16.0.0-x86_64-linux-gnu-ubuntu-18.04

# ---- llvm-config query results ----
# Program to query the various LLVM configuration options.
LLVM_CONFIG := $(CLANG_LLVM_INSTALL_DIR)/bin/llvm-config

# C++ compiler options to ensure ABI compatibility.
LLVM_CXXFLAGS := $(shell $(LLVM_CONFIG) --cxxflags)

# Directory containing the clang library files, both static and dynamic.
LLVM_LIBDIR := $(shell $(LLVM_CONFIG) --libdir)

# Other flags needed for linking, whether statically or dynamically.
LLVM_LDFLAGS_AND_SYSTEM_LIBS := $(shell $(LLVM_CONFIG) --ldflags --system-libs)


# ---- Compiler options ----
# C++ compiler.
CXX := $(CLANG_LLVM_INSTALL_DIR)/bin/clang++

# Compiler options, including preprocessor options.
CXXFLAGS =
CXXFLAGS += -Wall
CXXFLAGS += -Werror

# Get llvm compilation flags.
CXXFLAGS += $(LLVM_CXXFLAGS)

# Tell the source code where the clang installation directory is.
CXXFLAGS += -DCLANG_LLVM_INSTALL_DIR='"$(CLANG_LLVM_INSTALL_DIR)"'

# Linker options.
LDFLAGS =

# Pull in clang+llvm via libclang-cpp.so, which has everything, but is
# only available as a dynamic library.
LDFLAGS += -lclang-cpp

# Arrange for the compiled binary to search the libdir for that library.
# Otherwise, one can set the LD_LIBRARY_PATH envvar before running it.
# Note: the -rpath switch does not work on Windows.
LDFLAGS += -Wl,-rpath=$(LLVM_LIBDIR)

# It appears that llvm::raw_os_ostream::~raw_os_ostream is missing from
# libclang-cpp, so I have to link with LLVMSupport statically.
LDFLAGS += -lLLVMSupport

# Get the needed -L search path, plus things like -ldl.
LDFLAGS += $(LLVM_LDFLAGS_AND_SYSTEM_LIBS)


# ---- Recipes ----
# Compile a C++ source file.
%.o: %.cc
    $(CXX) -c -o $@ $(CXXFLAGS) $<

# Executable.
all: outer-tparams.exe
outer-tparams.exe: outer-tparams.o
    $(CXX) -g -Wall -o $@ $^ $(LDFLAGS)

# Test.
.PHONY: run
run: outer-tparams.exe
    ./outer-tparams.exe test.cc

.PHONY: clean
clean:
    $(RM) *.o *.exe


# EOF

当在

test.cc
上运行时,问题中的示例:

template<class T>
struct my_class
{
  template<class U>
  void foo(U arg);
};

template<class V>
template<class W> void my_class<V>::foo(W arg)
{
}

int main()
{
  return 0;
}

它打印:

FunctionDecl: my_class::foo declared at test.cc:5:8
FunctionDecl: my_class::foo declared at test.cc:9:37
  outer template parameter list 0
    syntax: "template <class V>"
    TemplateLoc: test.cc:8:1       <-------- what we wanted
    LAngleLoc: test.cc:8:9
    RAngleLoc: test.cc:8:17
FunctionDecl: main declared at test.cc:13:5

当运行这个更复杂的示例时:

// test2.cc
// More complicated example.

template <class P1>
class C1 {
  template <class P2>
  class C2 {
    template <class P3>
    class C3 {
      class C4 {
        template <class P5>
        class C5 {
          int regularMethod();

          template <class P6>
          int templatedMethod();
        };
      };
    };
  };
};

template <class P1>
template <class P2>
template <class P3>
template <class P5>     // Last "outer" list.
int C1<P1>::C2<P2>::C3<P3>::C4::C5<P5>::regularMethod()
{
  return 1;
}

template <class P1>
template <class P2>
template <class P3>
template <class P5>     // Last "outer" list.
template <class P6>     // Not an "outer" list.
int C1<P1>::C2<P2>::C3<P3>::C4::C5<P5>::templatedMethod()
{
  return 2;
}

template <>
template <>
template <>
template <>
template <>             // This *is* an "outer" template parameter list.
int C1<int>::C2<int>::C3<int>::C4::C5<int>::templatedMethod<int>()
{
  return 3;
}

// EOF

它打印:

FunctionDecl: C1::C2::C3::C4::C5::regularMethod declared at test2.cc:13:15
FunctionDecl: C1::C2::C3::C4::C5::templatedMethod declared at test2.cc:16:15
FunctionDecl: C1::C2::C3::C4::C5::regularMethod declared at test2.cc:27:41
  outer template parameter list 0
    syntax: "template <class P1>"
    TemplateLoc: test2.cc:23:1
    LAngleLoc: test2.cc:23:10
    RAngleLoc: test2.cc:23:19
  outer template parameter list 1
    syntax: "template <class P2>"
    TemplateLoc: test2.cc:24:1
    LAngleLoc: test2.cc:24:10
    RAngleLoc: test2.cc:24:19
  outer template parameter list 2
    syntax: "template <class P3>"
    TemplateLoc: test2.cc:25:1
    LAngleLoc: test2.cc:25:10
    RAngleLoc: test2.cc:25:19
  outer template parameter list 3
    syntax: "template <class P5>"
    TemplateLoc: test2.cc:26:1
    LAngleLoc: test2.cc:26:10
    RAngleLoc: test2.cc:26:19
FunctionDecl: C1::C2::C3::C4::C5::templatedMethod declared at test2.cc:37:41
  outer template parameter list 0
    syntax: "template <class P1>"
    TemplateLoc: test2.cc:32:1
    LAngleLoc: test2.cc:32:10
    RAngleLoc: test2.cc:32:19
  outer template parameter list 1
    syntax: "template <class P2>"
    TemplateLoc: test2.cc:33:1
    LAngleLoc: test2.cc:33:10
    RAngleLoc: test2.cc:33:19
  outer template parameter list 2
    syntax: "template <class P3>"
    TemplateLoc: test2.cc:34:1
    LAngleLoc: test2.cc:34:10
    RAngleLoc: test2.cc:34:19
  outer template parameter list 3
    syntax: "template <class P5>"
    TemplateLoc: test2.cc:35:1
    LAngleLoc: test2.cc:35:10
    RAngleLoc: test2.cc:35:19
FunctionDecl: C1<int>::C2<int>::C3<int>::C4::C5<int>::templatedMethod declared at test2.cc:47:45
  outer template parameter list 0
    syntax: "template <>"
    TemplateLoc: test2.cc:42:1
    LAngleLoc: test2.cc:42:10
    RAngleLoc: test2.cc:42:11
  outer template parameter list 1
    syntax: "template <>"
    TemplateLoc: test2.cc:43:1
    LAngleLoc: test2.cc:43:10
    RAngleLoc: test2.cc:43:11
  outer template parameter list 2
    syntax: "template <>"
    TemplateLoc: test2.cc:44:1
    LAngleLoc: test2.cc:44:10
    RAngleLoc: test2.cc:44:11
  outer template parameter list 3
    syntax: "template <>"
    TemplateLoc: test2.cc:45:1
    LAngleLoc: test2.cc:45:10
    RAngleLoc: test2.cc:45:11
  outer template parameter list 4
    syntax: "template <>"
    TemplateLoc: test2.cc:46:1
    LAngleLoc: test2.cc:46:10
    RAngleLoc: test2.cc:46:11
© www.soinside.com 2019 - 2024. All rights reserved.