How to find method declarations in LibTooling?

128 Views Asked by At

I'm writing a program in LibTooling to print information of all functions, including methods in classes. But it seems VisitFunctionDecl() only detects functions outside a class. I also tried VisiCXXMethodDecl(),

bool VisiCXXMethodDecl(CXXMethodDecl *node) {
    string return_type = node->getReturnType().getAsString();
    string function_name = node->getNameInfo().getAsString();
    cout << "function name: " << function_name << endl;
    cout << "return type: " << return_type << endl;
    cout << "has body: " << node->hasBody() << endl;
 }

but still failed to detect them. Can anyone tell me what's the correct way to detect methods defined in classes?

1

There are 1 best solutions below

0
Scott McPeak On

Quick fix

In your question, you have a typo in the name of the visitor method, which should be VisitCXXMethodDecl, not VisiCXXMethodDecl (missing the t in Visit). That may be all that needs fixing, but I can't tell without seeing the rest of the code.

Note that the compiler will not warn about that sort of typo because RecursiveASTVisitor does not use virtual methods to customize its action (if it did, and you used the override specifier, then the typo would result in an error). Instead, it uses template metaprogramming and static overriding, and in that context, a typo'd method name just looks like some unrelated method.

Complete example

Here is a complete program that prints the names and locations of every C++ method declaration (including definitions) in a translation unit.

method-decls.cc:

// method-decls.cc
// Print name and location of all method declarations using clang
// libtooling.

#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 <iostream>                                        // std::cout

using std::cout;


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

public:      // methods
  MethodVisitor(clang::ASTContext &astContext)
    : m_astContext(astContext)
  {}

  // Visit each method declaration (including definitions).
  bool VisitCXXMethodDecl(clang::CXXMethodDecl *methodDecl);

  // Print all methods in the translation unit.
  void printMethodInfo();
};

bool MethodVisitor::VisitCXXMethodDecl(clang::CXXMethodDecl *methodDecl)
{
  clang::SourceLocation loc = methodDecl->getLocation();

  cout << "C++ method \""
       << methodDecl->getQualifiedNameAsString()
       << "\" at "
       << loc.printToString(m_astContext.getSourceManager())
       << "\n";

  // Continue recursively visiting the children of 'methodDecl'.
  return true;
}


void MethodVisitor::printMethodInfo()
{
  this->TraverseDecl(m_astContext.getTranslationUnitDecl());
}


int main(int argc, char const **argv)
{
  // Point 'LoadFromCommandLine' at the clang binary so it will be able
  // to find its compiler headers such as stddef.h.
  argv[0] = CLANG_LLVM_INSTALL_DIR "/bin/clang";

  // Boilerplate setup for 'LoadFromCommandLine'.
  std::shared_ptr<clang::PCHContainerOperations> pchContainerOps(
    new clang::PCHContainerOperations());
  clang::IntrusiveRefCntPtr<clang::DiagnosticsEngine> diagnosticsEngine(
    clang::CompilerInstance::createDiagnostics(
      new clang::DiagnosticOptions));

  // Run the Clang parser to produce an AST.
  std::unique_ptr<clang::ASTUnit> ast(clang::ASTUnit::LoadFromCommandLine(
    argv,
    argv + argc,
    pchContainerOps,
    diagnosticsEngine,
    llvm::StringRef() /*ResourceFilesPath, evidently ignored*/));

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

  MethodVisitor methodVisitor(ast->getASTContext());
  methodVisitor.printMethodInfo();

  return 0;
}


// EOF

Makefile (based on the one at this answer of mine, but with a few further refinements):

# Makefile

# Default target.
all:
.PHONY: all


# ---- Configuration ----
# Installation directory from a binary distribution.
# Has five subdirectories: bin include lib libexec share.
# Downloaded from: https://github.com/llvm/llvm-project/releases/download/llvmorg-14.0.0/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz
CLANG_LLVM_INSTALL_DIR = $(HOME)/opt/clang+llvm-14.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 = g++

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

# Silence a warning about a multi-line comment in DeclOpenMP.h.
CXXFLAGS += -Wno-comment

# 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)

# 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: method-decls.exe
method-decls.exe: method-decls.o
    $(CXX) -g -Wall -o $@ $^ $(LDFLAGS)

# Quick test.
.PHONY: run-quick
run-quick: method-decls.exe
    ./method-decls.exe test.cc

# Slow test using a realistic source file.  This needs the same
# compilation flags as when compiling.  Also it produces a lot of
# output.
.PHONY: run-slow
run-slow: method-decls.exe
    ./method-decls.exe $(CXXFLAGS) method-decls.cc

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


# EOF

test.cc:

// test.cc
// Quick test of method-decls program.

class C {
public:
  int f1();
  int f2() { return 2; }
};

// EOF

Example invocation:

$ make run-quick
./method-decls.exe test.cc
C++ method "C::f1" at test.cc:6:7
C++ method "C::f2" at test.cc:7:7