Skip to content

Commit 50f5280

Browse files
committed
C, keyword changes
- Add new keywords from C23. - Add c_extra_keywords as confval. - Move macro names from keywords to c_extra_keywords. Fixes #9354
1 parent 8939a75 commit 50f5280

File tree

4 files changed

+54
-11
lines changed

4 files changed

+54
-11
lines changed

CHANGES

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ Deprecated
1313
Features added
1414
--------------
1515

16+
* C, add C23 keywords ``_Decimal32``, ``_Decimal64``, and ``_Decimal128``.
17+
* #9354: C, add :confval:`c_extra_keywords` to allow user-defined keywords
18+
during parsing.
19+
1620
Bugs fixed
1721
----------
1822

@@ -21,6 +25,8 @@ Bugs fixed
2125
* #9313: LaTeX: complex table with merged cells broken since 4.0
2226
* #9305: LaTeX: backslash may cause Improper discretionary list pdf build error
2327
with Japanese engines
28+
* #9354: C, remove special macro names from the keyword list.
29+
See also :confval:`c_extra_keywords`.
2430

2531
Testing
2632
--------

doc/usage/configuration.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2670,6 +2670,14 @@ Options for the C domain
26702670

26712671
.. versionadded:: 3.0
26722672

2673+
.. confval:: c_extra_keywords
2674+
2675+
A list of identifiers to be recognized as keywords by the C parser.
2676+
It defaults to ``['alignas', 'alignof', 'bool', 'complex', 'imaginary',
2677+
'noreturn', 'static_assert', 'thread_local']``.
2678+
2679+
.. versionadded:: 4.0.3
2680+
26732681
.. confval:: c_allow_pre_v3
26742682

26752683
A boolean (default ``False``) controlling whether to parse and try to

sphinx/domains/c.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,15 @@
5454
'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', 'inline', 'int', 'long',
5555
'register', 'restrict', 'return', 'short', 'signed', 'sizeof', 'static', 'struct',
5656
'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while',
57-
'_Alignas', 'alignas', '_Alignof', 'alignof', '_Atomic', '_Bool', 'bool',
58-
'_Complex', 'complex', '_Generic', '_Imaginary', 'imaginary',
59-
'_Noreturn', 'noreturn', '_Static_assert', 'static_assert',
60-
'_Thread_local', 'thread_local',
57+
'_Alignas', '_Alignof', '_Atomic', '_Bool', '_Complex',
58+
'_Decimal32', '_Decimal64', '_Decimal128',
59+
'_Generic', '_Imaginary', '_Noreturn', '_Static_assert', '_Thread_local',
60+
]
61+
# These are only keyword'y when the corresponding headers are included.
62+
# They are used as default value for c_extra_keywords.
63+
_macroKeywords = [
64+
'alignas', 'alignof', 'bool', 'complex', 'imaginary', 'noreturn', 'static_assert',
65+
'thread_local',
6166
]
6267

6368
# these are ordered by preceedence
@@ -2535,6 +2540,12 @@ def _parse_nested_name(self) -> ASTNestedName:
25352540
if identifier in _keywords:
25362541
self.fail("Expected identifier in nested name, "
25372542
"got keyword: %s" % identifier)
2543+
if self.matched_text in self.config.c_extra_keywords:
2544+
msg = "Expected identifier, got user-defined keyword: %s." \
2545+
+ " Remove it from c_extra_keywords to allow it as identifier.\n" \
2546+
+ "Currently c_extra_keywords is %s."
2547+
self.fail(msg % (self.matched_text,
2548+
str(self.config.c_extra_keywords)))
25382549
ident = ASTIdentifier(identifier)
25392550
names.append(ident)
25402551

@@ -2711,6 +2722,12 @@ def _parse_declarator_name_suffix(
27112722
if self.matched_text in _keywords:
27122723
self.fail("Expected identifier, "
27132724
"got keyword: %s" % self.matched_text)
2725+
if self.matched_text in self.config.c_extra_keywords:
2726+
msg = "Expected identifier, got user-defined keyword: %s." \
2727+
+ " Remove it from c_extra_keywords to allow it as identifier.\n" \
2728+
+ "Currently c_extra_keywords is %s."
2729+
self.fail(msg % (self.matched_text,
2730+
str(self.config.c_extra_keywords)))
27142731
identifier = ASTIdentifier(self.matched_text)
27152732
declId = ASTNestedName([identifier], rooted=False)
27162733
else:
@@ -3877,6 +3894,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
38773894
app.add_domain(CDomain)
38783895
app.add_config_value("c_id_attributes", [], 'env')
38793896
app.add_config_value("c_paren_attributes", [], 'env')
3897+
app.add_config_value("c_extra_keywords", _macroKeywords, 'env')
38803898
app.add_post_transform(AliasTransform)
38813899

38823900
app.add_config_value("c_allow_pre_v3", False, 'env')

tests/test_domain_c.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,20 @@
1515

1616
from sphinx import addnodes
1717
from sphinx.addnodes import desc
18-
from sphinx.domains.c import DefinitionError, DefinitionParser, Symbol, _id_prefix, _max_id
18+
from sphinx.domains.c import (DefinitionError, DefinitionParser, Symbol, _id_prefix,
19+
_macroKeywords, _max_id)
1920
from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping
2021
from sphinx.testing import restructuredtext
2122
from sphinx.testing.util import assert_node
2223

2324

25+
class Config:
26+
c_id_attributes = ["id_attr", 'LIGHTGBM_C_EXPORT']
27+
c_paren_attributes = ["paren_attr"]
28+
c_extra_keywords = _macroKeywords
29+
30+
2431
def parse(name, string):
25-
class Config:
26-
c_id_attributes = ["id_attr", 'LIGHTGBM_C_EXPORT']
27-
c_paren_attributes = ["paren_attr"]
2832
parser = DefinitionParser(string, location=None, config=Config())
2933
parser.allowFallbackExpressionParsing = False
3034
ast = parser.parse_declaration(name, name)
@@ -114,9 +118,6 @@ def check(name, input, idDict, output=None, key=None, asTextOutput=None):
114118

115119
def test_expressions():
116120
def exprCheck(expr, output=None):
117-
class Config:
118-
c_id_attributes = ["id_attr"]
119-
c_paren_attributes = ["paren_attr"]
120121
parser = DefinitionParser(expr, location=None, config=Config())
121122
parser.allowFallbackExpressionParsing = False
122123
ast = parser.parse_expression()
@@ -522,6 +523,16 @@ def test_attributes():
522523
check('function', 'LIGHTGBM_C_EXPORT int LGBM_BoosterFree(int handle)',
523524
{1: 'LGBM_BoosterFree'})
524525

526+
527+
def test_extra_keywords():
528+
with pytest.raises(DefinitionError,
529+
match='Expected identifier, got user-defined keyword: complex.'):
530+
parse('function', 'void f(int complex)')
531+
with pytest.raises(DefinitionError,
532+
match='Expected identifier, got user-defined keyword: complex.'):
533+
parse('function', 'void complex(void)')
534+
535+
525536
# def test_print():
526537
# # used for getting all the ids out for checking
527538
# for a in ids:

0 commit comments

Comments
 (0)