Skip to content

Commit 08a2a94

Browse files
Natalie Jamesonfacebook-github-bot
authored andcommitted
Request global symbol locations from LSP context
Summary: For symbols that are not available in any scope for a file, pass that information back to the server. The server can then lookup the URI, which can either be a file:// uri in the case of a prelude, or starlark: in the case of a native file. Reviewed By: stepancheg Differential Revision: D38452379 fbshipit-source-id: 59c924dce50b5bae0b864da25fa59591b246ad3a
1 parent 4813a8d commit 08a2a94

File tree

2 files changed

+123
-2
lines changed

2 files changed

+123
-2
lines changed

starlark/src/analysis/definition.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ pub enum DefinitionLocation {
6060
source: ResolvedSpan,
6161
literal: String,
6262
},
63+
/// A named symbol was found, but it was not found in any of the current scopes. It
64+
/// should be considered a global symbol, and attempted to be resolved externally.
65+
Unresolved { source: ResolvedSpan, name: String },
6366
/// Either the provided location was not an access of a variable, or no definition
6467
/// could be found.
6568
NotFound,
@@ -178,6 +181,7 @@ impl LspModule {
178181
let line_span = self.ast.codemap.line_span(line as usize);
179182
let current_pos = std::cmp::min(line_span.begin() + col, line_span.end());
180183

184+
// Finalize the results after recursing down from and back up to the the top level scope.
181185
match find_definition_in_scope(&scope, current_pos) {
182186
Definition::Location {
183187
source,
@@ -187,7 +191,10 @@ impl LspModule {
187191
destination: self.ast.codemap.resolve_span(destination),
188192
},
189193
Definition::Name { source, name } => match scope.bound.get(name) {
190-
None => DefinitionLocation::NotFound,
194+
None => DefinitionLocation::Unresolved {
195+
source: self.ast.codemap.resolve_span(source),
196+
name: name.to_owned(),
197+
},
191198
Some((Assigner::Load { path, name }, span)) => DefinitionLocation::LoadedLocation {
192199
source: self.ast.codemap.resolve_span(source),
193200
destination: self.ast.codemap.resolve_span(*span),
@@ -728,7 +735,10 @@ mod test {
728735
module.find_definition(parsed.begin_line("y_var2"), parsed.begin_column("y_var2"))
729736
);
730737
assert_eq!(
731-
DefinitionLocation::NotFound,
738+
DefinitionLocation::Unresolved {
739+
source: parsed.span("z_var"),
740+
name: "z".to_owned()
741+
},
732742
module.find_definition(parsed.begin_line("z_var"), parsed.begin_column("z_var"))
733743
);
734744
Ok(())

starlark/src/lsp/server.rs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,30 @@ impl<T: LspContext> Backend<T> {
527527
_ => None,
528528
}
529529
}
530+
DefinitionLocation::Unresolved { source, name } => {
531+
match self.context.get_url_for_global_symbol(&uri, &name)? {
532+
Some(uri) => {
533+
let loaded_location = self
534+
.get_ast_or_load_from_disk(&uri)?
535+
.and_then(|ast| ast.find_exported_symbol(&name));
536+
match loaded_location {
537+
None => Some(LocationLink {
538+
origin_selection_range: Some(source.into()),
539+
target_uri: uri.try_into()?,
540+
target_range: Range::default(),
541+
target_selection_range: Range::default(),
542+
}),
543+
Some(loaded_location) => Some(LocationLink {
544+
origin_selection_range: Some(source.into()),
545+
target_uri: uri.try_into()?,
546+
target_range: loaded_location.into(),
547+
target_selection_range: loaded_location.into(),
548+
}),
549+
}
550+
}
551+
None => None,
552+
}
553+
}
530554
},
531555
None => None,
532556
};
@@ -1565,4 +1589,91 @@ mod test {
15651589

15661590
Ok(())
15671591
}
1592+
1593+
#[test]
1594+
fn goto_works_for_native_symbols() -> anyhow::Result<()> {
1595+
let foo_uri = temp_file_uri("foo.star");
1596+
let native_uri = Url::parse("starlark:/native/builtin.bzl")?;
1597+
1598+
let mut server = TestServer::new()?;
1599+
1600+
let foo_contents = dedent(
1601+
r#"
1602+
<click_n1>na<n1>t</n1>ive_function1</click_n1>()
1603+
def f(<n2_loc>native_function1</n2_loc>):
1604+
print(<click_n2>nat<n2>i</n2>ve_function1</click_n2>)
1605+
mi<n3>s</n3>sing_global()
1606+
"#,
1607+
)
1608+
.trim()
1609+
.to_owned();
1610+
let native_contents = dedent(
1611+
r#"
1612+
def <n1_loc>native_function1</n1_loc>():
1613+
pass
1614+
1615+
def native_function2():
1616+
pass
1617+
"#,
1618+
)
1619+
.trim()
1620+
.to_owned();
1621+
1622+
let foo = FixtureWithRanges::from_fixture(foo_uri.path(), &foo_contents)?;
1623+
let native = FixtureWithRanges::from_fixture(native_uri.path(), &native_contents)?;
1624+
1625+
server.open_file(foo_uri.clone(), foo.program())?;
1626+
1627+
let expected_n1_location = expected_location_link_from_spans(
1628+
native_uri,
1629+
foo.span("click_n1"),
1630+
native.span("n1_loc"),
1631+
);
1632+
1633+
let goto_definition = goto_definition_request(
1634+
&mut server,
1635+
foo_uri.clone(),
1636+
foo.begin_line("n1"),
1637+
foo.begin_column("n1"),
1638+
);
1639+
let request_id = server.send_request(goto_definition)?;
1640+
let n1_location = goto_definition_response_location(&mut server, request_id)?;
1641+
1642+
assert_eq!(expected_n1_location, n1_location);
1643+
1644+
let expected_n2_location = expected_location_link_from_spans(
1645+
foo_uri.clone(),
1646+
foo.span("click_n2"),
1647+
foo.span("n2_loc"),
1648+
);
1649+
1650+
let goto_definition = goto_definition_request(
1651+
&mut server,
1652+
foo_uri.clone(),
1653+
foo.begin_line("n2"),
1654+
foo.begin_column("n2"),
1655+
);
1656+
let request_id = server.send_request(goto_definition)?;
1657+
let n2_location = goto_definition_response_location(&mut server, request_id)?;
1658+
1659+
assert_eq!(expected_n2_location, n2_location);
1660+
1661+
let goto_definition = goto_definition_request(
1662+
&mut server,
1663+
foo_uri,
1664+
foo.begin_line("n3"),
1665+
foo.begin_column("n3"),
1666+
);
1667+
let request_id = server.send_request(goto_definition)?;
1668+
let n3_response = server.get_response::<GotoDefinitionResponse>(request_id)?;
1669+
match n3_response {
1670+
GotoDefinitionResponse::Array(definitions) if definitions.is_empty() => Ok(()),
1671+
response => Err(anyhow::anyhow!(
1672+
"Expected empty definitions, got `{:?}`",
1673+
response
1674+
)),
1675+
}?;
1676+
1677+
Ok(())
1678+
}
15681679
}

0 commit comments

Comments
 (0)