From 07082866c38c58bd7892ff65bb428f71d0e77fbb Mon Sep 17 00:00:00 2001 From: chbndrhnns Date: Mon, 16 Feb 2026 12:26:33 +1000 Subject: [PATCH] PY-56268/PY-84930 go to type declaration: support callables GitOrigin-RevId: d5e05b87ff1fc5670dfdeca7b13c6aee4661a519 --- .../codeInsight/PyTypeDeclarationProvider.kt | 12 ++++++++- .../testing/PyTestFixtureResolvingTest.kt | 26 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/codeInsight/PyTypeDeclarationProvider.kt b/python/src/com/jetbrains/python/codeInsight/PyTypeDeclarationProvider.kt index 03445a060022..5abe62d5842e 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyTypeDeclarationProvider.kt +++ b/python/src/com/jetbrains/python/codeInsight/PyTypeDeclarationProvider.kt @@ -4,6 +4,8 @@ package com.jetbrains.python.codeInsight import com.intellij.codeInsight.navigation.actions.TypeDeclarationProvider import com.intellij.psi.PsiElement import com.jetbrains.python.psi.PyTypedElement +import com.jetbrains.python.psi.types.PyCallableType +import com.jetbrains.python.psi.types.PyClassType import com.jetbrains.python.psi.types.PyTypeUtil.componentSequence import com.jetbrains.python.psi.types.TypeEvalContext @@ -12,7 +14,15 @@ class PyTypeDeclarationProvider : TypeDeclarationProvider { override fun getSymbolTypeDeclarations(symbol: PsiElement): Array? { if (symbol is PyTypedElement) { val context = TypeEvalContext.userInitiated(symbol.project, symbol.containingFile) - return context.getType(symbol).componentSequence + + // When the symbol is a function, + // navigate to the return type declaration instead of the function type itself. + val type = when (val type = context.getType(symbol)) { + is PyCallableType if type !is PyClassType -> type.getReturnType(context) + else -> type + } + + return type.componentSequence .filterNotNull() .mapNotNull { it.declarationElement } .distinct() diff --git a/python/testSrc/com/jetbrains/python/testing/PyTestFixtureResolvingTest.kt b/python/testSrc/com/jetbrains/python/testing/PyTestFixtureResolvingTest.kt index 243d568cdff0..26f742a44f9a 100644 --- a/python/testSrc/com/jetbrains/python/testing/PyTestFixtureResolvingTest.kt +++ b/python/testSrc/com/jetbrains/python/testing/PyTestFixtureResolvingTest.kt @@ -1,12 +1,15 @@ // Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.jetbrains.python.testing +import com.intellij.codeInsight.navigation.actions.GotoTypeDeclarationAction +import com.intellij.idea.TestFor import com.intellij.psi.PsiElement import com.intellij.psi.PsiPolyVariantReference import com.intellij.psi.PsiReference import com.intellij.psi.ResolveResult import com.jetbrains.python.fixture.PythonCommonTestCase import com.jetbrains.python.fixtures.PyTestCase +import com.jetbrains.python.psi.PyFile import com.jetbrains.python.psi.PyNamedParameter import com.jetbrains.python.psi.resolve.ImportedResolveResult import com.jetbrains.python.psi.types.TypeEvalContext @@ -360,4 +363,27 @@ class PyTestFixtureResolvingTest : PyTestCase() { fun testNamedParameterTypes() { assertCorrectType(PARAMETRIZED_DIR, TEST_PARAMETER_TYPES, INT_STR_UNION) } + + @TestFor(issues = ["PY-56268"]) + fun `test goto type declaration for fixture parameter`() { + myFixture.configureByText("test.py", """ + import pytest + + + class A: pass + + + @pytest.fixture + def instance(): + return A() + + + def test(instance): + assert instance + """) + val types = GotoTypeDeclarationAction.findSymbolTypes(myFixture.editor, myFixture.caretOffset) + assertNotNull("Go to Type Declaration should resolve the fixture parameter type", types) + assertEquals(1, types!!.size) + assertEquals((myFixture.file as PyFile).findTopLevelClass("A"), types.single()) + } } \ No newline at end of file