From 36c3fceb955654455d1d65cc1ddad7ee6c41e68b Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Tue, 30 Oct 2018 16:42:54 +0300 Subject: [PATCH] While `zope.interface.Interface` is defined as target with call assigned, check super class qualified name (PY-17038) --- .../inspections/PyInspectionExtension.java | 14 +++++++ python/src/META-INF/python-core-common.xml | 1 + .../PyMethodParametersInspection.java | 27 +------------- .../PyThirdPartyInspectionExtension.kt | 37 +++++++++++++++++++ 4 files changed, 54 insertions(+), 25 deletions(-) create mode 100644 python/src/com/jetbrains/python/inspections/PyThirdPartyInspectionExtension.kt diff --git a/python/psi-api/src/com/jetbrains/python/inspections/PyInspectionExtension.java b/python/psi-api/src/com/jetbrains/python/inspections/PyInspectionExtension.java index bfb2de030fd0..a296f457bccf 100644 --- a/python/psi-api/src/com/jetbrains/python/inspections/PyInspectionExtension.java +++ b/python/psi-api/src/com/jetbrains/python/inspections/PyInspectionExtension.java @@ -44,10 +44,24 @@ public abstract class PyInspectionExtension { return null; } + /** + * @deprecated Use {@link PyInspectionExtension#ignoreMethodParameters(PyFunction, TypeEvalContext)} instead. + * This method will be removed in 2019.2. + */ + @Deprecated public boolean ignoreMethodParameters(@NotNull PyFunction function) { return false; } + /** + * @param function function that is inspecting in {@link com.jetbrains.python.inspections.PyMethodParametersInspection} + * @param context type evaluation context + * @return true if the passed function could be ignored + */ + public boolean ignoreMethodParameters(@NotNull PyFunction function, @NotNull TypeEvalContext context) { + return ignoreMethodParameters(function); + } + public boolean ignorePackageNameInRequirements(@NotNull PyQualifiedExpression importedExpression) { return false; } diff --git a/python/src/META-INF/python-core-common.xml b/python/src/META-INF/python-core-common.xml index 86bd3d429a83..82b42bf65eb8 100644 --- a/python/src/META-INF/python-core-common.xml +++ b/python/src/META-INF/python-core-common.xml @@ -814,6 +814,7 @@ + diff --git a/python/src/com/jetbrains/python/inspections/PyMethodParametersInspection.java b/python/src/com/jetbrains/python/inspections/PyMethodParametersInspection.java index 089980065e7a..725b8d381fd7 100644 --- a/python/src/com/jetbrains/python/inspections/PyMethodParametersInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyMethodParametersInspection.java @@ -8,17 +8,14 @@ import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.lang.ASTNode; import com.intellij.openapi.ui.ComboBox; -import com.intellij.openapi.util.Ref; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; -import com.intellij.psi.util.QualifiedName; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.inspections.quickfix.AddSelfQuickFix; import com.jetbrains.python.inspections.quickfix.RenameParameterQuickFix; import com.jetbrains.python.psi.*; -import com.jetbrains.python.psi.resolve.ResolveImportUtil; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -87,39 +84,19 @@ public class PyMethodParametersInspection extends PyInspection { } public class Visitor extends PyInspectionVisitor { - private Ref myPossibleZopeRef = null; public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session) { super(holder, session); } - @Nullable - private PsiElement findZopeInterface(PsiElement foothold) { - PsiElement ret; - synchronized (this) { // other threads would wait as long in resolveInRoots() anyway - if (myPossibleZopeRef == null) { - myPossibleZopeRef = new Ref<>(); - ret = ResolveImportUtil.resolveModuleInRoots(QualifiedName.fromDottedString("zope.interface.Interface"), foothold); - myPossibleZopeRef.set(ret); // null is OK - } - else ret = myPossibleZopeRef.get(); - } - return ret; - } - @Override public void visitPyFunction(final PyFunction node) { for (PyInspectionExtension extension : PyInspectionExtension.EP_NAME.getExtensionList()) { - if (extension.ignoreMethodParameters(node)) { + if (extension.ignoreMethodParameters(node, myTypeEvalContext)) { return; } } - // maybe it's a zope interface? - PsiElement zope_interface = findZopeInterface(node); - final PyClass cls = node.getContainingClass(); - if (zope_interface instanceof PyClass) { - if (cls != null && cls.isSubclass((PyClass) zope_interface, myTypeEvalContext)) return; // it can have any params - } + // analyze function itself PyUtil.MethodFlags flags = PyUtil.MethodFlags.of(node); if (flags != null) { diff --git a/python/src/com/jetbrains/python/inspections/PyThirdPartyInspectionExtension.kt b/python/src/com/jetbrains/python/inspections/PyThirdPartyInspectionExtension.kt new file mode 100644 index 000000000000..054b7d8a7913 --- /dev/null +++ b/python/src/com/jetbrains/python/inspections/PyThirdPartyInspectionExtension.kt @@ -0,0 +1,37 @@ +// 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.inspections + +import com.intellij.util.containers.ContainerUtil +import com.jetbrains.python.psi.PyElement +import com.jetbrains.python.psi.PyExpression +import com.jetbrains.python.psi.PyFunction +import com.jetbrains.python.psi.PyUtil +import com.jetbrains.python.psi.resolve.PyResolveContext +import com.jetbrains.python.psi.resolve.QualifiedNameFinder +import com.jetbrains.python.psi.types.TypeEvalContext + +class PyThirdPartyInspectionExtension : PyInspectionExtension() { + + override fun ignoreMethodParameters(function: PyFunction, context: TypeEvalContext): Boolean { + val cls = function.containingClass + if (cls != null) { + // zope.interface.Interface inheritor could have any parameters + val interfaceQName = "zope.interface.interface.Interface" + if (cls.isSubclass(interfaceQName, context)) return true + + // Checking for subclassing above does not help while zope.interface.Interface is defined as target with call expression assigned + val resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context) + for (expression in cls.superClassExpressions) { + if (resolvesTo(expression, interfaceQName, resolveContext)) return true + } + } + + return false + } + + private fun resolvesTo(expression: PyExpression, qualifiedName: String, resolveContext: PyResolveContext): Boolean { + return ContainerUtil.exists(PyUtil.multiResolveTopPriority(expression, resolveContext)) { + it is PyElement && QualifiedNameFinder.getQualifiedName(it) == qualifiedName + } + } +} \ No newline at end of file