From ee3928743beb133aa7549a8420993cc602aa8f72 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 24 Apr 2017 00:45:57 +0900 Subject: [PATCH 01/44] Bump version --- CHANGES | 18 ++++++++++++++++++ sphinx/__init__.py | 6 +++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 979c39ff3..2435523f4 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,21 @@ +Release 1.7 (in development) +============================ + +Incompatible changes +-------------------- + +Deprecated +---------- + +Features added +-------------- + +Bugs fixed +---------- + +Testing +-------- + Release 1.6 beta2 (in development) ================================== diff --git a/sphinx/__init__.py b/sphinx/__init__.py index a071097da..07d02c17c 100644 --- a/sphinx/__init__.py +++ b/sphinx/__init__.py @@ -34,13 +34,13 @@ if 'PYTHONWARNINGS' not in os.environ: warnings.filterwarnings('ignore', "'U' mode is deprecated", DeprecationWarning, module='docutils.io') -__version__ = '1.6+' -__released__ = '1.6' # used when Sphinx builds its own docs +__version__ = '1.7+' +__released__ = '1.7' # used when Sphinx builds its own docs # version info for better programmatic use # possible values for 3rd element: 'alpha', 'beta', 'rc', 'final' # 'final' has 0 as the last element -version_info = (1, 6, 0, 'beta', 2) +version_info = (1, 7, 0, 'beta', 0) package_dir = path.abspath(path.dirname(__file__)) From a7c8795d073d12ae36fdf240f2647f0529ad4ba4 Mon Sep 17 00:00:00 2001 From: Mark Hillebrand Date: Wed, 26 Apr 2017 16:17:11 +0200 Subject: [PATCH 02/44] doc/invocation.rst: document missing options for sphinx-apidoc --- doc/invocation.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/doc/invocation.rst b/doc/invocation.rst index 6cb16e919..d0dedbd8b 100644 --- a/doc/invocation.rst +++ b/doc/invocation.rst @@ -480,6 +480,18 @@ The :program:`sphinx-apidoc` script has several options: With this option given, no files will be written at all. +.. option:: -e, --separate + + Put documentation for each module on its own page. + + .. versionadded:: 1.2 + +.. option:: -P, --private + + Include "_private" modules. + + .. versionadded:: 1.2 + .. option:: -s suffix This option selects the file name suffix of output files. By default, this @@ -505,6 +517,13 @@ The :program:`sphinx-apidoc` script has several options: This prevents the generation of a table-of-contents file ``modules.rst``. This has no effect when :option:`--full` is given. +.. option:: -E, --no-headings + + Don't create headings for the module/package packages (e.g. when the + docstrings already contain them). + + .. versionadded:: 1.2 + .. option:: -F, --full This option makes sphinx-apidoc create a full Sphinx project, using the same From f92523751e8b438ae34c7ec12ad3395dda18ac95 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Wed, 26 Apr 2017 11:53:08 -0400 Subject: [PATCH 03/44] Docs: Add seealso message to config page leading to api docs While hanging around IRC in both #sphinx-doc and #readthedocs I have found that a lot of people do not know about these options even though a lot of theme are pretty common. For example, adding a simple theme overide, most people will edit layout.html instead of Sphinx.add_stylesheet. --- doc/config.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/config.rst b/doc/config.rst index 5e641cbfb..ab2eab0b9 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -50,6 +50,9 @@ Important points to note: Note that the current builder tag is not available in ``conf.py``, as it is created *after* the builder is initialized. +.. seealso:: Aditional configurations, such as adding stylesheets, javascripts, + builders, ect... can be made through the :doc:`/extdev/appapi`. + General configuration --------------------- From 458d1487b2b1630c2e260fcdf9374e51a2fe1a7a Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Wed, 26 Apr 2017 12:59:49 -0400 Subject: [PATCH 04/44] From Review: Fix typos --- doc/config.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/config.rst b/doc/config.rst index ab2eab0b9..8c77ee2c6 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -50,8 +50,8 @@ Important points to note: Note that the current builder tag is not available in ``conf.py``, as it is created *after* the builder is initialized. -.. seealso:: Aditional configurations, such as adding stylesheets, javascripts, - builders, ect... can be made through the :doc:`/extdev/appapi`. +.. seealso:: Additional configurations, such as adding stylesheets, + javascripts, builders, etc. can be made through the :doc:`/extdev/appapi`. General configuration From b4eadba3a1a6c787fbcfcef041d10383e1f51bcc Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 21:49:19 +0900 Subject: [PATCH 05/44] Remove deprecated feature: sphinx.util.nodes.process_only_nodes() --- sphinx/util/nodes.py | 26 -------------------------- tests/test_directive_only.py | 3 +-- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index 5c444fb56..14bd09791 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -11,14 +11,12 @@ from __future__ import absolute_import import re -import warnings from six import text_type from docutils import nodes from sphinx import addnodes -from sphinx.deprecation import RemovedInSphinx17Warning from sphinx.locale import pairindextypes from sphinx.util import logging @@ -354,30 +352,6 @@ def set_role_source_info(inliner, lineno, node): node.source, node.line = inliner.reporter.get_source_and_line(lineno) -def process_only_nodes(doctree, tags): - # type: (nodes.Node, Tags) -> None - # A comment on the comment() nodes being inserted: replacing by [] would - # result in a "Losing ids" exception if there is a target node before - # the only node, so we make sure docutils can transfer the id to - # something, even if it's just a comment and will lose the id anyway... - warnings.warn('process_only_nodes() is deprecated. ' - 'Use sphinx.environment.apply_post_transforms() instead.', - RemovedInSphinx17Warning) - - for node in doctree.traverse(addnodes.only): - try: - ret = tags.eval_condition(node['expr']) - except Exception as err: - logger.warning('exception while evaluating only directive expression: %s', err, - location=node) - node.replace_self(node.children or nodes.comment()) - else: - if ret: - node.replace_self(node.children or nodes.comment()) - else: - node.replace_self(nodes.comment()) - - # monkey-patch Element.copy to copy the rawsource and line def _new_copy(self): diff --git a/tests/test_directive_only.py b/tests/test_directive_only.py index 35d15b391..d8017f469 100644 --- a/tests/test_directive_only.py +++ b/tests/test_directive_only.py @@ -12,7 +12,6 @@ import re from docutils import nodes -from sphinx.util.nodes import process_only_nodes import pytest @@ -46,7 +45,7 @@ def test_sectioning(app, status, warning): app.builder.build(['only']) doctree = app.env.get_doctree('only') - process_only_nodes(doctree, app.builder.tags) + app.env.apply_post_transforms(doctree, 'only') parts = [getsects(n) for n in [_n for _n in doctree.children if isinstance(_n, nodes.section)]] From 3cd87176b6fccf009642c7f0d3f6293e87da654e Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 21:49:19 +0900 Subject: [PATCH 06/44] Remove deprecated feature: sphinx.util.nodes.compat module --- sphinx/util/compat.py | 48 ------------------------------------------- 1 file changed, 48 deletions(-) delete mode 100644 sphinx/util/compat.py diff --git a/sphinx/util/compat.py b/sphinx/util/compat.py deleted file mode 100644 index 30a89bbfb..000000000 --- a/sphinx/util/compat.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.util.compat - ~~~~~~~~~~~~~~~~~~ - - Stuff for docutils compatibility. - - :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" -from __future__ import absolute_import - -import sys -import warnings - -from docutils.parsers.rst import Directive # noqa -from docutils import __version__ as _du_version - -from sphinx.deprecation import RemovedInSphinx17Warning - -docutils_version = tuple(int(x) for x in _du_version.split('.')[:2]) - -if False: - # For type annotation - from typing import Any, Dict # NOQA - - -class _DeprecationWrapper(object): - def __init__(self, mod, deprecated): - # type: (Any, Dict) -> None - self._mod = mod - self._deprecated = deprecated - - def __getattr__(self, attr): - # type: (str) -> Any - if attr in self._deprecated: - warnings.warn("sphinx.util.compat.%s is deprecated and will be " - "removed in Sphinx 1.7, please use the standard " - "library version instead." % attr, - RemovedInSphinx17Warning, stacklevel=2) - return self._deprecated[attr] - return getattr(self._mod, attr) - - -sys.modules[__name__] = _DeprecationWrapper(sys.modules[__name__], dict( # type: ignore - docutils_version = docutils_version, - Directive = Directive, -)) From f2c93b3175c087cbb7305be3a9ed8f8b32cabc57 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 21:49:19 +0900 Subject: [PATCH 07/44] Remove deprecated feature: epub2 builder --- doc/builders.rst | 20 -------- sphinx/application.py | 1 - sphinx/builders/epub2.py | 100 --------------------------------------- sphinx/builders/epub3.py | 27 +++++++++-- tests/test_build.py | 2 +- 5 files changed, 25 insertions(+), 125 deletions(-) delete mode 100644 sphinx/builders/epub2.py diff --git a/doc/builders.rst b/doc/builders.rst index 618eb005c..bf63e7235 100644 --- a/doc/builders.rst +++ b/doc/builders.rst @@ -125,26 +125,6 @@ The builder's "name" must be given to the **-b** command-line option of .. autoattribute:: supported_image_types -.. module:: sphinx.builders.epub2 -.. class:: Epub2Builder - - This builder produces the same output as the standalone HTML builder, but - also generates an *epub* file for ebook readers. See :ref:`epub-faq` for - details about it. For definition of the epub format, have a look at - ``_ or ``_. - The builder creates *EPUB 2* files. - - .. autoattribute:: name - - .. autoattribute:: format - - .. autoattribute:: supported_image_types - - .. deprecated:: 1.5 - - Since Sphinx-1.5, the epub3 builder is used for the default builder of epub. - Now EpubBuilder is renamed to epub2. - .. module:: sphinx.builders.epub3 .. class:: Epub3Builder diff --git a/sphinx/application.py b/sphinx/application.py index 87ed66637..93032578f 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -60,7 +60,6 @@ if False: builtin_extensions = ( 'sphinx.builders.applehelp', 'sphinx.builders.changes', - 'sphinx.builders.epub2', 'sphinx.builders.epub3', 'sphinx.builders.devhelp', 'sphinx.builders.dummy', diff --git a/sphinx/builders/epub2.py b/sphinx/builders/epub2.py deleted file mode 100644 index a6dcc8568..000000000 --- a/sphinx/builders/epub2.py +++ /dev/null @@ -1,100 +0,0 @@ -# -*- coding: utf-8 -*- -""" - sphinx.builders.epub2 - ~~~~~~~~~~~~~~~~~~~~~ - - Build epub2 files. - Originally derived from qthelp.py. - - :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. -""" - -import warnings -from os import path - -from sphinx import package_dir -from sphinx.builders import _epub_base -from sphinx.util.osutil import make_filename -from sphinx.deprecation import RemovedInSphinx17Warning - -if False: - # For type annotation - from typing import Any, Dict # NOQA - from sphinx.application import Sphinx # NOQA - - -DOCTYPE = '''''' - - -# The epub publisher - -class Epub2Builder(_epub_base.EpubBuilder): - """ - Builder that outputs epub files. - - It creates the metainfo files container.opf, toc.ncx, mimetype, and - META-INF/container.xml. Afterwards, all necessary files are zipped to an - epub file. - """ - name = 'epub2' - - template_dir = path.join(package_dir, 'templates', 'epub2') - doctype = DOCTYPE - - # Finish by building the epub file - def handle_finish(self): - # type: () -> None - """Create the metainfo files and finally the epub.""" - self.get_toc() - self.build_mimetype(self.outdir, 'mimetype') - self.build_container(self.outdir, 'META-INF/container.xml') - self.build_content(self.outdir, 'content.opf') - self.build_toc(self.outdir, 'toc.ncx') - self.build_epub(self.outdir, self.config.epub_basename + '.epub') - - -def emit_deprecation_warning(app): - # type: (Sphinx) -> None - if app.builder.__class__ is Epub2Builder: - warnings.warn('epub2 builder is deprecated. Please use epub3 builder instead.', - RemovedInSphinx17Warning) - - -def setup(app): - # type: (Sphinx) -> Dict[unicode, Any] - app.setup_extension('sphinx.builders.html') - app.add_builder(Epub2Builder) - app.connect('builder-inited', emit_deprecation_warning) - - # config values - app.add_config_value('epub_basename', lambda self: make_filename(self.project), None) - app.add_config_value('epub_theme', 'epub', 'html') - app.add_config_value('epub_theme_options', {}, 'html') - app.add_config_value('epub_title', lambda self: self.html_title, 'html') - app.add_config_value('epub_author', 'unknown', 'html') - app.add_config_value('epub_language', lambda self: self.language or 'en', 'html') - app.add_config_value('epub_publisher', 'unknown', 'html') - app.add_config_value('epub_copyright', lambda self: self.copyright, 'html') - app.add_config_value('epub_identifier', 'unknown', 'html') - app.add_config_value('epub_scheme', 'unknown', 'html') - app.add_config_value('epub_uid', 'unknown', 'env') - app.add_config_value('epub_cover', (), 'env') - app.add_config_value('epub_guide', (), 'env') - app.add_config_value('epub_pre_files', [], 'env') - app.add_config_value('epub_post_files', [], 'env') - app.add_config_value('epub_exclude_files', [], 'env') - app.add_config_value('epub_tocdepth', 3, 'env') - app.add_config_value('epub_tocdup', True, 'env') - app.add_config_value('epub_tocscope', 'default', 'env') - app.add_config_value('epub_fix_images', False, 'env') - app.add_config_value('epub_max_image_width', 0, 'env') - app.add_config_value('epub_show_urls', 'inline', 'html') - app.add_config_value('epub_use_index', lambda self: self.html_use_index, 'html') - - return { - 'version': 'builtin', - 'parallel_read_safe': True, - 'parallel_write_safe': True, - } diff --git a/sphinx/builders/epub3.py b/sphinx/builders/epub3.py index 19baad344..574447544 100644 --- a/sphinx/builders/epub3.py +++ b/sphinx/builders/epub3.py @@ -19,6 +19,7 @@ from sphinx.config import string_classes, ENUM from sphinx.builders import _epub_base from sphinx.util import logging from sphinx.util.fileutil import copy_asset_file +from sphinx.util.osutil import make_filename if False: # For type annotation @@ -225,12 +226,32 @@ class Epub3Builder(_epub_base.EpubBuilder): def setup(app): # type: (Sphinx) -> Dict[unicode, Any] - - app.setup_extension('sphinx.builders.epub2') - app.add_builder(Epub3Builder) # config values + app.add_config_value('epub_basename', lambda self: make_filename(self.project), None) + app.add_config_value('epub_theme', 'epub', 'html') + app.add_config_value('epub_theme_options', {}, 'html') + app.add_config_value('epub_title', lambda self: self.html_title, 'html') + app.add_config_value('epub_author', 'unknown', 'html') + app.add_config_value('epub_language', lambda self: self.language or 'en', 'html') + app.add_config_value('epub_publisher', 'unknown', 'html') + app.add_config_value('epub_copyright', lambda self: self.copyright, 'html') + app.add_config_value('epub_identifier', 'unknown', 'html') + app.add_config_value('epub_scheme', 'unknown', 'html') + app.add_config_value('epub_uid', 'unknown', 'env') + app.add_config_value('epub_cover', (), 'env') + app.add_config_value('epub_guide', (), 'env') + app.add_config_value('epub_pre_files', [], 'env') + app.add_config_value('epub_post_files', [], 'env') + app.add_config_value('epub_exclude_files', [], 'env') + app.add_config_value('epub_tocdepth', 3, 'env') + app.add_config_value('epub_tocdup', True, 'env') + app.add_config_value('epub_tocscope', 'default', 'env') + app.add_config_value('epub_fix_images', False, 'env') + app.add_config_value('epub_max_image_width', 0, 'env') + app.add_config_value('epub_show_urls', 'inline', 'html') + app.add_config_value('epub_use_index', lambda self: self.html_use_index, 'html') app.add_config_value('epub_description', 'unknown', 'epub3', string_classes) app.add_config_value('epub_contributor', 'unknown', 'epub3', string_classes) app.add_config_value('epub_writing_mode', 'horizontal', 'epub3', diff --git a/tests/test_build.py b/tests/test_build.py index 221ea6462..02c29d393 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -58,7 +58,7 @@ def nonascii_srcdir(request): [ # note: no 'html' - if it's ok with dirhtml it's ok with html 'dirhtml', 'singlehtml', 'latex', 'texinfo', 'pickle', 'json', 'text', - 'htmlhelp', 'qthelp', 'epub2', 'epub', 'applehelp', 'changes', 'xml', + 'htmlhelp', 'qthelp', 'epub', 'applehelp', 'changes', 'xml', 'pseudoxml', 'man', 'linkcheck', ], ) From 361a34257becbc0ae87c6ae10609427dab7ab11f Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 21:49:19 +0900 Subject: [PATCH 08/44] Remove deprecated feature: latex_keep_old_macro_names --- doc/_static/conf.py.txt | 6 ------ doc/config.rst | 20 -------------------- doc/latex.rst | 5 +---- sphinx/builders/latex.py | 18 ------------------ sphinx/texinputs/sphinx.sty | 32 -------------------------------- sphinx/writers/latex.py | 4 +--- 6 files changed, 2 insertions(+), 83 deletions(-) diff --git a/doc/_static/conf.py.txt b/doc/_static/conf.py.txt index be0c846db..f1a4ecdbc 100644 --- a/doc/_static/conf.py.txt +++ b/doc/_static/conf.py.txt @@ -280,12 +280,6 @@ latex_documents = [ # # latex_appendices = [] -# If false, will not define \strong, \code, \titleref, \crossref ... but only -# \sphinxstrong, ..., \sphinxtitleref, ... to help avoid clash with user added -# packages. -# -# latex_keep_old_macro_names = True - # If false, no module index is generated. # # latex_domain_indices = True diff --git a/doc/config.rst b/doc/config.rst index 8c77ee2c6..7d303b0ee 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -1615,26 +1615,6 @@ These options influence LaTeX output. See further :doc:`latex`. value selected the ``'inline'`` display. For backwards compatibility, ``True`` is still accepted. -.. confval:: latex_keep_old_macro_names - - If ``True`` the ``\strong``, ``\code``, ``\bfcode``, ``\email``, - ``\tablecontinued``, ``\titleref``, ``\menuselection``, ``\accelerator``, - ``\crossref``, ``\termref``, and ``\optional`` text styling macros are - pre-defined by Sphinx and may be user-customized by some - ``\renewcommand``'s inserted either via ``'preamble'`` key or :dudir:`raw - ` directive. If ``False``, only ``\sphinxstrong``, - etc... macros are defined (and may be redefined by user). - - The default is ``False`` as it prevents macro name conflicts caused by - latex packages. For example (``lualatex`` or ``xelatex``) ``fontspec v2.6`` - has its own ``\strong`` macro. - - .. versionadded:: 1.4.5 - .. versionchanged:: 1.6 - Default was changed from ``True`` to ``False``. - .. deprecated:: 1.6 - This setting will be removed at Sphinx 1.7. - .. confval:: latex_use_latex_multicolumn If ``False`` (default), the LaTeX writer uses for merged cells in grid diff --git a/doc/latex.rst b/doc/latex.rst index e2c518058..b8795fa19 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -414,10 +414,7 @@ Let us now list some macros from the package file - text styling commands (they have one argument): ``\sphinx`` with ```` being one of ``strong``, ``bfcode``, ``email``, ``tablecontinued``, ``titleref``, ``menuselection``, ``accelerator``, ``crossref``, ``termref``, - ``optional``. The non-prefixed macros will still be defined if option - :confval:`latex_keep_old_macro_names` has been set to ``True`` (default is - ``False``), in which case the prefixed macros expand to the - non-prefixed ones. + ``optional``. .. versionadded:: 1.4.5 Use of ``\sphinx`` prefixed macro names to limit possibilities of conflict diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex.py index 5b4150d1d..4e906ea60 100644 --- a/sphinx/builders/latex.py +++ b/sphinx/builders/latex.py @@ -10,7 +10,6 @@ """ import os -import warnings from os import path from docutils import nodes @@ -19,7 +18,6 @@ from docutils.utils import new_document from docutils.frontend import OptionParser from sphinx import package_dir, addnodes, highlighting -from sphinx.deprecation import RemovedInSphinx17Warning from sphinx.config import string_classes, ENUM from sphinx.errors import SphinxError from sphinx.locale import _ @@ -257,21 +255,6 @@ def validate_config_values(app): app.config.latex_toplevel_sectioning) app.config.latex_toplevel_sectioning = None # type: ignore - if 'footer' in app.config.latex_elements: - if 'postamble' in app.config.latex_elements: - logger.warning("latex_elements['footer'] conflicts with " - "latex_elements['postamble'], ignored.") - else: - warnings.warn("latex_elements['footer'] is deprecated. " - "Use latex_elements['preamble'] instead.", - RemovedInSphinx17Warning) - app.config.latex_elements['postamble'] = app.config.latex_elements['footer'] - - if app.config.latex_keep_old_macro_names: - warnings.warn("latex_keep_old_macro_names is deprecated. " - "LaTeX markup since Sphinx 1.4.5 uses only prefixed macro names.", - RemovedInSphinx17Warning) - def default_latex_engine(config): # type: (Config) -> unicode @@ -305,7 +288,6 @@ def setup(app): None) app.add_config_value('latex_logo', None, None, string_classes) app.add_config_value('latex_appendices', [], None) - app.add_config_value('latex_keep_old_macro_names', False, None) app.add_config_value('latex_use_latex_multicolumn', False, None) app.add_config_value('latex_toplevel_sectioning', None, None, [str]) app.add_config_value('latex_domain_indices', True, None, [list]) diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 0689a9480..f50384a62 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -185,7 +185,6 @@ \DeclareStringOption[.5\dimexpr\inv@mag in\relax]{marginpar} \fi -\DeclareBoolOption{dontkeepoldnames} % \ifspx@opt@dontkeepoldnames = \iffalse \DeclareStringOption[0]{maxlistdepth}% \newcommand*\spx@opt@maxlistdepth{0} % dimensions, we declare the \dimen registers here. @@ -1347,37 +1346,6 @@ \long\protected\def\sphinxoptional#1{% {\textnormal{\Large[}}{#1}\hspace{0.5mm}{\textnormal{\Large]}}} -\ifspx@opt@dontkeepoldnames\else - \let\spx@alreadydefinedlist\@empty - \typeout{** (sphinx) defining (legacy) text style macros without \string\sphinx\space prefix} - \typeout{** if clashes with packages, do not set latex_keep_old_macro_names=True - in conf.py} - \@for\@tempa:=code,strong,bfcode,email,tablecontinued,titleref,% - menuselection,accelerator,crossref,termref,optional\do - {% first, check if command with no prefix already exists - \ltx@ifundefined{\@tempa}{% - % give it the meaning defined so far with \sphinx prefix - \expandafter\let\csname\@tempa\expandafter\endcsname - \csname sphinx\@tempa\endcsname - % redefine the \sphinx prefixed macro to expand to non-prefixed one - \expandafter\def\csname sphinx\@tempa\expandafter\endcsname - \expandafter{\csname\@tempa\endcsname}% - }{\edef\spx@alreadydefinedlist{\spx@alreadydefinedlist{\@tempa}}}% - }% - \ifx\spx@alreadydefinedlist\@empty\else - \expandafter\@tfor\expandafter\@tempa\expandafter:\expandafter=\spx@alreadydefinedlist\do - {% emit warning now - \PackageWarning{sphinx}{not redefining already existing \@backslashchar\@tempa\space!^^J% - Anyhow, Sphinx mark-up uses only \string\sphinx\@tempa.}% - % and also at end of log for better visibility - \expandafter\sphinxdeprecationwarning\expandafter{\csname\@tempa\endcsname}{1.6}{1.7} - {\sphinxdeprecatedmacro\space already existed at Sphinx loading time! Not redefined!^^J - Sphinx mark-up uses only \string\sphinx\expandafter\@gobble\sphinxdeprecatedmacro.}% - }% - \fi - \sphinxdeprecationwarning{latex_keep_old_macro_names=True}{1.6}{1.7}{}% -\fi - % additional customizable styling % FIXME: convert this to package options ? \protected\def\sphinxstyleindexentry {\texttt} diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index fc2cbb272..4900dbe29 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -58,7 +58,7 @@ DEFAULT_SETTINGS = { 'classoptions': '', 'extraclassoptions': '', 'maxlistdepth': '', - 'sphinxpkgoptions': 'dontkeepoldnames', + 'sphinxpkgoptions': '', 'sphinxsetup': '', 'passoptionstopackages': '', 'geometry': '\\usepackage{geometry}', @@ -548,8 +548,6 @@ class LaTeXTranslator(nodes.NodeVisitor): self.elements.update({ 'releasename': _('Release'), }) - if builder.config.latex_keep_old_macro_names: - self.elements['sphinxpkgoptions'] = '' if document.settings.docclass == 'howto': docclass = builder.config.latex_docclass.get('howto', 'article') else: From c74eb9448b3a335510daa362be25d7ac5bd2277c Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 21:49:19 +0900 Subject: [PATCH 09/44] Remove deprecated feature: deprecated APIs --- sphinx/application.py | 67 ++-------------------------------- sphinx/builders/__init__.py | 3 -- sphinx/environment/__init__.py | 20 +--------- 3 files changed, 5 insertions(+), 85 deletions(-) diff --git a/sphinx/application.py b/sphinx/application.py index 93032578f..63834b119 100644 --- a/sphinx/application.py +++ b/sphinx/application.py @@ -29,7 +29,7 @@ import sphinx from sphinx import package_dir, locale from sphinx.config import Config from sphinx.errors import ConfigError, ExtensionError, VersionRequirementError -from sphinx.deprecation import RemovedInSphinx17Warning, RemovedInSphinx20Warning +from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.environment import BuildEnvironment from sphinx.events import EventManager from sphinx.extension import verify_required_extensions @@ -39,10 +39,9 @@ from sphinx.registry import SphinxComponentRegistry from sphinx.util import pycompat # noqa: F401 from sphinx.util import import_object from sphinx.util import logging -from sphinx.util import status_iterator, old_status_iterator, display_chunk from sphinx.util.tags import Tags from sphinx.util.osutil import ENOENT -from sphinx.util.console import bold, darkgreen # type: ignore +from sphinx.util.console import bold # type: ignore from sphinx.util.docutils import is_html5_writer_available, directive_helper from sphinx.util.i18n import find_catalog_source_files @@ -314,13 +313,6 @@ class Sphinx(object): for node, settings in iteritems(self.enumerable_nodes): self.env.get_domain('std').enumerable_nodes[node] = settings # type: ignore - @property - def buildername(self): - # type: () -> unicode - warnings.warn('app.buildername is deprecated. Please use app.builder.name instead', - RemovedInSphinx17Warning) - return self.builder.name - # ---- main "build" method ------------------------------------------------- def build(self, force_all=False, filenames=None): @@ -356,31 +348,15 @@ class Sphinx(object): self.builder.cleanup() # ---- logging handling ---------------------------------------------------- - def warn(self, message, location=None, prefix=None, - type=None, subtype=None, colorfunc=None): - # type: (unicode, unicode, unicode, unicode, unicode, Callable) -> None + def warn(self, message, location=None, type=None, subtype=None): + # type: (unicode, unicode, unicode, unicode) -> None """Emit a warning. If *location* is given, it should either be a tuple of (docname, lineno) or a string describing the location of the warning as well as possible. - *prefix* usually should not be changed. - *type* and *subtype* are used to suppress warnings with :confval:`suppress_warnings`. - - .. note:: - - For warnings emitted during parsing, you should use - :meth:`.BuildEnvironment.warn` since that will collect all - warnings during parsing for later output. """ - if prefix: - warnings.warn('prefix option of warn() is now deprecated.', - RemovedInSphinx17Warning) - if colorfunc: - warnings.warn('colorfunc option of warn() is now deprecated.', - RemovedInSphinx17Warning) - warnings.warn('app.warning() is now deprecated. Use sphinx.util.logging instead.', RemovedInSphinx20Warning) logger.warning(message, type=type, subtype=subtype, location=location) @@ -417,34 +393,6 @@ class Sphinx(object): RemovedInSphinx20Warning) logger.debug(message, *args, **kwargs) - def _display_chunk(chunk): - # type: (Any) -> unicode - warnings.warn('app._display_chunk() is now deprecated. ' - 'Use sphinx.util.display_chunk() instead.', - RemovedInSphinx17Warning) - return display_chunk(chunk) - - def old_status_iterator(self, iterable, summary, colorfunc=darkgreen, - stringify_func=display_chunk): - # type: (Iterable, unicode, Callable, Callable[[Any], unicode]) -> Iterator - warnings.warn('app.old_status_iterator() is now deprecated. ' - 'Use sphinx.util.status_iterator() instead.', - RemovedInSphinx17Warning) - for item in old_status_iterator(iterable, summary, - color="darkgreen", stringify_func=stringify_func): - yield item - - # new version with progress info - def status_iterator(self, iterable, summary, colorfunc=darkgreen, length=0, - stringify_func=_display_chunk): - # type: (Iterable, unicode, Callable, int, Callable[[Any], unicode]) -> Iterable - warnings.warn('app.status_iterator() is now deprecated. ' - 'Use sphinx.util.status_iterator() instead.', - RemovedInSphinx17Warning) - for item in status_iterator(iterable, summary, length=length, verbosity=self.verbosity, - color="darkgreen", stringify_func=stringify_func): - yield item - # ---- general extensibility interface ------------------------------------- def setup_extension(self, extname): @@ -566,13 +514,6 @@ class Sphinx(object): self.enumerable_nodes[node] = (figtype, title_getter) self.add_node(node, **kwds) - def _directive_helper(self, obj, has_content=None, argument_spec=None, **option_spec): - # type: (Any, bool, Tuple[int, int, bool], Any) -> Any - warnings.warn('_directive_helper() is now deprecated. ' - 'Please use sphinx.util.docutils.directive_helper() instead.', - RemovedInSphinx17Warning) - return directive_helper(obj, has_content, argument_spec, **option_spec) - def add_directive(self, name, obj, content=None, arguments=None, **options): # type: (unicode, Any, bool, Tuple[int, int, bool], Any) -> None logger.debug('[app] adding directive: %r', diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index fdf524bf6..a8a507eaa 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -91,9 +91,6 @@ class Builder(object): self.tags.add(self.name) self.tags.add("format_%s" % self.format) self.tags.add("builder_%s" % self.name) - # compatibility aliases - self.status_iterator = app.status_iterator - self.old_status_iterator = app.old_status_iterator # images that need to be copied over (source -> dest) self.images = {} # type: Dict[unicode, unicode] diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index 066f57209..1a56d0299 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -46,7 +46,7 @@ from sphinx.errors import SphinxError, ExtensionError from sphinx.locale import _ from sphinx.transforms import SphinxTransformer from sphinx.versioning import add_uids, merge_doctrees -from sphinx.deprecation import RemovedInSphinx17Warning, RemovedInSphinx20Warning +from sphinx.deprecation import RemovedInSphinx20Warning from sphinx.environment.adapters.indexentries import IndexEntries from sphinx.environment.adapters.toctree import TocTree @@ -767,24 +767,6 @@ class BuildEnvironment(object): """Returns the docname of the document currently being parsed.""" return self.temp_data['docname'] - @property - def currmodule(self): - # type: () -> None - """Backwards compatible alias. Will be removed.""" - warnings.warn('env.currmodule is deprecated. ' - 'Use env.ref_context["py:module"] instead.', - RemovedInSphinx17Warning) - return self.ref_context.get('py:module') - - @property - def currclass(self): - # type: () -> None - """Backwards compatible alias. Will be removed.""" - warnings.warn('env.currclass is deprecated. ' - 'Use env.ref_context["py:class"] instead.', - RemovedInSphinx17Warning) - return self.ref_context.get('py:class') - def new_serialno(self, category=''): # type: (unicode) -> int """Return a serial number, e.g. for index entry targets. From b15cf7f89fced7d270e19b9bfd175ec6bda4b04b Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 21:49:19 +0900 Subject: [PATCH 10/44] Remove deprecated feature: drop RemovedInSphinx17Warning --- sphinx/deprecation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sphinx/deprecation.py b/sphinx/deprecation.py index 608b23c1b..bb63df330 100644 --- a/sphinx/deprecation.py +++ b/sphinx/deprecation.py @@ -10,11 +10,11 @@ """ -class RemovedInSphinx17Warning(DeprecationWarning): +class RemovedInSphinx18Warning(DeprecationWarning): pass -class RemovedInSphinx18Warning(PendingDeprecationWarning): +class RemovedInSphinx19Warning(PendingDeprecationWarning): pass @@ -22,4 +22,4 @@ class RemovedInSphinx20Warning(PendingDeprecationWarning): pass -RemovedInNextVersionWarning = RemovedInSphinx17Warning +RemovedInNextVersionWarning = RemovedInSphinx18Warning From 8d5d70ba62c4138c52bba3c640a00b964ffa82c1 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 21:49:19 +0900 Subject: [PATCH 11/44] latex: Use ENUM for validate config value --- sphinx/builders/latex.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/sphinx/builders/latex.py b/sphinx/builders/latex.py index 4e906ea60..c03803c5a 100644 --- a/sphinx/builders/latex.py +++ b/sphinx/builders/latex.py @@ -248,14 +248,6 @@ class LaTeXBuilder(Builder): path.join(self.srcdir, src), err) -def validate_config_values(app): - # type: (Sphinx) -> None - if app.config.latex_toplevel_sectioning not in (None, 'part', 'chapter', 'section'): - logger.warning('invalid latex_toplevel_sectioning, ignored: %s', - app.config.latex_toplevel_sectioning) - app.config.latex_toplevel_sectioning = None # type: ignore - - def default_latex_engine(config): # type: (Config) -> unicode """ Better default latex_engine settings for specific languages. """ @@ -278,7 +270,6 @@ def default_latex_docclass(config): def setup(app): # type: (Sphinx) -> Dict[unicode, Any] app.add_builder(LaTeXBuilder) - app.connect('builder-inited', validate_config_values) app.add_config_value('latex_engine', default_latex_engine, None, ENUM('pdflatex', 'xelatex', 'lualatex', 'platex')) @@ -289,7 +280,8 @@ def setup(app): app.add_config_value('latex_logo', None, None, string_classes) app.add_config_value('latex_appendices', [], None) app.add_config_value('latex_use_latex_multicolumn', False, None) - app.add_config_value('latex_toplevel_sectioning', None, None, [str]) + app.add_config_value('latex_toplevel_sectioning', None, None, + ENUM('part', 'chapter', 'section')) app.add_config_value('latex_domain_indices', True, None, [list]) app.add_config_value('latex_show_urls', 'no', None) app.add_config_value('latex_show_pagerefs', False, None) From 8fda21099d33696b84318c51e22a807cda9f32da Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 27 Apr 2017 23:38:42 +0900 Subject: [PATCH 12/44] Remove deprecated testing utilities --- tests/test_build_html5.py | 11 +-- tests/test_build_latex.py | 8 +- tests/test_build_texinfo.py | 4 +- tests/test_build_text.py | 5 +- tests/test_docutilsconf.py | 4 +- tests/test_ext_math.py | 9 +- tests/test_quickstart.py | 4 +- tests/util.py | 160 ------------------------------------ 8 files changed, 18 insertions(+), 187 deletions(-) diff --git a/tests/test_build_html5.py b/tests/test_build_html5.py index b491b6306..e43decaf9 100644 --- a/tests/test_build_html5.py +++ b/tests/test_build_html5.py @@ -14,19 +14,13 @@ :license: BSD, see LICENSE for details. """ -import os -import re -from itertools import cycle, chain import xml.etree.cElementTree as ElementTree -from six import PY3 import pytest from html5lib import getTreeBuilder, HTMLParser -from sphinx import __display_version__ from sphinx.util.docutils import is_html5_writer_available -from util import remove_unicode_literals, strip_escseq, skip_unless from test_build_html import flat_dict, tail_check, check_xpath TREE_BUILDER = getTreeBuilder('etree', implementation=ElementTree) @@ -35,7 +29,8 @@ HTML_PARSER = HTMLParser(TREE_BUILDER, namespaceHTMLElements=False) etree_cache = {} -@skip_unless(is_html5_writer_available()) + +@pytest.mark.skipif(not is_html5_writer_available(), reason='HTML5 writer is not available') @pytest.fixture(scope='module') def cached_etree_parse(): def parse(fname): @@ -50,7 +45,7 @@ def cached_etree_parse(): etree_cache.clear() -@skip_unless(is_html5_writer_available()) +@pytest.mark.skipif(not is_html5_writer_available(), reason='HTML5 writer is not available') @pytest.mark.parametrize("fname,expect", flat_dict({ 'images.html': [ (".//img[@src='_images/img.png']", ''), diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index ad882758c..3cda73546 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -24,7 +24,7 @@ from sphinx.util.osutil import cd, ensuredir from sphinx.util import docutils from sphinx.writers.latex import LaTeXTranslator -from util import SkipTest, remove_unicode_literals, strip_escseq, skip_if +from util import remove_unicode_literals, strip_escseq from test_build_html import ENV_WARNINGS @@ -77,7 +77,7 @@ def compile_latex_document(app): 'SphinxTests.tex'], stdout=PIPE, stderr=PIPE) except OSError: # most likely the latex executable was not found - raise SkipTest + raise pytest.skip.Exception else: stdout, stderr = p.communicate() if p.returncode != 0: @@ -90,7 +90,7 @@ def compile_latex_document(app): def skip_if_requested(testfunc): if 'SKIP_LATEX_BUILD' in os.environ: msg = 'Skip LaTeX builds because SKIP_LATEX_BUILD is set' - return skip_if(True, msg)(testfunc) + return pytest.mark.skipif(True, reason=msg)(testfunc) else: return testfunc @@ -98,7 +98,7 @@ def skip_if_requested(testfunc): def skip_if_stylefiles_notfound(testfunc): if kpsetest(*STYLEFILES) is False: msg = 'not running latex, the required styles do not seem to be installed' - return skip_if(True, msg)(testfunc) + return pytest.mark.skipif(True, reason=msg)(testfunc) else: return testfunc diff --git a/tests/test_build_texinfo.py b/tests/test_build_texinfo.py index 0177bc392..68b0e2067 100644 --- a/tests/test_build_texinfo.py +++ b/tests/test_build_texinfo.py @@ -19,7 +19,7 @@ import pytest from sphinx.writers.texinfo import TexinfoTranslator -from util import SkipTest, remove_unicode_literals, strip_escseq +from util import remove_unicode_literals, strip_escseq from test_build_html import ENV_WARNINGS @@ -58,7 +58,7 @@ def test_texinfo(app, status, warning): p = Popen(['makeinfo', '--no-split', 'SphinxTests.texi'], stdout=PIPE, stderr=PIPE) except OSError: - raise SkipTest # most likely makeinfo was not found + raise pytest.skip.Exception # most likely makeinfo was not found else: stdout, stderr = p.communicate() retcode = p.returncode diff --git a/tests/test_build_text.py b/tests/test_build_text.py index 6ce3b5276..81e354ecd 100644 --- a/tests/test_build_text.py +++ b/tests/test_build_text.py @@ -8,12 +8,11 @@ :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ +import pytest from docutils.utils import column_width from sphinx.writers.text import MAXWIDTH -from util import with_app - def with_text_app(*args, **kw): default_kw = { @@ -21,7 +20,7 @@ def with_text_app(*args, **kw): 'testroot': 'build-text', } default_kw.update(kw) - return with_app(*args, **default_kw) + return pytest.mark.sphinx(*args, **default_kw) @with_text_app() diff --git a/tests/test_docutilsconf.py b/tests/test_docutilsconf.py index 3c43b6e7c..fe848de2e 100644 --- a/tests/test_docutilsconf.py +++ b/tests/test_docutilsconf.py @@ -12,7 +12,7 @@ import re import pytest -from util import path, SkipTest +from util import path def regex_count(expr, result): @@ -78,7 +78,7 @@ def test_docutils_source_link_with_nonascii_file(app, status, warning): (srcdir / (mb_name + '.txt')).write_text('') except UnicodeEncodeError: from path import FILESYSTEMENCODING - raise SkipTest( + raise pytest.skip.Exception( 'nonascii filename not supported on this filesystem encoding: ' '%s', FILESYSTEMENCODING) diff --git a/tests/test_ext_math.py b/tests/test_ext_math.py index 296bba94f..a6de6b788 100644 --- a/tests/test_ext_math.py +++ b/tests/test_ext_math.py @@ -12,7 +12,6 @@ import re import pytest -from util import SkipTest @pytest.mark.sphinx( @@ -40,9 +39,9 @@ def test_jsmath(app, status, warning): def test_imgmath_png(app, status, warning): app.builder.build_all() if "LaTeX command 'latex' cannot be run" in warning.getvalue(): - raise SkipTest('LaTeX command "latex" is not available') + raise pytest.skip.Exception('LaTeX command "latex" is not available') if "dvipng command 'dvipng' cannot be run" in warning.getvalue(): - raise SkipTest('dvipng command "dvipng" is not available') + raise pytest.skip.Exception('dvipng command "dvipng" is not available') content = (app.outdir / 'index.html').text() html = (r'
\s*

\s*\s*

\s* Date: Sat, 29 Apr 2017 15:15:22 +0900 Subject: [PATCH 13/44] Update doc --- doc/latex.rst | 9 --------- 1 file changed, 9 deletions(-) diff --git a/doc/latex.rst b/doc/latex.rst index b8795fa19..14f21f6e2 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -416,15 +416,6 @@ Let us now list some macros from the package file ``titleref``, ``menuselection``, ``accelerator``, ``crossref``, ``termref``, ``optional``. - .. versionadded:: 1.4.5 - Use of ``\sphinx`` prefixed macro names to limit possibilities of conflict - with LaTeX packages. - .. versionchanged:: 1.6 - The default value of :confval:`latex_keep_old_macro_names` changes to - ``False``, and even if set to ``True``, if a non-prefixed macro - already exists at ``sphinx.sty`` loading time, only the ``\sphinx`` - prefixed one will be defined. The setting will be removed at 1.7. - - more text styling commands: ``\sphinxstyle`` with ```` one of ``indexentry``, ``indexextra``, ``indexpageref``, ``topictitle``, ``sidebartitle``, ``othertitle``, ``sidebarsubtitle``, ``thead``, From cddb163e3b109c129bca0e8b7083418b02e9d09f Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 29 Apr 2017 15:16:44 +0900 Subject: [PATCH 14/44] sphinx.sty: Remove old comment --- sphinx/texinputs/sphinx.sty | 1 - 1 file changed, 1 deletion(-) diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index f50384a62..2d11d81d1 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -1310,7 +1310,6 @@ %% TEXT STYLING % % Some custom font markup commands. -% *** the macros without \sphinx prefix are still defined farther down *** \protected\def\sphinxstrong#1{{\textbf{#1}}} % to obtain straight quotes we execute \@noligs as patched by upquote, and % \scantokens is needed in cases where it would be too late for the macro to From 609a08660e8978cccea7ffc061afbe493da542cd Mon Sep 17 00:00:00 2001 From: jfbu Date: Mon, 1 May 2017 18:12:59 +0200 Subject: [PATCH 15/44] Remove deprecated latex features at 1.7 notice environment, loading of packages eqparbox and multirow --- sphinx/templates/latex/latex.tex_t | 10 ---------- sphinx/texinputs/sphinx.sty | 8 -------- 2 files changed, 18 deletions(-) diff --git a/sphinx/templates/latex/latex.tex_t b/sphinx/templates/latex/latex.tex_t index 55f60437e..6c57939aa 100644 --- a/sphinx/templates/latex/latex.tex_t +++ b/sphinx/templates/latex/latex.tex_t @@ -25,16 +25,6 @@ \usepackage<%= sphinxpkgoptions %>{sphinx} <%= sphinxsetup %> <%= geometry %> -\usepackage{multirow} -\let\originalmutirow\multirow\protected\def\multirow{% - \sphinxdeprecationwarning{\multirow}{1.6}{1.7} - {Sphinx does not use package multirow. Its loading will be removed at 1.7.}% - \originalmultirow}% -\usepackage{eqparbox} -\let\originaleqparbox\eqparbox\protected\def\eqparbox{% - \sphinxdeprecationwarning{\eqparbox}{1.6}{1.7} - {Sphinx does not use package eqparbox. Its loading will be removed at 1.7.}% - \originaleqparbox}% <%= usepackages %> <%= hyperref %> <%= contentsname %> diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 6317485cf..b069b712f 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -1159,14 +1159,6 @@ \begin{sphinx#1}{#2}} % in end part, need to go around a LaTeX's "feature" {\edef\spx@temp{\noexpand\end{sphinx\spx@noticetype}}\spx@temp} -% use of ``notice'' is for backwards compatibility and will be removed in -% Sphinx 1.7. -\newenvironment{notice} - {\sphinxdeprecationwarning {notice}{1.6}{1.7}{% - This document was probably built with a Sphinx extension using ``notice''^^J - environment. At Sphinx 1.7, ``notice'' environment will be removed. Please^^J - report to extension author to use ``sphinxadmonition'' instead.^^J% - ****}\begin{sphinxadmonition}}{\end{sphinxadmonition}} %% PYTHON DOCS MACROS AND ENVIRONMENTS From 5cd0f235a891de80b637c20e0d90fd916cc68a86 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Tue, 2 May 2017 19:51:04 +0900 Subject: [PATCH 16/44] C++, handle decltype(auto) --- CHANGES | 2 ++ sphinx/domains/cpp.py | 26 ++++++++++++++++++++++++-- tests/test_domain_cpp.py | 5 ++--- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index c949983ec..0bf3e4765 100644 --- a/CHANGES +++ b/CHANGES @@ -10,6 +10,8 @@ Deprecated Features added -------------- +- C++, handle ``decltype(auto)``. + Bugs fixed ---------- diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index b1e83a041..308d20b24 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -47,7 +47,7 @@ logger = logging.getLogger(__name__) It is not the actual old code, but a replication of the behaviour. - v2: 1.3 <= version < now Standardised mangling scheme from - http://mentorembedded.github.io/cxx-abi/abi.html#mangling + http://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling though not completely implemented. All versions are generated and attached to elements. The newest is used for the index. All of the versions should work as permalinks. @@ -1440,6 +1440,21 @@ class ASTTrailingTypeSpecName(ASTBase): self.nestedName.describe_signature(signode, mode, env, symbol=symbol) +class ASTTrailingTypeSpecDecltypeAuto(ASTBase): + def __unicode__(self): + return u'decltype(auto)' + + def get_id_v1(self): + raise NoOldIdError() + + def get_id_v2(self): + return 'Dc' + + def describe_signature(self, signode, mode, env, symbol): + # type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None + signode.append(nodes.Text(text_type(self))) + + class ASTFunctionParameter(ASTBase): def __init__(self, arg, ellipsis=False): # type: (Any, bool) -> None @@ -3642,7 +3657,14 @@ class DefinitionParser(object): # decltype self.skip_ws() if self.skip_word_and_ws('decltype'): - self.fail('"decltype(.)" in trailing_type_spec not implemented') + if not self.skip_string_and_ws('('): + self.fail("Expected '(' after 'decltype'.") + if self.skip_word_and_ws('auto'): + if not self.skip_string(')'): + self.fail("Expected ')' after 'decltype(auto'.") + return ASTTrailingTypeSpecDecltypeAuto() + self.fail('"decltype()" in trailing_type_spec not implemented') + #return ASTTrailingTypeSpecDecltype() # prefixed prefix = None diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 49d8e3206..446c264fd 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -85,10 +85,9 @@ def check(name, input, idv1output=None, idv2output=None, output=None): def test_fundamental_types(): # see http://en.cppreference.com/w/cpp/language/types for t, id_v2 in cppDomain._id_fundamental_v2.items(): - if t == "decltype(auto)": - continue - def makeIdV1(): + if t == 'decltype(auto)': + return None id = t.replace(" ", "-").replace("long", "l").replace("int", "i") id = id.replace("bool", "b").replace("char", "c") id = id.replace("wc_t", "wchar_t").replace("c16_t", "char16_t") From 87b30cc3cbb23eb8d1f95c9a16536f1b7937d54f Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 01:07:20 +0900 Subject: [PATCH 17/44] Fix #3685: AttributeError when using 3rd party domains --- CHANGES | 1 + sphinx/util/docfields.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 3e9f938bb..b48e17778 100644 --- a/CHANGES +++ b/CHANGES @@ -19,6 +19,7 @@ Bugs fixed * #3657: EPUB builder crashes if document startswith genindex exists * #3588: No compact (p tag) html output in the i18n document build even when :confval:`html_compact_lists` is True. +* #3685: AttributeError when using 3rd party domains Testing -------- diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py index 79b19d00e..45bdb0600 100644 --- a/sphinx/util/docfields.py +++ b/sphinx/util/docfields.py @@ -310,7 +310,8 @@ class DocFieldTransformer(object): else: fieldtype, content = entry fieldtypes = types.get(fieldtype.name, {}) + env = self.directive.state.document.settings.env new_list += fieldtype.make_field(fieldtypes, self.directive.domain, - content, env=self.directive.env) + content, env=env) node.replace_self(new_list) From 097cd87fd8ceda2fed377c5207909e351315b24e Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 01:18:13 +0900 Subject: [PATCH 18/44] Update CHANGES for PR #2454 (refs: 3691) --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index b48e17778..9b96777ba 100644 --- a/CHANGES +++ b/CHANGES @@ -259,6 +259,7 @@ Incompatible changes * Emit warnings that will be deprecated in Sphinx 1.6 by default. Users can change the behavior by setting the environment variable PYTHONWARNINGS. Please refer :ref:`when-deprecation-warnings-are-displayed`. +* #2454: new JavaScript variable ``SOURCELINK_SUFFIX`` is added Deprecated ---------- From 4ffe79c54872eea943663dc5bfe9d74139adfb80 Mon Sep 17 00:00:00 2001 From: jfbu Date: Tue, 2 May 2017 18:35:33 +0200 Subject: [PATCH 19/44] Fix duplicate entry in CHANGES (in 1.5a1 Incompatible changes) --- CHANGES | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 9b96777ba..7c0c7e564 100644 --- a/CHANGES +++ b/CHANGES @@ -218,10 +218,8 @@ Incompatible changes * QtHelpBuilder doens't generate search page (ref: #2352) * QtHelpBuilder uses ``nonav`` theme instead of default one to improve readability. -* latex: To provide good default settings to Japanese docs, Sphinx uses ``jsbook`` - as a docclass by default if the ``language`` is ``ja``. -* latex: To provide good default settings to Japanese docs, Sphinx uses - ``jreport`` and ``jsbooks`` as a docclass by default if the ``language`` is +* latex: To provide good default settings to Japanese documents, Sphinx uses + ``jreport`` and ``jsbook`` as docclass if :confval:`language` is ``ja``. * ``sphinx-quickstart`` now allows a project version is empty * Fix :download: role on epub/qthelp builder. They ignore the role because they don't support it. From b27fbc4aa5454723b83d234d1f29a0efcd8c8810 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 17:27:32 +0900 Subject: [PATCH 20/44] Remove html_use_smartypants (deprecated) --- doc/_static/conf.py.txt | 5 ----- doc/config.rst | 12 ------------ sphinx/builders/html.py | 1 - sphinx/environment/__init__.py | 8 +------- 4 files changed, 1 insertion(+), 25 deletions(-) diff --git a/doc/_static/conf.py.txt b/doc/_static/conf.py.txt index f1a4ecdbc..50c7bb782 100644 --- a/doc/_static/conf.py.txt +++ b/doc/_static/conf.py.txt @@ -167,11 +167,6 @@ html_static_path = ['_static'] # # html_last_updated_fmt = None -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -# -# html_use_smartypants = True - # Custom sidebar templates, maps document names to template names. # # html_sidebars = {} diff --git a/doc/config.rst b/doc/config.rst index 229b0348f..e04d46b71 100644 --- a/doc/config.rst +++ b/doc/config.rst @@ -765,18 +765,6 @@ that use Sphinx's HTMLWriter class. The empty string is equivalent to ``'%b %d, %Y'`` (or a locale-dependent equivalent). -.. confval:: html_use_smartypants - - If true, `SmartyPants `_ - will be used to convert quotes and dashes to typographically correct - entities. Default: ``True``. - - .. deprecated:: 1.6 - Use the `smart_quotes option`_ in the Docutils configuration file - (``docutils.conf``) instead. - - .. _`smart_quotes option`: http://docutils.sourceforge.net/docs/user/config.html#smart-quotes - .. confval:: html_add_permalinks Sphinx will add "permalinks" for each heading and description environment as diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 08ec76618..4c9a5c492 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -1346,7 +1346,6 @@ def setup(app): app.add_config_value('html_static_path', [], 'html') app.add_config_value('html_extra_path', [], 'html') app.add_config_value('html_last_updated_fmt', None, 'html', string_classes) - app.add_config_value('html_use_smartypants', None, 'html') app.add_config_value('html_sidebars', {}, 'html') app.add_config_value('html_additional_pages', {}, 'html') app.add_config_value('html_domain_indices', True, 'html', [list]) diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py index ed2740f02..3fa90459c 100644 --- a/sphinx/environment/__init__.py +++ b/sphinx/environment/__init__.py @@ -674,13 +674,7 @@ class BuildEnvironment(object): self.settings['gettext_compact'] = self.config.gettext_compact language = (self.config.language or 'en').replace('_', '-') self.settings['language_code'] = language - if self.config.html_use_smartypants is not None: - warnings.warn("html_use_smartypants option is deprecated. Use the " - "smart_quotes option in docutils.conf instead.", - RemovedInSphinx17Warning) - if language in smartchars.quotes: - self.settings['smart_quotes'] = self.config.html_use_smartypants - elif language in smartchars.quotes: # We enable smartypants by default + if language in smartchars.quotes: # We enable smartypants by default self.settings['smart_quotes'] = True docutilsconf = path.join(self.srcdir, 'docutils.conf') From 349881a52fec2cd585bbf299e582840efd1cfd5f Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 17:28:04 +0900 Subject: [PATCH 21/44] Fix flake8 violation --- sphinx/domains/cpp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 308d20b24..4dfe15f02 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -3664,7 +3664,7 @@ class DefinitionParser(object): self.fail("Expected ')' after 'decltype(auto'.") return ASTTrailingTypeSpecDecltypeAuto() self.fail('"decltype()" in trailing_type_spec not implemented') - #return ASTTrailingTypeSpecDecltype() + # return ASTTrailingTypeSpecDecltype() # prefixed prefix = None From 51ad88f1487a442f4ac244e2941f6b25d1fa9c06 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 17:35:45 +0900 Subject: [PATCH 22/44] Update CHANGES --- CHANGES | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/CHANGES b/CHANGES index 0bf3e4765..94207fb8d 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,33 @@ Features added - C++, handle ``decltype(auto)``. +Features removed +---------------- + +* Configuration variables + + - html_use_smartypants + - latex_keep_old_macro_names + - latex_elements['footer'] + +* utility methods of ``sphinx.application.Sphinx`` class + + - buildername (property) + - _display_chunk() + - old_status_iterator() + - status_iterator() + - _directive_helper() + +* utility methods of ``sphinx.environment.BuildEnvironment`` class + + - currmodule (property) + - currclass (property) + +* epub2 builder +* prefix and colorfunc parameter for warn() +* ``sphinx.util.compat`` module +* ``sphinx.util.nodes.process_only_nodes()`` + Bugs fixed ---------- From 81a1835cf1d9409a234f835b6c6fe805f80ea6a5 Mon Sep 17 00:00:00 2001 From: jfbu Date: Mon, 1 May 2017 18:12:59 +0200 Subject: [PATCH 23/44] Remove deprecated latex features at 1.7 notice environment, loading of packages eqparbox and multirow --- sphinx/templates/latex/latex.tex_t | 10 ---------- sphinx/texinputs/sphinx.sty | 8 -------- 2 files changed, 18 deletions(-) diff --git a/sphinx/templates/latex/latex.tex_t b/sphinx/templates/latex/latex.tex_t index 55f60437e..6c57939aa 100644 --- a/sphinx/templates/latex/latex.tex_t +++ b/sphinx/templates/latex/latex.tex_t @@ -25,16 +25,6 @@ \usepackage<%= sphinxpkgoptions %>{sphinx} <%= sphinxsetup %> <%= geometry %> -\usepackage{multirow} -\let\originalmutirow\multirow\protected\def\multirow{% - \sphinxdeprecationwarning{\multirow}{1.6}{1.7} - {Sphinx does not use package multirow. Its loading will be removed at 1.7.}% - \originalmultirow}% -\usepackage{eqparbox} -\let\originaleqparbox\eqparbox\protected\def\eqparbox{% - \sphinxdeprecationwarning{\eqparbox}{1.6}{1.7} - {Sphinx does not use package eqparbox. Its loading will be removed at 1.7.}% - \originaleqparbox}% <%= usepackages %> <%= hyperref %> <%= contentsname %> diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 6317485cf..b069b712f 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -1159,14 +1159,6 @@ \begin{sphinx#1}{#2}} % in end part, need to go around a LaTeX's "feature" {\edef\spx@temp{\noexpand\end{sphinx\spx@noticetype}}\spx@temp} -% use of ``notice'' is for backwards compatibility and will be removed in -% Sphinx 1.7. -\newenvironment{notice} - {\sphinxdeprecationwarning {notice}{1.6}{1.7}{% - This document was probably built with a Sphinx extension using ``notice''^^J - environment. At Sphinx 1.7, ``notice'' environment will be removed. Please^^J - report to extension author to use ``sphinxadmonition'' instead.^^J% - ****}\begin{sphinxadmonition}}{\end{sphinxadmonition}} %% PYTHON DOCS MACROS AND ENVIRONMENTS From 6c293ced2b54b1c455b077cf1f391c5b1f236951 Mon Sep 17 00:00:00 2001 From: jfbu Date: Wed, 3 May 2017 11:39:26 +0200 Subject: [PATCH 24/44] Update CHANGES after PR #3687 --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index 48477560e..8fdcf36da 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,9 @@ Release 1.6 beta3 (in development) Incompatible changes -------------------- +* LaTeX package ``eqparbox`` is not used and not loaded by Sphinx anymore +* LaTeX package ``multirow`` is not used and not loaded by Sphinx anymore + Deprecated ---------- From 3485f003498d349b6311a289fec0406fb022ef97 Mon Sep 17 00:00:00 2001 From: jfbu Date: Wed, 3 May 2017 11:48:21 +0200 Subject: [PATCH 25/44] Define (again) deprecated `notice` environment for 1.6 series It was removed by mistake at 81a1835 --- sphinx/texinputs/sphinx.sty | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index b069b712f..6317485cf 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -1159,6 +1159,14 @@ \begin{sphinx#1}{#2}} % in end part, need to go around a LaTeX's "feature" {\edef\spx@temp{\noexpand\end{sphinx\spx@noticetype}}\spx@temp} +% use of ``notice'' is for backwards compatibility and will be removed in +% Sphinx 1.7. +\newenvironment{notice} + {\sphinxdeprecationwarning {notice}{1.6}{1.7}{% + This document was probably built with a Sphinx extension using ``notice''^^J + environment. At Sphinx 1.7, ``notice'' environment will be removed. Please^^J + report to extension author to use ``sphinxadmonition'' instead.^^J% + ****}\begin{sphinxadmonition}}{\end{sphinxadmonition}} %% PYTHON DOCS MACROS AND ENVIRONMENTS From 71a1ffb30376c9a2f21f5027a7ef2ee73423bcc2 Mon Sep 17 00:00:00 2001 From: jfbu Date: Wed, 3 May 2017 11:51:38 +0200 Subject: [PATCH 26/44] Remove ``notice`` environment which was deprecated at 1.6 --- CHANGES | 1 + sphinx/texinputs/sphinx.sty | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index b6a81c486..2dcefca01 100644 --- a/CHANGES +++ b/CHANGES @@ -38,6 +38,7 @@ Features removed * prefix and colorfunc parameter for warn() * ``sphinx.util.compat`` module * ``sphinx.util.nodes.process_only_nodes()`` +* LaTeX environment ``notice``, use ``sphinxadmonition`` instead Bugs fixed ---------- diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 3d6a678ac..28f6d8431 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -1158,14 +1158,6 @@ \begin{sphinx#1}{#2}} % in end part, need to go around a LaTeX's "feature" {\edef\spx@temp{\noexpand\end{sphinx\spx@noticetype}}\spx@temp} -% use of ``notice'' is for backwards compatibility and will be removed in -% Sphinx 1.7. -\newenvironment{notice} - {\sphinxdeprecationwarning {notice}{1.6}{1.7}{% - This document was probably built with a Sphinx extension using ``notice''^^J - environment. At Sphinx 1.7, ``notice'' environment will be removed. Please^^J - report to extension author to use ``sphinxadmonition'' instead.^^J% - ****}\begin{sphinxadmonition}}{\end{sphinxadmonition}} %% PYTHON DOCS MACROS AND ENVIRONMENTS From 1bfa25e6feb67d2e77df941698c0e61f3afa0bf4 Mon Sep 17 00:00:00 2001 From: jfbu Date: Wed, 3 May 2017 12:46:51 +0200 Subject: [PATCH 27/44] Update CHANGES better after PR #3695 --- CHANGES | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 8fdcf36da..d2308ed4c 100644 --- a/CHANGES +++ b/CHANGES @@ -13,15 +13,18 @@ Deprecated Features added -------------- +* New ``LATEXMKOPTS`` variable to pass options to ``latexmk`` when executing + ``make latexpdf``. Default is ``-f``. (refs #3695) + Bugs fixed ---------- * #3588: No compact (p tag) html output in the i18n document build even when :confval:`html_compact_lists` is True. -* The ``make latexpdf`` (which uses ``latexmk``) aborted earlier in case of - LaTeX errors than was the case with 1.5 series. Now, ``LATEXMKOPTS`` - variable is provided whose default value ``-f`` mimics former behaviour. - (refs #3695) + +* The ``make latexpdf`` from 1.6b1 (for GNU/Linux and Mac OS, using + ``latexmk``) aborted earlier in case of LaTeX errors than was the case with + 1.5 series, due to hard-coded usage of ``--halt-on-error`` option. (refs #3695) Testing -------- From 02c4854ba04e1267968c87b84cccaf6ba6fbf355 Mon Sep 17 00:00:00 2001 From: jfbu Date: Wed, 3 May 2017 12:52:24 +0200 Subject: [PATCH 28/44] Update CHANGES (again!) for PR #3695 --- CHANGES | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index d2308ed4c..f8183a1b3 100644 --- a/CHANGES +++ b/CHANGES @@ -13,8 +13,9 @@ Deprecated Features added -------------- -* New ``LATEXMKOPTS`` variable to pass options to ``latexmk`` when executing - ``make latexpdf``. Default is ``-f``. (refs #3695) +* ``LATEXMKOPTS`` variable for the Makefile in ``$BUILDDIR/latex`` to pass + options to ``latexmk`` when executing ``make latexpdf``. Default is ``-f``. + (refs #3695) Bugs fixed ---------- From 1048a239c68a93f0ea245f86e3676c0c1424cc62 Mon Sep 17 00:00:00 2001 From: jfbu Date: Wed, 3 May 2017 15:06:04 +0200 Subject: [PATCH 29/44] Remove extra white line in CHANGES will I ever get this entry right ? --- CHANGES | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGES b/CHANGES index f8183a1b3..8408d0849 100644 --- a/CHANGES +++ b/CHANGES @@ -22,7 +22,6 @@ Bugs fixed * #3588: No compact (p tag) html output in the i18n document build even when :confval:`html_compact_lists` is True. - * The ``make latexpdf`` from 1.6b1 (for GNU/Linux and Mac OS, using ``latexmk``) aborted earlier in case of LaTeX errors than was the case with 1.5 series, due to hard-coded usage of ``--halt-on-error`` option. (refs #3695) From 0135948485f1a57ff0b6fd81f017a7cf310acac8 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 22:58:30 +0900 Subject: [PATCH 30/44] Fix #3683: sphinx.websupport module is not provided by default --- CHANGES | 1 + setup.py | 4 +++- test-reqs.txt | 1 - tox.ini | 1 - 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 8408d0849..872ff5756 100644 --- a/CHANGES +++ b/CHANGES @@ -25,6 +25,7 @@ Bugs fixed * The ``make latexpdf`` from 1.6b1 (for GNU/Linux and Mac OS, using ``latexmk``) aborted earlier in case of LaTeX errors than was the case with 1.5 series, due to hard-coded usage of ``--halt-on-error`` option. (refs #3695) +* #3683: sphinx.websupport module is not provided by default Testing -------- diff --git a/setup.py b/setup.py index 4354b0baa..95bb3778f 100644 --- a/setup.py +++ b/setup.py @@ -53,6 +53,7 @@ requires = [ 'requests>=2.0.0', 'typing', 'setuptools', + 'sphinxcontrib-websupport', ] extras_require = { # Environment Marker works for wheel 0.24 or later @@ -60,7 +61,8 @@ extras_require = { 'colorama>=0.3.5', ], 'websupport': [ - 'sphinxcontrib-websupport', + 'sqlalchemy>=0.9', + 'whoosh>=2.0', ], 'test': [ 'pytest', diff --git a/test-reqs.txt b/test-reqs.txt index 874948dd5..a08cc8300 100644 --- a/test-reqs.txt +++ b/test-reqs.txt @@ -10,7 +10,6 @@ snowballstemmer>=1.1 babel alabaster sphinx_rtd_theme -sphinxcontrib-websupport imagesize requests html5lib diff --git a/tox.ini b/tox.ini index d8db6442f..530e5b941 100644 --- a/tox.ini +++ b/tox.ini @@ -9,7 +9,6 @@ deps= mock enum34 typing - sphinxcontrib-websupport setenv = SPHINX_TEST_TEMPDIR = {envdir}/testbuild PYTHONDONTWRITEBYTECODE = true From b8799b2aee4c83ecb93a20b2bb4308a71ad72305 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 23:16:12 +0900 Subject: [PATCH 31/44] Fix #3683: Failed to build document if builder.css_file.insert() is called --- CHANGES | 1 + sphinx/builders/html.py | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CHANGES b/CHANGES index 872ff5756..9831addde 100644 --- a/CHANGES +++ b/CHANGES @@ -26,6 +26,7 @@ Bugs fixed ``latexmk``) aborted earlier in case of LaTeX errors than was the case with 1.5 series, due to hard-coded usage of ``--halt-on-error`` option. (refs #3695) * #3683: sphinx.websupport module is not provided by default +* #3683: Failed to build document if builder.css_file.insert() is called Testing -------- diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 08ec76618..178e5424a 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -100,6 +100,12 @@ class CSSContainer(list): else: super(CSSContainer, self).append(Stylesheet(obj, None, 'stylesheet')) + def insert(self, index, other): + warnings.warn('builder.css_files is deprecated. ' + 'Please use app.add_stylesheet() instead.', + RemovedInSphinx20Warning) + self.insert(index, other) + def extend(self, other): warnings.warn('builder.css_files is deprecated. ' 'Please use app.add_stylesheet() instead.', From 0e5a610b2c7e576cde8669bf0a17c8350259ecb5 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 23:39:49 +0900 Subject: [PATCH 32/44] Fix CSSContainer should call super method --- sphinx/builders/html.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 178e5424a..d147f0dd1 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -104,7 +104,7 @@ class CSSContainer(list): warnings.warn('builder.css_files is deprecated. ' 'Please use app.add_stylesheet() instead.', RemovedInSphinx20Warning) - self.insert(index, other) + super(CSSContainer, self).insert(index, other) def extend(self, other): warnings.warn('builder.css_files is deprecated. ' From fa4817eb2e6491c208a7cc0c8ad56120f50e4f45 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 23:40:10 +0900 Subject: [PATCH 33/44] Add sphinxcontrib-websupport to test-reqs.txt --- test-reqs.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test-reqs.txt b/test-reqs.txt index a08cc8300..874948dd5 100644 --- a/test-reqs.txt +++ b/test-reqs.txt @@ -10,6 +10,7 @@ snowballstemmer>=1.1 babel alabaster sphinx_rtd_theme +sphinxcontrib-websupport imagesize requests html5lib From 5f4a88e0a4fd4e5f8d4418b71fd3bc5a72eda2d0 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Wed, 3 May 2017 23:48:23 +0900 Subject: [PATCH 34/44] Fix CSSContainer.insert() should convert strings to Stylesheet objects --- sphinx/builders/html.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index d147f0dd1..c98f5fee8 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -100,11 +100,14 @@ class CSSContainer(list): else: super(CSSContainer, self).append(Stylesheet(obj, None, 'stylesheet')) - def insert(self, index, other): + def insert(self, index, obj): warnings.warn('builder.css_files is deprecated. ' 'Please use app.add_stylesheet() instead.', RemovedInSphinx20Warning) - super(CSSContainer, self).insert(index, other) + if isinstance(obj, Stylesheet): + super(CSSContainer, self).insert(index, obj) + else: + super(CSSContainer, self).insert(index, Stylesheet(obj, None, 'stylesheet')) def extend(self, other): warnings.warn('builder.css_files is deprecated. ' From 85a94b79cad231818793b5704e35cbe51c0169cd Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Tue, 21 Mar 2017 20:10:41 +0900 Subject: [PATCH 35/44] C++, internal changes to id generation --- sphinx/domains/cpp.py | 855 ++++++++++++++++----------------------- tests/test_domain_cpp.py | 459 ++++++++++----------- 2 files changed, 582 insertions(+), 732 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 4dfe15f02..9245c851a 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -318,6 +318,9 @@ _keywords = [ 'while', 'xor', 'xor_eq' ] +_max_id = 2 +_id_prefix = [None, '', '_CPPv2'] + # ------------------------------------------------------------------------------ # Id v1 constants # ------------------------------------------------------------------------------ @@ -393,10 +396,9 @@ _id_operator_v1 = { } # type: Dict[unicode, unicode] # ------------------------------------------------------------------------------ -# Id v2 constants +# Id v > 1 constants # ------------------------------------------------------------------------------ -_id_prefix_v2 = '_CPPv2' _id_fundamental_v2 = { # not all of these are actually parsed as fundamental types, TODO: do that 'void': 'v', @@ -546,14 +548,9 @@ class ASTBase(UnicodeMixin): """Clone a definition expression node.""" return deepcopy(self) - def get_id_v1(self): - # type: () -> unicode - """Return the v1 id for the node.""" - raise NotImplementedError(repr(self)) - - def get_id_v2(self): - # type: () -> unicode - """Return the v2 id for the node.""" + def get_id(self, version): + # type: (int) -> unicode + """Return the id for the node.""" raise NotImplementedError(repr(self)) def get_name(self): @@ -678,15 +675,13 @@ class ASTIdentifier(ASTBase): assert identifier is not None self.identifier = identifier - def get_id_v1(self): - # type: () -> unicode - if self.identifier == 'size_t': - return 's' - else: - return self.identifier - - def get_id_v2(self): - # type: () -> unicode + def get_id(self, version): + # type: (int) -> unicode + if version == 1: + if self.identifier == 'size_t': + return 's' + else: + return self.identifier if self.identifier == "std": return 'St' elif self.identifier[0] == "~": @@ -736,8 +731,9 @@ class ASTTemplateKeyParamPackIdDefault(ASTBase): # type: () -> unicode return self.identifier - def get_id_v2(self): - # type: () -> unicode + def get_id(self, version): + # type: (int) -> unicode + assert version >= 2 # this is not part of the normal name mangling in C++ res = [] if self.parameterPack: @@ -794,14 +790,15 @@ class ASTTemplateParamType(ASTBase): # type: () -> unicode return self.data.get_identifier() - def get_id_v2(self, objectType=None, symbol=None): + def get_id(self, version, objectType=None, symbol=None): # type: (unicode, Symbol) -> unicode # this is not part of the normal name mangling in C++ + assert version >= 2 if symbol: # the anchor will be our parent - return symbol.parent.declaration.get_id_v2(prefixed=None) + return symbol.parent.declaration.get_id(version, prefixed=False) else: - return self.data.get_id_v2() + return self.data.get_id(version) def __unicode__(self): # type: () -> unicode @@ -830,14 +827,15 @@ class ASTTemplateParamTemplateType(ASTBase): # type: () -> unicode return self.data.get_identifier() - def get_id_v2(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode + def get_id(self, version, objectType=None, symbol=None): + # type: (int, unicode, Symbol) -> unicode + assert version >= 2 # 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) + return symbol.parent.declaration.get_id(version, prefixed=None) else: - return self.nestedParams.get_id_v2() + self.data.get_id_v2() + return self.nestedParams.get_id(version) + self.data.get_id(version) def __unicode__(self): # type: () -> unicode @@ -873,14 +871,15 @@ class ASTTemplateParamNonType(ASTBase): else: return None - def get_id_v2(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode + def get_id(self, version, objectType=None, symbol=None): + # type: (int, unicode, Symbol) -> unicode + assert version >= 2 # 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) + return symbol.parent.declaration.get_id(version, prefixed=None) else: - return '_' + self.param.get_id_v2() + return '_' + self.param.get_id(version) def __unicode__(self): # type: () -> unicode @@ -898,12 +897,13 @@ class ASTTemplateParams(ASTBase): self.params = params self.isNested = False # whether it's a template template param - def get_id_v2(self): - # type: () -> unicode + def get_id(self, version): + # type: (int) -> unicode + assert version >= 2 res = [] res.append("I") for param in self.params: - res.append(param.get_id_v2()) + res.append(param.get_id(version)) res.append("E") return ''.join(res) @@ -951,22 +951,24 @@ class ASTTemplateIntroductionParameter(ASTBase): # type: () -> unicode return self.identifier - def get_id_v2(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode + def get_id(self, version, objectType=None, symbol=None): + # type: (int, unicode, Symbol) -> unicode + assert version >= 2 # 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) + return symbol.parent.declaration.get_id(version, prefixed=None) else: if self.parameterPack: return 'Dp' else: return '0' # we need to put something - def get_id_v2_as_arg(self): - # type: () -> unicode + def get_id_as_arg(self, version): + # type: (int) -> unicode + assert version >= 2 # used for the implicit requires clause - res = self.identifier.get_id_v2() + res = self.identifier.get_id(version) if self.parameterPack: return u'sp' + res else: @@ -994,22 +996,21 @@ class ASTTemplateIntroduction(ASTBase): self.concept = concept self.params = params - # id_v1 does not exist - - def get_id_v2(self): - # type: () -> unicode + def get_id(self, version): + # type: (int) -> unicode + assert version >= 2 # first do the same as a normal template parameter list res = [] res.append("I") for param in self.params: - res.append(param.get_id_v2()) + res.append(param.get_id(version)) res.append("E") # let's use X expr E, which is otherwise for constant template args res.append("X") - res.append(self.concept.get_id_v2()) + res.append(self.concept.get_id(version)) res.append("I") for param in self.params: - res.append(param.get_id_v2_as_arg()) + res.append(param.get_id_as_arg(version)) res.append("E") res.append("E") return ''.join(res) @@ -1047,14 +1048,13 @@ class ASTTemplateDeclarationPrefix(ASTBase): assert len(templates) > 0 self.templates = templates - # id_v1 does not exist - - def get_id_v2(self): - # type: () -> unicode + def get_id(self, version): + # type: (int) -> unicode + assert version >= 2 # this is not part of a normal name mangling system res = [] for t in self.templates: - res.append(t.get_id_v2()) + res.append(t.get_id(version)) return u''.join(res) def __unicode__(self): @@ -1080,19 +1080,16 @@ class ASTOperatorBuildIn(ASTBase): # type: () -> bool return True - def get_id_v1(self): - # type: () -> unicode - if self.op not in _id_operator_v1: + def get_id(self, version): + # type: (int) -> unicode + if version == 1: + ids = _id_operator_v1 + else: + ids = _id_operator_v2 + if self.op not in ids: raise Exception('Internal error: Build-in operator "%s" can not ' 'be mapped to an id.' % self.op) - return _id_operator_v1[self.op] - - def get_id_v2(self): - # type: () -> unicode - if self.op not in _id_operator_v2: - raise Exception('Internal error: Build-in operator "%s" can not ' - 'be mapped to an id.' % self.op) - return _id_operator_v2[self.op] + return ids[self.op] def __unicode__(self): # type: () -> unicode @@ -1120,13 +1117,12 @@ class ASTOperatorType(ASTBase): # type: () -> bool return True - def get_id_v1(self): - # type: () -> unicode - return u'castto-%s-operator' % self.type.get_id_v1() - - def get_id_v2(self): - # type: () -> unicode - return u'cv' + self.type.get_id_v2() + def get_id(self, version): + # type: (int) -> unicode + if version == 1: + return u'castto-%s-operator' % self.type.get_id(version) + else: + return u'cv' + self.type.get_id(version) def __unicode__(self): # type: () -> unicode @@ -1155,13 +1151,12 @@ class ASTOperatorLiteral(ASTBase): # type: () -> bool return True - def get_id_v1(self): - # type: () -> unicode - raise NoOldIdError() - - def get_id_v2(self): - # type: () -> unicode - return u'li' + self.identifier.get_id_v2() + def get_id(self, version): + # type: (int) -> unicode + if version == 1: + raise NoOldIdError() + else: + return u'li' + self.identifier.get_id(version) def __unicode__(self): # type: () -> unicode @@ -1186,15 +1181,15 @@ class ASTTemplateArgConstant(ASTBase): # type: () -> unicode return text_type(self.value) - def get_id_v1(self): - # type: () -> unicode - return text_type(self).replace(u' ', u'-') - - def get_id_v2(self): - # type: () -> unicode - # TODO: doing this properly needs parsing of expressions, let's just - # juse it verbatim for now - return u'X' + text_type(self) + u'E' + def get_id(self, version): + # type: (int) -> unicode + if version == 1: + return text_type(self).replace(u' ', u'-') + if version == 2: + # TODO: doing this properly needs parsing of expressions, let's just + # use it verbatim for now + return u'X' + text_type(self) + u'E' + assert False def describe_signature(self, signode, mode, env, symbol): # type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None @@ -1209,20 +1204,19 @@ class ASTTemplateArgs(ASTBase): assert len(args) > 0 self.args = args - def get_id_v1(self): - # type: () -> unicode - res = [] # type: List[unicode] - res.append(':') - res.append(u'.'.join(a.get_id_v1() for a in self.args)) - res.append(':') - return u''.join(res) + def get_id(self, version): + # type: (int) -> unicode + if version == 1: + res = [] # type: List[unicode] + res.append(':') + res.append(u'.'.join(a.get_id(version) for a in self.args)) + res.append(':') + return u''.join(res) - def get_id_v2(self): - # type: () -> unicode res = [] res.append('I') for a in self.args: - res.append(a.get_id_v2()) + res.append(a.get_id(version)) res.append('E') return u''.join(res) @@ -1254,18 +1248,11 @@ class ASTNestedNameElement(ASTBase): # type: () -> bool return False - def get_id_v1(self): - # type: () -> unicode - res = self.identifier.get_id_v1() + def get_id(self, version): + # type: (int) -> unicode + res = self.identifier.get_id(version) if self.templateArgs: - res += self.templateArgs.get_id_v1() - return res - - def get_id_v2(self): - # type: () -> unicode - res = self.identifier.get_id_v2() - if self.templateArgs: - res += self.templateArgs.get_id_v2() + res += self.templateArgs.get_id(version) return res def __unicode__(self): @@ -1304,22 +1291,20 @@ class ASTNestedName(ASTBase): count += 1 return count - def get_id_v1(self): - # type: () -> unicode - tt = text_type(self) - if tt in _id_shorthands_v1: - return _id_shorthands_v1[tt] - else: - return u'::'.join(n.get_id_v1() for n in self.names) - - def get_id_v2(self, modifiers=""): - # type: (unicode) -> unicode + def get_id(self, version, modifiers=''): + # type: (int, unicode) -> unicode + if version == 1: + tt = text_type(self) + if tt in _id_shorthands_v1: + return _id_shorthands_v1[tt] + else: + return u'::'.join(n.get_id(version) for n in self.names) res = [] # type: List[unicode] if len(self.names) > 1 or len(modifiers) > 0: res.append('N') res.append(modifiers) for n in self.names: - res.append(n.get_id_v2()) + res.append(n.get_id(version)) if len(self.names) > 1 or len(modifiers) > 0: res.append('E') return u''.join(res) @@ -1380,18 +1365,17 @@ class ASTTrailingTypeSpecFundamental(ASTBase): # type: () -> unicode return self.name - def get_id_v1(self): - # type: () -> unicode - res = [] - for a in self.name.split(' '): - if a in _id_fundamental_v1: - res.append(_id_fundamental_v1[a]) - else: - res.append(a) - return u'-'.join(res) + def get_id(self, version): + # type: (int) -> unicode + if version == 1: + res = [] + for a in self.name.split(' '): + if a in _id_fundamental_v1: + res.append(_id_fundamental_v1[a]) + else: + res.append(a) + return u'-'.join(res) - def get_id_v2(self): - # type: () -> unicode if self.name not in _id_fundamental_v2: raise Exception( 'Semi-internal error: Fundamental type "%s" can not be mapped ' @@ -1415,13 +1399,9 @@ class ASTTrailingTypeSpecName(ASTBase): # type: () -> Any return self.nestedName - def get_id_v1(self): - # type: () -> unicode - return self.nestedName.get_id_v1() - - def get_id_v2(self): - # type: () -> unicode - return self.nestedName.get_id_v2() + def get_id(self, version): + # type: (int) -> unicode + return self.nestedName.get_id(version) def __unicode__(self): # type: () -> unicode @@ -1444,10 +1424,9 @@ class ASTTrailingTypeSpecDecltypeAuto(ASTBase): def __unicode__(self): return u'decltype(auto)' - def get_id_v1(self): - raise NoOldIdError() - - def get_id_v2(self): + def get_id(self, version): + if version == 1: + raise NoOldIdError() return 'Dc' def describe_signature(self, signode, mode, env, symbol): @@ -1461,19 +1440,12 @@ class ASTFunctionParameter(ASTBase): self.arg = arg self.ellipsis = ellipsis - def get_id_v1(self): - # type: () -> unicode + def get_id(self, version): + # type: (int) -> unicode if self.ellipsis: return 'z' else: - return self.arg.get_id_v1() - - def get_id_v2(self): - # type: () -> unicode - if self.ellipsis: - return 'z' - else: - return self.arg.get_id_v2() + return self.arg.get_id(version) def __unicode__(self): # type: () -> unicode @@ -1504,49 +1476,33 @@ class ASTParametersQualifiers(ASTBase): self.final = final self.initializer = initializer - # Id v1 ------------------------------------------------------------------ - - def get_modifiers_id_v1(self): - # type: () -> unicode + def get_modifiers_id(self, version): + # type: (int) -> unicode res = [] if self.volatile: res.append('V') if self.const: - res.append('C') + if version == 1: + res.append('C') + else: + res.append('K') if self.refQual == '&&': res.append('O') elif self.refQual == '&': res.append('R') return u''.join(res) - def get_param_id_v1(self): - # type: () -> unicode - if len(self.args) == 0: - return '' - else: - return u'__' + u'.'.join(a.get_id_v1() for a in self.args) - - # Id v2 ------------------------------------------------------------------ - - def get_modifiers_id_v2(self): - # type: () -> unicode - res = [] - if self.volatile: - res.append('V') - if self.const: - res.append('K') - if self.refQual == '&&': - res.append('O') - elif self.refQual == '&': - res.append('R') - return u''.join(res) - - def get_param_id_v2(self): - # type: () -> unicode + def get_param_id(self, version): + # type: (int) -> unicode + if version == 1: + if len(self.args) == 0: + return '' + else: + return u'__' + u'.'.join(a.get_id(version) for a in self.args) if len(self.args) == 0: return 'v' else: - return u''.join(a.get_id_v2() for a in self.args) + return u''.join(a.get_id(version) for a in self.args) def __unicode__(self): # type: () -> unicode @@ -1713,24 +1669,22 @@ class ASTDeclSpecs(ASTBase): # type: () -> unicode return self.trailingTypeSpec.name - def get_id_v1(self): - # type: () -> unicode - res = [] - res.append(self.trailingTypeSpec.get_id_v1()) - if self.allSpecs.volatile: - res.append('V') - if self.allSpecs.const: - res.append('C') - return u''.join(res) - - def get_id_v2(self): - # type: () -> unicode + def get_id(self, version): + # type: (int) -> unicode + if version == 1: + res = [] + res.append(self.trailingTypeSpec.get_id(version)) + if self.allSpecs.volatile: + res.append('V') + if self.allSpecs.const: + res.append('C') + return u''.join(res) res = [] if self.leftSpecs.volatile or self.rightSpecs.volatile: res.append('V') if self.leftSpecs.const or self.rightSpecs.volatile: res.append('K') - res.append(self.trailingTypeSpec.get_id_v2()) + res.append(self.trailingTypeSpec.get_id(version)) return u''.join(res) def __unicode__(self): @@ -1788,14 +1742,14 @@ class ASTArray(ASTBase): # type: () -> unicode return u''.join(['[', text_type(self.size), ']']) - def get_id_v1(self): - # type: () -> unicode - return u'A' - - def get_id_v2(self): - # type: () -> unicode - # TODO: this should maybe be done differently - return u'A' + text_type(self.size) + u'_' + def get_id(self, version): + # type: (int) -> unicode + if version == 1: + return u'A' + if version == 2: + # TODO: this should maybe be done differently + return u'A' + text_type(self.size) + u'_' + assert False def describe_signature(self, signode, mode, env): _verify_description_mode(mode) @@ -1835,38 +1789,25 @@ class ASTDeclaratorPtr(ASTBase): res.append(text_type(self.next)) return u''.join(res) - # Id v1 ------------------------------------------------------------------ + def get_modifiers_id(self, version): + # type: (int) -> unicode + return self.next.get_modifiers_id(version) - def get_modifiers_id_v1(self): - # type: () -> unicode - return self.next.get_modifiers_id_v1() + def get_param_id(self, version): + # type: (int) -> unicode + return self.next.get_param_id(version) - def get_param_id_v1(self): - # type: () -> unicode - return self.next.get_param_id_v1() + def get_ptr_suffix_id(self, version): + # type: (int) -> unicode + if version == 1: + res = 'P' + if self.volatile: + res += 'V' + if self.const: + res += 'C' + return res + self.next.get_ptr_suffix_id(version) - def get_ptr_suffix_id_v1(self): - # type: () -> unicode - res = 'P' - if self.volatile: - res += 'V' - if self.const: - res += 'C' - return res + self.next.get_ptr_suffix_id_v1() - - # Id v2 ------------------------------------------------------------------ - - def get_modifiers_id_v2(self): - # type: () -> unicode - return self.next.get_modifiers_id_v2() - - def get_param_id_v2(self): - # type: () -> unicode - return self.next.get_param_id_v2() - - def get_ptr_suffix_id_v2(self): - # type: () -> unicode - res = [self.next.get_ptr_suffix_id_v2()] # type: List[unicode] + res = [self.next.get_ptr_suffix_id(version)] # type: List[unicode] res.append('P') if self.volatile: res.append('V') @@ -1874,8 +1815,8 @@ class ASTDeclaratorPtr(ASTBase): res.append('C') return u''.join(res) - def get_type_id_v2(self, returnTypeId): - # type: (unicode) -> unicode + def get_type_id(self, version, returnTypeId): + # type: (int, unicode) -> unicode # ReturnType *next, so we are part of the return type of 'next res = ['P'] # type: List[unicode] if self.volatile: @@ -1883,9 +1824,7 @@ class ASTDeclaratorPtr(ASTBase): if self.const: res.append('C') res.append(returnTypeId) - return self.next.get_type_id_v2(returnTypeId=u''.join(res)) - - # ------------------------------------------------------------------------ + return self.next.get_type_id(version, returnTypeId=u''.join(res)) def is_function_type(self): # type: () -> bool @@ -1929,40 +1868,26 @@ class ASTDeclaratorRef(ASTBase): # type: () -> unicode return '&' + text_type(self.next) - # Id v1 ------------------------------------------------------------------ + def get_modifiers_id(self, version): + # type: (int) -> unicode + return self.next.get_modifiers_id(version) - def get_modifiers_id_v1(self): - # type: () -> unicode - return self.next.get_modifiers_id_v1() + def get_param_id(self, version): # only the parameters (if any) + # type: (int) -> unicode + return self.next.get_param_id(version) - def get_param_id_v1(self): # only the parameters (if any) - # type: () -> unicode - return self.next.get_param_id_v1() + def get_ptr_suffix_id(self, version): + # type: (int) -> unicode + if version == 1: + return u'R' + self.next.get_ptr_suffix_id(version) + else: + return self.next.get_ptr_suffix_id(version) + u'R' - def get_ptr_suffix_id_v1(self): - # type: () -> unicode - return u'R' + self.next.get_ptr_suffix_id_v1() - - # Id v2 ------------------------------------------------------------------ - - def get_modifiers_id_v2(self): - # type: () -> unicode - return self.next.get_modifiers_id_v2() - - def get_param_id_v2(self): # only the parameters (if any) - # type: () -> unicode - return self.next.get_param_id_v2() - - def get_ptr_suffix_id_v2(self): - # type: () -> unicode - return self.next.get_ptr_suffix_id_v2() + u'R' - - def get_type_id_v2(self, returnTypeId): - # type: (unicode) -> unicode + def get_type_id(self, version, returnTypeId): + # type: (int, unicode) -> unicode + assert version >= 2 # ReturnType &next, so we are part of the return type of 'next - return self.next.get_type_id_v2(returnTypeId=u'R' + returnTypeId) - - # ------------------------------------------------------------------------ + return self.next.get_type_id(version, returnTypeId=u'R' + returnTypeId) def is_function_type(self): # type: () -> bool @@ -1997,39 +1922,26 @@ class ASTDeclaratorParamPack(ASTBase): res = ' ' + res return '...' + res - # Id v1 ------------------------------------------------------------------ + def get_modifiers_id(self, version): + # type: (int) -> unicode + return self.next.get_modifiers_id(version) - def get_modifiers_id_v1(self): - # type: () -> unicode - return self.next.get_modifiers_id_v1() + def get_param_id(self, version): # only the parameters (if any) + # type: (int) -> unicode + return self.next.get_param_id(version) - def get_param_id_v1(self): # only the parameters (if any) - # type: () -> unicode - return self.next.get_param_id_v1() + def get_ptr_suffix_id(self, version): + # type: (int) -> unicode + if version == 1: + return 'Dp' + self.next.get_ptr_suffix_id(version) + else: + return self.next.get_ptr_suffix_id(version) + u'Dp' - def get_ptr_suffix_id_v1(self): - # type: () -> unicode - return 'Dp' + self.next.get_ptr_suffix_id_v2() - - # Id v2 ------------------------------------------------------------------ - - def get_modifiers_id_v2(self): - # type: () -> unicode - return self.next.get_modifiers_id_v2() - - def get_param_id_v2(self): # only the parameters (if any) - return self.next.get_param_id_v2() - - def get_ptr_suffix_id_v2(self): - # type: () -> unicode - return self.next.get_ptr_suffix_id_v2() + u'Dp' - - def get_type_id_v2(self, returnTypeId): - # type: (unicode) -> unicode + def get_type_id(self, version, returnTypeId): + # type: (int, unicode) -> unicode + assert version >= 2 # ReturnType... next, so we are part of the return type of 'next - return self.next.get_type_id_v2(returnTypeId=u'Dp' + returnTypeId) - - # ------------------------------------------------------------------------ + return self.next.get_type_id(version, returnTypeId=u'Dp' + returnTypeId) def is_function_type(self): # type: () -> bool @@ -2077,37 +1989,31 @@ class ASTDeclaratorMemPtr(ASTBase): res.append(text_type(self.next)) return ''.join(res) - # Id v1 ------------------------------------------------------------------ + def get_modifiers_id(self, version): + # type: (int) -> unicode + if version == 1: + raise NoOldIdError() + else: + return self.next.get_modifiers_id(version) - def get_modifiers_id_v1(self): - # type: () -> unicode - raise NoOldIdError() + def get_param_id(self, version): # only the parameters (if any) + # type: (int) -> unicode + if version == 1: + raise NoOldIdError() + else: + return self.next.get_param_id(version) - def get_param_id_v1(self): # only the parameters (if any) - # type: () -> unicode - raise NoOldIdError() + def get_ptr_suffix_id(self, version): + # type: (int) -> unicode + if version == 1: + raise NoOldIdError() + else: + raise NotImplementedError() + return self.next.get_ptr_suffix_id(version) + u'Dp' - def get_ptr_suffix_id_v1(self): - # type: () -> unicode - raise NoOldIdError() - - # Id v2 ------------------------------------------------------------------ - - def get_modifiers_id_v2(self): - # type: () -> unicode - return self.next.get_modifiers_id_v2() - - def get_param_id_v2(self): # only the parameters (if any) - # type: () -> unicode - return self.next.get_param_id_v2() - - def get_ptr_suffix_id_v2(self): - # type: () -> unicode - raise NotImplementedError() - return self.next.get_ptr_suffix_id_v2() + u'Dp' - - def get_type_id_v2(self, returnTypeId): - # type: (unicode) -> unicode + def get_type_id(self, version, returnTypeId): + # type: (int, unicode) -> unicode + assert version >= 2 # ReturnType name::* next, so we are part of the return type of next nextReturnTypeId = '' # type: unicode if self.volatile: @@ -2115,11 +2021,9 @@ class ASTDeclaratorMemPtr(ASTBase): if self.const: nextReturnTypeId += 'K' nextReturnTypeId += 'M' - nextReturnTypeId += self.className.get_id_v2() + nextReturnTypeId += self.className.get_id(version) nextReturnTypeId += returnTypeId - return self.next.get_type_id_v2(nextReturnTypeId) - - # ------------------------------------------------------------------------ + return self.next.get_type_id(version, nextReturnTypeId) def is_function_type(self): # type: () -> bool @@ -2171,44 +2075,30 @@ class ASTDeclaratorParen(ASTBase): res.append(text_type(self.next)) return ''.join(res) - # Id v1 ------------------------------------------------------------------ + def get_modifiers_id(self, version): + # type: (int) -> unicode + return self.inner.get_modifiers_id(version) - def get_modifiers_id_v1(self): - # type: () -> unicode - return self.inner.get_modifiers_id_v1() + def get_param_id(self, version): # only the parameters (if any) + # type: (int) -> unicode + return self.inner.get_param_id(version) - def get_param_id_v1(self): # only the parameters (if any) - # type: () -> unicode - return self.inner.get_param_id_v1() + def get_ptr_suffix_id(self, version): + # type: (int) -> unicode + if version == 1: + raise NoOldIdError() # TODO: was this implemented before? + return self.next.get_ptr_suffix_id(version) + \ + self.inner.get_ptr_suffix_id(version) + else: + return self.inner.get_ptr_suffix_id(version) + \ + self.next.get_ptr_suffix_id(version) - def get_ptr_suffix_id_v1(self): - # type: () -> unicode - raise NoOldIdError() # TODO: was this implemented before? - return self.next.get_ptr_suffix_id_v2() + \ - self.inner.get_ptr_suffix_id_v2() - - # Id v2 ------------------------------------------------------------------ - - def get_modifiers_id_v2(self): - # type: () -> unicode - return self.inner.get_modifiers_id_v2() - - def get_param_id_v2(self): # only the parameters (if any) - # type: () -> unicode - return self.inner.get_param_id_v2() - - def get_ptr_suffix_id_v2(self): - # type: () -> unicode - return self.inner.get_ptr_suffix_id_v2() + \ - self.next.get_ptr_suffix_id_v2() - - def get_type_id_v2(self, returnTypeId): - # type: (unicode) -> unicode + def get_type_id(self, version, returnTypeId): + # type: (int, unicode) -> unicode + assert version >= 2 # ReturnType (inner)next, so 'inner' returns everything outside - nextId = self.next.get_type_id_v2(returnTypeId) - return self.inner.get_type_id_v2(returnTypeId=nextId) - - # ------------------------------------------------------------------------ + nextId = self.next.get_type_id(version, returnTypeId) + return self.inner.get_type_id(version, returnTypeId=nextId) def is_function_type(self): # type: () -> bool @@ -2235,58 +2125,36 @@ class ASTDeclaratorNameParamQual(ASTBase): # type: () -> unicode return self.declId - # Id v1 ------------------------------------------------------------------ - - def get_modifiers_id_v1(self): # only the modifiers for a function, e.g., - # type: () -> unicode + def get_modifiers_id(self, version): # only the modifiers for a function, e.g., + # type: (int) -> unicode # cv-qualifiers if self.paramQual: - return self.paramQual.get_modifiers_id_v1() + return self.paramQual.get_modifiers_id(version) raise Exception( "This should only be called on a function: %s" % text_type(self)) - def get_param_id_v1(self): # only the parameters (if any) - # type: () -> unicode + def get_param_id(self, version): # only the parameters (if any) + # type: (int) -> unicode if self.paramQual: - return self.paramQual.get_param_id_v1() + return self.paramQual.get_param_id(version) else: return '' - def get_ptr_suffix_id_v1(self): # only the array specifiers - # type: () -> unicode - return u''.join(a.get_id_v1() for a in self.arrayOps) + def get_ptr_suffix_id(self, version): # only the array specifiers + # type: (int) -> unicode + return u''.join(a.get_id(version) for a in self.arrayOps) - # Id v2 ------------------------------------------------------------------ - - def get_modifiers_id_v2(self): # only the modifiers for a function, e.g., - # type: () -> unicode - # cv-qualifiers - if self.paramQual: - return self.paramQual.get_modifiers_id_v2() - raise Exception( - "This should only be called on a function: %s" % text_type(self)) - - def get_param_id_v2(self): # only the parameters (if any) - # type: () -> unicode - if self.paramQual: - return self.paramQual.get_param_id_v2() - else: - return '' - - def get_ptr_suffix_id_v2(self): # only the array specifiers - # type: () -> unicode - return u''.join(a.get_id_v2() for a in self.arrayOps) - - def get_type_id_v2(self, returnTypeId): - # type: (unicode) -> unicode + def get_type_id(self, version, returnTypeId): + # type: (int, unicode) -> unicode + assert version >= 2 res = [] # TOOD: can we actually have both array ops and paramQual? - res.append(self.get_ptr_suffix_id_v2()) + res.append(self.get_ptr_suffix_id(version)) if self.paramQual: - res.append(self.get_modifiers_id_v2()) + res.append(self.get_modifiers_id(version)) res.append('F') res.append(returnTypeId) - res.append(self.get_param_id_v2()) + res.append(self.get_param_id(version)) res.append('E') else: res.append(returnTypeId) @@ -2353,49 +2221,48 @@ class ASTType(ASTBase): name = self.decl.name return name - def get_id_v1(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode + def get_id(self, version, objectType=None, symbol=None): + # type: (int, unicode, Symbol) -> unicode + if version == 1: + res = [] + if objectType: # needs the name + if objectType == 'function': # also modifiers + res.append(symbol.get_full_nested_name().get_id(version)) + res.append(self.decl.get_param_id(version)) + res.append(self.decl.get_modifiers_id(version)) + if (self.declSpecs.leftSpecs.constexpr or + (self.declSpecs.rightSpecs and + self.declSpecs.rightSpecs.constexpr)): + res.append('CE') + elif objectType == 'type': # just the name + res.append(symbol.get_full_nested_name().get_id(version)) + else: + print(objectType) + assert False + else: # only type encoding + if self.decl.is_function_type(): + raise NoOldIdError() + res.append(self.declSpecs.get_id(version)) + res.append(self.decl.get_ptr_suffix_id(version)) + res.append(self.decl.get_param_id(version)) + return u''.join(res) + # other versions res = [] if objectType: # needs the name if objectType == 'function': # also modifiers - res.append(symbol.get_full_nested_name().get_id_v1()) - res.append(self.decl.get_param_id_v1()) - res.append(self.decl.get_modifiers_id_v1()) - if (self.declSpecs.leftSpecs.constexpr or - (self.declSpecs.rightSpecs and - self.declSpecs.rightSpecs.constexpr)): - res.append('CE') + modifiers = self.decl.get_modifiers_id(version) + res.append(symbol.get_full_nested_name().get_id(version, modifiers)) + res.append(self.decl.get_param_id(version)) elif objectType == 'type': # just the name - res.append(symbol.get_full_nested_name().get_id_v1()) - else: - print(objectType) - assert False - else: # only type encoding - if self.decl.is_function_type(): - raise NoOldIdError() - res.append(self.declSpecs.get_id_v1()) - res.append(self.decl.get_ptr_suffix_id_v1()) - res.append(self.decl.get_param_id_v1()) - return u''.join(res) - - def get_id_v2(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode - res = [] - if objectType: # needs the name - if objectType == 'function': # also modifiers - modifiers = self.decl.get_modifiers_id_v2() - res.append(symbol.get_full_nested_name().get_id_v2(modifiers)) - res.append(self.decl.get_param_id_v2()) - elif objectType == 'type': # just the name - res.append(symbol.get_full_nested_name().get_id_v2()) + res.append(symbol.get_full_nested_name().get_id(version)) else: print(objectType) assert False else: # only type encoding # the 'returnType' of a non-function type is simply just the last # type, i.e., for 'int*' it is 'int' - returnTypeId = self.declSpecs.get_id_v2() - typeId = self.decl.get_type_id_v2(returnTypeId) + returnTypeId = self.declSpecs.get_id(version) + typeId = self.decl.get_type_id(version, returnTypeId) res.append(typeId) return u''.join(res) @@ -2437,20 +2304,14 @@ class ASTTypeWithInit(ASTBase): # type: () -> unicode return self.type.name - def get_id_v1(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode - if objectType == 'member': - return symbol.get_full_nested_name().get_id_v1() + u'__' \ - + self.type.get_id_v1() - else: - return self.type.get_id_v1(objectType) - - def get_id_v2(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode - if objectType == 'member': - return symbol.get_full_nested_name().get_id_v2() - else: - return self.type.get_id_v2() + def get_id(self, version, objectType=None, symbol=None): + # type: (int, unicode, Symbol) -> unicode + if objectType != 'member': + return self.type.get_id(version, objectType) + if version == 1: + return symbol.get_full_nested_name().get_id(version) + u'__' \ + + self.type.get_id(version) + return symbol.get_full_nested_name().get_id(version) def __unicode__(self): # type: () -> unicode @@ -2474,13 +2335,11 @@ class ASTTypeUsing(ASTBase): self.name = name self.type = type - def get_id_v1(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode - raise NoOldIdError() - - def get_id_v2(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode - return symbol.get_full_nested_name().get_id_v2() + def get_id(self, version, objectType=None, symbol=None): + # type: (int, unicode, Symbol) -> unicode + if version == 1: + raise NoOldIdError() + return symbol.get_full_nested_name().get_id(version) def __unicode__(self): # type: () -> unicode @@ -2516,13 +2375,11 @@ class ASTConcept(ASTBase): # type: () -> unicode return self.nestedName - def get_id_v1(self, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode - raise NoOldIdError() - - def get_id_v2(self, objectType, symbol): # type: ignore - # type: (unicode, Symbol) -> unicode - return symbol.get_full_nested_name().get_id_v2() + def get_id(self, version, objectType=None, symbol=None): + # type: (int, unicode, Symbol) -> unicode + if version == 1: + raise NoOldIdError() + return symbol.get_full_nested_name().get_id(version) def __unicode__(self): # type: () -> unicode @@ -2586,13 +2443,9 @@ class ASTClass(ASTBase): self.final = final self.bases = bases - def get_id_v1(self, objectType, symbol): # type: ignore - # type: (unicode, Symbol) -> unicode - return symbol.get_full_nested_name().get_id_v1() - - def get_id_v2(self, objectType, symbol): # type: ignore - # type: (unicode, Symbol) -> unicode - return symbol.get_full_nested_name().get_id_v2() + def get_id(self, version, objectType, symbol): + # type: (int, unicode, Symbol) -> unicode + return symbol.get_full_nested_name().get_id(version) def __unicode__(self): # type: () -> unicode @@ -2632,13 +2485,11 @@ class ASTEnum(ASTBase): self.scoped = scoped self.underlyingType = underlyingType - def get_id_v1(self, objectType, symbol): # type: ignore - # type: (unicode, Symbol) -> unicode - raise NoOldIdError() - - def get_id_v2(self, objectType, symbol): # type: ignore - # type: (unicode, Symbol) -> unicode - return symbol.get_full_nested_name().get_id_v2() + def get_id(self, version, objectType, symbol): + # type: (int, unicode, Symbol) -> unicode + if version == 1: + raise NoOldIdError() + return symbol.get_full_nested_name().get_id(version) def __unicode__(self): # type: () -> unicode @@ -2669,13 +2520,11 @@ class ASTEnumerator(ASTBase): self.name = name self.init = init - def get_id_v1(self, objectType, symbol): # type: ignore - # type: (unicode, Symbol) -> unicode - raise NoOldIdError() - - def get_id_v2(self, objectType, symbol): # type: ignore - # type: (unicode, Symbol) -> unicode - return symbol.get_full_nested_name().get_id_v2() + def get_id(self, version, objectType, symbol): + # type: (int, unicode, Symbol) -> unicode + if version == 1: + raise NoOldIdError() + return symbol.get_full_nested_name().get_id(version) def __unicode__(self): # type: () -> unicode @@ -2720,30 +2569,29 @@ class ASTDeclaration(ASTBase): # type: () -> unicode return self.declaration.name - def get_id_v1(self): - # type: () -> unicode - if self.templatePrefix: - raise NoOldIdError() + def get_id(self, version, prefixed=True): + # type: (int) -> unicode + if version == 1: + if self.templatePrefix: + raise NoOldIdError() + if self.objectType == 'enumerator' and self.enumeratorScopedSymbol: + return self.enumeratorScopedSymbol.declaration.get_id(version) + return self.declaration.get_id(version, self.objectType, self.symbol) + # version >= 2 if self.objectType == 'enumerator' and self.enumeratorScopedSymbol: - return self.enumeratorScopedSymbol.declaration.get_id_v1() - return self.declaration.get_id_v1(self.objectType, self.symbol) - - def get_id_v2(self, prefixed=True): - # type: (bool) -> unicode - if self.objectType == 'enumerator' and self.enumeratorScopedSymbol: - return self.enumeratorScopedSymbol.declaration.get_id_v2(prefixed) + return self.enumeratorScopedSymbol.declaration.get_id(version, prefixed) if prefixed: - res = [_id_prefix_v2] + res = [_id_prefix[version]] else: res = [] if self.templatePrefix: - res.append(self.templatePrefix.get_id_v2()) - res.append(self.declaration.get_id_v2(self.objectType, self.symbol)) + res.append(self.templatePrefix.get_id(version)) + res.append(self.declaration.get_id(version, self.objectType, self.symbol)) return u''.join(res) def get_newest_id(self): # type: () -> unicode - return self.get_id_v2() + return self.get_id(_max_id, True) def __unicode__(self): # type: () -> unicode @@ -4520,14 +4368,15 @@ class CPPObject(ObjectDescription): def add_target_and_index(self, ast, sig, signode): # type: (Any, unicode, addnodes.desc_signature) -> None # general note: name must be lstrip(':')'ed, to remove "::" - try: - id_v1 = ast.get_id_v1() - except NoOldIdError: - id_v1 = None - id_v2 = ast.get_id_v2() - # store them in reverse order, so the newest is first - ids = [id_v2, id_v1] - + ids = [] + for i in range(1, _max_id + 1): + try: + id = ast.get_id(version=i) + ids.append(id) + except NoOldIdError: + assert i < _max_id + # let's keep the newest first + ids = list(reversed(ids)) newestId = ids[0] assert newestId # shouldn't be None if not re.compile(r'^[a-zA-Z0-9_]*$').match(newestId): diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 446c264fd..e7f12f09c 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -16,11 +16,9 @@ import pytest from sphinx import addnodes from sphinx.domains.cpp import DefinitionParser, DefinitionError, NoOldIdError -from sphinx.domains.cpp import Symbol +from sphinx.domains.cpp import Symbol, _max_id, _id_prefix import sphinx.domains.cpp as cppDomain -ids = [] - def parse(name, string): class Config(object): @@ -39,7 +37,7 @@ def parse(name, string): return ast -def check(name, input, idv1output=None, idv2output=None, output=None): +def check(name, input, idDict, output=None): # first a simple check of the AST if output is None: output = input @@ -58,28 +56,35 @@ def check(name, input, idv1output=None, idv2output=None, output=None): parentNode += signode ast.describe_signature(signode, 'lastIsName', symbol, options={}) - if idv2output: - idv2output = "_CPPv2" + idv2output - try: - idv1 = ast.get_id_v1() - assert idv1 is not None - except NoOldIdError: - idv1 = None - try: - idv2 = ast.get_id_v2() - assert idv2 is not None - except NoOldIdError: - idv2 = None - if idv1 != idv1output or idv2 != idv2output: + idExpected = [None] + for i in range(1, _max_id + 1): + if i in idDict: + idExpected.append(idDict[i]) + else: + idExpected.append(idExpected[i - 1]) + idActual = [None] + for i in range(1, _max_id + 1): + try: + id = ast.get_id(version=i) + assert id is not None + idActual.append(id[len(_id_prefix[i]):]) + except NoOldIdError: + idActual.append(None) + + res = [True] + for i in range(1, _max_id + 1): + res.append(idExpected[i] == idActual[i]) + + if not all(res): print("input: %s" % text_type(input).rjust(20)) - print(" %s %s" % ("Id v1".rjust(20), "Id v2".rjust(20))) - print("result: %s %s" % (str(idv1).rjust(20), str(idv2).rjust(20))) - print("expected: %s %s" % (str(idv1output).rjust(20), - str(idv2output).rjust(20))) + for i in range(1, _max_id + 1): + if res[i]: + continue + print("Error in id version %d." % i) + print("result: %s" % str(idActual[i])) + print("expected: %s" % str(idExpected[i])) print(rootSymbol.dump(0)) raise DefinitionError("") - ids.append(ast.get_id_v2()) - # print ".. %s:: %s" % (name, input) def test_fundamental_types(): @@ -99,55 +104,55 @@ def test_fundamental_types(): if t == "std::nullptr_t": id = "NSt9nullptr_tE" return "1f%s" % id - check("function", "void f(%s arg)" % t, makeIdV1(), makeIdV2()) + check("function", "void f(%s arg)" % t, {1: makeIdV1(), 2:makeIdV2()}) def test_type_definitions(): - check("type", "public bool b", "b", "1b", "bool b") - check("type", "bool A::b", "A::b", "N1A1bE") - check("type", "bool *b", "b", "1b") - check("type", "bool *const b", "b", "1b") - check("type", "bool *volatile const b", "b", "1b") - check("type", "bool *volatile const b", "b", "1b") - check("type", "bool *volatile const *b", "b", "1b") - check("type", "bool &b", "b", "1b") - check("type", "bool b[]", "b", "1b") - check("type", "std::pair coord", "coord", "5coord") - check("type", "long long int foo", "foo", "3foo") + check("type", "public bool b", {1:"b", 2:"1b"}, "bool b") + check("type", "bool A::b", {1:"A::b", 2:"N1A1bE"}) + check("type", "bool *b", {1:"b", 2:"1b"}) + check("type", "bool *const b", {1:"b", 2:"1b"}) + check("type", "bool *volatile const b", {1:"b", 2:"1b"}) + check("type", "bool *volatile const b", {1:"b", 2:"1b"}) + check("type", "bool *volatile const *b", {1:"b", 2:"1b"}) + check("type", "bool &b", {1:"b", 2:"1b"}) + check("type", "bool b[]", {1:"b", 2:"1b"}) + check("type", "std::pair coord", {1:"coord", 2:"5coord"}) + check("type", "long long int foo", {1:"foo", 2:"3foo"}) check("type", 'std::vector> module::blah', - "module::blah", "N6module4blahE") - check("type", "std::function F", "F", "1F") - check("type", "std::function F", "F", "1F") - check("type", "std::function F", "F", "1F") - check("type", "std::function F", "F", "1F") + {1:"module::blah", 2:"N6module4blahE"}) + check("type", "std::function F", {1:"F", 2:"1F"}) + check("type", "std::function F", {1:"F", 2:"1F"}) + check("type", "std::function F", {1:"F", 2:"1F"}) + check("type", "std::function F", {1:"F", 2:"1F"}) check("type", "MyContainer::const_iterator", - "MyContainer::const_iterator", "N11MyContainer14const_iteratorE") + {1:"MyContainer::const_iterator", 2:"N11MyContainer14const_iteratorE"}) check("type", "public MyContainer::const_iterator", - "MyContainer::const_iterator", "N11MyContainer14const_iteratorE", + {1:"MyContainer::const_iterator", 2:"N11MyContainer14const_iteratorE"}, output="MyContainer::const_iterator") # test decl specs on right - check("type", "bool const b", "b", "1b") + check("type", "bool const b", {1:"b", 2:"1b"}) # test name in global scope - check("type", "bool ::B::b", "B::b", "N1B1bE") + check("type", "bool ::B::b", {1:"B::b", 2:"N1B1bE"}) - check('type', 'A = B', None, '1A') + check('type', 'A = B', {2:'1A'}) # from breathe#267 (named function parameters for function pointers check('type', 'void (*gpio_callback_t)(struct device *port, uint32_t pin)', - 'gpio_callback_t', '15gpio_callback_t') - check('type', 'void (*f)(std::function g)', 'f', '1f') + {1:'gpio_callback_t', 2:'15gpio_callback_t'}) + check('type', 'void (*f)(std::function g)', {1:'f', 2:'1f'}) def test_concept_definitions(): check('concept', 'template A::B::Concept', - None, 'I0EN1A1B7ConceptE') + {2:'I0EN1A1B7ConceptE'}) check('concept', 'template Foo', - None, 'I00DpE3Foo') + {2:'I00DpE3Foo'}) check('concept', 'template A::B::Concept()', - None, 'I0EN1A1B7ConceptE') + {2:'I0EN1A1B7ConceptE'}) check('concept', 'template Foo()', - None, 'I00DpE3Foo') + {2:'I00DpE3Foo'}) with pytest.raises(DefinitionError): parse('concept', 'Foo') with pytest.raises(DefinitionError): @@ -156,256 +161,254 @@ def test_concept_definitions(): def test_member_definitions(): check('member', ' const std::string & name = 42', - "name__ssCR", "4name", output='const std::string &name = 42') - check('member', ' const std::string & name', "name__ssCR", "4name", + {1:"name__ssCR", 2:"4name"}, output='const std::string &name = 42') + check('member', ' const std::string & name', {1:"name__ssCR", 2:"4name"}, output='const std::string &name') check('member', ' const std::string & name [ n ]', - "name__ssCRA", "4name", output='const std::string &name[n]') + {1:"name__ssCRA", 2:"4name"}, output='const std::string &name[n]') check('member', 'const std::vector< unsigned int, long> &name', - "name__std::vector:unsigned-i.l:CR", - "4name", output='const std::vector &name') - check('member', 'module::myclass foo[n]', "foo__module::myclassA", "3foo") - check('member', 'int *const p', 'p__iPC', '1p') - check('member', 'extern int myInt', 'myInt__i', '5myInt') - check('member', 'thread_local int myInt', 'myInt__i', '5myInt') - check('member', 'extern thread_local int myInt', 'myInt__i', '5myInt') - check('member', 'thread_local extern int myInt', 'myInt__i', '5myInt', + {1:"name__std::vector:unsigned-i.l:CR", 2:"4name"}, + output='const std::vector &name') + check('member', 'module::myclass foo[n]', {1:"foo__module::myclassA", 2:"3foo"}) + check('member', 'int *const p', {1:'p__iPC', 2:'1p'}) + check('member', 'extern int myInt', {1:'myInt__i', 2:'5myInt'}) + check('member', 'thread_local int myInt', {1:'myInt__i', 2:'5myInt'}) + check('member', 'extern thread_local int myInt', {1:'myInt__i', 2:'5myInt'}) + check('member', 'thread_local extern int myInt', {1:'myInt__i', 2:'5myInt'}, 'extern thread_local int myInt') def test_function_definitions(): - check('function', 'operator bool() const', "castto-b-operatorC", "NKcvbEv") + check('function', 'operator bool() const', {1:"castto-b-operatorC", 2:"NKcvbEv"}) check('function', 'A::operator bool() const', - "A::castto-b-operatorC", "NK1AcvbEv") + {1:"A::castto-b-operatorC", 2:"NK1AcvbEv"}) check('function', 'A::operator bool() volatile const &', - "A::castto-b-operatorVCR", "NVKR1AcvbEv") + {1:"A::castto-b-operatorVCR", 2:"NVKR1AcvbEv"}) check('function', 'A::operator bool() volatile const &&', - "A::castto-b-operatorVCO", "NVKO1AcvbEv") + {1:"A::castto-b-operatorVCO", 2:"NVKO1AcvbEv"}) check('function', 'bool namespaced::theclass::method(arg1, arg2)', - "namespaced::theclass::method__arg1.arg2", - "N10namespaced8theclass6methodE4arg14arg2") + {1:"namespaced::theclass::method__arg1.arg2", + 2:"N10namespaced8theclass6methodE4arg14arg2"}) x = 'std::vector> &module::test(register int ' \ 'foo, bar, std::string baz = "foobar, blah, bleh") const = 0' - check('function', x, "module::test__i.bar.ssC", - "NK6module4testEi3barNSt6stringE") + check('function', x, {1:"module::test__i.bar.ssC", + 2:"NK6module4testEi3barNSt6stringE"}) check('function', 'void f(std::pair)', - "f__std::pair:A.B:", "1fNSt4pairI1A1BEE") + {1:"f__std::pair:A.B:", 2:"1fNSt4pairI1A1BEE"}) check('function', 'explicit module::myclass::foo::foo()', - "module::myclass::foo::foo", "N6module7myclass3foo3fooEv") + {1:"module::myclass::foo::foo", 2:"N6module7myclass3foo3fooEv"}) check('function', 'module::myclass::foo::~foo()', - "module::myclass::foo::~foo", "N6module7myclass3fooD0Ev") + {1:"module::myclass::foo::~foo", 2:"N6module7myclass3fooD0Ev"}) check('function', 'int printf(const char *fmt, ...)', - "printf__cCP.z", "6printfPKcz") + {1:"printf__cCP.z", 2:"6printfPKcz"}) check('function', 'int foo(const unsigned int j)', - "foo__unsigned-iC", "3fooKj") + {1:"foo__unsigned-iC", 2:"3fooKj"}) check('function', 'int foo(const int *const ptr)', - "foo__iCPC", "3fooPCKi") + {1:"foo__iCPC", 2:"3fooPCKi"}) check('function', 'module::myclass::operator std::vector()', - "module::myclass::castto-std::vector:ss:-operator", - "N6module7myclasscvNSt6vectorINSt6stringEEEEv") + {1:"module::myclass::castto-std::vector:ss:-operator", + 2:"N6module7myclasscvNSt6vectorINSt6stringEEEEv"}) check('function', 'void operator()(const boost::array &v) const', - "call-operator__boost::array:VertexID.2:CRC", - "NKclERKN5boost5arrayI8VertexIDX2EEE") + {1:"call-operator__boost::array:VertexID.2:CRC", + 2:"NKclERKN5boost5arrayI8VertexIDX2EEE"}) check('function', 'void operator()(const boost::array &v) const', - 'call-operator__boost::array:VertexID.2."foo,--bar":CRC', - 'NKclERKN5boost5arrayI8VertexIDX2EX"foo, bar"EEE') + {1:'call-operator__boost::array:VertexID.2."foo,--bar":CRC', + 2:'NKclERKN5boost5arrayI8VertexIDX2EX"foo, bar"EEE'}) check('function', 'MyClass::MyClass(MyClass::MyClass&&)', - "MyClass::MyClass__MyClass::MyClassRR", - "N7MyClass7MyClassERRN7MyClass7MyClassE") - check('function', 'constexpr int get_value()', "get_valueCE", "9get_valuev") + {1:"MyClass::MyClass__MyClass::MyClassRR", + 2:"N7MyClass7MyClassERRN7MyClass7MyClassE"}) + check('function', 'constexpr int get_value()', {1:"get_valueCE", 2:"9get_valuev"}) check('function', 'static constexpr int get_value()', - "get_valueCE", "9get_valuev") + {1:"get_valueCE", 2:"9get_valuev"}) check('function', 'int get_value() const noexcept', - "get_valueC", "NK9get_valueEv") + {1:"get_valueC", 2:"NK9get_valueEv"}) check('function', 'int get_value() const noexcept = delete', - "get_valueC", "NK9get_valueEv") + {1:"get_valueC", 2:"NK9get_valueEv"}) check('function', 'int get_value() volatile const', - "get_valueVC", "NVK9get_valueEv") + {1:"get_valueVC", 2:"NVK9get_valueEv"}) check('function', 'MyClass::MyClass(MyClass::MyClass&&) = default', - "MyClass::MyClass__MyClass::MyClassRR", - "N7MyClass7MyClassERRN7MyClass7MyClassE") + {1:"MyClass::MyClass__MyClass::MyClassRR", + 2:"N7MyClass7MyClassERRN7MyClass7MyClassE"}) check('function', 'virtual MyClass::a_virtual_function() const override', - "MyClass::a_virtual_functionC", "NK7MyClass18a_virtual_functionEv") - check('function', 'A B() override', "B", "1Bv") - check('function', 'A B() final', "B", "1Bv") - check('function', 'A B() final override', "B", "1Bv") - check('function', 'A B() override final', "B", "1Bv", + {1:"MyClass::a_virtual_functionC", 2:"NK7MyClass18a_virtual_functionEv"}) + check('function', 'A B() override', {1:"B", 2:"1Bv"}) + check('function', 'A B() final', {1:"B", 2:"1Bv"}) + check('function', 'A B() final override', {1:"B", 2:"1Bv"}) + check('function', 'A B() override final', {1:"B", 2:"1Bv"}, output='A B() final override') check('function', 'MyClass::a_member_function() volatile', - "MyClass::a_member_functionV", "NV7MyClass17a_member_functionEv") + {1:"MyClass::a_member_functionV", 2:"NV7MyClass17a_member_functionEv"}) check('function', 'MyClass::a_member_function() volatile const', - "MyClass::a_member_functionVC", "NVK7MyClass17a_member_functionEv") + {1:"MyClass::a_member_functionVC", 2:"NVK7MyClass17a_member_functionEv"}) check('function', 'MyClass::a_member_function() &&', - "MyClass::a_member_functionO", "NO7MyClass17a_member_functionEv") + {1:"MyClass::a_member_functionO", 2:"NO7MyClass17a_member_functionEv"}) check('function', 'MyClass::a_member_function() &', - "MyClass::a_member_functionR", "NR7MyClass17a_member_functionEv") + {1:"MyClass::a_member_functionR", 2:"NR7MyClass17a_member_functionEv"}) check('function', 'MyClass::a_member_function() const &', - "MyClass::a_member_functionCR", "NKR7MyClass17a_member_functionEv") + {1:"MyClass::a_member_functionCR", 2:"NKR7MyClass17a_member_functionEv"}) check('function', 'int main(int argc, char *argv[])', - "main__i.cPA", "4mainiA_Pc") + {1:"main__i.cPA", 2:"4mainiA_Pc"}) check('function', 'MyClass &MyClass::operator++()', - "MyClass::inc-operator", "N7MyClassppEv") + {1:"MyClass::inc-operator", 2:"N7MyClassppEv"}) check('function', 'MyClass::pointer MyClass::operator->()', - "MyClass::pointer-operator", "N7MyClassptEv") + {1:"MyClass::pointer-operator", 2:"N7MyClassptEv"}) x = 'std::vector> &module::test(register int ' \ 'foo, bar[n], std::string baz = "foobar, blah, bleh") const = 0' - check('function', x, "module::test__i.barA.ssC", - "NK6module4testEiAn_3barNSt6stringE") + check('function', x, {1:"module::test__i.barA.ssC", + 2:"NK6module4testEiAn_3barNSt6stringE"}) check('function', 'int foo(Foo f = Foo(double(), std::make_pair(int(2), double(3.4))))', - "foo__Foo", "3foo3Foo") - check('function', 'int foo(A a = x(a))', "foo__A", "3foo1A") + {1:"foo__Foo", 2:"3foo3Foo"}) + check('function', 'int foo(A a = x(a))', {1:"foo__A", 2:"3foo1A"}) with pytest.raises(DefinitionError): parse('function', 'int foo(B b=x(a)') with pytest.raises(DefinitionError): parse('function', 'int foo)C c=x(a))') with pytest.raises(DefinitionError): parse('function', 'int foo(D d=x(a') - check('function', 'int foo(const A&... a)', "foo__ACRDp", "3fooDpRK1A") - check('function', 'virtual void f()', "f", "1fv") + check('function', 'int foo(const A&... a)', {1:"foo__ACRDp", 2:"3fooDpRK1A"}) + check('function', 'virtual void f()', {1:"f", 2:"1fv"}) # test for ::nestedName, from issue 1738 check("function", "result(int val, ::std::error_category const &cat)", - "result__i.std::error_categoryCR", "6resultiRNSt14error_categoryE") - check("function", "int *f()", "f", "1fv") + {1:"result__i.std::error_categoryCR", 2:"6resultiRNSt14error_categoryE"}) + check("function", "int *f()", {1:"f", 2:"1fv"}) # tests derived from issue #1753 (skip to keep sanity) - check("function", "f(int (&array)[10])", None, "1fRA10_i") - check("function", "void f(int (&array)[10])", None, "1fRA10_i") - check("function", "void f(float *q(double))", None, "1fFPfdE") - check("function", "void f(float *(*q)(double))", None, "1fPFPfdE") - check("function", "void f(float (*q)(double))", None, "1fPFfdE") - check("function", "int (*f(double d))(float)", "f__double", "1fd") - check("function", "int (*f(bool b))[5]", "f__b", "1fb") + check("function", "f(int (&array)[10])", {2:"1fRA10_i"}) + check("function", "void f(int (&array)[10])", {2:"1fRA10_i"}) + check("function", "void f(float *q(double))", {2:"1fFPfdE"}) + check("function", "void f(float *(*q)(double))", {2:"1fPFPfdE"}) + check("function", "void f(float (*q)(double))", {2:"1fPFfdE"}) + check("function", "int (*f(double d))(float)", {1:"f__double", 2:"1fd"}) + check("function", "int (*f(bool b))[5]", {1:"f__b", 2:"1fb"}) check("function", "int (*A::f(double d) const)(float)", - "A::f__doubleC", "NK1A1fEd") + {1:"A::f__doubleC", 2:"NK1A1fEd"}) check("function", "void f(std::shared_ptr ptr)", - None, "1fNSt10shared_ptrIFidEEE") - check("function", "void f(int *const p)", "f__iPC", "1fPCi") - check("function", "void f(int *volatile const p)", "f__iPVC", "1fPVCi") + {2:"1fNSt10shared_ptrIFidEEE"}) + check("function", "void f(int *const p)", {1:"f__iPC", 2:"1fPCi"}) + check("function", "void f(int *volatile const p)", {1:"f__iPVC", 2:"1fPVCi"}) - check('function', 'extern int f()', 'f', '1fv') + check('function', 'extern int f()', {1:'f', 2:'1fv'}) # TODO: make tests for functions in a template, e.g., Test # such that the id generation for function type types is correct. check('function', 'friend std::ostream &f(std::ostream&, int)', - 'f__osR.i', '1fRNSt7ostreamEi') + {1:'f__osR.i', 2:'1fRNSt7ostreamEi'}) # from breathe#223 - check('function', 'void f(struct E e)', 'f__E', '1f1E') - check('function', 'void f(class E e)', 'f__E', '1f1E') - check('function', 'void f(typename E e)', 'f__E', '1f1E') - check('function', 'void f(enum E e)', 'f__E', '1f1E') - check('function', 'void f(union E e)', 'f__E', '1f1E') + check('function', 'void f(struct E e)', {1:'f__E', 2:'1f1E'}) + check('function', 'void f(class E e)', {1:'f__E', 2:'1f1E'}) + check('function', 'void f(typename E e)', {1:'f__E', 2:'1f1E'}) + check('function', 'void f(enum E e)', {1:'f__E', 2:'1f1E'}) + check('function', 'void f(union E e)', {1:'f__E', 2:'1f1E'}) # pointer to member (function) - check('function', 'void f(int C::*)', None, '1fM1Ci') - check('function', 'void f(int C::* p)', None, '1fM1Ci') - check('function', 'void f(int ::C::* p)', None, '1fM1Ci') - check('function', 'void f(int C::* const)', None, '1fKM1Ci') - check('function', 'void f(int C::* const&)', None, '1fRKM1Ci') - check('function', 'void f(int C::* volatile)', None, '1fVM1Ci') - check('function', 'void f(int C::* const volatile)', None, '1fVKM1Ci', + check('function', 'void f(int C::*)', {2:'1fM1Ci'}) + check('function', 'void f(int C::* p)', {2:'1fM1Ci'}) + check('function', 'void f(int ::C::* p)', {2:'1fM1Ci'}) + check('function', 'void f(int C::* const)', {2:'1fKM1Ci'}) + check('function', 'void f(int C::* const&)', {2:'1fRKM1Ci'}) + check('function', 'void f(int C::* volatile)', {2:'1fVM1Ci'}) + check('function', 'void f(int C::* const volatile)', {2:'1fVKM1Ci'}, output='void f(int C::* volatile const)') - check('function', 'void f(int C::* volatile const)', None, '1fVKM1Ci') - check('function', 'void f(int (C::*)(float, double))', None, '1fM1CFifdE') - check('function', 'void f(int (C::* p)(float, double))', None, '1fM1CFifdE') - check('function', 'void f(int (::C::* p)(float, double))', None, '1fM1CFifdE') - check('function', 'void f(void (C::*)() const &)', None, '1fM1CKRFvvE') - check('function', 'int C::* f(int, double)', None, '1fid') - check('function', 'void f(int C::* *)', None, '1fPM1Ci') + check('function', 'void f(int C::* volatile const)', {2:'1fVKM1Ci'}) + check('function', 'void f(int (C::*)(float, double))', {2:'1fM1CFifdE'}) + check('function', 'void f(int (C::* p)(float, double))', {2:'1fM1CFifdE'}) + check('function', 'void f(int (::C::* p)(float, double))', {2:'1fM1CFifdE'}) + check('function', 'void f(void (C::*)() const &)', {2:'1fM1CKRFvvE'}) + check('function', 'int C::* f(int, double)', {2:'1fid'}) + check('function', 'void f(int C::* *)', {2:'1fPM1Ci'}) def test_operators(): check('function', 'void operator new [ ] ()', - "new-array-operator", "nav", output='void operator new[]()') + {1:"new-array-operator", 2:"nav"}, output='void operator new[]()') check('function', 'void operator delete ()', - "delete-operator", "dlv", output='void operator delete()') + {1:"delete-operator", 2:"dlv"}, output='void operator delete()') check('function', 'operator bool() const', - "castto-b-operatorC", "NKcvbEv", output='operator bool() const') + {1:"castto-b-operatorC", 2:"NKcvbEv"}, output='operator bool() const') check('function', 'void operator * ()', - "mul-operator", "mlv", output='void operator*()') + {1:"mul-operator", 2:"mlv"}, output='void operator*()') check('function', 'void operator - ()', - "sub-operator", "miv", output='void operator-()') + {1:"sub-operator", 2:"miv"}, output='void operator-()') check('function', 'void operator + ()', - "add-operator", "plv", output='void operator+()') + {1:"add-operator", 2:"plv"}, output='void operator+()') check('function', 'void operator = ()', - "assign-operator", "aSv", output='void operator=()') + {1:"assign-operator", 2:"aSv"}, output='void operator=()') check('function', 'void operator / ()', - "div-operator", "dvv", output='void operator/()') + {1:"div-operator", 2:"dvv"}, output='void operator/()') check('function', 'void operator % ()', - "mod-operator", "rmv", output='void operator%()') + {1:"mod-operator", 2:"rmv"}, output='void operator%()') check('function', 'void operator ! ()', - "not-operator", "ntv", output='void operator!()') + {1:"not-operator", 2:"ntv"}, output='void operator!()') check('function', 'void operator "" _udl()', - None, 'li4_udlv', output='void operator""_udl()') + {2:'li4_udlv'}, output='void operator""_udl()') def test_class_definitions(): - check('class', 'public A', "A", "1A", output='A') - check('class', 'private A', "A", "1A") - check('class', 'A final', 'A', '1A') + check('class', 'public A', {1:"A", 2:"1A"}, output='A') + check('class', 'private A', {1:"A", 2:"1A"}) + check('class', 'A final', {1:'A', 2:'1A'}) # test bases - check('class', 'A', "A", "1A") - check('class', 'A::B::C', "A::B::C", "N1A1B1CE") - check('class', 'A : B', "A", "1A") - check('class', 'A : private B', "A", "1A", output='A : B') - check('class', 'A : public B', "A", "1A") - check('class', 'A : B, C', "A", "1A") - check('class', 'A : B, protected C, D', "A", "1A") - check('class', 'A : virtual private B', 'A', '1A', output='A : virtual B') - check('class', 'A : B, virtual C', 'A', '1A') - check('class', 'A : public virtual B', 'A', '1A') - check('class', 'A : B, C...', 'A', '1A') - check('class', 'A : B..., C', 'A', '1A') + check('class', 'A', {1:"A", 2:"1A"}) + check('class', 'A::B::C', {1:"A::B::C", 2:"N1A1B1CE"}) + check('class', 'A : B', {1:"A", 2:"1A"}) + check('class', 'A : private B', {1:"A", 2:"1A"}, output='A : B') + check('class', 'A : public B', {1:"A", 2:"1A"}) + check('class', 'A : B, C', {1:"A", 2:"1A"}) + check('class', 'A : B, protected C, D', {1:"A", 2:"1A"}) + check('class', 'A : virtual private B', {1:'A', 2:'1A'}, output='A : virtual B') + check('class', 'A : B, virtual C', {1:'A', 2:'1A'}) + check('class', 'A : public virtual B', {1:'A', 2:'1A'}) + check('class', 'A : B, C...', {1:'A', 2:'1A'}) + check('class', 'A : B..., C', {1:'A', 2:'1A'}) def test_enum_definitions(): - check('enum', 'A', None, "1A") - check('enum', 'A : std::underlying_type::type', None, "1A") - check('enum', 'A : unsigned int', None, "1A") - check('enum', 'public A', None, "1A", output='A') - check('enum', 'private A', None, "1A") + check('enum', 'A', {2:"1A"}) + check('enum', 'A : std::underlying_type::type', {2:"1A"}) + check('enum', 'A : unsigned int', {2:"1A"}) + check('enum', 'public A', {2:"1A"}, output='A') + check('enum', 'private A', {2:"1A"}) - check('enumerator', 'A', None, "1A") - check('enumerator', 'A = std::numeric_limits::max()', - None, "1A") + check('enumerator', 'A', {2:"1A"}) + check('enumerator', 'A = std::numeric_limits::max()', {2:"1A"}) def test_templates(): - check('class', "A", None, "IE1AI1TE", output="template<> A") + check('class', "A", {2:"IE1AI1TE"}, output="template<> A") # first just check which objects support templating - 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('class', "template<> A", {2:"IE1A"}) + check('function', "template<> void A()", {2:"IE1Av"}) + check('member', "template<> A a", {2:"IE1a"}) + check('type', "template<> a = A", {2:"IE1a"}) with pytest.raises(DefinitionError): parse('enum', "template<> A") with pytest.raises(DefinitionError): parse('enumerator', "template<> A") # then all the real tests - check('class', "template A", None, "I00E1A") - check('type', "template<> a", None, "IE1a") + check('class', "template A", {2:"I00E1A"}) + check('type', "template<> a", {2:"IE1a"}) - check('class', "template A", None, "I0E1A") - check('class', "template A", None, "I0E1A") - check('class', "template A", None, "IDpE1A") - check('class', "template A", None, "IDpE1A") - check('class', "template A", None, "I0E1A") - check('class', "template A", None, "I0E1A") + check('class', "template A", {2:"I0E1A"}) + check('class', "template A", {2:"I0E1A"}) + check('class', "template A", {2:"IDpE1A"}) + check('class', "template A", {2:"IDpE1A"}) + check('class', "template A", {2:"I0E1A"}) + check('class', "template A", {2:"I0E1A"}) - check('class', "template typename T> A", - None, "II0E0E1A") - check('class', "template A", None, "I_iE1A") - check('class', "template A", None, "I_iE1A") - check('class', "template A", None, "I_DpiE1A") - check('class', "template A", None, "I_iE1A") - check('class', "template A", None, "I_iE1A") + check('class', "template typename T> A", {2:"II0E0E1A"}) + check('class', "template A", {2:"I_iE1A"}) + check('class', "template A", {2:"I_iE1A"}) + check('class', "template A", {2:"I_DpiE1A"}) + check('class', "template A", {2:"I_iE1A"}) + check('class', "template A", {2:"I_iE1A"}) # from #2058 check('function', @@ -413,8 +416,8 @@ def test_templates(): "inline std::basic_ostream &operator<<(" "std::basic_ostream &os, " "const c_string_view_base &str)", - None, "I00ElsRNSt13basic_ostreamI4Char6TraitsEE" - "RK18c_string_view_baseIK4Char6TraitsE") + {2:"I00ElsRNSt13basic_ostreamI4Char6TraitsEE" + "RK18c_string_view_baseIK4Char6TraitsE"}) # template introductions with pytest.raises(DefinitionError): @@ -422,63 +425,61 @@ def test_templates(): with pytest.raises(DefinitionError): 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', - None, 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE') + {2:'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'}) check('class', 'abc::ns::foo{id_0, id_1, ...id_2} xyz::bar', - None, 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE') + {2:'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'}) check('class', 'abc::ns::foo{id_0, id_1, id_2} xyz::bar', - None, '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', - None, 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barI4id_04id_1Dp4id_2EE') + {2:'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barI4id_04id_1Dp4id_2EE'}) - check('class', 'template<> Concept{U} A::B', - None, 'IEI0EX7ConceptI1UEEN1AIiE1BE') + check('class', 'template<> Concept{U} A::B', {2:'IEI0EX7ConceptI1UEEN1AIiE1BE'}) check('type', 'abc::ns::foo{id_0, id_1, id_2} xyz::bar = ghi::qux', - None, 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE') + {2:'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'}) check('type', 'abc::ns::foo{id_0, id_1, ...id_2} xyz::bar = ghi::qux', - None, 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE') + {2:'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'}) check('function', 'abc::ns::foo{id_0, id_1, id_2} void xyz::bar()', - None, 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barEv') + {2:'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barEv'}) check('function', 'abc::ns::foo{id_0, id_1, ...id_2} void xyz::bar()', - None, 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barEv') + {2:'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barEv'}) check('member', 'abc::ns::foo{id_0, id_1, id_2} ghi::qux xyz::bar', - None, 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE') + {2:'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'}) check('member', 'abc::ns::foo{id_0, id_1, ...id_2} ghi::qux xyz::bar', - None, 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE') - check('concept', 'Iterator{T, U} Another', - None, 'I00EX8IteratorI1T1UEE7Another') + {2:'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'}) + check('concept', 'Iterator{T, U} Another', {2:'I00EX8IteratorI1T1UEE7Another'}) check('concept', 'template Numerics = (... && Numeric)', - None, 'IDpE8Numerics') + {2:'IDpE8Numerics'}) def test_template_args(): # from breathe#218 check('function', "template " "void allow(F *f, typename func::type tt)", - None, "I0E5allowP1FN4funcI1F1BXG!=1EE4typeE") + {2:"I0E5allowP1FN4funcI1F1BXG!=1EE4typeE"}) # from #3542 check('type', "template " "enable_if_not_array_t = std::enable_if_t::value, int>", - None, "I0E21enable_if_not_array_t") + {2:"I0E21enable_if_not_array_t"}) def test_attributes(): # style: C++ - check('member', '[[]] int f', 'f__i', '1f') - check('member', '[ [ ] ] int f', 'f__i', '1f', + check('member', '[[]] int f', {1:'f__i', 2:'1f'}) + check('member', '[ [ ] ] int f', {1:'f__i', 2:'1f'}, # this will fail when the proper grammar is implemented output='[[ ]] int f') - check('member', '[[a]] int f', 'f__i', '1f') + check('member', '[[a]] int f', {1:'f__i', 2:'1f'}) # style: GNU - check('member', '__attribute__(()) int f', 'f__i', '1f') - check('member', '__attribute__((a)) int f', 'f__i', '1f') - check('member', '__attribute__((a, b)) int f', 'f__i', '1f') + check('member', '__attribute__(()) int f', {1:'f__i', 2:'1f'}) + check('member', '__attribute__((a)) int f', {1:'f__i', 2:'1f'}) + check('member', '__attribute__((a, b)) int f', {1:'f__i', 2:'1f'}) # style: user-defined id - check('member', 'id_attr int f', 'f__i', '1f') + check('member', 'id_attr int f', {1:'f__i', 2:'1f'}) # style: user-defined paren - check('member', 'paren_attr() int f', 'f__i', '1f') - check('member', 'paren_attr(a) int f', 'f__i', '1f') - check('member', 'paren_attr("") int f', 'f__i', '1f') - check('member', 'paren_attr(()[{}][]{}) int f', 'f__i', '1f') + check('member', 'paren_attr() int f', {1:'f__i', 2:'1f'}) + check('member', 'paren_attr(a) int f', {1:'f__i', 2:'1f'}) + check('member', 'paren_attr("") int f', {1:'f__i', 2:'1f'}) + check('member', 'paren_attr(()[{}][]{}) int f', {1:'f__i', 2:'1f'}) with pytest.raises(DefinitionError): parse('member', 'paren_attr(() int f') with pytest.raises(DefinitionError): @@ -494,7 +495,7 @@ def test_attributes(): # position: decl specs check('function', 'static inline __attribute__(()) void f()', - 'f', '1fv', + {1:'f', 2:'1fv'}, output='__attribute__(()) static inline void f()') From 4bcaf137667c734a2640c66e0a538432cebb2167 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Mon, 20 Mar 2017 12:19:48 +0900 Subject: [PATCH 36/44] C++, expressions --- sphinx/domains/cpp.py | 882 +++++++++++++++++++++++++++++++++++++-- tests/test_domain_cpp.py | 98 ++++- 2 files changed, 937 insertions(+), 43 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 9245c851a..e95de4be9 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -94,7 +94,7 @@ logger = logging.getLogger(__name__) "class" "..."[opt] identifier[opt] | "template" "<" template-parameter-list ">" "class" identifier[opt] "=" id-expression - # also, from C++17 we can have "typname" in template templates + # also, from C++17 we can have "typename" in template templates templateDeclPrefix -> "template" "<" template-parameter-list ">" @@ -288,6 +288,9 @@ logger = logging.getLogger(__name__) nested-name """ +# TODO: support hex, oct, etc. work +_integer_literal_re = re.compile(r'-?[1-9][0-9]*') +_float_literal_re = re.compile(r'[+-]?[0-9]*\.[0-9]+') _identifier_re = re.compile(r'(~?\b[a-zA-Z_][a-zA-Z0-9_]*)\b') _whitespace_re = re.compile(r'\s+(?u)') _string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'" @@ -301,6 +304,12 @@ _operator_re = re.compile(r'''(?x) | (<<|>>)=? | && | \|\| | [!<>=/*%+|&^~-]=? ''') +_fold_operator_re = re.compile(r'''(?x) + ->\* | \.\* | \, + | (<<|>>)=? | && | \|\| + | != + | [<>=/*%+|&^~-]=? +''') # see http://en.cppreference.com/w/cpp/keyword _keywords = [ 'alignas', 'alignof', 'and', 'and_eq', 'asm', 'auto', 'bitand', 'bitor', @@ -445,6 +454,7 @@ _id_operator_v2 = { 'delete': 'dl', 'delete[]': 'da', # the arguments will make the difference between unary and binary + # in operator definitions # '+(unary)' : 'ps', # '-(unary)' : 'ng', # '&(unary)' : 'ad', @@ -486,8 +496,36 @@ _id_operator_v2 = { '->*': 'pm', '->': 'pt', '()': 'cl', - '[]': 'ix' + '[]': 'ix', + '.*': 'ds' # this one is not overloadable, but we need it for expressions } # type: Dict[unicode, unicode] +_id_operator_unary_v2 = { + '++': 'pp_', + '--': 'mm_', + '*': 'de', + '&': 'ad', + '+': 'ps', + '-': 'ng', + '!': 'nt', + '~': 'co' +} +# these are ordered by preceedence +_expression_bin_ops = [ + ['||'], + ['&&'], + ['|'], + ['^'], + ['&'], + ['==', '!='], + ['<=', '>=', '<', '>'], + ['<<', '>>'], + ['+', '-'], + ['*', '/', '%'], + ['.*', '->*'] +] +_expression_unary_ops = ["++", "--", "*", "&", "+", "-", "!", "~"] +_expression_assignment_ops = ["=", "*=", "/=", "%=", "+=", "-=", + ">>=", "<<=", "&=", "^=", "|="] class NoOldIdError(UnicodeMixin, Exception): @@ -581,6 +619,10 @@ def _verify_description_mode(mode): raise Exception("Description mode '%s' is invalid." % mode) +################################################################################ +# Attributes +################################################################################ + class ASTCPPAttribute(ASTBase): def __init__(self, arg): # type: (unicode) -> None @@ -669,6 +711,368 @@ class ASTParenAttribute(ASTBase): signode.append(nodes.Text(txt, txt)) +################################################################################ +# Expressions and Literals +################################################################################ + +class ASTPointerLiteral(ASTBase): + def __unicode__(self): + return u'nullptr' + + def get_id_v2(self): + return 'LDnE' + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('nullptr')) + + +class ASTBooleanLiteral(ASTBase): + def __init__(self, value): + self.value = value + + def __unicode__(self): + if self.value: + return u'true' + else: + return u'false' + + def get_id_v2(self): + if self.value: + return 'L1E' + else: + return 'L0E' + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text(text_type(self))) + + +class ASTNumberLiteral(ASTBase): + def __init__(self, data): + # type: (unicode) -> None + self.data = data + + def __unicode__(self): + return self.data + + def get_id_v2(self): + return "L%sE" % self.data + + def describe_signature(self, signode, mode, env, symbol): + txt = text_type(self) + signode.append(nodes.Text(txt, txt)) + + +class ASTStringLiteral(ASTBase): + def __init__(self, data): + # type: (unicode) -> None + self.data = data + + def __unicode__(self): + return self.data + + def get_id_v2(self): + # note: the length is not really correct with escaping + return "LA%d_KcE" % (len(self.data) - 2) + + def describe_signature(self, signode, mode, env, symbol): + txt = text_type(self) + signode.append(nodes.Text(txt, txt)) + + +class ASTParenExpr(ASTBase): + def __init__(self, expr): + self.expr = expr + + def __unicode__(self): + return '(' + text_type(self.expr) + ')' + + def get_id_v2(self): + return self.expr.get_id_v2() + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('(', '(')) + self.expr.describe_signature(signode, mode, env, symbol) + signode.append(nodes.Text(')', ')')) + + +class ASTFoldExpr(ASTBase): + def __init__(self, leftExpr, op, rightExpr): + assert leftExpr is not None or rightExpr is not None + self.leftExpr = leftExpr + self.op = op + self.rightExpr = rightExpr + + def __unicode__(self): + res = ['('] + if self.leftExpr: + res.append(text_type(self.leftExpr)) + res.append(' ') + res.append(text_type(self.op)) + res.append(' ') + res.append('...') + if self.rightExpr: + res.append(' ') + res.append(text_type(self.op)) + res.append(' ') + res.append(text_type(self.rightExpr)) + res.append(')') + return u''.join(res) + + def get_id_v2(self): + # TODO: find the right mangling scheme + return text_type(self) + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('(')) + if self.leftExpr: + self.leftExpr.describe_signature(signode, mode, env, symbol) + signode.append(nodes.Text(' ')) + signode.append(nodes.Text(self.op)) + signode.append(nodes.Text(' ')) + signode.append(nodes.Text('...')) + if self.rightExpr: + signode.append(nodes.Text(' ')) + signode.append(nodes.Text(self.op)) + signode.append(nodes.Text(' ')) + self.rightExpr.describe_signature(signode, mode, env, symbol) + signode.append(nodes.Text(')')) + + +class ASTBinOpExpr(ASTBase): + def __init__(self, exprs, ops): + assert len(exprs) > 0 + assert len(exprs) == len(ops) + 1 + self.exprs = exprs + self.ops = ops + + def __unicode__(self): + res = [] + res.append(text_type(self.exprs[0])) + for i in range(1, len(self.exprs)): + res.append(' ') + res.append(self.ops[i - 1]) + res.append(' ') + res.append(text_type(self.exprs[i])) + return u''.join(res) + + def get_id_v2(self): + res = [] + for i in range(len(self.ops)): + res.append(_id_operator_v2[self.ops[i]]) + res.append(self.exprs[i].get_id_v2()) + res.append(self.exprs[-1].get_id_v2()) + return u''.join(res) + + def describe_signature(self, signode, mode, env, symbol): + self.exprs[0].describe_signature(signode, mode, env, symbol) + for i in range(1, len(self.exprs)): + signode.append(nodes.Text(' ')) + signode.append(nodes.Text(self.ops[i - 1])) + signode.append(nodes.Text(' ')) + self.exprs[i].describe_signature(signode, mode, env, symbol) + + +class ASTAssignmentExpr(ASTBase): + def __init__(self, exprs, ops): + assert len(exprs) > 0 + assert len(exprs) == len(ops) + 1 + self.exprs = exprs + self.ops = ops + + def __unicode__(self): + res = [] + res.append(text_type(self.exprs[0])) + for i in range(1, len(self.exprs)): + res.append(' ') + res.append(self.ops[i - 1]) + res.append(' ') + res.append(text_type(self.exprs[i])) + return u''.join(res) + + def get_id(self, version): + res = [] + for i in range(len(self.ops)): + res.append(_id_operator_v2[self.ops[i]]) + res.append(self.exprs[i].get_id(version)) + res.append(self.exprs[-1].get_id(version)) + return u''.join(res) + + def describe_signature(self, signode, mode, env, symbol): + self.exprs[0].describe_signature(signode, mode, env, symbol) + for i in range(1, len(self.exprs)): + signode.append(nodes.Text(' ')) + signode.append(nodes.Text(self.ops[i - 1])) + signode.append(nodes.Text(' ')) + self.exprs[i].describe_signature(signode, mode, env, symbol) + + +class ASTCastExpr(ASTBase): + def __init__(self, typ, expr): + self.typ = typ + self.expr = expr + + def __unicode__(self): + res = ['('] + res.append(text_type(self.typ)) + res.append(')') + res.append(text_type(self.expr)) + return u''.join(res) + + def get_id(self, version): + return 'cv' + self.typ.get_id(version) + self.expr.get_id(version) + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('(')) + self.typ.describe_signature(signode, mode, env, symbol) + signode.append(nodes.Text(')')) + self.expr.describe_signature(signode, mode, env, symbol) + + +class ASTUnaryOpExpr(ASTBase): + def __init__(self, op, expr): + self.op = op + self.expr = expr + + def __unicode__(self): + return text_type(self.op) + text_type(self.expr) + + def get_id_v2(self): + return _id_operator_unary_v2[self.op] + self.expr.get_id_v2() + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text(self.op)) + self.expr.describe_signature(signode, mode, env, symbol) + + +class ASTPostfixCallExpr(ASTBase): + def __init__(self, exprs): + self.exprs = exprs + + def __unicode__(self): + res = ['('] + first = True + for e in self.exprs: + if not first: + res.append(', ') + first = False + res.append(text_type(e)) + res.append(')') + return u''.join(res) + + def get_id_v2(self, idPrefix): + res = ['cl', idPrefix] + for e in self.exprs: + res.append(e.get_id_v2()) + res.append('E') + return u''.join(res) + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('(')) + first = True + for e in self.exprs: + if not first: + signode.append(nodes.Text(', ')) + first = False + e.describe_signature(signode, mode, env, symbol) + signode.append(nodes.Text(')')) + + +class ASTPostfixArray(ASTBase): + def __init__(self, expr): + self.expr = expr + + def __unicode__(self): + return u'[' + text_type(self.expr) + ']' + + def get_id_v2(self, idPrefix): + return 'ix' + idPrefix + self.expr.get_id_v2() + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('[')) + self.expr.describe_signature(signode, mode, env, symbol) + signode.append(nodes.Text(']')) + + +class ASTPostfixInc(ASTBase): + def __unicode__(self): + return u'++' + + def get_id(self, idPrefix, version): + return 'pp' + idPrefix + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('++')) + + +class ASTPostfixDec(ASTBase): + def __unicode__(self): + return u'--' + + def get_id(self, idPrefix, version): + return 'mm' + idPrefix + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('--')) + + +class ASTPostfixMember(ASTBase): + def __init__(self, name): + self.name = name + + def __unicode__(self): + return u'.' + text_type(self.name) + + def get_id(self, idPrefix, version): + return 'dt' + idPrefix + self.name.get_id(version) + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('.')) + self.name.describe_signature(signode, 'noneIsName', env, symbol) + + +class ASTPostfixMemberOfPointer(ASTBase): + def __init__(self, name): + self.name = name + + def __unicode__(self): + return u'->' + text_type(self.name) + + def get_id(self, idPrefix, version): + return 'pt' + idPrefix + self.name.get_id(version) + + def describe_signature(self, signode, mode, env, symbol): + signode.append(nodes.Text('->')) + self.name.describe_signature(signode, 'noneIsName', env, symbol) + + +class ASTPostfixExpr(ASTBase): + def __init__(self, prefix, postFixes): + assert len(postFixes) > 0 + self.prefix = prefix + self.postFixes = postFixes + + def __unicode__(self): + res = [text_type(self.prefix)] + for p in self.postFixes: + res.append(text_type(p)) + return u''.join(res) + + def get_id_v2(self): + id = self.prefix.get_id_v2() + for p in self.postFixes: + id = p.get_id_v2(id) + return id + + def describe_signature(self, signode, mode, env, symbol): + self.prefix.describe_signature(signode, mode, env, symbol) + for p in self.postFixes: + p.describe_signature(signode, mode, env, symbol) + + +################################################################################ +# The Rest +################################################################################ + class ASTIdentifier(ASTBase): def __init__(self, identifier): # type: (unicode) -> None @@ -1186,15 +1590,13 @@ class ASTTemplateArgConstant(ASTBase): if version == 1: return text_type(self).replace(u' ', u'-') if version == 2: - # TODO: doing this properly needs parsing of expressions, let's just - # use it verbatim for now return u'X' + text_type(self) + u'E' - assert False + return u'X' + self.value.get_id_v2() + u'E' def describe_signature(self, signode, mode, env, symbol): # type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None _verify_description_mode(mode) - signode += nodes.Text(text_type(self)) + self.value.describe_signature(signode, mode, env, symbol) class ASTTemplateArgs(ASTBase): @@ -1740,20 +2142,31 @@ class ASTArray(ASTBase): def __unicode__(self): # type: () -> unicode - return u''.join(['[', text_type(self.size), ']']) + if self.size: + return u''.join(['[', text_type(self.size), ']']) + else: + return u'[]' def get_id(self, version): # type: (int) -> unicode if version == 1: return u'A' if version == 2: - # TODO: this should maybe be done differently - return u'A' + text_type(self.size) + u'_' - assert False + if self.size: + return u'A' + text_type(self.size) + u'_' + else: + return u'A_' + if self.size: + return u'A' + self.size.get_id_v2() + u'_' + else: + return u'A_' - def describe_signature(self, signode, mode, env): + def describe_signature(self, signode, mode, env, symbol): _verify_description_mode(mode) - signode += nodes.Text(text_type(self)) + signode.append(nodes.Text("[")) + if self.size: + self.size.describe_signature(signode, mode, env, symbol) + signode.append(nodes.Text("]")) class ASTDeclaratorPtr(ASTBase): @@ -2187,7 +2600,7 @@ class ASTDeclaratorNameParamQual(ASTBase): if self.declId: self.declId.describe_signature(signode, mode, env, symbol) for op in self.arrayOps: - op.describe_signature(signode, mode, env) + op.describe_signature(signode, mode, env, symbol) if self.paramQual: self.paramQual.describe_signature(signode, mode, env, symbol) @@ -2201,10 +2614,11 @@ class ASTInitializer(ASTBase): # type: () -> unicode return u''.join([' = ', text_type(self.value)]) - def describe_signature(self, signode, mode): + def describe_signature(self, signode, mode, env, symbol): # type: (addnodes.desc_signature, unicode) -> None _verify_description_mode(mode) - signode += nodes.Text(text_type(self)) + signode.append(nodes.Text(' = ')) + self.value.describe_signature(signode, 'markType', env, symbol) class ASTType(ASTBase): @@ -2324,9 +2738,9 @@ class ASTTypeWithInit(ASTBase): def describe_signature(self, signode, mode, env, symbol): # type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None _verify_description_mode(mode) - self.type.describe_signature(signode, mode, env, symbol=symbol) + self.type.describe_signature(signode, mode, env, symbol) if self.init: - self.init.describe_signature(signode, mode) + self.init.describe_signature(signode, mode, env, symbol) class ASTTypeUsing(ASTBase): @@ -2397,7 +2811,7 @@ class ASTConcept(ASTBase): if self.isFunction: signode += nodes.Text("()") if self.initializer: - self.initializer.describe_signature(signode, mode) + self.initializer.describe_signature(signode, mode, env, symbol) class ASTBaseClass(ASTBase): @@ -2537,9 +2951,9 @@ class ASTEnumerator(ASTBase): def describe_signature(self, signode, mode, env, symbol): # type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None _verify_description_mode(mode) - self.name.describe_signature(signode, mode, env, symbol=symbol) + self.name.describe_signature(signode, mode, env, symbol) if self.init: - self.init.describe_signature(signode, 'noneIsName') + self.init.describe_signature(signode, 'markType', env, symbol) class ASTDeclaration(ASTBase): @@ -2646,8 +3060,7 @@ class ASTDeclaration(ASTBase): mainDeclNode += addnodes.desc_annotation('enumerator ', 'enumerator ') else: assert False - self.declaration.describe_signature(mainDeclNode, mode, env, - symbol=self.symbol) + self.declaration.describe_signature(mainDeclNode, mode, env, self.symbol) class ASTNamespace(ASTBase): @@ -3130,6 +3543,7 @@ class DefinitionParser(object): self.end = len(self.definition) self.last_match = None # type: Match self._previous_state = (0, None) # type: Tuple[int, Match] + self.otherErrors = [] # type: List[DefinitionError] self.warnEnv = warnEnv self.config = config @@ -3137,7 +3551,10 @@ class DefinitionParser(object): def _make_multi_error(self, errors, header): # type: (List[Any], unicode) -> DefinitionError if len(errors) == 1: - return DefinitionError(header + '\n' + errors[0][0].description) + if len(header) > 0: + return DefinitionError(header + '\n' + errors[0][0].description) + else: + return DefinitionError(errors[0][0].description) result = [header, '\n'] for e in errors: if len(e[1]) > 0: @@ -3162,10 +3579,16 @@ class DefinitionParser(object): def fail(self, msg): # type: (unicode) -> None + errors = [] indicator = '-' * self.pos + '^' - raise DefinitionError( + exMain = DefinitionError( 'Invalid definition: %s [error at %d]\n %s\n %s' % (msg, self.pos, self.definition, indicator)) + errors.append((exMain, "Main error")) + for err in self.otherErrors: + errors.append((err, "Potential other error")) + self.otherErrors = [] + raise self._make_multi_error(errors, '') def warn(self, msg): # type: (unicode) -> None @@ -3251,6 +3674,25 @@ class DefinitionParser(object): if not self.eof: self.fail('Expected end of definition.') + def _parse_string(self): + if self.current_char != '"': + return None + startPos = self.pos + self.pos += 1 + escape = False + while True: + if self.eof: + self.fail("Unexpected end during inside string.") + elif self.current_char == '"' and not escape: + self.pos += 1 + break + elif self.current_char == '\\': + escape = True + else: + escape = False + self.pos += 1 + return self.definition[startPos:self.pos] + def _parse_balanced_token_seq(self, end): # type: (List[unicode]) -> unicode # TODO: add handling of string literals and similar @@ -3332,11 +3774,332 @@ class DefinitionParser(object): return None - def _parse_expression(self, end): + def _parse_literal(self): + # -> integer-literal + # | character-literal + # | floating-literal + # | string-literal + # | boolean-literal -> "false" | "true" + # | pointer-literal -> "nullptr" + # | user-defined-literal + self.skip_ws() + if self.skip_word('nullptr'): + return ASTPointerLiteral() + if self.skip_word('true'): + return ASTBooleanLiteral(True) + if self.skip_word('false'): + return ASTBooleanLiteral(False) + if self.match(_float_literal_re): + return ASTNumberLiteral(self.matched_text) + if self.match(_integer_literal_re): + return ASTNumberLiteral(self.matched_text) + string = self._parse_string() + if string is not None: + return ASTStringLiteral(string) + # TODO: char lit + # TODO: user-defined lit + return None + + def _parse_fold_or_paren_expression(self): + # "(" expression ")" + # fold-expression + # -> ( cast-expression fold-operator ... ) + # | ( ... fold-operator cast-expression ) + # | ( cast-expression fold-operator ... fold-operator cast-expression + if self.current_char != '(': + return None + self.pos += 1 + self.skip_ws() + if self.skip_string_and_ws("..."): + # ( ... fold-operator cast-expression ) + if not self.match(_fold_operator_re): + self.fail("Expected fold operator after '...' in fold expression.") + op = self.matched_text + rightExpr = self._parse_cast_expression() + if not self.skip_string(')'): + self.fail("Expected ')' in end of fold expression.") + return ASTFoldExpr(None, op, rightExpr) + # TODO: actually try to parse fold expression + # fall back to a paren expression + res = self._parse_expression(inTemplate=False) + self.skip_ws() + if not self.skip_string(')'): + self.fail("Expected ')' in end of fold expression or parenthesized expression.") + return ASTParenExpr(res) + + def _parse_primary_expression(self): + # literal + # "this" + # lambda-expression + # "(" expression ")" + # fold-expression + # id-expression -> we parse this with _parse_nested_name + self.skip_ws() + res = self._parse_literal() + if res is not None: + return res + # TODO: try 'this' and lambda expression + res = self._parse_fold_or_paren_expression() + if res is not None: + return res + return self._parse_nested_name() + + def _parse_postfix_expression(self): + # -> primary + # | postfix "[" expression "]" + # | postfix "[" braced-init-list [opt] "]" + # | postfix "(" expression-list [opt] ")" + # | postfix "." "template" [opt] id-expression + # | postfix "->" "template" [opt] id-expression + # | postfix "." pseudo-destructor-name + # | postfix "->" pseudo-destructor-name + # | postfix "++" + # | postfix "--" + # | simple-type-specifier "(" expression-list [opt] ")" + # | simple-type-specifier braced-init-list + # | typename-specifier "(" expression-list [opt] ")" + # | typename-specifier braced-init-list + # | "dynamic_cast" "<" type-id ">" "(" expression ")" + # | "static_cast" "<" type-id ">" "(" expression ")" + # | "reinterpret_cast" "<" type-id ">" "(" expression ")" + # | "const_cast" "<" type-id ">" "(" expression ")" + # | "typeid" "(" expression ")" + # | "typeid" "(" type-id ")" + + # TODO: try the productions with prefixes: + # dynamic_cast, static_cast, reinterpret_cast, const_cast, typeid + prefixType = None + pos = self.pos + try: + prefix = self._parse_primary_expression() + prefixType = 'expr' + except DefinitionError as eOuter: + self.pos = pos + try: + # we are potentially casting, so save parens for us + # TODO: hmm, would we need to try both with operatorCast and with None? + prefix = self._parse_type(False, 'operatorCast') + prefixType = 'typeOperatorCast' + except DefinitionError as eInner: + self.pos = pos + header = "Error in postfix expression, expected primary expression or type." + errors = [] + errors.append((eOuter, "If primary expression")) + errors.append((eInner, "If type")) + raise self._make_multi_error(errors, header) + # and now parse postfixes + postFixes = [] + while True: + self.skip_ws() + if prefixType == 'expr': + if self.skip_string_and_ws('['): + expr = self._parse_expression(inTemplate=False) + self.skip_ws() + if not self.skip_string(']'): + self.fail("Expected ']' in end of postfix expression.") + postFixes.append(ASTPostfixArray(expr)) + continue + if self.skip_string('.'): + if self.skip_string('*'): + # don't steal the dot + self.pos -= 2 + else: + name = self._parse_nested_name() + postFixes.append(ASTPostfixMember(name)) + continue + if self.skip_string('->'): + if self.skip_string('*'): + # don't steal the arrow + self.pos -= 3 + else: + name = self._parse_nested_name() + postFixes.append(ASTPostfixMemberOfPointer(name)) + continue + if self.skip_string('++'): + postFixes.append(ASTPostfixInc()) + continue + if self.skip_string('--'): + postFixes.append(ASTPostfixDec()) + continue + if self.skip_string_and_ws('('): + # TODO: handled braced init + exprs = [] + self.skip_ws() + if not self.skip_string(')'): + while True: + self.skip_ws() + expr = self._parse_expression(inTemplate=False) + exprs.append(expr) + self.skip_ws() + if self.skip_string(')'): + break + if not self.skip_string(','): + self.fail("Error in cast or call, expected ',' or ')'.") + postFixes.append(ASTPostfixCallExpr(exprs)) + continue + break + if len(postFixes) == 0: + return prefix + else: + return ASTPostfixExpr(prefix, postFixes) + + def _parse_unary_expression(self): + # -> postfix + # | "++" cast + # | "--" cast + # | unary-operator cast -> (* | & | + | - | ! | ~) cast + # | "sizeof" unary + # | "sizeof" "(" type-id ")" + # | "sizeof" "..." "(" identifier ")" + # | "alignof" "(" type-id ")" + # | noexcept-expression -> noexcept "(" expression ")" + # | new-expression + # | delete-expression + self.skip_ws() + for op in _expression_unary_ops: + # TODO: hmm, should we be able to backtrack here? + if self.skip_string(op): + expr = self._parse_cast_expression() + return ASTUnaryOpExpr(op, expr) + # TODO: the rest + return self._parse_postfix_expression() + + def _parse_cast_expression(self): + # -> unary | "(" type-id ")" cast + pos = self.pos + self.skip_ws() + if self.skip_string('('): + try: + typ = self._parse_type(False) + if not self.skip_string(')'): + raise DefinitionError("Expected ')' in cast expression.") + expr = self._parse_cast_expression() + return ASTCastExpr(typ, expr) + except DefinitionError as exCast: + self.pos = pos + try: + return self._parse_unary_expression() + except DefinitionError as exUnary: + errs = [] + errs.append((exCast, "If type cast expression")) + errs.append((exUnary, "If unary expression")) + raise self._make_multi_error(errs, "Error in cast expression.") + else: + return self._parse_unary_expression() + + def _parse_logical_or_expression(self, inTemplate): + # logical-or = logical-and || + # logical-and = inclusive-or && + # inclusive-or = exclusive-or | + # exclusive-or = and ^ + # and = equality & + # equality = relational ==, != + # relational = shift <, >, <=, >= + # shift = additive <<, >> + # additive = multiplicative +, - + # multiplicative = pm *, /, % + # pm = cast .*, ->* + def _parse_bin_op_expr(self, opId, inTemplate): + if opId + 1 == len(_expression_bin_ops): + def parser(inTemplate): + return self._parse_cast_expression() + else: + def parser(inTemplate): + return _parse_bin_op_expr(self, opId + 1, inTemplate=inTemplate) + exprs = [] + ops = [] + exprs.append(parser(inTemplate=inTemplate)) + while True: + self.skip_ws() + if inTemplate and self.current_char == '>': + break + pos = self.pos + oneMore = False + for op in _expression_bin_ops[opId]: + if not self.skip_string(op): + continue + if op == '&' and self.current_char == '&': + # don't split the && 'token' + self.pos -= 1 + # and btw. && has lower precedence, so we are done + break + try: + expr = parser(inTemplate=inTemplate) + exprs.append(expr) + ops.append(op) + oneMore = True + break + except DefinitionError: + self.pos = pos + if not oneMore: + break + return ASTBinOpExpr(exprs, ops) + return _parse_bin_op_expr(self, 0, inTemplate=inTemplate) + + def _parse_conditional_expression_tail(self, orExprHead): + # -> "?" expression ":" assignment-expression + return None + + def _parse_assignment_expression(self, inTemplate): + # -> conditional-expression + # | logical-or-expression assignment-operator initializer-clause + # | throw-expression + # TODO: parse throw-expression: "throw" assignment-expression [opt] + # if not a throw expression, then: + # -> conditional-expression -> + # logical-or-expression + # | logical-or-expression "?" expression ":" assignment-expression + # | logical-or-expression assignment-operator initializer-clause + exprs = [] + ops = [] + orExpr = self._parse_logical_or_expression(inTemplate=inTemplate) + exprs.append(orExpr) + # TODO: handle ternary with _parse_conditional_expression_tail + while True: + oneMore = False + self.skip_ws() + for op in _expression_assignment_ops: + if not self.skip_string(op): + continue + expr = self._parse_logical_or_expression(False) + exprs.append(expr) + ops.append(op) + oneMore = True + if not oneMore: + break + if len(ops) == 0: + return orExpr + else: + return ASTAssignmentExpr(exprs, ops) + + def _parse_constant_expression(self, inTemplate): + # -> conditional-expression + orExpr = self._parse_logical_or_expression(inTemplate=inTemplate) + # TODO: use _parse_conditional_expression_tail + return orExpr + + def _parse_expression(self, inTemplate): + # -> assignment-expression + # | expression "," assignment-expresion + # TODO: actually parse the second production + return self._parse_assignment_expression(inTemplate=inTemplate) + + def _parse_expression_fallback(self, end, parser): # type: (List[unicode]) -> unicode # Stupidly "parse" an expression. # 'end' should be a list of characters which ends the expression. - assert end + + # first try to use the provided parser + prevPos = self.pos + try: + return parser() + except DefinitionError as e: + raise + self.warn("Parsing of expression failed. Using fallback parser." + " Error was:\n%s" % e.description) + self.pos = prevPos + # and then the fallback scanning + assert end is not None self.skip_ws() startPos = self.pos if self.match(_string_re): @@ -3353,7 +4116,7 @@ class DefinitionParser(object): elif len(symbols) > 0 and self.current_char == symbols[-1]: symbols.pop() self.pos += 1 - if self.eof: + if len(end) > 0 and self.eof: self.fail("Could not find end of expression starting at %d." % startPos) value = self.definition[startPos:self.pos].strip() @@ -3417,7 +4180,9 @@ class DefinitionParser(object): prevErrors.append((e, "If type argument")) self.pos = pos try: - value = self._parse_expression(end=[',', '>']) + def parser(): + return self._parse_constant_expression(inTemplate=True) + value = self._parse_expression_fallback([',', '>'], parser) self.skip_ws() if self.skip_string('>'): parsedEnd = True @@ -3461,7 +4226,15 @@ class DefinitionParser(object): if identifier in _keywords: self.fail("Expected identifier in nested name, " "got keyword: %s" % identifier) - templateArgs = self._parse_template_argument_list() + # try greedily to get template parameters, + # but otherwise a < might be because we are in an expression + pos = self.pos + try: + templateArgs = self._parse_template_argument_list() + except DefinitionError as ex: + self.pos = pos + templateArgs = None + self.otherErrors.append(ex) identifier = ASTIdentifier(identifier) # type: ignore names.append(ASTNestedNameElement(identifier, templateArgs)) @@ -3747,9 +4520,16 @@ class DefinitionParser(object): while 1: self.skip_ws() if typed and self.skip_string('['): - value = self._parse_expression(end=[']']) - res = self.skip_string(']') - assert res + self.skip_ws() + if self.skip_string(']'): + arrayOps.append(ASTArray(None)) + continue + + def parser(): + return self._parse_expression(inTemplate=False) + value = self._parse_expression_fallback([']'], parser) + if not self.skip_string(']'): + self.fail("Expected ']' in end of array operator.") arrayOps.append(ASTArray(value)) continue else: @@ -3867,11 +4647,17 @@ class DefinitionParser(object): return None else: if outer == 'member': - value = self.read_rest().strip() # type: unicode + def parser(): + return self._parse_assignment_expression(inTemplate=False) + value = self._parse_expression_fallback([], parser) elif outer == 'templateParam': - value = self._parse_expression(end=[',', '>']) + def parser(): + return self._parse_assignment_expression(inTemplate=True) + value = self._parse_expression_fallback([',', '>'], parser) elif outer is None: # function parameter - value = self._parse_expression(end=[',', ')']) + def parser(): + return self._parse_assignment_expression(inTemplate=False) + value = self._parse_expression_fallback([',', ')'], parser) else: self.fail("Internal error, initializer for outer '%s' not " "implemented." % outer) @@ -4045,7 +4831,10 @@ class DefinitionParser(object): init = None if self.skip_string('='): self.skip_ws() - init = ASTInitializer(self.read_rest()) + + def parser(): + return self._parse_constant_expression(inTemplate=False) + init = ASTInitializer(self._parse_expression_fallback([], parser)) return ASTEnumerator(name, init) def _parse_template_parameter_list(self): @@ -4682,6 +5471,27 @@ class CPPXRefRole(XRefRole): return title, target +class CPPExprRole(object): + def __call__(self, typ, rawtext, text, lineno, inliner, options={}, content=[]): + class Warner(object): + def warn(self, msg): + inliner.reporter.warning(msg, line=lineno) + env = inliner.document.settings.env + parser = DefinitionParser(text, Warner(), env.config) + try: + ast = parser.parse_expression() + except DefinitionError as ex: + Warner().warn('Unparseable C++ expression: %r\n%s' + % (text, text_type(ex.description))) + return [nodes.literal(text)], [] + parentSymbol = env.temp_data.get('cpp:parent_symbol', None) + if parentSymbol is None: + parentSymbol = env.domaindata['cpp']['root_symbol'] + p = nodes.literal() + ast.describe_signature(p, 'markType', env, parentSymbol) + return [p], [] + + class CPPDomain(Domain): """C++ language domain.""" name = 'cpp' diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index e7f12f09c..f8ebdac7d 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -26,11 +26,7 @@ def parse(name, string): cpp_paren_attributes = ["paren_attr"] parser = DefinitionParser(string, None, Config()) ast = parser.parse_declaration(name) - if not parser.eof: - print("Parsing stopped at", parser.pos) - print(string) - print('-' * parser.pos + '^') - raise DefinitionError("") + parser.assert_end() # The scopedness would usually have been set by CPPEnumObject if name == "enum": ast.scoped = None # simulate unscoped enum @@ -107,6 +103,92 @@ def test_fundamental_types(): check("function", "void f(%s arg)" % t, {1: makeIdV1(), 2:makeIdV2()}) +def test_expressions(): + def exprCheck(expr, id): + ids = 'IE1CIA%s_1aE' + check('class', 'template<> C' % expr, {2:ids % expr, 3:ids % id}) + # primary + exprCheck('nullptr', 'LDnE') + exprCheck('true', 'L1E') + exprCheck('false', 'L0E') + exprCheck('5', 'L5E') + exprCheck('5.0', 'L5.0E') + exprCheck('"abc\\"cba"', 'LA8_KcE') + # TODO: test the rest + exprCheck('(... + Ns)', '(... + Ns)') + exprCheck('(5)', 'L5E') + exprCheck('C', '1C') + # postfix + exprCheck('A(2)', 'cl1AL2EE') + exprCheck('A[2]', 'ix1AL2E') + exprCheck('a.b.c', 'dtdt1a1b1c') + exprCheck('a->b->c', 'ptpt1a1b1c') + exprCheck('i++', 'pp1i') + exprCheck('i--', 'mm1i') + # TODO, those with prefixes + # unary + exprCheck('++5', 'pp_L5E') + exprCheck('--5', 'mm_L5E') + exprCheck('*5', 'deL5E') + exprCheck('&5', 'adL5E') + exprCheck('+5', 'psL5E') + exprCheck('-5', 'ngL5E') + exprCheck('!5', 'ntL5E') + exprCheck('~5', 'coL5E') + # cast + exprCheck('(int)2', 'cviL2E') + # binary op + exprCheck('5 || 42', 'ooL5EL42E') + exprCheck('5 && 42', 'aaL5EL42E') + exprCheck('5 | 42', 'orL5EL42E') + exprCheck('5 ^ 42', 'eoL5EL42E') + exprCheck('5 & 42', 'anL5EL42E') + # ['==', '!='] + exprCheck('5 == 42', 'eqL5EL42E') + exprCheck('5 != 42', 'neL5EL42E') + # ['<=', '>=', '<', '>'] + exprCheck('5 <= 42', 'leL5EL42E') + exprCheck('5 >= 42', 'geL5EL42E') + exprCheck('5 < 42', 'ltL5EL42E') + exprCheck('5 > 42', 'gtL5EL42E') + # ['<<', '>>'] + exprCheck('5 << 42', 'lsL5EL42E') + exprCheck('5 >> 42', 'rsL5EL42E') + # ['+', '-'] + exprCheck('5 + 42', 'plL5EL42E') + exprCheck('5 - 42', 'miL5EL42E') + # ['*', '/', '%'] + exprCheck('5 * 42', 'mlL5EL42E') + exprCheck('5 / 42', 'dvL5EL42E') + exprCheck('5 % 42', 'rmL5EL42E') + # ['.*', '->*'] + exprCheck('5 .* 42', 'dsL5EL42E') + exprCheck('5 ->* 42', 'pmL5EL42E') + # conditional + # TODO + # assignment + exprCheck('a = 5', 'aS1aL5E') + exprCheck('a *= 5', 'mL1aL5E') + exprCheck('a /= 5', 'dV1aL5E') + exprCheck('a %= 5', 'rM1aL5E') + exprCheck('a += 5', 'pL1aL5E') + exprCheck('a -= 5', 'mI1aL5E') + exprCheck('a >>= 5', 'rS1aL5E') + exprCheck('a <<= 5', 'lS1aL5E') + exprCheck('a &= 5', 'aN1aL5E') + exprCheck('a ^= 5', 'eO1aL5E') + exprCheck('a |= 5', 'oR1aL5E') + + # Additional tests + # a < expression that starts with something that could be a template + exprCheck('A < 42', 'lt1AL42E') + check('function', 'template<> void f(A &v)', + {2:"IE1fR1AI1BX2EE", 3:"IE1fR1AI1BXL2EEE"}) + exprCheck('A<1>::value', {2:'N1AIXL1EEE5valueE'}) + check('class', "template A", {2:"I_iE1A"}) + check('enumerator', 'A = std::numeric_limits::max()', {2:"1A"}) + + def test_type_definitions(): check("type", "public bool b", {1:"b", 2:"1b"}, "bool b") check("type", "bool A::b", {1:"A::b", 2:"N1A1bE"}) @@ -451,17 +533,19 @@ def test_templates(): check('concept', 'template Numerics = (... && Numeric)', {2:'IDpE8Numerics'}) + def test_template_args(): # from breathe#218 check('function', "template " - "void allow(F *f, typename func::type tt)", - {2:"I0E5allowP1FN4funcI1F1BXG!=1EE4typeE"}) + "void allow(F *f, typename func::type tt)", + {2:"I0E5allowP1FN4funcI1F1BXG != 1EE4typeE"}) # from #3542 check('type', "template " "enable_if_not_array_t = std::enable_if_t::value, int>", {2:"I0E21enable_if_not_array_t"}) + def test_attributes(): # style: C++ check('member', '[[]] int f', {1:'f__i', 2:'1f'}) From 32788c4e81a1b1501abbbd11f6ced9742ae7d010 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Tue, 28 Mar 2017 22:29:30 +0900 Subject: [PATCH 37/44] C++, turn on version 3 mangling --- sphinx/domains/cpp.py | 52 +++++++++++++++++++++------------------- tests/test_domain_cpp.py | 18 ++++++++------ 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index e95de4be9..ce0b589b8 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -327,8 +327,8 @@ _keywords = [ 'while', 'xor', 'xor_eq' ] -_max_id = 2 -_id_prefix = [None, '', '_CPPv2'] +_max_id = 3 +_id_prefix = [None, '', '_CPPv2', '_CPPv3'] # ------------------------------------------------------------------------------ # Id v1 constants @@ -719,7 +719,7 @@ class ASTPointerLiteral(ASTBase): def __unicode__(self): return u'nullptr' - def get_id_v2(self): + def get_id(self, version): return 'LDnE' def describe_signature(self, signode, mode, env, symbol): @@ -736,7 +736,7 @@ class ASTBooleanLiteral(ASTBase): else: return u'false' - def get_id_v2(self): + def get_id(self, version): if self.value: return 'L1E' else: @@ -754,7 +754,7 @@ class ASTNumberLiteral(ASTBase): def __unicode__(self): return self.data - def get_id_v2(self): + def get_id(self, version): return "L%sE" % self.data def describe_signature(self, signode, mode, env, symbol): @@ -770,7 +770,7 @@ class ASTStringLiteral(ASTBase): def __unicode__(self): return self.data - def get_id_v2(self): + def get_id(self, version): # note: the length is not really correct with escaping return "LA%d_KcE" % (len(self.data) - 2) @@ -786,8 +786,8 @@ class ASTParenExpr(ASTBase): def __unicode__(self): return '(' + text_type(self.expr) + ')' - def get_id_v2(self): - return self.expr.get_id_v2() + def get_id(self, version): + return self.expr.get_id(version) def describe_signature(self, signode, mode, env, symbol): signode.append(nodes.Text('(', '(')) @@ -818,9 +818,12 @@ class ASTFoldExpr(ASTBase): res.append(')') return u''.join(res) - def get_id_v2(self): + def get_id(self, version): + assert version >= 3 + if version == 3: + return text_type(self) # TODO: find the right mangling scheme - return text_type(self) + assert False def describe_signature(self, signode, mode, env, symbol): signode.append(nodes.Text('(')) @@ -855,12 +858,13 @@ class ASTBinOpExpr(ASTBase): res.append(text_type(self.exprs[i])) return u''.join(res) - def get_id_v2(self): + def get_id(self, version): + assert version >= 2 res = [] for i in range(len(self.ops)): res.append(_id_operator_v2[self.ops[i]]) - res.append(self.exprs[i].get_id_v2()) - res.append(self.exprs[-1].get_id_v2()) + res.append(self.exprs[i].get_id(version)) + res.append(self.exprs[-1].get_id(version)) return u''.join(res) def describe_signature(self, signode, mode, env, symbol): @@ -936,8 +940,8 @@ class ASTUnaryOpExpr(ASTBase): def __unicode__(self): return text_type(self.op) + text_type(self.expr) - def get_id_v2(self): - return _id_operator_unary_v2[self.op] + self.expr.get_id_v2() + def get_id(self, version): + return _id_operator_unary_v2[self.op] + self.expr.get_id(version) def describe_signature(self, signode, mode, env, symbol): signode.append(nodes.Text(self.op)) @@ -959,10 +963,10 @@ class ASTPostfixCallExpr(ASTBase): res.append(')') return u''.join(res) - def get_id_v2(self, idPrefix): + def get_id(self, idPrefix, version): res = ['cl', idPrefix] for e in self.exprs: - res.append(e.get_id_v2()) + res.append(e.get_id(version)) res.append('E') return u''.join(res) @@ -984,8 +988,8 @@ class ASTPostfixArray(ASTBase): def __unicode__(self): return u'[' + text_type(self.expr) + ']' - def get_id_v2(self, idPrefix): - return 'ix' + idPrefix + self.expr.get_id_v2() + def get_id(self, idPrefix, version): + return 'ix' + idPrefix + self.expr.get_id(version) def describe_signature(self, signode, mode, env, symbol): signode.append(nodes.Text('[')) @@ -1057,10 +1061,10 @@ class ASTPostfixExpr(ASTBase): res.append(text_type(p)) return u''.join(res) - def get_id_v2(self): - id = self.prefix.get_id_v2() + def get_id(self, version): + id = self.prefix.get_id(version) for p in self.postFixes: - id = p.get_id_v2(id) + id = p.get_id(id, version) return id def describe_signature(self, signode, mode, env, symbol): @@ -1591,7 +1595,7 @@ class ASTTemplateArgConstant(ASTBase): return text_type(self).replace(u' ', u'-') if version == 2: return u'X' + text_type(self) + u'E' - return u'X' + self.value.get_id_v2() + u'E' + return u'X' + self.value.get_id(version) + u'E' def describe_signature(self, signode, mode, env, symbol): # type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None @@ -2157,7 +2161,7 @@ class ASTArray(ASTBase): else: return u'A_' if self.size: - return u'A' + self.size.get_id_v2() + u'_' + return u'A' + self.size.get_id(version) + u'_' else: return u'A_' diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index f8ebdac7d..7c471d61e 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -184,7 +184,7 @@ def test_expressions(): exprCheck('A < 42', 'lt1AL42E') check('function', 'template<> void f(A &v)', {2:"IE1fR1AI1BX2EE", 3:"IE1fR1AI1BXL2EEE"}) - exprCheck('A<1>::value', {2:'N1AIXL1EEE5valueE'}) + exprCheck('A<1>::value', 'N1AIXL1EEE5valueE') check('class', "template A", {2:"I_iE1A"}) check('enumerator', 'A = std::numeric_limits::max()', {2:"1A"}) @@ -293,11 +293,13 @@ def test_function_definitions(): check('function', 'void operator()(const boost::array &v) const', {1:"call-operator__boost::array:VertexID.2:CRC", - 2:"NKclERKN5boost5arrayI8VertexIDX2EEE"}) + 2:"NKclERKN5boost5arrayI8VertexIDX2EEE", + 3:"NKclERKN5boost5arrayI8VertexIDXL2EEEE"}) check('function', 'void operator()(const boost::array &v) const', {1:'call-operator__boost::array:VertexID.2."foo,--bar":CRC', - 2:'NKclERKN5boost5arrayI8VertexIDX2EX"foo, bar"EEE'}) + 2:'NKclERKN5boost5arrayI8VertexIDX2EX"foo, bar"EEE', + 3:'NKclERKN5boost5arrayI8VertexIDXL2EEXLA9_KcEEEE'}) check('function', 'MyClass::MyClass(MyClass::MyClass&&)', {1:"MyClass::MyClass__MyClass::MyClassRR", 2:"N7MyClass7MyClassERRN7MyClass7MyClassE"}) @@ -340,7 +342,8 @@ def test_function_definitions(): x = 'std::vector> &module::test(register int ' \ 'foo, bar[n], std::string baz = "foobar, blah, bleh") const = 0' check('function', x, {1:"module::test__i.barA.ssC", - 2:"NK6module4testEiAn_3barNSt6stringE"}) + 2:"NK6module4testEiAn_3barNSt6stringE", + 3:"NK6module4testEiA1n_3barNSt6stringE"}) check('function', 'int foo(Foo f = Foo(double(), std::make_pair(int(2), double(3.4))))', {1:"foo__Foo", 2:"3foo3Foo"}) @@ -358,8 +361,8 @@ def test_function_definitions(): {1:"result__i.std::error_categoryCR", 2:"6resultiRNSt14error_categoryE"}) check("function", "int *f()", {1:"f", 2:"1fv"}) # tests derived from issue #1753 (skip to keep sanity) - check("function", "f(int (&array)[10])", {2:"1fRA10_i"}) - check("function", "void f(int (&array)[10])", {2:"1fRA10_i"}) + check("function", "f(int (&array)[10])", {2:"1fRA10_i", 3:"1fRAL10E_i"}) + check("function", "void f(int (&array)[10])", {2:"1fRA10_i", 3:"1fRAL10E_i"}) check("function", "void f(float *q(double))", {2:"1fFPfdE"}) check("function", "void f(float *(*q)(double))", {2:"1fPFPfdE"}) check("function", "void f(float (*q)(double))", {2:"1fPFfdE"}) @@ -539,7 +542,8 @@ def test_template_args(): check('function', "template " "void allow(F *f, typename func::type tt)", - {2:"I0E5allowP1FN4funcI1F1BXG != 1EE4typeE"}) + {2:"I0E5allowP1FN4funcI1F1BXG != 1EE4typeE", + 3:"I0E5allowP1FN4funcI1F1BXne1GL1EEE4typeE"}) # from #3542 check('type', "template " "enable_if_not_array_t = std::enable_if_t::value, int>", From d0fc4f80f10fa768c7c32d6bb2c344ff83fe5251 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Thu, 23 Mar 2017 20:09:55 +0900 Subject: [PATCH 38/44] C++, add expr role --- sphinx/domains/cpp.py | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index ce0b589b8..89da9d2ce 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -18,6 +18,7 @@ from docutils import nodes from docutils.parsers.rst import Directive, directives from sphinx import addnodes +from sphinx.environment import NoUri from sphinx.roles import XRefRole from sphinx.locale import l_, _ from sphinx.domains import Domain, ObjType @@ -27,6 +28,7 @@ from sphinx.util.nodes import make_refnode from sphinx.util.pycompat import UnicodeMixin from sphinx.util.docfields import Field, GroupedField + if False: # For type annotation from typing import Any, Callable, Dict, Iterator, List, Match, Pattern, Tuple, Union # NOQA @@ -1108,11 +1110,10 @@ class ASTIdentifier(ASTBase): if mode == 'markType': targetText = prefix + self.identifier pnode = addnodes.pending_xref('', refdomain='cpp', - reftype='typeOrConcept', + reftype='identifier', reftarget=targetText, modname=None, classname=None) key = symbol.get_lookup_key() - assert key pnode['cpp:parent_key'] = key pnode += nodes.Text(self.identifier) signode += pnode @@ -3159,9 +3160,6 @@ class Symbol(object): def get_lookup_key(self): # type: () -> List[Tuple[ASTNestedNameElement, Any]] - if not self.parent: - # specialise for the root - return None symbols = [] s = self while s.parent: @@ -5085,6 +5083,26 @@ class DefinitionParser(object): res.objectType = 'xref' # type: ignore return res + def parse_expression(self): + pos = self.pos + try: + expr = self._parse_expression(False) + self.skip_ws() + self.assert_end() + except DefinitionError as exExpr: + self.pos = pos + try: + expr = self._parse_type(False) + self.skip_ws() + self.assert_end() + except DefinitionError as exType: + header = "Error when parsing (type) expression." + errs = [] + errs.append((exExpr, "If expression:")) + errs.append((exType, "If type:")) + raise self._make_multi_error(errs, header) + return expr + def _make_phony_error_name(): # type: () -> ASTNestedName @@ -5480,6 +5498,7 @@ class CPPExprRole(object): class Warner(object): def warn(self, msg): inliner.reporter.warning(msg, line=lineno) + env = inliner.document.settings.env parser = DefinitionParser(text, Warner(), env.config) try: @@ -5534,7 +5553,8 @@ class CPPDomain(Domain): 'type': CPPXRefRole(), 'concept': CPPXRefRole(), 'enum': CPPXRefRole(), - 'enumerator': CPPXRefRole() + 'enumerator': CPPXRefRole(), + 'expr': CPPExprRole() } initial_data = { 'root_symbol': Symbol(None, None, None, None, None, None), @@ -5627,6 +5647,9 @@ class CPPDomain(Domain): templateShorthand=True, matchSelf=True) if s is None or s.declaration is None: + txtName = text_type(name) + if txtName.startswith('std::') or txtName == 'std': + raise NoUri() return None, None if typ.startswith('cpp:'): @@ -5636,7 +5659,7 @@ class CPPDomain(Domain): declTyp = s.declaration.objectType def checkType(): - if typ == 'any': + if typ == 'any' or typ == 'identifier': return True if declTyp == 'templateParam': return True From d4d0ea68fe78e7b5d5a44439152d244d27b6a53c Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Thu, 4 May 2017 13:34:00 +0900 Subject: [PATCH 39/44] C++, update docs and changelog --- CHANGES | 2 ++ doc/domains.rst | 54 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 2dcefca01..9030eb447 100644 --- a/CHANGES +++ b/CHANGES @@ -11,6 +11,8 @@ Features added -------------- - C++, handle ``decltype(auto)``. +- #2406: C++, add proper parsing of expressions, including linking of identifiers. +- C++, add a ``cpp:expr`` role for inserting inline C++ expressions or types. Features removed ---------------- diff --git a/doc/domains.rst b/doc/domains.rst index 9b2743645..ce6ef9e70 100644 --- a/doc/domains.rst +++ b/doc/domains.rst @@ -563,7 +563,7 @@ a visibility statement (``public``, ``private`` or ``protected``). .. cpp:class:: OuterScope::MyClass : public MyBase, MyOtherBase - A template class can be declared:: + A class template can be declared:: .. cpp:class:: template std::array @@ -728,6 +728,17 @@ a visibility statement (``public``, ``private`` or ``protected``). Proxy to an element of a notional sequence that can be compared, indirected, or incremented. + **Notation** + + .. cpp:var:: It r + + An lvalue. + + **Valid Expressions** + + - :cpp:expr:`*r`, when :cpp:expr:`r` is dereferenceable. + - :cpp:expr:`++r`, with return type :cpp:expr:`It&`, when :cpp:expr:`r` is incrementable. + .. cpp:concept:: template std::Container() Holder of elements, to which it can provide access via @@ -740,6 +751,17 @@ a visibility statement (``public``, ``private`` or ``protected``). Proxy to an element of a notional sequence that can be compared, indirected, or incremented. + **Notation** + + .. cpp:var:: It r + + An lvalue. + + **Valid Expressions** + + - :cpp:expr:`*r`, when :cpp:expr:`r` is dereferenceable. + - :cpp:expr:`++r`, with return type :cpp:expr:`It&`, when :cpp:expr:`r` is incrementable. + .. cpp:concept:: template std::Container() Holder of elements, to which it can provide access via @@ -807,6 +829,30 @@ compatibility. E.g., ``Iterator{A, B, C}`` will be accepted as an introduction even though it would not be valid C++. +Inline Expressions and Tpes +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. rst:role:: cpp:expr + + A role for inserting a C++ expression or type as inline text. + For example:: + + .. cpp:var:: int a = 42 + + .. cpp:function:: int f(int i) + + An expression: :cpp:expr:`a * f(a)`. + A type: :cpp:expr:`const MySortedContainer&`. + + will be rendered as follows: + + .. cpp:var:: int a = 42 + + .. cpp:function:: int f(int i) + + An expression: :cpp:expr:`a * f(a)`. + A type: :cpp:expr:`const MySortedContainer&`. + Namespacing ~~~~~~~~~~~~~~~~~ @@ -842,7 +888,7 @@ directive. .. cpp:function:: std::size_t size() const - declares ``size`` as a member function of the template class ``std::vector``. + declares ``size`` as a member function of the class template ``std::vector``. Equivalently this could have been declared using:: .. cpp:class:: template \ @@ -922,7 +968,7 @@ These roles link to the given declaration types: .. 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 + linking to class templates, if nothing follows the closing angle bracket, i.e. if the link looks like this: ``:cpp:class:`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, @@ -961,7 +1007,7 @@ In general the reference must include the template paraemter declarations, e.g., Currently the lookup only succeed if the template parameter identifiers are equal strings. That is, ``template\ Wrapper::Outer`` will not work. -The inner template class can not be directly referenced, unless the current namespace +The inner class template 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. From b55526f4e87ecfdd5c43cbe4e0dab847ebb0c7c9 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Thu, 4 May 2017 13:43:18 +0900 Subject: [PATCH 40/44] C++, remove fallback expression parser Fixes sphinx-doc/sphinx#2406 --- sphinx/domains/cpp.py | 65 +++++-------------------------------------- 1 file changed, 7 insertions(+), 58 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 89da9d2ce..863433cbc 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -4086,44 +4086,6 @@ class DefinitionParser(object): # TODO: actually parse the second production return self._parse_assignment_expression(inTemplate=inTemplate) - def _parse_expression_fallback(self, end, parser): - # type: (List[unicode]) -> unicode - # Stupidly "parse" an expression. - # 'end' should be a list of characters which ends the expression. - - # first try to use the provided parser - prevPos = self.pos - try: - return parser() - except DefinitionError as e: - raise - self.warn("Parsing of expression failed. Using fallback parser." - " Error was:\n%s" % e.description) - self.pos = prevPos - # and then the fallback scanning - assert end is not None - self.skip_ws() - startPos = self.pos - if self.match(_string_re): - value = self.matched_text - else: - # TODO: add handling of more bracket-like things, and quote handling - brackets = {'(': ')', '[': ']', '<': '>'} # type: Dict[unicode, unicode] - symbols = [] # type: List[unicode] - while not self.eof: - if (len(symbols) == 0 and self.current_char in end): - break - if self.current_char in brackets.keys(): - symbols.append(brackets[self.current_char]) - elif len(symbols) > 0 and self.current_char == symbols[-1]: - symbols.pop() - self.pos += 1 - if len(end) > 0 and self.eof: - self.fail("Could not find end of expression starting at %d." - % startPos) - value = self.definition[startPos:self.pos].strip() - return value.strip() - def _parse_operator(self): # type: () -> Any self.skip_ws() @@ -4182,9 +4144,7 @@ class DefinitionParser(object): prevErrors.append((e, "If type argument")) self.pos = pos try: - def parser(): - return self._parse_constant_expression(inTemplate=True) - value = self._parse_expression_fallback([',', '>'], parser) + value = self._parse_constant_expression(inTemplate=True) self.skip_ws() if self.skip_string('>'): parsedEnd = True @@ -4526,10 +4486,7 @@ class DefinitionParser(object): if self.skip_string(']'): arrayOps.append(ASTArray(None)) continue - - def parser(): - return self._parse_expression(inTemplate=False) - value = self._parse_expression_fallback([']'], parser) + value = self._parse_expression(inTemplate=False) if not self.skip_string(']'): self.fail("Expected ']' in end of array operator.") arrayOps.append(ASTArray(value)) @@ -4649,17 +4606,11 @@ class DefinitionParser(object): return None else: if outer == 'member': - def parser(): - return self._parse_assignment_expression(inTemplate=False) - value = self._parse_expression_fallback([], parser) + value = self._parse_assignment_expression(inTemplate=False) elif outer == 'templateParam': - def parser(): - return self._parse_assignment_expression(inTemplate=True) - value = self._parse_expression_fallback([',', '>'], parser) + value = self._parse_assignment_expression(inTemplate=True) elif outer is None: # function parameter - def parser(): - return self._parse_assignment_expression(inTemplate=False) - value = self._parse_expression_fallback([',', ')'], parser) + value = self._parse_assignment_expression(inTemplate=False) else: self.fail("Internal error, initializer for outer '%s' not " "implemented." % outer) @@ -4833,10 +4784,8 @@ class DefinitionParser(object): init = None if self.skip_string('='): self.skip_ws() - - def parser(): - return self._parse_constant_expression(inTemplate=False) - init = ASTInitializer(self._parse_expression_fallback([], parser)) + initVal = self._parse_constant_expression(inTemplate=False) + init = ASTInitializer(initVal) return ASTEnumerator(name, init) def _parse_template_parameter_list(self): From 139e09d12023a255b206c1158487d215217be920 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Thu, 4 May 2017 15:57:18 +0900 Subject: [PATCH 41/44] C++, fix type check errors --- sphinx/domains/cpp.py | 60 +++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 863433cbc..70a806ae5 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -588,11 +588,6 @@ class ASTBase(UnicodeMixin): """Clone a definition expression node.""" return deepcopy(self) - def get_id(self, version): - # type: (int) -> unicode - """Return the id for the node.""" - raise NotImplementedError(repr(self)) - def get_name(self): # type: () -> unicode """Return the name. @@ -805,19 +800,19 @@ class ASTFoldExpr(ASTBase): self.rightExpr = rightExpr def __unicode__(self): - res = ['('] + res = [u'('] if self.leftExpr: res.append(text_type(self.leftExpr)) - res.append(' ') + res.append(u' ') res.append(text_type(self.op)) - res.append(' ') - res.append('...') + res.append(u' ') + res.append(u'...') if self.rightExpr: - res.append(' ') + res.append(u' ') res.append(text_type(self.op)) - res.append(' ') + res.append(u' ') res.append(text_type(self.rightExpr)) - res.append(')') + res.append(u')') return u''.join(res) def get_id(self, version): @@ -918,9 +913,9 @@ class ASTCastExpr(ASTBase): self.expr = expr def __unicode__(self): - res = ['('] + res = [u'('] res.append(text_type(self.typ)) - res.append(')') + res.append(u')') res.append(text_type(self.expr)) return u''.join(res) @@ -955,14 +950,14 @@ class ASTPostfixCallExpr(ASTBase): self.exprs = exprs def __unicode__(self): - res = ['('] + res = [u'('] first = True for e in self.exprs: if not first: - res.append(', ') + res.append(u', ') first = False res.append(text_type(e)) - res.append(')') + res.append(u')') return u''.join(res) def get_id(self, idPrefix, version): @@ -1200,7 +1195,7 @@ class ASTTemplateParamType(ASTBase): return self.data.get_identifier() def get_id(self, version, objectType=None, symbol=None): - # type: (unicode, Symbol) -> unicode + # type: (int, unicode, Symbol) -> unicode # this is not part of the normal name mangling in C++ assert version >= 2 if symbol: @@ -2142,7 +2137,6 @@ class ASTDeclSpecs(ASTBase): class ASTArray(ASTBase): def __init__(self, size): - # type: (unicode) -> None self.size = size def __unicode__(self): @@ -2218,14 +2212,15 @@ class ASTDeclaratorPtr(ASTBase): def get_ptr_suffix_id(self, version): # type: (int) -> unicode if version == 1: - res = 'P' + res = ['P'] if self.volatile: - res += 'V' + res.append('V') if self.const: - res += 'C' - return res + self.next.get_ptr_suffix_id(version) + res.append('C') + res.append(self.next.get_ptr_suffix_id(version)) + return u''.join(res) - res = [self.next.get_ptr_suffix_id(version)] # type: List[unicode] + res = [self.next.get_ptr_suffix_id(version)] res.append('P') if self.volatile: res.append('V') @@ -2612,7 +2607,6 @@ class ASTDeclaratorNameParamQual(ASTBase): class ASTInitializer(ASTBase): def __init__(self, value): - # type: (unicode) -> None self.value = value def __unicode__(self): @@ -2620,7 +2614,7 @@ class ASTInitializer(ASTBase): return u''.join([' = ', text_type(self.value)]) def describe_signature(self, signode, mode, env, symbol): - # type: (addnodes.desc_signature, unicode) -> None + # type: (addnodes.desc_signature, unicode, BuildEnvironment, Symbol) -> None _verify_description_mode(mode) signode.append(nodes.Text(' = ')) self.value.describe_signature(signode, 'markType', env, symbol) @@ -2989,7 +2983,7 @@ class ASTDeclaration(ASTBase): return self.declaration.name def get_id(self, version, prefixed=True): - # type: (int) -> unicode + # type: (int, bool) -> unicode if version == 1: if self.templatePrefix: raise NoOldIdError() @@ -3907,7 +3901,7 @@ class DefinitionParser(object): self.pos -= 2 else: name = self._parse_nested_name() - postFixes.append(ASTPostfixMember(name)) + postFixes.append(ASTPostfixMember(name)) # type: ignore continue if self.skip_string('->'): if self.skip_string('*'): @@ -3915,13 +3909,13 @@ class DefinitionParser(object): self.pos -= 3 else: name = self._parse_nested_name() - postFixes.append(ASTPostfixMemberOfPointer(name)) + postFixes.append(ASTPostfixMemberOfPointer(name)) # type: ignore continue if self.skip_string('++'): - postFixes.append(ASTPostfixInc()) + postFixes.append(ASTPostfixInc()) # type: ignore continue if self.skip_string('--'): - postFixes.append(ASTPostfixDec()) + postFixes.append(ASTPostfixDec()) # type: ignore continue if self.skip_string_and_ws('('): # TODO: handled braced init @@ -3937,7 +3931,7 @@ class DefinitionParser(object): break if not self.skip_string(','): self.fail("Error in cast or call, expected ',' or ')'.") - postFixes.append(ASTPostfixCallExpr(exprs)) + postFixes.append(ASTPostfixCallExpr(exprs)) # type: ignore continue break if len(postFixes) == 0: @@ -5493,7 +5487,7 @@ class CPPDomain(Domain): 'namespace-push': CPPNamespacePushObject, 'namespace-pop': CPPNamespacePopObject } - roles = { + roles = { # type: ignore 'any': CPPXRefRole(), 'class': CPPXRefRole(), 'func': CPPXRefRole(fix_parens=True), From 321a5e7a5e757b7138e4dfb9aa0e163afe3916ea Mon Sep 17 00:00:00 2001 From: jfbu Date: Thu, 4 May 2017 09:36:04 +0200 Subject: [PATCH 42/44] Fix #3702: abstract LaTeX styling of figure legends into sphinxlegend --- doc/latex.rst | 9 ++++++++- sphinx/texinputs/sphinx.sty | 2 ++ sphinx/writers/latex.py | 4 ++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/doc/latex.rst b/doc/latex.rst index ebc14765e..b027528fa 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -438,7 +438,14 @@ Let us now list some macros from the package file the new macros are wrappers of the formerly hard-coded ``\texttt``, ``\emph``, ... The default definitions can be found in :file:`sphinx.sty`. -- paragraph level environments: for each admonition type ````, the +- a :dudir:`figure` may have an optional legend with arbitrary body + elements: they are rendered in a ``sphinxlegend`` environment. The default + definition issues ``\small``, and ends with ``\par``. + + .. versionadded:: 1.5.6 + formerly, the ``\small`` was hardcoded in LaTeX writer and the ending + ``\par`` was lacking. +- for each admonition type ````, the used environment is named ``sphinx``. They may be ``\renewenvironment`` 'd individually, and must then be defined with one argument (it is the heading of the notice, for example ``Warning:`` for :dudir:`warning` directive, if diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 15f9910ff..7a142c116 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -1383,6 +1383,8 @@ \protected\def\sphinxstyleliteralstrong {\sphinxbfcode} \protected\def\sphinxstyleabbreviation {\textsc} \protected\def\sphinxstyleliteralintitle {\sphinxcode} +% figure legend comes after caption and may contain arbitrary body elements +\newenvironment{sphinxlegend}{\par\small}{\par} % LaTeX writer uses macros to hide double quotes from \sphinxcode's \@noligs \protected\def\sphinxquotedblleft{``} diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index be0771c09..44155da71 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -1599,10 +1599,10 @@ class LaTeXTranslator(nodes.NodeVisitor): self.unrestrict_footnote(node) def visit_legend(self, node): - self.body.append('{\\small ') + self.body.append('\n\\begin{sphinxlegend}') def depart_legend(self, node): - self.body.append('}') + self.body.append('\\end{sphinxlegend}\n') def visit_admonition(self, node): self.body.append('\n\\begin{sphinxadmonition}{note}') From da8007c3f481615085157a18c3fe8932366913e6 Mon Sep 17 00:00:00 2001 From: jfbu Date: Fri, 5 May 2017 13:15:41 +0200 Subject: [PATCH 43/44] Update CHANGES for PR #3703 --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index 7c0c7e564..e5fc48393 100644 --- a/CHANGES +++ b/CHANGES @@ -20,6 +20,7 @@ Bugs fixed * #3588: No compact (p tag) html output in the i18n document build even when :confval:`html_compact_lists` is True. * #3685: AttributeError when using 3rd party domains +* #3702: LaTeX writer styles figure legends with a hard-coded ``\small`` Testing -------- From 698d8d440eebdbea04f769e625dcecf834778ab2 Mon Sep 17 00:00:00 2001 From: jfbu Date: Fri, 5 May 2017 18:18:51 +0200 Subject: [PATCH 44/44] Change slightly console output of LaTeX deprecation macro --- sphinx/texinputs/sphinx.sty | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 7a142c116..b7a514a5d 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -25,15 +25,16 @@ %% for deprecation warnings \newcommand\sphinxdeprecationwarning[4]{% #1 the deprecated macro or name, -% #2 = version when deprecated, #3 = version when removed, #4 = message +% #2 = when deprecated, #3 = when removed, #4 = additional info \edef\spx@tempa{\detokenize{#1}}% \spx@ifundefined{sphinx_depr_\spx@tempa}{% \global\expandafter\let\csname sphinx_depr_\spx@tempa\endcsname\spx@tempa \expandafter\AtEndDocument\expandafter{\expandafter\let\expandafter \sphinxdeprecatedmacro\csname sphinx_depr_\spx@tempa\endcsname \PackageWarningNoLine{sphinx}{^^J**** SPHINX DEPRECATION WARNING:^^J - \sphinxdeprecatedmacro\space will be (or has been) - deprecated at Sphinx #2^^J and will be removed at Sphinx #3.^^J + \sphinxdeprecatedmacro^^J + \@spaces- is deprecated at Sphinx #2^^J + \@spaces- and removed at Sphinx #3.^^J #4^^J****}}% }{% warning already emitted (at end of latex log), don't repeat }} @@ -1361,8 +1362,10 @@ Anyhow, Sphinx mark-up uses only \string\sphinx\@tempa.}% % and also at end of log for better visibility \expandafter\sphinxdeprecationwarning\expandafter{\csname\@tempa\endcsname}{1.6}{1.7} - {\sphinxdeprecatedmacro\space already existed at Sphinx loading time! Not redefined!^^J - Sphinx mark-up uses only \string\sphinx\expandafter\@gobble\sphinxdeprecatedmacro.}% + {\sphinxdeprecatedmacro already existed at Sphinx loading time! Not redefined!^^J + Sphinx mark-up uses only \string\sphinx\expandafter\@gobble\sphinxdeprecatedmacro.^^J + Note: if this warning is about macro \string\strong, it presumably results^^J + from fontspec 2.6 having defined it prior to Sphinx. No need for alarm!}% }% \fi \fi