summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Lykke Andersen <jakobandersen@users.noreply.github.com>2020-10-13 13:14:18 +0200
committerGitHub <noreply@github.com>2020-10-13 13:14:18 +0200
commit3b1c48f3b64f55e023192850f8b3da0a188fd9d7 (patch)
tree1ebfee1f941862f14b479dbb44c2bbb590f9e1c3
parent488f4a6ac44f8fc81cb55ad3c7fb1fd1271b03ff (diff)
parent15251574a97ffa13b9c62255733296c2a3ab50b4 (diff)
downloadsphinx-git-3b1c48f3b64f55e023192850f8b3da0a188fd9d7.tar.gz
Merge pull request #8310 from jakobandersen/c_function_param_target
C, fix links to function parameters
-rw-r--r--CHANGES2
-rw-r--r--sphinx/domains/c.py38
-rw-r--r--sphinx/domains/cpp.py2
-rw-r--r--tests/roots/test-domain-c/function_param_target.rst5
-rw-r--r--tests/test_domain_c.py34
5 files changed, 72 insertions, 9 deletions
diff --git a/CHANGES b/CHANGES
index 93cc5fb8c..a8426dff8 100644
--- a/CHANGES
+++ b/CHANGES
@@ -21,6 +21,8 @@ Bugs fixed
* C, fix anon objects in intersphinx.
* #8270, C++, properly reject functions as duplicate declarations if a
non-function declaration of the same name already exists.
+* C, fix references to function parameters.
+ Link to the function instead of a non-existing anchor.
Testing
diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py
index 4d88bd8e8..16f2c876f 100644
--- a/sphinx/domains/c.py
+++ b/sphinx/domains/c.py
@@ -10,7 +10,7 @@
import re
from typing import (
- Any, Callable, Dict, Generator, Iterator, List, Type, TypeVar, Tuple, Union
+ Any, Callable, cast, Dict, Generator, Iterator, List, Type, TypeVar, Tuple, Union
)
from docutils import nodes
@@ -46,6 +46,11 @@ from sphinx.util.nodes import make_refnode
logger = logging.getLogger(__name__)
T = TypeVar('T')
+DeclarationType = Union[
+ "ASTStruct", "ASTUnion", "ASTEnum", "ASTEnumerator",
+ "ASTType", "ASTTypeWithInit", "ASTMacro",
+]
+
# https://en.cppreference.com/w/c/keyword
_keywords = [
'auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do', 'double',
@@ -635,6 +640,10 @@ class ASTFunctionParameter(ASTBase):
self.arg = arg
self.ellipsis = ellipsis
+ def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
+ # the anchor will be our parent
+ return symbol.parent.declaration.get_id(version, prefixed=False)
+
def _stringify(self, transform: StringifyTransform) -> str:
if self.ellipsis:
return '...'
@@ -1148,6 +1157,9 @@ class ASTType(ASTBase):
def name(self) -> ASTNestedName:
return self.decl.name
+ def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
+ return symbol.get_full_nested_name().get_id(version)
+
@property
def function_params(self) -> List[ASTFunctionParameter]:
return self.decl.function_params
@@ -1190,6 +1202,9 @@ class ASTTypeWithInit(ASTBase):
def name(self) -> ASTNestedName:
return self.type.name
+ def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
+ return self.type.get_id(version, objectType, symbol)
+
def _stringify(self, transform: StringifyTransform) -> str:
res = []
res.append(transform(self.type))
@@ -1241,6 +1256,9 @@ class ASTMacro(ASTBase):
def name(self) -> ASTNestedName:
return self.ident
+ def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
+ return symbol.get_full_nested_name().get_id(version)
+
def _stringify(self, transform: StringifyTransform) -> str:
res = []
res.append(transform(self.ident))
@@ -1341,7 +1359,8 @@ class ASTEnumerator(ASTBase):
class ASTDeclaration(ASTBaseBase):
- def __init__(self, objectType: str, directiveType: str, declaration: Any,
+ def __init__(self, objectType: str, directiveType: str,
+ declaration: Union[DeclarationType, ASTFunctionParameter],
semicolon: bool = False) -> None:
self.objectType = objectType
self.directiveType = directiveType
@@ -1358,18 +1377,20 @@ class ASTDeclaration(ASTBaseBase):
@property
def name(self) -> ASTNestedName:
- return self.declaration.name
+ decl = cast(DeclarationType, self.declaration)
+ return decl.name
@property
def function_params(self) -> List[ASTFunctionParameter]:
if self.objectType != 'function':
return None
- return self.declaration.function_params
+ decl = cast(ASTType, self.declaration)
+ return decl.function_params
def get_id(self, version: int, prefixed: bool = True) -> str:
if self.objectType == 'enumerator' and self.enumeratorScopedSymbol:
return self.enumeratorScopedSymbol.declaration.get_id(version, prefixed)
- id_ = self.symbol.get_full_nested_name().get_id(version)
+ id_ = self.declaration.get_id(version, self.objectType, self.symbol)
if prefixed:
return _id_prefix[version] + id_
else:
@@ -1412,7 +1433,8 @@ class ASTDeclaration(ASTBaseBase):
elif self.objectType == 'enumerator':
mainDeclNode += addnodes.desc_annotation('enumerator ', 'enumerator ')
elif self.objectType == 'type':
- prefix = self.declaration.get_type_declaration_prefix()
+ decl = cast(ASTType, self.declaration)
+ prefix = decl.get_type_declaration_prefix()
prefix += ' '
mainDeclNode += addnodes.desc_annotation(prefix, prefix)
else:
@@ -2982,7 +3004,7 @@ class DefinitionParser(BaseParser):
def parse_pre_v3_type_definition(self) -> ASTDeclaration:
self.skip_ws()
- declaration = None # type: Any
+ declaration = None # type: DeclarationType
if self.skip_word('struct'):
typ = 'struct'
declaration = self._parse_struct()
@@ -3005,7 +3027,7 @@ class DefinitionParser(BaseParser):
'macro', 'struct', 'union', 'enum', 'enumerator', 'type'):
raise Exception('Internal error, unknown directiveType "%s".' % directiveType)
- declaration = None # type: Any
+ declaration = None # type: DeclarationType
if objectType == 'member':
declaration = self._parse_type_with_init(named=True, outer='member')
elif objectType == 'function':
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py
index c43c55d5b..0e996532f 100644
--- a/sphinx/domains/cpp.py
+++ b/sphinx/domains/cpp.py
@@ -1836,7 +1836,7 @@ class ASTFunctionParameter(ASTBase):
# this is not part of the normal name mangling in C++
if symbol:
# the anchor will be our parent
- return symbol.parent.declaration.get_id(version, prefixed=None)
+ return symbol.parent.declaration.get_id(version, prefixed=False)
# else, do the usual
if self.ellipsis:
return 'z'
diff --git a/tests/roots/test-domain-c/function_param_target.rst b/tests/roots/test-domain-c/function_param_target.rst
new file mode 100644
index 000000000..05de01445
--- /dev/null
+++ b/tests/roots/test-domain-c/function_param_target.rst
@@ -0,0 +1,5 @@
+.. c:function:: void f(int i)
+
+ - :c:var:`i`
+
+- :c:var:`f.i`
diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py
index 7509b1aa9..43d71f74e 100644
--- a/tests/test_domain_c.py
+++ b/tests/test_domain_c.py
@@ -9,6 +9,8 @@
"""
import pytest
+from xml.etree import ElementTree
+
from sphinx import addnodes
from sphinx.addnodes import desc
from sphinx.domains.c import DefinitionParser, DefinitionError
@@ -529,6 +531,25 @@ def filter_warnings(warning, file):
return res
+def extract_role_links(app, filename):
+ t = (app.outdir / filename).read_text()
+ lis = [l for l in t.split('\n') if l.startswith("<li")]
+ entries = []
+ for l in lis:
+ li = ElementTree.fromstring(l)
+ aList = list(li.iter('a'))
+ assert len(aList) == 1
+ a = aList[0]
+ target = a.attrib['href'].lstrip('#')
+ title = a.attrib['title']
+ assert len(a) == 1
+ code = a[0]
+ assert code.tag == 'code'
+ text = ''.join(code.itertext())
+ entries.append((target, title, text))
+ return entries
+
+
@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
def test_build_domain_c(app, status, warning):
app.builder.build_all()
@@ -562,6 +583,19 @@ def test_build_domain_c_semicolon(app, status, warning):
assert len(ws) == 0
+@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
+def test_build_function_param_target(app, warning):
+ # the anchor for function parameters should be the function
+ app.builder.build_all()
+ ws = filter_warnings(warning, "function_param_target")
+ 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'),
+ ]
+
+
def _get_obj(app, queryName):
domain = app.env.get_domain('c')
for name, dispname, objectType, docname, anchor, prio in domain.get_objects():