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:
@@ -764,7 +764,7 @@ The C++ directives support the following info fields (see also :ref:`info-field-
|
|||||||
Cross-referencing
|
Cross-referencing
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
These roles link to the given object types:
|
These roles link to the given declaration types:
|
||||||
|
|
||||||
.. rst:role:: cpp:any
|
.. rst:role:: cpp:any
|
||||||
cpp:class
|
cpp:class
|
||||||
@@ -775,19 +775,19 @@ These roles link to the given object types:
|
|||||||
cpp:enum
|
cpp:enum
|
||||||
cpp:enumerator
|
cpp:enumerator
|
||||||
|
|
||||||
Reference a C++ object by name. The name must be properly qualified relative to the
|
Reference a C++ declaration by name (see below for details).
|
||||||
position of the link.
|
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
|
Sphinx's syntax to give references a custom title can interfere with
|
||||||
linking to template classes, if nothing follows the closing angle
|
linking to template classes, if nothing follows the closing angle
|
||||||
bracket, i.e. if the link looks like this: ``:cpp:class:`MyClass<T>```.
|
bracket, i.e. if the link looks like this: ``:cpp:class:`MyClass<int>```.
|
||||||
This is interpreted as a link to ``T`` with a title of ``MyClass``.
|
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,
|
In this case, please escape the opening angle bracket with a backslash,
|
||||||
like this: ``:cpp:class:`MyClass\<T>```.
|
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
|
It is currently impossible to link to a specific version of an
|
||||||
overloaded method. Currently the C++ domain is the first domain
|
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
|
specific overload. Currently Sphinx will link to the first overloaded
|
||||||
version of the method / function.
|
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
|
The Standard Domain
|
||||||
-------------------
|
-------------------
|
||||||
|
|||||||
@@ -2169,7 +2169,8 @@ class Symbol(object):
|
|||||||
return ASTNestedName(names, rooted=False)
|
return ASTNestedName(names, rooted=False)
|
||||||
|
|
||||||
def _find_named_symbol(self, identifier, templateParams,
|
def _find_named_symbol(self, identifier, templateParams,
|
||||||
templateArgs, operator):
|
templateArgs, operator,
|
||||||
|
templateShorthand):
|
||||||
assert (identifier is None) != (operator is None)
|
assert (identifier is None) != (operator is None)
|
||||||
for s in self.children:
|
for s in self.children:
|
||||||
if s.identifier != identifier:
|
if s.identifier != identifier:
|
||||||
@@ -2184,8 +2185,13 @@ class Symbol(object):
|
|||||||
if text_type(name) != text_type(operator):
|
if text_type(name) != text_type(operator):
|
||||||
continue
|
continue
|
||||||
if (s.templateParams is None) != (templateParams is None):
|
if (s.templateParams is None) != (templateParams is None):
|
||||||
continue
|
if templateParams is not None:
|
||||||
if s.templateParams:
|
# 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
|
# TODO: do better comparison
|
||||||
if text_type(s.templateParams) != text_type(templateParams):
|
if text_type(s.templateParams) != text_type(templateParams):
|
||||||
continue
|
continue
|
||||||
@@ -2227,7 +2233,8 @@ class Symbol(object):
|
|||||||
symbol = parentSymbol._find_named_symbol(identifier,
|
symbol = parentSymbol._find_named_symbol(identifier,
|
||||||
templateParams,
|
templateParams,
|
||||||
templateArgs,
|
templateArgs,
|
||||||
operator=None)
|
operator=None,
|
||||||
|
templateShorthand=False)
|
||||||
if symbol is None:
|
if symbol is None:
|
||||||
symbol = Symbol(parent=parentSymbol, identifier=identifier,
|
symbol = Symbol(parent=parentSymbol, identifier=identifier,
|
||||||
templateParams=templateParams,
|
templateParams=templateParams,
|
||||||
@@ -2255,7 +2262,8 @@ class Symbol(object):
|
|||||||
symbol = parentSymbol._find_named_symbol(identifier,
|
symbol = parentSymbol._find_named_symbol(identifier,
|
||||||
templateParams,
|
templateParams,
|
||||||
templateArgs,
|
templateArgs,
|
||||||
operator)
|
operator,
|
||||||
|
templateShorthand=False)
|
||||||
if symbol:
|
if symbol:
|
||||||
if not declaration:
|
if not declaration:
|
||||||
# good, just a scope creation
|
# good, just a scope creation
|
||||||
@@ -2325,12 +2333,15 @@ class Symbol(object):
|
|||||||
templateArgs = name.templateArgs
|
templateArgs = name.templateArgs
|
||||||
operator = None
|
operator = None
|
||||||
s = s._find_named_symbol(identifier, templateParams,
|
s = s._find_named_symbol(identifier, templateParams,
|
||||||
templateArgs, operator)
|
templateArgs, operator,
|
||||||
|
templateShorthand=False)
|
||||||
if not s:
|
if not s:
|
||||||
return None
|
return None
|
||||||
return s
|
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
|
# TODO: unify this with the _add_symbols
|
||||||
# This condition should be checked at the parser level.
|
# This condition should be checked at the parser level.
|
||||||
assert len(templateDecls) <= nestedName.num_templates() + 1
|
assert len(templateDecls) <= nestedName.num_templates() + 1
|
||||||
@@ -2369,12 +2380,11 @@ class Symbol(object):
|
|||||||
symbol = parentSymbol._find_named_symbol(identifier,
|
symbol = parentSymbol._find_named_symbol(identifier,
|
||||||
templateParams,
|
templateParams,
|
||||||
templateArgs,
|
templateArgs,
|
||||||
operator)
|
operator,
|
||||||
|
templateShorthand=templateShorthand)
|
||||||
if symbol:
|
if symbol:
|
||||||
return symbol
|
return symbol
|
||||||
else:
|
else:
|
||||||
# TODO: search for version without template args,
|
|
||||||
# if not specific_specialisation
|
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
# there shouldn't be anything inside an operator
|
# there shouldn't be anything inside an operator
|
||||||
@@ -2389,7 +2399,8 @@ class Symbol(object):
|
|||||||
symbol = parentSymbol._find_named_symbol(identifier,
|
symbol = parentSymbol._find_named_symbol(identifier,
|
||||||
templateParams,
|
templateParams,
|
||||||
templateArgs,
|
templateArgs,
|
||||||
operator=None)
|
operator=None,
|
||||||
|
templateShorthand=templateShorthand)
|
||||||
if symbol is None:
|
if symbol is None:
|
||||||
# TODO: maybe search without template args
|
# TODO: maybe search without template args
|
||||||
return None
|
return None
|
||||||
@@ -2503,8 +2514,7 @@ class DefinitionParser(object):
|
|||||||
def assert_end(self):
|
def assert_end(self):
|
||||||
self.skip_ws()
|
self.skip_ws()
|
||||||
if not self.eof:
|
if not self.eof:
|
||||||
self.fail('expected end of definition, got %r' %
|
self.fail('Expected end of definition.')
|
||||||
self.definition[self.pos:])
|
|
||||||
|
|
||||||
def _parse_expression(self, end):
|
def _parse_expression(self, end):
|
||||||
# Stupidly "parse" an expression.
|
# Stupidly "parse" an expression.
|
||||||
@@ -3195,7 +3205,8 @@ class DefinitionParser(object):
|
|||||||
else:
|
else:
|
||||||
return ASTTemplateDeclarationPrefix(templates)
|
return ASTTemplateDeclarationPrefix(templates)
|
||||||
|
|
||||||
def _check_template_consistency(self, nestedName, templatePrefix):
|
def _check_template_consistency(self, nestedName, templatePrefix,
|
||||||
|
fullSpecShorthand):
|
||||||
numArgs = nestedName.num_templates()
|
numArgs = nestedName.num_templates()
|
||||||
if not templatePrefix:
|
if not templatePrefix:
|
||||||
numParams = 0
|
numParams = 0
|
||||||
@@ -3207,15 +3218,16 @@ class DefinitionParser(object):
|
|||||||
% (numArgs, numParams))
|
% (numArgs, numParams))
|
||||||
if numArgs > numParams:
|
if numArgs > numParams:
|
||||||
numExtra = numArgs - numParams
|
numExtra = numArgs - numParams
|
||||||
msg = "Too many template argument lists compared to parameter" \
|
if not fullSpecShorthand:
|
||||||
" lists. Argument lists: %d, Parameter lists: %d," \
|
msg = "Too many template argument lists compared to parameter" \
|
||||||
" Extra empty parameters lists prepended: %d." \
|
" lists. Argument lists: %d, Parameter lists: %d," \
|
||||||
% (numArgs, numParams, numExtra)
|
" Extra empty parameters lists prepended: %d." \
|
||||||
msg += " Declaration:\n\t"
|
% (numArgs, numParams, numExtra)
|
||||||
if templatePrefix:
|
msg += " Declaration:\n\t"
|
||||||
msg += "%s\n\t" % text_type(templatePrefix)
|
if templatePrefix:
|
||||||
msg += text_type(nestedName)
|
msg += "%s\n\t" % text_type(templatePrefix)
|
||||||
self.warn(msg)
|
msg += text_type(nestedName)
|
||||||
|
self.warn(msg)
|
||||||
|
|
||||||
newTemplates = []
|
newTemplates = []
|
||||||
for i in range(numExtra):
|
for i in range(numExtra):
|
||||||
@@ -3273,14 +3285,16 @@ class DefinitionParser(object):
|
|||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
templatePrefix = self._check_template_consistency(declaration.name,
|
templatePrefix = self._check_template_consistency(declaration.name,
|
||||||
templatePrefix)
|
templatePrefix,
|
||||||
|
fullSpecShorthand=False)
|
||||||
return ASTDeclaration(objectType, visibility,
|
return ASTDeclaration(objectType, visibility,
|
||||||
templatePrefix, declaration)
|
templatePrefix, declaration)
|
||||||
|
|
||||||
def parse_namespace_object(self):
|
def parse_namespace_object(self):
|
||||||
templatePrefix = self._parse_template_declaration_prefix()
|
templatePrefix = self._parse_template_declaration_prefix()
|
||||||
name = self._parse_nested_name()
|
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 = ASTNamespace(name, templatePrefix)
|
||||||
res.objectType = 'namespace'
|
res.objectType = 'namespace'
|
||||||
return res
|
return res
|
||||||
@@ -3288,6 +3302,8 @@ class DefinitionParser(object):
|
|||||||
def parse_xref_object(self):
|
def parse_xref_object(self):
|
||||||
templatePrefix = self._parse_template_declaration_prefix()
|
templatePrefix = self._parse_template_declaration_prefix()
|
||||||
name = self._parse_nested_name()
|
name = self._parse_nested_name()
|
||||||
|
templatePrefix = self._check_template_consistency(name, templatePrefix,
|
||||||
|
fullSpecShorthand=True)
|
||||||
res = ASTNamespace(name, templatePrefix)
|
res = ASTNamespace(name, templatePrefix)
|
||||||
res.objectType = 'xref'
|
res.objectType = 'xref'
|
||||||
return res
|
return res
|
||||||
@@ -3708,10 +3724,10 @@ class CPPDomain(Domain):
|
|||||||
try:
|
try:
|
||||||
ast = parser.parse_xref_object()
|
ast = parser.parse_xref_object()
|
||||||
parser.skip_ws()
|
parser.skip_ws()
|
||||||
if not parser.eof:
|
parser.assert_end()
|
||||||
raise DefinitionError('')
|
except DefinitionError as e:
|
||||||
except DefinitionError:
|
warner.warn('Unparseable C++ cross-reference: %r\n%s'
|
||||||
warner.warn('Unparseable C++ cross-reference: %r' % target)
|
% (target, str(e.description)))
|
||||||
return None, None
|
return None, None
|
||||||
parentKey = node.get("cpp:parentKey", None)
|
parentKey = node.get("cpp:parentKey", None)
|
||||||
rootSymbol = self.data['rootSymbol']
|
rootSymbol = self.data['rootSymbol']
|
||||||
@@ -3730,7 +3746,7 @@ class CPPDomain(Domain):
|
|||||||
else:
|
else:
|
||||||
templateDecls = []
|
templateDecls = []
|
||||||
s = parentSymbol.find_name(name, templateDecls,
|
s = parentSymbol.find_name(name, templateDecls,
|
||||||
specific_specialisation=False)
|
templateShorthand=True)
|
||||||
if s is None or s.declaration is None:
|
if s is None or s.declaration is None:
|
||||||
return None, None
|
return None, None
|
||||||
declaration = s.declaration
|
declaration = s.declaration
|
||||||
|
|||||||
Reference in New Issue
Block a user