From 2980ba1fde50a6fc8863750b9dd7f09e3c1227ce Mon Sep 17 00:00:00 2001 From: robojumper Date: Mon, 4 May 2020 13:29:09 +0200 Subject: Support build.rs cargo:rustc-cfg --- crates/ra_cfg/src/lib.rs | 9 ++ crates/ra_project_model/src/cargo_workspace.rs | 10 +- crates/ra_project_model/src/lib.rs | 7 ++ crates/rust-analyzer/tests/heavy_tests/main.rs | 135 ++++++++++++++++++++++++- 4 files changed, 158 insertions(+), 3 deletions(-) diff --git a/crates/ra_cfg/src/lib.rs b/crates/ra_cfg/src/lib.rs index 51d953f6e..697a04581 100644 --- a/crates/ra_cfg/src/lib.rs +++ b/crates/ra_cfg/src/lib.rs @@ -53,4 +53,13 @@ impl CfgOptions { pub fn insert_features(&mut self, iter: impl IntoIterator) { iter.into_iter().for_each(|feat| self.insert_key_value("feature".into(), feat)); } + + /// Shortcut to set cfgs + pub fn insert_cfgs(&mut self, iter: impl IntoIterator) { + iter.into_iter().for_each(|cfg| match cfg.find('=') { + Some(split) => self + .insert_key_value(cfg[0..split].into(), cfg[split + 1..].trim_matches('"').into()), + None => self.insert_atom(cfg), + }); + } } diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 362ee30fe..afbd30164 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs @@ -83,6 +83,7 @@ pub struct PackageData { pub dependencies: Vec, pub edition: Edition, pub features: Vec, + pub cfgs: Vec, pub out_dir: Option, pub proc_macro_dylib_path: Option, } @@ -165,10 +166,12 @@ impl CargoWorkspace { })?; let mut out_dir_by_id = FxHashMap::default(); + let mut cfgs = FxHashMap::default(); let mut proc_macro_dylib_paths = FxHashMap::default(); if cargo_features.load_out_dirs_from_check { let resources = load_extern_resources(cargo_toml, cargo_features)?; out_dir_by_id = resources.out_dirs; + cfgs = resources.cfgs; proc_macro_dylib_paths = resources.proc_dylib_paths; } @@ -194,6 +197,7 @@ impl CargoWorkspace { edition, dependencies: Vec::new(), features: Vec::new(), + cfgs: cfgs.get(&id).cloned().unwrap_or_default(), out_dir: out_dir_by_id.get(&id).cloned(), proc_macro_dylib_path: proc_macro_dylib_paths.get(&id).cloned(), }); @@ -275,6 +279,7 @@ impl CargoWorkspace { pub struct ExternResources { out_dirs: FxHashMap, proc_dylib_paths: FxHashMap, + cfgs: FxHashMap>, } pub fn load_extern_resources( @@ -300,8 +305,9 @@ pub fn load_extern_resources( for message in cargo_metadata::parse_messages(output.stdout.as_slice()) { if let Ok(message) = message { match message { - Message::BuildScriptExecuted(BuildScript { package_id, out_dir, .. }) => { - res.out_dirs.insert(package_id, out_dir); + Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => { + res.out_dirs.insert(package_id.clone(), out_dir); + res.cfgs.insert(package_id, cfgs); } Message::CompilerArtifact(message) => { diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 731cbd291..2d5d61b61 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -399,6 +399,13 @@ impl ProjectWorkspace { let cfg_options = { let mut opts = default_cfg_options.clone(); opts.insert_features(cargo[pkg].features.iter().map(Into::into)); + opts.insert_cfgs( + cargo[pkg] + .cfgs + .iter() + .filter_map(|c| c.to_str()) + .map(Into::into), + ); opts }; let mut env = Env::default(); diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index a218da76d..e94fbce3a 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -9,7 +9,8 @@ use lsp_types::{ }; use rust_analyzer::req::{ CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument, - Formatting, GotoDefinition, HoverRequest, OnEnter, Runnables, RunnablesParams, + Formatting, GotoDefinition, GotoTypeDefinition, HoverRequest, OnEnter, Runnables, + RunnablesParams, }; use serde_json::json; use tempfile::TempDir; @@ -707,3 +708,135 @@ pub fn foo(_input: TokenStream) -> TokenStream { let value = res.get("contents").unwrap().get("value").unwrap().to_string(); assert_eq!(value, r#""```rust\nfoo::Bar\nfn bar()\n```""#) } + +#[test] +fn build_rs_cfgs() { + if skip_slow_tests() { + return; + } + + let server = Project::with_fixture( + r###" +//- Cargo.toml +[package] +name = "foo" +version = "0.0.0" + +//- build.rs + +fn main() { + println!("cargo:rustc-cfg=atom_cfg"); + println!("cargo:rustc-cfg=featlike=\"set\""); + println!("cargo:rerun-if-changed=build.rs"); +} +//- src/main.rs +#[cfg(atom_cfg)] +struct A; + +#[cfg(bad_atom_cfg)] +struct A; + +#[cfg(featlike = "set")] +struct B; + +#[cfg(featlike = "not_set")] +struct B; + +fn main() { + let va = A; + let vb = B; +} +"###, + ) + .with_config(|config| { + config.cargo.load_out_dirs_from_check = true; + }) + .server(); + server.wait_until_workspace_is_loaded(); + server.request::( + GotoDefinitionParams { + text_document_position_params: TextDocumentPositionParams::new( + server.doc_id("src/main.rs"), + Position::new(13, 9), + ), + work_done_progress_params: Default::default(), + partial_result_params: Default::default(), + }, + json!([{ + "originSelectionRange": { + "end": { + "character": 10, + "line": 13 + }, + "start": { + "character": 8, + "line":13 + } + }, + "targetRange": { + "end": { + "character": 9, + "line": 1 + }, + "start": { + "character": 0, + "line":0 + } + }, + "targetSelectionRange": { + "end": { + "character": 8, + "line": 1 + }, + "start": { + "character": 7, + "line": 1 + } + }, + "targetUri": "file:///[..]src/main.rs" + }]), + ); + server.request::( + GotoDefinitionParams { + text_document_position_params: TextDocumentPositionParams::new( + server.doc_id("src/main.rs"), + Position::new(14, 9), + ), + work_done_progress_params: Default::default(), + partial_result_params: Default::default(), + }, + json!([{ + "originSelectionRange": { + "end": { + "character": 10, + "line": 14 + }, + "start": { + "character": 8, + "line":14 + } + }, + "targetRange": { + "end": { + "character": 9, + "line": 7 + }, + "start": { + "character": 0, + "line":6 + } + }, + "targetSelectionRange": { + "end": { + "character": 8, + "line": 7 + }, + "start": { + "character": 7, + "line": 7 + } + }, + "targetUri": "file:///[..]src/main.rs" + }]), + ); +} -- cgit v1.2.3 From 059fc8696331c2413565e8c682067c76aa6acaf8 Mon Sep 17 00:00:00 2001 From: robojumper Date: Tue, 5 May 2020 14:53:18 +0200 Subject: Merge heavy tests --- crates/rust-analyzer/tests/heavy_tests/main.rs | 239 +++++++++++-------------- 1 file changed, 105 insertions(+), 134 deletions(-) diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index e94fbce3a..2e52a29a8 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -575,7 +575,7 @@ version = \"0.0.0\" } #[test] -fn resolve_include_concat_env() { +fn out_dirs_check() { if skip_slow_tests() { return; } @@ -598,11 +598,28 @@ fn main() { r#"pub fn message() -> &'static str { "Hello, World!" }"#, ) .unwrap(); + println!("cargo:rustc-cfg=atom_cfg"); + println!("cargo:rustc-cfg=featlike=\"set\""); println!("cargo:rerun-if-changed=build.rs"); } //- src/main.rs include!(concat!(env!("OUT_DIR"), "/hello.rs")); +#[cfg(atom_cfg)] +struct A; +#[cfg(bad_atom_cfg)] +struct A; +#[cfg(featlike = "set")] +struct B; +#[cfg(featlike = "not_set")] +struct B; + +fn main() { + let va = A; + let vb = B; + message(); +} + fn main() { message(); } "###, ) @@ -614,12 +631,98 @@ fn main() { message(); } let res = server.send_request::(GotoDefinitionParams { text_document_position_params: TextDocumentPositionParams::new( server.doc_id("src/main.rs"), - Position::new(2, 15), + Position::new(14, 8), ), work_done_progress_params: Default::default(), partial_result_params: Default::default(), }); assert!(format!("{}", res).contains("hello.rs")); + server.request::( + GotoDefinitionParams { + text_document_position_params: TextDocumentPositionParams::new( + server.doc_id("src/main.rs"), + Position::new(12, 9), + ), + work_done_progress_params: Default::default(), + partial_result_params: Default::default(), + }, + json!([{ + "originSelectionRange": { + "end": { + "character": 10, + "line": 12 + }, + "start": { + "character": 8, + "line": 12 + } + }, + "targetRange": { + "end": { + "character": 9, + "line": 3 + }, + "start": { + "character": 0, + "line": 2 + } + }, + "targetSelectionRange": { + "end": { + "character": 8, + "line": 3 + }, + "start": { + "character": 7, + "line": 3 + } + }, + "targetUri": "file:///[..]src/main.rs" + }]), + ); + server.request::( + GotoDefinitionParams { + text_document_position_params: TextDocumentPositionParams::new( + server.doc_id("src/main.rs"), + Position::new(13, 9), + ), + work_done_progress_params: Default::default(), + partial_result_params: Default::default(), + }, + json!([{ + "originSelectionRange": { + "end": { + "character": 10, + "line": 13 + }, + "start": { + "character": 8, + "line":13 + } + }, + "targetRange": { + "end": { + "character": 9, + "line": 7 + }, + "start": { + "character": 0, + "line":6 + } + }, + "targetSelectionRange": { + "end": { + "character": 8, + "line": 7 + }, + "start": { + "character": 7, + "line": 7 + } + }, + "targetUri": "file:///[..]src/main.rs" + }]), + ); } #[test] @@ -708,135 +811,3 @@ pub fn foo(_input: TokenStream) -> TokenStream { let value = res.get("contents").unwrap().get("value").unwrap().to_string(); assert_eq!(value, r#""```rust\nfoo::Bar\nfn bar()\n```""#) } - -#[test] -fn build_rs_cfgs() { - if skip_slow_tests() { - return; - } - - let server = Project::with_fixture( - r###" -//- Cargo.toml -[package] -name = "foo" -version = "0.0.0" - -//- build.rs - -fn main() { - println!("cargo:rustc-cfg=atom_cfg"); - println!("cargo:rustc-cfg=featlike=\"set\""); - println!("cargo:rerun-if-changed=build.rs"); -} -//- src/main.rs -#[cfg(atom_cfg)] -struct A; - -#[cfg(bad_atom_cfg)] -struct A; - -#[cfg(featlike = "set")] -struct B; - -#[cfg(featlike = "not_set")] -struct B; - -fn main() { - let va = A; - let vb = B; -} -"###, - ) - .with_config(|config| { - config.cargo.load_out_dirs_from_check = true; - }) - .server(); - server.wait_until_workspace_is_loaded(); - server.request::( - GotoDefinitionParams { - text_document_position_params: TextDocumentPositionParams::new( - server.doc_id("src/main.rs"), - Position::new(13, 9), - ), - work_done_progress_params: Default::default(), - partial_result_params: Default::default(), - }, - json!([{ - "originSelectionRange": { - "end": { - "character": 10, - "line": 13 - }, - "start": { - "character": 8, - "line":13 - } - }, - "targetRange": { - "end": { - "character": 9, - "line": 1 - }, - "start": { - "character": 0, - "line":0 - } - }, - "targetSelectionRange": { - "end": { - "character": 8, - "line": 1 - }, - "start": { - "character": 7, - "line": 1 - } - }, - "targetUri": "file:///[..]src/main.rs" - }]), - ); - server.request::( - GotoDefinitionParams { - text_document_position_params: TextDocumentPositionParams::new( - server.doc_id("src/main.rs"), - Position::new(14, 9), - ), - work_done_progress_params: Default::default(), - partial_result_params: Default::default(), - }, - json!([{ - "originSelectionRange": { - "end": { - "character": 10, - "line": 14 - }, - "start": { - "character": 8, - "line":14 - } - }, - "targetRange": { - "end": { - "character": 9, - "line": 7 - }, - "start": { - "character": 0, - "line":6 - } - }, - "targetSelectionRange": { - "end": { - "character": 8, - "line": 7 - }, - "start": { - "character": 7, - "line": 7 - } - }, - "targetUri": "file:///[..]src/main.rs" - }]), - ); -} -- cgit v1.2.3 From f2dd233ddc60b647fe9c32ea2d712224005ae99e Mon Sep 17 00:00:00 2001 From: robojumper Date: Tue, 5 May 2020 14:53:52 +0200 Subject: Assume cargo_metadata uses String for cfgs soon --- crates/ra_project_model/src/cargo_workspace.rs | 11 ++++++++--- crates/ra_project_model/src/lib.rs | 8 +------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index afbd30164..bf83adc42 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs @@ -83,7 +83,7 @@ pub struct PackageData { pub dependencies: Vec, pub edition: Edition, pub features: Vec, - pub cfgs: Vec, + pub cfgs: Vec, pub out_dir: Option, pub proc_macro_dylib_path: Option, } @@ -279,7 +279,7 @@ impl CargoWorkspace { pub struct ExternResources { out_dirs: FxHashMap, proc_dylib_paths: FxHashMap, - cfgs: FxHashMap>, + cfgs: FxHashMap>, } pub fn load_extern_resources( @@ -307,7 +307,12 @@ pub fn load_extern_resources( match message { Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => { res.out_dirs.insert(package_id.clone(), out_dir); - res.cfgs.insert(package_id, cfgs); + res.cfgs.insert( + package_id, + // FIXME: Current `cargo_metadata` uses `PathBuf` instead of `String`, + // change when https://github.com/oli-obk/cargo_metadata/pulls/112 reaches crates.io + cfgs.iter().filter_map(|c| c.to_str().map(|s| s.to_owned())).collect(), + ); } Message::CompilerArtifact(message) => { diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 2d5d61b61..7231da221 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -399,13 +399,7 @@ impl ProjectWorkspace { let cfg_options = { let mut opts = default_cfg_options.clone(); opts.insert_features(cargo[pkg].features.iter().map(Into::into)); - opts.insert_cfgs( - cargo[pkg] - .cfgs - .iter() - .filter_map(|c| c.to_str()) - .map(Into::into), - ); + opts.insert_cfgs(cargo[pkg].cfgs.iter().map(Into::into)); opts }; let mut env = Env::default(); -- cgit v1.2.3 From ffaef1b7aeb61984992e231d9af20f39486403ea Mon Sep 17 00:00:00 2001 From: Craig Disselkoen Date: Tue, 5 May 2020 11:59:41 -0700 Subject: ra_project_model: look for Cargo in more places See #3118 --- Cargo.lock | 77 +++++++++++++++++++++++++- crates/ra_project_model/Cargo.toml | 2 + crates/ra_project_model/src/cargo_workspace.rs | 59 +++++++++++++++++--- 3 files changed, 128 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5d50a766f..b2d4569b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,6 +21,12 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344" +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + [[package]] name = "arrayvec" version = "0.5.1" @@ -66,6 +72,12 @@ dependencies = [ "libc", ] +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + [[package]] name = "base64" version = "0.12.0" @@ -78,6 +90,17 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "blake2b_simd" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + [[package]] name = "bstr" version = "0.2.12" @@ -212,6 +235,12 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "crossbeam" version = "0.7.3" @@ -289,6 +318,28 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" +[[package]] +name = "dirs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +dependencies = [ + "cfg-if", + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" +dependencies = [ + "cfg-if", + "libc", + "redox_users", + "winapi 0.3.8", +] + [[package]] name = "drop_bomb" version = "0.1.4" @@ -659,7 +710,7 @@ version = "0.74.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0e6a2b8837d27b29deb3f3e6dc1c6d2f57947677f9be1024e482ec5b59525" dependencies = [ - "base64", + "base64 0.12.0", "bitflags", "serde", "serde_json", @@ -1158,6 +1209,7 @@ version = "0.1.0" dependencies = [ "anyhow", "cargo_metadata", + "dirs", "log", "ra_arena", "ra_cfg", @@ -1298,6 +1350,17 @@ version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +[[package]] +name = "redox_users" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" +dependencies = [ + "getrandom", + "redox_syscall", + "rust-argon2", +] + [[package]] name = "regex" version = "1.3.7" @@ -1382,6 +1445,18 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "rust-argon2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" +dependencies = [ + "base64 0.11.0", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + [[package]] name = "rustc-ap-rustc_lexer" version = "656.0.0" diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml index 5e651fe70..91958ed94 100644 --- a/crates/ra_project_model/Cargo.toml +++ b/crates/ra_project_model/Cargo.toml @@ -22,3 +22,5 @@ serde = { version = "1.0.106", features = ["derive"] } serde_json = "1.0.48" anyhow = "1.0.26" + +dirs = "2.0" diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 59f46a2a0..a15a8c68e 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs @@ -8,7 +8,7 @@ use std::{ process::Command, }; -use anyhow::{Context, Result}; +use anyhow::{Context, Error, Result}; use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; use ra_arena::{Arena, Idx}; use ra_db::Edition; @@ -145,12 +145,8 @@ impl CargoWorkspace { cargo_toml: &Path, cargo_features: &CargoConfig, ) -> Result { - let _ = Command::new(cargo_binary()) - .arg("--version") - .output() - .context("failed to run `cargo --version`, is `cargo` in PATH?")?; - let mut meta = MetadataCommand::new(); + meta.cargo_path(cargo_binary()?); meta.manifest_path(cargo_toml); if cargo_features.all_features { meta.features(CargoOpt::AllFeatures); @@ -288,7 +284,7 @@ pub fn load_extern_resources( cargo_toml: &Path, cargo_features: &CargoConfig, ) -> Result { - let mut cmd = Command::new(cargo_binary()); + let mut cmd = Command::new(cargo_binary()?); cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml); if cargo_features.all_features { cmd.arg("--all-features"); @@ -337,6 +333,51 @@ fn is_dylib(path: &Path) -> bool { } } -fn cargo_binary() -> String { - env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()) +/// Return a `String` to use for executable `cargo`. +/// +/// E.g., this may just be `cargo` if that gives a valid Cargo executable; or it +/// may be a full path to a valid Cargo. +fn cargo_binary() -> Result { + // The current implementation checks three places for a `cargo` to use: + // 1) $CARGO environment variable (erroring if this is set but not a usable Cargo) + // 2) `cargo` + // 3) `~/.cargo/bin/cargo` + if let Ok(path) = env::var("CARGO") { + if is_valid_cargo(&path) { + Ok(path) + } else { + Err(Error::msg("`CARGO` environment variable points to something that's not a valid Cargo executable")) + } + } else { + let final_path: Option = if is_valid_cargo("cargo") { + Some("cargo".to_owned()) + } else { + if let Some(mut path) = dirs::home_dir() { + path.push(".cargo"); + path.push("bin"); + path.push("cargo"); + if is_valid_cargo(&path) { + Some(path.into_os_string().into_string().expect("Invalid Unicode in path")) + } else { + None + } + } else { + None + } + }; + final_path.ok_or( + // This error message may also be caused by $PATH or $CARGO not being set correctly for VSCode, + // even if they are set correctly in a terminal. + // On macOS in particular, launching VSCode from terminal with `code ` causes VSCode + // to inherit environment variables including $PATH and $CARGO from that terminal; but + // launching VSCode from Dock does not inherit environment variables from a terminal. + // For more discussion, see #3118. + Error::msg("Failed to find `cargo` executable. Make sure `cargo` is in `$PATH`, or set `$CARGO` to point to a valid Cargo executable.") + ) + } +} + +/// Does the given `Path` point to a usable `Cargo`? +fn is_valid_cargo(p: impl AsRef) -> bool { + Command::new(p.as_ref()).arg("--version").output().is_ok() } -- cgit v1.2.3 From 5aa1bba107ef434e61c3136120b9478a307d67a9 Mon Sep 17 00:00:00 2001 From: Craig Disselkoen Date: Tue, 5 May 2020 13:44:43 -0700 Subject: more generic, find rustc as well --- crates/ra_project_model/src/cargo_workspace.rs | 57 ++-------------------- crates/ra_project_model/src/find_executables.rs | 63 +++++++++++++++++++++++++ crates/ra_project_model/src/lib.rs | 1 + crates/ra_project_model/src/sysroot.rs | 4 +- 4 files changed, 71 insertions(+), 54 deletions(-) create mode 100644 crates/ra_project_model/src/find_executables.rs diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index a15a8c68e..c7350d1e0 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs @@ -1,14 +1,14 @@ //! FIXME: write short doc here use std::{ - env, ffi::OsStr, ops, path::{Path, PathBuf}, process::Command, }; -use anyhow::{Context, Error, Result}; +use super::find_executables::get_path_for_executable; +use anyhow::{Context, Result}; use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; use ra_arena::{Arena, Idx}; use ra_db::Edition; @@ -146,7 +146,7 @@ impl CargoWorkspace { cargo_features: &CargoConfig, ) -> Result { let mut meta = MetadataCommand::new(); - meta.cargo_path(cargo_binary()?); + meta.cargo_path(get_path_for_executable("cargo")?); meta.manifest_path(cargo_toml); if cargo_features.all_features { meta.features(CargoOpt::AllFeatures); @@ -284,7 +284,7 @@ pub fn load_extern_resources( cargo_toml: &Path, cargo_features: &CargoConfig, ) -> Result { - let mut cmd = Command::new(cargo_binary()?); + let mut cmd = Command::new(get_path_for_executable("cargo")?); cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml); if cargo_features.all_features { cmd.arg("--all-features"); @@ -332,52 +332,3 @@ fn is_dylib(path: &Path) -> bool { Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"), } } - -/// Return a `String` to use for executable `cargo`. -/// -/// E.g., this may just be `cargo` if that gives a valid Cargo executable; or it -/// may be a full path to a valid Cargo. -fn cargo_binary() -> Result { - // The current implementation checks three places for a `cargo` to use: - // 1) $CARGO environment variable (erroring if this is set but not a usable Cargo) - // 2) `cargo` - // 3) `~/.cargo/bin/cargo` - if let Ok(path) = env::var("CARGO") { - if is_valid_cargo(&path) { - Ok(path) - } else { - Err(Error::msg("`CARGO` environment variable points to something that's not a valid Cargo executable")) - } - } else { - let final_path: Option = if is_valid_cargo("cargo") { - Some("cargo".to_owned()) - } else { - if let Some(mut path) = dirs::home_dir() { - path.push(".cargo"); - path.push("bin"); - path.push("cargo"); - if is_valid_cargo(&path) { - Some(path.into_os_string().into_string().expect("Invalid Unicode in path")) - } else { - None - } - } else { - None - } - }; - final_path.ok_or( - // This error message may also be caused by $PATH or $CARGO not being set correctly for VSCode, - // even if they are set correctly in a terminal. - // On macOS in particular, launching VSCode from terminal with `code ` causes VSCode - // to inherit environment variables including $PATH and $CARGO from that terminal; but - // launching VSCode from Dock does not inherit environment variables from a terminal. - // For more discussion, see #3118. - Error::msg("Failed to find `cargo` executable. Make sure `cargo` is in `$PATH`, or set `$CARGO` to point to a valid Cargo executable.") - ) - } -} - -/// Does the given `Path` point to a usable `Cargo`? -fn is_valid_cargo(p: impl AsRef) -> bool { - Command::new(p.as_ref()).arg("--version").output().is_ok() -} diff --git a/crates/ra_project_model/src/find_executables.rs b/crates/ra_project_model/src/find_executables.rs new file mode 100644 index 000000000..9b020d3da --- /dev/null +++ b/crates/ra_project_model/src/find_executables.rs @@ -0,0 +1,63 @@ +use anyhow::{Error, Result}; +use std::env; +use std::path::Path; +use std::process::Command; + +/// Return a `String` to use for the given executable. +/// +/// E.g., `get_path_for_executable("cargo")` may return just `cargo` if that +/// gives a valid Cargo executable; or it may return a full path to a valid +/// Cargo. +pub fn get_path_for_executable(executable_name: impl AsRef) -> Result { + // The current implementation checks three places for an executable to use: + // 1) Appropriate environment variable (erroring if this is set but not a usable executable) + // example: for cargo, this checks $CARGO environment variable; for rustc, $RUSTC; etc + // 2) `` + // example: for cargo, this tries just `cargo`, which will succeed if `cargo` in on the $PATH + // 3) `~/.cargo/bin/` + // example: for cargo, this tries ~/.cargo/bin/cargo + // It seems that this is a reasonable place to try for cargo, rustc, and rustup + let executable_name = executable_name.as_ref(); + let env_var = executable_name.to_ascii_uppercase(); + if let Ok(path) = env::var(&env_var) { + if is_valid_executable(&path) { + Ok(path) + } else { + Err(Error::msg(format!("`{}` environment variable points to something that's not a valid executable", env_var))) + } + } else { + let final_path: Option = if is_valid_executable(executable_name) { + Some(executable_name.to_owned()) + } else { + if let Some(mut path) = dirs::home_dir() { + path.push(".cargo"); + path.push("bin"); + path.push(executable_name); + if is_valid_executable(&path) { + Some(path.into_os_string().into_string().expect("Invalid Unicode in path")) + } else { + None + } + } else { + None + } + }; + final_path.ok_or( + // This error message may also be caused by $PATH or $CARGO/$RUSTC/etc not being set correctly + // for VSCode, even if they are set correctly in a terminal. + // On macOS in particular, launching VSCode from terminal with `code ` causes VSCode + // to inherit environment variables including $PATH, $CARGO, $RUSTC, etc from that terminal; + // but launching VSCode from Dock does not inherit environment variables from a terminal. + // For more discussion, see #3118. + Error::msg(format!("Failed to find `{}` executable. Make sure `{}` is in `$PATH`, or set `${}` to point to a valid executable.", executable_name, executable_name, env_var)) + ) + } +} + +/// Does the given `Path` point to a usable executable? +/// +/// (assumes the executable takes a `--version` switch and writes to stdout, +/// which is true for `cargo`, `rustc`, and `rustup`) +fn is_valid_executable(p: impl AsRef) -> bool { + Command::new(p.as_ref()).arg("--version").output().is_ok() +} diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index c2b33c1dc..5028b6b6d 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -1,6 +1,7 @@ //! FIXME: write short doc here mod cargo_workspace; +mod find_executables; mod json_project; mod sysroot; diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs index 55ff5ad80..8d68032b2 100644 --- a/crates/ra_project_model/src/sysroot.rs +++ b/crates/ra_project_model/src/sysroot.rs @@ -1,5 +1,6 @@ //! FIXME: write short doc here +use super::find_executables::get_path_for_executable; use anyhow::{bail, Context, Result}; use std::{ env, ops, @@ -114,7 +115,8 @@ fn get_or_install_rust_src(cargo_toml: &Path) -> Result { if let Ok(path) = env::var("RUST_SRC_PATH") { return Ok(path.into()); } - let rustc_output = run_command_in_cargo_dir(cargo_toml, "rustc", &["--print", "sysroot"])?; + let rustc = get_path_for_executable("rustc")?; + let rustc_output = run_command_in_cargo_dir(cargo_toml, &rustc, &["--print", "sysroot"])?; let stdout = String::from_utf8(rustc_output.stdout)?; let sysroot_path = Path::new(stdout.trim()); let src_path = sysroot_path.join("lib/rustlib/src/rust/src"); -- cgit v1.2.3 From 303b444dbb66019fc916dd350e54f7675aa3007f Mon Sep 17 00:00:00 2001 From: Craig Disselkoen Date: Tue, 5 May 2020 14:07:10 -0700 Subject: pull function out into new crate ra_env; use in ra_flycheck as well --- Cargo.lock | 11 ++++- crates/ra_env/Cargo.toml | 9 ++++ crates/ra_env/src/lib.rs | 63 +++++++++++++++++++++++++ crates/ra_flycheck/Cargo.toml | 1 + crates/ra_flycheck/src/lib.rs | 8 +--- crates/ra_project_model/Cargo.toml | 5 +- crates/ra_project_model/src/cargo_workspace.rs | 2 +- crates/ra_project_model/src/find_executables.rs | 63 ------------------------- crates/ra_project_model/src/lib.rs | 4 +- crates/ra_project_model/src/sysroot.rs | 5 +- 10 files changed, 93 insertions(+), 78 deletions(-) create mode 100644 crates/ra_env/Cargo.toml create mode 100644 crates/ra_env/src/lib.rs delete mode 100644 crates/ra_project_model/src/find_executables.rs diff --git a/Cargo.lock b/Cargo.lock index b2d4569b3..28424b7d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -999,6 +999,14 @@ dependencies = [ "test_utils", ] +[[package]] +name = "ra_env" +version = "0.1.0" +dependencies = [ + "anyhow", + "dirs", +] + [[package]] name = "ra_flycheck" version = "0.1.0" @@ -1009,6 +1017,7 @@ dependencies = [ "jod-thread", "log", "lsp-types", + "ra_env", "serde_json", ] @@ -1209,11 +1218,11 @@ version = "0.1.0" dependencies = [ "anyhow", "cargo_metadata", - "dirs", "log", "ra_arena", "ra_cfg", "ra_db", + "ra_env", "ra_proc_macro", "rustc-hash", "serde", diff --git a/crates/ra_env/Cargo.toml b/crates/ra_env/Cargo.toml new file mode 100644 index 000000000..7fed446a7 --- /dev/null +++ b/crates/ra_env/Cargo.toml @@ -0,0 +1,9 @@ +[package] +edition = "2018" +name = "ra_env" +version = "0.1.0" +authors = ["rust-analyzer developers"] + +[dependencies] +anyhow = "1.0.26" +dirs = "2.0" diff --git a/crates/ra_env/src/lib.rs b/crates/ra_env/src/lib.rs new file mode 100644 index 000000000..cb9fbf80c --- /dev/null +++ b/crates/ra_env/src/lib.rs @@ -0,0 +1,63 @@ +use anyhow::{Error, Result}; +use std::env; +use std::path::Path; +use std::process::Command; + +/// Return a `String` to use for the given executable. +/// +/// E.g., `get_path_for_executable("cargo")` may return just `cargo` if that +/// gives a valid Cargo executable; or it may return a full path to a valid +/// Cargo. +pub fn get_path_for_executable(executable_name: impl AsRef) -> Result { + // The current implementation checks three places for an executable to use: + // 1) Appropriate environment variable (erroring if this is set but not a usable executable) + // example: for cargo, this checks $CARGO environment variable; for rustc, $RUSTC; etc + // 2) `` + // example: for cargo, this tries just `cargo`, which will succeed if `cargo` is on the $PATH + // 3) `~/.cargo/bin/` + // example: for cargo, this tries ~/.cargo/bin/cargo + // It seems that this is a reasonable place to try for cargo, rustc, and rustup + let executable_name = executable_name.as_ref(); + let env_var = executable_name.to_ascii_uppercase(); + if let Ok(path) = env::var(&env_var) { + if is_valid_executable(&path) { + Ok(path) + } else { + Err(Error::msg(format!("`{}` environment variable points to something that's not a valid executable", env_var))) + } + } else { + let final_path: Option = if is_valid_executable(executable_name) { + Some(executable_name.to_owned()) + } else { + if let Some(mut path) = dirs::home_dir() { + path.push(".cargo"); + path.push("bin"); + path.push(executable_name); + if is_valid_executable(&path) { + Some(path.into_os_string().into_string().expect("Invalid Unicode in path")) + } else { + None + } + } else { + None + } + }; + final_path.ok_or( + // This error message may also be caused by $PATH or $CARGO/$RUSTC/etc not being set correctly + // for VSCode, even if they are set correctly in a terminal. + // On macOS in particular, launching VSCode from terminal with `code ` causes VSCode + // to inherit environment variables including $PATH, $CARGO, $RUSTC, etc from that terminal; + // but launching VSCode from Dock does not inherit environment variables from a terminal. + // For more discussion, see #3118. + Error::msg(format!("Failed to find `{}` executable. Make sure `{}` is in `$PATH`, or set `${}` to point to a valid executable.", executable_name, executable_name, env_var)) + ) + } +} + +/// Does the given `Path` point to a usable executable? +/// +/// (assumes the executable takes a `--version` switch and writes to stdout, +/// which is true for `cargo`, `rustc`, and `rustup`) +fn is_valid_executable(p: impl AsRef) -> bool { + Command::new(p.as_ref()).arg("--version").output().is_ok() +} diff --git a/crates/ra_flycheck/Cargo.toml b/crates/ra_flycheck/Cargo.toml index 3d5093264..d0f7fb2dc 100644 --- a/crates/ra_flycheck/Cargo.toml +++ b/crates/ra_flycheck/Cargo.toml @@ -14,6 +14,7 @@ log = "0.4.8" cargo_metadata = "0.9.1" serde_json = "1.0.48" jod-thread = "0.1.1" +ra_env = { path = "../ra_env" } [dev-dependencies] insta = "0.16.0" diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs index f27252949..d8b727b0e 100644 --- a/crates/ra_flycheck/src/lib.rs +++ b/crates/ra_flycheck/src/lib.rs @@ -4,7 +4,6 @@ mod conv; use std::{ - env, io::{self, BufRead, BufReader}, path::PathBuf, process::{Command, Stdio}, @@ -17,6 +16,7 @@ use lsp_types::{ CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressEnd, WorkDoneProgressReport, }; +use ra_env::get_path_for_executable; use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic}; @@ -216,7 +216,7 @@ impl FlycheckThread { let mut cmd = match &self.config { FlycheckConfig::CargoCommand { command, all_targets, all_features, extra_args } => { - let mut cmd = Command::new(cargo_binary()); + let mut cmd = Command::new(get_path_for_executable("cargo").unwrap()); cmd.arg(command); cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]); cmd.arg(self.workspace_root.join("Cargo.toml")); @@ -337,7 +337,3 @@ fn run_cargo( Ok(()) } - -fn cargo_binary() -> String { - env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()) -} diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml index 91958ed94..626478468 100644 --- a/crates/ra_project_model/Cargo.toml +++ b/crates/ra_project_model/Cargo.toml @@ -14,13 +14,12 @@ rustc-hash = "1.1.0" cargo_metadata = "0.9.1" ra_arena = { path = "../ra_arena" } -ra_db = { path = "../ra_db" } ra_cfg = { path = "../ra_cfg" } +ra_db = { path = "../ra_db" } +ra_env = { path = "../ra_env" } ra_proc_macro = { path = "../ra_proc_macro" } serde = { version = "1.0.106", features = ["derive"] } serde_json = "1.0.48" anyhow = "1.0.26" - -dirs = "2.0" diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index c7350d1e0..4027f020f 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs @@ -7,11 +7,11 @@ use std::{ process::Command, }; -use super::find_executables::get_path_for_executable; use anyhow::{Context, Result}; use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; use ra_arena::{Arena, Idx}; use ra_db::Edition; +use ra_env::get_path_for_executable; use rustc_hash::FxHashMap; /// `CargoWorkspace` represents the logical structure of, well, a Cargo diff --git a/crates/ra_project_model/src/find_executables.rs b/crates/ra_project_model/src/find_executables.rs deleted file mode 100644 index 9b020d3da..000000000 --- a/crates/ra_project_model/src/find_executables.rs +++ /dev/null @@ -1,63 +0,0 @@ -use anyhow::{Error, Result}; -use std::env; -use std::path::Path; -use std::process::Command; - -/// Return a `String` to use for the given executable. -/// -/// E.g., `get_path_for_executable("cargo")` may return just `cargo` if that -/// gives a valid Cargo executable; or it may return a full path to a valid -/// Cargo. -pub fn get_path_for_executable(executable_name: impl AsRef) -> Result { - // The current implementation checks three places for an executable to use: - // 1) Appropriate environment variable (erroring if this is set but not a usable executable) - // example: for cargo, this checks $CARGO environment variable; for rustc, $RUSTC; etc - // 2) `` - // example: for cargo, this tries just `cargo`, which will succeed if `cargo` in on the $PATH - // 3) `~/.cargo/bin/` - // example: for cargo, this tries ~/.cargo/bin/cargo - // It seems that this is a reasonable place to try for cargo, rustc, and rustup - let executable_name = executable_name.as_ref(); - let env_var = executable_name.to_ascii_uppercase(); - if let Ok(path) = env::var(&env_var) { - if is_valid_executable(&path) { - Ok(path) - } else { - Err(Error::msg(format!("`{}` environment variable points to something that's not a valid executable", env_var))) - } - } else { - let final_path: Option = if is_valid_executable(executable_name) { - Some(executable_name.to_owned()) - } else { - if let Some(mut path) = dirs::home_dir() { - path.push(".cargo"); - path.push("bin"); - path.push(executable_name); - if is_valid_executable(&path) { - Some(path.into_os_string().into_string().expect("Invalid Unicode in path")) - } else { - None - } - } else { - None - } - }; - final_path.ok_or( - // This error message may also be caused by $PATH or $CARGO/$RUSTC/etc not being set correctly - // for VSCode, even if they are set correctly in a terminal. - // On macOS in particular, launching VSCode from terminal with `code ` causes VSCode - // to inherit environment variables including $PATH, $CARGO, $RUSTC, etc from that terminal; - // but launching VSCode from Dock does not inherit environment variables from a terminal. - // For more discussion, see #3118. - Error::msg(format!("Failed to find `{}` executable. Make sure `{}` is in `$PATH`, or set `${}` to point to a valid executable.", executable_name, executable_name, env_var)) - ) - } -} - -/// Does the given `Path` point to a usable executable? -/// -/// (assumes the executable takes a `--version` switch and writes to stdout, -/// which is true for `cargo`, `rustc`, and `rustup`) -fn is_valid_executable(p: impl AsRef) -> bool { - Command::new(p.as_ref()).arg("--version").output().is_ok() -} diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 5028b6b6d..e4b86f1e2 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -1,7 +1,6 @@ //! FIXME: write short doc here mod cargo_workspace; -mod find_executables; mod json_project; mod sysroot; @@ -15,6 +14,7 @@ use std::{ use anyhow::{bail, Context, Result}; use ra_cfg::CfgOptions; use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; +use ra_env::get_path_for_executable; use rustc_hash::FxHashMap; use serde_json::from_reader; @@ -559,7 +559,7 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { match (|| -> Result { // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here. - let mut cmd = Command::new("rustc"); + let mut cmd = Command::new(get_path_for_executable("rustc")?); cmd.args(&["--print", "cfg", "-O"]); if let Some(target) = target { cmd.args(&["--target", target.as_str()]); diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs index 8d68032b2..516e0472d 100644 --- a/crates/ra_project_model/src/sysroot.rs +++ b/crates/ra_project_model/src/sysroot.rs @@ -1,6 +1,5 @@ //! FIXME: write short doc here -use super::find_executables::get_path_for_executable; use anyhow::{bail, Context, Result}; use std::{ env, ops, @@ -9,6 +8,7 @@ use std::{ }; use ra_arena::{Arena, Idx}; +use ra_env::get_path_for_executable; #[derive(Default, Debug, Clone)] pub struct Sysroot { @@ -122,7 +122,8 @@ fn get_or_install_rust_src(cargo_toml: &Path) -> Result { let src_path = sysroot_path.join("lib/rustlib/src/rust/src"); if !src_path.exists() { - run_command_in_cargo_dir(cargo_toml, "rustup", &["component", "add", "rust-src"])?; + let rustup = get_path_for_executable("rustup")?; + run_command_in_cargo_dir(cargo_toml, &rustup, &["component", "add", "rust-src"])?; } if !src_path.exists() { bail!( -- cgit v1.2.3 From 7e60264ba0dc33110559390868c7c966f0ab2e64 Mon Sep 17 00:00:00 2001 From: Craig Disselkoen Date: Tue, 5 May 2020 14:07:52 -0700 Subject: cargo fmt --- crates/ra_env/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/ra_env/src/lib.rs b/crates/ra_env/src/lib.rs index cb9fbf80c..df20f783d 100644 --- a/crates/ra_env/src/lib.rs +++ b/crates/ra_env/src/lib.rs @@ -23,7 +23,10 @@ pub fn get_path_for_executable(executable_name: impl AsRef) -> Result = if is_valid_executable(executable_name) { -- cgit v1.2.3 From 3e603a8fdd207f9ad5a2ad2898350f54d5bc2fb8 Mon Sep 17 00:00:00 2001 From: Craig Disselkoen Date: Tue, 5 May 2020 14:41:47 -0700 Subject: add module-level docs so that tests pass --- crates/ra_env/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/ra_env/src/lib.rs b/crates/ra_env/src/lib.rs index df20f783d..c7b49e997 100644 --- a/crates/ra_env/src/lib.rs +++ b/crates/ra_env/src/lib.rs @@ -1,3 +1,7 @@ +//! This crate contains a single public function +//! [`get_path_for_executable`](fn.get_path_for_executable.html). +//! See docs there for more information. + use anyhow::{Error, Result}; use std::env; use std::path::Path; -- cgit v1.2.3 From a78dd06951dffcc6ff69aec21a2d8224c12f5026 Mon Sep 17 00:00:00 2001 From: veetaha Date: Wed, 6 May 2020 01:22:02 +0300 Subject: Preliminary refactoring of cargo.ts --- editors/code/src/cargo.ts | 45 +++++++++++----------------------- editors/code/src/commands/runnables.ts | 7 ++++-- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts index a328ba9bd..613aa82da 100644 --- a/editors/code/src/cargo.ts +++ b/editors/code/src/cargo.ts @@ -10,17 +10,9 @@ interface CompilationArtifact { } export class Cargo { - rootFolder: string; - env?: Record; - output: OutputChannel; + constructor(readonly rootFolder: string, readonly output: OutputChannel) { } - public constructor(cargoTomlFolder: string, output: OutputChannel, env: Record | undefined = undefined) { - this.rootFolder = cargoTomlFolder; - this.output = output; - this.env = env; - } - - public async artifactsFromArgs(cargoArgs: string[]): Promise { + private async artifactsFromArgs(cargoArgs: string[]): Promise { const artifacts: CompilationArtifact[] = []; try { @@ -37,17 +29,13 @@ export class Cargo { isTest: message.profile.test }); } - } - else if (message.reason === 'compiler-message') { + } else if (message.reason === 'compiler-message') { this.output.append(message.message.rendered); } }, - stderr => { - this.output.append(stderr); - } + stderr => this.output.append(stderr), ); - } - catch (err) { + } catch (err) { this.output.show(true); throw new Error(`Cargo invocation has failed: ${err}`); } @@ -55,9 +43,8 @@ export class Cargo { return artifacts; } - public async executableFromArgs(args: string[]): Promise { - const cargoArgs = [...args]; // to remain args unchanged - cargoArgs.push("--message-format=json"); + async executableFromArgs(args: readonly string[]): Promise { + const cargoArgs = [...args, "--message-format=json"]; const artifacts = await this.artifactsFromArgs(cargoArgs); @@ -70,24 +57,20 @@ export class Cargo { return artifacts[0].fileName; } - runCargo( + private runCargo( cargoArgs: string[], onStdoutJson: (obj: any) => void, onStderrString: (data: string) => void ): Promise { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const cargo = cp.spawn('cargo', cargoArgs, { stdio: ['ignore', 'pipe', 'pipe'], - cwd: this.rootFolder, - env: this.env, + cwd: this.rootFolder }); - cargo.on('error', err => { - reject(new Error(`could not launch cargo: ${err}`)); - }); - cargo.stderr.on('data', chunk => { - onStderrString(chunk.toString()); - }); + cargo.on('error', err => reject(new Error(`could not launch cargo: ${err}`))); + + cargo.stderr.on('data', chunk => onStderrString(chunk.toString())); const rl = readline.createInterface({ input: cargo.stdout }); rl.on('line', line => { @@ -103,4 +86,4 @@ export class Cargo { }); }); } -} \ No newline at end of file +} diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index d77e8188c..2ed150e25 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -119,8 +119,11 @@ export function debugSingle(ctx: Ctx): Cmd { } if (!debugEngine) { - vscode.window.showErrorMessage(`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=${lldbId})` - + ` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=${cpptoolsId}) extension for debugging.`); + vscode.window.showErrorMessage( + `Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=${lldbId}) ` + + `or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=${cpptoolsId}) ` + + `extension for debugging.` + ); return; } -- cgit v1.2.3 From c9b395be2bfcd67e045c1031143b7e8c27a6d3fb Mon Sep 17 00:00:00 2001 From: veetaha Date: Wed, 6 May 2020 01:42:04 +0300 Subject: Fix cargo not found on macos bug at vscode extension side --- editors/code/src/cargo.ts | 36 +++++++++++++++++++++++++++++++++++- editors/code/src/main.ts | 8 ++------ editors/code/src/util.ts | 11 +++++++++++ 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts index 613aa82da..2a2c2e0e1 100644 --- a/editors/code/src/cargo.ts +++ b/editors/code/src/cargo.ts @@ -1,6 +1,9 @@ import * as cp from 'child_process'; +import * as os from 'os'; +import * as path from 'path'; import * as readline from 'readline'; import { OutputChannel } from 'vscode'; +import { isValidExecutable } from './util'; interface CompilationArtifact { fileName: string; @@ -63,7 +66,14 @@ export class Cargo { onStderrString: (data: string) => void ): Promise { return new Promise((resolve, reject) => { - const cargo = cp.spawn('cargo', cargoArgs, { + let cargoPath; + try { + cargoPath = getCargoPathOrFail(); + } catch (err) { + return reject(err); + } + + const cargo = cp.spawn(cargoPath, cargoArgs, { stdio: ['ignore', 'pipe', 'pipe'], cwd: this.rootFolder }); @@ -87,3 +97,27 @@ export class Cargo { }); } } + +// Mirrors `ra_env::get_path_for_executable` implementation +function getCargoPathOrFail(): string { + const envVar = process.env.CARGO; + const executableName = "cargo"; + + if (envVar) { + if (isValidExecutable(envVar)) return envVar; + + throw new Error(`\`${envVar}\` environment variable points to something that's not a valid executable`); + } + + if (isValidExecutable(executableName)) return executableName; + + const standardLocation = path.join(os.homedir(), '.cargo', 'bin', executableName); + + if (isValidExecutable(standardLocation)) return standardLocation; + + throw new Error( + `Failed to find \`${executableName}\` executable. ` + + `Make sure \`${executableName}\` is in \`$PATH\`, ` + + `or set \`${envVar}\` to point to a valid executable.` + ); +} diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index efd56a84b..9b020d001 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -8,10 +8,9 @@ import { activateInlayHints } from './inlay_hints'; import { activateStatusDisplay } from './status_display'; import { Ctx } from './ctx'; import { Config, NIGHTLY_TAG } from './config'; -import { log, assert } from './util'; +import { log, assert, isValidExecutable } from './util'; import { PersistentState } from './persistent_state'; import { fetchRelease, download } from './net'; -import { spawnSync } from 'child_process'; import { activateTaskProvider } from './tasks'; let ctx: Ctx | undefined; @@ -179,10 +178,7 @@ async function bootstrapServer(config: Config, state: PersistentState): Promise< log.debug("Using server binary at", path); - const res = spawnSync(path, ["--version"], { encoding: 'utf8' }); - log.debug("Checked binary availability via --version", res); - log.debug(res, "--version output:", res.output); - if (res.status !== 0) { + if (!isValidExecutable(path)) { throw new Error(`Failed to execute ${path} --version`); } diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index 6f91f81d6..127a9e911 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts @@ -1,6 +1,7 @@ import * as lc from "vscode-languageclient"; import * as vscode from "vscode"; import { strict as nativeAssert } from "assert"; +import { spawnSync } from "child_process"; export function assert(condition: boolean, explanation: string): asserts condition { try { @@ -82,3 +83,13 @@ export function isRustDocument(document: vscode.TextDocument): document is RustD export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor { return isRustDocument(editor.document); } + +export function isValidExecutable(path: string): boolean { + log.debug("Checking availability of a binary at", path); + + const res = spawnSync(path, ["--version"], { encoding: 'utf8' }); + + log.debug(res, "--version output:", res.output); + + return res.status === 0; +} -- cgit v1.2.3 From 1b76b4281e90292922455a9192f82a2b6b80d279 Mon Sep 17 00:00:00 2001 From: Craig Disselkoen Date: Tue, 5 May 2020 16:09:39 -0700 Subject: simplify some code using early returns --- crates/ra_env/src/lib.rs | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/crates/ra_env/src/lib.rs b/crates/ra_env/src/lib.rs index c7b49e997..8d6aa9268 100644 --- a/crates/ra_env/src/lib.rs +++ b/crates/ra_env/src/lib.rs @@ -33,31 +33,24 @@ pub fn get_path_for_executable(executable_name: impl AsRef) -> Result = if is_valid_executable(executable_name) { - Some(executable_name.to_owned()) - } else { - if let Some(mut path) = dirs::home_dir() { - path.push(".cargo"); - path.push("bin"); - path.push(executable_name); - if is_valid_executable(&path) { - Some(path.into_os_string().into_string().expect("Invalid Unicode in path")) - } else { - None - } - } else { - None + if is_valid_executable(executable_name) { + return Ok(executable_name.to_owned()); + } + if let Some(mut path) = dirs::home_dir() { + path.push(".cargo"); + path.push("bin"); + path.push(executable_name); + if is_valid_executable(&path) { + return Ok(path.into_os_string().into_string().expect("Invalid Unicode in path")); } - }; - final_path.ok_or( - // This error message may also be caused by $PATH or $CARGO/$RUSTC/etc not being set correctly - // for VSCode, even if they are set correctly in a terminal. - // On macOS in particular, launching VSCode from terminal with `code ` causes VSCode - // to inherit environment variables including $PATH, $CARGO, $RUSTC, etc from that terminal; - // but launching VSCode from Dock does not inherit environment variables from a terminal. - // For more discussion, see #3118. - Error::msg(format!("Failed to find `{}` executable. Make sure `{}` is in `$PATH`, or set `${}` to point to a valid executable.", executable_name, executable_name, env_var)) - ) + } + // This error message may also be caused by $PATH or $CARGO/$RUSTC/etc not being set correctly + // for VSCode, even if they are set correctly in a terminal. + // On macOS in particular, launching VSCode from terminal with `code ` causes VSCode + // to inherit environment variables including $PATH, $CARGO, $RUSTC, etc from that terminal; + // but launching VSCode from Dock does not inherit environment variables from a terminal. + // For more discussion, see #3118. + Err(Error::msg(format!("Failed to find `{}` executable. Make sure `{}` is in `$PATH`, or set `${}` to point to a valid executable.", executable_name, executable_name, env_var))) } } -- cgit v1.2.3 From c4ca6e29c25df209c2733ef24b8b4eca70ee93a4 Mon Sep 17 00:00:00 2001 From: vsrs Date: Wed, 6 May 2020 16:01:35 +0300 Subject: Uniformed way to get Debug Lens target executable. --- editors/code/src/commands/runnables.ts | 59 +++++++++++++++++++--------------- editors/code/src/config.ts | 6 ++-- 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index d77e8188c..7bb8727e7 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -64,29 +64,19 @@ export function runSingle(ctx: Ctx): Cmd { }; } -function getLldbDebugConfig(config: ra.Runnable, sourceFileMap: Record): vscode.DebugConfiguration { +function getLldbDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record): vscode.DebugConfiguration { return { type: "lldb", request: "launch", name: config.label, - cargo: { - args: config.args, - }, + program: executable, args: config.extraArgs, cwd: config.cwd, sourceMap: sourceFileMap }; } -const debugOutput = vscode.window.createOutputChannel("Debug"); - -async function getCppvsDebugConfig(config: ra.Runnable, sourceFileMap: Record): Promise { - debugOutput.clear(); - - const cargo = new Cargo(config.cwd || '.', debugOutput); - const executable = await cargo.executableFromArgs(config.args); - - // if we are here, there were no compilation errors. +function getCppvsDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record): vscode.DebugConfiguration { return { type: (os.platform() === "win32") ? "cppvsdbg" : 'cppdbg', request: "launch", @@ -98,36 +88,53 @@ async function getCppvsDebugConfig(config: ra.Runnable, sourceFileMap: Record { + debugOutput.clear(); + + const cargo = new Cargo(config.cwd || '.', debugOutput); + const executable = await cargo.executableFromArgs(config.args); + + // if we are here, there were no compilation errors. + return executable; +} + +type DebugConfigProvider = (config: ra.Runnable, executable: string, sourceFileMap?: Record) => vscode.DebugConfiguration; + export function debugSingle(ctx: Ctx): Cmd { return async (config: ra.Runnable) => { const editor = ctx.activeRustEditor; if (!editor) return; - const lldbId = "vadimcn.vscode-lldb"; - const cpptoolsId = "ms-vscode.cpptools"; + const knownEngines: Record = { + "vadimcn.vscode-lldb": getLldbDebugConfig, + "ms-vscode.cpptools": getCppvsDebugConfig + }; + const debugOptions = ctx.config.debug; - const debugEngineId = ctx.config.debug.engine; let debugEngine = null; - if (debugEngineId === "auto") { - debugEngine = vscode.extensions.getExtension(lldbId); - if (!debugEngine) { - debugEngine = vscode.extensions.getExtension(cpptoolsId); + if (debugOptions.engine === "auto") { + for (var engineId in knownEngines) { + debugEngine = vscode.extensions.getExtension(engineId); + if (debugEngine) break; } } else { - debugEngine = vscode.extensions.getExtension(debugEngineId); + debugEngine = vscode.extensions.getExtension(debugOptions.engine); } if (!debugEngine) { - vscode.window.showErrorMessage(`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=${lldbId})` - + ` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=${cpptoolsId}) extension for debugging.`); + vscode.window.showErrorMessage(`Install [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)` + + ` or [MS C++ tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) extension for debugging.`); return; } - const debugConfig = lldbId === debugEngine.id - ? getLldbDebugConfig(config, ctx.config.debug.sourceFileMap) - : await getCppvsDebugConfig(config, ctx.config.debug.sourceFileMap); + const executable = await getDebugExecutable(config); + const debugConfig = knownEngines[debugEngine.id](config, executable, debugOptions.sourceFileMap); + debugOutput.appendLine("Launching debug configuration:"); + debugOutput.appendLine(JSON.stringify(debugConfig, null, 2)); return vscode.debug.startDebugging(undefined, debugConfig); }; } diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 110e54180..8bceaaf72 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -108,10 +108,12 @@ export class Config { } get debug() { + // "/rustc/" used by suggestions only. + const { ["/rustc/"]: _, ...sourceFileMap } = this.get>("debug.sourceFileMap"); + return { engine: this.get("debug.engine"), - sourceFileMap: this.get>("debug.sourceFileMap"), + sourceFileMap: sourceFileMap, }; } - } -- cgit v1.2.3 From bd9f1f7eb78843ddd91d259a04e988b0681a5db4 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Wed, 6 May 2020 17:17:35 +0300 Subject: Fix rename of enum variant visible from module --- crates/ra_hir/src/code_model.rs | 7 ++++ crates/ra_hir_def/src/adt.rs | 4 ++- crates/ra_ide/src/references/rename.rs | 62 ++++++++++++++++++++++++++++++++++ crates/ra_ide_db/src/defs.rs | 5 ++- 4 files changed, 76 insertions(+), 2 deletions(-) diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 5f480c304..7eba0b233 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -474,6 +474,13 @@ impl EnumVariant { } } +impl HasVisibility for EnumVariant { + fn visibility(&self, db: &dyn HirDatabase) -> Visibility { + let visibility = &db.enum_data(self.parent.id).visibility; + visibility.resolve(db.upcast(), &self.parent.id.resolver(db.upcast())) + } +} + /// A Data Type #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum Adt { diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs index 2bc34d449..0fda4d6c9 100644 --- a/crates/ra_hir_def/src/adt.rs +++ b/crates/ra_hir_def/src/adt.rs @@ -33,6 +33,7 @@ pub struct StructData { #[derive(Debug, Clone, PartialEq, Eq)] pub struct EnumData { pub name: Name, + pub visibility: RawVisibility, pub variants: Arena, } @@ -91,7 +92,8 @@ impl EnumData { let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); let mut trace = Trace::new_for_arena(); lower_enum(db, &mut trace, &src, e.lookup(db).container.module(db)); - Arc::new(EnumData { name, variants: trace.into_arena() }) + let visibility = RawVisibility::from_ast(db, src.with_value(src.value.visibility())); + Arc::new(EnumData { name, visibility, variants: trace.into_arena() }) } pub fn variant(&self, name: &Name) -> Option { diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs index 0398d53bc..2cbb82c1a 100644 --- a/crates/ra_ide/src/references/rename.rs +++ b/crates/ra_ide/src/references/rename.rs @@ -712,6 +712,68 @@ mod tests { "###); } + #[test] + fn test_enum_variant_from_module_1() { + test_rename( + r#" + mod foo { + pub enum Foo { + Bar<|>, + } + } + + fn func(f: foo::Foo) { + match f { + foo::Foo::Bar => {} + } + } + "#, + "Baz", + r#" + mod foo { + pub enum Foo { + Baz, + } + } + + fn func(f: foo::Foo) { + match f { + foo::Foo::Baz => {} + } + } + "#, + ); + } + + #[test] + fn test_enum_variant_from_module_2() { + test_rename( + r#" + mod foo { + pub struct Foo { + pub bar<|>: uint, + } + } + + fn foo(f: foo::Foo) { + let _ = f.bar; + } + "#, + "baz", + r#" + mod foo { + pub struct Foo { + pub baz: uint, + } + } + + fn foo(f: foo::Foo) { + let _ = f.baz; + } + "#, + ); + } + fn test_rename(text: &str, new_name: &str, expected: &str) { let (analysis, position) = single_file_with_position(text); let source_change = analysis.rename(position, new_name).unwrap(); diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index 40d0e77b5..2e2850efb 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs @@ -47,7 +47,10 @@ impl Definition { match self { Definition::Macro(_) => None, Definition::Field(sf) => Some(sf.visibility(db)), - Definition::ModuleDef(def) => module?.visibility_of(db, def), + Definition::ModuleDef(def) => match def { + ModuleDef::EnumVariant(id) => Some(id.visibility(db)), + _ => module?.visibility_of(db, def), + }, Definition::SelfType(_) => None, Definition::Local(_) => None, Definition::TypeParam(_) => None, -- cgit v1.2.3 From 44b01ccff3d993daae237c75d466050711d06268 Mon Sep 17 00:00:00 2001 From: Craig Disselkoen Date: Wed, 6 May 2020 12:39:11 -0700 Subject: return a PathBuf instead of String --- crates/ra_env/src/lib.rs | 12 ++++++------ crates/ra_project_model/src/sysroot.rs | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/crates/ra_env/src/lib.rs b/crates/ra_env/src/lib.rs index 8d6aa9268..a1c4239be 100644 --- a/crates/ra_env/src/lib.rs +++ b/crates/ra_env/src/lib.rs @@ -4,15 +4,15 @@ use anyhow::{Error, Result}; use std::env; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::process::Command; -/// Return a `String` to use for the given executable. +/// Return a `PathBuf` to use for the given executable. /// /// E.g., `get_path_for_executable("cargo")` may return just `cargo` if that /// gives a valid Cargo executable; or it may return a full path to a valid /// Cargo. -pub fn get_path_for_executable(executable_name: impl AsRef) -> Result { +pub fn get_path_for_executable(executable_name: impl AsRef) -> Result { // The current implementation checks three places for an executable to use: // 1) Appropriate environment variable (erroring if this is set but not a usable executable) // example: for cargo, this checks $CARGO environment variable; for rustc, $RUSTC; etc @@ -25,7 +25,7 @@ pub fn get_path_for_executable(executable_name: impl AsRef) -> Result) -> Result String { format!("{} {}", program, args.join(" ")) } -fn run_command_in_cargo_dir(cargo_toml: &Path, program: &str, args: &[&str]) -> Result { +fn run_command_in_cargo_dir(cargo_toml: impl AsRef, program: impl AsRef, args: &[&str]) -> Result { + let program = program.as_ref().as_os_str().to_str().expect("Invalid Unicode in path"); let output = Command::new(program) - .current_dir(cargo_toml.parent().unwrap()) + .current_dir(cargo_toml.as_ref().parent().unwrap()) .args(args) .output() .context(format!("{} failed", create_command_text(program, args)))?; -- cgit v1.2.3 From 5d4648884baf591fe8f53e8ceae6d559564e9797 Mon Sep 17 00:00:00 2001 From: Craig Disselkoen Date: Wed, 6 May 2020 12:47:13 -0700 Subject: cargo fmt --- crates/ra_project_model/src/sysroot.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs index ed374f241..11c26ad89 100644 --- a/crates/ra_project_model/src/sysroot.rs +++ b/crates/ra_project_model/src/sysroot.rs @@ -89,7 +89,11 @@ fn create_command_text(program: &str, args: &[&str]) -> String { format!("{} {}", program, args.join(" ")) } -fn run_command_in_cargo_dir(cargo_toml: impl AsRef, program: impl AsRef, args: &[&str]) -> Result { +fn run_command_in_cargo_dir( + cargo_toml: impl AsRef, + program: impl AsRef, + args: &[&str], +) -> Result { let program = program.as_ref().as_os_str().to_str().expect("Invalid Unicode in path"); let output = Command::new(program) .current_dir(cargo_toml.as_ref().parent().unwrap()) -- cgit v1.2.3 From 227929f9ddd3a5dfdab8a5552f457fd6184d3eb2 Mon Sep 17 00:00:00 2001 From: Craig Disselkoen Date: Wed, 6 May 2020 13:28:32 -0700 Subject: simplify by using bail! macro --- crates/ra_env/src/lib.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/ra_env/src/lib.rs b/crates/ra_env/src/lib.rs index a1c4239be..4f94bffde 100644 --- a/crates/ra_env/src/lib.rs +++ b/crates/ra_env/src/lib.rs @@ -2,7 +2,7 @@ //! [`get_path_for_executable`](fn.get_path_for_executable.html). //! See docs there for more information. -use anyhow::{Error, Result}; +use anyhow::{bail, Result}; use std::env; use std::path::{Path, PathBuf}; use std::process::Command; @@ -27,10 +27,10 @@ pub fn get_path_for_executable(executable_name: impl AsRef) -> Result) -> Result Date: Thu, 7 May 2020 14:29:01 +0200 Subject: Fix panic in FunctionSignature --- crates/ra_ide/src/display/function_signature.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index db3907fe6..f16d42276 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs @@ -1,5 +1,7 @@ //! FIXME: write short doc here +// FIXME: this modules relies on strings and AST way too much, and it should be +// rewritten (matklad 2020-05-07) use std::{ convert::From, fmt::{self, Display}, @@ -202,7 +204,11 @@ impl From<&'_ ast::FnDef> for FunctionSignature { res.extend(param_list.params().map(|param| param.syntax().text().to_string())); res_types.extend(param_list.params().map(|param| { - param.syntax().text().to_string().split(':').nth(1).unwrap()[1..].to_string() + let param_text = param.syntax().text().to_string(); + match param_text.split(':').nth(1) { + Some(it) => it[1..].to_string(), + None => param_text, + } })); } (has_self_param, res, res_types) -- cgit v1.2.3 From 210f0cbd27b0621c47e15c74bbb16ab47a642999 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Thu, 7 May 2020 16:30:20 +0300 Subject: Remove HasVisibility implementation --- crates/ra_hir/src/code_model.rs | 7 ------- crates/ra_hir_def/src/adt.rs | 4 +--- crates/ra_ide_db/src/defs.rs | 7 +++++-- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 7eba0b233..5f480c304 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -474,13 +474,6 @@ impl EnumVariant { } } -impl HasVisibility for EnumVariant { - fn visibility(&self, db: &dyn HirDatabase) -> Visibility { - let visibility = &db.enum_data(self.parent.id).visibility; - visibility.resolve(db.upcast(), &self.parent.id.resolver(db.upcast())) - } -} - /// A Data Type #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum Adt { diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs index 0fda4d6c9..2bc34d449 100644 --- a/crates/ra_hir_def/src/adt.rs +++ b/crates/ra_hir_def/src/adt.rs @@ -33,7 +33,6 @@ pub struct StructData { #[derive(Debug, Clone, PartialEq, Eq)] pub struct EnumData { pub name: Name, - pub visibility: RawVisibility, pub variants: Arena, } @@ -92,8 +91,7 @@ impl EnumData { let name = src.value.name().map_or_else(Name::missing, |n| n.as_name()); let mut trace = Trace::new_for_arena(); lower_enum(db, &mut trace, &src, e.lookup(db).container.module(db)); - let visibility = RawVisibility::from_ast(db, src.with_value(src.value.visibility())); - Arc::new(EnumData { name, visibility, variants: trace.into_arena() }) + Arc::new(EnumData { name, variants: trace.into_arena() }) } pub fn variant(&self, name: &Name) -> Option { diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index 2e2850efb..f990e3bb9 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs @@ -6,7 +6,7 @@ // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). use hir::{ - Field, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, Name, PathResolution, + Adt, Field, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility, }; use ra_prof::profile; @@ -48,7 +48,10 @@ impl Definition { Definition::Macro(_) => None, Definition::Field(sf) => Some(sf.visibility(db)), Definition::ModuleDef(def) => match def { - ModuleDef::EnumVariant(id) => Some(id.visibility(db)), + ModuleDef::EnumVariant(id) => { + let parent = id.parent_enum(db); + module?.visibility_of(db, &ModuleDef::Adt(Adt::Enum(parent))) + } _ => module?.visibility_of(db, def), }, Definition::SelfType(_) => None, -- cgit v1.2.3 From 5426e2927e317a5e78179a5bd74b9414c0651b86 Mon Sep 17 00:00:00 2001 From: vsrs Date: Thu, 7 May 2020 17:07:58 +0300 Subject: Add additional debug options --- editors/code/package.json | 10 ++++++++++ editors/code/src/commands/runnables.ts | 12 +++++++++--- editors/code/src/config.ts | 2 ++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/editors/code/package.json b/editors/code/package.json index eeb3d3513..84aea8249 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -418,6 +418,16 @@ "default": { "/rustc/": "${env:USERPROFILE}/.rustup/toolchains//lib/rustlib/src/rust" } + }, + "rust-analyzer.debug.openUpDebugPane": { + "description": "Whether to open up the Debug Pane on debugging start.", + "type": "boolean", + "default": false + }, + "rust-analyzer.debug.engineSettings": { + "type": "object", + "default": {}, + "description": "Optional settings passed to the debug engine." } } }, diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index 7bb8727e7..782a7ba89 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -91,8 +91,6 @@ function getCppvsDebugConfig(config: ra.Runnable, executable: string, sourceFile const debugOutput = vscode.window.createOutputChannel("Debug"); async function getDebugExecutable(config: ra.Runnable): Promise { - debugOutput.clear(); - const cargo = new Cargo(config.cwd || '.', debugOutput); const executable = await cargo.executableFromArgs(config.args); @@ -130,8 +128,16 @@ export function debugSingle(ctx: Ctx): Cmd { return; } + debugOutput.clear(); + if (ctx.config.debug.openUpDebugPane) { + debugOutput.show(true); + } + const executable = await getDebugExecutable(config); - const debugConfig = knownEngines[debugEngine.id](config, executable, debugOptions.sourceFileMap); + let debugConfig = knownEngines[debugEngine.id](config, executable, debugOptions.sourceFileMap); + for (var key in debugOptions.engineSettings) { + debugConfig[key] = (debugOptions.engineSettings as any)[key]; + } debugOutput.appendLine("Launching debug configuration:"); debugOutput.appendLine(JSON.stringify(debugConfig, null, 2)); diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 8bceaaf72..533be1913 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -113,6 +113,8 @@ export class Config { return { engine: this.get("debug.engine"), + engineSettings: this.get("debug.engineSettings"), + openUpDebugPane: this.get("debug.openUpDebugPane"), sourceFileMap: sourceFileMap, }; } -- cgit v1.2.3 From 4867968d22899395e6551f22641b3617e995140c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 6 May 2020 18:45:35 +0200 Subject: Refactor assists API to be more convenient for adding new assists It now duplicates completion API in its shape. --- crates/ra_assists/src/assist_context.rs | 234 ++++++++++++++++++ crates/ra_assists/src/assist_ctx.rs | 269 --------------------- crates/ra_assists/src/handlers/add_custom_impl.rs | 9 +- crates/ra_assists/src/handlers/add_derive.rs | 9 +- .../ra_assists/src/handlers/add_explicit_type.rs | 6 +- .../src/handlers/add_from_impl_for_enum.rs | 10 +- crates/ra_assists/src/handlers/add_function.rs | 21 +- crates/ra_assists/src/handlers/add_impl.rs | 61 +++-- .../src/handlers/add_missing_impl_members.rs | 32 +-- crates/ra_assists/src/handlers/add_new.rs | 8 +- crates/ra_assists/src/handlers/apply_demorgan.rs | 6 +- crates/ra_assists/src/handlers/auto_import.rs | 41 ++-- .../src/handlers/change_return_type_to_result.rs | 10 +- .../ra_assists/src/handlers/change_visibility.rs | 31 +-- crates/ra_assists/src/handlers/early_return.rs | 161 ++++++------ crates/ra_assists/src/handlers/fill_match_arms.rs | 6 +- crates/ra_assists/src/handlers/flip_binexpr.rs | 6 +- crates/ra_assists/src/handlers/flip_comma.rs | 6 +- crates/ra_assists/src/handlers/flip_trait_bound.rs | 6 +- .../src/handlers/inline_local_variable.rs | 31 ++- .../ra_assists/src/handlers/introduce_variable.rs | 6 +- crates/ra_assists/src/handlers/invert_if.rs | 36 +-- crates/ra_assists/src/handlers/merge_imports.rs | 13 +- crates/ra_assists/src/handlers/merge_match_arms.rs | 6 +- crates/ra_assists/src/handlers/move_bounds.rs | 57 ++--- crates/ra_assists/src/handlers/move_guard.rs | 10 +- crates/ra_assists/src/handlers/raw_string.rs | 18 +- crates/ra_assists/src/handlers/remove_dbg.rs | 6 +- crates/ra_assists/src/handlers/remove_mut.rs | 6 +- crates/ra_assists/src/handlers/reorder_fields.rs | 29 +-- .../src/handlers/replace_if_let_with_match.rs | 51 ++-- .../src/handlers/replace_let_with_if_let.rs | 12 +- .../handlers/replace_qualified_name_with_use.rs | 19 +- .../src/handlers/replace_unwrap_with_match.rs | 41 ++-- crates/ra_assists/src/handlers/split_import.rs | 6 +- crates/ra_assists/src/handlers/unwrap_block.rs | 6 +- crates/ra_assists/src/lib.rs | 42 ++-- crates/ra_assists/src/tests.rs | 21 +- crates/ra_assists/src/utils/insert_use.rs | 10 +- 39 files changed, 643 insertions(+), 715 deletions(-) create mode 100644 crates/ra_assists/src/assist_context.rs delete mode 100644 crates/ra_assists/src/assist_ctx.rs diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs new file mode 100644 index 000000000..203ad1273 --- /dev/null +++ b/crates/ra_assists/src/assist_context.rs @@ -0,0 +1,234 @@ +//! See `AssistContext` + +use algo::find_covering_element; +use hir::Semantics; +use ra_db::{FileId, FileRange}; +use ra_fmt::{leading_indent, reindent}; +use ra_ide_db::{ + source_change::{SingleFileChange, SourceChange}, + RootDatabase, +}; +use ra_syntax::{ + algo::{self, find_node_at_offset, SyntaxRewriter}, + AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, + TokenAtOffset, +}; +use ra_text_edit::TextEditBuilder; + +use crate::{AssistId, AssistLabel, GroupLabel, ResolvedAssist}; + +/// `AssistContext` allows to apply an assist or check if it could be applied. +/// +/// Assists use a somewhat over-engineered approach, given the current needs. +/// The assists workflow consists of two phases. In the first phase, a user asks +/// for the list of available assists. In the second phase, the user picks a +/// particular assist and it gets applied. +/// +/// There are two peculiarities here: +/// +/// * first, we ideally avoid computing more things then necessary to answer "is +/// assist applicable" in the first phase. +/// * second, when we are applying assist, we don't have a guarantee that there +/// weren't any changes between the point when user asked for assists and when +/// they applied a particular assist. So, when applying assist, we need to do +/// all the checks from scratch. +/// +/// To avoid repeating the same code twice for both "check" and "apply" +/// functions, we use an approach reminiscent of that of Django's function based +/// views dealing with forms. Each assist receives a runtime parameter, +/// `resolve`. It first check if an edit is applicable (potentially computing +/// info required to compute the actual edit). If it is applicable, and +/// `resolve` is `true`, it then computes the actual edit. +/// +/// So, to implement the original assists workflow, we can first apply each edit +/// with `resolve = false`, and then applying the selected edit again, with +/// `resolve = true` this time. +/// +/// Note, however, that we don't actually use such two-phase logic at the +/// moment, because the LSP API is pretty awkward in this place, and it's much +/// easier to just compute the edit eagerly :-) +pub(crate) struct AssistContext<'a> { + pub(crate) sema: Semantics<'a, RootDatabase>, + pub(super) db: &'a RootDatabase, + pub(crate) frange: FileRange, + source_file: SourceFile, +} + +impl<'a> AssistContext<'a> { + pub fn new(sema: Semantics<'a, RootDatabase>, frange: FileRange) -> AssistContext<'a> { + let source_file = sema.parse(frange.file_id); + let db = sema.db; + AssistContext { sema, db, frange, source_file } + } + + // NB, this ignores active selection. + pub(crate) fn offset(&self) -> TextSize { + self.frange.range.start() + } + + pub(crate) fn token_at_offset(&self) -> TokenAtOffset { + self.source_file.syntax().token_at_offset(self.offset()) + } + pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option { + self.token_at_offset().find(|it| it.kind() == kind) + } + pub(crate) fn find_node_at_offset(&self) -> Option { + find_node_at_offset(self.source_file.syntax(), self.offset()) + } + pub(crate) fn find_node_at_offset_with_descend(&self) -> Option { + self.sema + .find_node_at_offset_with_descend(self.source_file.syntax(), self.frange.range.start()) + } + pub(crate) fn covering_element(&self) -> SyntaxElement { + find_covering_element(self.source_file.syntax(), self.frange.range) + } + // FIXME: remove + pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement { + find_covering_element(self.source_file.syntax(), range) + } +} + +pub(crate) struct Assists { + resolve: bool, + file: FileId, + buf: Vec<(AssistLabel, Option)>, +} + +impl Assists { + pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists { + Assists { resolve: true, file: ctx.frange.file_id, buf: Vec::new() } + } + pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists { + Assists { resolve: false, file: ctx.frange.file_id, buf: Vec::new() } + } + + pub(crate) fn finish_unresolved(self) -> Vec { + assert!(!self.resolve); + self.finish() + .into_iter() + .map(|(label, edit)| { + assert!(edit.is_none()); + label + }) + .collect() + } + + pub(crate) fn finish_resolved(self) -> Vec { + assert!(self.resolve); + self.finish() + .into_iter() + .map(|(label, edit)| ResolvedAssist { label, source_change: edit.unwrap() }) + .collect() + } + + pub(crate) fn add( + &mut self, + id: AssistId, + label: impl Into, + target: TextRange, + f: impl FnOnce(&mut AssistBuilder), + ) -> Option<()> { + let label = AssistLabel::new(id, label.into(), None, target); + self.add_impl(label, f) + } + pub(crate) fn add_group( + &mut self, + group: &GroupLabel, + id: AssistId, + label: impl Into, + target: TextRange, + f: impl FnOnce(&mut AssistBuilder), + ) -> Option<()> { + let label = AssistLabel::new(id, label.into(), Some(group.clone()), target); + self.add_impl(label, f) + } + fn add_impl(&mut self, label: AssistLabel, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { + let change_label = label.label.clone(); + let source_change = if self.resolve { + let mut builder = AssistBuilder::new(self.file); + f(&mut builder); + Some(builder.finish(change_label)) + } else { + None + }; + + self.buf.push((label, source_change)); + Some(()) + } + + fn finish(mut self) -> Vec<(AssistLabel, Option)> { + self.buf.sort_by_key(|(label, _edit)| label.target.len()); + self.buf + } +} + +pub(crate) struct AssistBuilder { + edit: TextEditBuilder, + cursor_position: Option, + file: FileId, +} + +impl AssistBuilder { + pub(crate) fn new(file: FileId) -> AssistBuilder { + AssistBuilder { edit: TextEditBuilder::default(), cursor_position: None, file } + } + + /// Remove specified `range` of text. + pub(crate) fn delete(&mut self, range: TextRange) { + self.edit.delete(range) + } + /// Append specified `text` at the given `offset` + pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into) { + self.edit.insert(offset, text.into()) + } + /// Replaces specified `range` of text with a given string. + pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into) { + self.edit.replace(range, replace_with.into()) + } + pub(crate) fn replace_ast(&mut self, old: N, new: N) { + algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) + } + /// Replaces specified `node` of text with a given string, reindenting the + /// string to maintain `node`'s existing indent. + // FIXME: remove in favor of ra_syntax::edit::IndentLevel::increase_indent + pub(crate) fn replace_node_and_indent( + &mut self, + node: &SyntaxNode, + replace_with: impl Into, + ) { + let mut replace_with = replace_with.into(); + if let Some(indent) = leading_indent(node) { + replace_with = reindent(&replace_with, &indent) + } + self.replace(node.text_range(), replace_with) + } + pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { + let node = rewriter.rewrite_root().unwrap(); + let new = rewriter.rewrite(&node); + algo::diff(&node, &new).into_text_edit(&mut self.edit) + } + + /// Specify desired position of the cursor after the assist is applied. + pub(crate) fn set_cursor(&mut self, offset: TextSize) { + self.cursor_position = Some(offset) + } + // FIXME: better API + pub(crate) fn set_file(&mut self, assist_file: FileId) { + self.file = assist_file; + } + + // FIXME: kill this API + /// Get access to the raw `TextEditBuilder`. + pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder { + &mut self.edit + } + + fn finish(self, change_label: String) -> SourceChange { + let edit = self.edit.finish(); + if edit.is_empty() && self.cursor_position.is_none() { + panic!("Only call `add_assist` if the assist can be applied") + } + SingleFileChange { label: change_label, edit, cursor_position: self.cursor_position } + .into_source_change(self.file) + } +} diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs deleted file mode 100644 index 871671de2..000000000 --- a/crates/ra_assists/src/assist_ctx.rs +++ /dev/null @@ -1,269 +0,0 @@ -//! This module defines `AssistCtx` -- the API surface that is exposed to assists. -use hir::Semantics; -use ra_db::{FileId, FileRange}; -use ra_fmt::{leading_indent, reindent}; -use ra_ide_db::{ - source_change::{SingleFileChange, SourceChange}, - RootDatabase, -}; -use ra_syntax::{ - algo::{self, find_covering_element, find_node_at_offset, SyntaxRewriter}, - AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, - TokenAtOffset, -}; -use ra_text_edit::TextEditBuilder; - -use crate::{AssistId, AssistLabel, GroupLabel, ResolvedAssist}; - -#[derive(Clone, Debug)] -pub(crate) struct Assist(pub(crate) Vec); - -#[derive(Clone, Debug)] -pub(crate) struct AssistInfo { - pub(crate) label: AssistLabel, - pub(crate) group_label: Option, - pub(crate) source_change: Option, -} - -impl AssistInfo { - fn new(label: AssistLabel) -> AssistInfo { - AssistInfo { label, group_label: None, source_change: None } - } - - fn resolved(self, source_change: SourceChange) -> AssistInfo { - AssistInfo { source_change: Some(source_change), ..self } - } - - fn with_group(self, group_label: GroupLabel) -> AssistInfo { - AssistInfo { group_label: Some(group_label), ..self } - } - - pub(crate) fn into_resolved(self) -> Option { - let label = self.label; - self.source_change.map(|source_change| ResolvedAssist { label, source_change }) - } -} - -/// `AssistCtx` allows to apply an assist or check if it could be applied. -/// -/// Assists use a somewhat over-engineered approach, given the current needs. The -/// assists workflow consists of two phases. In the first phase, a user asks for -/// the list of available assists. In the second phase, the user picks a -/// particular assist and it gets applied. -/// -/// There are two peculiarities here: -/// -/// * first, we ideally avoid computing more things then necessary to answer -/// "is assist applicable" in the first phase. -/// * second, when we are applying assist, we don't have a guarantee that there -/// weren't any changes between the point when user asked for assists and when -/// they applied a particular assist. So, when applying assist, we need to do -/// all the checks from scratch. -/// -/// To avoid repeating the same code twice for both "check" and "apply" -/// functions, we use an approach reminiscent of that of Django's function based -/// views dealing with forms. Each assist receives a runtime parameter, -/// `should_compute_edit`. It first check if an edit is applicable (potentially -/// computing info required to compute the actual edit). If it is applicable, -/// and `should_compute_edit` is `true`, it then computes the actual edit. -/// -/// So, to implement the original assists workflow, we can first apply each edit -/// with `should_compute_edit = false`, and then applying the selected edit -/// again, with `should_compute_edit = true` this time. -/// -/// Note, however, that we don't actually use such two-phase logic at the -/// moment, because the LSP API is pretty awkward in this place, and it's much -/// easier to just compute the edit eagerly :-) -#[derive(Clone)] -pub(crate) struct AssistCtx<'a> { - pub(crate) sema: &'a Semantics<'a, RootDatabase>, - pub(crate) db: &'a RootDatabase, - pub(crate) frange: FileRange, - source_file: SourceFile, - should_compute_edit: bool, -} - -impl<'a> AssistCtx<'a> { - pub fn new( - sema: &'a Semantics<'a, RootDatabase>, - frange: FileRange, - should_compute_edit: bool, - ) -> AssistCtx<'a> { - let source_file = sema.parse(frange.file_id); - AssistCtx { sema, db: sema.db, frange, source_file, should_compute_edit } - } - - pub(crate) fn add_assist( - self, - id: AssistId, - label: impl Into, - target: TextRange, - f: impl FnOnce(&mut ActionBuilder), - ) -> Option { - let label = AssistLabel::new(id, label.into(), None, target); - let change_label = label.label.clone(); - let mut info = AssistInfo::new(label); - if self.should_compute_edit { - let source_change = { - let mut edit = ActionBuilder::new(&self); - f(&mut edit); - edit.build(change_label) - }; - info = info.resolved(source_change) - }; - - Some(Assist(vec![info])) - } - - pub(crate) fn add_assist_group(self, group_name: impl Into) -> AssistGroup<'a> { - let group = GroupLabel(group_name.into()); - AssistGroup { ctx: self, group, assists: Vec::new() } - } - - pub(crate) fn token_at_offset(&self) -> TokenAtOffset { - self.source_file.syntax().token_at_offset(self.frange.range.start()) - } - - pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option { - self.token_at_offset().find(|it| it.kind() == kind) - } - - pub(crate) fn find_node_at_offset(&self) -> Option { - find_node_at_offset(self.source_file.syntax(), self.frange.range.start()) - } - - pub(crate) fn find_node_at_offset_with_descend(&self) -> Option { - self.sema - .find_node_at_offset_with_descend(self.source_file.syntax(), self.frange.range.start()) - } - - pub(crate) fn covering_element(&self) -> SyntaxElement { - find_covering_element(self.source_file.syntax(), self.frange.range) - } - pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement { - find_covering_element(self.source_file.syntax(), range) - } -} - -pub(crate) struct AssistGroup<'a> { - ctx: AssistCtx<'a>, - group: GroupLabel, - assists: Vec, -} - -impl<'a> AssistGroup<'a> { - pub(crate) fn add_assist( - &mut self, - id: AssistId, - label: impl Into, - target: TextRange, - f: impl FnOnce(&mut ActionBuilder), - ) { - let label = AssistLabel::new(id, label.into(), Some(self.group.clone()), target); - let change_label = label.label.clone(); - let mut info = AssistInfo::new(label).with_group(self.group.clone()); - if self.ctx.should_compute_edit { - let source_change = { - let mut edit = ActionBuilder::new(&self.ctx); - f(&mut edit); - edit.build(change_label) - }; - info = info.resolved(source_change) - }; - - self.assists.push(info) - } - - pub(crate) fn finish(self) -> Option { - if self.assists.is_empty() { - None - } else { - Some(Assist(self.assists)) - } - } -} - -pub(crate) struct ActionBuilder<'a, 'b> { - edit: TextEditBuilder, - cursor_position: Option, - file: FileId, - ctx: &'a AssistCtx<'b>, -} - -impl<'a, 'b> ActionBuilder<'a, 'b> { - fn new(ctx: &'a AssistCtx<'b>) -> Self { - Self { - edit: TextEditBuilder::default(), - cursor_position: None, - file: ctx.frange.file_id, - ctx, - } - } - - pub(crate) fn ctx(&self) -> &AssistCtx<'b> { - &self.ctx - } - - /// Replaces specified `range` of text with a given string. - pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into) { - self.edit.replace(range, replace_with.into()) - } - - /// Replaces specified `node` of text with a given string, reindenting the - /// string to maintain `node`'s existing indent. - // FIXME: remove in favor of ra_syntax::edit::IndentLevel::increase_indent - pub(crate) fn replace_node_and_indent( - &mut self, - node: &SyntaxNode, - replace_with: impl Into, - ) { - let mut replace_with = replace_with.into(); - if let Some(indent) = leading_indent(node) { - replace_with = reindent(&replace_with, &indent) - } - self.replace(node.text_range(), replace_with) - } - - /// Remove specified `range` of text. - #[allow(unused)] - pub(crate) fn delete(&mut self, range: TextRange) { - self.edit.delete(range) - } - - /// Append specified `text` at the given `offset` - pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into) { - self.edit.insert(offset, text.into()) - } - - /// Specify desired position of the cursor after the assist is applied. - pub(crate) fn set_cursor(&mut self, offset: TextSize) { - self.cursor_position = Some(offset) - } - - /// Get access to the raw `TextEditBuilder`. - pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder { - &mut self.edit - } - - pub(crate) fn replace_ast(&mut self, old: N, new: N) { - algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) - } - pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { - let node = rewriter.rewrite_root().unwrap(); - let new = rewriter.rewrite(&node); - algo::diff(&node, &new).into_text_edit(&mut self.edit) - } - - pub(crate) fn set_file(&mut self, assist_file: FileId) { - self.file = assist_file; - } - - fn build(self, change_label: String) -> SourceChange { - let edit = self.edit.finish(); - if edit.is_empty() && self.cursor_position.is_none() { - panic!("Only call `add_assist` if the assist can be applied") - } - SingleFileChange { label: change_label, edit, cursor_position: self.cursor_position } - .into_source_change(self.file) - } -} diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs index 869d4dc04..795a225a4 100644 --- a/crates/ra_assists/src/handlers/add_custom_impl.rs +++ b/crates/ra_assists/src/handlers/add_custom_impl.rs @@ -6,7 +6,10 @@ use ra_syntax::{ }; use stdx::SepBy; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{ + assist_context::{AssistContext, Assists}, + AssistId, +}; // Assist: add_custom_impl // @@ -25,7 +28,7 @@ use crate::{Assist, AssistCtx, AssistId}; // // } // ``` -pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option { +pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let input = ctx.find_node_at_offset::()?; let attr = input.syntax().parent().and_then(ast::Attr::cast)?; @@ -49,7 +52,7 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option { format!("Add custom impl '{}' for '{}'", trait_token.text().as_str(), annotated_name); let target = attr.syntax().text_range(); - ctx.add_assist(AssistId("add_custom_impl"), label, target, |edit| { + acc.add(AssistId("add_custom_impl"), label, target, |edit| { let new_attr_input = input .syntax() .descendants_with_tokens() diff --git a/crates/ra_assists/src/handlers/add_derive.rs b/crates/ra_assists/src/handlers/add_derive.rs index 2a6bb1cae..fb08c19e9 100644 --- a/crates/ra_assists/src/handlers/add_derive.rs +++ b/crates/ra_assists/src/handlers/add_derive.rs @@ -4,7 +4,7 @@ use ra_syntax::{ TextSize, }; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: add_derive // @@ -24,11 +24,11 @@ use crate::{Assist, AssistCtx, AssistId}; // y: u32, // } // ``` -pub(crate) fn add_derive(ctx: AssistCtx) -> Option { +pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let nominal = ctx.find_node_at_offset::()?; let node_start = derive_insertion_offset(&nominal)?; let target = nominal.syntax().text_range(); - ctx.add_assist(AssistId("add_derive"), "Add `#[derive]`", target, |edit| { + acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |edit| { let derive_attr = nominal .attrs() .filter_map(|x| x.as_simple_call()) @@ -57,9 +57,10 @@ fn derive_insertion_offset(nominal: &ast::NominalDef) -> Option { #[cfg(test)] mod tests { - use super::*; use crate::tests::{check_assist, check_assist_target}; + use super::*; + #[test] fn add_derive_new() { check_assist( diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index a59ec16b2..55409e501 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs @@ -4,7 +4,7 @@ use ra_syntax::{ TextRange, }; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: add_explicit_type // @@ -21,7 +21,7 @@ use crate::{Assist, AssistCtx, AssistId}; // let x: i32 = 92; // } // ``` -pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option { +pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let stmt = ctx.find_node_at_offset::()?; let expr = stmt.initializer()?; let pat = stmt.pat()?; @@ -59,7 +59,7 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option { let db = ctx.db; let new_type_string = ty.display_truncated(db, None).to_string(); - ctx.add_assist( + acc.add( AssistId("add_explicit_type"), format!("Insert explicit type '{}'", new_type_string), pat_range, diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs index 81deb3dfa..275184e24 100644 --- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs +++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs @@ -4,10 +4,10 @@ use ra_syntax::{ TextSize, }; use stdx::format_to; - -use crate::{utils::FamousDefs, Assist, AssistCtx, AssistId}; use test_utils::tested_by; +use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; + // Assist add_from_impl_for_enum // // Adds a From impl for an enum variant with one tuple field @@ -25,7 +25,7 @@ use test_utils::tested_by; // } // } // ``` -pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option { +pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let variant = ctx.find_node_at_offset::()?; let variant_name = variant.name()?; let enum_name = variant.parent_enum().name()?; @@ -42,13 +42,13 @@ pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option { _ => return None, }; - if existing_from_impl(ctx.sema, &variant).is_some() { + if existing_from_impl(&ctx.sema, &variant).is_some() { tested_by!(test_add_from_impl_already_exists); return None; } let target = variant.syntax().text_range(); - ctx.add_assist( + acc.add( AssistId("add_from_impl_for_enum"), "Add From impl for this enum variant", target, diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index 278079665..6b5616aa9 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs @@ -1,14 +1,13 @@ +use hir::HirDisplay; +use ra_db::FileId; use ra_syntax::{ - ast::{self, AstNode}, + ast::{self, edit::IndentLevel, ArgListOwner, AstNode, ModuleItemOwner}, SyntaxKind, SyntaxNode, TextSize, }; - -use crate::{Assist, AssistCtx, AssistId}; -use ast::{edit::IndentLevel, ArgListOwner, ModuleItemOwner}; -use hir::HirDisplay; -use ra_db::FileId; use rustc_hash::{FxHashMap, FxHashSet}; +use crate::{AssistContext, AssistId, Assists}; + // Assist: add_function // // Adds a stub function with a signature matching the function under the cursor. @@ -34,7 +33,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; // } // // ``` -pub(crate) fn add_function(ctx: AssistCtx) -> Option { +pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; let path = path_expr.path()?; @@ -59,7 +58,7 @@ pub(crate) fn add_function(ctx: AssistCtx) -> Option { let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; let target = call.syntax().text_range(); - ctx.add_assist(AssistId("add_function"), "Add function", target, |edit| { + acc.add(AssistId("add_function"), "Add function", target, |edit| { let function_template = function_builder.render(); edit.set_file(function_template.file); edit.set_cursor(function_template.cursor_offset); @@ -87,7 +86,7 @@ impl FunctionBuilder { /// Prepares a generated function that matches `call` in `generate_in` /// (or as close to `call` as possible, if `generate_in` is `None`) fn from_call( - ctx: &AssistCtx, + ctx: &AssistContext, call: &ast::CallExpr, path: &ast::Path, target_module: Option>, @@ -152,7 +151,7 @@ fn fn_name(call: &ast::Path) -> Option { /// Computes the type variables and arguments required for the generated function fn fn_args( - ctx: &AssistCtx, + ctx: &AssistContext, call: &ast::CallExpr, ) -> Option<(Option, ast::ParamList)> { let mut arg_names = Vec::new(); @@ -219,7 +218,7 @@ fn fn_arg_name(fn_arg: &ast::Expr) -> Option { } } -fn fn_arg_type(ctx: &AssistCtx, fn_arg: &ast::Expr) -> Option { +fn fn_arg_type(ctx: &AssistContext, fn_arg: &ast::Expr) -> Option { let ty = ctx.sema.type_of_expr(fn_arg)?; if ty.is_unknown() { return None; diff --git a/crates/ra_assists/src/handlers/add_impl.rs b/crates/ra_assists/src/handlers/add_impl.rs index 557344ebb..df114a0d8 100644 --- a/crates/ra_assists/src/handlers/add_impl.rs +++ b/crates/ra_assists/src/handlers/add_impl.rs @@ -4,7 +4,7 @@ use ra_syntax::{ }; use stdx::{format_to, SepBy}; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: add_impl // @@ -25,43 +25,36 @@ use crate::{Assist, AssistCtx, AssistId}; // // } // ``` -pub(crate) fn add_impl(ctx: AssistCtx) -> Option { +pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let nominal = ctx.find_node_at_offset::()?; let name = nominal.name()?; let target = nominal.syntax().text_range(); - ctx.add_assist( - AssistId("add_impl"), - format!("Implement {}", name.text().as_str()), - target, - |edit| { - let type_params = nominal.type_param_list(); - let start_offset = nominal.syntax().text_range().end(); - let mut buf = String::new(); - buf.push_str("\n\nimpl"); - if let Some(type_params) = &type_params { - format_to!(buf, "{}", type_params.syntax()); - } - buf.push_str(" "); - buf.push_str(name.text().as_str()); - if let Some(type_params) = type_params { - let lifetime_params = type_params - .lifetime_params() - .filter_map(|it| it.lifetime_token()) - .map(|it| it.text().clone()); - let type_params = type_params - .type_params() - .filter_map(|it| it.name()) - .map(|it| it.text().clone()); + acc.add(AssistId("add_impl"), format!("Implement {}", name.text().as_str()), target, |edit| { + let type_params = nominal.type_param_list(); + let start_offset = nominal.syntax().text_range().end(); + let mut buf = String::new(); + buf.push_str("\n\nimpl"); + if let Some(type_params) = &type_params { + format_to!(buf, "{}", type_params.syntax()); + } + buf.push_str(" "); + buf.push_str(name.text().as_str()); + if let Some(type_params) = type_params { + let lifetime_params = type_params + .lifetime_params() + .filter_map(|it| it.lifetime_token()) + .map(|it| it.text().clone()); + let type_params = + type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone()); - let generic_params = lifetime_params.chain(type_params).sep_by(", "); - format_to!(buf, "<{}>", generic_params) - } - buf.push_str(" {\n"); - edit.set_cursor(start_offset + TextSize::of(&buf)); - buf.push_str("\n}"); - edit.insert(start_offset, buf); - }, - ) + let generic_params = lifetime_params.chain(type_params).sep_by(", "); + format_to!(buf, "<{}>", generic_params) + } + buf.push_str(" {\n"); + edit.set_cursor(start_offset + TextSize::of(&buf)); + buf.push_str("\n}"); + edit.insert(start_offset, buf); + }) } #[cfg(test)] diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs index 7df786590..3482a75bf 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs @@ -9,9 +9,10 @@ use ra_syntax::{ }; use crate::{ + assist_context::{AssistContext, Assists}, ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, utils::{get_missing_assoc_items, resolve_target_trait}, - Assist, AssistCtx, AssistId, + AssistId, }; #[derive(PartialEq)] @@ -50,8 +51,9 @@ enum AddMissingImplMembersMode { // // } // ``` -pub(crate) fn add_missing_impl_members(ctx: AssistCtx) -> Option { +pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { add_missing_impl_members_inner( + acc, ctx, AddMissingImplMembersMode::NoDefaultMethods, "add_impl_missing_members", @@ -91,8 +93,9 @@ pub(crate) fn add_missing_impl_members(ctx: AssistCtx) -> Option { // // } // ``` -pub(crate) fn add_missing_default_members(ctx: AssistCtx) -> Option { +pub(crate) fn add_missing_default_members(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { add_missing_impl_members_inner( + acc, ctx, AddMissingImplMembersMode::DefaultMethodsOnly, "add_impl_default_members", @@ -101,11 +104,12 @@ pub(crate) fn add_missing_default_members(ctx: AssistCtx) -> Option { } fn add_missing_impl_members_inner( - ctx: AssistCtx, + acc: &mut Assists, + ctx: &AssistContext, mode: AddMissingImplMembersMode, assist_id: &'static str, label: &'static str, -) -> Option { +) -> Option<()> { let _p = ra_prof::profile("add_missing_impl_members_inner"); let impl_def = ctx.find_node_at_offset::()?; let impl_item_list = impl_def.item_list()?; @@ -142,12 +146,11 @@ fn add_missing_impl_members_inner( return None; } - let sema = ctx.sema; let target = impl_def.syntax().text_range(); - ctx.add_assist(AssistId(assist_id), label, target, |edit| { + acc.add(AssistId(assist_id), label, target, |edit| { let n_existing_items = impl_item_list.assoc_items().count(); - let source_scope = sema.scope_for_def(trait_); - let target_scope = sema.scope(impl_item_list.syntax()); + let source_scope = ctx.sema.scope_for_def(trait_); + let target_scope = ctx.sema.scope(impl_item_list.syntax()); let ast_transform = QualifyPaths::new(&target_scope, &source_scope) .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def)); let items = missing_items @@ -170,13 +173,12 @@ fn add_missing_impl_members_inner( } fn add_body(fn_def: ast::FnDef) -> ast::FnDef { - if fn_def.body().is_none() { - let body = make::block_expr(None, Some(make::expr_todo())); - let body = IndentLevel(1).increase_indent(body); - fn_def.with_body(body) - } else { - fn_def + if fn_def.body().is_some() { + return fn_def; } + let body = make::block_expr(None, Some(make::expr_todo())); + let body = IndentLevel(1).increase_indent(body); + fn_def.with_body(body) } #[cfg(test)] diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs index 1c3f8435a..fe7451dcf 100644 --- a/crates/ra_assists/src/handlers/add_new.rs +++ b/crates/ra_assists/src/handlers/add_new.rs @@ -7,7 +7,7 @@ use ra_syntax::{ }; use stdx::{format_to, SepBy}; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: add_new // @@ -29,7 +29,7 @@ use crate::{Assist, AssistCtx, AssistId}; // } // // ``` -pub(crate) fn add_new(ctx: AssistCtx) -> Option { +pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let strukt = ctx.find_node_at_offset::()?; // We want to only apply this to non-union structs with named fields @@ -42,7 +42,7 @@ pub(crate) fn add_new(ctx: AssistCtx) -> Option { let impl_def = find_struct_impl(&ctx, &strukt)?; let target = strukt.syntax().text_range(); - ctx.add_assist(AssistId("add_new"), "Add default constructor", target, |edit| { + acc.add(AssistId("add_new"), "Add default constructor", target, |edit| { let mut buf = String::with_capacity(512); if impl_def.is_some() { @@ -123,7 +123,7 @@ fn generate_impl_text(strukt: &ast::StructDef, code: &str) -> String { // // FIXME: change the new fn checking to a more semantic approach when that's more // viable (e.g. we process proc macros, etc) -fn find_struct_impl(ctx: &AssistCtx, strukt: &ast::StructDef) -> Option> { +fn find_struct_impl(ctx: &AssistContext, strukt: &ast::StructDef) -> Option> { let db = ctx.db; let module = strukt.syntax().ancestors().find(|node| { ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) diff --git a/crates/ra_assists/src/handlers/apply_demorgan.rs b/crates/ra_assists/src/handlers/apply_demorgan.rs index a5b26e5b9..0feba5e11 100644 --- a/crates/ra_assists/src/handlers/apply_demorgan.rs +++ b/crates/ra_assists/src/handlers/apply_demorgan.rs @@ -1,6 +1,6 @@ use ra_syntax::ast::{self, AstNode}; -use crate::{utils::invert_boolean_expression, Assist, AssistCtx, AssistId}; +use crate::{utils::invert_boolean_expression, AssistContext, AssistId, Assists}; // Assist: apply_demorgan // @@ -21,7 +21,7 @@ use crate::{utils::invert_boolean_expression, Assist, AssistCtx, AssistId}; // if !(x == 4 && y) {} // } // ``` -pub(crate) fn apply_demorgan(ctx: AssistCtx) -> Option { +pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let expr = ctx.find_node_at_offset::()?; let op = expr.op_kind()?; let op_range = expr.op_token()?.text_range(); @@ -39,7 +39,7 @@ pub(crate) fn apply_demorgan(ctx: AssistCtx) -> Option { let rhs_range = rhs.syntax().text_range(); let not_rhs = invert_boolean_expression(rhs); - ctx.add_assist(AssistId("apply_demorgan"), "Apply De Morgan's law", op_range, |edit| { + acc.add(AssistId("apply_demorgan"), "Apply De Morgan's law", op_range, |edit| { edit.replace(op_range, opposite_op); edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text())); edit.replace(rhs_range, format!("{})", not_rhs.syntax().text())); diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 2224b9714..78d23150d 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs @@ -1,5 +1,6 @@ use std::collections::BTreeSet; +use either::Either; use hir::{ AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait, Type, @@ -12,12 +13,7 @@ use ra_syntax::{ }; use rustc_hash::FxHashSet; -use crate::{ - assist_ctx::{Assist, AssistCtx}, - utils::insert_use_statement, - AssistId, -}; -use either::Either; +use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists, GroupLabel}; // Assist: auto_import // @@ -38,7 +34,7 @@ use either::Either; // } // # pub mod std { pub mod collections { pub struct HashMap { } } } // ``` -pub(crate) fn auto_import(ctx: AssistCtx) -> Option { +pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let auto_import_assets = AutoImportAssets::new(&ctx)?; let proposed_imports = auto_import_assets.search_for_imports(ctx.db); if proposed_imports.is_empty() { @@ -46,13 +42,19 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option { } let range = ctx.sema.original_range(&auto_import_assets.syntax_under_caret).range; - let mut group = ctx.add_assist_group(auto_import_assets.get_import_group_message()); + let group = auto_import_assets.get_import_group_message(); for import in proposed_imports { - group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), range, |edit| { - insert_use_statement(&auto_import_assets.syntax_under_caret, &import, edit); - }); + acc.add_group( + &group, + AssistId("auto_import"), + format!("Import `{}`", &import), + range, + |builder| { + insert_use_statement(&auto_import_assets.syntax_under_caret, &import, ctx, builder); + }, + ); } - group.finish() + Some(()) } #[derive(Debug)] @@ -63,7 +65,7 @@ struct AutoImportAssets { } impl AutoImportAssets { - fn new(ctx: &AssistCtx) -> Option { + fn new(ctx: &AssistContext) -> Option { if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::() { Self::for_regular_path(path_under_caret, &ctx) } else { @@ -71,7 +73,7 @@ impl AutoImportAssets { } } - fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option { + fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistContext) -> Option { let syntax_under_caret = method_call.syntax().to_owned(); let module_with_name_to_import = ctx.sema.scope(&syntax_under_caret).module()?; Some(Self { @@ -81,7 +83,7 @@ impl AutoImportAssets { }) } - fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistCtx) -> Option { + fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistContext) -> Option { let syntax_under_caret = path_under_caret.syntax().to_owned(); if syntax_under_caret.ancestors().find_map(ast::UseItem::cast).is_some() { return None; @@ -104,8 +106,8 @@ impl AutoImportAssets { } } - fn get_import_group_message(&self) -> String { - match &self.import_candidate { + fn get_import_group_message(&self) -> GroupLabel { + let name = match &self.import_candidate { ImportCandidate::UnqualifiedName(name) => format!("Import {}", name), ImportCandidate::QualifierStart(qualifier_start) => { format!("Import {}", qualifier_start) @@ -116,7 +118,8 @@ impl AutoImportAssets { ImportCandidate::TraitMethod(_, trait_method_name) => { format!("Import a trait for method {}", trait_method_name) } - } + }; + GroupLabel(name) } fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet { @@ -383,7 +386,7 @@ mod tests { } ", r" - use PubMod1::PubStruct; + use PubMod3::PubStruct; PubSt<|>ruct diff --git a/crates/ra_assists/src/handlers/change_return_type_to_result.rs b/crates/ra_assists/src/handlers/change_return_type_to_result.rs index 1e8d986cd..5c907097e 100644 --- a/crates/ra_assists/src/handlers/change_return_type_to_result.rs +++ b/crates/ra_assists/src/handlers/change_return_type_to_result.rs @@ -1,11 +1,11 @@ use ra_syntax::{ - ast, AstNode, + ast::{self, BlockExpr, Expr, LoopBodyOwner}, + AstNode, SyntaxKind::{COMMENT, WHITESPACE}, SyntaxNode, TextSize, }; -use crate::{Assist, AssistCtx, AssistId}; -use ast::{BlockExpr, Expr, LoopBodyOwner}; +use crate::{AssistContext, AssistId, Assists}; // Assist: change_return_type_to_result // @@ -18,7 +18,7 @@ use ast::{BlockExpr, Expr, LoopBodyOwner}; // ``` // fn foo() -> Result { Ok(42i32) } // ``` -pub(crate) fn change_return_type_to_result(ctx: AssistCtx) -> Option { +pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let fn_def = ctx.find_node_at_offset::(); let fn_def = &mut fn_def?; let ret_type = &fn_def.ret_type()?.type_ref()?; @@ -33,7 +33,7 @@ pub(crate) fn change_return_type_to_result(ctx: AssistCtx) -> Option { return None; } - ctx.add_assist( + acc.add( AssistId("change_return_type_to_result"), "Change return type to Result", ret_type.syntax().text_range(), diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs index 489db83e6..e631766ef 100644 --- a/crates/ra_assists/src/handlers/change_visibility.rs +++ b/crates/ra_assists/src/handlers/change_visibility.rs @@ -7,10 +7,10 @@ use ra_syntax::{ }, SyntaxNode, TextSize, T, }; - -use crate::{Assist, AssistCtx, AssistId}; use test_utils::tested_by; +use crate::{AssistContext, AssistId, Assists}; + // Assist: change_visibility // // Adds or changes existing visibility specifier. @@ -22,14 +22,14 @@ use test_utils::tested_by; // ``` // pub(crate) fn frobnicate() {} // ``` -pub(crate) fn change_visibility(ctx: AssistCtx) -> Option { +pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { if let Some(vis) = ctx.find_node_at_offset::() { - return change_vis(ctx, vis); + return change_vis(acc, vis); } - add_vis(ctx) + add_vis(acc, ctx) } -fn add_vis(ctx: AssistCtx) -> Option { +fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let item_keyword = ctx.token_at_offset().find(|leaf| match leaf.kind() { T![const] | T![fn] | T![mod] | T![struct] | T![enum] | T![trait] => true, _ => false, @@ -66,15 +66,10 @@ fn add_vis(ctx: AssistCtx) -> Option { return None; }; - ctx.add_assist( - AssistId("change_visibility"), - "Change visibility to pub(crate)", - target, - |edit| { - edit.insert(offset, "pub(crate) "); - edit.set_cursor(offset); - }, - ) + acc.add(AssistId("change_visibility"), "Change visibility to pub(crate)", target, |edit| { + edit.insert(offset, "pub(crate) "); + edit.set_cursor(offset); + }) } fn vis_offset(node: &SyntaxNode) -> TextSize { @@ -88,10 +83,10 @@ fn vis_offset(node: &SyntaxNode) -> TextSize { .unwrap_or_else(|| node.text_range().start()) } -fn change_vis(ctx: AssistCtx, vis: ast::Visibility) -> Option { +fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { if vis.syntax().text() == "pub" { let target = vis.syntax().text_range(); - return ctx.add_assist( + return acc.add( AssistId("change_visibility"), "Change Visibility to pub(crate)", target, @@ -103,7 +98,7 @@ fn change_vis(ctx: AssistCtx, vis: ast::Visibility) -> Option { } if vis.syntax().text() == "pub(crate)" { let target = vis.syntax().text_range(); - return ctx.add_assist( + return acc.add( AssistId("change_visibility"), "Change visibility to pub", target, diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs index 4bd6040b2..ccf91797c 100644 --- a/crates/ra_assists/src/handlers/early_return.rs +++ b/crates/ra_assists/src/handlers/early_return.rs @@ -9,7 +9,7 @@ use ra_syntax::{ }; use crate::{ - assist_ctx::{Assist, AssistCtx}, + assist_context::{AssistContext, Assists}, utils::invert_boolean_expression, AssistId, }; @@ -36,7 +36,7 @@ use crate::{ // bar(); // } // ``` -pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option { +pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; if if_expr.else_branch().is_some() { return None; @@ -96,93 +96,88 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option { let cursor_position = ctx.frange.range.start(); let target = if_expr.syntax().text_range(); - ctx.add_assist( - AssistId("convert_to_guarded_return"), - "Convert to guarded return", - target, - |edit| { - let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); - let new_block = match if_let_pat { - None => { - // If. - let new_expr = { - let then_branch = - make::block_expr(once(make::expr_stmt(early_expression).into()), None); - let cond = invert_boolean_expression(cond_expr); - let e = make::expr_if(make::condition(cond, None), then_branch); - if_indent_level.increase_indent(e) - }; - replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) - } - Some((path, bound_ident)) => { - // If-let. - let match_expr = { - let happy_arm = { - let pat = make::tuple_struct_pat( - path, - once(make::bind_pat(make::name("it")).into()), - ); - let expr = { - let name_ref = make::name_ref("it"); - let segment = make::path_segment(name_ref); - let path = make::path_unqualified(segment); - make::expr_path(path) - }; - make::match_arm(once(pat.into()), expr) - }; - - let sad_arm = make::match_arm( - // FIXME: would be cool to use `None` or `Err(_)` if appropriate - once(make::placeholder_pat().into()), - early_expression, + acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| { + let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); + let new_block = match if_let_pat { + None => { + // If. + let new_expr = { + let then_branch = + make::block_expr(once(make::expr_stmt(early_expression).into()), None); + let cond = invert_boolean_expression(cond_expr); + let e = make::expr_if(make::condition(cond, None), then_branch); + if_indent_level.increase_indent(e) + }; + replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) + } + Some((path, bound_ident)) => { + // If-let. + let match_expr = { + let happy_arm = { + let pat = make::tuple_struct_pat( + path, + once(make::bind_pat(make::name("it")).into()), ); - - make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm])) + let expr = { + let name_ref = make::name_ref("it"); + let segment = make::path_segment(name_ref); + let path = make::path_unqualified(segment); + make::expr_path(path) + }; + make::match_arm(once(pat.into()), expr) }; - let let_stmt = make::let_stmt( - make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), - Some(match_expr), + let sad_arm = make::match_arm( + // FIXME: would be cool to use `None` or `Err(_)` if appropriate + once(make::placeholder_pat().into()), + early_expression, ); - let let_stmt = if_indent_level.increase_indent(let_stmt); - replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) - } - }; - edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap()); - edit.set_cursor(cursor_position); - - fn replace( - new_expr: &SyntaxNode, - then_block: &ast::BlockExpr, - parent_block: &ast::BlockExpr, - if_expr: &ast::IfExpr, - ) -> SyntaxNode { - let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone()); - let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); - let end_of_then = - if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { - end_of_then.prev_sibling_or_token().unwrap() - } else { - end_of_then - }; - let mut then_statements = new_expr.children_with_tokens().chain( - then_block_items - .syntax() - .children_with_tokens() - .skip(1) - .take_while(|i| *i != end_of_then), + + make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm])) + }; + + let let_stmt = make::let_stmt( + make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), + Some(match_expr), ); - replace_children( - &parent_block.syntax(), - RangeInclusive::new( - if_expr.clone().syntax().clone().into(), - if_expr.syntax().clone().into(), - ), - &mut then_statements, - ) + let let_stmt = if_indent_level.increase_indent(let_stmt); + replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) } - }, - ) + }; + edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap()); + edit.set_cursor(cursor_position); + + fn replace( + new_expr: &SyntaxNode, + then_block: &ast::BlockExpr, + parent_block: &ast::BlockExpr, + if_expr: &ast::IfExpr, + ) -> SyntaxNode { + let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone()); + let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); + let end_of_then = + if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { + end_of_then.prev_sibling_or_token().unwrap() + } else { + end_of_then + }; + let mut then_statements = new_expr.children_with_tokens().chain( + then_block_items + .syntax() + .children_with_tokens() + .skip(1) + .take_while(|i| *i != end_of_then), + ); + replace_children( + &parent_block.syntax(), + RangeInclusive::new( + if_expr.clone().syntax().clone().into(), + if_expr.syntax().clone().into(), + ), + &mut then_statements, + ) + } + }) } #[cfg(test)] diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs index 7c8f8bdf2..13c1e7e80 100644 --- a/crates/ra_assists/src/handlers/fill_match_arms.rs +++ b/crates/ra_assists/src/handlers/fill_match_arms.rs @@ -5,7 +5,7 @@ use itertools::Itertools; use ra_ide_db::RootDatabase; use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: fill_match_arms // @@ -31,7 +31,7 @@ use crate::{Assist, AssistCtx, AssistId}; // } // } // ``` -pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option { +pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let match_expr = ctx.find_node_at_offset::()?; let match_arm_list = match_expr.match_arm_list()?; @@ -93,7 +93,7 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option { } let target = match_expr.syntax().text_range(); - ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", target, |edit| { + acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |edit| { let new_arm_list = match_arm_list.remove_placeholder().append_arms(missing_arms); edit.set_cursor(expr.syntax().text_range().start()); edit.replace_ast(match_arm_list, new_arm_list); diff --git a/crates/ra_assists/src/handlers/flip_binexpr.rs b/crates/ra_assists/src/handlers/flip_binexpr.rs index cb7264d7b..692ba4895 100644 --- a/crates/ra_assists/src/handlers/flip_binexpr.rs +++ b/crates/ra_assists/src/handlers/flip_binexpr.rs @@ -1,6 +1,6 @@ use ra_syntax::ast::{AstNode, BinExpr, BinOp}; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: flip_binexpr // @@ -17,7 +17,7 @@ use crate::{Assist, AssistCtx, AssistId}; // let _ = 2 + 90; // } // ``` -pub(crate) fn flip_binexpr(ctx: AssistCtx) -> Option { +pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let expr = ctx.find_node_at_offset::()?; let lhs = expr.lhs()?.syntax().clone(); let rhs = expr.rhs()?.syntax().clone(); @@ -33,7 +33,7 @@ pub(crate) fn flip_binexpr(ctx: AssistCtx) -> Option { return None; } - ctx.add_assist(AssistId("flip_binexpr"), "Flip binary expression", op_range, |edit| { + acc.add(AssistId("flip_binexpr"), "Flip binary expression", op_range, |edit| { if let FlipAction::FlipAndReplaceOp(new_op) = action { edit.replace(op_range, new_op); } diff --git a/crates/ra_assists/src/handlers/flip_comma.rs b/crates/ra_assists/src/handlers/flip_comma.rs index 24982ae22..dfe2a7fed 100644 --- a/crates/ra_assists/src/handlers/flip_comma.rs +++ b/crates/ra_assists/src/handlers/flip_comma.rs @@ -1,6 +1,6 @@ use ra_syntax::{algo::non_trivia_sibling, Direction, T}; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: flip_comma // @@ -17,7 +17,7 @@ use crate::{Assist, AssistCtx, AssistId}; // ((3, 4), (1, 2)); // } // ``` -pub(crate) fn flip_comma(ctx: AssistCtx) -> Option { +pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let comma = ctx.find_token_at_offset(T![,])?; let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?; let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?; @@ -28,7 +28,7 @@ pub(crate) fn flip_comma(ctx: AssistCtx) -> Option { return None; } - ctx.add_assist(AssistId("flip_comma"), "Flip comma", comma.text_range(), |edit| { + acc.add(AssistId("flip_comma"), "Flip comma", comma.text_range(), |edit| { edit.replace(prev.text_range(), next.to_string()); edit.replace(next.text_range(), prev.to_string()); }) diff --git a/crates/ra_assists/src/handlers/flip_trait_bound.rs b/crates/ra_assists/src/handlers/flip_trait_bound.rs index 6a3b2df67..8a08702ab 100644 --- a/crates/ra_assists/src/handlers/flip_trait_bound.rs +++ b/crates/ra_assists/src/handlers/flip_trait_bound.rs @@ -4,7 +4,7 @@ use ra_syntax::{ Direction, T, }; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: flip_trait_bound // @@ -17,7 +17,7 @@ use crate::{Assist, AssistCtx, AssistId}; // ``` // fn foo() { } // ``` -pub(crate) fn flip_trait_bound(ctx: AssistCtx) -> Option { +pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { // We want to replicate the behavior of `flip_binexpr` by only suggesting // the assist when the cursor is on a `+` let plus = ctx.find_token_at_offset(T![+])?; @@ -33,7 +33,7 @@ pub(crate) fn flip_trait_bound(ctx: AssistCtx) -> Option { ); let target = plus.text_range(); - ctx.add_assist(AssistId("flip_trait_bound"), "Flip trait bounds", target, |edit| { + acc.add(AssistId("flip_trait_bound"), "Flip trait bounds", target, |edit| { edit.replace(before.text_range(), after.to_string()); edit.replace(after.text_range(), before.to_string()); }) diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs index e5765c845..5b26814d3 100644 --- a/crates/ra_assists/src/handlers/inline_local_variable.rs +++ b/crates/ra_assists/src/handlers/inline_local_variable.rs @@ -5,7 +5,10 @@ use ra_syntax::{ }; use test_utils::tested_by; -use crate::{assist_ctx::ActionBuilder, Assist, AssistCtx, AssistId}; +use crate::{ + assist_context::{AssistContext, Assists}, + AssistId, +}; // Assist: inline_local_variable // @@ -23,7 +26,7 @@ use crate::{assist_ctx::ActionBuilder, Assist, AssistCtx, AssistId}; // (1 + 2) * 4; // } // ``` -pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option { +pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let let_stmt = ctx.find_node_at_offset::()?; let bind_pat = match let_stmt.pat()? { ast::Pat::BindPat(pat) => pat, @@ -33,7 +36,7 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option { tested_by!(test_not_inline_mut_variable); return None; } - if !bind_pat.syntax().text_range().contains_inclusive(ctx.frange.range.start()) { + if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) { tested_by!(not_applicable_outside_of_bind_pat); return None; } @@ -107,20 +110,14 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option { let init_in_paren = format!("({})", &init_str); let target = bind_pat.syntax().text_range(); - ctx.add_assist( - AssistId("inline_local_variable"), - "Inline variable", - target, - move |edit: &mut ActionBuilder| { - edit.delete(delete_range); - for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { - let replacement = - if should_wrap { init_in_paren.clone() } else { init_str.clone() }; - edit.replace(desc.file_range.range, replacement) - } - edit.set_cursor(delete_range.start()) - }, - ) + acc.add(AssistId("inline_local_variable"), "Inline variable", target, move |builder| { + builder.delete(delete_range); + for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { + let replacement = if should_wrap { init_in_paren.clone() } else { init_str.clone() }; + builder.replace(desc.file_range.range, replacement) + } + builder.set_cursor(delete_range.start()) + }) } #[cfg(test)] diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs index 3c340ff3b..fdf3ada0d 100644 --- a/crates/ra_assists/src/handlers/introduce_variable.rs +++ b/crates/ra_assists/src/handlers/introduce_variable.rs @@ -9,7 +9,7 @@ use ra_syntax::{ use stdx::format_to; use test_utils::tested_by; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: introduce_variable // @@ -27,7 +27,7 @@ use crate::{Assist, AssistCtx, AssistId}; // var_name * 4; // } // ``` -pub(crate) fn introduce_variable(ctx: AssistCtx) -> Option { +pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { if ctx.frange.range.is_empty() { return None; } @@ -43,7 +43,7 @@ pub(crate) fn introduce_variable(ctx: AssistCtx) -> Option { return None; } let target = expr.syntax().text_range(); - ctx.add_assist(AssistId("introduce_variable"), "Extract into variable", target, move |edit| { + acc.add(AssistId("introduce_variable"), "Extract into variable", target, move |edit| { let mut buf = String::new(); let cursor_offset = if wrap_in_block { diff --git a/crates/ra_assists/src/handlers/invert_if.rs b/crates/ra_assists/src/handlers/invert_if.rs index b16271443..527c7caef 100644 --- a/crates/ra_assists/src/handlers/invert_if.rs +++ b/crates/ra_assists/src/handlers/invert_if.rs @@ -3,7 +3,11 @@ use ra_syntax::{ T, }; -use crate::{utils::invert_boolean_expression, Assist, AssistCtx, AssistId}; +use crate::{ + assist_context::{AssistContext, Assists}, + utils::invert_boolean_expression, + AssistId, +}; // Assist: invert_if // @@ -24,7 +28,7 @@ use crate::{utils::invert_boolean_expression, Assist, AssistCtx, AssistId}; // } // ``` -pub(crate) fn invert_if(ctx: AssistCtx) -> Option { +pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let if_keyword = ctx.find_token_at_offset(T![if])?; let expr = ast::IfExpr::cast(if_keyword.parent())?; let if_range = if_keyword.text_range(); @@ -40,21 +44,21 @@ pub(crate) fn invert_if(ctx: AssistCtx) -> Option { let cond = expr.condition()?.expr()?; let then_node = expr.then_branch()?.syntax().clone(); + let else_block = match expr.else_branch()? { + ast::ElseBranch::Block(it) => it, + ast::ElseBranch::IfExpr(_) => return None, + }; - if let ast::ElseBranch::Block(else_block) = expr.else_branch()? { - let cond_range = cond.syntax().text_range(); - let flip_cond = invert_boolean_expression(cond); - let else_node = else_block.syntax(); - let else_range = else_node.text_range(); - let then_range = then_node.text_range(); - return ctx.add_assist(AssistId("invert_if"), "Invert if", if_range, |edit| { - edit.replace(cond_range, flip_cond.syntax().text()); - edit.replace(else_range, then_node.text()); - edit.replace(then_range, else_node.text()); - }); - } - - None + let cond_range = cond.syntax().text_range(); + let flip_cond = invert_boolean_expression(cond); + let else_node = else_block.syntax(); + let else_range = else_node.text_range(); + let then_range = then_node.text_range(); + acc.add(AssistId("invert_if"), "Invert if", if_range, |edit| { + edit.replace(cond_range, flip_cond.syntax().text()); + edit.replace(else_range, then_node.text()); + edit.replace(then_range, else_node.text()); + }) } #[cfg(test)] diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs index de74d83d8..8e1d93312 100644 --- a/crates/ra_assists/src/handlers/merge_imports.rs +++ b/crates/ra_assists/src/handlers/merge_imports.rs @@ -6,7 +6,10 @@ use ra_syntax::{ AstNode, Direction, InsertPosition, SyntaxElement, T, }; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{ + assist_context::{AssistContext, Assists}, + AssistId, +}; // Assist: merge_imports // @@ -20,7 +23,7 @@ use crate::{Assist, AssistCtx, AssistId}; // ``` // use std::{fmt::Formatter, io}; // ``` -pub(crate) fn merge_imports(ctx: AssistCtx) -> Option { +pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let tree: ast::UseTree = ctx.find_node_at_offset()?; let mut rewriter = SyntaxRewriter::default(); let mut offset = ctx.frange.range.start(); @@ -53,10 +56,10 @@ pub(crate) fn merge_imports(ctx: AssistCtx) -> Option { }; let target = tree.syntax().text_range(); - ctx.add_assist(AssistId("merge_imports"), "Merge imports", target, |edit| { - edit.rewrite(rewriter); + acc.add(AssistId("merge_imports"), "Merge imports", target, |builder| { + builder.rewrite(rewriter); // FIXME: we only need because our diff is imprecise - edit.set_cursor(offset); + builder.set_cursor(offset); }) } diff --git a/crates/ra_assists/src/handlers/merge_match_arms.rs b/crates/ra_assists/src/handlers/merge_match_arms.rs index 7c4d9d55d..cfe4df47b 100644 --- a/crates/ra_assists/src/handlers/merge_match_arms.rs +++ b/crates/ra_assists/src/handlers/merge_match_arms.rs @@ -6,7 +6,7 @@ use ra_syntax::{ Direction, TextSize, }; -use crate::{Assist, AssistCtx, AssistId, TextRange}; +use crate::{AssistContext, AssistId, Assists, TextRange}; // Assist: merge_match_arms // @@ -32,7 +32,7 @@ use crate::{Assist, AssistCtx, AssistId, TextRange}; // } // } // ``` -pub(crate) fn merge_match_arms(ctx: AssistCtx) -> Option { +pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let current_arm = ctx.find_node_at_offset::()?; // Don't try to handle arms with guards for now - can add support for this later if current_arm.guard().is_some() { @@ -70,7 +70,7 @@ pub(crate) fn merge_match_arms(ctx: AssistCtx) -> Option { return None; } - ctx.add_assist(AssistId("merge_match_arms"), "Merge match arms", current_text_range, |edit| { + acc.add(AssistId("merge_match_arms"), "Merge match arms", current_text_range, |edit| { let pats = if arms_to_merge.iter().any(contains_placeholder) { "_".into() } else { diff --git a/crates/ra_assists/src/handlers/move_bounds.rs b/crates/ra_assists/src/handlers/move_bounds.rs index 44e50cb6e..a41aacfc3 100644 --- a/crates/ra_assists/src/handlers/move_bounds.rs +++ b/crates/ra_assists/src/handlers/move_bounds.rs @@ -5,7 +5,7 @@ use ra_syntax::{ T, }; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: move_bounds_to_where_clause // @@ -22,7 +22,7 @@ use crate::{Assist, AssistCtx, AssistId}; // f(x) // } // ``` -pub(crate) fn move_bounds_to_where_clause(ctx: AssistCtx) -> Option { +pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let type_param_list = ctx.find_node_at_offset::()?; let mut type_params = type_param_list.type_params(); @@ -50,36 +50,29 @@ pub(crate) fn move_bounds_to_where_clause(ctx: AssistCtx) -> Option { }; let target = type_param_list.syntax().text_range(); - ctx.add_assist( - AssistId("move_bounds_to_where_clause"), - "Move to where clause", - target, - |edit| { - let new_params = type_param_list - .type_params() - .filter(|it| it.type_bound_list().is_some()) - .map(|type_param| { - let without_bounds = type_param.remove_bounds(); - (type_param, without_bounds) - }); - - let new_type_param_list = type_param_list.replace_descendants(new_params); - edit.replace_ast(type_param_list.clone(), new_type_param_list); - - let where_clause = { - let predicates = type_param_list.type_params().filter_map(build_predicate); - make::where_clause(predicates) - }; - - let to_insert = match anchor.prev_sibling_or_token() { - Some(ref elem) if elem.kind() == WHITESPACE => { - format!("{} ", where_clause.syntax()) - } - _ => format!(" {}", where_clause.syntax()), - }; - edit.insert(anchor.text_range().start(), to_insert); - }, - ) + acc.add(AssistId("move_bounds_to_where_clause"), "Move to where clause", target, |edit| { + let new_params = type_param_list + .type_params() + .filter(|it| it.type_bound_list().is_some()) + .map(|type_param| { + let without_bounds = type_param.remove_bounds(); + (type_param, without_bounds) + }); + + let new_type_param_list = type_param_list.replace_descendants(new_params); + edit.replace_ast(type_param_list.clone(), new_type_param_list); + + let where_clause = { + let predicates = type_param_list.type_params().filter_map(build_predicate); + make::where_clause(predicates) + }; + + let to_insert = match anchor.prev_sibling_or_token() { + Some(ref elem) if elem.kind() == WHITESPACE => format!("{} ", where_clause.syntax()), + _ => format!(" {}", where_clause.syntax()), + }; + edit.insert(anchor.text_range().start(), to_insert); + }) } fn build_predicate(param: ast::TypeParam) -> Option { diff --git a/crates/ra_assists/src/handlers/move_guard.rs b/crates/ra_assists/src/handlers/move_guard.rs index 29bc9a9ff..fc0335b57 100644 --- a/crates/ra_assists/src/handlers/move_guard.rs +++ b/crates/ra_assists/src/handlers/move_guard.rs @@ -4,7 +4,7 @@ use ra_syntax::{ TextSize, }; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: move_guard_to_arm_body // @@ -31,7 +31,7 @@ use crate::{Assist, AssistCtx, AssistId}; // } // } // ``` -pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx) -> Option { +pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let match_arm = ctx.find_node_at_offset::()?; let guard = match_arm.guard()?; let space_before_guard = guard.syntax().prev_sibling_or_token(); @@ -41,7 +41,7 @@ pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx) -> Option { let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text()); let target = guard.syntax().text_range(); - ctx.add_assist(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| { + acc.add(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| { let offseting_amount = match space_before_guard.and_then(|it| it.into_token()) { Some(tok) => { if ast::Whitespace::cast(tok.clone()).is_some() { @@ -88,7 +88,7 @@ pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx) -> Option { // } // } // ``` -pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option { +pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let match_arm: MatchArm = ctx.find_node_at_offset::()?; let match_pat = match_arm.pat()?; @@ -109,7 +109,7 @@ pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option { let buf = format!(" if {}", cond.syntax().text()); let target = if_expr.syntax().text_range(); - ctx.add_assist( + acc.add( AssistId("move_arm_cond_to_match_guard"), "Move condition to match guard", target, diff --git a/crates/ra_assists/src/handlers/raw_string.rs b/crates/ra_assists/src/handlers/raw_string.rs index 155c679b4..c20ffe0b3 100644 --- a/crates/ra_assists/src/handlers/raw_string.rs +++ b/crates/ra_assists/src/handlers/raw_string.rs @@ -5,7 +5,7 @@ use ra_syntax::{ TextSize, }; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: make_raw_string // @@ -22,11 +22,11 @@ use crate::{Assist, AssistCtx, AssistId}; // r#"Hello, World!"#; // } // ``` -pub(crate) fn make_raw_string(ctx: AssistCtx) -> Option { +pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let token = ctx.find_token_at_offset(STRING).and_then(ast::String::cast)?; let value = token.value()?; let target = token.syntax().text_range(); - ctx.add_assist(AssistId("make_raw_string"), "Rewrite as raw string", target, |edit| { + acc.add(AssistId("make_raw_string"), "Rewrite as raw string", target, |edit| { let max_hash_streak = count_hashes(&value); let mut hashes = String::with_capacity(max_hash_streak + 1); for _ in 0..hashes.capacity() { @@ -51,11 +51,11 @@ pub(crate) fn make_raw_string(ctx: AssistCtx) -> Option { // "Hello, \"World!\""; // } // ``` -pub(crate) fn make_usual_string(ctx: AssistCtx) -> Option { +pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let token = ctx.find_token_at_offset(RAW_STRING).and_then(ast::RawString::cast)?; let value = token.value()?; let target = token.syntax().text_range(); - ctx.add_assist(AssistId("make_usual_string"), "Rewrite as regular string", target, |edit| { + acc.add(AssistId("make_usual_string"), "Rewrite as regular string", target, |edit| { // parse inside string to escape `"` let escaped = value.escape_default().to_string(); edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped)); @@ -77,10 +77,10 @@ pub(crate) fn make_usual_string(ctx: AssistCtx) -> Option { // r##"Hello, World!"##; // } // ``` -pub(crate) fn add_hash(ctx: AssistCtx) -> Option { +pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let token = ctx.find_token_at_offset(RAW_STRING)?; let target = token.text_range(); - ctx.add_assist(AssistId("add_hash"), "Add # to raw string", target, |edit| { + acc.add(AssistId("add_hash"), "Add # to raw string", target, |edit| { edit.insert(token.text_range().start() + TextSize::of('r'), "#"); edit.insert(token.text_range().end(), "#"); }) @@ -101,7 +101,7 @@ pub(crate) fn add_hash(ctx: AssistCtx) -> Option { // r"Hello, World!"; // } // ``` -pub(crate) fn remove_hash(ctx: AssistCtx) -> Option { +pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let token = ctx.find_token_at_offset(RAW_STRING)?; let text = token.text().as_str(); if text.starts_with("r\"") { @@ -109,7 +109,7 @@ pub(crate) fn remove_hash(ctx: AssistCtx) -> Option { return None; } let target = token.text_range(); - ctx.add_assist(AssistId("remove_hash"), "Remove hash from raw string", target, |edit| { + acc.add(AssistId("remove_hash"), "Remove hash from raw string", target, |edit| { let result = &text[2..text.len() - 1]; let result = if result.starts_with('\"') { // FIXME: this logic is wrong, not only the last has has to handled specially diff --git a/crates/ra_assists/src/handlers/remove_dbg.rs b/crates/ra_assists/src/handlers/remove_dbg.rs index e6e02f2ae..8eef578cf 100644 --- a/crates/ra_assists/src/handlers/remove_dbg.rs +++ b/crates/ra_assists/src/handlers/remove_dbg.rs @@ -3,7 +3,7 @@ use ra_syntax::{ TextSize, T, }; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: remove_dbg // @@ -20,7 +20,7 @@ use crate::{Assist, AssistCtx, AssistId}; // 92; // } // ``` -pub(crate) fn remove_dbg(ctx: AssistCtx) -> Option { +pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let macro_call = ctx.find_node_at_offset::()?; if !is_valid_macrocall(¯o_call, "dbg")? { @@ -58,7 +58,7 @@ pub(crate) fn remove_dbg(ctx: AssistCtx) -> Option { }; let target = macro_call.syntax().text_range(); - ctx.add_assist(AssistId("remove_dbg"), "Remove dbg!()", target, |edit| { + acc.add(AssistId("remove_dbg"), "Remove dbg!()", target, |edit| { edit.replace(macro_range, macro_content); edit.set_cursor(cursor_pos); }) diff --git a/crates/ra_assists/src/handlers/remove_mut.rs b/crates/ra_assists/src/handlers/remove_mut.rs index 9f72f879d..dce546db7 100644 --- a/crates/ra_assists/src/handlers/remove_mut.rs +++ b/crates/ra_assists/src/handlers/remove_mut.rs @@ -1,6 +1,6 @@ use ra_syntax::{SyntaxKind, TextRange, T}; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: remove_mut // @@ -17,7 +17,7 @@ use crate::{Assist, AssistCtx, AssistId}; // fn feed(&self, amount: u32) {} // } // ``` -pub(crate) fn remove_mut(ctx: AssistCtx) -> Option { +pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let mut_token = ctx.find_token_at_offset(T![mut])?; let delete_from = mut_token.text_range().start(); let delete_to = match mut_token.next_token() { @@ -26,7 +26,7 @@ pub(crate) fn remove_mut(ctx: AssistCtx) -> Option { }; let target = mut_token.text_range(); - ctx.add_assist(AssistId("remove_mut"), "Remove `mut` keyword", target, |edit| { + acc.add(AssistId("remove_mut"), "Remove `mut` keyword", target, |edit| { edit.set_cursor(delete_from); edit.delete(TextRange::new(delete_from, delete_to)); }) diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs index 0b930dea2..757f6406e 100644 --- a/crates/ra_assists/src/handlers/reorder_fields.rs +++ b/crates/ra_assists/src/handlers/reorder_fields.rs @@ -3,18 +3,9 @@ use std::collections::HashMap; use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; use itertools::Itertools; use ra_ide_db::RootDatabase; -use ra_syntax::{ - algo, - ast::{self, Path, RecordLit, RecordPat}, - match_ast, AstNode, SyntaxKind, - SyntaxKind::*, - SyntaxNode, -}; - -use crate::{ - assist_ctx::{Assist, AssistCtx}, - AssistId, -}; +use ra_syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode}; + +use crate::{AssistContext, AssistId, Assists}; // Assist: reorder_fields // @@ -31,13 +22,13 @@ use crate::{ // const test: Foo = Foo {foo: 1, bar: 0} // ``` // -pub(crate) fn reorder_fields(ctx: AssistCtx) -> Option { - reorder::(ctx.clone()).or_else(|| reorder::(ctx)) +pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + reorder::(acc, ctx.clone()).or_else(|| reorder::(acc, ctx)) } -fn reorder(ctx: AssistCtx) -> Option { +fn reorder(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let record = ctx.find_node_at_offset::()?; - let path = record.syntax().children().find_map(Path::cast)?; + let path = record.syntax().children().find_map(ast::Path::cast)?; let ranks = compute_fields_ranks(&path, &ctx)?; @@ -51,7 +42,7 @@ fn reorder(ctx: AssistCtx) -> Option { } let target = record.syntax().text_range(); - ctx.add_assist(AssistId("reorder_fields"), "Reorder record fields", target, |edit| { + acc.add(AssistId("reorder_fields"), "Reorder record fields", target, |edit| { for (old, new) in fields.iter().zip(&sorted_fields) { algo::diff(old, new).into_text_edit(edit.text_edit_builder()); } @@ -96,9 +87,9 @@ fn struct_definition(path: &ast::Path, sema: &Semantics) -> Option } } -fn compute_fields_ranks(path: &Path, ctx: &AssistCtx) -> Option> { +fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option> { Some( - struct_definition(path, ctx.sema)? + struct_definition(path, &ctx.sema)? .fields(ctx.db) .iter() .enumerate() diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs index 2eb8348f8..a59a06efa 100644 --- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs @@ -4,7 +4,7 @@ use ra_syntax::{ AstNode, }; -use crate::{utils::TryEnum, Assist, AssistCtx, AssistId}; +use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; // Assist: replace_if_let_with_match // @@ -32,7 +32,7 @@ use crate::{utils::TryEnum, Assist, AssistCtx, AssistId}; // } // } // ``` -pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option { +pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; let cond = if_expr.condition()?; let pat = cond.pat()?; @@ -43,36 +43,31 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option { ast::ElseBranch::IfExpr(_) => return None, }; - let sema = ctx.sema; let target = if_expr.syntax().text_range(); - ctx.add_assist( - AssistId("replace_if_let_with_match"), - "Replace with match", - target, - move |edit| { - let match_expr = { - let then_arm = { - let then_expr = unwrap_trivial_block(then_block); - make::match_arm(vec![pat.clone()], then_expr) - }; - let else_arm = { - let pattern = sema - .type_of_pat(&pat) - .and_then(|ty| TryEnum::from_ty(sema, &ty)) - .map(|it| it.sad_pattern()) - .unwrap_or_else(|| make::placeholder_pat().into()); - let else_expr = unwrap_trivial_block(else_block); - make::match_arm(vec![pattern], else_expr) - }; - make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) + acc.add(AssistId("replace_if_let_with_match"), "Replace with match", target, move |edit| { + let match_expr = { + let then_arm = { + let then_expr = unwrap_trivial_block(then_block); + make::match_arm(vec![pat.clone()], then_expr) }; + let else_arm = { + let pattern = ctx + .sema + .type_of_pat(&pat) + .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty)) + .map(|it| it.sad_pattern()) + .unwrap_or_else(|| make::placeholder_pat().into()); + let else_expr = unwrap_trivial_block(else_block); + make::match_arm(vec![pattern], else_expr) + }; + make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) + }; - let match_expr = IndentLevel::from_node(if_expr.syntax()).increase_indent(match_expr); + let match_expr = IndentLevel::from_node(if_expr.syntax()).increase_indent(match_expr); - edit.set_cursor(if_expr.syntax().text_range().start()); - edit.replace_ast::(if_expr.into(), match_expr); - }, - ) + edit.set_cursor(if_expr.syntax().text_range().start()); + edit.replace_ast::(if_expr.into(), match_expr); + }) } #[cfg(test)] diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs index a5509a567..d3f214591 100644 --- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs @@ -9,11 +9,7 @@ use ra_syntax::{ AstNode, T, }; -use crate::{ - assist_ctx::{Assist, AssistCtx}, - utils::TryEnum, - AssistId, -}; +use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; // Assist: replace_let_with_if_let // @@ -39,16 +35,16 @@ use crate::{ // // fn compute() -> Option { None } // ``` -pub(crate) fn replace_let_with_if_let(ctx: AssistCtx) -> Option { +pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let let_kw = ctx.find_token_at_offset(T![let])?; let let_stmt = let_kw.ancestors().find_map(ast::LetStmt::cast)?; let init = let_stmt.initializer()?; let original_pat = let_stmt.pat()?; let ty = ctx.sema.type_of_expr(&init)?; - let happy_variant = TryEnum::from_ty(ctx.sema, &ty).map(|it| it.happy_case()); + let happy_variant = TryEnum::from_ty(&ctx.sema, &ty).map(|it| it.happy_case()); let target = let_kw.text_range(); - ctx.add_assist(AssistId("replace_let_with_if_let"), "Replace with if-let", target, |edit| { + acc.add(AssistId("replace_let_with_if_let"), "Replace with if-let", target, |edit| { let with_placeholder: ast::Pat = match happy_variant { None => make::placeholder_pat().into(), Some(var_name) => make::tuple_struct_pat( diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs index fd41da64b..1a81d8a0e 100644 --- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs @@ -1,11 +1,7 @@ use hir; use ra_syntax::{ast, AstNode, SmolStr, TextRange}; -use crate::{ - assist_ctx::{Assist, AssistCtx}, - utils::insert_use_statement, - AssistId, -}; +use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists}; // Assist: replace_qualified_name_with_use // @@ -20,7 +16,10 @@ use crate::{ // // fn process(map: HashMap) {} // ``` -pub(crate) fn replace_qualified_name_with_use(ctx: AssistCtx) -> Option { +pub(crate) fn replace_qualified_name_with_use( + acc: &mut Assists, + ctx: &AssistContext, +) -> Option<()> { let path: ast::Path = ctx.find_node_at_offset()?; // We don't want to mess with use statements if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() { @@ -34,18 +33,18 @@ pub(crate) fn replace_qualified_name_with_use(ctx: AssistCtx) -> Option } let target = path.syntax().text_range(); - ctx.add_assist( + acc.add( AssistId("replace_qualified_name_with_use"), "Replace qualified path with use", target, - |edit| { + |builder| { let path_to_import = hir_path.mod_path().clone(); - insert_use_statement(path.syntax(), &path_to_import, edit); + insert_use_statement(path.syntax(), &path_to_import, ctx, builder); if let Some(last) = path.segment() { // Here we are assuming the assist will provide a correct use statement // so we can delete the path qualifier - edit.delete(TextRange::new( + builder.delete(TextRange::new( path.syntax().text_range().start(), last.syntax().text_range().start(), )); diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs index c6b73da67..a46998b8e 100644 --- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs @@ -5,7 +5,7 @@ use ra_syntax::{ AstNode, }; -use crate::{utils::TryEnum, Assist, AssistCtx, AssistId}; +use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; // Assist: replace_unwrap_with_match // @@ -29,7 +29,7 @@ use crate::{utils::TryEnum, Assist, AssistCtx, AssistId}; // }; // } // ``` -pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option { +pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?; let name = method_call.name_ref()?; if name.text() != "unwrap" { @@ -37,33 +37,26 @@ pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option { } let caller = method_call.expr()?; let ty = ctx.sema.type_of_expr(&caller)?; - let happy_variant = TryEnum::from_ty(ctx.sema, &ty)?.happy_case(); + let happy_variant = TryEnum::from_ty(&ctx.sema, &ty)?.happy_case(); let target = method_call.syntax().text_range(); - ctx.add_assist( - AssistId("replace_unwrap_with_match"), - "Replace unwrap with match", - target, - |edit| { - let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); - let it = make::bind_pat(make::name("a")).into(); - let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); + acc.add(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", target, |edit| { + let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); + let it = make::bind_pat(make::name("a")).into(); + let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); - let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); - let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); + let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); + let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); - let unreachable_call = make::unreachable_macro_call().into(); - let err_arm = - make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); + let unreachable_call = make::unreachable_macro_call().into(); + let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); - let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); - let match_expr = make::expr_match(caller.clone(), match_arm_list); - let match_expr = - IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr); + let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); + let match_expr = make::expr_match(caller.clone(), match_arm_list); + let match_expr = IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr); - edit.set_cursor(caller.syntax().text_range().start()); - edit.replace_ast::(method_call.into(), match_expr); - }, - ) + edit.set_cursor(caller.syntax().text_range().start()); + edit.replace_ast::(method_call.into(), match_expr); + }) } #[cfg(test)] diff --git a/crates/ra_assists/src/handlers/split_import.rs b/crates/ra_assists/src/handlers/split_import.rs index d49563974..159033731 100644 --- a/crates/ra_assists/src/handlers/split_import.rs +++ b/crates/ra_assists/src/handlers/split_import.rs @@ -2,7 +2,7 @@ use std::iter::successors; use ra_syntax::{ast, AstNode, T}; -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; // Assist: split_import // @@ -15,7 +15,7 @@ use crate::{Assist, AssistCtx, AssistId}; // ``` // use std::{collections::HashMap}; // ``` -pub(crate) fn split_import(ctx: AssistCtx) -> Option { +pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let colon_colon = ctx.find_token_at_offset(T![::])?; let path = ast::Path::cast(colon_colon.parent())?.qualifier()?; let top_path = successors(Some(path.clone()), |it| it.parent_path()).last()?; @@ -29,7 +29,7 @@ pub(crate) fn split_import(ctx: AssistCtx) -> Option { let cursor = ctx.frange.range.start(); let target = colon_colon.text_range(); - ctx.add_assist(AssistId("split_import"), "Split import", target, |edit| { + acc.add(AssistId("split_import"), "Split import", target, |edit| { edit.replace_ast(use_tree, new_tree); edit.set_cursor(cursor); }) diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index 6df927abb..eba0631a4 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs @@ -1,4 +1,4 @@ -use crate::{Assist, AssistCtx, AssistId}; +use crate::{AssistContext, AssistId, Assists}; use ast::LoopBodyOwner; use ra_fmt::unwrap_trivial_block; @@ -21,7 +21,7 @@ use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; // println!("foo"); // } // ``` -pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option { +pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let l_curly_token = ctx.find_token_at_offset(T!['{'])?; let block = ast::BlockExpr::cast(l_curly_token.parent())?; let parent = block.syntax().parent()?; @@ -58,7 +58,7 @@ pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option { }; let target = expr_to_unwrap.syntax().text_range(); - ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", target, |edit| { + acc.add(AssistId("unwrap_block"), "Unwrap block", target, |edit| { edit.set_cursor(expr.syntax().text_range().start()); let pat_start: &[_] = &[' ', '{', '\n']; diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 0473fd8c2..011613762 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -10,7 +10,7 @@ macro_rules! eprintln { ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; } -mod assist_ctx; +mod assist_context; mod marks; #[cfg(test)] mod tests; @@ -22,7 +22,7 @@ use ra_db::FileRange; use ra_ide_db::{source_change::SourceChange, RootDatabase}; use ra_syntax::TextRange; -pub(crate) use crate::assist_ctx::{Assist, AssistCtx}; +pub(crate) use crate::assist_context::{AssistContext, Assists}; /// Unique identifier of the assist, should not be shown to the user /// directly. @@ -68,13 +68,12 @@ pub struct ResolvedAssist { /// returned, without actual edits. pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec { let sema = Semantics::new(db); - let ctx = AssistCtx::new(&sema, range, false); - handlers::all() - .iter() - .filter_map(|f| f(ctx.clone())) - .flat_map(|it| it.0) - .map(|a| a.label) - .collect() + let ctx = AssistContext::new(sema, range); + let mut acc = Assists::new_unresolved(&ctx); + handlers::all().iter().for_each(|handler| { + handler(&mut acc, &ctx); + }); + acc.finish_unresolved() } /// Return all the assists applicable at the given position. @@ -83,31 +82,30 @@ pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec Vec { let sema = Semantics::new(db); - let ctx = AssistCtx::new(&sema, range, true); - let mut a = handlers::all() - .iter() - .filter_map(|f| f(ctx.clone())) - .flat_map(|it| it.0) - .map(|it| it.into_resolved().unwrap()) - .collect::>(); - a.sort_by_key(|it| it.label.target.len()); - a + let ctx = AssistContext::new(sema, range); + let mut acc = Assists::new_resolved(&ctx); + handlers::all().iter().for_each(|handler| { + handler(&mut acc, &ctx); + }); + acc.finish_resolved() } mod handlers { - use crate::{Assist, AssistCtx}; + use crate::{AssistContext, Assists}; - pub(crate) type Handler = fn(AssistCtx) -> Option; + pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>; mod add_custom_impl; mod add_derive; mod add_explicit_type; + mod add_from_impl_for_enum; mod add_function; mod add_impl; mod add_missing_impl_members; mod add_new; mod apply_demorgan; mod auto_import; + mod change_return_type_to_result; mod change_visibility; mod early_return; mod fill_match_arms; @@ -124,14 +122,12 @@ mod handlers { mod raw_string; mod remove_dbg; mod remove_mut; + mod reorder_fields; mod replace_if_let_with_match; mod replace_let_with_if_let; mod replace_qualified_name_with_use; mod replace_unwrap_with_match; mod split_import; - mod change_return_type_to_result; - mod add_from_impl_for_enum; - mod reorder_fields; mod unwrap_block; pub(crate) fn all() -> &'static [Handler] { diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs index 17e3ece9f..45b2d9733 100644 --- a/crates/ra_assists/src/tests.rs +++ b/crates/ra_assists/src/tests.rs @@ -11,7 +11,7 @@ use test_utils::{ RangeOrOffset, }; -use crate::{handlers::Handler, resolved_assists, AssistCtx}; +use crate::{handlers::Handler, resolved_assists, AssistContext, Assists}; pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { let (mut db, file_id) = RootDatabase::with_single_file(text); @@ -71,7 +71,7 @@ enum ExpectedResult<'a> { Target(&'a str), } -fn check(assist: Handler, before: &str, expected: ExpectedResult) { +fn check(handler: Handler, before: &str, expected: ExpectedResult) { let (text_without_caret, file_with_caret_id, range_or_offset, db) = if before.contains("//-") { let (mut db, position) = RootDatabase::with_position(before); db.set_local_roots(Arc::new(vec![db.file_source_root(position.file_id)])); @@ -90,17 +90,20 @@ fn check(assist: Handler, before: &str, expected: ExpectedResult) { let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() }; let sema = Semantics::new(&db); - let assist_ctx = AssistCtx::new(&sema, frange, true); - - match (assist(assist_ctx), expected) { + let ctx = AssistContext::new(sema, frange); + let mut acc = Assists::new_resolved(&ctx); + handler(&mut acc, &ctx); + let mut res = acc.finish_resolved(); + let assist = res.pop(); + match (assist, expected) { (Some(assist), ExpectedResult::After(after)) => { - let mut action = assist.0[0].source_change.clone().unwrap(); - let change = action.source_file_edits.pop().unwrap(); + let mut source_change = assist.source_change; + let change = source_change.source_file_edits.pop().unwrap(); let mut actual = db.file_text(change.file_id).as_ref().to_owned(); change.edit.apply(&mut actual); - match action.cursor_position { + match source_change.cursor_position { None => { if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset { let off = change @@ -116,7 +119,7 @@ fn check(assist: Handler, before: &str, expected: ExpectedResult) { assert_eq_text!(after, &actual); } (Some(assist), ExpectedResult::Target(target)) => { - let range = assist.0[0].label.target; + let range = assist.label.target; assert_eq_text!(&text_without_caret[range], target); } (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), diff --git a/crates/ra_assists/src/utils/insert_use.rs b/crates/ra_assists/src/utils/insert_use.rs index c1f447efe..1214e3cd4 100644 --- a/crates/ra_assists/src/utils/insert_use.rs +++ b/crates/ra_assists/src/utils/insert_use.rs @@ -2,7 +2,6 @@ // FIXME: rewrite according to the plan, outlined in // https://github.com/rust-analyzer/rust-analyzer/issues/3301#issuecomment-592931553 -use crate::assist_ctx::ActionBuilder; use hir::{self, ModPath}; use ra_syntax::{ ast::{self, NameOwner}, @@ -12,6 +11,8 @@ use ra_syntax::{ }; use ra_text_edit::TextEditBuilder; +use crate::assist_context::{AssistBuilder, AssistContext}; + /// Creates and inserts a use statement for the given path to import. /// The use statement is inserted in the scope most appropriate to the /// the cursor position given, additionally merged with the existing use imports. @@ -19,10 +20,11 @@ pub(crate) fn insert_use_statement( // Ideally the position of the cursor, used to position: &SyntaxNode, path_to_import: &ModPath, - edit: &mut ActionBuilder, + ctx: &AssistContext, + builder: &mut AssistBuilder, ) { let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::>(); - let container = edit.ctx().sema.ancestors_with_macros(position.clone()).find_map(|n| { + let container = ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| { if let Some(module) = ast::Module::cast(n.clone()) { return module.item_list().map(|it| it.syntax().clone()); } @@ -31,7 +33,7 @@ pub(crate) fn insert_use_statement( if let Some(container) = container { let action = best_action_for_target(container, position.clone(), &target); - make_assist(&action, &target, edit.text_edit_builder()); + make_assist(&action, &target, builder.text_edit_builder()); } } -- cgit v1.2.3 From c839d4f7a9e667b5aca30388c43fa8b1dbab14a7 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Thu, 7 May 2020 16:52:14 +0200 Subject: do not show runnables for main function outside of a binary target #4356 Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/rust-analyzer/src/main_loop/handlers.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index 15e8305f8..f4353af64 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -42,6 +42,7 @@ use crate::{ world::WorldSnapshot, LspError, Result, }; +use ra_project_model::TargetKind; pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result { let _p = profile("handle_analyzer_status"); @@ -384,16 +385,27 @@ pub fn handle_runnables( let offset = params.position.map(|it| it.conv_with(&line_index)); let mut res = Vec::new(); let workspace_root = world.workspace_root_for(file_id); + let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; for runnable in world.analysis().runnables(file_id)? { if let Some(offset) = offset { if !runnable.range.contains_inclusive(offset) { continue; } } + // Do not suggest binary run on other target than binary + if let RunnableKind::Bin = runnable.kind { + if let Some(spec) = &cargo_spec { + match spec.target_kind { + TargetKind::Bin => {} + _ => continue, + } + } + } res.push(to_lsp_runnable(&world, file_id, runnable)?); } + // Add `cargo check` and `cargo test` for the whole package - match CargoTargetSpec::for_file(&world, file_id)? { + match cargo_spec { Some(spec) => { for &cmd in ["check", "test"].iter() { res.push(req::Runnable { @@ -831,13 +843,23 @@ pub fn handle_code_lens( let mut lenses: Vec = Default::default(); + let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; // Gather runnables for runnable in world.analysis().runnables(file_id)? { let title = match &runnable.kind { RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => "▶️\u{fe0e}Run Test", RunnableKind::DocTest { .. } => "▶️\u{fe0e}Run Doctest", RunnableKind::Bench { .. } => "Run Bench", - RunnableKind::Bin => "Run", + RunnableKind::Bin => { + // Do not suggest binary run on other target than binary + match &cargo_spec { + Some(spec) => match spec.target_kind { + TargetKind::Bin => "Run", + _ => continue, + }, + None => continue, + } + } } .to_string(); let mut r = to_lsp_runnable(&world, file_id, runnable)?; -- cgit v1.2.3 From c6b81bc013b5278b917d109b723405e0df413323 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 7 May 2020 17:09:59 +0200 Subject: Rename AssitLabel -> Assist --- crates/ra_assists/src/assist_context.rs | 16 ++++++++-------- crates/ra_assists/src/lib.rs | 12 ++++++------ crates/ra_assists/src/tests.rs | 14 +++++++------- crates/ra_ide/src/lib.rs | 6 +++--- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 203ad1273..81052ab49 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs @@ -15,7 +15,7 @@ use ra_syntax::{ }; use ra_text_edit::TextEditBuilder; -use crate::{AssistId, AssistLabel, GroupLabel, ResolvedAssist}; +use crate::{Assist, AssistId, GroupLabel, ResolvedAssist}; /// `AssistContext` allows to apply an assist or check if it could be applied. /// @@ -91,7 +91,7 @@ impl<'a> AssistContext<'a> { pub(crate) struct Assists { resolve: bool, file: FileId, - buf: Vec<(AssistLabel, Option)>, + buf: Vec<(Assist, Option)>, } impl Assists { @@ -102,7 +102,7 @@ impl Assists { Assists { resolve: false, file: ctx.frange.file_id, buf: Vec::new() } } - pub(crate) fn finish_unresolved(self) -> Vec { + pub(crate) fn finish_unresolved(self) -> Vec { assert!(!self.resolve); self.finish() .into_iter() @@ -117,7 +117,7 @@ impl Assists { assert!(self.resolve); self.finish() .into_iter() - .map(|(label, edit)| ResolvedAssist { label, source_change: edit.unwrap() }) + .map(|(label, edit)| ResolvedAssist { assist: label, source_change: edit.unwrap() }) .collect() } @@ -128,7 +128,7 @@ impl Assists { target: TextRange, f: impl FnOnce(&mut AssistBuilder), ) -> Option<()> { - let label = AssistLabel::new(id, label.into(), None, target); + let label = Assist::new(id, label.into(), None, target); self.add_impl(label, f) } pub(crate) fn add_group( @@ -139,10 +139,10 @@ impl Assists { target: TextRange, f: impl FnOnce(&mut AssistBuilder), ) -> Option<()> { - let label = AssistLabel::new(id, label.into(), Some(group.clone()), target); + let label = Assist::new(id, label.into(), Some(group.clone()), target); self.add_impl(label, f) } - fn add_impl(&mut self, label: AssistLabel, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { + fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { let change_label = label.label.clone(); let source_change = if self.resolve { let mut builder = AssistBuilder::new(self.file); @@ -156,7 +156,7 @@ impl Assists { Some(()) } - fn finish(mut self) -> Vec<(AssistLabel, Option)> { + fn finish(mut self) -> Vec<(Assist, Option)> { self.buf.sort_by_key(|(label, _edit)| label.target.len()); self.buf } diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 011613762..a91975d8c 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -30,7 +30,7 @@ pub(crate) use crate::assist_context::{AssistContext, Assists}; pub struct AssistId(pub &'static str); #[derive(Debug, Clone)] -pub struct AssistLabel { +pub struct Assist { pub id: AssistId, /// Short description of the assist, as shown in the UI. pub label: String, @@ -43,22 +43,22 @@ pub struct AssistLabel { #[derive(Clone, Debug)] pub struct GroupLabel(pub String); -impl AssistLabel { +impl Assist { pub(crate) fn new( id: AssistId, label: String, group: Option, target: TextRange, - ) -> AssistLabel { + ) -> Assist { // FIXME: make fields private, so that this invariant can't be broken assert!(label.starts_with(|c: char| c.is_uppercase())); - AssistLabel { id, label, group, target } + Assist { id, label, group, target } } } #[derive(Debug, Clone)] pub struct ResolvedAssist { - pub label: AssistLabel, + pub assist: Assist, pub source_change: SourceChange, } @@ -66,7 +66,7 @@ pub struct ResolvedAssist { /// /// Assists are returned in the "unresolved" state, that is only labels are /// returned, without actual edits. -pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec { +pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec { let sema = Semantics::new(db); let ctx = AssistContext::new(sema, range); let mut acc = Assists::new_unresolved(&ctx); diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs index 45b2d9733..a81c54d07 100644 --- a/crates/ra_assists/src/tests.rs +++ b/crates/ra_assists/src/tests.rs @@ -43,14 +43,14 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) { let mut assist = resolved_assists(&db, frange) .into_iter() - .find(|assist| assist.label.id.0 == assist_id) + .find(|assist| assist.assist.id.0 == assist_id) .unwrap_or_else(|| { panic!( "\n\nAssist is not applicable: {}\nAvailable assists: {}", assist_id, resolved_assists(&db, frange) .into_iter() - .map(|assist| assist.label.id.0) + .map(|assist| assist.assist.id.0) .collect::>() .join(", ") ) @@ -119,7 +119,7 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult) { assert_eq_text!(after, &actual); } (Some(assist), ExpectedResult::Target(target)) => { - let range = assist.label.target; + let range = assist.assist.target; assert_eq_text!(&text_without_caret[range], target); } (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), @@ -140,10 +140,10 @@ fn assist_order_field_struct() { let mut assists = assists.iter(); assert_eq!( - assists.next().expect("expected assist").label.label, + assists.next().expect("expected assist").assist.label, "Change visibility to pub(crate)" ); - assert_eq!(assists.next().expect("expected assist").label.label, "Add `#[derive]`"); + assert_eq!(assists.next().expect("expected assist").assist.label, "Add `#[derive]`"); } #[test] @@ -162,6 +162,6 @@ fn assist_order_if_expr() { let assists = resolved_assists(&db, frange); let mut assists = assists.iter(); - assert_eq!(assists.next().expect("expected assist").label.label, "Extract into variable"); - assert_eq!(assists.next().expect("expected assist").label.label, "Replace with match"); + assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); + assert_eq!(assists.next().expect("expected assist").assist.label, "Replace with match"); } diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 737f87109..0e15f1ccd 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -475,9 +475,9 @@ impl Analysis { ra_assists::resolved_assists(db, frange) .into_iter() .map(|assist| Assist { - id: assist.label.id, - label: assist.label.label, - group_label: assist.label.group.map(|it| it.0), + id: assist.assist.id, + label: assist.assist.label, + group_label: assist.assist.group.map(|it| it.0), source_change: assist.source_change, }) .collect() -- cgit v1.2.3 From 28fcff125a73ab2fc4aeaa100fc472af5178db20 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 7 May 2020 17:29:23 +0200 Subject: Nicer API --- crates/ra_assists/src/lib.rs | 72 +++++++++++++++++++++--------------------- crates/ra_assists/src/tests.rs | 10 +++--- crates/ra_ide/src/lib.rs | 2 +- 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index a91975d8c..b6dc7cb1b 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -29,6 +29,9 @@ pub(crate) use crate::assist_context::{AssistContext, Assists}; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct AssistId(pub &'static str); +#[derive(Clone, Debug)] +pub struct GroupLabel(pub String); + #[derive(Debug, Clone)] pub struct Assist { pub id: AssistId, @@ -40,10 +43,41 @@ pub struct Assist { pub target: TextRange, } -#[derive(Clone, Debug)] -pub struct GroupLabel(pub String); +#[derive(Debug, Clone)] +pub struct ResolvedAssist { + pub assist: Assist, + pub source_change: SourceChange, +} impl Assist { + /// Return all the assists applicable at the given position. + /// + /// Assists are returned in the "unresolved" state, that is only labels are + /// returned, without actual edits. + pub fn unresolved(db: &RootDatabase, range: FileRange) -> Vec { + let sema = Semantics::new(db); + let ctx = AssistContext::new(sema, range); + let mut acc = Assists::new_unresolved(&ctx); + handlers::all().iter().for_each(|handler| { + handler(&mut acc, &ctx); + }); + acc.finish_unresolved() + } + + /// Return all the assists applicable at the given position. + /// + /// Assists are returned in the "resolved" state, that is with edit fully + /// computed. + pub fn resolved(db: &RootDatabase, range: FileRange) -> Vec { + let sema = Semantics::new(db); + let ctx = AssistContext::new(sema, range); + let mut acc = Assists::new_resolved(&ctx); + handlers::all().iter().for_each(|handler| { + handler(&mut acc, &ctx); + }); + acc.finish_resolved() + } + pub(crate) fn new( id: AssistId, label: String, @@ -56,40 +90,6 @@ impl Assist { } } -#[derive(Debug, Clone)] -pub struct ResolvedAssist { - pub assist: Assist, - pub source_change: SourceChange, -} - -/// Return all the assists applicable at the given position. -/// -/// Assists are returned in the "unresolved" state, that is only labels are -/// returned, without actual edits. -pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec { - let sema = Semantics::new(db); - let ctx = AssistContext::new(sema, range); - let mut acc = Assists::new_unresolved(&ctx); - handlers::all().iter().for_each(|handler| { - handler(&mut acc, &ctx); - }); - acc.finish_unresolved() -} - -/// Return all the assists applicable at the given position. -/// -/// Assists are returned in the "resolved" state, that is with edit fully -/// computed. -pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec { - let sema = Semantics::new(db); - let ctx = AssistContext::new(sema, range); - let mut acc = Assists::new_resolved(&ctx); - handlers::all().iter().for_each(|handler| { - handler(&mut acc, &ctx); - }); - acc.finish_resolved() -} - mod handlers { use crate::{AssistContext, Assists}; diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs index a81c54d07..a3eacb8f1 100644 --- a/crates/ra_assists/src/tests.rs +++ b/crates/ra_assists/src/tests.rs @@ -11,7 +11,7 @@ use test_utils::{ RangeOrOffset, }; -use crate::{handlers::Handler, resolved_assists, AssistContext, Assists}; +use crate::{handlers::Handler, Assist, AssistContext, Assists}; pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { let (mut db, file_id) = RootDatabase::with_single_file(text); @@ -41,14 +41,14 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) { let (db, file_id) = crate::tests::with_single_file(&before); let frange = FileRange { file_id, range: selection.into() }; - let mut assist = resolved_assists(&db, frange) + let mut assist = Assist::resolved(&db, frange) .into_iter() .find(|assist| assist.assist.id.0 == assist_id) .unwrap_or_else(|| { panic!( "\n\nAssist is not applicable: {}\nAvailable assists: {}", assist_id, - resolved_assists(&db, frange) + Assist::resolved(&db, frange) .into_iter() .map(|assist| assist.assist.id.0) .collect::>() @@ -136,7 +136,7 @@ fn assist_order_field_struct() { let (before_cursor_pos, before) = extract_offset(before); let (db, file_id) = with_single_file(&before); let frange = FileRange { file_id, range: TextRange::empty(before_cursor_pos) }; - let assists = resolved_assists(&db, frange); + let assists = Assist::resolved(&db, frange); let mut assists = assists.iter(); assert_eq!( @@ -159,7 +159,7 @@ fn assist_order_if_expr() { let (range, before) = extract_range(before); let (db, file_id) = with_single_file(&before); let frange = FileRange { file_id, range }; - let assists = resolved_assists(&db, frange); + let assists = Assist::resolved(&db, frange); let mut assists = assists.iter(); assert_eq!(assists.next().expect("expected assist").assist.label, "Extract into variable"); diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 0e15f1ccd..915199bd8 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -472,7 +472,7 @@ impl Analysis { /// position. pub fn assists(&self, frange: FileRange) -> Cancelable> { self.with_db(|db| { - ra_assists::resolved_assists(db, frange) + ra_assists::Assist::resolved(db, frange) .into_iter() .map(|assist| Assist { id: assist.assist.id, -- cgit v1.2.3 From 1e790ea3149f085e49cb66e6a052920f72da01e9 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 7 May 2020 17:32:01 +0200 Subject: Simplify --- crates/ra_assists/src/assist_context.rs | 3 +-- crates/ra_assists/src/handlers/early_return.rs | 2 +- crates/ra_assists/src/handlers/merge_imports.rs | 2 +- crates/ra_assists/src/handlers/merge_match_arms.rs | 2 +- crates/ra_assists/src/handlers/split_import.rs | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 81052ab49..3085c4330 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs @@ -76,8 +76,7 @@ impl<'a> AssistContext<'a> { find_node_at_offset(self.source_file.syntax(), self.offset()) } pub(crate) fn find_node_at_offset_with_descend(&self) -> Option { - self.sema - .find_node_at_offset_with_descend(self.source_file.syntax(), self.frange.range.start()) + self.sema.find_node_at_offset_with_descend(self.source_file.syntax(), self.offset()) } pub(crate) fn covering_element(&self) -> SyntaxElement { find_covering_element(self.source_file.syntax(), self.frange.range) diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs index ccf91797c..810784ad5 100644 --- a/crates/ra_assists/src/handlers/early_return.rs +++ b/crates/ra_assists/src/handlers/early_return.rs @@ -93,7 +93,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) } then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?; - let cursor_position = ctx.frange.range.start(); + let cursor_position = ctx.offset(); let target = if_expr.syntax().text_range(); acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| { diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs index 8e1d93312..ac3e53c27 100644 --- a/crates/ra_assists/src/handlers/merge_imports.rs +++ b/crates/ra_assists/src/handlers/merge_imports.rs @@ -26,7 +26,7 @@ use crate::{ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let tree: ast::UseTree = ctx.find_node_at_offset()?; let mut rewriter = SyntaxRewriter::default(); - let mut offset = ctx.frange.range.start(); + let mut offset = ctx.offset(); if let Some(use_item) = tree.syntax().parent().and_then(ast::UseItem::cast) { let (merged, to_delete) = next_prev() diff --git a/crates/ra_assists/src/handlers/merge_match_arms.rs b/crates/ra_assists/src/handlers/merge_match_arms.rs index cfe4df47b..d4e38aa6a 100644 --- a/crates/ra_assists/src/handlers/merge_match_arms.rs +++ b/crates/ra_assists/src/handlers/merge_match_arms.rs @@ -45,7 +45,7 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option InExpr(TextSize), InPat(TextSize), } - let cursor_pos = ctx.frange.range.start(); + let cursor_pos = ctx.offset(); let cursor_pos = if current_expr.syntax().text_range().contains(cursor_pos) { CursorPos::InExpr(current_text_range.end() - cursor_pos) } else { diff --git a/crates/ra_assists/src/handlers/split_import.rs b/crates/ra_assists/src/handlers/split_import.rs index 159033731..b2757e50c 100644 --- a/crates/ra_assists/src/handlers/split_import.rs +++ b/crates/ra_assists/src/handlers/split_import.rs @@ -26,7 +26,7 @@ pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> if new_tree == use_tree { return None; } - let cursor = ctx.frange.range.start(); + let cursor = ctx.offset(); let target = colon_colon.text_range(); acc.add(AssistId("split_import"), "Split import", target, |edit| { -- cgit v1.2.3 From 435a17ecd8806f3ae81edf6277c17363b01f4334 Mon Sep 17 00:00:00 2001 From: vsrs Date: Thu, 7 May 2020 18:35:48 +0300 Subject: Add separate settings for each debug engine. --- editors/code/package.json | 4 ++-- editors/code/src/commands/runnables.ts | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/editors/code/package.json b/editors/code/package.json index 84aea8249..e4dd66924 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -427,7 +427,7 @@ "rust-analyzer.debug.engineSettings": { "type": "object", "default": {}, - "description": "Optional settings passed to the debug engine." + "description": "Optional settings passed to the debug engine. Example:\n{ \"lldb\": { \"terminal\":\"external\"} }" } } }, @@ -609,4 +609,4 @@ } ] } -} +} \ No newline at end of file diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index 782a7ba89..e62de7d6e 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -134,9 +134,12 @@ export function debugSingle(ctx: Ctx): Cmd { } const executable = await getDebugExecutable(config); - let debugConfig = knownEngines[debugEngine.id](config, executable, debugOptions.sourceFileMap); - for (var key in debugOptions.engineSettings) { - debugConfig[key] = (debugOptions.engineSettings as any)[key]; + const debugConfig = knownEngines[debugEngine.id](config, executable, debugOptions.sourceFileMap); + if (debugConfig.type in debugOptions.engineSettings) { + const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; + for (var key in settingsMap) { + debugConfig[key] = settingsMap[key]; + } } debugOutput.appendLine("Launching debug configuration:"); -- cgit v1.2.3 From 23f4859166ba16f02927b476aad2ae91e618b1ef Mon Sep 17 00:00:00 2001 From: vsrs Date: Thu, 7 May 2020 18:53:14 +0300 Subject: Add CodeLLDB Rust visualization --- editors/code/package.json | 2 +- editors/code/src/commands/runnables.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/editors/code/package.json b/editors/code/package.json index e4dd66924..e8e9598f6 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -609,4 +609,4 @@ } ] } -} \ No newline at end of file +} diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts index e62de7d6e..ae328d2a4 100644 --- a/editors/code/src/commands/runnables.ts +++ b/editors/code/src/commands/runnables.ts @@ -72,7 +72,8 @@ function getLldbDebugConfig(config: ra.Runnable, executable: string, sourceFileM program: executable, args: config.extraArgs, cwd: config.cwd, - sourceMap: sourceFileMap + sourceMap: sourceFileMap, + sourceLanguages: ["rust"] }; } -- cgit v1.2.3 From 2904311664e0aeb2faa3324616083e1bcf7333fc Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 7 May 2020 18:46:58 +0200 Subject: Use the correct color for structs This works around https://github.com/microsoft/vscode/issues/97162 --- editors/code/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/editors/code/package.json b/editors/code/package.json index 12a08ba40..6935fa7a5 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -589,6 +589,9 @@ "union": [ "entity.name.union" ], + "struct": [ + "entity.name.type.struct" + ], "keyword.unsafe": [ "keyword.other.unsafe" ], -- cgit v1.2.3 From 3077eae2a61f97c28c0d4e3456f6ab873126e5b8 Mon Sep 17 00:00:00 2001 From: Craig Disselkoen Date: Thu, 7 May 2020 12:06:44 -0700 Subject: use home crate instead of dirs --- Cargo.lock | 87 ++++++------------------------------------------ crates/ra_env/Cargo.toml | 2 +- crates/ra_env/src/lib.rs | 2 +- 3 files changed, 13 insertions(+), 78 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 28424b7d4..000c26c4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,12 +21,6 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344" -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - [[package]] name = "arrayvec" version = "0.5.1" @@ -72,12 +66,6 @@ dependencies = [ "libc", ] -[[package]] -name = "base64" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" - [[package]] name = "base64" version = "0.12.0" @@ -90,17 +78,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" -[[package]] -name = "blake2b_simd" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" -dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", -] - [[package]] name = "bstr" version = "0.2.12" @@ -235,12 +212,6 @@ dependencies = [ "winapi 0.3.8", ] -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - [[package]] name = "crossbeam" version = "0.7.3" @@ -318,28 +289,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" -[[package]] -name = "dirs" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" -dependencies = [ - "cfg-if", - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" -dependencies = [ - "cfg-if", - "libc", - "redox_users", - "winapi 0.3.8", -] - [[package]] name = "drop_bomb" version = "0.1.4" @@ -515,6 +464,15 @@ dependencies = [ "libc", ] +[[package]] +name = "home" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654" +dependencies = [ + "winapi 0.3.8", +] + [[package]] name = "idna" version = "0.2.0" @@ -710,7 +668,7 @@ version = "0.74.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0e6a2b8837d27b29deb3f3e6dc1c6d2f57947677f9be1024e482ec5b59525" dependencies = [ - "base64 0.12.0", + "base64", "bitflags", "serde", "serde_json", @@ -1004,7 +962,7 @@ name = "ra_env" version = "0.1.0" dependencies = [ "anyhow", - "dirs", + "home", ] [[package]] @@ -1359,17 +1317,6 @@ version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" -[[package]] -name = "redox_users" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" -dependencies = [ - "getrandom", - "redox_syscall", - "rust-argon2", -] - [[package]] name = "regex" version = "1.3.7" @@ -1454,18 +1401,6 @@ dependencies = [ "winapi 0.3.8", ] -[[package]] -name = "rust-argon2" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" -dependencies = [ - "base64 0.11.0", - "blake2b_simd", - "constant_time_eq", - "crossbeam-utils", -] - [[package]] name = "rustc-ap-rustc_lexer" version = "656.0.0" diff --git a/crates/ra_env/Cargo.toml b/crates/ra_env/Cargo.toml index 7fed446a7..f0a401be5 100644 --- a/crates/ra_env/Cargo.toml +++ b/crates/ra_env/Cargo.toml @@ -6,4 +6,4 @@ authors = ["rust-analyzer developers"] [dependencies] anyhow = "1.0.26" -dirs = "2.0" +home = "0.5.3" diff --git a/crates/ra_env/src/lib.rs b/crates/ra_env/src/lib.rs index 4f94bffde..413da1982 100644 --- a/crates/ra_env/src/lib.rs +++ b/crates/ra_env/src/lib.rs @@ -36,7 +36,7 @@ pub fn get_path_for_executable(executable_name: impl AsRef) -> Result Date: Thu, 7 May 2020 16:22:53 -0400 Subject: Update deps --- Cargo.lock | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5d50a766f..264b9b7fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,9 +68,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5ca2cd0adc3f48f9e9ea5a6bbdf9ccc0bfade884847e484d452414c7ccffb3" +checksum = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42" [[package]] name = "bitflags" @@ -342,9 +342,9 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f59efc38004c988e4201d11d263b8171f49a2e7ec0bdbb71773433f271504a5e" +checksum = "affc17579b132fc2461adf7c575cc6e8b134ebca52c51f5411388965227dc695" dependencies = [ "cfg-if", "libc", @@ -610,18 +610,18 @@ checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" [[package]] name = "libloading" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c4f51b790f5bdb65acb4cc94bb81d7b2ee60348a5431ac1467d390b017600b0" +checksum = "2cadb8e769f070c45df05c78c7520eb4cd17061d4ab262e43cfc68b4d00ac71c" dependencies = [ "winapi 0.3.8", ] [[package]] name = "linked-hash-map" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" +checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" [[package]] name = "lock_api" @@ -824,9 +824,9 @@ dependencies = [ [[package]] name = "paste" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3c897744f63f34f7ae3a024d9162bb5001f4ad661dd24bea0dc9f075d2de1c6" +checksum = "0a229b1c58c692edcaa5b9b0948084f130f55d2dcc15b02fcc5340b2b4521476" dependencies = [ "paste-impl", "proc-macro-hack", @@ -834,9 +834,9 @@ dependencies = [ [[package]] name = "paste-impl" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66fd6f92e3594f2dd7b3fc23e42d82e292f7bcda6d8e5dcd167072327234ab89" +checksum = "2e0bf239e447e67ff6d16a8bb5e4d4bd2343acf5066061c0e8e06ac5ba8ca68c" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -886,9 +886,9 @@ checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" [[package]] name = "proc-macro2" -version = "1.0.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" dependencies = [ "unicode-xid", ] @@ -1581,9 +1581,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" [[package]] name = "syn" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" +checksum = "e8e5aa70697bb26ee62214ae3288465ecec0000f05182f039b477001f08f5ae7" dependencies = [ "proc-macro2", "quote", -- cgit v1.2.3 From d3110859ba4e97cf17d2c997befa92fb63bfb138 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 8 May 2020 02:56:53 +0200 Subject: Move feature desugaring to the right abstraction layer --- crates/ra_cfg/src/lib.rs | 16 ---------------- crates/ra_project_model/src/lib.rs | 14 ++++++++++++-- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/crates/ra_cfg/src/lib.rs b/crates/ra_cfg/src/lib.rs index 697a04581..57feabcb2 100644 --- a/crates/ra_cfg/src/lib.rs +++ b/crates/ra_cfg/src/lib.rs @@ -2,8 +2,6 @@ mod cfg_expr; -use std::iter::IntoIterator; - use ra_syntax::SmolStr; use rustc_hash::FxHashSet; @@ -48,18 +46,4 @@ impl CfgOptions { pub fn insert_key_value(&mut self, key: SmolStr, value: SmolStr) { self.key_values.insert((key, value)); } - - /// Shortcut to set features - pub fn insert_features(&mut self, iter: impl IntoIterator) { - iter.into_iter().for_each(|feat| self.insert_key_value("feature".into(), feat)); - } - - /// Shortcut to set cfgs - pub fn insert_cfgs(&mut self, iter: impl IntoIterator) { - iter.into_iter().for_each(|cfg| match cfg.find('=') { - Some(split) => self - .insert_key_value(cfg[0..split].into(), cfg[split + 1..].trim_matches('"').into()), - None => self.insert_atom(cfg), - }); - } } diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 8703429d4..c226ffa57 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -398,8 +398,18 @@ impl ProjectWorkspace { let edition = cargo[pkg].edition; let cfg_options = { let mut opts = default_cfg_options.clone(); - opts.insert_features(cargo[pkg].features.iter().map(Into::into)); - opts.insert_cfgs(cargo[pkg].cfgs.iter().map(Into::into)); + for feature in cargo[pkg].features.iter() { + opts.insert_key_value("feature".into(), feature.into()); + } + for cfg in cargo[pkg].cfgs.iter() { + match cfg.find('=') { + Some(split) => opts.insert_key_value( + cfg[..split].into(), + cfg[split + 1..].trim_matches('"').into(), + ), + None => opts.insert_atom(cfg.into()), + }; + } opts }; let mut env = Env::default(); -- cgit v1.2.3 From 3bf5ef02c0dc3087fb4cdd0a794892edde359a0d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 8 May 2020 09:28:15 +0200 Subject: Add master config for inlayHints to make disabling easy --- editors/code/package.json | 5 +++++ editors/code/src/config.ts | 1 + editors/code/src/inlay_hints.ts | 14 +++++++------- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/editors/code/package.json b/editors/code/package.json index 6935fa7a5..853fc513b 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -300,6 +300,11 @@ "default": true, "markdownDescription": "Check with all features (will be passed as `--all-features`)" }, + "rust-analyzer.inlayHints.enable": { + "type": "boolean", + "default": true, + "description": "Disable all inlay hints" + }, "rust-analyzer.inlayHints.typeHints": { "type": "boolean", "default": true, diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 110e54180..46de922f3 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -94,6 +94,7 @@ export class Config { get inlayHints() { return { + enable: this.get("inlayHints.enable"), typeHints: this.get("inlayHints.typeHints"), parameterHints: this.get("inlayHints.parameterHints"), chainingHints: this.get("inlayHints.chainingHints"), diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts index a09531797..a2b07d003 100644 --- a/editors/code/src/inlay_hints.ts +++ b/editors/code/src/inlay_hints.ts @@ -10,13 +10,13 @@ export function activateInlayHints(ctx: Ctx) { const maybeUpdater = { updater: null as null | HintsUpdater, async onConfigChange() { - if ( - !ctx.config.inlayHints.typeHints && - !ctx.config.inlayHints.parameterHints && - !ctx.config.inlayHints.chainingHints - ) { - return this.dispose(); - } + const anyEnabled = ctx.config.inlayHints.typeHints + || ctx.config.inlayHints.parameterHints + || ctx.config.inlayHints.chainingHints; + const enabled = ctx.config.inlayHints.enable && anyEnabled; + + if (!enabled) return this.dispose(); + await sleep(100); if (this.updater) { this.updater.syncCacheAndRenderHints(); -- cgit v1.2.3 From 6713be0b130670324c61d9deb38b7b6aee6a8bac Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 8 May 2020 12:25:36 +0200 Subject: Rename ra_env -> ra_toolchain --- Cargo.lock | 20 ++++---- crates/ra_env/Cargo.toml | 9 ---- crates/ra_env/src/lib.rs | 66 ------------------------- crates/ra_flycheck/Cargo.toml | 2 +- crates/ra_flycheck/src/lib.rs | 2 +- crates/ra_project_model/Cargo.toml | 2 +- crates/ra_project_model/src/cargo_workspace.rs | 2 +- crates/ra_project_model/src/lib.rs | 2 +- crates/ra_project_model/src/sysroot.rs | 4 +- crates/ra_toolchain/Cargo.toml | 9 ++++ crates/ra_toolchain/src/lib.rs | 68 ++++++++++++++++++++++++++ 11 files changed, 94 insertions(+), 92 deletions(-) delete mode 100644 crates/ra_env/Cargo.toml delete mode 100644 crates/ra_env/src/lib.rs create mode 100644 crates/ra_toolchain/Cargo.toml create mode 100644 crates/ra_toolchain/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 36cff6402..656969c87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -957,14 +957,6 @@ dependencies = [ "test_utils", ] -[[package]] -name = "ra_env" -version = "0.1.0" -dependencies = [ - "anyhow", - "home", -] - [[package]] name = "ra_flycheck" version = "0.1.0" @@ -975,7 +967,7 @@ dependencies = [ "jod-thread", "log", "lsp-types", - "ra_env", + "ra_toolchain", "serde_json", ] @@ -1180,8 +1172,8 @@ dependencies = [ "ra_arena", "ra_cfg", "ra_db", - "ra_env", "ra_proc_macro", + "ra_toolchain", "rustc-hash", "serde", "serde_json", @@ -1213,6 +1205,14 @@ dependencies = [ "text-size", ] +[[package]] +name = "ra_toolchain" +version = "0.1.0" +dependencies = [ + "anyhow", + "home", +] + [[package]] name = "ra_tt" version = "0.1.0" diff --git a/crates/ra_env/Cargo.toml b/crates/ra_env/Cargo.toml deleted file mode 100644 index f0a401be5..000000000 --- a/crates/ra_env/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -edition = "2018" -name = "ra_env" -version = "0.1.0" -authors = ["rust-analyzer developers"] - -[dependencies] -anyhow = "1.0.26" -home = "0.5.3" diff --git a/crates/ra_env/src/lib.rs b/crates/ra_env/src/lib.rs deleted file mode 100644 index 413da1982..000000000 --- a/crates/ra_env/src/lib.rs +++ /dev/null @@ -1,66 +0,0 @@ -//! This crate contains a single public function -//! [`get_path_for_executable`](fn.get_path_for_executable.html). -//! See docs there for more information. - -use anyhow::{bail, Result}; -use std::env; -use std::path::{Path, PathBuf}; -use std::process::Command; - -/// Return a `PathBuf` to use for the given executable. -/// -/// E.g., `get_path_for_executable("cargo")` may return just `cargo` if that -/// gives a valid Cargo executable; or it may return a full path to a valid -/// Cargo. -pub fn get_path_for_executable(executable_name: impl AsRef) -> Result { - // The current implementation checks three places for an executable to use: - // 1) Appropriate environment variable (erroring if this is set but not a usable executable) - // example: for cargo, this checks $CARGO environment variable; for rustc, $RUSTC; etc - // 2) `` - // example: for cargo, this tries just `cargo`, which will succeed if `cargo` is on the $PATH - // 3) `~/.cargo/bin/` - // example: for cargo, this tries ~/.cargo/bin/cargo - // It seems that this is a reasonable place to try for cargo, rustc, and rustup - let executable_name = executable_name.as_ref(); - let env_var = executable_name.to_ascii_uppercase(); - if let Ok(path) = env::var(&env_var) { - if is_valid_executable(&path) { - Ok(path.into()) - } else { - bail!( - "`{}` environment variable points to something that's not a valid executable", - env_var - ) - } - } else { - if is_valid_executable(executable_name) { - return Ok(executable_name.into()); - } - if let Some(mut path) = ::home::home_dir() { - path.push(".cargo"); - path.push("bin"); - path.push(executable_name); - if is_valid_executable(&path) { - return Ok(path); - } - } - // This error message may also be caused by $PATH or $CARGO/$RUSTC/etc not being set correctly - // for VSCode, even if they are set correctly in a terminal. - // On macOS in particular, launching VSCode from terminal with `code ` causes VSCode - // to inherit environment variables including $PATH, $CARGO, $RUSTC, etc from that terminal; - // but launching VSCode from Dock does not inherit environment variables from a terminal. - // For more discussion, see #3118. - bail!( - "Failed to find `{}` executable. Make sure `{}` is in `$PATH`, or set `${}` to point to a valid executable.", - executable_name, executable_name, env_var - ) - } -} - -/// Does the given `Path` point to a usable executable? -/// -/// (assumes the executable takes a `--version` switch and writes to stdout, -/// which is true for `cargo`, `rustc`, and `rustup`) -fn is_valid_executable(p: impl AsRef) -> bool { - Command::new(p.as_ref()).arg("--version").output().is_ok() -} diff --git a/crates/ra_flycheck/Cargo.toml b/crates/ra_flycheck/Cargo.toml index d0f7fb2dc..03e557148 100644 --- a/crates/ra_flycheck/Cargo.toml +++ b/crates/ra_flycheck/Cargo.toml @@ -14,7 +14,7 @@ log = "0.4.8" cargo_metadata = "0.9.1" serde_json = "1.0.48" jod-thread = "0.1.1" -ra_env = { path = "../ra_env" } +ra_toolchain = { path = "../ra_toolchain" } [dev-dependencies] insta = "0.16.0" diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs index d8b727b0e..561657edb 100644 --- a/crates/ra_flycheck/src/lib.rs +++ b/crates/ra_flycheck/src/lib.rs @@ -16,7 +16,7 @@ use lsp_types::{ CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressEnd, WorkDoneProgressReport, }; -use ra_env::get_path_for_executable; +use ra_toolchain::get_path_for_executable; use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic}; diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml index 626478468..a32a5daab 100644 --- a/crates/ra_project_model/Cargo.toml +++ b/crates/ra_project_model/Cargo.toml @@ -16,7 +16,7 @@ cargo_metadata = "0.9.1" ra_arena = { path = "../ra_arena" } ra_cfg = { path = "../ra_cfg" } ra_db = { path = "../ra_db" } -ra_env = { path = "../ra_env" } +ra_toolchain = { path = "../ra_toolchain" } ra_proc_macro = { path = "../ra_proc_macro" } serde = { version = "1.0.106", features = ["derive"] } diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index eb9f33ee8..9683bfcc0 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs @@ -11,7 +11,7 @@ use anyhow::{Context, Result}; use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; use ra_arena::{Arena, Idx}; use ra_db::Edition; -use ra_env::get_path_for_executable; +use ra_toolchain::get_path_for_executable; use rustc_hash::FxHashMap; /// `CargoWorkspace` represents the logical structure of, well, a Cargo diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 88a6ffb2a..4f0b9c77e 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -14,7 +14,7 @@ use std::{ use anyhow::{bail, Context, Result}; use ra_cfg::CfgOptions; use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; -use ra_env::get_path_for_executable; +use ra_toolchain::get_path_for_executable; use rustc_hash::FxHashMap; use serde_json::from_reader; diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs index 11c26ad89..2b628c2a3 100644 --- a/crates/ra_project_model/src/sysroot.rs +++ b/crates/ra_project_model/src/sysroot.rs @@ -1,14 +1,14 @@ //! FIXME: write short doc here -use anyhow::{bail, Context, Result}; use std::{ env, ops, path::{Path, PathBuf}, process::{Command, Output}, }; +use anyhow::{bail, Context, Result}; use ra_arena::{Arena, Idx}; -use ra_env::get_path_for_executable; +use ra_toolchain::get_path_for_executable; #[derive(Default, Debug, Clone)] pub struct Sysroot { diff --git a/crates/ra_toolchain/Cargo.toml b/crates/ra_toolchain/Cargo.toml new file mode 100644 index 000000000..fbad1073e --- /dev/null +++ b/crates/ra_toolchain/Cargo.toml @@ -0,0 +1,9 @@ +[package] +edition = "2018" +name = "ra_toolchain" +version = "0.1.0" +authors = ["rust-analyzer developers"] + +[dependencies] +anyhow = "1.0.26" +home = "0.5.3" diff --git a/crates/ra_toolchain/src/lib.rs b/crates/ra_toolchain/src/lib.rs new file mode 100644 index 000000000..110c92c88 --- /dev/null +++ b/crates/ra_toolchain/src/lib.rs @@ -0,0 +1,68 @@ +//! This crate contains a single public function +//! [`get_path_for_executable`](fn.get_path_for_executable.html). +//! See docs there for more information. +use std::{ + env, + path::{Path, PathBuf}, + process::Command, +}; + +use anyhow::{bail, Result}; + +/// Return a `PathBuf` to use for the given executable. +/// +/// E.g., `get_path_for_executable("cargo")` may return just `cargo` if that +/// gives a valid Cargo executable; or it may return a full path to a valid +/// Cargo. +pub fn get_path_for_executable(executable_name: impl AsRef) -> Result { + // The current implementation checks three places for an executable to use: + // 1) Appropriate environment variable (erroring if this is set but not a usable executable) + // example: for cargo, this checks $CARGO environment variable; for rustc, $RUSTC; etc + // 2) `` + // example: for cargo, this tries just `cargo`, which will succeed if `cargo` is on the $PATH + // 3) `~/.cargo/bin/` + // example: for cargo, this tries ~/.cargo/bin/cargo + // It seems that this is a reasonable place to try for cargo, rustc, and rustup + let executable_name = executable_name.as_ref(); + let env_var = executable_name.to_ascii_uppercase(); + if let Ok(path) = env::var(&env_var) { + if is_valid_executable(&path) { + Ok(path.into()) + } else { + bail!( + "`{}` environment variable points to something that's not a valid executable", + env_var + ) + } + } else { + if is_valid_executable(executable_name) { + return Ok(executable_name.into()); + } + if let Some(mut path) = ::home::home_dir() { + path.push(".cargo"); + path.push("bin"); + path.push(executable_name); + if is_valid_executable(&path) { + return Ok(path); + } + } + // This error message may also be caused by $PATH or $CARGO/$RUSTC/etc not being set correctly + // for VSCode, even if they are set correctly in a terminal. + // On macOS in particular, launching VSCode from terminal with `code ` causes VSCode + // to inherit environment variables including $PATH, $CARGO, $RUSTC, etc from that terminal; + // but launching VSCode from Dock does not inherit environment variables from a terminal. + // For more discussion, see #3118. + bail!( + "Failed to find `{}` executable. Make sure `{}` is in `$PATH`, or set `${}` to point to a valid executable.", + executable_name, executable_name, env_var + ) + } +} + +/// Does the given `Path` point to a usable executable? +/// +/// (assumes the executable takes a `--version` switch and writes to stdout, +/// which is true for `cargo`, `rustc`, and `rustup`) +fn is_valid_executable(p: impl AsRef) -> bool { + Command::new(p.as_ref()).arg("--version").output().is_ok() +} -- cgit v1.2.3 From 7c0409e0c7249fe793b5d05829fcd984d06ec770 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 8 May 2020 12:34:39 +0200 Subject: Cleanup --- crates/ra_toolchain/src/lib.rs | 55 +++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/crates/ra_toolchain/src/lib.rs b/crates/ra_toolchain/src/lib.rs index 110c92c88..afa76619d 100644 --- a/crates/ra_toolchain/src/lib.rs +++ b/crates/ra_toolchain/src/lib.rs @@ -14,7 +14,8 @@ use anyhow::{bail, Result}; /// E.g., `get_path_for_executable("cargo")` may return just `cargo` if that /// gives a valid Cargo executable; or it may return a full path to a valid /// Cargo. -pub fn get_path_for_executable(executable_name: impl AsRef) -> Result { +pub fn get_path_for_executable(executable_name: &'static str) -> Result { + assert!(executable_name == "rustc" || executable_name == "cargo"); // The current implementation checks three places for an executable to use: // 1) Appropriate environment variable (erroring if this is set but not a usable executable) // example: for cargo, this checks $CARGO environment variable; for rustc, $RUSTC; etc @@ -23,46 +24,46 @@ pub fn get_path_for_executable(executable_name: impl AsRef) -> Result` // example: for cargo, this tries ~/.cargo/bin/cargo // It seems that this is a reasonable place to try for cargo, rustc, and rustup - let executable_name = executable_name.as_ref(); let env_var = executable_name.to_ascii_uppercase(); if let Ok(path) = env::var(&env_var) { - if is_valid_executable(&path) { + return if is_valid_executable(&path) { Ok(path.into()) } else { bail!( "`{}` environment variable points to something that's not a valid executable", env_var ) + }; + } + + if is_valid_executable(executable_name) { + return Ok(executable_name.into()); + } + + if let Some(mut path) = home::home_dir() { + path.push(".cargo"); + path.push("bin"); + path.push(executable_name); + if is_valid_executable(&path) { + return Ok(path); } - } else { - if is_valid_executable(executable_name) { - return Ok(executable_name.into()); - } - if let Some(mut path) = ::home::home_dir() { - path.push(".cargo"); - path.push("bin"); - path.push(executable_name); - if is_valid_executable(&path) { - return Ok(path); - } - } - // This error message may also be caused by $PATH or $CARGO/$RUSTC/etc not being set correctly - // for VSCode, even if they are set correctly in a terminal. - // On macOS in particular, launching VSCode from terminal with `code ` causes VSCode - // to inherit environment variables including $PATH, $CARGO, $RUSTC, etc from that terminal; - // but launching VSCode from Dock does not inherit environment variables from a terminal. - // For more discussion, see #3118. - bail!( - "Failed to find `{}` executable. Make sure `{}` is in `$PATH`, or set `${}` to point to a valid executable.", - executable_name, executable_name, env_var - ) } + // This error message may also be caused by $PATH or $CARGO/$RUSTC/etc not being set correctly + // for VSCode, even if they are set correctly in a terminal. + // On macOS in particular, launching VSCode from terminal with `code ` causes VSCode + // to inherit environment variables including $PATH, $CARGO, $RUSTC, etc from that terminal; + // but launching VSCode from Dock does not inherit environment variables from a terminal. + // For more discussion, see #3118. + bail!( + "Failed to find `{}` executable. Make sure `{}` is in `$PATH`, or set `${}` to point to a valid executable.", + executable_name, executable_name, env_var + ) } /// Does the given `Path` point to a usable executable? /// /// (assumes the executable takes a `--version` switch and writes to stdout, /// which is true for `cargo`, `rustc`, and `rustup`) -fn is_valid_executable(p: impl AsRef) -> bool { - Command::new(p.as_ref()).arg("--version").output().is_ok() +fn is_valid_executable(p: &'static str) -> bool { + Command::new(p).arg("--version").output().is_ok() } -- cgit v1.2.3 From ecff5dc141046c5b9e40639657247a05fb9b0344 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 8 May 2020 14:54:29 +0200 Subject: Cleanup --- Cargo.lock | 1 - crates/ra_flycheck/src/lib.rs | 7 ++- crates/ra_project_model/src/cargo_workspace.rs | 5 +- crates/ra_project_model/src/lib.rs | 32 ++++++------ crates/ra_project_model/src/sysroot.rs | 49 ++++-------------- crates/ra_toolchain/Cargo.toml | 1 - crates/ra_toolchain/src/lib.rs | 69 ++++++++++++-------------- 7 files changed, 64 insertions(+), 100 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 656969c87..41855f22e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1209,7 +1209,6 @@ dependencies = [ name = "ra_toolchain" version = "0.1.0" dependencies = [ - "anyhow", "home", ] diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs index 561657edb..68dcee285 100644 --- a/crates/ra_flycheck/src/lib.rs +++ b/crates/ra_flycheck/src/lib.rs @@ -16,7 +16,6 @@ use lsp_types::{ CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressEnd, WorkDoneProgressReport, }; -use ra_toolchain::get_path_for_executable; use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic}; @@ -216,10 +215,10 @@ impl FlycheckThread { let mut cmd = match &self.config { FlycheckConfig::CargoCommand { command, all_targets, all_features, extra_args } => { - let mut cmd = Command::new(get_path_for_executable("cargo").unwrap()); + let mut cmd = Command::new(ra_toolchain::cargo()); cmd.arg(command); - cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]); - cmd.arg(self.workspace_root.join("Cargo.toml")); + cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]) + .arg(self.workspace_root.join("Cargo.toml")); if *all_targets { cmd.arg("--all-targets"); } diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 9683bfcc0..082af4f96 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs @@ -11,7 +11,6 @@ use anyhow::{Context, Result}; use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; use ra_arena::{Arena, Idx}; use ra_db::Edition; -use ra_toolchain::get_path_for_executable; use rustc_hash::FxHashMap; /// `CargoWorkspace` represents the logical structure of, well, a Cargo @@ -147,7 +146,7 @@ impl CargoWorkspace { cargo_features: &CargoConfig, ) -> Result { let mut meta = MetadataCommand::new(); - meta.cargo_path(get_path_for_executable("cargo")?); + meta.cargo_path(ra_toolchain::cargo()); meta.manifest_path(cargo_toml); if cargo_features.all_features { meta.features(CargoOpt::AllFeatures); @@ -289,7 +288,7 @@ pub fn load_extern_resources( cargo_toml: &Path, cargo_features: &CargoConfig, ) -> Result { - let mut cmd = Command::new(get_path_for_executable("cargo")?); + let mut cmd = Command::new(ra_toolchain::cargo()); cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml); if cargo_features.all_features { cmd.arg("--all-features"); diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 4f0b9c77e..5a0a87ed7 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -8,13 +8,12 @@ use std::{ fs::{read_dir, File, ReadDir}, io::{self, BufReader}, path::{Path, PathBuf}, - process::Command, + process::{Command, Output}, }; use anyhow::{bail, Context, Result}; use ra_cfg::CfgOptions; use ra_db::{CrateGraph, CrateName, Edition, Env, ExternSource, ExternSourceId, FileId}; -use ra_toolchain::get_path_for_executable; use rustc_hash::FxHashMap; use serde_json::from_reader; @@ -568,25 +567,18 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { } } - match (|| -> Result { + let rustc_cfgs = || -> Result { // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here. - let mut cmd = Command::new(get_path_for_executable("rustc")?); + let mut cmd = Command::new(ra_toolchain::rustc()); cmd.args(&["--print", "cfg", "-O"]); if let Some(target) = target { cmd.args(&["--target", target.as_str()]); } - let output = cmd.output().context("Failed to get output from rustc --print cfg -O")?; - if !output.status.success() { - bail!( - "rustc --print cfg -O exited with exit code ({})", - output - .status - .code() - .map_or(String::from("no exit code"), |code| format!("{}", code)) - ); - } + let output = output(cmd)?; Ok(String::from_utf8(output.stdout)?) - })() { + }(); + + match rustc_cfgs { Ok(rustc_cfgs) => { for line in rustc_cfgs.lines() { match line.find('=') { @@ -599,8 +591,16 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { } } } - Err(e) => log::error!("failed to get rustc cfgs: {}", e), + Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), } cfg_options } + +fn output(mut cmd: Command) -> Result { + let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?; + if !output.status.success() { + bail!("{:?} failed, {}", cmd, output.status) + } + Ok(output) +} diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs index 2b628c2a3..a8a196e64 100644 --- a/crates/ra_project_model/src/sysroot.rs +++ b/crates/ra_project_model/src/sysroot.rs @@ -3,12 +3,13 @@ use std::{ env, ops, path::{Path, PathBuf}, - process::{Command, Output}, + process::Command, }; -use anyhow::{bail, Context, Result}; +use anyhow::{bail, Result}; use ra_arena::{Arena, Idx}; -use ra_toolchain::get_path_for_executable; + +use crate::output; #[derive(Default, Debug, Clone)] pub struct Sysroot { @@ -85,50 +86,22 @@ impl Sysroot { } } -fn create_command_text(program: &str, args: &[&str]) -> String { - format!("{} {}", program, args.join(" ")) -} - -fn run_command_in_cargo_dir( - cargo_toml: impl AsRef, - program: impl AsRef, - args: &[&str], -) -> Result { - let program = program.as_ref().as_os_str().to_str().expect("Invalid Unicode in path"); - let output = Command::new(program) - .current_dir(cargo_toml.as_ref().parent().unwrap()) - .args(args) - .output() - .context(format!("{} failed", create_command_text(program, args)))?; - if !output.status.success() { - match output.status.code() { - Some(code) => bail!( - "failed to run the command: '{}' exited with code {}", - create_command_text(program, args), - code - ), - None => bail!( - "failed to run the command: '{}' terminated by signal", - create_command_text(program, args) - ), - }; - } - Ok(output) -} - fn get_or_install_rust_src(cargo_toml: &Path) -> Result { if let Ok(path) = env::var("RUST_SRC_PATH") { return Ok(path.into()); } - let rustc = get_path_for_executable("rustc")?; - let rustc_output = run_command_in_cargo_dir(cargo_toml, &rustc, &["--print", "sysroot"])?; + let current_dir = cargo_toml.parent().unwrap(); + let mut rustc = Command::new(ra_toolchain::rustc()); + rustc.current_dir(current_dir).args(&["--print", "sysroot"]); + let rustc_output = output(rustc)?; let stdout = String::from_utf8(rustc_output.stdout)?; let sysroot_path = Path::new(stdout.trim()); let src_path = sysroot_path.join("lib/rustlib/src/rust/src"); if !src_path.exists() { - let rustup = get_path_for_executable("rustup")?; - run_command_in_cargo_dir(cargo_toml, &rustup, &["component", "add", "rust-src"])?; + let mut rustup = Command::new(ra_toolchain::rustup()); + rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]); + let _output = output(rustup)?; } if !src_path.exists() { bail!( diff --git a/crates/ra_toolchain/Cargo.toml b/crates/ra_toolchain/Cargo.toml index fbad1073e..1873fbe16 100644 --- a/crates/ra_toolchain/Cargo.toml +++ b/crates/ra_toolchain/Cargo.toml @@ -5,5 +5,4 @@ version = "0.1.0" authors = ["rust-analyzer developers"] [dependencies] -anyhow = "1.0.26" home = "0.5.3" diff --git a/crates/ra_toolchain/src/lib.rs b/crates/ra_toolchain/src/lib.rs index afa76619d..3c307a0ea 100644 --- a/crates/ra_toolchain/src/lib.rs +++ b/crates/ra_toolchain/src/lib.rs @@ -1,21 +1,26 @@ //! This crate contains a single public function //! [`get_path_for_executable`](fn.get_path_for_executable.html). //! See docs there for more information. -use std::{ - env, - path::{Path, PathBuf}, - process::Command, -}; +use std::{env, iter, path::PathBuf}; -use anyhow::{bail, Result}; +pub fn cargo() -> PathBuf { + get_path_for_executable("cargo") +} + +pub fn rustc() -> PathBuf { + get_path_for_executable("rustc") +} + +pub fn rustup() -> PathBuf { + get_path_for_executable("rustup") +} /// Return a `PathBuf` to use for the given executable. /// /// E.g., `get_path_for_executable("cargo")` may return just `cargo` if that /// gives a valid Cargo executable; or it may return a full path to a valid /// Cargo. -pub fn get_path_for_executable(executable_name: &'static str) -> Result { - assert!(executable_name == "rustc" || executable_name == "cargo"); +fn get_path_for_executable(executable_name: &'static str) -> PathBuf { // The current implementation checks three places for an executable to use: // 1) Appropriate environment variable (erroring if this is set but not a usable executable) // example: for cargo, this checks $CARGO environment variable; for rustc, $RUSTC; etc @@ -25,45 +30,35 @@ pub fn get_path_for_executable(executable_name: &'static str) -> Result // example: for cargo, this tries ~/.cargo/bin/cargo // It seems that this is a reasonable place to try for cargo, rustc, and rustup let env_var = executable_name.to_ascii_uppercase(); - if let Ok(path) = env::var(&env_var) { - return if is_valid_executable(&path) { - Ok(path.into()) - } else { - bail!( - "`{}` environment variable points to something that's not a valid executable", - env_var - ) - }; + if let Some(path) = env::var_os(&env_var) { + return path.into(); } - if is_valid_executable(executable_name) { - return Ok(executable_name.into()); + if lookup_in_path(executable_name) { + return executable_name.into(); } if let Some(mut path) = home::home_dir() { path.push(".cargo"); path.push("bin"); path.push(executable_name); - if is_valid_executable(&path) { - return Ok(path); + if path.is_file() { + return path; } } - // This error message may also be caused by $PATH or $CARGO/$RUSTC/etc not being set correctly - // for VSCode, even if they are set correctly in a terminal. - // On macOS in particular, launching VSCode from terminal with `code ` causes VSCode - // to inherit environment variables including $PATH, $CARGO, $RUSTC, etc from that terminal; - // but launching VSCode from Dock does not inherit environment variables from a terminal. - // For more discussion, see #3118. - bail!( - "Failed to find `{}` executable. Make sure `{}` is in `$PATH`, or set `${}` to point to a valid executable.", - executable_name, executable_name, env_var - ) + executable_name.into() } -/// Does the given `Path` point to a usable executable? -/// -/// (assumes the executable takes a `--version` switch and writes to stdout, -/// which is true for `cargo`, `rustc`, and `rustup`) -fn is_valid_executable(p: &'static str) -> bool { - Command::new(p).arg("--version").output().is_ok() +fn lookup_in_path(exec: &str) -> bool { + let paths = env::var_os("PATH").unwrap_or_default(); + let mut candidates = env::split_paths(&paths).flat_map(|path| { + let candidate = path.join(&exec); + let with_exe = if env::consts::EXE_EXTENSION == "" { + None + } else { + Some(candidate.with_extension(env::consts::EXE_EXTENSION)) + }; + iter::once(candidate).chain(with_exe) + }); + candidates.any(|it| it.is_file()) } -- cgit v1.2.3 From f5177f91ae61e1d812efd6b55a1ec7ab07c7c7e1 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 8 May 2020 15:17:35 +0200 Subject: Fix type of byte literals They're `&[u8; N]`, not `&[u8]` (see #4374). --- crates/ra_hir_ty/src/infer/expr.rs | 4 ++-- crates/ra_hir_ty/src/tests/method_resolution.rs | 13 +++++++------ crates/ra_hir_ty/src/tests/simple.rs | 4 ++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 83f946eee..614c352a0 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -501,8 +501,8 @@ impl<'a> InferenceContext<'a> { } Literal::ByteString(..) => { let byte_type = Ty::simple(TypeCtor::Int(Uncertain::Known(IntTy::u8()))); - let slice_type = Ty::apply_one(TypeCtor::Slice, byte_type); - Ty::apply_one(TypeCtor::Ref(Mutability::Shared), slice_type) + let array_type = Ty::apply_one(TypeCtor::Array, byte_type); + Ty::apply_one(TypeCtor::Ref(Mutability::Shared), array_type) } Literal::Char(..) => Ty::simple(TypeCtor::Char), Literal::Int(_v, ty) => Ty::simple(TypeCtor::Int((*ty).into())), diff --git a/crates/ra_hir_ty/src/tests/method_resolution.rs b/crates/ra_hir_ty/src/tests/method_resolution.rs index ab87f598a..67f964ab5 100644 --- a/crates/ra_hir_ty/src/tests/method_resolution.rs +++ b/crates/ra_hir_ty/src/tests/method_resolution.rs @@ -17,8 +17,8 @@ impl [T] { #[lang = "slice_alloc"] impl [T] {} -fn test() { - <[_]>::foo(b"foo"); +fn test(x: &[u8]) { + <[_]>::foo(x); } "#), @r###" @@ -26,10 +26,11 @@ fn test() { 56..79 '{ ... }': T 66..73 'loop {}': ! 71..73 '{}': () - 133..160 '{ ...o"); }': () - 139..149 '<[_]>::foo': fn foo(&[u8]) -> u8 - 139..157 '<[_]>:..."foo")': u8 - 150..156 'b"foo"': &[u8] + 131..132 'x': &[u8] + 141..163 '{ ...(x); }': () + 147..157 '<[_]>::foo': fn foo(&[u8]) -> u8 + 147..160 '<[_]>::foo(x)': u8 + 158..159 'x': &[u8] "### ); } diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 3d3088965..e17a17900 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs @@ -414,7 +414,7 @@ fn test() { 27..31 '5f32': f32 37..41 '5f64': f64 47..54 '"hello"': &str - 60..68 'b"bytes"': &[u8] + 60..68 'b"bytes"': &[u8; _] 74..77 ''c'': char 83..87 'b'b'': u8 93..97 '3.14': f64 @@ -422,7 +422,7 @@ fn test() { 113..118 'false': bool 124..128 'true': bool 134..202 'r#" ... "#': &str - 208..218 'br#"yolo"#': &[u8] + 208..218 'br#"yolo"#': &[u8; _] "### ); } -- cgit v1.2.3 From fe93675e8ac2b55d051156151489dbe0496efec3 Mon Sep 17 00:00:00 2001 From: Timo Freiberg Date: Sat, 25 Apr 2020 16:57:59 +0200 Subject: New HirDisplay method for displaying sourcecode --- crates/ra_hir/src/code_model.rs | 9 +- crates/ra_hir_ty/src/display.rs | 176 +++++++++++++++++----- crates/ra_hir_ty/src/tests.rs | 17 ++- crates/ra_hir_ty/src/tests/display_source_code.rs | 23 +++ 4 files changed, 184 insertions(+), 41 deletions(-) create mode 100644 crates/ra_hir_ty/src/tests/display_source_code.rs diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 5f480c304..be18c845c 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -22,8 +22,11 @@ use hir_expand::{ MacroDefId, MacroDefKind, }; use hir_ty::{ - autoderef, display::HirFormatter, expr::ExprValidator, method_resolution, ApplicationTy, - Canonical, InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor, + autoderef, + display::{HirDisplayError, HirFormatter}, + expr::ExprValidator, + method_resolution, ApplicationTy, Canonical, InEnvironment, Substs, TraitEnvironment, Ty, + TyDefId, TypeCtor, }; use ra_db::{CrateId, CrateName, Edition, FileId}; use ra_prof::profile; @@ -1319,7 +1322,7 @@ impl Type { } impl HirDisplay for Type { - fn hir_fmt(&self, f: &mut HirFormatter) -> std::fmt::Result { + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { self.ty.value.hir_fmt(f) } } diff --git a/crates/ra_hir_ty/src/display.rs b/crates/ra_hir_ty/src/display.rs index d03bbd5a7..f5edaea8c 100644 --- a/crates/ra_hir_ty/src/display.rs +++ b/crates/ra_hir_ty/src/display.rs @@ -6,28 +6,42 @@ use crate::{ db::HirDatabase, utils::generics, ApplicationTy, CallableDef, FnSig, GenericPredicate, Obligation, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, }; -use hir_def::{generics::TypeParamProvenance, AdtId, AssocContainerId, Lookup}; +use hir_def::{ + find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, AssocContainerId, + Lookup, ModuleId, +}; use hir_expand::name::Name; -pub struct HirFormatter<'a, 'b> { +pub struct HirFormatter<'a> { pub db: &'a dyn HirDatabase, - fmt: &'a mut fmt::Formatter<'b>, + fmt: &'a mut dyn fmt::Write, buf: String, curr_size: usize, pub(crate) max_size: Option, omit_verbose_types: bool, + display_target: DisplayTarget, } pub trait HirDisplay { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result; + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError>; + /// Returns a `Display`able type that is human-readable. + /// Use this for showing types to the user (e.g. diagnostics) fn display<'a>(&'a self, db: &'a dyn HirDatabase) -> HirDisplayWrapper<'a, Self> where Self: Sized, { - HirDisplayWrapper(db, self, None, false) + HirDisplayWrapper { + db, + t: self, + max_size: None, + omit_verbose_types: false, + display_target: DisplayTarget::Diagnostics, + } } + /// Returns a `Display`able type that is human-readable and tries to be succinct. + /// Use this for showing types to the user where space is constrained (e.g. doc popups) fn display_truncated<'a>( &'a self, db: &'a dyn HirDatabase, @@ -36,16 +50,46 @@ pub trait HirDisplay { where Self: Sized, { - HirDisplayWrapper(db, self, max_size, true) + HirDisplayWrapper { + db, + t: self, + max_size, + omit_verbose_types: true, + display_target: DisplayTarget::Diagnostics, + } + } + + /// Returns a String representation of `self` that can be inserted into the given module. + /// Use this when generating code (e.g. assists) + fn display_source_code<'a>( + &'a self, + db: &'a dyn HirDatabase, + module_id: ModuleId, + ) -> Result { + let mut result = String::new(); + match self.hir_fmt(&mut HirFormatter { + db, + fmt: &mut result, + buf: String::with_capacity(20), + curr_size: 0, + max_size: None, + omit_verbose_types: false, + display_target: DisplayTarget::SourceCode { module_id }, + }) { + Ok(()) => {} + Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"), + Err(HirDisplayError::DisplaySourceCodeError(e)) => return Err(e), + }; + Ok(result) } } -impl<'a, 'b> HirFormatter<'a, 'b> { +impl<'a> HirFormatter<'a> { pub fn write_joined( &mut self, iter: impl IntoIterator, sep: &str, - ) -> fmt::Result { + ) -> Result<(), HirDisplayError> { let mut first = true; for e in iter { if !first { @@ -58,14 +102,14 @@ impl<'a, 'b> HirFormatter<'a, 'b> { } /// This allows using the `write!` macro directly with a `HirFormatter`. - pub fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result { + pub fn write_fmt(&mut self, args: fmt::Arguments) -> Result<(), HirDisplayError> { // We write to a buffer first to track output size self.buf.clear(); fmt::write(&mut self.buf, args)?; self.curr_size += self.buf.len(); // Then we write to the internal formatter from the buffer - self.fmt.write_str(&self.buf) + self.fmt.write_str(&self.buf).map_err(HirDisplayError::from) } pub fn should_truncate(&self) -> bool { @@ -81,34 +125,76 @@ impl<'a, 'b> HirFormatter<'a, 'b> { } } -pub struct HirDisplayWrapper<'a, T>(&'a dyn HirDatabase, &'a T, Option, bool); +#[derive(Clone, Copy)] +enum DisplayTarget { + /// Display types for inlays, doc popups, autocompletion, etc... + /// Showing `{unknown}` or not qualifying paths is fine here. + /// There's no reason for this to fail. + Diagnostics, + /// Display types for inserting them in source files. + /// The generated code should compile, so paths need to be qualified. + SourceCode { module_id: ModuleId }, +} + +#[derive(Debug)] +pub enum DisplaySourceCodeError { + PathNotFound, +} + +pub enum HirDisplayError { + /// Errors that can occur when generating source code + DisplaySourceCodeError(DisplaySourceCodeError), + /// `FmtError` is required to be compatible with std::fmt::Display + FmtError, +} +impl From for HirDisplayError { + fn from(_: fmt::Error) -> Self { + Self::FmtError + } +} + +pub struct HirDisplayWrapper<'a, T> { + db: &'a dyn HirDatabase, + t: &'a T, + max_size: Option, + omit_verbose_types: bool, + display_target: DisplayTarget, +} impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T> where T: HirDisplay, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.1.hir_fmt(&mut HirFormatter { - db: self.0, + match self.t.hir_fmt(&mut HirFormatter { + db: self.db, fmt: f, buf: String::with_capacity(20), curr_size: 0, - max_size: self.2, - omit_verbose_types: self.3, - }) + max_size: self.max_size, + omit_verbose_types: self.omit_verbose_types, + display_target: self.display_target, + }) { + Ok(()) => Ok(()), + Err(HirDisplayError::FmtError) => Err(fmt::Error), + Err(HirDisplayError::DisplaySourceCodeError(_)) => { + // This should never happen + panic!("HirDisplay failed when calling Display::fmt!") + } + } } } const TYPE_HINT_TRUNCATION: &str = "…"; impl HirDisplay for &Ty { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { HirDisplay::hir_fmt(*self, f) } } impl HirDisplay for ApplicationTy { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { if f.should_truncate() { return write!(f, "{}", TYPE_HINT_TRUNCATION); } @@ -191,12 +277,30 @@ impl HirDisplay for ApplicationTy { } } TypeCtor::Adt(def_id) => { - let name = match def_id { - AdtId::StructId(it) => f.db.struct_data(it).name.clone(), - AdtId::UnionId(it) => f.db.union_data(it).name.clone(), - AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), - }; - write!(f, "{}", name)?; + match f.display_target { + DisplayTarget::Diagnostics => { + let name = match def_id { + AdtId::StructId(it) => f.db.struct_data(it).name.clone(), + AdtId::UnionId(it) => f.db.union_data(it).name.clone(), + AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), + }; + write!(f, "{}", name)?; + } + DisplayTarget::SourceCode { module_id } => { + if let Some(path) = find_path::find_path( + f.db.upcast(), + ItemInNs::Types(def_id.into()), + module_id, + ) { + write!(f, "{}", path)?; + } else { + return Err(HirDisplayError::DisplaySourceCodeError( + DisplaySourceCodeError::PathNotFound, + )); + } + } + } + if self.parameters.len() > 0 { let mut non_default_parameters = Vec::with_capacity(self.parameters.len()); let parameters_to_write = if f.omit_verbose_types() { @@ -269,7 +373,7 @@ impl HirDisplay for ApplicationTy { } impl HirDisplay for ProjectionTy { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { if f.should_truncate() { return write!(f, "{}", TYPE_HINT_TRUNCATION); } @@ -287,7 +391,7 @@ impl HirDisplay for ProjectionTy { } impl HirDisplay for Ty { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { if f.should_truncate() { return write!(f, "{}", TYPE_HINT_TRUNCATION); } @@ -332,7 +436,7 @@ impl HirDisplay for Ty { fn write_bounds_like_dyn_trait( predicates: &[GenericPredicate], f: &mut HirFormatter, -) -> fmt::Result { +) -> Result<(), HirDisplayError> { // Note: This code is written to produce nice results (i.e. // corresponding to surface Rust) for types that can occur in // actual Rust. It will have weird results if the predicates @@ -394,7 +498,7 @@ fn write_bounds_like_dyn_trait( } impl TraitRef { - fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> fmt::Result { + fn hir_fmt_ext(&self, f: &mut HirFormatter, use_as: bool) -> Result<(), HirDisplayError> { if f.should_truncate() { return write!(f, "{}", TYPE_HINT_TRUNCATION); } @@ -416,19 +520,19 @@ impl TraitRef { } impl HirDisplay for TraitRef { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { self.hir_fmt_ext(f, false) } } impl HirDisplay for &GenericPredicate { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { HirDisplay::hir_fmt(*self, f) } } impl HirDisplay for GenericPredicate { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { if f.should_truncate() { return write!(f, "{}", TYPE_HINT_TRUNCATION); } @@ -452,15 +556,15 @@ impl HirDisplay for GenericPredicate { } impl HirDisplay for Obligation { - fn hir_fmt(&self, f: &mut HirFormatter) -> fmt::Result { - match self { - Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db)), + fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { + Ok(match self { + Obligation::Trait(tr) => write!(f, "Implements({})", tr.display(f.db))?, Obligation::Projection(proj) => write!( f, "Normalize({} => {})", proj.projection_ty.display(f.db), proj.ty.display(f.db) - ), - } + )?, + }) } } diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index d60732e19..623d00010 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs @@ -6,6 +6,7 @@ mod patterns; mod traits; mod method_resolution; mod macros; +mod display_source_code; use std::sync::Arc; @@ -16,7 +17,7 @@ use hir_def::{ item_scope::ItemScope, keys, nameres::CrateDefMap, - AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, + AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId, ModuleId, }; use hir_expand::{db::AstDatabase, InFile}; use insta::assert_snapshot; @@ -37,6 +38,18 @@ use crate::{ // update the snapshots. fn type_at_pos(db: &TestDB, pos: FilePosition) -> String { + type_at_pos_displayed(db, pos, |ty, _| ty.display(db).to_string()) +} + +fn displayed_source_at_pos(db: &TestDB, pos: FilePosition) -> String { + type_at_pos_displayed(db, pos, |ty, module_id| ty.display_source_code(db, module_id).unwrap()) +} + +fn type_at_pos_displayed( + db: &TestDB, + pos: FilePosition, + display_fn: impl FnOnce(&Ty, ModuleId) -> String, +) -> String { let file = db.parse(pos.file_id).ok().unwrap(); let expr = algo::find_node_at_offset::(file.syntax(), pos.offset).unwrap(); let fn_def = expr.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); @@ -49,7 +62,7 @@ fn type_at_pos(db: &TestDB, pos: FilePosition) -> String { if let Some(expr_id) = source_map.node_expr(InFile::new(pos.file_id.into(), &expr)) { let infer = db.infer(func.into()); let ty = &infer[expr_id]; - return ty.display(db).to_string(); + return display_fn(ty, module); } panic!("Can't find expression") } diff --git a/crates/ra_hir_ty/src/tests/display_source_code.rs b/crates/ra_hir_ty/src/tests/display_source_code.rs new file mode 100644 index 000000000..ca1748615 --- /dev/null +++ b/crates/ra_hir_ty/src/tests/display_source_code.rs @@ -0,0 +1,23 @@ +use super::displayed_source_at_pos; +use crate::test_db::TestDB; +use ra_db::fixture::WithFixture; + +#[test] +fn qualify_path_to_submodule() { + let (db, pos) = TestDB::with_position( + r#" +//- /main.rs + +mod foo { + pub struct Foo; +} + +fn bar() { + let foo: foo::Foo = foo::Foo; + foo<|> +} + +"#, + ); + assert_eq!("foo::Foo", displayed_source_at_pos(&db, pos)); +} -- cgit v1.2.3 From 64e6b8200bfceea92b0dcaab3e533a9152994e78 Mon Sep 17 00:00:00 2001 From: Timo Freiberg Date: Sat, 25 Apr 2020 16:58:28 +0200 Subject: Use new HirDisplay variant in add_function assist --- crates/ra_assists/src/handlers/add_function.rs | 174 ++++++++++++++++--------- 1 file changed, 116 insertions(+), 58 deletions(-) diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index 6b5616aa9..95faf0f4f 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs @@ -43,16 +43,12 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> return None; } - let target_module = if let Some(qualifier) = path.qualifier() { - if let Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) = - ctx.sema.resolve_path(&qualifier) - { - Some(module.definition_source(ctx.sema.db)) - } else { - return None; - } - } else { - None + let target_module = match path.qualifier() { + Some(qualifier) => match ctx.sema.resolve_path(&qualifier) { + Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) => Some(module), + _ => return None, + }, + None => None, }; let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; @@ -83,25 +79,29 @@ struct FunctionBuilder { } impl FunctionBuilder { - /// Prepares a generated function that matches `call` in `generate_in` - /// (or as close to `call` as possible, if `generate_in` is `None`) + /// Prepares a generated function that matches `call`. + /// The function is generated in `target_module` or next to `call` fn from_call( ctx: &AssistContext, call: &ast::CallExpr, path: &ast::Path, - target_module: Option>, + target_module: Option, ) -> Option { - let needs_pub = target_module.is_some(); let mut file = ctx.frange.file_id; - let target = if let Some(target_module) = target_module { - let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, target_module)?; - file = in_file; - target - } else { - next_space_for_fn_after_call_site(&call)? + let target = match &target_module { + Some(target_module) => { + let module_source = target_module.definition_source(ctx.db); + let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, &module_source)?; + file = in_file; + target + } + None => next_space_for_fn_after_call_site(&call)?, }; + let needs_pub = target_module.is_some(); + let target_module = target_module.or_else(|| ctx.sema.scope(target.syntax()).module())?; let fn_name = fn_name(&path)?; - let (type_params, params) = fn_args(ctx, &call)?; + let (type_params, params) = fn_args(ctx, target_module, &call)?; + Some(Self { target, fn_name, type_params, params, file, needs_pub }) } @@ -144,6 +144,15 @@ enum GeneratedFunctionTarget { InEmptyItemList(ast::ItemList), } +impl GeneratedFunctionTarget { + fn syntax(&self) -> &SyntaxNode { + match self { + GeneratedFunctionTarget::BehindItem(it) => it, + GeneratedFunctionTarget::InEmptyItemList(it) => it.syntax(), + } + } +} + fn fn_name(call: &ast::Path) -> Option { let name = call.segment()?.syntax().to_string(); Some(ast::make::name(&name)) @@ -152,17 +161,17 @@ fn fn_name(call: &ast::Path) -> Option { /// Computes the type variables and arguments required for the generated function fn fn_args( ctx: &AssistContext, + target_module: hir::Module, call: &ast::CallExpr, ) -> Option<(Option, ast::ParamList)> { let mut arg_names = Vec::new(); let mut arg_types = Vec::new(); for arg in call.arg_list()?.args() { - let arg_name = match fn_arg_name(&arg) { + arg_names.push(match fn_arg_name(&arg) { Some(name) => name, None => String::from("arg"), - }; - arg_names.push(arg_name); - arg_types.push(match fn_arg_type(ctx, &arg) { + }); + arg_types.push(match fn_arg_type(ctx, target_module, &arg) { Some(ty) => ty, None => String::from("()"), }); @@ -218,12 +227,21 @@ fn fn_arg_name(fn_arg: &ast::Expr) -> Option { } } -fn fn_arg_type(ctx: &AssistContext, fn_arg: &ast::Expr) -> Option { +fn fn_arg_type( + ctx: &AssistContext, + target_module: hir::Module, + fn_arg: &ast::Expr, +) -> Option { let ty = ctx.sema.type_of_expr(fn_arg)?; if ty.is_unknown() { return None; } - Some(ty.display(ctx.sema.db).to_string()) + + if let Ok(rendered) = ty.display_source_code(ctx.db, target_module.into()) { + Some(rendered) + } else { + None + } } /// Returns the position inside the current mod or file @@ -252,10 +270,10 @@ fn next_space_for_fn_after_call_site(expr: &ast::CallExpr) -> Option, + module_source: &hir::InFile, ) -> Option<(FileId, GeneratedFunctionTarget)> { - let file = module.file_id.original_file(db); - let assist_item = match module.value { + let file = module_source.file_id.original_file(db); + let assist_item = match &module_source.value { hir::ModuleSource::SourceFile(it) => { if let Some(last_item) = it.items().last() { GeneratedFunctionTarget::BehindItem(last_item.syntax().clone()) @@ -599,8 +617,33 @@ fn bar(foo: impl Foo) { } #[test] - #[ignore] - // FIXME print paths properly to make this test pass + fn borrowed_arg() { + check_assist( + add_function, + r" +struct Baz; +fn baz() -> Baz { todo!() } + +fn foo() { + bar<|>(&baz()) +} +", + r" +struct Baz; +fn baz() -> Baz { todo!() } + +fn foo() { + bar(&baz()) +} + +fn bar(baz: &Baz) { + <|>todo!() +} +", + ) + } + + #[test] fn add_function_with_qualified_path_arg() { check_assist( add_function, @@ -609,10 +652,8 @@ mod Baz { pub struct Bof; pub fn baz() -> Bof { Bof } } -mod Foo { - fn foo() { - <|>bar(super::Baz::baz()) - } +fn foo() { + <|>bar(Baz::baz()) } ", r" @@ -620,14 +661,12 @@ mod Baz { pub struct Bof; pub fn baz() -> Bof { Bof } } -mod Foo { - fn foo() { - bar(super::Baz::baz()) - } +fn foo() { + bar(Baz::baz()) +} - fn bar(baz: super::Baz::Bof) { - <|>todo!() - } +fn bar(baz: Baz::Bof) { + <|>todo!() } ", ) @@ -808,6 +847,40 @@ fn foo() { ) } + #[test] + #[ignore] + // Ignored until local imports are supported. + // See https://github.com/rust-analyzer/rust-analyzer/issues/1165 + fn qualified_path_uses_correct_scope() { + check_assist( + add_function, + " +mod foo { + pub struct Foo; +} +fn bar() { + use foo::Foo; + let foo = Foo; + baz<|>(foo) +} +", + " +mod foo { + pub struct Foo; +} +fn bar() { + use foo::Foo; + let foo = Foo; + baz(foo) +} + +fn baz(foo: foo::Foo) { + <|>todo!() +} +", + ) + } + #[test] fn add_function_in_module_containing_other_items() { check_assist( @@ -919,21 +992,6 @@ fn bar(baz: ()) {} ) } - #[test] - fn add_function_not_applicable_if_function_path_not_singleton() { - // In the future this assist could be extended to generate functions - // if the path is in the same crate (or even the same workspace). - // For the beginning, I think this is fine. - check_assist_not_applicable( - add_function, - r" -fn foo() { - other_crate::bar<|>(); -} - ", - ) - } - #[test] #[ignore] fn create_method_with_no_args() { -- cgit v1.2.3 From fe7bf993aa8d64668707e348f2ea69918cfda9a4 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 8 May 2020 17:36:11 +0200 Subject: Implement better handling of divergence Divergence here means that for some reason, the end of a block will not be reached. We tried to model this just using the never type, but that doesn't work fully (e.g. in `let x = { loop {}; "foo" };` x should still have type `&str`); so this introduces a `diverges` flag that the type checker keeps track of, like rustc does. --- crates/ra_hir_ty/src/infer.rs | 40 ++++++++++++ crates/ra_hir_ty/src/infer/expr.rs | 53 +++++++++++---- crates/ra_hir_ty/src/lib.rs | 7 ++ crates/ra_hir_ty/src/tests/coercion.rs | 4 +- crates/ra_hir_ty/src/tests/macros.rs | 2 +- crates/ra_hir_ty/src/tests/never_type.rs | 107 ++++++++++++++++++++++++++++++- crates/ra_hir_ty/src/tests/simple.rs | 10 +-- 7 files changed, 200 insertions(+), 23 deletions(-) diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index bd4ef69a0..d3a066268 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -210,6 +210,7 @@ struct InferenceContext<'a> { /// closures, but currently this is the only field that will change there, /// so it doesn't make sense. return_ty: Ty, + diverges: Diverges, } impl<'a> InferenceContext<'a> { @@ -224,6 +225,7 @@ impl<'a> InferenceContext<'a> { owner, body: db.body(owner), resolver, + diverges: Diverges::Maybe, } } @@ -666,6 +668,44 @@ impl Expectation { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +enum Diverges { + Maybe, + Always, +} + +impl Diverges { + fn is_always(self) -> bool { + self == Diverges::Always + } +} + +impl std::ops::BitAnd for Diverges { + type Output = Self; + fn bitand(self, other: Self) -> Self { + std::cmp::min(self, other) + } +} + +impl std::ops::BitOr for Diverges { + type Output = Self; + fn bitor(self, other: Self) -> Self { + std::cmp::max(self, other) + } +} + +impl std::ops::BitAndAssign for Diverges { + fn bitand_assign(&mut self, other: Self) { + *self = *self & other; + } +} + +impl std::ops::BitOrAssign for Diverges { + fn bitor_assign(&mut self, other: Self) { + *self = *self | other; + } +} + mod diagnostics { use hir_def::{expr::ExprId, FunctionId}; use hir_expand::diagnostics::DiagnosticSink; diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 614c352a0..f2f9883b2 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -1,7 +1,7 @@ //! Type inference for expressions. use std::iter::{repeat, repeat_with}; -use std::sync::Arc; +use std::{mem, sync::Arc}; use hir_def::{ builtin_type::Signedness, @@ -21,11 +21,15 @@ use crate::{ Ty, TypeCtor, Uncertain, }; -use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch}; +use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch, Diverges}; impl<'a> InferenceContext<'a> { pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { let ty = self.infer_expr_inner(tgt_expr, expected); + if ty.is_never() { + // Any expression that produces a value of type `!` must have diverged + self.diverges = Diverges::Always; + } let could_unify = self.unify(&ty, &expected.ty); if !could_unify { self.result.type_mismatches.insert( @@ -64,11 +68,18 @@ impl<'a> InferenceContext<'a> { // if let is desugared to match, so this is always simple if self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); + let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); + let mut both_arms_diverge = Diverges::Always; + let then_ty = self.infer_expr_inner(*then_branch, &expected); + both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe); let else_ty = match else_branch { Some(else_branch) => self.infer_expr_inner(*else_branch, &expected), None => Ty::unit(), }; + both_arms_diverge &= self.diverges; + + self.diverges = condition_diverges | both_arms_diverge; self.coerce_merge_branch(&then_ty, &else_ty) } @@ -132,10 +143,12 @@ impl<'a> InferenceContext<'a> { // infer the body. self.coerce(&closure_ty, &expected.ty); - let prev_ret_ty = std::mem::replace(&mut self.return_ty, ret_ty.clone()); + let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe); + let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone()); self.infer_expr_coerce(*body, &Expectation::has_type(ret_ty)); + self.diverges = prev_diverges; self.return_ty = prev_ret_ty; closure_ty @@ -165,7 +178,11 @@ impl<'a> InferenceContext<'a> { self.table.new_type_var() }; + let matchee_diverges = self.diverges; + let mut all_arms_diverge = Diverges::Always; + for arm in arms { + self.diverges = Diverges::Maybe; let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default()); if let Some(guard_expr) = arm.guard { self.infer_expr( @@ -175,9 +192,12 @@ impl<'a> InferenceContext<'a> { } let arm_ty = self.infer_expr_inner(arm.expr, &expected); + all_arms_diverge &= self.diverges; result_ty = self.coerce_merge_branch(&result_ty, &arm_ty); } + self.diverges = matchee_diverges | all_arms_diverge; + result_ty } Expr::Path(p) => { @@ -522,7 +542,6 @@ impl<'a> InferenceContext<'a> { tail: Option, expected: &Expectation, ) -> Ty { - let mut diverges = false; for stmt in statements { match stmt { Statement::Let { pat, type_ref, initializer } => { @@ -544,9 +563,7 @@ impl<'a> InferenceContext<'a> { self.infer_pat(*pat, &ty, BindingMode::default()); } Statement::Expr(expr) => { - if let ty_app!(TypeCtor::Never) = self.infer_expr(*expr, &Expectation::none()) { - diverges = true; - } + self.infer_expr(*expr, &Expectation::none()); } } } @@ -554,14 +571,22 @@ impl<'a> InferenceContext<'a> { let ty = if let Some(expr) = tail { self.infer_expr_coerce(expr, expected) } else { - self.coerce(&Ty::unit(), expected.coercion_target()); - Ty::unit() + // Citing rustc: if there is no explicit tail expression, + // that is typically equivalent to a tail expression + // of `()` -- except if the block diverges. In that + // case, there is no value supplied from the tail + // expression (assuming there are no other breaks, + // this implies that the type of the block will be + // `!`). + if self.diverges.is_always() { + // we don't even make an attempt at coercion + self.table.new_maybe_never_type_var() + } else { + self.coerce(&Ty::unit(), expected.coercion_target()); + Ty::unit() + } }; - if diverges { - Ty::simple(TypeCtor::Never) - } else { - ty - } + ty } fn infer_method_call( diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index a6f56c661..ac0ef1bfe 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -730,6 +730,13 @@ impl Ty { } } + pub fn is_never(&self) -> bool { + match self { + Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }) => true, + _ => false, + } + } + /// If this is a `dyn Trait` type, this returns the `Trait` part. pub fn dyn_trait_ref(&self) -> Option<&TraitRef> { match self { diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index e6fb3e123..0c3a833bd 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs @@ -384,7 +384,7 @@ fn foo() -> u32 { } "#, true), @r###" - 17..40 '{ ...own; }': ! + 17..40 '{ ...own; }': u32 23..37 'return unknown': ! 30..37 'unknown': u32 "### @@ -514,7 +514,7 @@ fn foo() { 27..103 '{ ... }': &u32 37..82 'if tru... }': () 40..44 'true': bool - 45..82 '{ ... }': ! + 45..82 '{ ... }': () 59..71 'return &1u32': ! 66..71 '&1u32': &u32 67..71 '1u32': u32 diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs index 07398ddcc..4c6099aa2 100644 --- a/crates/ra_hir_ty/src/tests/macros.rs +++ b/crates/ra_hir_ty/src/tests/macros.rs @@ -197,7 +197,7 @@ fn spam() { !0..6 '1isize': isize !0..6 '1isize': isize !0..6 '1isize': isize - 54..457 '{ ...!(); }': ! + 54..457 '{ ...!(); }': () 88..109 'spam!(...am!())': {unknown} 115..134 'for _ ...!() {}': () 119..120 '_': {unknown} diff --git a/crates/ra_hir_ty/src/tests/never_type.rs b/crates/ra_hir_ty/src/tests/never_type.rs index a77209480..1721f97c5 100644 --- a/crates/ra_hir_ty/src/tests/never_type.rs +++ b/crates/ra_hir_ty/src/tests/never_type.rs @@ -1,4 +1,6 @@ -use super::type_at; +use insta::assert_snapshot; + +use super::{infer_with_mismatches, type_at}; #[test] fn infer_never1() { @@ -261,3 +263,106 @@ fn test(a: i32) { ); assert_eq!(t, "f64"); } + +#[test] +fn diverging_expression_1() { + let t = infer_with_mismatches( + r#" +//- /main.rs +fn test1() { + let x: u32 = return; +} +fn test2() { + let x: u32 = { return; }; +} +fn test3() { + let x: u32 = loop {}; +} +fn test4() { + let x: u32 = { loop {} }; +} +fn test5() { + let x: u32 = { if true { loop {}; } else { loop {}; } }; +} +fn test6() { + let x: u32 = { let y: u32 = { loop {}; }; }; +} +"#, + true, + ); + assert_snapshot!(t, @r###" + 25..53 '{ ...urn; }': () + 35..36 'x': u32 + 44..50 'return': ! + 65..98 '{ ...; }; }': () + 75..76 'x': u32 + 84..95 '{ return; }': u32 + 86..92 'return': ! + 110..139 '{ ... {}; }': () + 120..121 'x': u32 + 129..136 'loop {}': ! + 134..136 '{}': () + 151..184 '{ ...} }; }': () + 161..162 'x': u32 + 170..181 '{ loop {} }': u32 + 172..179 'loop {}': ! + 177..179 '{}': () + 196..260 '{ ...} }; }': () + 206..207 'x': u32 + 215..257 '{ if t...}; } }': u32 + 217..255 'if tru... {}; }': u32 + 220..224 'true': bool + 225..237 '{ loop {}; }': u32 + 227..234 'loop {}': ! + 232..234 '{}': () + 243..255 '{ loop {}; }': u32 + 245..252 'loop {}': ! + 250..252 '{}': () + 272..324 '{ ...; }; }': () + 282..283 'x': u32 + 291..321 '{ let ...; }; }': u32 + 297..298 'y': u32 + 306..318 '{ loop {}; }': u32 + 308..315 'loop {}': ! + 313..315 '{}': () + "###); +} + +#[test] +fn diverging_expression_2() { + let t = infer_with_mismatches( + r#" +//- /main.rs +fn test1() { + // should give type mismatch + let x: u32 = { loop {}; "foo" }; +} +"#, + true, + ); + assert_snapshot!(t, @r###" + 25..98 '{ ..." }; }': () + 68..69 'x': u32 + 77..95 '{ loop...foo" }': &str + 79..86 'loop {}': ! + 84..86 '{}': () + 88..93 '"foo"': &str + 77..95: expected u32, got &str + 88..93: expected u32, got &str + "###); +} + +#[test] +fn diverging_expression_3_break() { + let t = infer_with_mismatches( + r#" +//- /main.rs +fn test1() { + // should give type mismatch + let x: u32 = { loop { break; } }; +} +"#, + true, + ); + assert_snapshot!(t, @r###""###); +} diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index e17a17900..3820175f6 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs @@ -179,7 +179,7 @@ fn test(a: u32, b: isize, c: !, d: &str) { 17..18 'b': isize 27..28 'c': ! 33..34 'd': &str - 42..121 '{ ...f32; }': ! + 42..121 '{ ...f32; }': () 48..49 'a': u32 55..56 'b': isize 62..63 'c': ! @@ -935,7 +935,7 @@ fn foo() { 29..33 'true': bool 34..51 '{ ... }': i32 44..45 '1': i32 - 57..80 '{ ... }': ! + 57..80 '{ ... }': i32 67..73 'return': ! 90..93 '_x2': i32 96..149 'if tru... }': i32 @@ -951,7 +951,7 @@ fn foo() { 186..190 'true': bool 194..195 '3': i32 205..206 '_': bool - 210..241 '{ ... }': ! + 210..241 '{ ... }': i32 224..230 'return': ! 257..260 '_x4': i32 263..320 'match ... }': i32 @@ -1687,7 +1687,7 @@ fn foo() -> u32 { 17..59 '{ ...; }; }': () 27..28 'x': || -> usize 31..56 '|| -> ...n 1; }': || -> usize - 43..56 '{ return 1; }': ! + 43..56 '{ return 1; }': usize 45..53 'return 1': ! 52..53 '1': usize "### @@ -1706,7 +1706,7 @@ fn foo() -> u32 { 17..48 '{ ...; }; }': () 27..28 'x': || -> () 31..45 '|| { return; }': || -> () - 34..45 '{ return; }': ! + 34..45 '{ return; }': () 36..42 'return': ! "### ); -- cgit v1.2.3 From 1be6320ea6cf7830195f80681fa0f43cc340da7e Mon Sep 17 00:00:00 2001 From: vsrs <62505555+vsrs@users.noreply.github.com> Date: Fri, 8 May 2020 19:22:26 +0300 Subject: "rust-analyzer.debug.openDebugPane" Co-authored-by: bjorn3 --- editors/code/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editors/code/package.json b/editors/code/package.json index e8e9598f6..e750412a7 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -419,7 +419,7 @@ "/rustc/": "${env:USERPROFILE}/.rustup/toolchains//lib/rustlib/src/rust" } }, - "rust-analyzer.debug.openUpDebugPane": { + "rust-analyzer.debug.openDebugPane": { "description": "Whether to open up the Debug Pane on debugging start.", "type": "boolean", "default": false -- cgit v1.2.3 From b60970fd2050f844e3e52fcfd1724a8c527a11af Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 8 May 2020 17:59:58 +0200 Subject: Handle break somewhat better Still no break-with-value or labels, but at least we know that `loop { break; }` doesn't diverge. --- crates/ra_hir_ty/src/infer.rs | 7 ++++ crates/ra_hir_ty/src/infer/expr.rs | 29 ++++++++++++- crates/ra_hir_ty/src/tests/never_type.rs | 72 +++++++++++++++++++++++++++++++- 3 files changed, 105 insertions(+), 3 deletions(-) diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index d3a066268..413904518 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -211,6 +211,12 @@ struct InferenceContext<'a> { /// so it doesn't make sense. return_ty: Ty, diverges: Diverges, + breakables: Vec, +} + +#[derive(Clone, Debug)] +struct BreakableContext { + pub may_break: bool, } impl<'a> InferenceContext<'a> { @@ -226,6 +232,7 @@ impl<'a> InferenceContext<'a> { body: db.body(owner), resolver, diverges: Diverges::Maybe, + breakables: Vec::new(), } } diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index f2f9883b2..9cac0c787 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -21,7 +21,10 @@ use crate::{ Ty, TypeCtor, Uncertain, }; -use super::{BindingMode, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch, Diverges}; +use super::{ + BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, + TypeMismatch, +}; impl<'a> InferenceContext<'a> { pub(super) fn infer_expr(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { @@ -90,24 +93,43 @@ impl<'a> InferenceContext<'a> { Ty::Unknown } Expr::Loop { body } => { + self.breakables.push(BreakableContext { may_break: false }); self.infer_expr(*body, &Expectation::has_type(Ty::unit())); + + let ctxt = self.breakables.pop().expect("breakable stack broken"); + if ctxt.may_break { + self.diverges = Diverges::Maybe; + } // FIXME handle break with value - Ty::simple(TypeCtor::Never) + if ctxt.may_break { + Ty::unit() + } else { + Ty::simple(TypeCtor::Never) + } } Expr::While { condition, body } => { + self.breakables.push(BreakableContext { may_break: false }); // while let is desugared to a match loop, so this is always simple while self.infer_expr(*condition, &Expectation::has_type(Ty::simple(TypeCtor::Bool))); self.infer_expr(*body, &Expectation::has_type(Ty::unit())); + let _ctxt = self.breakables.pop().expect("breakable stack broken"); + // the body may not run, so it diverging doesn't mean we diverge + self.diverges = Diverges::Maybe; Ty::unit() } Expr::For { iterable, body, pat } => { let iterable_ty = self.infer_expr(*iterable, &Expectation::none()); + self.breakables.push(BreakableContext { may_break: false }); let pat_ty = self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item()); self.infer_pat(*pat, &pat_ty, BindingMode::default()); + self.infer_expr(*body, &Expectation::has_type(Ty::unit())); + let _ctxt = self.breakables.pop().expect("breakable stack broken"); + // the body may not run, so it diverging doesn't mean we diverge + self.diverges = Diverges::Maybe; Ty::unit() } Expr::Lambda { body, args, ret_type, arg_types } => { @@ -211,6 +233,9 @@ impl<'a> InferenceContext<'a> { // FIXME handle break with value self.infer_expr(*expr, &Expectation::none()); } + if let Some(ctxt) = self.breakables.last_mut() { + ctxt.may_break = true; + } Ty::simple(TypeCtor::Never) } Expr::Return { expr } => { diff --git a/crates/ra_hir_ty/src/tests/never_type.rs b/crates/ra_hir_ty/src/tests/never_type.rs index 1721f97c5..082c47208 100644 --- a/crates/ra_hir_ty/src/tests/never_type.rs +++ b/crates/ra_hir_ty/src/tests/never_type.rs @@ -361,8 +361,78 @@ fn test1() { // should give type mismatch let x: u32 = { loop { break; } }; } +fn test2() { + // should give type mismatch + let x: u32 = { for a in b { break; }; }; + // should give type mismatch as well + let x: u32 = { for a in b {}; }; + // should give type mismatch as well + let x: u32 = { for a in b { return; }; }; +} +fn test3() { + // should give type mismatch + let x: u32 = { while true { break; }; }; + // should give type mismatch as well -- there's an implicit break, even if it's never hit + let x: u32 = { while true {}; }; + // should give type mismatch as well + let x: u32 = { while true { return; }; }; +} "#, true, ); - assert_snapshot!(t, @r###""###); + assert_snapshot!(t, @r###" + 25..99 '{ ...} }; }': () + 68..69 'x': u32 + 77..96 '{ loop...k; } }': () + 79..94 'loop { break; }': () + 84..94 '{ break; }': () + 86..91 'break': ! + 77..96: expected u32, got () + 79..94: expected u32, got () + 111..357 '{ ...; }; }': () + 154..155 'x': u32 + 163..189 '{ for ...; }; }': () + 165..186 'for a ...eak; }': () + 169..170 'a': {unknown} + 174..175 'b': {unknown} + 176..186 '{ break; }': () + 178..183 'break': ! + 240..241 'x': u32 + 249..267 '{ for ... {}; }': () + 251..264 'for a in b {}': () + 255..256 'a': {unknown} + 260..261 'b': {unknown} + 262..264 '{}': () + 318..319 'x': u32 + 327..354 '{ for ...; }; }': () + 329..351 'for a ...urn; }': () + 333..334 'a': {unknown} + 338..339 'b': {unknown} + 340..351 '{ return; }': () + 342..348 'return': ! + 163..189: expected u32, got () + 249..267: expected u32, got () + 327..354: expected u32, got () + 369..668 '{ ...; }; }': () + 412..413 'x': u32 + 421..447 '{ whil...; }; }': () + 423..444 'while ...eak; }': () + 429..433 'true': bool + 434..444 '{ break; }': () + 436..441 'break': ! + 551..552 'x': u32 + 560..578 '{ whil... {}; }': () + 562..575 'while true {}': () + 568..572 'true': bool + 573..575 '{}': () + 629..630 'x': u32 + 638..665 '{ whil...; }; }': () + 640..662 'while ...urn; }': () + 646..650 'true': bool + 651..662 '{ return; }': () + 653..659 'return': ! + 421..447: expected u32, got () + 560..578: expected u32, got () + 638..665: expected u32, got () + "###); } -- cgit v1.2.3 From f739e0119c0d74c155f91ad27616a638fe494e2d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 8 May 2020 18:53:53 +0200 Subject: Add stderr to error message --- crates/ra_project_model/src/lib.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 5a0a87ed7..3adb8baf6 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -600,7 +600,12 @@ pub fn get_rustc_cfg_options(target: Option<&String>) -> CfgOptions { fn output(mut cmd: Command) -> Result { let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?; if !output.status.success() { - bail!("{:?} failed, {}", cmd, output.status) + match String::from_utf8(output.stderr) { + Ok(stderr) if !stderr.is_empty() => { + bail!("{:?} failed, {}\nstderr:\n{}", cmd, output.status, stderr) + } + _ => bail!("{:?} failed, {}", cmd, output.status), + } } Ok(output) } -- cgit v1.2.3 From f8bf94a4b94074eb344e495dfb4dab4bec6bc20e Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 8 May 2020 19:30:02 +0200 Subject: Use matches! --- crates/ra_hir_ty/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index ac0ef1bfe..3e5f38d0d 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -731,10 +731,7 @@ impl Ty { } pub fn is_never(&self) -> bool { - match self { - Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }) => true, - _ => false, - } + matches!(self, Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. })) } /// If this is a `dyn Trait` type, this returns the `Trait` part. -- cgit v1.2.3 From d0129c4ddba3b72e7b26e94e9c25546d37dbf166 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 8 May 2020 19:48:03 +0200 Subject: Add diagnostic for break outside of loop --- crates/ra_hir_ty/src/diagnostics.rs | 28 ++++++++++++++++++++++++++++ crates/ra_hir_ty/src/infer.rs | 13 ++++++++++++- crates/ra_hir_ty/src/infer/expr.rs | 4 ++++ crates/ra_hir_ty/src/tests.rs | 18 ++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index c8fd54861..41ac70272 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs @@ -131,3 +131,31 @@ impl AstDiagnostic for MissingOkInTailExpr { ast::Expr::cast(node).unwrap() } } + +#[derive(Debug)] +pub struct BreakOutsideOfLoop { + pub file: HirFileId, + pub expr: AstPtr, +} + +impl Diagnostic for BreakOutsideOfLoop { + fn message(&self) -> String { + "break outside of loop".to_string() + } + fn source(&self) -> InFile { + InFile { file_id: self.file, value: self.expr.clone().into() } + } + fn as_any(&self) -> &(dyn Any + Send + 'static) { + self + } +} + +impl AstDiagnostic for BreakOutsideOfLoop { + type AST = ast::Expr; + + fn ast(&self, db: &impl AstDatabase) -> Self::AST { + let root = db.parse_or_expand(self.file).unwrap(); + let node = self.source().value.to_node(&root); + ast::Expr::cast(node).unwrap() + } +} diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 413904518..a21ad8d86 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -717,11 +717,15 @@ mod diagnostics { use hir_def::{expr::ExprId, FunctionId}; use hir_expand::diagnostics::DiagnosticSink; - use crate::{db::HirDatabase, diagnostics::NoSuchField}; + use crate::{ + db::HirDatabase, + diagnostics::{BreakOutsideOfLoop, NoSuchField}, + }; #[derive(Debug, PartialEq, Eq, Clone)] pub(super) enum InferenceDiagnostic { NoSuchField { expr: ExprId, field: usize }, + BreakOutsideOfLoop { expr: ExprId }, } impl InferenceDiagnostic { @@ -737,6 +741,13 @@ mod diagnostics { let field = source_map.field_syntax(*expr, *field); sink.push(NoSuchField { file: field.file_id, field: field.value }) } + InferenceDiagnostic::BreakOutsideOfLoop { expr } => { + let (_, source_map) = db.body_with_source_map(owner.into()); + let ptr = source_map + .expr_syntax(*expr) + .expect("break outside of loop in synthetic syntax"); + sink.push(BreakOutsideOfLoop { file: ptr.file_id, expr: ptr.value }) + } } } } diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs index 9cac0c787..0b67d216a 100644 --- a/crates/ra_hir_ty/src/infer/expr.rs +++ b/crates/ra_hir_ty/src/infer/expr.rs @@ -235,6 +235,10 @@ impl<'a> InferenceContext<'a> { } if let Some(ctxt) = self.breakables.last_mut() { ctxt.may_break = true; + } else { + self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop { + expr: tgt_expr, + }); } Ty::simple(TypeCtor::Never) } diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index d60732e19..5af88b368 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs @@ -518,3 +518,21 @@ fn missing_record_pat_field_no_diagnostic_if_not_exhaustive() { assert_snapshot!(diagnostics, @""); } + +#[test] +fn break_outside_of_loop() { + let diagnostics = TestDB::with_files( + r" + //- /lib.rs + fn foo() { + break; + } + ", + ) + .diagnostics() + .0; + + assert_snapshot!(diagnostics, @r###""break": break outside of loop + "### + ); +} -- cgit v1.2.3 From a3d866e776f43c1ae717740bf0c507f4d9fe47cb Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 8 May 2020 22:12:16 +0200 Subject: Handle coercing function types to function pointers in match E.g. in ```rust match x { 1 => function1, 2 => function2, } ``` we need to try coercing both to pointers. Turns out this is a special case in rustc as well (see the link in the comment). --- crates/ra_hir_ty/src/infer/coerce.rs | 34 ++++++++++++++++++--------- crates/ra_hir_ty/src/lib.rs | 6 +++++ crates/ra_hir_ty/src/marks.rs | 1 + crates/ra_hir_ty/src/tests/coercion.rs | 42 ++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 11 deletions(-) diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs index 89200255a..173ec59ed 100644 --- a/crates/ra_hir_ty/src/infer/coerce.rs +++ b/crates/ra_hir_ty/src/infer/coerce.rs @@ -20,21 +20,35 @@ impl<'a> InferenceContext<'a> { self.coerce_inner(from_ty, &to_ty) } - /// Merge two types from different branches, with possible implicit coerce. + /// Merge two types from different branches, with possible coercion. /// - /// Note that it is only possible that one type are coerced to another. - /// Coercing both types to another least upper bound type is not possible in rustc, - /// which will simply result in "incompatible types" error. + /// Mostly this means trying to coerce one to the other, but + /// - if we have two function types for different functions, we need to + /// coerce both to function pointers; + /// - if we were concerned with lifetime subtyping, we'd need to look for a + /// least upper bound. pub(super) fn coerce_merge_branch(&mut self, ty1: &Ty, ty2: &Ty) -> Ty { if self.coerce(ty1, ty2) { ty2.clone() } else if self.coerce(ty2, ty1) { ty1.clone() } else { - tested_by!(coerce_merge_fail_fallback); - // For incompatible types, we use the latter one as result - // to be better recovery for `if` without `else`. - ty2.clone() + if let (ty_app!(TypeCtor::FnDef(_)), ty_app!(TypeCtor::FnDef(_))) = (ty1, ty2) { + tested_by!(coerce_fn_reification); + // Special case: two function types. Try to coerce both to + // pointers to have a chance at getting a match. See + // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 + let sig1 = ty1.callable_sig(self.db).expect("FnDef without callable sig"); + let sig2 = ty2.callable_sig(self.db).expect("FnDef without callable sig"); + let ptr_ty1 = Ty::fn_ptr(sig1); + let ptr_ty2 = Ty::fn_ptr(sig2); + self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) + } else { + tested_by!(coerce_merge_fail_fallback); + // For incompatible types, we use the latter one as result + // to be better recovery for `if` without `else`. + ty2.clone() + } } } @@ -84,9 +98,7 @@ impl<'a> InferenceContext<'a> { match from_ty.callable_sig(self.db) { None => return false, Some(sig) => { - let num_args = sig.params_and_return.len() as u16 - 1; - from_ty = - Ty::apply(TypeCtor::FnPtr { num_args }, Substs(sig.params_and_return)); + from_ty = Ty::fn_ptr(sig); } } } diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs index 3e5f38d0d..e8f3482fe 100644 --- a/crates/ra_hir_ty/src/lib.rs +++ b/crates/ra_hir_ty/src/lib.rs @@ -683,6 +683,12 @@ impl Ty { pub fn unit() -> Self { Ty::apply(TypeCtor::Tuple { cardinality: 0 }, Substs::empty()) } + pub fn fn_ptr(sig: FnSig) -> Self { + Ty::apply( + TypeCtor::FnPtr { num_args: sig.params().len() as u16 }, + Substs(sig.params_and_return), + ) + } pub fn as_reference(&self) -> Option<(&Ty, Mutability)> { match self { diff --git a/crates/ra_hir_ty/src/marks.rs b/crates/ra_hir_ty/src/marks.rs index de5cb1d6b..a39740143 100644 --- a/crates/ra_hir_ty/src/marks.rs +++ b/crates/ra_hir_ty/src/marks.rs @@ -7,5 +7,6 @@ test_utils::marks!( impl_self_type_match_without_receiver match_ergonomics_ref coerce_merge_fail_fallback + coerce_fn_reification trait_self_implements_self ); diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs index 0c3a833bd..6dc4b2cd1 100644 --- a/crates/ra_hir_ty/src/tests/coercion.rs +++ b/crates/ra_hir_ty/src/tests/coercion.rs @@ -545,6 +545,48 @@ fn test() { ); } +#[test] +fn coerce_fn_items_in_match_arms() { + covers!(coerce_fn_reification); + assert_snapshot!( + infer_with_mismatches(r#" +fn foo1(x: u32) -> isize { 1 } +fn foo2(x: u32) -> isize { 2 } +fn foo3(x: u32) -> isize { 3 } +fn test() { + let x = match 1 { + 1 => foo1, + 2 => foo2, + _ => foo3, + }; +} +"#, true), + @r###" + 9..10 'x': u32 + 26..31 '{ 1 }': isize + 28..29 '1': isize + 40..41 'x': u32 + 57..62 '{ 2 }': isize + 59..60 '2': isize + 71..72 'x': u32 + 88..93 '{ 3 }': isize + 90..91 '3': isize + 104..193 '{ ... }; }': () + 114..115 'x': fn(u32) -> isize + 118..190 'match ... }': fn(u32) -> isize + 124..125 '1': i32 + 136..137 '1': i32 + 136..137 '1': i32 + 141..145 'foo1': fn foo1(u32) -> isize + 155..156 '2': i32 + 155..156 '2': i32 + 160..164 'foo2': fn foo2(u32) -> isize + 174..175 '_': i32 + 179..183 'foo3': fn foo3(u32) -> isize + "### + ); +} + #[test] fn coerce_closure_to_fn_ptr() { assert_snapshot!( -- cgit v1.2.3 From aca9aa8b7e7f363779aeba1dfc60397f46b9544d Mon Sep 17 00:00:00 2001 From: veetaha Date: Sat, 9 May 2020 02:24:51 +0300 Subject: Simplify --- crates/rust-analyzer/src/main_loop.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index b77f0c5a9..17b0b95b9 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -96,23 +96,21 @@ pub fn main_loop(ws_roots: Vec, config: Config, connection: Connection) let mut world_state = { let workspaces = { // FIXME: support dynamic workspace loading. - let mut visited = FxHashSet::default(); - let project_roots = ws_roots + let project_roots: FxHashSet<_> = ws_roots .iter() .filter_map(|it| ra_project_model::ProjectRoot::discover(it).ok()) .flatten() - .filter(|it| visited.insert(it.clone())) - .collect::>(); + .collect(); if project_roots.is_empty() && config.notifications.cargo_toml_not_found { show_message( - req::MessageType::Error, - format!( - "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}", - ws_roots.iter().format_with(", ", |it, f| f(&it.display())) - ), - &connection.sender, - ); + req::MessageType::Error, + format!( + "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}", + ws_roots.iter().format_with(", ", |it, f| f(&it.display())) + ), + &connection.sender, + ); }; project_roots -- cgit v1.2.3 From e83a2912b8deaab560d1ea39232c06a29530d6e5 Mon Sep 17 00:00:00 2001 From: veetaha Date: Sat, 9 May 2020 02:51:59 +0300 Subject: Simpify project discovery --- crates/ra_project_model/src/lib.rs | 51 +++++++++++--------------------------- 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 3adb8baf6..4f098b706 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -88,46 +88,28 @@ impl ProjectRoot { } pub fn discover(path: &Path) -> io::Result> { - if let Some(project_json) = find_rust_project_json(path) { + if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") { return Ok(vec![ProjectRoot::ProjectJson(project_json)]); } return find_cargo_toml(path) .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); - fn find_rust_project_json(path: &Path) -> Option { - if path.ends_with("rust-project.json") { - return Some(path.to_path_buf()); - } - - let mut curr = Some(path); - while let Some(path) = curr { - let candidate = path.join("rust-project.json"); - if candidate.exists() { - return Some(candidate); - } - curr = path.parent(); - } - - None - } - fn find_cargo_toml(path: &Path) -> io::Result> { - if path.ends_with("Cargo.toml") { - return Ok(vec![path.to_path_buf()]); + match find_in_parent_dirs(path, "Cargo.toml") { + Some(it) => Ok(vec![it]), + None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)), } + } - if let Some(p) = find_cargo_toml_in_parent_dir(path) { - return Ok(vec![p]); + fn find_in_parent_dirs(path: &Path, target_file_name: &str) -> Option { + if path.ends_with(target_file_name) { + return Some(path.to_owned()); } - let entities = read_dir(path)?; - Ok(find_cargo_toml_in_child_dir(entities)) - } - - fn find_cargo_toml_in_parent_dir(path: &Path) -> Option { let mut curr = Some(path); + while let Some(path) = curr { - let candidate = path.join("Cargo.toml"); + let candidate = path.join(target_file_name); if candidate.exists() { return Some(candidate); } @@ -139,14 +121,11 @@ impl ProjectRoot { fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec { // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects - let mut valid_canditates = vec![]; - for entity in entities.filter_map(Result::ok) { - let candidate = entity.path().join("Cargo.toml"); - if candidate.exists() { - valid_canditates.push(candidate) - } - } - valid_canditates + entities + .filter_map(Result::ok) + .map(|it| it.path().join("Cargo.toml")) + .filter(|it| it.exists()) + .collect() } } } -- cgit v1.2.3 From c6334285e3b8369ca685ca436d94ff1f64201044 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 9 May 2020 13:57:19 +0200 Subject: Fix visibility --- crates/ra_assists/src/assist_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 3085c4330..a680f752b 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs @@ -49,7 +49,7 @@ use crate::{Assist, AssistId, GroupLabel, ResolvedAssist}; /// easier to just compute the edit eagerly :-) pub(crate) struct AssistContext<'a> { pub(crate) sema: Semantics<'a, RootDatabase>, - pub(super) db: &'a RootDatabase, + pub(crate) db: &'a RootDatabase, pub(crate) frange: FileRange, source_file: SourceFile, } -- cgit v1.2.3 From 231fddab5420ffe5edf7b93609ea21155653254a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 9 May 2020 14:40:11 +0200 Subject: More fluent indent API --- crates/ra_assists/src/handlers/add_function.rs | 13 ++++++----- .../src/handlers/add_missing_impl_members.rs | 5 ++--- crates/ra_assists/src/handlers/early_return.rs | 13 ++++++----- .../src/handlers/replace_if_let_with_match.rs | 9 +++++--- .../src/handlers/replace_let_with_if_let.rs | 3 +-- .../src/handlers/replace_unwrap_with_match.rs | 10 ++++++--- crates/ra_syntax/src/ast/edit.rs | 26 +++++++++++----------- 7 files changed, 45 insertions(+), 34 deletions(-) diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index 95faf0f4f..de016ae4e 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs @@ -1,7 +1,11 @@ use hir::HirDisplay; use ra_db::FileId; use ra_syntax::{ - ast::{self, edit::IndentLevel, ArgListOwner, AstNode, ModuleItemOwner}, + ast::{ + self, + edit::{AstNodeEdit, IndentLevel}, + ArgListOwner, AstNode, ModuleItemOwner, + }, SyntaxKind, SyntaxNode, TextSize, }; use rustc_hash::{FxHashMap, FxHashSet}; @@ -116,17 +120,16 @@ impl FunctionBuilder { let (fn_def, insert_offset) = match self.target { GeneratedFunctionTarget::BehindItem(it) => { let with_leading_blank_line = ast::make::add_leading_newlines(2, fn_def); - let indented = IndentLevel::from_node(&it).increase_indent(with_leading_blank_line); + let indented = with_leading_blank_line.indent(IndentLevel::from_node(&it)); (indented, it.text_range().end()) } GeneratedFunctionTarget::InEmptyItemList(it) => { let indent_once = IndentLevel(1); let indent = IndentLevel::from_node(it.syntax()); - let fn_def = ast::make::add_leading_newlines(1, fn_def); - let fn_def = indent_once.increase_indent(fn_def); + let fn_def = fn_def.indent(indent_once); let fn_def = ast::make::add_trailing_newlines(1, fn_def); - let fn_def = indent.increase_indent(fn_def); + let fn_def = fn_def.indent(indent); (fn_def, it.syntax().text_range().start() + TextSize::of('{')) } }; diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs index 3482a75bf..c1ce87914 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs @@ -2,7 +2,7 @@ use hir::HasSource; use ra_syntax::{ ast::{ self, - edit::{self, IndentLevel}, + edit::{self, AstNodeEdit, IndentLevel}, make, AstNode, NameOwner, }, SmolStr, @@ -176,8 +176,7 @@ fn add_body(fn_def: ast::FnDef) -> ast::FnDef { if fn_def.body().is_some() { return fn_def; } - let body = make::block_expr(None, Some(make::expr_todo())); - let body = IndentLevel(1).increase_indent(body); + let body = make::block_expr(None, Some(make::expr_todo())).indent(IndentLevel(1)); fn_def.with_body(body) } diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs index 810784ad5..0f2914950 100644 --- a/crates/ra_assists/src/handlers/early_return.rs +++ b/crates/ra_assists/src/handlers/early_return.rs @@ -2,7 +2,11 @@ use std::{iter::once, ops::RangeInclusive}; use ra_syntax::{ algo::replace_children, - ast::{self, edit::IndentLevel, make}, + ast::{ + self, + edit::{AstNodeEdit, IndentLevel}, + make, + }, AstNode, SyntaxKind::{FN_DEF, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE}, SyntaxNode, @@ -105,8 +109,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) let then_branch = make::block_expr(once(make::expr_stmt(early_expression).into()), None); let cond = invert_boolean_expression(cond_expr); - let e = make::expr_if(make::condition(cond, None), then_branch); - if_indent_level.increase_indent(e) + make::expr_if(make::condition(cond, None), then_branch).indent(if_indent_level) }; replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) } @@ -140,7 +143,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), Some(match_expr), ); - let let_stmt = if_indent_level.increase_indent(let_stmt); + let let_stmt = let_stmt.indent(if_indent_level); replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) } }; @@ -153,7 +156,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) parent_block: &ast::BlockExpr, if_expr: &ast::IfExpr, ) -> SyntaxNode { - let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone()); + let then_block_items = then_block.unindent(IndentLevel::from(1)); let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); let end_of_then = if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs index a59a06efa..65f5fc6ab 100644 --- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs @@ -1,6 +1,10 @@ use ra_fmt::unwrap_trivial_block; use ra_syntax::{ - ast::{self, edit::IndentLevel, make}, + ast::{ + self, + edit::{AstNodeEdit, IndentLevel}, + make, + }, AstNode, }; @@ -61,10 +65,9 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) make::match_arm(vec![pattern], else_expr) }; make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) + .indent(IndentLevel::from_node(if_expr.syntax())) }; - let match_expr = IndentLevel::from_node(if_expr.syntax()).increase_indent(match_expr); - edit.set_cursor(if_expr.syntax().text_range().start()); edit.replace_ast::(if_expr.into(), match_expr); }) diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs index d3f214591..482957dc6 100644 --- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs @@ -53,8 +53,7 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) -> ) .into(), }; - let block = - IndentLevel::from_node(let_stmt.syntax()).increase_indent(make::block_expr(None, None)); + let block = make::block_expr(None, None).indent(IndentLevel::from_node(let_stmt.syntax())); let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block); let stmt = make::expr_stmt(if_); diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs index a46998b8e..c4b56f6e9 100644 --- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs @@ -1,7 +1,11 @@ use std::iter; use ra_syntax::{ - ast::{self, edit::IndentLevel, make}, + ast::{ + self, + edit::{AstNodeEdit, IndentLevel}, + make, + }, AstNode, }; @@ -51,8 +55,8 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); - let match_expr = make::expr_match(caller.clone(), match_arm_list); - let match_expr = IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr); + let match_expr = make::expr_match(caller.clone(), match_arm_list) + .indent(IndentLevel::from_node(method_call.syntax())); edit.set_cursor(caller.syntax().text_range().start()); edit.replace_ast::(method_call.into(), match_expr); diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 3e6dd6061..94dfb1a13 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs @@ -453,11 +453,7 @@ impl IndentLevel { IndentLevel(0) } - pub fn increase_indent(self, node: N) -> N { - N::cast(self._increase_indent(node.syntax().clone())).unwrap() - } - - fn _increase_indent(self, node: SyntaxNode) -> SyntaxNode { + fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { let mut rewriter = SyntaxRewriter::default(); node.descendants_with_tokens() .filter_map(|el| el.into_token()) @@ -478,11 +474,7 @@ impl IndentLevel { rewriter.rewrite(&node) } - pub fn decrease_indent(self, node: N) -> N { - N::cast(self._decrease_indent(node.syntax().clone())).unwrap() - } - - fn _decrease_indent(self, node: SyntaxNode) -> SyntaxNode { + fn decrease_indent(self, node: SyntaxNode) -> SyntaxNode { let mut rewriter = SyntaxRewriter::default(); node.descendants_with_tokens() .filter_map(|el| el.into_token()) @@ -521,7 +513,7 @@ fn prev_tokens(token: SyntaxToken) -> impl Iterator { iter::successors(Some(token), |token| token.prev_token()) } -pub trait AstNodeEdit: AstNode + Sized { +pub trait AstNodeEdit: AstNode + Clone + Sized { #[must_use] fn insert_children( &self, @@ -558,9 +550,17 @@ pub trait AstNodeEdit: AstNode + Sized { } rewriter.rewrite_ast(self) } + #[must_use] + fn indent(&self, indent: IndentLevel) -> Self { + Self::cast(indent.increase_indent(self.syntax().clone())).unwrap() + } + #[must_use] + fn unindent(&self, indent: IndentLevel) -> Self { + Self::cast(indent.decrease_indent(self.syntax().clone())).unwrap() + } } -impl AstNodeEdit for N {} +impl AstNodeEdit for N {} fn single_node(element: impl Into) -> RangeInclusive { let element = element.into(); @@ -580,7 +580,7 @@ fn test_increase_indent() { _ => (), }" ); - let indented = IndentLevel(2).increase_indent(arm_list); + let indented = arm_list.indent(IndentLevel(2)); assert_eq!( indented.syntax().to_string(), "{ -- cgit v1.2.3 From 5c04d8544c647e1f9bbf3c5a2f1e86409d4080f5 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 9 May 2020 14:48:43 +0200 Subject: unindent -> dedent --- crates/ra_assists/src/handlers/early_return.rs | 2 +- crates/ra_syntax/src/ast/edit.rs | 2 +- crates/test_utils/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs index 0f2914950..66b296081 100644 --- a/crates/ra_assists/src/handlers/early_return.rs +++ b/crates/ra_assists/src/handlers/early_return.rs @@ -156,7 +156,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) parent_block: &ast::BlockExpr, if_expr: &ast::IfExpr, ) -> SyntaxNode { - let then_block_items = then_block.unindent(IndentLevel::from(1)); + let then_block_items = then_block.dedent(IndentLevel::from(1)); let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); let end_of_then = if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 94dfb1a13..24a1e1d91 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs @@ -555,7 +555,7 @@ pub trait AstNodeEdit: AstNode + Clone + Sized { Self::cast(indent.increase_indent(self.syntax().clone())).unwrap() } #[must_use] - fn unindent(&self, indent: IndentLevel) -> Self { + fn dedent(&self, indent: IndentLevel) -> Self { Self::cast(indent.decrease_indent(self.syntax().clone())).unwrap() } } diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index b13e13af2..b1e3c328f 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -270,7 +270,7 @@ fn parse_fixture_checks_further_indented_metadata() { } #[test] -fn parse_fixture_can_handle_unindented_first_line() { +fn parse_fixture_can_handle_dedented_first_line() { let fixture = "//- /lib.rs mod foo; //- /foo.rs -- cgit v1.2.3 From e201e7834d623f954e06925b9de0f14cd5ff90c1 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 9 May 2020 15:55:59 +0200 Subject: Ascribe more correct types --- crates/ra_assists/src/handlers/add_explicit_type.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index 55409e501..146cc75df 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs @@ -23,6 +23,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { let stmt = ctx.find_node_at_offset::()?; + let module = ctx.sema.scope(stmt.syntax()).module()?; let expr = stmt.initializer()?; let pat = stmt.pat()?; // Must be a binding @@ -57,17 +58,17 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio return None; } - let db = ctx.db; - let new_type_string = ty.display_truncated(db, None).to_string(); + let inferred_type = ty.display_source_code(ctx.db, module.into()).ok()?; acc.add( AssistId("add_explicit_type"), - format!("Insert explicit type '{}'", new_type_string), + format!("Insert explicit type '{}'", inferred_type), pat_range, - |edit| { - if let Some(ascribed_ty) = ascribed_ty { - edit.replace(ascribed_ty.syntax().text_range(), new_type_string); - } else { - edit.insert(name_range.end(), format!(": {}", new_type_string)); + |builder| match ascribed_ty { + Some(ascribed_ty) => { + builder.replace(ascribed_ty.syntax().text_range(), inferred_type); + } + None => { + builder.insert(name_range.end(), format!(": {}", inferred_type)); } }, ) @@ -208,7 +209,7 @@ struct Test { } fn main() { - let test<|>: Test = Test { t: 23, k: 33 }; + let test<|>: Test = Test { t: 23, k: 33 }; }"#, ); } -- cgit v1.2.3 From d9828a57fcbff6c1bcaa9d2b6a54a08d8428a95f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 9 May 2020 16:21:10 +0200 Subject: Add From should not move the cursor --- .../src/handlers/add_from_impl_for_enum.rs | 25 ++++++++++------------ 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs index 275184e24..6a49b7dbd 100644 --- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs +++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs @@ -1,8 +1,5 @@ use ra_ide_db::RootDatabase; -use ra_syntax::{ - ast::{self, AstNode, NameOwner}, - TextSize, -}; +use ra_syntax::ast::{self, AstNode, NameOwner}; use stdx::format_to; use test_utils::tested_by; @@ -69,7 +66,6 @@ impl From<{0}> for {1} {{ variant_name ); edit.insert(start_offset, buf); - edit.set_cursor(start_offset + TextSize::of("\n\n")); }, ) } @@ -97,19 +93,20 @@ fn existing_from_impl( #[cfg(test)] mod tests { - use super::*; + use test_utils::covers; use crate::tests::{check_assist, check_assist_not_applicable}; - use test_utils::covers; + + use super::*; #[test] fn test_add_from_impl_for_enum() { check_assist( add_from_impl_for_enum, "enum A { <|>One(u32) }", - r#"enum A { One(u32) } + r#"enum A { <|>One(u32) } -<|>impl From for A { +impl From for A { fn from(v: u32) -> Self { A::One(v) } @@ -121,10 +118,10 @@ mod tests { fn test_add_from_impl_for_enum_complicated_path() { check_assist( add_from_impl_for_enum, - "enum A { <|>One(foo::bar::baz::Boo) }", - r#"enum A { One(foo::bar::baz::Boo) } + r#"enum A { <|>One(foo::bar::baz::Boo) }"#, + r#"enum A { <|>One(foo::bar::baz::Boo) } -<|>impl From for A { +impl From for A { fn from(v: foo::bar::baz::Boo) -> Self { A::One(v) } @@ -184,9 +181,9 @@ impl From for A { pub trait From { fn from(T) -> Self; }"#, - r#"enum A { One(u32), Two(String), } + r#"enum A { <|>One(u32), Two(String), } -<|>impl From for A { +impl From for A { fn from(v: u32) -> Self { A::One(v) } -- cgit v1.2.3 From 9405116d51b2d078557873fafbf3d91f19d332a7 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sun, 10 May 2020 16:27:31 +0800 Subject: Hot fix panic for function_signature --- crates/ra_ide/src/display/function_signature.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index f16d42276..3d3147254 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs @@ -84,8 +84,8 @@ impl FunctionSignature { let ty = field.signature_ty(db); let raw_param = format!("{}", ty.display(db)); - if let Some(param_type) = raw_param.split(':').nth(1) { - parameter_types.push(param_type[1..].to_string()); + if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) { + parameter_types.push(param_type.to_string()); } else { // useful when you have tuple struct parameter_types.push(raw_param.clone()); @@ -129,8 +129,9 @@ impl FunctionSignature { for field in variant.fields(db).into_iter() { let ty = field.signature_ty(db); let raw_param = format!("{}", ty.display(db)); - if let Some(param_type) = raw_param.split(':').nth(1) { - parameter_types.push(param_type[1..].to_string()); + dbg!(&raw_param); + if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) { + parameter_types.push(param_type.to_string()); } else { // The unwrap_or_else is useful when you have tuple parameter_types.push(raw_param); @@ -197,7 +198,12 @@ impl From<&'_ ast::FnDef> for FunctionSignature { let raw_param = self_param.syntax().text().to_string(); res_types.push( - raw_param.split(':').nth(1).unwrap_or_else(|| " Self")[1..].to_string(), + raw_param + .split(':') + .nth(1) + .and_then(|it| it.get(1..)) + .unwrap_or_else(|| "Self") + .to_string(), ); res.push(raw_param); } @@ -205,8 +211,8 @@ impl From<&'_ ast::FnDef> for FunctionSignature { res.extend(param_list.params().map(|param| param.syntax().text().to_string())); res_types.extend(param_list.params().map(|param| { let param_text = param.syntax().text().to_string(); - match param_text.split(':').nth(1) { - Some(it) => it[1..].to_string(), + match param_text.split(':').nth(1).and_then(|it| it.get(1..)) { + Some(it) => it.to_string(), None => param_text, } })); -- cgit v1.2.3 From a3375c1a88848195a7fd83d29acdab4029ca1459 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sun, 10 May 2020 18:03:44 +0800 Subject: Remove dbg --- crates/ra_ide/src/display/function_signature.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index 3d3147254..9572debd8 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs @@ -129,7 +129,6 @@ impl FunctionSignature { for field in variant.fields(db).into_iter() { let ty = field.signature_ty(db); let raw_param = format!("{}", ty.display(db)); - dbg!(&raw_param); if let Some(param_type) = raw_param.split(':').nth(1).and_then(|it| it.get(1..)) { parameter_types.push(param_type.to_string()); } else { -- cgit v1.2.3