Fix crate-name import resolution for bin+lib crates
Register the Cargo package name (normalized - -> _) as an alias for
src/lib.rs in the resolver, so 'use <crate>::{...}' in src/main.rs
resolves to src/lib.rs. Adds rustdemo/Cargo.toml fixture and test
coverage; updates blast-radius test for the new transitive edge.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
1 parent
0bb09da454
commit
6875998ab6
4 files changed
+64
-5
No files matched your search
+12
-1
@@ -294,7 +294,7 @@ fn clean(target: &str) -> Option<String> {
|
||||
}
|
||||
|
||||
impl Resolver {
|
||||
pub fn build(files: Vec<(String, i64)>) -> Self {
|
||||
pub fn build(files: Vec<(String, i64)>, crate_name: Option<&str>) -> Self {
|
||||
let mut by_path = HashMap::new();
|
||||
let mut by_name = HashMap::new();
|
||||
let mut by_stem = HashMap::new();
|
||||
@@ -308,6 +308,17 @@ impl Resolver {
|
||||
.unwrap_or(name);
|
||||
by_stem.entry(stem).or_insert(id);
|
||||
}
|
||||
// `use <crate>::…` names the crate's own lib root, which always lives
|
||||
// at src/lib.rs — there is no `<crate>.rs` file. Register the package
|
||||
// name as an alias so bin→lib edges resolve. Workspaces (no [package]
|
||||
// name) are skipped.
|
||||
if let Some(name) = crate_name.filter(|n| !n.is_empty())
|
||||
&& let Some(id) = by_path.get("src/lib.rs").copied()
|
||||
{
|
||||
by_path.insert(name.to_string(), id);
|
||||
by_name.entry(name.to_string()).or_insert(id);
|
||||
by_stem.entry(name.to_string()).or_insert(id);
|
||||
}
|
||||
Resolver {
|
||||
by_path,
|
||||
by_name,
|
||||
|
||||
+28
-2
@@ -18,6 +18,30 @@ pub struct IndexStats {
|
||||
pub imports: i64,
|
||||
}
|
||||
|
||||
/// Read the `[package] name` from `Cargo.toml`, normalized to the crate
|
||||
/// identifier (`-` → `_`) used in `use <crate>::…`.
|
||||
fn read_crate_name(project: &Path) -> Option<String> {
|
||||
let toml = std::fs::read_to_string(project.join("Cargo.toml")).ok()?;
|
||||
let mut in_package = false;
|
||||
for line in toml.lines() {
|
||||
let l = line.trim();
|
||||
if l.starts_with('[') {
|
||||
in_package = l == "[package]";
|
||||
continue;
|
||||
}
|
||||
if in_package
|
||||
&& let Some(v) = l.strip_prefix("name").map(str::trim)
|
||||
&& let Some(v) = v.strip_prefix('=').map(str::trim)
|
||||
{
|
||||
let name = v.trim_matches('"');
|
||||
if !name.is_empty() {
|
||||
return Some(name.replace('-', "_"));
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// (Re)index a project. `recreate` drops any existing index first.
|
||||
pub fn run(project: &Path, db_path: &Path, recreate: bool) -> Result<IndexStats> {
|
||||
if recreate && db_path.exists() {
|
||||
@@ -83,8 +107,10 @@ pub fn run(project: &Path, db_path: &Path, recreate: bool) -> Result<IndexStats>
|
||||
}
|
||||
}
|
||||
|
||||
let resolver =
|
||||
imports::Resolver::build(file_ids.iter().map(|(k, v)| (k.clone(), *v)).collect());
|
||||
let resolver = imports::Resolver::build(
|
||||
file_ids.iter().map(|(k, v)| (k.clone(), *v)).collect(),
|
||||
read_crate_name(project).as_deref(),
|
||||
);
|
||||
|
||||
for f in &changed {
|
||||
let fid = file_ids[&f.rel];
|
||||
|
||||
Reference in new issue
Block a user