mirror of https://github.com/astral-sh/ruff
[ty] Don't show hover for expressions with no inferred type (#21924)
This commit is contained in:
parent
4fdb4e8219
commit
fbeeb050af
|
|
@ -295,7 +295,7 @@ impl<'db> Definitions<'db> {
|
||||||
|
|
||||||
impl GotoTarget<'_> {
|
impl GotoTarget<'_> {
|
||||||
pub(crate) fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Option<Type<'db>> {
|
pub(crate) fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Option<Type<'db>> {
|
||||||
let ty = match self {
|
match self {
|
||||||
GotoTarget::Expression(expression) => expression.inferred_type(model),
|
GotoTarget::Expression(expression) => expression.inferred_type(model),
|
||||||
GotoTarget::FunctionDef(function) => function.inferred_type(model),
|
GotoTarget::FunctionDef(function) => function.inferred_type(model),
|
||||||
GotoTarget::ClassDef(class) => class.inferred_type(model),
|
GotoTarget::ClassDef(class) => class.inferred_type(model),
|
||||||
|
|
@ -317,7 +317,7 @@ impl GotoTarget<'_> {
|
||||||
} => {
|
} => {
|
||||||
// We don't currently support hovering the bare `.` so there is always a name
|
// We don't currently support hovering the bare `.` so there is always a name
|
||||||
let module = import_name(module_name, *component_index);
|
let module = import_name(module_name, *component_index);
|
||||||
model.resolve_module_type(Some(module), *level)?
|
model.resolve_module_type(Some(module), *level)
|
||||||
}
|
}
|
||||||
GotoTarget::StringAnnotationSubexpr {
|
GotoTarget::StringAnnotationSubexpr {
|
||||||
string_expr,
|
string_expr,
|
||||||
|
|
@ -334,16 +334,16 @@ impl GotoTarget<'_> {
|
||||||
} else {
|
} else {
|
||||||
// TODO: force the typechecker to tell us its secrets
|
// TODO: force the typechecker to tell us its secrets
|
||||||
// (it computes but then immediately discards these types)
|
// (it computes but then immediately discards these types)
|
||||||
return None;
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GotoTarget::BinOp { expression, .. } => {
|
GotoTarget::BinOp { expression, .. } => {
|
||||||
let (_, ty) = ty_python_semantic::definitions_for_bin_op(model, expression)?;
|
let (_, ty) = ty_python_semantic::definitions_for_bin_op(model, expression)?;
|
||||||
ty
|
Some(ty)
|
||||||
}
|
}
|
||||||
GotoTarget::UnaryOp { expression, .. } => {
|
GotoTarget::UnaryOp { expression, .. } => {
|
||||||
let (_, ty) = ty_python_semantic::definitions_for_unary_op(model, expression)?;
|
let (_, ty) = ty_python_semantic::definitions_for_unary_op(model, expression)?;
|
||||||
ty
|
Some(ty)
|
||||||
}
|
}
|
||||||
// TODO: Support identifier targets
|
// TODO: Support identifier targets
|
||||||
GotoTarget::PatternMatchRest(_)
|
GotoTarget::PatternMatchRest(_)
|
||||||
|
|
@ -353,10 +353,8 @@ impl GotoTarget<'_> {
|
||||||
| GotoTarget::TypeParamParamSpecName(_)
|
| GotoTarget::TypeParamParamSpecName(_)
|
||||||
| GotoTarget::TypeParamTypeVarTupleName(_)
|
| GotoTarget::TypeParamTypeVarTupleName(_)
|
||||||
| GotoTarget::NonLocal { .. }
|
| GotoTarget::NonLocal { .. }
|
||||||
| GotoTarget::Globals { .. } => return None,
|
| GotoTarget::Globals { .. } => None,
|
||||||
};
|
}
|
||||||
|
|
||||||
Some(ty)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to get a simplified display of this callable type by resolving overloads
|
/// Try to get a simplified display of this callable type by resolving overloads
|
||||||
|
|
|
||||||
|
|
@ -3610,6 +3610,20 @@ def function():
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hover_tuple_assignment_target() {
|
||||||
|
let test = CursorTest::builder()
|
||||||
|
.source(
|
||||||
|
"test.py",
|
||||||
|
r#"
|
||||||
|
(x, y)<CURSOR> = "test", 10
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert_snapshot!(test.hover(), @"Hover provided no content");
|
||||||
|
}
|
||||||
|
|
||||||
impl CursorTest {
|
impl CursorTest {
|
||||||
fn hover(&self) -> String {
|
fn hover(&self) -> String {
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
|
||||||
|
|
@ -362,8 +362,9 @@ impl<'a> SourceOrderVisitor<'a> for InlayHintVisitor<'a, '_> {
|
||||||
Expr::Name(name) => {
|
Expr::Name(name) => {
|
||||||
if let Some(rhs) = self.assignment_rhs {
|
if let Some(rhs) = self.assignment_rhs {
|
||||||
if name.ctx.is_store() {
|
if name.ctx.is_store() {
|
||||||
let ty = expr.inferred_type(&self.model);
|
if let Some(ty) = expr.inferred_type(&self.model) {
|
||||||
self.add_type_hint(expr, rhs, ty, !self.in_no_edits_allowed);
|
self.add_type_hint(expr, rhs, ty, !self.in_no_edits_allowed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
source_order::walk_expr(self, expr);
|
source_order::walk_expr(self, expr);
|
||||||
|
|
@ -371,8 +372,9 @@ impl<'a> SourceOrderVisitor<'a> for InlayHintVisitor<'a, '_> {
|
||||||
Expr::Attribute(attribute) => {
|
Expr::Attribute(attribute) => {
|
||||||
if let Some(rhs) = self.assignment_rhs {
|
if let Some(rhs) = self.assignment_rhs {
|
||||||
if attribute.ctx.is_store() {
|
if attribute.ctx.is_store() {
|
||||||
let ty = expr.inferred_type(&self.model);
|
if let Some(ty) = expr.inferred_type(&self.model) {
|
||||||
self.add_type_hint(expr, rhs, ty, !self.in_no_edits_allowed);
|
self.add_type_hint(expr, rhs, ty, !self.in_no_edits_allowed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
source_order::walk_expr(self, expr);
|
source_order::walk_expr(self, expr);
|
||||||
|
|
|
||||||
|
|
@ -273,7 +273,7 @@ impl<'db> SemanticTokenVisitor<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to type-based classification.
|
// Fall back to type-based classification.
|
||||||
let ty = name.inferred_type(self.model);
|
let ty = name.inferred_type(self.model).unwrap_or(Type::unknown());
|
||||||
let name_str = name.id.as_str();
|
let name_str = name.id.as_str();
|
||||||
self.classify_from_type_and_name_str(ty, name_str)
|
self.classify_from_type_and_name_str(ty, name_str)
|
||||||
}
|
}
|
||||||
|
|
@ -302,7 +302,9 @@ impl<'db> SemanticTokenVisitor<'db> {
|
||||||
let parsed = parsed_module(db, definition.file(db));
|
let parsed = parsed_module(db, definition.file(db));
|
||||||
let ty = parameter.node(&parsed.load(db)).inferred_type(&model);
|
let ty = parameter.node(&parsed.load(db)).inferred_type(&model);
|
||||||
|
|
||||||
if let Type::TypeVar(type_var) = ty {
|
if let Some(ty) = ty
|
||||||
|
&& let Type::TypeVar(type_var) = ty
|
||||||
|
{
|
||||||
match type_var.typevar(db).kind(db) {
|
match type_var.typevar(db).kind(db) {
|
||||||
TypeVarKind::TypingSelf => {
|
TypeVarKind::TypingSelf => {
|
||||||
return Some((SemanticTokenType::SelfParameter, modifiers));
|
return Some((SemanticTokenType::SelfParameter, modifiers));
|
||||||
|
|
@ -344,9 +346,9 @@ impl<'db> SemanticTokenVisitor<'db> {
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(value) = value {
|
if let Some(value) = value
|
||||||
let value_ty = value.inferred_type(&model);
|
&& let Some(value_ty) = value.inferred_type(&model)
|
||||||
|
{
|
||||||
if value_ty.is_class_literal()
|
if value_ty.is_class_literal()
|
||||||
|| value_ty.is_subclass_of()
|
|| value_ty.is_subclass_of()
|
||||||
|| value_ty.is_generic_alias()
|
|| value_ty.is_generic_alias()
|
||||||
|
|
@ -710,12 +712,12 @@ impl SourceOrderVisitor<'_> for SemanticTokenVisitor<'_> {
|
||||||
for alias in &import.names {
|
for alias in &import.names {
|
||||||
if let Some(asname) = &alias.asname {
|
if let Some(asname) = &alias.asname {
|
||||||
// For aliased imports (from X import Y as Z), classify Z based on what Y is
|
// For aliased imports (from X import Y as Z), classify Z based on what Y is
|
||||||
let ty = alias.inferred_type(self.model);
|
let ty = alias.inferred_type(self.model).unwrap_or(Type::unknown());
|
||||||
let (token_type, modifiers) = self.classify_from_alias_type(ty, asname);
|
let (token_type, modifiers) = self.classify_from_alias_type(ty, asname);
|
||||||
self.add_token(asname, token_type, modifiers);
|
self.add_token(asname, token_type, modifiers);
|
||||||
} else {
|
} else {
|
||||||
// For direct imports (from X import Y), use semantic classification
|
// For direct imports (from X import Y), use semantic classification
|
||||||
let ty = alias.inferred_type(self.model);
|
let ty = alias.inferred_type(self.model).unwrap_or(Type::unknown());
|
||||||
let (token_type, modifiers) =
|
let (token_type, modifiers) =
|
||||||
self.classify_from_alias_type(ty, &alias.name);
|
self.classify_from_alias_type(ty, &alias.name);
|
||||||
self.add_token(&alias.name, token_type, modifiers);
|
self.add_token(&alias.name, token_type, modifiers);
|
||||||
|
|
@ -835,7 +837,7 @@ impl SourceOrderVisitor<'_> for SemanticTokenVisitor<'_> {
|
||||||
self.visit_expr(&attr.value);
|
self.visit_expr(&attr.value);
|
||||||
|
|
||||||
// Then add token for the attribute name (e.g., 'path' in 'os.path')
|
// Then add token for the attribute name (e.g., 'path' in 'os.path')
|
||||||
let ty = expr.inferred_type(self.model);
|
let ty = expr.inferred_type(self.model).unwrap_or(Type::unknown());
|
||||||
let (token_type, modifiers) =
|
let (token_type, modifiers) =
|
||||||
Self::classify_from_type_for_attribute(ty, &attr.attr);
|
Self::classify_from_type_for_attribute(ty, &attr.attr);
|
||||||
self.add_token(&attr.attr, token_type, modifiers);
|
self.add_token(&attr.attr, token_type, modifiers);
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,10 @@ impl<'db> SemanticModel<'db> {
|
||||||
|
|
||||||
/// Returns completions for symbols available in a `object.<CURSOR>` context.
|
/// Returns completions for symbols available in a `object.<CURSOR>` context.
|
||||||
pub fn attribute_completions(&self, node: &ast::ExprAttribute) -> Vec<Completion<'db>> {
|
pub fn attribute_completions(&self, node: &ast::ExprAttribute) -> Vec<Completion<'db>> {
|
||||||
let ty = node.value.inferred_type(self);
|
let Some(ty) = node.value.inferred_type(self) else {
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
|
|
||||||
all_members(self.db, ty)
|
all_members(self.db, ty)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|member| Completion {
|
.map(|member| Completion {
|
||||||
|
|
@ -400,7 +403,7 @@ pub trait HasType {
|
||||||
///
|
///
|
||||||
/// ## Panics
|
/// ## Panics
|
||||||
/// May panic if `self` is from another file than `model`.
|
/// May panic if `self` is from another file than `model`.
|
||||||
fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Type<'db>;
|
fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Option<Type<'db>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HasDefinition {
|
pub trait HasDefinition {
|
||||||
|
|
@ -412,18 +415,16 @@ pub trait HasDefinition {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasType for ast::ExprRef<'_> {
|
impl HasType for ast::ExprRef<'_> {
|
||||||
fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Type<'db> {
|
fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Option<Type<'db>> {
|
||||||
let index = semantic_index(model.db, model.file);
|
let index = semantic_index(model.db, model.file);
|
||||||
// TODO(#1637): semantic tokens is making this crash even with
|
// TODO(#1637): semantic tokens is making this crash even with
|
||||||
// `try_expr_ref_in_ast` guarding this, for now just use `try_expression_scope_id`.
|
// `try_expr_ref_in_ast` guarding this, for now just use `try_expression_scope_id`.
|
||||||
// The problematic input is `x: "float` (with a dangling quote). I imagine the issue
|
// The problematic input is `x: "float` (with a dangling quote). I imagine the issue
|
||||||
// is we're too eagerly setting `is_string_annotation` in inference.
|
// is we're too eagerly setting `is_string_annotation` in inference.
|
||||||
let Some(file_scope) = index.try_expression_scope_id(&model.expr_ref_in_ast(*self)) else {
|
let file_scope = index.try_expression_scope_id(&model.expr_ref_in_ast(*self))?;
|
||||||
return Type::unknown();
|
|
||||||
};
|
|
||||||
let scope = file_scope.to_scope_id(model.db, model.file);
|
let scope = file_scope.to_scope_id(model.db, model.file);
|
||||||
|
|
||||||
infer_scope_types(model.db, scope).expression_type(*self)
|
infer_scope_types(model.db, scope).try_expression_type(*self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -431,7 +432,7 @@ macro_rules! impl_expression_has_type {
|
||||||
($ty: ty) => {
|
($ty: ty) => {
|
||||||
impl HasType for $ty {
|
impl HasType for $ty {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Type<'db> {
|
fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Option<Type<'db>> {
|
||||||
let expression_ref = ExprRef::from(self);
|
let expression_ref = ExprRef::from(self);
|
||||||
expression_ref.inferred_type(model)
|
expression_ref.inferred_type(model)
|
||||||
}
|
}
|
||||||
|
|
@ -474,7 +475,7 @@ impl_expression_has_type!(ast::ExprSlice);
|
||||||
impl_expression_has_type!(ast::ExprIpyEscapeCommand);
|
impl_expression_has_type!(ast::ExprIpyEscapeCommand);
|
||||||
|
|
||||||
impl HasType for ast::Expr {
|
impl HasType for ast::Expr {
|
||||||
fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Type<'db> {
|
fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Option<Type<'db>> {
|
||||||
match self {
|
match self {
|
||||||
Expr::BoolOp(inner) => inner.inferred_type(model),
|
Expr::BoolOp(inner) => inner.inferred_type(model),
|
||||||
Expr::Named(inner) => inner.inferred_type(model),
|
Expr::Named(inner) => inner.inferred_type(model),
|
||||||
|
|
@ -525,9 +526,9 @@ macro_rules! impl_binding_has_ty_def {
|
||||||
|
|
||||||
impl HasType for $ty {
|
impl HasType for $ty {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Type<'db> {
|
fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Option<Type<'db>> {
|
||||||
let binding = HasDefinition::definition(self, model);
|
let binding = HasDefinition::definition(self, model);
|
||||||
binding_type(model.db, binding)
|
Some(binding_type(model.db, binding))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -541,12 +542,12 @@ impl_binding_has_ty_def!(ast::ExceptHandlerExceptHandler);
|
||||||
impl_binding_has_ty_def!(ast::TypeParamTypeVar);
|
impl_binding_has_ty_def!(ast::TypeParamTypeVar);
|
||||||
|
|
||||||
impl HasType for ast::Alias {
|
impl HasType for ast::Alias {
|
||||||
fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Type<'db> {
|
fn inferred_type<'db>(&self, model: &SemanticModel<'db>) -> Option<Type<'db>> {
|
||||||
if &self.name == "*" {
|
if &self.name == "*" {
|
||||||
return Type::Never;
|
return Some(Type::Never);
|
||||||
}
|
}
|
||||||
let index = semantic_index(model.db, model.file);
|
let index = semantic_index(model.db, model.file);
|
||||||
binding_type(model.db, index.expect_single_definition(self))
|
Some(binding_type(model.db, index.expect_single_definition(self)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -584,7 +585,7 @@ mod tests {
|
||||||
|
|
||||||
let function = ast.suite()[0].as_function_def_stmt().unwrap();
|
let function = ast.suite()[0].as_function_def_stmt().unwrap();
|
||||||
let model = SemanticModel::new(&db, foo);
|
let model = SemanticModel::new(&db, foo);
|
||||||
let ty = function.inferred_type(&model);
|
let ty = function.inferred_type(&model).unwrap();
|
||||||
|
|
||||||
assert!(ty.is_function_literal());
|
assert!(ty.is_function_literal());
|
||||||
|
|
||||||
|
|
@ -603,7 +604,7 @@ mod tests {
|
||||||
|
|
||||||
let class = ast.suite()[0].as_class_def_stmt().unwrap();
|
let class = ast.suite()[0].as_class_def_stmt().unwrap();
|
||||||
let model = SemanticModel::new(&db, foo);
|
let model = SemanticModel::new(&db, foo);
|
||||||
let ty = class.inferred_type(&model);
|
let ty = class.inferred_type(&model).unwrap();
|
||||||
|
|
||||||
assert!(ty.is_class_literal());
|
assert!(ty.is_class_literal());
|
||||||
|
|
||||||
|
|
@ -624,7 +625,7 @@ mod tests {
|
||||||
let import = ast.suite()[0].as_import_from_stmt().unwrap();
|
let import = ast.suite()[0].as_import_from_stmt().unwrap();
|
||||||
let alias = &import.names[0];
|
let alias = &import.names[0];
|
||||||
let model = SemanticModel::new(&db, bar);
|
let model = SemanticModel::new(&db, bar);
|
||||||
let ty = alias.inferred_type(&model);
|
let ty = alias.inferred_type(&model).unwrap();
|
||||||
|
|
||||||
assert!(ty.is_class_literal());
|
assert!(ty.is_class_literal());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -878,7 +878,7 @@ impl<'db> Type<'db> {
|
||||||
Self::Dynamic(DynamicType::Any)
|
Self::Dynamic(DynamicType::Any)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) const fn unknown() -> Self {
|
pub const fn unknown() -> Self {
|
||||||
Self::Dynamic(DynamicType::Unknown)
|
Self::Dynamic(DynamicType::Unknown)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,8 @@ pub fn definitions_for_name<'db>(
|
||||||
// https://typing.python.org/en/latest/spec/special-types.html#special-cases-for-float-and-complex
|
// https://typing.python.org/en/latest/spec/special-types.html#special-cases-for-float-and-complex
|
||||||
if matches!(name_str, "float" | "complex")
|
if matches!(name_str, "float" | "complex")
|
||||||
&& let Some(expr) = node.expr_name()
|
&& let Some(expr) = node.expr_name()
|
||||||
&& let Some(union) = expr.inferred_type(&SemanticModel::new(db, file)).as_union()
|
&& let Some(ty) = expr.inferred_type(model)
|
||||||
|
&& let Some(union) = ty.as_union()
|
||||||
&& is_float_or_complex_annotation(db, union, name_str)
|
&& is_float_or_complex_annotation(db, union, name_str)
|
||||||
{
|
{
|
||||||
return union
|
return union
|
||||||
|
|
@ -234,7 +235,10 @@ pub fn definitions_for_attribute<'db>(
|
||||||
let mut resolved = Vec::new();
|
let mut resolved = Vec::new();
|
||||||
|
|
||||||
// Determine the type of the LHS
|
// Determine the type of the LHS
|
||||||
let lhs_ty = attribute.value.inferred_type(model);
|
let Some(lhs_ty) = attribute.value.inferred_type(model) else {
|
||||||
|
return resolved;
|
||||||
|
};
|
||||||
|
|
||||||
let tys = match lhs_ty {
|
let tys = match lhs_ty {
|
||||||
Type::Union(union) => union.elements(model.db()).to_vec(),
|
Type::Union(union) => union.elements(model.db()).to_vec(),
|
||||||
_ => vec![lhs_ty],
|
_ => vec![lhs_ty],
|
||||||
|
|
@ -374,7 +378,9 @@ pub fn definitions_for_keyword_argument<'db>(
|
||||||
call_expr: &ast::ExprCall,
|
call_expr: &ast::ExprCall,
|
||||||
) -> Vec<ResolvedDefinition<'db>> {
|
) -> Vec<ResolvedDefinition<'db>> {
|
||||||
let db = model.db();
|
let db = model.db();
|
||||||
let func_type = call_expr.func.inferred_type(model);
|
let Some(func_type) = call_expr.func.inferred_type(model) else {
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
|
|
||||||
let Some(keyword_name) = keyword.arg.as_ref() else {
|
let Some(keyword_name) = keyword.arg.as_ref() else {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
|
|
@ -498,7 +504,9 @@ pub fn call_signature_details<'db>(
|
||||||
model: &SemanticModel<'db>,
|
model: &SemanticModel<'db>,
|
||||||
call_expr: &ast::ExprCall,
|
call_expr: &ast::ExprCall,
|
||||||
) -> Vec<CallSignatureDetails<'db>> {
|
) -> Vec<CallSignatureDetails<'db>> {
|
||||||
let func_type = call_expr.func.inferred_type(model);
|
let Some(func_type) = call_expr.func.inferred_type(model) else {
|
||||||
|
return Vec::new();
|
||||||
|
};
|
||||||
|
|
||||||
// Use into_callable to handle all the complex type conversions
|
// Use into_callable to handle all the complex type conversions
|
||||||
if let Some(callable_type) = func_type
|
if let Some(callable_type) = func_type
|
||||||
|
|
@ -507,7 +515,9 @@ pub fn call_signature_details<'db>(
|
||||||
{
|
{
|
||||||
let call_arguments =
|
let call_arguments =
|
||||||
CallArguments::from_arguments(&call_expr.arguments, |_, splatted_value| {
|
CallArguments::from_arguments(&call_expr.arguments, |_, splatted_value| {
|
||||||
splatted_value.inferred_type(model)
|
splatted_value
|
||||||
|
.inferred_type(model)
|
||||||
|
.unwrap_or(Type::unknown())
|
||||||
});
|
});
|
||||||
let bindings = callable_type
|
let bindings = callable_type
|
||||||
.bindings(model.db())
|
.bindings(model.db())
|
||||||
|
|
@ -564,7 +574,7 @@ pub fn call_type_simplified_by_overloads(
|
||||||
call_expr: &ast::ExprCall,
|
call_expr: &ast::ExprCall,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
let db = model.db();
|
let db = model.db();
|
||||||
let func_type = call_expr.func.inferred_type(model);
|
let func_type = call_expr.func.inferred_type(model)?;
|
||||||
|
|
||||||
// Use into_callable to handle all the complex type conversions
|
// Use into_callable to handle all the complex type conversions
|
||||||
let callable_type = func_type.try_upcast_to_callable(db)?.into_type(db);
|
let callable_type = func_type.try_upcast_to_callable(db)?.into_type(db);
|
||||||
|
|
@ -579,7 +589,9 @@ pub fn call_type_simplified_by_overloads(
|
||||||
|
|
||||||
// Hand the overload resolution system as much type info as we have
|
// Hand the overload resolution system as much type info as we have
|
||||||
let args = CallArguments::from_arguments_typed(&call_expr.arguments, |_, splatted_value| {
|
let args = CallArguments::from_arguments_typed(&call_expr.arguments, |_, splatted_value| {
|
||||||
splatted_value.inferred_type(model)
|
splatted_value
|
||||||
|
.inferred_type(model)
|
||||||
|
.unwrap_or(Type::unknown())
|
||||||
});
|
});
|
||||||
|
|
||||||
// Try to resolve overloads with the arguments/types we have
|
// Try to resolve overloads with the arguments/types we have
|
||||||
|
|
@ -612,8 +624,8 @@ pub fn definitions_for_bin_op<'db>(
|
||||||
model: &SemanticModel<'db>,
|
model: &SemanticModel<'db>,
|
||||||
binary_op: &ast::ExprBinOp,
|
binary_op: &ast::ExprBinOp,
|
||||||
) -> Option<(Vec<ResolvedDefinition<'db>>, Type<'db>)> {
|
) -> Option<(Vec<ResolvedDefinition<'db>>, Type<'db>)> {
|
||||||
let left_ty = binary_op.left.inferred_type(model);
|
let left_ty = binary_op.left.inferred_type(model)?;
|
||||||
let right_ty = binary_op.right.inferred_type(model);
|
let right_ty = binary_op.right.inferred_type(model)?;
|
||||||
|
|
||||||
let Ok(bindings) = Type::try_call_bin_op(model.db(), left_ty, binary_op.op, right_ty) else {
|
let Ok(bindings) = Type::try_call_bin_op(model.db(), left_ty, binary_op.op, right_ty) else {
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -639,7 +651,7 @@ pub fn definitions_for_unary_op<'db>(
|
||||||
model: &SemanticModel<'db>,
|
model: &SemanticModel<'db>,
|
||||||
unary_op: &ast::ExprUnaryOp,
|
unary_op: &ast::ExprUnaryOp,
|
||||||
) -> Option<(Vec<ResolvedDefinition<'db>>, Type<'db>)> {
|
) -> Option<(Vec<ResolvedDefinition<'db>>, Type<'db>)> {
|
||||||
let operand_ty = unary_op.operand.inferred_type(model);
|
let operand_ty = unary_op.operand.inferred_type(model)?;
|
||||||
|
|
||||||
let unary_dunder_method = match unary_op.op {
|
let unary_dunder_method = match unary_op.op {
|
||||||
ast::UnaryOp::Invert => "__invert__",
|
ast::UnaryOp::Invert => "__invert__",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue