[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_db::source::source_text;
use ruff_python_ast::name::Name; use ruff_python_ast::name::Name;
use ruff_python_ast::parenthesize::parentheses_iterator; 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_python_trivia::CommentRanges;
use ruff_text_size::{Ranged, TextRange}; use ruff_text_size::{Ranged, TextRange};
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
@ -3427,7 +3427,7 @@ pub(super) fn hint_if_stdlib_attribute_exists_on_other_versions(
db: &dyn Db, db: &dyn Db,
mut diagnostic: LintDiagnosticGuard, mut diagnostic: LintDiagnosticGuard,
value_type: &Type, value_type: &Type,
attr: &Identifier, attr: &str,
) { ) {
// Currently we limit this analysis to attributes of stdlib modules, // Currently we limit this analysis to attributes of stdlib modules,
// as this covers the most important cases while not being too noisy // 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( add_inferred_python_version_hint_to_diagnostic(
db, db,
&mut diagnostic, &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); 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 // Exclude non-definitely-bound places for purposes of reachability
// analysis. We currently do not perform boundness analysis for implicit // analysis. We currently do not perform boundness analysis for implicit
// instance attributes, so we exclude them here as well. // instance attributes, so we exclude them here as well.
@ -9024,91 +9024,98 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
self.all_definitely_bound = false; self.all_definitely_bound = false;
} }
let resolved_type = fallback_place = fallback_place.map_type(|ty| {
fallback_place.map_type(|ty| {
self.narrow_expr_with_applicable_constraints(attribute, ty, &constraint_keys) 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);
if report_unresolved_attribute { let attr_name = &attr.id;
let bound_on_instance = match value_type {
Type::ClassLiteral(class) => {
!class.instance_member(db, None, attr).is_undefined()
}
Type::SubclassOf(subclass_of @ SubclassOfType { .. }) => {
match subclass_of.subclass_of() {
SubclassOfInner::Class(class) => {
!class.instance_member(db, attr).is_undefined()
}
SubclassOfInner::Dynamic(_) => unreachable!(
"Attribute lookup on a dynamic `SubclassOf` type should always return a bound symbol"
),
}
}
_ => false,
};
if let Some(builder) = self let resolved_type = fallback_place.unwrap_with_diagnostic(|lookup_err| match lookup_err {
.context LookupError::Undefined(_) => {
.report_lint(&UNRESOLVED_ATTRIBUTE, attribute) let fallback = || {
{ TypeAndQualifiers::new(
if bound_on_instance { Type::unknown(),
builder.into_diagnostic( TypeOrigin::Inferred,
format_args!( TypeQualifiers::empty(),
"Attribute `{}` can only be accessed on instances, \ )
not on the class object `{}` itself.", };
attr.id,
value_type.display(db) if !self.is_reachable(attribute) {
), return fallback();
); }
} else {
let diagnostic = match value_type { let bound_on_instance = match value_type {
Type::ModuleLiteral(module) => builder.into_diagnostic(format_args!( Type::ClassLiteral(class) => {
"Module `{}` has no member `{}`", !class.instance_member(db, None, attr).is_undefined()
module.module(db).name(db), }
&attr.id Type::SubclassOf(subclass_of @ SubclassOfType { .. }) => {
)), match subclass_of.subclass_of() {
Type::ClassLiteral(class) => builder.into_diagnostic(format_args!( SubclassOfInner::Class(class) => {
"Class `{}` has no attribute `{}`", !class.instance_member(db, attr).is_undefined()
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);
} }
SubclassOfInner::Dynamic(_) => unreachable!(
"Attribute lookup on a dynamic `SubclassOf` type \
should always return a bound symbol"
),
} }
} }
_ => false,
};
TypeAndQualifiers::new(Type::unknown(), TypeOrigin::Inferred, TypeQualifiers::empty()) let Some(builder) = self.context.report_lint(&UNRESOLVED_ATTRIBUTE, attribute)
} else {
LookupError::PossiblyUndefined(type_when_bound) => { return fallback();
report_possibly_missing_attribute( };
&self.context,
attribute,
&attr.id,
value_type,
);
type_when_bound 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();
} }
})
.inner_type(); 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);
type_when_bound
}
});
let resolved_type = resolved_type.inner_type();
self.check_deprecated(attr, resolved_type); self.check_deprecated(attr, resolved_type);