Skip to content

Commit

Permalink
C, keyword changes
Browse files Browse the repository at this point in the history
- Add new keywords from C23.
- Add c_extra_keywords as confval.
- Move macro names from keywords to c_extra_keywords.

Fixes sphinx-doc#9354
  • Loading branch information
jakobandersen committed Jun 17, 2021
1 parent 8939a75 commit 50f5280
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 11 deletions.
6 changes: 6 additions & 0 deletions CHANGES
Expand Up @@ -13,6 +13,10 @@ Deprecated
Features added
--------------

* C, add C23 keywords ``_Decimal32``, ``_Decimal64``, and ``_Decimal128``.
* #9354: C, add :confval:`c_extra_keywords` to allow user-defined keywords
during parsing.

Bugs fixed
----------

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

Testing
--------
Expand Down
8 changes: 8 additions & 0 deletions doc/usage/configuration.rst
Expand Up @@ -2670,6 +2670,14 @@ Options for the C domain

.. versionadded:: 3.0

.. confval:: c_extra_keywords

A list of identifiers to be recognized as keywords by the C parser.
It defaults to ``['alignas', 'alignof', 'bool', 'complex', 'imaginary',
'noreturn', 'static_assert', 'thread_local']``.

.. versionadded:: 4.0.3

.. confval:: c_allow_pre_v3

A boolean (default ``False``) controlling whether to parse and try to
Expand Down
26 changes: 22 additions & 4 deletions sphinx/domains/c.py
Expand Up @@ -54,10 +54,15 @@
'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', 'inline', 'int', 'long',
'register', 'restrict', 'return', 'short', 'signed', 'sizeof', 'static', 'struct',
'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while',
'_Alignas', 'alignas', '_Alignof', 'alignof', '_Atomic', '_Bool', 'bool',
'_Complex', 'complex', '_Generic', '_Imaginary', 'imaginary',
'_Noreturn', 'noreturn', '_Static_assert', 'static_assert',
'_Thread_local', 'thread_local',
'_Alignas', '_Alignof', '_Atomic', '_Bool', '_Complex',
'_Decimal32', '_Decimal64', '_Decimal128',
'_Generic', '_Imaginary', '_Noreturn', '_Static_assert', '_Thread_local',
]
# These are only keyword'y when the corresponding headers are included.
# They are used as default value for c_extra_keywords.
_macroKeywords = [
'alignas', 'alignof', 'bool', 'complex', 'imaginary', 'noreturn', 'static_assert',
'thread_local',
]

# these are ordered by preceedence
Expand Down Expand Up @@ -2535,6 +2540,12 @@ def _parse_nested_name(self) -> ASTNestedName:
if identifier in _keywords:
self.fail("Expected identifier in nested name, "
"got keyword: %s" % identifier)
if self.matched_text in self.config.c_extra_keywords:
msg = "Expected identifier, got user-defined keyword: %s." \
+ " Remove it from c_extra_keywords to allow it as identifier.\n" \
+ "Currently c_extra_keywords is %s."
self.fail(msg % (self.matched_text,
str(self.config.c_extra_keywords)))
ident = ASTIdentifier(identifier)
names.append(ident)

Expand Down Expand Up @@ -2711,6 +2722,12 @@ def _parse_declarator_name_suffix(
if self.matched_text in _keywords:
self.fail("Expected identifier, "
"got keyword: %s" % self.matched_text)
if self.matched_text in self.config.c_extra_keywords:
msg = "Expected identifier, got user-defined keyword: %s." \
+ " Remove it from c_extra_keywords to allow it as identifier.\n" \
+ "Currently c_extra_keywords is %s."
self.fail(msg % (self.matched_text,
str(self.config.c_extra_keywords)))
identifier = ASTIdentifier(self.matched_text)
declId = ASTNestedName([identifier], rooted=False)
else:
Expand Down Expand Up @@ -3877,6 +3894,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_domain(CDomain)
app.add_config_value("c_id_attributes", [], 'env')
app.add_config_value("c_paren_attributes", [], 'env')
app.add_config_value("c_extra_keywords", _macroKeywords, 'env')
app.add_post_transform(AliasTransform)

app.add_config_value("c_allow_pre_v3", False, 'env')
Expand Down
25 changes: 18 additions & 7 deletions tests/test_domain_c.py
Expand Up @@ -15,16 +15,20 @@

from sphinx import addnodes
from sphinx.addnodes import desc
from sphinx.domains.c import DefinitionError, DefinitionParser, Symbol, _id_prefix, _max_id
from sphinx.domains.c import (DefinitionError, DefinitionParser, Symbol, _id_prefix,
_macroKeywords, _max_id)
from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping
from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node


class Config:
c_id_attributes = ["id_attr", 'LIGHTGBM_C_EXPORT']
c_paren_attributes = ["paren_attr"]
c_extra_keywords = _macroKeywords


def parse(name, string):
class Config:
c_id_attributes = ["id_attr", 'LIGHTGBM_C_EXPORT']
c_paren_attributes = ["paren_attr"]
parser = DefinitionParser(string, location=None, config=Config())
parser.allowFallbackExpressionParsing = False
ast = parser.parse_declaration(name, name)
Expand Down Expand Up @@ -114,9 +118,6 @@ def check(name, input, idDict, output=None, key=None, asTextOutput=None):

def test_expressions():
def exprCheck(expr, output=None):
class Config:
c_id_attributes = ["id_attr"]
c_paren_attributes = ["paren_attr"]
parser = DefinitionParser(expr, location=None, config=Config())
parser.allowFallbackExpressionParsing = False
ast = parser.parse_expression()
Expand Down Expand Up @@ -522,6 +523,16 @@ def test_attributes():
check('function', 'LIGHTGBM_C_EXPORT int LGBM_BoosterFree(int handle)',
{1: 'LGBM_BoosterFree'})


def test_extra_keywords():
with pytest.raises(DefinitionError,
match='Expected identifier, got user-defined keyword: complex.'):
parse('function', 'void f(int complex)')
with pytest.raises(DefinitionError,
match='Expected identifier, got user-defined keyword: complex.'):
parse('function', 'void complex(void)')


# def test_print():
# # used for getting all the ids out for checking
# for a in ids:
Expand Down

0 comments on commit 50f5280

Please sign in to comment.