Skip to content

Commit

Permalink
Collect tokens for dep and library names (FuelLabs#3982)
Browse files Browse the repository at this point in the history
## Description

Add proper tokens for library type identifiers and `dep` imports that
point to the correct library module declaration.

Fix FuelLabs#3718
Fix FuelLabs#3716

## Checklist

- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [x] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.

Co-authored-by: Joshua Batty <[email protected]>
  • Loading branch information
IGI-111 and JoshuaBatty authored Feb 5, 2023
1 parent 63f677a commit 9fbad50
Show file tree
Hide file tree
Showing 15 changed files with 232 additions and 34 deletions.
3 changes: 2 additions & 1 deletion sway-core/src/language/parsed/module.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::language::DepName;

use super::ParseTree;
use sway_types::Ident;
use sway_types::{Ident, Span};

/// A module and its submodules in the form of a tree.
#[derive(Debug, Clone)]
Expand All @@ -20,4 +20,5 @@ pub struct ParseSubmodule {
/// The name of a submodule, parsed from the `library` declaration within the module itself.
pub library_name: Ident,
pub module: ParseModule,
pub dependency_path_span: Span,
}
3 changes: 2 additions & 1 deletion sway-core/src/language/ty/module.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use sway_types::Ident;
use sway_types::{Ident, Span};

use crate::{
decl_engine::{DeclEngine, DeclId},
Expand All @@ -18,6 +18,7 @@ pub struct TyModule {
pub struct TySubmodule {
pub library_name: Ident,
pub module: TyModule,
pub dependency_path_span: Span,
}

/// Iterator type for iterating over submodules.
Expand Down
1 change: 1 addition & 0 deletions sway-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ fn parse_submodules(
let parse_submodule = parsed::ParseSubmodule {
library_name: library_name.clone(),
module: parse_module,
dependency_path_span: dep.path.span(),
};
let lexed_submodule = lexed::LexedSubmodule {
library_name,
Expand Down
2 changes: 2 additions & 0 deletions sway-core/src/semantic_analysis/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,14 @@ impl ty::TySubmodule {
let ParseSubmodule {
library_name,
module,
dependency_path_span,
} = submodule;
parent_ctx.enter_submodule(dep_name, |submod_ctx| {
let module_res = ty::TyModule::type_check(submod_ctx, module);
module_res.map(|module| ty::TySubmodule {
library_name: library_name.clone(),
module,
dependency_path_span: dependency_path_span.clone(),
})
})
}
Expand Down
2 changes: 2 additions & 0 deletions sway-lsp/src/core/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,14 @@ impl Session {

// Next, populate our token_map with un-typed yet parsed ast nodes.
let parsed_tree = ParsedTree::new(type_engine, &self.token_map);
parsed_tree.collect_module_spans(&parsed);
self.parse_ast_to_tokens(&parsed, |an| parsed_tree.traverse_node(an));

// Finally, create runnables and populate our token_map with typed ast nodes.
self.create_runnables(typed_program);

let typed_tree = TypedTree::new(engines, &self.token_map);
typed_tree.collect_module_spans(typed_program);
self.parse_ast_to_typed_tokens(typed_program, |node, namespace| {
typed_tree.traverse_node(node, namespace)
});
Expand Down
7 changes: 6 additions & 1 deletion sway-lsp/src/core/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use sway_core::{
parsed::{
Declaration, EnumVariant, Expression, FunctionDeclaration, FunctionParameter,
ReassignmentExpression, Scrutinee, StorageField, StructExpressionField, StructField,
Supertrait, TraitFn,
Supertrait, TraitFn, TreeType,
},
ty,
},
Expand Down Expand Up @@ -35,6 +35,8 @@ pub enum AstToken {
Keyword(Ident),
Intrinsic(Intrinsic),
Attribute(Attribute),
TreeType(TreeType),
IncludeStatement,
}

/// The `TypedAstToken` holds the types produced by the [sway_core::language::ty::TyProgram].
Expand All @@ -53,6 +55,9 @@ pub enum TypedAstToken {
TypedReassignment(ty::TyReassignment),
TypedArgument(TypeArgument),
TypedParameter(TypeParameter),
TypedProgramKind(ty::TyProgramKind),
TypedLibraryName(Ident),
TypedIncludeStatement,
}

/// These variants are used to represent the semantic type of the [Token].
Expand Down
113 changes: 86 additions & 27 deletions sway-lsp/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -653,8 +653,7 @@ mod tests {
def_path: &'a str,
}

fn load_sway_example(manifest_dir: PathBuf) -> (Url, String) {
let src_path = manifest_dir.join("src/main.sw");
fn load_sway_example(src_path: PathBuf) -> (Url, String) {
let mut file = fs::File::open(&src_path).unwrap();
let mut sway_program = String::new();
file.read_to_string(&mut sway_program).unwrap();
Expand Down Expand Up @@ -1087,10 +1086,10 @@ mod tests {
code_lens
}

async fn init_and_open(service: &mut LspService<Backend>, manifest_dir: PathBuf) -> Url {
async fn init_and_open(service: &mut LspService<Backend>, entry_point: PathBuf) -> Url {
let _ = initialize_request(service).await;
initialized_notification(service).await;
let (uri, sway_program) = load_sway_example(manifest_dir);
let (uri, sway_program) = load_sway_example(entry_point);
did_open_notification(service, &uri, &sway_program).await;
uri
}
Expand Down Expand Up @@ -1142,7 +1141,7 @@ mod tests {
}

let example_dir = Some(Url::from_file_path(example_dir).unwrap());
let (uri, sway_program) = load_sway_example(manifest_dir);
let (uri, sway_program) = load_sway_example(manifest_dir.join("src/main.sw"));
did_open_notification(&mut service, &uri, &sway_program).await;
let _ = show_ast_request(&mut service, &uri, "lexed", example_dir.clone()).await;
let _ = show_ast_request(&mut service, &uri, "parsed", example_dir.clone()).await;
Expand Down Expand Up @@ -1200,30 +1199,30 @@ mod tests {
#[tokio::test]
async fn did_open() {
let (mut service, _) = LspService::new(Backend::new);
let _ = init_and_open(&mut service, e2e_test_dir()).await;
let _ = init_and_open(&mut service, e2e_test_dir().join("src/main.sw")).await;
shutdown_and_exit(&mut service).await;
}

#[tokio::test]
async fn did_close() {
let (mut service, _) = LspService::new(Backend::new);
let _ = init_and_open(&mut service, e2e_test_dir()).await;
let _ = init_and_open(&mut service, e2e_test_dir().join("src/main.sw")).await;
did_close_notification(&mut service).await;
shutdown_and_exit(&mut service).await;
}

#[tokio::test]
async fn did_change() {
let (mut service, _) = LspService::new(Backend::new);
let uri = init_and_open(&mut service, doc_comments_dir()).await;
let uri = init_and_open(&mut service, doc_comments_dir().join("src/main.sw")).await;
let _ = did_change_request(&mut service, &uri).await;
shutdown_and_exit(&mut service).await;
}

#[tokio::test]
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 uri = init_and_open(&mut service, doc_comments_dir().join("src/main.sw")).await;
let mut go_to = GotoDefintion {
req_uri: &uri,
req_line: 44,
Expand All @@ -1246,15 +1245,15 @@ mod tests {
.custom_method("sway/show_ast", Backend::show_ast)
.finish();

let uri = init_and_open(&mut service, e2e_test_dir()).await;
let uri = init_and_open(&mut service, e2e_test_dir().join("src/main.sw")).await;
let _ = show_ast_request(&mut service, &uri, "typed", None).await;
shutdown_and_exit(&mut service).await;
}

#[tokio::test]
async fn go_to_definition() {
let (mut service, _) = LspService::new(Backend::new);
let uri = init_and_open(&mut service, doc_comments_dir()).await;
let uri = init_and_open(&mut service, doc_comments_dir().join("src/main.sw")).await;
let go_to = GotoDefintion {
req_uri: &uri,
req_line: 44,
Expand Down Expand Up @@ -1285,7 +1284,7 @@ mod tests {
let (mut service, _) = LspService::new(Backend::new);
let uri = init_and_open(
&mut service,
test_fixtures_dir().join("tokens").join("turbofish"),
test_fixtures_dir().join("tokens/turbofish/src/main.sw"),
)
.await;

Expand Down Expand Up @@ -1326,12 +1325,48 @@ mod tests {
shutdown_and_exit(&mut service).await;
}

#[tokio::test]
async fn go_to_definition_for_modules() {
let (mut service, _) = LspService::new(Backend::new);
let uri = init_and_open(
&mut service,
test_fixtures_dir().join("tokens/modules/src/lib.sw"),
)
.await;

let opt_go_to = GotoDefintion {
req_uri: &uri,
req_line: 2,
req_char: 6,
def_line: 0,
def_start_char: 8,
def_end_char: 16,
def_path: "sway-lsp/test/fixtures/tokens/modules/src/test_mod.sw",
};
// dep test_mod;
let _ = definition_check(&mut service, &opt_go_to, 2).await;

let opt_go_to = GotoDefintion {
req_uri: &uri,
req_line: 3,
req_char: 6,
def_line: 0,
def_start_char: 8,
def_end_char: 15,
def_path: "sway-lsp/test/fixtures/tokens/modules/src/dir_mod/mod.sw",
};
// dep dir_mod/mod;
let _ = definition_check(&mut service, &opt_go_to, 3).await;

shutdown_and_exit(&mut service).await;
}

#[tokio::test]
async fn go_to_definition_for_paths() {
let (mut service, _) = LspService::new(Backend::new);
let uri = init_and_open(
&mut service,
test_fixtures_dir().join("tokens").join("paths"),
test_fixtures_dir().join("tokens/paths/src/main.sw"),
)
.await;

Expand Down Expand Up @@ -1604,7 +1639,7 @@ mod tests {
let (mut service, _) = LspService::new(Backend::new);
let uri = init_and_open(
&mut service,
test_fixtures_dir().join("tokens").join("traits"),
test_fixtures_dir().join("tokens/traits/src/main.sw"),
)
.await;

Expand Down Expand Up @@ -1634,7 +1669,7 @@ mod tests {
let (mut service, _) = LspService::new(Backend::new);
let uri = init_and_open(
&mut service,
test_fixtures_dir().join("tokens").join("variables"),
test_fixtures_dir().join("tokens/variables/src/main.sw"),
)
.await;

Expand Down Expand Up @@ -1699,7 +1734,7 @@ mod tests {
let socket_handle = assert_server_requests(socket, expected_requests, None).await;
let _ = init_and_open(
&mut service,
test_fixtures_dir().join("diagnostics/dead_code"),
test_fixtures_dir().join("diagnostics/dead_code/src/main.sw"),
)
.await;
socket_handle
Expand All @@ -1714,29 +1749,53 @@ mod tests {
// It then runs the specific capability to test before gracefully shutting down.
// The capability argument is an async function.
macro_rules! test_lsp_capability {
($example_dir:expr, $capability:expr) => {{
($entry_point:expr, $capability:expr) => {{
let (mut service, _) = LspService::new(Backend::new);
let uri = init_and_open(&mut service, $example_dir).await;
let uri = init_and_open(&mut service, $entry_point).await;
// Call the specific LSP capability function that was passed in.
let _ = $capability(&mut service, &uri).await;
shutdown_and_exit(&mut service).await;
}};
}

macro_rules! lsp_capability_test {
($test:ident, $capability:expr, $dir:expr) => {
($test:ident, $capability:expr, $entry_path:expr) => {
#[tokio::test]
async fn $test() {
test_lsp_capability!($dir(), $capability);
test_lsp_capability!($entry_path, $capability);
}
};
}

lsp_capability_test!(semantic_tokens, semantic_tokens_request, doc_comments_dir);
lsp_capability_test!(document_symbol, document_symbol_request, doc_comments_dir);
lsp_capability_test!(format, format_request, doc_comments_dir);
lsp_capability_test!(hover, hover_request, doc_comments_dir);
lsp_capability_test!(highlight, highlight_request, doc_comments_dir);
lsp_capability_test!(code_action, code_action_request, doc_comments_dir);
lsp_capability_test!(code_lens, code_lens_request, runnables_test_dir);
lsp_capability_test!(
semantic_tokens,
semantic_tokens_request,
doc_comments_dir().join("src/main.sw")
);
lsp_capability_test!(
document_symbol,
document_symbol_request,
doc_comments_dir().join("src/main.sw")
);
lsp_capability_test!(
format,
format_request,
doc_comments_dir().join("src/main.sw")
);
lsp_capability_test!(hover, hover_request, doc_comments_dir().join("src/main.sw"));
lsp_capability_test!(
highlight,
highlight_request,
doc_comments_dir().join("src/main.sw")
);
lsp_capability_test!(
code_action,
code_action_request,
doc_comments_dir().join("src/main.sw")
);
lsp_capability_test!(
code_lens,
code_lens_request,
runnables_test_dir().join("src/main.sw")
);
}
54 changes: 51 additions & 3 deletions sway-lsp/src/traverse/parsed_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ use sway_core::{
AstNodeContent, CodeBlock, Declaration, DelineatedPathExpression, Expression,
ExpressionKind, FunctionApplicationExpression, FunctionDeclaration, FunctionParameter,
IfExpression, IntrinsicFunctionExpression, LazyOperatorExpression, MatchExpression,
MethodApplicationExpression, MethodName, ReassignmentTarget, Scrutinee,
StorageAccessExpression, StructExpression, StructScrutineeField, SubfieldExpression,
TraitFn, TupleIndexExpression, WhileLoopExpression,
MethodApplicationExpression, MethodName, ParseModule, ParseProgram, ParseSubmodule,
ReassignmentTarget, Scrutinee, StorageAccessExpression, StructExpression,
StructScrutineeField, SubfieldExpression, TraitFn, TreeType, TupleIndexExpression,
WhileLoopExpression,
},
Literal,
},
Expand Down Expand Up @@ -58,6 +59,53 @@ impl<'a> ParsedTree<'a> {
};
}

pub fn collect_module_spans(&self, parse_program: &ParseProgram) {
self.collect_tree_type(&parse_program.kind);
self.collect_parse_module(&parse_program.root);
}

fn collect_parse_module(&self, parse_module: &ParseModule) {
for (
_,
ParseSubmodule {
library_name,
module,
dependency_path_span,
},
) in &parse_module.submodules
{
self.tokens.insert(
to_ident_key(&Ident::new(dependency_path_span.clone())),
Token::from_parsed(AstToken::IncludeStatement, SymbolKind::Module),
);

self.tokens.insert(
to_ident_key(library_name),
Token::from_parsed(
AstToken::TreeType(TreeType::Library {
name: library_name.clone(),
}),
SymbolKind::Module,
),
);

self.collect_parse_module(module);
}
}

fn collect_tree_type(&self, tree_type: &TreeType) {
use TreeType::*;
match tree_type {
Library { name } => {
self.tokens.insert(
to_ident_key(name),
Token::from_parsed(AstToken::TreeType(tree_type.clone()), SymbolKind::Module),
);
}
Script | Contract | Predicate => {}
}
}

fn handle_function_declation(&self, func: &FunctionDeclaration) {
let token = Token::from_parsed(
AstToken::FunctionDeclaration(func.clone()),
Expand Down
Loading

0 comments on commit 9fbad50

Please sign in to comment.