Merge pull request #7673 from jakobandersen/cpp_test_with_astext

C++, fix rendering of rooted nested names
This commit is contained in:
Jakob Lykke Andersen 2020-05-16 12:20:38 +02:00 committed by GitHub
commit 218415ac74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 157 additions and 117 deletions

View File

@ -104,6 +104,8 @@ Bugs fixed
supporting images supporting images
* #7610: incorrectly renders consecutive backslashes for docutils-0.16 * #7610: incorrectly renders consecutive backslashes for docutils-0.16
* #7646: handle errors on event handlers * #7646: handle errors on event handlers
* C++, fix rendering and xrefs in nested names explicitly starting
in global scope, e.g., ``::A::B``.
Testing Testing
-------- --------

View File

@ -752,11 +752,17 @@ class ASTNestedName(ASTBase):
names = self.names[:-1] if mode == 'lastIsName' else self.names names = self.names[:-1] if mode == 'lastIsName' else self.names
# If lastIsName, then wrap all of the prefix in a desc_addname, # If lastIsName, then wrap all of the prefix in a desc_addname,
# else append directly to signode. # else append directly to signode.
# NOTE: Breathe relies on the prefix being in the desc_addname node, # NOTE: Breathe previously relied on the prefix being in the desc_addname node,
# so it can remove it in inner declarations. # so it can remove it in inner declarations.
dest = signode dest = signode
if mode == 'lastIsName': if mode == 'lastIsName':
dest = addnodes.desc_addname() dest = addnodes.desc_addname()
if self.rooted:
prefix += '::'
if mode == 'lastIsName' and len(names) == 0:
signode += nodes.Text('::')
else:
dest += nodes.Text('::')
for i in range(len(names)): for i in range(len(names)):
nne = names[i] nne = names[i]
template = self.templates[i] template = self.templates[i]

View File

