From 17337a3257927fbd3e8aeeabd73201054e39331b Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sun, 11 Oct 2020 13:57:04 +0200 Subject: [PATCH 1/5] C, properly error on keywords as function parameters --- CHANGES | 2 ++ sphinx/domains/c.py | 3 +++ tests/test_domain_c.py | 3 +++ 3 files changed, 8 insertions(+) diff --git a/CHANGES b/CHANGES index f9f267c3a..0e03a3f0a 100644 --- a/CHANGES +++ b/CHANGES @@ -77,6 +77,8 @@ Bugs fixed with size explicitly set in pixels) (fixed for ``'pdflatex'/'lualatex'`` only) * #8911: C++: remove the longest matching prefix in :confval:`cpp_index_common_prefix` instead of the first that matches. +* C, properly reject function declarations when a keyword is used + as parameter name. Testing -------- diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 061010d66..4909844cb 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -2703,6 +2703,9 @@ class DefinitionParser(BaseParser): declId = None elif named == 'single': if self.match(identifier_re): + if self.matched_text in _keywords: + self.fail("Expected identifier, " + "got keyword: %s" % self.matched_text) identifier = ASTIdentifier(self.matched_text) declId = ASTNestedName([identifier], rooted=False) else: diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index 2cfcf74fa..999c99f4d 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -417,6 +417,9 @@ def test_function_definitions(): check('function', 'void f(int arr[const static volatile 42])', {1: 'f'}, output='void f(int arr[static volatile const 42])') + with pytest.raises(DefinitionError): + parse('function', 'void f(int for)') + def test_nested_name(): check('struct', '{key}.A', {1: "A"}) From 40f4db30e8943a1ea3bcb2c1726dbacbf6d419ac Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sun, 11 Oct 2020 14:05:07 +0200 Subject: [PATCH 2/5] C, remove dead code --- sphinx/domains/c.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 4909844cb..3693e536a 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -2693,15 +2693,9 @@ class DefinitionParser(BaseParser): def _parse_declarator_name_suffix( self, named: Union[bool, str], paramMode: str, typed: bool ) -> ASTDeclarator: + assert named in (True, False, 'single') # now we should parse the name, and then suffixes - if named == 'maybe': - pos = self.pos - try: - declId = self._parse_nested_name() - except DefinitionError: - self.pos = pos - declId = None - elif named == 'single': + if named == 'single': if self.match(identifier_re): if self.matched_text in _keywords: self.fail("Expected identifier, " @@ -2883,8 +2877,8 @@ class DefinitionParser(BaseParser): def _parse_type(self, named: Union[bool, str], outer: str = None) -> ASTType: """ - named=False|'maybe'|True: 'maybe' is e.g., for function objects which - doesn't need to name the arguments + named=False|'single'|True: 'single' is e.g., for function objects which + doesn't need to name the arguments, but otherwise is a single name """ if outer: # always named if outer not in ('type', 'member', 'function'): From 088cef98bcb34522c2ec88c2fb4fd74199e2f10d Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sun, 11 Oct 2020 15:17:32 +0200 Subject: [PATCH 3/5] C, remove more dead code --- sphinx/domains/c.py | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 3693e536a..79d4e145c 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -387,19 +387,6 @@ class ASTPostfixDec(ASTPostfixOp): signode.append(nodes.Text('--')) -class ASTPostfixMember(ASTPostfixOp): - def __init__(self, name): - self.name = name - - def _stringify(self, transform: StringifyTransform) -> str: - return '.' + transform(self.name) - - def describe_signature(self, signode: TextElement, mode: str, - env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('.')) - self.name.describe_signature(signode, 'noneIsName', env, symbol) - - class ASTPostfixMemberOfPointer(ASTPostfixOp): def __init__(self, name): self.name = name @@ -2256,7 +2243,7 @@ class DefinitionParser(BaseParser): # | postfix "[" expression "]" # | postfix "[" braced-init-list [opt] "]" # | postfix "(" expression-list [opt] ")" - # | postfix "." id-expression + # | postfix "." id-expression // taken care of in primary by nested name # | postfix "->" id-expression # | postfix "++" # | postfix "--" @@ -2274,17 +2261,6 @@ class DefinitionParser(BaseParser): self.fail("Expected ']' in end of postfix expression.") postFixes.append(ASTPostfixArray(expr)) continue - if self.skip_string('.'): - if self.skip_string('*'): - # don't steal the dot - self.pos -= 2 - elif self.skip_string('..'): - # don't steal the dot - self.pos -= 3 - else: - name = self._parse_nested_name() - postFixes.append(ASTPostfixMember(name)) - continue if self.skip_string('->'): if self.skip_string('*'): # don't steal the arrow From 71a656498b0c091adf22394a5a004bee759d059c Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sun, 11 Oct 2020 15:23:11 +0200 Subject: [PATCH 4/5] C, simplify tests --- tests/roots/test-domain-c/semicolon.rst | 10 -------- tests/test_domain_c.py | 31 +++++++++++++++++++++---- 2 files changed, 26 insertions(+), 15 deletions(-) delete mode 100644 tests/roots/test-domain-c/semicolon.rst diff --git a/tests/roots/test-domain-c/semicolon.rst b/tests/roots/test-domain-c/semicolon.rst deleted file mode 100644 index 14ba17756..000000000 --- a/tests/roots/test-domain-c/semicolon.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. c:member:: int member; -.. c:var:: int var; -.. c:function:: void f(); -.. .. c:macro:: NO_SEMICOLON; -.. c:struct:: Struct; -.. c:union:: Union; -.. c:enum:: Enum; -.. c:enumerator:: Enumerator; -.. c:type:: Type; -.. c:type:: int TypeDef; diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index 999c99f4d..5d2b2c505 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -526,8 +526,15 @@ def test_attributes(): # raise DefinitionError("") +def split_warnigns(warning): + ws = warning.getvalue().split("\n") + assert len(ws) >= 1 + assert ws[-1] == "" + return ws[:-1] + + def filter_warnings(warning, file): - lines = warning.getvalue().split("\n") + lines = split_warnigns(warning) res = [l for l in lines if "domain-c" in l and "{}.rst".format(file) in l and "WARNING: document isn't included in any toctree" not in l] print("Filtered warnings for file '{}':".format(file)) @@ -581,10 +588,22 @@ def test_build_domain_c_anon_dup_decl(app, status, warning): assert "WARNING: c:identifier reference target not found: @b" in ws[1] -@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True}) -def test_build_domain_c_semicolon(app, status, warning): - app.builder.build_all() - ws = filter_warnings(warning, "semicolon") +@pytest.mark.sphinx(confoverrides={'nitpicky': True}) +def test_build_domain_c_semicolon(app, warning): + text = """ +.. c:member:: int member; +.. c:var:: int var; +.. c:function:: void f(); +.. .. c:macro:: NO_SEMICOLON; +.. c:struct:: Struct; +.. c:union:: Union; +.. c:enum:: Enum; +.. c:enumerator:: Enumerator; +.. c:type:: Type; +.. c:type:: int TypeDef; +""" + restructuredtext.parse(app, text) + ws = split_warnigns(warning) assert len(ws) == 0 @@ -659,6 +678,8 @@ def test_noindexentry(app): @pytest.mark.sphinx(testroot='domain-c-intersphinx', confoverrides={'nitpicky': True}) def test_intersphinx(tempdir, app, status, warning): + # a splitting of test_ids_vs_tags0 into the primary directives in a remote project, + # and then the references in the test project origSource = """\ .. c:member:: int _member .. c:var:: int _var From 0256daf11281650031c84fff3041aa5ca593fa93 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Wed, 24 Feb 2021 15:27:23 +0100 Subject: [PATCH 5/5] C, test namespace revamp --- tests/roots/test-domain-c/anon-dup-decl.rst | 2 ++ tests/roots/test-domain-c/function_param_target.rst | 2 ++ tests/roots/test-domain-c/index.rst | 2 ++ tests/test_domain_c.py | 4 ++-- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/roots/test-domain-c/anon-dup-decl.rst b/tests/roots/test-domain-c/anon-dup-decl.rst index 5f6c3bdfe..743ae2f6a 100644 --- a/tests/roots/test-domain-c/anon-dup-decl.rst +++ b/tests/roots/test-domain-c/anon-dup-decl.rst @@ -1,3 +1,5 @@ +.. c:namespace:: anon_dup_decl_ns + .. c:struct:: anon_dup_decl .. c:struct:: @a.A diff --git a/tests/roots/test-domain-c/function_param_target.rst b/tests/roots/test-domain-c/function_param_target.rst index 05de01445..d316d7bcd 100644 --- a/tests/roots/test-domain-c/function_param_target.rst +++ b/tests/roots/test-domain-c/function_param_target.rst @@ -1,3 +1,5 @@ +.. c:namespace:: function_param_target + .. c:function:: void f(int i) - :c:var:`i` diff --git a/tests/roots/test-domain-c/index.rst b/tests/roots/test-domain-c/index.rst index 7e2c18be9..4febd63ef 100644 --- a/tests/roots/test-domain-c/index.rst +++ b/tests/roots/test-domain-c/index.rst @@ -1,3 +1,5 @@ +.. c:namespace:: index + test-domain-c ============= diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index 5d2b2c505..38e83a77e 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -615,8 +615,8 @@ def test_build_function_param_target(app, warning): assert len(ws) == 0 entries = extract_role_links(app, "function_param_target.html") assert entries == [ - ('c.f', 'i', 'i'), - ('c.f', 'f.i', 'f.i'), + ('c.function_param_target.f', 'i', 'i'), + ('c.function_param_target.f', 'f.i', 'f.i'), ]