[clang] [libclang/python] Improve test coverage (PR #109846)
Jannick Kremer via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 24 13:18:24 PDT 2024
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/109846
>From 68ca7ee24712a48c1b6df6aff480fb4ff3054c57 Mon Sep 17 00:00:00 2001
From: Jannick Kremer <jannick.kremer at mailbox.org>
Date: Tue, 24 Sep 2024 20:44:23 +0200
Subject: [PATCH 1/2] [libclang/python] Improve test coverage
Achieve 100% test coverage on classes Cursor, Diagnostic, Type
---
.../python/tests/cindex/test_cursor.py | 183 ++++++++++++++++++
.../python/tests/cindex/test_diagnostics.py | 17 ++
.../bindings/python/tests/cindex/test_type.py | 54 +++++-
3 files changed, 250 insertions(+), 4 deletions(-)
diff --git a/clang/bindings/python/tests/cindex/test_cursor.py b/clang/bindings/python/tests/cindex/test_cursor.py
index 7476947bde2ea6..77d8ca415708f8 100644
--- a/clang/bindings/python/tests/cindex/test_cursor.py
+++ b/clang/bindings/python/tests/cindex/test_cursor.py
@@ -14,6 +14,7 @@
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
@@ -279,6 +280,90 @@ def test_is_default_method(self):
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 = """
@@ -482,6 +567,41 @@ def test_is_scoped_enum(self):
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")
@@ -570,6 +690,23 @@ def test_enum_values_cpp(self):
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")));'
@@ -625,6 +762,25 @@ def test_result_type_objc_method_decl(self):
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")
@@ -681,6 +837,23 @@ def test_get_token_cursor(self):
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")
@@ -799,3 +972,13 @@ def test_binop(self):
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)
diff --git a/clang/bindings/python/tests/cindex/test_diagnostics.py b/clang/bindings/python/tests/cindex/test_diagnostics.py
index 57c41baaa25419..041083d12c7f16 100644
--- a/clang/bindings/python/tests/cindex/test_diagnostics.py
+++ b/clang/bindings/python/tests/cindex/test_diagnostics.py
@@ -46,6 +46,8 @@ def test_diagnostic_fixit(self):
self.assertEqual(tu.diagnostics[0].location.column, 26)
self.assertRegex(tu.diagnostics[0].spelling, "use of GNU old-style.*")
self.assertEqual(len(tu.diagnostics[0].fixits), 1)
+ with self.assertRaises(IndexError):
+ tu.diagnostics[0].fixits[1]
self.assertEqual(tu.diagnostics[0].fixits[0].range.start.line, 1)
self.assertEqual(tu.diagnostics[0].fixits[0].range.start.column, 26)
self.assertEqual(tu.diagnostics[0].fixits[0].range.end.line, 1)
@@ -97,6 +99,8 @@ def test_diagnostic_children(self):
children = d.children
self.assertEqual(len(children), 1)
+ with self.assertRaises(IndexError):
+ children[1]
self.assertEqual(children[0].severity, Diagnostic.Note)
self.assertRegex(children[0].spelling, ".*declared here")
self.assertEqual(children[0].location.line, 1)
@@ -111,3 +115,16 @@ def test_diagnostic_string_repr(self):
repr(d),
"<Diagnostic severity 3, location <SourceLocation file 't.c', line 1, column 26>, spelling \"expected ';' after struct\">",
)
+
+ def test_diagnostic_string_format(self):
+ tu = get_tu("struct MissingSemicolon{}")
+ self.assertEqual(len(tu.diagnostics), 1)
+ d = tu.diagnostics[0]
+
+ self.assertEqual(str(d), "t.c:1:26: error: expected ';' after struct")
+ self.assertEqual(
+ d.format(0b111111),
+ "t.c:1:26: error: expected ';' after struct [3, Parse Issue]",
+ )
+ with self.assertRaises(ValueError):
+ d.format(0b1000000)
diff --git a/clang/bindings/python/tests/cindex/test_type.py b/clang/bindings/python/tests/cindex/test_type.py
index 1dd8db0e3e814c..c8769c121cad8c 100644
--- a/clang/bindings/python/tests/cindex/test_type.py
+++ b/clang/bindings/python/tests/cindex/test_type.py
@@ -10,7 +10,9 @@
from clang.cindex import CursorKind
from clang.cindex import TranslationUnit
from clang.cindex import TypeKind
+from clang.cindex import RefQualifierKind
from .util import get_cursor
+from .util import get_cursors
from .util import get_tu
@@ -308,10 +310,10 @@ def test_element_type(self):
def test_invalid_element_type(self):
"""Ensure Type.element_type raises if type doesn't have elements."""
tu = get_tu("int i;")
- i = get_cursor(tu, "i")
- self.assertIsNotNone(i)
- with self.assertRaises(Exception):
- i.element_type
+ ty = get_cursor(tu, "i").type
+ with self.assertRaises(Exception) as ctx:
+ ty.element_type
+ self.assertEqual(str(ctx.exception), "Element type not available on this type.")
def test_element_count(self):
"""Ensure Type.element_count works."""
@@ -357,6 +359,50 @@ def test_is_restrict_qualified(self):
self.assertTrue(i.type.is_restrict_qualified())
self.assertFalse(j.type.is_restrict_qualified())
+ def test_get_result(self):
+ tu = get_tu("void foo(); int bar(char, short);")
+ foo = get_cursor(tu, "foo")
+ bar = get_cursor(tu, "bar")
+ self.assertEqual(foo.type.get_result().spelling, "void")
+ self.assertEqual(bar.type.get_result().spelling, "int")
+
+ def test_get_class_type(self):
+ tu = get_tu(
+ """
+class myClass
+{
+ char *myAttr;
+};
+
+char *myClass::*pMyAttr = &myClass::myAttr;
+""",
+ lang="cpp",
+ )
+ cur = get_cursor(tu, "pMyAttr")
+ self.assertEqual(cur.type.get_class_type().spelling, "myClass")
+
+ def test_get_named_type(self):
+ tu = get_tu("using char_alias = char; char_alias xyz;", lang="cpp")
+ cur = get_cursor(tu, "xyz")
+ self.assertEqual(cur.type.kind, TypeKind.ELABORATED)
+ self.assertEqual(cur.type.get_named_type().spelling, "char_alias")
+
+ def test_get_ref_qualifier(self):
+ tu = get_tu(
+ """
+class A
+{
+ const int& getAttr() const &;
+ int getAttr() const &&;
+};
+""",
+ lang="cpp",
+ )
+ getters = get_cursors(tu, "getAttr")
+ self.assertEqual(len(getters), 2)
+ self.assertEqual(getters[0].type.get_ref_qualifier(), RefQualifierKind.LVALUE)
+ self.assertEqual(getters[1].type.get_ref_qualifier(), RefQualifierKind.RVALUE)
+
def test_record_layout(self):
"""Ensure Cursor.type.get_size, Cursor.type.get_align and
Cursor.type.get_offset works."""
>From 0d5e6a12c69c9b6ed071d691b837660305c31559 Mon Sep 17 00:00:00 2001
From: Jannick Kremer <jannick.kremer at mailbox.org>
Date: Tue, 24 Sep 2024 22:17:49 +0200
Subject: [PATCH 2/2] Remove TypeKind.ELABORATED assert in test
---
clang/bindings/python/tests/cindex/test_type.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang/bindings/python/tests/cindex/test_type.py b/clang/bindings/python/tests/cindex/test_type.py
index c8769c121cad8c..928a9794e42133 100644
--- a/clang/bindings/python/tests/cindex/test_type.py
+++ b/clang/bindings/python/tests/cindex/test_type.py
@@ -384,7 +384,6 @@ class myClass
def test_get_named_type(self):
tu = get_tu("using char_alias = char; char_alias xyz;", lang="cpp")
cur = get_cursor(tu, "xyz")
- self.assertEqual(cur.type.kind, TypeKind.ELABORATED)
self.assertEqual(cur.type.get_named_type().spelling, "char_alias")
def test_get_ref_qualifier(self):
More information about the cfe-commits
mailing list