diff --git a/toolchains/solidity/core/Cargo.lock b/toolchains/solidity/core/Cargo.lock index 95fa11e5..757eb476 100644 --- a/toolchains/solidity/core/Cargo.lock +++ b/toolchains/solidity/core/Cargo.lock @@ -251,6 +251,12 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.8" @@ -411,6 +417,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "itoa" version = "1.0.10" @@ -798,6 +814,15 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -821,12 +846,15 @@ name = "slither-server" version = "0.3.0" dependencies = [ "colored", + "glob", "os", "serde", "serde_derive", "serde_json", "thiserror", "tokio", + "tokio-util", + "toml", "tower-lsp", ] @@ -1004,6 +1032,40 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower" version = "0.4.13" @@ -1290,3 +1352,12 @@ name = "windows_x86_64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winnow" +version = "0.5.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +dependencies = [ + "memchr", +] diff --git a/toolchains/solidity/core/crates/slither-server/Cargo.toml b/toolchains/solidity/core/crates/slither-server/Cargo.toml index bdf4d969..a333b85c 100644 --- a/toolchains/solidity/core/crates/slither-server/Cargo.toml +++ b/toolchains/solidity/core/crates/slither-server/Cargo.toml @@ -17,3 +17,6 @@ tokio = { version="1.34.0", features = ["full"] } tower-lsp = "0.20.0" colored = "2.0.4" thiserror = "1.0.50" +glob = "0.3.1" +toml = "0.8.8" +tokio-util = "0.7.10" diff --git a/toolchains/solidity/core/crates/slither-server/src/error.rs b/toolchains/solidity/core/crates/slither-server/src/error.rs index c8526990..72df57d3 100644 --- a/toolchains/solidity/core/crates/slither-server/src/error.rs +++ b/toolchains/solidity/core/crates/slither-server/src/error.rs @@ -2,10 +2,12 @@ use thiserror::Error; #[derive(Error, Debug)] pub enum SlitherError { - #[error("Error while runing slither")] - Unknown, + #[error("Error while runing slither: {0}")] + Unknown(String), #[error("Error while runing the slither command: {0}")] IoCommandError(#[from] std::io::Error), #[error("Error while parsing slither output: {0}")] ParsingFailed(#[from] serde_json::Error), + #[error("Error when trying to find the foundry.toml file")] + FoundryTomlNotFound, } diff --git a/toolchains/solidity/core/crates/slither-server/src/main.rs b/toolchains/solidity/core/crates/slither-server/src/main.rs index bf78bd26..52606e00 100644 --- a/toolchains/solidity/core/crates/slither-server/src/main.rs +++ b/toolchains/solidity/core/crates/slither-server/src/main.rs @@ -1,21 +1,33 @@ mod error; mod slither; mod types; +mod utils; -use crate::error::SlitherError; -use crate::slither::*; +use crate::{error::SlitherError, slither::parse_slither_out, types::*}; + +use std::sync::Arc; +use std::vec; +use tokio::sync::{Mutex, MutexGuard}; +use tokio_util::sync::CancellationToken; use tower_lsp::jsonrpc::Result; use tower_lsp::lsp_types::*; use tower_lsp::{Client, LanguageServer, LspService, Server}; +use utils::find_foundry_toml_config; +use utils::is_slither_installed; +use utils::is_solc_installed; +use utils::normalize_slither_path; +use utils::parse_foundry_toml; #[derive(Debug)] struct Backend { client: Client, + data: Mutex, + join_handle: Arc>>>, } #[tower_lsp::async_trait] impl LanguageServer for Backend { - async fn initialize(&self, _: InitializeParams) -> Result { + async fn initialize(&self, params: InitializeParams) -> Result { if !is_slither_installed() { self.client .show_message( @@ -40,6 +52,46 @@ impl LanguageServer for Backend { .await; return Err(tower_lsp::jsonrpc::Error::internal_error()); } + + self.client + .log_message(MessageType::INFO, "Initializing diagnostic receiver ...") + .await; + let mut state = self.data.lock().await; + let mut receiver = state.receiver.take().unwrap(); + let client = self.client.clone(); + + self.join_handle + .lock() + .await + .replace(tokio::spawn(async move { + while let Some(diagnostics) = receiver.recv().await { + client + .publish_diagnostics(diagnostics.uri, diagnostics.diagnostics, None) + .await; + } + })); + self.client + .log_message( + MessageType::INFO, + "Finished initializing diagnostic receiver!", + ) + .await; + + self.client + .log_message(MessageType::INFO, "Initializing Workspace ...") + .await; + state.workspace = self + .fetch_workspace(params.workspace_folders, params.root_uri) + .await; + + self.client + .log_message(MessageType::INFO, "Initializing filters ...") + .await; + self.initialize_filters(&mut state); + + self.client + .log_message(MessageType::INFO, "Slither-Server initialized!") + .await; Ok(InitializeResult { server_info: None, capabilities: ServerCapabilities { @@ -64,100 +116,189 @@ impl LanguageServer for Backend { .await; } + async fn did_change_workspace_folders(&self, params: DidChangeWorkspaceFoldersParams) { + let mut state = self.data.lock().await; + if params.event.added.is_empty() + && !params.event.removed.is_empty() + && state.workspace == "." + { + self.client + .log_message( + MessageType::WARNING, + "No workspace folder found, please open a folder!", + ) + .await; + return; + } + let folders: Vec = params + .event + .added + .iter() + .map(|folder| folder.to_owned()) + .collect(); + state.workspace = self.fetch_workspace(Some(folders), None).await; + } + async fn shutdown(&self) -> Result<()> { + let state = self.data.lock().await; + for process in state.slither_processes.iter() { + process.cancel(); + } + self.join_handle.lock().await.take().unwrap().abort(); Ok(()) } - async fn did_open(&self, file: DidOpenTextDocumentParams) { + async fn did_save(&self, file: DidSaveTextDocumentParams) { self.client .log_message( MessageType::INFO, format!( - "Opened file '{}' for analyzing.", + "Saved file '{}' for analyzing.", file.text_document.uri.path() ), ) .await; - if file.text_document.uri.path().ends_with(".t.sol") { - self.client - .log_message( - MessageType::INFO, - format!( - "File '{}' is a test solidity file, skipping analysis.", - file.text_document.uri.path() - ), - ) - .await; - return; - } - self.check_slither_result(file.text_document.uri).await + self.analyze_file(file.text_document.uri).await } - async fn did_save(&self, file: DidSaveTextDocumentParams) { + async fn did_open(&self, file: DidOpenTextDocumentParams) { self.client .log_message( MessageType::INFO, format!( - "Saved file '{}' for analyzing.", + "Opened file '{}' for analyzing.", file.text_document.uri.path() ), ) .await; - if file.text_document.uri.path().ends_with(".t.sol") { + self.analyze_file(file.text_document.uri).await + } +} + +impl Backend { + fn new(client: Client) -> Self { + Self { + client, + data: Mutex::new(SlitherData::new()), + join_handle: Arc::new(Mutex::new(None)), + } + } + + async fn analyze_file(&self, file: Url) { + let normalized_path = normalize_slither_path(file.path()); + if !self.is_in_src(&normalized_path).await { self.client .log_message( MessageType::INFO, format!( - "File '{}' is a test solidity file, skipping analysis.", - file.text_document.uri.path() + "File '{}' is not a source solidity code file, skipping analysis.", + file.path() ), ) .await; return; } - self.check_slither_result(file.text_document.uri).await + self.launch_slither(file).await } -} -impl Backend { - async fn check_slither_result(&self, uri: Url) { - let res = exec_slither(uri.path()); - match res { - Ok(res) => { - self.client - .log_message( - MessageType::INFO, - format!( - "File '{}' did generate {} security diagnostics.", - uri.path(), - res.len() - ), - ) - .await; - self.client.publish_diagnostics(uri, res, None).await; + async fn is_in_src(&self, path: &str) -> bool { + let state = self.data.lock().await; + for src in state.src_paths.iter() { + let fsrc = format!("/{}/", src.replace('\"', "")); + if path.strip_prefix(&state.workspace).unwrap().contains(&fsrc) { + return true; } - Err(SlitherError::ParsingFailed(e)) => { - self.client - .log_message( - MessageType::ERROR, - format!( - "File '{}' did generate an error while parsing the output: {:?}", - uri.path(), - e - ), - ) - .await; - self.client.publish_diagnostics(uri, vec![], None).await; + } + false + } + + fn initialize_filters(&self, state: &mut MutexGuard) { + //register all work directories folder aliases using foundry.toml for each workspace folder + let foundry_path = find_foundry_toml_config(&state.workspace); + if let Ok(path) = foundry_path { + let foundry = std::fs::read_to_string(path.clone()); + match foundry { + Ok(foundry) => { + parse_foundry_toml(foundry, state); + } + Err(e) => { + eprintln!( + "Error while reading foundry.toml file: {:?}, path: {}", + e, path + ); + } } - Err(e) => { - self.client - .log_message( - MessageType::ERROR, - format!("File '{}' did generate an error: {:?}", uri.path(), e), - ) - .await; + } + } + + async fn launch_slither(&self, uri: Url) { + let filepath = normalize_slither_path(uri.path()); + let mut state = self.data.lock().await; + let token = CancellationToken::new(); + let clone = token.clone(); + state.slither_processes.push(token); + let sender_handle = state.sender.clone(); + let client = self.client.clone(); + let workspace = state.workspace.clone(); + + tokio::spawn(async move { + tokio::select! { + _ = clone.cancelled() => { + eprintln!("SLITHER CANCELLED"); + } + output = parse_slither_out(&filepath, &workspace) => { + match output { + Ok(res) => { + let _ = sender_handle.send(SlitherDiag::new(uri, res)).await; + }, + Err(SlitherError::ParsingFailed(e)) => { + client + .log_message( + MessageType::ERROR, + format!( + "File '{}' did generate an error while parsing the output: {:?}", + filepath, + e + ), + ) + .await; + client.publish_diagnostics(uri, vec![], None).await; + } + Err(e) => { + client + .log_message( + MessageType::ERROR, + format!("File '{}' did generate an error: {:?}", filepath, e), + ) + .await; + } + } + } } + }); + } + + async fn fetch_workspace( + &self, + workspace_folders: Option>, + root_uri: Option, + ) -> String { + let mut workspace = ".".to_string(); + match workspace_folders { + Some(workspaces) => workspace = normalize_slither_path(workspaces[0].uri.path()), + None => match root_uri { + Some(uri) => workspace = normalize_slither_path(uri.path()), + None => { + self.client + .log_message( + MessageType::WARNING, + "No workspace folder found, please open a folder!", + ) + .await; + } + }, } + workspace } } @@ -166,6 +307,6 @@ async fn main() { let stdin = tokio::io::stdin(); let stdout = tokio::io::stdout(); - let (service, socket) = LspService::new(|client| Backend { client }); + let (service, socket) = LspService::new(Backend::new); Server::new(stdin, stdout, socket).serve(service).await; } diff --git a/toolchains/solidity/core/crates/slither-server/src/slither.rs b/toolchains/solidity/core/crates/slither-server/src/slither.rs index f23586eb..dd4d4cf6 100644 --- a/toolchains/solidity/core/crates/slither-server/src/slither.rs +++ b/toolchains/solidity/core/crates/slither-server/src/slither.rs @@ -1,50 +1,56 @@ -use crate::error::SlitherError; -use crate::types::SlitherResult; -use std::process::Command; +use crate::{error::SlitherError, types::SlitherResult}; +use std::process::Stdio; +use tokio::{io::AsyncReadExt, process::Command}; use tower_lsp::lsp_types::Diagnostic; -pub fn is_slither_installed() -> bool { - let output = Command::new("slither").arg("--version").output(); - output.is_ok() -} +pub async fn parse_slither_out( + uri: &str, + workspace: &str, +) -> Result, SlitherError> { + let mut results: Vec = Vec::new(); -pub fn is_solc_installed() -> bool { - let output = Command::new("solc").arg("--version").output(); - output.is_ok() -} + let mut output = exec_slither(uri, workspace)?; + let out = match output.stdout.take() { + Some(out) => out, + None => { + return Err(SlitherError::Unknown( + "Failed to get slither output pipe".to_string(), + )) + } + }; -#[cfg(target_family = "windows")] -fn normalize_slither_path(path: &str) -> String { - let mut path = path.replace("%3A/", "://"); - path.remove(0); - path.to_string() -} + let mut buffer = tokio::io::BufReader::new(out); + let mut dst = String::new(); + + output.wait().await?; + + buffer.read_to_string(&mut dst).await?; + let json: Result = serde_json::from_str(&dst); + match json { + Ok(json) => { + for detector in json.results.detectors { + results.append(&mut crate::types::diag_from_json(detector.clone())); + } + } + Err(e) => { + eprintln!("Error parsing slither output: {}", e); + return Err(SlitherError::ParsingFailed(e)); + } + } -#[cfg(not(target_family = "windows"))] -fn normalize_slither_path(path: &str) -> String { - path.to_string() + Ok(results) } -pub fn exec_slither(filepath: &str) -> Result, SlitherError> { - let mut results: Vec = Vec::new(); - let out = Command::new("slither") - .arg(normalize_slither_path(filepath)) +fn exec_slither(uri: &str, workspace: &str) -> Result { + Command::new("slither") + .current_dir(workspace) + .arg(uri) .arg("--exclude") .arg("naming-convention") .arg("--json") .arg("-") - .output()?; - if out.status.code() == Some(1) { - eprintln!("Unknown error occured: {:?}", out); - return Err(SlitherError::Unknown); - } - if out.stdout.is_empty() { - return Ok(results); - } - let json: SlitherResult = - serde_json::from_str(&String::from_utf8_lossy(&out.stdout).replace("\\\"", "\""))?; - for detector in json.results.detectors { - results.append(&mut crate::types::diag_from_json(detector.clone())); - } - Ok(results) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .stdin(Stdio::null()) + .spawn() } diff --git a/toolchains/solidity/core/crates/slither-server/src/types.rs b/toolchains/solidity/core/crates/slither-server/src/types.rs index 0486f555..3fe5e92f 100644 --- a/toolchains/solidity/core/crates/slither-server/src/types.rs +++ b/toolchains/solidity/core/crates/slither-server/src/types.rs @@ -1,6 +1,68 @@ use serde::{Deserialize, Serialize}; +use std::vec; +use tokio::sync::mpsc::{Receiver, Sender}; +use tokio_util::sync::CancellationToken; +use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity as Severity, Position, Range}; +#[derive(Debug)] +pub struct SlitherDiag { + pub diagnostics: Vec, + pub uri: Url, +} + +impl SlitherDiag { + pub fn new(uri: Url, diagnostics: Vec) -> Self { + Self { uri, diagnostics } + } +} + +#[derive(Debug)] +pub struct SlitherData { + pub slither_processes: Vec, + pub receiver: Option>, + pub sender: Sender, + pub src_paths: Vec, + pub workspace: String, +} + +impl SlitherData { + pub fn new() -> Self { + let (sender, receiver) = tokio::sync::mpsc::channel::(100); + Self { + src_paths: vec![], + slither_processes: vec![], + receiver: Some(receiver), + sender, + workspace: String::new(), + } + } +} + +#[derive(Debug, Deserialize, Clone)] +pub enum FoundryArrOrStr { + Arr(Vec), + Str(String), +} + +#[derive(Debug, Deserialize, Clone)] +pub struct FoundryProfile { + pub src: Option, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct FoundryProfiles { + pub default: Option, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct FoundryToml { + pub profiles: Option, +} + +///////////////////////// +// SLITHER JSON OUTPUT // +///////////////////////// #[derive(Clone, Serialize, Deserialize, Debug)] pub struct SlitherResult { pub results: SlitherResults, @@ -119,7 +181,3 @@ pub fn diag_from_json(json: SlitherDetector) -> Vec { results } - -//////////////////////////////////////////////////////////// -/////////////////// RELATED TYPES: ///////////////////////// -//////////////////////////////////////////////////////////// diff --git a/toolchains/solidity/core/crates/slither-server/src/utils.rs b/toolchains/solidity/core/crates/slither-server/src/utils.rs new file mode 100644 index 00000000..06bb8542 --- /dev/null +++ b/toolchains/solidity/core/crates/slither-server/src/utils.rs @@ -0,0 +1,71 @@ +use crate::error::SlitherError; +use crate::{FoundryArrOrStr, FoundryToml, SlitherData}; +use glob::glob; +use std::error::Error; +use std::process::Command as StdCommand; + +pub fn is_slither_installed() -> bool { + let output = StdCommand::new("slither").arg("--version").output(); + output.is_ok() +} + +pub fn is_solc_installed() -> bool { + let output = StdCommand::new("solc").arg("--version").output(); + output.is_ok() +} + +#[cfg(target_family = "windows")] +pub fn normalize_slither_path(path: &str) -> String { + let mut path = path.replace("%3A/", "://"); + path.remove(0); + path.to_string() +} + +#[cfg(not(target_family = "windows"))] +pub fn normalize_slither_path(path: &str) -> String { + path.to_string() +} + +fn extract_foundry_src(foundry: FoundryToml) -> Option { + foundry.profiles?.default?.src +} + +pub fn parse_foundry_toml(foundry: String, state: &mut SlitherData) { + let foundry: FoundryToml = match toml::from_str(&foundry) { + Ok(foundry) => foundry, + Err(e) => { + eprintln!("Error parsing foundry.toml: {}", e); + return; + } + }; + match extract_foundry_src(foundry.clone()).unwrap_or(FoundryArrOrStr::Str("src".to_string())) { + FoundryArrOrStr::Arr(srcs) => { + for src in srcs { + state.src_paths.push(src.to_string()); + } + } + FoundryArrOrStr::Str(src) => { + state.src_paths.push(src); + } + }; +} + +/** + * Find the foundry.toml config file in the given workspace using glob. + */ +pub fn find_foundry_toml_config(workspace: &str) -> Result> { + let mut foundry_toml_path = String::new(); + for entry in glob(&format!("{}/**/foundry.toml", workspace))? { + match entry { + Ok(path) => { + foundry_toml_path = path.display().to_string(); + break; + } + Err(e) => eprintln!("{:?}", e), + } + } + if foundry_toml_path.is_empty() { + return Err(Box::new(SlitherError::FoundryTomlNotFound)); + } + Ok(foundry_toml_path) +} diff --git a/toolchains/solidity/extension/.vscode/extensions.json b/toolchains/solidity/extension/.vscode/extensions.json new file mode 100644 index 00000000..dd01eb35 --- /dev/null +++ b/toolchains/solidity/extension/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": ["dbaeumer.vscode-eslint", "amodio.tsl-problem-matcher", "ms-vscode.extension-test-runner"] +} diff --git a/toolchains/solidity/extension/.vscode/launch.json b/toolchains/solidity/extension/.vscode/launch.json index 81b773fe..c42edc04 100644 --- a/toolchains/solidity/extension/.vscode/launch.json +++ b/toolchains/solidity/extension/.vscode/launch.json @@ -1,3 +1,7 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +// Use IntelliSense to learn about possible attributes. +// Hover to view descriptions of existing attributes. +// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 { "version": "0.2.0", "configurations": [ diff --git a/toolchains/solidity/extension/.vscode/settings.json b/toolchains/solidity/extension/.vscode/settings.json new file mode 100644 index 00000000..5c5ac48c --- /dev/null +++ b/toolchains/solidity/extension/.vscode/settings.json @@ -0,0 +1,13 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "files.exclude": { + "out": false, // set this to true to hide the "out" folder with the compiled JS files + "dist": false // set this to true to hide the "dist" folder with the compiled JS files + }, + "search.exclude": { + "out": true, // set this to false to include "out" folder in search results + "dist": true // set this to false to include "dist" folder in search results + }, + // Turn off tsc task auto detection since we have the necessary tasks as npm scripts + "typescript.tsc.autoDetect": "off" +} \ No newline at end of file diff --git a/toolchains/solidity/extension/.vscode/tasks.json b/toolchains/solidity/extension/.vscode/tasks.json index d039804f..41f39b03 100644 --- a/toolchains/solidity/extension/.vscode/tasks.json +++ b/toolchains/solidity/extension/.vscode/tasks.json @@ -1,3 +1,5 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format { "version": "2.0.0", "tasks": [ @@ -14,7 +16,7 @@ "kind": "build", "isDefault": true } - }, + },| { "type": "npm", "script": "watch-tests", @@ -35,4 +37,4 @@ "problemMatcher": [] } ] -} \ No newline at end of file +} diff --git a/toolchains/solidity/extension/package.json b/toolchains/solidity/extension/package.json index abeb5bcd..36580d4a 100644 --- a/toolchains/solidity/extension/package.json +++ b/toolchains/solidity/extension/package.json @@ -24,7 +24,7 @@ "explorer/context": [ { "when": "resourceLangId == solidity", - "command": "osmium.lint-sol-file", + "command": "osmium.format-sol-file", "group": "7_modification" }, { diff --git a/toolchains/solidity/extension/src/extension.ts b/toolchains/solidity/extension/src/extension.ts index a8f7376a..6c7c9fa8 100644 --- a/toolchains/solidity/extension/src/extension.ts +++ b/toolchains/solidity/extension/src/extension.ts @@ -19,10 +19,11 @@ let testManager: TestManager; export async function activate(context: ExtensionContext) { linterClient = await createLinterClient(context); foundryCompilerClient = createFoundryCompilerClient(context); - slitherClient = createSlitherClient(context); + slitherClient = await createSlitherClient(context); testsPositionsClient = await createTestsPositionsClient(context); - if (workspace.workspaceFolders?.length) - {testManager = new TestManager(testsPositionsClient, workspace.workspaceFolders[0].uri.fsPath);} + if (workspace.workspaceFolders?.length) { + testManager = new TestManager(testsPositionsClient, workspace.workspaceFolders[0].uri.fsPath); + } context.subscriptions.push(linterClient, foundryCompilerClient, slitherClient, testsPositionsClient, testManager.testController); diff --git a/toolchains/solidity/extension/src/slither.ts b/toolchains/solidity/extension/src/slither.ts index ac1128e8..1250545d 100644 --- a/toolchains/solidity/extension/src/slither.ts +++ b/toolchains/solidity/extension/src/slither.ts @@ -9,7 +9,7 @@ import { } from 'vscode-languageclient/node'; import { TextDecoder } from 'util'; -export function createSlitherClient(context: ExtensionContext): LanguageClient { +export async function createSlitherClient(context: ExtensionContext): Promise { // The server is implemented in node const serverBinary = context.asAbsolutePath( path.join( @@ -55,7 +55,7 @@ export function createSlitherClient(context: ExtensionContext): LanguageClient { }); // Start the client. This will also launch the server - client.start(); + await client.start(); return client; } \ No newline at end of file