summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml3
-rw-r--r--CHANGES12
-rw-r--r--EXAMPLES1
-rw-r--r--doc/_static/favicon.svg8
-rw-r--r--doc/conf.py1
-rw-r--r--doc/glossary.rst4
-rw-r--r--doc/usage/configuration.rst5
-rw-r--r--setup.py2
-rw-r--r--sphinx/builders/_epub_base.py7
-rw-r--r--sphinx/builders/linkcheck.py5
-rw-r--r--sphinx/domains/c.py64
-rw-r--r--sphinx/domains/cpp.py27
-rw-r--r--sphinx/ext/autodoc/__init__.py2
-rw-r--r--sphinx/ext/autodoc/importer.py5
-rw-r--r--sphinx/search/__init__.py10
-rw-r--r--sphinx/themes/basic/static/basic.css_t1
-rw-r--r--sphinx/util/typing.py4
-rw-r--r--sphinx/writers/texinfo.py2
-rw-r--r--tests/roots/test-domain-c/function_param_target.rst5
-rw-r--r--tests/roots/test-ext-autodoc/target/slots.py4
-rw-r--r--tests/roots/test-root/conf.py2
-rw-r--r--tests/roots/test-root/markup.txt4
-rw-r--r--tests/test_build_html.py2
-rw-r--r--tests/test_domain_c.py52
-rw-r--r--tests/test_domain_cpp.py15
-rw-r--r--tests/test_domain_py.py10
-rw-r--r--tests/test_ext_autodoc.py8
27 files changed, 203 insertions, 62 deletions
diff --git a/.travis.yml b/.travis.yml
index d73be03ec..47a8e7c7a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -24,9 +24,6 @@ jobs:
env:
- TOXENV=du15
- PYTEST_ADDOPTS="--cov ./ --cov-append --cov-config setup.cfg"
- - python: 'nightly'
- env:
- - TOXENV=du16
- language: node_js
node_js: '10.7'
diff --git a/CHANGES b/CHANGES
index dd4a56fba..76275d246 100644
--- a/CHANGES
+++ b/CHANGES
@@ -26,6 +26,8 @@ Features added
just before writing .tex file
* #7996: manpage: Add :confval:`man_make_section_directory` to make a section
directory on build man page
+* #8289: epub: Allow to suppress "duplicated ToC entry found" warnings from epub
+ builder using :confval:`suppress_warnings`.
Bugs fixed
----------
@@ -43,6 +45,7 @@ Bugs fixed
* #7964: autodoc: Tuple in default value is wrongly rendered
* #8200: autodoc: type aliases break type formatting of autoattribute
* #7786: autodoc: can't detect overloaded methods defined in other file
+* #8294: autodoc: single-string __slots__ is not handled correctly
* #8192: napoleon: description is disappeared when it contains inline literals
* #8142: napoleon: Potential of regex denial of service in google style docs
* #8169: LaTeX: pxjahyper loaded even when latex_engine is not platex
@@ -58,7 +61,11 @@ Bugs fixed
* #8239: Failed to refer a token in productionlist if it is indented
* #8268: linkcheck: Report HTTP errors when ``linkcheck_anchors`` is ``True``
* #8245: linkcheck: take source directory into account for local files
+* #8321: linkcheck: ``tel:`` schema hyperlinks are detected as errors
+* #8323: linkcheck: An exit status is incorrect when links having unsupported
+ schema found
* #6914: figure numbers are unexpectedly assigned to uncaptioned items
+* #8320: make "inline" line numbers un-selectable
Testing
--------
@@ -85,6 +92,11 @@ Bugs fixed
* #8188: C, add missing items to internal object types dictionary,
e.g., preventing intersphinx from resolving them.
+* C, fix anon objects in intersphinx.
+* #8270, C++, properly reject functions as duplicate declarations if a
+ non-function declaration of the same name already exists.
+* C, fix references to function parameters.
+ Link to the function instead of a non-existing anchor.
Testing
diff --git a/EXAMPLES b/EXAMPLES
index 19f23172f..74fa36510 100644
--- a/EXAMPLES
+++ b/EXAMPLES
@@ -230,6 +230,7 @@ Documentation using sphinx_rtd_theme
* `MyHDL <http://docs.myhdl.org/>`__
* `Nextflow <https://www.nextflow.io/docs/latest/index.html>`__
* `NICOS <https://forge.frm2.tum.de/nicos/doc/nicos-master/>`__ (customized)
+* `OpenFAST <https://openfast.readthedocs.io/>`__
* `Pelican <http://docs.getpelican.com/>`__
* `picamera <https://picamera.readthedocs.io/>`__
* `Pillow <https://pillow.readthedocs.io/>`__
diff --git a/doc/_static/favicon.svg b/doc/_static/favicon.svg
new file mode 100644
index 000000000..c3e1acd35
--- /dev/null
+++ b/doc/_static/favicon.svg
@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
+ <style>
+ @media (prefers-color-scheme: dark) {
+ svg { fill: white; }
+ }
+ </style>
+ <path d="m 67.780707,71.526216 c 0,-2.720856 0.735772,-7.633735 1.635035,-10.917507 2.076574,-7.582764 3.222746,-16.97568 2.071477,-16.97568 -0.485619,0 -3.994408,3.173002 -7.797313,7.051115 -14.448869,14.734603 -29.952812,23.068339 -42.915946,23.068339 -7.400211,0 -12.4298817,-1.871115 -17.2867007,-6.430912 -2.94436186,-2.764297 -3.47532146,-4.129685 -3.47532146,-8.936928 0,-4.94488 0.4862322,-6.108589 3.78321146,-9.054437 2.987989,-2.669773 4.875111,-3.380296 8.9779137,-3.380296 3.163221,0.711278 5.032659,0.664017 6.063532,1.917191 1.045041,1.231842 1.406892,5.262673 0.143323,7.623675 -0.674746,1.260763 -2.435471,2.043539 -4.5966,2.043539 -2.040303,0 -3.203991,-0.483702 -2.786976,-1.15844 1.31395,-2.126021 -0.560952,-3.566616 -2.9664067,-2.279256 -2.907025,1.555792 -2.957418,7.069066 -0.08839,9.665535 4.0345357,3.651203 15.1912207,5.023925 21.9019857,2.694828 7.250749,-2.516503 16.739014,-8.578986 24.30831,-15.531674 l 6.657407,-6.115083 -8.688303,-0.05007 C 43.622519,44.707714 37.702703,43.621524 18.54695,38.489741 12.175528,36.782852 6.0502733,35.306342 4.9352743,35.208608 3.6710803,35.097791 2.841723,34.067882 2.9080043,32.476074 3.0199286,29.788108 4.4800823,27.78768 6.2067673,27.033038 7.2437505,26.579828 14.43583,25.894406 22.0605,23.866486 c 29.699148,-7.899023 31.502043,-6.781254 51.28707,-1.772167 6.461504,1.635896 13.942408,3.414988 17.256961,3.474566 5.106245,0.09178 6.211825,0.514653 7.240255,2.76932 0.66758,1.46355 1.21378,2.858905 1.21378,3.10079 0,0.241884 -2.89333,1.764397 -6.429613,3.383363 -12.984983,5.944723 -17.083271,9.093943 -12.855172,15.130399 1.753219,2.503069 1.718037,2.768923 -0.57922,4.37799 -1.345193,0.942203 -2.457238,2.856456 -2.471232,4.253898 -0.03777,3.776976 -2.424786,11.884847 -5.893734,15.080164 l -3.048923,2.808424 z m 6.632814,-34.658372 c 5.169656,-1.440693 8.302047,-3.07045 14.72913,-6.500861 -5.292267,-1.548658 -18.570782,-3.724097 -18.570782,-3.724097 -9.796513,-1.964547 -8.76916,-1.865132 -9.21348,0.29669 -0.176673,0.859598 -0.702644,2.763948 -1.872329,4.596663 -2.251474,3.527711 -10.489307,4.271075 -15.214327,2.009703 -1.482367,-0.709454 -2.971272,-3.416276 -2.950606,-5.336922 0.02911,-2.705486 -1.505386,-3.336055 -2.486689,-2.975309 -0.796428,0.292781 -3.384665,0.330004 -9.071284,1.864262 -18.784765,5.068157 -21.3552119,4.487473 -9.110967,6.223299 1.472409,0.208739 9.252992,2.381926 13.052028,3.39412 9.318588,2.482796 11.064717,2.665087 23.125496,2.414247 8.385835,-0.174409 11.891174,-0.675356 17.58381,-2.261795 z M 3.0589449,14.916483 C 3.2921927,12.514245 3.424378,11.992797 10.100599,10.647894 13.924923,9.8774962 23.355266,7.3808108 31.056903,5.0997052 c 17.703937,-5.2436279 22.73392,-5.2565016 41.092202,-0.105175 7.923233,2.2232606 16.798382,4.047803 19.72254,4.054541 4.567242,0.01054 6.941892,2.0284768 6.941892,2.0284768 2.101843,4.825342 1.718463,5.158474 -6.484103,5.158474 -5.714193,0 -10.641875,-0.963081 -18.245438,-3.565943 C 68.300078,10.69012 60.060462,8.8316882 55.557963,8.4915615 47.342337,7.8709375 47.353713,7.8687835 21.963188,14.855617 17.503192,16.082896 11.34213,17.454164 8.2719268,17.902883 l -5.5821654,0.81585 z" />
+</svg>
diff --git a/doc/conf.py b/doc/conf.py
index 74e5a8b80..8b242dc7e 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -28,6 +28,7 @@ html_sidebars = {'index': ['indexsidebar.html', 'searchbox.html']}
html_additional_pages = {'index': 'index.html'}
html_use_opensearch = 'https://www.sphinx-doc.org/en/master'
html_baseurl = 'https://www.sphinx-doc.org/en/master/'
+html_favicon = '_static/favicon.svg'
htmlhelp_basename = 'Sphinxdoc'
diff --git a/doc/glossary.rst b/doc/glossary.rst
index d3367e5df..87b7014b6 100644
--- a/doc/glossary.rst
+++ b/doc/glossary.rst
@@ -9,8 +9,8 @@ Glossary
A class (inheriting from :class:`~sphinx.builders.Builder`) that takes
parsed documents and performs an action on them. Normally, builders
translate the documents to an output format, but it is also possible to
- use the builder builders that e.g. check for broken links in the
- documentation, or build coverage information.
+ use builders that e.g. check for broken links in the documentation, or
+ build coverage information.
See :doc:`/usage/builders/index` for an overview over Sphinx's built-in
builders.
diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst
index 270fcbf33..4881b8629 100644
--- a/doc/usage/configuration.rst
+++ b/doc/usage/configuration.rst
@@ -316,6 +316,7 @@ General configuration
* ``toc.circular``
* ``toc.secnum``
* ``epub.unknown_project_files``
+ * ``epub.duplicated_toc_entry``
* ``autosectionlabel.*``
You can choose from these types.
@@ -340,6 +341,10 @@ General configuration
Added ``autosectionlabel.*``
+ .. versionchanged:: 3.3.0
+
+ Added ``epub.duplicated_toc_entry``
+
.. confval:: needs_sphinx
If set to a ``major.minor`` version string like ``'1.1'``, Sphinx will
diff --git a/setup.py b/setup.py
index a404f1fa5..8505d2679 100644
--- a/setup.py
+++ b/setup.py
@@ -44,7 +44,7 @@ extras_require = {
'lint': [
'flake8>=3.5.0',
'flake8-import-order',
- 'mypy>=0.780',
+ 'mypy>=0.790',
'docutils-stubs',
],
'test': [
diff --git a/sphinx/builders/_epub_base.py b/sphinx/builders/_epub_base.py
index 95f9ab8ed..b126182e9 100644
--- a/sphinx/builders/_epub_base.py
+++ b/sphinx/builders/_epub_base.py
@@ -208,7 +208,12 @@ class EpubBuilder(StandaloneHTMLBuilder):
appeared = set() # type: Set[str]
for node in nodes:
if node['refuri'] in appeared:
- logger.warning(__('duplicated ToC entry found: %s'), node['refuri'])
+ logger.warning(
+ __('duplicated ToC entry found: %s'),
+ node['refuri'],
+ type="epub",
+ subtype="duplicated_toc_entry",
+ )
else:
appeared.add(node['refuri'])
diff --git a/sphinx/builders/linkcheck.py b/sphinx/builders/linkcheck.py
index a9e6b05b0..7652761b5 100644
--- a/sphinx/builders/linkcheck.py
+++ b/sphinx/builders/linkcheck.py
@@ -106,8 +106,7 @@ class CheckExternalLinksBuilder(Builder):
self.rqueue = queue.Queue() # type: queue.Queue
self.workers = [] # type: List[threading.Thread]
for i in range(self.app.config.linkcheck_workers):
- thread = threading.Thread(target=self.check_thread)
- thread.setDaemon(True)
+ thread = threading.Thread(target=self.check_thread, daemon=True)
thread.start()
self.workers.append(thread)
@@ -213,7 +212,7 @@ class CheckExternalLinksBuilder(Builder):
def check(docname: str) -> Tuple[str, str, int]:
# check for various conditions without bothering the network
- if len(uri) == 0 or uri.startswith(('#', 'mailto:')):
+ if len(uri) == 0 or uri.startswith(('#', 'mailto:', 'tel:')):
return 'unchecked', '', 0
elif not uri.startswith(('http:', 'https:')):
if uri_re.match(uri):
diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py
index 304c871ea..fafe827af 100644
--- a/sphinx/domains/c.py
+++ b/sphinx/domains/c.py
@@ -10,9 +10,8 @@
import re
from typing import (
- Any, Callable, Dict, Generator, Iterator, List, Type, TypeVar, Tuple, Union
+ Any, Callable, cast, Dict, Generator, Iterator, List, Type, TypeVar, Tuple, Union
)
-from typing import cast
from docutils import nodes
from docutils.nodes import Element, Node, TextElement, system_message
@@ -47,6 +46,11 @@ from sphinx.util.nodes import make_refnode
logger = logging.getLogger(__name__)
T = TypeVar('T')
+DeclarationType = Union[
+ "ASTStruct", "ASTUnion", "ASTEnum", "ASTEnumerator",
+ "ASTType", "ASTTypeWithInit", "ASTMacro",
+]
+
# https://en.cppreference.com/w/c/keyword
_keywords = [
'auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do', 'double',
@@ -636,6 +640,10 @@ class ASTFunctionParameter(ASTBase):
self.arg = arg
self.ellipsis = ellipsis
+ def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
+ # the anchor will be our parent
+ return symbol.parent.declaration.get_id(version, prefixed=False)
+
def _stringify(self, transform: StringifyTransform) -> str:
if self.ellipsis:
return '...'
@@ -1149,6 +1157,9 @@ class ASTType(ASTBase):
def name(self) -> ASTNestedName:
return self.decl.name
+ def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
+ return symbol.get_full_nested_name().get_id(version)
+
@property
def function_params(self) -> List[ASTFunctionParameter]:
return self.decl.function_params
@@ -1191,6 +1202,9 @@ class ASTTypeWithInit(ASTBase):
def name(self) -> ASTNestedName:
return self.type.name
+ def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
+ return self.type.get_id(version, objectType, symbol)
+
def _stringify(self, transform: StringifyTransform) -> str:
res = []
res.append(transform(self.type))
@@ -1242,6 +1256,9 @@ class ASTMacro(ASTBase):
def name(self) -> ASTNestedName:
return self.ident
+ def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
+ return symbol.get_full_nested_name().get_id(version)
+
def _stringify(self, transform: StringifyTransform) -> str:
res = []
res.append(transform(self.ident))
@@ -1342,7 +1359,8 @@ class ASTEnumerator(ASTBase):
class ASTDeclaration(ASTBaseBase):
- def __init__(self, objectType: str, directiveType: str, declaration: Any,
+ def __init__(self, objectType: str, directiveType: str,
+ declaration: Union[DeclarationType, ASTFunctionParameter],
semicolon: bool = False) -> None:
self.objectType = objectType
self.directiveType = directiveType
@@ -1359,18 +1377,20 @@ class ASTDeclaration(ASTBaseBase):
@property
def name(self) -> ASTNestedName:
- return self.declaration.name
+ decl = cast(DeclarationType, self.declaration)
+ return decl.name
@property
def function_params(self) -> List[ASTFunctionParameter]:
if self.objectType != 'function':
return None
- return self.declaration.function_params
+ decl = cast(ASTType, self.declaration)
+ return decl.function_params
def get_id(self, version: int, prefixed: bool = True) -> str:
if self.objectType == 'enumerator' and self.enumeratorScopedSymbol:
return self.enumeratorScopedSymbol.declaration.get_id(version, prefixed)
- id_ = self.symbol.get_full_nested_name().get_id(version)
+ id_ = self.declaration.get_id(version, self.objectType, self.symbol)
if prefixed:
return _id_prefix[version] + id_
else:
@@ -1413,7 +1433,8 @@ class ASTDeclaration(ASTBaseBase):
elif self.objectType == 'enumerator':
mainDeclNode += addnodes.desc_annotation('enumerator ', 'enumerator ')
elif self.objectType == 'type':
- prefix = self.declaration.get_type_declaration_prefix()
+ decl = cast(ASTType, self.declaration)
+ prefix = decl.get_type_declaration_prefix()
prefix += ' '
mainDeclNode += addnodes.desc_annotation(prefix, prefix)
else:
@@ -2988,7 +3009,7 @@ class DefinitionParser(BaseParser):
def parse_pre_v3_type_definition(self) -> ASTDeclaration:
self.skip_ws()
- declaration = None # type: Any
+ declaration = None # type: DeclarationType
if self.skip_word('struct'):
typ = 'struct'
declaration = self._parse_struct()
@@ -3011,7 +3032,7 @@ class DefinitionParser(BaseParser):
'macro', 'struct', 'union', 'enum', 'enumerator', 'type'):
raise Exception('Internal error, unknown directiveType "%s".' % directiveType)
- declaration = None # type: Any
+ declaration = None # type: DeclarationType
if objectType == 'member':
declaration = self._parse_type_with_init(named=True, outer='member')
elif objectType == 'function':
@@ -3158,10 +3179,6 @@ class CObject(ObjectDescription):
self.state.document.note_explicit_target(signode)
- domain = cast(CDomain, self.env.get_domain('c'))
- if name not in domain.objects:
- domain.objects[name] = (domain.env.docname, newestId, self.objtype)
-
if 'noindexentry' not in self.options:
indexText = self.get_index_text(name)
self.indexnode['entries'].append(('single', indexText, newestId, '', None))
@@ -3681,10 +3698,6 @@ class CDomain(Domain):
'objects': {}, # fullname -> docname, node_id, objtype
} # type: Dict[str, Union[Symbol, Dict[str, Tuple[str, str, str]]]]
- @property
- def objects(self) -> Dict[str, Tuple[str, str, str]]:
- return self.data.setdefault('objects', {}) # fullname -> docname, node_id, objtype
-
def clear_doc(self, docname: str) -> None:
if Symbol.debug_show_tree:
print("clear_doc:", docname)
@@ -3700,9 +3713,6 @@ class CDomain(Domain):
print(self.data['root_symbol'].dump(1))
print("\tafter end")
print("clear_doc end:", docname)
- for fullname, (fn, _id, _l) in list(self.objects.items()):
- if fn == docname:
- del self.objects[fullname]
def process_doc(self, env: BuildEnvironment, docname: str,
document: nodes.document) -> None:
@@ -3788,8 +3798,18 @@ class CDomain(Domain):
return []
def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
- for refname, (docname, node_id, objtype) in list(self.objects.items()):
- yield (refname, refname, objtype, docname, node_id, 1)
+ rootSymbol = self.data['root_symbol']
+ for symbol in rootSymbol.get_all_symbols():
+ if symbol.declaration is None:
+ continue
+ assert symbol.docname
+ fullNestedName = symbol.get_full_nested_name()
+ name = str(fullNestedName).lstrip('.')
+ dispname = fullNestedName.get_display_string().lstrip('.')
+ objectType = symbol.declaration.objectType
+ docname = symbol.docname
+ newestId = symbol.declaration.get_newest_id()
+ yield (name, dispname, objectType, docname, newestId, 1)
def setup(app: Sphinx) -> Dict[str, Any]:
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py
index 7b10f8166..0e996532f 100644
--- a/sphinx/domains/cpp.py
+++ b/sphinx/domains/cpp.py
@@ -1836,7 +1836,7 @@ class ASTFunctionParameter(ASTBase):
# 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(version, prefixed=None)
+ return symbol.parent.declaration.get_id(version, prefixed=False)
# else, do the usual
if self.ellipsis:
return 'z'
@@ -4107,7 +4107,7 @@ class Symbol:
Symbol.debug_print("self:")
print(self.to_string(Symbol.debug_indent + 1), end="")
Symbol.debug_print("nestedName: ", nestedName)
- Symbol.debug_print("templateDecls: ", templateDecls)
+ Symbol.debug_print("templateDecls: ", ",".join(str(t) for t in templateDecls))
Symbol.debug_print("strictTemplateParamArgLists:", strictTemplateParamArgLists)
Symbol.debug_print("ancestorLookupType:", ancestorLookupType)
Symbol.debug_print("templateShorthand: ", templateShorthand)
@@ -4231,7 +4231,7 @@ class Symbol:
Symbol.debug_indent += 1
Symbol.debug_print("_add_symbols:")
Symbol.debug_indent += 1
- Symbol.debug_print("tdecls:", templateDecls)
+ Symbol.debug_print("tdecls:", ",".join(str(t) for t in templateDecls))
Symbol.debug_print("nn: ", nestedName)
Symbol.debug_print("decl: ", declaration)
Symbol.debug_print("doc: ", docname)
@@ -4360,6 +4360,11 @@ class Symbol:
if Symbol.debug_lookup:
Symbol.debug_print("candId:", candId)
for symbol in withDecl:
+ # but all existing must be functions as well,
+ # otherwise we declare it to be a duplicate
+ if symbol.declaration.objectType != 'function':
+ handleDuplicateDeclaration(symbol, candSymbol)
+ # (not reachable)
oldId = symbol.declaration.get_newest_id()
if Symbol.debug_lookup:
Symbol.debug_print("oldId: ", oldId)
@@ -4370,7 +4375,11 @@ class Symbol:
# if there is an empty symbol, fill that one
if len(noDecl) == 0:
if Symbol.debug_lookup:
- Symbol.debug_print("no match, no empty, candSybmol is not None?:", candSymbol is not None) # NOQA
+ Symbol.debug_print("no match, no empty")
+ if candSymbol is not None:
+ Symbol.debug_print("result is already created candSymbol")
+ else:
+ Symbol.debug_print("result is makeCandSymbol()")
Symbol.debug_indent -= 2
if candSymbol is not None:
return candSymbol
@@ -6814,10 +6823,12 @@ class CPPObject(ObjectDescription):
parentSymbol = env.temp_data['cpp:parent_symbol']
parentDecl = parentSymbol.declaration
if parentDecl is not None and parentDecl.objectType == 'function':
- logger.warning("C++ declarations inside functions are not supported." +
- " Parent function is " +
- str(parentSymbol.get_full_nested_name()),
- location=self.get_source_info())
+ msg = "C++ declarations inside functions are not supported." \
+ " Parent function: {}\nDirective name: {}\nDirective arg: {}"
+ logger.warning(msg.format(
+ str(parentSymbol.get_full_nested_name()),
+ self.name, self.arguments[0]
+ ), location=self.get_source_info())
name = _make_phony_error_name()
symbol = parentSymbol.add_name(name)
env.temp_data['cpp:last_symbol'] = symbol
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index b68fc73c9..978fd5df8 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -1764,7 +1764,7 @@ class TypeVarDocumenter(DataDocumenter):
@classmethod
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
) -> bool:
- return isinstance(member, TypeVar) and isattr # type: ignore
+ return isinstance(member, TypeVar) and isattr
def add_directive_header(self, sig: str) -> None:
self.options = Options(self.options)
diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py
index 133ce1439..52b07639a 100644
--- a/sphinx/ext/autodoc/importer.py
+++ b/sphinx/ext/autodoc/importer.py
@@ -206,7 +206,10 @@ def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
if isclass(subject) and getattr(subject, '__slots__', None) is not None:
from sphinx.ext.autodoc import SLOTSATTR
- for name in subject.__slots__:
+ slots = subject.__slots__
+ if isinstance(slots, str):
+ slots = [slots]
+ for name in slots:
members[name] = Attribute(name, True, SLOTSATTR)
# other members
diff --git a/sphinx/search/__init__.py b/sphinx/search/__init__.py
index 4534dd333..048b333d5 100644
--- a/sphinx/search/__init__.py
+++ b/sphinx/search/__init__.py
@@ -297,8 +297,8 @@ class IndexBuilder:
frozen.get('envversion') != self.env.version:
raise ValueError('old format')
index2fn = frozen['docnames']
- self._filenames = dict(zip(index2fn, frozen['filenames'])) # type: ignore
- self._titles = dict(zip(index2fn, frozen['titles'])) # type: ignore
+ self._filenames = dict(zip(index2fn, frozen['filenames']))
+ self._titles = dict(zip(index2fn, frozen['titles']))
def load_terms(mapping: Dict[str, Any]) -> Dict[str, Set[str]]:
rv = {}
@@ -359,13 +359,13 @@ class IndexBuilder:
def get_terms(self, fn2index: Dict) -> Tuple[Dict[str, List[str]], Dict[str, List[str]]]:
rvs = {}, {} # type: Tuple[Dict[str, List[str]], Dict[str, List[str]]]
for rv, mapping in zip(rvs, (self._mapping, self._title_mapping)):
- for k, v in mapping.items(): # type: ignore
+ for k, v in mapping.items():
if len(v) == 1:
fn, = v
if fn in fn2index:
- rv[k] = fn2index[fn] # type: ignore
+ rv[k] = fn2index[fn]
else:
- rv[k] = sorted([fn2index[fn] for fn in v if fn in fn2index]) # type: ignore # NOQA
+ rv[k] = sorted([fn2index[fn] for fn in v if fn in fn2index])
return rvs
def freeze(self) -> Dict[str, Any]:
diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t
index 0a71a7a91..38b9fb553 100644
--- a/sphinx/themes/basic/static/basic.css_t
+++ b/sphinx/themes/basic/static/basic.css_t
@@ -764,6 +764,7 @@ div.code-block-caption code {
}
table.highlighttable td.linenos,
+span.linenos,
div.doctest > div.highlight span.gp { /* gp: Generic.Prompt */
user-select: none;
}
diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py
index 8eca67220..76438889b 100644
--- a/sphinx/util/typing.py
+++ b/sphinx/util/typing.py
@@ -57,7 +57,7 @@ Inventory = Dict[str, Dict[str, Tuple[str, str, str, str]]]
def is_system_TypeVar(typ: Any) -> bool:
"""Check *typ* is system defined TypeVar."""
modname = getattr(typ, '__module__', '')
- return modname == 'typing' and isinstance(typ, TypeVar) # type: ignore
+ return modname == 'typing' and isinstance(typ, TypeVar)
def stringify(annotation: Any) -> str:
@@ -68,7 +68,7 @@ def stringify(annotation: Any) -> str:
return annotation[1:-2]
else:
return annotation
- elif isinstance(annotation, TypeVar): # type: ignore
+ elif isinstance(annotation, TypeVar):
return annotation.__name__
elif not annotation:
return repr(annotation)
diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py
index 5ad2831dd..a33d3363f 100644
--- a/sphinx/writers/texinfo.py
+++ b/sphinx/writers/texinfo.py
@@ -369,7 +369,7 @@ class TexinfoTranslator(SphinxTranslator):
"""Return an escaped string suitable for use as an argument
to a Texinfo command."""
s = self.escape(s)
- # commas are the argument delimeters
+ # commas are the argument delimiters
s = s.replace(',', '@comma{}')
# normalize white space
s = ' '.join(s.split()).strip()
diff --git a/tests/roots/test-domain-c/function_param_target.rst b/tests/roots/test-domain-c/function_param_target.rst
new file mode 100644
index 000000000..05de01445
--- /dev/null
+++ b/tests/roots/test-domain-c/function_param_target.rst
@@ -0,0 +1,5 @@
+.. c:function:: void f(int i)
+
+ - :c:var:`i`
+
+- :c:var:`f.i`
diff --git a/tests/roots/test-ext-autodoc/target/slots.py b/tests/roots/test-ext-autodoc/target/slots.py
index 44e750320..17e469cac 100644
--- a/tests/roots/test-ext-autodoc/target/slots.py
+++ b/tests/roots/test-ext-autodoc/target/slots.py
@@ -2,6 +2,10 @@ class Foo:
__slots__ = ['attr']
+class FooSingleString:
+ __slots__ = 'attr'
+
+
class Bar:
__slots__ = {'attr1': 'docstring of attr1',
'attr2': 'docstring of attr2',
diff --git a/tests/roots/test-root/conf.py b/tests/roots/test-root/conf.py
index b3cc12ae0..c3a890045 100644
--- a/tests/roots/test-root/conf.py
+++ b/tests/roots/test-root/conf.py
@@ -32,8 +32,6 @@ pygments_style = 'sphinx'
show_authors = True
numfig = True
-rst_epilog = '.. |subst| replace:: global substitution'
-
html_sidebars = {'**': ['localtoc.html', 'relations.html', 'sourcelink.html',
'customsb.html', 'searchbox.html'],
'index': ['contentssb.html', 'localtoc.html', 'globaltoc.html']}
diff --git a/tests/roots/test-root/markup.txt b/tests/roots/test-root/markup.txt
index e6adef55e..7cd3e95ea 100644
--- a/tests/roots/test-root/markup.txt
+++ b/tests/roots/test-root/markup.txt
@@ -22,7 +22,9 @@ Meta markup
Generic reST
------------
-A |subst| (the definition is in rst_epilog).
+A |subst|!
+
+.. |subst| replace:: global substitution
.. highlight:: none
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index 8cd541481..1f9f8eac7 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -258,7 +258,7 @@ def test_html4_output(app, status, warning):
(".//pre/strong", 'try_stmt'),
(".//pre/a[@href='#grammar-token-try1_stmt']/code/span", 'try1_stmt'),
# tests for ``only`` directive
- (".//p", 'A global substitution.'),
+ (".//p", 'A global substitution!'),
(".//p", 'In HTML.'),
(".//p", 'In both.'),
(".//p", 'Always present'),
diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py
index b6f72287e..43d71f74e 100644
--- a/tests/test_domain_c.py
+++ b/tests/test_domain_c.py
@@ -9,6 +9,8 @@
"""
import pytest
+from xml.etree import ElementTree
+
from sphinx import addnodes
from sphinx.addnodes import desc
from sphinx.domains.c import DefinitionParser, DefinitionError
@@ -529,6 +531,25 @@ def filter_warnings(warning, file):
return res
+def extract_role_links(app, filename):
+ t = (app.outdir / filename).read_text()
+ lis = [l for l in t.split('\n') if l.startswith("<li")]
+ entries = []
+ for l in lis:
+ li = ElementTree.fromstring(l)
+ aList = list(li.iter('a'))
+ assert len(aList) == 1
+ a = aList[0]
+ target = a.attrib['href'].lstrip('#')
+ title = a.attrib['title']
+ assert len(a) == 1
+ code = a[0]
+ assert code.tag == 'code'
+ text = ''.join(code.itertext())
+ entries.append((target, title, text))
+ return entries
+
+
@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
def test_build_domain_c(app, status, warning):
app.builder.build_all()
@@ -562,6 +583,26 @@ def test_build_domain_c_semicolon(app, status, warning):
assert len(ws) == 0
+@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
+def test_build_function_param_target(app, warning):
+ # the anchor for function parameters should be the function
+ app.builder.build_all()
+ ws = filter_warnings(warning, "function_param_target")
+ assert len(ws) == 0
+ entries = extract_role_links(app, "function_param_target.html")
+ assert entries == [
+ ('c.f', 'i', 'i'),
+ ('c.f', 'f.i', 'f.i'),
+ ]
+
+
+def _get_obj(app, queryName):
+ domain = app.env.get_domain('c')
+ for name, dispname, objectType, docname, anchor, prio in domain.get_objects():
+ if name == queryName:
+ return (docname, anchor, objectType)
+ return (queryName, "not", "found")
+
def test_cfunction(app):
text = (".. c:function:: PyObject* "
"PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)")
@@ -569,8 +610,7 @@ def test_cfunction(app):
assert_node(doctree[1], addnodes.desc, desctype="function",
domain="c", objtype="function", noindex=False)
- domain = app.env.get_domain('c')
- entry = domain.objects.get('PyType_GenericAlloc')
+ entry = _get_obj(app, 'PyType_GenericAlloc')
assert entry == ('index', 'c.PyType_GenericAlloc', 'function')
@@ -580,8 +620,7 @@ def test_cmember(app):
assert_node(doctree[1], addnodes.desc, desctype="member",
domain="c", objtype="member", noindex=False)
- domain = app.env.get_domain('c')
- entry = domain.objects.get('PyTypeObject.tp_bases')
+ entry = _get_obj(app, 'PyTypeObject.tp_bases')
assert entry == ('index', 'c.PyTypeObject.tp_bases', 'member')
@@ -591,9 +630,8 @@ def test_cvar(app):
assert_node(doctree[1], addnodes.desc, desctype="var",
domain="c", objtype="var", noindex=False)
- domain = app.env.get_domain('c')
- entry = domain.objects.get('PyClass_Type')
- assert entry == ('index', 'c.PyClass_Type', 'var')
+ entry = _get_obj(app, 'PyClass_Type')
+ assert entry == ('index', 'c.PyClass_Type', 'member')
def test_noindexentry(app):
diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py
index 558d69911..ec7a2b262 100644
--- a/tests/test_domain_cpp.py
+++ b/tests/test_domain_cpp.py
@@ -1236,3 +1236,18 @@ def test_noindexentry(app):
assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
assert_node(doctree[0], addnodes.index, entries=[('single', 'f (C++ function)', '_CPPv41fv', '', None)])
assert_node(doctree[2], addnodes.index, entries=[])
+
+
+def test_mix_decl_duplicate(app, warning):
+ # Issue 8270
+ text = (".. cpp:struct:: A\n"
+ ".. cpp:function:: void A()\n"
+ ".. cpp:struct:: A\n")
+ restructuredtext.parse(app, text)
+ ws = warning.getvalue().split("\n")
+ assert len(ws) == 5
+ assert "index.rst:2: WARNING: Duplicate C++ declaration, also defined in 'index'." in ws[0]
+ assert "Declaration is 'void A()'." in ws[1]
+ assert "index.rst:3: WARNING: Duplicate C++ declaration, also defined in 'index'." in ws[2]
+ assert "Declaration is 'A'." in ws[3]
+ assert ws[4] == "" \ No newline at end of file
diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py
index 8040af9cc..ceea51508 100644
--- a/tests/test_domain_py.py
+++ b/tests/test_domain_py.py
@@ -313,7 +313,7 @@ def test_pyfunction_signature(app):
def test_pyfunction_signature_full(app):
text = (".. py:function:: hello(a: str, b = 1, *args: str, "
- "c: bool = True, **kwargs: str) -> str")
+ "c: bool = True, d: tuple = (1, 2), **kwargs: str) -> str")
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index,
[desc, ([desc_signature, ([desc_name, "hello"],
@@ -343,6 +343,14 @@ def test_pyfunction_signature_full(app):
[desc_sig_operator, "="],
" ",
[nodes.inline, "True"])],
+ [desc_parameter, ([desc_sig_name, "d"],
+ [desc_sig_punctuation, ":"],
+ " ",
+ [desc_sig_name, pending_xref, "tuple"],
+ " ",
+ [desc_sig_operator, "="],
+ " ",
+ [nodes.inline, "(1, 2)"])],
[desc_parameter, ([desc_sig_operator, "**"],
[desc_sig_name, "kwargs"],
[desc_sig_punctuation, ":"],
diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py
index 9cb54de5b..c0676f23f 100644
--- a/tests/test_ext_autodoc.py
+++ b/tests/test_ext_autodoc.py
@@ -1205,6 +1205,14 @@ def test_slots(app):
' .. py:attribute:: Foo.attr',
' :module: target.slots',
'',
+ '',
+ '.. py:class:: FooSingleString()',
+ ' :module: target.slots',
+ '',
+ '',
+ ' .. py:attribute:: FooSingleString.attr',
+ ' :module: target.slots',
+ '',
]