From 4397606d03516cfa692b6a262ba5f41225b7a0f6 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 11 Aug 2018 10:30:10 +0200 Subject: [PATCH] C++, fix direct lookup problem Also clarify documentation regarding cross-references involving templates. See also sphinx-doc/sphinx#2057 --- CHANGES | 2 ++ doc/usage/restructuredtext/domains.rst | 28 +++++++++------- sphinx/domains/cpp.py | 45 +++++++++++++++++--------- 3 files changed, 48 insertions(+), 27 deletions(-) diff --git a/CHANGES b/CHANGES index 911e3889d..c693ef190 100644 --- a/CHANGES +++ b/CHANGES @@ -172,6 +172,8 @@ Features added * C++, add support for anonymous entities using names staring with ``@``. Fixes #3593 and #2683. * #5147: C++, add support for (most) character literals. +* C++, cross-referencing entities inside primary templates is supported, + and now properly documented. * #3606: MathJax should be loaded with async attribute * html: Output ``canonical_url`` metadata if :confval:`html_baseurl` set (refs: #4193) diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst index 95d31f6ce..afc8e8d39 100644 --- a/doc/usage/restructuredtext/domains.rst +++ b/doc/usage/restructuredtext/domains.rst @@ -1033,19 +1033,25 @@ Assume the following declarations. Inner In general the reference must include the template parameter declarations, -e.g., ``template\ Wrapper::Outer`` -(:cpp:class:`template\ Wrapper::Outer`). Currently the lookup -only succeed if the template parameter identifiers are equal strings. That is, -``template\ Wrapper::Outer`` will not work. +and template arguments for the prefix of qualified names. For example: -The inner class template cannot 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. +- ``template\ Wrapper::Outer`` + (:cpp:class:`template\ Wrapper::Outer`) +- ``template\ template\ Wrapper::Outer::Inner`` + (:cpp:class:`template\ template\ Wrapper::Outer::Inner`) -- ``Wrapper::Outer`` (:cpp:class:`Wrapper::Outer`) -- ``Wrapper::Outer::Inner`` (:cpp:class:`Wrapper::Outer::Inner`) +Currently the lookup only succeed if the template parameter identifiers are equal strings. +That is, ``template\ Wrapper::Outer`` will not work. + +As a shorthand notation, if a template parameter list is omitted, +then the lookup will assume either a primary template or a non-template, +but not a partial template specialisation. +This means the following references work as well: + +- ``Wrapper::Outer`` + (:cpp:class:`Wrapper::Outer`) +- ``Wrapper::Outer::Inner`` + (:cpp:class:`Wrapper::Outer::Inner`) - ``template\ Wrapper::Outer::Inner`` (:cpp:class:`template\ Wrapper::Outer::Inner`) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 3155b9326..2db416376 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -3634,8 +3634,9 @@ class Symbol(object): return ASTNestedName(names, templates, rooted=False) def _find_named_symbol(self, identOrOp, templateParams, templateArgs, - templateShorthand, matchSelf, recurseInAnon): - # type: (Any, Any, Any, Any, bool, bool) -> Symbol + templateShorthand, matchSelf, recurseInAnon, + correctPrimaryTemplateArgs): + # type: (Any, Any, Any, Any, bool, bool, bool) -> Symbol def isSpecialization(): # the names of the template parameters must be given exactly as args @@ -3658,12 +3659,13 @@ class Symbol(object): if paramName != argName: return True return False - if templateParams is not None and templateArgs is not None: - # If both are given, but it's not a specialization, then do lookup as if - # there is no argument list. - # For example: template int A::var; - if not isSpecialization(): - templateArgs = None + if correctPrimaryTemplateArgs: + if templateParams is not None and templateArgs is not None: + # If both are given, but it's not a specialization, then do lookup as if + # there is no argument list. + # For example: template int A::var; + if not isSpecialization(): + templateArgs = None def matches(s): if s.identOrOp != identOrOp: @@ -3722,7 +3724,8 @@ class Symbol(object): templateArgs, templateShorthand=False, matchSelf=False, - recurseInAnon=True) + recurseInAnon=True, + correctPrimaryTemplateArgs=True) if symbol is None: symbol = Symbol(parent=parentSymbol, identOrOp=identOrOp, templateParams=templateParams, @@ -3746,7 +3749,8 @@ class Symbol(object): templateArgs, templateShorthand=False, matchSelf=False, - recurseInAnon=True) + recurseInAnon=True, + correctPrimaryTemplateArgs=False) if symbol: if not declaration: # good, just a scope creation @@ -3797,7 +3801,8 @@ class Symbol(object): templateArgs=otherChild.templateArgs, templateShorthand=False, matchSelf=False, - recurseInAnon=False) + recurseInAnon=False, + correctPrimaryTemplateArgs=False) if ourChild is None: # TODO: hmm, should we prune by docnames? self._children.append(otherChild) @@ -3860,7 +3865,8 @@ class Symbol(object): templateParams, templateArgs, templateShorthand=False, matchSelf=False, - recurseInAnon=False) + recurseInAnon=False, + correctPrimaryTemplateArgs=False) if not s: return None return s @@ -3911,7 +3917,8 @@ class Symbol(object): templateParams, templateArgs, templateShorthand=templateShorthand, matchSelf=matchSelf, - recurseInAnon=recurseInAnon) + recurseInAnon=recurseInAnon, + correctPrimaryTemplateArgs=False) if symbol is not None: return symbol # try without template params and args @@ -3919,7 +3926,8 @@ class Symbol(object): None, None, templateShorthand=templateShorthand, matchSelf=matchSelf, - recurseInAnon=recurseInAnon) + recurseInAnon=recurseInAnon, + correctPrimaryTemplateArgs=False) return symbol else: identOrOp = name.identOrOp @@ -3933,9 +3941,13 @@ class Symbol(object): templateParams, templateArgs, templateShorthand=templateShorthand, matchSelf=matchSelf, - recurseInAnon=recurseInAnon) + recurseInAnon=recurseInAnon, + correctPrimaryTemplateArgs=True) if symbol is None: - # TODO: maybe search without template args + # TODO: Maybe search without template args? + # Though, the correctPrimaryTemplateArgs above does + # that for primary templates. + # Is there another case where it would be good? return None # We have now matched part of a nested name, and need to match more # so even if we should matchSelf before, we definitely shouldn't @@ -6343,6 +6355,7 @@ class CPPDomain(Domain): if not parentSymbol: print("Target: ", target) print("ParentKey: ", parentKey) + print(rootSymbol.dump(1)) assert parentSymbol # should be there else: parentSymbol = rootSymbol