diff --git a/crates/ty_python_semantic/resources/mdtest/annotations/new_types.md b/crates/ty_python_semantic/resources/mdtest/annotations/new_types.md index a7b354688f..253344dd3c 100644 --- a/crates/ty_python_semantic/resources/mdtest/annotations/new_types.md +++ b/crates/ty_python_semantic/resources/mdtest/annotations/new_types.md @@ -541,7 +541,7 @@ class Foo(TypedDict): Bar = NewType("Bar", Foo) # error: [invalid-newtype] ``` -## TODO: A `NewType` cannot be generic +## A `NewType` cannot be generic ```py from typing import Any, NewType, TypeVar @@ -553,7 +553,14 @@ B = NewType("B", list[Any]) # But a free typevar is not allowed. T = TypeVar("T") -C = NewType("C", list[T]) # TODO: should be "error: [invalid-newtype]" +C = NewType("C", list[T]) # error: [invalid-newtype] + +D = dict[str, T] +E = NewType("E", D[T]) # error: [invalid-newtype] + +# this is fine: omitting the type argument means that this is equivalent +# to `F = NewType("F", dict[str, Any]) +F = NewType("F", D) ``` ## Forward references in stub files diff --git a/crates/ty_python_semantic/src/types/infer/builder.rs b/crates/ty_python_semantic/src/types/infer/builder.rs index 19f9ba5f7b..8aa106a6ee 100644 --- a/crates/ty_python_semantic/src/types/infer/builder.rs +++ b/crates/ty_python_semantic/src/types/infer/builder.rs @@ -6093,7 +6093,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { return error( &self.context, format!( - "Wrong number of arguments in `NewType` creation, expected 2, found {}", + "Wrong number of arguments in `NewType` creation: expected 2, found {}", arguments.args.len() ), call_expr, @@ -6183,7 +6183,26 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { fn infer_newtype_assignment_deferred(&mut self, arguments: &ast::Arguments) { let inferred = self.infer_type_expression(&arguments.args[1]); match inferred { - Type::NominalInstance(_) | Type::NewTypeInstance(_) => return, + Type::NewTypeInstance(_) => return, + Type::NominalInstance(instance) => { + match instance.class(self.db()) { + ClassType::NonGeneric(_) => {} + ClassType::Generic(alias) => { + let spec = alias.specialization(self.db()); + if spec.types(self.db()).iter().any(|t| { + matches!(t, Type::KnownInstance(KnownInstanceType::TypeVar(_))) + }) && let Some(builder) = self + .context + .report_lint(&INVALID_NEWTYPE, &arguments.args[1]) + { + let mut diag = + builder.into_diagnostic("invalid base for `typing.NewType`"); + diag.set_primary_message("A `NewType` base cannot be generic"); + } + } + } + return; + } // There are exactly two union types allowed as bases for NewType: `int | float` and // `int | float | complex`. These are allowed because that's what `float` and `complex` // expand into in type position. We don't currently ask whether the union was implicit