[ty] Improve diagnostics when a submodule is not available as an attribute on a module-literal type (#21561)

This commit is contained in:
Alex Waygood
2025-11-22 14:07:48 +00:00
committed by GitHub
parent f2ce5e561a
commit 3410041b4c
7 changed files with 166 additions and 41 deletions

View File

@@ -364,10 +364,12 @@ pub(crate) struct ImportFromDefinitionNodeRef<'ast> {
pub(crate) alias_index: usize,
pub(crate) is_reexported: bool,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct ImportFromSubmoduleDefinitionNodeRef<'ast> {
pub(crate) node: &'ast ast::StmtImportFrom,
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct AssignmentDefinitionNodeRef<'ast, 'db> {
pub(crate) unpack: Option<(UnpackPosition, Unpack<'db>)>,

View File

@@ -9082,10 +9082,30 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
}
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::ModuleLiteral(module) => {
let module = module.module(db);
let module_name = module.name(db);
if module.kind(db).is_package()
&& let Some(relative_submodule) = ModuleName::new(attr_name)
{
let mut maybe_submodule_name = module_name.clone();
maybe_submodule_name.extend(&relative_submodule);
if resolve_module(db, &maybe_submodule_name).is_some() {
let mut diag = builder.into_diagnostic(format_args!(
"Submodule `{attr_name}` may not be available as an attribute \
on module `{module_name}`"
));
diag.help(format_args!(
"Consider explicitly importing `{maybe_submodule_name}`"
));
return fallback();
}
}
builder.into_diagnostic(format_args!(
"Module `{module_name}` has no member `{attr_name}`",
))
}
Type::ClassLiteral(class) => builder.into_diagnostic(format_args!(
"Class `{}` has no attribute `{attr_name}`",
class.name(db),