[ty] Add support for @warnings.deprecated (#19376)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run

* [x] basic handling
  * [x] parse and discover `@warnings.deprecated` attributes
  * [x] associate them with function definitions
  * [x] associate them with class definitions
  * [x] add a new "deprecated" diagnostic
* [x] ensure diagnostic is styled appropriately for LSPs
(DiagnosticTag::Deprecated)

* [x] functions
  * [x] fire on calls
  * [x] fire on arbitrary references 
* [x] classes
  * [x] fire on initializers
  * [x] fire on arbitrary references
* [x] methods
  * [x] fire on calls
  * [x] fire on arbitrary references
* [ ] overloads
  * [ ] fire on calls
  * [ ] fire on arbitrary references(??? maybe not ???)
  * [ ] only fire if the actual selected overload is deprecated 

* [ ] dunder desugarring (warn on deprecated `__add__` if `+` is
invoked)
* [ ] alias supression? (don't warn on uses of variables that deprecated
items were assigned to)

* [ ] import logic
  * [x] fire on imports of deprecated items
* [ ] suppress subsequent diagnostics if the import diagnostic fired (is
this handled by alias supression?)
  * [x] fire on all qualified references (`module.mydeprecated`)
  * [x] fire on all references that depend on a `*` import
    


Fixes https://github.com/astral-sh/ty/issues/153
This commit is contained in:
Aria Desires 2025-07-18 19:50:29 -04:00 committed by GitHub
parent e9a64e5825
commit 06f9f52e59
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 1000 additions and 73 deletions

View file

@ -275,6 +275,7 @@ pub enum KnownModule {
UnittestMock,
#[cfg(test)]
Uuid,
Warnings,
}
impl KnownModule {
@ -294,6 +295,7 @@ impl KnownModule {
Self::TypeCheckerInternals => "_typeshed._type_checker_internals",
Self::TyExtensions => "ty_extensions",
Self::ImportLib => "importlib",
Self::Warnings => "warnings",
#[cfg(test)]
Self::UnittestMock => "unittest.mock",
#[cfg(test)]

View file

@ -4187,6 +4187,45 @@ impl<'db> Type<'db> {
.into()
}
Some(KnownClass::Deprecated) => {
// ```py
// class deprecated:
// def __new__(
// cls,
// message: LiteralString,
// /,
// *,
// category: type[Warning] | None = ...,
// stacklevel: int = 1
// ) -> Self: ...
// ```
Binding::single(
self,
Signature::new(
Parameters::new([
Parameter::positional_only(Some(Name::new_static("message")))
.with_annotated_type(Type::LiteralString),
Parameter::keyword_only(Name::new_static("category"))
.with_annotated_type(UnionType::from_elements(
db,
[
// TODO: should be `type[Warning]`
Type::any(),
KnownClass::NoneType.to_instance(db),
],
))
// TODO: should be `type[Warning]`
.with_default_type(Type::any()),
Parameter::keyword_only(Name::new_static("stacklevel"))
.with_annotated_type(KnownClass::Int.to_instance(db))
.with_default_type(Type::IntLiteral(1)),
]),
Some(KnownClass::Deprecated.to_instance(db)),
),
)
.into()
}
Some(KnownClass::TypeAliasType) => {
// ```py
// def __new__(
@ -4450,8 +4489,11 @@ impl<'db> Type<'db> {
Type::EnumLiteral(enum_literal) => enum_literal.enum_class_instance(db).bindings(db),
Type::KnownInstance(known_instance) => {
known_instance.instance_fallback(db).bindings(db)
}
Type::PropertyInstance(_)
| Type::KnownInstance(_)
| Type::AlwaysFalsy
| Type::AlwaysTruthy
| Type::IntLiteral(_)
@ -5016,6 +5058,10 @@ impl<'db> Type<'db> {
Type::KnownInstance(known_instance) => match known_instance {
KnownInstanceType::TypeAliasType(alias) => Ok(alias.value_type(db)),
KnownInstanceType::TypeVar(typevar) => Ok(Type::TypeVar(*typevar)),
KnownInstanceType::Deprecated(_) => Err(InvalidTypeExpressionError {
invalid_expressions: smallvec::smallvec![InvalidTypeExpression::Deprecated],
fallback_type: Type::unknown(),
}),
KnownInstanceType::SubscriptedProtocol(_) => Err(InvalidTypeExpressionError {
invalid_expressions: smallvec::smallvec_inline![
InvalidTypeExpression::Protocol
@ -5861,6 +5907,9 @@ pub enum KnownInstanceType<'db> {
/// A single instance of `typing.TypeAliasType` (PEP 695 type alias)
TypeAliasType(TypeAliasType<'db>),
/// A single instance of `warnings.deprecated` or `typing_extensions.deprecated`
Deprecated(DeprecatedInstance<'db>),
}
fn walk_known_instance_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
@ -5879,6 +5928,9 @@ fn walk_known_instance_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
KnownInstanceType::TypeAliasType(type_alias) => {
visitor.visit_type_alias_type(db, type_alias);
}
KnownInstanceType::Deprecated(_) => {
// Nothing to visit
}
}
}
@ -5895,6 +5947,10 @@ impl<'db> KnownInstanceType<'db> {
Self::TypeAliasType(type_alias) => {
Self::TypeAliasType(type_alias.normalized_impl(db, visitor))
}
Self::Deprecated(deprecated) => {
// Nothing to normalize
Self::Deprecated(deprecated)
}
}
}
@ -5903,6 +5959,7 @@ impl<'db> KnownInstanceType<'db> {
Self::SubscriptedProtocol(_) | Self::SubscriptedGeneric(_) => KnownClass::SpecialForm,
Self::TypeVar(_) => KnownClass::TypeVar,
Self::TypeAliasType(_) => KnownClass::TypeAliasType,
Self::Deprecated(_) => KnownClass::Deprecated,
}
}
@ -5947,6 +6004,7 @@ impl<'db> KnownInstanceType<'db> {
// it as an instance of `typing.TypeVar`. Inside of a generic class or function, we'll
// have a `Type::TypeVar(_)`, which is rendered as the typevar's name.
KnownInstanceType::TypeVar(_) => f.write_str("typing.TypeVar"),
KnownInstanceType::Deprecated(_) => f.write_str("warnings.deprecated"),
}
}
}
@ -6135,6 +6193,8 @@ enum InvalidTypeExpression<'db> {
Protocol,
/// Same for `Generic`
Generic,
/// Same for `@deprecated`
Deprecated,
/// Type qualifiers are always invalid in *type expressions*,
/// but these ones are okay with 0 arguments in *annotation expressions*
TypeQualifier(SpecialFormType),
@ -6176,6 +6236,9 @@ impl<'db> InvalidTypeExpression<'db> {
InvalidTypeExpression::Generic => {
f.write_str("`typing.Generic` is not allowed in type expressions")
}
InvalidTypeExpression::Deprecated => {
f.write_str("`warnings.deprecated` is not allowed in type expressions")
}
InvalidTypeExpression::TypeQualifier(qualifier) => write!(
f,
"Type qualifier `{qualifier}` is not allowed in type expressions \
@ -6231,6 +6294,17 @@ impl<'db> InvalidTypeExpression<'db> {
}
}
/// Data regarding a `warnings.deprecated` or `typing_extensions.deprecated` decorator.
#[salsa::interned(debug)]
#[derive(PartialOrd, Ord)]
pub struct DeprecatedInstance<'db> {
/// The message for the deprecation
pub message: Option<StringLiteralType<'db>>,
}
// The Salsa heap is tracked separately.
impl get_size2::GetSize for DeprecatedInstance<'_> {}
/// Whether this typecar was created via the legacy `TypeVar` constructor, or using PEP 695 syntax.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum TypeVarKind {

View file

@ -848,6 +848,7 @@ impl<'db> Bindings<'db> {
class_literal.name(db),
class_literal.body_scope(db),
class_literal.known(db),
class_literal.deprecated(db),
Some(params),
class_literal.dataclass_transformer_params(db),
)));

View file

@ -22,8 +22,9 @@ use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signatu
use crate::types::tuple::TupleType;
use crate::types::{
BareTypeAliasType, Binding, BoundSuperError, BoundSuperType, CallableType, DataclassParams,
DynamicType, KnownInstanceType, TypeAliasType, TypeMapping, TypeRelation, TypeTransformer,
TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, infer_definition_types,
DeprecatedInstance, DynamicType, KnownInstanceType, TypeAliasType, TypeMapping, TypeRelation,
TypeTransformer, TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind,
infer_definition_types,
};
use crate::{
Db, FxOrderSet, KnownModule, Program,
@ -799,6 +800,9 @@ pub struct ClassLiteral<'db> {
pub(crate) known: Option<KnownClass>,
/// If this class is deprecated, this holds the deprecation message.
pub(crate) deprecated: Option<DeprecatedInstance<'db>>,
pub(crate) dataclass_params: Option<DataclassParams>,
pub(crate) dataclass_transformer_params: Option<DataclassTransformerParams>,
}
@ -2418,6 +2422,7 @@ pub enum KnownClass {
NoneType, // Part of `types` for Python >= 3.10
// Typing
Any,
Deprecated,
StdlibAlias,
SpecialForm,
TypeVar,
@ -2535,6 +2540,7 @@ impl KnownClass {
| Self::NotImplementedType
| Self::Staticmethod
| Self::Classmethod
| Self::Deprecated
| Self::Field
| Self::KwOnly
| Self::NamedTupleFallback => Truthiness::Ambiguous,
@ -2562,6 +2568,7 @@ impl KnownClass {
| Self::Property
| Self::Staticmethod
| Self::Classmethod
| Self::Deprecated
| Self::Type
| Self::ModuleType
| Self::Super
@ -2648,6 +2655,7 @@ impl KnownClass {
| KnownClass::ExceptionGroup
| KnownClass::Staticmethod
| KnownClass::Classmethod
| KnownClass::Deprecated
| KnownClass::Super
| KnownClass::Enum
| KnownClass::Auto
@ -2731,6 +2739,7 @@ impl KnownClass {
| Self::ExceptionGroup
| Self::Staticmethod
| Self::Classmethod
| Self::Deprecated
| Self::GenericAlias
| Self::GeneratorType
| Self::AsyncGeneratorType
@ -2797,6 +2806,7 @@ impl KnownClass {
Self::ExceptionGroup => "ExceptionGroup",
Self::Staticmethod => "staticmethod",
Self::Classmethod => "classmethod",
Self::Deprecated => "deprecated",
Self::GenericAlias => "GenericAlias",
Self::ModuleType => "ModuleType",
Self::FunctionType => "FunctionType",
@ -3071,6 +3081,7 @@ impl KnownClass {
| Self::ParamSpec
| Self::ParamSpecArgs
| Self::ParamSpecKwargs
| Self::Deprecated
| Self::NewType => KnownModule::TypingExtensions,
Self::NoDefaultType => {
let python_version = Program::get(db).python_version(db);
@ -3139,6 +3150,7 @@ impl KnownClass {
| Self::ExceptionGroup
| Self::Staticmethod
| Self::Classmethod
| Self::Deprecated
| Self::GenericAlias
| Self::ModuleType
| Self::FunctionType
@ -3226,6 +3238,7 @@ impl KnownClass {
| Self::ExceptionGroup
| Self::Staticmethod
| Self::Classmethod
| Self::Deprecated
| Self::TypeVar
| Self::ParamSpec
| Self::ParamSpecArgs
@ -3278,6 +3291,7 @@ impl KnownClass {
"ExceptionGroup" => Self::ExceptionGroup,
"staticmethod" => Self::Staticmethod,
"classmethod" => Self::Classmethod,
"deprecated" => Self::Deprecated,
"GenericAlias" => Self::GenericAlias,
"NoneType" => Self::NoneType,
"ModuleType" => Self::ModuleType,
@ -3397,6 +3411,8 @@ impl KnownClass {
| Self::NamedTuple
| Self::Iterable
| Self::NewType => matches!(module, KnownModule::Typing | KnownModule::TypingExtensions),
Self::Deprecated => matches!(module, KnownModule::Warnings | KnownModule::TypingExtensions),
}
}
@ -3481,7 +3497,32 @@ impl KnownClass {
_ => {}
}
}
KnownClass::Deprecated => {
// Parsing something of the form:
//
// @deprecated("message")
// @deprecated("message", caregory = DeprecationWarning, stacklevel = 1)
//
// "Static type checker behavior is not affected by the category and stacklevel arguments"
// so we only need the message and can ignore everything else. The message is mandatory,
// must be a LiteralString, and always comes first.
//
// We aren't guaranteed to know the static value of a LiteralString, so we need to
// accept that sometimes we will fail to include the message.
//
// We don't do any serious validation/diagnostics here, as the signature for this
// is included in `Type::bindings`.
//
// See: <https://typing.python.org/en/latest/spec/directives.html#deprecated>
let [Some(message), ..] = overload.parameter_types() else {
// Checking in Type::bindings will complain about this for us
return;
};
overload.set_return_type(Type::KnownInstance(KnownInstanceType::Deprecated(
DeprecatedInstance::new(db, message.into_string_literal()),
)));
}
KnownClass::TypeVar => {
let assigned_to = index
.try_expression(ast::ExprRef::from(call_expression))

View file

@ -163,7 +163,9 @@ impl<'db> ClassBase<'db> {
Type::KnownInstance(known_instance) => match known_instance {
KnownInstanceType::SubscriptedGeneric(_) => Some(Self::Generic),
KnownInstanceType::SubscriptedProtocol(_) => Some(Self::Protocol),
KnownInstanceType::TypeAliasType(_) | KnownInstanceType::TypeVar(_) => None,
KnownInstanceType::TypeAliasType(_)
| KnownInstanceType::TypeVar(_)
| KnownInstanceType::Deprecated(_) => None,
},
Type::SpecialForm(special_form) => match special_form {

View file

@ -288,7 +288,6 @@ impl LintDiagnosticGuard<'_, '_> {
///
/// Callers can add additional primary or secondary annotations via the
/// `DerefMut` trait implementation to a `Diagnostic`.
#[expect(dead_code)]
pub(super) fn add_primary_tag(&mut self, tag: DiagnosticTag) {
let ann = self.primary_annotation_mut().unwrap();
ann.push_tag(tag);

View file

@ -34,6 +34,7 @@ pub(crate) fn register_lints(registry: &mut LintRegistryBuilder) {
registry.register_lint(&CONFLICTING_DECLARATIONS);
registry.register_lint(&CONFLICTING_METACLASS);
registry.register_lint(&CYCLIC_CLASS_DEFINITION);
registry.register_lint(&DEPRECATED);
registry.register_lint(&DIVISION_BY_ZERO);
registry.register_lint(&DUPLICATE_BASE);
registry.register_lint(&DUPLICATE_KW_ONLY);
@ -261,6 +262,27 @@ declare_lint! {
}
}
declare_lint! {
/// ## What it does
/// Checks for uses of deprecated items
///
/// ## Why is this bad?
/// Deprecated items should no longer be used.
///
/// ## Examples
/// ```python
/// @warnings.deprecated("use new_func instead")
/// def old_func(): ...
///
/// old_func() # emits [deprecated] diagnostic
/// ```
pub(crate) static DEPRECATED = {
summary: "detects uses of deprecated items",
status: LintStatus::preview("1.0.0"),
default_level: Level::Warn,
}
}
declare_lint! {
/// ## What it does
/// Checks for class definitions with duplicate bases.

View file

@ -76,8 +76,8 @@ use crate::types::narrow::ClassInfoConstraintFunction;
use crate::types::signatures::{CallableSignature, Signature};
use crate::types::visitor::any_over_type;
use crate::types::{
BoundMethodType, CallableType, DynamicType, KnownClass, Type, TypeMapping, TypeRelation,
TypeTransformer, TypeVarInstance, UnionBuilder, walk_type_mapping,
BoundMethodType, CallableType, DeprecatedInstance, DynamicType, KnownClass, Type, TypeMapping,
TypeRelation, TypeTransformer, TypeVarInstance, UnionBuilder, walk_type_mapping,
};
use crate::{Db, FxOrderSet, ModuleName, resolve_module};
@ -199,6 +199,9 @@ pub struct OverloadLiteral<'db> {
/// A set of special decorators that were applied to this function
pub(crate) decorators: FunctionDecorators,
/// If `Some` then contains the `@warnings.deprecated`
pub(crate) deprecated: Option<DeprecatedInstance<'db>>,
/// The arguments to `dataclass_transformer`, if this function was annotated
/// with `@dataclass_transformer(...)`.
pub(crate) dataclass_transformer_params: Option<DataclassTransformerParams>,
@ -220,6 +223,7 @@ impl<'db> OverloadLiteral<'db> {
self.known(db),
self.body_scope(db),
self.decorators(db),
self.deprecated(db),
Some(params),
)
}
@ -465,6 +469,14 @@ impl<'db> FunctionLiteral<'db> {
.any(|overload| overload.decorators(db).contains(decorator))
}
/// If the implementation of this function is deprecated, returns the `@warnings.deprecated`.
///
/// Checking if an overload is deprecated requires deeper call analysis.
fn implementation_deprecated(self, db: &'db dyn Db) -> Option<DeprecatedInstance<'db>> {
let (_overloads, implementation) = self.overloads_and_implementation(db);
implementation.and_then(|overload| overload.deprecated(db))
}
fn definition(self, db: &'db dyn Db) -> Definition<'db> {
self.last_definition(db).definition(db)
}
@ -672,6 +684,16 @@ impl<'db> FunctionType<'db> {
self.literal(db).has_known_decorator(db, decorator)
}
/// If the implementation of this function is deprecated, returns the `@warnings.deprecated`.
///
/// Checking if an overload is deprecated requires deeper call analysis.
pub(crate) fn implementation_deprecated(
self,
db: &'db dyn Db,
) -> Option<DeprecatedInstance<'db>> {
self.literal(db).implementation_deprecated(db)
}
/// Returns the [`Definition`] of the implementation or first overload of this function.
///
/// ## Warning

View file

@ -2298,6 +2298,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let mut decorator_types_and_nodes = Vec::with_capacity(decorator_list.len());
let mut function_decorators = FunctionDecorators::empty();
let mut deprecated = None;
let mut dataclass_transformer_params = None;
for decorator in decorator_list {
@ -2315,6 +2316,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
continue;
}
}
Type::KnownInstance(KnownInstanceType::Deprecated(deprecated_inst)) => {
deprecated = Some(deprecated_inst);
}
Type::DataclassTransformer(params) => {
dataclass_transformer_params = Some(params);
}
@ -2362,6 +2366,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
known_function,
body_scope,
function_decorators,
deprecated,
dataclass_transformer_params,
);
@ -2624,6 +2629,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
body: _,
} = class_node;
let mut deprecated = None;
let mut dataclass_params = None;
let mut dataclass_transformer_params = None;
for decorator in decorator_list {
@ -2641,6 +2647,13 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
continue;
}
if let Type::KnownInstance(KnownInstanceType::Deprecated(deprecated_inst)) =
decorator_ty
{
deprecated = Some(deprecated_inst);
continue;
}
if let Type::FunctionLiteral(f) = decorator_ty {
// We do not yet detect or flag `@dataclass_transform` applied to more than one
// overload, or an overload and the implementation both. Nevertheless, this is not
@ -2673,6 +2686,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
name.id.clone(),
body_scope,
maybe_known_class,
deprecated,
dataclass_params,
dataclass_transformer_params,
));
@ -4358,7 +4372,14 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
for alias in names {
for definition in self.index.definitions(alias) {
self.extend(infer_definition_types(self.db(), *definition));
let inferred = infer_definition_types(self.db(), *definition);
// Check non-star imports for deprecations
if definition.kind(self.db()).as_star_import().is_none() {
for ty in inferred.declarations.values() {
self.check_deprecated(alias, ty.inner);
}
}
self.extend(inferred);
}
}
}
@ -5562,6 +5583,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
| KnownClass::NamedTuple
| KnownClass::TypeAliasType
| KnownClass::Tuple
| KnownClass::Deprecated
)
)
// temporary special-casing for all subclasses of `enum.Enum`
@ -5799,6 +5821,62 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
ty
}
/// Check if the given ty is `@deprecated` or not
fn check_deprecated<T: Ranged>(&self, ranged: T, ty: Type) {
// First handle classes
if let Type::ClassLiteral(class_literal) = ty {
let Some(deprecated) = class_literal.deprecated(self.db()) else {
return;
};
let Some(builder) = self
.context
.report_lint(&crate::types::diagnostic::DEPRECATED, ranged)
else {
return;
};
let class_name = class_literal.name(self.db());
let mut diag =
builder.into_diagnostic(format_args!(r#"The class `{class_name}` is deprecated"#));
if let Some(message) = deprecated.message(self.db()) {
diag.set_primary_message(message.value(self.db()));
}
diag.add_primary_tag(ruff_db::diagnostic::DiagnosticTag::Deprecated);
return;
}
// Next handle functions
let function = match ty {
Type::FunctionLiteral(function) => function,
Type::BoundMethod(bound) => bound.function(self.db()),
_ => return,
};
// Currently we only check the final implementation for deprecation, as
// that check can be done on any reference to the function. Analysis of
// deprecated overloads needs to be done in places where we resolve the
// actual overloads being used.
let Some(deprecated) = function.implementation_deprecated(self.db()) else {
return;
};
let Some(builder) = self
.context
.report_lint(&crate::types::diagnostic::DEPRECATED, ranged)
else {
return;
};
let func_name = function.name(self.db());
let mut diag =
builder.into_diagnostic(format_args!(r#"The function `{func_name}` is deprecated"#));
if let Some(message) = deprecated.message(self.db()) {
diag.set_primary_message(message.value(self.db()));
}
diag.add_primary_tag(ruff_db::diagnostic::DiagnosticTag::Deprecated);
}
fn infer_name_load(&mut self, name_node: &ast::ExprName) -> Type<'db> {
let ast::ExprName {
range: _,
@ -5811,6 +5889,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let (resolved, constraint_keys) =
self.infer_place_load(&expr, ast::ExprRef::Name(name_node));
resolved
// Not found in the module's explicitly declared global symbols?
// Check the "implicit globals" such as `__doc__`, `__file__`, `__name__`, etc.
@ -5892,6 +5971,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let use_id = expr_ref.scoped_use_id(db, scope);
let place = place_from_bindings(db, use_def.bindings_at_use(use_id));
(place, Some(use_id))
}
}
@ -6165,6 +6245,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
})
});
if let Some(ty) = place.place.ignore_possibly_unbound() {
self.check_deprecated(expr_ref, ty);
}
(place, constraint_keys)
}
@ -6368,6 +6452,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
}
})
.inner_type();
self.check_deprecated(attr, resolved_type);
// Even if we can obtain the attribute type based on the assignments, we still perform default type inference
// (to report errors).
assigned_type.unwrap_or(resolved_type)
@ -9294,6 +9381,15 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
}
Type::unknown()
}
KnownInstanceType::Deprecated(_) => {
self.infer_type_expression(&subscript.slice);
if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) {
builder.into_diagnostic(format_args!(
"`warnings.deprecated` is not allowed in type expressions",
));
}
Type::unknown()
}
KnownInstanceType::TypeVar(_) => {
self.infer_type_expression(&subscript.slice);
todo_type!("TypeVar annotations")