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);
}