From d9ec78971d8f7bac2f7fafc02e8815aa32ae8788 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 25 Apr 2026 08:34:04 +0200 Subject: [PATCH 1/3] Add test for 14223 --- .../conf.py | 0 .../index.rst | 28 ++++++++++++++ tests/test_domains/test_domain_py.py | 37 +++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 tests/roots/test-domain-py-xref-type-alias-builtin/conf.py create mode 100644 tests/roots/test-domain-py-xref-type-alias-builtin/index.rst diff --git a/tests/roots/test-domain-py-xref-type-alias-builtin/conf.py b/tests/roots/test-domain-py-xref-type-alias-builtin/conf.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/roots/test-domain-py-xref-type-alias-builtin/index.rst b/tests/roots/test-domain-py-xref-type-alias-builtin/index.rst new file mode 100644 index 00000000000..60b0cdec92c --- /dev/null +++ b/tests/roots/test-domain-py-xref-type-alias-builtin/index.rst @@ -0,0 +1,28 @@ +Builtin Type Alias False Positive Test +======================================= + +This tests that builtin types like ``list`` in function signatures do not +incorrectly link to class attributes with the same name. + + +.. py:module:: mymodule + + Module to test builtin type cross-reference false positives. + + +.. py:class:: MyClass + + A class with an attribute that shadows a builtin type name. + + .. py:attribute:: list + :value: [1, 2, 3] + + An attribute named ``list`` that shadows the builtin. + + +.. py:function:: process(items: list) -> None + :module: mymodule + + Process a list of items. + + The ``list`` type annotation here should NOT link to ``MyClass.list``. diff --git a/tests/test_domains/test_domain_py.py b/tests/test_domains/test_domain_py.py index 19e92eca7a6..d5cf0e855d6 100644 --- a/tests/test_domains/test_domain_py.py +++ b/tests/test_domains/test_domain_py.py @@ -1900,3 +1900,40 @@ def test_type_alias_xref_resolution(app: SphinxTestApp) -> None: ' None: + """Test that builtin type annotations don't falsely link to class attributes. + + Regression test for a bug where ``def process(items: list)`` combined with + ``class MyClass`` having a ``list`` attribute would cause the ``list`` type + annotation to incorrectly resolve to ``MyClass.list`` via the class→data/attr + fallback in PythonDomain.resolve_xref (searchmode must be 0 for exact match). + """ + app.build() + + html_content = (app.outdir / 'index.html').read_text(encoding='utf8') + + # The MyClass.list attribute should still be properly documented + assert 'id="mymodule.MyClass.list"' in html_content, ( + 'MyClass.list attribute anchor not found in HTML' + ) + + # Find the process() function signature + process_match = re.search( + r'process.*?', html_content, re.DOTALL + ) + assert process_match is not None, 'Could not find process function signature' + process_signature = process_match.group(0) + + # The critical assertion: ``list`` in the function signature must NOT link + # to MyClass.list. If the fallback used fuzzy matching (searchmode=1), + # "list" would match "MyClass.list" and produce a false-positive link. + assert ( + ' Date: Sun, 12 Apr 2026 10:10:10 +0200 Subject: [PATCH 2/3] Python Domain: Avoid fuzzy match when looing up attrs as type annotations The should prevent matches on class attributes when the type is a buildin such as list, set or type --- sphinx/domains/python/__init__.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/sphinx/domains/python/__init__.py b/sphinx/domains/python/__init__.py index a23207ea3c2..7e6baccc3ac 100644 --- a/sphinx/domains/python/__init__.py +++ b/sphinx/domains/python/__init__.py @@ -945,11 +945,12 @@ def resolve_xref( if not matches and type == 'class': # fallback to data/attr (for type aliases) # type aliases are documented as data/attr but referenced as class - matches = self.find_obj(env, modname, clsname, target, 'data', searchmode) + # Use searchmode=0 (exact match only) to avoid fuzzy matching + # that could incorrectly match class attributes (e.g. D.list) + # when resolving builtin type annotations (e.g. list[int]). + matches = self.find_obj(env, modname, clsname, target, 'data', 0) if not matches: - matches = self.find_obj( - env, modname, clsname, target, 'attr', searchmode - ) + matches = self.find_obj(env, modname, clsname, target, 'attr', 0) if not matches and type == 'attr': # fallback to meth (for property; Sphinx 2.4.x) # this ensures that `:attr:` role continues to refer to the old property entry From 62303406890ad68e45ac373e5579b775c5e2813c Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Sat, 25 Apr 2026 08:45:36 +0200 Subject: [PATCH 3/3] Add my self to authors --- AUTHORS.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index fd129bb8f72..c798657ff21 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -70,6 +70,7 @@ Contributors * Ian Lee -- quickstart improvements * Jacob Mason -- websupport library (GSOC project) * James Addison -- linkcheck and HTML search improvements +* Jens H. Nielsen -- Small fixes for incorrect xref resolution * Jeppe Pihl -- literalinclude improvements * Jeremy Maitin-Shepard -- C++ domain improvements * Joel Wurtz -- cellspanning support in LaTeX