While zope.interface.Interface is defined as target with call assigned, check super class qualified name (PY-17038)

This commit is contained in:
Semyon Proshev
2018-11-12 13:50:25 +03:00
parent 31577e142a
commit 36c3fceb95
4 changed files with 54 additions and 25 deletions
@@ -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;
}
@@ -814,6 +814,7 @@
<!-- Other -->
<pythonFlavorProvider implementation="com.jetbrains.python.sdk.flavors.MayaFlavorProvider"/>
<pyPregeneratedSkeletonsProvider id="default" implementation="com.jetbrains.python.sdk.skeletons.DefaultPregeneratedSkeletonsProvider"/>
<inspectionExtension implementation="com.jetbrains.python.inspections.PyThirdPartyInspectionExtension"/>
</extensions>
<actions>
@@ -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<PsiElement> 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) {
@@ -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
}
}
}