mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:35:58 +00:00
[red-knot] feat: introduce a new [Type::Todo]
variant (#13548)
This variant shows inference that is not yet implemented.. ## Summary PR #13500 reopened the idea of adding a new type variant to keep track of not-implemented features in Red Knot. It was based off of #12986 with a more generic approach of keeping track of different kind of unknowns. Discussion in #13500 agreed that keeping track of different `Unknown` is complicated for now, and this feature is better achieved through a new variant of `Type`. ### Requirements Requirements for this implementation can be summed up with some extracts of comment from @carljm on the previous PR > So at the moment we are leaning towards simplifying this PR to just use a new top-level variant, which behaves like Any and Unknown but represents inference that is not yet implemented in red-knot. > I think the general rule should be that Todo should propagate only when the presence of the input Todo caused the output to be unknown. > > To take a specific example, the inferred result of addition must be Unknown if either operand is Unknown. That is, Unknown + X will always be Unknown regardless of what X is. (Same for X + Unknown.) In this case, I believe that Unknown + Todo (or Todo + Unknown) should result in Unknown, not result in Todo. If we fix the upstream source of the Todo, the result would still be Unknown, so it's not useful to propagate the Todo in this case: it wrongly suggests that the output is unknown because of a todo item. ## Test Plan This PR does not introduce new tests, but it did required to edit some tests with the display of `[Type::Todo]` (currently `@Todo`), which suggests that those test are placeholders requirements for features we don't support yet.
This commit is contained in:
parent
9d8a4c0057
commit
6cdf996af6
4 changed files with 114 additions and 79 deletions
|
@ -233,31 +233,40 @@ fn declarations_ty<'db>(
|
||||||
/// Unique ID for a type.
|
/// Unique ID for a type.
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum Type<'db> {
|
pub enum Type<'db> {
|
||||||
/// the dynamic type: a statically-unknown set of values
|
/// The dynamic type: a statically-unknown set of values
|
||||||
Any,
|
Any,
|
||||||
/// the empty set of values
|
/// The empty set of values
|
||||||
Never,
|
Never,
|
||||||
/// unknown type (either no annotation, or some kind of type error)
|
/// Unknown type (either no annotation, or some kind of type error).
|
||||||
/// equivalent to Any, or possibly to object in strict mode
|
/// Equivalent to Any, or possibly to object in strict mode
|
||||||
Unknown,
|
Unknown,
|
||||||
/// name does not exist or is not bound to any value (this represents an error, but with some
|
/// Name does not exist or is not bound to any value (this represents an error, but with some
|
||||||
/// leniency options it could be silently resolved to Unknown in some cases)
|
/// leniency options it could be silently resolved to Unknown in some cases)
|
||||||
Unbound,
|
Unbound,
|
||||||
/// the None object -- TODO remove this in favor of Instance(types.NoneType)
|
/// The None object -- TODO remove this in favor of Instance(types.NoneType)
|
||||||
None,
|
None,
|
||||||
/// a specific function object
|
/// Temporary type for symbols that can't be inferred yet because of missing implementations.
|
||||||
|
/// Behaves equivalently to `Any`.
|
||||||
|
///
|
||||||
|
/// This variant should eventually be removed once red-knot is spec-compliant.
|
||||||
|
///
|
||||||
|
/// General rule: `Todo` should only propagate when the presence of the input `Todo` caused the
|
||||||
|
/// output to be unknown. An output should only be `Todo` if fixing all `Todo` inputs to be not
|
||||||
|
/// `Todo` would change the output type.
|
||||||
|
Todo,
|
||||||
|
/// A specific function object
|
||||||
Function(FunctionType<'db>),
|
Function(FunctionType<'db>),
|
||||||
/// The `typing.reveal_type` function, which has special `__call__` behavior.
|
/// The `typing.reveal_type` function, which has special `__call__` behavior.
|
||||||
RevealTypeFunction(FunctionType<'db>),
|
RevealTypeFunction(FunctionType<'db>),
|
||||||
/// a specific module object
|
/// A specific module object
|
||||||
Module(File),
|
Module(File),
|
||||||
/// a specific class object
|
/// A specific class object
|
||||||
Class(ClassType<'db>),
|
Class(ClassType<'db>),
|
||||||
/// the set of Python objects with the given class in their __class__'s method resolution order
|
/// The set of Python objects with the given class in their __class__'s method resolution order
|
||||||
Instance(ClassType<'db>),
|
Instance(ClassType<'db>),
|
||||||
/// the set of objects in any of the types in the union
|
/// The set of objects in any of the types in the union
|
||||||
Union(UnionType<'db>),
|
Union(UnionType<'db>),
|
||||||
/// the set of objects in all of the types in the intersection
|
/// The set of objects in all of the types in the intersection
|
||||||
Intersection(IntersectionType<'db>),
|
Intersection(IntersectionType<'db>),
|
||||||
/// An integer literal
|
/// An integer literal
|
||||||
IntLiteral(i64),
|
IntLiteral(i64),
|
||||||
|
@ -402,8 +411,8 @@ impl<'db> Type<'db> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
match (self, target) {
|
match (self, target) {
|
||||||
(Type::Unknown | Type::Any, _) => false,
|
(Type::Unknown | Type::Any | Type::Todo, _) => false,
|
||||||
(_, Type::Unknown | Type::Any) => false,
|
(_, Type::Unknown | Type::Any | Type::Todo) => false,
|
||||||
(Type::Never, _) => true,
|
(Type::Never, _) => true,
|
||||||
(_, Type::Never) => false,
|
(_, Type::Never) => false,
|
||||||
(Type::IntLiteral(_), Type::Instance(class))
|
(Type::IntLiteral(_), Type::Instance(class))
|
||||||
|
@ -438,8 +447,8 @@ impl<'db> Type<'db> {
|
||||||
/// [assignable to]: https://typing.readthedocs.io/en/latest/spec/concepts.html#the-assignable-to-or-consistent-subtyping-relation
|
/// [assignable to]: https://typing.readthedocs.io/en/latest/spec/concepts.html#the-assignable-to-or-consistent-subtyping-relation
|
||||||
pub(crate) fn is_assignable_to(self, db: &'db dyn Db, target: Type<'db>) -> bool {
|
pub(crate) fn is_assignable_to(self, db: &'db dyn Db, target: Type<'db>) -> bool {
|
||||||
match (self, target) {
|
match (self, target) {
|
||||||
(Type::Unknown | Type::Any, _) => true,
|
(Type::Unknown | Type::Any | Type::Todo, _) => true,
|
||||||
(_, Type::Unknown | Type::Any) => true,
|
(_, Type::Unknown | Type::Any | Type::Todo) => true,
|
||||||
(ty, Type::Union(union)) => union
|
(ty, Type::Union(union)) => union
|
||||||
.elements(db)
|
.elements(db)
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -475,53 +484,54 @@ impl<'db> Type<'db> {
|
||||||
Type::Any => Type::Any,
|
Type::Any => Type::Any,
|
||||||
Type::Never => {
|
Type::Never => {
|
||||||
// TODO: attribute lookup on Never type
|
// TODO: attribute lookup on Never type
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
Type::Unknown => Type::Unknown,
|
Type::Unknown => Type::Unknown,
|
||||||
Type::Unbound => Type::Unbound,
|
Type::Unbound => Type::Unbound,
|
||||||
Type::None => {
|
Type::None => {
|
||||||
// TODO: attribute lookup on None type
|
// TODO: attribute lookup on None type
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
Type::Function(_) | Type::RevealTypeFunction(_) => {
|
Type::Function(_) | Type::RevealTypeFunction(_) => {
|
||||||
// TODO: attribute lookup on function type
|
// TODO: attribute lookup on function type
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
Type::Module(file) => global_symbol_ty(db, *file, name),
|
Type::Module(file) => global_symbol_ty(db, *file, name),
|
||||||
Type::Class(class) => class.class_member(db, name),
|
Type::Class(class) => class.class_member(db, name),
|
||||||
Type::Instance(_) => {
|
Type::Instance(_) => {
|
||||||
// TODO MRO? get_own_instance_member, get_instance_member
|
// TODO MRO? get_own_instance_member, get_instance_member
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
Type::Union(union) => union.map(db, |element| element.member(db, name)),
|
Type::Union(union) => union.map(db, |element| element.member(db, name)),
|
||||||
Type::Intersection(_) => {
|
Type::Intersection(_) => {
|
||||||
// TODO perform the get_member on each type in the intersection
|
// TODO perform the get_member on each type in the intersection
|
||||||
// TODO return the intersection of those results
|
// TODO return the intersection of those results
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
Type::IntLiteral(_) => {
|
Type::IntLiteral(_) => {
|
||||||
// TODO raise error
|
// TODO raise error
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
Type::BooleanLiteral(_) => Type::Unknown,
|
Type::BooleanLiteral(_) => Type::Todo,
|
||||||
Type::StringLiteral(_) => {
|
Type::StringLiteral(_) => {
|
||||||
// TODO defer to `typing.LiteralString`/`builtins.str` methods
|
// TODO defer to `typing.LiteralString`/`builtins.str` methods
|
||||||
// from typeshed's stubs
|
// from typeshed's stubs
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
Type::LiteralString => {
|
Type::LiteralString => {
|
||||||
// TODO defer to `typing.LiteralString`/`builtins.str` methods
|
// TODO defer to `typing.LiteralString`/`builtins.str` methods
|
||||||
// from typeshed's stubs
|
// from typeshed's stubs
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
Type::BytesLiteral(_) => {
|
Type::BytesLiteral(_) => {
|
||||||
// TODO defer to Type::Instance(<bytes from typeshed>).member
|
// TODO defer to Type::Instance(<bytes from typeshed>).member
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
Type::Tuple(_) => {
|
Type::Tuple(_) => {
|
||||||
// TODO: implement tuple methods
|
// TODO: implement tuple methods
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
|
Type::Todo => Type::Todo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,7 +541,9 @@ impl<'db> Type<'db> {
|
||||||
/// when `bool(x)` is called on an object `x`.
|
/// when `bool(x)` is called on an object `x`.
|
||||||
fn bool(&self, db: &'db dyn Db) -> Truthiness {
|
fn bool(&self, db: &'db dyn Db) -> Truthiness {
|
||||||
match self {
|
match self {
|
||||||
Type::Any | Type::Never | Type::Unknown | Type::Unbound => Truthiness::Ambiguous,
|
Type::Any | Type::Todo | Type::Never | Type::Unknown | Type::Unbound => {
|
||||||
|
Truthiness::Ambiguous
|
||||||
|
}
|
||||||
Type::None => Truthiness::AlwaysFalse,
|
Type::None => Truthiness::AlwaysFalse,
|
||||||
Type::Function(_) | Type::RevealTypeFunction(_) => Truthiness::AlwaysTrue,
|
Type::Function(_) | Type::RevealTypeFunction(_) => Truthiness::AlwaysTrue,
|
||||||
Type::Module(_) => Truthiness::AlwaysTrue,
|
Type::Module(_) => Truthiness::AlwaysTrue,
|
||||||
|
@ -602,11 +614,13 @@ impl<'db> Type<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: handle classes which implement the `__call__` protocol
|
// TODO: handle classes which implement the `__call__` protocol
|
||||||
Type::Instance(_instance_ty) => CallOutcome::callable(Type::Unknown),
|
Type::Instance(_instance_ty) => CallOutcome::callable(Type::Todo),
|
||||||
|
|
||||||
// `Any` is callable, and its return type is also `Any`.
|
// `Any` is callable, and its return type is also `Any`.
|
||||||
Type::Any => CallOutcome::callable(Type::Any),
|
Type::Any => CallOutcome::callable(Type::Any),
|
||||||
|
|
||||||
|
Type::Todo => CallOutcome::callable(Type::Todo),
|
||||||
|
|
||||||
Type::Unknown => CallOutcome::callable(Type::Unknown),
|
Type::Unknown => CallOutcome::callable(Type::Unknown),
|
||||||
|
|
||||||
Type::Union(union) => CallOutcome::union(
|
Type::Union(union) => CallOutcome::union(
|
||||||
|
@ -619,7 +633,7 @@ impl<'db> Type<'db> {
|
||||||
),
|
),
|
||||||
|
|
||||||
// TODO: intersection types
|
// TODO: intersection types
|
||||||
Type::Intersection(_) => CallOutcome::callable(Type::Unknown),
|
Type::Intersection(_) => CallOutcome::callable(Type::Todo),
|
||||||
|
|
||||||
_ => CallOutcome::not_callable(self),
|
_ => CallOutcome::not_callable(self),
|
||||||
}
|
}
|
||||||
|
@ -640,6 +654,12 @@ impl<'db> Type<'db> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Type::Unknown | Type::Any = self {
|
||||||
|
// Explicit handling of `Unknown` and `Any` necessary until `type[Unknown]` and
|
||||||
|
// `type[Any]` are not defined as `Todo` anymore.
|
||||||
|
return IterationOutcome::Iterable { element_ty: self };
|
||||||
|
}
|
||||||
|
|
||||||
// `self` represents the type of the iterable;
|
// `self` represents the type of the iterable;
|
||||||
// `__iter__` and `__next__` are both looked up on the class of the iterable:
|
// `__iter__` and `__next__` are both looked up on the class of the iterable:
|
||||||
let iterable_meta_type = self.to_meta_type(db);
|
let iterable_meta_type = self.to_meta_type(db);
|
||||||
|
@ -686,13 +706,14 @@ impl<'db> Type<'db> {
|
||||||
pub fn to_instance(&self, db: &'db dyn Db) -> Type<'db> {
|
pub fn to_instance(&self, db: &'db dyn Db) -> Type<'db> {
|
||||||
match self {
|
match self {
|
||||||
Type::Any => Type::Any,
|
Type::Any => Type::Any,
|
||||||
|
Type::Todo => Type::Todo,
|
||||||
Type::Unknown => Type::Unknown,
|
Type::Unknown => Type::Unknown,
|
||||||
Type::Unbound => Type::Unknown,
|
Type::Unbound => Type::Unknown,
|
||||||
Type::Never => Type::Never,
|
Type::Never => Type::Never,
|
||||||
Type::Class(class) => Type::Instance(*class),
|
Type::Class(class) => Type::Instance(*class),
|
||||||
Type::Union(union) => union.map(db, |element| element.to_instance(db)),
|
Type::Union(union) => union.map(db, |element| element.to_instance(db)),
|
||||||
// TODO: we can probably do better here: --Alex
|
// TODO: we can probably do better here: --Alex
|
||||||
Type::Intersection(_) => Type::Unknown,
|
Type::Intersection(_) => Type::Todo,
|
||||||
// TODO: calling `.to_instance()` on any of these should result in a diagnostic,
|
// TODO: calling `.to_instance()` on any of these should result in a diagnostic,
|
||||||
// since they already indicate that the object is an instance of some kind:
|
// since they already indicate that the object is an instance of some kind:
|
||||||
Type::BooleanLiteral(_)
|
Type::BooleanLiteral(_)
|
||||||
|
@ -723,18 +744,19 @@ impl<'db> Type<'db> {
|
||||||
Type::IntLiteral(_) => builtins_symbol_ty(db, "int"),
|
Type::IntLiteral(_) => builtins_symbol_ty(db, "int"),
|
||||||
Type::Function(_) | Type::RevealTypeFunction(_) => types_symbol_ty(db, "FunctionType"),
|
Type::Function(_) | Type::RevealTypeFunction(_) => types_symbol_ty(db, "FunctionType"),
|
||||||
Type::Module(_) => types_symbol_ty(db, "ModuleType"),
|
Type::Module(_) => types_symbol_ty(db, "ModuleType"),
|
||||||
|
Type::Tuple(_) => builtins_symbol_ty(db, "tuple"),
|
||||||
Type::None => typeshed_symbol_ty(db, "NoneType"),
|
Type::None => typeshed_symbol_ty(db, "NoneType"),
|
||||||
// TODO not accurate if there's a custom metaclass...
|
// TODO not accurate if there's a custom metaclass...
|
||||||
Type::Class(_) => builtins_symbol_ty(db, "type"),
|
Type::Class(_) => builtins_symbol_ty(db, "type"),
|
||||||
// TODO can we do better here? `type[LiteralString]`?
|
// TODO can we do better here? `type[LiteralString]`?
|
||||||
Type::StringLiteral(_) | Type::LiteralString => builtins_symbol_ty(db, "str"),
|
Type::StringLiteral(_) | Type::LiteralString => builtins_symbol_ty(db, "str"),
|
||||||
// TODO: `type[Any]`?
|
// TODO: `type[Any]`?
|
||||||
Type::Any => Type::Any,
|
Type::Any => Type::Todo,
|
||||||
// TODO: `type[Unknown]`?
|
// TODO: `type[Unknown]`?
|
||||||
Type::Unknown => Type::Unknown,
|
Type::Unknown => Type::Todo,
|
||||||
// TODO intersections
|
// TODO intersections
|
||||||
Type::Intersection(_) => Type::Unknown,
|
Type::Intersection(_) => Type::Todo,
|
||||||
Type::Tuple(_) => builtins_symbol_ty(db, "tuple"),
|
Type::Todo => Type::Todo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1064,7 +1086,7 @@ impl<'db> FunctionType<'db> {
|
||||||
// rather than from `bar`'s return annotation
|
// rather than from `bar`'s return annotation
|
||||||
// in order to determine the type that `bar` returns
|
// in order to determine the type that `bar` returns
|
||||||
if !function_stmt_node.decorator_list.is_empty() {
|
if !function_stmt_node.decorator_list.is_empty() {
|
||||||
return Type::Unknown;
|
return Type::Todo;
|
||||||
}
|
}
|
||||||
|
|
||||||
function_stmt_node
|
function_stmt_node
|
||||||
|
@ -1073,7 +1095,7 @@ impl<'db> FunctionType<'db> {
|
||||||
.map(|returns| {
|
.map(|returns| {
|
||||||
if function_stmt_node.is_async {
|
if function_stmt_node.is_async {
|
||||||
// TODO: generic `types.CoroutineType`!
|
// TODO: generic `types.CoroutineType`!
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
} else {
|
} else {
|
||||||
definition_expression_ty(db, definition, returns.as_ref())
|
definition_expression_ty(db, definition, returns.as_ref())
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,6 +215,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
|
||||||
|
|
||||||
/// Adds a positive type to this intersection.
|
/// Adds a positive type to this intersection.
|
||||||
fn add_positive(&mut self, db: &'db dyn Db, ty: Type<'db>) {
|
fn add_positive(&mut self, db: &'db dyn Db, ty: Type<'db>) {
|
||||||
|
// TODO `Any`/`Unknown`/`Todo` actually should not self-cancel
|
||||||
match ty {
|
match ty {
|
||||||
Type::Intersection(inter) => {
|
Type::Intersection(inter) => {
|
||||||
let pos = inter.positive(db);
|
let pos = inter.positive(db);
|
||||||
|
@ -234,7 +235,7 @@ impl<'db> InnerIntersectionBuilder<'db> {
|
||||||
|
|
||||||
/// Adds a negative type to this intersection.
|
/// Adds a negative type to this intersection.
|
||||||
fn add_negative(&mut self, db: &'db dyn Db, ty: Type<'db>) {
|
fn add_negative(&mut self, db: &'db dyn Db, ty: Type<'db>) {
|
||||||
// TODO Any/Unknown actually should not self-cancel
|
// TODO `Any`/`Unknown`/`Todo` actually should not self-cancel
|
||||||
match ty {
|
match ty {
|
||||||
Type::Intersection(intersection) => {
|
Type::Intersection(intersection) => {
|
||||||
let pos = intersection.negative(db);
|
let pos = intersection.negative(db);
|
||||||
|
|
|
@ -67,6 +67,9 @@ impl Display for DisplayRepresentation<'_> {
|
||||||
Type::Unknown => f.write_str("Unknown"),
|
Type::Unknown => f.write_str("Unknown"),
|
||||||
Type::Unbound => f.write_str("Unbound"),
|
Type::Unbound => f.write_str("Unbound"),
|
||||||
Type::None => f.write_str("None"),
|
Type::None => f.write_str("None"),
|
||||||
|
// `[Type::Todo]`'s display should be explicit that is not a valid display of
|
||||||
|
// any other type
|
||||||
|
Type::Todo => f.write_str("@Todo"),
|
||||||
Type::Module(file) => {
|
Type::Module(file) => {
|
||||||
write!(f, "<module '{:?}'>", file.path(self.db))
|
write!(f, "<module '{:?}'>", file.path(self.db))
|
||||||
}
|
}
|
||||||
|
|
|
@ -782,7 +782,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
) {
|
) {
|
||||||
// TODO(dhruvmanila): Annotation expression is resolved at the enclosing scope, infer the
|
// TODO(dhruvmanila): Annotation expression is resolved at the enclosing scope, infer the
|
||||||
// parameter type from there
|
// parameter type from there
|
||||||
let annotated_ty = Type::Unknown;
|
let annotated_ty = Type::Todo;
|
||||||
if parameter.annotation.is_some() {
|
if parameter.annotation.is_some() {
|
||||||
self.add_declaration_with_binding(
|
self.add_declaration_with_binding(
|
||||||
parameter.into(),
|
parameter.into(),
|
||||||
|
@ -968,6 +968,8 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
let node_ty = except_handler_definition
|
let node_ty = except_handler_definition
|
||||||
.handled_exceptions()
|
.handled_exceptions()
|
||||||
.map(|ty| self.infer_expression(ty))
|
.map(|ty| self.infer_expression(ty))
|
||||||
|
// If there is no handled exception, it's invalid syntax;
|
||||||
|
// a diagnostic will have already been emitted
|
||||||
.unwrap_or(Type::Unknown);
|
.unwrap_or(Type::Unknown);
|
||||||
|
|
||||||
let symbol_ty = if except_handler_definition.is_star() {
|
let symbol_ty = if except_handler_definition.is_star() {
|
||||||
|
@ -983,7 +985,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
match node_ty {
|
match node_ty {
|
||||||
Type::Any | Type::Unknown => node_ty,
|
Type::Any | Type::Unknown => node_ty,
|
||||||
Type::Class(class_ty) => Type::Instance(class_ty),
|
Type::Class(class_ty) => Type::Instance(class_ty),
|
||||||
_ => Type::Unknown,
|
_ => Type::Todo,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1028,7 +1030,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
// against the subject expression type (which we can query via `infer_expression_types`)
|
// against the subject expression type (which we can query via `infer_expression_types`)
|
||||||
// and extract the type at the `index` position if the pattern matches. This will be
|
// and extract the type at the `index` position if the pattern matches. This will be
|
||||||
// similar to the logic in `self.infer_assignment_definition`.
|
// similar to the logic in `self.infer_assignment_definition`.
|
||||||
self.add_binding(pattern.into(), definition, Type::Unknown);
|
self.add_binding(pattern.into(), definition, Type::Todo);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_match_pattern(&mut self, pattern: &ast::Pattern) {
|
fn infer_match_pattern(&mut self, pattern: &ast::Pattern) {
|
||||||
|
@ -1200,7 +1202,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
self.infer_expression(target);
|
self.infer_expression(target);
|
||||||
|
|
||||||
// TODO(dhruvmanila): Resolve the target type using the value type and the operator
|
// TODO(dhruvmanila): Resolve the target type using the value type and the operator
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_type_alias_statement(&mut self, type_alias_statement: &ast::StmtTypeAlias) {
|
fn infer_type_alias_statement(&mut self, type_alias_statement: &ast::StmtTypeAlias) {
|
||||||
|
@ -1302,7 +1304,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
|
|
||||||
let loop_var_value_ty = if is_async {
|
let loop_var_value_ty = if is_async {
|
||||||
// TODO(Alex): async iterables/iterators!
|
// TODO(Alex): async iterables/iterators!
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
} else {
|
} else {
|
||||||
iterable_ty
|
iterable_ty
|
||||||
.iterate(self.db)
|
.iterate(self.db)
|
||||||
|
@ -1816,7 +1818,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
self.infer_first_comprehension_iter(generators);
|
self.infer_first_comprehension_iter(generators);
|
||||||
|
|
||||||
// TODO generator type
|
// TODO generator type
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_list_comprehension_expression(&mut self, listcomp: &ast::ExprListComp) -> Type<'db> {
|
fn infer_list_comprehension_expression(&mut self, listcomp: &ast::ExprListComp) -> Type<'db> {
|
||||||
|
@ -1829,7 +1831,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
self.infer_first_comprehension_iter(generators);
|
self.infer_first_comprehension_iter(generators);
|
||||||
|
|
||||||
// TODO list type
|
// TODO list type
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_dict_comprehension_expression(&mut self, dictcomp: &ast::ExprDictComp) -> Type<'db> {
|
fn infer_dict_comprehension_expression(&mut self, dictcomp: &ast::ExprDictComp) -> Type<'db> {
|
||||||
|
@ -1843,7 +1845,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
self.infer_first_comprehension_iter(generators);
|
self.infer_first_comprehension_iter(generators);
|
||||||
|
|
||||||
// TODO dict type
|
// TODO dict type
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_set_comprehension_expression(&mut self, setcomp: &ast::ExprSetComp) -> Type<'db> {
|
fn infer_set_comprehension_expression(&mut self, setcomp: &ast::ExprSetComp) -> Type<'db> {
|
||||||
|
@ -1856,7 +1858,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
self.infer_first_comprehension_iter(generators);
|
self.infer_first_comprehension_iter(generators);
|
||||||
|
|
||||||
// TODO set type
|
// TODO set type
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_generator_expression_scope(&mut self, generator: &ast::ExprGenerator) {
|
fn infer_generator_expression_scope(&mut self, generator: &ast::ExprGenerator) {
|
||||||
|
@ -1971,7 +1973,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
|
|
||||||
let target_ty = if is_async {
|
let target_ty = if is_async {
|
||||||
// TODO: async iterables/iterators! -- Alex
|
// TODO: async iterables/iterators! -- Alex
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
} else {
|
} else {
|
||||||
iterable_ty
|
iterable_ty
|
||||||
.iterate(self.db)
|
.iterate(self.db)
|
||||||
|
@ -2050,7 +2052,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO function type
|
// TODO function type
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_call_expression(&mut self, call_expression: &ast::ExprCall) -> Type<'db> {
|
fn infer_call_expression(&mut self, call_expression: &ast::ExprCall) -> Type<'db> {
|
||||||
|
@ -2081,7 +2083,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
.unwrap_with_diagnostic(value.as_ref().into(), self);
|
.unwrap_with_diagnostic(value.as_ref().into(), self);
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_yield_expression(&mut self, yield_expression: &ast::ExprYield) -> Type<'db> {
|
fn infer_yield_expression(&mut self, yield_expression: &ast::ExprYield) -> Type<'db> {
|
||||||
|
@ -2090,7 +2092,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
self.infer_optional_expression(value.as_deref());
|
self.infer_optional_expression(value.as_deref());
|
||||||
|
|
||||||
// TODO awaitable type
|
// TODO awaitable type
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_yield_from_expression(&mut self, yield_from: &ast::ExprYieldFrom) -> Type<'db> {
|
fn infer_yield_from_expression(&mut self, yield_from: &ast::ExprYieldFrom) -> Type<'db> {
|
||||||
|
@ -2102,7 +2104,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
.unwrap_with_diagnostic(value.as_ref().into(), self);
|
.unwrap_with_diagnostic(value.as_ref().into(), self);
|
||||||
|
|
||||||
// TODO get type from `ReturnType` of generator
|
// TODO get type from `ReturnType` of generator
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_await_expression(&mut self, await_expression: &ast::ExprAwait) -> Type<'db> {
|
fn infer_await_expression(&mut self, await_expression: &ast::ExprAwait) -> Type<'db> {
|
||||||
|
@ -2111,7 +2113,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
self.infer_expression(value);
|
self.infer_expression(value);
|
||||||
|
|
||||||
// TODO awaitable type
|
// TODO awaitable type
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Look up a name reference that isn't bound in the local scope.
|
/// Look up a name reference that isn't bound in the local scope.
|
||||||
|
@ -2255,7 +2257,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
|
|
||||||
(UnaryOp::Not, ty) => ty.bool(self.db).negate().into_type(self.db),
|
(UnaryOp::Not, ty) => ty.bool(self.db).negate().into_type(self.db),
|
||||||
|
|
||||||
_ => Type::Unknown, // TODO other unary op types
|
_ => Type::Todo, // TODO other unary op types
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2271,6 +2273,8 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
let right_ty = self.infer_expression(right);
|
let right_ty = self.infer_expression(right);
|
||||||
|
|
||||||
match (left_ty, right_ty, op) {
|
match (left_ty, right_ty, op) {
|
||||||
|
// When interacting with Todo, Any and Unknown should propagate (as if we fix this
|
||||||
|
// `Todo` in the future, the result would then become Any or Unknown, respectively.)
|
||||||
(Type::Any, _, _) | (_, Type::Any, _) => Type::Any,
|
(Type::Any, _, _) | (_, Type::Any, _) => Type::Any,
|
||||||
(Type::Unknown, _, _) | (_, Type::Unknown, _) => Type::Unknown,
|
(Type::Unknown, _, _) | (_, Type::Unknown, _) => Type::Unknown,
|
||||||
|
|
||||||
|
@ -2306,8 +2310,8 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
(Type::IntLiteral(n), Type::IntLiteral(m), ast::Operator::Mod) => n
|
(Type::IntLiteral(n), Type::IntLiteral(m), ast::Operator::Mod) => n
|
||||||
.checked_rem(m)
|
.checked_rem(m)
|
||||||
.map(Type::IntLiteral)
|
.map(Type::IntLiteral)
|
||||||
// TODO: division by zero error
|
// TODO division by zero error
|
||||||
.unwrap_or(Type::Unknown),
|
.unwrap_or(Type::Todo),
|
||||||
|
|
||||||
(Type::BytesLiteral(lhs), Type::BytesLiteral(rhs), ast::Operator::Add) => {
|
(Type::BytesLiteral(lhs), Type::BytesLiteral(rhs), ast::Operator::Add) => {
|
||||||
Type::BytesLiteral(BytesLiteralType::new(
|
Type::BytesLiteral(BytesLiteralType::new(
|
||||||
|
@ -2363,7 +2367,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => Type::Unknown, // TODO
|
_ => Type::Todo, // TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2414,7 +2418,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
for right in comparators.as_ref() {
|
for right in comparators.as_ref() {
|
||||||
self.infer_expression(right);
|
self.infer_expression(right);
|
||||||
}
|
}
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_subscript_expression(&mut self, subscript: &ast::ExprSubscript) -> Type<'db> {
|
fn infer_subscript_expression(&mut self, subscript: &ast::ExprSubscript) -> Type<'db> {
|
||||||
|
@ -2544,7 +2548,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
Type::Unknown
|
Type::Unknown
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => Type::Unknown,
|
_ => Type::Todo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2561,7 +2565,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
self.infer_optional_expression(step.as_deref());
|
self.infer_optional_expression(step.as_deref());
|
||||||
|
|
||||||
// TODO slice
|
// TODO slice
|
||||||
Type::Unknown
|
Type::Todo
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_type_parameters(&mut self, type_parameters: &ast::TypeParams) {
|
fn infer_type_parameters(&mut self, type_parameters: &ast::TypeParams) {
|
||||||
|
@ -2643,7 +2647,7 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
// TODO: parse the expression and check whether it is a string annotation, since they
|
// TODO: parse the expression and check whether it is a string annotation, since they
|
||||||
// can be annotation expressions distinct from type expressions.
|
// can be annotation expressions distinct from type expressions.
|
||||||
// https://typing.readthedocs.io/en/latest/spec/annotations.html#string-annotations
|
// https://typing.readthedocs.io/en/latest/spec/annotations.html#string-annotations
|
||||||
ast::Expr::StringLiteral(_literal) => Type::Unknown,
|
ast::Expr::StringLiteral(_literal) => Type::Todo,
|
||||||
|
|
||||||
// Annotation expressions also get special handling for `*args` and `**kwargs`.
|
// Annotation expressions also get special handling for `*args` and `**kwargs`.
|
||||||
ast::Expr::Starred(starred) => self.infer_starred_expression(starred),
|
ast::Expr::Starred(starred) => self.infer_starred_expression(starred),
|
||||||
|
@ -2677,18 +2681,24 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
|
|
||||||
// TODO: parse the expression and check whether it is a string annotation.
|
// TODO: parse the expression and check whether it is a string annotation.
|
||||||
// https://typing.readthedocs.io/en/latest/spec/annotations.html#string-annotations
|
// https://typing.readthedocs.io/en/latest/spec/annotations.html#string-annotations
|
||||||
ast::Expr::StringLiteral(_literal) => Type::Unknown,
|
ast::Expr::StringLiteral(_literal) => Type::Todo,
|
||||||
|
|
||||||
// TODO: an Ellipsis literal *on its own* does not have any meaning in annotation
|
// TODO: an Ellipsis literal *on its own* does not have any meaning in annotation
|
||||||
// expressions, but is meaningful in the context of a number of special forms.
|
// expressions, but is meaningful in the context of a number of special forms.
|
||||||
ast::Expr::EllipsisLiteral(_literal) => Type::Unknown,
|
ast::Expr::EllipsisLiteral(_literal) => Type::Todo,
|
||||||
|
|
||||||
// Other literals do not have meaningful values in the annotation expression context.
|
// Other literals do not have meaningful values in the annotation expression context.
|
||||||
// However, we will we want to handle these differently when working with special forms,
|
// However, we will we want to handle these differently when working with special forms,
|
||||||
// since (e.g.) `123` is not valid in an annotation expression but `Literal[123]` is.
|
// since (e.g.) `123` is not valid in an annotation expression but `Literal[123]` is.
|
||||||
ast::Expr::BytesLiteral(_literal) => Type::Unknown,
|
ast::Expr::BytesLiteral(_literal) => Type::Todo,
|
||||||
ast::Expr::NumberLiteral(_literal) => Type::Unknown,
|
ast::Expr::NumberLiteral(_literal) => Type::Todo,
|
||||||
ast::Expr::BooleanLiteral(_literal) => Type::Unknown,
|
ast::Expr::BooleanLiteral(_literal) => Type::Todo,
|
||||||
|
|
||||||
|
// TODO: this may be a place we need to revisit with special forms.
|
||||||
|
ast::Expr::Subscript(subscript) => {
|
||||||
|
self.infer_subscript_expression(subscript);
|
||||||
|
Type::Todo
|
||||||
|
}
|
||||||
|
|
||||||
// Forms which are invalid in the context of annotation expressions: we infer their
|
// Forms which are invalid in the context of annotation expressions: we infer their
|
||||||
// nested expressions as normal expressions, but the type of the top-level expression is
|
// nested expressions as normal expressions, but the type of the top-level expression is
|
||||||
|
@ -2770,11 +2780,6 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||||
self.infer_attribute_expression(attribute);
|
self.infer_attribute_expression(attribute);
|
||||||
Type::Unknown
|
Type::Unknown
|
||||||
}
|
}
|
||||||
// TODO: this may be a place we need to revisit with special forms.
|
|
||||||
ast::Expr::Subscript(subscript) => {
|
|
||||||
self.infer_subscript_expression(subscript);
|
|
||||||
Type::Unknown
|
|
||||||
}
|
|
||||||
ast::Expr::Starred(starred) => {
|
ast::Expr::Starred(starred) => {
|
||||||
self.infer_starred_expression(starred);
|
self.infer_starred_expression(starred);
|
||||||
Type::Unknown
|
Type::Unknown
|
||||||
|
@ -3911,7 +3916,7 @@ mod tests {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// TODO: Generic `types.CoroutineType`!
|
// TODO: Generic `types.CoroutineType`!
|
||||||
assert_public_ty(&db, "src/a.py", "x", "Unknown");
|
assert_public_ty(&db, "src/a.py", "x", "@Todo");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -3940,7 +3945,7 @@ mod tests {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// TODO: should be `int`!
|
// TODO: should be `int`!
|
||||||
assert_public_ty(&db, "src/a.py", "x", "Unknown");
|
assert_public_ty(&db, "src/a.py", "x", "@Todo");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -5439,7 +5444,9 @@ mod tests {
|
||||||
",
|
",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
assert_scope_ty(&db, "src/a.py", &["foo"], "x", "Unbound | Unknown");
|
// We currently return `Todo` for all `async for` loops,
|
||||||
|
// including loops that have invalid syntax
|
||||||
|
assert_scope_ty(&db, "src/a.py", &["foo"], "x", "Unbound | @Todo");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -5466,7 +5473,7 @@ mod tests {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// TODO(Alex) async iterables/iterators!
|
// TODO(Alex) async iterables/iterators!
|
||||||
assert_scope_ty(&db, "src/a.py", &["foo"], "x", "Unbound | Unknown");
|
assert_scope_ty(&db, "src/a.py", &["foo"], "x", "Unbound | @Todo");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -5596,9 +5603,9 @@ mod tests {
|
||||||
// For these TODOs we need support for `tuple` types:
|
// For these TODOs we need support for `tuple` types:
|
||||||
|
|
||||||
// TODO: Should be `RuntimeError | OSError` --Alex
|
// TODO: Should be `RuntimeError | OSError` --Alex
|
||||||
assert_public_ty(&db, "src/a.py", "e", "Unknown");
|
assert_public_ty(&db, "src/a.py", "e", "@Todo");
|
||||||
// TODO: Should be `AttributeError | TypeError` --Alex
|
// TODO: Should be `AttributeError | TypeError` --Alex
|
||||||
assert_public_ty(&db, "src/a.py", "e", "Unknown");
|
assert_public_ty(&db, "src/a.py", "e", "@Todo");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -6001,7 +6008,9 @@ mod tests {
|
||||||
",
|
",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
assert_scope_ty(&db, "src/a.py", &["foo", "<listcomp>"], "x", "Unknown");
|
// We currently return `Todo` for all async comprehensions,
|
||||||
|
// including comprehensions that have invalid syntax
|
||||||
|
assert_scope_ty(&db, "src/a.py", &["foo", "<listcomp>"], "x", "@Todo");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -6025,7 +6034,7 @@ mod tests {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// TODO async iterables/iterators! --Alex
|
// TODO async iterables/iterators! --Alex
|
||||||
assert_scope_ty(&db, "src/a.py", &["foo", "<listcomp>"], "x", "Unknown");
|
assert_scope_ty(&db, "src/a.py", &["foo", "<listcomp>"], "x", "@Todo");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue