From 2c0a65d50092cb545a8ec3da35020b4e5bcecc6f Mon Sep 17 00:00:00 2001 From: David Peter Date: Sat, 17 May 2025 21:41:17 +0200 Subject: [PATCH 1/3] [ty] Support `typing.TypeAliasType` --- crates/ty_project/tests/check.rs | 6 +- .../resources/mdtest/pep695_type_aliases.md | 37 ++++++++ crates/ty_python_semantic/src/types.rs | 85 ++++++++++++++++++- .../ty_python_semantic/src/types/call/bind.rs | 20 ++++- crates/ty_python_semantic/src/types/infer.rs | 18 ++-- 5 files changed, 151 insertions(+), 15 deletions(-) diff --git a/crates/ty_project/tests/check.rs b/crates/ty_project/tests/check.rs index d7f0231c981bd6..36f8b16f1be387 100644 --- a/crates/ty_project/tests/check.rs +++ b/crates/ty_project/tests/check.rs @@ -294,4 +294,8 @@ impl SourceOrderVisitor<'_> for PullTypesVisitor<'_> { /// Whether or not the .py/.pyi version of this file is expected to fail #[rustfmt::skip] -const KNOWN_FAILURES: &[(&str, bool, bool)] = &[]; +const KNOWN_FAILURES: &[(&str, bool, bool)] = &[ + // Fails with too-many-cycle-iterations due to a self-referential + // type alias, see https://github.com/astral-sh/ty/issues/256 + ("crates/ruff_linter/resources/test/fixtures/pyflakes/F401_34.py", true, true), +]; diff --git a/crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md b/crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md index efe494babed322..8458ec95f4d6a2 100644 --- a/crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md +++ b/crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md @@ -87,3 +87,40 @@ type TypeAliasType2 = TypeOf[Alias2] static_assert(not is_equivalent_to(TypeAliasType1, TypeAliasType2)) static_assert(is_disjoint_from(TypeAliasType1, TypeAliasType2)) ``` + +## Direct use of `TypeAliasType` + +`TypeAliasType` can also be used directly. This is useful for versions of Python prior to 3.12. + +```toml +[environment] +python-version = "3.9" +``` + +### Basic example + +```py +from typing_extensions import TypeAliasType, Union + +IntOrStr = TypeAliasType("IntOrStr", Union[int, str]) + +reveal_type(IntOrStr) # revealed: typing.TypeAliasType + +reveal_type(IntOrStr.__name__) # revealed: Literal["IntOrStr"] + +def f(x: IntOrStr) -> None: + reveal_type(x) # revealed: int | str +``` + +### Generic example + +```py +from typing_extensions import TypeAliasType, TypeVar + +T = TypeVar("T") + +IntAnd = TypeAliasType("IntAndT", tuple[int, T], type_params=(T,)) + +def f(x: IntAnd[str]) -> None: + reveal_type(x) # revealed: @Todo(Generic PEP-695 type alias) +``` diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 5b9c410729cddc..77e5792ad8f2a9 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -4012,6 +4012,45 @@ impl<'db> Type<'db> { Signatures::single(signature) } + Some(KnownClass::TypeAliasType) => { + // ```py + // def __new__( + // cls, + // name: str, + // value: Any, + // *, + // type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = () + // ) -> Self: ... + // ``` + let signature = CallableSignature::single( + self, + Signature::new( + Parameters::new([ + Parameter::positional_or_keyword(Name::new_static("name")) + .with_annotated_type(KnownClass::Str.to_instance(db)), + Parameter::positional_or_keyword(Name::new_static("value")) + .with_annotated_type(Type::any()) + .type_form(), + Parameter::keyword_only(Name::new_static("type_params")) + .with_annotated_type(KnownClass::Tuple.to_specialized_instance( + db, + [UnionType::from_elements( + db, + [ + KnownClass::TypeVar.to_instance(db), + KnownClass::ParamSpec.to_instance(db), + KnownClass::TypeVarTuple.to_instance(db), + ], + )], + )) + .with_default_type(TupleType::empty(db)), + ]), + None, + ), + ); + Signatures::single(signature) + } + Some(KnownClass::Property) => { let getter_signature = Signature::new( Parameters::new([ @@ -5318,7 +5357,7 @@ impl<'db> Type<'db> { Some(TypeDefinition::TypeVar(var.definition(db))) } KnownInstanceType::TypeAliasType(type_alias) => { - Some(TypeDefinition::TypeAlias(type_alias.definition(db))) + type_alias.definition(db).map(TypeDefinition::TypeAlias) } _ => None, }, @@ -7467,7 +7506,7 @@ impl<'db> ModuleLiteralType<'db> { } #[salsa::interned(debug)] -pub struct TypeAliasType<'db> { +pub struct PEP695TypeAliasType<'db> { #[returns(ref)] pub name: ast::name::Name, @@ -7475,7 +7514,7 @@ pub struct TypeAliasType<'db> { } #[salsa::tracked] -impl<'db> TypeAliasType<'db> { +impl<'db> PEP695TypeAliasType<'db> { pub(crate) fn definition(self, db: &'db dyn Db) -> Definition<'db> { let scope = self.rhs_scope(db); let type_alias_stmt_node = scope.node(db).expect_type_alias(); @@ -7492,6 +7531,42 @@ impl<'db> TypeAliasType<'db> { } } +#[salsa::interned(debug)] +pub struct BareTypeAliasType<'db> { + #[returns(ref)] + pub name: String, + pub value: Type<'db>, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, salsa::Update)] +pub enum TypeAliasType<'db> { + PEP695(PEP695TypeAliasType<'db>), + Bare(BareTypeAliasType<'db>), +} + +impl<'db> TypeAliasType<'db> { + pub(crate) fn name(self, db: &'db dyn Db) -> &'db str { + match self { + TypeAliasType::PEP695(type_alias) => type_alias.name(db), + TypeAliasType::Bare(type_alias) => type_alias.name(db).as_str(), + } + } + + pub(crate) fn definition(self, db: &'db dyn Db) -> Option> { + match self { + TypeAliasType::PEP695(type_alias) => Some(type_alias.definition(db)), + TypeAliasType::Bare(_) => None, + } + } + + pub(crate) fn value_type(self, db: &'db dyn Db) -> Type<'db> { + match self { + TypeAliasType::PEP695(type_alias) => type_alias.value_type(db), + TypeAliasType::Bare(type_alias) => type_alias.value(db), + } + } +} + /// Either the explicit `metaclass=` keyword of the class, or the inferred metaclass of one of its base classes. #[derive(Debug, Clone, PartialEq, Eq, salsa::Update)] pub(super) struct MetaclassCandidate<'db> { @@ -8004,6 +8079,10 @@ impl<'db> TupleType<'db> { .to_specialized_instance(db, [UnionType::from_elements(db, self.elements(db))]) } + pub(crate) fn empty(db: &'db dyn Db) -> Type<'db> { + Type::Tuple(TupleType::new(db, Box::<[Type<'db>]>::from([]))) + } + pub(crate) fn from_elements>>( db: &'db dyn Db, types: impl IntoIterator, diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index 990681c8da54d9..cef4e6d9eb5840 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -20,9 +20,10 @@ use crate::types::diagnostic::{ use crate::types::generics::{Specialization, SpecializationBuilder, SpecializationError}; use crate::types::signatures::{Parameter, ParameterForm}; use crate::types::{ - BoundMethodType, DataclassParams, DataclassTransformerParams, FunctionDecorators, FunctionType, - KnownClass, KnownFunction, KnownInstanceType, MethodWrapperKind, PropertyInstanceType, - TupleType, UnionType, WrapperDescriptorKind, todo_type, + BareTypeAliasType, BoundMethodType, DataclassParams, DataclassTransformerParams, + FunctionDecorators, FunctionType, KnownClass, KnownFunction, KnownInstanceType, + MethodWrapperKind, PropertyInstanceType, TupleType, TypeAliasType, UnionType, + WrapperDescriptorKind, todo_type, }; use ruff_db::diagnostic::{Annotation, Diagnostic, Severity, SubDiagnostic}; use ruff_python_ast as ast; @@ -908,6 +909,19 @@ impl<'db> Bindings<'db> { } } + Some(KnownClass::TypeAliasType) => { + if let [Some(name), Some(value), ..] = overload.parameter_types() { + // TODO: emit a diagnostic if the name is not a string literal + if let Some(name) = name.into_string_literal() { + overload.set_return_type(Type::KnownInstance( + KnownInstanceType::TypeAliasType(TypeAliasType::Bare( + BareTypeAliasType::new(db, name.value(db), value), + )), + )); + } + } + } + _ => {} }, diff --git a/crates/ty_python_semantic/src/types/infer.rs b/crates/ty_python_semantic/src/types/infer.rs index 2866bf5dc681ee..0ed13fabc06bfc 100644 --- a/crates/ty_python_semantic/src/types/infer.rs +++ b/crates/ty_python_semantic/src/types/infer.rs @@ -86,11 +86,11 @@ use crate::types::{ CallDunderError, CallableSignature, CallableType, ClassLiteral, ClassType, DataclassParams, DynamicType, FunctionDecorators, FunctionType, GenericAlias, IntersectionBuilder, IntersectionType, KnownClass, KnownFunction, KnownInstanceType, MemberLookupPolicy, - MetaclassCandidate, Parameter, ParameterForm, Parameters, Signature, Signatures, - StringLiteralType, SubclassOfType, Symbol, SymbolAndQualifiers, Truthiness, TupleType, Type, - TypeAliasType, TypeAndQualifiers, TypeArrayDisplay, TypeQualifiers, TypeVarBoundOrConstraints, - TypeVarInstance, TypeVarKind, TypeVarVariance, UnionBuilder, UnionType, binding_type, - todo_type, + MetaclassCandidate, PEP695TypeAliasType, Parameter, ParameterForm, Parameters, Signature, + Signatures, StringLiteralType, SubclassOfType, Symbol, SymbolAndQualifiers, Truthiness, + TupleType, Type, TypeAliasType, TypeAndQualifiers, TypeArrayDisplay, TypeQualifiers, + TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, TypeVarVariance, UnionBuilder, + UnionType, binding_type, todo_type, }; use crate::unpack::{Unpack, UnpackPosition}; use crate::util::subscript::{PyIndex, PySlice}; @@ -2374,12 +2374,13 @@ impl<'db> TypeInferenceBuilder<'db> { .node_scope(NodeWithScopeRef::TypeAlias(type_alias)) .to_scope_id(self.db(), self.file()); - let type_alias_ty = - Type::KnownInstance(KnownInstanceType::TypeAliasType(TypeAliasType::new( + let type_alias_ty = Type::KnownInstance(KnownInstanceType::TypeAliasType( + TypeAliasType::PEP695(PEP695TypeAliasType::new( self.db(), &type_alias.name.as_name_expr().unwrap().id, rhs_scope, - ))); + )), + )); self.add_declaration_with_binding( type_alias.into(), @@ -4860,6 +4861,7 @@ impl<'db> TypeInferenceBuilder<'db> { | KnownClass::Super | KnownClass::TypeVar | KnownClass::NamedTuple + | KnownClass::TypeAliasType ) ) // temporary special-casing for all subclasses of `enum.Enum` From 26d40ec4261af2435e47d8a339c494cb88919168 Mon Sep 17 00:00:00 2001 From: David Peter Date: Mon, 19 May 2025 08:57:33 +0200 Subject: [PATCH 2/3] Use ast::name::Name --- crates/ty_python_semantic/src/types.rs | 2 +- crates/ty_python_semantic/src/types/call/bind.rs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 77e5792ad8f2a9..e0118c2a3b1ca7 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -7534,7 +7534,7 @@ impl<'db> PEP695TypeAliasType<'db> { #[salsa::interned(debug)] pub struct BareTypeAliasType<'db> { #[returns(ref)] - pub name: String, + pub name: ast::name::Name, pub value: Type<'db>, } diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index cef4e6d9eb5840..47fbc7d846a3f2 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -915,7 +915,11 @@ impl<'db> Bindings<'db> { if let Some(name) = name.into_string_literal() { overload.set_return_type(Type::KnownInstance( KnownInstanceType::TypeAliasType(TypeAliasType::Bare( - BareTypeAliasType::new(db, name.value(db), value), + BareTypeAliasType::new( + db, + ast::name::Name::new(name.value(db)), + value, + ), )), )); } From 485200967ce36bc46b52d7275283ba6c59a7d660 Mon Sep 17 00:00:00 2001 From: David Peter Date: Mon, 19 May 2025 09:38:22 +0200 Subject: [PATCH 3/3] Move to infer.rs, add diagnostic --- crates/ty/docs/rules.md | 130 +++++++++++------- crates/ty_ide/src/goto.rs | 34 +++++ .../resources/mdtest/pep695_type_aliases.md | 14 ++ crates/ty_python_semantic/src/types.rs | 3 +- .../ty_python_semantic/src/types/call/bind.rs | 24 +--- .../src/types/diagnostic.rs | 22 +++ crates/ty_python_semantic/src/types/infer.rs | 74 ++++++++-- ty.schema.json | 10 ++ 8 files changed, 222 insertions(+), 89 deletions(-) diff --git a/crates/ty/docs/rules.md b/crates/ty/docs/rules.md index b8dba78d7c9711..7772a0b1d1813f 100644 --- a/crates/ty/docs/rules.md +++ b/crates/ty/docs/rules.md @@ -50,7 +50,7 @@ Calling a non-callable object will raise a `TypeError` at runtime. ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20call-non-callable) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L86) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L87) ## `conflicting-argument-forms` @@ -81,7 +81,7 @@ f(int) # error ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-argument-forms) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L117) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L118) ## `conflicting-declarations` @@ -111,7 +111,7 @@ a = 1 ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-declarations) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L143) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L144) ## `conflicting-metaclass` @@ -142,7 +142,7 @@ class C(A, B): ... ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20conflicting-metaclass) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L168) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L169) ## `cyclic-class-definition` @@ -173,7 +173,7 @@ class B(A): ... ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20cyclic-class-definition) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L194) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L195) ## `division-by-zero` @@ -196,7 +196,7 @@ Dividing by zero raises a `ZeroDivisionError` at runtime. ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20division-by-zero) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L220) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L221) ## `duplicate-base` @@ -222,7 +222,7 @@ class B(A, A): ... ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20duplicate-base) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L238) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L239) ## `escape-character-in-forward-annotation` @@ -359,7 +359,7 @@ TypeError: multiple bases have instance lay-out conflict ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20incompatible-slots) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L259) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L260) ## `inconsistent-mro` @@ -388,7 +388,7 @@ class C(A, B): ... ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20inconsistent-mro) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L345) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L346) ## `index-out-of-bounds` @@ -413,7 +413,7 @@ t[3] # IndexError: tuple index out of range ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20index-out-of-bounds) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L369) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L370) ## `invalid-argument-type` @@ -439,7 +439,7 @@ func("foo") # error: [invalid-argument-type] ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-argument-type) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L389) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L390) ## `invalid-assignment` @@ -466,7 +466,7 @@ a: int = '' ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-assignment) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L429) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L430) ## `invalid-attribute-access` @@ -499,7 +499,7 @@ C.instance_var = 3 # error: Cannot assign to instance variable ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-attribute-access) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1314) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1336) ## `invalid-base` @@ -513,7 +513,7 @@ TODO #14889 ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-base) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L451) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L452) ## `invalid-context-manager` @@ -539,7 +539,7 @@ with 1: ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-context-manager) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L460) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L461) ## `invalid-declaration` @@ -567,7 +567,7 @@ a: str ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-declaration) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L481) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L482) ## `invalid-exception-caught` @@ -608,7 +608,7 @@ except ZeroDivisionError: ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-exception-caught) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L504) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L505) ## `invalid-generic-class` @@ -639,7 +639,7 @@ class C[U](Generic[T]): ... ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-generic-class) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L540) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L541) ## `invalid-legacy-type-variable` @@ -672,7 +672,7 @@ def f(t: TypeVar("U")): ... ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-legacy-type-variable) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L566) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L567) ## `invalid-metaclass` @@ -704,7 +704,7 @@ class B(metaclass=f): ... ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-metaclass) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L594) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L616) ## `invalid-overload` @@ -752,7 +752,7 @@ def foo(x: int) -> int: ... ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-overload) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L621) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L643) ## `invalid-parameter-default` @@ -777,7 +777,7 @@ def f(a: int = ''): ... ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-parameter-default) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L664) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L686) ## `invalid-protocol` @@ -810,7 +810,7 @@ TypeError: Protocols can only inherit from other protocols, got ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-protocol) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L317) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L318) ## `invalid-raise` @@ -858,7 +858,7 @@ def g(): ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-raise) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L684) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L706) ## `invalid-return-type` @@ -882,7 +882,7 @@ def func() -> int: ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-return-type) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L410) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L411) ## `invalid-super-argument` @@ -926,7 +926,7 @@ super(B, A) # error: `A` does not satisfy `issubclass(A, B)` ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-super-argument) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L727) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L749) ## `invalid-syntax-in-forward-annotation` @@ -943,6 +943,32 @@ TODO #14889 * [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fstring_annotation.rs#L111) +## `invalid-type-alias-type` + +**Default level**: error + +
+detects invalid TypeAliasType definitions + +### What it does +Checks for the creation of invalid `TypeAliasType`s + +### Why is this bad? +There are several requirements that you must follow when creating a `TypeAliasType`. + +### Examples +```python +from typing import TypeAliasType + +IntOrStr = TypeAliasType("IntOrStr", int | str) # okay +NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name must be a string literal +``` + +### Links +* [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-alias-type) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L595) +
+ ## `invalid-type-checking-constant` **Default level**: error @@ -969,7 +995,7 @@ TYPE_CHECKING = '' ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-checking-constant) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L766) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L788) ## `invalid-type-form` @@ -998,7 +1024,7 @@ b: Annotated[int] # `Annotated` expects at least two arguments ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-form) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L790) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L812) ## `invalid-type-variable-constraints` @@ -1032,7 +1058,7 @@ T = TypeVar('T', bound=str) # valid bound TypeVar ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20invalid-type-variable-constraints) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L814) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L836) ## `missing-argument` @@ -1056,7 +1082,7 @@ func() # TypeError: func() missing 1 required positional argument: 'x' ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20missing-argument) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L843) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L865) ## `no-matching-overload` @@ -1084,7 +1110,7 @@ func("string") # error: [no-matching-overload] ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20no-matching-overload) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L862) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L884) ## `non-subscriptable` @@ -1107,7 +1133,7 @@ Subscripting an object that does not support it will raise a `TypeError` at runt ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20non-subscriptable) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L885) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L907) ## `not-iterable` @@ -1132,7 +1158,7 @@ for i in 34: # TypeError: 'int' object is not iterable ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20not-iterable) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L903) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L925) ## `parameter-already-assigned` @@ -1158,7 +1184,7 @@ f(1, x=2) # Error raised here ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20parameter-already-assigned) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L954) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L976) ## `raw-string-type-annotation` @@ -1217,7 +1243,7 @@ static_assert(int(2.0 * 3.0) == 6) # error: does not have a statically known tr ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20static-assert-error) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1290) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1312) ## `subclass-of-final-class` @@ -1245,7 +1271,7 @@ class B(A): ... # Error raised here ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20subclass-of-final-class) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1045) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1067) ## `too-many-positional-arguments` @@ -1271,7 +1297,7 @@ f("foo") # Error raised here ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20too-many-positional-arguments) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1090) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1112) ## `type-assertion-failure` @@ -1298,7 +1324,7 @@ def _(x: int): ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20type-assertion-failure) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1068) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1090) ## `unavailable-implicit-super-arguments` @@ -1342,7 +1368,7 @@ class A: ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unavailable-implicit-super-arguments) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1111) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1133) ## `unknown-argument` @@ -1368,7 +1394,7 @@ f(x=1, y=2) # Error raised here ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unknown-argument) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1168) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1190) ## `unresolved-attribute` @@ -1395,7 +1421,7 @@ A().foo # AttributeError: 'A' object has no attribute 'foo' ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-attribute) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1189) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1211) ## `unresolved-import` @@ -1419,7 +1445,7 @@ import foo # ModuleNotFoundError: No module named 'foo' ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-import) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1211) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1233) ## `unresolved-reference` @@ -1443,7 +1469,7 @@ print(x) # NameError: name 'x' is not defined ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unresolved-reference) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1230) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1252) ## `unsupported-bool-conversion` @@ -1479,7 +1505,7 @@ b1 < b2 < b1 # exception raised here ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-bool-conversion) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L923) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L945) ## `unsupported-operator` @@ -1506,7 +1532,7 @@ A() + A() # TypeError: unsupported operand type(s) for +: 'A' and 'A' ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20unsupported-operator) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1249) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1271) ## `zero-stepsize-in-slice` @@ -1530,7 +1556,7 @@ l[1:10:0] # ValueError: slice step cannot be zero ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20zero-stepsize-in-slice) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1271) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1293) ## `call-possibly-unbound-method` @@ -1548,7 +1574,7 @@ Calling an unbound method will raise an `AttributeError` at runtime. ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20call-possibly-unbound-method) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L104) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L105) ## `invalid-ignore-comment` @@ -1604,7 +1630,7 @@ A.c # AttributeError: type object 'A' has no attribute 'c' ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-attribute) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L975) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L997) ## `possibly-unbound-import` @@ -1635,7 +1661,7 @@ from module import a # ImportError: cannot import name 'a' from 'module' ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unbound-import) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L997) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1019) ## `redundant-cast` @@ -1661,7 +1687,7 @@ cast(int, f()) # Redundant ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20redundant-cast) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1342) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1364) ## `undefined-reveal` @@ -1684,7 +1710,7 @@ reveal_type(1) # NameError: name 'reveal_type' is not defined ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20undefined-reveal) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1150) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1172) ## `unknown-rule` @@ -1741,7 +1767,7 @@ print(x) # NameError: name 'x' is not defined ### Links * [Related issues](https://github.com/astral-sh/ty/issues?q=sort%3Aupdated-desc%20is%3Aissue%20is%3Aopen%20possibly-unresolved-reference) -* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1023) +* [View source](https://github.com/astral-sh/ruff/blob/main/crates%2Fty_python_semantic%2Fsrc%2Ftypes%2Fdiagnostic.rs#L1045) ## `unused-ignore-comment` diff --git a/crates/ty_ide/src/goto.rs b/crates/ty_ide/src/goto.rs index 046d6c1d784a3f..eaac8473c5afbf 100644 --- a/crates/ty_ide/src/goto.rs +++ b/crates/ty_ide/src/goto.rs @@ -522,6 +522,40 @@ mod tests { assert_snapshot!(test.goto_type_definition(), @"No type definitions found"); } + #[test] + fn goto_type_of_bare_type_alias_type() { + let test = cursor_test( + r#" + from typing_extensions import TypeAliasType + + Alias = TypeAliasType("Alias", tuple[int, int]) + + Alias + "#, + ); + + assert_snapshot!(test.goto_type_definition(), @r#" + info[goto-type-definition]: Type definition + --> main.py:4:13 + | + 2 | from typing_extensions import TypeAliasType + 3 | + 4 | Alias = TypeAliasType("Alias", tuple[int, int]) + | ^^^^^ + 5 | + 6 | Alias + | + info: Source + --> main.py:6:13 + | + 4 | Alias = TypeAliasType("Alias", tuple[int, int]) + 5 | + 6 | Alias + | ^^^^^ + | + "#); + } + #[test] fn goto_type_on_keyword_argument() { let test = cursor_test( diff --git a/crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md b/crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md index 8458ec95f4d6a2..65c4f988659b13 100644 --- a/crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md +++ b/crates/ty_python_semantic/resources/mdtest/pep695_type_aliases.md @@ -124,3 +124,17 @@ IntAnd = TypeAliasType("IntAndT", tuple[int, T], type_params=(T,)) def f(x: IntAnd[str]) -> None: reveal_type(x) # revealed: @Todo(Generic PEP-695 type alias) ``` + +### Error cases + +#### Name is not a string literal + +```py +from typing_extensions import TypeAliasType + +def get_name() -> str: + return "IntOrStr" + +# error: [invalid-type-alias-type] "The name of a `typing.TypeAlias` must be a string literal" +IntOrStr = TypeAliasType(get_name(), int | str) +``` diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index e0118c2a3b1ca7..15d675a869be06 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -7535,6 +7535,7 @@ impl<'db> PEP695TypeAliasType<'db> { pub struct BareTypeAliasType<'db> { #[returns(ref)] pub name: ast::name::Name, + pub definition: Option>, pub value: Type<'db>, } @@ -7555,7 +7556,7 @@ impl<'db> TypeAliasType<'db> { pub(crate) fn definition(self, db: &'db dyn Db) -> Option> { match self { TypeAliasType::PEP695(type_alias) => Some(type_alias.definition(db)), - TypeAliasType::Bare(_) => None, + TypeAliasType::Bare(type_alias) => type_alias.definition(db), } } diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index 47fbc7d846a3f2..990681c8da54d9 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -20,10 +20,9 @@ use crate::types::diagnostic::{ use crate::types::generics::{Specialization, SpecializationBuilder, SpecializationError}; use crate::types::signatures::{Parameter, ParameterForm}; use crate::types::{ - BareTypeAliasType, BoundMethodType, DataclassParams, DataclassTransformerParams, - FunctionDecorators, FunctionType, KnownClass, KnownFunction, KnownInstanceType, - MethodWrapperKind, PropertyInstanceType, TupleType, TypeAliasType, UnionType, - WrapperDescriptorKind, todo_type, + BoundMethodType, DataclassParams, DataclassTransformerParams, FunctionDecorators, FunctionType, + KnownClass, KnownFunction, KnownInstanceType, MethodWrapperKind, PropertyInstanceType, + TupleType, UnionType, WrapperDescriptorKind, todo_type, }; use ruff_db::diagnostic::{Annotation, Diagnostic, Severity, SubDiagnostic}; use ruff_python_ast as ast; @@ -909,23 +908,6 @@ impl<'db> Bindings<'db> { } } - Some(KnownClass::TypeAliasType) => { - if let [Some(name), Some(value), ..] = overload.parameter_types() { - // TODO: emit a diagnostic if the name is not a string literal - if let Some(name) = name.into_string_literal() { - overload.set_return_type(Type::KnownInstance( - KnownInstanceType::TypeAliasType(TypeAliasType::Bare( - BareTypeAliasType::new( - db, - ast::name::Name::new(name.value(db)), - value, - ), - )), - )); - } - } - } - _ => {} }, diff --git a/crates/ty_python_semantic/src/types/diagnostic.rs b/crates/ty_python_semantic/src/types/diagnostic.rs index 93eced51dc3ca7..57cec9260e2b0a 100644 --- a/crates/ty_python_semantic/src/types/diagnostic.rs +++ b/crates/ty_python_semantic/src/types/diagnostic.rs @@ -41,6 +41,7 @@ pub(crate) fn register_lints(registry: &mut LintRegistryBuilder) { registry.register_lint(&INVALID_EXCEPTION_CAUGHT); registry.register_lint(&INVALID_GENERIC_CLASS); registry.register_lint(&INVALID_LEGACY_TYPE_VARIABLE); + registry.register_lint(&INVALID_TYPE_ALIAS_TYPE); registry.register_lint(&INVALID_METACLASS); registry.register_lint(&INVALID_OVERLOAD); registry.register_lint(&INVALID_PARAMETER_DEFAULT); @@ -591,6 +592,27 @@ declare_lint! { } } +declare_lint! { + /// ## What it does + /// Checks for the creation of invalid `TypeAliasType`s + /// + /// ## Why is this bad? + /// There are several requirements that you must follow when creating a `TypeAliasType`. + /// + /// ## Examples + /// ```python + /// from typing import TypeAliasType + /// + /// IntOrStr = TypeAliasType("IntOrStr", int | str) # okay + /// NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name must be a string literal + /// ``` + pub(crate) static INVALID_TYPE_ALIAS_TYPE = { + summary: "detects invalid TypeAliasType definitions", + status: LintStatus::preview("1.0.0"), + default_level: Level::Error, + } +} + declare_lint! { /// ## What it does /// Checks for arguments to `metaclass=` that are invalid. diff --git a/crates/ty_python_semantic/src/types/infer.rs b/crates/ty_python_semantic/src/types/infer.rs index 0ed13fabc06bfc..67aa0ec1f65a12 100644 --- a/crates/ty_python_semantic/src/types/infer.rs +++ b/crates/ty_python_semantic/src/types/infer.rs @@ -71,26 +71,26 @@ use crate::types::diagnostic::{ CONFLICTING_METACLASS, CYCLIC_CLASS_DEFINITION, DIVISION_BY_ZERO, INCONSISTENT_MRO, INVALID_ARGUMENT_TYPE, INVALID_ASSIGNMENT, INVALID_ATTRIBUTE_ACCESS, INVALID_BASE, INVALID_DECLARATION, INVALID_GENERIC_CLASS, INVALID_LEGACY_TYPE_VARIABLE, - INVALID_PARAMETER_DEFAULT, INVALID_TYPE_FORM, INVALID_TYPE_VARIABLE_CONSTRAINTS, - POSSIBLY_UNBOUND_IMPORT, TypeCheckDiagnostics, UNDEFINED_REVEAL, UNRESOLVED_ATTRIBUTE, - UNRESOLVED_IMPORT, UNSUPPORTED_OPERATOR, report_implicit_return_type, - report_invalid_arguments_to_annotated, report_invalid_arguments_to_callable, - report_invalid_assignment, report_invalid_attribute_assignment, - report_invalid_generator_function_return_type, report_invalid_return_type, - report_possibly_unbound_attribute, + INVALID_PARAMETER_DEFAULT, INVALID_TYPE_ALIAS_TYPE, INVALID_TYPE_FORM, + INVALID_TYPE_VARIABLE_CONSTRAINTS, POSSIBLY_UNBOUND_IMPORT, TypeCheckDiagnostics, + UNDEFINED_REVEAL, UNRESOLVED_ATTRIBUTE, UNRESOLVED_IMPORT, UNSUPPORTED_OPERATOR, + report_implicit_return_type, report_invalid_arguments_to_annotated, + report_invalid_arguments_to_callable, report_invalid_assignment, + report_invalid_attribute_assignment, report_invalid_generator_function_return_type, + report_invalid_return_type, report_possibly_unbound_attribute, }; use crate::types::generics::GenericContext; use crate::types::mro::MroErrorKind; use crate::types::unpacker::{UnpackResult, Unpacker}; use crate::types::{ - CallDunderError, CallableSignature, CallableType, ClassLiteral, ClassType, DataclassParams, - DynamicType, FunctionDecorators, FunctionType, GenericAlias, IntersectionBuilder, - IntersectionType, KnownClass, KnownFunction, KnownInstanceType, MemberLookupPolicy, - MetaclassCandidate, PEP695TypeAliasType, Parameter, ParameterForm, Parameters, Signature, - Signatures, StringLiteralType, SubclassOfType, Symbol, SymbolAndQualifiers, Truthiness, - TupleType, Type, TypeAliasType, TypeAndQualifiers, TypeArrayDisplay, TypeQualifiers, - TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, TypeVarVariance, UnionBuilder, - UnionType, binding_type, todo_type, + BareTypeAliasType, CallDunderError, CallableSignature, CallableType, ClassLiteral, ClassType, + DataclassParams, DynamicType, FunctionDecorators, FunctionType, GenericAlias, + IntersectionBuilder, IntersectionType, KnownClass, KnownFunction, KnownInstanceType, + MemberLookupPolicy, MetaclassCandidate, PEP695TypeAliasType, Parameter, ParameterForm, + Parameters, Signature, Signatures, StringLiteralType, SubclassOfType, Symbol, + SymbolAndQualifiers, Truthiness, TupleType, Type, TypeAliasType, TypeAndQualifiers, + TypeArrayDisplay, TypeQualifiers, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, + TypeVarVariance, UnionBuilder, UnionType, binding_type, todo_type, }; use crate::unpack::{Unpack, UnpackPosition}; use crate::util::subscript::{PyIndex, PySlice}; @@ -5365,6 +5365,50 @@ impl<'db> TypeInferenceBuilder<'db> { )); } + KnownClass::TypeAliasType => { + let assigned_to = (self.index) + .try_expression(call_expression_node) + .and_then(|expr| expr.assigned_to(self.db())); + + let containing_assignment = + assigned_to.as_ref().and_then(|assigned_to| { + match assigned_to.node().targets.as_slice() { + [ast::Expr::Name(target)] => Some( + self.index.expect_single_definition(target), + ), + _ => None, + } + }); + + let [Some(name), Some(value), ..] = + overload.parameter_types() + else { + continue; + }; + + if let Some(name) = name.into_string_literal() { + overload.set_return_type(Type::KnownInstance( + KnownInstanceType::TypeAliasType( + TypeAliasType::Bare(BareTypeAliasType::new( + self.db(), + ast::name::Name::new(name.value(self.db())), + containing_assignment, + value, + )), + ), + )); + } else { + if let Some(builder) = self.context.report_lint( + &INVALID_TYPE_ALIAS_TYPE, + call_expression, + ) { + builder.into_diagnostic(format_args!( + "The name of a `typing.TypeAlias` must be a string literal", + )); + } + } + } + _ => (), } } diff --git a/ty.schema.json b/ty.schema.json index 85fd63614fcd48..6dcd55fcfdfc55 100644 --- a/ty.schema.json +++ b/ty.schema.json @@ -550,6 +550,16 @@ } ] }, + "invalid-type-alias-type": { + "title": "detects invalid TypeAliasType definitions", + "description": "## What it does\nChecks for the creation of invalid `TypeAliasType`s\n\n## Why is this bad?\nThere are several requirements that you must follow when creating a `TypeAliasType`.\n\n## Examples\n```python\nfrom typing import TypeAliasType\n\nIntOrStr = TypeAliasType(\"IntOrStr\", int | str) # okay\nNewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name must be a string literal\n```", + "default": "error", + "oneOf": [ + { + "$ref": "#/definitions/Level" + } + ] + }, "invalid-type-checking-constant": { "title": "detects invalid `TYPE_CHECKING` constant assignments", "description": "## What it does\nChecks for a value other than `False` assigned to the `TYPE_CHECKING` variable, or an\nannotation not assignable from `bool`.\n\n## Why is this bad?\nThe name `TYPE_CHECKING` is reserved for a flag that can be used to provide conditional\ncode seen only by the type checker, and not at runtime. Normally this flag is imported from\n`typing` or `typing_extensions`, but it can also be defined locally. If defined locally, it\nmust be assigned the value `False` at runtime; the type checker will consider its value to\nbe `True`. If annotated, it must be annotated as a type that can accept `bool` values.\n\n## Examples\n```python\nTYPE_CHECKING: str\nTYPE_CHECKING = ''\n```",