[PY-33917] Now '__init_subclass__' is treated as and constructor-like method in appropriate cases

GitOrigin-RevId: c2ac58960f641868006141ae3001c932c6a8eaaf
This commit is contained in:
Alexander Kuklev
2024-11-22 07:52:25 +01:00
committed by intellij-monorepo-bot
parent ac7fd43a32
commit b076ea2794
11 changed files with 41 additions and 10 deletions

View File

@@ -41,7 +41,7 @@ public interface PyAstCallExpression extends PyAstCallSiteExpression {
@Contract("null -> false")
private static boolean isImplicitlyInvokedMethod(@Nullable PyAstCallable resolvedCallee) {
if (PyUtilCore.isInitOrNewMethod(resolvedCallee)) return true;
if (PyUtilCore.isConstructorLikeMethod(resolvedCallee)) return true;
if (resolvedCallee instanceof PyAstFunction function) {
return PyNames.CALL.equals(function.getName()) && function.getContainingClass() != null;

View File

@@ -191,7 +191,26 @@ public final class PyUtilCore {
if (function == null) return false;
final String name = function.getName();
return (PyNames.INIT.equals(name) || PyNames.NEW.equals(name)) && function.getContainingClass() != null;
return (PyNames.INIT.equals(name) ||
PyNames.NEW.equals(name)) && function.getContainingClass() != null;
}
/**
* @return true if passed {@code element} is a method (this means a function inside a class) named {@code __init__},
* {@code __init_subclass__}, or {@code __new__}.
* @see PyUtil#isInitMethod(PsiElement)
* @see PyUtil#isNewMethod(PsiElement)
* @see PyUtil#turnConstructorIntoClass(PyFunction)
*/
@Contract("null -> false")
public static boolean isConstructorLikeMethod(@Nullable PsiElement element) {
final PyAstFunction function = ObjectUtils.tryCast(element, PyAstFunction.class);
if (function == null) return false;
final String name = function.getName();
return (PyNames.INIT_SUBCLASS.equals(name) ||
PyNames.INIT.equals(name) ||
PyNames.NEW.equals(name)) && function.getContainingClass() != null;
}
public static boolean isStringLiteral(@Nullable PyAstStatement stmt) {

View File

@@ -187,7 +187,7 @@ public final class PyCompatibilityInspection extends PyInspection {
if (resolvedCallee instanceof PyFunction function) {
final PyClass containingClass = function.getContainingClass();
final String functionName = PyUtil.isInitOrNewMethod(function) ? callee.getText() : function.getName();
final String functionName = PyUtil.isConstructorLikeMethod(function) ? callee.getText() : function.getName();
if (containingClass != null) {
final String className = containingClass.getName();

View File

@@ -37,7 +37,7 @@ public final class PyMethodOverridingInspection extends PyInspection {
final PyClass cls = function.getContainingClass();
if (cls == null) return;
if (PyUtil.isInitOrNewMethod(function) ||
if (PyUtil.isConstructorLikeMethod(function) ||
PyKnownDecoratorUtil.hasUnknownOrChangingSignatureDecorator(function, myTypeEvalContext) ||
ContainerUtil.exists(PyInspectionExtension.EP_NAME.getExtensions(), e -> e.ignoreMethodParameters(function, myTypeEvalContext))) {
return;

View File

@@ -1482,6 +1482,18 @@ public final class PyUtil {
public static boolean isInitOrNewMethod(@Nullable PsiElement element) {
return PyUtilCore.isInitOrNewMethod(element);
}
/**
* @return true if passed {@code element} is a method (this means a function inside a class) named {@code __init__},
* {@code __init_subclass__}, or {@code __new__}.
* @see PyUtil#isInitMethod(PsiElement)
* @see PyUtil#isNewMethod(PsiElement)
* @see PyUtil#turnConstructorIntoClass(PyFunction)
*/
@Contract("null -> false")
public static boolean isConstructorLikeMethod(@Nullable PsiElement element) {
return PyUtilCore.isConstructorLikeMethod(element);
}
/**
* @return containing class for a method named {@code __init__} or {@code __new__}.

View File

@@ -353,7 +353,7 @@ public class PyQualifiedReference extends PyReferenceImpl {
if (pyElement == null) {
return false;
}
if (Objects.equals(referencedName, pyElement.getName()) && !PyUtil.isInitOrNewMethod(element)) {
if (Objects.equals(referencedName, pyElement.getName()) && !PyUtil.isConstructorLikeMethod(element)) {
final PyExpression qualifier = myElement.getQualifier();
if (qualifier != null) {
final PyType qualifierType = resolveContext.getTypeEvalContext().getType(qualifier);

View File

@@ -54,7 +54,7 @@ class PyInlineFunctionHandler : InlineActionHandler() {
val error = when {
element.isAsync -> PyPsiBundle.message("refactoring.inline.function.async")
element.isGenerator -> PyPsiBundle.message("refactoring.inline.function.generator")
PyUtil.isInitOrNewMethod(element) -> PyPsiBundle.message("refactoring.inline.function.constructor")
PyUtil.isConstructorLikeMethod(element) -> PyPsiBundle.message("refactoring.inline.function.constructor")
PyBuiltinCache.getInstance(element).isBuiltin(element) -> PyPsiBundle.message("refactoring.inline.function.builtin")
isSpecialMethod(element) -> PyPsiBundle.message("refactoring.inline.function.special.method")
isUnderSkeletonDir(element) -> PyPsiBundle.message("refactoring.inline.function.skeleton.only")

View File

@@ -196,7 +196,7 @@ public final class PyDocstringGenerator {
final RaiseVisitor visitor = new RaiseVisitor();
final PyAstStatementList statementList = ((PyAstFunction)myDocStringOwner).getStatementList();
statementList.accept(visitor);
if (!PyUtilCore.isInitOrNewMethod(myDocStringOwner) && (visitor.myHasReturn || addReturn)) {
if (!PyUtilCore.isConstructorLikeMethod(myDocStringOwner) && (visitor.myHasReturn || addReturn)) {
// will add :return: placeholder in Sphinx/Epydoc docstrings
withReturnValue(null);
}

View File

@@ -197,7 +197,7 @@ public final class PyLineMarkerProvider implements LineMarkerProvider, PyLineSep
}
private static @Nullable LineMarkerInfo<PsiElement> getMethodMarker(final PsiElement identifier, final PyFunction function) {
if (PyUtil.isInitMethod(function)) {
if (PyUtil.isConstructorLikeMethod(function)) {
return null;
}
final TypeEvalContext context = TypeEvalContext.codeAnalysis(identifier.getProject(), function.getContainingFile());

View File

@@ -134,7 +134,7 @@ public class PyChangeSignatureHandler implements ChangeSignatureHandler {
if (containingClass == null) {
return function;
}
if (PyUtil.isInitOrNewMethod(function)) {
if (PyUtil.isConstructorLikeMethod(function)) {
return function;
}
final PyFunction deepestSuperMethod = PySuperMethodsSearch.findDeepestSuperMethod(function);

View File

@@ -42,7 +42,7 @@ public class PyChangeSignatureUsageProcessor implements ChangeSignatureUsageProc
if (info instanceof PyChangeInfo) {
final PyFunction targetFunction = ((PyChangeInfo)info).getMethod();
final List<UsageInfo> usages = PyPsiIndexUtil.findUsages(targetFunction, true);
if (!PyUtil.isInitOrNewMethod(targetFunction)) {
if (!PyUtil.isConstructorLikeMethod(targetFunction)) {
final Query<PyFunction> search = PyOverridingMethodsSearch.search(targetFunction, true);
for (PyFunction override : search.findAll()) {
usages.add(new UsageInfo(override));