This is a rework of patch [D10833](https://reviews.llvm.org/D10833) previously posted on LLVM Phabricator by arthurp in 2015. It allows to retrieve the type of binary operator via libclangs python bindings. I did clean up the changes, removed unrelated changes and rebased the changeset to the latest main branch. As this is my first contribution to the LLVM project, let me know if any required tests or documentation are missing.
802 lines
26 KiB
Python
802 lines
26 KiB
Python
import os
|
|
from clang.cindex import Config
|
|
|
|
if "CLANG_LIBRARY_PATH" in os.environ:
|
|
Config.set_library_path(os.environ["CLANG_LIBRARY_PATH"])
|
|
|
|
import ctypes
|
|
import gc
|
|
import unittest
|
|
|
|
from clang.cindex import AvailabilityKind
|
|
from clang.cindex import CursorKind
|
|
from clang.cindex import TemplateArgumentKind
|
|
from clang.cindex import TranslationUnit
|
|
from clang.cindex import TypeKind
|
|
from clang.cindex import BinaryOperator
|
|
from .util import get_cursor
|
|
from .util import get_cursors
|
|
from .util import get_tu
|
|
|
|
|
|
kInput = """\
|
|
struct s0 {
|
|
int a;
|
|
int b;
|
|
};
|
|
|
|
struct s1;
|
|
|
|
void f0(int a0, int a1) {
|
|
int l0, l1;
|
|
|
|
if (a0)
|
|
return;
|
|
|
|
for (;;) {
|
|
break;
|
|
}
|
|
}
|
|
"""
|
|
|
|
kParentTest = """\
|
|
class C {
|
|
void f();
|
|
}
|
|
|
|
void C::f() { }
|
|
"""
|
|
|
|
kTemplateArgTest = """\
|
|
template <int kInt, typename T, bool kBool>
|
|
void foo();
|
|
|
|
template<>
|
|
void foo<-7, float, true>();
|
|
"""
|
|
|
|
kBinops = """\
|
|
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(kInput)
|
|
|
|
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.
|
|
parent = 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_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_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(kParentTest, "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(kParentTest, "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_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_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_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(kTemplateArgTest, 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(kTemplateArgTest, 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(kTemplateArgTest, 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(kTemplateArgTest, 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(kTemplateArgTest, 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(kBinops, 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
|