mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Fix #6914: Emit a detailed warning when failed to resolve :ref:
To be clear the ambiguous warning for missing-reference :ref:, this separates the warning to missing-label and missing-caption. To emit a warning dynamically, this also adds a new event: `warn-missing-reference` to customize warning messages via event handlers.
This commit is contained in:
parent
487b8436c6
commit
0e98e9b1a8
4
CHANGES
4
CHANGES
@ -13,6 +13,10 @@ Deprecated
|
|||||||
Features added
|
Features added
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
* #6914: Add a new event :event:`warn-missing-reference` to custom warning
|
||||||
|
messages when failed to resolve a cross-reference
|
||||||
|
* #6914: Emit a detailed warning when failed to resolve a ``:ref:`` reference
|
||||||
|
|
||||||
Bugs fixed
|
Bugs fixed
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
@ -186,6 +186,7 @@ type for that event::
|
|||||||
13. apply post-transforms (by priority): docutils.document -> docutils.document
|
13. apply post-transforms (by priority): docutils.document -> docutils.document
|
||||||
14. event.doctree-resolved(app, doctree, docname)
|
14. event.doctree-resolved(app, doctree, docname)
|
||||||
- (for any reference node that fails to resolve) event.missing-reference(env, node, contnode)
|
- (for any reference node that fails to resolve) event.missing-reference(env, node, contnode)
|
||||||
|
- (for any reference node that fails to resolve) event.warn-missing-reference(domain, node)
|
||||||
|
|
||||||
15. Generate output files
|
15. Generate output files
|
||||||
16. event.build-finished(app, exception)
|
16. event.build-finished(app, exception)
|
||||||
@ -284,6 +285,14 @@ Here is a more detailed list of these events.
|
|||||||
|
|
||||||
.. versionadded:: 0.5
|
.. versionadded:: 0.5
|
||||||
|
|
||||||
|
.. event:: warn-missing-reference (app, domain, node)
|
||||||
|
|
||||||
|
Emitted when a cross-reference to an object cannot be resolved even after
|
||||||
|
:event:`missing-reference`. If the event handler can emit warnings for
|
||||||
|
the missing reference, it should return ``True``.
|
||||||
|
|
||||||
|
.. versionadded:: 3.4
|
||||||
|
|
||||||
.. event:: doctree-resolved (app, doctree, docname)
|
.. event:: doctree-resolved (app, doctree, docname)
|
||||||
|
|
||||||
Emitted when a doctree has been "resolved" by the environment, that is, all
|
Emitted when a doctree has been "resolved" by the environment, that is, all
|
||||||
|
@ -610,8 +610,6 @@ class StandardDomain(Domain):
|
|||||||
|
|
||||||
dangling_warnings = {
|
dangling_warnings = {
|
||||||
'term': 'term not in glossary: %(target)s',
|
'term': 'term not in glossary: %(target)s',
|
||||||
'ref': 'undefined label: %(target)s (if the link has no caption '
|
|
||||||
'the label must precede a section header)',
|
|
||||||
'numref': 'undefined label: %(target)s',
|
'numref': 'undefined label: %(target)s',
|
||||||
'keyword': 'unknown keyword: %(target)s',
|
'keyword': 'unknown keyword: %(target)s',
|
||||||
'doc': 'unknown document: %(target)s',
|
'doc': 'unknown document: %(target)s',
|
||||||
@ -1107,8 +1105,23 @@ class StandardDomain(Domain):
|
|||||||
RemovedInSphinx40Warning, stacklevel=2)
|
RemovedInSphinx40Warning, stacklevel=2)
|
||||||
|
|
||||||
|
|
||||||
|
def warn_missing_reference(app: "Sphinx", domain: Domain, node: pending_xref) -> bool:
|
||||||
|
if domain.name != 'std' or node['reftype'] != 'ref':
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
target = node['reftarget']
|
||||||
|
if target not in domain.anonlabels: # type: ignore
|
||||||
|
msg = __('undefined label: %s')
|
||||||
|
else:
|
||||||
|
msg = __('Failed to create a cross reference. A title or caption not found: %s')
|
||||||
|
|
||||||
|
logger.warning(msg % target, location=node, type='ref', subtype=node['reftype'])
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def setup(app: "Sphinx") -> Dict[str, Any]:
|
def setup(app: "Sphinx") -> Dict[str, Any]:
|
||||||
app.add_domain(StandardDomain)
|
app.add_domain(StandardDomain)
|
||||||
|
app.connect('warn-missing-reference', warn_missing_reference)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'version': 'builtin',
|
'version': 'builtin',
|
||||||
|
@ -46,6 +46,7 @@ core_events = {
|
|||||||
'doctree-read': 'the doctree before being pickled',
|
'doctree-read': 'the doctree before being pickled',
|
||||||
'env-merge-info': 'env, read docnames, other env instance',
|
'env-merge-info': 'env, read docnames, other env instance',
|
||||||
'missing-reference': 'env, node, contnode',
|
'missing-reference': 'env, node, contnode',
|
||||||
|
'warn-missing-reference': 'domain, node',
|
||||||
'doctree-resolved': 'doctree, docname',
|
'doctree-resolved': 'doctree, docname',
|
||||||
'env-updated': 'env',
|
'env-updated': 'env',
|
||||||
'html-collect-pages': 'builder',
|
'html-collect-pages': 'builder',
|
||||||
|
@ -166,7 +166,10 @@ class ReferencesResolver(SphinxPostTransform):
|
|||||||
warn = False
|
warn = False
|
||||||
if not warn:
|
if not warn:
|
||||||
return
|
return
|
||||||
if domain and typ in domain.dangling_warnings:
|
|
||||||
|
if self.app.emit_firstresult('warn-missing-reference', domain, node):
|
||||||
|
return
|
||||||
|
elif domain and typ in domain.dangling_warnings:
|
||||||
msg = domain.dangling_warnings[typ]
|
msg = domain.dangling_warnings[typ]
|
||||||
elif node.get('refdomain', 'std') not in ('', 'std'):
|
elif node.get('refdomain', 'std') not in ('', 'std'):
|
||||||
msg = (__('%s:%s reference target not found: %%(target)s') %
|
msg = (__('%s:%s reference target not found: %%(target)s') %
|
||||||
|
0
tests/roots/test-domain-py-xref-warning/conf.py
Normal file
0
tests/roots/test-domain-py-xref-warning/conf.py
Normal file
7
tests/roots/test-domain-py-xref-warning/index.rst
Normal file
7
tests/roots/test-domain-py-xref-warning/index.rst
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
test-domain-py-xref-warning
|
||||||
|
===========================
|
||||||
|
|
||||||
|
.. _existing-label:
|
||||||
|
|
||||||
|
:ref:`no-label`
|
||||||
|
:ref:`existing-label`
|
@ -859,3 +859,11 @@ def test_noindexentry(app):
|
|||||||
assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
|
assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
|
||||||
assert_node(doctree[0], addnodes.index, entries=[('single', 'f (built-in class)', 'f', '', None)])
|
assert_node(doctree[0], addnodes.index, entries=[('single', 'f (built-in class)', 'f', '', None)])
|
||||||
assert_node(doctree[2], addnodes.index, entries=[])
|
assert_node(doctree[2], addnodes.index, entries=[])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('dummy', testroot='domain-py-xref-warning')
|
||||||
|
def test_warn_missing_reference(app, status, warning):
|
||||||
|
app.build()
|
||||||
|
assert 'index.rst:6: WARNING: undefined label: no-label' in warning.getvalue()
|
||||||
|
assert ('index.rst:6: WARNING: Failed to create a cross reference. A title or caption not found: existing-label'
|
||||||
|
in warning.getvalue())
|
||||||
|
Loading…
Reference in New Issue
Block a user