Skip to content

Commit

Permalink
Helper method: Write all AST's to disk (FuelLabs#3922)
Browse files Browse the repository at this point in the history
This PR adds a method which iterates over all of the examples in the
e2e/should_pass/language directory and then saves the lexed, parsed, and
typed ASTs to the users home directory.

The motivation for adding this is to make it easy to grep for certain
compiler types to inspect their use cases in order to provide the
necessary context when working on the traversal modules.
  • Loading branch information
JoshuaBatty authored Jan 31, 2023
1 parent 678415a commit 13dde2a
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 13 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions sway-lsp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@ tracing = "0.1"
[dev-dependencies]
assert-json-diff = "2.0"
async-trait = "0.1"
dirs = "4.0"
futures = { version = "0.3", default-features = false, features = ["std", "async-await"] }
tower = { version = "0.4.12", default-features = false, features = ["util"] }
99 changes: 87 additions & 12 deletions sway-lsp/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@ impl LanguageServer for Backend {
pub struct ShowAstParams {
pub text_document: TextDocumentIdentifier,
pub ast_kind: String,
pub save_path: Url,
}

// Custom LSP-Server Methods
Expand Down Expand Up @@ -546,6 +547,7 @@ impl Backend {
ident.span().path().map(|a| a.deref()) == path.as_ref()
};

let ast_path = PathBuf::from(params.save_path.path());
{
let program = session.compiled_program.read();
match params.ast_kind.as_str() {
Expand All @@ -558,8 +560,10 @@ impl Backend {
formatted_ast = format!("{:#?}", submodule.module.tree);
}
}
let tmp_ast_path = Path::new("/tmp/lexed_ast.rs");
write_ast_to_file(tmp_ast_path, &formatted_ast)
write_ast_to_file(
ast_path.join("lexed.rs").as_path(),
&formatted_ast,
)
}))
}
"parsed" => {
Expand All @@ -574,8 +578,10 @@ impl Backend {
format!("{:#?}", submodule.module.tree.root_nodes);
}
}
let tmp_ast_path = Path::new("/tmp/parsed_ast.rs");
write_ast_to_file(tmp_ast_path, &formatted_ast)
write_ast_to_file(
ast_path.join("parsed.rs").as_path(),
&formatted_ast,
)
}))
}
"typed" => {
Expand All @@ -594,8 +600,10 @@ impl Backend {
);
}
}
let tmp_ast_path = Path::new("/tmp/typed_ast.rs");
write_ast_to_file(tmp_ast_path, &formatted_ast)
write_ast_to_file(
ast_path.join("typed.rs").as_path(),
&formatted_ast,
)
}))
}
_ => Ok(None),
Expand All @@ -614,8 +622,8 @@ impl Backend {
mod tests {
use super::*;
use crate::utils::test::{
assert_server_requests, doc_comments_dir, e2e_test_dir, get_fixture, runnables_test_dir,
test_fixtures_dir,
assert_server_requests, dir_contains_forc_manifest, doc_comments_dir, e2e_language_dir,
e2e_test_dir, get_fixture, runnables_test_dir, sway_workspace_dir, test_fixtures_dir,
};
use assert_json_diff::assert_json_eq;
use serde_json::json;
Expand Down Expand Up @@ -735,16 +743,31 @@ mod tests {
assert_eq!(response, Ok(None));
}

async fn show_ast_request(service: &mut LspService<Backend>, uri: &Url) -> Request {
async fn show_ast_request(
service: &mut LspService<Backend>,
uri: &Url,
ast_kind: &str,
save_path: Option<Url>,
) -> Request {
// The path where the AST will be written to.
// If no path is provided, the default path is "/tmp"
let save_path = match save_path {
Some(path) => path,
None => Url::from_file_path(Path::new("/tmp")).unwrap(),
};
let params = json!({
"textDocument": {
"uri": uri
},
"astKind": "typed",
"astKind": ast_kind,
"savePath": save_path,
});
let show_ast = build_request_with_id("sway/show_ast", params, 1);
let response = call_request(service, show_ast.clone()).await;
let expected = Response::from_ok(1.into(), json!({"uri": "file:///tmp/typed_ast.rs"}));
let expected = Response::from_ok(
1.into(),
json!({ "uri": format!("{save_path}/{ast_kind}.rs") }),
);
assert_json_eq!(expected, response.ok().unwrap());
show_ast
}
Expand Down Expand Up @@ -1121,6 +1144,58 @@ mod tests {
exit_notification(service).await;
}

// This method iterates over all of the examples in the e2e langauge should_pass dir
// and saves the lexed, parsed, and typed ASTs to the users home directory.
// This makes it easy to grep for certain compiler types to inspect their use cases,
// providing necessary context when working on the traversal modules.
#[allow(unused)]
//#[tokio::test]
async fn write_all_example_asts() {
let (mut service, _) = LspService::build(Backend::new)
.custom_method("sway/show_ast", Backend::show_ast)
.finish();
let _ = initialize_request(&mut service).await;
initialized_notification(&mut service).await;

let ast_folder = dirs::home_dir()
.expect("could not get users home directory")
.join("sway_asts");
let _ = fs::create_dir(&ast_folder);
let e2e_dir = sway_workspace_dir().join(e2e_language_dir());
let mut entries = fs::read_dir(&e2e_dir)
.unwrap()
.map(|res| res.map(|e| e.path()))
.collect::<Result<Vec<_>, std::io::Error>>()
.unwrap();

// The order in which `read_dir` returns entries is not guaranteed. If reproducible
// ordering is required the entries should be explicitly sorted.
entries.sort();

for entry in entries {
let manifest_dir = entry;
let example_name = manifest_dir.file_name().unwrap();
if manifest_dir.is_dir() {
let example_dir = ast_folder.join(example_name);
if !dir_contains_forc_manifest(manifest_dir.as_path()) {
continue;
}
match fs::create_dir(&example_dir) {
Ok(_) => (),
Err(_) => continue,
}

let example_dir = Some(Url::from_file_path(example_dir).unwrap());
let (uri, sway_program) = load_sway_example(manifest_dir);
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;
let _ = show_ast_request(&mut service, &uri, "typed", example_dir).await;
}
}
shutdown_and_exit(&mut service).await;
}

#[tokio::test]
async fn initialize() {
let (mut service, _) = LspService::new(Backend::new);
Expand Down Expand Up @@ -1206,7 +1281,7 @@ mod tests {
.finish();

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

Expand Down
18 changes: 17 additions & 1 deletion sway-lsp/src/utils/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
use assert_json_diff::assert_json_include;
use futures::StreamExt;
use serde_json::Value;
use std::{env, path::PathBuf, time::Duration};
use std::{
env, fs,
path::{Path, PathBuf},
time::Duration,
};
use tokio::task::JoinHandle;
use tower_lsp::{lsp_types::Url, ClientSocket};

Expand Down Expand Up @@ -57,6 +61,18 @@ pub(crate) fn sway_example_dir() -> PathBuf {
sway_workspace_dir().join("examples/storage_variables")
}

// Check if the given directory contains `Forc.toml` at its root.
pub(crate) fn dir_contains_forc_manifest(path: &Path) -> bool {
if let Ok(entries) = fs::read_dir(path) {
for entry in entries.flatten() {
if entry.path().file_name().and_then(|s| s.to_str()) == Some("Forc.toml") {
return true;
}
}
}
false
}

pub(crate) async fn assert_server_requests(
socket: ClientSocket,
expected_requests: Vec<Value>,
Expand Down

0 comments on commit 13dde2a

Please sign in to comment.