PY-74012 Deprecation decorator stub resolves during indexing

Avoid using resolve when calculating deprecation messages

GitOrigin-RevId: 813849bdaeb3819b445db600fc0efbcb014ed5c9
This commit is contained in:
Andrey Vokin
2024-07-19 19:18:54 +00:00
committed by intellij-monorepo-bot
parent 2f610dc4a8
commit e6c66dfd5e
9 changed files with 54 additions and 55 deletions
@@ -1,21 +0,0 @@
package com.jetbrains.python.ast.impl
import com.jetbrains.python.ast.PyAstDecoratable
import com.jetbrains.python.ast.PyAstStringLiteralExpression
val deprecatedDecoratorContainers = arrayOf("typing_extensions.pyi", "warnings.pyi")
fun extractDeprecationMessageFromDecorator(element: PyAstDecoratable): String? {
val deprecatedDecorator = element.decoratorList?.decorators?.firstOrNull { it.name == "deprecated" } ?: return null
val annotationClass = deprecatedDecorator.callee?.reference?.resolve() ?: return null
if (annotationClass.containingFile?.name !in deprecatedDecoratorContainers) {
return null
}
if (deprecatedDecorator.arguments.isEmpty()) {
return null
}
val argument = deprecatedDecorator.arguments[0] as? PyAstStringLiteralExpression ?: return null
return argument.stringValue
}
@@ -36,8 +36,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
import static com.jetbrains.python.ast.impl.PyDeprecationUtilKt.extractDeprecationMessageFromDecorator;
/**
* Represents a class declaration in source.
*/
@@ -369,12 +367,6 @@ public interface PyClass extends PyAstClass, PsiNameIdentifierOwner, PyCompoundS
@Nullable
PyClassLikeType getType(@NotNull TypeEvalContext context);
@Nullable
@Override
default String getDeprecationMessage() {
return extractDeprecationMessageFromDecorator(this);
}
@Override
@Nullable
default PyStringLiteralExpression getDocStringExpression() {
@@ -5,5 +5,5 @@ import org.jetbrains.annotations.Nullable;
public interface PyDeprecatable {
@Nullable
String getDeprecationMessage();
default String getDeprecationMessage() { return null; }
}
@@ -14,10 +14,8 @@ import com.jetbrains.python.psi.types.TypeEvalContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
import static com.jetbrains.python.ast.impl.PyDeprecationUtilKt.extractDeprecationMessageFromDecorator;
/**
* Function declaration in source (the {@code def} and everything within).
@@ -72,27 +70,6 @@ public interface PyFunction extends PyAstFunction, StubBasedPsiElement<PyFunctio
return (PyStringLiteralExpression)PyAstFunction.super.getDocStringExpression();
}
/**
* If the function raises a DeprecationWarning or a PendingDeprecationWarning, returns the explanation text provided for the warning..
*
* @return the deprecation message or null if the function is not deprecated.
*/
@Nullable
@Override
default String getDeprecationMessage() {
return extractDeprecationMessage();
}
@Nullable
default String extractDeprecationMessage() {
String deprecationMessageFromDecorator = extractDeprecationMessageFromDecorator(this);
if (deprecationMessageFromDecorator != null) {
return deprecationMessageFromDecorator;
}
PyAstStatementList statementList = getStatementList();
return extractDeprecationMessage(Arrays.asList(statementList.getStatements()));
}
static @Nullable String extractDeprecationMessage(List<? extends PyAstStatement> statements) {
for (PyAstStatement statement : statements) {
if (statement instanceof PyAstExpressionStatement expressionStatement) {
@@ -56,6 +56,7 @@ import java.util.*;
import static com.intellij.openapi.util.text.StringUtil.join;
import static com.intellij.openapi.util.text.StringUtil.notNullize;
import static com.jetbrains.python.psi.PyUtil.as;
import static com.jetbrains.python.psi.impl.PyDeprecationUtilKt.extractDeprecationMessageFromDecorator;
public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyClass {
@@ -1311,7 +1312,7 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
if (stub != null) {
return stub.getDeprecationMessage();
}
return PyClass.super.getDeprecationMessage();
return extractDeprecationMessageFromDecorator(this);
}
@Nullable
@@ -0,0 +1,25 @@
package com.jetbrains.python.psi.impl
import com.jetbrains.python.ast.PyAstDecoratable
import com.jetbrains.python.ast.PyAstStringLiteralExpression
import com.jetbrains.python.psi.PyFromImportStatement
import com.jetbrains.python.psi.PyReferenceExpression
import com.jetbrains.python.psi.resolve.PyResolveUtil
val deprecationDecorators = arrayOf("typing_extensions.deprecated", "warnings.deprecated")
fun extractDeprecationMessageFromDecorator(element: PyAstDecoratable): String? {
val deprecatedDecorator = element.decoratorList?.decorators?.firstOrNull { it.name == "deprecated" } ?: return null
if (deprecatedDecorator.arguments.isEmpty()) {
return null
}
val decoratorCall = deprecatedDecorator.callee as? PyReferenceExpression ?: return null
if (decoratorCall.asQualifiedName()?.toString() !in deprecationDecorators) {
if (!PyResolveUtil.resolveLocally(decoratorCall).mapNotNull { it.parent as? PyFromImportStatement }.
flatMap { it.fullyQualifiedObjectNames }.any { it in deprecationDecorators }) {
return null
}
}
return (deprecatedDecorator.arguments[0] as? PyAstStringLiteralExpression)?.stringValue
}
@@ -46,6 +46,7 @@ import static com.jetbrains.python.ast.PyAstFunction.Modifier.CLASSMETHOD;
import static com.jetbrains.python.ast.PyAstFunction.Modifier.STATICMETHOD;
import static com.jetbrains.python.psi.PyUtil.as;
import static com.jetbrains.python.psi.impl.PyCallExpressionHelper.interpretAsModifierWrappingCall;
import static com.jetbrains.python.psi.impl.PyDeprecationUtilKt.extractDeprecationMessageFromDecorator;
public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements PyFunction {
@@ -349,7 +350,17 @@ public class PyFunctionImpl extends PyBaseElementImpl<PyFunctionStub> implements
if (stub != null) {
return stub.getDeprecationMessage();
}
return PyFunction.super.getDeprecationMessage();
return extractDeprecationMessage();
}
@Nullable
public String extractDeprecationMessage() {
String deprecationMessageFromDecorator = extractDeprecationMessageFromDecorator(this);
if (deprecationMessageFromDecorator != null) {
return deprecationMessageFromDecorator;
}
PyStatementList statementList = getStatementList();
return PyFunction.extractDeprecationMessage(Arrays.asList(statementList.getStatements()));
}
@Override
@@ -0,0 +1,8 @@
import warnings
@warnings.deprecated("deprecated")
class MyClass:
pass
var = <warning descr="deprecated">MyClass</warning>()
@@ -90,6 +90,12 @@ public class PyDeprecationTest extends PyTestCase {
myFixture.checkHighlighting(true, false, false);
}
public void testFqnDecorator() {
myFixture.enableInspections(PyDeprecationInspection.class);
myFixture.configureByFile("deprecation/fqnDeprecation.py");
myFixture.checkHighlighting(true, false, false);
}
public void testDeprecatedMethod() {
myFixture.enableInspections(PyDeprecationInspection.class);
myFixture.configureByFile("deprecation/deprecatedMethod.py");