llvm/clang/bindings/python/tests/cindex/test_cursor.py

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 clang.cindex import StorageClass
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_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.assertTrue(len(non_copy_assignment_operators_cursors), 9)

        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(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_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(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

    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)