PY-71002 PEP-696: Support new syntax for default types of Type Parameters in new-style declarations

- PEP-696 adds a new syntax for declaring the default types of Type Parameters in new-new style generic classes, functions and type alias statements. Support these grammar changes.
- Store info about default types in stubs for Type Parameters
- Increment the stub version counter in PyFileElementType

GitOrigin-RevId: b6b22e3eaa86ce06132885781e5775a89bf4b840
This commit is contained in:
Daniil Kalinin
2024-08-12 10:49:01 +02:00
committed by intellij-monorepo-bot
parent d73bf77d9a
commit 7751fceaed
30 changed files with 393 additions and 23 deletions

View File

@@ -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();

View File

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

View File

@@ -12,9 +12,16 @@ import org.jetbrains.annotations.Nullable;
* For more information see <a href="https://peps.python.org/pep-0695/">PEP 695</a>
*/
public interface PyTypeParameter extends PyAstTypeParameter, PyElement, PsiNameIdentifierOwner, PyTypedElement, StubBasedPsiElement<PyTypeParameterStub> {
@Override
@Nullable
default PyExpression getBoundExpression() {
return (PyExpression)PyAstTypeParameter.super.getBoundExpression();
}
@Override
@Nullable
default PyExpression getDefaultExpression() {
return (PyExpression)PyAstTypeParameter.super.getDefaultExpression();
}
}

View File

@@ -11,6 +11,9 @@ public interface PyTypeParameterStub extends NamedStub<PyTypeParameter> {
@Nullable
String getBoundExpressionText();
@Nullable
String getDefaultExpressionText();
@NotNull
PyTypeParameter.Kind getKind();
}

View File

@@ -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 92;
return 93;
}
@Nullable

View File

@@ -58,6 +58,16 @@ public class PyTypeParameterImpl extends PyBaseElementImpl<PyTypeParameterStub>
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() {

View File

@@ -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<PyTypeParamete
@Override
@NotNull
public PyTypeParameterStub createStub(@NotNull PyTypeParameter psi, StubElement<? extends PsiElement> 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<PyTypeParamete
dataStream.writeName(stub.getName());
dataStream.writeVarInt(stub.getKind().getIndex());
dataStream.writeName(stub.getBoundExpressionText());
dataStream.writeName(stub.getDefaultExpressionText());
}
@Override
@@ -47,9 +49,11 @@ public class PyTypeParameterElementType extends PyStubElementType<PyTypeParamete
String name = dataStream.readNameString();
PyTypeParameter.Kind kind = PyTypeParameter.Kind.fromIndex(dataStream.readVarInt());
String boundExpressionText = dataStream.readNameString();
String defaultExpressionText = dataStream.readNameString();
return new PyTypeParameterStubImpl(name,
kind,
boundExpressionText,
defaultExpressionText,
parentStub, getStubElementType());
}

View File

@@ -12,6 +12,7 @@ public class PyTypeParameterStubImpl extends StubBase<PyTypeParameter> 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<PyTypeParameter> 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<PyTypeParameter> 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<PyTypeParameter> implement
@Override
public String toString() {
return "PyTypeParameterStub(name=" + myName + ", kind=" + myKind + ", bound=" + myBoundExpressionText + ")";
return "PyTypeParameterStub(name=" + myName +
", kind=" + myKind +
", bound=" + myBoundExpressionText +
", default=" + myDefaultExpressionText + ")";
}
}

View File

@@ -0,0 +1 @@
class Baz[**P = [int, str]]: ...

View File

@@ -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
<empty list>
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyStatementList
PyExpressionStatement
PyNoneLiteralExpression
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')

View File

@@ -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
<empty list>
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PySubscriptionExpression
PyReferenceExpression: list
PsiElement(Py:IDENTIFIER)('list')
PsiElement(Py:LBRACKET)('[')
PyReferenceExpression: T
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:RBRACKET)(']')
<empty list>

View File

@@ -0,0 +1 @@
type MyType[T = list[T] = list[T]

View File

@@ -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
<empty list>
PsiWhiteSpace(' ')
PsiElement(Py:EQ)('=')
PsiWhiteSpace(' ')
PySubscriptionExpression
PyReferenceExpression: list
PsiElement(Py:IDENTIFIER)('list')
PsiElement(Py:LBRACKET)('[')
PyReferenceExpression: T
PsiElement(Py:IDENTIFIER)('T')
PsiElement(Py:RBRACKET)(']')

View File

@@ -0,0 +1 @@
class Qux[*Ts = *tuple[int, bool]]: ...

View File

@@ -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
<empty list>
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyStatementList
PyExpressionStatement
PyNoneLiteralExpression
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')

View File

@@ -0,0 +1 @@
class Foo[T = str]: ...

View File

@@ -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
<empty list>
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyStatementList
PyExpressionStatement
PyNoneLiteralExpression
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')

View File

@@ -0,0 +1 @@
type Foo[T = str] = Bar[T]

View File

@@ -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)(']')

View File

@@ -0,0 +1 @@
class Foo[T = ]: ...

View File

@@ -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
<empty list>
PsiWhiteSpace(' ')
PsiElement(Py:RBRACKET)(']')
PyArgumentList
<empty list>
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyStatementList
PyExpressionStatement
PyNoneLiteralExpression
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')

View File

@@ -0,0 +1 @@
class Foo[T: str = ]: ...

View File

@@ -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
<empty list>
PsiWhiteSpace(' ')
PsiElement(Py:RBRACKET)(']')
PyArgumentList
<empty list>
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyStatementList
PyExpressionStatement
PyNoneLiteralExpression
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')

View File

@@ -0,0 +1 @@
class Foo[T: str = str]: ...

View File

@@ -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
<empty list>
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyStatementList
PyExpressionStatement
PyNoneLiteralExpression
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')
PsiElement(Py:DOT)('.')

View File

@@ -1 +1 @@
type myType[T, U: str, *Ts, **P] = Union[T, U]
type myType[T, U: str, M = int, N: float = list, *Ts, **P] = Union[T, U]

View File

@@ -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

View File

@@ -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

View File

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

View File

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