diff --git a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/ScopeUtil.java b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/ScopeUtil.java index 4a632c4b40a6..359ed2a09e3e 100644 --- a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/ScopeUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/ScopeUtil.java @@ -100,20 +100,29 @@ public class ScopeUtil { if (decoratorAncestor != null && !isAncestor(decoratorAncestor, firstOwner, true)) { return nextOwner; } - // References in default values of parameters are resolved outside of the function (if the lambda is not inside the default value) - final PyParameter parameterAncestor = getParentOfType(element, PyParameter.class); + // References in default values or in annotations of parameters are resolved outside of the function (if the lambda is not inside the + // default value) + final PyNamedParameter parameterAncestor = getParentOfType(element, PyNamedParameter.class); if (parameterAncestor != null && !isAncestor(parameterAncestor, firstOwner, true)) { final PyExpression defaultValue = parameterAncestor.getDefaultValue(); - if (element != null && isAncestor(defaultValue, element, false)) { + final PyAnnotation annotation = parameterAncestor.getAnnotation(); + if (isAncestor(defaultValue, element, false) || isAncestor(annotation, element, false)) { return nextOwner; } } // Superclasses are resolved outside of the class final PyClass containingClass = getParentOfType(element, PyClass.class); - if (containingClass != null && element != null && - isAncestor(containingClass.getSuperClassExpressionList(), element, false)) { + if (containingClass != null && isAncestor(containingClass.getSuperClassExpressionList(), element, false)) { return nextOwner; } + // Function return annotations are resolved outside of the function + if (firstOwner instanceof PyFunction) { + final PyFunction function = (PyFunction)firstOwner; + final PyAnnotation annotation = function.getAnnotation(); + if (isAncestor(annotation, element, false)) { + return nextOwner; + } + } return firstOwner; } diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/classLevelReferenceInMethodAnnotation.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/classLevelReferenceInMethodAnnotation.py new file mode 100644 index 000000000000..29e686c75a13 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection3K/classLevelReferenceInMethodAnnotation.py @@ -0,0 +1,11 @@ +class B: + class C: + pass + + class Return: + pass + + value = 10 + + def foo(self, x: C, y: value) -> Return: + pass diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java index 32505db617a1..1a29300f947d 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java @@ -116,4 +116,9 @@ public class Py3UnresolvedReferencesInspectionTest extends PyTestCase { public void testNotImportedSubmodulesOfNamespacePackage() { doMultiFileTest("main.py"); } + + // PY-15017 + public void testClassLevelReferenceInMethodAnnotation() { + doTest(); + } }