From 30c594fbc592a741956c067f5188f9bf6ecd25da Mon Sep 17 00:00:00 2001
From: Takeshi KOMIYA <i.tkomiya@gmail.com>
Date: Sat, 19 Dec 2020 16:28:48 +0900
Subject: [PATCH] Fix #8365: py domain: :type: and :rtype: gives false
 ambiguous warnings

The searching context like py:module and py:class are missing in the
pending_xref nodes generated from info-field-lists.  This sets these
searching context info to them.
---
 CHANGES                  |  2 ++
 sphinx/domains/python.py |  2 ++
 sphinx/util/docfields.py |  1 +
 tests/test_domain_py.py  | 47 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 52 insertions(+)

diff --git a/CHANGES b/CHANGES
index 484e654eda5..047eeb70c88 100644
--- a/CHANGES
+++ b/CHANGES
@@ -81,6 +81,8 @@ Bugs fixed
 * #8131: linkcheck: Use GET when HEAD requests cause Too Many Redirects, to
   accommodate infinite redirect loops on HEAD
 * #8437: Makefile: ``make clean`` with empty BUILDDIR is dangerous
+* #8365: py domain: ``:type:`` and ``:rtype:`` gives false ambiguous class
+  lookup warnings
 * #8352: std domain: Failed to parse an option that starts with bracket
 * #8519: LaTeX: Prevent page brake in the middle of a seealso
 
diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py
index 79d7e4f466d..c4d134d0874 100644
--- a/sphinx/domains/python.py
+++ b/sphinx/domains/python.py
@@ -272,6 +272,8 @@ def make_xref(self, rolename: str, domain: str, target: str,
         result = super().make_xref(rolename, domain, target,  # type: ignore
                                    innernode, contnode, env)
         result['refspecific'] = True
+        result['py:module'] = env.ref_context.get('py:module')
+        result['py:class'] = env.ref_context.get('py:class')
         if target.startswith(('.', '~')):
             prefix, result['reftarget'] = target[0], target[1:]
             if prefix == '.':
diff --git a/sphinx/util/docfields.py b/sphinx/util/docfields.py
index 404bb127f82..9a57ccff75a 100644
--- a/sphinx/util/docfields.py
+++ b/sphinx/util/docfields.py
@@ -295,6 +295,7 @@ def transform(self, node: nodes.field_list) -> None:
                         self.directive.domain,
                         target,
                         contnode=content[0],
+                        env=self.directive.state.document.settings.env
                     )
                     if _is_single_paragraph(field_body):
                         paragraph = cast(nodes.paragraph, field_body[0])
diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py
index 2dc97bed99e..f95521e2e91 100644
--- a/tests/test_domain_py.py
+++ b/tests/test_domain_py.py
@@ -774,6 +774,53 @@ def test_pydecoratormethod_signature(app):
     assert domain.objects['deco'] == ('index', 'deco', 'method')
 
 
+def test_info_field_list(app):
+    text = (".. py:module:: example\n"
+            ".. py:class:: Class\n"
+            "\n"
+            "   :param str name: blah blah\n"
+            "   :param age: blah blah\n"
+            "   :type age: int\n")
+    doctree = restructuredtext.parse(app, text)
+    print(doctree)
+
+    assert_node(doctree, (nodes.target,
+                          addnodes.index,
+                          addnodes.index,
+                          [desc, ([desc_signature, ([desc_annotation, "class "],
+                                                    [desc_addname, "example."],
+                                                    [desc_name, "Class"])],
+                                  [desc_content, nodes.field_list, nodes.field])]))
+    assert_node(doctree[3][1][0][0],
+                ([nodes.field_name, "Parameters"],
+                 [nodes.field_body, nodes.bullet_list, ([nodes.list_item, nodes.paragraph],
+                                                        [nodes.list_item, nodes.paragraph])]))
+
+    # :param str name:
+    assert_node(doctree[3][1][0][0][1][0][0][0],
+                ([addnodes.literal_strong, "name"],
+                 " (",
+                 [pending_xref, addnodes.literal_emphasis, "str"],
+                 ")",
+                 " -- ",
+                 "blah blah"))
+    assert_node(doctree[3][1][0][0][1][0][0][0][2], pending_xref,
+                refdomain="py", reftype="class", reftarget="str",
+                **{"py:module": "example", "py:class": "Class"})
+
+    # :param age: + :type age:
+    assert_node(doctree[3][1][0][0][1][0][1][0],
+                ([addnodes.literal_strong, "age"],
+                 " (",
+                 [pending_xref, addnodes.literal_emphasis, "int"],
+                 ")",
+                 " -- ",
+                 "blah blah"))
+    assert_node(doctree[3][1][0][0][1][0][1][0][2], pending_xref,
+                refdomain="py", reftype="class", reftarget="int",
+                **{"py:module": "example", "py:class": "Class"})
+
+
 @pytest.mark.sphinx(freshenv=True)
 def test_module_index(app):
     text = (".. py:module:: docutils\n"