[red-knot] fix unresolvable import range (#15976)

This causes the diagnostic to highlight the actual unresovable import
instead of the entire `from ... import ...` statement.

While we're here, we expand the test coverage to cover all of the
possible ways that an `import` or a `from ... import` can fail.

Some considerations:

* The first commit in this PR adds a regression test for the current
behavior.
* This creates a new `mdtest/diagnostics` directory. Are folks cool
with this? I guess the idea is to put tests more devoted to diagnostics
than semantics in this directory. (Although I'm guessing there will
be some overlap.)

Fixes #15866
This commit is contained in:
Andrew Gallant 2025-02-05 14:01:58 -05:00 committed by GitHub
parent 1f0ad675d3
commit d47088c8f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 305 additions and 5 deletions

View file

@ -2538,6 +2538,12 @@ impl<'db> TypeInferenceBuilder<'db> {
// - Absolute `*` imports (`from collections import *`)
// - Relative `*` imports (`from ...foo import *`)
let ast::StmtImportFrom { module, level, .. } = import_from;
// For diagnostics, we want to highlight the unresolvable
// module and not the entire `from ... import ...` statement.
let module_ref = module
.as_ref()
.map(AnyNodeRef::from)
.unwrap_or_else(|| AnyNodeRef::from(import_from));
let module = module.as_deref();
let module_name = if let Some(level) = NonZeroU32::new(*level) {
@ -2572,7 +2578,7 @@ impl<'db> TypeInferenceBuilder<'db> {
"Relative module resolution `{}` failed: too many leading dots",
format_import_from_module(*level, module),
);
report_unresolved_module(&self.context, import_from, *level, module);
report_unresolved_module(&self.context, module_ref, *level, module);
self.add_unknown_declaration_with_binding(alias.into(), definition);
return;
}
@ -2582,14 +2588,14 @@ impl<'db> TypeInferenceBuilder<'db> {
format_import_from_module(*level, module),
self.file().path(self.db())
);
report_unresolved_module(&self.context, import_from, *level, module);
report_unresolved_module(&self.context, module_ref, *level, module);
self.add_unknown_declaration_with_binding(alias.into(), definition);
return;
}
};
let Some(module_ty) = self.module_type_from_name(&module_name) else {
report_unresolved_module(&self.context, import_from, *level, module);
report_unresolved_module(&self.context, module_ref, *level, module);
self.add_unknown_declaration_with_binding(alias.into(), definition);
return;
};