Skip to content

Commit 38b6520

Browse files
committed
Fix file level variables with unknown types.
Previously file level variables with unknown types would cause errors in Sphinx processing. This was due to only the name of the variable being passed to sphinx. Now an attempt will be made to parse the variable's type signature and provide that to sphinx. closes #13
1 parent 660b140 commit 38b6520

File tree

6 files changed

+107
-4
lines changed

6 files changed

+107
-4
lines changed

CHANGELOG.rst

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,25 @@ This project adheres to `Semantic Versioning <https://semver.org/>`_.
88
`v0.4.0-dev`_ (unreleased)
99
==========================
1010

11+
Changed
12+
-------
13+
14+
* Undocumented constructs are no longer added to the documentation by default.
15+
To maintain previous behavior add `:undoc-members:` to the project's
16+
`autodoc_default_options`_.
17+
1118
Added
1219
-----
1320

1421
* `:undoc-members:` and `:no-undoc-members:` option for the autocmodule
1522
directive. This option set allows for controlling the listing of undocumented
1623
constructs. The default is to not list undocumented constructs.
1724

18-
.. note:: This is a behavior change. To maintain previous behavior add
19-
`:undoc-members:` to the project's `autodoc_default_options`_.
20-
2125
Fixes
2226
-----
2327

28+
* Fix file level variables with unknown types. Previously variables with
29+
unknown types would cause an error in Sphinx processing.
2430
* Fix documentation of members that are arrays. Previously struct members that
2531
were array types would cause an error as the array size was put between the
2632
type and the member name.

src/sphinx_c_autodoc/loader.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,54 @@ def is_public(self) -> bool:
658658

659659
return True
660660

661+
def get_parsed_declaration(self) -> str:
662+
"""
663+
Get the declaration as parsed from the :attr:`node`.
664+
"""
665+
# Libclang will return the type as `float [20]` when looking at
666+
# `float foo[20]`. We could look at the kind `TypeKind.CONSTANTARRAY`
667+
# but partitioning on the "[" just seems more straight forward.
668+
type_ = self.node.type.spelling
669+
type_, *(array) = type_.partition("[")
670+
array_contents = "".join(array)
671+
672+
real_type = self._find_declaration_type()
673+
type_ = type_.replace("int", real_type)
674+
675+
return f"{type_} {self.name} {array_contents}"
676+
677+
def _find_declaration_type(self) -> str:
678+
"""
679+
Makes an attempt to try and find the identifier, and storage class,
680+
representing the variable type.
681+
682+
Returns:
683+
str: The type of the variable. If this can't be derived falls back to
684+
`int`.
685+
"""
686+
type_ = "int"
687+
tokens = list(
688+
filter(
689+
lambda t: t.kind == cindex.TokenKind.IDENTIFIER, self.node.get_tokens()
690+
)
691+
)
692+
try:
693+
type_ = tokens[-2].spelling
694+
except IndexError:
695+
# For array variables with unknown types libclang fails to provide the
696+
# tokens.
697+
pass
698+
699+
# clang doesn't provide the storage class in the type name, so we'll add it here
700+
storage_keyword = ""
701+
storage_class = self.node.storage_class
702+
if storage_class == cindex.StorageClass.STATIC:
703+
storage_keyword = "static"
704+
if storage_class == cindex.StorageClass.EXTERN:
705+
storage_keyword = "extern"
706+
707+
return f"{storage_keyword} {type_}"
708+
661709

662710
CURSORKIND_TO_OBJECT_CLASS = {
663711
cindex.CursorKind.TRANSLATION_UNIT: DocumentedFile,

tests/assets/c_source/file_2.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
*
66
***************************************/
77

8+
unknown_type * file_level_variable[32];
9+
810
/**
911
1012
This is a type comment

tests/assets/c_source/variables.c

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,32 @@
22
* File focusing on variables
33
*/
44

5+
// Provide a way to mix defines into an unknown variable's type
6+
#define MAYBE_CONST
7+
58
/**
69
* A variable
710
*/
8-
static const char * file_level_variable = "hello";
11+
static const char * file_level_variable = "hello";
12+
13+
/**
14+
* A variable with unknown type
15+
* This one we can parse the tokens and try to replace clang's ``int``
16+
* usage with a stab at the underlying type. We can't take this token
17+
* for token as sphinx is too strict at parsing and will assume that
18+
* ``MAYBE_CONST`` is the type.
19+
*/
20+
static MAYBE_CONST /* throw in a pinch of comment to the mix */
21+
unknown_type * unknown_type_var = 30;
22+
23+
/**
24+
* A an array variable with an unknown type.
25+
* For whatever reason clang will come back with no extent on this so
26+
* we have to fall back to this being treated as an int
27+
*/
28+
unknown_type unknown_array_type_var[24];
29+
30+
/**
31+
* Unknown extern type
32+
*/
33+
extern unknown_type * unknown_extern_type_var;

tests/directives/test_autocdata.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,30 @@ class TestAutoCData:
2727
float b
2828
"""
2929

30+
unknown_file_level_variable_type = """\
31+
static unknown_type *unknown_type_var
32+
A variable with unknown type
33+
This one we can parse the tokens and try to replace clang's int
34+
usage with a stab at the underlying type. We can't take this token
35+
for token as sphinx is too strict at parsing and will assume that
36+
MAYBE_CONST is the type."""
37+
38+
unknown_file_level_array_type = """\
39+
int unknown_array_type_var[24]
40+
A an array variable with an unknown type.
41+
For whatever reason clang will come back with no extent on this so
42+
we have to fall back to this being treated as an int"""
43+
44+
unknown_extern_file_variable = """\
45+
extern unknown_type *unknown_extern_type_var
46+
Unknown extern type"""
47+
3048
doc_data = [
3149
("variables.c::file_level_variable", file_level_variable),
3250
("example.c::inline_struct_variable", inline_struct_variable),
51+
("variables.c::unknown_type_var", unknown_file_level_variable_type),
52+
("variables.c::unknown_array_type_var", unknown_file_level_array_type),
53+
("variables.c::unknown_extern_type_var", unknown_extern_file_variable),
3354
]
3455

3556
@pytest.mark.parametrize("variable, expected_doc", doc_data)

tests/viewcode/test_viewcode.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ def test_viewcode_of_sphinx_project(tmp_path):
5656

5757
chosen_links = (
5858
'<a class="reference internal" href="../_modules/file_2.c.html#c.unknown_member.foo"><span class="viewcode-link">[source]</span></a>',
59+
'<a class="reference internal" href="../_modules/file_2.c.html#c.file_level_variable"><span class="viewcode-link">[source]</span></a>',
5960
)
6061
for l in chosen_links:
6162
assert l in contents

0 commit comments

Comments
 (0)