Clang

Table of Contents

1 Invoking

-ast-dump
dump ast
-ast-dump-filter
filter to only dump part of the AST
-ast-list
list ast nodes
clang -Xclang -ast-dump -fsyntax-only a.c
clang -emit-ast a.c
clang-check -ast-list lib/parser.cpp | grep AddValue
clang-check a.cc -ast-dump -ast-dump-filter=StdStringA --

2 LibTooling

2.1 Project Setup

2.1.1 Main File

First of all, get the CMakeLists.txt setup:

The first line:

cmake_minimum_required(VERSION 3.0)

Setting directory to lib and bin

set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

Other setup

SET(CMAKE_EXPORT_COMPILE_COMMANDS ON)

Thread library:

find_package (Threads)
link_libraries(${CMAKE_THREAD_LIBS_INIT})

LLVM library configuration:

find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMCOnfig.cmake in: ${LLVM_DIR}")
add_definitions(${LLVM_DEFINITIONS})
include_directories(${LLVM_INCLUDE_DIRS})
set(LLVM_LINK_COMPONENTS support)

Clang library setup

find_package(Clang REQUIRED CONFIG)

Trouble shooting setup

# Otherwise error: undefined reference to typeinfo for xxx
add_compile_options(-fno-rtti)

link library

link_libraries(clang clangTooling clangFrontend clangFrontendTool)
link_libraries(libclang gtest)

Add sub-directories

enable_testing()
add_subdirectory (src)
add_subdirectory (test)

2.1.2 Sub-directory files

src/CMakeLists.txt to add libraries, executables

add_library (Sqr sqr.cpp sqr.h)
add_executable (demo main.cpp)
target_link_libraries (demo Sqr)

add_executable(ast ast.cpp)
add_executable(token token.cpp)
add_executable(rewriter rewriter.cpp)

test/CMakeLists.txt

The only requirement is to have enable_testing before add_test. The command can be in src level list if no test source files.

add_test(NAME toktest COMMAND hetok ../test/a.c)
add_test(NAME MyTest COMMAND Test)

2.2 Header files

Some representative header files:

#include "clang/AST/ASTConsumer.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Tooling/Tooling.h"
#include "clang/Frontend/FrontendActions.h"
#include "llvm/Support/CommandLine.h"
#include "clang/Tooling/CommonOptionsParser.h"

2.3 Entry Point

The entry point is creating the tooling::ClangTool class. Just pass argc/v into it. The command line option -- at the end to invoke the tool will not trying to find compilation database.

int main(int argc, const char **argv) {
  CommonOptionsParser OptionsParser(argc, argv, MyToolCategory);
  ClangTool Tool(OptionsParser.getCompilations(), OptionsParser.getSourcePathList());
  Tool.run(newFrontendActionFactory<MyAction>().get());
}

The Tool would run on some "action". This is our main logic. The action derives from ASTFrontendAction, and override the CreateASTConsumer class.

class MyAction : public clang::ASTFrontendAction {
public:
  virtual std::unique_ptr<clang::ASTConsumer>
  CreateASTConsumer(clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
    return std::unique_ptr<clang::ASTConsumer>
      (new MyConsumer(&Compiler.getASTContext()));
  }
};

The Consumer would derive from ASTConsumer and override HandleTranslationUnit. This function is called when the whole translation unit is parsed. This provides the entry point of the AST by the top most decl by Context.getTranslationUnitDecl().

The visitor will automatically call WalkUpFromXXX(x) to recursively visit child nodes of x returning false of TraverseXXX or WalkUpFromXXX will terminate the traversal. By default this will be a pre-order traversal. Calling a method to change to post-order.

class MyConsumer : public clang::ASTConsumer {
public:
  explicit MyConsumer(ASTContext *Context)
    : Visitor(Context) {}
  virtual void HandleTranslationUnit(clang::ASTContext &Context) {
    Visitor.TraverseDecl(Context.getTranslationUnitDecl());
  }
private:
  MyVisitor Visitor;
};

The visitor itself implement what to do with each AST node. Override the list of VisitXXX method for each type of AST node.

class TokenVisitor
  : public RecursiveASTVisitor<TokenVisitor> {
public:
  explicit TokenVisitor(ASTContext *Context)
    : Context(Context) {}
  bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {}
  bool VisitFunctionDecl(FunctionDecl *func_decl) {}
private:
  ASTContext *Context;
};

2.4 Location

  • Decl::getLocStart -> SourceLocation loc
  • context->getFullLoc(loc) -> FullSourceLoc full
  • full.getSpellingLinenumber

2.5 APIs

Decl

SourceLocation getLocStart ();
SourceLocation getLocEnd ();
virtual SourceRange getSourceRange ();

ASTContext

FullSourceLoc getFullLoc (SourceLocation Loc) const
SourceManager& getSourceManager ()

FullSourceLoc

unsigned getSpellingLineNumber (bool *Invalid=nullptr) const
unsigned getSpellingColumnNumber (bool *Invalid=nullptr) const
FileID  getFileID () const

SourceManager

FileManager& getFileManager () const;
FileID getMainFileID () const; // this file being processed
const FileEntry *getFileEntryForID (FileID FID) const;

3 Reference

Author: Hebi Li

Created: 2017-03-27 Mon 14:36

Validate