From abab20b4cd4546a91f2eb0fe7f6a5a16fc141ab9 Mon Sep 17 00:00:00 2001 From: Aleksei Latyshev Date: Fri, 24 May 2024 12:43:37 +0200 Subject: [PATCH 1/2] [`pyupgrade`] lint TypeAliasType in UP040 --- .../test/fixtures/pyupgrade/UP040.py | 34 +++ .../src/checkers/ast/analyze/statement.rs | 3 + .../pyupgrade/rules/use_pep695_type_alias.rs | 265 +++++++++++++----- ...er__rules__pyupgrade__tests__UP040.py.snap | 112 ++++++++ 4 files changed, 340 insertions(+), 74 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP040.py b/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP040.py index 0368a34800db2..e107f8da25494 100644 --- a/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP040.py +++ b/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP040.py @@ -51,3 +51,37 @@ class Foo: # type alias. T = typing.TypeVar["T"] Decorator: TypeAlias = typing.Callable[[T], T] + + +from typing import TypeVar, Annotated, TypeAliasType + +from annotated_types import Gt, SupportGt + + +# https://github.com/astral-sh/ruff/issues/11422 +T = TypeVar("T") +PositiveList = TypeAliasType( + "PositiveList", list[Annotated[T, Gt(0)]], type_params=(T,) +) + +# Bound +T = TypeVar("T", bound=SupportGt) +PositiveList = TypeAliasType( + "PositiveList", list[Annotated[T, Gt(0)]], type_params=(T,) +) + +# Multiple bounds +T1 = TypeVar("T1", bound=SupportGt) +T2 = TypeVar("T2") +T3 = TypeVar("T3") +Tuple3 = TypeAliasType("Tuple3", tuple[T1, T2, T3], type_params=(T1, T2, T3)) + +# No type_params +PositiveInt = TypeAliasType("PositiveInt", Annotated[int, Gt(0)]) +PositiveInt = TypeAliasType("PositiveInt", Annotated[int, Gt(0)], type_params=()) + +# OK: Other name +T = TypeVar("T", bound=SupportGt) +PositiveList = TypeAliasType( + "PositiveList2", list[Annotated[T, Gt(0)]], type_params=(T,) +) diff --git a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs index ddaf1d382523b..375ce1aafab06 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs @@ -1558,6 +1558,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { if checker.enabled(Rule::ListReverseCopy) { refurb::rules::list_assign_reversed(checker, assign); } + if checker.enabled(Rule::NonPEP695TypeAlias) { + pyupgrade::rules::non_pep695_type_alias_type(checker, assign); + } } Stmt::AnnAssign( assign_stmt @ ast::StmtAnnAssign { diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep695_type_alias.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep695_type_alias.rs index 0039194d21e63..aa6b6230a6ab9 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep695_type_alias.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep695_type_alias.rs @@ -1,13 +1,14 @@ use itertools::Itertools; -use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; +use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::{ self as ast, visitor::{self, Visitor}, - Expr, ExprCall, ExprName, ExprSubscript, Identifier, Stmt, StmtAnnAssign, StmtAssign, + Expr, ExprCall, ExprName, ExprSubscript, Identifier, Keyword, Stmt, StmtAnnAssign, StmtAssign, StmtTypeAlias, TypeParam, TypeParamTypeVar, }; +use ruff_python_codegen::Generator; use ruff_python_semantic::SemanticModel; use ruff_text_size::{Ranged, TextRange}; @@ -15,7 +16,8 @@ use crate::checkers::ast::Checker; use crate::settings::types::PythonVersion; /// ## What it does -/// Checks for use of `TypeAlias` annotation for declaring type aliases. +/// Checks for use of `TypeAlias` annotation or `TypeAliasType` assignment for declaring type +/// aliases. /// /// ## Why is this bad? /// The `type` keyword was introduced in Python 3.12 by [PEP 695] for defining @@ -36,17 +38,26 @@ use crate::settings::types::PythonVersion; /// ## Example /// ```python /// ListOfInt: TypeAlias = list[int] +/// PositiveInt = TypeAliasType("PositiveInt", Annotated[int, Gt(0)]) /// ``` /// /// Use instead: /// ```python /// type ListOfInt = list[int] +/// type PositiveInt = Annotated[int, Gt(0)] /// ``` /// /// [PEP 695]: https://peps.python.org/pep-0695/ #[violation] pub struct NonPEP695TypeAlias { name: String, + type_alias_kind: TypeAliasKind, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum TypeAliasKind { + TypeAlias, + TypeAliasType, } impl Violation for NonPEP695TypeAlias { @@ -54,8 +65,15 @@ impl Violation for NonPEP695TypeAlias { #[derive_message_formats] fn message(&self) -> String { - let NonPEP695TypeAlias { name } = self; - format!("Type alias `{name}` uses `TypeAlias` annotation instead of the `type` keyword") + let NonPEP695TypeAlias { + name, + type_alias_kind, + } = self; + let type_alias_method = match type_alias_kind { + TypeAliasKind::TypeAlias => "`TypeAlias` annotation", + TypeAliasKind::TypeAliasType => "`TypeAliasType` assignment", + }; + format!("Type alias `{name}` uses {type_alias_method} instead of the `type` keyword") } fn fix_title(&self) -> Option { @@ -63,6 +81,70 @@ impl Violation for NonPEP695TypeAlias { } } +/// UP040 +pub(crate) fn non_pep695_type_alias_type(checker: &mut Checker, stmt: &StmtAssign) { + if checker.settings.target_version < PythonVersion::Py312 { + return; + } + + let StmtAssign { targets, value, .. } = stmt; + let Expr::Call(ExprCall { + func, arguments, .. + }) = value.as_ref() + else { + return; + }; + let [Expr::Name(target_name)] = targets.as_slice() else { + return; + }; + let [Expr::StringLiteral(name), value] = arguments.args.as_ref() else { + return; + }; + if name.value.to_str() != target_name.id { + return; + } + let type_params = match arguments.keywords.as_ref() { + [] => &[], + [Keyword { + arg: Some(name), + value: Expr::Tuple(type_params), + .. + }] if name.as_str() == "type_params" => type_params.elts.as_slice(), + _ => return, + }; + + if !checker + .semantic() + .match_typing_expr(func.as_ref(), "TypeAliasType") + { + return; + } + + let Some(vars) = type_params + .iter() + .map(|expr| { + expr.as_name_expr().map(|name| { + expr_name_to_type_var(checker.semantic(), name).unwrap_or(TypeVar { + name, + restriction: None, + }) + }) + }) + .collect() + else { + return; + }; + checker.diagnostics.push(get_diagnostic( + checker.generator(), + stmt.range(), + target_name.id.clone(), + Box::new(value.clone()), + vars, + Applicability::Safe, + TypeAliasKind::TypeAliasType, + )); +} + /// UP040 pub(crate) fn non_pep695_type_alias(checker: &mut Checker, stmt: &StmtAnnAssign) { let StmtAnnAssign { @@ -109,6 +191,32 @@ pub(crate) fn non_pep695_type_alias(checker: &mut Checker, stmt: &StmtAnnAssign) .unique_by(|TypeVar { name, .. }| name.id.as_str()) .collect::>(); + checker.diagnostics.push(get_diagnostic( + checker.generator(), + stmt.range(), + name.clone(), + value.clone(), + vars, + // The fix is only safe in a type stub because new-style aliases have different runtime behavior + // See https://github.com/astral-sh/ruff/issues/6434 + if checker.source_type.is_stub() { + Applicability::Safe + } else { + Applicability::Unsafe + }, + TypeAliasKind::TypeAlias, + )); +} + +fn get_diagnostic( + generator: Generator, + stmt_range: TextRange, + name: String, + value: Box, + vars: Vec, + applicability: Applicability, + type_alias_kind: TypeAliasKind, +) -> Diagnostic { let type_params = if vars.is_empty() { None } else { @@ -141,27 +249,29 @@ pub(crate) fn non_pep695_type_alias(checker: &mut Checker, stmt: &StmtAnnAssign) }) }; - let mut diagnostic = Diagnostic::new(NonPEP695TypeAlias { name: name.clone() }, stmt.range()); - - let edit = Edit::range_replacement( - checker.generator().stmt(&Stmt::from(StmtTypeAlias { - range: TextRange::default(), - name: target.clone(), - type_params, - value: value.clone(), - })), - stmt.range(), - ); - // The fix is only safe in a type stub because new-style aliases have different runtime behavior - // See https://github.com/astral-sh/ruff/issues/6434 - let fix = if checker.source_type.is_stub() { - Fix::safe_edit(edit) - } else { - Fix::unsafe_edit(edit) - }; - diagnostic.set_fix(fix); - - checker.diagnostics.push(diagnostic); + Diagnostic::new( + NonPEP695TypeAlias { + name: name.clone(), + type_alias_kind, + }, + stmt_range, + ) + .with_fix(Fix::applicable_edit( + Edit::range_replacement( + generator.stmt(&Stmt::from(StmtTypeAlias { + range: TextRange::default(), + name: Box::new(Expr::Name(ExprName { + range: TextRange::default(), + id: name, + ctx: ast::ExprContext::Load, + })), + type_params, + value, + })), + stmt_range, + ), + applicability, + )) } #[derive(Debug)] @@ -188,57 +298,64 @@ impl<'a> Visitor<'a> for TypeVarReferenceVisitor<'a> { fn visit_expr(&mut self, expr: &'a Expr) { match expr { Expr::Name(name) if name.ctx.is_load() => { - let Some(Stmt::Assign(StmtAssign { value, .. })) = self - .semantic - .lookup_symbol(name.id.as_str()) - .and_then(|binding_id| { - self.semantic - .binding(binding_id) - .source - .map(|node_id| self.semantic.statement(node_id)) - }) - else { - return; + self.vars.extend(expr_name_to_type_var(self.semantic, name)); + } + _ => visitor::walk_expr(self, expr), + } + } +} + +fn expr_name_to_type_var<'a>( + semantic: &'a SemanticModel, + name: &'a ExprName, +) -> Option> { + let Some(Stmt::Assign(StmtAssign { value, .. })) = semantic + .lookup_symbol(name.id.as_str()) + .and_then(|binding_id| { + semantic + .binding(binding_id) + .source + .map(|node_id| semantic.statement(node_id)) + }) + else { + return None; + }; + + match value.as_ref() { + Expr::Subscript(ExprSubscript { + value: ref subscript_value, + .. + }) => { + if semantic.match_typing_expr(subscript_value, "TypeVar") { + return Some(TypeVar { + name, + restriction: None, + }); + } + } + Expr::Call(ExprCall { + func, arguments, .. + }) => { + if semantic.match_typing_expr(func, "TypeVar") + && arguments + .args + .first() + .is_some_and(Expr::is_string_literal_expr) + { + let restriction = if let Some(bound) = arguments.find_keyword("bound") { + Some(TypeVarRestriction::Bound(&bound.value)) + } else if arguments.args.len() > 1 { + Some(TypeVarRestriction::Constraint( + arguments.args.iter().skip(1).collect(), + )) + } else { + None }; - match value.as_ref() { - Expr::Subscript(ExprSubscript { - value: ref subscript_value, - .. - }) => { - if self.semantic.match_typing_expr(subscript_value, "TypeVar") { - self.vars.push(TypeVar { - name, - restriction: None, - }); - } - } - Expr::Call(ExprCall { - func, arguments, .. - }) => { - if self.semantic.match_typing_expr(func, "TypeVar") - && arguments - .args - .first() - .is_some_and(Expr::is_string_literal_expr) - { - let restriction = if let Some(bound) = arguments.find_keyword("bound") { - Some(TypeVarRestriction::Bound(&bound.value)) - } else if arguments.args.len() > 1 { - Some(TypeVarRestriction::Constraint( - arguments.args.iter().skip(1).collect(), - )) - } else { - None - }; - - self.vars.push(TypeVar { name, restriction }); - } - } - _ => {} - } + return Some(TypeVar { name, restriction }); } - _ => visitor::walk_expr(self, expr), } + _ => {} } + None } diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP040.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP040.py.snap index e7692cb305451..03c7ea34d3bb6 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP040.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP040.py.snap @@ -245,5 +245,117 @@ UP040.py:53:1: UP040 [*] Type alias `Decorator` uses `TypeAlias` annotation inst 52 52 | T = typing.TypeVar["T"] 53 |-Decorator: TypeAlias = typing.Callable[[T], T] 53 |+type Decorator[T] = typing.Callable[[T], T] +54 54 | +55 55 | +56 56 | from typing import TypeVar, Annotated, TypeAliasType +UP040.py:63:1: UP040 [*] Type alias `PositiveList` uses `TypeAliasType` assignment instead of the `type` keyword + | +61 | # https://github.com/astral-sh/ruff/issues/11422 +62 | T = TypeVar("T") +63 | / PositiveList = TypeAliasType( +64 | | "PositiveList", list[Annotated[T, Gt(0)]], type_params=(T,) +65 | | ) + | |_^ UP040 +66 | +67 | # Bound + | + = help: Use the `type` keyword + +ℹ Safe fix +60 60 | +61 61 | # https://github.com/astral-sh/ruff/issues/11422 +62 62 | T = TypeVar("T") +63 |-PositiveList = TypeAliasType( +64 |- "PositiveList", list[Annotated[T, Gt(0)]], type_params=(T,) +65 |-) + 63 |+type PositiveList[T] = list[Annotated[T, Gt(0)]] +66 64 | +67 65 | # Bound +68 66 | T = TypeVar("T", bound=SupportGt) + +UP040.py:69:1: UP040 [*] Type alias `PositiveList` uses `TypeAliasType` assignment instead of the `type` keyword + | +67 | # Bound +68 | T = TypeVar("T", bound=SupportGt) +69 | / PositiveList = TypeAliasType( +70 | | "PositiveList", list[Annotated[T, Gt(0)]], type_params=(T,) +71 | | ) + | |_^ UP040 +72 | +73 | # Multiple bounds + | + = help: Use the `type` keyword + +ℹ Safe fix +66 66 | +67 67 | # Bound +68 68 | T = TypeVar("T", bound=SupportGt) +69 |-PositiveList = TypeAliasType( +70 |- "PositiveList", list[Annotated[T, Gt(0)]], type_params=(T,) +71 |-) + 69 |+type PositiveList[T: SupportGt] = list[Annotated[T, Gt(0)]] +72 70 | +73 71 | # Multiple bounds +74 72 | T1 = TypeVar("T1", bound=SupportGt) + +UP040.py:77:1: UP040 [*] Type alias `Tuple3` uses `TypeAliasType` assignment instead of the `type` keyword + | +75 | T2 = TypeVar("T2") +76 | T3 = TypeVar("T3") +77 | Tuple3 = TypeAliasType("Tuple3", tuple[T1, T2, T3], type_params=(T1, T2, T3)) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP040 +78 | +79 | # No type_params + | + = help: Use the `type` keyword + +ℹ Safe fix +74 74 | T1 = TypeVar("T1", bound=SupportGt) +75 75 | T2 = TypeVar("T2") +76 76 | T3 = TypeVar("T3") +77 |-Tuple3 = TypeAliasType("Tuple3", tuple[T1, T2, T3], type_params=(T1, T2, T3)) + 77 |+type Tuple3[T1: SupportGt, T2, T3] = tuple[T1, T2, T3] +78 78 | +79 79 | # No type_params +80 80 | PositiveInt = TypeAliasType("PositiveInt", Annotated[int, Gt(0)]) + +UP040.py:80:1: UP040 [*] Type alias `PositiveInt` uses `TypeAliasType` assignment instead of the `type` keyword + | +79 | # No type_params +80 | PositiveInt = TypeAliasType("PositiveInt", Annotated[int, Gt(0)]) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP040 +81 | PositiveInt = TypeAliasType("PositiveInt", Annotated[int, Gt(0)], type_params=()) + | + = help: Use the `type` keyword + +ℹ Safe fix +77 77 | Tuple3 = TypeAliasType("Tuple3", tuple[T1, T2, T3], type_params=(T1, T2, T3)) +78 78 | +79 79 | # No type_params +80 |-PositiveInt = TypeAliasType("PositiveInt", Annotated[int, Gt(0)]) + 80 |+type PositiveInt = Annotated[int, Gt(0)] +81 81 | PositiveInt = TypeAliasType("PositiveInt", Annotated[int, Gt(0)], type_params=()) +82 82 | +83 83 | # OK: Other name + +UP040.py:81:1: UP040 [*] Type alias `PositiveInt` uses `TypeAliasType` assignment instead of the `type` keyword + | +79 | # No type_params +80 | PositiveInt = TypeAliasType("PositiveInt", Annotated[int, Gt(0)]) +81 | PositiveInt = TypeAliasType("PositiveInt", Annotated[int, Gt(0)], type_params=()) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP040 +82 | +83 | # OK: Other name + | + = help: Use the `type` keyword +ℹ Safe fix +78 78 | +79 79 | # No type_params +80 80 | PositiveInt = TypeAliasType("PositiveInt", Annotated[int, Gt(0)]) +81 |-PositiveInt = TypeAliasType("PositiveInt", Annotated[int, Gt(0)], type_params=()) + 81 |+type PositiveInt = Annotated[int, Gt(0)] +82 82 | +83 83 | # OK: Other name +84 84 | T = TypeVar("T", bound=SupportGt) From 9bcbf031b0249390342785bc628fa7955c94c66a Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Sun, 26 May 2024 14:57:38 -0400 Subject: [PATCH 2/2] Minor tweaks --- .../pyupgrade/rules/use_pep695_type_alias.rs | 60 +++++++++++-------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep695_type_alias.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep695_type_alias.rs index aa6b6230a6ab9..670954e408f24 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep695_type_alias.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep695_type_alias.rs @@ -16,8 +16,8 @@ use crate::checkers::ast::Checker; use crate::settings::types::PythonVersion; /// ## What it does -/// Checks for use of `TypeAlias` annotation or `TypeAliasType` assignment for declaring type -/// aliases. +/// Checks for use of `TypeAlias` annotations and `TypeAliasType` assignments +/// for declaring type aliases. /// /// ## Why is this bad? /// The `type` keyword was introduced in Python 3.12 by [PEP 695] for defining @@ -88,21 +88,26 @@ pub(crate) fn non_pep695_type_alias_type(checker: &mut Checker, stmt: &StmtAssig } let StmtAssign { targets, value, .. } = stmt; + let Expr::Call(ExprCall { func, arguments, .. }) = value.as_ref() else { return; }; + let [Expr::Name(target_name)] = targets.as_slice() else { return; }; + let [Expr::StringLiteral(name), value] = arguments.args.as_ref() else { return; }; + if name.value.to_str() != target_name.id { return; } + let type_params = match arguments.keywords.as_ref() { [] => &[], [Keyword { @@ -130,16 +135,17 @@ pub(crate) fn non_pep695_type_alias_type(checker: &mut Checker, stmt: &StmtAssig }) }) }) - .collect() + .collect::>>() else { return; }; - checker.diagnostics.push(get_diagnostic( + + checker.diagnostics.push(create_diagnostic( checker.generator(), stmt.range(), - target_name.id.clone(), - Box::new(value.clone()), - vars, + &target_name.id, + value, + &vars, Applicability::Safe, TypeAliasKind::TypeAliasType, )); @@ -147,6 +153,10 @@ pub(crate) fn non_pep695_type_alias_type(checker: &mut Checker, stmt: &StmtAssig /// UP040 pub(crate) fn non_pep695_type_alias(checker: &mut Checker, stmt: &StmtAnnAssign) { + if checker.settings.target_version < PythonVersion::Py312 { + return; + } + let StmtAnnAssign { target, annotation, @@ -154,11 +164,6 @@ pub(crate) fn non_pep695_type_alias(checker: &mut Checker, stmt: &StmtAnnAssign) .. } = stmt; - // Syntax only available in 3.12+ - if checker.settings.target_version < PythonVersion::Py312 { - return; - } - if !checker .semantic() .match_typing_expr(annotation, "TypeAlias") @@ -191,12 +196,12 @@ pub(crate) fn non_pep695_type_alias(checker: &mut Checker, stmt: &StmtAnnAssign) .unique_by(|TypeVar { name, .. }| name.id.as_str()) .collect::>(); - checker.diagnostics.push(get_diagnostic( + checker.diagnostics.push(create_diagnostic( checker.generator(), stmt.range(), - name.clone(), - value.clone(), - vars, + name, + value, + &vars, // The fix is only safe in a type stub because new-style aliases have different runtime behavior // See https://github.com/astral-sh/ruff/issues/6434 if checker.source_type.is_stub() { @@ -208,12 +213,13 @@ pub(crate) fn non_pep695_type_alias(checker: &mut Checker, stmt: &StmtAnnAssign) )); } -fn get_diagnostic( +/// Generate a [`Diagnostic`] for a non-PEP 695 type alias or type alias type. +fn create_diagnostic( generator: Generator, stmt_range: TextRange, - name: String, - value: Box, - vars: Vec, + name: &str, + value: &Expr, + vars: &[TypeVar], applicability: Applicability, type_alias_kind: TypeAliasKind, ) -> Diagnostic { @@ -223,17 +229,19 @@ fn get_diagnostic( Some(ast::TypeParams { range: TextRange::default(), type_params: vars - .into_iter() + .iter() .map(|TypeVar { name, restriction }| { TypeParam::TypeVar(TypeParamTypeVar { range: TextRange::default(), name: Identifier::new(name.id.clone(), TextRange::default()), bound: match restriction { - Some(TypeVarRestriction::Bound(bound)) => Some(Box::new(bound.clone())), + Some(TypeVarRestriction::Bound(bound)) => { + Some(Box::new((*bound).clone())) + } Some(TypeVarRestriction::Constraint(constraints)) => { Some(Box::new(Expr::Tuple(ast::ExprTuple { range: TextRange::default(), - elts: constraints.into_iter().cloned().collect(), + elts: constraints.iter().map(|expr| (*expr).clone()).collect(), ctx: ast::ExprContext::Load, parenthesized: true, }))) @@ -251,7 +259,7 @@ fn get_diagnostic( Diagnostic::new( NonPEP695TypeAlias { - name: name.clone(), + name: name.to_string(), type_alias_kind, }, stmt_range, @@ -262,11 +270,11 @@ fn get_diagnostic( range: TextRange::default(), name: Box::new(Expr::Name(ExprName { range: TextRange::default(), - id: name, + id: name.to_string(), ctx: ast::ExprContext::Load, })), type_params, - value, + value: Box::new(value.clone()), })), stmt_range, ),