From 1ba7081bf2730335393ded114f6c23ed876f5c62 Mon Sep 17 00:00:00 2001 From: Marek Kaput Date: Sun, 22 Dec 2024 00:54:04 +0100 Subject: [PATCH] Add starting real goto definition tests (#118) --- tests/e2e/{goto.rs => goto_definition.rs} | 49 ++++-- tests/e2e/main.rs | 2 +- tests/test_data/goto/enum_variants.txt | 28 ++++ tests/test_data/goto/inline_macros.txt | 17 ++ tests/test_data/goto/items.txt | 185 ++++++++++++++++++++++ tests/test_data/goto/modules.txt | 64 ++++++++ tests/test_data/goto/struct_members.txt | 43 +++-- tests/test_data/goto/variables.txt | 60 +++++++ 8 files changed, 421 insertions(+), 27 deletions(-) rename tests/e2e/{goto.rs => goto_definition.rs} (67%) create mode 100644 tests/test_data/goto/enum_variants.txt create mode 100644 tests/test_data/goto/inline_macros.txt create mode 100644 tests/test_data/goto/items.txt create mode 100644 tests/test_data/goto/modules.txt create mode 100644 tests/test_data/goto/variables.txt diff --git a/tests/e2e/goto.rs b/tests/e2e/goto_definition.rs similarity index 67% rename from tests/e2e/goto.rs rename to tests/e2e/goto_definition.rs index 3760d2ad..355398d6 100644 --- a/tests/e2e/goto.rs +++ b/tests/e2e/goto_definition.rs @@ -1,5 +1,6 @@ use cairo_lang_test_utils::parse_test_file::TestRunnerResult; use cairo_lang_utils::ordered_hash_map::OrderedHashMap; +use indoc::indoc; use lsp_types::{ ClientCapabilities, GotoCapability, GotoDefinitionParams, GotoDefinitionResponse, TextDocumentClientCapabilities, TextDocumentPositionParams, lsp_request, @@ -9,12 +10,17 @@ use crate::support::cursor::{peek_caret, peek_selection}; use crate::support::{cursors, sandbox}; cairo_lang_test_utils::test_file_test!( - goto, + goto_definition, "tests/test_data/goto", { + enum_variants: "enum_variants.txt", + inline_macros: "inline_macros.txt", + items: "items.txt", + modules: "modules.txt", struct_members: "struct_members.txt", + variables: "variables.txt", }, - test_goto_members + test_goto_definition ); fn caps(base: ClientCapabilities) -> ClientCapabilities { @@ -32,13 +38,7 @@ fn caps(base: ClientCapabilities) -> ClientCapabilities { } } -/// Perform hover test. -/// -/// This function spawns a sandbox language server with the given code in the `src/lib.cairo` file. -/// The Cairo source code is expected to contain caret markers. -/// The function then requests goto definition information at each caret position and compares -/// the result with the expected hover information from the snapshot file. -fn test_goto_members( +fn test_goto_definition( inputs: &OrderedHashMap, _args: &OrderedHashMap, ) -> TestRunnerResult { @@ -46,7 +46,13 @@ fn test_goto_members( let mut ls = sandbox! { files { - "cairo_project.toml" => inputs["cairo_project.toml"].clone(), + "cairo_project.toml" => indoc! {r#" + [crate_roots] + hello = "src" + + [config.global] + edition = "2024_07" + "#}, "src/lib.cairo" => cairo.clone(), } client_capabilities = caps; @@ -68,17 +74,26 @@ fn test_goto_members( work_done_progress_params: Default::default(), partial_result_params: Default::default(), }; - let goto_definition_response = + let response = ls.send_request::(code_action_params); - if let Some(goto_definition_response) = goto_definition_response { - if let GotoDefinitionResponse::Scalar(location) = goto_definition_response { + match response { + Some(GotoDefinitionResponse::Scalar(location)) => { + report.push_str("---\n"); report.push_str(&peek_selection(&cairo, &location.range)); - } else { - panic!("Unexpected GotoDefinitionResponse variant.") } - } else { - panic!("Goto definition request failed."); + Some(GotoDefinitionResponse::Array(locations)) => { + for location in locations { + report.push_str("---\n"); + report.push_str(&peek_selection(&cairo, &location.range)); + } + } + Some(GotoDefinitionResponse::Link(_)) => { + panic!("unexpected GotoDefinitionResponse::Link"); + } + None => { + report.push_str("None"); + } } goto_definitions.insert(format!("Goto definition #{}", n), report); } diff --git a/tests/e2e/main.rs b/tests/e2e/main.rs index 3a9e0c00..ae05b23a 100644 --- a/tests/e2e/main.rs +++ b/tests/e2e/main.rs @@ -1,7 +1,7 @@ mod analysis; mod code_actions; mod completions; -mod goto; +mod goto_definition; mod hover; mod macro_expand; mod semantic_tokens; diff --git a/tests/test_data/goto/enum_variants.txt b/tests/test_data/goto/enum_variants.txt new file mode 100644 index 00000000..2a79f294 --- /dev/null +++ b/tests/test_data/goto/enum_variants.txt @@ -0,0 +1,28 @@ +//! > Test goto definition of an enum variant. + +//! > test_runner_name +test_goto_definition + +//! > cairo_code +enum Foo { + Bar, + Baz, +} + +fn main() { + let foo = Foo::Bar; + match foo { + Foo::Bar => {} + _ => {} + } +} + +//! > Goto definition #0 + let foo = Foo::Bar; +--- + Bar, + +//! > Goto definition #1 + Foo::Bar => {} +--- + Bar, diff --git a/tests/test_data/goto/inline_macros.txt b/tests/test_data/goto/inline_macros.txt new file mode 100644 index 00000000..361ee3e3 --- /dev/null +++ b/tests/test_data/goto/inline_macros.txt @@ -0,0 +1,17 @@ +//! > Test goto definition on inline macro. + +//! > test_runner_name +test_goto_definition + +//! > cairo_code +// FIXME(#116): This is wrong. +fn main() { + print!("Hello, world!"); +} + +//! > Goto definition #0 + print!("Hello, world!"); +--- +fn main() { + print!("Hello, world!"); +} diff --git a/tests/test_data/goto/items.txt b/tests/test_data/goto/items.txt new file mode 100644 index 00000000..1e410026 --- /dev/null +++ b/tests/test_data/goto/items.txt @@ -0,0 +1,185 @@ +//! > Test goto definition of a function. + +//! > test_runner_name +test_goto_definition + +//! > cairo_code +fn main() { + foo(); +} + +fn foo() {} // good + +mod bar { + fn foo() {} // bad +} + +//! > Goto definition #0 + foo(); +--- +fn foo() {} // good + +//! > ========================================================================== + +//! > Test goto definition of a struct. + +//! > test_runner_name +test_goto_definition + +//! > cairo_code +struct Foo { + field: felt252, +} + +fn main() { + let foo = Foo { field: 0 }; +} + +fn calc(foo: Foo) {} + +//! > Goto definition #0 + let foo = Foo { field: 0 }; +--- +struct Foo { + field: felt252, +} + +//! > Goto definition #1 +fn calc(foo: Foo) {} +--- +struct Foo { + field: felt252, +} + +//! > ========================================================================== + +//! > Test goto definition of an enum. + +//! > test_runner_name +test_goto_definition + +//! > cairo_code +enum Foo { + Bar, + Baz, +} + +fn main() { + let foo = Foo::Bar; +} + +fn calc(foo: Foo) {} + +//! > Goto definition #0 + let foo = Foo::Bar; +--- +enum Foo { + Bar, + Baz, +} + +//! > Goto definition #1 +fn calc(foo: Foo) {} +--- +enum Foo { + Bar, + Baz, +} + +//! > ========================================================================== + +//! > Test goto definition with traits. + +//! > test_runner_name +test_goto_definition + +//! > cairo_code +pub trait ShapeGeometry { + fn boundary(self: T) -> u64; + fn area(self: T) -> u64; +} + +mod rectangle { + use super::ShapeGeometry; + + #[derive(Copy, Drop)] + pub struct Rectangle { + pub height: u64, + pub width: u64, + } + + impl RectangleGeometry of ShapeGeometryngle> { + fn boundary(self: Rectangle) -> u64 { + 2 * (self.height + self.width) + } + fn area(self: Rectangle) -> u64 { + self.height * self.width + } + } +} + +use rectangle::Rectangle; + +fn main() { + let rect = Rectangle { height: 5, width: 7 }; + let area = ShapeGeometry::area(rect); +} + +//! > Goto definition #0 + use super::ShapeGeometry; +--- +pub trait ShapeGeometry { + fn boundary(self: T) -> u64; + fn area(self: T) -> u64; +} + +//! > Goto definition #1 + impl RectangleGeometry of ShapeGeometry { +--- +pub trait ShapeGeometry { + fn boundary(self: T) -> u64; + fn area(self: T) -> u64; +} + +//! > Goto definition #2 + impl RectangleGeometry of ShapeGeometryngle> { +--- + #[derive(Copy, Drop)] + pub struct Rectangle { + pub height: u64, + pub width: u64, + } + +//! > Goto definition #3 + fn boundary(self: Rectangle) -> u64 { +--- + impl RectangleGeometry of ShapeGeometry { + fn boundary(self: Rectangle) -> u64 { + 2 * (self.height + self.width) + } + fn area(self: Rectangle) -> u64 { + self.height * self.width + } + } + +//! > Goto definition #4 + fn boundary(self: Rectangle) -> u64 { +--- + #[derive(Copy, Drop)] + pub struct Rectangle { + pub height: u64, + pub width: u64, + } + +//! > Goto definition #5 + let area = ShapeGeometry::area(rect); +--- +pub trait ShapeGeometry { + fn boundary(self: T) -> u64; + fn area(self: T) -> u64; +} + +//! > Goto definition #6 + let area = ShapeGeometry::area(rect); +--- + fn area(self: T) -> u64; diff --git a/tests/test_data/goto/modules.txt b/tests/test_data/goto/modules.txt new file mode 100644 index 00000000..5f73284b --- /dev/null +++ b/tests/test_data/goto/modules.txt @@ -0,0 +1,64 @@ +//! > Test goto definition of a module. + +//! > test_runner_name +test_goto_definition + +//! > cairo_code +fn main() { + module::bar::foo(); +} + +mod module { + // good + mod module { + // bad + } + + mod bar { // good + fn foo() {} + } +} + +//! > Goto definition #0 + module::bar::foo(); +--- +mod module { + // good + mod module { + // bad + } + + mod bar { // good + fn foo() {} + } +} + +//! > Goto definition #1 + module::bar::foo(); +--- + mod bar { // good + fn foo() {} + } + +//! > ========================================================================== + +//! > Test goto definition of a function in a submodule. + +//! > test_runner_name +test_goto_definition + +//! > cairo_code +fn main() { + module::foo(); +} + +fn foo() {} // bad + +mod module { + fn foo() {} // good +} + +//! > Goto definition #0 + module::foo(); +--- + fn foo() {} // good diff --git a/tests/test_data/goto/struct_members.txt b/tests/test_data/goto/struct_members.txt index 0197c118..48fda962 100644 --- a/tests/test_data/goto/struct_members.txt +++ b/tests/test_data/goto/struct_members.txt @@ -1,14 +1,7 @@ -//! > Test simple goto definition on struct members. +//! > Test goto definition on struct member access. //! > test_runner_name -test_goto_members - -//! > cairo_project.toml -[crate_roots] -hello = "src" - -[config.global] -edition = "2024_07" +test_goto_definition //! > cairo_code #[derive(Drop)] @@ -23,16 +16,48 @@ fn calculate_area(rectangle: Rectangle) -> u64 { //! > Goto definition #0 rectangle.width * rectangle.height +--- fn calculate_area(rectangle: Rectangle) -> u64 { //! > Goto definition #1 rectangle.width * rectangle.height +--- width: u64, //! > Goto definition #2 rectangle.width * rectangle.height +--- fn calculate_area(rectangle: Rectangle) -> u64 { //! > Goto definition #3 rectangle.width * rectangle.height +--- + height: u64, + +//! > ========================================================================== + +//! > Test goto definition on struct member constructor. + +//! > test_runner_name +test_goto_definition + +//! > cairo_code +#[derive(Drop)] +struct Rectangle { + width: u64, + height: u64, +} + +fn main() { + let rectangle = Rectangle { width: 0, height: 0 }; +} + +//! > Goto definition #0 + let rectangle = Rectangle { width: 0, height: 0 }; +--- + width: u64, + +//! > Goto definition #1 + let rectangle = Rectangle { width: 0, height: 0 }; +--- height: u64, diff --git a/tests/test_data/goto/variables.txt b/tests/test_data/goto/variables.txt new file mode 100644 index 00000000..3ef6d2cf --- /dev/null +++ b/tests/test_data/goto/variables.txt @@ -0,0 +1,60 @@ +//! > Test goto definition on variables. + +//! > test_runner_name +test_goto_definition + +//! > cairo_code +fn main() { + let abc: felt252 = 0; // good + let _ = abc * 2; +} + +fn foo() { + let abc: felt252 = 1; // bad +} + +//! > Goto definition #0 + let _ = abc * 2; +--- + let abc: felt252 = 0; // good + +//! > ========================================================================== + +//! > Test goto definition on function parameters. + +//! > test_runner_name +test_goto_definition + +//! > cairo_code +fn main(abc: felt252, def: felt252) { + let _ = abc * 2; +} + +fn foo(abc: felt252) {} + +//! > Goto definition #0 + let _ = abc * 2; +--- +fn main(abc: felt252, def: felt252) { + +//! > ========================================================================== + +//! > Test goto definition on closure parameters. + +//! > test_runner_name +test_goto_definition + +//! > cairo_code +fn foo(a: felt252) -> felt252 { + let abc: felt252 = 0; // bad + let c = |abc| { // good + abc + 3 + }; +} + +fn foo(abc: felt252) {} + +//! > Goto definition #0 + abc + 3 +--- + let c = |abc| { // good