mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 02:12:22 +00:00
[red-knot] Move attribute access on ModuleLiteral
types into a dedicated method (#15067)
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 / 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 / benchmarks (push) Blocked by required conditions
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 / 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 / benchmarks (push) Blocked by required conditions
This commit is contained in:
parent
bb43085939
commit
a06099dffe
1 changed files with 62 additions and 59 deletions
|
@ -1468,65 +1468,7 @@ impl<'db> Type<'db> {
|
|||
_ => KnownClass::FunctionType.to_instance(db).member(db, name),
|
||||
},
|
||||
|
||||
Type::ModuleLiteral(module_ref) => {
|
||||
// `__dict__` is a very special member that is never overridden by module globals;
|
||||
// we should always look it up directly as an attribute on `types.ModuleType`,
|
||||
// never in the global scope of the module.
|
||||
if name == "__dict__" {
|
||||
return KnownClass::ModuleType
|
||||
.to_instance(db)
|
||||
.member(db, "__dict__");
|
||||
}
|
||||
|
||||
// If the file that originally imported the module has also imported a submodule
|
||||
// named [name], then the result is (usually) that submodule, even if the module
|
||||
// also defines a (non-module) symbol with that name.
|
||||
//
|
||||
// Note that technically, either the submodule or the non-module symbol could take
|
||||
// priority, depending on the ordering of when the submodule is loaded relative to
|
||||
// the parent module's `__init__.py` file being evaluated. That said, we have
|
||||
// chosen to always have the submodule take priority. (This matches pyright's
|
||||
// current behavior, and opposite of mypy's current behavior.)
|
||||
if let Some(submodule_name) = ModuleName::new(name) {
|
||||
let importing_file = module_ref.importing_file(db);
|
||||
let imported_submodules = imported_modules(db, importing_file);
|
||||
let mut full_submodule_name = module_ref.module(db).name().clone();
|
||||
full_submodule_name.extend(&submodule_name);
|
||||
if imported_submodules.contains(&full_submodule_name) {
|
||||
if let Some(submodule) = resolve_module(db, &full_submodule_name) {
|
||||
let submodule_ty = Type::module_literal(db, importing_file, submodule);
|
||||
return Symbol::Type(submodule_ty, Boundness::Bound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let global_lookup =
|
||||
symbol(db, global_scope(db, module_ref.module(db).file()), name);
|
||||
|
||||
// If it's unbound, check if it's present as an instance on `types.ModuleType`
|
||||
// or `builtins.object`.
|
||||
//
|
||||
// We do a more limited version of this in `global_symbol_ty`,
|
||||
// but there are two crucial differences here:
|
||||
// - If a member is looked up as an attribute, `__init__` is also available
|
||||
// on the module, but it isn't available as a global from inside the module
|
||||
// - If a member is looked up as an attribute, members on `builtins.object`
|
||||
// are also available (because `types.ModuleType` inherits from `object`);
|
||||
// these attributes are also not available as globals from inside the module.
|
||||
//
|
||||
// The same way as in `global_symbol_ty`, however, we need to be careful to
|
||||
// ignore `__getattr__`. Typeshed has a fake `__getattr__` on `types.ModuleType`
|
||||
// to help out with dynamic imports; we shouldn't use it for `ModuleLiteral` types
|
||||
// where we know exactly which module we're dealing with.
|
||||
if name != "__getattr__" && global_lookup.possibly_unbound() {
|
||||
// TODO: this should use `.to_instance()`, but we don't understand instance attribute yet
|
||||
let module_type_instance_member =
|
||||
KnownClass::ModuleType.to_class_literal(db).member(db, name);
|
||||
global_lookup.or_fall_back_to(db, &module_type_instance_member)
|
||||
} else {
|
||||
global_lookup
|
||||
}
|
||||
}
|
||||
Type::ModuleLiteral(module) => module.member(db, name),
|
||||
|
||||
Type::ClassLiteral(class_ty) => class_ty.member(db, name),
|
||||
|
||||
|
@ -3030,6 +2972,67 @@ pub struct ModuleLiteralType<'db> {
|
|||
pub module: Module,
|
||||
}
|
||||
|
||||
impl<'db> ModuleLiteralType<'db> {
|
||||
fn member(self, db: &'db dyn Db, name: &str) -> Symbol<'db> {
|
||||
// `__dict__` is a very special member that is never overridden by module globals;
|
||||
// we should always look it up directly as an attribute on `types.ModuleType`,
|
||||
// never in the global scope of the module.
|
||||
if name == "__dict__" {
|
||||
return KnownClass::ModuleType
|
||||
.to_instance(db)
|
||||
.member(db, "__dict__");
|
||||
}
|
||||
|
||||
// If the file that originally imported the module has also imported a submodule
|
||||
// named `name`, then the result is (usually) that submodule, even if the module
|
||||
// also defines a (non-module) symbol with that name.
|
||||
//
|
||||
// Note that technically, either the submodule or the non-module symbol could take
|
||||
// priority, depending on the ordering of when the submodule is loaded relative to
|
||||
// the parent module's `__init__.py` file being evaluated. That said, we have
|
||||
// chosen to always have the submodule take priority. (This matches pyright's
|
||||
// current behavior, but is the opposite of mypy's current behavior.)
|
||||
if let Some(submodule_name) = ModuleName::new(name) {
|
||||
let importing_file = self.importing_file(db);
|
||||
let imported_submodules = imported_modules(db, importing_file);
|
||||
let mut full_submodule_name = self.module(db).name().clone();
|
||||
full_submodule_name.extend(&submodule_name);
|
||||
if imported_submodules.contains(&full_submodule_name) {
|
||||
if let Some(submodule) = resolve_module(db, &full_submodule_name) {
|
||||
let submodule_ty = Type::module_literal(db, importing_file, submodule);
|
||||
return Symbol::Type(submodule_ty, Boundness::Bound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let global_lookup = symbol(db, global_scope(db, self.module(db).file()), name);
|
||||
|
||||
// If it's unbound, check if it's present as an instance on `types.ModuleType`
|
||||
// or `builtins.object`.
|
||||
//
|
||||
// We do a more limited version of this in `global_symbol_ty`,
|
||||
// but there are two crucial differences here:
|
||||
// - If a member is looked up as an attribute, `__init__` is also available
|
||||
// on the module, but it isn't available as a global from inside the module
|
||||
// - If a member is looked up as an attribute, members on `builtins.object`
|
||||
// are also available (because `types.ModuleType` inherits from `object`);
|
||||
// these attributes are also not available as globals from inside the module.
|
||||
//
|
||||
// The same way as in `global_symbol_ty`, however, we need to be careful to
|
||||
// ignore `__getattr__`. Typeshed has a fake `__getattr__` on `types.ModuleType`
|
||||
// to help out with dynamic imports; we shouldn't use it for `ModuleLiteral` types
|
||||
// where we know exactly which module we're dealing with.
|
||||
if name != "__getattr__" && global_lookup.possibly_unbound() {
|
||||
// TODO: this should use `.to_instance()`, but we don't understand instance attribute yet
|
||||
let module_type_instance_member =
|
||||
KnownClass::ModuleType.to_class_literal(db).member(db, name);
|
||||
global_lookup.or_fall_back_to(db, &module_type_instance_member)
|
||||
} else {
|
||||
global_lookup
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Representation of a runtime class object.
|
||||
///
|
||||
/// Does not in itself represent a type,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue