From ce9b67df15c69d233a297ee04c11e018a2fb03bd Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 26 Jul 2017 17:02:56 +0300 Subject: [PATCH] PY-25171 Fixed: False positive of signature check in metaclass's __new__ Update PyInitNewSignatureInspection to use PyClass.multiFindMethodByName while looking for complementary methods. --- .../PyInitNewSignatureInspection.java | 61 +++++++++++++------ .../PyInitNewSignatureInspection/test.py | 6 ++ 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/python/src/com/jetbrains/python/inspections/PyInitNewSignatureInspection.java b/python/src/com/jetbrains/python/inspections/PyInitNewSignatureInspection.java index 4a497b14893d..64f39161ef7d 100644 --- a/python/src/com/jetbrains/python/inspections/PyInitNewSignatureInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyInitNewSignatureInspection.java @@ -16,6 +16,7 @@ package com.jetbrains.python.inspections; import com.intellij.codeInspection.LocalInspectionToolSession; +import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.openapi.extensions.Extensions; import com.intellij.psi.PsiElementVisitor; @@ -25,11 +26,15 @@ import com.jetbrains.python.PyNames; import com.jetbrains.python.inspections.quickfix.PyChangeSignatureQuickFix; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyFunction; +import com.jetbrains.python.psi.PyParameterList; import com.jetbrains.python.psi.PyUtil; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; +import java.util.List; + /** * Detect and report incompatibilities between __new__ and __init__ signatures. * @@ -64,34 +69,52 @@ public class PyInitNewSignatureInspection extends PyInspection { final PyClass cls = node.getContainingClass(); if (cls == null || !cls.isNewStyleClass(myTypeEvalContext)) return; - final PyFunction complementaryMethod = findComplementaryMethod(cls, node); - if (complementaryMethod == null) return; + final List complementaryMethods = findComplementaryMethods(cls, node); - if (!PyUtil.isSignatureCompatibleTo(complementaryMethod, node, myTypeEvalContext) && - !PyUtil.isSignatureCompatibleTo(node, complementaryMethod, myTypeEvalContext) && - node.getContainingFile() == cls.getContainingFile()) { - registerProblem(node.getParameterList(), PyBundle.message(PyNames.NEW.equals(node.getName()) ? "INSP.new.incompatible.to.init" - : "INSP.init.incompatible.to.new"), - PyChangeSignatureQuickFix.forMismatchingMethods(node, complementaryMethod)); + for (PyFunction complementaryMethod : complementaryMethods) { + if (PyUtil.isSignatureCompatibleTo(complementaryMethod, node, myTypeEvalContext) || + PyUtil.isSignatureCompatibleTo(node, complementaryMethod, myTypeEvalContext)) { + return; + } + } + + if (complementaryMethods.size() == 1) { + registerIncompatibilityProblem(node, PyChangeSignatureQuickFix.forMismatchingMethods(node, complementaryMethods.get(0))); + } + else if (!complementaryMethods.isEmpty()) { + registerIncompatibilityProblem(node, null); } } - @Nullable - private PyFunction findComplementaryMethod(@NotNull PyClass cls, @NotNull PyFunction original) { + @NotNull + private List findComplementaryMethods(@NotNull PyClass cls, @NotNull PyFunction original) { final String complementaryName = PyNames.NEW.equals(original.getName()) ? PyNames.INIT : PyNames.NEW; - final PyFunction complementaryMethod = cls.findMethodByName(complementaryName, true, myTypeEvalContext); + final List complementaryMethods = cls.multiFindMethodByName(complementaryName, true, myTypeEvalContext); - if (complementaryMethod == null) return null; + for (PyFunction complementaryMethod : complementaryMethods) { + final PyClass complementaryMethodClass = complementaryMethod.getContainingClass(); - final PyClass complementaryMethodClass = complementaryMethod.getContainingClass(); - if (complementaryMethodClass == null || - PyUtil.isObjectClass(complementaryMethodClass) || - ContainerUtil.exists(Extensions.getExtensions(PyInspectionExtension.EP_NAME), - extension -> extension.ignoreInitNewSignatures(original, complementaryMethod))) { - return null; + if (complementaryMethodClass == null || + PyUtil.isObjectClass(complementaryMethodClass) || + ContainerUtil.exists(Extensions.getExtensions(PyInspectionExtension.EP_NAME), + extension -> extension.ignoreInitNewSignatures(original, complementaryMethod))) { + return Collections.emptyList(); + } } - return complementaryMethod; + return complementaryMethods; + } + + private void registerIncompatibilityProblem(@NotNull PyFunction function, @Nullable LocalQuickFix quickFix) { + final PyParameterList parameterList = function.getParameterList(); + final String message = PyBundle.message(PyNames.NEW.equals(function.getName()) ? "INSP.new.incompatible.to.init" + : "INSP.init.incompatible.to.new"); + if (quickFix != null) { + registerProblem(parameterList, message, quickFix); + } + else { + registerProblem(parameterList, message); + } } } } \ No newline at end of file diff --git a/python/testData/inspections/PyInitNewSignatureInspection/test.py b/python/testData/inspections/PyInitNewSignatureInspection/test.py index aef17b94e2da..49d725d98c87 100644 --- a/python/testData/inspections/PyInitNewSignatureInspection/test.py +++ b/python/testData/inspections/PyInitNewSignatureInspection/test.py @@ -40,4 +40,10 @@ class Planet(enum.Enum): EARTH = (5.976e+24, 6.37814e6) def __init__(self, mass, radius): # OK + pass + + +# PY-25171 +class Meta(type): + def __new__(mcls, name, bases, namespace): pass \ No newline at end of file