diff --git a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java index cdd1132c14ce..2027723c92f5 100644 --- a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java @@ -378,14 +378,26 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { } } else { - final Scope scope = ControlFlowCache.getScope(scopeOwner); - return StreamEx.of(scope.getNamedElements(name, false)) - .select(PyTargetExpression.class) - .map(x -> getTypeFromTargetExpressionAnnotation(x, context)) - .nonNull() - .map(Ref::get) - .findFirst() - .orElse(null); + StreamEx candidates = null; + if (context.maySwitchToAST(target)) { + final Scope scope = ControlFlowCache.getScope(scopeOwner); + candidates = StreamEx.of(scope.getNamedElements(name, false)).select(PyTargetExpression.class); + } + // Unqualified target expression in either class or module + else if (scopeOwner instanceof PyFile) { + candidates = StreamEx.of(((PyFile)scopeOwner).getTopLevelAttributes()).filter(t -> name.equals(t.getName())); + } + else if (scopeOwner instanceof PyClass) { + candidates = StreamEx.of(((PyClass)scopeOwner).getClassAttributes()).filter(t -> name.equals(t.getName())); + } + if (candidates != null) { + return candidates + .map(x -> getTypeFromTargetExpressionAnnotation(x, context)) + .nonNull() + .map(Ref::get) + .findFirst() + .orElse(null); + } } } return null; diff --git a/python/testData/types/ClassAttributeAnnotationAheadOfTimeInAnotherFile/other.py b/python/testData/types/ClassAttributeAnnotationAheadOfTimeInAnotherFile/other.py new file mode 100644 index 000000000000..f461ba75df02 --- /dev/null +++ b/python/testData/types/ClassAttributeAnnotationAheadOfTimeInAnotherFile/other.py @@ -0,0 +1,3 @@ +class C: + attr: int + attr, _ = None, None diff --git a/python/testData/types/TopLevelVariableAnnotationAheadOfTimeInAnotherFileForTarget/other.py b/python/testData/types/TopLevelVariableAnnotationAheadOfTimeInAnotherFileForTarget/other.py new file mode 100644 index 000000000000..fdfc302cd0d9 --- /dev/null +++ b/python/testData/types/TopLevelVariableAnnotationAheadOfTimeInAnotherFileForTarget/other.py @@ -0,0 +1,3 @@ +x: int +for x in foo(): + expr = x diff --git a/python/testData/types/TopLevelVariableAnnotationAheadOfTimeInAnotherFileUnpackingTarget/other.py b/python/testData/types/TopLevelVariableAnnotationAheadOfTimeInAnotherFileUnpackingTarget/other.py new file mode 100644 index 000000000000..0b97de1c2fea --- /dev/null +++ b/python/testData/types/TopLevelVariableAnnotationAheadOfTimeInAnotherFileUnpackingTarget/other.py @@ -0,0 +1,3 @@ +x: int +x, y = foo() +expr = x \ No newline at end of file diff --git a/python/testData/types/TopLevelVariableAnnotationAheadOfTimeInAnotherFileWithTarget/other.py b/python/testData/types/TopLevelVariableAnnotationAheadOfTimeInAnotherFileWithTarget/other.py new file mode 100644 index 000000000000..4e1e852d478c --- /dev/null +++ b/python/testData/types/TopLevelVariableAnnotationAheadOfTimeInAnotherFileWithTarget/other.py @@ -0,0 +1,3 @@ +x: int +with foo() as x: + expr = x diff --git a/python/testSrc/com/jetbrains/python/PyTypingTest.java b/python/testSrc/com/jetbrains/python/PyTypingTest.java index acc84fe4d304..2255f76b1c12 100644 --- a/python/testSrc/com/jetbrains/python/PyTypingTest.java +++ b/python/testSrc/com/jetbrains/python/PyTypingTest.java @@ -729,6 +729,13 @@ public class PyTypingTest extends PyTestCase { } // PY-21864 + public void testTopLevelVariableAnnotationAheadOfTimeInAnotherFileWithTarget() { + doMultiFileStubAwareTest("int", + "from other import x\n" + + "\n" + + "expr = x"); + } + public void testLocalVariableAnnotationAheadOfTimeForTarget() { doTest("int", "x: int\n" + @@ -736,6 +743,14 @@ public class PyTypingTest extends PyTestCase { " expr = x\n"); } + // PY-21864 + public void testTopLevelVariableAnnotationAheadOfTimeInAnotherFileForTarget() { + doMultiFileStubAwareTest("int", + "from other import x\n" + + "\n" + + "expr = x"); + } + // PY-21864 public void testLocalVariableAnnotationAheadOfTimeUnpackingTarget() { doTest("int", @@ -744,6 +759,14 @@ public class PyTypingTest extends PyTestCase { "expr = x"); } + // PY-21864 + public void testTopLevelVariableAnnotationAheadOfTimeInAnotherFileUnpackingTarget() { + doMultiFileStubAwareTest("int", + "from other import x\n" + + "\n" + + "expr = x"); + } + // PY-21864 public void testLocalVariableAnnotationAheadOfTimeOnlyFirstHintConsidered() { doTest("int", @@ -754,6 +777,14 @@ public class PyTypingTest extends PyTestCase { "expr = x"); } + // PY-21864 + public void testClassAttributeAnnotationAheadOfTimeInAnotherFile() { + doMultiFileStubAwareTest("int", + "from other import C\n" + + "\n" + + "expr = C().attr"); + } + public void testInstanceAttributeAnnotation() { doTest("int", "class C:\n" + @@ -1047,9 +1078,9 @@ public class PyTypingTest extends PyTestCase { // PY-24729 public void testAnnotatedInstanceAttributeInOtherFile() { doMultiFileStubAwareTest("int", - "from other import C\n" + - "\n" + - "expr = C().attr"); + "from other import C\n" + + "\n" + + "expr = C().attr"); } private void doTestNoInjectedText(@NotNull String text) {