mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
PY-76820 Improvements in PyTypingAliasStubType
1. Handle type aliases with the other names imported via `as`, e.g. ``` from typing import TypeAlias as TA a: TA = list[int] ``` This is important for the conformance tests suite 2. Consider binary expressions with OR operators as type hints 3. Do not consider f-string and prefixed strings as type hints GitOrigin-RevId: fcdf7adb7aa3b921091772c521f26c4474a3877e
This commit is contained in:
committed by
intellij-monorepo-bot
parent
0c0380dc72
commit
16d35ed4ca
@@ -60,7 +60,7 @@ public class PyFileElementType extends IStubFileElementType<PyFileStub> {
|
||||
@Override
|
||||
public int getStubVersion() {
|
||||
// Don't forget to update versions of indexes that use the updated stub-based elements
|
||||
return 98;
|
||||
return 99;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,16 +17,24 @@ package com.jetbrains.python.psi.impl.stubs;
|
||||
|
||||
import com.intellij.extapi.psi.ASTDelegatePsiElement;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.stubs.StubInputStream;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.QualifiedName;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.ast.impl.PyUtilCore;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
|
||||
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveUtil;
|
||||
import com.jetbrains.python.psi.stubs.PyTargetExpressionStub;
|
||||
import com.jetbrains.python.psi.stubs.PyTargetExpressionStub.InitializerType;
|
||||
import com.jetbrains.python.psi.stubs.PyTypingAliasStub;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -96,18 +104,20 @@ public final class PyTypingAliasStubType extends CustomTargetExpressionStubType<
|
||||
}
|
||||
|
||||
private static boolean isExplicitTypeAlias(@NotNull PyTargetExpression target) {
|
||||
String typeHintText = "";
|
||||
PyAnnotation annotation = target.getAnnotation();
|
||||
if (annotation != null) {
|
||||
PyExpression value = annotation.getValue();
|
||||
if (value != null) {
|
||||
typeHintText = value.getText();
|
||||
if (value instanceof PyReferenceExpression referenceExpression) {
|
||||
return StreamEx.of(PyResolveUtil.resolveImportedElementQNameLocally(referenceExpression))
|
||||
.map(QualifiedName::toString)
|
||||
.anyMatch(name -> name.equals(PyTypingTypeProvider.TYPE_ALIAS) || name.equals(PyTypingTypeProvider.TYPE_ALIAS_EXT));
|
||||
}
|
||||
}
|
||||
else {
|
||||
typeHintText = StringUtil.notNullize(target.getTypeCommentAnnotation());
|
||||
String typeHintText = StringUtil.notNullize(target.getTypeCommentAnnotation());
|
||||
return typeHintText.equals("TypeAlias") || typeHintText.endsWith(".TypeAlias");
|
||||
}
|
||||
return typeHintText.equals("TypeAlias") || typeHintText.endsWith(".TypeAlias");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
@@ -123,9 +133,17 @@ public final class PyTypingAliasStubType extends CustomTargetExpressionStubType<
|
||||
|
||||
final PyStringLiteralExpression pyString = as(expression, PyStringLiteralExpression.class);
|
||||
if (pyString != null) {
|
||||
if (pyString.isInterpolated()) { // f-strings are not allowed
|
||||
return false;
|
||||
}
|
||||
if (pyString.getStringNodes().size() != 1 || pyString.getTextLength() > STRING_LITERAL_LENGTH_THRESHOLD) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
if (!pyString.getStringElements().get(0).getPrefix().isEmpty()) { // prefixed strings are not allowed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
final String content = pyString.getStringValue();
|
||||
return TYPE_ANNOTATION_LIKE.matcher(content).matches();
|
||||
}
|
||||
@@ -133,11 +151,22 @@ public final class PyTypingAliasStubType extends CustomTargetExpressionStubType<
|
||||
if (expression instanceof PyReferenceExpression || expression instanceof PySubscriptionExpression) {
|
||||
return isSyntacticallyValidAnnotation(expression);
|
||||
}
|
||||
if (expression instanceof PyBinaryExpression binaryExpression) {
|
||||
if (binaryExpression.getOperator() == PyTokenTypes.OR) {
|
||||
PyExpression leftOperand = binaryExpression.getLeftExpression();
|
||||
PyExpression rightOperand = binaryExpression.getRightExpression();
|
||||
return leftOperand != null && rightOperand != null &&
|
||||
isSyntacticallyValidAnnotation(leftOperand) && isSyntacticallyValidAnnotation(rightOperand);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isSyntacticallyValidAnnotation(@NotNull PyExpression expression) {
|
||||
if (expression instanceof PyBinaryExpression) {
|
||||
return looksLikeTypeHint(expression);
|
||||
}
|
||||
return PsiTreeUtil.processElements(expression, element -> {
|
||||
// Check only composite elements
|
||||
if (element instanceof ASTDelegatePsiElement) {
|
||||
|
||||
@@ -3,6 +3,7 @@ __all__ = ['S1', 'S2']
|
||||
__version__ = '0.1'
|
||||
|
||||
from typing_extensions import TypeAlias
|
||||
from typing import TypeAlias as TA
|
||||
|
||||
S1_ok = "foo"
|
||||
S2_ok = "foo.bar"
|
||||
@@ -11,8 +12,18 @@ too_long_string = "foo.foo.foo.foo.foo.foo.foo.foo.foo.foo.foo.foo.foo.foo.foo.f
|
||||
natural_text = "Foo is baz."
|
||||
glued_string = 'foo' '.bar'
|
||||
|
||||
S4_notOk = f"int"
|
||||
S5_notOk = u"int"
|
||||
S6_notOk = b"int"
|
||||
|
||||
bin1_ok = int | str
|
||||
bin2_ok = int | str | bool | None
|
||||
bin3_ok = Union[str, bool] | None
|
||||
bin4_notOk = str & int
|
||||
|
||||
explicit_alias1_ok: TypeAlias = 'Foo is bar.'
|
||||
explicit_alias2_ok = 'Foo is bar.' # type: TypeAlias
|
||||
explicit_alias_imported_via_as_ok: TA = int | str
|
||||
|
||||
# Such expressions are kept as qualified expressions in PyTargetExpressionStub
|
||||
# with initializer type of ReferenceExpression instead of custom stubs for
|
||||
|
||||
Reference in New Issue
Block a user