diff --git a/libcst/__init__.py b/libcst/__init__.py index ff63033d5..2a8e47b31 100644 --- a/libcst/__init__.py +++ b/libcst/__init__.py @@ -183,6 +183,7 @@ MatchValue, NameItem, Nonlocal, + ParamSpec, Pass, Raise, Return, @@ -190,6 +191,11 @@ SimpleStatementSuite, Try, TryStar, + TypeAlias, + TypeParam, + TypeParameters, + TypeVar, + TypeVarTuple, While, With, WithItem, @@ -438,4 +444,10 @@ "VisitorMetadataProvider", "MetadataDependent", "MetadataWrapper", + "TypeVar", + "TypeVarTuple", + "ParamSpec", + "TypeParam", + "TypeParameters", + "TypeAlias", ] diff --git a/libcst/_nodes/statement.py b/libcst/_nodes/statement.py index de5161fa6..a9502da8f 100644 --- a/libcst/_nodes/statement.py +++ b/libcst/_nodes/statement.py @@ -48,6 +48,7 @@ AssignEqual, BaseAugOp, BitOr, + Colon, Comma, Dot, ImportStar, @@ -1738,8 +1739,8 @@ class FunctionDef(BaseCompoundStatement): #: Whitespace after the ``def`` keyword and before the function name. whitespace_after_def: SimpleWhitespace = SimpleWhitespace.field(" ") - #: Whitespace after the function name and before the opening parenthesis for - #: the parameters. + #: Whitespace after the function name and before the type parameters or the opening + #: parenthesis for the parameters. whitespace_after_name: SimpleWhitespace = SimpleWhitespace.field("") #: Whitespace after the opening parenthesis for the parameters but before @@ -1750,6 +1751,13 @@ class FunctionDef(BaseCompoundStatement): #: the colon. whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("") + #: An optional declaration of type parameters. + type_parameters: Optional["TypeParameters"] = None + + #: Whitespace between the type parameters and the opening parenthesis for the + #: (non-type) parameters. + whitespace_after_type_parameters: SimpleWhitespace = SimpleWhitespace.field("") + def _validate(self) -> None: if len(self.name.lpar) > 0 or len(self.name.rpar) > 0: raise CSTValidationError("Cannot have parens around Name in a FunctionDef.") @@ -1758,6 +1766,15 @@ def _validate(self) -> None: "There must be at least one space between 'def' and name." ) + if ( + self.type_parameters is None + and not self.whitespace_after_type_parameters.empty + ): + raise CSTValidationError( + "whitespace_after_type_parameters must be empty if there are no type " + "parameters in FunctionDef" + ) + def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "FunctionDef": return FunctionDef( leading_lines=visit_sequence( @@ -1777,6 +1794,15 @@ def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "FunctionDef": whitespace_after_name=visit_required( self, "whitespace_after_name", self.whitespace_after_name, visitor ), + type_parameters=visit_optional( + self, "type_parameters", self.type_parameters, visitor + ), + whitespace_after_type_parameters=visit_required( + self, + "whitespace_after_type_parameters", + self.whitespace_after_type_parameters, + visitor, + ), whitespace_before_params=visit_required( self, "whitespace_before_params", self.whitespace_before_params, visitor ), @@ -1805,6 +1831,10 @@ def _codegen_impl(self, state: CodegenState) -> None: self.whitespace_after_def._codegen(state) self.name._codegen(state) self.whitespace_after_name._codegen(state) + type_params = self.type_parameters + if type_params is not None: + type_params._codegen(state) + self.whitespace_after_type_parameters._codegen(state) state.add_token("(") self.whitespace_before_params._codegen(state) self.params._codegen(state) @@ -1866,19 +1896,34 @@ class ClassDef(BaseCompoundStatement): #: Whitespace after the ``class`` keyword and before the class name. whitespace_after_class: SimpleWhitespace = SimpleWhitespace.field(" ") - #: Whitespace after the class name and before the opening parenthesis for - #: the bases and keywords. + #: Whitespace after the class name and before the type parameters or the opening + #: parenthesis for the bases and keywords. whitespace_after_name: SimpleWhitespace = SimpleWhitespace.field("") #: Whitespace after the closing parenthesis or class name and before #: the colon. whitespace_before_colon: SimpleWhitespace = SimpleWhitespace.field("") + #: An optional declaration of type parameters. + type_parameters: Optional["TypeParameters"] = None + + #: Whitespace between type parameters and opening parenthesis for the bases and + #: keywords. + whitespace_after_type_parameters: SimpleWhitespace = SimpleWhitespace.field("") + def _validate_whitespace(self) -> None: if self.whitespace_after_class.empty: raise CSTValidationError( "There must be at least one space between 'class' and name." ) + if ( + self.type_parameters is None + and not self.whitespace_after_type_parameters.empty + ): + raise CSTValidationError( + "whitespace_after_type_parameters must be empty if there are no type" + "parameters in a ClassDef" + ) def _validate_parens(self) -> None: if len(self.name.lpar) > 0 or len(self.name.rpar) > 0: @@ -1921,6 +1966,15 @@ def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ClassDef": whitespace_after_name=visit_required( self, "whitespace_after_name", self.whitespace_after_name, visitor ), + type_parameters=visit_optional( + self, "type_parameters", self.type_parameters, visitor + ), + whitespace_after_type_parameters=visit_required( + self, + "whitespace_after_type_parameters", + self.whitespace_after_type_parameters, + visitor, + ), lpar=visit_sentinel(self, "lpar", self.lpar, visitor), bases=visit_sequence(self, "bases", self.bases, visitor), keywords=visit_sequence(self, "keywords", self.keywords, visitor), @@ -1945,6 +1999,10 @@ def _codegen_impl(self, state: CodegenState) -> None: # noqa: C901 self.whitespace_after_class._codegen(state) self.name._codegen(state) self.whitespace_after_name._codegen(state) + type_params = self.type_parameters + if type_params is not None: + type_params._codegen(state) + self.whitespace_after_type_parameters._codegen(state) lpar = self.lpar if isinstance(lpar, MaybeSentinel): if self.bases or self.keywords: @@ -3476,3 +3534,283 @@ def _codegen_impl(self, state: CodegenState) -> None: pats = self.patterns for idx, pat in enumerate(pats): pat._codegen(state, default_separator=idx + 1 < len(pats)) + + +@add_slots +@dataclass(frozen=True) +class TypeVar(CSTNode): + """ + A simple (non-variadic) type variable. + + Note: this node represents type a variable when declared using PEP-695 syntax. + """ + + #: The name of the type variable. + name: Name + + #: An optional bound on the type. + bound: Optional[BaseExpression] = None + + #: The colon used to separate the name and bound. If not specified, + #: :class:`MaybeSentinel` will be replaced with a colon if there is a bound, + #: otherwise will be left empty. + colon: Union[Colon, MaybeSentinel] = MaybeSentinel.DEFAULT + + def _codegen_impl(self, state: CodegenState) -> None: + with state.record_syntactic_position(self): + self.name._codegen(state) + bound = self.bound + colon = self.colon + if not isinstance(colon, MaybeSentinel): + colon._codegen(state) + else: + if bound is not None: + state.add_token(": ") + + if bound is not None: + bound._codegen(state) + + def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeVar": + return TypeVar( + name=visit_required(self, "name", self.name, visitor), + colon=visit_sentinel(self, "colon", self.colon, visitor), + bound=visit_optional(self, "bound", self.bound, visitor), + ) + + +@add_slots +@dataclass(frozen=True) +class TypeVarTuple(CSTNode): + """ + A variadic type variable. + """ + + #: The name of this type variable. + name: Name + + #: The (optional) whitespace between the star declaring this type variable as + #: variadic, and the variable's name. + whitespace_after_star: SimpleWhitespace = SimpleWhitespace.field("") + + def _codegen_impl(self, state: CodegenState) -> None: + with state.record_syntactic_position(self): + state.add_token("*") + self.whitespace_after_star._codegen(state) + self.name._codegen(state) + + def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeVarTuple": + return TypeVarTuple( + name=visit_required(self, "name", self.name, visitor), + whitespace_after_star=visit_required( + self, "whitespace_after_star", self.whitespace_after_star, visitor + ), + ) + + +@add_slots +@dataclass(frozen=True) +class ParamSpec(CSTNode): + """ + A parameter specification. + + Note: this node represents a parameter specification when declared using PEP-695 + syntax. + """ + + #: The name of this parameter specification. + name: Name + + #: The (optional) whitespace between the double star declaring this type variable as + #: a parameter specification, and the name. + whitespace_after_star: SimpleWhitespace = SimpleWhitespace.field("") + + def _codegen_impl(self, state: CodegenState) -> None: + with state.record_syntactic_position(self): + state.add_token("**") + self.whitespace_after_star._codegen(state) + self.name._codegen(state) + + def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "ParamSpec": + return ParamSpec( + name=visit_required(self, "name", self.name, visitor), + whitespace_after_star=visit_required( + self, "whitespace_after_star", self.whitespace_after_star, visitor + ), + ) + + +@add_slots +@dataclass(frozen=True) +class TypeParam(CSTNode): + """ + A single type parameter that is contained in a :class:`TypeParameters` list. + """ + + #: The actual parameter. + param: Union[TypeVar, TypeVarTuple, ParamSpec] + + #: A trailing comma. If one is not provided, :class:`MaybeSentinel` will be replaced + #: with a comma only if a comma is required. + comma: Union[Comma, MaybeSentinel] = MaybeSentinel.DEFAULT + + def _codegen_impl(self, state: CodegenState, default_comma: bool = False) -> None: + self.param._codegen(state) + comma = self.comma + if isinstance(comma, MaybeSentinel): + if default_comma: + state.add_token(", ") + else: + comma._codegen(state) + + def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeParam": + return TypeParam( + param=visit_required(self, "param", self.param, visitor), + comma=visit_sentinel(self, "comma", self.comma, visitor), + ) + + +@add_slots +@dataclass(frozen=True) +class TypeParameters(CSTNode): + """ + Type parameters when specified with PEP-695 syntax. + + This node captures all specified parameters that are enclosed with square brackets. + """ + + #: The parameters within the square brackets. + params: Sequence[TypeParam] = () + + #: Opening square bracket that marks the start of these parameters. + lbracket: LeftSquareBracket = LeftSquareBracket.field() + #: Closing square bracket that marks the end of these parameters. + rbracket: RightSquareBracket = RightSquareBracket.field() + + def _codegen_impl(self, state: CodegenState) -> None: + self.lbracket._codegen(state) + params_len = len(self.params) + for idx, param in enumerate(self.params): + param._codegen(state, default_comma=idx + 1 < params_len) + self.rbracket._codegen(state) + + def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeParameters": + return TypeParameters( + lbracket=visit_required(self, "lbracket", self.lbracket, visitor), + params=visit_sequence(self, "params", self.params, visitor), + rbracket=visit_required(self, "rbracket", self.rbracket, visitor), + ) + + +@add_slots +@dataclass(frozen=True) +class TypeAlias(BaseSmallStatement): + """ + A type alias statement. + + This node represents the ``type`` statement as specified initially by PEP-695. + Example: ``type ListOrSet[T] = list[T] | set[T]``. + """ + + #: The name being introduced in this statement. + name: Name + + #: Everything on the right hand side of the ``=``. + value: BaseExpression + + #: An optional list of type parameters, specified after the name. + type_parameters: Optional[TypeParameters] = None + + #: Whitespace between the ``type`` soft keyword and the name. + whitespace_after_type: SimpleWhitespace = SimpleWhitespace.field(" ") + + #: Whitespace between the name and the type parameters (if they exist) or the ``=``. + #: If not specified, :class:`MaybeSentinel` will be replaced with a single space if + #: there are no type parameters, otherwise no spaces. + whitespace_after_name: Union[ + SimpleWhitespace, MaybeSentinel + ] = MaybeSentinel.DEFAULT + + #: Whitespace between the type parameters and the ``=``. Always empty if there are + #: no type parameters. If not specified, :class:`MaybeSentinel` will be replaced + #: with a single space if there are type parameters. + whitespace_after_type_parameters: Union[ + SimpleWhitespace, MaybeSentinel + ] = MaybeSentinel.DEFAULT + + #: Whitespace between the ``=`` and the value. + whitespace_after_equals: SimpleWhitespace = SimpleWhitespace.field(" ") + + #: Optional semicolon when this is used in a statement line. This semicolon + #: owns the whitespace on both sides of it when it is used. + semicolon: Union[Semicolon, MaybeSentinel] = MaybeSentinel.DEFAULT + + def _validate(self) -> None: + if ( + self.type_parameters is None + and self.whitespace_after_type_parameters + not in { + SimpleWhitespace(""), + MaybeSentinel.DEFAULT, + } + ): + raise CSTValidationError( + "whitespace_after_type_parameters must be empty when there are no type parameters in a TypeAlias" + ) + + def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "TypeAlias": + return TypeAlias( + whitespace_after_type=visit_required( + self, "whitespace_after_type", self.whitespace_after_type, visitor + ), + name=visit_required(self, "name", self.name, visitor), + whitespace_after_name=visit_sentinel( + self, "whitespace_after_name", self.whitespace_after_name, visitor + ), + type_parameters=visit_optional( + self, "type_parameters", self.type_parameters, visitor + ), + whitespace_after_type_parameters=visit_sentinel( + self, + "whitespace_after_type_parameters", + self.whitespace_after_type_parameters, + visitor, + ), + whitespace_after_equals=visit_required( + self, "whitespace_after_equals", self.whitespace_after_equals, visitor + ), + value=visit_required(self, "value", self.value, visitor), + semicolon=visit_sentinel(self, "semicolon", self.semicolon, visitor), + ) + + def _codegen_impl( + self, state: CodegenState, default_semicolon: bool = False + ) -> None: + with state.record_syntactic_position(self): + state.add_token("type") + self.whitespace_after_type._codegen(state) + self.name._codegen(state) + ws_after_name = self.whitespace_after_name + if isinstance(ws_after_name, MaybeSentinel): + if self.type_parameters is None: + state.add_token(" ") + else: + ws_after_name._codegen(state) + + ws_after_type_params = self.whitespace_after_type_parameters + if self.type_parameters is not None: + self.type_parameters._codegen(state) + if isinstance(ws_after_type_params, MaybeSentinel): + state.add_token(" ") + else: + ws_after_type_params._codegen(state) + + state.add_token("=") + self.whitespace_after_equals._codegen(state) + self.value._codegen(state) + + semi = self.semicolon + if isinstance(semi, MaybeSentinel): + if default_semicolon: + state.add_token("; ") + else: + semi._codegen(state) diff --git a/libcst/_nodes/tests/test_classdef.py b/libcst/_nodes/tests/test_classdef.py index db582dceb..cca36fbbd 100644 --- a/libcst/_nodes/tests/test_classdef.py +++ b/libcst/_nodes/tests/test_classdef.py @@ -8,6 +8,7 @@ import libcst as cst from libcst import parse_statement from libcst._nodes.tests.base import CSTNodeTest +from libcst._parser.entrypoints import is_native from libcst.metadata import CodeRange from libcst.testing.utils import data_provider @@ -112,6 +113,107 @@ class ClassDefCreationTest(CSTNodeTest): def test_valid(self, **kwargs: Any) -> None: self.validate_node(**kwargs) + @data_provider( + ( + { + "node": cst.ClassDef( + cst.Name("Foo"), + cst.SimpleStatementSuite((cst.Pass(),)), + type_parameters=cst.TypeParameters( + ( + cst.TypeParam( + cst.TypeVar( + cst.Name("T"), + bound=cst.Name("int"), + colon=cst.Colon( + whitespace_after=cst.SimpleWhitespace(" ") + ), + ), + cst.Comma(whitespace_after=cst.SimpleWhitespace(" ")), + ), + cst.TypeParam( + cst.TypeVarTuple(cst.Name("Ts")), + cst.Comma(whitespace_after=cst.SimpleWhitespace(" ")), + ), + cst.TypeParam(cst.ParamSpec(cst.Name("KW"))), + ) + ), + ), + "code": "class Foo[T: int, *Ts, **KW]: pass\n", + }, + { + "node": cst.ClassDef( + cst.Name("Foo"), + cst.SimpleStatementSuite((cst.Pass(),)), + type_parameters=cst.TypeParameters( + params=( + cst.TypeParam( + param=cst.TypeVar( + cst.Name("T"), + bound=cst.Name("str"), + colon=cst.Colon( + whitespace_before=cst.SimpleWhitespace(" "), + whitespace_after=cst.ParenthesizedWhitespace( + empty_lines=(cst.EmptyLine(),), + indent=True, + ), + ), + ), + comma=cst.Comma(cst.SimpleWhitespace(" ")), + ), + cst.TypeParam( + cst.ParamSpec( + cst.Name("PS"), cst.SimpleWhitespace(" ") + ), + cst.Comma(cst.SimpleWhitespace(" ")), + ), + ) + ), + whitespace_after_type_parameters=cst.SimpleWhitespace(" "), + ), + "code": "class Foo[T :\n\nstr ,** PS ,] : pass\n", + }, + { + "node": cst.ClassDef( + cst.Name("Foo"), + cst.SimpleStatementSuite((cst.Pass(),)), + type_parameters=cst.TypeParameters( + params=( + cst.TypeParam( + param=cst.TypeVar( + cst.Name("T"), + bound=cst.Name("str"), + colon=cst.Colon( + whitespace_before=cst.SimpleWhitespace(" "), + whitespace_after=cst.ParenthesizedWhitespace( + empty_lines=(cst.EmptyLine(),), + indent=True, + ), + ), + ), + comma=cst.Comma(cst.SimpleWhitespace(" ")), + ), + cst.TypeParam( + cst.ParamSpec( + cst.Name("PS"), cst.SimpleWhitespace(" ") + ), + cst.Comma(cst.SimpleWhitespace(" ")), + ), + ) + ), + lpar=cst.LeftParen(), + rpar=cst.RightParen(), + whitespace_after_type_parameters=cst.SimpleWhitespace(" "), + ), + "code": "class Foo[T :\n\nstr ,** PS ,] (): pass\n", + }, + ) + ) + def test_valid_native(self, **kwargs: Any) -> None: + if not is_native(): + self.skipTest("Disabled for pure python parser") + self.validate_node(**kwargs) + @data_provider( ( # Basic parenthesis tests. diff --git a/libcst/_nodes/tests/test_funcdef.py b/libcst/_nodes/tests/test_funcdef.py index 250030c45..087dde19c 100644 --- a/libcst/_nodes/tests/test_funcdef.py +++ b/libcst/_nodes/tests/test_funcdef.py @@ -811,11 +811,88 @@ def test_valid(self, **kwargs: Any) -> None: "parser": parse_statement, "code": "def foo(*a: *tuple[int,*Ts,...]): pass\n", }, + # Single type variable + { + "node": cst.FunctionDef( + cst.Name("foo"), + cst.Parameters(), + cst.SimpleStatementSuite((cst.Pass(),)), + type_parameters=cst.TypeParameters( + (cst.TypeParam(cst.TypeVar(cst.Name("T"))),) + ), + ), + "code": "def foo[T](): pass\n", + "parser": parse_statement, + }, + # All the type parameters + { + "node": cst.FunctionDef( + cst.Name("foo"), + cst.Parameters(), + cst.SimpleStatementSuite((cst.Pass(),)), + type_parameters=cst.TypeParameters( + ( + cst.TypeParam( + cst.TypeVar( + cst.Name("T"), + bound=cst.Name("int"), + colon=cst.Colon( + whitespace_after=cst.SimpleWhitespace(" ") + ), + ), + cst.Comma(whitespace_after=cst.SimpleWhitespace(" ")), + ), + cst.TypeParam( + cst.TypeVarTuple(cst.Name("Ts")), + cst.Comma(whitespace_after=cst.SimpleWhitespace(" ")), + ), + cst.TypeParam(cst.ParamSpec(cst.Name("KW"))), + ) + ), + ), + "code": "def foo[T: int, *Ts, **KW](): pass\n", + "parser": parse_statement, + }, + # Type parameters with whitespace + { + "node": cst.FunctionDef( + cst.Name("foo"), + cst.Parameters(), + cst.SimpleStatementSuite((cst.Pass(),)), + type_parameters=cst.TypeParameters( + params=( + cst.TypeParam( + param=cst.TypeVar( + cst.Name("T"), + bound=cst.Name("str"), + colon=cst.Colon( + whitespace_before=cst.SimpleWhitespace(" "), + whitespace_after=cst.ParenthesizedWhitespace( + empty_lines=(cst.EmptyLine(),), + indent=True, + ), + ), + ), + comma=cst.Comma(cst.SimpleWhitespace(" ")), + ), + cst.TypeParam( + cst.ParamSpec( + cst.Name("PS"), cst.SimpleWhitespace(" ") + ), + cst.Comma(cst.SimpleWhitespace(" ")), + ), + ) + ), + whitespace_after_type_parameters=cst.SimpleWhitespace(" "), + ), + "code": "def foo[T :\n\nstr ,** PS ,] (): pass\n", + "parser": parse_statement, + }, ) ) def test_valid_native(self, **kwargs: Any) -> None: if not is_native(): - self.skipTest("Disabled for native parser") + self.skipTest("Disabled for pure python parser") self.validate_node(**kwargs) @data_provider( diff --git a/libcst/_nodes/tests/test_type_alias.py b/libcst/_nodes/tests/test_type_alias.py new file mode 100644 index 000000000..11fd11232 --- /dev/null +++ b/libcst/_nodes/tests/test_type_alias.py @@ -0,0 +1,133 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +from typing import Any + +import libcst as cst +from libcst import parse_statement +from libcst._nodes.tests.base import CSTNodeTest +from libcst._parser.entrypoints import is_native +from libcst.metadata import CodeRange +from libcst.testing.utils import data_provider + + +class TypeAliasCreationTest(CSTNodeTest): + @data_provider( + ( + { + "node": cst.TypeAlias( + cst.Name("foo"), + cst.Name("bar"), + ), + "code": "type foo = bar", + "expected_position": CodeRange((1, 0), (1, 14)), + }, + { + "node": cst.TypeAlias( + cst.Name("foo"), + type_parameters=cst.TypeParameters( + [cst.TypeParam(cst.TypeVar(cst.Name("T")))] + ), + value=cst.BinaryOperation( + cst.Name("bar"), cst.BitOr(), cst.Name("baz") + ), + ), + "code": "type foo[T] = bar | baz", + "expected_position": CodeRange((1, 0), (1, 23)), + }, + { + "node": cst.TypeAlias( + cst.Name("foo"), + type_parameters=cst.TypeParameters( + [ + cst.TypeParam( + cst.TypeVar(cst.Name("T"), bound=cst.Name("str")) + ), + cst.TypeParam(cst.TypeVarTuple(cst.Name("Ts"))), + cst.TypeParam(cst.ParamSpec(cst.Name("KW"))), + ] + ), + value=cst.BinaryOperation( + cst.Name("bar"), cst.BitOr(), cst.Name("baz") + ), + ), + "code": "type foo[T: str, *Ts, **KW] = bar | baz", + "expected_position": CodeRange((1, 0), (1, 39)), + }, + ) + ) + def test_valid(self, **kwargs: Any) -> None: + if not is_native(): + self.skipTest("Disabled in the old parser") + self.validate_node(**kwargs) + + +class TypeAliasParserTest(CSTNodeTest): + @data_provider( + ( + { + "node": cst.SimpleStatementLine( + [ + cst.TypeAlias( + cst.Name("foo"), + cst.Name("bar"), + whitespace_after_name=cst.SimpleWhitespace(" "), + ) + ] + ), + "code": "type foo = bar\n", + "parser": parse_statement, + }, + { + "node": cst.SimpleStatementLine( + [ + cst.TypeAlias( + cst.Name("foo"), + cst.Name("bar"), + type_parameters=cst.TypeParameters( + params=[ + cst.TypeParam( + cst.TypeVar( + cst.Name("T"), cst.Name("str"), cst.Colon() + ), + cst.Comma(), + ), + cst.TypeParam( + cst.ParamSpec( + cst.Name("KW"), + whitespace_after_star=cst.SimpleWhitespace( + " " + ), + ), + cst.Comma( + whitespace_before=cst.SimpleWhitespace(" "), + whitespace_after=cst.SimpleWhitespace(" "), + ), + ), + ], + rbracket=cst.RightSquareBracket( + cst.SimpleWhitespace("") + ), + ), + whitespace_after_name=cst.SimpleWhitespace(" "), + whitespace_after_type=cst.SimpleWhitespace(" "), + whitespace_after_equals=cst.SimpleWhitespace(" "), + whitespace_after_type_parameters=cst.SimpleWhitespace(" "), + semicolon=cst.Semicolon( + whitespace_before=cst.SimpleWhitespace(" "), + whitespace_after=cst.SimpleWhitespace(" "), + ), + ) + ] + ), + "code": "type foo [T:str,** KW , ] = bar ; \n", + "parser": parse_statement, + }, + ) + ) + def test_valid(self, **kwargs: Any) -> None: + if not is_native(): + self.skipTest("Disabled in the old parser") + self.validate_node(**kwargs) diff --git a/libcst/_typed_visitor.py b/libcst/_typed_visitor.py index a28f3fd12..7e1d1c420 100644 --- a/libcst/_typed_visitor.py +++ b/libcst/_typed_visitor.py @@ -178,6 +178,7 @@ MatchValue, NameItem, Nonlocal, + ParamSpec, Pass, Raise, Return, @@ -185,6 +186,11 @@ SimpleStatementSuite, Try, TryStar, + TypeAlias, + TypeParam, + TypeParameters, + TypeVar, + TypeVarTuple, While, With, WithItem, @@ -1053,6 +1059,22 @@ def visit_ClassDef_whitespace_before_colon(self, node: "ClassDef") -> None: def leave_ClassDef_whitespace_before_colon(self, node: "ClassDef") -> None: pass + @mark_no_op + def visit_ClassDef_type_parameters(self, node: "ClassDef") -> None: + pass + + @mark_no_op + def leave_ClassDef_type_parameters(self, node: "ClassDef") -> None: + pass + + @mark_no_op + def visit_ClassDef_whitespace_after_type_parameters(self, node: "ClassDef") -> None: + pass + + @mark_no_op + def leave_ClassDef_whitespace_after_type_parameters(self, node: "ClassDef") -> None: + pass + @mark_no_op def visit_Colon(self, node: "Colon") -> Optional[bool]: pass @@ -2339,6 +2361,26 @@ def visit_FunctionDef_whitespace_before_colon(self, node: "FunctionDef") -> None def leave_FunctionDef_whitespace_before_colon(self, node: "FunctionDef") -> None: pass + @mark_no_op + def visit_FunctionDef_type_parameters(self, node: "FunctionDef") -> None: + pass + + @mark_no_op + def leave_FunctionDef_type_parameters(self, node: "FunctionDef") -> None: + pass + + @mark_no_op + def visit_FunctionDef_whitespace_after_type_parameters( + self, node: "FunctionDef" + ) -> None: + pass + + @mark_no_op + def leave_FunctionDef_whitespace_after_type_parameters( + self, node: "FunctionDef" + ) -> None: + pass + @mark_no_op def visit_GeneratorExp(self, node: "GeneratorExp") -> Optional[bool]: pass @@ -4315,6 +4357,26 @@ def visit_ParamSlash_whitespace_after(self, node: "ParamSlash") -> None: def leave_ParamSlash_whitespace_after(self, node: "ParamSlash") -> None: pass + @mark_no_op + def visit_ParamSpec(self, node: "ParamSpec") -> Optional[bool]: + pass + + @mark_no_op + def visit_ParamSpec_name(self, node: "ParamSpec") -> None: + pass + + @mark_no_op + def leave_ParamSpec_name(self, node: "ParamSpec") -> None: + pass + + @mark_no_op + def visit_ParamSpec_whitespace_after_star(self, node: "ParamSpec") -> None: + pass + + @mark_no_op + def leave_ParamSpec_whitespace_after_star(self, node: "ParamSpec") -> None: + pass + @mark_no_op def visit_ParamStar(self, node: "ParamStar") -> Optional[bool]: pass @@ -5279,6 +5341,174 @@ def visit_Tuple_rpar(self, node: "Tuple") -> None: def leave_Tuple_rpar(self, node: "Tuple") -> None: pass + @mark_no_op + def visit_TypeAlias(self, node: "TypeAlias") -> Optional[bool]: + pass + + @mark_no_op + def visit_TypeAlias_name(self, node: "TypeAlias") -> None: + pass + + @mark_no_op + def leave_TypeAlias_name(self, node: "TypeAlias") -> None: + pass + + @mark_no_op + def visit_TypeAlias_value(self, node: "TypeAlias") -> None: + pass + + @mark_no_op + def leave_TypeAlias_value(self, node: "TypeAlias") -> None: + pass + + @mark_no_op + def visit_TypeAlias_type_parameters(self, node: "TypeAlias") -> None: + pass + + @mark_no_op + def leave_TypeAlias_type_parameters(self, node: "TypeAlias") -> None: + pass + + @mark_no_op + def visit_TypeAlias_whitespace_after_type(self, node: "TypeAlias") -> None: + pass + + @mark_no_op + def leave_TypeAlias_whitespace_after_type(self, node: "TypeAlias") -> None: + pass + + @mark_no_op + def visit_TypeAlias_whitespace_after_name(self, node: "TypeAlias") -> None: + pass + + @mark_no_op + def leave_TypeAlias_whitespace_after_name(self, node: "TypeAlias") -> None: + pass + + @mark_no_op + def visit_TypeAlias_whitespace_after_type_parameters( + self, node: "TypeAlias" + ) -> None: + pass + + @mark_no_op + def leave_TypeAlias_whitespace_after_type_parameters( + self, node: "TypeAlias" + ) -> None: + pass + + @mark_no_op + def visit_TypeAlias_whitespace_after_equals(self, node: "TypeAlias") -> None: + pass + + @mark_no_op + def leave_TypeAlias_whitespace_after_equals(self, node: "TypeAlias") -> None: + pass + + @mark_no_op + def visit_TypeAlias_semicolon(self, node: "TypeAlias") -> None: + pass + + @mark_no_op + def leave_TypeAlias_semicolon(self, node: "TypeAlias") -> None: + pass + + @mark_no_op + def visit_TypeParam(self, node: "TypeParam") -> Optional[bool]: + pass + + @mark_no_op + def visit_TypeParam_param(self, node: "TypeParam") -> None: + pass + + @mark_no_op + def leave_TypeParam_param(self, node: "TypeParam") -> None: + pass + + @mark_no_op + def visit_TypeParam_comma(self, node: "TypeParam") -> None: + pass + + @mark_no_op + def leave_TypeParam_comma(self, node: "TypeParam") -> None: + pass + + @mark_no_op + def visit_TypeParameters(self, node: "TypeParameters") -> Optional[bool]: + pass + + @mark_no_op + def visit_TypeParameters_params(self, node: "TypeParameters") -> None: + pass + + @mark_no_op + def leave_TypeParameters_params(self, node: "TypeParameters") -> None: + pass + + @mark_no_op + def visit_TypeParameters_lbracket(self, node: "TypeParameters") -> None: + pass + + @mark_no_op + def leave_TypeParameters_lbracket(self, node: "TypeParameters") -> None: + pass + + @mark_no_op + def visit_TypeParameters_rbracket(self, node: "TypeParameters") -> None: + pass + + @mark_no_op + def leave_TypeParameters_rbracket(self, node: "TypeParameters") -> None: + pass + + @mark_no_op + def visit_TypeVar(self, node: "TypeVar") -> Optional[bool]: + pass + + @mark_no_op + def visit_TypeVar_name(self, node: "TypeVar") -> None: + pass + + @mark_no_op + def leave_TypeVar_name(self, node: "TypeVar") -> None: + pass + + @mark_no_op + def visit_TypeVar_bound(self, node: "TypeVar") -> None: + pass + + @mark_no_op + def leave_TypeVar_bound(self, node: "TypeVar") -> None: + pass + + @mark_no_op + def visit_TypeVar_colon(self, node: "TypeVar") -> None: + pass + + @mark_no_op + def leave_TypeVar_colon(self, node: "TypeVar") -> None: + pass + + @mark_no_op + def visit_TypeVarTuple(self, node: "TypeVarTuple") -> Optional[bool]: + pass + + @mark_no_op + def visit_TypeVarTuple_name(self, node: "TypeVarTuple") -> None: + pass + + @mark_no_op + def leave_TypeVarTuple_name(self, node: "TypeVarTuple") -> None: + pass + + @mark_no_op + def visit_TypeVarTuple_whitespace_after_star(self, node: "TypeVarTuple") -> None: + pass + + @mark_no_op + def leave_TypeVarTuple_whitespace_after_star(self, node: "TypeVarTuple") -> None: + pass + @mark_no_op def visit_UnaryOperation(self, node: "UnaryOperation") -> Optional[bool]: pass @@ -6003,6 +6233,10 @@ def leave_Param(self, original_node: "Param") -> None: def leave_ParamSlash(self, original_node: "ParamSlash") -> None: pass + @mark_no_op + def leave_ParamSpec(self, original_node: "ParamSpec") -> None: + pass + @mark_no_op def leave_ParamStar(self, original_node: "ParamStar") -> None: pass @@ -6133,6 +6367,26 @@ def leave_TryStar(self, original_node: "TryStar") -> None: def leave_Tuple(self, original_node: "Tuple") -> None: pass + @mark_no_op + def leave_TypeAlias(self, original_node: "TypeAlias") -> None: + pass + + @mark_no_op + def leave_TypeParam(self, original_node: "TypeParam") -> None: + pass + + @mark_no_op + def leave_TypeParameters(self, original_node: "TypeParameters") -> None: + pass + + @mark_no_op + def leave_TypeVar(self, original_node: "TypeVar") -> None: + pass + + @mark_no_op + def leave_TypeVarTuple(self, original_node: "TypeVarTuple") -> None: + pass + @mark_no_op def leave_UnaryOperation(self, original_node: "UnaryOperation") -> None: pass @@ -6931,6 +7185,12 @@ def leave_ParamSlash( ) -> Union["ParamSlash", MaybeSentinel]: return updated_node + @mark_no_op + def leave_ParamSpec( + self, original_node: "ParamSpec", updated_node: "ParamSpec" + ) -> "ParamSpec": + return updated_node + @mark_no_op def leave_ParamStar( self, original_node: "ParamStar", updated_node: "ParamStar" @@ -7131,6 +7391,38 @@ def leave_Tuple( ) -> "BaseExpression": return updated_node + @mark_no_op + def leave_TypeAlias( + self, original_node: "TypeAlias", updated_node: "TypeAlias" + ) -> Union[ + "BaseSmallStatement", FlattenSentinel["BaseSmallStatement"], RemovalSentinel + ]: + return updated_node + + @mark_no_op + def leave_TypeParam( + self, original_node: "TypeParam", updated_node: "TypeParam" + ) -> Union["TypeParam", FlattenSentinel["TypeParam"], RemovalSentinel]: + return updated_node + + @mark_no_op + def leave_TypeParameters( + self, original_node: "TypeParameters", updated_node: "TypeParameters" + ) -> "TypeParameters": + return updated_node + + @mark_no_op + def leave_TypeVar( + self, original_node: "TypeVar", updated_node: "TypeVar" + ) -> "TypeVar": + return updated_node + + @mark_no_op + def leave_TypeVarTuple( + self, original_node: "TypeVarTuple", updated_node: "TypeVarTuple" + ) -> "TypeVarTuple": + return updated_node + @mark_no_op def leave_UnaryOperation( self, original_node: "UnaryOperation", updated_node: "UnaryOperation" diff --git a/libcst/matchers/__init__.py b/libcst/matchers/__init__.py index 8323578c5..7e3761b82 100644 --- a/libcst/matchers/__init__.py +++ b/libcst/matchers/__init__.py @@ -2317,6 +2317,32 @@ class ClassDef(BaseCompoundStatement, BaseStatement, BaseMatcherNode): OneOf[SimpleWhitespaceMatchType], AllOf[SimpleWhitespaceMatchType], ] = DoNotCare() + type_parameters: Union[ + Optional["TypeParameters"], + MetadataMatchType, + MatchIfTrue[Optional[cst.TypeParameters]], + DoNotCareSentinel, + OneOf[ + Union[ + Optional["TypeParameters"], + MetadataMatchType, + MatchIfTrue[Optional[cst.TypeParameters]], + ] + ], + AllOf[ + Union[ + Optional["TypeParameters"], + MetadataMatchType, + MatchIfTrue[Optional[cst.TypeParameters]], + ] + ], + ] = DoNotCare() + whitespace_after_type_parameters: Union[ + SimpleWhitespaceMatchType, + DoNotCareSentinel, + OneOf[SimpleWhitespaceMatchType], + AllOf[SimpleWhitespaceMatchType], + ] = DoNotCare() metadata: Union[ MetadataMatchType, DoNotCareSentinel, @@ -5698,6 +5724,32 @@ class FunctionDef(BaseCompoundStatement, BaseStatement, BaseMatcherNode): OneOf[SimpleWhitespaceMatchType], AllOf[SimpleWhitespaceMatchType], ] = DoNotCare() + type_parameters: Union[ + Optional["TypeParameters"], + MetadataMatchType, + MatchIfTrue[Optional[cst.TypeParameters]], + DoNotCareSentinel, + OneOf[ + Union[ + Optional["TypeParameters"], + MetadataMatchType, + MatchIfTrue[Optional[cst.TypeParameters]], + ] + ], + AllOf[ + Union[ + Optional["TypeParameters"], + MetadataMatchType, + MatchIfTrue[Optional[cst.TypeParameters]], + ] + ], + ] = DoNotCare() + whitespace_after_type_parameters: Union[ + SimpleWhitespaceMatchType, + DoNotCareSentinel, + OneOf[SimpleWhitespaceMatchType], + AllOf[SimpleWhitespaceMatchType], + ] = DoNotCare() metadata: Union[ MetadataMatchType, DoNotCareSentinel, @@ -11962,6 +12014,25 @@ class ParamSlash(BaseMatcherNode): ] = DoNotCare() +@dataclass(frozen=True, eq=False, unsafe_hash=False) +class ParamSpec(BaseMatcherNode): + name: Union[ + NameMatchType, DoNotCareSentinel, OneOf[NameMatchType], AllOf[NameMatchType] + ] = DoNotCare() + whitespace_after_star: Union[ + SimpleWhitespaceMatchType, + DoNotCareSentinel, + OneOf[SimpleWhitespaceMatchType], + AllOf[SimpleWhitespaceMatchType], + ] = DoNotCare() + metadata: Union[ + MetadataMatchType, + DoNotCareSentinel, + OneOf[MetadataMatchType], + AllOf[MetadataMatchType], + ] = DoNotCare() + + @dataclass(frozen=True, eq=False, unsafe_hash=False) class ParamStar(BaseMatcherNode): comma: Union[ @@ -14946,6 +15017,263 @@ class Tuple( ] = DoNotCare() +@dataclass(frozen=True, eq=False, unsafe_hash=False) +class TypeAlias(BaseSmallStatement, BaseMatcherNode): + name: Union[ + NameMatchType, DoNotCareSentinel, OneOf[NameMatchType], AllOf[NameMatchType] + ] = DoNotCare() + value: Union[ + BaseExpressionMatchType, + DoNotCareSentinel, + OneOf[BaseExpressionMatchType], + AllOf[BaseExpressionMatchType], + ] = DoNotCare() + type_parameters: Union[ + Optional["TypeParameters"], + MetadataMatchType, + MatchIfTrue[Optional[cst.TypeParameters]], + DoNotCareSentinel, + OneOf[ + Union[ + Optional["TypeParameters"], + MetadataMatchType, + MatchIfTrue[Optional[cst.TypeParameters]], + ] + ], + AllOf[ + Union[ + Optional["TypeParameters"], + MetadataMatchType, + MatchIfTrue[Optional[cst.TypeParameters]], + ] + ], + ] = DoNotCare() + whitespace_after_type: Union[ + SimpleWhitespaceMatchType, + DoNotCareSentinel, + OneOf[SimpleWhitespaceMatchType], + AllOf[SimpleWhitespaceMatchType], + ] = DoNotCare() + whitespace_after_name: Union[ + SimpleWhitespaceMatchType, + DoNotCareSentinel, + OneOf[SimpleWhitespaceMatchType], + AllOf[SimpleWhitespaceMatchType], + ] = DoNotCare() + whitespace_after_type_parameters: Union[ + SimpleWhitespaceMatchType, + DoNotCareSentinel, + OneOf[SimpleWhitespaceMatchType], + AllOf[SimpleWhitespaceMatchType], + ] = DoNotCare() + whitespace_after_equals: Union[ + SimpleWhitespaceMatchType, + DoNotCareSentinel, + OneOf[SimpleWhitespaceMatchType], + AllOf[SimpleWhitespaceMatchType], + ] = DoNotCare() + semicolon: Union[ + SemicolonMatchType, + DoNotCareSentinel, + OneOf[SemicolonMatchType], + AllOf[SemicolonMatchType], + ] = DoNotCare() + metadata: Union[ + MetadataMatchType, + DoNotCareSentinel, + OneOf[MetadataMatchType], + AllOf[MetadataMatchType], + ] = DoNotCare() + + +TypeVarOrTypeVarTupleOrParamSpecMatchType = Union[ + "TypeVar", + "TypeVarTuple", + "ParamSpec", + MetadataMatchType, + MatchIfTrue[Union[cst.TypeVar, cst.TypeVarTuple, cst.ParamSpec]], +] + + +@dataclass(frozen=True, eq=False, unsafe_hash=False) +class TypeParam(BaseMatcherNode): + param: Union[ + TypeVarOrTypeVarTupleOrParamSpecMatchType, + DoNotCareSentinel, + OneOf[TypeVarOrTypeVarTupleOrParamSpecMatchType], + AllOf[TypeVarOrTypeVarTupleOrParamSpecMatchType], + ] = DoNotCare() + comma: Union[ + CommaMatchType, DoNotCareSentinel, OneOf[CommaMatchType], AllOf[CommaMatchType] + ] = DoNotCare() + metadata: Union[ + MetadataMatchType, + DoNotCareSentinel, + OneOf[MetadataMatchType], + AllOf[MetadataMatchType], + ] = DoNotCare() + + +TypeParamMatchType = Union["TypeParam", MetadataMatchType, MatchIfTrue[cst.TypeParam]] + + +@dataclass(frozen=True, eq=False, unsafe_hash=False) +class TypeParameters(BaseMatcherNode): + params: Union[ + Sequence[ + Union[ + TypeParamMatchType, + DoNotCareSentinel, + OneOf[TypeParamMatchType], + AllOf[TypeParamMatchType], + AtLeastN[ + Union[ + TypeParamMatchType, + DoNotCareSentinel, + OneOf[TypeParamMatchType], + AllOf[TypeParamMatchType], + ] + ], + AtMostN[ + Union[ + TypeParamMatchType, + DoNotCareSentinel, + OneOf[TypeParamMatchType], + AllOf[TypeParamMatchType], + ] + ], + ] + ], + DoNotCareSentinel, + MatchIfTrue[Sequence[cst.TypeParam]], + OneOf[ + Union[ + Sequence[ + Union[ + TypeParamMatchType, + OneOf[TypeParamMatchType], + AllOf[TypeParamMatchType], + AtLeastN[ + Union[ + TypeParamMatchType, + OneOf[TypeParamMatchType], + AllOf[TypeParamMatchType], + ] + ], + AtMostN[ + Union[ + TypeParamMatchType, + OneOf[TypeParamMatchType], + AllOf[TypeParamMatchType], + ] + ], + ] + ], + MatchIfTrue[Sequence[cst.TypeParam]], + ] + ], + AllOf[ + Union[ + Sequence[ + Union[ + TypeParamMatchType, + OneOf[TypeParamMatchType], + AllOf[TypeParamMatchType], + AtLeastN[ + Union[ + TypeParamMatchType, + OneOf[TypeParamMatchType], + AllOf[TypeParamMatchType], + ] + ], + AtMostN[ + Union[ + TypeParamMatchType, + OneOf[TypeParamMatchType], + AllOf[TypeParamMatchType], + ] + ], + ] + ], + MatchIfTrue[Sequence[cst.TypeParam]], + ] + ], + ] = DoNotCare() + lbracket: Union[ + LeftSquareBracketMatchType, + DoNotCareSentinel, + OneOf[LeftSquareBracketMatchType], + AllOf[LeftSquareBracketMatchType], + ] = DoNotCare() + rbracket: Union[ + RightSquareBracketMatchType, + DoNotCareSentinel, + OneOf[RightSquareBracketMatchType], + AllOf[RightSquareBracketMatchType], + ] = DoNotCare() + metadata: Union[ + MetadataMatchType, + DoNotCareSentinel, + OneOf[MetadataMatchType], + AllOf[MetadataMatchType], + ] = DoNotCare() + + +@dataclass(frozen=True, eq=False, unsafe_hash=False) +class TypeVar(BaseMatcherNode): + name: Union[ + NameMatchType, DoNotCareSentinel, OneOf[NameMatchType], AllOf[NameMatchType] + ] = DoNotCare() + bound: Union[ + Optional["BaseExpression"], + MetadataMatchType, + MatchIfTrue[Optional[cst.BaseExpression]], + DoNotCareSentinel, + OneOf[ + Union[ + Optional["BaseExpression"], + MetadataMatchType, + MatchIfTrue[Optional[cst.BaseExpression]], + ] + ], + AllOf[ + Union[ + Optional["BaseExpression"], + MetadataMatchType, + MatchIfTrue[Optional[cst.BaseExpression]], + ] + ], + ] = DoNotCare() + colon: Union[ + ColonMatchType, DoNotCareSentinel, OneOf[ColonMatchType], AllOf[ColonMatchType] + ] = DoNotCare() + metadata: Union[ + MetadataMatchType, + DoNotCareSentinel, + OneOf[MetadataMatchType], + AllOf[MetadataMatchType], + ] = DoNotCare() + + +@dataclass(frozen=True, eq=False, unsafe_hash=False) +class TypeVarTuple(BaseMatcherNode): + name: Union[ + NameMatchType, DoNotCareSentinel, OneOf[NameMatchType], AllOf[NameMatchType] + ] = DoNotCare() + whitespace_after_star: Union[ + SimpleWhitespaceMatchType, + DoNotCareSentinel, + OneOf[SimpleWhitespaceMatchType], + AllOf[SimpleWhitespaceMatchType], + ] = DoNotCare() + metadata: Union[ + MetadataMatchType, + DoNotCareSentinel, + OneOf[MetadataMatchType], + AllOf[MetadataMatchType], + ] = DoNotCare() + + BaseUnaryOpMatchType = Union[ "BaseUnaryOp", MetadataMatchType, MatchIfTrue[cst.BaseUnaryOp] ] @@ -15874,6 +16202,7 @@ class Yield(BaseExpression, BaseMatcherNode): "Or", "Param", "ParamSlash", + "ParamSpec", "ParamStar", "Parameters", "ParenthesizedWhitespace", @@ -15907,7 +16236,12 @@ class Yield(BaseExpression, BaseMatcherNode): "Try", "TryStar", "Tuple", + "TypeAlias", "TypeOf", + "TypeParam", + "TypeParameters", + "TypeVar", + "TypeVarTuple", "UnaryOperation", "While", "With", diff --git a/libcst/matchers/_return_types.py b/libcst/matchers/_return_types.py index 87475d05c..9d20a23ad 100644 --- a/libcst/matchers/_return_types.py +++ b/libcst/matchers/_return_types.py @@ -174,6 +174,7 @@ MatchValue, NameItem, Nonlocal, + ParamSpec, Pass, Raise, Return, @@ -181,6 +182,11 @@ SimpleStatementSuite, Try, TryStar, + TypeAlias, + TypeParam, + TypeParameters, + TypeVar, + TypeVarTuple, While, With, WithItem, @@ -323,6 +329,7 @@ Or: BaseBooleanOp, Param: Union[Param, MaybeSentinel, RemovalSentinel], ParamSlash: Union[ParamSlash, MaybeSentinel], + ParamSpec: ParamSpec, ParamStar: Union[ParamStar, MaybeSentinel], Parameters: Parameters, ParenthesizedWhitespace: Union[BaseParenthesizableWhitespace, MaybeSentinel], @@ -355,6 +362,11 @@ Try: Union[BaseStatement, RemovalSentinel], TryStar: Union[BaseStatement, RemovalSentinel], Tuple: BaseExpression, + TypeAlias: Union[BaseSmallStatement, RemovalSentinel], + TypeParam: Union[TypeParam, RemovalSentinel], + TypeParameters: TypeParameters, + TypeVar: TypeVar, + TypeVarTuple: TypeVarTuple, UnaryOperation: BaseExpression, While: Union[BaseStatement, RemovalSentinel], With: Union[BaseStatement, RemovalSentinel], diff --git a/libcst/tests/test_tool.py b/libcst/tests/test_tool.py index 929d7225b..2042ef9b3 100644 --- a/libcst/tests/test_tool.py +++ b/libcst/tests/test_tool.py @@ -152,6 +152,10 @@ def test_full_tree(self) -> None: whitespace_before_colon=SimpleWhitespace( value='', ), + type_parameters=None, + whitespace_after_type_parameters=SimpleWhitespace( + value='', + ), ), ], header=[], @@ -243,6 +247,7 @@ def test_hidden_whitespace(self) -> None: ), ), asynchronous=None, + type_parameters=None, ), ], encoding='utf-8', @@ -532,6 +537,10 @@ def test_hidden_syntax(self) -> None: whitespace_before_colon=SimpleWhitespace( value='', ), + type_parameters=None, + whitespace_after_type_parameters=SimpleWhitespace( + value='', + ), ), ], header=[], @@ -612,6 +621,7 @@ def test_hidden_whitespace_and_syntax(self) -> None: ), ), asynchronous=None, + type_parameters=None, ), ], ) diff --git a/native/libcst/src/nodes/mod.rs b/native/libcst/src/nodes/mod.rs index d6d8152d5..9fbdb1afe 100644 --- a/native/libcst/src/nodes/mod.rs +++ b/native/libcst/src/nodes/mod.rs @@ -112,13 +112,15 @@ pub(crate) mod deflated { DeflatedMatchSingleton as MatchSingleton, DeflatedMatchStar as MatchStar, DeflatedMatchTuple as MatchTuple, DeflatedMatchValue as MatchValue, DeflatedNameItem as NameItem, DeflatedNonlocal as Nonlocal, DeflatedOrElse as OrElse, - DeflatedPass as Pass, DeflatedRaise as Raise, DeflatedReturn as Return, - DeflatedSimpleStatementLine as SimpleStatementLine, + DeflatedParamSpec as ParamSpec, DeflatedPass as Pass, DeflatedRaise as Raise, + DeflatedReturn as Return, DeflatedSimpleStatementLine as SimpleStatementLine, DeflatedSimpleStatementSuite as SimpleStatementSuite, DeflatedSmallStatement as SmallStatement, DeflatedStarrableMatchSequenceElement as StarrableMatchSequenceElement, DeflatedStatement as Statement, DeflatedSuite as Suite, DeflatedTry as Try, - DeflatedTryStar as TryStar, DeflatedWhile as While, DeflatedWith as With, - DeflatedWithItem as WithItem, + DeflatedTryStar as TryStar, DeflatedTypeAlias as TypeAlias, DeflatedTypeParam as TypeParam, + DeflatedTypeParameters as TypeParameters, DeflatedTypeVar as TypeVar, + DeflatedTypeVarLike as TypeVarLike, DeflatedTypeVarTuple as TypeVarTuple, + DeflatedWhile as While, DeflatedWith as With, DeflatedWithItem as WithItem, }; } diff --git a/native/libcst/src/nodes/statement.rs b/native/libcst/src/nodes/statement.rs index 65006ab36..43bb6886b 100644 --- a/native/libcst/src/nodes/statement.rs +++ b/native/libcst/src/nodes/statement.rs @@ -304,6 +304,7 @@ pub enum SmallStatement<'a> { Nonlocal(Nonlocal<'a>), AugAssign(AugAssign<'a>), Del(Del<'a>), + TypeAlias(TypeAlias<'a>), } impl<'r, 'a> DeflatedSmallStatement<'r, 'a> { @@ -324,6 +325,7 @@ impl<'r, 'a> DeflatedSmallStatement<'r, 'a> { Self::Nonlocal(l) => Self::Nonlocal(l.with_semicolon(semicolon)), Self::AugAssign(a) => Self::AugAssign(a.with_semicolon(semicolon)), Self::Del(d) => Self::Del(d.with_semicolon(semicolon)), + Self::TypeAlias(t) => Self::TypeAlias(t.with_semicolon(semicolon)), } } } @@ -793,6 +795,7 @@ impl<'a> Codegen<'a> for ImportNames<'a> { #[cst_node] pub struct FunctionDef<'a> { pub name: Name<'a>, + pub type_parameters: Option>, pub params: Parameters<'a>, pub body: Suite<'a>, pub decorators: Vec>, @@ -802,6 +805,7 @@ pub struct FunctionDef<'a> { pub lines_after_decorators: Vec>, pub whitespace_after_def: SimpleWhitespace<'a>, pub whitespace_after_name: SimpleWhitespace<'a>, + pub whitespace_after_type_parameters: SimpleWhitespace<'a>, pub whitespace_before_params: ParenthesizableWhitespace<'a>, pub whitespace_before_colon: SimpleWhitespace<'a>, @@ -838,6 +842,12 @@ impl<'a> Codegen<'a> for FunctionDef<'a> { self.whitespace_after_def.codegen(state); self.name.codegen(state); self.whitespace_after_name.codegen(state); + + if let Some(tp) = &self.type_parameters { + tp.codegen(state); + self.whitespace_after_type_parameters.codegen(state); + } + state.add_token("("); self.whitespace_before_params.codegen(state); self.params.codegen(state); @@ -893,10 +903,26 @@ impl<'r, 'a> Inflate<'a> for DeflatedFunctionDef<'r, 'a> { parse_simple_whitespace(config, &mut (*self.def_tok).whitespace_after.borrow_mut())?; let name = self.name.inflate(config)?; - let whitespace_after_name = parse_simple_whitespace( - config, - &mut (*self.open_paren_tok).whitespace_before.borrow_mut(), - )?; + + let whitespace_after_name; + let mut type_parameters = Default::default(); + let mut whitespace_after_type_parameters = Default::default(); + + if let Some(tp) = self.type_parameters { + let rbracket_tok = tp.rbracket.tok.clone(); + whitespace_after_name = parse_simple_whitespace( + config, + &mut tp.lbracket.tok.whitespace_before.borrow_mut(), + )?; + type_parameters = Some(tp.inflate(config)?); + whitespace_after_type_parameters = + parse_simple_whitespace(config, &mut rbracket_tok.whitespace_after.borrow_mut())?; + } else { + whitespace_after_name = parse_simple_whitespace( + config, + &mut self.open_paren_tok.whitespace_before.borrow_mut(), + )?; + } let whitespace_before_params = parse_parenthesizable_whitespace( config, @@ -914,6 +940,7 @@ impl<'r, 'a> Inflate<'a> for DeflatedFunctionDef<'r, 'a> { let body = self.body.inflate(config)?; Ok(Self::Inflated { name, + type_parameters, params, body, decorators, @@ -923,6 +950,7 @@ impl<'r, 'a> Inflate<'a> for DeflatedFunctionDef<'r, 'a> { lines_after_decorators, whitespace_after_def, whitespace_after_name, + whitespace_after_type_parameters, whitespace_before_params, whitespace_before_colon, }) @@ -1673,6 +1701,7 @@ impl<'r, 'a> Inflate<'a> for DeflatedWhile<'r, 'a> { #[cst_node] pub struct ClassDef<'a> { pub name: Name<'a>, + pub type_parameters: Option>, pub body: Suite<'a>, pub bases: Vec>, pub keywords: Vec>, @@ -1683,6 +1712,7 @@ pub struct ClassDef<'a> { pub lines_after_decorators: Vec>, pub whitespace_after_class: SimpleWhitespace<'a>, pub whitespace_after_name: SimpleWhitespace<'a>, + pub whitespace_after_type_parameters: SimpleWhitespace<'a>, pub whitespace_before_colon: SimpleWhitespace<'a>, pub(crate) class_tok: TokenRef<'a>, @@ -1709,6 +1739,11 @@ impl<'a> Codegen<'a> for ClassDef<'a> { self.name.codegen(state); self.whitespace_after_name.codegen(state); + if let Some(tp) = &self.type_parameters { + tp.codegen(state); + self.whitespace_after_type_parameters.codegen(state); + } + let need_parens = !self.bases.is_empty() || !self.keywords.is_empty(); if let Some(lpar) = &self.lpar { @@ -1753,19 +1788,27 @@ impl<'r, 'a> Inflate<'a> for DeflatedClassDef<'r, 'a> { parse_simple_whitespace(config, &mut (*self.class_tok).whitespace_after.borrow_mut())?; let name = self.name.inflate(config)?; - let (whitespace_after_name, lpar, bases, keywords, rpar) = - if let Some(lpar_tok) = self.lpar_tok.as_mut() { - ( - parse_simple_whitespace(config, &mut lpar_tok.whitespace_before.borrow_mut())?, - self.lpar.map(|lpar| lpar.inflate(config)).transpose()?, - self.bases.inflate(config)?, - self.keywords.inflate(config)?, - self.rpar.map(|rpar| rpar.inflate(config)).transpose()?, - // TODO: set whitespace_after_arg for last arg? - ) - } else { - Default::default() - }; + let (mut whitespace_after_name, mut type_parameters, mut whitespace_after_type_parameters) = + Default::default(); + + if let Some(tparams) = self.type_parameters { + let rbracket_tok = tparams.rbracket.tok.clone(); + whitespace_after_name = parse_simple_whitespace( + config, + &mut tparams.lbracket.tok.whitespace_before.borrow_mut(), + )?; + type_parameters = Some(tparams.inflate(config)?); + whitespace_after_type_parameters = + parse_simple_whitespace(config, &mut rbracket_tok.whitespace_after.borrow_mut())?; + } else if let Some(lpar_tok) = self.lpar_tok.as_mut() { + whitespace_after_name = + parse_simple_whitespace(config, &mut lpar_tok.whitespace_before.borrow_mut())?; + } + + let lpar = self.lpar.inflate(config)?; + let bases = self.bases.inflate(config)?; + let keywords = self.keywords.inflate(config)?; + let rpar = self.rpar.inflate(config)?; let whitespace_before_colon = parse_simple_whitespace( config, @@ -1775,6 +1818,7 @@ impl<'r, 'a> Inflate<'a> for DeflatedClassDef<'r, 'a> { Ok(Self::Inflated { name, + type_parameters, body, bases, keywords, @@ -1784,6 +1828,7 @@ impl<'r, 'a> Inflate<'a> for DeflatedClassDef<'r, 'a> { leading_lines, lines_after_decorators, whitespace_after_class, + whitespace_after_type_parameters, whitespace_after_name, whitespace_before_colon, }) @@ -3332,3 +3377,245 @@ impl<'r, 'a> Inflate<'a> for DeflatedMatchOr<'r, 'a> { }) } } + +#[cst_node] +pub struct TypeVar<'a> { + pub name: Name<'a>, + pub bound: Option>>, + pub colon: Option>, +} + +impl<'a> Codegen<'a> for TypeVar<'a> { + fn codegen(&self, state: &mut CodegenState<'a>) { + self.name.codegen(state); + self.colon.codegen(state); + if let Some(bound) = &self.bound { + bound.codegen(state); + } + } +} + +impl<'r, 'a> Inflate<'a> for DeflatedTypeVar<'r, 'a> { + type Inflated = TypeVar<'a>; + fn inflate(self, config: &Config<'a>) -> Result { + let name = self.name.inflate(config)?; + let colon = self.colon.inflate(config)?; + let bound = self.bound.inflate(config)?; + Ok(Self::Inflated { name, bound, colon }) + } +} + +#[cst_node] +pub struct TypeVarTuple<'a> { + pub name: Name<'a>, + + pub whitespace_after_star: SimpleWhitespace<'a>, + + pub(crate) star_tok: TokenRef<'a>, +} + +impl<'a> Codegen<'a> for TypeVarTuple<'a> { + fn codegen(&self, state: &mut CodegenState<'a>) { + state.add_token("*"); + self.whitespace_after_star.codegen(state); + self.name.codegen(state); + } +} + +impl<'r, 'a> Inflate<'a> for DeflatedTypeVarTuple<'r, 'a> { + type Inflated = TypeVarTuple<'a>; + fn inflate(self, config: &Config<'a>) -> Result { + let whitespace_after_star = + parse_simple_whitespace(config, &mut self.star_tok.whitespace_after.borrow_mut())?; + let name = self.name.inflate(config)?; + Ok(Self::Inflated { + name, + whitespace_after_star, + }) + } +} + +#[cst_node] +pub struct ParamSpec<'a> { + pub name: Name<'a>, + + pub whitespace_after_star: SimpleWhitespace<'a>, + + pub(crate) star_tok: TokenRef<'a>, +} + +impl<'a> Codegen<'a> for ParamSpec<'a> { + fn codegen(&self, state: &mut CodegenState<'a>) { + state.add_token("**"); + self.whitespace_after_star.codegen(state); + self.name.codegen(state); + } +} + +impl<'r, 'a> Inflate<'a> for DeflatedParamSpec<'r, 'a> { + type Inflated = ParamSpec<'a>; + fn inflate(self, config: &Config<'a>) -> Result { + let whitespace_after_star = + parse_simple_whitespace(config, &mut self.star_tok.whitespace_after.borrow_mut())?; + let name = self.name.inflate(config)?; + Ok(Self::Inflated { + name, + whitespace_after_star, + }) + } +} + +#[cst_node(Inflate, Codegen)] +pub enum TypeVarLike<'a> { + TypeVar(TypeVar<'a>), + TypeVarTuple(TypeVarTuple<'a>), + ParamSpec(ParamSpec<'a>), +} + +#[cst_node] +pub struct TypeParam<'a> { + pub param: TypeVarLike<'a>, + pub comma: Option>, +} + +impl<'a> Codegen<'a> for TypeParam<'a> { + fn codegen(&self, state: &mut CodegenState<'a>) { + self.param.codegen(state); + self.comma.codegen(state); + } +} + +impl<'r, 'a> Inflate<'a> for DeflatedTypeParam<'r, 'a> { + type Inflated = TypeParam<'a>; + fn inflate(self, config: &Config<'a>) -> Result { + let param = self.param.inflate(config)?; + let comma = self.comma.inflate(config)?; + Ok(Self::Inflated { param, comma }) + } +} + +impl<'r, 'a> WithComma<'r, 'a> for DeflatedTypeParam<'r, 'a> { + fn with_comma(self, comma: DeflatedComma<'r, 'a>) -> Self { + Self { + comma: Some(comma), + ..self + } + } +} + +#[cst_node] +pub struct TypeParameters<'a> { + pub params: Vec>, + + pub lbracket: LeftSquareBracket<'a>, + pub rbracket: RightSquareBracket<'a>, +} + +impl<'a> Codegen<'a> for TypeParameters<'a> { + fn codegen(&self, state: &mut CodegenState<'a>) { + self.lbracket.codegen(state); + let params_len = self.params.len(); + for (idx, param) in self.params.iter().enumerate() { + param.codegen(state); + if idx + 1 < params_len && param.comma.is_none() { + state.add_token(", "); + } + } + self.rbracket.codegen(state); + } +} + +impl<'r, 'a> Inflate<'a> for DeflatedTypeParameters<'r, 'a> { + type Inflated = TypeParameters<'a>; + fn inflate(self, config: &Config<'a>) -> Result { + let lbracket = self.lbracket.inflate(config)?; + let params = self.params.inflate(config)?; + let rbracket = self.rbracket.inflate(config)?; + Ok(Self::Inflated { + params, + lbracket, + rbracket, + }) + } +} + +#[cst_node] +pub struct TypeAlias<'a> { + pub name: Name<'a>, + pub value: Box>, + pub type_parameters: Option>, + + pub whitespace_after_type: SimpleWhitespace<'a>, + pub whitespace_after_name: Option>, + pub whitespace_after_type_parameters: Option>, + pub whitespace_after_equals: SimpleWhitespace<'a>, + pub semicolon: Option>, + + pub(crate) type_tok: TokenRef<'a>, + pub(crate) lbracket_tok: Option>, + pub(crate) equals_tok: TokenRef<'a>, +} + +impl<'a> Codegen<'a> for TypeAlias<'a> { + fn codegen(&self, state: &mut CodegenState<'a>) { + state.add_token("type"); + self.whitespace_after_type.codegen(state); + self.name.codegen(state); + if self.whitespace_after_name.is_none() && self.type_parameters.is_none() { + state.add_token(" "); + } else { + self.whitespace_after_name.codegen(state); + } + if self.type_parameters.is_some() { + self.type_parameters.codegen(state); + self.whitespace_after_type_parameters.codegen(state); + } + state.add_token("="); + self.whitespace_after_equals.codegen(state); + self.value.codegen(state); + self.semicolon.codegen(state); + } +} + +impl<'r, 'a> Inflate<'a> for DeflatedTypeAlias<'r, 'a> { + type Inflated = TypeAlias<'a>; + fn inflate(self, config: &Config<'a>) -> Result { + let whitespace_after_type = + parse_simple_whitespace(config, &mut self.type_tok.whitespace_after.borrow_mut())?; + let name = self.name.inflate(config)?; + let whitespace_after_name = Some(if let Some(tok) = self.lbracket_tok { + parse_simple_whitespace(config, &mut tok.whitespace_before.borrow_mut()) + } else { + parse_simple_whitespace(config, &mut self.equals_tok.whitespace_before.borrow_mut()) + }?); + let type_parameters = self.type_parameters.inflate(config)?; + let whitespace_after_type_parameters = if type_parameters.is_some() { + Some(parse_simple_whitespace( + config, + &mut self.equals_tok.whitespace_before.borrow_mut(), + )?) + } else { + None + }; + let whitespace_after_equals = + parse_simple_whitespace(config, &mut self.equals_tok.whitespace_after.borrow_mut())?; + let value = self.value.inflate(config)?; + let semicolon = self.semicolon.inflate(config)?; + Ok(Self::Inflated { + name, + value, + type_parameters, + whitespace_after_type, + whitespace_after_name, + whitespace_after_type_parameters, + whitespace_after_equals, + semicolon, + }) + } +} + +impl<'r, 'a> DeflatedTypeAlias<'r, 'a> { + pub fn with_semicolon(self, semicolon: Option>) -> Self { + Self { semicolon, ..self } + } +} diff --git a/native/libcst/src/parser/grammar.rs b/native/libcst/src/parser/grammar.rs index 5e23357d3..71ea86e7a 100644 --- a/native/libcst/src/parser/grammar.rs +++ b/native/libcst/src/parser/grammar.rs @@ -131,6 +131,7 @@ parser! { #[cache] rule simple_stmt() -> SmallStatement<'input, 'a> = assignment() + / &lit("type") s: type_stmt() {SmallStatement::TypeAlias(s)} / e:star_expressions() { SmallStatement::Expr(Expr { value: e, semicolon: None }) } / &lit("return") s:return_stmt() { SmallStatement::Return(s) } // this is expanded from the original grammar's import_stmt rule @@ -320,9 +321,9 @@ parser! { / class_def_raw() rule class_def_raw() -> ClassDef<'input, 'a> - = kw:lit("class") n:name() arg:(l:lpar() a:arguments()? r:rpar() {(l, a, r)})? + = kw:lit("class") n:name() t:type_params()? arg:(l:lpar() a:arguments()? r:rpar() {(l, a, r)})? col:lit(":") b:block() {? - make_class_def(kw, n, arg, col, b) + make_class_def(kw, n, t, arg, col, b) } // Function definitions @@ -337,13 +338,13 @@ parser! { } rule function_def_raw() -> FunctionDef<'input, 'a> - = def:lit("def") n:name() op:lit("(") params:params()? + = def:lit("def") n:name() t:type_params()? op:lit("(") params:params()? cp:lit(")") ty:_returns()? c:lit(":") b:block() { - make_function_def(None, def, n, op, params, cp, ty, c, b) + make_function_def(None, def, n, t, op, params, cp, ty, c, b) } - / asy:tok(Async, "ASYNC") def:lit("def") n:name() op:lit("(") params:params()? + / asy:tok(Async, "ASYNC") def:lit("def") n:name() t:type_params()? op:lit("(") params:params()? cp:lit(")") ty:_returns()? c:lit(":") b:block() { - make_function_def(Some(asy), def, n, op, params, cp, ty, c, b) + make_function_def(Some(asy), def, n, t, op, params, cp, ty, c, b) } // Function parameters @@ -776,6 +777,27 @@ parser! { make_match_keyword_element(arg, eq, value) } + // Type statement + + rule type_stmt() -> TypeAlias<'input, 'a> + = t:lit("type") n:name() ps:type_params()? eq:lit("=") v:expression() { + make_type_alias(t, n, ps, eq, v) + } + + // Type parameter declaration + + rule type_params() -> TypeParameters<'input, 'a> + = lb:lbrak() ps:separated_trailer(, ) rb:rbrak() { + make_type_parameters(lb, comma_separate(ps.0, ps.1, ps.2), rb) + } + + rule type_param() -> TypeParam<'input, 'a> + = n:name() b:type_param_bound()? { make_type_var(n, b) } + / s:lit("*") n:name() { make_type_var_tuple(s, n) } + / s:lit("**") n:name() { make_param_spec(s, n) } + + rule type_param_bound() -> TypeParamBound<'input, 'a> + = c:lit(":") e:expression() { make_type_param_bound(c, e) } // Expressions #[cache] @@ -1511,6 +1533,7 @@ fn make_function_def<'input, 'a>( async_tok: Option>, def_tok: TokenRef<'input, 'a>, name: Name<'input, 'a>, + type_parameters: Option>, open_paren_tok: TokenRef<'input, 'a>, params: Option>, close_paren_tok: TokenRef<'input, 'a>, @@ -1521,6 +1544,7 @@ fn make_function_def<'input, 'a>( let asynchronous = async_tok.as_ref().map(|_| make_async()); FunctionDef { name, + type_parameters, params: params.unwrap_or_default(), body, decorators: Default::default(), @@ -2761,6 +2785,7 @@ fn make_await<'input, 'a>( fn make_class_def<'input, 'a>( class_tok: TokenRef<'input, 'a>, name: Name<'input, 'a>, + type_parameters: Option>, args: Option<( LeftParen<'input, 'a>, Option>>, @@ -2801,6 +2826,7 @@ fn make_class_def<'input, 'a>( } Ok(ClassDef { name, + type_parameters, body, bases, keywords, @@ -3339,3 +3365,81 @@ fn make_match_keyword_element<'input, 'a>( equal_tok, } } + +struct TypeParamBound<'input, 'a>(TokenRef<'input, 'a>, Expression<'input, 'a>); + +fn make_type_param_bound<'input, 'a>( + colon_tok: TokenRef<'input, 'a>, + e: Expression<'input, 'a>, +) -> TypeParamBound<'input, 'a> { + TypeParamBound(colon_tok, e) +} + +fn make_param_spec<'input, 'a>( + star_tok: TokenRef<'input, 'a>, + name: Name<'input, 'a>, +) -> TypeParam<'input, 'a> { + TypeParam { + param: TypeVarLike::ParamSpec(ParamSpec { name, star_tok }), + comma: Default::default(), + } +} + +fn make_type_var_tuple<'input, 'a>( + star_tok: TokenRef<'input, 'a>, + name: Name<'input, 'a>, +) -> TypeParam<'input, 'a> { + TypeParam { + param: TypeVarLike::TypeVarTuple(TypeVarTuple { name, star_tok }), + comma: Default::default(), + } +} + +fn make_type_var<'input, 'a>( + name: Name<'input, 'a>, + bound: Option>, +) -> TypeParam<'input, 'a> { + let (bound, colon) = match bound { + Some(TypeParamBound(c, e)) => (Some(Box::new(e)), Some(make_colon(c))), + _ => (None, None), + }; + TypeParam { + param: TypeVarLike::TypeVar(TypeVar { name, bound, colon }), + comma: Default::default(), + } +} + +fn make_type_parameters<'input, 'a>( + lbracket: LeftSquareBracket<'input, 'a>, + params: Vec>, + rbracket: RightSquareBracket<'input, 'a>, +) -> TypeParameters<'input, 'a> { + TypeParameters { + lbracket, + params, + rbracket, + } +} + +fn make_type_alias<'input, 'a>( + type_tok: TokenRef<'input, 'a>, + name: Name<'input, 'a>, + type_parameters: Option>, + equals_tok: TokenRef<'input, 'a>, + value: Expression<'input, 'a>, +) -> TypeAlias<'input, 'a> { + let lbracket_tok = if let Some(tp) = &type_parameters { + Some(tp.lbracket.tok) + } else { + None + }; + TypeAlias { + type_tok, + name, + type_parameters, + equals_tok, + value: Box::new(value), + semicolon: Default::default(), + lbracket_tok, + } +} diff --git a/native/libcst/tests/fixtures/type_parameters.py b/native/libcst/tests/fixtures/type_parameters.py new file mode 100644 index 000000000..e5329f015 --- /dev/null +++ b/native/libcst/tests/fixtures/type_parameters.py @@ -0,0 +1,57 @@ +# fmt: off + +type TA = int + +type TA1[A] = lambda A: A + +class Outer[A]: + type TA1[A] = None + +type TA1[A, B] = dict[A, B] + +class Outer[A]: + def inner[B](self): + type TA1[C] = TA1[A, B] | int + return TA1 + +def more_generic[T, *Ts, **P](): + type TA[T2, *Ts2, **P2] = tuple[Callable[P, tuple[T, *Ts]], Callable[P2, tuple[T2, *Ts2]]] + return TA + +type Recursive = Recursive + +def func[A](A): return A + +class ClassA: + def func[__A](self, __A): return __A + +class ClassA[A, B](dict[A, B]): + ... + +class ClassA[A]: + def funcB[B](self): + class ClassC[C]: + def funcD[D](self): + return lambda: (A, B, C, D) + return ClassC + +class Child[T](Base[lambda: (int, outer_var, T)]): ... + +type Alias[T: ([T for T in (T, [1])[1]], T)] = [T for T in T.__name__] +type Alias[T: [lambda: T for T in (T, [1])[1]]] = [lambda: T for T in T.__name__] + +class Foo[T: Foo, U: (Foo, Foo)]: + pass + +def func[T](a: T = "a", *, b: T = "b"): + return (a, b) + +def func1[A: str, B: str | int, C: (int, str)](): + return (A, B, C) + +type A [ T , * V ] =foo;type B=A + +def AAAAAAAAAAAAAAAAAA [ T : int ,*Ts , ** TT ] ():pass +class AAAAAAAAAAAAAAAAAA [ T : int ,*Ts , ** TT ] :pass + +def yikes[A:int,*B,**C](*d:*tuple[A,*B,...])->A:pass \ No newline at end of file