Skip to content

Commit 9e74fde

Browse files
Michael UsachenkoGitLab
authored andcommitted
feat(code-graph): directory-scoped resolution (SameDirectory) for HCL
1 parent 70bb833 commit 9e74fde

2 files changed

Lines changed: 58 additions & 1 deletion

File tree

crates/code-graph/src/v2/linker/imports.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ pub struct ResolveSettings {
2323
/// Maximum number of results from `global_name` before discarding
2424
/// as too ambiguous. Prevents fan-out on common names.
2525
pub global_name_max_results: usize,
26+
/// When set, restrict the qualified-name fast path (resolver.rs) to
27+
/// definitions in the SAME DIRECTORY as the referencing file. For
28+
/// languages whose module scope is a directory (Terraform/HCL), this
29+
/// stops a `local.x` / `type.name` reference from resolving to a
30+
/// same-named definition in an unrelated module directory.
31+
pub same_directory_scope: bool,
2632
}
2733

2834
impl Default for ResolveSettings {
@@ -34,10 +40,20 @@ impl Default for ResolveSettings {
3440
compound_key_recovery: true,
3541
implicit_scope_on_base: true,
3642
global_name_max_results: 5,
43+
same_directory_scope: false,
3744
}
3845
}
3946
}
4047

48+
/// The directory portion of a file path (everything before the last `/`),
49+
/// or `""` for a repo-root file. Used for directory-scoped resolution.
50+
pub(crate) fn dir_of(path: &str) -> &str {
51+
match path.rfind('/') {
52+
Some(i) => &path[..i],
53+
None => "",
54+
}
55+
}
56+
4157
// ── ImportResolver ──────────────────────────────────────────────
4258

4359
/// Per-file import resolver. Holds shared state so individual
@@ -381,3 +397,28 @@ impl<'a> ImportResolver<'a> {
381397
.collect()
382398
}
383399
}
400+
401+
#[cfg(test)]
402+
mod tests {
403+
use super::dir_of;
404+
405+
#[test]
406+
fn dir_of_nested_path() {
407+
assert_eq!(dir_of("modules/vpc/main.tf"), "modules/vpc");
408+
}
409+
410+
#[test]
411+
fn dir_of_root_file() {
412+
assert_eq!(dir_of("main.tf"), "");
413+
}
414+
415+
#[test]
416+
fn dir_of_empty() {
417+
assert_eq!(dir_of(""), "");
418+
}
419+
420+
#[test]
421+
fn dir_of_trailing_slash() {
422+
assert_eq!(dir_of("a/b/"), "a/b");
423+
}
424+
}

crates/code-graph/src/v2/linker/resolver.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,23 @@ impl<'a> ResolveCtx<'a> {
657657
.by_fqn
658658
.lookup(r.name, |idx| self.graph.def_fqn(idx) == r.name);
659659
if !matches.is_empty() {
660-
return Ok(matches.to_vec());
660+
if self.settings.same_directory_scope {
661+
// Directory-scoped languages (HCL): a qualified ref such as
662+
// `local.x` / `aws_vpc.this` only addresses its own module
663+
// (directory). Keep same-directory matches; if none, fall
664+
// through rather than bind to an unrelated module's def.
665+
let dir = super::imports::dir_of(self.graph.graph[self.file_node].path());
666+
let same: Vec<_> = matches
667+
.iter()
668+
.copied()
669+
.filter(|&idx| super::imports::dir_of(self.graph.graph[idx].path()) == dir)
670+
.collect();
671+
if !same.is_empty() {
672+
return Ok(same);
673+
}
674+
} else {
675+
return Ok(matches.to_vec());
676+
}
661677
}
662678
}
663679

0 commit comments

Comments
 (0)