From dd9ab736e6f6f0813e15ebf24647f1e1788f2ce1 Mon Sep 17 00:00:00 2001 From: Joshua Batty Date: Thu, 2 Feb 2023 11:02:10 +1100 Subject: [PATCH] Clean up go to definition tests and adds a test for variable tokens (#3955) This PR cleans up the go_to testing code so it's easier to use. I've also added a new example so we can test the definition response of variables in the following cases: - Variable expressions - Function arguments - Struct fields - Enum fields - Tuple elements - Array elements - Scoped declarations - If let scopes - Shadowing --------- Co-authored-by: Sophie Dankel <47993817+sdankel@users.noreply.github.com> --- sway-lsp/src/server.rs | 293 +++++++++++------- .../test/fixtures/tokens/turbofish/Forc.lock | 12 +- .../fixtures/tokens/turbofish/src/main.sw | 2 +- .../test/fixtures/tokens/variables/.gitignore | 2 + .../test/fixtures/tokens/variables/Forc.lock | 13 + .../test/fixtures/tokens/variables/Forc.toml | 8 + .../fixtures/tokens/variables/src/main.sw | 55 ++++ 7 files changed, 266 insertions(+), 119 deletions(-) create mode 100644 sway-lsp/test/fixtures/tokens/variables/.gitignore create mode 100644 sway-lsp/test/fixtures/tokens/variables/Forc.lock create mode 100644 sway-lsp/test/fixtures/tokens/variables/Forc.toml create mode 100644 sway-lsp/test/fixtures/tokens/variables/src/main.sw diff --git a/sway-lsp/src/server.rs b/sway-lsp/src/server.rs index 32c2695b94d..3d7445be962 100644 --- a/sway-lsp/src/server.rs +++ b/sway-lsp/src/server.rs @@ -634,6 +634,17 @@ mod tests { ExitedError, LspService, }; + /// Holds the information needed to check the response of a goto definition request. + struct GotoDefintion<'a> { + req_uri: &'a Url, + req_line: i32, + req_char: i32, + def_line: i32, + def_start_char: i32, + def_end_char: i32, + def_path: &'a str, + } + fn load_sway_example(manifest_dir: PathBuf) -> (Url, String) { let src_path = manifest_dir.join("src/main.sw"); let mut file = fs::File::open(&src_path).unwrap(); @@ -807,103 +818,35 @@ mod tests { build_request_with_id("textDocument/definition", params, id) } - fn definition_response( - uri: &Url, - start_line: i32, - start_char: i32, - end_line: i32, - end_char: i32, - id: i64, - ) -> Response { - Response::from_ok( - id.into(), - json!({ - "range": { - "end": { - "character": end_char, - "line": end_line, - }, - "start": { - "character": start_char, - "line": start_line, - } - }, - "uri": uri, - }), - ) - } - - async fn doc_comments_definition_check( + async fn definition_check<'a>( service: &mut LspService, - uri: &Url, - token_req_line: i32, - token_def_line: i32, + go_to: &'a GotoDefintion<'a>, id: i64, ) -> Request { - let definition = definition_request(uri, token_req_line, 24, id); - let response = call_request(service, definition.clone()).await; - let expected = definition_response(uri, token_def_line, 7, token_def_line, 11, id); - assert_json_eq!(expected, response.ok().unwrap()); - definition - } - - async fn turbofish_definition_check_option( - service: &mut LspService, - uri: &Url, - token_line: i32, - token_char: i32, - id: i64, - ) { - let definition = definition_request(uri, token_line, token_char, id); - let response = call_request(service, definition).await.unwrap().unwrap(); - let json = response.result().unwrap(); - let uri = json - .as_object() - .unwrap() - .get("uri") - .unwrap() - .as_str() - .unwrap(); - assert!(uri.ends_with("sway-lib-std/src/option.sw")); - } - - async fn turbofish_definition_check_result( - service: &mut LspService, - uri: &Url, - token_line: i32, - token_char: i32, - id: i64, - ) { - let definition = definition_request(uri, token_line, token_char, id); - let response = call_request(service, definition).await.unwrap().unwrap(); - let json = response.result().unwrap(); - let uri = json - .as_object() - .unwrap() - .get("uri") - .unwrap() - .as_str() - .unwrap(); - assert!(uri.ends_with("sway-lib-std/src/result.sw")); - } - async fn traits_definition_check( - service: &mut LspService, - uri: &Url, - token_line: i32, - token_char: i32, - id: i64, - ) { - let definition = definition_request(uri, token_line, token_char, id); - let response = call_request(service, definition).await.unwrap().unwrap(); - let json = response.result().unwrap(); - let uri = json - .as_object() - .unwrap() - .get("uri") + let definition = definition_request(go_to.req_uri, go_to.req_line, go_to.req_char, id); + let response = call_request(service, definition.clone()) + .await .unwrap() - .as_str() .unwrap(); - assert!(uri.ends_with("sway-lsp/test/fixtures/tokens/traits/src/traits.sw")); + let value = response.result().unwrap().clone(); + if let GotoDefinitionResponse::Scalar(response) = serde_json::from_value(value).unwrap() { + let uri = response.uri.as_str(); + let range = json!({ + "end": { + "character": go_to.def_end_char, + "line": go_to.def_line, + }, + "start": { + "character": go_to.def_start_char, + "line": go_to.def_line, + } + }); + assert_json_eq!(response.range, range); + assert!(uri.ends_with(go_to.def_path)); + } else { + panic!("Expected GotoDefinitionResponse::Scalar"); + } + definition } async fn hover_request(service: &mut LspService, uri: &Url) -> Request { @@ -1268,9 +1211,19 @@ mod tests { async fn lsp_syncs_with_workspace_edits() { let (mut service, _) = LspService::new(Backend::new); let uri = init_and_open(&mut service, doc_comments_dir()).await; - let _ = doc_comments_definition_check(&mut service, &uri, 44, 19, 1).await; + let mut go_to = GotoDefintion { + req_uri: &uri, + req_line: 44, + req_char: 24, + def_line: 19, + def_start_char: 7, + def_end_char: 11, + def_path: uri.as_str(), + }; + let _ = definition_check(&mut service, &go_to, 1).await; let _ = did_change_request(&mut service, &uri).await; - let _ = doc_comments_definition_check(&mut service, &uri, 45, 20, 2).await; + go_to.def_line = 20; + definition_check_with_req_offset(&mut service, &mut go_to, 45, 24, 2).await; shutdown_and_exit(&mut service).await; } @@ -1289,10 +1242,31 @@ mod tests { async fn go_to_definition() { let (mut service, _) = LspService::new(Backend::new); let uri = init_and_open(&mut service, doc_comments_dir()).await; - let _ = doc_comments_definition_check(&mut service, &uri, 44, 19, 1).await; + let go_to = GotoDefintion { + req_uri: &uri, + req_line: 44, + req_char: 24, + def_line: 19, + def_start_char: 7, + def_end_char: 11, + def_path: uri.as_str(), + }; + let _ = definition_check(&mut service, &go_to, 1).await; shutdown_and_exit(&mut service).await; } + async fn definition_check_with_req_offset<'a>( + service: &mut LspService, + go_to: &mut GotoDefintion<'a>, + req_line: i32, + req_char: i32, + id: i64, + ) { + go_to.req_line = req_line; + go_to.req_char = req_char; + let _ = definition_check(service, go_to, id).await; + } + #[tokio::test] async fn go_to_definition_inside_turbofish() { let (mut service, _) = LspService::new(Backend::new); @@ -1302,19 +1276,39 @@ mod tests { ) .await; - turbofish_definition_check_option(&mut service, &uri, 15, 12, 1).await; - turbofish_definition_check_option(&mut service, &uri, 16, 17, 2).await; - turbofish_definition_check_option(&mut service, &uri, 17, 29, 3).await; - turbofish_definition_check_option(&mut service, &uri, 18, 19, 4).await; - - turbofish_definition_check_option(&mut service, &uri, 20, 13, 5).await; - turbofish_definition_check_result(&mut service, &uri, 20, 19, 6).await; - turbofish_definition_check_option(&mut service, &uri, 21, 19, 7).await; - turbofish_definition_check_result(&mut service, &uri, 21, 25, 8).await; - turbofish_definition_check_option(&mut service, &uri, 22, 29, 9).await; - turbofish_definition_check_result(&mut service, &uri, 22, 36, 10).await; - turbofish_definition_check_option(&mut service, &uri, 23, 18, 11).await; - turbofish_definition_check_result(&mut service, &uri, 23, 27, 12).await; + let mut opt_go_to = GotoDefintion { + req_uri: &uri, + req_line: 15, + req_char: 12, + def_line: 80, + def_start_char: 9, + def_end_char: 15, + def_path: "sway-lib-std/src/option.sw", + }; + // option.sw + let _ = definition_check(&mut service, &opt_go_to, 1).await; + definition_check_with_req_offset(&mut service, &mut opt_go_to, 16, 17, 2).await; + definition_check_with_req_offset(&mut service, &mut opt_go_to, 17, 29, 3).await; + definition_check_with_req_offset(&mut service, &mut opt_go_to, 18, 19, 4).await; + definition_check_with_req_offset(&mut service, &mut opt_go_to, 20, 13, 5).await; + definition_check_with_req_offset(&mut service, &mut opt_go_to, 21, 19, 6).await; + definition_check_with_req_offset(&mut service, &mut opt_go_to, 22, 29, 7).await; + definition_check_with_req_offset(&mut service, &mut opt_go_to, 23, 18, 8).await; + + let mut res_go_to = GotoDefintion { + req_uri: &uri, + req_line: 20, + req_char: 19, + def_line: 60, + def_start_char: 9, + def_end_char: 15, + def_path: "sway-lib-std/src/result.sw", + }; + // result.sw + let _ = definition_check(&mut service, &res_go_to, 9).await; + definition_check_with_req_offset(&mut service, &mut res_go_to, 21, 25, 10).await; + definition_check_with_req_offset(&mut service, &mut res_go_to, 22, 36, 11).await; + definition_check_with_req_offset(&mut service, &mut res_go_to, 23, 27, 12).await; shutdown_and_exit(&mut service).await; } @@ -1328,10 +1322,85 @@ mod tests { ) .await; - traits_definition_check(&mut service, &uri, 6, 10, 1).await; - traits_definition_check(&mut service, &uri, 7, 10, 2).await; - traits_definition_check(&mut service, &uri, 7, 20, 3).await; - traits_definition_check(&mut service, &uri, 10, 6, 4).await; + let mut trait_go_to = GotoDefintion { + req_uri: &uri, + req_line: 6, + req_char: 10, + def_line: 2, + def_start_char: 10, + def_end_char: 15, + def_path: "sway-lsp/test/fixtures/tokens/traits/src/traits.sw", + }; + + let _ = definition_check(&mut service, &trait_go_to, 1).await; + definition_check_with_req_offset(&mut service, &mut trait_go_to, 7, 10, 2).await; + definition_check_with_req_offset(&mut service, &mut trait_go_to, 10, 6, 3).await; + trait_go_to.req_line = 7; + trait_go_to.req_char = 20; + trait_go_to.def_line = 3; + let _ = definition_check(&mut service, &trait_go_to, 3).await; + + shutdown_and_exit(&mut service).await; + } + + #[tokio::test] + async fn go_to_definition_for_variables() { + let (mut service, _) = LspService::new(Backend::new); + let uri = init_and_open( + &mut service, + test_fixtures_dir().join("tokens").join("variables"), + ) + .await; + + let mut go_to = GotoDefintion { + req_uri: &uri, + req_line: 23, + req_char: 26, + def_line: 22, + def_start_char: 8, + def_end_char: 17, + def_path: uri.as_str(), + }; + // Variable expressions + let _ = definition_check(&mut service, &go_to, 1).await; + + // Function arguments + go_to.def_line = 23; + definition_check_with_req_offset(&mut service, &mut go_to, 28, 35, 2).await; + + // Struct fields + go_to.def_line = 22; + definition_check_with_req_offset(&mut service, &mut go_to, 31, 45, 3).await; + + // Enum fields + go_to.def_line = 22; + definition_check_with_req_offset(&mut service, &mut go_to, 34, 39, 4).await; + + // Tuple elements + go_to.def_line = 24; + definition_check_with_req_offset(&mut service, &mut go_to, 37, 20, 5).await; + + // Array elements + go_to.def_line = 25; + definition_check_with_req_offset(&mut service, &mut go_to, 40, 20, 6).await; + + // Scoped declarations + go_to.def_line = 44; + go_to.def_start_char = 12; + go_to.def_end_char = 21; + definition_check_with_req_offset(&mut service, &mut go_to, 45, 13, 7).await; + + // If let scopes + go_to.def_line = 50; + go_to.def_start_char = 38; + go_to.def_end_char = 39; + definition_check_with_req_offset(&mut service, &mut go_to, 50, 47, 8).await; + + // Shadowing + go_to.def_line = 50; + go_to.def_start_char = 8; + go_to.def_end_char = 17; + definition_check_with_req_offset(&mut service, &mut go_to, 53, 29, 9).await; shutdown_and_exit(&mut service).await; } diff --git a/sway-lsp/test/fixtures/tokens/turbofish/Forc.lock b/sway-lsp/test/fixtures/tokens/turbofish/Forc.lock index e888a89af9b..1732b83b6cb 100644 --- a/sway-lsp/test/fixtures/tokens/turbofish/Forc.lock +++ b/sway-lsp/test/fixtures/tokens/turbofish/Forc.lock @@ -1,13 +1,13 @@ [[package]] name = 'core' -source = 'path+from-root-C35A83477D5B662F' +source = 'path+from-root-70DB58A2053CC06C' + +[[package]] +name = 'std' +source = 'path+from-root-70DB58A2053CC06C' +dependencies = ['core'] [[package]] name = 'turbofish' source = 'member' dependencies = ['std'] - -[[package]] -name = 'std' -source = 'path+from-root-C35A83477D5B662F' -dependencies = ['core'] diff --git a/sway-lsp/test/fixtures/tokens/turbofish/src/main.sw b/sway-lsp/test/fixtures/tokens/turbofish/src/main.sw index 1e110813ccf..8b9b26e54c1 100644 --- a/sway-lsp/test/fixtures/tokens/turbofish/src/main.sw +++ b/sway-lsp/test/fixtures/tokens/turbofish/src/main.sw @@ -24,4 +24,4 @@ pub fn g(amount: u64, to: Identity) { let b = B::>>{}; } -fn fun(t: T){} +fn fun(t: T){} \ No newline at end of file diff --git a/sway-lsp/test/fixtures/tokens/variables/.gitignore b/sway-lsp/test/fixtures/tokens/variables/.gitignore new file mode 100644 index 00000000000..77d3844f58c --- /dev/null +++ b/sway-lsp/test/fixtures/tokens/variables/.gitignore @@ -0,0 +1,2 @@ +out +target diff --git a/sway-lsp/test/fixtures/tokens/variables/Forc.lock b/sway-lsp/test/fixtures/tokens/variables/Forc.lock new file mode 100644 index 00000000000..09635af64d7 --- /dev/null +++ b/sway-lsp/test/fixtures/tokens/variables/Forc.lock @@ -0,0 +1,13 @@ +[[package]] +name = 'core' +source = 'path+from-root-516E592B177796C8' + +[[package]] +name = 'std' +source = 'path+from-root-516E592B177796C8' +dependencies = ['core'] + +[[package]] +name = 'variables' +source = 'member' +dependencies = ['std'] diff --git a/sway-lsp/test/fixtures/tokens/variables/Forc.toml b/sway-lsp/test/fixtures/tokens/variables/Forc.toml new file mode 100644 index 00000000000..89f0c3a8d4e --- /dev/null +++ b/sway-lsp/test/fixtures/tokens/variables/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "variables" + +[dependencies] +std = { path = "../../../../../sway-lib-std" } diff --git a/sway-lsp/test/fixtures/tokens/variables/src/main.sw b/sway-lsp/test/fixtures/tokens/variables/src/main.sw new file mode 100644 index 00000000000..29c91a13064 --- /dev/null +++ b/sway-lsp/test/fixtures/tokens/variables/src/main.sw @@ -0,0 +1,55 @@ +script; + +struct ExampleStruct { + variable: u32, +} + +enum ExampleEnum { + Variants: u32, +} + +// Function parameters +fn example_function(variable: u32) -> u32 { + variable +} + +enum Result { + Ok: T, + Err: E, +} + +fn main() { + // Variable usage: Variable Declarations + let variable1 = 10; + let variable2 = variable1; + let variable3 = false; + let variable4 = "test"; + + // Variable usage: Function arguments + let _ = example_function(variable2); + + // Variable usage: Struct fields + let _ = ExampleStruct { variable: variable1 }; + + // Variable usage: Enum variants + let _ = ExampleEnum::Variants(variable1); + + // Variable usage: Tuple elements + let _ = (variable3, 20); + + // Variable usage: Array elements + let _ = [variable4, 20]; + + // Variable usage: Scoped Declarations + { + let variable1 = 1234; + log(variable1); + } + + // Variable usage: If let scopes + let x: Result = Result::Ok::(5u64); + let variable3 = if let Result::Ok(y) = x { y + 10 } else { 1 }; + + // Variable usage: Shadowing + let variable5 = variable3; +} \ No newline at end of file