mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:34:57 +00:00
[ty] Show the raw argument type in reveal_type
(#19400)
This PR is changes how `reveal_type` determines what type to reveal, in a way that should be a no-op to most callers. Previously, we would reveal the type of the first parameter, _after_ all of the call binding machinery had done its work. This includes inferring the specialization of a generic function, and then applying that specialization to all parameter and argument types, which is relevant since the typeshed definition of `reveal_type` is generic: ```pyi def reveal_type(obj: _T, /) -> _T: ... ``` Normally this does not matter, since we infer `_T = [arg type]` and apply that to the parameter type, yielding `[arg type]`. But applying that specialization also simplifies the argument type, which makes `reveal_type` less useful as a debugging aid when we want to see the actual, raw, unsimplified argument type. With this patch, we now grab the original unmodified argument type and reveal that instead. In addition to making the debugging aid example work, this also makes our `reveal_type` implementation more robust to custom typeshed definitions, such as ```py def reveal_type(obj: Any) -> Any: ... ``` (That custom definition is probably not what anyone would want, since you wouldn't be able to depend on the return type being equivalent to the argument type, but still)
This commit is contained in:
parent
1fd9103e81
commit
4aee0398cb
3 changed files with 212 additions and 194 deletions
|
@ -3387,10 +3387,10 @@ impl KnownClass {
|
|||
self,
|
||||
context: &InferContext<'db, '_>,
|
||||
index: &SemanticIndex<'db>,
|
||||
overload_binding: &Binding<'db>,
|
||||
call_argument_types: &CallArguments<'_, 'db>,
|
||||
overload: &mut Binding<'db>,
|
||||
call_arguments: &CallArguments<'_, 'db>,
|
||||
call_expression: &ast::ExprCall,
|
||||
) -> Option<Type<'db>> {
|
||||
) {
|
||||
let db = context.db();
|
||||
let scope = context.scope();
|
||||
let module = context.module();
|
||||
|
@ -3401,14 +3401,15 @@ impl KnownClass {
|
|||
// In this case, we need to infer the two arguments:
|
||||
// 1. The nearest enclosing class
|
||||
// 2. The first parameter of the current function (typically `self` or `cls`)
|
||||
match overload_binding.parameter_types() {
|
||||
match overload.parameter_types() {
|
||||
[] => {
|
||||
let Some(enclosing_class) =
|
||||
nearest_enclosing_class(db, index, scope, module)
|
||||
else {
|
||||
BoundSuperError::UnavailableImplicitArguments
|
||||
.report_diagnostic(context, call_expression.into());
|
||||
return Some(Type::unknown());
|
||||
overload.set_return_type(Type::unknown());
|
||||
return;
|
||||
};
|
||||
|
||||
// The type of the first parameter if the given scope is function-like (i.e. function or lambda).
|
||||
|
@ -3430,7 +3431,8 @@ impl KnownClass {
|
|||
let Some(first_param) = first_param else {
|
||||
BoundSuperError::UnavailableImplicitArguments
|
||||
.report_diagnostic(context, call_expression.into());
|
||||
return Some(Type::unknown());
|
||||
overload.set_return_type(Type::unknown());
|
||||
return;
|
||||
};
|
||||
|
||||
let definition = index.expect_single_definition(first_param);
|
||||
|
@ -3447,7 +3449,7 @@ impl KnownClass {
|
|||
Type::unknown()
|
||||
});
|
||||
|
||||
Some(bound_super)
|
||||
overload.set_return_type(bound_super);
|
||||
}
|
||||
[Some(pivot_class_type), Some(owner_type)] => {
|
||||
let bound_super = BoundSuperType::build(db, *pivot_class_type, *owner_type)
|
||||
|
@ -3455,10 +3457,9 @@ impl KnownClass {
|
|||
err.report_diagnostic(context, call_expression.into());
|
||||
Type::unknown()
|
||||
});
|
||||
|
||||
Some(bound_super)
|
||||
overload.set_return_type(bound_super);
|
||||
}
|
||||
_ => None,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3473,12 +3474,14 @@ impl KnownClass {
|
|||
_ => None,
|
||||
}
|
||||
}) else {
|
||||
let builder =
|
||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)?;
|
||||
builder.into_diagnostic(
|
||||
"A legacy `typing.TypeVar` must be immediately assigned to a variable",
|
||||
);
|
||||
return None;
|
||||
if let Some(builder) =
|
||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
|
||||
{
|
||||
builder.into_diagnostic(
|
||||
"A legacy `typing.TypeVar` must be immediately assigned to a variable",
|
||||
);
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
let [
|
||||
|
@ -3489,9 +3492,9 @@ impl KnownClass {
|
|||
contravariant,
|
||||
covariant,
|
||||
_infer_variance,
|
||||
] = overload_binding.parameter_types()
|
||||
] = overload.parameter_types()
|
||||
else {
|
||||
return None;
|
||||
return;
|
||||
};
|
||||
|
||||
let covariant = covariant
|
||||
|
@ -3504,30 +3507,37 @@ impl KnownClass {
|
|||
|
||||
let variance = match (contravariant, covariant) {
|
||||
(Truthiness::Ambiguous, _) => {
|
||||
let builder =
|
||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)?;
|
||||
builder.into_diagnostic(
|
||||
"The `contravariant` parameter of a legacy `typing.TypeVar` \
|
||||
if let Some(builder) =
|
||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
|
||||
{
|
||||
builder.into_diagnostic(
|
||||
"The `contravariant` parameter of a legacy `typing.TypeVar` \
|
||||
cannot have an ambiguous value",
|
||||
);
|
||||
return None;
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
(_, Truthiness::Ambiguous) => {
|
||||
let builder =
|
||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)?;
|
||||
builder.into_diagnostic(
|
||||
"The `covariant` parameter of a legacy `typing.TypeVar` \
|
||||
if let Some(builder) =
|
||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
|
||||
{
|
||||
builder.into_diagnostic(
|
||||
"The `covariant` parameter of a legacy `typing.TypeVar` \
|
||||
cannot have an ambiguous value",
|
||||
);
|
||||
return None;
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
(Truthiness::AlwaysTrue, Truthiness::AlwaysTrue) => {
|
||||
let builder =
|
||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)?;
|
||||
builder.into_diagnostic(
|
||||
"A legacy `typing.TypeVar` cannot be both covariant and contravariant",
|
||||
);
|
||||
return None;
|
||||
if let Some(builder) =
|
||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
|
||||
{
|
||||
builder.into_diagnostic(
|
||||
"A legacy `typing.TypeVar` cannot be both \
|
||||
covariant and contravariant",
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
(Truthiness::AlwaysTrue, Truthiness::AlwaysFalse) => {
|
||||
TypeVarVariance::Contravariant
|
||||
|
@ -3541,19 +3551,21 @@ impl KnownClass {
|
|||
let name_param = name_param.into_string_literal().map(|name| name.value(db));
|
||||
|
||||
if name_param.is_none_or(|name_param| name_param != target.id) {
|
||||
let builder =
|
||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)?;
|
||||
builder.into_diagnostic(format_args!(
|
||||
"The name of a legacy `typing.TypeVar`{} must match \
|
||||
if let Some(builder) =
|
||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
|
||||
{
|
||||
builder.into_diagnostic(format_args!(
|
||||
"The name of a legacy `typing.TypeVar`{} must match \
|
||||
the name of the variable it is assigned to (`{}`)",
|
||||
if let Some(name_param) = name_param {
|
||||
format!(" (`{name_param}`)")
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
target.id,
|
||||
));
|
||||
return None;
|
||||
if let Some(name_param) = name_param {
|
||||
format!(" (`{name_param}`)")
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
target.id,
|
||||
));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let bound_or_constraint = match (bound, constraints) {
|
||||
|
@ -3568,8 +3580,8 @@ impl KnownClass {
|
|||
// typevar constraints.
|
||||
let elements = UnionType::new(
|
||||
db,
|
||||
overload_binding
|
||||
.arguments_for_parameter(call_argument_types, 1)
|
||||
overload
|
||||
.arguments_for_parameter(call_arguments, 1)
|
||||
.map(|(_, ty)| ty)
|
||||
.collect::<Box<_>>(),
|
||||
);
|
||||
|
@ -3578,13 +3590,13 @@ impl KnownClass {
|
|||
|
||||
// TODO: Emit a diagnostic that TypeVar cannot be both bounded and
|
||||
// constrained
|
||||
(Some(_), Some(_)) => return None,
|
||||
(Some(_), Some(_)) => return,
|
||||
|
||||
(None, None) => None,
|
||||
};
|
||||
|
||||
let containing_assignment = index.expect_single_definition(target);
|
||||
Some(Type::KnownInstance(KnownInstanceType::TypeVar(
|
||||
overload.set_return_type(Type::KnownInstance(KnownInstanceType::TypeVar(
|
||||
TypeVarInstance::new(
|
||||
db,
|
||||
&target.id,
|
||||
|
@ -3594,7 +3606,7 @@ impl KnownClass {
|
|||
*default,
|
||||
TypeVarKind::Legacy,
|
||||
),
|
||||
)))
|
||||
)));
|
||||
}
|
||||
|
||||
KnownClass::TypeAliasType => {
|
||||
|
@ -3609,32 +3621,31 @@ impl KnownClass {
|
|||
}
|
||||
});
|
||||
|
||||
let [Some(name), Some(value), ..] = overload_binding.parameter_types() else {
|
||||
return None;
|
||||
let [Some(name), Some(value), ..] = overload.parameter_types() else {
|
||||
return;
|
||||
};
|
||||
|
||||
name.into_string_literal()
|
||||
.map(|name| {
|
||||
Type::KnownInstance(KnownInstanceType::TypeAliasType(TypeAliasType::Bare(
|
||||
BareTypeAliasType::new(
|
||||
db,
|
||||
ast::name::Name::new(name.value(db)),
|
||||
containing_assignment,
|
||||
value,
|
||||
),
|
||||
)))
|
||||
})
|
||||
.or_else(|| {
|
||||
let builder =
|
||||
context.report_lint(&INVALID_TYPE_ALIAS_TYPE, call_expression)?;
|
||||
let Some(name) = name.into_string_literal() else {
|
||||
if let Some(builder) =
|
||||
context.report_lint(&INVALID_TYPE_ALIAS_TYPE, call_expression)
|
||||
{
|
||||
builder.into_diagnostic(
|
||||
"The name of a `typing.TypeAlias` must be a string literal",
|
||||
);
|
||||
None
|
||||
})
|
||||
}
|
||||
return;
|
||||
};
|
||||
overload.set_return_type(Type::KnownInstance(KnownInstanceType::TypeAliasType(
|
||||
TypeAliasType::Bare(BareTypeAliasType::new(
|
||||
db,
|
||||
ast::name::Name::new(name.value(db)),
|
||||
containing_assignment,
|
||||
value,
|
||||
)),
|
||||
)));
|
||||
}
|
||||
|
||||
_ => None,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ use crate::semantic_index::ast_ids::HasScopedUseId;
|
|||
use crate::semantic_index::definition::Definition;
|
||||
use crate::semantic_index::place::ScopeId;
|
||||
use crate::semantic_index::semantic_index;
|
||||
use crate::types::call::{Binding, CallArguments};
|
||||
use crate::types::context::InferContext;
|
||||
use crate::types::diagnostic::{
|
||||
REDUNDANT_CAST, STATIC_ASSERT_ERROR, TYPE_ASSERTION_FAILURE,
|
||||
|
@ -76,7 +77,7 @@ 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, walk_type_mapping,
|
||||
TypeTransformer, TypeVarInstance, UnionBuilder, walk_type_mapping,
|
||||
};
|
||||
use crate::{Db, FxOrderSet, ModuleName, resolve_module};
|
||||
|
||||
|
@ -1039,86 +1040,90 @@ impl KnownFunction {
|
|||
pub(super) fn check_call<'db>(
|
||||
self,
|
||||
context: &InferContext<'db, '_>,
|
||||
parameter_types: &[Option<Type<'db>>],
|
||||
overload: &mut Binding<'db>,
|
||||
call_arguments: &CallArguments<'_, 'db>,
|
||||
call_expression: &ast::ExprCall,
|
||||
file: File,
|
||||
) -> Option<Type<'db>> {
|
||||
) {
|
||||
let db = context.db();
|
||||
let parameter_types = overload.parameter_types();
|
||||
|
||||
match self {
|
||||
KnownFunction::RevealType => {
|
||||
let [Some(revealed_type)] = parameter_types else {
|
||||
return None;
|
||||
};
|
||||
let builder =
|
||||
context.report_diagnostic(DiagnosticId::RevealedType, Severity::Info)?;
|
||||
let mut diag = builder.into_diagnostic("Revealed type");
|
||||
let span = context.span(&call_expression.arguments.args[0]);
|
||||
diag.annotate(
|
||||
Annotation::primary(span)
|
||||
.message(format_args!("`{}`", revealed_type.display(db))),
|
||||
);
|
||||
None
|
||||
let revealed_type = overload
|
||||
.arguments_for_parameter(call_arguments, 0)
|
||||
.fold(UnionBuilder::new(db), |builder, (_, ty)| builder.add(ty))
|
||||
.build();
|
||||
if let Some(builder) =
|
||||
context.report_diagnostic(DiagnosticId::RevealedType, Severity::Info)
|
||||
{
|
||||
let mut diag = builder.into_diagnostic("Revealed type");
|
||||
let span = context.span(&call_expression.arguments.args[0]);
|
||||
diag.annotate(
|
||||
Annotation::primary(span)
|
||||
.message(format_args!("`{}`", revealed_type.display(db))),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
KnownFunction::AssertType => {
|
||||
let [Some(actual_ty), Some(asserted_ty)] = parameter_types else {
|
||||
return None;
|
||||
return;
|
||||
};
|
||||
|
||||
if actual_ty.is_equivalent_to(db, *asserted_ty) {
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
let builder = context.report_lint(&TYPE_ASSERTION_FAILURE, call_expression)?;
|
||||
if let Some(builder) = context.report_lint(&TYPE_ASSERTION_FAILURE, call_expression)
|
||||
{
|
||||
let mut diagnostic = builder.into_diagnostic(format_args!(
|
||||
"Argument does not have asserted type `{}`",
|
||||
asserted_ty.display(db),
|
||||
));
|
||||
|
||||
let mut diagnostic = builder.into_diagnostic(format_args!(
|
||||
"Argument does not have asserted type `{}`",
|
||||
asserted_ty.display(db),
|
||||
));
|
||||
diagnostic.annotate(
|
||||
Annotation::secondary(context.span(&call_expression.arguments.args[0]))
|
||||
.message(format_args!(
|
||||
"Inferred type of argument is `{}`",
|
||||
actual_ty.display(db),
|
||||
)),
|
||||
);
|
||||
|
||||
diagnostic.annotate(
|
||||
Annotation::secondary(context.span(&call_expression.arguments.args[0]))
|
||||
.message(format_args!(
|
||||
"Inferred type of argument is `{}`",
|
||||
actual_ty.display(db),
|
||||
)),
|
||||
);
|
||||
|
||||
diagnostic.info(format_args!(
|
||||
"`{asserted_type}` and `{inferred_type}` are not equivalent types",
|
||||
asserted_type = asserted_ty.display(db),
|
||||
inferred_type = actual_ty.display(db),
|
||||
));
|
||||
|
||||
None
|
||||
diagnostic.info(format_args!(
|
||||
"`{asserted_type}` and `{inferred_type}` are not equivalent types",
|
||||
asserted_type = asserted_ty.display(db),
|
||||
inferred_type = actual_ty.display(db),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
KnownFunction::AssertNever => {
|
||||
let [Some(actual_ty)] = parameter_types else {
|
||||
return None;
|
||||
return;
|
||||
};
|
||||
if actual_ty.is_equivalent_to(db, Type::Never) {
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
if let Some(builder) = context.report_lint(&TYPE_ASSERTION_FAILURE, call_expression)
|
||||
{
|
||||
let mut diagnostic =
|
||||
builder.into_diagnostic("Argument does not have asserted type `Never`");
|
||||
diagnostic.annotate(
|
||||
Annotation::secondary(context.span(&call_expression.arguments.args[0]))
|
||||
.message(format_args!(
|
||||
"Inferred type of argument is `{}`",
|
||||
actual_ty.display(db)
|
||||
)),
|
||||
);
|
||||
diagnostic.info(format_args!(
|
||||
"`Never` and `{inferred_type}` are not equivalent types",
|
||||
inferred_type = actual_ty.display(db),
|
||||
));
|
||||
}
|
||||
let builder = context.report_lint(&TYPE_ASSERTION_FAILURE, call_expression)?;
|
||||
|
||||
let mut diagnostic =
|
||||
builder.into_diagnostic("Argument does not have asserted type `Never`");
|
||||
diagnostic.annotate(
|
||||
Annotation::secondary(context.span(&call_expression.arguments.args[0]))
|
||||
.message(format_args!(
|
||||
"Inferred type of argument is `{}`",
|
||||
actual_ty.display(db)
|
||||
)),
|
||||
);
|
||||
diagnostic.info(format_args!(
|
||||
"`Never` and `{inferred_type}` are not equivalent types",
|
||||
inferred_type = actual_ty.display(db),
|
||||
));
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
KnownFunction::StaticAssert => {
|
||||
let [Some(parameter_ty), message] = parameter_types else {
|
||||
return None;
|
||||
return;
|
||||
};
|
||||
let truthiness = match parameter_ty.try_bool(db) {
|
||||
Ok(truthiness) => truthiness,
|
||||
|
@ -1138,41 +1143,42 @@ impl KnownFunction {
|
|||
|
||||
err.report_diagnostic(context, condition);
|
||||
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let builder = context.report_lint(&STATIC_ASSERT_ERROR, call_expression)?;
|
||||
if truthiness.is_always_true() {
|
||||
return None;
|
||||
}
|
||||
if let Some(message) = message
|
||||
.and_then(Type::into_string_literal)
|
||||
.map(|s| s.value(db))
|
||||
{
|
||||
builder.into_diagnostic(format_args!("Static assertion error: {message}"));
|
||||
} else if *parameter_ty == Type::BooleanLiteral(false) {
|
||||
builder
|
||||
.into_diagnostic("Static assertion error: argument evaluates to `False`");
|
||||
} else if truthiness.is_always_false() {
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Static assertion error: argument of type `{parameter_ty}` \
|
||||
if let Some(builder) = context.report_lint(&STATIC_ASSERT_ERROR, call_expression) {
|
||||
if truthiness.is_always_true() {
|
||||
return;
|
||||
}
|
||||
if let Some(message) = message
|
||||
.and_then(Type::into_string_literal)
|
||||
.map(|s| s.value(db))
|
||||
{
|
||||
builder.into_diagnostic(format_args!("Static assertion error: {message}"));
|
||||
} else if *parameter_ty == Type::BooleanLiteral(false) {
|
||||
builder.into_diagnostic(
|
||||
"Static assertion error: argument evaluates to `False`",
|
||||
);
|
||||
} else if truthiness.is_always_false() {
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Static assertion error: argument of type `{parameter_ty}` \
|
||||
is statically known to be falsy",
|
||||
parameter_ty = parameter_ty.display(db)
|
||||
));
|
||||
} else {
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Static assertion error: argument of type `{parameter_ty}` \
|
||||
parameter_ty = parameter_ty.display(db)
|
||||
));
|
||||
} else {
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Static assertion error: argument of type `{parameter_ty}` \
|
||||
has an ambiguous static truthiness",
|
||||
parameter_ty = parameter_ty.display(db)
|
||||
));
|
||||
parameter_ty = parameter_ty.display(db)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
KnownFunction::Cast => {
|
||||
let [Some(casted_type), Some(source_type)] = parameter_types else {
|
||||
return None;
|
||||
return;
|
||||
};
|
||||
let contains_unknown_or_todo =
|
||||
|ty| matches!(ty, Type::Dynamic(dynamic) if dynamic != DynamicType::Any);
|
||||
|
@ -1180,31 +1186,34 @@ impl KnownFunction {
|
|||
&& !any_over_type(db, *source_type, &contains_unknown_or_todo)
|
||||
&& !any_over_type(db, *casted_type, &contains_unknown_or_todo)
|
||||
{
|
||||
let builder = context.report_lint(&REDUNDANT_CAST, call_expression)?;
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Value is already of type `{}`",
|
||||
casted_type.display(db),
|
||||
));
|
||||
if let Some(builder) = context.report_lint(&REDUNDANT_CAST, call_expression) {
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Value is already of type `{}`",
|
||||
casted_type.display(db),
|
||||
));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
KnownFunction::GetProtocolMembers => {
|
||||
let [Some(Type::ClassLiteral(class))] = parameter_types else {
|
||||
return None;
|
||||
return;
|
||||
};
|
||||
if class.is_protocol(db) {
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
report_bad_argument_to_get_protocol_members(context, call_expression, *class);
|
||||
None
|
||||
}
|
||||
|
||||
KnownFunction::IsInstance | KnownFunction::IsSubclass => {
|
||||
let [_, Some(Type::ClassLiteral(class))] = parameter_types else {
|
||||
return None;
|
||||
return;
|
||||
};
|
||||
let Some(protocol_class) = class.into_protocol_class(db) else {
|
||||
return;
|
||||
};
|
||||
let protocol_class = class.into_protocol_class(db)?;
|
||||
if protocol_class.is_runtime_checkable(db) {
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
report_runtime_check_against_non_runtime_checkable_protocol(
|
||||
context,
|
||||
|
@ -1212,16 +1221,16 @@ impl KnownFunction {
|
|||
protocol_class,
|
||||
self,
|
||||
);
|
||||
None
|
||||
}
|
||||
|
||||
known @ (KnownFunction::DunderImport | KnownFunction::ImportModule) => {
|
||||
let [Some(Type::StringLiteral(full_module_name)), rest @ ..] = parameter_types
|
||||
else {
|
||||
return None;
|
||||
return;
|
||||
};
|
||||
|
||||
if rest.iter().any(Option::is_some) {
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
|
||||
let module_name = full_module_name.value(db);
|
||||
|
@ -1231,16 +1240,20 @@ impl KnownFunction {
|
|||
// `importlib.import_module("collections.abc")` returns the `collections.abc` module.
|
||||
// ty doesn't have a way to represent the return type of the former yet.
|
||||
// https://github.com/astral-sh/ruff/pull/19008#discussion_r2173481311
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
|
||||
let module_name = ModuleName::new(module_name)?;
|
||||
let module = resolve_module(db, &module_name)?;
|
||||
let Some(module_name) = ModuleName::new(module_name) else {
|
||||
return;
|
||||
};
|
||||
let Some(module) = resolve_module(db, &module_name) else {
|
||||
return;
|
||||
};
|
||||
|
||||
Some(Type::module_literal(db, file, &module))
|
||||
overload.set_return_type(Type::module_literal(db, file, &module));
|
||||
}
|
||||
|
||||
_ => None,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5615,30 +5615,24 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
match binding_type {
|
||||
Type::FunctionLiteral(function_literal) => {
|
||||
if let Some(known_function) = function_literal.known(self.db()) {
|
||||
if let Some(return_type) = known_function.check_call(
|
||||
known_function.check_call(
|
||||
&self.context,
|
||||
overload.parameter_types(),
|
||||
overload,
|
||||
&call_arguments,
|
||||
call_expression,
|
||||
self.file(),
|
||||
) {
|
||||
overload.set_return_type(return_type);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Type::ClassLiteral(class) => {
|
||||
let Some(known_class) = class.known(self.db()) else {
|
||||
continue;
|
||||
};
|
||||
let overridden_return = known_class.check_call(
|
||||
&self.context,
|
||||
self.index,
|
||||
overload,
|
||||
&call_arguments,
|
||||
call_expression,
|
||||
);
|
||||
if let Some(overridden_return) = overridden_return {
|
||||
overload.set_return_type(overridden_return);
|
||||
if let Some(known_class) = class.known(self.db()) {
|
||||
known_class.check_call(
|
||||
&self.context,
|
||||
self.index,
|
||||
overload,
|
||||
&call_arguments,
|
||||
call_expression,
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue