mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
parent
ed29c298a6
commit
89c3dd81c3
2
CHANGES
2
CHANGES
@ -11,6 +11,8 @@ Features added
|
||||
an element is already present (built-in or added by another extension).
|
||||
* #1935: Make "numfig_format" overridable in latex_elements.
|
||||
* #1909: Add "doc" references to Intersphinx inventories.
|
||||
* C++ type alias support (e.g., `.. type:: T = int`)
|
||||
* C++ template support for classes, functions, type aliases, and variables (#1729, #1314).
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
153
doc/domains.rst
153
doc/domains.rst
@ -528,60 +528,111 @@ a visibility statement (``public``, ``private`` or ``protected``).
|
||||
|
||||
Describe a class/struct, possibly with specification of inheritance, e.g.,::
|
||||
|
||||
.. cpp:class:: SomeName::SomeClass : public MyBase, MyOtherBase
|
||||
.. cpp:class:: MyClass : public MyBase, MyOtherBase
|
||||
|
||||
.. rst:directive:: .. cpp:function:: (member-)function prototype
|
||||
The class can be directly declared inside a nested scope, e.g.,::
|
||||
|
||||
.. cpp:class:: OuterScope::MyClass : public MyBase, MyOtherBase
|
||||
|
||||
A template class can be declared::
|
||||
|
||||
.. cpp:class:: template<typename T, std::size_t N> std::array
|
||||
|
||||
or with a line beak::
|
||||
|
||||
.. cpp:class:: template<typename T, std::size_t N> \
|
||||
std::array
|
||||
|
||||
Full and partial template specialisations can be declared::
|
||||
|
||||
.. cpp::class:: template<> \
|
||||
std::array<bool, 256>
|
||||
|
||||
.. cpp::class:: template<typename T> \
|
||||
std::array<T, 42>
|
||||
|
||||
|
||||
.. rst:directive:: .. cpp:function:: (member) function prototype
|
||||
|
||||
Describe a function or member function, e.g.,::
|
||||
|
||||
.. cpp:function:: bool namespaced::theclass::method(int arg1, std::string arg2)
|
||||
.. cpp:function:: bool myMethod(int arg1, std::string arg2)
|
||||
|
||||
Describes a method with parameters and types.
|
||||
A function with parameters and types.
|
||||
|
||||
.. cpp:function:: bool namespaced::theclass::method(T1, T2)
|
||||
.. cpp:function:: bool myMethod(int, double)
|
||||
|
||||
Describes a method with unnamed parameters.
|
||||
A function with unnamed parameters.
|
||||
|
||||
.. cpp:function:: const T &array<T>::operator[]() const
|
||||
.. cpp:function:: const T &MyClass::operator[](std::size_t i) const
|
||||
|
||||
Describes the constant indexing operator of a templated array.
|
||||
An overload for the indexing operator.
|
||||
|
||||
.. cpp:function:: operator bool() const
|
||||
|
||||
Describe a casting operator here.
|
||||
A casting operator.
|
||||
|
||||
.. cpp:function:: constexpr void foo(std::string &bar[2]) noexcept
|
||||
|
||||
Describe a constexpr function here.
|
||||
A constexpr function.
|
||||
|
||||
.. cpp:function:: MyClass::MyClass(const MyClass&) = default
|
||||
|
||||
Describe a copy constructor with default implementation.
|
||||
A copy constructor with default implementation.
|
||||
|
||||
.. rst:directive:: .. cpp:member:: (member-)variable declaration
|
||||
.. cpp:var:: (member-)variable declaration
|
||||
Function templates can also be described::
|
||||
|
||||
.. cpp:function:: template<typename U> \
|
||||
void print(U &&u)
|
||||
|
||||
and function template specialisations::
|
||||
|
||||
.. cpp:function:: template<> \
|
||||
void print(int i)
|
||||
|
||||
|
||||
.. rst:directive:: .. cpp:member:: (member) variable declaration
|
||||
.. cpp:var:: (member) variable declaration
|
||||
|
||||
Describe a varible or member variable, e.g.,::
|
||||
|
||||
.. cpp:member:: std::string theclass::name
|
||||
.. cpp:member:: std::string MyClass::myMember
|
||||
|
||||
.. cpp:member:: std::string theclass::name[N][M]
|
||||
.. cpp:var:: std::string MyClass::myOtherMember[N][M]
|
||||
|
||||
.. cpp:member:: int a = 42
|
||||
|
||||
.. rst:directive:: .. cpp:type:: typedef-like declaration
|
||||
.. cpp:type:: name
|
||||
Variable templates can also be described::
|
||||
|
||||
Describe a type as in a typedef declaration, or the name of a type with unspecified type, e.g.,::
|
||||
.. cpp:member:: template<class T> \
|
||||
constexpr T pi = T(3.1415926535897932385)
|
||||
|
||||
|
||||
.. rst:directive:: .. cpp:type:: typedef declaration
|
||||
.. cpp:type:: name
|
||||
.. cpp:type:: type alias declaration
|
||||
|
||||
Describe a type as in a typedef declaration, a type alias declaration,
|
||||
or simply the name of a type with unspecified type, e.g.,::
|
||||
|
||||
.. cpp:type:: std::vector<int> MyList
|
||||
|
||||
A typedef-like declaration of a type.
|
||||
|
||||
.. cpp:type:: theclass::const_iterator
|
||||
.. cpp:type:: MyContainer::const_iterator
|
||||
|
||||
Declaration of a type alias with unspecified type.
|
||||
|
||||
.. cpp:type:: MyType = std::unordered_map<int, std::string>
|
||||
|
||||
Declaration of a type alias.
|
||||
|
||||
A type alias can also be templated::
|
||||
|
||||
.. cpp:type:: template<typename T>
|
||||
MyContainer = std::vector<T>
|
||||
|
||||
|
||||
.. rst:directive:: .. cpp:enum:: unscoped enum declaration
|
||||
.. cpp:enum-struct:: scoped enum declaration
|
||||
.. cpp:enum-class:: scoped enum declaration
|
||||
@ -610,24 +661,68 @@ a visibility statement (``public``, ``private`` or ``protected``).
|
||||
.. rst:directive:: .. cpp:enumerator:: name
|
||||
.. cpp:enumerator:: name = constant
|
||||
|
||||
Describe an enumerator, optionally with its value defined.
|
||||
Describe an enumerator, optionally with its value defined, e.g.,::
|
||||
|
||||
.. rst:directive:: .. cpp:namespace:: namespace
|
||||
.. cpp::enumerator:: MyEnum::myEnumerator
|
||||
|
||||
Select the current namespace for the subsequent objects. Note that the namespace
|
||||
does not need to correspond to C++ namespaces, but can end in names of classes, e.g.,::
|
||||
.. cpp::enumerator:: MyEnum::myOtherEnumerator = 42
|
||||
|
||||
|
||||
Namespacing
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. rst:directive:: .. cpp:namespace:: scope specification
|
||||
|
||||
Declarations in the C++ doamin are as default placed in global scope.
|
||||
The ``namespace`` directive changes the current scope for the subsequent objects.
|
||||
Note that the namespace does not need to correspond to C++ namespaces,
|
||||
but can end in names of classes, e.g.,::
|
||||
|
||||
.. cpp:namespace:: Namespace1::Namespace2::SomeClass::AnInnerClass
|
||||
|
||||
All subsequent objects will be defined as if their name were declared with the namespace
|
||||
prepended. The subsequent cross-references will be searched for by both their specified name
|
||||
and with the namespace prepended.
|
||||
All subsequent objects will be defined as if their name were declared with the scope
|
||||
prepended. The subsequent cross-references will be searched for starting in the current scope.
|
||||
|
||||
Using ``NULL``, ``0``, or ``nullptr`` as the namespace will reset it to the global namespace.
|
||||
|
||||
A namespace declaration can also be templated, e.g.,::
|
||||
|
||||
.. cpp:class:: template<typename T> \
|
||||
std::vector
|
||||
|
||||
.. cpp:namespace:: template<typename T> std::vector
|
||||
|
||||
.. cpp:function:: std::size_t size() const
|
||||
|
||||
declares ``size`` as a member function of the template class ``std::vector``.
|
||||
Equivalently this could have been declared using::
|
||||
|
||||
.. cpp:class:: template<typename T> \
|
||||
std::vector
|
||||
|
||||
.. cpp:function:: std::size_t size() const
|
||||
|
||||
or:::
|
||||
|
||||
.. cpp:class:: template<typename T> \
|
||||
std::vector
|
||||
|
||||
|
||||
Info field lists
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The C++ directives support the following info fields (see also :ref:`info-field-lists`):
|
||||
|
||||
* `param`, `parameter`, `arg`, `argument`: Description of a parameter.
|
||||
* `returns`, `return`: Description of a return value.
|
||||
* `throws`, `throw`, `exception`: Description of a possibly thrown exception.
|
||||
|
||||
|
||||
.. _cpp-roles:
|
||||
|
||||
Cross-referencing
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
These roles link to the given object types:
|
||||
|
||||
.. rst:role:: cpp:any
|
||||
@ -660,12 +755,6 @@ These roles link to the given object types:
|
||||
specific overload. Currently Sphinx will link to the first overloaded
|
||||
version of the method / function.
|
||||
|
||||
.. admonition:: Note on Template Delcarations
|
||||
|
||||
The C++ domain currently does not support template classes/functions/aliases/variables
|
||||
(e.g., ``template<typename T> MyClass``), only template instantiations
|
||||
(e.g., ``MyClass<T>``).
|
||||
|
||||
|
||||
The Standard Domain
|
||||
-------------------
|
||||
|
@ -24,7 +24,6 @@ from sphinx.util.nodes import make_refnode
|
||||
from sphinx.util.compat import Directive
|
||||
from sphinx.util.pycompat import UnicodeMixin
|
||||
from sphinx.util.docfields import Field, GroupedField
|
||||
from fileinput import lineno
|
||||
|
||||
"""
|
||||
Important note on ids:
|
||||
@ -60,15 +59,7 @@ from fileinput import lineno
|
||||
"class" "..."[opt] identifier[opt]
|
||||
| "template" "<" template-parameter-list ">"
|
||||
"class" identifier[opt] "=" id-expression
|
||||
-> # also, from C++17 we can have "struct" in template templates
|
||||
(
|
||||
"class"
|
||||
| "struct"
|
||||
| "template" "<" template-parameter-list ">"
|
||||
) (
|
||||
"..."[opt] identifier[opt]
|
||||
| identifier[opt] "=" type-id
|
||||
)
|
||||
# also, from C++17 we can have "typname" in template templates
|
||||
templateDeclPrefix ->
|
||||
"template" "<" template-parameter-list ">"
|
||||
|
||||
@ -549,10 +540,159 @@ class ASTIdentifier(ASTBase):
|
||||
signode += pnode
|
||||
elif mode == 'lastIsName':
|
||||
signode += addnodes.desc_name(self.identifier, self.identifier)
|
||||
elif mode == 'noneIsName':
|
||||
signode += nodes.Text(self.identifier)
|
||||
else:
|
||||
raise Exception('Unknown description mode: %s' % mode)
|
||||
|
||||
|
||||
class ASTTemplateKeyParamPackIdDefault(ASTBase):
|
||||
def __init__(self, key, identifier, parameterPack, default):
|
||||
assert key
|
||||
if parameterPack:
|
||||
assert default is None
|
||||
self.key = key
|
||||
self.identifier = identifier
|
||||
self.parameterPack = parameterPack
|
||||
self.default = default
|
||||
|
||||
def get_identifier(self):
|
||||
return self.identifier
|
||||
|
||||
def get_id_v2(self):
|
||||
# this is not part of the normal name mangling in C++
|
||||
res = []
|
||||
if self.parameterPack:
|
||||
res.append('Dp')
|
||||
else:
|
||||
res.append('0') # we need to put something
|
||||
return ''.join(res)
|
||||
|
||||
def __unicode__(self):
|
||||
res = [self.key]
|
||||
if self.parameterPack:
|
||||
if self.identifier:
|
||||
res.append(' ')
|
||||
res.append('...')
|
||||
if self.identifier:
|
||||
if not self.parameterPack:
|
||||
res.append(' ')
|
||||
res.append(text_type(self.identifier))
|
||||
if self.default:
|
||||
res.append(' = ')
|
||||
res.append(text_type(self.default))
|
||||
return ''.join(res)
|
||||
|
||||
def describe_signature(self, signode, mode, env, symbol):
|
||||
signode += nodes.Text(self.key)
|
||||
if self.parameterPack:
|
||||
if self.identifier:
|
||||
signode += nodes.Text(' ')
|
||||
signode += nodes.Text('...')
|
||||
if self.identifier:
|
||||
if not self.parameterPack:
|
||||
signode += nodes.Text(' ')
|
||||
self.identifier.describe_signature(signode, mode, env, '', symbol)
|
||||
if self.default:
|
||||
signode += nodes.Text(' = ')
|
||||
self.default.describe_signature(signode, 'markType', env, symbol)
|
||||
|
||||
|
||||
class ASTTemplateParamType(ASTBase):
|
||||
def __init__(self, data):
|
||||
assert data
|
||||
self.data = data
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
id = self.get_identifier()
|
||||
return ASTNestedName([ASTNestedNameElement(id, None)], rooted=False)
|
||||
|
||||
def get_identifier(self):
|
||||
return self.data.get_identifier()
|
||||
|
||||
def get_id_v2(self, objectType=None, symbol=None):
|
||||
# this is not part of the normal name mangling in C++
|
||||
if symbol:
|
||||
# the anchor will be our parent
|
||||
return symbol.parent.declaration.get_id_v2(prefixed=None)
|
||||
else:
|
||||
return self.data.get_id_v2()
|
||||
|
||||
def __unicode__(self):
|
||||
return text_type(self.data)
|
||||
|
||||
def describe_signature(self, signode, mode, env, symbol):
|
||||
self.data.describe_signature(signode, mode, env, symbol)
|
||||
|
||||
|
||||
class ASTTemplateParamTemplateType(ASTBase):
|
||||
def __init__(self, nestedParams, data):
|
||||
assert nestedParams
|
||||
assert data
|
||||
self.nestedParams = nestedParams
|
||||
self.data = data
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
id = self.get_identifier()
|
||||
return ASTNestedName([ASTNestedNameElement(id, None)], rooted=False)
|
||||
|
||||
def get_identifier(self):
|
||||
return self.data.get_identifier()
|
||||
|
||||
def get_id_v2(self, objectType=None, symbol=None):
|
||||
# this is not part of the normal name mangling in C++
|
||||
if symbol:
|
||||
# the anchor will be our parent
|
||||
return symbol.parent.declaration.get_id_v2(prefixed=None)
|
||||
else:
|
||||
return self.nestedParams.get_id_v2() + self.data.get_id_v2()
|
||||
|
||||
def __unicode__(self):
|
||||
return text_type(self.nestedParams) + text_type(self.data)
|
||||
|
||||
def describe_signature(self, signode, mode, env, symbol):
|
||||
self.nestedParams.describe_signature(signode, 'noneIsName', env, symbol)
|
||||
signode += nodes.Text(' ')
|
||||
self.data.describe_signature(signode, mode, env, symbol)
|
||||
|
||||
|
||||
class ASTTemplateParamNonType(ASTBase):
|
||||
def __init__(self, param):
|
||||
assert param
|
||||
self.param = param
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
id = self.get_identifier()
|
||||
return ASTNestedName([ASTNestedNameElement(id, None)], rooted=False)
|
||||
|
||||
def get_identifier(self):
|
||||
name = self.param.name
|
||||
if name:
|
||||
assert len(name.names) == 1
|
||||
assert name.names[0].identifier
|
||||
assert not name.names[0].templateArgs
|
||||
return name.names[0].identifier
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_id_v2(self, objectType=None, symbol=None):
|
||||
# this is not part of the normal name mangling in C++
|
||||
if symbol:
|
||||
# the anchor will be our parent
|
||||
return symbol.parent.declaration.get_id_v2(prefixed=None)
|
||||
else:
|
||||
return '_' + self.param.get_id_v2()
|
||||
|
||||
def __unicode__(self):
|
||||
return text_type(self.param)
|
||||
|
||||
def describe_signature(self, signode, mode, env, symbol):
|
||||
self.param.describe_signature(signode, mode, env, symbol)
|
||||
|
||||
|
||||
class ASTTemplateParams(ASTBase):
|
||||
def __init__(self, params):
|
||||
assert params is not None
|
||||
@ -561,8 +701,8 @@ class ASTTemplateParams(ASTBase):
|
||||
def get_id_v2(self):
|
||||
res = []
|
||||
res.append("I")
|
||||
for arg in self.params:
|
||||
raise NotImplementedError("")
|
||||
for param in self.params:
|
||||
res.append(param.get_id_v2())
|
||||
res.append("E")
|
||||
return ''.join(res)
|
||||
|
||||
@ -575,8 +715,12 @@ class ASTTemplateParams(ASTBase):
|
||||
|
||||
def describe_signature(self, signode, mode, env, symbol):
|
||||
signode += nodes.Text("template<")
|
||||
first = True
|
||||
for param in self.params:
|
||||
raise NotImplementedError("")
|
||||
if not first:
|
||||
signode += nodes.Text(", ")
|
||||
first = False
|
||||
param.describe_signature(signode, mode, env, symbol)
|
||||
signode += nodes.Text(">")
|
||||
|
||||
|
||||
@ -605,7 +749,7 @@ class ASTTemplateDeclarationPrefix(ASTBase):
|
||||
_verify_description_mode(mode)
|
||||
for t in self.templates:
|
||||
templateNode = nodes.line()
|
||||
t.describe_signature(templateNode, mode, env, symbol)
|
||||
t.describe_signature(templateNode, 'lastIsName', env, symbol)
|
||||
signode += templateNode
|
||||
|
||||
|
||||
@ -816,6 +960,8 @@ class ASTNestedName(ASTBase):
|
||||
for n in self.names[:-1]:
|
||||
addname.append(text_type(n))
|
||||
addname = '::'.join(addname)
|
||||
if len(self.names) > 1:
|
||||
addname += '::'
|
||||
signode += addnodes.desc_addname(addname, addname)
|
||||
self.names[-1].describe_signature(signode, mode, env, '', symbol)
|
||||
elif mode == 'noneIsName':
|
||||
@ -1563,7 +1709,6 @@ class ASTType(ASTBase):
|
||||
@property
|
||||
def name(self):
|
||||
name = self.decl.name
|
||||
assert name
|
||||
return name
|
||||
|
||||
def get_id_v1(self, objectType=None, symbol=None):
|
||||
@ -1664,6 +1809,33 @@ class ASTTypeWithInit(ASTBase):
|
||||
self.init.describe_signature(signode, mode)
|
||||
|
||||
|
||||
class ASTTypeUsing(ASTBase):
|
||||
def __init__(self, name, type):
|
||||
self.name = name
|
||||
self.type = type
|
||||
|
||||
def get_id_v1(self, objectType=None, symbol=None):
|
||||
return None
|
||||
|
||||
def get_id_v2(self, objectType=None, symbol=None):
|
||||
return symbol.get_full_nested_name().get_id_v2()
|
||||
|
||||
def __unicode__(self):
|
||||
res = []
|
||||
res.append(text_type(self.name))
|
||||
if self.type:
|
||||
res.append(' = ')
|
||||
res.append(text_type(self.type))
|
||||
return u''.join(res)
|
||||
|
||||
def describe_signature(self, signode, mode, env, symbol):
|
||||
_verify_description_mode(mode)
|
||||
self.name.describe_signature(signode, mode, env, symbol=symbol)
|
||||
if self.type:
|
||||
signode += nodes.Text(' = ')
|
||||
self.type.describe_signature(signode, 'markType', env, symbol=symbol)
|
||||
|
||||
|
||||
class ASTBaseClass(ASTBase):
|
||||
def __init__(self, name, visibility):
|
||||
self.name = name
|
||||
@ -1787,10 +1959,10 @@ class ASTDeclaration(ASTBase):
|
||||
def __init__(self, objectType, visibility, templatePrefix, declaration):
|
||||
self.objectType = objectType
|
||||
self.visibility = visibility
|
||||
self.templatePrefix = templatePrefix # will be set to None after symbol
|
||||
self.templatePrefix = templatePrefix
|
||||
self.declaration = declaration
|
||||
|
||||
self.symbol = None # set by Symbol.add_declaration
|
||||
self.symbol = None
|
||||
self.declarationScope = None # set by Symbol.add_declaration
|
||||
|
||||
@property
|
||||
@ -1802,13 +1974,19 @@ class ASTDeclaration(ASTBase):
|
||||
raise NoOldIdError()
|
||||
return self.declaration.get_id_v1(self.objectType, self.symbol)
|
||||
|
||||
def get_id_v2(self):
|
||||
res = [_id_prefix_v2]
|
||||
def get_id_v2(self, prefixed=True):
|
||||
if prefixed:
|
||||
res = [_id_prefix_v2]
|
||||
else:
|
||||
res = []
|
||||
if self.templatePrefix:
|
||||
res.append(self.templatePrefix.get_id_v2())
|
||||
res.append(self.declaration.get_id_v2(self.objectType, self.symbol))
|
||||
return u''.join(res)
|
||||
|
||||
def get_newest_id(self):
|
||||
return self.get_id_v2()
|
||||
|
||||
def __unicode__(self):
|
||||
res = []
|
||||
if self.visibility and self.visibility != "public":
|
||||
@ -1823,7 +2001,7 @@ class ASTDeclaration(ASTBase):
|
||||
_verify_description_mode(mode)
|
||||
assert self.symbol
|
||||
if not self.declarationScope:
|
||||
raise NotImplementedError("hmm, %s" % text_type(self))
|
||||
raise NotImplementedError("hmm, a bug? %s" % text_type(self))
|
||||
assert self.declarationScope
|
||||
if self.visibility and self.visibility != "public":
|
||||
signode += addnodes.desc_annotation(self.visibility + " ",
|
||||
@ -1883,37 +2061,53 @@ class Symbol(object):
|
||||
self.parent.children.append(self)
|
||||
if self.declaration:
|
||||
self.declaration.symbol = self
|
||||
# add symbols for the template params
|
||||
# (do it after self.children has been initialised
|
||||
if self.templateParams:
|
||||
for p in self.templateParams.params:
|
||||
if not p.get_identifier():
|
||||
continue
|
||||
decl = ASTDeclaration('templateParam', None, None, p)
|
||||
nne = ASTNestedNameElement(p.get_identifier(), None)
|
||||
nn = ASTNestedName([nne], rooted=False)
|
||||
self._add_symbols(nn, [], decl)
|
||||
|
||||
def get_all_symbols(self):
|
||||
# assumed to be in post-order
|
||||
for sChild in self.children:
|
||||
for s in sChild.get_all_symbols():
|
||||
yield s
|
||||
yield self
|
||||
|
||||
def get_lookup_key(self):
|
||||
if not self.parent:
|
||||
# specialise for the root
|
||||
return None, None
|
||||
return None
|
||||
symbols = []
|
||||
templateDecls = []
|
||||
s = self
|
||||
while s.parent:
|
||||
symbols.append(s)
|
||||
if s.templateParams:
|
||||
templateDecls.append(s.templateParams)
|
||||
s = s.parent
|
||||
symbols.reverse()
|
||||
templateDecls.reverse()
|
||||
names = []
|
||||
key = []
|
||||
for s in symbols[:-1]:
|
||||
assert s.identifier
|
||||
nne = ASTNestedNameElement(s.identifier, s.templateArgs)
|
||||
names.append(nne)
|
||||
key.append((nne, s.templateParams))
|
||||
s = symbols[-1]
|
||||
if s.identifier:
|
||||
names.append(ASTNestedNameElement(s.identifier, s.templateArgs))
|
||||
nne = ASTNestedNameElement(s.identifier, s.templateArgs)
|
||||
else:
|
||||
assert self.declaration
|
||||
names.append(s.declaration.name.names[-1])
|
||||
return ASTNestedName(names, rooted=False), templateDecls
|
||||
nne = s.declaration.name.names[-1]
|
||||
key.append((nne, s.templateParams))
|
||||
return key
|
||||
|
||||
def get_full_nested_name(self):
|
||||
names, _ = self.get_lookup_key()
|
||||
return names
|
||||
names = []
|
||||
for nne, _ in self.get_lookup_key():
|
||||
names.append(nne)
|
||||
return ASTNestedName(names, rooted=False)
|
||||
|
||||
def _find_named_symbol(self, identifier, templateParams,
|
||||
templateArgs, operator):
|
||||
@ -1971,6 +2165,9 @@ class Symbol(object):
|
||||
templateArgs = name.templateArgs
|
||||
operator = None
|
||||
if iTemplateDecl < len(templateDecls):
|
||||
if iTemplateDecl + 1 != len(templateDecls):
|
||||
print(text_type(templateDecls))
|
||||
print(text_type(nestedName))
|
||||
assert iTemplateDecl + 1 == len(templateDecls)
|
||||
templateParams = templateDecls[iTemplateDecl]
|
||||
else:
|
||||
@ -2016,9 +2213,9 @@ class Symbol(object):
|
||||
identifier = name.identifier
|
||||
templateArgs = name.templateArgs
|
||||
if templateArgs:
|
||||
assert iTemplateDecl + 1 < len(templateDecls)
|
||||
|
||||
raise NotImplementedError(text_type(declaration))
|
||||
assert iTemplateDecl < len(templateDecls)
|
||||
templateParams = templateDecls[iTemplateDecl]
|
||||
iTemplateDecl += 1
|
||||
else:
|
||||
templateParams = None
|
||||
symbol = parentSymbol._find_named_symbol(identifier,
|
||||
@ -2053,6 +2250,23 @@ class Symbol(object):
|
||||
return s
|
||||
return None
|
||||
|
||||
def direct_lookup(self, key):
|
||||
s = self
|
||||
for name, templateParams in key:
|
||||
if name.is_operator():
|
||||
identifier = None
|
||||
templateArgs = None
|
||||
operator = name
|
||||
else:
|
||||
identifier = name.identifier
|
||||
templateArgs = name.templateArgs
|
||||
operator = None
|
||||
s = s._find_named_symbol(identifier, templateParams,
|
||||
templateArgs, operator)
|
||||
if not s:
|
||||
return None
|
||||
return s
|
||||
|
||||
def find_name(self, nestedName, templateDecls, specific_specialisation):
|
||||
# TODO: unify this with the _add_symbols
|
||||
# This condition should be checked at the parser level.
|
||||
@ -2065,7 +2279,6 @@ class Symbol(object):
|
||||
while parentSymbol.parent:
|
||||
parentSymbol = parentSymbol.parent
|
||||
names = nestedName.names
|
||||
# TODO: lookup in template params when len(names) == 1
|
||||
|
||||
# walk up until we find the first identifier
|
||||
firstName = names[0]
|
||||
@ -2528,7 +2741,7 @@ class DefinitionParser(object):
|
||||
|
||||
def _parse_decl_specs(self, outer, typed=True):
|
||||
if outer:
|
||||
if outer not in ('type', 'member', 'function'):
|
||||
if outer not in ('type', 'member', 'function', 'templateParam'):
|
||||
raise Exception('Internal error, unknown outer "%s".' % outer)
|
||||
"""
|
||||
storage-class-specifier function-specifier "constexpr"
|
||||
@ -2560,6 +2773,13 @@ class DefinitionParser(object):
|
||||
declId = self._parse_nested_name()
|
||||
except DefinitionError:
|
||||
declId = None
|
||||
elif named == 'single':
|
||||
if self.match(_identifier_re):
|
||||
identifier = ASTIdentifier(self.matched_text)
|
||||
nne = ASTNestedNameElement(identifier, None)
|
||||
declId = ASTNestedName([nne], rooted=False)
|
||||
else:
|
||||
declId = None
|
||||
elif named:
|
||||
declId = self._parse_nested_name()
|
||||
else:
|
||||
@ -2659,13 +2879,14 @@ class DefinitionParser(object):
|
||||
else:
|
||||
if outer == 'member':
|
||||
value = self.read_rest().strip()
|
||||
return ASTInitializer(value)
|
||||
elif outer == 'templateParam':
|
||||
value = self._parse_expression(end=[',', '>'])
|
||||
elif outer is None: # function parameter
|
||||
value = self._parse_expression(end=[',', ')'])
|
||||
return ASTInitializer(value)
|
||||
else:
|
||||
self.fail("Internal error, initializer for outer '%s' not "
|
||||
"implemented." % outer)
|
||||
return ASTInitializer(value)
|
||||
|
||||
def _parse_type(self, named, outer=None):
|
||||
"""
|
||||
@ -2675,7 +2896,8 @@ class DefinitionParser(object):
|
||||
outer == operatorCast: annoying case, we should not take the params
|
||||
"""
|
||||
if outer: # always named
|
||||
if outer not in ('type', 'member', 'function', 'operatorCast'):
|
||||
if outer not in ('type', 'member', 'function',
|
||||
'operatorCast', 'templateParam'):
|
||||
raise Exception('Internal error, unknown outer "%s".' % outer)
|
||||
if outer != 'operatorCast':
|
||||
assert named
|
||||
@ -2730,17 +2952,27 @@ class DefinitionParser(object):
|
||||
elif outer == 'operatorCast':
|
||||
paramMode = 'operatorCast'
|
||||
outer = None
|
||||
elif outer == 'templateParam':
|
||||
named = 'single'
|
||||
declSpecs = self._parse_decl_specs(outer=outer)
|
||||
decl = self._parse_declerator(named=named, paramMode=paramMode)
|
||||
return ASTType(declSpecs, decl)
|
||||
|
||||
def _parse_type_with_init(self, named, outer):
|
||||
if outer:
|
||||
assert outer in ('type', 'member', 'function')
|
||||
assert outer in ('type', 'member', 'function', 'templateParam')
|
||||
type = self._parse_type(outer=outer, named=named)
|
||||
init = self._parse_initializer(outer=outer)
|
||||
return ASTTypeWithInit(type, init)
|
||||
|
||||
def _parse_type_using(self):
|
||||
name = self._parse_nested_name()
|
||||
self.skip_ws()
|
||||
if not self.skip_string('='):
|
||||
return ASTTypeUsing(name, None)
|
||||
type = self._parse_type(False, None)
|
||||
return ASTTypeUsing(name, type)
|
||||
|
||||
def _parse_class(self):
|
||||
name = self._parse_nested_name()
|
||||
bases = []
|
||||
@ -2779,20 +3011,82 @@ class DefinitionParser(object):
|
||||
init = ASTInitializer(self.read_rest())
|
||||
return ASTEnumerator(name, init)
|
||||
|
||||
def _parse_template_parameter_list(self):
|
||||
# only: '<' parameter-list '>'
|
||||
# we assume that 'template' has just been parsed
|
||||
templateParams = []
|
||||
self.skip_ws()
|
||||
if not self.skip_string("<"):
|
||||
self.fail("Expected '<' after 'template'")
|
||||
while 1:
|
||||
extraError = ''
|
||||
self.skip_ws()
|
||||
if self.skip_word('template'):
|
||||
# declare a tenplate template parameter
|
||||
nestedParams = self._parse_template_parameter_list()
|
||||
else:
|
||||
nestedParams = None
|
||||
self.skip_ws()
|
||||
key = None
|
||||
if self.skip_word_and_ws('typename'):
|
||||
key = 'typename'
|
||||
elif self.skip_word_and_ws('class'):
|
||||
key = 'class'
|
||||
elif nestedParams:
|
||||
self.fail("Expected 'typename' or 'class' after "
|
||||
"template template parameter list.")
|
||||
if key:
|
||||
# declare a type or template type parameter
|
||||
self.skip_ws()
|
||||
parameterPack = self.skip_string('...')
|
||||
self.skip_ws()
|
||||
if self.match(_identifier_re):
|
||||
identifier = ASTIdentifier(self.matched_text)
|
||||
else:
|
||||
identifier = None
|
||||
self.skip_ws()
|
||||
if not parameterPack and self.skip_string('='):
|
||||
default = self._parse_type(named=False, outer=None)
|
||||
else:
|
||||
default = None
|
||||
data = ASTTemplateKeyParamPackIdDefault(key, identifier,
|
||||
parameterPack, default)
|
||||
if nestedParams:
|
||||
# template type
|
||||
param = ASTTemplateParamTemplateType(nestedParams, data)
|
||||
else:
|
||||
# type
|
||||
param = ASTTemplateParamType(data)
|
||||
templateParams.append(param)
|
||||
else:
|
||||
# declare a non-type parameter
|
||||
pos = self.pos
|
||||
try:
|
||||
param = self._parse_type_with_init('maybe', 'templateParam')
|
||||
templateParams.append(ASTTemplateParamNonType(param))
|
||||
except DefinitionError as e:
|
||||
self.pos = pos
|
||||
extraError = "Error if non-type template parameter: %s"
|
||||
extraError = extraError % e.description
|
||||
self.skip_ws()
|
||||
if self.skip_string('>'):
|
||||
return ASTTemplateParams(templateParams)
|
||||
elif self.skip_string(','):
|
||||
continue
|
||||
else:
|
||||
msg = 'Expected "=", ",", or ">" in template parameter list.'
|
||||
if len(extraError) > 0:
|
||||
msg += '\n%s' % extraError
|
||||
self.fail(msg)
|
||||
|
||||
def _parse_template_declaration_prefix(self):
|
||||
templates = []
|
||||
while 1:
|
||||
self.skip_ws()
|
||||
if not self.skip_word_and_ws("template"):
|
||||
if not self.skip_word("template"):
|
||||
break
|
||||
templateParams = []
|
||||
if not self.skip_string("<"):
|
||||
self.fail("Expected '<' after 'template'")
|
||||
|
||||
self.skip_ws()
|
||||
if not self.skip_string(">"):
|
||||
self.fail("Expected '>' in the end of template parameter list.")
|
||||
templates.append(ASTTemplateParams(templateParams))
|
||||
params = self._parse_template_parameter_list()
|
||||
templates.append(params)
|
||||
if len(templates) == 0:
|
||||
return None
|
||||
else:
|
||||
@ -2838,7 +3132,23 @@ class DefinitionParser(object):
|
||||
templatePrefix = self._parse_template_declaration_prefix()
|
||||
|
||||
if objectType == 'type':
|
||||
declaration = self._parse_type(named=True, outer='type')
|
||||
error = None
|
||||
try:
|
||||
if not templatePrefix:
|
||||
declaration = self._parse_type(named=True, outer='type')
|
||||
except DefinitionError as e:
|
||||
error = e.description
|
||||
try:
|
||||
if not declaration:
|
||||
declaration = self._parse_type_using()
|
||||
except DefinitionError as e:
|
||||
if error:
|
||||
msg = "Error if typedef:\n%s\n" \
|
||||
"Error if type alias or template alias:\n%s" \
|
||||
% (error, e.description)
|
||||
raise DefinitionError(msg)
|
||||
else:
|
||||
raise e
|
||||
elif objectType == 'member':
|
||||
declaration = self._parse_type_with_init(named=True, outer='member')
|
||||
elif objectType == 'function':
|
||||
@ -2873,8 +3183,9 @@ class DefinitionParser(object):
|
||||
return res
|
||||
|
||||
|
||||
def _make_phony_error_name(self):
|
||||
return ASTNestedName([ASTNestedNameElement("PhonyNameDueToError", None)])
|
||||
def _make_phony_error_name():
|
||||
nne = ASTNestedNameElement(ASTIdentifier("PhonyNameDueToError"), None)
|
||||
return ASTNestedName([nne], rooted=False)
|
||||
|
||||
|
||||
class CPPObject(ObjectDescription):
|
||||
@ -2894,7 +3205,7 @@ class CPPObject(ObjectDescription):
|
||||
def warn(self, msg):
|
||||
self.state_machine.reporter.warning(msg, lineno=self.lineno)
|
||||
|
||||
def _add_enumerator_to_parent(self, ast, objects):
|
||||
def _add_enumerator_to_parent(self, ast):
|
||||
assert ast.objectType == 'enumerator'
|
||||
# find the parent, if it exists && is an enum
|
||||
# && it's unscoped,
|
||||
@ -2928,13 +3239,9 @@ class CPPObject(ObjectDescription):
|
||||
if s is not None:
|
||||
# something is already declared with that name
|
||||
return
|
||||
copySymbol = Symbol(parent=targetSymbol, identifier=symbol.identifier,
|
||||
templateParams=None, templateArgs=None,
|
||||
declaration=symbol.declaration.clone())
|
||||
name = text_type(copySymbol.get_full_nested_name()).lstrip(':')
|
||||
if name not in objects:
|
||||
objects.setdefault(name,
|
||||
(self.env.docname, copySymbol.declaration))
|
||||
_ = Symbol(parent=targetSymbol, identifier=symbol.identifier,
|
||||
templateParams=None, templateArgs=None,
|
||||
declaration=symbol.declaration.clone())
|
||||
|
||||
def add_target_and_index(self, ast, sig, signode):
|
||||
# general note: name must be lstrip(':')'ed, to remove "::"
|
||||
@ -2946,35 +3253,30 @@ class CPPObject(ObjectDescription):
|
||||
# store them in reverse order, so the newest is first
|
||||
ids = [id_v2, id_v1]
|
||||
|
||||
theid = ids[0]
|
||||
ast.newestId = theid
|
||||
assert theid # shouldn't be None
|
||||
newestId = ids[0]
|
||||
assert newestId # shouldn't be None
|
||||
if not re.compile(r'^[a-zA-Z0-9_]*$').match(newestId):
|
||||
self.warn('Index id generation for C++ object "%s" failed, please '
|
||||
'report as bug (id=%s).' % (text_type(ast), newestId))
|
||||
|
||||
name = text_type(ast.symbol.get_full_nested_name()).lstrip(':')
|
||||
if theid not in self.state.document.ids:
|
||||
indexText = self.get_index_text(name)
|
||||
self.indexnode['entries'].append(('single', indexText, newestId, ''))
|
||||
|
||||
if newestId not in self.state.document.ids:
|
||||
# if the name is not unique, the first one will win
|
||||
objects = self.env.domaindata['cpp']['objects']
|
||||
if name not in objects:
|
||||
names = self.env.domaindata['cpp']['names']
|
||||
if name not in names:
|
||||
names[name] = ast.symbol.docname
|
||||
signode['names'].append(name)
|
||||
else:
|
||||
pass
|
||||
#print("[CPP] non-unique name:", name)
|
||||
pass
|
||||
for id in ids:
|
||||
if id: # is None when the element didn't exist in that version
|
||||
signode['ids'].append(id)
|
||||
signode['first'] = (not self.names)
|
||||
signode['first'] = (not self.names) # hmm, what is this abound?
|
||||
self.state.document.note_explicit_target(signode)
|
||||
if name not in objects:
|
||||
objects.setdefault(name, (self.env.docname, ast))
|
||||
if ast.objectType == 'enumerator':
|
||||
self._add_enumerator_to_parent(ast, objects)
|
||||
|
||||
indextext = self.get_index_text(name)
|
||||
if not re.compile(r'^[a-zA-Z0-9_]*$').match(theid):
|
||||
self.state_machine.reporter.warning(
|
||||
'Index id generation for C++ object "%s" failed, please '
|
||||
'report as bug (id=%s).' % (text_type(ast), theid),
|
||||
line=self.lineno)
|
||||
self.indexnode['entries'].append(('single', indextext, theid, ''))
|
||||
|
||||
def parse_definition(self, parser):
|
||||
raise NotImplementedError()
|
||||
@ -2993,17 +3295,20 @@ class CPPObject(ObjectDescription):
|
||||
ast = self.parse_definition(parser)
|
||||
parser.assert_end()
|
||||
except DefinitionError as e:
|
||||
self.warn(e.description, line=self.lineno)
|
||||
self.warn(e.description)
|
||||
# It is easier to assume some phony name than handling the error in
|
||||
# the possibly inner declarations.
|
||||
name = _make_phony_error_name()
|
||||
symbol = parentSymbol.add_nested_name(name)
|
||||
symbol = parentSymbol.add_name(name)
|
||||
self.env.ref_context['cpp:lastSymbol'] = symbol
|
||||
raise ValueError
|
||||
symbol = parentSymbol.add_declaration(ast)
|
||||
self.env.ref_context['cpp:lastSymbol'] = symbol
|
||||
symbol.docname = self.env.docname
|
||||
|
||||
if ast.objectType == 'enumerator':
|
||||
self._add_enumerator_to_parent(ast)
|
||||
|
||||
self.describe_signature(signode, ast)
|
||||
return ast
|
||||
|
||||
@ -3192,25 +3497,36 @@ class CPPDomain(Domain):
|
||||
}
|
||||
initial_data = {
|
||||
'rootSymbol' : Symbol(None, None, None, None, None),
|
||||
'objects': {}, # qualifiedName -> (docname, ast)
|
||||
'names': {} # full name for indexing -> docname
|
||||
}
|
||||
|
||||
def clear_doc(self, docname):
|
||||
for fullname, data in list(self.data['objects'].items()):
|
||||
if data[0] == docname:
|
||||
del self.data['objects'][fullname]
|
||||
rootSymbol = self.data['rootSymbol']
|
||||
for symbol in rootSymbol.get_all_symbols():
|
||||
if not symbol.declaration:
|
||||
continue
|
||||
try:
|
||||
sDocname = symbol.docname
|
||||
except AttributeError:
|
||||
# it's a template parameter
|
||||
# the symbols are yielded in post-order, so this should be fine
|
||||
assert symbol.parent
|
||||
sDocname = symbol.parent.docname
|
||||
if sDocname != docname:
|
||||
continue
|
||||
symbol.declaration = None
|
||||
symbol.docname = None
|
||||
for name, nDocname in list(self.data['names'].items()):
|
||||
if nDocname == docname:
|
||||
del self.data['names'][name]
|
||||
|
||||
def process_doc(self, env, docname, document):
|
||||
# just for debugging
|
||||
#print(self.data['rootSymbol'].dump(0))
|
||||
pass
|
||||
|
||||
# def merge_domaindata(self, docnames, otherdata):
|
||||
# # XXX check duplicates
|
||||
# # TODO: merge rootSymbol
|
||||
# for fullname, data in otherdata['objects'].items():
|
||||
# if data[0] in docnames:
|
||||
# self.data['objects'][fullname] = data
|
||||
#def merge_domaindata(self, docnames, otherdata):
|
||||
# # TODO: merge rootSymbol
|
||||
|
||||
def _resolve_xref_inner(self, env, fromdocname, builder,
|
||||
target, node, contnode, warn=True):
|
||||
@ -3227,14 +3543,10 @@ class CPPDomain(Domain):
|
||||
parentKey = node.get("cpp:parentKey", None)
|
||||
rootSymbol = self.data['rootSymbol']
|
||||
if parentKey:
|
||||
names, templateDecls = parentKey
|
||||
assert names is not None # hmm
|
||||
assert templateDecls is not None # hmm
|
||||
if templateDecls is None:
|
||||
print(names)
|
||||
assert False
|
||||
parentSymbol = rootSymbol.find_name(names, templateDecls,
|
||||
specific_specialisation=True)
|
||||
parentSymbol = rootSymbol.direct_lookup(parentKey)
|
||||
if not parentSymbol:
|
||||
print("Target: ", target)
|
||||
print("ParentKey: ", parentKey)
|
||||
assert parentSymbol # should be there
|
||||
else:
|
||||
parentSymbol = rootSymbol
|
||||
@ -3251,9 +3563,15 @@ class CPPDomain(Domain):
|
||||
declaration = s.declaration
|
||||
fullNestedName = s.get_full_nested_name()
|
||||
name = text_type(fullNestedName).lstrip(':')
|
||||
docname = s.docname
|
||||
return make_refnode(builder, fromdocname, docname, declaration.newestId,
|
||||
contnode, name), declaration.objectType
|
||||
try:
|
||||
docname = s.docname
|
||||
except AttributeError:
|
||||
# it's a template parameter
|
||||
assert s.parent
|
||||
docname = s.parent.docname
|
||||
return make_refnode(builder, fromdocname, docname,
|
||||
declaration.get_newest_id(), contnode, name
|
||||
), declaration.objectType
|
||||
|
||||
def resolve_xref(self, env, fromdocname, builder,
|
||||
typ, target, node, contnode):
|
||||
@ -3270,5 +3588,15 @@ class CPPDomain(Domain):
|
||||
return []
|
||||
|
||||
def get_objects(self):
|
||||
for refname, (docname, ast) in iteritems(self.data['objects']):
|
||||
yield (refname, refname, ast.objectType, docname, ast.newestId, 1)
|
||||
rootSymbol = self.data['rootSymbol']
|
||||
for symbol in rootSymbol.get_all_symbols():
|
||||
if not symbol.declaration:
|
||||
continue
|
||||
name = text_type(symbol.get_full_nested_name()).lstrip(':')
|
||||
objectType = symbol.declaration.objectType
|
||||
try:
|
||||
docname = symbol.docname
|
||||
except AttributeError:
|
||||
continue
|
||||
newestId = symbol.declaration.get_newest_id()
|
||||
yield (name, name, objectType, docname, newestId, 1)
|
||||
|
@ -237,10 +237,27 @@ def test_templates():
|
||||
check('class', "template<> A", None, "IE1A")
|
||||
check('function', "template<> void A()", None, "IE1Av")
|
||||
check('member', "template<> A a", None, "IE1a")
|
||||
check('type', "template<> A a", None, "IE1a")
|
||||
check('type', "template<> a = A", None, "IE1a")
|
||||
raises(DefinitionError, parse, 'enum', "template<> A")
|
||||
raises(DefinitionError, parse, 'enumerator', "template<> A")
|
||||
# then all the real tests
|
||||
check('class', "template<typename T1, typename T2> A", None, "I00E1A")
|
||||
check('type', "template<> a", None, "IE1a")
|
||||
|
||||
check('class', "template<typename T> A", None, "I0E1A")
|
||||
check('class', "template<class T> A", None, "I0E1A")
|
||||
check('class', "template<typename ...T> A", None, "IDpE1A")
|
||||
check('class', "template<typename...> A", None, "IDpE1A")
|
||||
check('class', "template<typename = Test> A", None, "I0E1A")
|
||||
check('class', "template<typename T = Test> A", None, "I0E1A")
|
||||
|
||||
check('class', "template<template<typename> typename T> A",
|
||||
None, "II0E0E1A")
|
||||
check('class', "template<int> A", None, "I_iE1A")
|
||||
check('class', "template<int T> A", None, "I_iE1A")
|
||||
check('class', "template<int... T> A", None, "I_DpiE1A")
|
||||
check('class', "template<int T = 42> A", None, "I_iE1A")
|
||||
check('class', "template<int = 42> A", None, "I_iE1A")
|
||||
|
||||
|
||||
def test_bases():
|
||||
@ -252,6 +269,7 @@ def test_bases():
|
||||
check('class', 'A : B, C', "A", "1A")
|
||||
check('class', 'A : B, protected C, D', "A", "1A")
|
||||
|
||||
|
||||
def test_operators():
|
||||
check('function', 'void operator new [ ] ()',
|
||||
"new-array-operator", "nav", output='void operator new[]()')
|
||||
|
Loading…
Reference in New Issue
Block a user