@ -33,15 +33,29 @@ def parse(name, string):
return ast return ast
def _check(name, input, idDict, output): def _check(name, input, idDict, output, key, asTextOutput):
if key is None:
key = name
key += ' '
if name in ('function', 'member'):
inputActual = input
outputAst = output
outputAsText = output
else:
inputActual = input.format(key='')
outputAst = output.format(key='')
outputAsText = output.format(key=key)
if asTextOutput is not None:
outputAsText = asTextOutput
# first a simple check of the AST # first a simple check of the AST
ast = parse(name, input) ast = parse(name, inputActual)
res = str(ast) res = str(ast)
if res != output: if res != outputAst:
print("") print("")
print("Input: ", input) print("Input: ", input)
print("Result: ", res) print("Result: ", res)
print("Expected: ", output) print("Expected: ", outputAst)
raise DefinitionError("") raise DefinitionError("")
rootSymbol = Symbol(None, None, None, None, None, None) rootSymbol = Symbol(None, None, None, None, None, None)
symbol = rootSymbol.add_declaration(ast, docname="TestDoc") symbol = rootSymbol.add_declaration(ast, docname="TestDoc")
@ -49,6 +63,13 @@ def _check(name, input, idDict, output):
signode = addnodes.desc_signature(input, '') signode = addnodes.desc_signature(input, '')
parentNode += signode parentNode += signode
ast.describe_signature(signode, 'lastIsName', symbol, options={}) ast.describe_signature(signode, 'lastIsName', symbol, options={})
resAsText = parentNode.astext()
if resAsText != outputAsText:
print("")
print("Input: ", input)
print("astext(): ", resAsText)
print("Expected: ", outputAsText)
raise DefinitionError("")
idExpected = [None] idExpected = [None]
for i in range(1, _max_id + 1): for i in range(1, _max_id + 1):
@ -81,13 +102,14 @@ def _check(name, input, idDict, output):
raise DefinitionError("") raise DefinitionError("")
def check(name, input, idDict, output=None): def check(name, input, idDict, output=None, key=None, asTextOutput=None):
if output is None: if output is None:
output = input output = input
# First, check without semicolon # First, check without semicolon
_check(name, input, idDict, output) _check(name, input, idDict, output, key, asTextOutput)
# Second, check with semicolon # Second, check with semicolon
_check(name, input + ' ;', idDict, output + ';') _check(name, input + ' ;', idDict, output + ';', key,
asTextOutput + ';' if asTextOutput is not None else None)
def test_fundamental_types(): def test_fundamental_types():
@ -113,10 +135,11 @@ def test_fundamental_types():
def test_expressions(): def test_expressions():
def exprCheck(expr, id, id4=None): def exprCheck(expr, id, id4=None):
ids = 'IE1CIA%s_1aE' ids = 'IE1CIA%s_1aE'
idDict = {2: ids % expr, 3: ids % id} # call .format() on the expr to unescape double curly braces
idDict = {2: ids % expr.format(), 3: ids % id}
if id4 is not None: if id4 is not None:
idDict[4] = ids % id4 idDict[4] = ids % id4
check('class', 'template<> C<a[%s]>' % expr, idDict) check('class', 'template<> {key}C<a[%s]>' % expr, idDict)
class Config: class Config:
cpp_id_attributes = ["id_attr"] cpp_id_attributes = ["id_attr"]
@ -229,8 +252,8 @@ def test_expressions():
exprCheck('new int()', 'nw_ipiE') exprCheck('new int()', 'nw_ipiE')
exprCheck('new int(5, 42)', 'nw_ipiL5EL42EE') exprCheck('new int(5, 42)', 'nw_ipiL5EL42EE')
exprCheck('::new int', 'nw_iE') exprCheck('::new int', 'nw_iE')
exprCheck('new int{}', 'nw_iilE') exprCheck('new int{{}}', 'nw_iilE')
exprCheck('new int{5, 42}', 'nw_iilL5EL42EE') exprCheck('new int{{5, 42}}', 'nw_iilL5EL42EE')
# delete-expression # delete-expression
exprCheck('delete p', 'dl1p') exprCheck('delete p', 'dl1p')
exprCheck('delete [] p', 'da1p') exprCheck('delete [] p', 'da1p')
@ -291,7 +314,7 @@ def test_expressions():
exprCheck('a xor_eq 5', 'eO1aL5E') exprCheck('a xor_eq 5', 'eO1aL5E')
exprCheck('a |= 5', 'oR1aL5E') exprCheck('a |= 5', 'oR1aL5E')
exprCheck('a or_eq 5', 'oR1aL5E') exprCheck('a or_eq 5', 'oR1aL5E')
exprCheck('a = {1, 2, 3}', 'aS1ailL1EL2EL3EE') exprCheck('a = {{1, 2, 3}}', 'aS1ailL1EL2EL3EE')
# comma operator # comma operator
exprCheck('a, 5', 'cm1aL5E') exprCheck('a, 5', 'cm1aL5E')
@ -301,8 +324,8 @@ def test_expressions():
check('function', 'template<> void f(A<B, 2> &v)', check('function', 'template<> void f(A<B, 2> &v)',
{2: "IE1fR1AI1BX2EE", 3: "IE1fR1AI1BXL2EEE", 4: "IE1fvR1AI1BXL2EEE"}) {2: "IE1fR1AI1BX2EE", 3: "IE1fR1AI1BXL2EEE", 4: "IE1fvR1AI1BXL2EEE"})
exprCheck('A<1>::value', 'N1AIXL1EEE5valueE') exprCheck('A<1>::value', 'N1AIXL1EEE5valueE')
check('class', "template<int T = 42> A", {2: "I_iE1A"}) check('class', "template<int T = 42> {key}A", {2: "I_iE1A"})
check('enumerator', 'A = std::numeric_limits<unsigned long>::max()', {2: "1A"}) check('enumerator', '{key}A = std::numeric_limits<unsigned long>::max()', {2: "1A"})
exprCheck('operator()()', 'clclE') exprCheck('operator()()', 'clclE')
exprCheck('operator()<int>()', 'clclIiEE') exprCheck('operator()<int>()', 'clclIiEE')
@ -312,58 +335,59 @@ def test_expressions():
def test_type_definitions(): def test_type_definitions():
check("type", "public bool b", {1: "b", 2: "1b"}, "bool b") check("type", "public bool b", {1: "b", 2: "1b"}, "{key}bool b", key='typedef')
check("type", "bool A::b", {1: "A::b", 2: "N1A1bE"}) check("type", "{key}bool A::b", {1: "A::b", 2: "N1A1bE"}, key='typedef')
check("type", "bool *b", {1: "b", 2: "1b"}) check("type", "{key}bool *b", {1: "b", 2: "1b"}, key='typedef')
check("type", "bool *const b", {1: "b", 2: "1b"}) check("type", "{key}bool *const b", {1: "b", 2: "1b"}, key='typedef')
check("type", "bool *volatile const b", {1: "b", 2: "1b"}) check("type", "{key}bool *volatile const b", {1: "b", 2: "1b"}, key='typedef')
check("type", "bool *volatile const b", {1: "b", 2: "1b"}) check("type", "{key}bool *volatile const b", {1: "b", 2: "1b"}, key='typedef')
check("type", "bool *volatile const *b", {1: "b", 2: "1b"}) check("type", "{key}bool *volatile const *b", {1: "b", 2: "1b"}, key='typedef')
check("type", "bool &b", {1: "b", 2: "1b"}) check("type", "{key}bool &b", {1: "b", 2: "1b"}, key='typedef')
check("type", "bool b[]", {1: "b", 2: "1b"}) check("type", "{key}bool b[]", {1: "b", 2: "1b"}, key='typedef')
check("type", "std::pair<int, int> coord", {1: "coord", 2: "5coord"}) check("type", "{key}std::pair<int, int> coord", {1: "coord", 2: "5coord"}, key='typedef')
check("type", "long long int foo", {1: "foo", 2: "3foo"}) check("type", "{key}long long int foo", {1: "foo", 2: "3foo"}, key='typedef')
check("type", 'std::vector<std::pair<std::string, long long>> module::blah', check("type", '{key}std::vector<std::pair<std::string, long long>> module::blah',
{1: "module::blah", 2: "N6module4blahE"}) {1: "module::blah", 2: "N6module4blahE"}, key='typedef')
check("type", "std::function<void()> F", {1: "F", 2: "1F"}) check("type", "{key}std::function<void()> F", {1: "F", 2: "1F"}, key='typedef')
check("type", "std::function<R(A1, A2)> F", {1: "F", 2: "1F"}) check("type", "{key}std::function<R(A1, A2)> F", {1: "F", 2: "1F"}, key='typedef')
check("type", "std::function<R(A1, A2, A3)> F", {1: "F", 2: "1F"}) check("type", "{key}std::function<R(A1, A2, A3)> F", {1: "F", 2: "1F"}, key='typedef')
check("type", "std::function<R(A1, A2, A3, As...)> F", {1: "F", 2: "1F"}) check("type", "{key}std::function<R(A1, A2, A3, As...)> F", {1: "F", 2: "1F"}, key='typedef')
check("type", "MyContainer::const_iterator", check("type", "{key}MyContainer::const_iterator",
{1: "MyContainer::const_iterator", 2: "N11MyContainer14const_iteratorE"}) {1: "MyContainer::const_iterator", 2: "N11MyContainer14const_iteratorE"})
check("type", check("type",
"public MyContainer::const_iterator", "public MyContainer::const_iterator",
{1: "MyContainer::const_iterator", 2: "N11MyContainer14const_iteratorE"}, {1: "MyContainer::const_iterator", 2: "N11MyContainer14const_iteratorE"},
output="MyContainer::const_iterator") output="{key}MyContainer::const_iterator")
# test decl specs on right # test decl specs on right
check("type", "bool const b", {1: "b", 2: "1b"}) check("type", "{key}bool const b", {1: "b", 2: "1b"}, key='typedef')
# test name in global scope # test name in global scope
check("type", "bool ::B::b", {1: "B::b", 2: "N1B1bE"}) check("type", "{key}bool ::B::b", {1: "B::b", 2: "N1B1bE"}, key='typedef')
check('type', 'A = B', {2: '1A'}) check('type', '{key}A = B', {2: '1A'}, key='using')
check('type', 'A = decltype(b)', {2: '1A'}) check('type', '{key}A = decltype(b)', {2: '1A'}, key='using')
# from breathe#267 (named function parameters for function pointers # from breathe#267 (named function parameters for function pointers
check('type', 'void (*gpio_callback_t)(struct device *port, uint32_t pin)', check('type', '{key}void (*gpio_callback_t)(struct device *port, uint32_t pin)',
{1: 'gpio_callback_t', 2: '15gpio_callback_t'}) {1: 'gpio_callback_t', 2: '15gpio_callback_t'}, key='typedef')
check('type', 'void (*f)(std::function<void(int i)> g)', {1: 'f', 2: '1f'}) check('type', '{key}void (*f)(std::function<void(int i)> g)', {1: 'f', 2: '1f'},
key='typedef')
check('type', 'T = A::template B<int>::template C<double>', {2: '1T'}) check('type', '{key}T = A::template B<int>::template C<double>', {2: '1T'}, key='using')
check('type', 'T = Q<A::operator()>', {2: '1T'}) check('type', '{key}T = Q<A::operator()>', {2: '1T'}, key='using')
check('type', 'T = Q<A::operator()<int>>', {2: '1T'}) check('type', '{key}T = Q<A::operator()<int>>', {2: '1T'}, key='using')
check('type', 'T = Q<A::operator bool>', {2: '1T'}) check('type', '{key}T = Q<A::operator bool>', {2: '1T'}, key='using')
def test_concept_definitions(): def test_concept_definitions():
check('concept', 'template<typename Param> A::B::Concept', check('concept', 'template<typename Param> {key}A::B::Concept',
{2: 'I0EN1A1B7ConceptE'}) {2: 'I0EN1A1B7ConceptE'})
check('concept', 'template<typename A, typename B, typename ...C> Foo', check('concept', 'template<typename A, typename B, typename ...C> {key}Foo',
{2: 'I00DpE3Foo'}) {2: 'I00DpE3Foo'})
with pytest.raises(DefinitionError): with pytest.raises(DefinitionError):
parse('concept', 'Foo') parse('concept', '{key}Foo')
with pytest.raises(DefinitionError): with pytest.raises(DefinitionError):
parse('concept', 'template<typename T> template<typename U> Foo') parse('concept', 'template<typename T> template<typename U> {key}Foo')
def test_member_definitions(): def test_member_definitions():
@ -639,95 +663,102 @@ def test_operators():
check('function', 'void operator[]()', {1: "subscript-operator", 2: "ixv"}) check('function', 'void operator[]()', {1: "subscript-operator", 2: "ixv"})
class test_nested_name():
check('class', '{key}::A', {1: "A", 2: "1A"})
check('class', '{key}::A::B', {1: "A::B", 2: "N1A1BE"})
check('function', 'void f(::A a)', {1: "f__A", 2: "1f1A"})
check('function', 'void f(::A::B a)', {1: "f__A::B", 2: "1fN1A1BE"})
def test_class_definitions(): def test_class_definitions():
check('class', 'public A', {1: "A", 2: "1A"}, output='A') check('class', 'public A', {1: "A", 2: "1A"}, output='{key}A')
check('class', 'private A', {1: "A", 2: "1A"}) check('class', 'private {key}A', {1: "A", 2: "1A"})
check('class', 'A final', {1: 'A', 2: '1A'}) check('class', '{key}A final', {1: 'A', 2: '1A'})
# test bases # test bases
check('class', 'A', {1: "A", 2: "1A"}) check('class', '{key}A', {1: "A", 2: "1A"})
check('class', 'A::B::C', {1: "A::B::C", 2: "N1A1B1CE"}) check('class', '{key}A::B::C', {1: "A::B::C", 2: "N1A1B1CE"})
check('class', 'A : B', {1: "A", 2: "1A"}) check('class', '{key}A : B', {1: "A", 2: "1A"})
check('class', 'A : private B', {1: "A", 2: "1A"}) check('class', '{key}A : private B', {1: "A", 2: "1A"})
check('class', 'A : public B', {1: "A", 2: "1A"}) check('class', '{key}A : public B', {1: "A", 2: "1A"})
check('class', 'A : B, C', {1: "A", 2: "1A"}) check('class', '{key}A : B, C', {1: "A", 2: "1A"})
check('class', 'A : B, protected C, D', {1: "A", 2: "1A"}) check('class', '{key}A : B, protected C, D', {1: "A", 2: "1A"})
check('class', 'A : virtual private B', {1: 'A', 2: '1A'}, output='A : private virtual B') check('class', 'A : virtual private B', {1: 'A', 2: '1A'}, output='{key}A : private virtual B')
check('class', 'A : private virtual B', {1: 'A', 2: '1A'}) check('class', '{key}A : private virtual B', {1: 'A', 2: '1A'})
check('class', 'A : B, virtual C', {1: 'A', 2: '1A'}) check('class', '{key}A : B, virtual C', {1: 'A', 2: '1A'})
check('class', 'A : public virtual B', {1: 'A', 2: '1A'}) check('class', '{key}A : public virtual B', {1: 'A', 2: '1A'})
check('class', 'A : B, C...', {1: 'A', 2: '1A'}) check('class', '{key}A : B, C...', {1: 'A', 2: '1A'})
check('class', 'A : B..., C', {1: 'A', 2: '1A'}) check('class', '{key}A : B..., C', {1: 'A', 2: '1A'})
# from #4094 # from #4094
check('class', 'template<class, class = std::void_t<>> has_var', {2: 'I00E7has_var'}) check('class', 'template<class, class = std::void_t<>> {key}has_var', {2: 'I00E7has_var'})
check('class', 'template<class T> has_var<T, std::void_t<decltype(&T::var)>>', check('class', 'template<class T> {key}has_var<T, std::void_t<decltype(&T::var)>>',
{2: 'I0E7has_varI1TNSt6void_tIDTadN1T3varEEEEE'}) {2: 'I0E7has_varI1TNSt6void_tIDTadN1T3varEEEEE'})
check('class', 'template<typename ...Ts> T<int (*)(Ts)...>', check('class', 'template<typename ...Ts> {key}T<int (*)(Ts)...>',
{2: 'IDpE1TIJPFi2TsEEE'}) {2: 'IDpE1TIJPFi2TsEEE'})
check('class', 'template<int... Is> T<(Is)...>', check('class', 'template<int... Is> {key}T<(Is)...>',
{2: 'I_DpiE1TIJX(Is)EEE', 3: 'I_DpiE1TIJX2IsEEE'}) {2: 'I_DpiE1TIJX(Is)EEE', 3: 'I_DpiE1TIJX2IsEEE'})
def test_union_definitions(): def test_union_definitions():
check('union', 'A', {2: "1A"}) check('union', '{key}A', {2: "1A"})
def test_enum_definitions(): def test_enum_definitions():
check('enum', 'A', {2: "1A"}) check('enum', '{key}A', {2: "1A"})
check('enum', 'A : std::underlying_type<B>::type', {2: "1A"}) check('enum', '{key}A : std::underlying_type<B>::type', {2: "1A"})
check('enum', 'A : unsigned int', {2: "1A"}) check('enum', '{key}A : unsigned int', {2: "1A"})
check('enum', 'public A', {2: "1A"}, output='A') check('enum', 'public A', {2: "1A"}, output='{key}A')
check('enum', 'private A', {2: "1A"}) check('enum', 'private {key}A', {2: "1A"})
check('enumerator', 'A', {2: "1A"}) check('enumerator', '{key}A', {2: "1A"})
check('enumerator', 'A = std::numeric_limits<unsigned long>::max()', {2: "1A"}) check('enumerator', '{key}A = std::numeric_limits<unsigned long>::max()', {2: "1A"})
def test_anon_definitions(): def test_anon_definitions():
check('class', '@a', {3: "Ut1_a"}) check('class', '@a', {3: "Ut1_a"}, asTextOutput='class [anonymous]')
check('union', '@a', {3: "Ut1_a"}) check('union', '@a', {3: "Ut1_a"}, asTextOutput='union [anonymous]')
check('enum', '@a', {3: "Ut1_a"}) check('enum', '@a', {3: "Ut1_a"}, asTextOutput='enum [anonymous]')
check('class', '@1', {3: "Ut1_1"}) check('class', '@1', {3: "Ut1_1"}, asTextOutput='class [anonymous]')
check('class', '@a::A', {3: "NUt1_a1AE"}) check('class', '@a::A', {3: "NUt1_a1AE"}, asTextOutput='class [anonymous]::A')
def test_templates(): def test_templates():
check('class', "A<T>", {2: "IE1AI1TE"}, output="template<> A<T>") check('class', "A<T>", {2: "IE1AI1TE"}, output="template<> {key}A<T>")
# first just check which objects support templating # first just check which objects support templating
check('class', "template<> A", {2: "IE1A"}) check('class', "template<> {key}A", {2: "IE1A"})
check('function', "template<> void A()", {2: "IE1Av", 4: "IE1Avv"}) check('function', "template<> void A()", {2: "IE1Av", 4: "IE1Avv"})
check('member', "template<> A a", {2: "IE1a"}) check('member', "template<> A a", {2: "IE1a"})
check('type', "template<> a = A", {2: "IE1a"}) check('type', "template<> {key}a = A", {2: "IE1a"}, key='using')
with pytest.raises(DefinitionError): with pytest.raises(DefinitionError):
parse('enum', "template<> A") parse('enum', "template<> A")
with pytest.raises(DefinitionError): with pytest.raises(DefinitionError):
parse('enumerator', "template<> A") parse('enumerator', "template<> A")
# then all the real tests # then all the real tests
check('class', "template<typename T1, typename T2> A", {2: "I00E1A"}) check('class', "template<typename T1, typename T2> {key}A", {2: "I00E1A"})
check('type', "template<> a", {2: "IE1a"}) check('type', "template<> {key}a", {2: "IE1a"}, key='using')
check('class', "template<typename T> A", {2: "I0E1A"}) check('class', "template<typename T> {key}A", {2: "I0E1A"})
check('class', "template<class T> A", {2: "I0E1A"}) check('class', "template<class T> {key}A", {2: "I0E1A"})
check('class', "template<typename ...T> A", {2: "IDpE1A"}) check('class', "template<typename ...T> {key}A", {2: "IDpE1A"})
check('class', "template<typename...> A", {2: "IDpE1A"}) check('class', "template<typename...> {key}A", {2: "IDpE1A"})
check('class', "template<typename = Test> A", {2: "I0E1A"}) check('class', "template<typename = Test> {key}A", {2: "I0E1A"})
check('class', "template<typename T = Test> A", {2: "I0E1A"}) check('class', "template<typename T = Test> {key}A", {2: "I0E1A"})
check('class', "template<template<typename> typename T> A", {2: "II0E0E1A"}) check('class', "template<template<typename> typename T> {key}A", {2: "II0E0E1A"})
check('class', "template<template<typename> typename> A", {2: "II0E0E1A"}) check('class', "template<template<typename> typename> {key}A", {2: "II0E0E1A"})
check('class', "template<template<typename> typename ...T> A", {2: "II0EDpE1A"}) check('class', "template<template<typename> typename ...T> {key}A", {2: "II0EDpE1A"})
check('class', "template<template<typename> typename...> A", {2: "II0EDpE1A"}) check('class', "template<template<typename> typename...> {key}A", {2: "II0EDpE1A"})
check('class', "template<int> A", {2: "I_iE1A"}) check('class', "template<int> {key}A", {2: "I_iE1A"})
check('class', "template<int T> A", {2: "I_iE1A"}) check('class', "template<int T> {key}A", {2: "I_iE1A"})
check('class', "template<int... T> A", {2: "I_DpiE1A"}) check('class', "template<int... T> {key}A", {2: "I_DpiE1A"})
check('class', "template<int T = 42> A", {2: "I_iE1A"}) check('class', "template<int T = 42> {key}A", {2: "I_iE1A"})
check('class', "template<int = 42> A", {2: "I_iE1A"}) check('class', "template<int = 42> {key}A", {2: "I_iE1A"})
check('class', "template<> A<NS::B<>>", {2: "IE1AIN2NS1BIEEE"}) check('class', "template<> {key}A<NS::B<>>", {2: "IE1AIN2NS1BIEEE"})
# from #2058 # from #2058
check('function', check('function',
@ -747,21 +778,21 @@ def test_templates():
parse('enum', 'abc::ns::foo{id_0, id_1, id_2} A') parse('enum', 'abc::ns::foo{id_0, id_1, id_2} A')
with pytest.raises(DefinitionError): with pytest.raises(DefinitionError):
parse('enumerator', 'abc::ns::foo{id_0, id_1, id_2} A') parse('enumerator', 'abc::ns::foo{id_0, id_1, id_2} A')
check('class', 'abc::ns::foo{id_0, id_1, id_2} xyz::bar', check('class', 'abc::ns::foo{{id_0, id_1, id_2}} {key}xyz::bar',
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'}) {2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'})
check('class', 'abc::ns::foo{id_0, id_1, ...id_2} xyz::bar', check('class', 'abc::ns::foo{{id_0, id_1, ...id_2}} {key}xyz::bar',
{2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'}) {2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'})
check('class', 'abc::ns::foo{id_0, id_1, id_2} xyz::bar<id_0, id_1, id_2>', check('class', 'abc::ns::foo{{id_0, id_1, id_2}} {key}xyz::bar<id_0, id_1, id_2>',
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barI4id_04id_14id_2EE'}) {2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barI4id_04id_14id_2EE'})
check('class', 'abc::ns::foo{id_0, id_1, ...id_2} xyz::bar<id_0, id_1, id_2...>', check('class', 'abc::ns::foo{{id_0, id_1, ...id_2}} {key}xyz::bar<id_0, id_1, id_2...>',
{2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barI4id_04id_1Dp4id_2EE'}) {2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barI4id_04id_1Dp4id_2EE'})
check('class', 'template<> Concept{U} A<int>::B', {2: 'IEI0EX7ConceptI1UEEN1AIiE1BE'}) check('class', 'template<> Concept{{U}} {key}A<int>::B', {2: 'IEI0EX7ConceptI1UEEN1AIiE1BE'})
check('type', 'abc::ns::foo{id_0, id_1, id_2} xyz::bar = ghi::qux', check('type', 'abc::ns::foo{{id_0, id_1, id_2}} {key}xyz::bar = ghi::qux',
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'}) {2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'}, key='using')
check('type', 'abc::ns::foo{id_0, id_1, ...id_2} xyz::bar = ghi::qux', check('type', 'abc::ns::foo{{id_0, id_1, ...id_2}} {key}xyz::bar = ghi::qux',
{2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'}) {2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'}, key='using')
check('function', 'abc::ns::foo{id_0, id_1, id_2} void xyz::bar()', check('function', 'abc::ns::foo{id_0, id_1, id_2} void xyz::bar()',
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barEv', {2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barEv',
4: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barEvv'}) 4: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barEvv'})
@ -772,8 +803,8 @@ def test_templates():
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'}) {2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'})
check('member', 'abc::ns::foo{id_0, id_1, ...id_2} ghi::qux xyz::bar', check('member', 'abc::ns::foo{id_0, id_1, ...id_2} ghi::qux xyz::bar',
{2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'}) {2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'})
check('concept', 'Iterator{T, U} Another', {2: 'I00EX8IteratorI1T1UEE7Another'}) check('concept', 'Iterator{{T, U}} {key}Another', {2: 'I00EX8IteratorI1T1UEE7Another'})
check('concept', 'template<typename ...Pack> Numerics = (... && Numeric<Pack>)', check('concept', 'template<typename ...Pack> {key}Numerics = (... && Numeric<Pack>)',
{2: 'IDpE8Numerics'}) {2: 'IDpE8Numerics'})
# explicit specializations of members # explicit specializations of members
@ -785,7 +816,7 @@ def test_templates():
output='template<> template<> int A<int>::B<int>::b') # same as above output='template<> template<> int A<int>::B<int>::b') # same as above
# defaulted constrained type parameters # defaulted constrained type parameters
check('type', 'template<C T = int&> A', {2: 'I_1CE1A'}) check('type', 'template<C T = int&> {key}A', {2: 'I_1CE1A'}, key='using')
def test_template_args(): def test_template_args():
@ -797,9 +828,10 @@ def test_template_args():
3: "I0E5allowP1FN4funcI1F1BXne1GL1EEE4typeE", 3: "I0E5allowP1FN4funcI1F1BXne1GL1EEE4typeE",
4: "I0E5allowvP1FN4funcI1F1BXne1GL1EEE4typeE"}) 4: "I0E5allowvP1FN4funcI1F1BXne1GL1EEE4typeE"})
# from #3542 # from #3542
check('type', "template<typename T> " check('type', "template<typename T> {key}"
"enable_if_not_array_t = std::enable_if_t<!is_array<T>::value, int>", "enable_if_not_array_t = std::enable_if_t<!is_array<T>::value, int>",
{2: "I0E21enable_if_not_array_t"}) {2: "I0E21enable_if_not_array_t"},
key='using')
def test_initializers(): def test_initializers():