Skip to content

Commit

Permalink
Merge pull request sphinx-doc#8075 from tk0miya/6914_detailed_warning…
Browse files Browse the repository at this point in the history
…_for_missing_ref

Fix sphinx-doc#6914: Emit a detailed warning when failed to resolve :ref:
  • Loading branch information
tk0miya committed Nov 3, 2020
2 parents 487b843 + 0e98e9b commit 8575b43
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 3 deletions.
4 changes: 4 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ Deprecated
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
----------

Expand Down
9 changes: 9 additions & 0 deletions doc/extdev/appapi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ type for that event::
13. apply post-transforms (by priority): docutils.document -> docutils.document
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.warn-missing-reference(domain, node)

15. Generate output files
16. event.build-finished(app, exception)
Expand Down Expand Up @@ -284,6 +285,14 @@ Here is a more detailed list of these events.

.. 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)

Emitted when a doctree has been "resolved" by the environment, that is, all
Expand Down
17 changes: 15 additions & 2 deletions sphinx/domains/std.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,8 +610,6 @@ class StandardDomain(Domain):

dangling_warnings = {
'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',
'keyword': 'unknown keyword: %(target)s',
'doc': 'unknown document: %(target)s',
Expand Down Expand Up @@ -1107,8 +1105,23 @@ def note_labels(self, env: "BuildEnvironment", docname: str, document: nodes.doc
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]:
app.add_domain(StandardDomain)
app.connect('warn-missing-reference', warn_missing_reference)

return {
'version': 'builtin',
Expand Down
1 change: 1 addition & 0 deletions sphinx/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
'doctree-read': 'the doctree before being pickled',
'env-merge-info': 'env, read docnames, other env instance',
'missing-reference': 'env, node, contnode',
'warn-missing-reference': 'domain, node',
'doctree-resolved': 'doctree, docname',
'env-updated': 'env',
'html-collect-pages': 'builder',
Expand Down
5 changes: 4 additions & 1 deletion sphinx/transforms/post_transforms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,10 @@ def warn_missing_reference(self, refdoc: str, typ: str, target: str,
warn = False
if not warn:
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]
elif node.get('refdomain', 'std') not in ('', 'std'):
msg = (__('%s:%s reference target not found: %%(target)s') %
Expand Down
Empty file.
7 changes: 7 additions & 0 deletions tests/roots/test-domain-py-xref-warning/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
test-domain-py-xref-warning
===========================

.. _existing-label:

:ref:`no-label`
:ref:`existing-label`
8 changes: 8 additions & 0 deletions tests/test_domain_py.py
Original file line number Diff line number Diff line change
Expand Up @@ -859,3 +859,11 @@ def test_noindexentry(app):
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[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())

0 comments on commit 8575b43

Please sign in to comment.