From 83b6278502ad021561e11eb38f5263fe2d647040 Mon Sep 17 00:00:00 2001 From: "Ilya.Kazakevich" Date: Fri, 13 Jul 2018 03:36:36 +0300 Subject: [PATCH] PY-30762: Rename param usages when fixture (to which param has reference) is renamed Param usage has reference to PyNamedParam. PyNamedParam has reference to fixture. When we rename fixture, PyNamedParam is also renamed because PyNamedParam#isReferenceTo(Fixture) == true, but ParamUsage#isReferenceTo(Fixture) is not. We use EP to fix it. --- python/src/META-INF/python-core-common.xml | 2 ++ .../PyReferenceCustomTargetChecker.kt | 18 +++++++++++++ .../psi/impl/references/PyReferenceImpl.java | 3 ++- .../testing/pyTestFixtures/PyTestFixture.kt | 9 ++++--- .../PyTestFixtureInspectionExtension.kt | 3 +-- .../PyTestFixtureReferenceContributor.kt | 6 ++--- .../PyTestFixtureTargetChecker.kt | 26 +++++++++++++++++++ 7 files changed, 58 insertions(+), 9 deletions(-) create mode 100644 python/src/com/jetbrains/python/psi/impl/references/PyReferenceCustomTargetChecker.kt create mode 100644 python/src/com/jetbrains/python/testing/pyTestFixtures/PyTestFixtureTargetChecker.kt diff --git a/python/src/META-INF/python-core-common.xml b/python/src/META-INF/python-core-common.xml index 61ec85b1a0b8..848da83a1f57 100644 --- a/python/src/META-INF/python-core-common.xml +++ b/python/src/META-INF/python-core-common.xml @@ -650,6 +650,7 @@ + @@ -703,6 +704,7 @@ + diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceCustomTargetChecker.kt b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceCustomTargetChecker.kt new file mode 100644 index 000000000000..e4dded3f9a06 --- /dev/null +++ b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceCustomTargetChecker.kt @@ -0,0 +1,18 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.jetbrains.python.psi.impl.references + +import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference + +/** + * EP to check if some reference points to some element + */ +interface PyReferenceCustomTargetChecker { + companion object { + val EP_NAME = ExtensionPointName.create("Pythonid.pyReferenceCustomTargetChecker") + fun isReferenceTo(reference: PsiReference, to: PsiElement) = EP_NAME.extensions.firstOrNull { it.isReferenceTo(reference, to) } != null + } + + fun isReferenceTo(reference: PsiReference, to: PsiElement): Boolean +} \ No newline at end of file diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java index 97876e0ae20a..98ab7088c307 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java @@ -570,7 +570,8 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference } } } - return false; + + return PyReferenceCustomTargetChecker.Companion.isReferenceTo(this, element); } private boolean resolvesToSameLocal(PsiElement element, String elementName, ScopeOwner ourScopeOwner, ScopeOwner theirScopeOwner) { diff --git a/python/src/com/jetbrains/python/testing/pyTestFixtures/PyTestFixture.kt b/python/src/com/jetbrains/python/testing/pyTestFixtures/PyTestFixture.kt index 4506711d8e8f..c61200b0aef4 100644 --- a/python/src/com/jetbrains/python/testing/pyTestFixtures/PyTestFixture.kt +++ b/python/src/com/jetbrains/python/testing/pyTestFixtures/PyTestFixture.kt @@ -41,9 +41,9 @@ internal fun getFixture(element: PyNamedParameter, typeEvalContext: TypeEvalCont fun PyNamedParameter.isFixture(typeEvalContext: TypeEvalContext) = getFixture(this, typeEvalContext) != null /** - * @return Boolean is function decorated as fixture + * @return Boolean is function decorated as fixture or marked so by EP */ -internal fun PyFunction.isFixture() = decoratorList?.findDecorator(decoratorName) != null +internal fun PyFunction.isFixture() = decoratorList?.findDecorator(decoratorName) != null || isCustomFixture() /** @@ -92,7 +92,7 @@ private val pyTestName = PyTestFrameworkService.getSdkReadableNameByFramework(Py internal fun getFixtures(module: Module, forWhat: PyFunction, typeEvalContext: TypeEvalContext): List { // Fixtures could be used only by test functions or other fixtures. val fixture = forWhat.isFixture() - val pyTestEnabled = TestRunnerService.getInstance(module).projectConfiguration == pyTestName + val pyTestEnabled = isPyTestEnabled(module) return if ( fixture || (pyTestEnabled && isTestElement(forWhat, ThreeState.NO, typeEvalContext)) || @@ -107,4 +107,7 @@ internal fun getFixtures(module: Module, forWhat: PyFunction, typeEvalContext: T else emptyList() } +internal fun isPyTestEnabled(module: Module) = + TestRunnerService.getInstance(module).projectConfiguration == pyTestName + diff --git a/python/src/com/jetbrains/python/testing/pyTestFixtures/PyTestFixtureInspectionExtension.kt b/python/src/com/jetbrains/python/testing/pyTestFixtures/PyTestFixtureInspectionExtension.kt index 4b86c12f4b3d..cd03d80fac6a 100644 --- a/python/src/com/jetbrains/python/testing/pyTestFixtures/PyTestFixtureInspectionExtension.kt +++ b/python/src/com/jetbrains/python/testing/pyTestFixtures/PyTestFixtureInspectionExtension.kt @@ -14,6 +14,5 @@ object PyTestFixtureInspectionExtension : PyInspectionExtension() { override fun ignoreUnused(local: PsiElement, evalContext: TypeEvalContext) = local is PyNamedParameter && local.isFixture(evalContext) - override fun ignoreShadowed(element: PsiElement) = element is PyFunction - && (element.isFixture() || element.isCustomFixture()) + override fun ignoreShadowed(element: PsiElement) = element is PyFunction && element.isFixture() } \ No newline at end of file diff --git a/python/src/com/jetbrains/python/testing/pyTestFixtures/PyTestFixtureReferenceContributor.kt b/python/src/com/jetbrains/python/testing/pyTestFixtures/PyTestFixtureReferenceContributor.kt index ee670482e986..f4adf348fe80 100644 --- a/python/src/com/jetbrains/python/testing/pyTestFixtures/PyTestFixtureReferenceContributor.kt +++ b/python/src/com/jetbrains/python/testing/pyTestFixtures/PyTestFixtureReferenceContributor.kt @@ -13,7 +13,7 @@ import com.jetbrains.python.psi.types.PyType import com.jetbrains.python.psi.types.PyTypeProviderBase import com.jetbrains.python.psi.types.TypeEvalContext -private class PyTextFixtureReference(namedParameter: PyNamedParameter, fixture: PyTestFixture) : BaseReference(namedParameter) { +class PyTestFixtureReference(namedParameter: PyNamedParameter, fixture: PyTestFixture) : BaseReference(namedParameter) { private val functionRef = SmartPointerManager.createPointer(fixture.function) private val resolveRef = SmartPointerManager.createPointer(fixture.resolveTarget) @@ -33,7 +33,7 @@ private class PyTextFixtureReference(namedParameter: PyNamedParameter, fixture: object PyTextFixtureTypeProvider : PyTypeProviderBase() { override fun getReferenceType(referenceTarget: PsiElement, context: TypeEvalContext, anchor: PsiElement?): Ref? { val param = referenceTarget as? PyNamedParameter ?: return null - val fixtureFunc = param.references.filterIsInstance(PyTextFixtureReference::class.java).firstOrNull()?.getFunction() ?: return null + val fixtureFunc = param.references.filterIsInstance(PyTestFixtureReference::class.java).firstOrNull()?.getFunction() ?: return null return context.getReturnType(fixtureFunc)?.let { Ref(it) } } @@ -43,7 +43,7 @@ private object PyTestReferenceProvider : PsiReferenceProvider() { override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array { val namedParam = element as? PyNamedParameter ?: return emptyArray() val fixture = getFixture(namedParam, TypeEvalContext.codeAnalysis(element.project, element.containingFile)) ?: return emptyArray() - return arrayOf(PyTextFixtureReference(namedParam, fixture)) + return arrayOf(PyTestFixtureReference(namedParam, fixture)) } } diff --git a/python/src/com/jetbrains/python/testing/pyTestFixtures/PyTestFixtureTargetChecker.kt b/python/src/com/jetbrains/python/testing/pyTestFixtures/PyTestFixtureTargetChecker.kt new file mode 100644 index 000000000000..5076da993bb4 --- /dev/null +++ b/python/src/com/jetbrains/python/testing/pyTestFixtures/PyTestFixtureTargetChecker.kt @@ -0,0 +1,26 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.jetbrains.python.testing.pyTestFixtures + +import com.intellij.openapi.module.ModuleUtilCore +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference +import com.jetbrains.python.psi.PyFunction +import com.jetbrains.python.psi.PyNamedParameter +import com.jetbrains.python.psi.impl.references.PyReferenceCustomTargetChecker + +class PyTestFixtureTargetChecker : PyReferenceCustomTargetChecker { + override fun isReferenceTo(reference: PsiReference, to: PsiElement): Boolean { + val function = to as? PyFunction ?: return false + val module = ModuleUtilCore.findModuleForPsiElement(to) ?: return false + + // reference is reference from param usage to param + // param has reference to fixture + + if (function.isFixture() && isPyTestEnabled(module)) { + val parameter = reference.resolve() as? PyNamedParameter ?: return false + val ref = parameter.references.filterIsInstance().firstOrNull() ?: return false + return ref.isReferenceTo(to) + } + return false + } +} \ No newline at end of file