diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py index f442aa2caeb..aae0109ddeb 100644 --- a/sphinx/domains/__init__.py +++ b/sphinx/domains/__init__.py @@ -11,8 +11,8 @@ import copy from abc import ABC, abstractmethod -from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, List, NamedTuple, - Optional, Tuple, Type, Union, cast) +from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, List, NamedTuple, Optional, + Tuple, Type, Union, cast) from docutils import nodes from docutils.nodes import Element, Node, TextElement, system_message @@ -409,7 +409,7 @@ def get_full_qualified_name(self, node: Element) -> str: """Return full qualified name for given node.""" return None - def intersphinx_add_entries_v2(self, store: Any, + def intersphinx_add_entries_v2(self, store: Dict, data: Dict[Tuple[str, str], InventoryItemSet]) -> None: """Store the given *data* for later intersphinx reference resolution. @@ -512,7 +512,7 @@ def intersphinx_resolve_xref(self, env: "BuildEnvironment", store: Any, return None return self._intersphinx_resolve_xref_1(store, target, node, contnode, objtypes) - def intersphinx_resolve_any_xref(self, env: "BuildEnvironment", store: Any, + def intersphinx_resolve_any_xref(self, env: "BuildEnvironment", store: Dict, target: str, node: pending_xref, contnode: TextElement) -> Optional[Element]: """Resolve the pending_xref *node* with the given *typ* and *target* via intersphinx. diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 0fecbad6d41..034f1f77b98 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -9,8 +9,8 @@ """ import re -from typing import (Any, Callable, Dict, Generator, Iterator, List, Tuple, Type, TypeVar, - Union, cast) +from typing import (Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple, Type, + TypeVar, Union, cast) from docutils import nodes from docutils.nodes import Element, Node, TextElement, system_message @@ -38,6 +38,7 @@ octal_literal_re, verify_description_mode) from sphinx.util.docfields import Field, TypedField from sphinx.util.docutils import SphinxDirective +from sphinx.util.inventory import InventoryItemSet from sphinx.util.nodes import make_refnode logger = logging.getLogger(__name__) @@ -46,6 +47,7 @@ DeclarationType = Union[ "ASTStruct", "ASTUnion", "ASTEnum", "ASTEnumerator", "ASTType", "ASTTypeWithInit", "ASTMacro", + "ASTIntersphinx_v2", ] # https://en.cppreference.com/w/c/keyword @@ -1350,6 +1352,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: InventoryItemSet): + 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: + assert False # Should not happen + + @property + def function_params(self): + # 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, declaration: Union[DeclarationType, ASTFunctionParameter], @@ -3735,6 +3759,10 @@ class CDomain(Domain): 'objects': {}, # fullname -> docname, node_id, objtype } # type: Dict[str, Union[Symbol, Dict[str, Tuple[str, str, str]]]] + initial_intersphinx_inventory = { + 'root_symbol': Symbol(None, None, None, None, None), + } + def clear_doc(self, docname: str) -> None: if Symbol.debug_show_tree: print("clear_doc:", docname) @@ -3781,9 +3809,10 @@ def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None: 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, str]: + def _resolve_xref_in_tree(self, env: BuildEnvironment, root: Symbol, + softParent: bool, + typ: str, target: str, node: pending_xref, + contnode: Element) -> Tuple[Symbol, ASTNestedName]: parser = DefinitionParser(target, location=node, config=env.config) try: name = parser.parse_xref_object() @@ -3792,20 +3821,34 @@ def _resolve_xref_inner(self, env: BuildEnvironment, fromdocname: str, builder: location=node) return None, None parentKey = node.get("c:parent_key", None) # type: LookupKey - rootSymbol = self.data['root_symbol'] if parentKey: - parentSymbol = rootSymbol.direct_lookup(parentKey) # type: Symbol + parentSymbol = root.direct_lookup(parentKey) # type: Symbol if not parentSymbol: - print("Target: ", target) - print("ParentKey: ", parentKey) - print(rootSymbol.dump(1)) - assert parentSymbol # should be there + if softParent: + parentSymbol = root + else: + print("Target: ", target) + print("ParentKey: ", parentKey) + print(root.dump(1)) + assert False 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: + 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, contnode) + if s is None: + return None, None # TODO: check role type vs. object type @@ -3848,6 +3891,47 @@ 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[Tuple[str, str], InventoryItemSet]) -> None: + root = store['root_symbol'] # type: Symbol + for k, v in data.items(): + object_name, object_type = k + 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, v)) + root.add_declaration(decl, docname="$FakeIntersphinxDoc", line=0) + + def _intersphinx_resolve_xref_inner(self, env: "BuildEnvironment", store: Dict, + target: str, + node: pending_xref, contnode: TextElement, + typ: str) -> Optional[Element]: + if Symbol.debug_lookup: + print("C._intersphinx_resolve_xref_inner(type={}, target={})".format(typ, target)) + s, name = self._resolve_xref_in_tree(env, store['root_symbol'], + True, typ, target, node, contnode) + if s is None: + return None + assert s.declaration is not None + decl = cast(ASTIntersphinx_v2, s.declaration.declaration) + return decl.data.make_refnode(self.name, target, node, contnode) + + def intersphinx_resolve_xref(self, env: "BuildEnvironment", store: Dict, + typ: str, target: str, node: pending_xref, + contnode: TextElement) -> Optional[Element]: + return self._intersphinx_resolve_xref_inner(env, store, target, node, contnode, typ) + + def intersphinx_resolve_any_xref(self, env: "BuildEnvironment", store: Dict, + target: str, node: pending_xref, + contnode: TextElement) -> Optional[Element]: + with logging.suppress_logging(): + return self._intersphinx_resolve_xref_inner( + env, store, target, node, contnode, 'any') + def setup(app: Sphinx) -> Dict[str, Any]: app.add_domain(CDomain)