From c93dddf65910eb94bb33d1895ff133cef74a308e Mon Sep 17 00:00:00 2001 From: David Smith Date: Thu, 28 Aug 2025 10:22:21 -0300 Subject: [PATCH] Added sphinxlint checker to flag relative targets in :doc: roles. Co-authored-by: Adam Johnson --- docs/lint.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/lint.py b/docs/lint.py index 5c7a168c97..2358fec78e 100644 --- a/docs/lint.py +++ b/docs/lint.py @@ -14,7 +14,7 @@ from sphinxlint.checkers import ( from sphinxlint.checkers import checker as sphinxlint_checker from sphinxlint.rst import SIMPLENAME from sphinxlint.sphinxlint import check_text -from sphinxlint.utils import PER_FILE_CACHES, hide_non_rst_blocks +from sphinxlint.utils import PER_FILE_CACHES, hide_non_rst_blocks, paragraphs def django_check_file(filename, checkers, options=None): @@ -136,6 +136,22 @@ def check_python_domain_in_roles(file, lines, options=None): yield lno, f":py domain is the default and can be omitted {role.group(0)!r}" +_DOC_CAPTURE_TARGET_RE = re.compile(r":doc:`(?:[^<`]+<)?([^>`]+)>?`") + + +@sphinxlint_checker(".rst", rst_only=True) +def check_absolute_targets_doc_role(file, lines, options=None): + for paragraph_lno, paragraph in paragraphs(lines): + for error in _DOC_CAPTURE_TARGET_RE.finditer(paragraph): + target = error.group(1) + # Skip absolute or intersphinx refs like "python:using/windows". + if target.startswith("/") or ":" in target.split("/", 1)[0]: + continue + # Relative target, report as a violation. + error_offset = paragraph[: error.start()].count("\n") + yield (paragraph_lno + error_offset, target) + + import sphinxlint # noqa: E402 sphinxlint.check_file = django_check_file