[ty] Support import <namespace> and from <namespace> import module (#18137)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run

This commit is contained in:
Micha Reiser 2025-05-21 09:28:33 +02:00 committed by GitHub
parent 7b253100f8
commit 76ab77fe01
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 359 additions and 158 deletions

View file

@ -19,19 +19,20 @@ impl<'a> Resolver<'a> {
pub(crate) fn resolve(&self, import: CollectedImport) -> Option<&'a FilePath> { pub(crate) fn resolve(&self, import: CollectedImport) -> Option<&'a FilePath> {
match import { match import {
CollectedImport::Import(import) => { CollectedImport::Import(import) => {
resolve_module(self.db, &import).map(|module| module.file().path(self.db)) let module = resolve_module(self.db, &import)?;
Some(module.file()?.path(self.db))
} }
CollectedImport::ImportFrom(import) => { CollectedImport::ImportFrom(import) => {
// Attempt to resolve the member (e.g., given `from foo import bar`, look for `foo.bar`). // Attempt to resolve the member (e.g., given `from foo import bar`, look for `foo.bar`).
let parent = import.parent(); let parent = import.parent();
resolve_module(self.db, &import) let module = resolve_module(self.db, &import).or_else(|| {
.map(|module| module.file().path(self.db))
.or_else(|| {
// Attempt to resolve the module (e.g., given `from foo import bar`, look for `foo`). // Attempt to resolve the module (e.g., given `from foo import bar`, look for `foo`).
resolve_module(self.db, &parent?).map(|module| module.file().path(self.db)) resolve_module(self.db, &parent?)
}) })?;
Some(module.file()?.path(self.db))
} }
} }
} }

View file

@ -1444,13 +1444,11 @@ mod unix {
) )
.expect("Expected bar.baz to exist in site-packages."); .expect("Expected bar.baz to exist in site-packages.");
let baz_project = case.project_path("bar/baz.py"); let baz_project = case.project_path("bar/baz.py");
let baz_file = baz.file().unwrap();
assert_eq!(source_text(case.db(), baz_file).as_str(), "def baz(): ...");
assert_eq!( assert_eq!(
source_text(case.db(), baz.file()).as_str(), baz_file.path(case.db()).as_system_path(),
"def baz(): ..."
);
assert_eq!(
baz.file().path(case.db()).as_system_path(),
Some(&*baz_project) Some(&*baz_project)
); );
@ -1465,7 +1463,7 @@ mod unix {
case.apply_changes(changes); case.apply_changes(changes);
assert_eq!( assert_eq!(
source_text(case.db(), baz.file()).as_str(), source_text(case.db(), baz_file).as_str(),
"def baz(): print('Version 2')" "def baz(): print('Version 2')"
); );
@ -1478,7 +1476,7 @@ mod unix {
case.apply_changes(changes); case.apply_changes(changes);
assert_eq!( assert_eq!(
source_text(case.db(), baz.file()).as_str(), source_text(case.db(), baz_file).as_str(),
"def baz(): print('Version 3')" "def baz(): print('Version 3')"
); );
@ -1524,6 +1522,7 @@ mod unix {
&ModuleName::new_static("bar.baz").unwrap(), &ModuleName::new_static("bar.baz").unwrap(),
) )
.expect("Expected bar.baz to exist in site-packages."); .expect("Expected bar.baz to exist in site-packages.");
let baz_file = baz.file().unwrap();
let bar_baz = case.project_path("bar/baz.py"); let bar_baz = case.project_path("bar/baz.py");
let patched_bar_baz = case.project_path("patched/bar/baz.py"); let patched_bar_baz = case.project_path("patched/bar/baz.py");
@ -1534,11 +1533,8 @@ mod unix {
"def baz(): ..." "def baz(): ..."
); );
assert_eq!( assert_eq!(source_text(case.db(), baz_file).as_str(), "def baz(): ...");
source_text(case.db(), baz.file()).as_str(), assert_eq!(baz_file.path(case.db()).as_system_path(), Some(&*bar_baz));
"def baz(): ..."
);
assert_eq!(baz.file().path(case.db()).as_system_path(), Some(&*bar_baz));
case.assert_indexed_project_files([patched_bar_baz_file]); case.assert_indexed_project_files([patched_bar_baz_file]);
@ -1567,7 +1563,7 @@ mod unix {
let patched_baz_text = source_text(case.db(), patched_bar_baz_file); let patched_baz_text = source_text(case.db(), patched_bar_baz_file);
let did_update_patched_baz = patched_baz_text.as_str() == "def baz(): print('Version 2')"; let did_update_patched_baz = patched_baz_text.as_str() == "def baz(): print('Version 2')";
let bar_baz_text = source_text(case.db(), baz.file()); let bar_baz_text = source_text(case.db(), baz_file);
let did_update_bar_baz = bar_baz_text.as_str() == "def baz(): print('Version 2')"; let did_update_bar_baz = bar_baz_text.as_str() == "def baz(): print('Version 2')";
assert!( assert!(
@ -1650,7 +1646,7 @@ mod unix {
"def baz(): ..." "def baz(): ..."
); );
assert_eq!( assert_eq!(
baz.file().path(case.db()).as_system_path(), baz.file().unwrap().path(case.db()).as_system_path(),
Some(&*baz_original) Some(&*baz_original)
); );

View file

@ -187,7 +187,10 @@ impl HasNavigationTargets for Type<'_> {
impl HasNavigationTargets for TypeDefinition<'_> { impl HasNavigationTargets for TypeDefinition<'_> {
fn navigation_targets(&self, db: &dyn Db) -> NavigationTargets { fn navigation_targets(&self, db: &dyn Db) -> NavigationTargets {
let full_range = self.full_range(db.upcast()); let Some(full_range) = self.full_range(db.upcast()) else {
return NavigationTargets::empty();
};
NavigationTargets::single(NavigationTarget { NavigationTargets::single(NavigationTarget {
file: full_range.file(), file: full_range.file(),
focus_range: self.focus_range(db.upcast()).unwrap_or(full_range).range(), focus_range: self.focus_range(db.upcast()).unwrap_or(full_range).range(),

View file

@ -29,8 +29,10 @@ import parent.child.two
`from.py` `from.py`
```py ```py
# TODO: This should not be an error from parent.child import one, two
from parent.child import one, two # error: [unresolved-import]
reveal_type(one) # revealed: <module 'parent.child.one'>
reveal_type(two) # revealed: <module 'parent.child.two'>
``` ```
## Regular package in namespace package ## Regular package in namespace package
@ -105,3 +107,42 @@ reveal_type(x) # revealed: Unknown | Literal["module"]
import foo.bar # error: [unresolved-import] import foo.bar # error: [unresolved-import]
``` ```
## `from` import with namespace package
Regression test for <https://github.com/astral-sh/ty/issues/363>
`google/cloud/pubsub_v1/__init__.py`:
```py
class PublisherClient: ...
```
```py
from google.cloud import pubsub_v1
reveal_type(pubsub_v1.PublisherClient) # revealed: <class 'PublisherClient'>
```
## `from` root importing sub-packages
Regresssion test for <https://github.com/astral-sh/ty/issues/375>
`opentelemetry/trace/__init__.py`:
```py
class Trace: ...
```
`opentelemetry/metrics/__init__.py`:
```py
class Metric: ...
```
```py
from opentelemetry import trace, metrics
reveal_type(trace) # revealed: <module 'opentelemetry.trace'>
reveal_type(metrics) # revealed: <module 'opentelemetry.metrics'>
```

View file

@ -109,8 +109,10 @@ impl<'db> DunderAllNamesCollector<'db> {
else { else {
return false; return false;
}; };
let Some(module_dunder_all_names) = let Some(module_dunder_all_names) = module_literal
dunder_all_names(self.db, module_literal.module(self.db).file()) .module(self.db)
.file()
.and_then(|file| dunder_all_names(self.db, file))
else { else {
// The module either does not have a `__all__` variable or it is invalid. // The module either does not have a `__all__` variable or it is invalid.
return false; return false;
@ -179,7 +181,7 @@ impl<'db> DunderAllNamesCollector<'db> {
let module_name = let module_name =
ModuleName::from_import_statement(self.db, self.file, import_from).ok()?; ModuleName::from_import_statement(self.db, self.file, import_from).ok()?;
let module = resolve_module(self.db, &module_name)?; let module = resolve_module(self.db, &module_name)?;
dunder_all_names(self.db, module.file()) dunder_all_names(self.db, module.file()?)
} }
/// Infer the type of a standalone expression. /// Infer the type of a standalone expression.

View file

@ -14,15 +14,16 @@ pub struct Module {
} }
impl Module { impl Module {
pub(crate) fn new( pub(crate) fn file_module(
name: ModuleName, name: ModuleName,
kind: ModuleKind, kind: ModuleKind,
search_path: SearchPath, search_path: SearchPath,
file: File, file: File,
) -> Self { ) -> Self {
let known = KnownModule::try_from_search_path_and_name(&search_path, &name); let known = KnownModule::try_from_search_path_and_name(&search_path, &name);
Self { Self {
inner: Arc::new(ModuleInner { inner: Arc::new(ModuleInner::FileModule {
name, name,
kind, kind,
search_path, search_path,
@ -32,19 +33,36 @@ impl Module {
} }
} }
pub(crate) fn namespace_package(name: ModuleName) -> Self {
Self {
inner: Arc::new(ModuleInner::NamespacePackage { name }),
}
}
/// The absolute name of the module (e.g. `foo.bar`) /// The absolute name of the module (e.g. `foo.bar`)
pub fn name(&self) -> &ModuleName { pub fn name(&self) -> &ModuleName {
&self.inner.name match &*self.inner {
ModuleInner::FileModule { name, .. } => name,
ModuleInner::NamespacePackage { name, .. } => name,
}
} }
/// The file to the source code that defines this module /// The file to the source code that defines this module
pub fn file(&self) -> File { ///
self.inner.file /// This is `None` for namespace packages.
pub fn file(&self) -> Option<File> {
match &*self.inner {
ModuleInner::FileModule { file, .. } => Some(*file),
ModuleInner::NamespacePackage { .. } => None,
}
} }
/// Is this a module that we special-case somehow? If so, which one? /// Is this a module that we special-case somehow? If so, which one?
pub fn known(&self) -> Option<KnownModule> { pub fn known(&self) -> Option<KnownModule> {
self.inner.known match &*self.inner {
ModuleInner::FileModule { known, .. } => *known,
ModuleInner::NamespacePackage { .. } => None,
}
} }
/// Does this module represent the given known module? /// Does this module represent the given known module?
@ -53,13 +71,19 @@ impl Module {
} }
/// The search path from which the module was resolved. /// The search path from which the module was resolved.
pub(crate) fn search_path(&self) -> &SearchPath { pub(crate) fn search_path(&self) -> Option<&SearchPath> {
&self.inner.search_path match &*self.inner {
ModuleInner::FileModule { search_path, .. } => Some(search_path),
ModuleInner::NamespacePackage { .. } => None,
}
} }
/// Determine whether this module is a single-file module or a package /// Determine whether this module is a single-file module or a package
pub fn kind(&self) -> ModuleKind { pub fn kind(&self) -> ModuleKind {
self.inner.kind match &*self.inner {
ModuleInner::FileModule { kind, .. } => *kind,
ModuleInner::NamespacePackage { .. } => ModuleKind::Package,
}
} }
} }
@ -70,17 +94,26 @@ impl std::fmt::Debug for Module {
.field("kind", &self.kind()) .field("kind", &self.kind())
.field("file", &self.file()) .field("file", &self.file())
.field("search_path", &self.search_path()) .field("search_path", &self.search_path())
.field("known", &self.known())
.finish() .finish()
} }
} }
#[derive(PartialEq, Eq, Hash)] #[derive(PartialEq, Eq, Hash)]
struct ModuleInner { enum ModuleInner {
/// A module that resolves to a file (`lib.py` or `package/__init__.py`)
FileModule {
name: ModuleName, name: ModuleName,
kind: ModuleKind, kind: ModuleKind,
search_path: SearchPath, search_path: SearchPath,
file: File, file: File,
known: Option<KnownModule>, known: Option<KnownModule>,
},
/// A namespace package. Namespace packages are special because
/// there are multiple possible paths and they have no corresponding
/// code file.
NamespacePackage { name: ModuleName },
} }
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]

View file

@ -143,6 +143,23 @@ impl ModulePath {
} }
} }
pub(super) fn to_system_path(&self) -> Option<SystemPathBuf> {
let ModulePath {
search_path,
relative_path,
} = self;
match &*search_path.0 {
SearchPathInner::Extra(search_path)
| SearchPathInner::FirstParty(search_path)
| SearchPathInner::SitePackages(search_path)
| SearchPathInner::Editable(search_path) => Some(search_path.join(relative_path)),
SearchPathInner::StandardLibraryCustom(stdlib_root) => {
Some(stdlib_root.join(relative_path))
}
SearchPathInner::StandardLibraryVendored(_) => None,
}
}
#[must_use] #[must_use]
pub(super) fn to_file(&self, resolver: &ResolverContext) -> Option<File> { pub(super) fn to_file(&self, resolver: &ResolverContext) -> Option<File> {
let db = resolver.db.upcast(); let db = resolver.db.upcast();

View file

@ -39,22 +39,24 @@ pub(crate) fn resolve_module_query<'db>(
let name = module_name.name(db); let name = module_name.name(db);
let _span = tracing::trace_span!("resolve_module", %name).entered(); let _span = tracing::trace_span!("resolve_module", %name).entered();
let Some((search_path, resolved_module)) = resolve_name(db, name) else { let Some(resolved) = resolve_name(db, name) else {
tracing::debug!("Module `{name}` not found in search paths"); tracing::debug!("Module `{name}` not found in search paths");
return None; return None;
}; };
let module = match resolved {
ResolvedName::FileModule(module) => {
tracing::trace!( tracing::trace!(
"Resolved module `{name}` to `{path}`", "Resolved module `{name}` to `{path}`",
path = resolved_module.file.path(db) path = module.file.path(db)
);
let module = Module::new(
name.clone(),
resolved_module.kind,
search_path,
resolved_module.file,
); );
Module::file_module(name.clone(), module.kind, module.search_path, module.file)
}
ResolvedName::NamespacePackage => {
tracing::trace!("Module `{name}` is a namespace package");
Module::namespace_package(name.clone())
}
};
Some(module) Some(module)
} }
@ -117,8 +119,9 @@ pub(crate) fn file_to_module(db: &dyn Db, file: File) -> Option<Module> {
// root paths, but that the module corresponding to `path` is in a lower priority search path, // root paths, but that the module corresponding to `path` is in a lower priority search path,
// in which case we ignore it. // in which case we ignore it.
let module = resolve_module(db, &module_name)?; let module = resolve_module(db, &module_name)?;
let module_file = module.file()?;
if file.path(db) == module.file().path(db) { if file.path(db) == module_file.path(db) {
Some(module) Some(module)
} else { } else {
// This path is for a module with the same name but with a different precedence. For example: // This path is for a module with the same name but with a different precedence. For example:
@ -616,7 +619,7 @@ struct ModuleNameIngredient<'db> {
/// Given a module name and a list of search paths in which to lookup modules, /// Given a module name and a list of search paths in which to lookup modules,
/// attempt to resolve the module name /// attempt to resolve the module name
fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<(SearchPath, ResolvedModule)> { fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<ResolvedName> {
let program = Program::get(db); let program = Program::get(db);
let python_version = program.python_version(db); let python_version = program.python_version(db);
let resolver_state = ResolverContext::new(db, python_version); let resolver_state = ResolverContext::new(db, python_version);
@ -625,6 +628,7 @@ fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<(SearchPath, ResolvedM
let name = RelaxedModuleName::new(name); let name = RelaxedModuleName::new(name);
let stub_name = name.to_stub_package(); let stub_name = name.to_stub_package();
let mut is_namespace_package = false;
for search_path in search_paths(db) { for search_path in search_paths(db) {
// When a builtin module is imported, standard module resolution is bypassed: // When a builtin module is imported, standard module resolution is bypassed:
@ -636,16 +640,19 @@ fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<(SearchPath, ResolvedM
} }
if !search_path.is_standard_library() { if !search_path.is_standard_library() {
match resolve_module_in_search_path(&resolver_state, &stub_name, search_path) { match resolve_name_in_search_path(&resolver_state, &stub_name, search_path) {
Ok(resolved_module) => { Ok((package_kind, ResolvedName::FileModule(module))) => {
if resolved_module.package_kind.is_root() && resolved_module.kind.is_module() { if package_kind.is_root() && module.kind.is_module() {
tracing::trace!( tracing::trace!(
"Search path '{search_path} contains a module named `{stub_name}` but a standalone module isn't a valid stub." "Search path '{search_path} contains a module named `{stub_name}` but a standalone module isn't a valid stub."
); );
} else { } else {
return Some((search_path.clone(), resolved_module)); return Some(ResolvedName::FileModule(module));
} }
} }
Ok((_, ResolvedName::NamespacePackage)) => {
is_namespace_package = true;
}
Err(PackageKind::Root) => { Err(PackageKind::Root) => {
tracing::trace!( tracing::trace!(
"Search path '{search_path}' contains no stub package named `{stub_name}`." "Search path '{search_path}' contains no stub package named `{stub_name}`."
@ -670,8 +677,13 @@ fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<(SearchPath, ResolvedM
} }
} }
match resolve_module_in_search_path(&resolver_state, &name, search_path) { match resolve_name_in_search_path(&resolver_state, &name, search_path) {
Ok(resolved_module) => return Some((search_path.clone(), resolved_module)), Ok((_, ResolvedName::FileModule(module))) => {
return Some(ResolvedName::FileModule(module));
}
Ok((_, ResolvedName::NamespacePackage)) => {
is_namespace_package = true;
}
Err(kind) => match kind { Err(kind) => match kind {
PackageKind::Root => { PackageKind::Root => {
tracing::trace!( tracing::trace!(
@ -693,21 +705,36 @@ fn resolve_name(db: &dyn Db, name: &ModuleName) -> Option<(SearchPath, ResolvedM
} }
} }
if is_namespace_package {
return Some(ResolvedName::NamespacePackage);
}
None None
} }
#[derive(Debug)] #[derive(Debug)]
struct ResolvedModule { enum ResolvedName {
/// A module that resolves to a file.
FileModule(ResolvedFileModule),
/// The module name resolved to a namespace package.
///
/// For example, `from opentelemetry import trace, metrics` where `opentelemetry` is a namespace package (and `trace` and `metrics` are sub packages).
NamespacePackage,
}
#[derive(Debug)]
struct ResolvedFileModule {
kind: ModuleKind, kind: ModuleKind,
package_kind: PackageKind, search_path: SearchPath,
file: File, file: File,
} }
fn resolve_module_in_search_path( fn resolve_name_in_search_path(
context: &ResolverContext, context: &ResolverContext,
name: &RelaxedModuleName, name: &RelaxedModuleName,
search_path: &SearchPath, search_path: &SearchPath,
) -> Result<ResolvedModule, PackageKind> { ) -> Result<(PackageKind, ResolvedName), PackageKind> {
let mut components = name.components(); let mut components = name.components();
let module_name = components.next_back().unwrap(); let module_name = components.next_back().unwrap();
@ -720,22 +747,55 @@ fn resolve_module_in_search_path(
// Check for a regular package first (highest priority) // Check for a regular package first (highest priority)
package_path.push("__init__"); package_path.push("__init__");
if let Some(regular_package) = resolve_file_module(&package_path, context) { if let Some(regular_package) = resolve_file_module(&package_path, context) {
return Ok(ResolvedModule { return Ok((
file: regular_package, resolved_package.kind,
ResolvedName::FileModule(ResolvedFileModule {
search_path: search_path.clone(),
kind: ModuleKind::Package, kind: ModuleKind::Package,
package_kind: resolved_package.kind, file: regular_package,
}); }),
));
} }
// Check for a file module next // Check for a file module next
package_path.pop(); package_path.pop();
if let Some(file_module) = resolve_file_module(&package_path, context) { if let Some(file_module) = resolve_file_module(&package_path, context) {
return Ok(ResolvedModule { return Ok((
resolved_package.kind,
ResolvedName::FileModule(ResolvedFileModule {
file: file_module, file: file_module,
kind: ModuleKind::Module, kind: ModuleKind::Module,
package_kind: resolved_package.kind, search_path: search_path.clone(),
}); }),
));
}
// Last resort, check if a folder with the given name exists.
// If so, then this is a namespace package.
// We need to skip this check for typeshed because the `resolve_file_module` can also return `None`
// if the `__init__.py` exists but isn't available for the current Python version.
// Let's assume that the `xml` module is only available on Python 3.11+ and we're resolving for Python 3.10:
// * `resolve_file_module("xml/__init__.pyi")` returns `None` even though the file exists but the
// module isn't available for the current Python version.
// * The check here would now return `true` because the `xml` directory exists, resulting
// in a false positive for a namespace package.
//
// Since typeshed doesn't use any namespace packages today (May 2025), simply skip this
// check which also helps performance. If typeshed ever uses namespace packages, ensure that
// this check also takes the `VERSIONS` file into consideration.
if !search_path.is_standard_library() && package_path.is_directory(context) {
if let Some(path) = package_path.to_system_path() {
let system = context.db.system();
if system.case_sensitivity().is_case_sensitive()
|| system.path_exists_case_sensitive(
&path,
package_path.search_path().as_system_path().unwrap(),
)
{
return Ok((resolved_package.kind, ResolvedName::NamespacePackage));
}
}
} }
Err(resolved_package.kind) Err(resolved_package.kind)
@ -937,11 +997,11 @@ mod tests {
); );
assert_eq!("foo", foo_module.name()); assert_eq!("foo", foo_module.name());
assert_eq!(&src, foo_module.search_path()); assert_eq!(&src, foo_module.search_path().unwrap());
assert_eq!(ModuleKind::Module, foo_module.kind()); assert_eq!(ModuleKind::Module, foo_module.kind());
let expected_foo_path = src.join("foo.py"); let expected_foo_path = src.join("foo.py");
assert_eq!(&expected_foo_path, foo_module.file().path(&db)); assert_eq!(&expected_foo_path, foo_module.file().unwrap().path(&db));
assert_eq!( assert_eq!(
Some(foo_module), Some(foo_module),
path_to_module(&db, &FilePath::System(expected_foo_path)) path_to_module(&db, &FilePath::System(expected_foo_path))
@ -958,7 +1018,10 @@ mod tests {
let builtins_module_name = ModuleName::new_static("builtins").unwrap(); let builtins_module_name = ModuleName::new_static("builtins").unwrap();
let builtins = resolve_module(&db, &builtins_module_name).expect("builtins to resolve"); let builtins = resolve_module(&db, &builtins_module_name).expect("builtins to resolve");
assert_eq!(builtins.file().path(&db), &stdlib.join("builtins.pyi")); assert_eq!(
builtins.file().unwrap().path(&db),
&stdlib.join("builtins.pyi")
);
} }
#[test] #[test]
@ -979,7 +1042,10 @@ mod tests {
let builtins_module_name = ModuleName::new_static("builtins").unwrap(); let builtins_module_name = ModuleName::new_static("builtins").unwrap();
let builtins = resolve_module(&db, &builtins_module_name).expect("builtins to resolve"); let builtins = resolve_module(&db, &builtins_module_name).expect("builtins to resolve");
assert_eq!(builtins.file().path(&db), &stdlib.join("builtins.pyi")); assert_eq!(
builtins.file().unwrap().path(&db),
&stdlib.join("builtins.pyi")
);
} }
#[test] #[test]
@ -1002,11 +1068,14 @@ mod tests {
resolve_module(&db, &functools_module_name).as_ref() resolve_module(&db, &functools_module_name).as_ref()
); );
assert_eq!(&stdlib, functools_module.search_path()); assert_eq!(&stdlib, functools_module.search_path().unwrap());
assert_eq!(ModuleKind::Module, functools_module.kind()); assert_eq!(ModuleKind::Module, functools_module.kind());
let expected_functools_path = stdlib.join("functools.pyi"); let expected_functools_path = stdlib.join("functools.pyi");
assert_eq!(&expected_functools_path, functools_module.file().path(&db)); assert_eq!(
&expected_functools_path,
functools_module.file().unwrap().path(&db)
);
assert_eq!( assert_eq!(
Some(functools_module), Some(functools_module),
@ -1052,7 +1121,7 @@ mod tests {
let resolved_module = resolve_module(&db, &module_name).unwrap_or_else(|| { let resolved_module = resolve_module(&db, &module_name).unwrap_or_else(|| {
panic!("Expected module {module_name} to exist in the mock stdlib") panic!("Expected module {module_name} to exist in the mock stdlib")
}); });
let search_path = resolved_module.search_path(); let search_path = resolved_module.search_path().unwrap();
assert_eq!( assert_eq!(
&stdlib, search_path, &stdlib, search_path,
"Search path for {module_name} was unexpectedly {search_path:?}" "Search path for {module_name} was unexpectedly {search_path:?}"
@ -1148,7 +1217,7 @@ mod tests {
let resolved_module = resolve_module(&db, &module_name).unwrap_or_else(|| { let resolved_module = resolve_module(&db, &module_name).unwrap_or_else(|| {
panic!("Expected module {module_name} to exist in the mock stdlib") panic!("Expected module {module_name} to exist in the mock stdlib")
}); });
let search_path = resolved_module.search_path(); let search_path = resolved_module.search_path().unwrap();
assert_eq!( assert_eq!(
&stdlib, search_path, &stdlib, search_path,
"Search path for {module_name} was unexpectedly {search_path:?}" "Search path for {module_name} was unexpectedly {search_path:?}"
@ -1209,9 +1278,12 @@ mod tests {
Some(&functools_module), Some(&functools_module),
resolve_module(&db, &functools_module_name).as_ref() resolve_module(&db, &functools_module_name).as_ref()
); );
assert_eq!(&src, functools_module.search_path()); assert_eq!(&src, functools_module.search_path().unwrap());
assert_eq!(ModuleKind::Module, functools_module.kind()); assert_eq!(ModuleKind::Module, functools_module.kind());
assert_eq!(&src.join("functools.py"), functools_module.file().path(&db)); assert_eq!(
&src.join("functools.py"),
functools_module.file().unwrap().path(&db)
);
assert_eq!( assert_eq!(
Some(functools_module), Some(functools_module),
@ -1230,9 +1302,9 @@ mod tests {
let pydoc_data_topics = resolve_module(&db, &pydoc_data_topics_name).unwrap(); let pydoc_data_topics = resolve_module(&db, &pydoc_data_topics_name).unwrap();
assert_eq!("pydoc_data.topics", pydoc_data_topics.name()); assert_eq!("pydoc_data.topics", pydoc_data_topics.name());
assert_eq!(pydoc_data_topics.search_path(), &stdlib); assert_eq!(pydoc_data_topics.search_path().unwrap(), &stdlib);
assert_eq!( assert_eq!(
pydoc_data_topics.file().path(&db), pydoc_data_topics.file().unwrap().path(&db),
&stdlib.join("pydoc_data/topics.pyi") &stdlib.join("pydoc_data/topics.pyi")
); );
} }
@ -1247,8 +1319,8 @@ mod tests {
let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap(); let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
assert_eq!("foo", foo_module.name()); assert_eq!("foo", foo_module.name());
assert_eq!(&src, foo_module.search_path()); assert_eq!(&src, foo_module.search_path().unwrap());
assert_eq!(&foo_path, foo_module.file().path(&db)); assert_eq!(&foo_path, foo_module.file().unwrap().path(&db));
assert_eq!( assert_eq!(
Some(&foo_module), Some(&foo_module),
@ -1274,8 +1346,8 @@ mod tests {
let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap(); let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let foo_init_path = src.join("foo/__init__.py"); let foo_init_path = src.join("foo/__init__.py");
assert_eq!(&src, foo_module.search_path()); assert_eq!(&src, foo_module.search_path().unwrap());
assert_eq!(&foo_init_path, foo_module.file().path(&db)); assert_eq!(&foo_init_path, foo_module.file().unwrap().path(&db));
assert_eq!(ModuleKind::Package, foo_module.kind()); assert_eq!(ModuleKind::Package, foo_module.kind());
assert_eq!( assert_eq!(
@ -1297,8 +1369,8 @@ mod tests {
let foo = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap(); let foo = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let foo_stub = src.join("foo.pyi"); let foo_stub = src.join("foo.pyi");
assert_eq!(&src, foo.search_path()); assert_eq!(&src, foo.search_path().unwrap());
assert_eq!(&foo_stub, foo.file().path(&db)); assert_eq!(&foo_stub, foo.file().unwrap().path(&db));
assert_eq!(Some(foo), path_to_module(&db, &FilePath::System(foo_stub))); assert_eq!(Some(foo), path_to_module(&db, &FilePath::System(foo_stub)));
assert_eq!( assert_eq!(
@ -1321,8 +1393,8 @@ mod tests {
resolve_module(&db, &ModuleName::new_static("foo.bar.baz").unwrap()).unwrap(); resolve_module(&db, &ModuleName::new_static("foo.bar.baz").unwrap()).unwrap();
let baz_path = src.join("foo/bar/baz.py"); let baz_path = src.join("foo/bar/baz.py");
assert_eq!(&src, baz_module.search_path()); assert_eq!(&src, baz_module.search_path().unwrap());
assert_eq!(&baz_path, baz_module.file().path(&db)); assert_eq!(&baz_path, baz_module.file().unwrap().path(&db));
assert_eq!( assert_eq!(
Some(baz_module), Some(baz_module),
@ -1345,8 +1417,8 @@ mod tests {
let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap(); let foo_module = resolve_module(&db, &ModuleName::new_static("foo").unwrap()).unwrap();
let foo_src_path = src.join("foo.py"); let foo_src_path = src.join("foo.py");
assert_eq!(&src, foo_module.search_path()); assert_eq!(&src, foo_module.search_path().unwrap());
assert_eq!(&foo_src_path, foo_module.file().path(&db)); assert_eq!(&foo_src_path, foo_module.file().unwrap().path(&db));
assert_eq!( assert_eq!(
Some(foo_module), Some(foo_module),
path_to_module(&db, &FilePath::System(foo_src_path)) path_to_module(&db, &FilePath::System(foo_src_path))
@ -1413,14 +1485,14 @@ mod tests {
assert_ne!(foo_module, bar_module); assert_ne!(foo_module, bar_module);
assert_eq!(&src, foo_module.search_path()); assert_eq!(&src, foo_module.search_path().unwrap());
assert_eq!(&foo, foo_module.file().path(&db)); assert_eq!(&foo, foo_module.file().unwrap().path(&db));
// `foo` and `bar` shouldn't resolve to the same file // `foo` and `bar` shouldn't resolve to the same file
assert_eq!(&src, bar_module.search_path()); assert_eq!(&src, bar_module.search_path().unwrap());
assert_eq!(&bar, bar_module.file().path(&db)); assert_eq!(&bar, bar_module.file().unwrap().path(&db));
assert_eq!(&foo, foo_module.file().path(&db)); assert_eq!(&foo, foo_module.file().unwrap().path(&db));
assert_ne!(&foo_module, &bar_module); assert_ne!(&foo_module, &bar_module);
@ -1484,7 +1556,7 @@ mod tests {
let foo_file = system_path_to_file(&db, &foo_path).expect("foo.py to exist"); let foo_file = system_path_to_file(&db, &foo_path).expect("foo.py to exist");
let foo_module = resolve_module(&db, &foo_module_name).expect("Foo module to resolve"); let foo_module = resolve_module(&db, &foo_module_name).expect("Foo module to resolve");
assert_eq!(foo_file, foo_module.file()); assert_eq!(foo_file, foo_module.file().unwrap());
Ok(()) Ok(())
} }
@ -1500,7 +1572,7 @@ mod tests {
let foo_module = resolve_module(&db, &foo_module_name).expect("foo module to exist"); let foo_module = resolve_module(&db, &foo_module_name).expect("foo module to exist");
let foo_init_path = src.join("foo/__init__.py"); let foo_init_path = src.join("foo/__init__.py");
assert_eq!(&foo_init_path, foo_module.file().path(&db)); assert_eq!(&foo_init_path, foo_module.file().unwrap().path(&db));
// Delete `foo/__init__.py` and the `foo` folder. `foo` should now resolve to `foo.py` // Delete `foo/__init__.py` and the `foo` folder. `foo` should now resolve to `foo.py`
db.memory_file_system().remove_file(&foo_init_path)?; db.memory_file_system().remove_file(&foo_init_path)?;
@ -1510,7 +1582,7 @@ mod tests {
File::sync_path(&mut db, foo_init_path.parent().unwrap()); File::sync_path(&mut db, foo_init_path.parent().unwrap());
let foo_module = resolve_module(&db, &foo_module_name).expect("Foo module to resolve"); let foo_module = resolve_module(&db, &foo_module_name).expect("Foo module to resolve");
assert_eq!(&src.join("foo.py"), foo_module.file().path(&db)); assert_eq!(&src.join("foo.py"), foo_module.file().unwrap().path(&db));
Ok(()) Ok(())
} }
@ -1536,9 +1608,9 @@ mod tests {
let stdlib_functools_path = stdlib.join("functools.pyi"); let stdlib_functools_path = stdlib.join("functools.pyi");
let functools_module = resolve_module(&db, &functools_module_name).unwrap(); let functools_module = resolve_module(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(), &stdlib); assert_eq!(functools_module.search_path().unwrap(), &stdlib);
assert_eq!( assert_eq!(
Ok(functools_module.file()), Ok(functools_module.file().unwrap()),
system_path_to_file(&db, &stdlib_functools_path) system_path_to_file(&db, &stdlib_functools_path)
); );
@ -1556,9 +1628,9 @@ mod tests {
ModuleNameIngredient::new(&db, functools_module_name), ModuleNameIngredient::new(&db, functools_module_name),
&events, &events,
); );
assert_eq!(functools_module.search_path(), &stdlib); assert_eq!(functools_module.search_path().unwrap(), &stdlib);
assert_eq!( assert_eq!(
Ok(functools_module.file()), Ok(functools_module.file().unwrap()),
system_path_to_file(&db, &stdlib_functools_path) system_path_to_file(&db, &stdlib_functools_path)
); );
} }
@ -1582,9 +1654,9 @@ mod tests {
let functools_module_name = ModuleName::new_static("functools").unwrap(); let functools_module_name = ModuleName::new_static("functools").unwrap();
let functools_module = resolve_module(&db, &functools_module_name).unwrap(); let functools_module = resolve_module(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(), &stdlib); assert_eq!(functools_module.search_path().unwrap(), &stdlib);
assert_eq!( assert_eq!(
Ok(functools_module.file()), Ok(functools_module.file().unwrap()),
system_path_to_file(&db, stdlib.join("functools.pyi")) system_path_to_file(&db, stdlib.join("functools.pyi"))
); );
@ -1593,9 +1665,9 @@ mod tests {
let src_functools_path = src.join("functools.py"); let src_functools_path = src.join("functools.py");
db.write_file(&src_functools_path, "FOO: int").unwrap(); db.write_file(&src_functools_path, "FOO: int").unwrap();
let functools_module = resolve_module(&db, &functools_module_name).unwrap(); let functools_module = resolve_module(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(), &src); assert_eq!(functools_module.search_path().unwrap(), &src);
assert_eq!( assert_eq!(
Ok(functools_module.file()), Ok(functools_module.file().unwrap()),
system_path_to_file(&db, &src_functools_path) system_path_to_file(&db, &src_functools_path)
); );
} }
@ -1624,9 +1696,9 @@ mod tests {
let src_functools_path = src.join("functools.py"); let src_functools_path = src.join("functools.py");
let functools_module = resolve_module(&db, &functools_module_name).unwrap(); let functools_module = resolve_module(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(), &src); assert_eq!(functools_module.search_path().unwrap(), &src);
assert_eq!( assert_eq!(
Ok(functools_module.file()), Ok(functools_module.file().unwrap()),
system_path_to_file(&db, &src_functools_path) system_path_to_file(&db, &src_functools_path)
); );
@ -1637,9 +1709,9 @@ mod tests {
.unwrap(); .unwrap();
File::sync_path(&mut db, &src_functools_path); File::sync_path(&mut db, &src_functools_path);
let functools_module = resolve_module(&db, &functools_module_name).unwrap(); let functools_module = resolve_module(&db, &functools_module_name).unwrap();
assert_eq!(functools_module.search_path(), &stdlib); assert_eq!(functools_module.search_path().unwrap(), &stdlib);
assert_eq!( assert_eq!(
Ok(functools_module.file()), Ok(functools_module.file().unwrap()),
system_path_to_file(&db, stdlib.join("functools.pyi")) system_path_to_file(&db, stdlib.join("functools.pyi"))
); );
} }
@ -1662,11 +1734,11 @@ mod tests {
let foo_bar_module = resolve_module(&db, &foo_bar_module_name).unwrap(); let foo_bar_module = resolve_module(&db, &foo_bar_module_name).unwrap();
assert_eq!( assert_eq!(
foo_module.file().path(&db), foo_module.file().unwrap().path(&db),
&FilePath::system("/x/src/foo/__init__.py") &FilePath::system("/x/src/foo/__init__.py")
); );
assert_eq!( assert_eq!(
foo_bar_module.file().path(&db), foo_bar_module.file().unwrap().path(&db),
&FilePath::system("/x/src/foo/bar.py") &FilePath::system("/x/src/foo/bar.py")
); );
} }
@ -1693,7 +1765,7 @@ mod tests {
let bar_module_name = ModuleName::new_static("bar").unwrap(); let bar_module_name = ModuleName::new_static("bar").unwrap();
let bar_module = resolve_module(&db, &bar_module_name).unwrap(); let bar_module = resolve_module(&db, &bar_module_name).unwrap();
assert_eq!( assert_eq!(
bar_module.file().path(&db), bar_module.file().unwrap().path(&db),
&FilePath::system("/y/src/bar.py") &FilePath::system("/y/src/bar.py")
); );
} }
@ -1713,7 +1785,7 @@ mod tests {
let foo_module = resolve_module(&db, &foo_module_name).unwrap(); let foo_module = resolve_module(&db, &foo_module_name).unwrap();
assert_eq!( assert_eq!(
foo_module.file().path(&db), foo_module.file().unwrap().path(&db),
&FilePath::system("/x/y/src/foo.pyi") &FilePath::system("/x/y/src/foo.pyi")
); );
} }
@ -1764,13 +1836,19 @@ not_a_directory
let spam_module = resolve_module(&db, &spam_module_name).unwrap(); let spam_module = resolve_module(&db, &spam_module_name).unwrap();
assert_eq!( assert_eq!(
foo_module.file().path(&db), foo_module.file().unwrap().path(&db),
&FilePath::system("/x/y/src/foo.pyi") &FilePath::system("/x/y/src/foo.pyi")
); );
assert_eq!(a_module.file().path(&db), &FilePath::system("/a.py"));
assert_eq!(b_module.file().path(&db), &FilePath::system("/baz/b.py"));
assert_eq!( assert_eq!(
spam_module.file().path(&db), a_module.file().unwrap().path(&db),
&FilePath::system("/a.py")
);
assert_eq!(
b_module.file().unwrap().path(&db),
&FilePath::system("/baz/b.py")
);
assert_eq!(
spam_module.file().unwrap().path(&db),
&FilePath::System(site_packages.join("spam/spam.py")) &FilePath::System(site_packages.join("spam/spam.py"))
); );
} }
@ -1791,14 +1869,14 @@ not_a_directory
let foo_module = resolve_module(&db, &foo_module_name).unwrap(); let foo_module = resolve_module(&db, &foo_module_name).unwrap();
assert_eq!( assert_eq!(
foo_module.file().path(&db), foo_module.file().unwrap().path(&db),
&FilePath::system("/x/src/foo.py") &FilePath::system("/x/src/foo.py")
); );
db.clear_salsa_events(); db.clear_salsa_events();
let bar_module = resolve_module(&db, &bar_module_name).unwrap(); let bar_module = resolve_module(&db, &bar_module_name).unwrap();
assert_eq!( assert_eq!(
bar_module.file().path(&db), bar_module.file().unwrap().path(&db),
&FilePath::system("/y/src/bar.py") &FilePath::system("/y/src/bar.py")
); );
let events = db.take_salsa_events(); let events = db.take_salsa_events();
@ -1823,7 +1901,7 @@ not_a_directory
let foo_module_name = ModuleName::new_static("foo").unwrap(); let foo_module_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, &foo_module_name).unwrap(); let foo_module = resolve_module(&db, &foo_module_name).unwrap();
assert_eq!( assert_eq!(
foo_module.file().path(&db), foo_module.file().unwrap().path(&db),
&FilePath::system("/x/src/foo.py") &FilePath::system("/x/src/foo.py")
); );
@ -1851,7 +1929,7 @@ not_a_directory
let foo_module = resolve_module(&db, &foo_module_name).unwrap(); let foo_module = resolve_module(&db, &foo_module_name).unwrap();
let src_path = SystemPathBuf::from("/x/src"); let src_path = SystemPathBuf::from("/x/src");
assert_eq!( assert_eq!(
foo_module.file().path(&db), foo_module.file().unwrap().path(&db),
&FilePath::System(src_path.join("foo.py")) &FilePath::System(src_path.join("foo.py"))
); );
@ -1925,7 +2003,10 @@ not_a_directory
// take precedence over the second `site-packages` directory... // take precedence over the second `site-packages` directory...
let a_module_name = ModuleName::new_static("a").unwrap(); let a_module_name = ModuleName::new_static("a").unwrap();
let a_module = resolve_module(&db, &a_module_name).unwrap(); let a_module = resolve_module(&db, &a_module_name).unwrap();
assert_eq!(a_module.file().path(&db), &editable_install_location); assert_eq!(
a_module.file().unwrap().path(&db),
&editable_install_location
);
db.memory_file_system() db.memory_file_system()
.remove_file(&site_packages_pth) .remove_file(&site_packages_pth)
@ -1936,7 +2017,10 @@ not_a_directory
// the editable install no longer exists, so the module now resolves to the file in the // the editable install no longer exists, so the module now resolves to the file in the
// second `site-packages` directory // second `site-packages` directory
let a_module = resolve_module(&db, &a_module_name).unwrap(); let a_module = resolve_module(&db, &a_module_name).unwrap();
assert_eq!(a_module.file().path(&db), &system_site_packages_location); assert_eq!(
a_module.file().unwrap().path(&db),
&system_site_packages_location
);
} }
#[test] #[test]
@ -2001,6 +2085,7 @@ not_a_directory
assert!( assert!(
a_module a_module
.file() .file()
.unwrap()
.path(&db) .path(&db)
.as_str() .as_str()
.ends_with("src/a/__init__.py"), .ends_with("src/a/__init__.py"),
@ -2035,6 +2120,6 @@ not_a_directory
let foo_module_file = File::new(&db, FilePath::System(installed_foo_module)); let foo_module_file = File::new(&db, FilePath::System(installed_foo_module));
let module = file_to_module(&db, foo_module_file).unwrap(); let module = file_to_module(&db, foo_module_file).unwrap();
assert_eq!(module.search_path(), &site_packages); assert_eq!(module.search_path().unwrap(), &site_packages);
} }
} }

View file

@ -1336,7 +1336,9 @@ where
continue; continue;
}; };
let referenced_module = module.file(); let Some(referenced_module) = module.file() else {
continue;
};
// In order to understand the visibility of definitions created by a `*` import, // In order to understand the visibility of definitions created by a `*` import,
// we need to know the visibility of the global-scope definitions in the // we need to know the visibility of the global-scope definitions in the

View file

@ -244,7 +244,12 @@ impl<'db> Visitor<'db> for ExportFinder<'db> {
.ok() .ok()
.and_then(|module_name| resolve_module(self.db, &module_name)) .and_then(|module_name| resolve_module(self.db, &module_name))
.iter() .iter()
.flat_map(|module| exported_names(self.db, module.file())) .flat_map(|module| {
module
.file()
.map(|file| exported_names(self.db, file))
.unwrap_or_default()
})
{ {
self.possibly_add_export(export, PossibleExportKind::Normal); self.possibly_add_export(export, PossibleExportKind::Normal);
} }

View file

@ -342,10 +342,12 @@ pub(crate) fn imported_symbol<'db>(
/// (e.g. `from builtins import int`). /// (e.g. `from builtins import int`).
pub(crate) fn builtins_symbol<'db>(db: &'db dyn Db, symbol: &str) -> SymbolAndQualifiers<'db> { pub(crate) fn builtins_symbol<'db>(db: &'db dyn Db, symbol: &str) -> SymbolAndQualifiers<'db> {
resolve_module(db, &KnownModule::Builtins.name()) resolve_module(db, &KnownModule::Builtins.name())
.map(|module| { .and_then(|module| {
let file = module.file()?;
Some(
symbol_impl( symbol_impl(
db, db,
global_scope(db, module.file()), global_scope(db, file),
symbol, symbol,
RequiresExplicitReExport::Yes, RequiresExplicitReExport::Yes,
) )
@ -354,7 +356,8 @@ pub(crate) fn builtins_symbol<'db>(db: &'db dyn Db, symbol: &str) -> SymbolAndQu
// do the normal lookup in `types.ModuleType` and not the special one as in // do the normal lookup in `types.ModuleType` and not the special one as in
// `imported_symbol`. // `imported_symbol`.
module_type_implicit_global_symbol(db, symbol) module_type_implicit_global_symbol(db, symbol)
}) }),
)
}) })
.unwrap_or_default() .unwrap_or_default()
} }
@ -368,7 +371,10 @@ pub(crate) fn known_module_symbol<'db>(
symbol: &str, symbol: &str,
) -> SymbolAndQualifiers<'db> { ) -> SymbolAndQualifiers<'db> {
resolve_module(db, &known_module.name()) resolve_module(db, &known_module.name())
.map(|module| imported_symbol(db, module.file(), symbol, None)) .and_then(|module| {
let file = module.file()?;
Some(imported_symbol(db, file, symbol, None))
})
.unwrap_or_default() .unwrap_or_default()
} }
@ -403,7 +409,8 @@ pub(crate) fn builtins_module_scope(db: &dyn Db) -> Option<ScopeId<'_>> {
/// ///
/// Can return `None` if a custom typeshed is used that is missing the core module in question. /// Can return `None` if a custom typeshed is used that is missing the core module in question.
fn core_module_scope(db: &dyn Db, core_module: KnownModule) -> Option<ScopeId<'_>> { fn core_module_scope(db: &dyn Db, core_module: KnownModule) -> Option<ScopeId<'_>> {
resolve_module(db, &core_module.name()).map(|module| global_scope(db, module.file())) let module = resolve_module(db, &core_module.name())?;
Some(global_scope(db, module.file()?))
} }
/// Infer the combined type from an iterator of bindings, and return it /// Infer the combined type from an iterator of bindings, and return it

View file

@ -7980,7 +7980,11 @@ impl<'db> ModuleLiteralType<'db> {
} }
} }
imported_symbol(db, self.module(db).file(), name, None).symbol self.module(db)
.file()
.map(|file| imported_symbol(db, file, name, None))
.unwrap_or_default()
.symbol
} }
} }

View file

@ -611,8 +611,12 @@ impl<'db> Bindings<'db> {
if let [Some(ty)] = overload.parameter_types() { if let [Some(ty)] = overload.parameter_types() {
overload.set_return_type(match ty { overload.set_return_type(match ty {
Type::ModuleLiteral(module_literal) => { Type::ModuleLiteral(module_literal) => {
match dunder_all_names(db, module_literal.module(db).file()) let all_names = module_literal
{ .module(db)
.file()
.map(|file| dunder_all_names(db, file))
.unwrap_or_default();
match all_names {
Some(names) => { Some(names) => {
let mut names = names.iter().collect::<Vec<_>>(); let mut names = names.iter().collect::<Vec<_>>();
names.sort(); names.sort();

View file

@ -2898,7 +2898,7 @@ mod tests {
let class_module = resolve_module(&db, &class.canonical_module(&db).name()).unwrap(); let class_module = resolve_module(&db, &class.canonical_module(&db).name()).unwrap();
assert_eq!( assert_eq!(
KnownClass::try_from_file_and_name(&db, class_module.file(), class_name), KnownClass::try_from_file_and_name(&db, class_module.file().unwrap(), class_name),
Some(class), Some(class),
"`KnownClass::candidate_from_str` appears to be missing a case for `{class_name}`" "`KnownClass::candidate_from_str` appears to be missing a case for `{class_name}`"
); );

View file

@ -24,16 +24,17 @@ impl TypeDefinition<'_> {
} }
} }
pub fn full_range(&self, db: &dyn Db) -> FileRange { pub fn full_range(&self, db: &dyn Db) -> Option<FileRange> {
match self { match self {
Self::Module(module) => { Self::Module(module) => {
let source = source_text(db.upcast(), module.file()); let file = module.file()?;
FileRange::new(module.file(), TextRange::up_to(source.text_len())) let source = source_text(db.upcast(), file);
Some(FileRange::new(file, TextRange::up_to(source.text_len())))
} }
Self::Class(definition) Self::Class(definition)
| Self::Function(definition) | Self::Function(definition)
| Self::TypeVar(definition) | Self::TypeVar(definition)
| Self::TypeAlias(definition) => definition.full_range(db), | Self::TypeAlias(definition) => Some(definition.full_range(db)),
} }
} }
} }

View file

@ -4049,7 +4049,7 @@ impl<'db> TypeInferenceBuilder<'db> {
// (e.g. `from parent import submodule` inside the `parent` module). // (e.g. `from parent import submodule` inside the `parent` module).
let import_is_self_referential = module_ty let import_is_self_referential = module_ty
.into_module_literal() .into_module_literal()
.is_some_and(|module| self.file() == module.module(self.db()).file()); .is_some_and(|module| Some(self.file()) == module.module(self.db()).file());
// First try loading the requested attribute from the module. // First try loading the requested attribute from the module.
if !import_is_self_referential { if !import_is_self_referential {