mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
C++, cross-reference update
(see also sphinx-doc/sphinx#2057) - Elaborate the documentation in regard to templates. - Add shorthand for template declarations. - Add shorthand for (full) template specialisations. - Add better error messages for unparseable references.
This commit is contained in:
parent
346df54358
commit
5f09ecd4cf
@ -764,7 +764,7 @@ The C++ directives support the following info fields (see also :ref:`info-field-
|
||||
Cross-referencing
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
These roles link to the given object types:
|
||||
These roles link to the given declaration types:
|
||||
|
||||
.. rst:role:: cpp:any
|
||||
cpp:class
|
||||
@ -775,19 +775,19 @@ These roles link to the given object types:
|
||||
cpp:enum
|
||||
cpp:enumerator
|
||||
|
||||
Reference a C++ object by name. The name must be properly qualified relative to the
|
||||
position of the link.
|
||||
Reference a C++ declaration by name (see below for details).
|
||||
The name must be properly qualified relative to the position of the link.
|
||||
|
||||
.. note::
|
||||
.. admonition:: Note on References with Templates Parameters/Arguments
|
||||
|
||||
Sphinx's syntax to give references a custom title can interfere with
|
||||
linking to template classes, if nothing follows the closing angle
|
||||
bracket, i.e. if the link looks like this: ``:cpp:class:`MyClass<T>```.
|
||||
This is interpreted as a link to ``T`` with a title of ``MyClass``.
|
||||
In this case, please escape the opening angle bracket with a backslash,
|
||||
like this: ``:cpp:class:`MyClass\<T>```.
|
||||
Sphinx's syntax to give references a custom title can interfere with
|
||||
linking to template classes, if nothing follows the closing angle
|
||||
bracket, i.e. if the link looks like this: ``:cpp:class:`MyClass<int>```.
|
||||
This is interpreted as a link to ``int`` with a title of ``MyClass``.
|
||||
In this case, please escape the opening angle bracket with a backslash,
|
||||
like this: ``:cpp:class:`MyClass\<int>```.
|
||||
|
||||
.. admonition:: Note on References
|
||||
.. admonition:: Note on References to Overloaded Functions
|
||||
|
||||
It is currently impossible to link to a specific version of an
|
||||
overloaded method. Currently the C++ domain is the first domain
|
||||
@ -796,6 +796,80 @@ These roles link to the given object types:
|
||||
specific overload. Currently Sphinx will link to the first overloaded
|
||||
version of the method / function.
|
||||
|
||||
Declarations without template parameters and template arguments
|
||||
.................................................................
|
||||
|
||||
For linking to non-templated declarations the name must be a nested name,
|
||||
e.g., ``f`` or ``MyClass::f``.
|
||||
|
||||
Templated declarations
|
||||
......................
|
||||
|
||||
Assume the following declarations.
|
||||
|
||||
.. cpp:class:: Wrapper
|
||||
|
||||
.. cpp:class:: template<typename TOuter> \
|
||||
Outer
|
||||
|
||||
.. cpp:class:: template<typename TInner> \
|
||||
Inner
|
||||
|
||||
In general the reference must include the template paraemter declarations, e.g.,
|
||||
``template\<typename TOuter> Wrapper::Outer`` (:cpp:class:`template\<typename TOuter> Wrapper::Outer`).
|
||||
Currently the lookup only succeed if the template parameter identifiers are equal strings. That is,
|
||||
``template\<typename UOuter> Wrapper::Outer`` will not work.
|
||||
|
||||
The inner template class can not be directly referenced, unless the current namespace
|
||||
is changed or the following shorthand is used.
|
||||
If a template parameter list is omitted, then the lookup will assume either a template or a non-template,
|
||||
but not a partial template specialisation.
|
||||
This means the following references work.
|
||||
|
||||
- ``Wrapper::Outer`` (:cpp:class:`Wrapper::Outer`)
|
||||
- ``Wrapper::Outer::Inner`` (:cpp:class:`Wrapper::Outer::Inner`)
|
||||
- ``template\<typename TInner> Wrapper::Outer::Inner`` (:cpp:class:`template\<typename TInner> Wrapper::Outer::Inner`)
|
||||
|
||||
(Full) Template Specialisations
|
||||
................................
|
||||
|
||||
Assume the following declarations.
|
||||
|
||||
.. cpp:class:: template<typename TOuter> \
|
||||
Outer
|
||||
|
||||
.. cpp:class:: template<typename TInner> \
|
||||
Inner
|
||||
|
||||
.. cpp:class:: template<> \
|
||||
Outer<int>
|
||||
|
||||
.. cpp:class:: template<typename TInner> \
|
||||
Inner
|
||||
|
||||
.. cpp:class:: template<> \
|
||||
Inner<bool>
|
||||
|
||||
In general the reference must include a template parameter list for each template argument list.
|
||||
The full specialisation above can therefore be referenced with ``template\<> Outer\<int>`` (:cpp:class:`template\<> Outer\<int>`)
|
||||
and ``template\<> template\<> Outer\<int>::Inner\<bool>`` (:cpp:class:`template\<> template\<> Outer\<int>::Inner\<bool>`).
|
||||
As a shorthand the empty template parameter list can be omitted, e.g., ``Outer\<int>`` (:cpp:class:`Outer\<int>`)
|
||||
and ``Outer\<int>::Inner\<bool>`` (:cpp:class:`Outer\<int>::Inner\<bool>`).
|
||||
|
||||
|
||||
Partial Template Specialisations
|
||||
.................................
|
||||
|
||||
Assume the following declaration.
|
||||
|
||||
.. cpp:class:: template<typename T> \
|
||||
Outer<T*>
|
||||
|
||||
References to partial specialisations must always include the template parameter lists, e.g.,
|
||||
``template\<typename T> Outer\<T*>`` (:cpp:class:`template\<typename T> Outer\<T*>`).
|
||||
Currently the lookup only succeed if the template parameter identifiers are equal strings.
|
||||
|
||||
|
||||
|
||||
The Standard Domain
|
||||
-------------------
|
||||
|
@ -2169,7 +2169,8 @@ class Symbol(object):
|
||||
return ASTNestedName(names, rooted=False)
|
||||
|
||||
def _find_named_symbol(self, identifier, templateParams,
|
||||
templateArgs, operator):
|
||||
templateArgs, operator,
|
||||
templateShorthand):
|
||||
assert (identifier is None) != (operator is None)
|
||||
for s in self.children:
|
||||
if s.identifier != identifier:
|
||||
@ -2184,8 +2185,13 @@ class Symbol(object):
|
||||
if text_type(name) != text_type(operator):
|
||||
continue
|
||||
if (s.templateParams is None) != (templateParams is None):
|
||||
continue
|
||||
if s.templateParams:
|
||||
if templateParams is not None:
|
||||
# we query with params, they must match params
|
||||
continue
|
||||
if not templateShorthand:
|
||||
# we don't query with params, and we do care about them
|
||||
continue
|
||||
if templateParams:
|
||||
# TODO: do better comparison
|
||||
if text_type(s.templateParams) != text_type(templateParams):
|
||||
continue
|
||||
@ -2227,7 +2233,8 @@ class Symbol(object):
|
||||
symbol = parentSymbol._find_named_symbol(identifier,
|
||||
templateParams,
|
||||
templateArgs,
|
||||
operator=None)
|
||||
operator=None,
|
||||
templateShorthand=False)
|
||||
if symbol is None:
|
||||
symbol = Symbol(parent=parentSymbol, identifier=identifier,
|
||||
templateParams=templateParams,
|
||||
@ -2255,7 +2262,8 @@ class Symbol(object):
|
||||
symbol = parentSymbol._find_named_symbol(identifier,
|
||||
templateParams,
|
||||
templateArgs,
|
||||
operator)
|
||||
operator,
|
||||
templateShorthand=False)
|
||||
if symbol:
|
||||
if not declaration:
|
||||
# good, just a scope creation
|
||||
@ -2325,12 +2333,15 @@ class Symbol(object):
|
||||
templateArgs = name.templateArgs
|
||||
operator = None
|
||||
s = s._find_named_symbol(identifier, templateParams,
|
||||
templateArgs, operator)
|
||||
templateArgs, operator,
|
||||
templateShorthand=False)
|
||||
if not s:
|
||||
return None
|
||||
return s
|
||||
|
||||
def find_name(self, nestedName, templateDecls, specific_specialisation):
|
||||
def find_name(self, nestedName, templateDecls, templateShorthand):
|
||||
# templateShorthand: missing template parameter lists for templates is ok
|
||||
|
||||
# TODO: unify this with the _add_symbols
|
||||
# This condition should be checked at the parser level.
|
||||
assert len(templateDecls) <= nestedName.num_templates() + 1
|
||||
@ -2369,12 +2380,11 @@ class Symbol(object):
|
||||
symbol = parentSymbol._find_named_symbol(identifier,
|
||||
templateParams,
|
||||
templateArgs,
|
||||
operator)
|
||||
operator,
|
||||
templateShorthand=templateShorthand)
|
||||
if symbol:
|
||||
return symbol
|
||||
else:
|
||||
# TODO: search for version without template args,
|
||||
# if not specific_specialisation
|
||||
return None
|
||||
else:
|
||||
# there shouldn't be anything inside an operator
|
||||
@ -2389,7 +2399,8 @@ class Symbol(object):
|
||||
symbol = parentSymbol._find_named_symbol(identifier,
|
||||
templateParams,
|
||||
templateArgs,
|
||||
operator=None)
|
||||
operator=None,
|
||||
templateShorthand=templateShorthand)
|
||||
if symbol is None:
|
||||
# TODO: maybe search without template args
|
||||
return None
|
||||
@ -2503,8 +2514,7 @@ class DefinitionParser(object):
|
||||
def assert_end(self):
|
||||
self.skip_ws()
|
||||
if not self.eof:
|
||||
self.fail('expected end of definition, got %r' %
|
||||
self.definition[self.pos:])
|
||||
self.fail('Expected end of definition.')
|
||||
|
||||
def _parse_expression(self, end):
|
||||
# Stupidly "parse" an expression.
|
||||
@ -3195,7 +3205,8 @@ class DefinitionParser(object):
|
||||
else:
|
||||
return ASTTemplateDeclarationPrefix(templates)
|
||||
|
||||
def _check_template_consistency(self, nestedName, templatePrefix):
|
||||
def _check_template_consistency(self, nestedName, templatePrefix,
|
||||
fullSpecShorthand):
|
||||
numArgs = nestedName.num_templates()
|
||||
if not templatePrefix:
|
||||
numParams = 0
|
||||
@ -3207,15 +3218,16 @@ class DefinitionParser(object):
|
||||
% (numArgs, numParams))
|
||||
if numArgs > numParams:
|
||||
numExtra = numArgs - numParams
|
||||
msg = "Too many template argument lists compared to parameter" \
|
||||
" lists. Argument lists: %d, Parameter lists: %d," \
|
||||
" Extra empty parameters lists prepended: %d." \
|
||||
% (numArgs, numParams, numExtra)
|
||||
msg += " Declaration:\n\t"
|
||||
if templatePrefix:
|
||||
msg += "%s\n\t" % text_type(templatePrefix)
|
||||
msg += text_type(nestedName)
|
||||
self.warn(msg)
|
||||
if not fullSpecShorthand:
|
||||
msg = "Too many template argument lists compared to parameter" \
|
||||
" lists. Argument lists: %d, Parameter lists: %d," \
|
||||
" Extra empty parameters lists prepended: %d." \
|
||||
% (numArgs, numParams, numExtra)
|
||||
msg += " Declaration:\n\t"
|
||||
if templatePrefix:
|
||||
msg += "%s\n\t" % text_type(templatePrefix)
|
||||
msg += text_type(nestedName)
|
||||
self.warn(msg)
|
||||
|
||||
newTemplates = []
|
||||
for i in range(numExtra):
|
||||
@ -3273,14 +3285,16 @@ class DefinitionParser(object):
|
||||
else:
|
||||
assert False
|
||||
templatePrefix = self._check_template_consistency(declaration.name,
|
||||
templatePrefix)
|
||||
templatePrefix,
|
||||
fullSpecShorthand=False)
|
||||
return ASTDeclaration(objectType, visibility,
|
||||
templatePrefix, declaration)
|
||||
|
||||
def parse_namespace_object(self):
|
||||
templatePrefix = self._parse_template_declaration_prefix()
|
||||
name = self._parse_nested_name()
|
||||
templatePrefix = self._check_template_consistency(name, templatePrefix)
|
||||
templatePrefix = self._check_template_consistency(name, templatePrefix,
|
||||
fullSpecShorthand=False)
|
||||
res = ASTNamespace(name, templatePrefix)
|
||||
res.objectType = 'namespace'
|
||||
return res
|
||||
@ -3288,6 +3302,8 @@ class DefinitionParser(object):
|
||||
def parse_xref_object(self):
|
||||
templatePrefix = self._parse_template_declaration_prefix()
|
||||
name = self._parse_nested_name()
|
||||
templatePrefix = self._check_template_consistency(name, templatePrefix,
|
||||
fullSpecShorthand=True)
|
||||
res = ASTNamespace(name, templatePrefix)
|
||||
res.objectType = 'xref'
|
||||
return res
|
||||
@ -3708,10 +3724,10 @@ class CPPDomain(Domain):
|
||||
try:
|
||||
ast = parser.parse_xref_object()
|
||||
parser.skip_ws()
|
||||
if not parser.eof:
|
||||
raise DefinitionError('')
|
||||
except DefinitionError:
|
||||
warner.warn('Unparseable C++ cross-reference: %r' % target)
|
||||
parser.assert_end()
|
||||
except DefinitionError as e:
|
||||
warner.warn('Unparseable C++ cross-reference: %r\n%s'
|
||||
% (target, str(e.description)))
|
||||
return None, None
|
||||
parentKey = node.get("cpp:parentKey", None)
|
||||
rootSymbol = self.data['rootSymbol']
|
||||
@ -3730,7 +3746,7 @@ class CPPDomain(Domain):
|
||||
else:
|
||||
templateDecls = []
|
||||
s = parentSymbol.find_name(name, templateDecls,
|
||||
specific_specialisation=False)
|
||||
templateShorthand=True)
|
||||
if s is None or s.declaration is None:
|
||||
return None, None
|
||||
declaration = s.declaration
|
||||
|
Loading…
Reference in New Issue
Block a user