diff --git a/python/python-ast/src/com/jetbrains/python/ast/PyAstTypeParameter.java b/python/python-ast/src/com/jetbrains/python/ast/PyAstTypeParameter.java index 73c2be720360..8de376e0c38a 100644 --- a/python/python-ast/src/com/jetbrains/python/ast/PyAstTypeParameter.java +++ b/python/python-ast/src/com/jetbrains/python/ast/PyAstTypeParameter.java @@ -6,6 +6,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiNameIdentifierOwner; import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.PyTokenTypes; +import one.util.streamex.StreamEx; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -43,7 +44,30 @@ public interface PyAstTypeParameter extends PyAstElement, PsiNameIdentifierOwner @Nullable default PyAstExpression getBoundExpression() { - return PsiTreeUtil.getChildOfType(this, PyAstExpression.class); + PsiElement element = StreamEx.of(getChildren()) + .findFirst(child -> { + PsiElement e = PsiTreeUtil.skipWhitespacesBackward(child); + return e != null && e.getNode().getElementType() == PyTokenTypes.COLON; + }) + .orElse(null); + if (element instanceof PyAstExpression expression) { + return expression; + } + return null; + } + + @Nullable + default PyAstExpression getDefaultExpression() { + PsiElement element = StreamEx.of(getChildren()) + .findFirst(child -> { + PsiElement e = PsiTreeUtil.skipWhitespacesBackward(child); + return e != null && e.getNode().getElementType() == PyTokenTypes.EQ; + }) + .orElse(null); + if (element instanceof PyAstExpression expression) { + return expression; + } + return null; } /** @@ -61,6 +85,16 @@ public interface PyAstTypeParameter extends PyAstElement, PsiNameIdentifierOwner return null; } + @Nullable + default String getDefaultExpressionText() { + PyAstExpression defaultExpression = getDefaultExpression(); + if (defaultExpression != null) { + return defaultExpression.getText(); + } + + return null; + } + @NotNull default Kind getKind() { String paramText = getText(); diff --git a/python/python-parser/src/com/jetbrains/python/parsing/StatementParsing.java b/python/python-parser/src/com/jetbrains/python/parsing/StatementParsing.java index 7f9735a08bad..3e2a3b4a27b1 100644 --- a/python/python-parser/src/com/jetbrains/python/parsing/StatementParsing.java +++ b/python/python-parser/src/com/jetbrains/python/parsing/StatementParsing.java @@ -1056,7 +1056,7 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { nextToken(); } - if (!parseIdentifierOrSkip(PyTokenTypes.RBRACKET, PyTokenTypes.COMMA, PyTokenTypes.COLON)) { + if (!parseIdentifierOrSkip(PyTokenTypes.RBRACKET, PyTokenTypes.COMMA, PyTokenTypes.COLON, PyTokenTypes.EQ)) { typeParamMarker.drop(); return false; } @@ -1066,6 +1066,12 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper { myBuilder.error(PyParsingBundle.message("PARSE.expected.expression")); } } + + if (matchToken(PyTokenTypes.EQ)) { + if (!myContext.getExpressionParser().parseSingleExpression(false)) { + myBuilder.error(PyParsingBundle.message("PARSE.expected.expression")); + } + } typeParamMarker.done(PyElementTypes.TYPE_PARAMETER); return true; } diff --git a/python/python-psi-api/src/com/jetbrains/python/psi/PyTypeParameter.java b/python/python-psi-api/src/com/jetbrains/python/psi/PyTypeParameter.java index 40709d4c632c..67fe4fca90df 100644 --- a/python/python-psi-api/src/com/jetbrains/python/psi/PyTypeParameter.java +++ b/python/python-psi-api/src/com/jetbrains/python/psi/PyTypeParameter.java @@ -12,9 +12,16 @@ import org.jetbrains.annotations.Nullable; * For more information see PEP 695 */ public interface PyTypeParameter extends PyAstTypeParameter, PyElement, PsiNameIdentifierOwner, PyTypedElement, StubBasedPsiElement { + @Override @Nullable default PyExpression getBoundExpression() { return (PyExpression)PyAstTypeParameter.super.getBoundExpression(); } + + @Override + @Nullable + default PyExpression getDefaultExpression() { + return (PyExpression)PyAstTypeParameter.super.getDefaultExpression(); + } } diff --git a/python/python-psi-api/src/com/jetbrains/python/psi/stubs/PyTypeParameterStub.java b/python/python-psi-api/src/com/jetbrains/python/psi/stubs/PyTypeParameterStub.java index ee75d053face..f6ae6227a65d 100644 --- a/python/python-psi-api/src/com/jetbrains/python/psi/stubs/PyTypeParameterStub.java +++ b/python/python-psi-api/src/com/jetbrains/python/psi/stubs/PyTypeParameterStub.java @@ -11,6 +11,9 @@ public interface PyTypeParameterStub extends NamedStub { @Nullable String getBoundExpressionText(); + @Nullable + String getDefaultExpressionText(); + @NotNull PyTypeParameter.Kind getKind(); } diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/PyFileElementType.java b/python/python-psi-impl/src/com/jetbrains/python/psi/PyFileElementType.java index f394f56cb437..67f1031917bd 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/PyFileElementType.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/PyFileElementType.java @@ -60,7 +60,7 @@ public class PyFileElementType extends IStubFileElementType { @Override public int getStubVersion() { // Don't forget to update versions of indexes that use the updated stub-based elements - return 92; + return 93; } @Nullable diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyTypeParameterImpl.java b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyTypeParameterImpl.java index 8d895853538e..27c6d4302712 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyTypeParameterImpl.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyTypeParameterImpl.java @@ -58,6 +58,16 @@ public class PyTypeParameterImpl extends PyBaseElementImpl return PyTypeParameter.super.getBoundExpressionText(); } + @Override + public @Nullable String getDefaultExpressionText() { + PyTypeParameterStub stub = getStub(); + if (stub != null) { + return stub.getDefaultExpressionText(); + } + + return PyTypeParameter.super.getDefaultExpressionText(); + } + @Override @NotNull public PyTypeParameter.Kind getKind() { diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/stubs/PyTypeParameterElementType.java b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/stubs/PyTypeParameterElementType.java index 84278aaf0fd1..95c4ab0bcba4 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/stubs/PyTypeParameterElementType.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/stubs/PyTypeParameterElementType.java @@ -6,7 +6,6 @@ import com.intellij.psi.stubs.IStubElementType; import com.intellij.psi.stubs.StubElement; import com.intellij.psi.stubs.StubInputStream; import com.intellij.psi.stubs.StubOutputStream; -import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.PyStubElementTypes; import com.jetbrains.python.psi.PyStubElementType; import com.jetbrains.python.psi.PyTypeParameter; @@ -30,7 +29,9 @@ public class PyTypeParameterElementType extends PyStubElementType parentStub) { - return new PyTypeParameterStubImpl(psi.getName(), psi.getKind(), psi.getBoundExpression() != null ? psi.getBoundExpression().getText() : null, + return new PyTypeParameterStubImpl(psi.getName(), psi.getKind(), + psi.getBoundExpression() != null ? psi.getBoundExpression().getText() : null, + psi.getDefaultExpression() != null ? psi.getDefaultExpression().getText() : null, parentStub, getStubElementType()); } @@ -39,6 +40,7 @@ public class PyTypeParameterElementType extends PyStubElementType implement private final String myName; private final String myBoundExpressionText; + private final String myDefaultExpressionText; private final PyTypeParameter.Kind myKind; @@ -19,12 +20,14 @@ public class PyTypeParameterStubImpl extends StubBase implement public PyTypeParameterStubImpl(@Nullable String name, @NotNull PyTypeParameter.Kind type, @Nullable String boundExpressionText, + @Nullable String defaultExpressionText, @Nullable StubElement parent, @NotNull IStubElementType stubElementType) { super(parent, stubElementType); myName = name; myKind = type; myBoundExpressionText = boundExpressionText; + myDefaultExpressionText = defaultExpressionText; } @Override @@ -33,6 +36,12 @@ public class PyTypeParameterStubImpl extends StubBase implement return myBoundExpressionText; } + @Override + @Nullable + public String getDefaultExpressionText() { + return myDefaultExpressionText; + } + @Override @NotNull public PyTypeParameter.Kind getKind() { @@ -47,6 +56,9 @@ public class PyTypeParameterStubImpl extends StubBase implement @Override public String toString() { - return "PyTypeParameterStub(name=" + myName + ", kind=" + myKind + ", bound=" + myBoundExpressionText + ")"; + return "PyTypeParameterStub(name=" + myName + + ", kind=" + myKind + + ", bound=" + myBoundExpressionText + + ", default=" + myDefaultExpressionText + ")"; } } diff --git a/python/testData/psi/ParamSpecTypeParameterDefaultInClassDeclaration.py b/python/testData/psi/ParamSpecTypeParameterDefaultInClassDeclaration.py new file mode 100644 index 000000000000..4a9d6e832159 --- /dev/null +++ b/python/testData/psi/ParamSpecTypeParameterDefaultInClassDeclaration.py @@ -0,0 +1 @@ +class Baz[**P = [int, str]]: ... \ No newline at end of file diff --git a/python/testData/psi/ParamSpecTypeParameterDefaultInClassDeclaration.txt b/python/testData/psi/ParamSpecTypeParameterDefaultInClassDeclaration.txt new file mode 100644 index 000000000000..ac12f11efe70 --- /dev/null +++ b/python/testData/psi/ParamSpecTypeParameterDefaultInClassDeclaration.txt @@ -0,0 +1,33 @@ +PyFile:ParamSpecTypeParameterDefaultInClassDeclaration.py + PyClass: Baz + PsiElement(Py:CLASS_KEYWORD)('class') + PsiWhiteSpace(' ') + PsiElement(Py:IDENTIFIER)('Baz') + PyTypeParameterList + PsiElement(Py:LBRACKET)('[') + PyTypeParameter + PsiElement(Py:EXP)('**') + PsiElement(Py:IDENTIFIER)('P') + PsiWhiteSpace(' ') + PsiElement(Py:EQ)('=') + PsiWhiteSpace(' ') + PyListLiteralExpression + PsiElement(Py:LBRACKET)('[') + PyReferenceExpression: int + PsiElement(Py:IDENTIFIER)('int') + PsiElement(Py:COMMA)(',') + PsiWhiteSpace(' ') + PyReferenceExpression: str + PsiElement(Py:IDENTIFIER)('str') + PsiElement(Py:RBRACKET)(']') + PsiElement(Py:RBRACKET)(']') + PyArgumentList + + PsiElement(Py:COLON)(':') + PsiWhiteSpace(' ') + PyStatementList + PyExpressionStatement + PyNoneLiteralExpression + PsiElement(Py:DOT)('.') + PsiElement(Py:DOT)('.') + PsiElement(Py:DOT)('.') \ No newline at end of file diff --git a/python/testData/psi/TypeParameterListInTypeAliasStatementRecoveryNotClosedRightBracket.txt b/python/testData/psi/TypeParameterListInTypeAliasStatementRecoveryNotClosedRightBracket.txt index dfbe85b96804..d5e6d1b7b113 100644 --- a/python/testData/psi/TypeParameterListInTypeAliasStatementRecoveryNotClosedRightBracket.txt +++ b/python/testData/psi/TypeParameterListInTypeAliasStatementRecoveryNotClosedRightBracket.txt @@ -7,15 +7,15 @@ PyFile:TypeParameterListInTypeAliasStatementRecoveryNotClosedRightBracket.py PsiElement(Py:LBRACKET)('[') PyTypeParameter PsiElement(Py:IDENTIFIER)('T') + PsiWhiteSpace(' ') + PsiElement(Py:EQ)('=') + PsiWhiteSpace(' ') + PySubscriptionExpression + PyReferenceExpression: list + PsiElement(Py:IDENTIFIER)('list') + PsiElement(Py:LBRACKET)('[') + PyReferenceExpression: T + PsiElement(Py:IDENTIFIER)('T') + PsiElement(Py:RBRACKET)(']') PsiErrorElement:',' or ']' expected - - PsiWhiteSpace(' ') - PsiElement(Py:EQ)('=') - PsiWhiteSpace(' ') - PySubscriptionExpression - PyReferenceExpression: list - PsiElement(Py:IDENTIFIER)('list') - PsiElement(Py:LBRACKET)('[') - PyReferenceExpression: T - PsiElement(Py:IDENTIFIER)('T') - PsiElement(Py:RBRACKET)(']') \ No newline at end of file + \ No newline at end of file diff --git a/python/testData/psi/TypeParameterListInTypeAliasStatementRecoveryNotClosedRightBracketAfterDefault.py b/python/testData/psi/TypeParameterListInTypeAliasStatementRecoveryNotClosedRightBracketAfterDefault.py new file mode 100644 index 000000000000..aef65080c531 --- /dev/null +++ b/python/testData/psi/TypeParameterListInTypeAliasStatementRecoveryNotClosedRightBracketAfterDefault.py @@ -0,0 +1 @@ +type MyType[T = list[T] = list[T] \ No newline at end of file diff --git a/python/testData/psi/TypeParameterListInTypeAliasStatementRecoveryNotClosedRightBracketAfterDefault.txt b/python/testData/psi/TypeParameterListInTypeAliasStatementRecoveryNotClosedRightBracketAfterDefault.txt new file mode 100644 index 000000000000..0514c50a1e10 --- /dev/null +++ b/python/testData/psi/TypeParameterListInTypeAliasStatementRecoveryNotClosedRightBracketAfterDefault.txt @@ -0,0 +1,31 @@ +PyFile:TypeParameterListInTypeAliasStatementRecoveryNotClosedRightBracketAfterDefault.py + PyTypeAliasStatement + PsiElement(Py:TYPE_KEYWORD)('type') + PsiWhiteSpace(' ') + PsiElement(Py:IDENTIFIER)('MyType') + PyTypeParameterList + PsiElement(Py:LBRACKET)('[') + PyTypeParameter + PsiElement(Py:IDENTIFIER)('T') + PsiWhiteSpace(' ') + PsiElement(Py:EQ)('=') + PsiWhiteSpace(' ') + PySubscriptionExpression + PyReferenceExpression: list + PsiElement(Py:IDENTIFIER)('list') + PsiElement(Py:LBRACKET)('[') + PyReferenceExpression: T + PsiElement(Py:IDENTIFIER)('T') + PsiElement(Py:RBRACKET)(']') + PsiErrorElement:',' or ']' expected + + PsiWhiteSpace(' ') + PsiElement(Py:EQ)('=') + PsiWhiteSpace(' ') + PySubscriptionExpression + PyReferenceExpression: list + PsiElement(Py:IDENTIFIER)('list') + PsiElement(Py:LBRACKET)('[') + PyReferenceExpression: T + PsiElement(Py:IDENTIFIER)('T') + PsiElement(Py:RBRACKET)(']') \ No newline at end of file diff --git a/python/testData/psi/TypeVarTupleTypeParameterDefaultInClassDeclaration.py b/python/testData/psi/TypeVarTupleTypeParameterDefaultInClassDeclaration.py new file mode 100644 index 000000000000..d1f6abb2fe5c --- /dev/null +++ b/python/testData/psi/TypeVarTupleTypeParameterDefaultInClassDeclaration.py @@ -0,0 +1 @@ +class Qux[*Ts = *tuple[int, bool]]: ... \ No newline at end of file diff --git a/python/testData/psi/TypeVarTupleTypeParameterDefaultInClassDeclaration.txt b/python/testData/psi/TypeVarTupleTypeParameterDefaultInClassDeclaration.txt new file mode 100644 index 000000000000..7ee544fd2bd9 --- /dev/null +++ b/python/testData/psi/TypeVarTupleTypeParameterDefaultInClassDeclaration.txt @@ -0,0 +1,38 @@ +PyFile:TypeVarTupleTypeParameterDefaultInClassDeclaration.py + PyClass: Qux + PsiElement(Py:CLASS_KEYWORD)('class') + PsiWhiteSpace(' ') + PsiElement(Py:IDENTIFIER)('Qux') + PyTypeParameterList + PsiElement(Py:LBRACKET)('[') + PyTypeParameter + PsiElement(Py:MULT)('*') + PsiElement(Py:IDENTIFIER)('Ts') + PsiWhiteSpace(' ') + PsiElement(Py:EQ)('=') + PsiWhiteSpace(' ') + PyStarExpression + PsiElement(Py:MULT)('*') + PySubscriptionExpression + PyReferenceExpression: tuple + PsiElement(Py:IDENTIFIER)('tuple') + PsiElement(Py:LBRACKET)('[') + PyTupleExpression + PyReferenceExpression: int + PsiElement(Py:IDENTIFIER)('int') + PsiElement(Py:COMMA)(',') + PsiWhiteSpace(' ') + PyReferenceExpression: bool + PsiElement(Py:IDENTIFIER)('bool') + PsiElement(Py:RBRACKET)(']') + PsiElement(Py:RBRACKET)(']') + PyArgumentList + + PsiElement(Py:COLON)(':') + PsiWhiteSpace(' ') + PyStatementList + PyExpressionStatement + PyNoneLiteralExpression + PsiElement(Py:DOT)('.') + PsiElement(Py:DOT)('.') + PsiElement(Py:DOT)('.') \ No newline at end of file diff --git a/python/testData/psi/TypeVarTypeParameterDefaultInClassDeclaration.py b/python/testData/psi/TypeVarTypeParameterDefaultInClassDeclaration.py new file mode 100644 index 000000000000..b3f837e112a9 --- /dev/null +++ b/python/testData/psi/TypeVarTypeParameterDefaultInClassDeclaration.py @@ -0,0 +1 @@ +class Foo[T = str]: ... \ No newline at end of file diff --git a/python/testData/psi/TypeVarTypeParameterDefaultInClassDeclaration.txt b/python/testData/psi/TypeVarTypeParameterDefaultInClassDeclaration.txt new file mode 100644 index 000000000000..8f34eb4cc218 --- /dev/null +++ b/python/testData/psi/TypeVarTypeParameterDefaultInClassDeclaration.txt @@ -0,0 +1,25 @@ +PyFile:TypeVarTypeParameterDefaultInClassDeclaration.py + PyClass: Foo + PsiElement(Py:CLASS_KEYWORD)('class') + PsiWhiteSpace(' ') + PsiElement(Py:IDENTIFIER)('Foo') + PyTypeParameterList + PsiElement(Py:LBRACKET)('[') + PyTypeParameter + PsiElement(Py:IDENTIFIER)('T') + PsiWhiteSpace(' ') + PsiElement(Py:EQ)('=') + PsiWhiteSpace(' ') + PyReferenceExpression: str + PsiElement(Py:IDENTIFIER)('str') + PsiElement(Py:RBRACKET)(']') + PyArgumentList + + PsiElement(Py:COLON)(':') + PsiWhiteSpace(' ') + PyStatementList + PyExpressionStatement + PyNoneLiteralExpression + PsiElement(Py:DOT)('.') + PsiElement(Py:DOT)('.') + PsiElement(Py:DOT)('.') \ No newline at end of file diff --git a/python/testData/psi/TypeVarTypeParameterDefaultInTypeAliasStatement.py b/python/testData/psi/TypeVarTypeParameterDefaultInTypeAliasStatement.py new file mode 100644 index 000000000000..6d48fd22e8f4 --- /dev/null +++ b/python/testData/psi/TypeVarTypeParameterDefaultInTypeAliasStatement.py @@ -0,0 +1 @@ +type Foo[T = str] = Bar[T] \ No newline at end of file diff --git a/python/testData/psi/TypeVarTypeParameterDefaultInTypeAliasStatement.txt b/python/testData/psi/TypeVarTypeParameterDefaultInTypeAliasStatement.txt new file mode 100644 index 000000000000..d77d27d96859 --- /dev/null +++ b/python/testData/psi/TypeVarTypeParameterDefaultInTypeAliasStatement.txt @@ -0,0 +1,25 @@ +PyFile:TypeVarTypeParameterDefaultInTypeAliasStatement.py + PyTypeAliasStatement + PsiElement(Py:TYPE_KEYWORD)('type') + PsiWhiteSpace(' ') + PsiElement(Py:IDENTIFIER)('Foo') + PyTypeParameterList + PsiElement(Py:LBRACKET)('[') + PyTypeParameter + PsiElement(Py:IDENTIFIER)('T') + PsiWhiteSpace(' ') + PsiElement(Py:EQ)('=') + PsiWhiteSpace(' ') + PyReferenceExpression: str + PsiElement(Py:IDENTIFIER)('str') + PsiElement(Py:RBRACKET)(']') + PsiWhiteSpace(' ') + PsiElement(Py:EQ)('=') + PsiWhiteSpace(' ') + PySubscriptionExpression + PyReferenceExpression: Bar + PsiElement(Py:IDENTIFIER)('Bar') + PsiElement(Py:LBRACKET)('[') + PyReferenceExpression: T + PsiElement(Py:IDENTIFIER)('T') + PsiElement(Py:RBRACKET)(']') \ No newline at end of file diff --git a/python/testData/psi/TypeVarTypeParameterDefaultMissingExpression.py b/python/testData/psi/TypeVarTypeParameterDefaultMissingExpression.py new file mode 100644 index 000000000000..2cab1ecac584 --- /dev/null +++ b/python/testData/psi/TypeVarTypeParameterDefaultMissingExpression.py @@ -0,0 +1 @@ +class Foo[T = ]: ... \ No newline at end of file diff --git a/python/testData/psi/TypeVarTypeParameterDefaultMissingExpression.txt b/python/testData/psi/TypeVarTypeParameterDefaultMissingExpression.txt new file mode 100644 index 000000000000..6c2166373fee --- /dev/null +++ b/python/testData/psi/TypeVarTypeParameterDefaultMissingExpression.txt @@ -0,0 +1,25 @@ +PyFile:TypeVarTypeParameterDefaultMissingExpression.py + PyClass: Foo + PsiElement(Py:CLASS_KEYWORD)('class') + PsiWhiteSpace(' ') + PsiElement(Py:IDENTIFIER)('Foo') + PyTypeParameterList + PsiElement(Py:LBRACKET)('[') + PyTypeParameter + PsiElement(Py:IDENTIFIER)('T') + PsiWhiteSpace(' ') + PsiElement(Py:EQ)('=') + PsiErrorElement:Expression expected + + PsiWhiteSpace(' ') + PsiElement(Py:RBRACKET)(']') + PyArgumentList + + PsiElement(Py:COLON)(':') + PsiWhiteSpace(' ') + PyStatementList + PyExpressionStatement + PyNoneLiteralExpression + PsiElement(Py:DOT)('.') + PsiElement(Py:DOT)('.') + PsiElement(Py:DOT)('.') \ No newline at end of file diff --git a/python/testData/psi/TypeVarTypeParameterWithBoundAndDefaultMissingExpression.py b/python/testData/psi/TypeVarTypeParameterWithBoundAndDefaultMissingExpression.py new file mode 100644 index 000000000000..81e150de9860 --- /dev/null +++ b/python/testData/psi/TypeVarTypeParameterWithBoundAndDefaultMissingExpression.py @@ -0,0 +1 @@ +class Foo[T: str = ]: ... \ No newline at end of file diff --git a/python/testData/psi/TypeVarTypeParameterWithBoundAndDefaultMissingExpression.txt b/python/testData/psi/TypeVarTypeParameterWithBoundAndDefaultMissingExpression.txt new file mode 100644 index 000000000000..2b1208c5040c --- /dev/null +++ b/python/testData/psi/TypeVarTypeParameterWithBoundAndDefaultMissingExpression.txt @@ -0,0 +1,29 @@ +PyFile:TypeVarTypeParameterWithBoundAndDefaultMissingExpression.py + PyClass: Foo + PsiElement(Py:CLASS_KEYWORD)('class') + PsiWhiteSpace(' ') + PsiElement(Py:IDENTIFIER)('Foo') + PyTypeParameterList + PsiElement(Py:LBRACKET)('[') + PyTypeParameter + PsiElement(Py:IDENTIFIER)('T') + PsiElement(Py:COLON)(':') + PsiWhiteSpace(' ') + PyReferenceExpression: str + PsiElement(Py:IDENTIFIER)('str') + PsiWhiteSpace(' ') + PsiElement(Py:EQ)('=') + PsiErrorElement:Expression expected + + PsiWhiteSpace(' ') + PsiElement(Py:RBRACKET)(']') + PyArgumentList + + PsiElement(Py:COLON)(':') + PsiWhiteSpace(' ') + PyStatementList + PyExpressionStatement + PyNoneLiteralExpression + PsiElement(Py:DOT)('.') + PsiElement(Py:DOT)('.') + PsiElement(Py:DOT)('.') \ No newline at end of file diff --git a/python/testData/psi/TypeVarTypeParameterWithDefaultAndBoundInClassDeclaration.py b/python/testData/psi/TypeVarTypeParameterWithDefaultAndBoundInClassDeclaration.py new file mode 100644 index 000000000000..39aff80ec3ee --- /dev/null +++ b/python/testData/psi/TypeVarTypeParameterWithDefaultAndBoundInClassDeclaration.py @@ -0,0 +1 @@ +class Foo[T: str = str]: ... \ No newline at end of file diff --git a/python/testData/psi/TypeVarTypeParameterWithDefaultAndBoundInClassDeclaration.txt b/python/testData/psi/TypeVarTypeParameterWithDefaultAndBoundInClassDeclaration.txt new file mode 100644 index 000000000000..2143889cf96f --- /dev/null +++ b/python/testData/psi/TypeVarTypeParameterWithDefaultAndBoundInClassDeclaration.txt @@ -0,0 +1,29 @@ +PyFile:TypeVarTypeParameterWithDefaultAndBoundInClassDeclaration.py + PyClass: Foo + PsiElement(Py:CLASS_KEYWORD)('class') + PsiWhiteSpace(' ') + PsiElement(Py:IDENTIFIER)('Foo') + PyTypeParameterList + PsiElement(Py:LBRACKET)('[') + PyTypeParameter + PsiElement(Py:IDENTIFIER)('T') + PsiElement(Py:COLON)(':') + PsiWhiteSpace(' ') + PyReferenceExpression: str + PsiElement(Py:IDENTIFIER)('str') + PsiWhiteSpace(' ') + PsiElement(Py:EQ)('=') + PsiWhiteSpace(' ') + PyReferenceExpression: str + PsiElement(Py:IDENTIFIER)('str') + PsiElement(Py:RBRACKET)(']') + PyArgumentList + + PsiElement(Py:COLON)(':') + PsiWhiteSpace(' ') + PyStatementList + PyExpressionStatement + PyNoneLiteralExpression + PsiElement(Py:DOT)('.') + PsiElement(Py:DOT)('.') + PsiElement(Py:DOT)('.') \ No newline at end of file diff --git a/python/testData/stubs/TypeAliasStatement.py b/python/testData/stubs/TypeAliasStatement.py index f4f7e977ca56..88628d0352a1 100644 --- a/python/testData/stubs/TypeAliasStatement.py +++ b/python/testData/stubs/TypeAliasStatement.py @@ -1 +1 @@ -type myType[T, U: str, *Ts, **P] = Union[T, U] \ No newline at end of file +type myType[T, U: str, M = int, N: float = list, *Ts, **P] = Union[T, U] \ No newline at end of file diff --git a/python/testData/stubs/TypeParameterListInClassDeclaration.py b/python/testData/stubs/TypeParameterListInClassDeclaration.py index fd5a237cfb7b..267c7287e7dd 100644 --- a/python/testData/stubs/TypeParameterListInClassDeclaration.py +++ b/python/testData/stubs/TypeParameterListInClassDeclaration.py @@ -1,3 +1,3 @@ -class Clazz[T, U: str, *Ts, **P]: +class Clazz[T, U: str, M = int, N: float = list, *Ts, **P]: a: T b: U \ No newline at end of file diff --git a/python/testData/stubs/TypeParameterListInFunctionDeclaration.py b/python/testData/stubs/TypeParameterListInFunctionDeclaration.py index 63bbb93d15c7..fec50affa6a9 100644 --- a/python/testData/stubs/TypeParameterListInFunctionDeclaration.py +++ b/python/testData/stubs/TypeParameterListInFunctionDeclaration.py @@ -1,2 +1,2 @@ -def foo[T, U: str, *Ts, **P](a: T, b: U): +def foo[T, U: str, M = int, N: float = list, *Ts, **P](a: T, b: U): pass \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyStubsTest.java b/python/testSrc/com/jetbrains/python/PyStubsTest.java index de03c777c8f1..cc34f39bf549 100644 --- a/python/testSrc/com/jetbrains/python/PyStubsTest.java +++ b/python/testSrc/com/jetbrains/python/PyStubsTest.java @@ -1227,11 +1227,15 @@ public class PyStubsTest extends PyTestCase { private static void doTestTypeParameterStub(@NotNull PyTypeParameterListOwner parameterListOwner, @NotNull PyFile file) { PyTypeParameter firstTypeParameter = parameterListOwner.getTypeParameterList().getTypeParameters().get(0); PyTypeParameter secondTypeParameter = parameterListOwner.getTypeParameterList().getTypeParameters().get(1); - PyTypeParameter typeVarTupleTypeParameter = parameterListOwner.getTypeParameterList().getTypeParameters().get(2); - PyTypeParameter paramSpecTypeParameter = parameterListOwner.getTypeParameterList().getTypeParameters().get(3); + PyTypeParameter thirdTypeParameter = parameterListOwner.getTypeParameterList().getTypeParameters().get(2); + PyTypeParameter fourthTypeParameter = parameterListOwner.getTypeParameterList().getTypeParameters().get(3); + PyTypeParameter typeVarTupleTypeParameter = parameterListOwner.getTypeParameterList().getTypeParameters().get(4); + PyTypeParameter paramSpecTypeParameter = parameterListOwner.getTypeParameterList().getTypeParameters().get(5); assertNotNull(firstTypeParameter); assertNotNull(secondTypeParameter); + assertNotNull(thirdTypeParameter); + assertNotNull(fourthTypeParameter); assertNotNull(typeVarTupleTypeParameter); assertNotNull(paramSpecTypeParameter); @@ -1239,13 +1243,20 @@ public class PyStubsTest extends PyTestCase { assertEquals(PyTypeParameter.Kind.TypeVar, firstTypeParameter.getKind()); assertEquals("U", secondTypeParameter.getName()); assertEquals(PyTypeParameter.Kind.TypeVar, secondTypeParameter.getKind()); + assertEquals("M", thirdTypeParameter.getName()); + assertEquals(PyTypeParameter.Kind.TypeVar, thirdTypeParameter.getKind()); + assertEquals("N", fourthTypeParameter.getName()); + assertEquals(PyTypeParameter.Kind.TypeVar, fourthTypeParameter.getKind()); assertEquals("Ts", typeVarTupleTypeParameter.getName()); assertEquals(PyTypeParameter.Kind.TypeVarTuple, typeVarTupleTypeParameter.getKind()); assertEquals("P", paramSpecTypeParameter.getName()); assertEquals(PyTypeParameter.Kind.ParamSpec, paramSpecTypeParameter.getKind()); assertNull(firstTypeParameter.getBoundExpressionText()); - assertEquals(secondTypeParameter.getBoundExpressionText(), "str"); + assertEquals("str", secondTypeParameter.getBoundExpressionText()); + assertEquals("int", thirdTypeParameter.getDefaultExpressionText()); + assertEquals("list", fourthTypeParameter.getDefaultExpressionText()); + assertEquals("float", fourthTypeParameter.getBoundExpressionText()); assertNotParsed(file); } diff --git a/python/testSrc/com/jetbrains/python/parsing/PythonParsingTest.java b/python/testSrc/com/jetbrains/python/parsing/PythonParsingTest.java index 8c4f6b065d38..21ea270a98c0 100644 --- a/python/testSrc/com/jetbrains/python/parsing/PythonParsingTest.java +++ b/python/testSrc/com/jetbrains/python/parsing/PythonParsingTest.java @@ -1308,6 +1308,11 @@ public class PythonParsingTest extends ParsingTestCase { doTest(LanguageLevel.PYTHON312); } + // PY-71002 + public void testTypeParameterListInTypeAliasStatementRecoveryNotClosedRightBracketAfterDefault() { + doTest(LanguageLevel.PYTHON313); + } + public void testTypeParameterListInTypeAliasStatementRecoveryUnexpectedSymbolAfterComma() { doTest(LanguageLevel.PYTHON312); } @@ -1324,6 +1329,41 @@ public class PythonParsingTest extends ParsingTestCase { doTest(LanguageLevel.PYTHON312); } + // PY-71002 + public void testTypeVarTypeParameterDefaultInClassDeclaration() { + doTest(LanguageLevel.PYTHON313); + } + + // PY-71002 + public void testTypeVarTypeParameterDefaultInTypeAliasStatement() { + doTest(LanguageLevel.PYTHON313); + } + + // PY-71002 + public void testParamSpecTypeParameterDefaultInClassDeclaration() { + doTest(LanguageLevel.PYTHON313); + } + + // PY-71002 + public void testTypeVarTupleTypeParameterDefaultInClassDeclaration() { + doTest(LanguageLevel.PYTHON313); + } + + // PY-71002 + public void testTypeVarTypeParameterWithDefaultAndBoundInClassDeclaration() { + doTest(LanguageLevel.PYTHON313); + } + + // PY-71002 + public void testTypeVarTypeParameterDefaultMissingExpression() { + doTest(LanguageLevel.PYTHON313); + } + + // PY-71002 + public void testTypeVarTypeParameterWithBoundAndDefaultMissingExpression() { + doTest(LanguageLevel.PYTHON313); + } + public void testTypeKeywordAsIdentifier() { doTest(LanguageLevel.PYTHON312); }