Skip to content

Commit 96558cd

Browse files
committed
feat(review): follow trait contract edges
1 parent e0ffc38 commit 96558cd

File tree

3 files changed

+302
-97
lines changed

3 files changed

+302
-97
lines changed

TODO.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ This roadmap is derived from deep research into Greptile's public docs, blog, MC
6363

6464
31. [ ] Turn the current symbol graph into a persisted repository graph with durable storage and reload support.
6565
32. [x] Add caller/callee expansion APIs for multi-hop impact analysis from changed symbols.
66-
33. [ ] Add contract edges between interfaces, implementations, and API endpoints.
66+
33. [x] Add contract edges between interfaces, implementations, and API endpoints.
6767
34. [ ] Add "similar implementation" lookup so repeated patterns and divergences are explicit.
6868
35. [x] Add cross-file blast-radius summaries to findings when a change affects many callers.
6969
36. [x] Add graph freshness/version metadata so reviews know whether they are using stale repository intelligence.

src/core/context.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,4 +481,60 @@ mod tests {
481481
.unwrap_or_default()
482482
.contains("calls"));
483483
}
484+
485+
#[tokio::test]
486+
async fn test_fetch_related_definitions_with_index_surfaces_trait_impl_contract_edges() {
487+
let dir = tempfile::tempdir().unwrap();
488+
489+
std::fs::write(
490+
dir.path().join("routes.rs"),
491+
"use crate::request::Request;\nuse crate::search::QueryRunner;\n\npub fn get_profile(runner: &dyn QueryRunner, request: &Request) -> String {\n runner.find_user(request.name())\n}\n",
492+
)
493+
.unwrap();
494+
std::fs::write(
495+
dir.path().join("search.rs"),
496+
"pub trait QueryRunner {\n fn find_user(&self, name: &str) -> String;\n}\n",
497+
)
498+
.unwrap();
499+
std::fs::write(
500+
dir.path().join("db.rs"),
501+
"use crate::search::QueryRunner;\n\npub struct PostgresQueryRunner;\n\nimpl QueryRunner for PostgresQueryRunner {\n fn find_user(&self, name: &str) -> String {\n format!(\"SELECT * FROM users WHERE name = '{}'\", name)\n }\n}\n",
502+
)
503+
.unwrap();
504+
std::fs::write(
505+
dir.path().join("request.rs"),
506+
"pub struct Request {\n name: String,\n}\n\nimpl Request {\n pub fn name(&self) -> &str {\n &self.name\n }\n}\n",
507+
)
508+
.unwrap();
509+
510+
let index = SymbolIndex::build(dir.path(), 20, 200_000, 10, |_| false).unwrap();
511+
let fetcher = ContextFetcher::new(dir.path().to_path_buf());
512+
513+
let chunks = fetcher
514+
.fetch_related_definitions_with_index(
515+
&PathBuf::from("routes.rs"),
516+
&["get_profile".to_string()],
517+
&index,
518+
10,
519+
2,
520+
8,
521+
)
522+
.await
523+
.unwrap();
524+
525+
let impl_chunk = chunks
526+
.iter()
527+
.find(|chunk| {
528+
chunk.file_path == Path::new("db.rs") && chunk.content.contains("find_user")
529+
})
530+
.expect("expected trait implementation context for db.rs");
531+
532+
assert_eq!(impl_chunk.context_type, ContextType::Definition);
533+
assert!(impl_chunk.content.contains("SELECT * FROM users"));
534+
assert!(impl_chunk
535+
.provenance_label()
536+
.as_deref()
537+
.unwrap_or_default()
538+
.contains("calls"));
539+
}
484540
}

0 commit comments

Comments
 (0)