Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

All :c:*: roles should check objtype on resolving refs (refs: #7243) #7256

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES
Expand Up @@ -31,6 +31,8 @@ Incompatible changes
node_id for cross reference
* #7229: rst domain: Non intended behavior is removed such as ``numref_`` links
to ``.. rst:role:: numref``
* #7243: c domain: Internal data structure has changed. Now keys for objects
is a pair of objtype and objname

Deprecated
----------
Expand All @@ -55,6 +57,8 @@ Features added
* #7165: autodoc: Support Annotated type (PEP-593)
* #6558: glossary: emit a warning for duplicated glossary entry
* #3106: domain: Register hyperlink target for index page automatically
* #7243: c domain: All ``:c:*:`` roles can refer all kind of C objects even if
objtype not matched
* #6558: std domain: emit a warning for duplicated generic objects
* #6830: py domain: Add new event: :event:`object-description-transform`
* py domain: Support lambda functions in function signature
Expand Down
53 changes: 30 additions & 23 deletions sphinx/domains/c.py
Expand Up @@ -271,65 +271,72 @@ class CDomain(Domain):
} # type: Dict[str, Dict[str, Tuple[str, Any]]]

@property
def objects(self) -> Dict[str, Tuple[str, str]]:
return self.data.setdefault('objects', {}) # fullname -> docname, objtype
def objects(self) -> Dict[Tuple[str, str], str]:
return self.data.setdefault('objects', {}) # objtype, fullname -> docname
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change the structure of self.objects later to add node_id into the value in next PR. It is also needed to avoid conflicts with other domains (refs: #6903).


def note_object(self, name: str, objtype: str, location: Any = None) -> None:
if name in self.objects:
docname = self.objects[name][0]
if (objtype, name) in self.objects:
docname = self.objects[objtype, name]
logger.warning(__('duplicate C object description of %s, '
'other instance in %s, use :noindex: for one of them'),
name, docname, location=location)
self.objects[name] = (self.env.docname, objtype)
self.objects[objtype, name] = self.env.docname

def clear_doc(self, docname: str) -> None:
for fullname, (fn, _l) in list(self.objects.items()):
for key, fn in list(self.objects.items()):
if fn == docname:
del self.objects[fullname]
del self.objects[key]

def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
# XXX check duplicates
for fullname, (fn, objtype) in otherdata['objects'].items():
for key, fn in otherdata['objects'].items():
if fn in docnames:
self.data['objects'][fullname] = (fn, objtype)
self.objects[key] = fn

def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
typ: str, target: str, node: pending_xref, contnode: Element
) -> Element:
objtypes = self.objtypes_for_role(typ)
# strip pointer asterisk
target = target.rstrip(' *')
# becase TypedField can generate xrefs
if target in CObject.stopwords:
return contnode
if target not in self.objects:
return None
obj = self.objects[target]
return make_refnode(builder, fromdocname, obj[0], 'c.' + target,
contnode, target)
for objtype in objtypes:
if (objtype, target) in self.objects:
docname = self.objects[objtype, target]
return make_refnode(builder, fromdocname, docname, 'c.' + target,
contnode, target)

return None

def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
target: str, node: pending_xref, contnode: Element
) -> List[Tuple[str, Element]]:
# strip pointer asterisk
target = target.rstrip(' *')
if target not in self.objects:
return []
obj = self.objects[target]
return [('c:' + self.role_for_objtype(obj[1]),
make_refnode(builder, fromdocname, obj[0], 'c.' + target,
contnode, target))]

results = [] # type: List[Tuple[str, Element]]
for (objtype, objname), docname in self.objects.items():
if target == objname:
role = 'c:' + self.role_for_objtype(objtype)
refnode = make_refnode(builder, fromdocname, docname, 'c.' + target,
contnode, target)
results.append((role, refnode))

return results

def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
for refname, (docname, type) in list(self.objects.items()):
yield (refname, refname, type, docname, 'c.' + refname, 1)
for (objtype, objname), docname in list(self.objects.items()):
yield (objname, objname, objtype, docname, 'c.' + objname, 1)


def setup(app: Sphinx) -> Dict[str, Any]:
app.add_domain(CDomain)

return {
'version': 'builtin',
'env_version': 1,
'env_version': 2,
'parallel_read_safe': True,
'parallel_write_safe': True,
}