Use `CompactString` for `ModuleName` (#12131)

This commit is contained in:
Micha Reiser 2024-07-01 10:22:34 +02:00 committed by GitHub
parent 5109b50bb3
commit 4cb6a09fc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 121 additions and 173 deletions

12
Cargo.lock generated
View File

@ -1881,7 +1881,6 @@ dependencies = [
"ruff_python_parser",
"ruff_text_size",
"rustc-hash 2.0.0",
"smol_str",
"tempfile",
"tracing",
"tracing-subscriber",
@ -1893,13 +1892,13 @@ name = "red_knot_module_resolver"
version = "0.0.0"
dependencies = [
"anyhow",
"compact_str",
"insta",
"path-slash",
"ruff_db",
"ruff_python_stdlib",
"rustc-hash 2.0.0",
"salsa",
"smol_str",
"tempfile",
"tracing",
"walkdir",
@ -2860,15 +2859,6 @@ version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "smol_str"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
dependencies = [
"serde",
]
[[package]]
name = "spin"
version = "0.9.8"

View File

@ -120,7 +120,6 @@ serde_with = { version = "3.6.0", default-features = false, features = [
shellexpand = { version = "3.0.0" }
similar = { version = "2.4.0", features = ["inline"] }
smallvec = { version = "1.13.2" }
smol_str = { version = "0.2.2" }
static_assertions = "1.1.0"
strum = { version = "0.26.0", features = ["strum_macros"] }
strum_macros = { version = "0.26.0" }

View File

@ -32,7 +32,6 @@ notify = { workspace = true }
parking_lot = { workspace = true }
rayon = { workspace = true }
rustc-hash = { workspace = true }
smol_str = { version = "0.2.1" }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
tracing-tree = { workspace = true }

View File

@ -1,3 +1,4 @@
use red_knot_module_resolver::ModuleName;
use std::cell::RefCell;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
@ -10,7 +11,7 @@ use ruff_python_parser::Parsed;
use crate::cache::KeyValueCache;
use crate::db::{LintDb, LintJar, QueryResult};
use crate::files::FileId;
use crate::module::{resolve_module, ModuleName};
use crate::module::resolve_module;
use crate::parse::parse;
use crate::semantic::{infer_definition_type, infer_symbol_public_type, Type};
use crate::semantic::{
@ -145,7 +146,9 @@ fn lint_bad_overrides(context: &SemanticLintContext) -> QueryResult<()> {
// TODO we should have a special marker on the real typing module (from typeshed) so if you
// have your own "typing" module in your project, we don't consider it THE typing module (and
// same for other stdlib modules that our lint rules care about)
let Some(typing_override) = context.resolve_global_symbol("typing", "override")? else {
let Some(typing_override) =
context.resolve_global_symbol(&ModuleName::new_static("typing").unwrap(), "override")?
else {
// TODO once we bundle typeshed, this should be unreachable!()
return Ok(());
};
@ -236,10 +239,10 @@ impl<'a> SemanticLintContext<'a> {
pub fn resolve_global_symbol(
&self,
module: &str,
module: &ModuleName,
symbol_name: &str,
) -> QueryResult<Option<GlobalSymbolId>> {
let Some(module) = resolve_module(self.db.upcast(), ModuleName::new(module))? else {
let Some(module) = resolve_module(self.db.upcast(), module)? else {
return Ok(None);
};

View File

@ -5,9 +5,8 @@ use std::sync::atomic::AtomicU32;
use std::sync::Arc;
use dashmap::mapref::entry::Entry;
use smol_str::SmolStr;
use red_knot_module_resolver::ModuleKind;
use red_knot_module_resolver::{ModuleKind, ModuleName};
use crate::db::{QueryResult, SemanticDb, SemanticJar};
use crate::files::FileId;
@ -95,87 +94,7 @@ impl Module {
name.push_str(part);
}
Ok(if name.is_empty() {
None
} else {
Some(ModuleName(SmolStr::new(name)))
})
}
}
/// A module name, e.g. `foo.bar`.
///
/// Always normalized to the absolute form
/// (never a relative module name, i.e., never `.foo`).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct ModuleName(smol_str::SmolStr);
impl ModuleName {
pub fn new(name: &str) -> Self {
debug_assert!(!name.is_empty());
Self(smol_str::SmolStr::new(name))
}
fn from_relative_path(path: &Path) -> Option<Self> {
let path = if path.ends_with("__init__.py") || path.ends_with("__init__.pyi") {
path.parent()?
} else {
path
};
let name = if let Some(parent) = path.parent() {
let mut name = String::with_capacity(path.as_os_str().len());
for component in parent.components() {
name.push_str(component.as_os_str().to_str()?);
name.push('.');
}
// SAFETY: Unwrap is safe here or `parent` would have returned `None`.
name.push_str(path.file_stem().unwrap().to_str()?);
smol_str::SmolStr::from(name)
} else {
smol_str::SmolStr::new(path.file_stem()?.to_str()?)
};
Some(Self(name))
}
/// An iterator over the components of the module name:
/// `foo.bar.baz` -> `foo`, `bar`, `baz`
pub fn components(&self) -> impl DoubleEndedIterator<Item = &str> {
self.0.split('.')
}
/// The name of this module's immediate parent, if it has a parent
pub fn parent(&self) -> Option<ModuleName> {
let (_, parent) = self.0.rsplit_once('.')?;
Some(Self(smol_str::SmolStr::new(parent)))
}
pub fn starts_with(&self, other: &ModuleName) -> bool {
self.0.starts_with(other.0.as_str())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl Deref for ModuleName {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl std::fmt::Display for ModuleName {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
Ok(ModuleName::new(&name))
}
}
@ -262,7 +181,7 @@ pub struct ModuleData {
/// and, therefore, cannot be used as part of a query.
/// For this to work with salsa, it would be necessary to intern all `ModuleName`s.
#[tracing::instrument(level = "debug", skip(db))]
pub fn resolve_module(db: &dyn SemanticDb, name: ModuleName) -> QueryResult<Option<Module>> {
pub fn resolve_module(db: &dyn SemanticDb, name: &ModuleName) -> QueryResult<Option<Module>> {
let jar: &SemanticJar = db.jar()?;
let modules = &jar.module_resolver;
@ -271,7 +190,7 @@ pub fn resolve_module(db: &dyn SemanticDb, name: ModuleName) -> QueryResult<Opti
match entry {
Entry::Occupied(entry) => Ok(Some(*entry.get())),
Entry::Vacant(entry) => {
let Some((root_path, absolute_path, kind)) = resolve_name(&name, &modules.search_paths)
let Some((root_path, absolute_path, kind)) = resolve_name(name, &modules.search_paths)
else {
return Ok(None);
};
@ -288,9 +207,14 @@ pub fn resolve_module(db: &dyn SemanticDb, name: ModuleName) -> QueryResult<Opti
.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
);
modules
.modules
.insert(module, Arc::from(ModuleData { name, path, kind }));
modules.modules.insert(
module,
Arc::from(ModuleData {
name: name.clone(),
path,
kind,
}),
);
// A path can map to multiple modules because of symlinks:
// ```
@ -346,7 +270,7 @@ pub fn file_to_module(db: &dyn SemanticDb, file: FileId) -> QueryResult<Option<M
return Ok(None);
};
let Some(module_name) = ModuleName::from_relative_path(relative_path) else {
let Some(module_name) = from_relative_path(relative_path) else {
return Ok(None);
};
@ -354,7 +278,7 @@ pub fn file_to_module(db: &dyn SemanticDb, file: FileId) -> QueryResult<Option<M
// If it doesn't, then that means that multiple modules have the same in different
// root paths, but that the module corresponding to the past path is in a lower priority search path,
// in which case we ignore it.
let Some(module) = resolve_module(db, module_name)? else {
let Some(module) = resolve_module(db, &module_name)? else {
return Ok(None);
};
let module_path = module.path(db)?;
@ -385,6 +309,32 @@ pub fn file_to_module(db: &dyn SemanticDb, file: FileId) -> QueryResult<Option<M
}
}
fn from_relative_path(path: &Path) -> Option<ModuleName> {
let path = if path.ends_with("__init__.py") || path.ends_with("__init__.pyi") {
path.parent()?
} else {
path
};
let name = if let Some(parent) = path.parent() {
let mut name = String::with_capacity(path.to_str().unwrap().len());
for component in parent.components() {
name.push_str(component.as_os_str().to_str()?);
name.push('.');
}
// SAFETY: Unwrap is safe here or `parent` would have returned `None`.
name.push_str(path.file_stem().unwrap().to_str().unwrap());
name
} else {
path.file_stem()?.to_str().unwrap().to_string()
};
ModuleName::new(&name)
}
//////////////////////////////////////////////////////
// Mutations
//////////////////////////////////////////////////////
@ -763,13 +713,14 @@ impl PackageKind {
#[cfg(test)]
mod tests {
use red_knot_module_resolver::ModuleName;
use std::num::NonZeroU32;
use std::path::PathBuf;
use crate::db::tests::TestDb;
use crate::db::SourceDb;
use crate::module::{
path_to_module, resolve_module, set_module_search_paths, ModuleKind, ModuleName,
path_to_module, resolve_module, set_module_search_paths, ModuleKind,
ModuleResolutionInputs, TYPESHED_STDLIB_DIRECTORY,
};
use crate::semantic::Dependency;
@ -829,14 +780,12 @@ mod tests {
let foo_path = src.join("foo.py");
std::fs::write(&foo_path, "print('Hello, world!')")?;
let foo_module = resolve_module(&db, ModuleName::new("foo"))?.unwrap();
let foo_name = ModuleName::new_static("foo").unwrap();
let foo_module = resolve_module(&db, &foo_name)?.unwrap();
assert_eq!(
Some(foo_module),
resolve_module(&db, ModuleName::new("foo"))?
);
assert_eq!(Some(foo_module), resolve_module(&db, &foo_name)?);
assert_eq!(ModuleName::new("foo"), foo_module.name(&db)?);
assert_eq!(foo_name, foo_module.name(&db)?);
assert_eq!(&src, foo_module.path(&db)?.root().path());
assert_eq!(ModuleKind::Module, foo_module.kind(&db)?);
assert_eq!(&foo_path, &*db.file_path(foo_module.path(&db)?.file()));
@ -855,13 +804,14 @@ mod tests {
} = create_resolver()?;
let stdlib_dir = custom_typeshed.join(TYPESHED_STDLIB_DIRECTORY);
std::fs::create_dir_all(&stdlib_dir).unwrap();
let functools_name = ModuleName::new_static("functools").unwrap();
let functools_path = stdlib_dir.join("functools.py");
std::fs::write(&functools_path, "def update_wrapper(): ...").unwrap();
let functools_module = resolve_module(&db, ModuleName::new("functools"))?.unwrap();
let functools_module = resolve_module(&db, &functools_name)?.unwrap();
assert_eq!(
Some(functools_module),
resolve_module(&db, ModuleName::new("functools"))?
resolve_module(&db, &functools_name)?
);
assert_eq!(&stdlib_dir, functools_module.path(&db)?.root().path());
assert_eq!(ModuleKind::Module, functools_module.kind(&db)?);
@ -895,11 +845,12 @@ mod tests {
let first_party_functools_path = src.join("functools.py");
std::fs::write(stdlib_functools_path, "def update_wrapper(): ...").unwrap();
std::fs::write(&first_party_functools_path, "def update_wrapper(): ...").unwrap();
let functools_module = resolve_module(&db, ModuleName::new("functools"))?.unwrap();
let functools_name = ModuleName::new_static("functools").unwrap();
let functools_module = resolve_module(&db, &functools_name)?.unwrap();
assert_eq!(
Some(functools_module),
resolve_module(&db, ModuleName::new("functools"))?
resolve_module(&db, &functools_name)?
);
assert_eq!(&src, functools_module.path(&db).unwrap().root().path());
assert_eq!(ModuleKind::Module, functools_module.kind(&db)?);
@ -925,14 +876,15 @@ mod tests {
..
} = create_resolver()?;
let foo_name = ModuleName::new("foo").unwrap();
let foo_dir = src.join("foo");
let foo_path = foo_dir.join("__init__.py");
std::fs::create_dir(&foo_dir)?;
std::fs::write(&foo_path, "print('Hello, world!')")?;
let foo_module = resolve_module(&db, ModuleName::new("foo"))?.unwrap();
let foo_module = resolve_module(&db, &foo_name)?.unwrap();
assert_eq!(ModuleName::new("foo"), foo_module.name(&db)?);
assert_eq!(foo_name, foo_module.name(&db)?);
assert_eq!(&src, foo_module.path(&db)?.root().path());
assert_eq!(&foo_path, &*db.file_path(foo_module.path(&db)?.file()));
@ -961,7 +913,7 @@ mod tests {
let foo_py = src.join("foo.py");
std::fs::write(&foo_py, "print('Hello, world!')")?;
let foo_module = resolve_module(&db, ModuleName::new("foo"))?.unwrap();
let foo_module = resolve_module(&db, &ModuleName::new("foo").unwrap())?.unwrap();
assert_eq!(&src, foo_module.path(&db)?.root().path());
assert_eq!(&foo_init, &*db.file_path(foo_module.path(&db)?.file()));
@ -987,7 +939,7 @@ mod tests {
std::fs::write(&foo_stub, "x: int")?;
std::fs::write(&foo_py, "print('Hello, world!')")?;
let foo = resolve_module(&db, ModuleName::new("foo"))?.unwrap();
let foo = resolve_module(&db, &ModuleName::new("foo").unwrap())?.unwrap();
assert_eq!(&src, foo.path(&db)?.root().path());
assert_eq!(&foo_stub, &*db.file_path(foo.path(&db)?.file()));
@ -1016,7 +968,7 @@ mod tests {
std::fs::write(bar.join("__init__.py"), "")?;
std::fs::write(&baz, "print('Hello, world!')")?;
let baz_module = resolve_module(&db, ModuleName::new("foo.bar.baz"))?.unwrap();
let baz_module = resolve_module(&db, &ModuleName::new("foo.bar.baz").unwrap())?.unwrap();
assert_eq!(&src, baz_module.path(&db)?.root().path());
assert_eq!(&baz, &*db.file_path(baz_module.path(&db)?.file()));
@ -1063,11 +1015,13 @@ mod tests {
std::fs::create_dir_all(&child2)?;
std::fs::write(&two, "print('Hello, world!')")?;
let one_module = resolve_module(&db, ModuleName::new("parent.child.one"))?.unwrap();
let one_module =
resolve_module(&db, &ModuleName::new("parent.child.one").unwrap())?.unwrap();
assert_eq!(Some(one_module), path_to_module(&db, &one)?);
let two_module = resolve_module(&db, ModuleName::new("parent.child.two"))?.unwrap();
let two_module =
resolve_module(&db, &ModuleName::new("parent.child.two").unwrap())?.unwrap();
assert_eq!(Some(two_module), path_to_module(&db, &two)?);
Ok(())
@ -1111,13 +1065,14 @@ mod tests {
std::fs::create_dir_all(&child2)?;
std::fs::write(two, "print('Hello, world!')")?;
let one_module = resolve_module(&db, ModuleName::new("parent.child.one"))?.unwrap();
let one_module =
resolve_module(&db, &ModuleName::new("parent.child.one").unwrap())?.unwrap();
assert_eq!(Some(one_module), path_to_module(&db, &one)?);
assert_eq!(
None,
resolve_module(&db, ModuleName::new("parent.child.two"))?
resolve_module(&db, &ModuleName::new("parent.child.two").unwrap())?
);
Ok(())
}
@ -1138,7 +1093,7 @@ mod tests {
std::fs::write(&foo_src, "")?;
std::fs::write(&foo_site_packages, "")?;
let foo_module = resolve_module(&db, ModuleName::new("foo"))?.unwrap();
let foo_module = resolve_module(&db, &ModuleName::new("foo").unwrap())?.unwrap();
assert_eq!(&src, foo_module.path(&db)?.root().path());
assert_eq!(&foo_src, &*db.file_path(foo_module.path(&db)?.file()));
@ -1165,8 +1120,8 @@ mod tests {
std::fs::write(&foo, "")?;
std::os::unix::fs::symlink(&foo, &bar)?;
let foo_module = resolve_module(&db, ModuleName::new("foo"))?.unwrap();
let bar_module = resolve_module(&db, ModuleName::new("bar"))?.unwrap();
let foo_module = resolve_module(&db, &ModuleName::new("foo").unwrap())?.unwrap();
let bar_module = resolve_module(&db, &ModuleName::new("bar").unwrap())?.unwrap();
assert_ne!(foo_module, bar_module);
@ -1202,12 +1157,12 @@ mod tests {
std::fs::write(foo_path, "from .bar import test")?;
std::fs::write(bar_path, "test = 'Hello world'")?;
let foo_module = resolve_module(&db, ModuleName::new("foo"))?.unwrap();
let bar_module = resolve_module(&db, ModuleName::new("foo.bar"))?.unwrap();
let foo_module = resolve_module(&db, &ModuleName::new("foo").unwrap())?.unwrap();
let bar_module = resolve_module(&db, &ModuleName::new("foo.bar").unwrap())?.unwrap();
// `from . import bar` in `foo/__init__.py` resolves to `foo`
assert_eq!(
Some(ModuleName::new("foo")),
ModuleName::new("foo"),
foo_module.resolve_dependency(
&db,
&Dependency::Relative {
@ -1219,18 +1174,19 @@ mod tests {
// `from baz import bar` in `foo/__init__.py` should resolve to `baz.py`
assert_eq!(
Some(ModuleName::new("baz")),
foo_module.resolve_dependency(&db, &Dependency::Module(ModuleName::new("baz")))?
ModuleName::new("baz"),
foo_module
.resolve_dependency(&db, &Dependency::Module(ModuleName::new("baz").unwrap()))?
);
// from .bar import test in `foo/__init__.py` should resolve to `foo/bar.py`
assert_eq!(
Some(ModuleName::new("foo.bar")),
ModuleName::new("foo.bar"),
foo_module.resolve_dependency(
&db,
&Dependency::Relative {
level: NonZeroU32::new(1).unwrap(),
module: Some(ModuleName::new("bar"))
module: ModuleName::new("bar")
}
)?
);
@ -1249,7 +1205,7 @@ mod tests {
// `from . import test` in `foo/bar.py` resolves to `foo`
assert_eq!(
Some(ModuleName::new("foo")),
ModuleName::new("foo"),
bar_module.resolve_dependency(
&db,
&Dependency::Relative {
@ -1261,18 +1217,19 @@ mod tests {
// `from baz import test` in `foo/bar.py` resolves to `baz`
assert_eq!(
Some(ModuleName::new("baz")),
bar_module.resolve_dependency(&db, &Dependency::Module(ModuleName::new("baz")))?
ModuleName::new("baz"),
bar_module
.resolve_dependency(&db, &Dependency::Module(ModuleName::new("baz").unwrap()))?
);
// `from .baz import test` in `foo/bar.py` resolves to `foo.baz`.
assert_eq!(
Some(ModuleName::new("foo.baz")),
ModuleName::new("foo.baz"),
bar_module.resolve_dependency(
&db,
&Dependency::Relative {
level: NonZeroU32::new(1).unwrap(),
module: Some(ModuleName::new("baz"))
module: ModuleName::new("baz")
}
)?
);

View File

@ -51,7 +51,7 @@ impl Program {
// TODO We may want to have a different check functions for non-first-party
// files because we only need to index them and not check them.
// Supporting non-first-party code also requires supporting typing stubs.
if let Some(dependency) = resolve_module(self, dependency_name)? {
if let Some(dependency) = resolve_module(self, &dependency_name)? {
if dependency.path(self)?.root().kind().is_first_party() {
context.schedule_dependency(dependency.path(self)?.file());
}

View File

@ -9,12 +9,12 @@ use crate::cache::KeyValueCache;
use crate::db::{QueryResult, SemanticDb, SemanticJar};
use crate::files::FileId;
use crate::module::Module;
use crate::module::ModuleName;
use crate::parse::parse;
pub(crate) use definitions::Definition;
use definitions::{ImportDefinition, ImportFromDefinition};
pub(crate) use flow_graph::ConstrainedDefinition;
use flow_graph::{FlowGraph, FlowGraphBuilder, FlowNodeId, ReachableDefinitionsIterator};
use red_knot_module_resolver::ModuleName;
use ruff_index::{newtype_index, IndexVec};
use rustc_hash::FxHashMap;
use std::ops::{Deref, DerefMut};
@ -410,7 +410,7 @@ impl SourceOrderVisitor<'_> for SemanticIndexer {
alias.name.id.split('.').next().unwrap()
};
let module = ModuleName::new(&alias.name.id);
let module = ModuleName::new(&alias.name.id).unwrap();
let def = Definition::Import(ImportDefinition {
module: module.clone(),
@ -426,7 +426,7 @@ impl SourceOrderVisitor<'_> for SemanticIndexer {
level,
..
}) => {
let module = module.as_ref().map(|m| ModuleName::new(&m.id));
let module = module.as_ref().and_then(|m| ModuleName::new(&m.id));
for alias in names {
let symbol_name = if let Some(asname) = &alias.asname {

View File

@ -1,5 +1,5 @@
use crate::ast_ids::TypedNodeKey;
use crate::semantic::ModuleName;
use red_knot_module_resolver::ModuleName;
use ruff_python_ast as ast;
use ruff_python_ast::name::Name;

View File

@ -6,13 +6,13 @@ use std::num::NonZeroU32;
use bitflags::bitflags;
use hashbrown::hash_map::{Keys, RawEntryMut};
use red_knot_module_resolver::ModuleName;
use rustc_hash::{FxHashMap, FxHasher};
use ruff_index::{newtype_index, IndexVec};
use ruff_python_ast::name::Name;
use crate::ast_ids::NodeKey;
use crate::module::ModuleName;
use crate::semantic::{Definition, ExpressionId};
type Map<K, V> = hashbrown::HashMap<K, V, ()>;

View File

@ -2,7 +2,7 @@
use crate::ast_ids::NodeKey;
use crate::db::{QueryResult, SemanticDb, SemanticJar};
use crate::files::FileId;
use crate::module::{Module, ModuleName};
use crate::module::Module;
use crate::semantic::{
resolve_global_symbol, semantic_index, GlobalSymbolId, ScopeId, ScopeKind, SymbolId,
};
@ -14,6 +14,7 @@ use rustc_hash::FxHashMap;
pub(crate) mod infer;
pub(crate) use infer::{infer_definition_type, infer_symbol_public_type};
use red_knot_module_resolver::ModuleName;
use ruff_python_ast::name::Name;
/// unique ID for a type

View File

@ -1,12 +1,13 @@
#![allow(dead_code)]
use red_knot_module_resolver::ModuleName;
use ruff_python_ast as ast;
use ruff_python_ast::AstNode;
use std::fmt::Debug;
use crate::db::{QueryResult, SemanticDb, SemanticJar};
use crate::module::{resolve_module, ModuleName};
use crate::module::resolve_module;
use crate::parse::parse;
use crate::semantic::types::{ModuleTypeId, Type};
use crate::semantic::{
@ -136,7 +137,7 @@ pub fn infer_definition_type(
Definition::Import(ImportDefinition {
module: module_name,
}) => {
if let Some(module) = resolve_module(db, module_name.clone())? {
if let Some(module) = resolve_module(db, &module_name)? {
Ok(Type::Module(ModuleTypeId { module, file_id }))
} else {
Ok(Type::Unknown)
@ -149,8 +150,9 @@ pub fn infer_definition_type(
}) => {
// TODO relative imports
assert!(matches!(level, 0));
let module_name = ModuleName::new(module.as_ref().expect("TODO relative imports"));
let Some(module) = resolve_module(db, module_name.clone())? else {
let module_name =
ModuleName::new(module.as_ref().expect("TODO relative imports")).unwrap();
let Some(module) = resolve_module(db, &module_name)? else {
return Ok(Type::Unknown);
};
@ -343,14 +345,13 @@ fn infer_expr_type(db: &dyn SemanticDb, file_id: FileId, expr: &ast::Expr) -> Qu
#[cfg(test)]
mod tests {
use red_knot_module_resolver::ModuleName;
use ruff_python_ast::name::Name;
use std::path::PathBuf;
use crate::db::tests::TestDb;
use crate::db::{HasJar, SemanticJar};
use crate::module::{
resolve_module, set_module_search_paths, ModuleName, ModuleResolutionInputs,
};
use crate::module::{resolve_module, set_module_search_paths, ModuleResolutionInputs};
use crate::semantic::{infer_symbol_public_type, resolve_global_symbol, Type};
// TODO with virtual filesystem we shouldn't have to write files to disk for these
@ -395,7 +396,8 @@ mod tests {
variable_name: &str,
) -> anyhow::Result<Type> {
let db = &case.db;
let module = resolve_module(db, ModuleName::new(module_name))?.expect("Module to exist");
let module =
resolve_module(db, &ModuleName::new(module_name).unwrap())?.expect("Module to exist");
let symbol = resolve_global_symbol(db, module, variable_name)?.expect("symbol to exist");
Ok(infer_symbol_public_type(db, symbol)?)

View File

@ -14,9 +14,9 @@ license = { workspace = true }
ruff_db = { workspace = true }
ruff_python_stdlib = { workspace = true }
compact_str = { workspace = true }
rustc-hash = { workspace = true }
salsa = { workspace = true }
smol_str = { workspace = true }
tracing = { workspace = true }
zip = { workspace = true }

View File

@ -1,3 +1,4 @@
use compact_str::ToCompactString;
use std::fmt::Formatter;
use std::ops::Deref;
use std::sync::Arc;
@ -12,7 +13,7 @@ use crate::Db;
///
/// Always normalized to the absolute form (never a relative module name, i.e., never `.foo`).
#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
pub struct ModuleName(smol_str::SmolStr);
pub struct ModuleName(compact_str::CompactString);
impl ModuleName {
/// Creates a new module name for `name`. Returns `Some` if `name` is a valid, absolute
@ -27,7 +28,7 @@ impl ModuleName {
/// * A component of a name (the part between two dots) isn't a valid python identifier.
#[inline]
pub fn new(name: &str) -> Option<Self> {
Self::new_from_smol(smol_str::SmolStr::new(name))
Self::is_valid_name(name).then(|| Self(compact_str::CompactString::from(name)))
}
/// Creates a new module name for `name` where `name` is a static string.
@ -56,19 +57,16 @@ impl ModuleName {
/// ```
#[inline]
pub fn new_static(name: &'static str) -> Option<Self> {
Self::new_from_smol(smol_str::SmolStr::new_static(name))
// TODO(Micha): Use CompactString::const_new once we upgrade to 0.8 https://github.com/ParkMyCar/compact_str/pull/336
Self::is_valid_name(name).then(|| Self(compact_str::CompactString::from(name)))
}
fn new_from_smol(name: smol_str::SmolStr) -> Option<Self> {
fn is_valid_name(name: &str) -> bool {
if name.is_empty() {
return None;
return false;
}
if name.split('.').all(is_identifier) {
Some(Self(name))
} else {
None
}
name.split('.').all(is_identifier)
}
/// An iterator over the components of the module name:
@ -97,8 +95,7 @@ impl ModuleName {
/// ```
pub fn parent(&self) -> Option<ModuleName> {
let (parent, _) = self.0.rsplit_once('.')?;
Some(Self(smol_str::SmolStr::new(parent)))
Some(Self(parent.to_compact_string()))
}
/// Returns `true` if the name starts with `other`.
@ -141,7 +138,7 @@ impl ModuleName {
};
let name = if let Some(parent) = path.parent() {
let mut name = String::with_capacity(path.as_str().len());
let mut name = compact_str::CompactString::with_capacity(path.as_str().len());
for component in parent.components() {
name.push_str(component.as_os_str().to_str()?);
@ -151,9 +148,9 @@ impl ModuleName {
// SAFETY: Unwrap is safe here or `parent` would have returned `None`.
name.push_str(path.file_stem().unwrap());
smol_str::SmolStr::from(name)
name
} else {
smol_str::SmolStr::new(path.file_stem()?)
path.file_stem()?.to_compact_string()
};
Some(Self(name))