Skip to content

Commit

Permalink
C, intersphinx delegation
Browse files Browse the repository at this point in the history
  • Loading branch information
jakobandersen committed Mar 28, 2024
1 parent 6e6f779 commit 54beff2
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 11 deletions.
85 changes: 74 additions & 11 deletions sphinx/domains/c/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from typing import TYPE_CHECKING, Any, ClassVar
from typing import TYPE_CHECKING, Any, ClassVar, cast

from docutils import nodes
from docutils.parsers.rst import directives
Expand All @@ -13,6 +13,7 @@
from sphinx.domains.c._ast import (
ASTDeclaration,
ASTIdentifier,
ASTIntersphinx_v2,
ASTNestedName,
)
from sphinx.domains.c._ids import _macroKeywords, _max_id
Expand Down Expand Up @@ -666,6 +667,10 @@ class CDomain(Domain):
'objects': {}, # fullname -> docname, node_id, objtype
}

initial_intersphinx_inventory = {
'root_symbol': Symbol(None, None, None, None, None),
}

def clear_doc(self, docname: str) -> None:
if Symbol.debug_show_tree:
logger.debug("clear_doc: %s", docname)
Expand Down Expand Up @@ -712,9 +717,10 @@ def merge_domaindata(self, docnames: list[str], otherdata: dict[str, Any]) -> No
ourObjects[fullname] = (fn, id_, objtype)
# no need to warn on duplicates, the symbol merge already does that

def _resolve_xref_inner(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
typ: str, target: str, node: pending_xref,
contnode: Element) -> tuple[Element | None, str | None]:
def _resolve_xref_in_tree(self, env: BuildEnvironment, root: Symbol,
softParent: bool,
typ: str, target: str,
node: pending_xref) -> tuple[Symbol, ASTNestedName]:
parser = DefinitionParser(target, location=node, config=env.config)
try:
name = parser.parse_xref_object()
Expand All @@ -723,20 +729,32 @@ def _resolve_xref_inner(self, env: BuildEnvironment, fromdocname: str, builder:
location=node)
return None, None
parentKey: LookupKey = node.get("c:parent_key", None)
rootSymbol = self.data['root_symbol']
if parentKey:
parentSymbol: Symbol = rootSymbol.direct_lookup(parentKey)
parentSymbol: Symbol = root.direct_lookup(parentKey)
if not parentSymbol:
logger.debug("Target: %s", target)
logger.debug("ParentKey: %s", parentKey)
logger.debug(rootSymbol.dump(1))
assert parentSymbol # should be there
if softParent:
parentSymbol = root
else:
msg = f"Target: {target}\nParentKey: {parentKey}\n{root.dump(1)}\n"
raise AssertionError(msg)
else:
parentSymbol = rootSymbol
parentSymbol = root
s = parentSymbol.find_declaration(name, typ,
matchSelf=True, recurseInAnon=True)
if s is None or s.declaration is None:
return None, None
# TODO: conditionally warn about xrefs with incorrect tagging?
return s, name

def _resolve_xref_inner(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
typ: str, target: str, node: pending_xref,
contnode: Element) -> tuple[Element, str]:
if Symbol.debug_lookup:
Symbol.debug_print("C._resolve_xref_inner(type={}, target={})".format(typ, target))
s, name = self._resolve_xref_in_tree(env, self.data['root_symbol'],
False, typ, target, node)
if s is None:
return None, None

# TODO: check role type vs. object type

Expand Down Expand Up @@ -779,6 +797,51 @@ def get_objects(self) -> Iterator[tuple[str, str, str, str, str, int]]:
newestId = symbol.declaration.get_newest_id()
yield (name, dispname, objectType, docname, newestId, 1)

def intersphinx_add_entries_v2(self, store: dict,
data: dict[str, dict[str, Any]]) -> None:
root = store['root_symbol'] # type: Symbol
for object_type, per_type_data in data.items():
for object_name, item_set in per_type_data.items():
parser = DefinitionParser(
object_name, location=('intersphinx', 0), config=self.env.config)
try:
ast = parser._parse_nested_name()
except DefinitionError as e:
logger.warning("Error in C entry in intersphinx inventory:\n" + str(e))
continue
decl = ASTDeclaration(object_type, 'intersphinx',
ASTIntersphinx_v2(ast, item_set))
root.add_declaration(decl, docname="$FakeIntersphinxDoc", line=0)

def _intersphinx_resolve_xref_inner(self, env: "BuildEnvironment", store: dict,
target: str,
node: pending_xref,
typ: str) -> Any | None:
if Symbol.debug_lookup:
Symbol.debug_print(
f"C._intersphinx_resolve_xref_inner(type={typ}, target={target})")
s, name = self._resolve_xref_in_tree(env, store['root_symbol'],
True, typ, target, node)
if s is None:
return None
assert s.declaration is not None
decl = cast(ASTIntersphinx_v2, s.declaration.declaration)
return decl.data

def intersphinx_resolve_xref(self, env: "BuildEnvironment",
store: Any,
typ: str, target: str,
disabled_object_types: list[str],
node: pending_xref, contnode: Element
) -> Any | None:
if typ == 'any':
with logging.suppress_logging():
return self._intersphinx_resolve_xref_inner(
env, store, target, node, typ)
else:
return self._intersphinx_resolve_xref_inner(
env, store, target, node, typ)


def setup(app: Sphinx) -> ExtensionMetadata:
app.add_domain(CDomain)
Expand Down
23 changes: 23 additions & 0 deletions sphinx/domains/c/_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
DeclarationType = Union[
"ASTStruct", "ASTUnion", "ASTEnum", "ASTEnumerator",
"ASTType", "ASTTypeWithInit", "ASTMacro",
"ASTIntersphinx_v2",
]


Expand Down Expand Up @@ -1329,6 +1330,28 @@ def describe_signature(self, signode: TextElement, mode: str,
self.init.describe_signature(signode, 'markType', env, symbol)


class ASTIntersphinx_v2(ASTBaseBase):
def __init__(self, name: ASTNestedName, data: Any) -> None:
self.name = name
self.data = data

def _stringify(self, transform: StringifyTransform) -> str:
return transform(self.name) + " (has data)"

def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
return symbol.get_full_nested_name().get_id(version)

def describe_signature(self, signode: TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None:
raise AssertionError # should not happen

@property
def function_params(self) -> list[ASTFunctionParameter] | None:
# the v2 data does not contain actual declarations, but just names
# so return nothing here
return None


class ASTDeclaration(ASTBaseBase):
def __init__(self, objectType: str, directiveType: str | None,
declaration: DeclarationType | ASTFunctionParameter,
Expand Down

0 comments on commit 54beff2

Please sign in to comment.