mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-20 04:29:47 +00:00
[ty] Improve error messages for unresolved attribute diagnostics (#20963)
## Summary - Type checkers (and type-checker authors) think in terms of types, but I think most Python users think in terms of values. Rather than saying that a _type_ `X` "has no attribute `foo`" (which I think sounds strange to many users), say that "an object of type `X` has no attribute `foo`" - Special-case certain types so that the diagnostic messages read more like normal English: rather than saying "Type `<class 'Foo'>` has no attribute `bar`" or "Object of type `<class 'Foo'>` has no attribute `bar`", just say "Class `Foo` has no attribute `bar`" ## Test Plan Mdtests and snapshots updated
This commit is contained in:
parent
b6b96d75eb
commit
1f8297cfe6
19 changed files with 102 additions and 63 deletions
|
|
@ -2269,10 +2269,25 @@ pub(super) fn report_possibly_missing_attribute(
|
|||
let Some(builder) = context.report_lint(&POSSIBLY_MISSING_ATTRIBUTE, target) else {
|
||||
return;
|
||||
};
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Attribute `{attribute}` on type `{}` may be missing",
|
||||
object_ty.display(context.db()),
|
||||
));
|
||||
let db = context.db();
|
||||
match object_ty {
|
||||
Type::ModuleLiteral(module) => builder.into_diagnostic(format_args!(
|
||||
"Member `{attribute}` may be missing on module `{}`",
|
||||
module.module(db).name(db),
|
||||
)),
|
||||
Type::ClassLiteral(class) => builder.into_diagnostic(format_args!(
|
||||
"Attribute `{attribute}` may be missing on class `{}`",
|
||||
class.name(db),
|
||||
)),
|
||||
Type::GenericAlias(alias) => builder.into_diagnostic(format_args!(
|
||||
"Attribute `{attribute}` may be missing on class `{}`",
|
||||
alias.display(db),
|
||||
)),
|
||||
_ => builder.into_diagnostic(format_args!(
|
||||
"Attribute `{attribute}` may be missing on object of type `{}`",
|
||||
object_ty.display(db),
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
pub(super) fn report_invalid_exception_caught(context: &InferContext, node: &ast::Expr, ty: Type) {
|
||||
|
|
|
|||
|
|
@ -814,6 +814,10 @@ impl Display for DisplayFunctionType<'_> {
|
|||
}
|
||||
|
||||
impl<'db> GenericAlias<'db> {
|
||||
pub(crate) fn display(&'db self, db: &'db dyn Db) -> DisplayGenericAlias<'db> {
|
||||
self.display_with(db, DisplaySettings::default())
|
||||
}
|
||||
|
||||
pub(crate) fn display_with(
|
||||
&'db self,
|
||||
db: &'db dyn Db,
|
||||
|
|
|
|||
|
|
@ -7618,25 +7618,45 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
.context
|
||||
.report_lint(&UNRESOLVED_ATTRIBUTE, attribute)
|
||||
{
|
||||
if bound_on_instance {
|
||||
builder.into_diagnostic(
|
||||
format_args!(
|
||||
"Attribute `{}` can only be accessed on instances, \
|
||||
not on the class object `{}` itself.",
|
||||
attr.id,
|
||||
value_type.display(db)
|
||||
),
|
||||
);
|
||||
} else {
|
||||
let diagnostic = builder.into_diagnostic(
|
||||
format_args!(
|
||||
"Type `{}` has no attribute `{}`",
|
||||
value_type.display(db),
|
||||
attr.id
|
||||
),
|
||||
);
|
||||
hint_if_stdlib_attribute_exists_on_other_versions(db, diagnostic, &value_type, attr);
|
||||
}
|
||||
if bound_on_instance {
|
||||
builder.into_diagnostic(
|
||||
format_args!(
|
||||
"Attribute `{}` can only be accessed on instances, \
|
||||
not on the class object `{}` itself.",
|
||||
attr.id,
|
||||
value_type.display(db)
|
||||
),
|
||||
);
|
||||
} else {
|
||||
let diagnostic = match value_type {
|
||||
Type::ModuleLiteral(module) => builder.into_diagnostic(format_args!(
|
||||
"Module `{}` has no member `{}`",
|
||||
module.module(db).name(db),
|
||||
&attr.id
|
||||
)),
|
||||
Type::ClassLiteral(class) => builder.into_diagnostic(format_args!(
|
||||
"Class `{}` has no attribute `{}`",
|
||||
class.name(db),
|
||||
&attr.id
|
||||
)),
|
||||
Type::GenericAlias(alias) => builder.into_diagnostic(format_args!(
|
||||
"Class `{}` has no attribute `{}`",
|
||||
alias.display(db),
|
||||
&attr.id
|
||||
)),
|
||||
Type::FunctionLiteral(function) => builder.into_diagnostic(format_args!(
|
||||
"Function `{}` has no attribute `{}`",
|
||||
function.name(db),
|
||||
&attr.id
|
||||
)),
|
||||
_ => builder.into_diagnostic(format_args!(
|
||||
"Object of type `{}` has no attribute `{}`",
|
||||
value_type.display(db),
|
||||
&attr.id
|
||||
)),
|
||||
};
|
||||
hint_if_stdlib_attribute_exists_on_other_versions(db, diagnostic, &value_type, attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue