[ty] Reduce indentation of `TypeInferenceBuilder::infer_attribute_load` (#21560)

This commit is contained in:
Alex Waygood 2025-11-21 14:12:39 +00:00 committed by GitHub
parent 59c6cb521d
commit 762c44527e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 87 additions and 80 deletions

View File

@ -32,7 +32,7 @@ use ruff_db::diagnostic::{Annotation, Diagnostic, Span, SubDiagnostic, SubDiagno
use ruff_db::source::source_text;
use ruff_python_ast::name::Name;
use ruff_python_ast::parenthesize::parentheses_iterator;
use ruff_python_ast::{self as ast, AnyNodeRef, Identifier};
use ruff_python_ast::{self as ast, AnyNodeRef};
use ruff_python_trivia::CommentRanges;
use ruff_text_size::{Ranged, TextRange};
use rustc_hash::FxHashSet;
@ -3427,7 +3427,7 @@ pub(super) fn hint_if_stdlib_attribute_exists_on_other_versions(
db: &dyn Db,
mut diagnostic: LintDiagnosticGuard,
value_type: &Type,
attr: &Identifier,
attr: &str,
) {
// Currently we limit this analysis to attributes of stdlib modules,
// as this covers the most important cases while not being too noisy
@ -3461,6 +3461,6 @@ pub(super) fn hint_if_stdlib_attribute_exists_on_other_versions(
add_inferred_python_version_hint_to_diagnostic(
db,
&mut diagnostic,
&format!("accessing `{}`", attr.id),
&format!("accessing `{attr}`"),
);
}

View File

@ -9012,7 +9012,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
assigned_type = Some(ty);
}
}
let fallback_place = value_type.member(db, &attr.id);
let mut fallback_place = value_type.member(db, &attr.id);
// Exclude non-definitely-bound places for purposes of reachability
// analysis. We currently do not perform boundness analysis for implicit
// instance attributes, so we exclude them here as well.
@ -9024,14 +9024,26 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
self.all_definitely_bound = false;
}
let resolved_type =
fallback_place.map_type(|ty| {
fallback_place = fallback_place.map_type(|ty| {
self.narrow_expr_with_applicable_constraints(attribute, ty, &constraint_keys)
}).unwrap_with_diagnostic(|lookup_error| match lookup_error {
LookupError::Undefined(_) => {
let report_unresolved_attribute = self.is_reachable(attribute);
});
let attr_name = &attr.id;
let resolved_type = fallback_place.unwrap_with_diagnostic(|lookup_err| match lookup_err {
LookupError::Undefined(_) => {
let fallback = || {
TypeAndQualifiers::new(
Type::unknown(),
TypeOrigin::Inferred,
TypeQualifiers::empty(),
)
};
if !self.is_reachable(attribute) {
return fallback();
}
if report_unresolved_attribute {
let bound_on_instance = match value_type {
Type::ClassLiteral(class) => {
!class.instance_member(db, None, attr).is_undefined()
@ -9042,73 +9054,68 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
!class.instance_member(db, attr).is_undefined()
}
SubclassOfInner::Dynamic(_) => unreachable!(
"Attribute lookup on a dynamic `SubclassOf` type should always return a bound symbol"
"Attribute lookup on a dynamic `SubclassOf` type \
should always return a bound symbol"
),
}
}
_ => false,
};
if let Some(builder) = self
.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 = 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
)),
let Some(builder) = self.context.report_lint(&UNRESOLVED_ATTRIBUTE, attribute)
else {
return fallback();
};
hint_if_stdlib_attribute_exists_on_other_versions(db, diagnostic, &value_type, attr);
}
}
if bound_on_instance {
builder.into_diagnostic(format_args!(
"Attribute `{attr_name}` can only be accessed on instances, \
not on the class object `{}` itself.",
value_type.display(db)
));
return fallback();
}
TypeAndQualifiers::new(Type::unknown(), TypeOrigin::Inferred, TypeQualifiers::empty())
let diagnostic = match value_type {
Type::ModuleLiteral(module) => builder.into_diagnostic(format_args!(
"Module `{}` has no member `{attr_name}`",
module.module(db).name(db),
)),
Type::ClassLiteral(class) => builder.into_diagnostic(format_args!(
"Class `{}` has no attribute `{attr_name}`",
class.name(db),
)),
Type::GenericAlias(alias) => builder.into_diagnostic(format_args!(
"Class `{}` has no attribute `{attr_name}`",
alias.display(db),
)),
Type::FunctionLiteral(function) => builder.into_diagnostic(format_args!(
"Function `{}` has no attribute `{attr_name}`",
function.name(db),
)),
_ => builder.into_diagnostic(format_args!(
"Object of type `{}` has no attribute `{attr_name}`",
value_type.display(db),
)),
};
hint_if_stdlib_attribute_exists_on_other_versions(
db,
diagnostic,
&value_type,
attr_name,
);
fallback()
}
LookupError::PossiblyUndefined(type_when_bound) => {
report_possibly_missing_attribute(
&self.context,
attribute,
&attr.id,
value_type,
);
report_possibly_missing_attribute(&self.context, attribute, &attr.id, value_type);
type_when_bound
}
})
.inner_type();
});
let resolved_type = resolved_type.inner_type();
self.check_deprecated(attr, resolved_type);