mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-07 21:25:08 +00:00
rework check_call
more
This commit is contained in:
parent
040bb0f932
commit
9ea95e9f24
3 changed files with 62 additions and 79 deletions
|
@ -67,7 +67,10 @@ impl<'db> Bindings<'db> {
|
|||
))
|
||||
}
|
||||
|
||||
pub(crate) fn from_overloads(callee: Type<'db>, overloads: impl IntoIterator<Item = Signature<'db>>) -> Self {
|
||||
pub(crate) fn from_overloads(
|
||||
callee: Type<'db>,
|
||||
overloads: impl IntoIterator<Item = Signature<'db>>,
|
||||
) -> Self {
|
||||
Self::from(CallableBinding::from_overloads(callee, overloads))
|
||||
}
|
||||
|
||||
|
|
|
@ -3184,16 +3184,16 @@ impl KnownClass {
|
|||
}
|
||||
}
|
||||
|
||||
/// Evaluate a call to this known class, and emit any diagnostics that are necessary
|
||||
/// as a result of the call.
|
||||
/// Evaluate a call to this known class, emit any diagnostics that are necessary
|
||||
/// as a result of the call, and return the type that results from the call.
|
||||
pub(super) fn check_call<'db>(
|
||||
self,
|
||||
context: &InferContext<'db, '_>,
|
||||
index: &SemanticIndex<'db>,
|
||||
overload_binding: &mut Binding<'db>,
|
||||
overload_binding: &Binding<'db>,
|
||||
call_argument_types: &CallArgumentTypes<'_, 'db>,
|
||||
call_expression: &ast::ExprCall,
|
||||
) {
|
||||
) -> Option<Type<'db>> {
|
||||
let db = context.db();
|
||||
let scope = context.scope();
|
||||
let module = context.module();
|
||||
|
@ -3209,10 +3209,9 @@ impl KnownClass {
|
|||
let Some(enclosing_class) =
|
||||
nearest_enclosing_class(db, index, scope, module)
|
||||
else {
|
||||
overload_binding.set_return_type(Type::unknown());
|
||||
BoundSuperError::UnavailableImplicitArguments
|
||||
.report_diagnostic(context, call_expression.into());
|
||||
return;
|
||||
return Some(Type::unknown());
|
||||
};
|
||||
|
||||
// The type of the first parameter if the given scope is function-like (i.e. function or lambda).
|
||||
|
@ -3232,10 +3231,9 @@ impl KnownClass {
|
|||
};
|
||||
|
||||
let Some(first_param) = first_param else {
|
||||
overload_binding.set_return_type(Type::unknown());
|
||||
BoundSuperError::UnavailableImplicitArguments
|
||||
.report_diagnostic(context, call_expression.into());
|
||||
return;
|
||||
return Some(Type::unknown());
|
||||
};
|
||||
|
||||
let definition = index.expect_single_definition(first_param);
|
||||
|
@ -3252,7 +3250,7 @@ impl KnownClass {
|
|||
Type::unknown()
|
||||
});
|
||||
|
||||
overload_binding.set_return_type(bound_super);
|
||||
Some(bound_super)
|
||||
}
|
||||
[Some(pivot_class_type), Some(owner_type)] => {
|
||||
let bound_super = BoundSuperType::build(db, *pivot_class_type, *owner_type)
|
||||
|
@ -3261,9 +3259,9 @@ impl KnownClass {
|
|||
Type::unknown()
|
||||
});
|
||||
|
||||
overload_binding.set_return_type(bound_super);
|
||||
Some(bound_super)
|
||||
}
|
||||
_ => {}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3278,14 +3276,12 @@ impl KnownClass {
|
|||
_ => None,
|
||||
}
|
||||
}) else {
|
||||
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 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;
|
||||
};
|
||||
|
||||
let [
|
||||
|
@ -3298,7 +3294,7 @@ impl KnownClass {
|
|||
_infer_variance,
|
||||
] = overload_binding.parameter_types()
|
||||
else {
|
||||
return;
|
||||
return None;
|
||||
};
|
||||
|
||||
let covariant = covariant
|
||||
|
@ -3311,39 +3307,30 @@ impl KnownClass {
|
|||
|
||||
let variance = match (contravariant, covariant) {
|
||||
(Truthiness::Ambiguous, _) => {
|
||||
let Some(builder) =
|
||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let 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;
|
||||
return None;
|
||||
}
|
||||
(_, Truthiness::Ambiguous) => {
|
||||
let Some(builder) =
|
||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let 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;
|
||||
return None;
|
||||
}
|
||||
(Truthiness::AlwaysTrue, Truthiness::AlwaysTrue) => {
|
||||
let Some(builder) =
|
||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
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;
|
||||
return None;
|
||||
}
|
||||
(Truthiness::AlwaysTrue, Truthiness::AlwaysFalse) => {
|
||||
TypeVarVariance::Contravariant
|
||||
|
@ -3357,11 +3344,8 @@ 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 Some(builder) =
|
||||
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
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 \
|
||||
the name of the variable it is assigned to (`{}`)",
|
||||
|
@ -3372,7 +3356,7 @@ impl KnownClass {
|
|||
},
|
||||
target.id,
|
||||
));
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
let bound_or_constraint = match (bound, constraints) {
|
||||
|
@ -3397,13 +3381,13 @@ impl KnownClass {
|
|||
|
||||
// TODO: Emit a diagnostic that TypeVar cannot be both bounded and
|
||||
// constrained
|
||||
(Some(_), Some(_)) => return,
|
||||
(Some(_), Some(_)) => return None,
|
||||
|
||||
(None, None) => None,
|
||||
};
|
||||
|
||||
let containing_assignment = index.expect_single_definition(target);
|
||||
overload_binding.set_return_type(Type::KnownInstance(KnownInstanceType::TypeVar(
|
||||
Some(Type::KnownInstance(KnownInstanceType::TypeVar(
|
||||
TypeVarInstance::new(
|
||||
db,
|
||||
target.id.clone(),
|
||||
|
@ -3413,7 +3397,7 @@ impl KnownClass {
|
|||
*default,
|
||||
TypeVarKind::Legacy,
|
||||
),
|
||||
)));
|
||||
)))
|
||||
}
|
||||
|
||||
KnownClass::TypeAliasType => {
|
||||
|
@ -3429,44 +3413,37 @@ impl KnownClass {
|
|||
});
|
||||
|
||||
let [Some(name), Some(value), ..] = overload_binding.parameter_types() else {
|
||||
return;
|
||||
return None;
|
||||
};
|
||||
|
||||
if let Some(name) = name.into_string_literal() {
|
||||
overload_binding.set_return_type(Type::KnownInstance(
|
||||
KnownInstanceType::TypeAliasType(TypeAliasType::Bare(
|
||||
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,
|
||||
),
|
||||
)),
|
||||
));
|
||||
} 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",
|
||||
);
|
||||
}
|
||||
)))
|
||||
})
|
||||
.or_else(|| {
|
||||
let 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
|
||||
})
|
||||
}
|
||||
|
||||
_ => {}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Ideally we'd use `try_call_constructor` for all constructor calls.
|
||||
// Currently we don't for a few special known types, either because their
|
||||
// constructors are defined with overloads, or because we want to special case
|
||||
// their return type beyond what typeshed provides (though this support could
|
||||
// likely be moved into the `try_call_constructor` path). Once we support
|
||||
// overloads, re-evaluate the need for these arms.
|
||||
pub(super) fn bindings<'db>(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
callee: Type<'db>,
|
||||
) -> Option<Bindings<'db>> {
|
||||
/// Return the bindings we should use when inferring a constructor call for this special-cased class.
|
||||
/// Return `None` if this class doesn't require special handling for calls to its constructor.
|
||||
pub(super) fn bindings<'db>(self, db: &'db dyn Db, callee: Type<'db>) -> Option<Bindings<'db>> {
|
||||
let bindings = match self {
|
||||
// ```py
|
||||
// class bool(int):
|
||||
|
@ -3536,7 +3513,7 @@ impl KnownClass {
|
|||
.with_annotated_type(str_instance)
|
||||
.with_default_type(Type::string_literal(db, "strict")),
|
||||
]),
|
||||
Some(KnownClass::Str.to_instance(db)),
|
||||
Some(str_instance),
|
||||
),
|
||||
],
|
||||
)
|
||||
|
@ -3655,7 +3632,7 @@ impl KnownClass {
|
|||
db,
|
||||
[Type::any(), none_instance],
|
||||
))
|
||||
.with_default_type(Type::none(db)),
|
||||
.with_default_type(none_instance),
|
||||
Parameter::keyword_only(Name::new_static("default"))
|
||||
.type_form()
|
||||
.with_annotated_type(Type::any())
|
||||
|
@ -3738,19 +3715,19 @@ impl KnownClass {
|
|||
db,
|
||||
[CallableType::single(db, getter_signature), none_instance],
|
||||
))
|
||||
.with_default_type(Type::none(db)),
|
||||
.with_default_type(none_instance),
|
||||
Parameter::positional_or_keyword(Name::new_static("fset"))
|
||||
.with_annotated_type(UnionType::from_elements(
|
||||
db,
|
||||
[CallableType::single(db, setter_signature), none_instance],
|
||||
))
|
||||
.with_default_type(Type::none(db)),
|
||||
.with_default_type(none_instance),
|
||||
Parameter::positional_or_keyword(Name::new_static("fdel"))
|
||||
.with_annotated_type(UnionType::from_elements(
|
||||
db,
|
||||
[CallableType::single(db, deleter_signature), none_instance],
|
||||
))
|
||||
.with_default_type(Type::none(db)),
|
||||
.with_default_type(none_instance),
|
||||
Parameter::positional_or_keyword(Name::new_static("doc"))
|
||||
.with_annotated_type(UnionType::from_elements(
|
||||
db,
|
||||
|
|
|
@ -5404,13 +5404,16 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
let Some(known_class) = class.known(self.db()) else {
|
||||
continue;
|
||||
};
|
||||
known_class.check_call(
|
||||
let overridden_return = known_class.check_call(
|
||||
&self.context,
|
||||
self.index,
|
||||
overload,
|
||||
&call_argument_types,
|
||||
call_expression,
|
||||
);
|
||||
if let Some(overridden_return) = overridden_return {
|
||||
overload.set_return_type(overridden_return);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue