As discussed in PR #142353, the current testsuite of the `clang` Python bindings has several issues: - If `libclang.so` cannot be loaded into `python` to run the testsuite, the whole `ninja check-all` aborts. - The result of running the testsuite isn't report like the `lit`-based tests, rendering them almost invisible. - The testsuite is disabled in a non-obvious way (`RUN_PYTHON_TESTS`) in `tests/CMakeLists.txt`, which again doesn't show up in the test results. All these issues can be avoided by integrating the Python bindings tests with `lit`, which is what this patch does: - The actual test lives in `clang/test/bindings/python/bindings.sh` and is run by `lit`. - The current `clang/bindings/python/tests` directory (minus the now-superfluous `CMakeLists.txt`) is moved into the same directory. - The check if `libclang` is loadable (originally from PR #142353) is now handled via a new `lit` feature, `libclang-loadable`. - The various ways to disable the tests have been turned into `XFAIL`s as appropriate. - AArch64 doesn't `FAIL` any longer, so no `XFAIL` is necessary. - It keeps the `check-clang-python` target for use by the Clang Python CI. Tested on `sparc-sun-solaris2.11`, `sparcv9-sun-solaris2.11`, `i386-pc-solaris2.11`, `amd64-pc-solaris2.11`, `i686-pc-linux-gnu`, and `x86_64-pc-linux-gnu`.
1067 lines
34 KiB
Python
1067 lines
34 KiB
Python
import os
|
|
|
|
from clang.cindex import (
|
|
AvailabilityKind,
|
|
BinaryOperator,
|
|
Config,
|
|
Cursor,
|
|
CursorKind,
|
|
PrintingPolicy,
|
|
PrintingPolicyProperty,
|
|
StorageClass,
|
|
TemplateArgumentKind,
|
|
TranslationUnit,
|
|
TypeKind,
|
|
conf,
|
|
)
|
|
|
|
if "CLANG_LIBRARY_PATH" in os.environ:
|
|
Config.set_library_path(os.environ["CLANG_LIBRARY_PATH"])
|
|
|
|
import gc
|
|
import unittest
|
|
|
|
from .util import get_cursor, get_cursors, get_tu
|
|
|
|
CHILDREN_TEST = """\
|
|
struct s0 {
|
|
int a;
|
|
int b;
|
|
};
|
|
|
|
struct s1;
|
|
|
|
void f0(int a0, int a1) {
|
|
int l0, l1;
|
|
|
|
if (a0)
|
|
return;
|
|
|
|
for (;;) {
|
|
break;
|
|
}
|
|
}
|
|
"""
|
|
|
|
PARENT_TEST = """\
|
|
class C {
|
|
void f();
|
|
}
|
|
|
|
void C::f() { }
|
|
"""
|
|
|
|
TEMPLATE_ARG_TEST = """\
|
|
template <int kInt, typename T, bool kBool>
|
|
void foo();
|
|
|
|
template<>
|
|
void foo<-7, float, true>();
|
|
"""
|
|
|
|
BINOPS = """\
|
|
struct C {
|
|
int m;
|
|
};
|
|
|
|
void func(void){
|
|
int a, b;
|
|
int C::* p = &C::
|
|
|
|
C c;
|
|
c.*p;
|
|
|
|
C* pc;
|
|
pc->*p;
|
|
|
|
a * b;
|
|
a / b;
|
|
a % b;
|
|
a + b;
|
|
a - b;
|
|
|
|
a << b;
|
|
a >> b;
|
|
|
|
a < b;
|
|
a > b;
|
|
|
|
a <= b;
|
|
a >= b;
|
|
a == b;
|
|
a != b;
|
|
|
|
a & b;
|
|
a ^ b;
|
|
a | b;
|
|
|
|
a && b;
|
|
a || b;
|
|
|
|
a = b;
|
|
|
|
a *= b;
|
|
a /= b;
|
|
a %= b;
|
|
a += b;
|
|
a -= b;
|
|
|
|
a <<= b;
|
|
a >>= b;
|
|
|
|
a &= b;
|
|
a ^= b;
|
|
a |= b;
|
|
a , b;
|
|
|
|
}
|
|
"""
|
|
|
|
|
|
class TestCursor(unittest.TestCase):
|
|
def test_get_children(self):
|
|
tu = get_tu(CHILDREN_TEST)
|
|
|
|
it = tu.cursor.get_children()
|
|
tu_nodes = list(it)
|
|
|
|
self.assertEqual(len(tu_nodes), 3)
|
|
for cursor in tu_nodes:
|
|
self.assertIsNotNone(cursor.translation_unit)
|
|
|
|
self.assertNotEqual(tu_nodes[0], tu_nodes[1])
|
|
self.assertEqual(tu_nodes[0].kind, CursorKind.STRUCT_DECL)
|
|
self.assertEqual(tu_nodes[0].spelling, "s0")
|
|
self.assertEqual(tu_nodes[0].is_definition(), True)
|
|
self.assertEqual(tu_nodes[0].location.file.name, "t.c")
|
|
self.assertEqual(tu_nodes[0].location.line, 1)
|
|
self.assertEqual(tu_nodes[0].location.column, 8)
|
|
self.assertGreater(tu_nodes[0].hash, 0)
|
|
self.assertIsNotNone(tu_nodes[0].translation_unit)
|
|
|
|
s0_nodes = list(tu_nodes[0].get_children())
|
|
self.assertEqual(len(s0_nodes), 2)
|
|
self.assertEqual(s0_nodes[0].kind, CursorKind.FIELD_DECL)
|
|
self.assertEqual(s0_nodes[0].spelling, "a")
|
|
self.assertEqual(s0_nodes[0].type.kind, TypeKind.INT)
|
|
self.assertEqual(s0_nodes[1].kind, CursorKind.FIELD_DECL)
|
|
self.assertEqual(s0_nodes[1].spelling, "b")
|
|
self.assertEqual(s0_nodes[1].type.kind, TypeKind.INT)
|
|
|
|
self.assertEqual(tu_nodes[1].kind, CursorKind.STRUCT_DECL)
|
|
self.assertEqual(tu_nodes[1].spelling, "s1")
|
|
self.assertEqual(tu_nodes[1].displayname, "s1")
|
|
self.assertEqual(tu_nodes[1].is_definition(), False)
|
|
|
|
self.assertEqual(tu_nodes[2].kind, CursorKind.FUNCTION_DECL)
|
|
self.assertEqual(tu_nodes[2].spelling, "f0")
|
|
self.assertEqual(tu_nodes[2].displayname, "f0(int, int)")
|
|
self.assertEqual(tu_nodes[2].is_definition(), True)
|
|
|
|
def test_references(self):
|
|
"""Ensure that references to TranslationUnit are kept."""
|
|
tu = get_tu("int x;")
|
|
cursors = list(tu.cursor.get_children())
|
|
self.assertGreater(len(cursors), 0)
|
|
|
|
cursor = cursors[0]
|
|
self.assertIsInstance(cursor.translation_unit, TranslationUnit)
|
|
|
|
# Delete reference to TU and perform a full GC.
|
|
del tu
|
|
gc.collect()
|
|
self.assertIsInstance(cursor.translation_unit, TranslationUnit)
|
|
|
|
# If the TU was destroyed, this should cause a segfault.
|
|
cursor.semantic_parent
|
|
|
|
def test_canonical(self):
|
|
source = "struct X; struct X; struct X { int member; };"
|
|
tu = get_tu(source)
|
|
|
|
cursors = []
|
|
for cursor in tu.cursor.get_children():
|
|
if cursor.spelling == "X":
|
|
cursors.append(cursor)
|
|
|
|
self.assertEqual(len(cursors), 3)
|
|
self.assertEqual(cursors[1].canonical, cursors[2].canonical)
|
|
|
|
def test_is_const_method(self):
|
|
"""Ensure Cursor.is_const_method works."""
|
|
source = "class X { void foo() const; void bar(); };"
|
|
tu = get_tu(source, lang="cpp")
|
|
|
|
cls = get_cursor(tu, "X")
|
|
foo = get_cursor(tu, "foo")
|
|
bar = get_cursor(tu, "bar")
|
|
self.assertIsNotNone(cls)
|
|
self.assertIsNotNone(foo)
|
|
self.assertIsNotNone(bar)
|
|
|
|
self.assertTrue(foo.is_const_method())
|
|
self.assertFalse(bar.is_const_method())
|
|
|
|
def test_is_converting_constructor(self):
|
|
"""Ensure Cursor.is_converting_constructor works."""
|
|
source = "class X { explicit X(int); X(double); X(); };"
|
|
tu = get_tu(source, lang="cpp")
|
|
|
|
xs = get_cursors(tu, "X")
|
|
|
|
self.assertEqual(len(xs), 4)
|
|
self.assertEqual(xs[0].kind, CursorKind.CLASS_DECL)
|
|
cs = xs[1:]
|
|
self.assertEqual(cs[0].kind, CursorKind.CONSTRUCTOR)
|
|
self.assertEqual(cs[1].kind, CursorKind.CONSTRUCTOR)
|
|
self.assertEqual(cs[2].kind, CursorKind.CONSTRUCTOR)
|
|
|
|
self.assertFalse(cs[0].is_converting_constructor())
|
|
self.assertTrue(cs[1].is_converting_constructor())
|
|
self.assertFalse(cs[2].is_converting_constructor())
|
|
|
|
def test_is_copy_constructor(self):
|
|
"""Ensure Cursor.is_copy_constructor works."""
|
|
source = "class X { X(); X(const X&); X(X&&); };"
|
|
tu = get_tu(source, lang="cpp")
|
|
|
|
xs = get_cursors(tu, "X")
|
|
self.assertEqual(xs[0].kind, CursorKind.CLASS_DECL)
|
|
cs = xs[1:]
|
|
self.assertEqual(cs[0].kind, CursorKind.CONSTRUCTOR)
|
|
self.assertEqual(cs[1].kind, CursorKind.CONSTRUCTOR)
|
|
self.assertEqual(cs[2].kind, CursorKind.CONSTRUCTOR)
|
|
|
|
self.assertFalse(cs[0].is_copy_constructor())
|
|
self.assertTrue(cs[1].is_copy_constructor())
|
|
self.assertFalse(cs[2].is_copy_constructor())
|
|
|
|
def test_is_default_constructor(self):
|
|
"""Ensure Cursor.is_default_constructor works."""
|
|
source = "class X { X(); X(int); };"
|
|
tu = get_tu(source, lang="cpp")
|
|
|
|
xs = get_cursors(tu, "X")
|
|
self.assertEqual(xs[0].kind, CursorKind.CLASS_DECL)
|
|
cs = xs[1:]
|
|
self.assertEqual(cs[0].kind, CursorKind.CONSTRUCTOR)
|
|
self.assertEqual(cs[1].kind, CursorKind.CONSTRUCTOR)
|
|
|
|
self.assertTrue(cs[0].is_default_constructor())
|
|
self.assertFalse(cs[1].is_default_constructor())
|
|
|
|
def test_is_move_constructor(self):
|
|
"""Ensure Cursor.is_move_constructor works."""
|
|
source = "class X { X(); X(const X&); X(X&&); };"
|
|
tu = get_tu(source, lang="cpp")
|
|
|
|
xs = get_cursors(tu, "X")
|
|
self.assertEqual(xs[0].kind, CursorKind.CLASS_DECL)
|
|
cs = xs[1:]
|
|
self.assertEqual(cs[0].kind, CursorKind.CONSTRUCTOR)
|
|
self.assertEqual(cs[1].kind, CursorKind.CONSTRUCTOR)
|
|
self.assertEqual(cs[2].kind, CursorKind.CONSTRUCTOR)
|
|
|
|
self.assertFalse(cs[0].is_move_constructor())
|
|
self.assertFalse(cs[1].is_move_constructor())
|
|
self.assertTrue(cs[2].is_move_constructor())
|
|
|
|
def test_is_default_method(self):
|
|
"""Ensure Cursor.is_default_method works."""
|
|
source = "class X { X() = default; }; class Y { Y(); };"
|
|
tu = get_tu(source, lang="cpp")
|
|
|
|
xs = get_cursors(tu, "X")
|
|
ys = get_cursors(tu, "Y")
|
|
|
|
self.assertEqual(len(xs), 2)
|
|
self.assertEqual(len(ys), 2)
|
|
|
|
xc = xs[1]
|
|
yc = ys[1]
|
|
|
|
self.assertTrue(xc.is_default_method())
|
|
self.assertFalse(yc.is_default_method())
|
|
|
|
def test_is_deleted_method(self):
|
|
source = "class X { X() = delete; }; class Y { Y(); };"
|
|
tu = get_tu(source, lang="cpp")
|
|
|
|
xs = get_cursors(tu, "X")
|
|
ys = get_cursors(tu, "Y")
|
|
|
|
self.assertEqual(len(xs), 2)
|
|
self.assertEqual(len(ys), 2)
|
|
|
|
xc = xs[1]
|
|
yc = ys[1]
|
|
|
|
self.assertTrue(xc.is_deleted_method())
|
|
self.assertFalse(yc.is_deleted_method())
|
|
|
|
def test_is_copy_assignment_operator_method(self):
|
|
source_with_copy_assignment_operators = """
|
|
struct Foo {
|
|
// Those are copy-assignment operators
|
|
bool operator=(const Foo&);
|
|
bool operator=(Foo&);
|
|
Foo operator=(Foo);
|
|
bool operator=(volatile Foo&);
|
|
bool operator=(const volatile Foo&);
|
|
|
|
// Positive-check that the recognition works for templated classes too
|
|
template <typename T>
|
|
class Bar {
|
|
bool operator=(const Bar&);
|
|
Bar operator=(const Bar);
|
|
bool operator=(Bar<T>&);
|
|
bool operator=(volatile Bar&);
|
|
bool operator=(const volatile Bar<T>&);
|
|
};
|
|
"""
|
|
source_without_copy_assignment_operators = """
|
|
struct Foo {
|
|
// Those are not copy-assignment operators
|
|
template<typename T>
|
|
bool operator=(const T&);
|
|
bool operator=(const bool&);
|
|
bool operator=(char&);
|
|
bool operator=(volatile unsigned int&);
|
|
bool operator=(const volatile unsigned char&);
|
|
bool operator=(int);
|
|
bool operator=(Foo&&);
|
|
};
|
|
"""
|
|
tu_with_copy_assignment_operators = get_tu(
|
|
source_with_copy_assignment_operators, lang="cpp"
|
|
)
|
|
tu_without_copy_assignment_operators = get_tu(
|
|
source_without_copy_assignment_operators, lang="cpp"
|
|
)
|
|
|
|
copy_assignment_operators_cursors = get_cursors(
|
|
tu_with_copy_assignment_operators, "operator="
|
|
)
|
|
non_copy_assignment_operators_cursors = get_cursors(
|
|
tu_without_copy_assignment_operators, "operator="
|
|
)
|
|
|
|
self.assertEqual(len(copy_assignment_operators_cursors), 10)
|
|
self.assertEqual(len(non_copy_assignment_operators_cursors), 7)
|
|
|
|
self.assertTrue(
|
|
all(
|
|
[
|
|
cursor.is_copy_assignment_operator_method()
|
|
for cursor in copy_assignment_operators_cursors
|
|
]
|
|
)
|
|
)
|
|
|
|
self.assertFalse(
|
|
any(
|
|
[
|
|
cursor.is_copy_assignment_operator_method()
|
|
for cursor in non_copy_assignment_operators_cursors
|
|
]
|
|
)
|
|
)
|
|
|
|
def test_is_move_assignment_operator_method(self):
|
|
"""Ensure Cursor.is_move_assignment_operator_method works."""
|
|
source_with_move_assignment_operators = """
|
|
struct Foo {
|
|
// Those are move-assignment operators
|
|
bool operator=(const Foo&&);
|
|
bool operator=(Foo&&);
|
|
bool operator=(volatile Foo&&);
|
|
bool operator=(const volatile Foo&&);
|
|
|
|
// Positive-check that the recognition works for templated classes too
|
|
template <typename T>
|
|
class Bar {
|
|
bool operator=(const Bar&&);
|
|
bool operator=(Bar<T>&&);
|
|
bool operator=(volatile Bar&&);
|
|
bool operator=(const volatile Bar<T>&&);
|
|
};
|
|
"""
|
|
source_without_move_assignment_operators = """
|
|
struct Foo {
|
|
// Those are not move-assignment operators
|
|
template<typename T>
|
|
bool operator=(const T&&);
|
|
bool operator=(const bool&&);
|
|
bool operator=(char&&);
|
|
bool operator=(volatile unsigned int&&);
|
|
bool operator=(const volatile unsigned char&&);
|
|
bool operator=(int);
|
|
bool operator=(Foo);
|
|
};
|
|
"""
|
|
tu_with_move_assignment_operators = get_tu(
|
|
source_with_move_assignment_operators, lang="cpp"
|
|
)
|
|
tu_without_move_assignment_operators = get_tu(
|
|
source_without_move_assignment_operators, lang="cpp"
|
|
)
|
|
|
|
move_assignment_operators_cursors = get_cursors(
|
|
tu_with_move_assignment_operators, "operator="
|
|
)
|
|
non_move_assignment_operators_cursors = get_cursors(
|
|
tu_without_move_assignment_operators, "operator="
|
|
)
|
|
|
|
self.assertEqual(len(move_assignment_operators_cursors), 8)
|
|
self.assertTrue(len(non_move_assignment_operators_cursors), 7)
|
|
|
|
self.assertTrue(
|
|
all(
|
|
[
|
|
cursor.is_move_assignment_operator_method()
|
|
for cursor in move_assignment_operators_cursors
|
|
]
|
|
)
|
|
)
|
|
self.assertFalse(
|
|
any(
|
|
[
|
|
cursor.is_move_assignment_operator_method()
|
|
for cursor in non_move_assignment_operators_cursors
|
|
]
|
|
)
|
|
)
|
|
|
|
def test_is_explicit_method(self):
|
|
"""Ensure Cursor.is_explicit_method works."""
|
|
source_with_explicit_methods = """
|
|
struct Foo {
|
|
// Those are explicit
|
|
explicit Foo(double);
|
|
explicit(true) Foo(char);
|
|
explicit operator double();
|
|
explicit(true) operator char();
|
|
};
|
|
"""
|
|
source_without_explicit_methods = """
|
|
struct Foo {
|
|
// Those are not explicit
|
|
Foo(int);
|
|
explicit(false) Foo(float);
|
|
operator int();
|
|
explicit(false) operator float();
|
|
};
|
|
"""
|
|
tu_with_explicit_methods = get_tu(source_with_explicit_methods, lang="cpp")
|
|
tu_without_explicit_methods = get_tu(
|
|
source_without_explicit_methods, lang="cpp"
|
|
)
|
|
|
|
explicit_methods_cursors = [
|
|
*get_cursors(tu_with_explicit_methods, "Foo")[1:],
|
|
get_cursor(tu_with_explicit_methods, "operator double"),
|
|
get_cursor(tu_with_explicit_methods, "operator char"),
|
|
]
|
|
|
|
non_explicit_methods_cursors = [
|
|
*get_cursors(tu_without_explicit_methods, "Foo")[1:],
|
|
get_cursor(tu_without_explicit_methods, "operator int"),
|
|
get_cursor(tu_without_explicit_methods, "operator float"),
|
|
]
|
|
|
|
self.assertEqual(len(explicit_methods_cursors), 4)
|
|
self.assertTrue(len(non_explicit_methods_cursors), 4)
|
|
|
|
self.assertTrue(
|
|
all([cursor.is_explicit_method() for cursor in explicit_methods_cursors])
|
|
)
|
|
self.assertFalse(
|
|
any(
|
|
[cursor.is_explicit_method() for cursor in non_explicit_methods_cursors]
|
|
)
|
|
)
|
|
|
|
def test_is_mutable_field(self):
|
|
"""Ensure Cursor.is_mutable_field works."""
|
|
source = "class X { int x_; mutable int y_; };"
|
|
tu = get_tu(source, lang="cpp")
|
|
|
|
cls = get_cursor(tu, "X")
|
|
x_ = get_cursor(tu, "x_")
|
|
y_ = get_cursor(tu, "y_")
|
|
self.assertIsNotNone(cls)
|
|
self.assertIsNotNone(x_)
|
|
self.assertIsNotNone(y_)
|
|
|
|
self.assertFalse(x_.is_mutable_field())
|
|
self.assertTrue(y_.is_mutable_field())
|
|
|
|
def test_is_static_method(self):
|
|
"""Ensure Cursor.is_static_method works."""
|
|
|
|
source = "class X { static void foo(); void bar(); };"
|
|
tu = get_tu(source, lang="cpp")
|
|
|
|
cls = get_cursor(tu, "X")
|
|
foo = get_cursor(tu, "foo")
|
|
bar = get_cursor(tu, "bar")
|
|
self.assertIsNotNone(cls)
|
|
self.assertIsNotNone(foo)
|
|
self.assertIsNotNone(bar)
|
|
|
|
self.assertTrue(foo.is_static_method())
|
|
self.assertFalse(bar.is_static_method())
|
|
|
|
def test_is_pure_virtual_method(self):
|
|
"""Ensure Cursor.is_pure_virtual_method works."""
|
|
source = "class X { virtual void foo() = 0; virtual void bar(); };"
|
|
tu = get_tu(source, lang="cpp")
|
|
|
|
cls = get_cursor(tu, "X")
|
|
foo = get_cursor(tu, "foo")
|
|
bar = get_cursor(tu, "bar")
|
|
self.assertIsNotNone(cls)
|
|
self.assertIsNotNone(foo)
|
|
self.assertIsNotNone(bar)
|
|
|
|
self.assertTrue(foo.is_pure_virtual_method())
|
|
self.assertFalse(bar.is_pure_virtual_method())
|
|
|
|
def test_is_virtual_method(self):
|
|
"""Ensure Cursor.is_virtual_method works."""
|
|
source = "class X { virtual void foo(); void bar(); };"
|
|
tu = get_tu(source, lang="cpp")
|
|
|
|
cls = get_cursor(tu, "X")
|
|
foo = get_cursor(tu, "foo")
|
|
bar = get_cursor(tu, "bar")
|
|
self.assertIsNotNone(cls)
|
|
self.assertIsNotNone(foo)
|
|
self.assertIsNotNone(bar)
|
|
|
|
self.assertTrue(foo.is_virtual_method())
|
|
self.assertFalse(bar.is_virtual_method())
|
|
|
|
def test_is_abstract_record(self):
|
|
"""Ensure Cursor.is_abstract_record works."""
|
|
source = "struct X { virtual void x() = 0; }; struct Y : X { void x(); };"
|
|
tu = get_tu(source, lang="cpp")
|
|
|
|
cls = get_cursor(tu, "X")
|
|
self.assertTrue(cls.is_abstract_record())
|
|
|
|
cls = get_cursor(tu, "Y")
|
|
self.assertFalse(cls.is_abstract_record())
|
|
|
|
def test_is_scoped_enum(self):
|
|
"""Ensure Cursor.is_scoped_enum works."""
|
|
source = "class X {}; enum RegularEnum {}; enum class ScopedEnum {};"
|
|
tu = get_tu(source, lang="cpp")
|
|
|
|
cls = get_cursor(tu, "X")
|
|
regular_enum = get_cursor(tu, "RegularEnum")
|
|
scoped_enum = get_cursor(tu, "ScopedEnum")
|
|
self.assertIsNotNone(cls)
|
|
self.assertIsNotNone(regular_enum)
|
|
self.assertIsNotNone(scoped_enum)
|
|
|
|
self.assertFalse(cls.is_scoped_enum())
|
|
self.assertFalse(regular_enum.is_scoped_enum())
|
|
self.assertTrue(scoped_enum.is_scoped_enum())
|
|
|
|
def test_get_definition(self):
|
|
"""Ensure Cursor.get_definition works."""
|
|
tu = get_tu(
|
|
"""
|
|
class A {
|
|
constexpr static int f(){return 3;}
|
|
};
|
|
struct B {
|
|
int b = A::f();
|
|
};
|
|
""",
|
|
lang="cpp",
|
|
)
|
|
curs = get_cursors(tu, "f")
|
|
self.assertEqual(len(curs), 4)
|
|
self.assertEqual(curs[0].kind, CursorKind.CXX_METHOD)
|
|
self.assertEqual(curs[1].get_definition(), curs[0])
|
|
self.assertEqual(curs[2].get_definition(), curs[0])
|
|
self.assertEqual(curs[3].get_definition(), curs[0])
|
|
|
|
def test_get_usr(self):
|
|
"""Ensure Cursor.get_usr works."""
|
|
tu = get_tu(
|
|
"""
|
|
int add(int, int);
|
|
int add(int a, int b) { return a + b; }
|
|
int add(float a, float b) { return a + b; }
|
|
""",
|
|
lang="cpp",
|
|
)
|
|
curs = get_cursors(tu, "add")
|
|
self.assertEqual(len(curs), 3)
|
|
self.assertEqual(curs[0].get_usr(), curs[1].get_usr())
|
|
self.assertNotEqual(curs[0].get_usr(), curs[2].get_usr())
|
|
|
|
def test_underlying_type(self):
|
|
tu = get_tu("typedef int foo;")
|
|
typedef = get_cursor(tu, "foo")
|
|
self.assertIsNotNone(typedef)
|
|
|
|
self.assertTrue(typedef.kind.is_declaration())
|
|
underlying = typedef.underlying_typedef_type
|
|
self.assertEqual(underlying.kind, TypeKind.INT)
|
|
|
|
def test_semantic_parent(self):
|
|
tu = get_tu(PARENT_TEST, "cpp")
|
|
curs = get_cursors(tu, "f")
|
|
decl = get_cursor(tu, "C")
|
|
self.assertEqual(len(curs), 2)
|
|
self.assertEqual(curs[0].semantic_parent, curs[1].semantic_parent)
|
|
self.assertEqual(curs[0].semantic_parent, decl)
|
|
|
|
def test_lexical_parent(self):
|
|
tu = get_tu(PARENT_TEST, "cpp")
|
|
curs = get_cursors(tu, "f")
|
|
decl = get_cursor(tu, "C")
|
|
self.assertEqual(len(curs), 2)
|
|
self.assertNotEqual(curs[0].lexical_parent, curs[1].lexical_parent)
|
|
self.assertEqual(curs[0].lexical_parent, decl)
|
|
self.assertEqual(curs[1].lexical_parent, tu.cursor)
|
|
|
|
def test_enum_type(self):
|
|
tu = get_tu("enum TEST { FOO=1, BAR=2 };")
|
|
enum = get_cursor(tu, "TEST")
|
|
self.assertIsNotNone(enum)
|
|
|
|
self.assertEqual(enum.kind, CursorKind.ENUM_DECL)
|
|
enum_type = enum.enum_type
|
|
self.assertIn(enum_type.kind, (TypeKind.UINT, TypeKind.INT))
|
|
|
|
def test_enum_type_cpp(self):
|
|
tu = get_tu("enum TEST : long long { FOO=1, BAR=2 };", lang="cpp")
|
|
enum = get_cursor(tu, "TEST")
|
|
self.assertIsNotNone(enum)
|
|
|
|
self.assertEqual(enum.kind, CursorKind.ENUM_DECL)
|
|
self.assertEqual(enum.enum_type.kind, TypeKind.LONGLONG)
|
|
|
|
def test_objc_type_encoding(self):
|
|
tu = get_tu("int i;", lang="objc")
|
|
i = get_cursor(tu, "i")
|
|
|
|
self.assertIsNotNone(i)
|
|
self.assertEqual(i.objc_type_encoding, "i")
|
|
|
|
def test_enum_values(self):
|
|
tu = get_tu("enum TEST { SPAM=1, EGG, HAM = EGG * 20};")
|
|
enum = get_cursor(tu, "TEST")
|
|
self.assertIsNotNone(enum)
|
|
|
|
self.assertEqual(enum.kind, CursorKind.ENUM_DECL)
|
|
|
|
enum_constants = list(enum.get_children())
|
|
self.assertEqual(len(enum_constants), 3)
|
|
|
|
spam, egg, ham = enum_constants
|
|
|
|
self.assertEqual(spam.kind, CursorKind.ENUM_CONSTANT_DECL)
|
|
self.assertEqual(spam.enum_value, 1)
|
|
self.assertEqual(egg.kind, CursorKind.ENUM_CONSTANT_DECL)
|
|
self.assertEqual(egg.enum_value, 2)
|
|
self.assertEqual(ham.kind, CursorKind.ENUM_CONSTANT_DECL)
|
|
self.assertEqual(ham.enum_value, 40)
|
|
|
|
def test_enum_values_cpp(self):
|
|
tu = get_tu(
|
|
"enum TEST : long long { SPAM = -1, HAM = 0x10000000000};", lang="cpp"
|
|
)
|
|
enum = get_cursor(tu, "TEST")
|
|
self.assertIsNotNone(enum)
|
|
|
|
self.assertEqual(enum.kind, CursorKind.ENUM_DECL)
|
|
|
|
enum_constants = list(enum.get_children())
|
|
self.assertEqual(len(enum_constants), 2)
|
|
|
|
spam, ham = enum_constants
|
|
|
|
self.assertEqual(spam.kind, CursorKind.ENUM_CONSTANT_DECL)
|
|
self.assertEqual(spam.enum_value, -1)
|
|
self.assertEqual(ham.kind, CursorKind.ENUM_CONSTANT_DECL)
|
|
self.assertEqual(ham.enum_value, 0x10000000000)
|
|
|
|
def test_enum_values_unsigned(self):
|
|
tu = get_tu("enum TEST : unsigned char { SPAM=0, HAM = 200};", lang="cpp")
|
|
enum = get_cursor(tu, "TEST")
|
|
self.assertIsNotNone(enum)
|
|
|
|
self.assertEqual(enum.kind, CursorKind.ENUM_DECL)
|
|
|
|
enum_constants = list(enum.get_children())
|
|
self.assertEqual(len(enum_constants), 2)
|
|
|
|
spam, ham = enum_constants
|
|
|
|
self.assertEqual(spam.kind, CursorKind.ENUM_CONSTANT_DECL)
|
|
self.assertEqual(spam.enum_value, 0)
|
|
self.assertEqual(ham.kind, CursorKind.ENUM_CONSTANT_DECL)
|
|
self.assertEqual(ham.enum_value, 200)
|
|
|
|
def test_annotation_attribute(self):
|
|
tu = get_tu(
|
|
'int foo (void) __attribute__ ((annotate("here be annotation attribute")));'
|
|
)
|
|
|
|
foo = get_cursor(tu, "foo")
|
|
self.assertIsNotNone(foo)
|
|
|
|
for c in foo.get_children():
|
|
if c.kind == CursorKind.ANNOTATE_ATTR:
|
|
self.assertEqual(c.displayname, "here be annotation attribute")
|
|
break
|
|
else:
|
|
self.fail("Couldn't find annotation")
|
|
|
|
def test_annotation_template(self):
|
|
annotation = '__attribute__ ((annotate("annotation")))'
|
|
for source, kind in [
|
|
("int foo (T value) %s;", CursorKind.FUNCTION_TEMPLATE),
|
|
("class %s foo {};", CursorKind.CLASS_TEMPLATE),
|
|
]:
|
|
source = "template<typename T> " + (source % annotation)
|
|
tu = get_tu(source, lang="cpp")
|
|
|
|
foo = get_cursor(tu, "foo")
|
|
self.assertIsNotNone(foo)
|
|
self.assertEqual(foo.kind, kind)
|
|
|
|
for c in foo.get_children():
|
|
if c.kind == CursorKind.ANNOTATE_ATTR:
|
|
self.assertEqual(c.displayname, "annotation")
|
|
break
|
|
else:
|
|
self.fail("Couldn't find annotation for {}".format(kind))
|
|
|
|
def test_result_type(self):
|
|
tu = get_tu("int foo();")
|
|
foo = get_cursor(tu, "foo")
|
|
|
|
self.assertIsNotNone(foo)
|
|
t = foo.result_type
|
|
self.assertEqual(t.kind, TypeKind.INT)
|
|
|
|
def test_result_type_objc_method_decl(self):
|
|
code = """\
|
|
@interface Interface : NSObject
|
|
-(void)voidMethod;
|
|
@end
|
|
"""
|
|
tu = get_tu(code, lang="objc")
|
|
cursor = get_cursor(tu, "voidMethod")
|
|
result_type = cursor.result_type
|
|
self.assertEqual(cursor.kind, CursorKind.OBJC_INSTANCE_METHOD_DECL)
|
|
self.assertEqual(result_type.kind, TypeKind.VOID)
|
|
|
|
def test_storage_class(self):
|
|
tu = get_tu(
|
|
"""
|
|
extern int ex;
|
|
register int reg;
|
|
int count(int a, int b){
|
|
static int counter = 0;
|
|
return 0;
|
|
}
|
|
""",
|
|
lang="cpp",
|
|
)
|
|
cursor = get_cursor(tu, "ex")
|
|
self.assertEqual(cursor.storage_class, StorageClass.EXTERN)
|
|
cursor = get_cursor(tu, "counter")
|
|
self.assertEqual(cursor.storage_class, StorageClass.STATIC)
|
|
cursor = get_cursor(tu, "reg")
|
|
self.assertEqual(cursor.storage_class, StorageClass.REGISTER)
|
|
|
|
def test_availability(self):
|
|
tu = get_tu("class A { A(A const&) = delete; };", lang="cpp")
|
|
|
|
# AvailabilityKind.AVAILABLE
|
|
cursor = get_cursor(tu, "A")
|
|
self.assertEqual(cursor.kind, CursorKind.CLASS_DECL)
|
|
self.assertEqual(cursor.availability, AvailabilityKind.AVAILABLE)
|
|
|
|
# AvailabilityKind.NOT_AVAILABLE
|
|
cursors = get_cursors(tu, "A")
|
|
for c in cursors:
|
|
if c.kind == CursorKind.CONSTRUCTOR:
|
|
self.assertEqual(c.availability, AvailabilityKind.NOT_AVAILABLE)
|
|
break
|
|
else:
|
|
self.fail("Could not find cursor for deleted constructor")
|
|
|
|
# AvailabilityKind.DEPRECATED
|
|
tu = get_tu("void test() __attribute__((deprecated));", lang="cpp")
|
|
cursor = get_cursor(tu, "test")
|
|
self.assertEqual(cursor.availability, AvailabilityKind.DEPRECATED)
|
|
|
|
# AvailabilityKind.NOT_ACCESSIBLE is only used in the code completion results
|
|
|
|
def test_get_tokens(self):
|
|
"""Ensure we can map cursors back to tokens."""
|
|
tu = get_tu("int foo(int i);")
|
|
foo = get_cursor(tu, "foo")
|
|
|
|
tokens = list(foo.get_tokens())
|
|
self.assertEqual(len(tokens), 6)
|
|
self.assertEqual(tokens[0].spelling, "int")
|
|
self.assertEqual(tokens[1].spelling, "foo")
|
|
|
|
def test_get_token_cursor(self):
|
|
"""Ensure we can map tokens to cursors."""
|
|
tu = get_tu("class A {}; int foo(A var = A());", lang="cpp")
|
|
foo = get_cursor(tu, "foo")
|
|
|
|
for cursor in foo.walk_preorder():
|
|
if cursor.kind.is_expression() and not cursor.kind.is_statement():
|
|
break
|
|
else:
|
|
self.fail("Could not find default value expression")
|
|
|
|
tokens = list(cursor.get_tokens())
|
|
self.assertEqual(len(tokens), 4, [t.spelling for t in tokens])
|
|
self.assertEqual(tokens[0].spelling, "=")
|
|
self.assertEqual(tokens[1].spelling, "A")
|
|
self.assertEqual(tokens[2].spelling, "(")
|
|
self.assertEqual(tokens[3].spelling, ")")
|
|
t_cursor = tokens[1].cursor
|
|
self.assertEqual(t_cursor.kind, CursorKind.TYPE_REF)
|
|
r_cursor = t_cursor.referenced # should not raise an exception
|
|
self.assertEqual(r_cursor.kind, CursorKind.CLASS_DECL)
|
|
|
|
def test_get_field_offsetof(self):
|
|
tu = get_tu(
|
|
"struct myStruct {int a; char b; char c; short d; char e;};", lang="cpp"
|
|
)
|
|
c1 = get_cursor(tu, "myStruct")
|
|
c2 = get_cursor(tu, "a")
|
|
c3 = get_cursor(tu, "b")
|
|
c4 = get_cursor(tu, "c")
|
|
c5 = get_cursor(tu, "d")
|
|
c6 = get_cursor(tu, "e")
|
|
self.assertEqual(c1.get_field_offsetof(), -1)
|
|
self.assertEqual(c2.get_field_offsetof(), 0)
|
|
self.assertEqual(c3.get_field_offsetof(), 32)
|
|
self.assertEqual(c4.get_field_offsetof(), 40)
|
|
self.assertEqual(c5.get_field_offsetof(), 48)
|
|
self.assertEqual(c6.get_field_offsetof(), 64)
|
|
|
|
def test_get_arguments(self):
|
|
tu = get_tu("void foo(int i, int j);")
|
|
foo = get_cursor(tu, "foo")
|
|
arguments = list(foo.get_arguments())
|
|
|
|
self.assertEqual(len(arguments), 2)
|
|
self.assertEqual(arguments[0].spelling, "i")
|
|
self.assertEqual(arguments[1].spelling, "j")
|
|
|
|
def test_get_num_template_arguments(self):
|
|
tu = get_tu(TEMPLATE_ARG_TEST, lang="cpp")
|
|
foos = get_cursors(tu, "foo")
|
|
|
|
self.assertEqual(foos[1].get_num_template_arguments(), 3)
|
|
|
|
def test_get_template_argument_kind(self):
|
|
tu = get_tu(TEMPLATE_ARG_TEST, lang="cpp")
|
|
foos = get_cursors(tu, "foo")
|
|
|
|
self.assertEqual(
|
|
foos[1].get_template_argument_kind(0), TemplateArgumentKind.INTEGRAL
|
|
)
|
|
self.assertEqual(
|
|
foos[1].get_template_argument_kind(1), TemplateArgumentKind.TYPE
|
|
)
|
|
self.assertEqual(
|
|
foos[1].get_template_argument_kind(2), TemplateArgumentKind.INTEGRAL
|
|
)
|
|
|
|
def test_get_template_argument_type(self):
|
|
tu = get_tu(TEMPLATE_ARG_TEST, lang="cpp")
|
|
foos = get_cursors(tu, "foo")
|
|
|
|
self.assertEqual(foos[1].get_template_argument_type(1).kind, TypeKind.FLOAT)
|
|
|
|
def test_get_template_argument_value(self):
|
|
tu = get_tu(TEMPLATE_ARG_TEST, lang="cpp")
|
|
foos = get_cursors(tu, "foo")
|
|
|
|
self.assertEqual(foos[1].get_template_argument_value(0), -7)
|
|
self.assertEqual(foos[1].get_template_argument_value(2), True)
|
|
|
|
def test_get_template_argument_unsigned_value(self):
|
|
tu = get_tu(TEMPLATE_ARG_TEST, lang="cpp")
|
|
foos = get_cursors(tu, "foo")
|
|
|
|
self.assertEqual(foos[1].get_template_argument_unsigned_value(0), 2**32 - 7)
|
|
self.assertEqual(foos[1].get_template_argument_unsigned_value(2), True)
|
|
|
|
def test_referenced(self):
|
|
tu = get_tu("void foo(); void bar() { foo(); }")
|
|
foo = get_cursor(tu, "foo")
|
|
bar = get_cursor(tu, "bar")
|
|
for c in bar.get_children():
|
|
if c.kind == CursorKind.CALL_EXPR:
|
|
self.assertEqual(c.referenced.spelling, foo.spelling)
|
|
break
|
|
|
|
def test_mangled_name(self):
|
|
kInputForMangling = """\
|
|
int foo(int, int);
|
|
"""
|
|
tu = get_tu(kInputForMangling, lang="cpp")
|
|
foo = get_cursor(tu, "foo")
|
|
|
|
# Since libclang does not link in targets, we cannot pass a triple to it
|
|
# and force the target. To enable this test to pass on all platforms, accept
|
|
# all valid manglings.
|
|
# [c-index-test handles this by running the source through clang, emitting
|
|
# an AST file and running libclang on that AST file]
|
|
self.assertIn(
|
|
foo.mangled_name, ("_Z3fooii", "__Z3fooii", "?foo@@YAHHH", "?foo@@YAHHH@Z")
|
|
)
|
|
|
|
def test_binop(self):
|
|
tu = get_tu(BINOPS, lang="cpp")
|
|
|
|
operators = {
|
|
# not exposed yet
|
|
# ".*" : BinaryOperator.PtrMemD,
|
|
"->*": BinaryOperator.PtrMemI,
|
|
"*": BinaryOperator.Mul,
|
|
"/": BinaryOperator.Div,
|
|
"%": BinaryOperator.Rem,
|
|
"+": BinaryOperator.Add,
|
|
"-": BinaryOperator.Sub,
|
|
"<<": BinaryOperator.Shl,
|
|
">>": BinaryOperator.Shr,
|
|
# tests do not run in C++2a mode so this operator is not available
|
|
# "<=>" : BinaryOperator.Cmp,
|
|
"<": BinaryOperator.LT,
|
|
">": BinaryOperator.GT,
|
|
"<=": BinaryOperator.LE,
|
|
">=": BinaryOperator.GE,
|
|
"==": BinaryOperator.EQ,
|
|
"!=": BinaryOperator.NE,
|
|
"&": BinaryOperator.And,
|
|
"^": BinaryOperator.Xor,
|
|
"|": BinaryOperator.Or,
|
|
"&&": BinaryOperator.LAnd,
|
|
"||": BinaryOperator.LOr,
|
|
"=": BinaryOperator.Assign,
|
|
"*=": BinaryOperator.MulAssign,
|
|
"/=": BinaryOperator.DivAssign,
|
|
"%=": BinaryOperator.RemAssign,
|
|
"+=": BinaryOperator.AddAssign,
|
|
"-=": BinaryOperator.SubAssign,
|
|
"<<=": BinaryOperator.ShlAssign,
|
|
">>=": BinaryOperator.ShrAssign,
|
|
"&=": BinaryOperator.AndAssign,
|
|
"^=": BinaryOperator.XorAssign,
|
|
"|=": BinaryOperator.OrAssign,
|
|
",": BinaryOperator.Comma,
|
|
}
|
|
|
|
for op, typ in operators.items():
|
|
c = get_cursor(tu, op)
|
|
assert c.binary_operator == typ
|
|
|
|
def test_from_result_null(self):
|
|
tu = get_tu("int a = 1+2;", lang="cpp")
|
|
op = next(next(tu.cursor.get_children()).get_children())
|
|
self.assertEqual(op.kind, CursorKind.BINARY_OPERATOR)
|
|
self.assertEqual(op.get_definition(), None)
|
|
|
|
def test_from_cursor_result_null(self):
|
|
tu = get_tu("")
|
|
self.assertEqual(tu.cursor.semantic_parent, None)
|
|
|
|
def test_pretty_print(self):
|
|
tu = get_tu("struct X { int x; }; void f(bool x) { }", lang="cpp")
|
|
f = get_cursor(tu, "f")
|
|
|
|
self.assertEqual(f.displayname, "f(bool)")
|
|
pp = PrintingPolicy.create(f)
|
|
self.assertEqual(pp.get_property(PrintingPolicyProperty.Bool), True)
|
|
self.assertEqual(f.pretty_printed(pp), "void f(bool x) {\n}\n")
|
|
pp.set_property(PrintingPolicyProperty.Bool, False)
|
|
self.assertEqual(pp.get_property(PrintingPolicyProperty.Bool), False)
|
|
self.assertEqual(f.pretty_printed(pp), "void f(_Bool x) {\n}\n")
|
|
|
|
def test_hash(self):
|
|
def accumulate_cursors(cursor: Cursor, all_cursors: list):
|
|
all_cursors.append(cursor)
|
|
for child in cursor.get_children():
|
|
all_cursors = accumulate_cursors(child, all_cursors)
|
|
return all_cursors
|
|
|
|
tu = get_tu(CHILDREN_TEST)
|
|
all_cursors = accumulate_cursors(tu.cursor, [])
|
|
cursor_hashes = set()
|
|
for cursor in all_cursors:
|
|
self.assertNotIn(hash(cursor), cursor_hashes)
|
|
cursor_hashes.add(hash(cursor))
|
|
|
|
def test_has_attrs(self):
|
|
tu = get_tu(
|
|
"""
|
|
struct A;
|
|
struct A final {};
|
|
|
|
struct B;
|
|
struct B {};
|
|
""",
|
|
lang="cpp",
|
|
)
|
|
A = get_cursor(tu, "A")
|
|
B = get_cursor(tu, "B")
|
|
self.assertTrue(A.get_definition().has_attrs())
|
|
self.assertFalse(B.get_definition().has_attrs())
|
|
|
|
def test_specialized_template(self):
|
|
tu = get_tu(TEMPLATE_ARG_TEST, lang="cpp")
|
|
foos = get_cursors(tu, "foo")
|
|
prime_foo = foos[1].specialized_template
|
|
|
|
self.assertNotEqual(foos[0], foos[1])
|
|
self.assertEqual(foos[0], prime_foo)
|
|
self.assertIsNone(tu.cursor.specialized_template)
|
|
|
|
def test_equality(self):
|
|
tu = get_tu(CHILDREN_TEST, lang="cpp")
|
|
cursor1 = get_cursor(tu, "s0")
|
|
cursor1_2 = get_cursor(tu, "s0")
|
|
cursor2 = get_cursor(tu, "f0")
|
|
|
|
self.assertIsNotNone(cursor1)
|
|
self.assertIsNotNone(cursor1_2)
|
|
self.assertIsNotNone(cursor2)
|
|
|
|
self.assertEqual(cursor1, cursor1)
|
|
self.assertEqual(cursor1, cursor1_2)
|
|
self.assertNotEqual(cursor1, cursor2)
|
|
self.assertNotEqual(cursor1, "foo")
|
|
|
|
def test_null_cursor(self):
|
|
tu = get_tu("int a = 729;")
|
|
|
|
for cursor in tu.cursor.walk_preorder():
|
|
self.assertFalse(cursor.is_null())
|
|
|
|
nc = conf.lib.clang_getNullCursor()
|
|
self.assertTrue(nc.is_null())
|
|
with self.assertRaises(Exception):
|
|
nc.is_definition()
|
|
with self.assertRaises(Exception):
|
|
nc.spelling
|