Skip to content

Commit 7a1b1a0

Browse files
author
Catherine Gasnier
committed
Fix bugs with SCIP field enclosing_range
Summary: Test Plan: Reviewers: Subscribers: Tasks: Tags:
1 parent 3d78b3f commit 7a1b1a0

File tree

2 files changed

+82
-16
lines changed

2 files changed

+82
-16
lines changed

crates/ide/src/static_index.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,14 @@ pub struct TokenStaticData {
4444
// FIXME: Make this have the lifetime of the database.
4545
pub documentation: Option<Documentation<'static>>,
4646
pub hover: Option<HoverResult>,
47-
/// The position of the token itself.
48-
///
49-
/// For example, in `fn foo() {}` this is the position of `foo`.
47+
/// Position that a go-to-def operation on this token would jump to.
48+
/// This means that if we have `fn foo() {}` and the token is `foo`, then this is the
49+
/// position of the `foo` name in the function definition
50+
/// (and not the range of the whole definition).
51+
/// If the token is a definition name, then that's the position of the token itself.
5052
pub definition: Option<FileRange>,
51-
/// The position of the entire definition that this token belongs to.
52-
///
53-
/// For example, in `fn foo() {}` this is the position from `fn`
54-
/// to the closing brace.
55-
pub definition_body: Option<FileRange>,
53+
/// The range of the parent token in the syntax tree.
54+
pub enclosing_range: Option<FileRange>,
5655
pub references: Vec<ReferenceData>,
5756
pub moniker: Option<MonikerResult>,
5857
pub display_name: Option<String>,
@@ -249,10 +248,13 @@ impl StaticIndex<'_> {
249248
definition: def.try_to_nav(&sema).map(UpmappingResult::call_site).map(|it| {
250249
FileRange { file_id: it.file_id, range: it.focus_or_full_range() }
251250
}),
252-
definition_body: def
253-
.try_to_nav(&sema)
254-
.map(UpmappingResult::call_site)
255-
.map(|it| FileRange { file_id: it.file_id, range: it.full_range }),
251+
enclosing_range: {
252+
let parent = scope_node.ancestors().find(|ancestor| {
253+
let ancestor_range = ancestor.text_range();
254+
ancestor_range.contains_range(range) && ancestor_range != range
255+
});
256+
parent.map(|p| FileRange { file_id, range: p.text_range() })
257+
},
256258
references: vec![],
257259
moniker: current_crate.and_then(|cc| def_to_moniker(self.db, def, cc)),
258260
display_name: def

crates/rust-analyzer/src/cli/scip.rs

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,9 +189,9 @@ impl flags::Scip {
189189
symbol_roles |= scip_types::SymbolRole::Definition as i32;
190190
}
191191

192-
let enclosing_range = match token.definition_body {
193-
Some(def_body) if def_body.file_id == file_id => {
194-
text_range_to_scip_range(&line_index, def_body.range)
192+
let enclosing_range = match token.enclosing_range {
193+
Some(range) if range.file_id == file_id => {
194+
text_range_to_scip_range(&line_index, range.range)
195195
}
196196
_ => Vec::new(),
197197
};
@@ -944,6 +944,70 @@ pub mod example_mod {
944944
range: TextRange::new(0.into(), 11.into()),
945945
};
946946

947-
assert_eq!(token.definition_body, Some(expected_range));
947+
assert_eq!(token.enclosing_range, Some(expected_range));
948+
}
949+
950+
#[test]
951+
fn method_in_impl_has_enclosing_range() {
952+
let s = r#"
953+
struct Foo;
954+
impl Foo {
955+
fn method(&self) {}
956+
}
957+
"#;
958+
959+
let mut host = AnalysisHost::default();
960+
let change_fixture = ChangeFixture::parse(s);
961+
host.raw_database_mut().apply_change(change_fixture.change);
962+
963+
let analysis = host.analysis();
964+
let si = StaticIndex::compute(
965+
&analysis,
966+
VendoredLibrariesConfig::Included {
967+
workspace_root: &VfsPath::new_virtual_path("/workspace".to_owned()),
968+
},
969+
);
970+
971+
let file = si.files.first().unwrap();
972+
// Find the definition token for `method` - it should have a enclosing_range
973+
let method_token = file
974+
.tokens
975+
.iter()
976+
.find_map(|(_range, id)| {
977+
let token = si.tokens.get(*id).unwrap();
978+
// Check if this is the method definition by looking at the display name and kind
979+
if token.display_name.as_deref() == Some("method")
980+
&& token.kind == SymbolInformationKind::Method
981+
{
982+
Some(id)
983+
} else {
984+
None
985+
}
986+
})
987+
.expect("Should find method token");
988+
989+
let token = si.tokens.get(*method_token).unwrap();
990+
991+
// The enclosing_range should exist and should be just the method, not the entire impl block
992+
let def_body = token.enclosing_range.expect("Method should have a enclosing_range");
993+
994+
// The enclosing_range should cover just the method
995+
// Based on the test output, we can see the actual range is 27..46
996+
// Let's verify it's not the entire impl block (which would be larger)
997+
let impl_start = s.find("impl Foo").unwrap();
998+
let impl_end = s.rfind('}').unwrap() + 1;
999+
let impl_range = TextRange::new((impl_start as u32).into(), (impl_end as u32).into());
1000+
1001+
// The method body should NOT be the same as the entire impl block
1002+
assert_ne!(
1003+
def_body.range, impl_range,
1004+
"Method enclosing range should not be the entire impl block"
1005+
);
1006+
1007+
// The method body should be smaller than the impl block
1008+
assert!(
1009+
def_body.range.len() < impl_range.len(),
1010+
"Method enclosing range should be smaller than the impl block"
1011+
);
9481012
}
9491013
}

0 commit comments

Comments
 (0)