Compare commits
1 Commits
ag/auto-im
...
cjm/displa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
222805e210 |
@@ -3,7 +3,7 @@ use std::iter::FusedIterator;
|
||||
pub use db::{Db, Jar};
|
||||
pub use module::{Module, ModuleKind};
|
||||
pub use module_name::ModuleName;
|
||||
pub use resolver::resolve_module;
|
||||
pub use resolver::{file_to_module, resolve_module};
|
||||
use ruff_db::system::SystemPath;
|
||||
pub use typeshed::{
|
||||
vendored_typeshed_stubs, TypeshedVersionsParseError, TypeshedVersionsParseErrorKind,
|
||||
|
||||
@@ -60,8 +60,9 @@ pub(crate) fn path_to_module(db: &dyn Db, path: &FilePath) -> Option<Module> {
|
||||
/// Resolves the module for the file with the given id.
|
||||
///
|
||||
/// Returns `None` if the file is not a module locatable via any of the known search paths.
|
||||
#[allow(unreachable_pub)]
|
||||
#[salsa::tracked]
|
||||
pub(crate) fn file_to_module(db: &dyn Db, file: File) -> Option<Module> {
|
||||
pub fn file_to_module(db: &dyn Db, file: File) -> Option<Module> {
|
||||
let _span = tracing::trace_span!("file_to_module", ?file).entered();
|
||||
|
||||
let path = file.path(db.upcast());
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use red_knot_module_resolver::{file_to_module, Module};
|
||||
use ruff_db::files::File;
|
||||
use ruff_python_ast::name::Name;
|
||||
|
||||
@@ -181,11 +182,18 @@ pub struct FunctionType<'db> {
|
||||
/// name of the function at definition
|
||||
pub name: Name,
|
||||
|
||||
/// file in which the function is defined
|
||||
pub file: File,
|
||||
|
||||
/// types of all decorators on this function
|
||||
decorators: Vec<Type<'db>>,
|
||||
}
|
||||
|
||||
impl<'db> FunctionType<'db> {
|
||||
pub fn module(self, db: &dyn Db) -> Option<Module> {
|
||||
file_to_module(db.upcast(), self.file(db))
|
||||
}
|
||||
|
||||
pub fn has_decorator(self, db: &dyn Db, decorator: Type<'_>) -> bool {
|
||||
self.decorators(db).contains(&decorator)
|
||||
}
|
||||
@@ -196,6 +204,9 @@ pub struct ClassType<'db> {
|
||||
/// Name of the class at definition
|
||||
pub name: Name,
|
||||
|
||||
/// file in which the class is defined
|
||||
pub file: File,
|
||||
|
||||
/// Types of all class bases
|
||||
bases: Vec<Type<'db>>,
|
||||
|
||||
@@ -203,6 +214,10 @@ pub struct ClassType<'db> {
|
||||
}
|
||||
|
||||
impl<'db> ClassType<'db> {
|
||||
pub fn module(self, db: &dyn Db) -> Option<Module> {
|
||||
file_to_module(db.upcast(), self.file(db))
|
||||
}
|
||||
|
||||
/// Returns the class member of this class named `name`.
|
||||
///
|
||||
/// The member resolves to a member of the class itself or any of its bases.
|
||||
|
||||
@@ -28,10 +28,31 @@ impl Display for DisplayType<'_> {
|
||||
Type::Module(file) => {
|
||||
write!(f, "<module '{:?}'>", file.path(self.db.upcast()))
|
||||
}
|
||||
// TODO functions and classes should display using a fully qualified name
|
||||
Type::Class(class) => write!(f, "Literal[{}]", class.name(self.db)),
|
||||
Type::Class(class) => {
|
||||
if let Some(module) = class.module(self.db) {
|
||||
write!(f, "Literal[{}.{}]", module.name(), class.name(self.db))
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"Literal[<{}>.{}]",
|
||||
class.file(self.db).path(self.db.upcast()).as_ref(),
|
||||
class.name(self.db)
|
||||
)
|
||||
}
|
||||
}
|
||||
Type::Instance(class) => f.write_str(&class.name(self.db)),
|
||||
Type::Function(function) => write!(f, "Literal[{}]", function.name(self.db)),
|
||||
Type::Function(function) => {
|
||||
if let Some(module) = function.module(self.db) {
|
||||
write!(f, "Literal[{}.{}]", module.name(), function.name(self.db))
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"Literal[<{}>.{}]",
|
||||
function.file(self.db).path(self.db.upcast()).as_ref(),
|
||||
function.name(self.db)
|
||||
)
|
||||
}
|
||||
}
|
||||
Type::Union(union) => union.display(self.db).fmt(f),
|
||||
Type::Intersection(intersection) => intersection.display(self.db).fmt(f),
|
||||
Type::IntLiteral(n) => write!(f, "Literal[{n}]"),
|
||||
|
||||
@@ -397,8 +397,12 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
self.infer_optional_expression(returns.as_deref());
|
||||
}
|
||||
|
||||
let function_ty =
|
||||
Type::Function(FunctionType::new(self.db, name.id.clone(), decorator_tys));
|
||||
let function_ty = Type::Function(FunctionType::new(
|
||||
self.db,
|
||||
name.id.clone(),
|
||||
self.file,
|
||||
decorator_tys,
|
||||
));
|
||||
|
||||
self.types.definitions.insert(definition, function_ty);
|
||||
}
|
||||
@@ -473,7 +477,13 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
.node_scope(NodeWithScopeRef::Class(class))
|
||||
.to_scope_id(self.db, self.file);
|
||||
|
||||
let class_ty = Type::Class(ClassType::new(self.db, name.id.clone(), bases, body_scope));
|
||||
let class_ty = Type::Class(ClassType::new(
|
||||
self.db,
|
||||
name.id.clone(),
|
||||
self.file,
|
||||
bases,
|
||||
body_scope,
|
||||
));
|
||||
|
||||
self.types.definitions.insert(definition, class_ty);
|
||||
}
|
||||
@@ -1554,7 +1564,7 @@ mod tests {
|
||||
("src/b.py", "class C: pass"),
|
||||
])?;
|
||||
|
||||
assert_public_ty(&db, "src/a.py", "E", "Literal[C]");
|
||||
assert_public_ty(&db, "src/a.py", "E", "Literal[b.C]");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1587,7 +1597,31 @@ mod tests {
|
||||
.map(|base_ty| format!("{}", base_ty.display(&db)))
|
||||
.collect();
|
||||
|
||||
assert_eq!(base_names, vec!["Literal[Base]"]);
|
||||
assert_eq!(base_names, vec!["Literal[mod.Base]"]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn class_name_not_in_module() -> anyhow::Result<()> {
|
||||
let mut db = setup_db();
|
||||
|
||||
db.write_file("/src/a.py", "")?; // we have to create /src or the resolver errors
|
||||
db.write_file("/not-a-module", "class C: pass")?;
|
||||
|
||||
assert_public_ty(&db, "not-a-module", "C", "Literal[</not-a-module>.C]");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_name_not_in_module() -> anyhow::Result<()> {
|
||||
let mut db = setup_db();
|
||||
|
||||
db.write_file("/src/a.py", "")?; // we have to create /src or the resolver errors
|
||||
db.write_file("/not-a-module", "def f(): pass")?;
|
||||
|
||||
assert_public_ty(&db, "not-a-module", "f", "Literal[</not-a-module>.f]");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1631,7 +1665,7 @@ mod tests {
|
||||
("src/b.py", "class C: pass"),
|
||||
])?;
|
||||
|
||||
assert_public_ty(&db, "src/a.py", "D", "Literal[C]");
|
||||
assert_public_ty(&db, "src/a.py", "D", "Literal[b.C]");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -2095,7 +2129,7 @@ mod tests {
|
||||
|
||||
db.write_file("/src/a.py", "c = copyright")?;
|
||||
|
||||
assert_public_ty(&db, "/src/a.py", "c", "Literal[copyright]");
|
||||
assert_public_ty(&db, "/src/a.py", "c", "Literal[builtins.copyright]");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -2113,7 +2147,7 @@ mod tests {
|
||||
("/typeshed/stdlib/VERSIONS", "builtins: 3.8-"),
|
||||
])?;
|
||||
|
||||
assert_public_ty(&db, "/src/a.py", "c", "Literal[copyright]");
|
||||
assert_public_ty(&db, "/src/a.py", "c", "Literal[builtins.copyright]");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -2150,7 +2184,7 @@ mod tests {
|
||||
|
||||
db.write_file("/src/a.py", "import builtins; x = builtins.copyright")?;
|
||||
|
||||
assert_public_ty(&db, "/src/a.py", "x", "Literal[copyright]");
|
||||
assert_public_ty(&db, "/src/a.py", "x", "Literal[builtins.copyright]");
|
||||
// imported builtins module is the same file as the implicit builtins
|
||||
let file = system_path_to_file(&db, "/src/a.py").expect("Expected file to exist.");
|
||||
let builtins_ty = global_symbol_ty_by_name(&db, file, "builtins");
|
||||
|
||||
Reference in New Issue
Block a user