Glud Documentation

Do you need to find something in a C++ file? Perhaps you’re generating bindings for C++ library, generating serialization code, collecting code-metrics or developing a style checker?

You can solve all of these problems using libclang, a library provided as part of Clang compiler. However, the challenge with libclang is that it’s easy to end up in a situation where the code you write can be difficult to write, compose and maintain.

Glud solves this problem by providing a higher level abstraction that makes it easier to find C++ constructs, inspired by libclangastmatchers.

Example

For example, to find all of the classes in C/C++ file:

import sys
import glud
matcher = glud.isClass()
translation_unit = glud.parse(sys.argv[1])
for cls in glud.walk(matcher, translation_unit.cursor):
    print(cls.type.spelling)

Contents

Installation

This part of the documentation covers the installation of Glud.

Glud is a pure Python package, with minimal dependencies, but does expect that libclang (the native code library that exposes the stable bindings to the Clang compiler) is installed and can be dynamically loaded.

For Python 2 development, it’s also recommended that you install the Python bindings that match your libclang, rather than relying on versions provided by PyPI.

Prerequisites

On Ubuntu distributions, the easiest way to get an appropriate build of libclang you can install pre-built binaries from the LLVM apt repositories.

Ubuntu Trusty (14.04)

In your terminal, enter the following commands to install libclang:

wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
echo "deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.8 main" | sudo tee -a /etc/apt/sources.list
sudo apt-get update -qq
sudo apt-get install -y python-clang-3.8 libclang1-3.8

You can then add libclang.so to your loader path, which makes the library discoverable.

export LD_LIBRARY_PATH=/usr/lib/llvm-3.8/lib
Installation with Pip

You can install Glud with the dependency manager pip

pip install glud

Quickstart

What is an Abstract Syntax Tree?

An Abstract Syntax Tree is the data structure that a compiler converts the source code of your program into, that is easier to work with than the bare source code. Python has it’s own ast module in the standard library.

To see the what Glud knows about an AST, you can parse source, and then display the AST. During parsing, Glud assumes that the code you are passing it is well-formed (would compile without errors), and will throw an exception if this does not occur.

import sys
from glud import *

translation_unit = parse(sys.argv[1])
print(dump(translation_unit.cursor))

Which when invoked on a C++ file (tmp.cpp) that looks like this:

class X {};
struct Y {};
class Z {
public:
  int f(double x, char);
};

You would expect to see an AST like:

TRANSLATION_UNIT tmp.cpp
  +--CLASS_DECL X
  +--STRUCT_DECL Y
  +--CLASS_DECL Z
     +--CXX_ACCESS_SPEC_DECL
     +--CXX_METHOD f
        +--PARM_DECL x
        +--PARM_DECL
Finding classes with methods

You can find class methods using cxxMethodDecl

import sys
from glud import *

matcher = cxxRecordDecl(isClass(), has(cxxMethodDecl()))
translation_unit = parse(sys.argv[1])
for cls in walk(matcher, translation_unit.cursor):
    print(cls.type.spelling)
Composing Matchers

You can compose a matchers together to match more interesting criteria. For example, if you wanted to match methods that were public and non-static, you could write

matcher = cxxMethodDecl(
            isPublic(),
            hasStaticStorageDuration())
Further Reading

Each of the matchers in the Glud API include a wide range of examples, see Matchers for more information.

API

Parsing

Simplify the parsing of C/C++ using libclang. This module specifically supports the use case of single translation units where it isn’t important that symbols from other translation units would be visible.

parse(name, **kwargs) Parse a C/C++ file
parse_string(contents[, name]) Parse a string of C/C++ code
Traversal

Traverse the libclang generate AST using semantics similar to the python ast module.

walk(predicate, cursor) Yield all nodes found by recursively visiting the AST
iter_child_nodes(predicate, cursor) Yield all direct child nodes of node
AST Pretty Printing

Display the libclang AST, or a filtered subset of

dump(cursor) Display the AST represented by the cursor
Matchers

Code that implements Abstract Syntax Tree node matchers, or allow

allOf(*args) Matches if all of the argument matchers match
anyOf(*args) Matches if any of the argument matchers match
anything() Matches anything
builtinType(*args) Matches builtin primitive types (eg/ integers, booleans and float)
classTemplateDecl(*args) Match C++ template class declarations
cxxConstructorDecl(*args) Match C++ constructors
cxxDestructorDecl(*args) Match C++ destructors
cxxMethodDecl(*args) Match C++ methods
cxxRecordDecl(*args) Matches C++ class declarations.
decl(*args) Match any declaration
enumDecl(*args) Match enumerations
fieldDecl(*args) Match struct / class fields
functionDecl(*args) Match function declarations
has(*args) Match if a cursor has a child that matches
hasAncestor(matcher) Matches if the current cursor has an ancestor that matches
hasAnyParameter(matcher) Match if any method or function argument matches
hasCanonicalType(m) Matches if a cursor has the specified number of arguments
hasName(name) Match a cursors spelling against a pattern
hasParameter(N, inner) Matches if the N-th parameter matches the inner matcher
hasParent(*args) Matches if the direct parent node matches
hasReturnType(matcher) Match a function/method with a specified return type
hasStaticStorageDuration() Match an item has static storage duration
hasType(matcher) Matches if the type associated with the current cursor matches
hasTypename(typename) Match if the spelling of the type of a cursor matches a pattern
isClass() Matches if a cursor is a class
isDefinition() Matches if the cursor is a definition
isDerivedFrom(name) Match if a C++ type inherits from a named class
isExpansionInFileMatching(pattern) Matches if the nodes location matches a pattern
isPrivate() Test if a cursor is private
isProtected() Test if a cursor is protected
isPublic() Test if a cursor is public
isSameOrDerivedFrom(name) Match if derives-from (or is-a) class with a given name
isStruct() Matches if a cursor is a struct
namespaceDecl(*args) Match a C++ namespace declaration
parameterCountIs(N) Matches if a cursor has the specified number of arguments
pointee(inner) Traverse from a pointer to the dereferenced type
pointerType(*args) Test if a cursor has pointer type
recordDecl(*args) Matches class, struct, and union declarations.
stmt(*args) Matches statements
typedefDecl(*args) Matches typedef declarations
unless(*args) Inverts the match of the children
varDecl(*args) Matches variable declarations
Definitions
exception glud.parsing.ClangDiagnosticException(diagnostic)[source]

Encapsulates Clang diagnostics as an exception

glud.parsing.parse_string(contents, name='tmp.cpp', **kwargs)[source]

Parse a string of C/C++ code

glud.parsing.parse(name, **kwargs)[source]

Parse a C/C++ file

glud.traversal.iter_child_nodes(predicate, cursor)[source]

Yield all direct child nodes of node

glud.traversal.walk(predicate, cursor)[source]

Yield all nodes found by recursively visiting the AST

glud.display.dump(cursor)[source]

Display the AST represented by the cursor

glud.matchers.allOf(*args)[source]

Matches if all of the argument matchers match

>>> from glud import *
>>> config = '''
...  class X;
...  class Y;
... '''
>>> m = allOf(cxxRecordDecl(), hasName('X'))
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
X
glud.matchers.anyOf(*args)[source]

Matches if any of the argument matchers match

>>> from glud import *
>>> config = '''
...  int x;
...  class Y {};
...  enum Z {};
... '''
>>> m = anyOf(varDecl(), isClass())
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
x
Y
glud.matchers.hasType(matcher)[source]

Matches if the type associated with the current cursor matches

>>> from glud import *
>>> config = '''
...  int x;
...  long y;
... '''
>>> m = varDecl(hasType(hasName('int')))
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
x
glud.matchers.anything()[source]

Matches anything

>>> from glud import *
>>> config = '''
... namespace W {
...  int x;
...  class Y;
...  enum Z {};
... }
... '''
>>> m = allOf(anything(), hasParent(namespaceDecl()))
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
x
Y
Z
glud.matchers.hasAnyParameter(matcher)[source]

Match if any method or function argument matches

>>> from glud import *
>>> config = '''
... void f();
... void g(int);
... '''
>>> m = functionDecl(hasAnyParameter(hasType(builtinType())))
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
g
glud.matchers.builtinType(*args)[source]

Matches builtin primitive types (eg/ integers, booleans and float)

>>> from glud import *
>>> config = '''
...  int x;
...  bool y;
... '''
>>> m = varDecl(hasType(builtinType()))
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
x
y
glud.matchers.classTemplateDecl(*args)[source]

Match C++ template class declarations

>>> from glud import *
>>> config = '''
...  template<typename T> class X {};
...  class Y {};
... '''
>>> m = classTemplateDecl()
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
X
glud.matchers.cxxRecordDecl(*args)[source]

Matches C++ class declarations.

>>> from glud import *
>>> config = '''
...  class W;
...  template<typename T> class X {};
...  struct Y {};
...  union Z {};
... '''
>>> m = cxxRecordDecl()
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
W
X
glud.matchers.cxxConstructorDecl(*args)[source]

Match C++ constructors

>>> from glud import *
>>> config = '''
...  class X {
...    X();
...  };
...  class Y {};
... '''
>>> m = cxxConstructorDecl()
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
X
glud.matchers.cxxDestructorDecl(*args)[source]

Match C++ destructors

>>> from glud import *
>>> config = '''
...  class X {
...    ~X();
...  };
...  class Y {};
... '''
>>> m = cxxDestructorDecl()
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
~X
glud.matchers.cxxMethodDecl(*args)[source]

Match C++ methods

>>> from glud import *
>>> config = '''
...  class X {
...    void u();
...    void v();
...  };
... '''
>>> m = cxxMethodDecl()
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
u
v
glud.matchers.decl(*args)[source]

Match any declaration

>>> from glud import *
>>> config = '''
...  class X {};
...  struct Y {};
...  enum Z {};
... '''
>>> m = decl()
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
X
Y
Z
glud.matchers.enumDecl(*args)[source]

Match enumerations

>>> from glud import *
>>> config = '''
...  enum X {};
...  enum class Y {};
... '''
>>> m = enumDecl()
>>> args = '-x c++ -std=c++11'.split()
>>> for c in walk(m, parse_string(config, args=args).cursor):
...     print(c.spelling)
X
Y
glud.matchers.fieldDecl(*args)[source]

Match struct / class fields

>>> from glud import *
>>> config = '''
...  struct X {
...   int u;
...   int v;
...  };
... '''
>>> m = fieldDecl()
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
u
v
glud.matchers.functionDecl(*args)[source]

Match function declarations

>>> from glud import *
>>> config = '''
...  int u();
...  int v();
... '''
>>> m = functionDecl()
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
u
v
glud.matchers.has(*args)[source]

Match if a cursor has a child that matches

>>> from glud import *
>>> config = '''
...  class X {
...   void f();
...  };
...  class Y;
... '''
>>> m = cxxRecordDecl(has(cxxMethodDecl()))
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
X
glud.matchers.hasName(name)[source]

Match a cursors spelling against a pattern

>>> from glud import *
>>> config = '''
...  class X {};
...  class Y {};
... '''
>>> m = cxxRecordDecl(hasName('X'))
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
X
glud.matchers.hasReturnType(matcher)[source]

Match a function/method with a specified return type

>>> from glud import *
>>> config = '''
... class X {};
... X u();
... int v();
... '''
>>> m = functionDecl(hasReturnType(builtinType()))
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
v
glud.matchers.hasStaticStorageDuration()[source]

Match an item has static storage duration

>>> from glud import *
>>> config = '''
... class X {
...  static void u();
...  void v();
... };
... '''
>>> m = cxxMethodDecl(
...         hasStaticStorageDuration())
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
u
glud.matchers.hasTypename(typename)[source]

Match if the spelling of the type of a cursor matches a pattern

>>> from glud import *
>>> config = '''
... namespace X {
...  class Y {};
... }
... class Y {};
... '''
>>> m = cxxRecordDecl(hasTypename('X::Y'))
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.type.spelling)
X::Y
glud.matchers.isDerivedFrom(name)[source]

Match if a C++ type inherits from a named class

>>> from glud import *
>>> config = '''
... class X {};
... class Y : public X {};
... class Z : public Y {};
... '''
>>> m = cxxRecordDecl(isDerivedFrom('X'))
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
Y
Z
glud.matchers.isSameOrDerivedFrom(name)[source]

Match if derives-from (or is-a) class with a given name

>>> from glud import *
>>> config = '''
... class X {};
... class Y : public X {};
... class Z : public Y {};
... '''
>>> m = cxxRecordDecl(isSameOrDerivedFrom('X'))
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
X
Y
Z
glud.matchers.namespaceDecl(*args)[source]

Match a C++ namespace declaration

>>> from glud import *
>>> config = '''
... namespace X { }
... '''
>>> m = namespaceDecl()
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
X
glud.matchers.recordDecl(*args)[source]

Matches class, struct, and union declarations.

>>> from glud import *
>>> config = '''
...  class W;
...  template<typename T> class X {};
...  struct Y {};
...  union Z {};
... '''
>>> m = recordDecl()
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
W
X
Y
Z
glud.matchers.stmt(*args)[source]

Matches statements

>>> from glud import *
>>> config = '''
... void f() { }
... '''
>>> m = stmt()
>>> i = 0
>>> for c in parse_string(config).cursor.walk_preorder():
...     if m(c):
...         i += 1
>>> print(i)
1
glud.matchers.typedefDecl(*args)[source]

Matches typedef declarations

>>> from glud import *
>>> config = '''
... typedef int X;
... '''
>>> m = typedefDecl()
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
X
glud.matchers.unless(*args)[source]

Inverts the match of the children

>>> from glud import *
>>> config = '''
... class X { };
... class Y {};
... class Z {};
... '''
>>> m = cxxRecordDecl(unless(hasName('Y')))
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
X
Z
glud.matchers.isDefinition()[source]

Matches if the cursor is a definition

>>> from glud import *
>>> config = '''
... class X {};
... class Y;
... '''
>>> m = cxxRecordDecl(isDefinition())
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
X
glud.matchers.hasAncestor(matcher)[source]

Matches if the current cursor has an ancestor that matches

>>> from glud import *
>>> config = '''
... namespace X {
...   class Y {};
... }
... class Z {};
... '''
>>> m = cxxRecordDecl(
...         hasName('Y'),
...         hasAncestor(namespaceDecl(hasName('X'))))
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
Y
glud.matchers.isExpansionInFileMatching(pattern)[source]

Matches if the nodes location matches a pattern

>>> from glud import *
>>> config = '''
... class X;
... '''
>>> m = isExpansionInFileMatching('tmp.cpp')
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
X
glud.matchers.varDecl(*args)[source]

Matches variable declarations

>>> from glud import *
>>> config = '''
...  int a;
... '''
>>> m = varDecl()
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
a
glud.matchers.hasParent(*args)[source]

Matches if the direct parent node matches

>>> from glud import *
>>> config = '''
... namespace X {
...   int a;
... }
... int b;
... '''
>>> m = varDecl(hasParent(namespaceDecl(hasName('X'))))
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
a
glud.matchers.parameterCountIs(N)[source]

Matches if a cursor has the specified number of arguments

>>> from glud import *
>>> config = '''
...  int f();
...  int g(int);
...  int h(int, int);
... '''
>>> m = functionDecl(parameterCountIs(1))
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
g
glud.matchers.hasCanonicalType(m)[source]

Matches if a cursor has the specified number of arguments

>>> from glud import *
>>> config = '''
...  namespace X {
...   struct Y;
...   Y f();
...  }
... '''
>>> m = functionDecl(hasReturnType(hasCanonicalType(hasName('X::Y'))))
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
f
glud.matchers.isStruct()[source]

Matches if a cursor is a struct

>>> from glud import *
>>> config = '''
...  class X;
...  struct Y;
... '''
>>> m = isStruct()
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
Y
glud.matchers.isClass()[source]

Matches if a cursor is a class

>>> from glud import *
>>> config = '''
...  class X;
...  struct Y;
... '''
>>> m = isClass()
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
X
glud.matchers.isPublic()[source]

Test if a cursor is public

>>> from glud import *
>>> config = '''
...  class X {
...   public:
...    int y;
...  };
... '''
>>> m = fieldDecl(isPublic())
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
y
glud.matchers.isProtected()[source]

Test if a cursor is protected

>>> from glud import *
>>> config = '''
...  class X {
...   protected:
...    int y;
...  };
... '''
>>> m = fieldDecl(isProtected())
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
y
glud.matchers.isPrivate()[source]

Test if a cursor is private

>>> from glud import *
>>> config = '''
...  class X {
...   private:
...    int y;
...  };
... '''
>>> m = fieldDecl(isPrivate())
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
y
glud.matchers.pointerType(*args)[source]

Test if a cursor has pointer type

>>> from glud import *
>>> config = '''
...  int w;
...  int* x;
...  int** y;
...  int& z = w;
... '''
>>> m = varDecl(hasType(pointerType()))
>>> for c in parse_string(config, args='-x c++ -std=c++11'.split()).cursor.walk_preorder():
...     if m(c):
...         print(c.spelling)
x
y
glud.matchers.pointee(inner)[source]

Traverse from a pointer to the dereferenced type

>>> from glud import *
>>> config = '''
...  int *x;
...  void *y;
... '''
>>> m = varDecl(hasType(pointerType(pointee(hasName('int')))))
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
x
glud.matchers.hasParameter(N, inner)[source]

Matches if the N-th parameter matches the inner matcher

>>> from glud import *
>>> config = '''
... void f(int x);
... void g(int y, int x);
... void h();
... '''
>>> m = functionDecl(hasParameter(1, hasName('x')))
>>> for c in walk(m, parse_string(config).cursor):
...     print(c.spelling)
g
Internal API

Internal implementation details that may be useful for building new matchers, or understanding the behavior of existing matchers. Nothing in this section is guaranteed to remain stable between releases.

glud.predicates.has_access(access)[source]

Test if a cursor has a access specifier

glud.predicates.has_storage_class(kind)[source]

Check if the cursor has a particular (eg/ static) storage class

glud.predicates.is_builtin(n)[source]

Test if a type is a simple types (integer, boolean, char, float)

glud.predicates.is_decl(c)[source]

Check if a cursor is a declaration

glud.predicates.is_definition(cursor)[source]

Test if a cursor refers to a definition

This occurs when the cursor has a definition, and shares the location of that definiton

glud.predicates.is_kind(kind)[source]

Test if a cursor or type is of a particular kind

glud.predicates.is_stmt(c)[source]

Check if a cursor is a statement

class glud.internal.Matcher[source]

Base class for matchers

class glud.internal.TrueMatcher[source]

Matcher that always returns true

class glud.internal.PredMatcher(pred)[source]
class glud.internal.UnlessMatcher(innerMatcher)[source]

Inverts the match of the children

class glud.internal.AllOfMatcher(*innerMatchers)[source]

Matches if all inner matchers match

matchNode(cursor)[source]
class glud.internal.AllOfTypeMatcher(*args)[source]
class glud.internal.AnyOfMatcher(*args)[source]

Matches if any of the inner matchers match

matchNode(cursor)[source]
class glud.internal.ChildAnyOfMatcher(*args)[source]
children(cursor)[source]
class glud.internal.AnyBaseClassMatcher(*args)[source]
traverse(cursor)[source]
class glud.internal.NameMatcher(pattern)[source]
name(cursor)[source]
class glud.internal.TypenameMatcher(pattern)[source]
name(cursor)[source]
class glud.internal.TypeTraversalMatcher(innerMatcher)[source]
traverse(cursor)[source]
class glud.internal.ReturnTypeTraversalMatcher(matcher)[source]
traverse(cursor)[source]
class glud.internal.AnyParameterMatcher(innerMatcher)[source]
matchNode(cursor)[source]
traverse(cursor)[source]
class glud.internal.AncestorMatcher(innerMatcher)[source]
matchNode(cursor)[source]
traverse(cursor)[source]
class glud.internal.LocationMatcher(pattern)[source]
class glud.internal.ParentMatcher(innerMatcher)[source]
matchNode(cursor)[source]
traverse(cursor)[source]
class glud.internal.ParameterMatcher(N, innerMatcher)[source]
matchNode(cursor)[source]
class glud.internal.ParameterCountMatcher(N)[source]
class glud.internal.CanonicalTypeTraversalMatcher(matcher)[source]
traverse(t)[source]
class glud.internal.PointeeTypeTraversalMatcher(innerMatcher)[source]
traverse(t)[source]

Changelog

Release History
0.4.0 (2016-07-23)

New Features

  • Added new matchers - parameterCountIs, isExpansionInFileMatching, varDecl, hasParent, hasParameter, hasCanonicalType, pointee and pointerType

Improvements

  • Improved behavior on older versions of libclang
  • Moved existing code from the internal API into the stable interface (isPublic, isProtected, isPrivate)

Bug Fixes

  • Corrected the behavior of recordDecl and cxxRecordDecl to more closely map to the libclangastmatchers vision of those matchers
  • Corrected the name of anyArgument to hasAnyParameter