From beb79ed104c686d8704eb7042318eefea78770df Mon Sep 17 00:00:00 2001 From: Aaron Wood Date: Fri, 8 May 2020 16:59:52 -0700 Subject: [PATCH 001/163] Begin transition to new fields for JsonProject crate cfgs This starts the transition to a new method of documenting the cfgs that are enabled for a given crate in the json file. This is changing from a list of atoms and a dict of key:value pairs, to a list of strings that is equivalent to that returned by `rustc --print cfg ..`, and parsed in the same manner by rust-analyzer. This is the first of two changes, which adds the new field that contains the list of strings. Next change will complete the transition and remove the previous fields. --- crates/ra_project_model/src/json_project.rs | 79 +++++++++++++++++++ crates/ra_project_model/src/lib.rs | 10 +++ .../rust-analyzer/tests/heavy_tests/main.rs | 5 +- 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs index b030c8a6a18f..bd2bae15e690 100644 --- a/crates/ra_project_model/src/json_project.rs +++ b/crates/ra_project_model/src/json_project.rs @@ -20,8 +20,17 @@ pub struct Crate { pub(crate) root_module: PathBuf, pub(crate) edition: Edition, pub(crate) deps: Vec, + + // This is the preferred method of providing cfg options. + #[serde(default)] + pub(crate) cfg: FxHashSet, + + // These two are here for transition only. + #[serde(default)] pub(crate) atom_cfgs: FxHashSet, + #[serde(default)] pub(crate) key_value_cfgs: FxHashMap, + pub(crate) out_dir: Option, pub(crate) proc_macro_dylib_path: Option, } @@ -54,3 +63,73 @@ pub struct JsonProject { pub(crate) roots: Vec, pub(crate) crates: Vec, } + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn test_crate_deserialization() { + let raw_json = json!( { + "crate_id": 2, + "root_module": "this/is/a/file/path.rs", + "deps": [ + { + "crate": 1, + "name": "some_dep_crate" + }, + ], + "edition": "2015", + "cfg": [ + "atom_1", + "atom_2", + "feature=feature_1", + "feature=feature_2", + "other=value", + ], + + }); + + let krate: Crate = serde_json::from_value(raw_json).unwrap(); + + assert!(krate.cfg.contains(&"atom_1".to_string())); + assert!(krate.cfg.contains(&"atom_2".to_string())); + assert!(krate.cfg.contains(&"feature=feature_1".to_string())); + assert!(krate.cfg.contains(&"feature=feature_2".to_string())); + assert!(krate.cfg.contains(&"other=value".to_string())); + } + + #[test] + fn test_crate_deserialization_old_json() { + let raw_json = json!( { + "crate_id": 2, + "root_module": "this/is/a/file/path.rs", + "deps": [ + { + "crate": 1, + "name": "some_dep_crate" + }, + ], + "edition": "2015", + "atom_cfgs": [ + "atom_1", + "atom_2", + ], + "key_value_cfgs": { + "feature": "feature_1", + "feature": "feature_2", + "other": "value", + }, + }); + + let krate: Crate = serde_json::from_value(raw_json).unwrap(); + + assert!(krate.atom_cfgs.contains(&"atom_1".to_string())); + assert!(krate.atom_cfgs.contains(&"atom_2".to_string())); + assert!(krate.key_value_cfgs.contains_key(&"feature".to_string())); + assert_eq!(krate.key_value_cfgs.get("feature"), Some(&"feature_2".to_string())); + assert!(krate.key_value_cfgs.contains_key(&"other".to_string())); + assert_eq!(krate.key_value_cfgs.get("other"), Some(&"value".to_string())); + } +} diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 731cbd291818..e7da683d6edf 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -273,6 +273,16 @@ impl ProjectWorkspace { }; let cfg_options = { let mut opts = default_cfg_options.clone(); + for cfg in &krate.cfg { + match cfg.find('=') { + None => opts.insert_atom(cfg.into()), + Some(pos) => { + let key = &cfg[..pos]; + let value = cfg[pos + 1..].trim_matches('"'); + opts.insert_key_value(key.into(), value.into()); + } + } + } for name in &krate.atom_cfgs { opts.insert_atom(name.into()); } diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index 07b8114c6139..3d5574c7ff9a 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs @@ -384,8 +384,9 @@ fn test_missing_module_code_action_in_json_project() { "root_module": path.join("src/lib.rs"), "deps": [], "edition": "2015", - "atom_cfgs": [], - "key_value_cfgs": {} + "cfg": [ "cfg_atom_1", "feature=cfg_1"], + "atom_cfgs": ["atom_2"], + "key_value_cfgs": { "feature": "key_value_feature", "other": "value"} } ] }); From eeb98237d1cf5134ca3e41b85e1e9e07cab83c75 Mon Sep 17 00:00:00 2001 From: vsrs Date: Sat, 16 May 2020 11:16:32 +0300 Subject: [PATCH 002/163] parse fixture meta in test_utils crate --- crates/test_utils/Cargo.toml | 4 ++ crates/test_utils/src/lib.rs | 98 +++++++++++++++++++++++++++++++++++- 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml index 8ec986bcb07f..4d185b01c752 100644 --- a/crates/test_utils/Cargo.toml +++ b/crates/test_utils/Cargo.toml @@ -11,3 +11,7 @@ doctest = false difference = "2.0.0" text-size = "1.0.0" serde_json = "1.0.48" +relative-path = "1.0.0" +rustc-hash = "1.1.0" + +ra_cfg = { path = "../ra_cfg" } \ No newline at end of file diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index b1e3c328f3c5..12ae5f451377 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -14,8 +14,12 @@ use std::{ path::{Path, PathBuf}, }; +pub use ra_cfg::CfgOptions; + use serde_json::Value; use text_size::{TextRange, TextSize}; +pub use relative_path::{RelativePath, RelativePathBuf}; +pub use rustc_hash::FxHashMap; pub use difference::Changeset as __Changeset; @@ -159,6 +163,33 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String { pub struct FixtureEntry { pub meta: String, pub text: String, + + pub parsed_meta: FixtureMeta, +} + +#[derive(Debug, Eq, PartialEq)] +pub enum FixtureMeta { + Root { path: RelativePathBuf }, + File(FileMeta), +} + +#[derive(Debug, Eq, PartialEq)] +pub struct FileMeta { + pub path: RelativePathBuf, + pub krate: Option, + pub deps: Vec, + pub cfg: ra_cfg::CfgOptions, + pub edition: Option, + pub env: FxHashMap, +} + +impl FixtureMeta { + pub fn path(&self) -> &RelativePath { + match self { + FixtureMeta::Root { path } => &path, + FixtureMeta::File(f) => &f.path, + } + } } /// Parses text which looks like this: @@ -200,7 +231,8 @@ The offending line: {:?}"#, for line in lines.by_ref() { if line.starts_with("//-") { let meta = line["//-".len()..].trim().to_string(); - res.push(FixtureEntry { meta, text: String::new() }) + let parsed_meta = parse_meta(&meta); + res.push(FixtureEntry { meta, parsed_meta, text: String::new() }) } else if let Some(entry) = res.last_mut() { entry.text.push_str(line); entry.text.push('\n'); @@ -209,6 +241,58 @@ The offending line: {:?}"#, res } +//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo +fn parse_meta(meta: &str) -> FixtureMeta { + let components = meta.split_ascii_whitespace().collect::>(); + + if components[0] == "root" { + let path: RelativePathBuf = components[1].into(); + assert!(path.starts_with("/") && path.ends_with("/")); + return FixtureMeta::Root { path }; + } + + let path: RelativePathBuf = components[0].into(); + assert!(path.starts_with("/")); + + let mut krate = None; + let mut deps = Vec::new(); + let mut edition = None; + let mut cfg = CfgOptions::default(); + let mut env = FxHashMap::default(); + for component in components[1..].iter() { + let (key, value) = split1(component, ':').unwrap(); + match key { + "crate" => krate = Some(value.to_string()), + "deps" => deps = value.split(',').map(|it| it.to_string()).collect(), + "edition" => edition = Some(value.to_string()), + "cfg" => { + for key in value.split(',') { + match split1(key, '=') { + None => cfg.insert_atom(key.into()), + Some((k, v)) => cfg.insert_key_value(k.into(), v.into()), + } + } + } + "env" => { + for key in value.split(',') { + if let Some((k, v)) = split1(key, '=') { + env.insert(k.into(), v.into()); + } + } + } + _ => panic!("bad component: {:?}", component), + } + } + + FixtureMeta::File(FileMeta { path, krate, deps, edition, cfg, env }) +} + +fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> { + let idx = haystack.find(delim)?; + Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..])) +} + + /// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines. /// This allows fixtures to start off in a different indentation, e.g. to align the first line with /// the other lines visually: @@ -288,6 +372,18 @@ struct Bar; ) } +#[test] +fn parse_fixture_gets_full_meta() { + let fixture = r" + //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo + "; + let parsed = parse_fixture(fixture); + assert_eq!(1, parsed.len()); + + let parsed = &parsed[0]; + assert_eq!("\n", parsed.text); +} + /// Same as `parse_fixture`, except it allow empty fixture pub fn parse_single_fixture(fixture: &str) -> Option { if !fixture.lines().any(|it| it.trim_start().starts_with("//-")) { From d901e0e709258f71183455865cb6f9e07b3dd5d3 Mon Sep 17 00:00:00 2001 From: vsrs Date: Sat, 16 May 2020 11:57:41 +0300 Subject: [PATCH 003/163] Reimplement ra_db::fixture::ParsedMeta in terms of test_utils::FixtureMeta --- crates/ra_db/src/fixture.rs | 74 +++++++++++++----------------------- crates/test_utils/src/lib.rs | 5 +-- 2 files changed, 28 insertions(+), 51 deletions(-) diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs index 51d4c493e3ed..6e2c7ff72504 100644 --- a/crates/ra_db/src/fixture.rs +++ b/crates/ra_db/src/fixture.rs @@ -49,7 +49,7 @@ use std::sync::Arc; use ra_cfg::CfgOptions; use rustc_hash::FxHashMap; -use test_utils::{extract_offset, parse_fixture, parse_single_fixture, CURSOR_MARKER}; +use test_utils::{extract_offset, parse_fixture, parse_single_fixture, FixtureMeta, CURSOR_MARKER}; use crate::{ input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, RelativePathBuf, @@ -99,7 +99,7 @@ fn with_single_file(db: &mut dyn SourceDatabaseExt, ra_fixture: &str) -> FileId let fixture = parse_single_fixture(ra_fixture); let crate_graph = if let Some(entry) = fixture { - let meta = match parse_meta(&entry.meta) { + let meta = match ParsedMeta::from(&entry.parsed_meta) { ParsedMeta::File(it) => it, _ => panic!("with_single_file only support file meta"), }; @@ -156,7 +156,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option { let source_root = std::mem::replace(&mut source_root, SourceRoot::new_local()); db.set_source_root(source_root_id, Arc::new(source_root)); @@ -244,53 +244,31 @@ struct FileMeta { env: Env, } -//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo) -fn parse_meta(meta: &str) -> ParsedMeta { - let components = meta.split_ascii_whitespace().collect::>(); - - if components[0] == "root" { - let path: RelativePathBuf = components[1].into(); - assert!(path.starts_with("/") && path.ends_with("/")); - return ParsedMeta::Root { path }; - } - - let path: RelativePathBuf = components[0].into(); - assert!(path.starts_with("/")); - - let mut krate = None; - let mut deps = Vec::new(); - let mut edition = Edition::Edition2018; - let mut cfg = CfgOptions::default(); - let mut env = Env::default(); - for component in components[1..].iter() { - let (key, value) = split1(component, ':').unwrap(); - match key { - "crate" => krate = Some(value.to_string()), - "deps" => deps = value.split(',').map(|it| it.to_string()).collect(), - "edition" => edition = Edition::from_str(&value).unwrap(), - "cfg" => { - for key in value.split(',') { - match split1(key, '=') { - None => cfg.insert_atom(key.into()), - Some((k, v)) => cfg.insert_key_value(k.into(), v.into()), - } - } +impl From<&FixtureMeta> for ParsedMeta { + fn from(meta: &FixtureMeta) -> Self { + match meta { + FixtureMeta::Root { path } => { + // `Self::Root` causes a false warning: 'variant is never constructed: `Root` ' + // see https://github.com/rust-lang/rust/issues/69018 + ParsedMeta::Root { path: path.to_owned() } } - "env" => { - for key in value.split(',') { - if let Some((k, v)) = split1(key, '=') { - env.set(k, v.into()); + FixtureMeta::File(f) => Self::File(FileMeta { + path: f.path.to_owned().into(), + krate: f.krate.to_owned().into(), + deps: f.deps.to_owned(), + cfg: f.cfg.to_owned(), + edition: f + .edition + .as_ref() + .map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()), + env: { + let mut env = Env::default(); + for (k, v) in &f.env { + env.set(&k, v.to_owned()); } - } - } - _ => panic!("bad component: {:?}", component), + env + }, + }), } } - - ParsedMeta::File(FileMeta { path, krate, deps, edition, cfg, env }) -} - -fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> { - let idx = haystack.find(delim)?; - Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..])) } diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index 12ae5f451377..0c367ce71a62 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -16,10 +16,10 @@ use std::{ pub use ra_cfg::CfgOptions; -use serde_json::Value; -use text_size::{TextRange, TextSize}; pub use relative_path::{RelativePath, RelativePathBuf}; pub use rustc_hash::FxHashMap; +use serde_json::Value; +use text_size::{TextRange, TextSize}; pub use difference::Changeset as __Changeset; @@ -292,7 +292,6 @@ fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> { Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..])) } - /// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines. /// This allows fixtures to start off in a different indentation, e.g. to align the first line with /// the other lines visually: From 256fb7556e9f4a329e673851427942c6403bacb6 Mon Sep 17 00:00:00 2001 From: vsrs Date: Sat, 16 May 2020 12:25:26 +0300 Subject: [PATCH 004/163] Remove temporary FixtureEntry parsed_meta field. --- crates/ra_db/src/fixture.rs | 4 ++-- crates/ra_ide/src/mock_analysis.rs | 7 ++++--- crates/rust-analyzer/tests/heavy_tests/support.rs | 2 +- crates/test_utils/src/lib.rs | 8 +++----- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs index 6e2c7ff72504..8b62fe9aae93 100644 --- a/crates/ra_db/src/fixture.rs +++ b/crates/ra_db/src/fixture.rs @@ -99,7 +99,7 @@ fn with_single_file(db: &mut dyn SourceDatabaseExt, ra_fixture: &str) -> FileId let fixture = parse_single_fixture(ra_fixture); let crate_graph = if let Some(entry) = fixture { - let meta = match ParsedMeta::from(&entry.parsed_meta) { + let meta = match ParsedMeta::from(&entry.meta) { ParsedMeta::File(it) => it, _ => panic!("with_single_file only support file meta"), }; @@ -156,7 +156,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option { let source_root = std::mem::replace(&mut source_root, SourceRoot::new_local()); db.set_source_root(source_root_id, Arc::new(source_root)); diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs index 2c13f206a17c..64c0684c5f61 100644 --- a/crates/ra_ide/src/mock_analysis.rs +++ b/crates/ra_ide/src/mock_analysis.rs @@ -35,7 +35,7 @@ impl MockAnalysis { pub fn with_files(fixture: &str) -> MockAnalysis { let mut res = MockAnalysis::new(); for entry in parse_fixture(fixture) { - res.add_file(&entry.meta, &entry.text); + res.add_file(entry.meta.path().as_str(), &entry.text); } res } @@ -48,9 +48,10 @@ impl MockAnalysis { for entry in parse_fixture(fixture) { if entry.text.contains(CURSOR_MARKER) { assert!(position.is_none(), "only one marker (<|>) per fixture is allowed"); - position = Some(res.add_file_with_position(&entry.meta, &entry.text)); + position = + Some(res.add_file_with_position(&entry.meta.path().as_str(), &entry.text)); } else { - res.add_file(&entry.meta, &entry.text); + res.add_file(&entry.meta.path().as_str(), &entry.text); } } let position = position.expect("expected a marker (<|>)"); diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs index 8756ad4a3c8a..7679e9ad7c4a 100644 --- a/crates/rust-analyzer/tests/heavy_tests/support.rs +++ b/crates/rust-analyzer/tests/heavy_tests/support.rs @@ -68,7 +68,7 @@ impl<'a> Project<'a> { let mut paths = vec![]; for entry in parse_fixture(self.fixture) { - let path = tmp_dir.path().join(entry.meta); + let path = tmp_dir.path().join(entry.meta.path().as_str()); fs::create_dir_all(path.parent().unwrap()).unwrap(); fs::write(path.as_path(), entry.text.as_bytes()).unwrap(); paths.push((path, entry.text)); diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index 0c367ce71a62..6a8d06ea7a73 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -161,10 +161,8 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String { #[derive(Debug, Eq, PartialEq)] pub struct FixtureEntry { - pub meta: String, + pub meta: FixtureMeta, pub text: String, - - pub parsed_meta: FixtureMeta, } #[derive(Debug, Eq, PartialEq)] @@ -231,8 +229,8 @@ The offending line: {:?}"#, for line in lines.by_ref() { if line.starts_with("//-") { let meta = line["//-".len()..].trim().to_string(); - let parsed_meta = parse_meta(&meta); - res.push(FixtureEntry { meta, parsed_meta, text: String::new() }) + let meta = parse_meta(&meta); + res.push(FixtureEntry { meta, text: String::new() }) } else if let Some(entry) = res.last_mut() { entry.text.push_str(line); entry.text.push('\n'); From 2dde9b19943d3f9557520428c92a52d75fb1deb3 Mon Sep 17 00:00:00 2001 From: vsrs Date: Sat, 16 May 2020 13:17:21 +0300 Subject: [PATCH 005/163] Use FixtureMeta in MockAnalysis --- crates/ra_ide/src/mock_analysis.rs | 93 +++++++++++++++++++++++++----- crates/test_utils/src/lib.rs | 9 ++- 2 files changed, 85 insertions(+), 17 deletions(-) diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs index 64c0684c5f61..8d8e30714854 100644 --- a/crates/ra_ide/src/mock_analysis.rs +++ b/crates/ra_ide/src/mock_analysis.rs @@ -4,18 +4,61 @@ use std::sync::Arc; use ra_cfg::CfgOptions; use ra_db::{CrateName, Env, RelativePathBuf}; -use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; +use test_utils::{extract_offset, extract_range, parse_fixture, FixtureEntry, CURSOR_MARKER}; use crate::{ Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition::Edition2018, FileId, FilePosition, FileRange, SourceRootId, }; +#[derive(Debug)] +enum MockFileData { + Plain { path: String, content: String }, + Fixture(FixtureEntry), +} + +impl MockFileData { + fn new(path: String, content: String) -> Self { + // `Self::Plain` causes a false warning: 'variant is never constructed: `Plain` ' + // see https://github.com/rust-lang/rust/issues/69018 + MockFileData::Plain { path, content } + } + + fn path(&self) -> &str { + match self { + MockFileData::Plain { path, .. } => path.as_str(), + MockFileData::Fixture(f) => f.meta.path().as_str(), + } + } + + fn content(&self) -> &str { + match self { + MockFileData::Plain { content, .. } => content, + MockFileData::Fixture(f) => f.text.as_str(), + } + } + + fn cfg_options(&self) -> CfgOptions { + match self { + MockFileData::Fixture(f) => { + f.meta.cfg_options().map_or_else(Default::default, |o| o.clone()) + } + _ => CfgOptions::default(), + } + } +} + +impl From for MockFileData { + fn from(fixture: FixtureEntry) -> Self { + Self::Fixture(fixture) + } +} + /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis /// from a set of in-memory files. #[derive(Debug, Default)] pub struct MockAnalysis { - files: Vec<(String, String)>, + files: Vec, } impl MockAnalysis { @@ -35,7 +78,7 @@ impl MockAnalysis { pub fn with_files(fixture: &str) -> MockAnalysis { let mut res = MockAnalysis::new(); for entry in parse_fixture(fixture) { - res.add_file(entry.meta.path().as_str(), &entry.text); + res.add_file_fixture(entry); } res } @@ -48,31 +91,44 @@ impl MockAnalysis { for entry in parse_fixture(fixture) { if entry.text.contains(CURSOR_MARKER) { assert!(position.is_none(), "only one marker (<|>) per fixture is allowed"); - position = - Some(res.add_file_with_position(&entry.meta.path().as_str(), &entry.text)); + position = Some(res.add_file_fixture_with_position(entry)); } else { - res.add_file(&entry.meta.path().as_str(), &entry.text); + res.add_file_fixture(entry); } } let position = position.expect("expected a marker (<|>)"); (res, position) } + pub fn add_file_fixture(&mut self, fixture: FixtureEntry) -> FileId { + let file_id = self.next_id(); + self.files.push(MockFileData::from(fixture)); + file_id + } + + pub fn add_file_fixture_with_position(&mut self, mut fixture: FixtureEntry) -> FilePosition { + let (offset, text) = extract_offset(&fixture.text); + fixture.text = text; + let file_id = self.next_id(); + self.files.push(MockFileData::from(fixture)); + FilePosition { file_id, offset } + } + pub fn add_file(&mut self, path: &str, text: &str) -> FileId { - let file_id = FileId((self.files.len() + 1) as u32); - self.files.push((path.to_string(), text.to_string())); + let file_id = self.next_id(); + self.files.push(MockFileData::new(path.to_string(), text.to_string())); file_id } pub fn add_file_with_position(&mut self, path: &str, text: &str) -> FilePosition { let (offset, text) = extract_offset(text); - let file_id = FileId((self.files.len() + 1) as u32); - self.files.push((path.to_string(), text)); + let file_id = self.next_id(); + self.files.push(MockFileData::new(path.to_string(), text)); FilePosition { file_id, offset } } pub fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange { let (range, text) = extract_range(text); - let file_id = FileId((self.files.len() + 1) as u32); - self.files.push((path.to_string(), text)); + let file_id = self.next_id(); + self.files.push(MockFileData::new(path.to_string(), text)); FileRange { file_id, range } } pub fn id_of(&self, path: &str) -> FileId { @@ -80,7 +136,7 @@ impl MockAnalysis { .files .iter() .enumerate() - .find(|(_, (p, _text))| path == p) + .find(|(_, data)| path == data.path()) .expect("no file in this mock"); FileId(idx as u32 + 1) } @@ -91,11 +147,12 @@ impl MockAnalysis { change.add_root(source_root, true); let mut crate_graph = CrateGraph::default(); let mut root_crate = None; - for (i, (path, contents)) in self.files.into_iter().enumerate() { + for (i, data) in self.files.into_iter().enumerate() { + let path = data.path(); assert!(path.starts_with('/')); let path = RelativePathBuf::from_path(&path[1..]).unwrap(); + let cfg_options = data.cfg_options(); let file_id = FileId(i as u32 + 1); - let cfg_options = CfgOptions::default(); if path == "/lib.rs" || path == "/main.rs" { root_crate = Some(crate_graph.add_crate_root( file_id, @@ -123,7 +180,7 @@ impl MockAnalysis { .unwrap(); } } - change.add_file(source_root, file_id, path, Arc::new(contents)); + change.add_file(source_root, file_id, path, Arc::new(data.content().to_owned())); } change.set_crate_graph(crate_graph); host.apply_change(change); @@ -132,6 +189,10 @@ impl MockAnalysis { pub fn analysis(self) -> Analysis { self.analysis_host().analysis() } + + fn next_id(&self) -> FileId { + FileId((self.files.len() + 1) as u32) + } } /// Creates analysis from a multi-file fixture, returns positions marked with <|>. diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index 6a8d06ea7a73..584ca5c39155 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -176,7 +176,7 @@ pub struct FileMeta { pub path: RelativePathBuf, pub krate: Option, pub deps: Vec, - pub cfg: ra_cfg::CfgOptions, + pub cfg: CfgOptions, pub edition: Option, pub env: FxHashMap, } @@ -188,6 +188,13 @@ impl FixtureMeta { FixtureMeta::File(f) => &f.path, } } + + pub fn cfg_options(&self) -> Option<&CfgOptions> { + match self { + FixtureMeta::File(f) => Some(&f.cfg), + _ => None, + } + } } /// Parses text which looks like this: From 2c00bd8c6a9476dbac427c84941fe6f54a8a95b1 Mon Sep 17 00:00:00 2001 From: vsrs Date: Sat, 16 May 2020 15:23:43 +0300 Subject: [PATCH 006/163] Propogate fixture meta to AnalysisHost Except crate name. --- crates/ra_db/src/fixture.rs | 10 ++----- crates/ra_db/src/input.rs | 15 ++++++++++ crates/ra_ide/src/call_hierarchy.rs | 29 +++++++++++++++++++ crates/ra_ide/src/mock_analysis.rs | 31 ++++++++++++++++---- crates/test_utils/src/lib.rs | 44 +++++++++++++++++++++++++++-- 5 files changed, 113 insertions(+), 16 deletions(-) diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs index 8b62fe9aae93..fd535ab15d4e 100644 --- a/crates/ra_db/src/fixture.rs +++ b/crates/ra_db/src/fixture.rs @@ -254,20 +254,14 @@ impl From<&FixtureMeta> for ParsedMeta { } FixtureMeta::File(f) => Self::File(FileMeta { path: f.path.to_owned().into(), - krate: f.krate.to_owned().into(), + krate: f.crate_name.to_owned().into(), deps: f.deps.to_owned(), cfg: f.cfg.to_owned(), edition: f .edition .as_ref() .map_or(Edition::Edition2018, |v| Edition::from_str(&v).unwrap()), - env: { - let mut env = Env::default(); - for (k, v) in &f.env { - env.set(&k, v.to_owned()); - } - env - }, + env: Env::from(f.env.iter()), }), } } diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index ab14e2d5e64d..4d2d3b48a0c0 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs @@ -311,6 +311,21 @@ impl fmt::Display for Edition { } } +impl<'a, T> From for Env +where + T: Iterator, +{ + fn from(iter: T) -> Self { + let mut result = Self::default(); + + for (k, v) in iter { + result.entries.insert(k.to_owned(), v.to_owned()); + } + + result + } +} + impl Env { pub fn set(&mut self, env: &str, value: String) { self.entries.insert(env.to_owned(), value); diff --git a/crates/ra_ide/src/call_hierarchy.rs b/crates/ra_ide/src/call_hierarchy.rs index 85d1f0cb15e7..defd8176ff87 100644 --- a/crates/ra_ide/src/call_hierarchy.rs +++ b/crates/ra_ide/src/call_hierarchy.rs @@ -245,6 +245,35 @@ mod tests { ); } + #[test] + fn test_call_hierarchy_in_tests_mod() { + check_hierarchy( + r#" + //- /lib.rs cfg:test + fn callee() {} + fn caller1() { + call<|>ee(); + } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + fn test_caller() { + callee(); + } + } + "#, + "callee FN_DEF FileId(1) 0..14 3..9", + &[ + "caller1 FN_DEF FileId(1) 15..45 18..25 : [34..40]", + "test_caller FN_DEF FileId(1) 93..147 108..119 : [132..138]", + ], + &[], + ); + } + #[test] fn test_call_hierarchy_in_different_files() { check_hierarchy( diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs index 8d8e30714854..ad78d2d93fd4 100644 --- a/crates/ra_ide/src/mock_analysis.rs +++ b/crates/ra_ide/src/mock_analysis.rs @@ -1,5 +1,6 @@ //! FIXME: write short doc here +use std::str::FromStr; use std::sync::Arc; use ra_cfg::CfgOptions; @@ -7,8 +8,8 @@ use ra_db::{CrateName, Env, RelativePathBuf}; use test_utils::{extract_offset, extract_range, parse_fixture, FixtureEntry, CURSOR_MARKER}; use crate::{ - Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition::Edition2018, FileId, FilePosition, - FileRange, SourceRootId, + Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange, + SourceRootId, }; #[derive(Debug)] @@ -46,6 +47,22 @@ impl MockFileData { _ => CfgOptions::default(), } } + + fn edition(&self) -> Edition { + match self { + MockFileData::Fixture(f) => { + f.meta.edition().map_or(Edition::Edition2018, |v| Edition::from_str(v).unwrap()) + } + _ => Edition::Edition2018, + } + } + + fn env(&self) -> Env { + match self { + MockFileData::Fixture(f) => Env::from(f.meta.env()), + _ => Env::default(), + } + } } impl From for MockFileData { @@ -153,13 +170,15 @@ impl MockAnalysis { let path = RelativePathBuf::from_path(&path[1..]).unwrap(); let cfg_options = data.cfg_options(); let file_id = FileId(i as u32 + 1); + let edition = data.edition(); + let env = data.env(); if path == "/lib.rs" || path == "/main.rs" { root_crate = Some(crate_graph.add_crate_root( file_id, - Edition2018, + edition, None, cfg_options, - Env::default(), + env, Default::default(), Default::default(), )); @@ -167,10 +186,10 @@ impl MockAnalysis { let crate_name = path.parent().unwrap().file_name().unwrap(); let other_crate = crate_graph.add_crate_root( file_id, - Edition2018, + edition, Some(CrateName::new(crate_name).unwrap()), cfg_options, - Env::default(), + env, Default::default(), Default::default(), ); diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index 584ca5c39155..e7fa201e9e7f 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -174,7 +174,7 @@ pub enum FixtureMeta { #[derive(Debug, Eq, PartialEq)] pub struct FileMeta { pub path: RelativePathBuf, - pub krate: Option, + pub crate_name: Option, pub deps: Vec, pub cfg: CfgOptions, pub edition: Option, @@ -189,12 +189,52 @@ impl FixtureMeta { } } + pub fn crate_name(&self) -> Option<&String> { + match self { + FixtureMeta::File(f) => f.crate_name.as_ref(), + _ => None, + } + } + pub fn cfg_options(&self) -> Option<&CfgOptions> { match self { FixtureMeta::File(f) => Some(&f.cfg), _ => None, } } + + pub fn edition(&self) -> Option<&String> { + match self { + FixtureMeta::File(f) => f.edition.as_ref(), + _ => None, + } + } + + pub fn env(&self) -> impl Iterator { + struct EnvIter<'a> { + iter: Option>, + } + + impl<'a> EnvIter<'a> { + fn new(meta: &'a FixtureMeta) -> Self { + Self { + iter: match meta { + FixtureMeta::File(f) => Some(f.env.iter()), + _ => None, + }, + } + } + } + + impl<'a> Iterator for EnvIter<'a> { + type Item = (&'a String, &'a String); + fn next(&mut self) -> Option { + self.iter.as_mut().and_then(|i| i.next()) + } + } + + EnvIter::new(self) + } } /// Parses text which looks like this: @@ -289,7 +329,7 @@ fn parse_meta(meta: &str) -> FixtureMeta { } } - FixtureMeta::File(FileMeta { path, krate, deps, edition, cfg, env }) + FixtureMeta::File(FileMeta { path, crate_name: krate, deps, edition, cfg, env }) } fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> { From 7e9c7ac4ee5c9295d811670277bc1aeb9775998c Mon Sep 17 00:00:00 2001 From: vsrs Date: Sat, 16 May 2020 16:56:41 +0300 Subject: [PATCH 007/163] smoke test --- crates/test_utils/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index e7fa201e9e7f..fc5ae8f0737f 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -426,6 +426,12 @@ fn parse_fixture_gets_full_meta() { let parsed = &parsed[0]; assert_eq!("\n", parsed.text); + + let meta = &parsed.meta; + assert_eq!("foo", meta.crate_name().unwrap()); + assert_eq!("/lib.rs", meta.path()); + assert!(meta.cfg_options().is_some()); + assert_eq!(2, meta.env().count()); } /// Same as `parse_fixture`, except it allow empty fixture From cd45c73b660f85acc9b564e536bc407f0836891d Mon Sep 17 00:00:00 2001 From: vsrs Date: Sat, 16 May 2020 17:25:12 +0300 Subject: [PATCH 008/163] JFF, ra_fixture arg name for a code highlighting --- crates/test_utils/src/lib.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index fc5ae8f0737f..45abfead99c8 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -245,8 +245,8 @@ impl FixtureMeta { /// line 2 /// // - other meta /// ``` -pub fn parse_fixture(fixture: &str) -> Vec { - let fixture = indent_first_line(fixture); +pub fn parse_fixture(ra_fixture: &str) -> Vec { + let fixture = indent_first_line(ra_fixture); let margin = fixture_margin(&fixture); let mut lines = fixture @@ -418,14 +418,16 @@ struct Bar; #[test] fn parse_fixture_gets_full_meta() { - let fixture = r" + let parsed = parse_fixture( + r" //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo - "; - let parsed = parse_fixture(fixture); + mod m; + ", + ); assert_eq!(1, parsed.len()); let parsed = &parsed[0]; - assert_eq!("\n", parsed.text); + assert_eq!("mod m;\n\n", parsed.text); let meta = &parsed.meta; assert_eq!("foo", meta.crate_name().unwrap()); @@ -435,12 +437,12 @@ fn parse_fixture_gets_full_meta() { } /// Same as `parse_fixture`, except it allow empty fixture -pub fn parse_single_fixture(fixture: &str) -> Option { - if !fixture.lines().any(|it| it.trim_start().starts_with("//-")) { +pub fn parse_single_fixture(ra_fixture: &str) -> Option { + if !ra_fixture.lines().any(|it| it.trim_start().starts_with("//-")) { return None; } - let fixtures = parse_fixture(fixture); + let fixtures = parse_fixture(ra_fixture); if fixtures.len() > 1 { panic!("too many fixtures"); } From 47ce5ea581f3fe53a31e166c5feac6c64a8a97e4 Mon Sep 17 00:00:00 2001 From: George Fraser Date: Tue, 12 May 2020 21:58:51 -0700 Subject: [PATCH 009/163] Color attribute functions --- crates/ra_ide/src/snapshots/highlighting.html | 2 +- crates/ra_ide/src/syntax_highlighting.rs | 4 +++- crates/ra_ide/src/syntax_highlighting/tags.rs | 6 +++++- crates/rust-analyzer/src/semantic_tokens.rs | 1 + crates/rust-analyzer/src/to_proto.rs | 1 + editors/code/package.json | 7 +++++++ editors/code/rust.tmGrammar.json | 7 ++++++- 7 files changed, 24 insertions(+), 4 deletions(-) diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html index 635fe5cf9ddf..2ceadf2fcbf7 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/src/snapshots/highlighting.html @@ -27,7 +27,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .keyword.unsafe { color: #BC8383; font-weight: bold; } .control { font-style: italic; } -
#[derive(Clone, Debug)]
+
#[derive(Clone, Debug)]
 struct Foo {
     pub x: i32,
     pub y: i32,
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index be57eeb0abea..b55cf748dc08 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -361,7 +361,9 @@ fn highlight_element(
         }
 
         // Highlight references like the definitions they resolve to
-        NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => return None,
+        NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => {
+            Highlight::from(HighlightTag::Function) | HighlightModifier::Attribute
+        }
         NAME_REF => {
             let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
             match classify_name_ref(sema, &name_ref) {
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index be1a0f12b25b..33e6619ec789 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -45,8 +45,10 @@ pub enum HighlightTag {
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
 #[repr(u8)]
 pub enum HighlightModifier {
+    /// Used to differentiate individual elements within attributes.
+    Attribute = 0,
     /// Used with keywords like `if` and `break`.
-    ControlFlow = 0,
+    ControlFlow,
     /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is
     /// not.
     Definition,
@@ -95,6 +97,7 @@ impl fmt::Display for HighlightTag {
 
 impl HighlightModifier {
     const ALL: &'static [HighlightModifier] = &[
+        HighlightModifier::Attribute,
         HighlightModifier::ControlFlow,
         HighlightModifier::Definition,
         HighlightModifier::Mutable,
@@ -103,6 +106,7 @@ impl HighlightModifier {
 
     fn as_str(self) -> &'static str {
         match self {
+            HighlightModifier::Attribute => "attribute",
             HighlightModifier::ControlFlow => "control",
             HighlightModifier::Definition => "declaration",
             HighlightModifier::Mutable => "mutable",
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 2dc5cb1196fc..90a6257ee8e1 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -67,6 +67,7 @@ define_semantic_token_modifiers![
     (CONTROL_FLOW, "controlFlow"),
     (MUTABLE, "mutable"),
     (UNSAFE, "unsafe"),
+    (ATTRIBUTE_MODIFIER, "attribute"),
 ];
 
 #[derive(Default)]
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index a8e2e535f9ab..09343f92517f 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -296,6 +296,7 @@ fn semantic_token_type_and_modifiers(
 
     for modifier in highlight.modifiers.iter() {
         let modifier = match modifier {
+            HighlightModifier::Attribute => semantic_tokens::ATTRIBUTE_MODIFIER,
             HighlightModifier::Definition => lsp_types::SemanticTokenModifier::DECLARATION,
             HighlightModifier::ControlFlow => semantic_tokens::CONTROL_FLOW,
             HighlightModifier::Mutable => semantic_tokens::MUTABLE,
diff --git a/editors/code/package.json b/editors/code/package.json
index d899f60e333c..d078f7472201 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -603,6 +603,10 @@
             }
         ],
         "semanticTokenModifiers": [
+            {
+                "id": "attribute",
+                "description": "Style for elements within attributes"
+            },
             {
                 "id": "constant",
                 "description": "Style for compile-time constants"
@@ -630,6 +634,9 @@
                     "attribute": [
                         "meta.attribute.rust"
                     ],
+                    "function.attribute": [
+                        "entity.name.function.attribute.rust"
+                    ],
                     "builtinType": [
                         "support.type.primitive.rust"
                     ],
diff --git a/editors/code/rust.tmGrammar.json b/editors/code/rust.tmGrammar.json
index aa0811326a0e..cdcd557dc173 100644
--- a/editors/code/rust.tmGrammar.json
+++ b/editors/code/rust.tmGrammar.json
@@ -75,8 +75,13 @@
 		{
 			"comment": "Attribute",
 			"name": "meta.attribute.rust",
-			"begin": "#\\!?\\[",
+			"begin": "#\\!?\\[(\\w*)",
 			"end": "\\]",
+			"captures": {
+				"1": {
+					"name": "entity.name.function.attribute.rust"
+				}
+			},
 			"patterns": [
 				{
 					"include": "#string_literal"

From e18f00882dd75cd5d9e89fc76e1cf67bbeaf36c5 Mon Sep 17 00:00:00 2001
From: vain0x 
Date: Sat, 9 May 2020 16:14:33 +0900
Subject: [PATCH 010/163] Add call postfix completion

To make it easier to wrap an expression with Ok/Some/Rc::new etc.
---
 .../ra_ide/src/completion/complete_postfix.rs | 59 +++++++++++++++++++
 1 file changed, 59 insertions(+)

diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index f2a52a407a97..02e660ca8e4b 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -184,6 +184,16 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
         &format!("dbg!({})", receiver_text),
     )
     .add_to(acc);
+
+    postfix_snippet(
+        ctx,
+        cap,
+        &dot_receiver,
+        "call",
+        "function(expr)",
+        &format!("${{1}}({})", receiver_text),
+    )
+    .add_to(acc);
 }
 
 fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
@@ -255,6 +265,13 @@ mod tests {
                 insert: "Box::new(bar)",
                 detail: "Box::new(expr)",
             },
+            CompletionItem {
+                label: "call",
+                source_range: 89..89,
+                delete: 85..89,
+                insert: "${1}(bar)",
+                detail: "function(expr)",
+            },
             CompletionItem {
                 label: "dbg",
                 source_range: 89..89,
@@ -334,6 +351,13 @@ mod tests {
                 insert: "Box::new(bar)",
                 detail: "Box::new(expr)",
             },
+            CompletionItem {
+                label: "call",
+                source_range: 210..210,
+                delete: 206..210,
+                insert: "${1}(bar)",
+                detail: "function(expr)",
+            },
             CompletionItem {
                 label: "dbg",
                 source_range: 210..210,
@@ -413,6 +437,13 @@ mod tests {
                 insert: "Box::new(bar)",
                 detail: "Box::new(expr)",
             },
+            CompletionItem {
+                label: "call",
+                source_range: 211..211,
+                delete: 207..211,
+                insert: "${1}(bar)",
+                detail: "function(expr)",
+            },
             CompletionItem {
                 label: "dbg",
                 source_range: 211..211,
@@ -487,6 +518,13 @@ mod tests {
                 insert: "Box::new(bar)",
                 detail: "Box::new(expr)",
             },
+            CompletionItem {
+                label: "call",
+                source_range: 91..91,
+                delete: 87..91,
+                insert: "${1}(bar)",
+                detail: "function(expr)",
+            },
             CompletionItem {
                 label: "dbg",
                 source_range: 91..91,
@@ -546,6 +584,13 @@ mod tests {
                 insert: "Box::new(42)",
                 detail: "Box::new(expr)",
             },
+            CompletionItem {
+                label: "call",
+                source_range: 52..52,
+                delete: 49..52,
+                insert: "${1}(42)",
+                detail: "function(expr)",
+            },
             CompletionItem {
                 label: "dbg",
                 source_range: 52..52,
@@ -607,6 +652,13 @@ mod tests {
                 insert: "Box::new(bar)",
                 detail: "Box::new(expr)",
             },
+            CompletionItem {
+                label: "call",
+                source_range: 149..150,
+                delete: 145..150,
+                insert: "${1}(bar)",
+                detail: "function(expr)",
+            },
             CompletionItem {
                 label: "dbg",
                 source_range: 149..150,
@@ -666,6 +718,13 @@ mod tests {
                 insert: "Box::new(&&&&42)",
                 detail: "Box::new(expr)",
             },
+            CompletionItem {
+                label: "call",
+                source_range: 56..56,
+                delete: 49..56,
+                insert: "${1}(&&&&42)",
+                detail: "function(expr)",
+            },
             CompletionItem {
                 label: "dbg",
                 source_range: 56..56,

From 8ee40ccbe963a0a5e3e998c1652378e1035dc40d Mon Sep 17 00:00:00 2001
From: vsrs 
Date: Wed, 20 May 2020 21:03:49 +0300
Subject: [PATCH 011/163] vscode client side tests

---
 .vscode/launch.json                           |  34 +
 .vscode/tasks.json                            |  12 +
 editors/code/.vscodeignore                    |   2 +-
 editors/code/package-lock.json                | 768 ++++++++++++++++++
 editors/code/package.json                     |  15 +-
 editors/code/rollup.config.js                 |   4 +-
 editors/code/src/cargo.ts                     |  63 +-
 editors/code/tests/runTests.ts                |  46 ++
 editors/code/tests/unit/index.ts              |  38 +
 editors/code/tests/unit/launch_config.test.ts |  52 ++
 editors/code/tsconfig.json                    |   9 +-
 11 files changed, 1011 insertions(+), 32 deletions(-)
 create mode 100644 editors/code/tests/runTests.ts
 create mode 100644 editors/code/tests/unit/index.ts
 create mode 100644 editors/code/tests/unit/launch_config.test.ts

diff --git a/.vscode/launch.json b/.vscode/launch.json
index 6a2fff906510..8ca27d878363 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -70,6 +70,28 @@
         "__RA_LSP_SERVER_DEBUG": "${workspaceFolder}/target/release/rust-analyzer"
       }
     },
+    {
+      // Used for testing the extension with a local build of the LSP server (in `target/release`)
+      // with all other extendions loaded.
+      "name": "Run With Extensions",
+      "type": "extensionHost",
+      "request": "launch",
+      "runtimeExecutable": "${execPath}",
+      "args": [
+        "--disable-extension", "matklad.rust-analyzer",
+        "--extensionDevelopmentPath=${workspaceFolder}/editors/code"
+      ],
+      "outFiles": [
+        "${workspaceFolder}/editors/code/out/**/*.js"
+      ],
+      "preLaunchTask": "Build Server (Release) and Extension",
+      "skipFiles": [
+        "/**/*.js"
+      ],
+      "env": {
+        "__RA_LSP_SERVER_DEBUG": "${workspaceFolder}/target/release/rust-analyzer"
+      }
+    },
     {
       // Used to attach LLDB to a running LSP server.
       // NOTE: Might require root permissions. For this run:
@@ -87,5 +109,17 @@
         "rust"
       ]
     },
+    {
+      "name": "Run Unit Tests",
+      "type": "extensionHost",
+      "request": "launch",
+      "runtimeExecutable": "${execPath}",
+      "args": [
+          "--extensionDevelopmentPath=${workspaceFolder}/editors/code",
+          "--extensionTestsPath=${workspaceFolder}/editors/code/out/tests/unit" ],
+      "sourceMaps": true,
+      "outFiles": [ "${workspaceFolder}/editors/code/out/tests/unit/**/*.js" ],
+      "preLaunchTask": "Pretest"
+    }
   ]
 }
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 0969ce89a107..a25dff19e415 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -40,6 +40,18 @@
       "command": "cargo build --release --package rust-analyzer",
       "problemMatcher": "$rustc"
     },
+    {
+      "label": "Pretest",
+      "group": "build",
+      "isBackground": false,
+      "type": "npm",
+      "script": "pretest",
+      "path": "editors/code/",
+      "problemMatcher": {
+        "base": "$tsc",
+        "fileLocation": ["relative", "${workspaceFolder}/editors/code/"]
+      }
+    },
 
     {
       "label": "Build Server and Extension",
diff --git a/editors/code/.vscodeignore b/editors/code/.vscodeignore
index 257b744bfeaa..7149ab799230 100644
--- a/editors/code/.vscodeignore
+++ b/editors/code/.vscodeignore
@@ -1,5 +1,5 @@
 **
-!out/main.js
+!out/src/main.js
 !package.json
 !package-lock.json
 !ra_syntax_tree.tmGrammar.json
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 06178990f1a7..c322b02c8472 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -81,12 +81,41 @@
             "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
             "dev": true
         },
+        "@types/events": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
+            "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==",
+            "dev": true
+        },
+        "@types/glob": {
+            "version": "7.1.1",
+            "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
+            "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==",
+            "dev": true,
+            "requires": {
+                "@types/events": "*",
+                "@types/minimatch": "*",
+                "@types/node": "*"
+            }
+        },
         "@types/json-schema": {
             "version": "7.0.4",
             "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz",
             "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==",
             "dev": true
         },
+        "@types/minimatch": {
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
+            "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
+            "dev": true
+        },
+        "@types/mocha": {
+            "version": "7.0.2",
+            "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-7.0.2.tgz",
+            "integrity": "sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==",
+            "dev": true
+        },
         "@types/node": {
             "version": "12.12.39",
             "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.39.tgz",
@@ -200,6 +229,15 @@
             "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==",
             "dev": true
         },
+        "agent-base": {
+            "version": "4.3.0",
+            "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
+            "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
+            "dev": true,
+            "requires": {
+                "es6-promisify": "^5.0.0"
+            }
+        },
         "ajv": {
             "version": "6.12.0",
             "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
@@ -212,6 +250,12 @@
                 "uri-js": "^4.2.2"
             }
         },
+        "ansi-colors": {
+            "version": "3.2.3",
+            "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
+            "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
+            "dev": true
+        },
         "ansi-escapes": {
             "version": "4.3.1",
             "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz",
@@ -244,6 +288,16 @@
                 "color-convert": "^1.9.0"
             }
         },
+        "anymatch": {
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
+            "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+            "dev": true,
+            "requires": {
+                "normalize-path": "^3.0.0",
+                "picomatch": "^2.0.4"
+            }
+        },
         "argparse": {
             "version": "1.0.10",
             "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@@ -283,6 +337,12 @@
             "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
             "dev": true
         },
+        "binary-extensions": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
+            "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==",
+            "dev": true
+        },
         "boolbase": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
@@ -308,6 +368,12 @@
                 "fill-range": "^7.0.1"
             }
         },
+        "browser-stdout": {
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+            "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+            "dev": true
+        },
         "buffer-crc32": {
             "version": "0.2.13",
             "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
@@ -326,6 +392,12 @@
             "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
             "dev": true
         },
+        "camelcase": {
+            "version": "5.3.1",
+            "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+            "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+            "dev": true
+        },
         "chalk": {
             "version": "2.4.2",
             "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -357,6 +429,22 @@
                 "parse5": "^3.0.1"
             }
         },
+        "chokidar": {
+            "version": "3.3.0",
+            "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz",
+            "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==",
+            "dev": true,
+            "requires": {
+                "anymatch": "~3.1.1",
+                "braces": "~3.0.2",
+                "fsevents": "~2.1.1",
+                "glob-parent": "~5.1.0",
+                "is-binary-path": "~2.1.0",
+                "is-glob": "~4.0.1",
+                "normalize-path": "~3.0.0",
+                "readdirp": "~3.2.0"
+            }
+        },
         "cli-cursor": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
@@ -372,6 +460,42 @@
             "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
             "dev": true
         },
+        "cliui": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+            "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+            "dev": true,
+            "requires": {
+                "string-width": "^3.1.0",
+                "strip-ansi": "^5.2.0",
+                "wrap-ansi": "^5.1.0"
+            },
+            "dependencies": {
+                "emoji-regex": {
+                    "version": "7.0.3",
+                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+                    "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+                    "dev": true
+                },
+                "is-fullwidth-code-point": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+                    "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+                    "dev": true
+                },
+                "string-width": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+                    "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+                    "dev": true,
+                    "requires": {
+                        "emoji-regex": "^7.0.1",
+                        "is-fullwidth-code-point": "^2.0.0",
+                        "strip-ansi": "^5.1.0"
+                    }
+                }
+            }
+        },
         "color-convert": {
             "version": "1.9.3",
             "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -468,12 +592,27 @@
                 "ms": "^2.1.1"
             }
         },
+        "decamelize": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+            "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+            "dev": true
+        },
         "deep-is": {
             "version": "0.1.3",
             "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
             "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
             "dev": true
         },
+        "define-properties": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+            "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+            "dev": true,
+            "requires": {
+                "object-keys": "^1.0.12"
+            }
+        },
         "delayed-stream": {
             "version": "1.0.0",
             "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -486,6 +625,12 @@
             "integrity": "sha1-OjYof1A05pnnV3kBBSwubJQlFjE=",
             "dev": true
         },
+        "diff": {
+            "version": "3.5.0",
+            "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
+            "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
+            "dev": true
+        },
         "doctrine": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -562,6 +707,51 @@
             "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
             "dev": true
         },
+        "es-abstract": {
+            "version": "1.17.5",
+            "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
+            "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
+            "dev": true,
+            "requires": {
+                "es-to-primitive": "^1.2.1",
+                "function-bind": "^1.1.1",
+                "has": "^1.0.3",
+                "has-symbols": "^1.0.1",
+                "is-callable": "^1.1.5",
+                "is-regex": "^1.0.5",
+                "object-inspect": "^1.7.0",
+                "object-keys": "^1.1.1",
+                "object.assign": "^4.1.0",
+                "string.prototype.trimleft": "^2.1.1",
+                "string.prototype.trimright": "^2.1.1"
+            }
+        },
+        "es-to-primitive": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+            "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+            "dev": true,
+            "requires": {
+                "is-callable": "^1.1.4",
+                "is-date-object": "^1.0.1",
+                "is-symbol": "^1.0.2"
+            }
+        },
+        "es6-promise": {
+            "version": "4.2.8",
+            "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
+            "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
+            "dev": true
+        },
+        "es6-promisify": {
+            "version": "5.0.0",
+            "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+            "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
+            "dev": true,
+            "requires": {
+                "es6-promise": "^4.0.3"
+            }
+        },
         "escape-string-regexp": {
             "version": "1.0.5",
             "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@@ -764,6 +954,24 @@
                 "to-regex-range": "^5.0.1"
             }
         },
+        "find-up": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+            "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+            "dev": true,
+            "requires": {
+                "locate-path": "^3.0.0"
+            }
+        },
+        "flat": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
+            "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
+            "dev": true,
+            "requires": {
+                "is-buffer": "~2.0.3"
+            }
+        },
         "flat-cache": {
             "version": "2.0.1",
             "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
@@ -805,12 +1013,24 @@
             "dev": true,
             "optional": true
         },
+        "function-bind": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+            "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+            "dev": true
+        },
         "functional-red-black-tree": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
             "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
             "dev": true
         },
+        "get-caller-file": {
+            "version": "2.0.5",
+            "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+            "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+            "dev": true
+        },
         "glob": {
             "version": "7.1.6",
             "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
@@ -843,12 +1063,39 @@
                 "type-fest": "^0.8.1"
             }
         },
+        "growl": {
+            "version": "1.10.5",
+            "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+            "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+            "dev": true
+        },
+        "has": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+            "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+            "dev": true,
+            "requires": {
+                "function-bind": "^1.1.1"
+            }
+        },
         "has-flag": {
             "version": "3.0.0",
             "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
             "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
             "dev": true
         },
+        "has-symbols": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+            "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
+            "dev": true
+        },
+        "he": {
+            "version": "1.2.0",
+            "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+            "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+            "dev": true
+        },
         "htmlparser2": {
             "version": "3.10.1",
             "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
@@ -863,6 +1110,54 @@
                 "readable-stream": "^3.1.1"
             }
         },
+        "http-proxy-agent": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz",
+            "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==",
+            "dev": true,
+            "requires": {
+                "agent-base": "4",
+                "debug": "3.1.0"
+            },
+            "dependencies": {
+                "debug": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+                    "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "2.0.0"
+                    }
+                },
+                "ms": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+                    "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+                    "dev": true
+                }
+            }
+        },
+        "https-proxy-agent": {
+            "version": "2.2.4",
+            "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
+            "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
+            "dev": true,
+            "requires": {
+                "agent-base": "^4.3.0",
+                "debug": "^3.1.0"
+            },
+            "dependencies": {
+                "debug": {
+                    "version": "3.2.6",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+                    "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "^2.1.1"
+                    }
+                }
+            }
+        },
         "iconv-lite": {
             "version": "0.4.24",
             "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -992,6 +1287,33 @@
                 }
             }
         },
+        "is-binary-path": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+            "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+            "dev": true,
+            "requires": {
+                "binary-extensions": "^2.0.0"
+            }
+        },
+        "is-buffer": {
+            "version": "2.0.4",
+            "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
+            "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
+            "dev": true
+        },
+        "is-callable": {
+            "version": "1.1.5",
+            "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
+            "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
+            "dev": true
+        },
+        "is-date-object": {
+            "version": "1.0.2",
+            "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+            "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
+            "dev": true
+        },
         "is-extglob": {
             "version": "2.1.1",
             "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -1040,6 +1362,24 @@
                 "@types/estree": "0.0.39"
             }
         },
+        "is-regex": {
+            "version": "1.0.5",
+            "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
+            "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
+            "dev": true,
+            "requires": {
+                "has": "^1.0.3"
+            }
+        },
+        "is-symbol": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+            "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+            "dev": true,
+            "requires": {
+                "has-symbols": "^1.0.1"
+            }
+        },
         "isexe": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -1099,12 +1439,31 @@
                 "uc.micro": "^1.0.1"
             }
         },
+        "locate-path": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+            "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+            "dev": true,
+            "requires": {
+                "p-locate": "^3.0.0",
+                "path-exists": "^3.0.0"
+            }
+        },
         "lodash": {
             "version": "4.17.15",
             "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
             "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
             "dev": true
         },
+        "log-symbols": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
+            "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
+            "dev": true,
+            "requires": {
+                "chalk": "^2.4.2"
+            }
+        },
         "lru-cache": {
             "version": "4.1.5",
             "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
@@ -1212,6 +1571,93 @@
                 "minimist": "^1.2.5"
             }
         },
+        "mocha": {
+            "version": "7.1.2",
+            "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.2.tgz",
+            "integrity": "sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA==",
+            "dev": true,
+            "requires": {
+                "ansi-colors": "3.2.3",
+                "browser-stdout": "1.3.1",
+                "chokidar": "3.3.0",
+                "debug": "3.2.6",
+                "diff": "3.5.0",
+                "escape-string-regexp": "1.0.5",
+                "find-up": "3.0.0",
+                "glob": "7.1.3",
+                "growl": "1.10.5",
+                "he": "1.2.0",
+                "js-yaml": "3.13.1",
+                "log-symbols": "3.0.0",
+                "minimatch": "3.0.4",
+                "mkdirp": "0.5.5",
+                "ms": "2.1.1",
+                "node-environment-flags": "1.0.6",
+                "object.assign": "4.1.0",
+                "strip-json-comments": "2.0.1",
+                "supports-color": "6.0.0",
+                "which": "1.3.1",
+                "wide-align": "1.1.3",
+                "yargs": "13.3.2",
+                "yargs-parser": "13.1.2",
+                "yargs-unparser": "1.6.0"
+            },
+            "dependencies": {
+                "debug": {
+                    "version": "3.2.6",
+                    "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+                    "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+                    "dev": true,
+                    "requires": {
+                        "ms": "^2.1.1"
+                    }
+                },
+                "glob": {
+                    "version": "7.1.3",
+                    "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+                    "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+                    "dev": true,
+                    "requires": {
+                        "fs.realpath": "^1.0.0",
+                        "inflight": "^1.0.4",
+                        "inherits": "2",
+                        "minimatch": "^3.0.4",
+                        "once": "^1.3.0",
+                        "path-is-absolute": "^1.0.0"
+                    }
+                },
+                "mkdirp": {
+                    "version": "0.5.5",
+                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+                    "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+                    "dev": true,
+                    "requires": {
+                        "minimist": "^1.2.5"
+                    }
+                },
+                "ms": {
+                    "version": "2.1.1",
+                    "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+                    "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+                    "dev": true
+                },
+                "strip-json-comments": {
+                    "version": "2.0.1",
+                    "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+                    "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+                    "dev": true
+                },
+                "supports-color": {
+                    "version": "6.0.0",
+                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz",
+                    "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==",
+                    "dev": true,
+                    "requires": {
+                        "has-flag": "^3.0.0"
+                    }
+                }
+            }
+        },
         "ms": {
             "version": "2.1.2",
             "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -1236,11 +1682,35 @@
             "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
             "dev": true
         },
+        "node-environment-flags": {
+            "version": "1.0.6",
+            "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz",
+            "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==",
+            "dev": true,
+            "requires": {
+                "object.getownpropertydescriptors": "^2.0.3",
+                "semver": "^5.7.0"
+            },
+            "dependencies": {
+                "semver": {
+                    "version": "5.7.1",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+                    "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+                    "dev": true
+                }
+            }
+        },
         "node-fetch": {
             "version": "2.6.0",
             "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
             "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
         },
+        "normalize-path": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+            "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+            "dev": true
+        },
         "nth-check": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
@@ -1250,6 +1720,40 @@
                 "boolbase": "~1.0.0"
             }
         },
+        "object-inspect": {
+            "version": "1.7.0",
+            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
+            "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
+            "dev": true
+        },
+        "object-keys": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+            "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+            "dev": true
+        },
+        "object.assign": {
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+            "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.2",
+                "function-bind": "^1.1.1",
+                "has-symbols": "^1.0.0",
+                "object-keys": "^1.0.11"
+            }
+        },
+        "object.getownpropertydescriptors": {
+            "version": "2.1.0",
+            "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz",
+            "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.0-next.1"
+            }
+        },
         "once": {
             "version": "1.4.0",
             "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -1310,6 +1814,30 @@
                 "os-tmpdir": "^1.0.0"
             }
         },
+        "p-limit": {
+            "version": "2.3.0",
+            "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+            "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+            "dev": true,
+            "requires": {
+                "p-try": "^2.0.0"
+            }
+        },
+        "p-locate": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+            "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+            "dev": true,
+            "requires": {
+                "p-limit": "^2.0.0"
+            }
+        },
+        "p-try": {
+            "version": "2.2.0",
+            "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+            "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+            "dev": true
+        },
         "parent-module": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -1345,6 +1873,12 @@
                 "@types/node": "*"
             }
         },
+        "path-exists": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+            "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+            "dev": true
+        },
         "path-is-absolute": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -1419,12 +1953,33 @@
                 "util-deprecate": "^1.0.1"
             }
         },
+        "readdirp": {
+            "version": "3.2.0",
+            "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz",
+            "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==",
+            "dev": true,
+            "requires": {
+                "picomatch": "^2.0.4"
+            }
+        },
         "regexpp": {
             "version": "3.1.0",
             "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
             "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==",
             "dev": true
         },
+        "require-directory": {
+            "version": "2.1.1",
+            "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+            "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+            "dev": true
+        },
+        "require-main-filename": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+            "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+            "dev": true
+        },
         "resolve": {
             "version": "1.16.1",
             "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.16.1.tgz",
@@ -1503,6 +2058,12 @@
             "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
             "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
         },
+        "set-blocking": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+            "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+            "dev": true
+        },
         "shebang-command": {
             "version": "1.2.0",
             "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
@@ -1583,6 +2144,48 @@
                 }
             }
         },
+        "string.prototype.trimend": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
+            "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.5"
+            }
+        },
+        "string.prototype.trimleft": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz",
+            "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.5",
+                "string.prototype.trimstart": "^1.0.0"
+            }
+        },
+        "string.prototype.trimright": {
+            "version": "2.1.2",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz",
+            "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.5",
+                "string.prototype.trimend": "^1.0.0"
+            }
+        },
+        "string.prototype.trimstart": {
+            "version": "1.0.1",
+            "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
+            "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+            "dev": true,
+            "requires": {
+                "define-properties": "^1.1.3",
+                "es-abstract": "^1.17.5"
+            }
+        },
         "string_decoder": {
             "version": "1.3.0",
             "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
@@ -1865,6 +2468,17 @@
             "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.1.tgz",
             "integrity": "sha512-tZFUSbyjUcrh+qQf13ALX4QDdOfDX0cVaBFgy7ktJ0VwS7AW/yRKgGPSxVqqP9OCMNPdqP57O5q47w2pEwfaUg=="
         },
+        "vscode-test": {
+            "version": "1.3.0",
+            "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.3.0.tgz",
+            "integrity": "sha512-LddukcBiSU2FVTDr3c1D8lwkiOvwlJdDL2hqVbn6gIz+rpTqUCkMZSKYm94Y1v0WXlHSDQBsXyY+tchWQgGVsw==",
+            "dev": true,
+            "requires": {
+                "http-proxy-agent": "^2.1.0",
+                "https-proxy-agent": "^2.2.4",
+                "rimraf": "^2.6.3"
+            }
+        },
         "which": {
             "version": "1.3.1",
             "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
@@ -1874,12 +2488,96 @@
                 "isexe": "^2.0.0"
             }
         },
+        "which-module": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+            "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+            "dev": true
+        },
+        "wide-align": {
+            "version": "1.1.3",
+            "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+            "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+            "dev": true,
+            "requires": {
+                "string-width": "^1.0.2 || 2"
+            },
+            "dependencies": {
+                "ansi-regex": {
+                    "version": "3.0.0",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+                    "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+                    "dev": true
+                },
+                "is-fullwidth-code-point": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+                    "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+                    "dev": true
+                },
+                "string-width": {
+                    "version": "2.1.1",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+                    "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+                    "dev": true,
+                    "requires": {
+                        "is-fullwidth-code-point": "^2.0.0",
+                        "strip-ansi": "^4.0.0"
+                    }
+                },
+                "strip-ansi": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+                    "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^3.0.0"
+                    }
+                }
+            }
+        },
         "word-wrap": {
             "version": "1.2.3",
             "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
             "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
             "dev": true
         },
+        "wrap-ansi": {
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+            "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+            "dev": true,
+            "requires": {
+                "ansi-styles": "^3.2.0",
+                "string-width": "^3.0.0",
+                "strip-ansi": "^5.0.0"
+            },
+            "dependencies": {
+                "emoji-regex": {
+                    "version": "7.0.3",
+                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+                    "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+                    "dev": true
+                },
+                "is-fullwidth-code-point": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+                    "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+                    "dev": true
+                },
+                "string-width": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+                    "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+                    "dev": true,
+                    "requires": {
+                        "emoji-regex": "^7.0.1",
+                        "is-fullwidth-code-point": "^2.0.0",
+                        "strip-ansi": "^5.1.0"
+                    }
+                }
+            }
+        },
         "wrappy": {
             "version": "1.0.2",
             "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@@ -1895,12 +2593,82 @@
                 "mkdirp": "^0.5.1"
             }
         },
+        "y18n": {
+            "version": "4.0.0",
+            "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+            "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
+            "dev": true
+        },
         "yallist": {
             "version": "2.1.2",
             "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
             "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
             "dev": true
         },
+        "yargs": {
+            "version": "13.3.2",
+            "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+            "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+            "dev": true,
+            "requires": {
+                "cliui": "^5.0.0",
+                "find-up": "^3.0.0",
+                "get-caller-file": "^2.0.1",
+                "require-directory": "^2.1.1",
+                "require-main-filename": "^2.0.0",
+                "set-blocking": "^2.0.0",
+                "string-width": "^3.0.0",
+                "which-module": "^2.0.0",
+                "y18n": "^4.0.0",
+                "yargs-parser": "^13.1.2"
+            },
+            "dependencies": {
+                "emoji-regex": {
+                    "version": "7.0.3",
+                    "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+                    "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+                    "dev": true
+                },
+                "is-fullwidth-code-point": {
+                    "version": "2.0.0",
+                    "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+                    "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+                    "dev": true
+                },
+                "string-width": {
+                    "version": "3.1.0",
+                    "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+                    "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+                    "dev": true,
+                    "requires": {
+                        "emoji-regex": "^7.0.1",
+                        "is-fullwidth-code-point": "^2.0.0",
+                        "strip-ansi": "^5.1.0"
+                    }
+                }
+            }
+        },
+        "yargs-parser": {
+            "version": "13.1.2",
+            "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+            "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+            "dev": true,
+            "requires": {
+                "camelcase": "^5.0.0",
+                "decamelize": "^1.2.0"
+            }
+        },
+        "yargs-unparser": {
+            "version": "1.6.0",
+            "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz",
+            "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==",
+            "dev": true,
+            "requires": {
+                "flat": "^4.1.0",
+                "lodash": "^4.17.15",
+                "yargs": "^13.3.0"
+            }
+        },
         "yauzl": {
             "version": "2.10.0",
             "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
diff --git a/editors/code/package.json b/editors/code/package.json
index d899f60e333c..cf66deafb373 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -29,8 +29,10 @@
         "package": "vsce package -o rust-analyzer.vsix",
         "build": "tsc",
         "watch": "tsc --watch",
-        "lint": "tsfmt --verify && eslint -c .eslintrc.js --ext ts ./src",
-        "fix": " tsfmt -r       && eslint -c .eslintrc.js --ext ts ./src --fix"
+        "lint": "tsfmt --verify && eslint -c .eslintrc.js --ext ts ./src ./tests",
+        "fix": " tsfmt -r       && eslint -c .eslintrc.js --ext ts ./src ./tests --fix",
+        "pretest": "npm run build",
+        "test": "node ./out/tests/runTests.js"
     },
     "dependencies": {
         "node-fetch": "^2.6.0",
@@ -39,17 +41,22 @@
     "devDependencies": {
         "@rollup/plugin-commonjs": "^11.1.0",
         "@rollup/plugin-node-resolve": "^7.1.3",
+        "@types/glob": "^7.1.1",
+        "@types/mocha": "^7.0.2",
         "@types/node": "^12.12.39",
         "@types/node-fetch": "^2.5.7",
         "@types/vscode": "^1.44.0",
         "@typescript-eslint/eslint-plugin": "^2.33.0",
         "@typescript-eslint/parser": "^2.33.0",
         "eslint": "^6.8.0",
+        "glob": "^7.1.6",
+        "mocha": "^7.1.2",
         "rollup": "^2.10.0",
         "tslib": "^1.12.0",
         "typescript": "^3.9.2",
         "typescript-formatter": "^7.2.2",
-        "vsce": "^1.75.0"
+        "vsce": "^1.75.0",
+        "vscode-test": "^1.3.0"
     },
     "activationEvents": [
         "onLanguage:rust",
@@ -57,7 +64,7 @@
         "onCommand:rust-analyzer.collectGarbage",
         "workspaceContains:**/Cargo.toml"
     ],
-    "main": "./out/main",
+    "main": "./out/src/main",
     "contributes": {
         "taskDefinitions": [
             {
diff --git a/editors/code/rollup.config.js b/editors/code/rollup.config.js
index 2ca27694d858..58360eabbf52 100644
--- a/editors/code/rollup.config.js
+++ b/editors/code/rollup.config.js
@@ -6,7 +6,7 @@ import nodeBuiltins from 'builtin-modules';
 
 /** @type { import('rollup').RollupOptions } */
 export default {
-    input: 'out/main.js',
+    input: 'out/src/main.js',
     plugins: [
         resolve({
             preferBuiltins: true
@@ -20,7 +20,7 @@ export default {
     ],
     external: [...nodeBuiltins, 'vscode'],
     output: {
-        file: './out/main.js',
+        file: './out/src/main.js',
         format: 'cjs',
         exports: 'named'
     }
diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts
index 6a41873d0067..c3e2e5c0513d 100644
--- a/editors/code/src/cargo.ts
+++ b/editors/code/src/cargo.ts
@@ -12,14 +12,46 @@ interface CompilationArtifact {
     isTest: boolean;
 }
 
+export interface ArtifactSpec {
+    cargoArgs: string[];
+    filter?: (artifacts: CompilationArtifact[]) => CompilationArtifact[];
+}
+
+export function artifactSpec(args: readonly string[]): ArtifactSpec {
+    const cargoArgs = [...args, "--message-format=json"];
+
+    // arguments for a runnable from the quick pick should be updated.
+    // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens
+    switch (cargoArgs[0]) {
+        case "run": cargoArgs[0] = "build"; break;
+        case "test": {
+            if (cargoArgs.indexOf("--no-run") === -1) {
+                cargoArgs.push("--no-run");
+            }
+            break;
+        }
+    }
+
+    const result: ArtifactSpec = { cargoArgs: cargoArgs };
+    if (cargoArgs[0] === "test") {
+        // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests
+        // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"}
+        result.filter = (artifacts) => {
+            return artifacts.filter(a => a.isTest);
+        };
+    }
+
+    return result;
+}
+
 export class Cargo {
     constructor(readonly rootFolder: string, readonly output: OutputChannel) { }
 
-    private async artifactsFromArgs(cargoArgs: string[]): Promise {
-        const artifacts: CompilationArtifact[] = [];
+    private async getArtifacts(spec: ArtifactSpec): Promise {
+        let artifacts: CompilationArtifact[] = [];
 
         try {
-            await this.runCargo(cargoArgs,
+            await this.runCargo(spec.cargoArgs,
                 message => {
                     if (message.reason === 'compiler-artifact' && message.executable) {
                         const isBinary = message.target.crate_types.includes('bin');
@@ -43,30 +75,15 @@ export class Cargo {
             throw new Error(`Cargo invocation has failed: ${err}`);
         }
 
+        if (spec.filter) {
+            artifacts = spec.filter(artifacts);
+        }
+
         return artifacts;
     }
 
     async executableFromArgs(args: readonly string[]): Promise {
-        const cargoArgs = [...args, "--message-format=json"];
-
-        // arguments for a runnable from the quick pick should be updated.
-        // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens
-        switch (cargoArgs[0]) {
-            case "run": cargoArgs[0] = "build"; break;
-            case "test": {
-                if (cargoArgs.indexOf("--no-run") === -1) {
-                    cargoArgs.push("--no-run");
-                }
-                break;
-            }
-        }
-
-        let artifacts = await this.artifactsFromArgs(cargoArgs);
-        if (cargoArgs[0] === "test") {
-            // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests
-            // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"}
-            artifacts = artifacts.filter(a => a.isTest);
-        }
+        const artifacts = await this.getArtifacts(artifactSpec(args));
 
         if (artifacts.length === 0) {
             throw new Error('No compilation artifacts');
diff --git a/editors/code/tests/runTests.ts b/editors/code/tests/runTests.ts
new file mode 100644
index 000000000000..81600f6a81fd
--- /dev/null
+++ b/editors/code/tests/runTests.ts
@@ -0,0 +1,46 @@
+import * as path from 'path';
+import * as fs from 'fs';
+
+import { runTests } from 'vscode-test';
+
+async function main() {
+    try {
+        // The folder containing the Extension Manifest package.json
+        // Passed to `--extensionDevelopmentPath`
+        const extensionDevelopmentPath = path.resolve(__dirname, '../../');
+
+        // Minimum supported version.
+        const jsonData = fs.readFileSync(path.join(extensionDevelopmentPath, 'package.json'));
+        const json = JSON.parse(jsonData.toString());
+        let minimalVersion: string = json.engines.vscode;
+        if (minimalVersion.startsWith('^')) minimalVersion = minimalVersion.slice(1);
+
+        const launchArgs = ["--disable-extensions"];
+
+        // All test suites (either unit tests or integration tests) should be in subfolders.
+        const extensionTestsPath = path.resolve(__dirname, './unit/index');
+
+        // Run tests using the minimal supported version.
+        await runTests({
+            version: minimalVersion,
+            launchArgs,
+            extensionDevelopmentPath,
+            extensionTestsPath
+        });
+
+        // and the latest one
+        await runTests({
+            version: 'stable',
+            launchArgs,
+            extensionDevelopmentPath,
+            extensionTestsPath
+        });
+
+    } catch (err) {
+        // eslint-disable-next-line no-console
+        console.error('Failed to run tests', err);
+        process.exit(1);
+    }
+}
+
+main();
diff --git a/editors/code/tests/unit/index.ts b/editors/code/tests/unit/index.ts
new file mode 100644
index 000000000000..1deb1c403d53
--- /dev/null
+++ b/editors/code/tests/unit/index.ts
@@ -0,0 +1,38 @@
+import * as path from 'path';
+import Mocha from 'mocha';
+import glob from 'glob';
+
+export function run(): Promise {
+    // Create the mocha test
+    const mocha = new Mocha({
+        ui: 'tdd',
+        color: true
+    });
+
+    const testsRoot = __dirname;
+
+    return new Promise((c, e) => {
+        glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
+            if (err) {
+                return e(err);
+            }
+
+            // Add files to the test suite
+            files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
+
+            try {
+                // Run the mocha test
+                mocha.timeout(100000);
+                mocha.run(failures => {
+                    if (failures > 0) {
+                        e(new Error(`${failures} tests failed.`));
+                    } else {
+                        c();
+                    }
+                });
+            } catch (err) {
+                e(err);
+            }
+        });
+    });
+}
diff --git a/editors/code/tests/unit/launch_config.test.ts b/editors/code/tests/unit/launch_config.test.ts
new file mode 100644
index 000000000000..d5cf1b74eadd
--- /dev/null
+++ b/editors/code/tests/unit/launch_config.test.ts
@@ -0,0 +1,52 @@
+import * as assert from 'assert';
+import * as cargo from '../../src/cargo';
+
+suite('Launch configuration', () => {
+
+    suite('Lens', () => {
+        test('A binary', async () => {
+            const args = cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "pkg_name"]);
+
+            assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]);
+            assert.deepEqual(args.filter, undefined);
+        });
+
+        test('One of Multiple Binaries', async () => {
+            const args = cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "bin1"]);
+
+            assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin1", "--message-format=json"]);
+            assert.deepEqual(args.filter, undefined);
+        });
+
+        test('A test', async () => {
+            const args = cargo.artifactSpec(["test", "--package", "pkg_name", "--lib", "--no-run"]);
+
+            assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--no-run", "--message-format=json"]);
+            assert.notDeepEqual(args.filter, undefined);
+        });
+    });
+
+    suite('QuickPick', () => {
+        test('A binary', async () => {
+            const args = cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "pkg_name"]);
+
+            assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]);
+            assert.deepEqual(args.filter, undefined);
+        });
+
+
+        test('One of Multiple Binaries', async () => {
+            const args = cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "bin2"]);
+
+            assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin2", "--message-format=json"]);
+            assert.deepEqual(args.filter, undefined);
+        });
+
+        test('A test', async () => {
+            const args = cargo.artifactSpec(["test", "--package", "pkg_name", "--lib"]);
+
+            assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--message-format=json", "--no-run"]);
+            assert.notDeepEqual(args.filter, undefined);
+        });
+    });
+});
diff --git a/editors/code/tsconfig.json b/editors/code/tsconfig.json
index ad134865aa66..32d1a865f0a1 100644
--- a/editors/code/tsconfig.json
+++ b/editors/code/tsconfig.json
@@ -9,7 +9,7 @@
         "esModuleInterop": true,
         "allowSyntheticDefaultImports": true,
         "sourceMap": true,
-        "rootDir": "src",
+        "rootDir": ".",
         "strict": true,
         "noUnusedLocals": true,
         "noUnusedParameters": true,
@@ -18,6 +18,11 @@
         "newLine": "LF"
     },
     "exclude": [
-        "node_modules"
+        "node_modules",
+        ".vscode-test"
+    ],
+    "include": [
+        "src",
+        "tests"
     ]
 }

From c41a10c29331127ee830badddae55f3e27c9a6ea Mon Sep 17 00:00:00 2001
From: vsrs 
Date: Thu, 21 May 2020 11:34:34 +0300
Subject: [PATCH 012/163] Apply suggestions from @Veetaha code review

---
 editors/code/src/cargo.ts        | 14 ++-----
 editors/code/tests/runTests.ts   | 63 +++++++++++++++-----------------
 editors/code/tests/unit/index.ts | 10 ++---
 3 files changed, 39 insertions(+), 48 deletions(-)

diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts
index c3e2e5c0513d..a55b2f860f68 100644
--- a/editors/code/src/cargo.ts
+++ b/editors/code/src/cargo.ts
@@ -25,7 +25,7 @@ export function artifactSpec(args: readonly string[]): ArtifactSpec {
     switch (cargoArgs[0]) {
         case "run": cargoArgs[0] = "build"; break;
         case "test": {
-            if (cargoArgs.indexOf("--no-run") === -1) {
+            if (!cargoArgs.includes("--no-run")) {
                 cargoArgs.push("--no-run");
             }
             break;
@@ -36,9 +36,7 @@ export function artifactSpec(args: readonly string[]): ArtifactSpec {
     if (cargoArgs[0] === "test") {
         // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests
         // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"}
-        result.filter = (artifacts) => {
-            return artifacts.filter(a => a.isTest);
-        };
+        result.filter = (artifacts) => artifacts.filter(it => it.isTest);
     }
 
     return result;
@@ -48,7 +46,7 @@ export class Cargo {
     constructor(readonly rootFolder: string, readonly output: OutputChannel) { }
 
     private async getArtifacts(spec: ArtifactSpec): Promise {
-        let artifacts: CompilationArtifact[] = [];
+        const artifacts: CompilationArtifact[] = [];
 
         try {
             await this.runCargo(spec.cargoArgs,
@@ -75,11 +73,7 @@ export class Cargo {
             throw new Error(`Cargo invocation has failed: ${err}`);
         }
 
-        if (spec.filter) {
-            artifacts = spec.filter(artifacts);
-        }
-
-        return artifacts;
+        return spec.filter?.(artifacts) ?? artifacts;
     }
 
     async executableFromArgs(args: readonly string[]): Promise {
diff --git a/editors/code/tests/runTests.ts b/editors/code/tests/runTests.ts
index 81600f6a81fd..22df80ad318e 100644
--- a/editors/code/tests/runTests.ts
+++ b/editors/code/tests/runTests.ts
@@ -4,43 +4,40 @@ import * as fs from 'fs';
 import { runTests } from 'vscode-test';
 
 async function main() {
-    try {
-        // The folder containing the Extension Manifest package.json
-        // Passed to `--extensionDevelopmentPath`
-        const extensionDevelopmentPath = path.resolve(__dirname, '../../');
+    // The folder containing the Extension Manifest package.json
+    // Passed to `--extensionDevelopmentPath`
+    const extensionDevelopmentPath = path.resolve(__dirname, '../../');
 
-        // Minimum supported version.
-        const jsonData = fs.readFileSync(path.join(extensionDevelopmentPath, 'package.json'));
-        const json = JSON.parse(jsonData.toString());
-        let minimalVersion: string = json.engines.vscode;
-        if (minimalVersion.startsWith('^')) minimalVersion = minimalVersion.slice(1);
+    // Minimum supported version.
+    const jsonData = fs.readFileSync(path.join(extensionDevelopmentPath, 'package.json'));
+    const json = JSON.parse(jsonData.toString());
+    let minimalVersion: string = json.engines.vscode;
+    if (minimalVersion.startsWith('^')) minimalVersion = minimalVersion.slice(1);
 
-        const launchArgs = ["--disable-extensions"];
+    const launchArgs = ["--disable-extensions"];
 
-        // All test suites (either unit tests or integration tests) should be in subfolders.
-        const extensionTestsPath = path.resolve(__dirname, './unit/index');
+    // All test suites (either unit tests or integration tests) should be in subfolders.
+    const extensionTestsPath = path.resolve(__dirname, './unit/index');
 
-        // Run tests using the minimal supported version.
-        await runTests({
-            version: minimalVersion,
-            launchArgs,
-            extensionDevelopmentPath,
-            extensionTestsPath
-        });
+    // Run tests using the minimal supported version.
+    await runTests({
+        version: minimalVersion,
+        launchArgs,
+        extensionDevelopmentPath,
+        extensionTestsPath
+    });
 
-        // and the latest one
-        await runTests({
-            version: 'stable',
-            launchArgs,
-            extensionDevelopmentPath,
-            extensionTestsPath
-        });
-
-    } catch (err) {
-        // eslint-disable-next-line no-console
-        console.error('Failed to run tests', err);
-        process.exit(1);
-    }
+    // and the latest one
+    await runTests({
+        version: 'stable',
+        launchArgs,
+        extensionDevelopmentPath,
+        extensionTestsPath
+    });
 }
 
-main();
+main().catch(err => {
+    // eslint-disable-next-line no-console
+    console.error('Failed to run tests', err);
+    process.exit(1);
+});
diff --git a/editors/code/tests/unit/index.ts b/editors/code/tests/unit/index.ts
index 1deb1c403d53..5165720b458b 100644
--- a/editors/code/tests/unit/index.ts
+++ b/editors/code/tests/unit/index.ts
@@ -11,10 +11,10 @@ export function run(): Promise {
 
     const testsRoot = __dirname;
 
-    return new Promise((c, e) => {
+    return new Promise((resolve, reject) => {
         glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
             if (err) {
-                return e(err);
+                return reject(err);
             }
 
             // Add files to the test suite
@@ -25,13 +25,13 @@ export function run(): Promise {
                 mocha.timeout(100000);
                 mocha.run(failures => {
                     if (failures > 0) {
-                        e(new Error(`${failures} tests failed.`));
+                        reject(new Error(`${failures} tests failed.`));
                     } else {
-                        c();
+                        resolve();
                     }
                 });
             } catch (err) {
-                e(err);
+                reject(err);
             }
         });
     });

From c6143742bd4e625d391ac3ea860be7578ab9f53f Mon Sep 17 00:00:00 2001
From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
Date: Thu, 21 May 2020 10:48:42 +0200
Subject: [PATCH 013/163] add support of feature flag for runnables #4464

Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
---
 crates/ra_cfg/src/cfg_expr.rs                 |  68 +++++++-
 crates/ra_hir/src/lib.rs                      |   1 +
 crates/ra_hir_def/src/attr.rs                 |   2 +-
 crates/ra_hir_def/src/body.rs                 |   2 +-
 crates/ra_ide/src/runnables.rs                | 146 ++++++++++++++++--
 crates/rust-analyzer/src/cargo_target_spec.rs |   9 ++
 .../rust-analyzer/src/main_loop/handlers.rs   |   3 +-
 7 files changed, 212 insertions(+), 19 deletions(-)

diff --git a/crates/ra_cfg/src/cfg_expr.rs b/crates/ra_cfg/src/cfg_expr.rs
index 39d71851ca74..a4b201e0e017 100644
--- a/crates/ra_cfg/src/cfg_expr.rs
+++ b/crates/ra_cfg/src/cfg_expr.rs
@@ -33,6 +33,36 @@ impl CfgExpr {
             CfgExpr::Not(pred) => pred.fold(query).map(|s| !s),
         }
     }
+
+    /// Return minimal features needed
+    pub fn minimal_features_needed(&self) -> Option> {
+        let mut features = vec![];
+        self.collect_minimal_features_needed(&mut features);
+        if features.is_empty() {
+            None
+        } else {
+            Some(features)
+        }
+    }
+
+    fn collect_minimal_features_needed(&self, features: &mut Vec) {
+        match self {
+            CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()),
+            CfgExpr::All(preds) => {
+                preds.iter().for_each(|cfg| cfg.collect_minimal_features_needed(features));
+            }
+            CfgExpr::Any(preds) => {
+                for cfg in preds {
+                    let len_features = features.len();
+                    cfg.collect_minimal_features_needed(features);
+                    if len_features != features.len() {
+                        break;
+                    }
+                }
+            }
+            _ => {}
+        }
+    }
 }
 
 pub fn parse_cfg(tt: &Subtree) -> CfgExpr {
@@ -88,13 +118,17 @@ fn next_cfg_expr(it: &mut SliceIter) -> Option {
 mod tests {
     use super::*;
 
-    use mbe::ast_to_token_tree;
+    use mbe::{ast_to_token_tree, TokenMap};
     use ra_syntax::ast::{self, AstNode};
 
-    fn assert_parse_result(input: &str, expected: CfgExpr) {
+    fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
         let source_file = ast::SourceFile::parse(input).ok().unwrap();
         let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
-        let (tt, _) = ast_to_token_tree(&tt).unwrap();
+        ast_to_token_tree(&tt).unwrap()
+    }
+
+    fn assert_parse_result(input: &str, expected: CfgExpr) {
+        let (tt, _) = get_token_tree_generated(input);
         assert_eq!(parse_cfg(&tt), expected);
     }
 
@@ -129,4 +163,32 @@ mod tests {
             ]),
         );
     }
+
+    #[test]
+    fn test_cfg_expr_minimal_features_needed() {
+        let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
+        let cfg_expr = parse_cfg(&subtree);
+
+        assert_eq!(cfg_expr.minimal_features_needed().unwrap(), vec![SmolStr::new("baz")]);
+
+        let (subtree, _) =
+            get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#);
+        let cfg_expr = parse_cfg(&subtree);
+
+        assert_eq!(
+            cfg_expr.minimal_features_needed().unwrap(),
+            vec![SmolStr::new("baz"), SmolStr::new("foo")]
+        );
+
+        let (subtree, _) =
+            get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#);
+        let cfg_expr = parse_cfg(&subtree);
+
+        assert_eq!(cfg_expr.minimal_features_needed().unwrap(), vec![SmolStr::new("baz")]);
+
+        let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#);
+        let cfg_expr = parse_cfg(&subtree);
+
+        assert!(cfg_expr.minimal_features_needed().is_none());
+    }
 }
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index c5df4ac24da4..3364a822f431 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -62,6 +62,7 @@ pub use crate::{
 
 pub use hir_def::{
     adt::StructKind,
+    attr::Attrs,
     body::scope::ExprScopes,
     builtin_type::BuiltinType,
     docs::Documentation,
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs
index 576cd0c65ba9..8b6c0bedee78 100644
--- a/crates/ra_hir_def/src/attr.rs
+++ b/crates/ra_hir_def/src/attr.rs
@@ -81,7 +81,7 @@ impl Attrs {
         }
     }
 
-    fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Attrs {
+    pub fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Attrs {
         let hygiene = Hygiene::new(db.upcast(), owner.file_id);
         Attrs::new(owner.value, &hygiene)
     }
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index f5a7305dc090..273036cee1cf 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -29,7 +29,7 @@ use crate::{
     AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
 };
 
-/// A subset of Exander that only deals with cfg attributes. We only need it to
+/// A subset of Expander that only deals with cfg attributes. We only need it to
 /// avoid cyclic queries in crate def map during enum processing.
 pub(crate) struct CfgExpander {
     cfg_options: CfgOptions,
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index fa8a9d92c991..4f7eb2c5b236 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -1,11 +1,11 @@
 //! FIXME: write short doc here
 
-use hir::Semantics;
+use hir::{Attrs, HirFileId, InFile, Semantics};
 use itertools::Itertools;
 use ra_ide_db::RootDatabase;
 use ra_syntax::{
     ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner},
-    match_ast, SyntaxNode, TextRange,
+    match_ast, SmolStr, SyntaxNode, TextRange,
 };
 
 use crate::FileId;
@@ -16,6 +16,7 @@ use std::fmt::Display;
 pub struct Runnable {
     pub range: TextRange,
     pub kind: RunnableKind,
+    pub features_needed: Option>,
 }
 
 #[derive(Debug)]
@@ -45,20 +46,24 @@ pub enum RunnableKind {
 pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec {
     let sema = Semantics::new(db);
     let source_file = sema.parse(file_id);
-    source_file.syntax().descendants().filter_map(|i| runnable(&sema, i)).collect()
+    source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect()
 }
 
-fn runnable(sema: &Semantics, item: SyntaxNode) -> Option {
+fn runnable(sema: &Semantics, item: SyntaxNode, file_id: FileId) -> Option {
     match_ast! {
         match item {
-            ast::FnDef(it) => runnable_fn(sema, it),
-            ast::Module(it) => runnable_mod(sema, it),
+            ast::FnDef(it) => runnable_fn(sema, it, file_id),
+            ast::Module(it) => runnable_mod(sema, it, file_id),
             _ => None,
         }
     }
 }
 
-fn runnable_fn(sema: &Semantics, fn_def: ast::FnDef) -> Option {
+fn runnable_fn(
+    sema: &Semantics,
+    fn_def: ast::FnDef,
+    file_id: FileId,
+) -> Option {
     let name_string = fn_def.name()?.text().to_string();
 
     let kind = if name_string == "main" {
@@ -89,7 +94,11 @@ fn runnable_fn(sema: &Semantics, fn_def: ast::FnDef) -> Option bool {
     fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```"))
 }
 
-fn runnable_mod(sema: &Semantics, module: ast::Module) -> Option {
+fn runnable_mod(
+    sema: &Semantics,
+    module: ast::Module,
+    file_id: FileId,
+) -> Option {
     let has_test_function = module
         .item_list()?
         .items()
@@ -138,11 +151,34 @@ fn runnable_mod(sema: &Semantics, module: ast::Module) -> Option Option> {
+    let cfg_expr = attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree));
+    let features_needed = cfg_expr.fold(vec![], |mut acc, cfg| {
+        if let Some(features_needed) = cfg.minimal_features_needed() {
+            acc.extend(features_needed);
+        }
+        acc
+    });
+    if features_needed.is_empty() {
+        None
+    } else {
+        Some(features_needed)
+    }
 }
 
 #[cfg(test)]
@@ -174,6 +210,7 @@ mod tests {
             Runnable {
                 range: 1..21,
                 kind: Bin,
+                features_needed: None,
             },
             Runnable {
                 range: 22..46,
@@ -185,6 +222,7 @@ mod tests {
                         ignore: false,
                     },
                 },
+                features_needed: None,
             },
             Runnable {
                 range: 47..81,
@@ -196,6 +234,7 @@ mod tests {
                         ignore: true,
                     },
                 },
+                features_needed: None,
             },
         ]
         "###
@@ -223,6 +262,7 @@ mod tests {
             Runnable {
                 range: 1..21,
                 kind: Bin,
+                features_needed: None,
             },
             Runnable {
                 range: 22..64,
@@ -231,6 +271,7 @@ mod tests {
                         "foo",
                     ),
                 },
+                features_needed: None,
             },
         ]
         "###
@@ -258,6 +299,7 @@ mod tests {
                 kind: TestMod {
                     path: "test_mod",
                 },
+                features_needed: None,
             },
             Runnable {
                 range: 28..57,
@@ -269,6 +311,7 @@ mod tests {
                         ignore: false,
                     },
                 },
+                features_needed: None,
             },
         ]
         "###
@@ -298,6 +341,7 @@ mod tests {
                 kind: TestMod {
                     path: "foo::test_mod",
                 },
+                features_needed: None,
             },
             Runnable {
                 range: 46..79,
@@ -309,6 +353,7 @@ mod tests {
                         ignore: false,
                     },
                 },
+                features_needed: None,
             },
         ]
         "###
@@ -340,6 +385,7 @@ mod tests {
                 kind: TestMod {
                     path: "foo::bar::test_mod",
                 },
+                features_needed: None,
             },
             Runnable {
                 range: 68..105,
@@ -351,6 +397,80 @@ mod tests {
                         ignore: false,
                     },
                 },
+                features_needed: None,
+            },
+        ]
+        "###
+                );
+    }
+
+    #[test]
+    fn test_runnables_with_feature() {
+        let (analysis, pos) = analysis_and_position(
+            r#"
+        //- /lib.rs crate:foo cfg:feature=foo
+        <|> //empty
+        #[test]
+        #[cfg(feature = "foo")]
+        fn test_foo1() {}
+        "#,
+        );
+        let runnables = analysis.runnables(pos.file_id).unwrap();
+        assert_debug_snapshot!(&runnables,
+        @r###"
+        [
+            Runnable {
+                range: 1..58,
+                kind: Test {
+                    test_id: Name(
+                        "test_foo1",
+                    ),
+                    attr: TestAttr {
+                        ignore: false,
+                    },
+                },
+                features_needed: Some(
+                    [
+                        "foo",
+                    ],
+                ),
+            },
+        ]
+        "###
+                );
+    }
+
+    #[test]
+    fn test_runnables_with_features() {
+        let (analysis, pos) = analysis_and_position(
+            r#"
+        //- /lib.rs crate:foo cfg:feature=foo,feature=bar
+        <|> //empty
+        #[test]
+        #[cfg(all(feature = "foo", feature = "bar"))]
+        fn test_foo1() {}
+        "#,
+        );
+        let runnables = analysis.runnables(pos.file_id).unwrap();
+        assert_debug_snapshot!(&runnables,
+        @r###"
+        [
+            Runnable {
+                range: 1..80,
+                kind: Test {
+                    test_id: Name(
+                        "test_foo1",
+                    ),
+                    attr: TestAttr {
+                        ignore: false,
+                    },
+                },
+                features_needed: Some(
+                    [
+                        "foo",
+                        "bar",
+                    ],
+                ),
             },
         ]
         "###
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index 5e5a17943d2a..a2f85060ba37 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -4,6 +4,7 @@ use ra_ide::{FileId, RunnableKind, TestId};
 use ra_project_model::{self, ProjectWorkspace, TargetKind};
 
 use crate::{world::WorldSnapshot, Result};
+use ra_syntax::SmolStr;
 
 /// Abstract representation of Cargo target.
 ///
@@ -20,6 +21,7 @@ impl CargoTargetSpec {
     pub(crate) fn runnable_args(
         spec: Option,
         kind: &RunnableKind,
+        features_needed: &Option>,
     ) -> Result<(Vec, Vec)> {
         let mut args = Vec::new();
         let mut extra_args = Vec::new();
@@ -73,6 +75,13 @@ impl CargoTargetSpec {
                 }
             }
         }
+
+        if let Some(features_needed) = features_needed {
+            features_needed.iter().for_each(|feature| {
+                args.push("--features".to_string());
+                args.push(feature.to_string());
+            });
+        }
         Ok((args, extra_args))
     }
 
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 6b14830b6dfa..0232cc6f0cc3 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -1012,7 +1012,8 @@ fn to_lsp_runnable(
 ) -> Result {
     let spec = CargoTargetSpec::for_file(world, file_id)?;
     let target = spec.as_ref().map(|s| s.target.clone());
-    let (args, extra_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind)?;
+    let (args, extra_args) =
+        CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.features_needed)?;
     let line_index = world.analysis().file_line_index(file_id)?;
     let label = match &runnable.kind {
         RunnableKind::Test { test_id, .. } => format!("test {}", test_id),

From 3a9aa80502e9be1bc9341393cb53804843f8e834 Mon Sep 17 00:00:00 2001
From: Vladimir Serov 
Date: Thu, 21 May 2020 18:26:50 +0300
Subject: [PATCH 014/163] editors/vscode: added patchelf after download

---
 editors/code/src/main.ts | 49 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 48 insertions(+), 1 deletion(-)

diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index ac3bb365e2f0..0a234cb84737 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -1,7 +1,7 @@
 import * as vscode from 'vscode';
 import * as path from "path";
 import * as os from "os";
-import { promises as fs } from "fs";
+import { promises as fs, PathLike } from "fs";
 
 import * as commands from './commands';
 import { activateInlayHints } from './inlay_hints';
@@ -12,6 +12,7 @@ import { log, assert, isValidExecutable } from './util';
 import { PersistentState } from './persistent_state';
 import { fetchRelease, download } from './net';
 import { activateTaskProvider } from './tasks';
+import { exec } from 'child_process';
 
 let ctx: Ctx | undefined;
 
@@ -188,6 +189,46 @@ async function bootstrapServer(config: Config, state: PersistentState): Promise<
     return path;
 }
 
+async function patchelf(dest: PathLike): Promise {
+    await vscode.window.withProgress(
+        {
+            location: vscode.ProgressLocation.Notification, 
+            title: "Patching rust-analysis for NixOS"
+        }, 
+        async (progress, _) => {
+            let patch_path = path.join(os.tmpdir(), "patch-ra.nix")
+            progress.report({message: "Writing nix file", increment: 5})
+            await fs.writeFile(patch_path, `
+            {src, pkgs ? import  {}}:
+                pkgs.stdenv.mkDerivation {
+                    name = "rust-analyzer";
+                    inherit src;
+                    phases = [ "installPhase" "fixupPhase" ];
+                    installPhase = "cp $src $out";
+                    fixupPhase = ''
+                    chmod 755 $out
+                    patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" $out
+                    '';
+                }
+            `)
+            let orig_file = dest + "-orig"
+            await fs.rename(dest, orig_file)
+            progress.report({message: "Patching executable", increment: 20})
+            await new Promise((resolve, reject) => {
+                exec(`nix-build ${patch_path} --arg src '${orig_file}' -o ${dest}`,
+                (err, stdout, stderr) => {
+                    if (err != null) {
+                        reject(Error(stderr))
+                    } else {
+                        resolve(stdout)
+                    }
+                })
+            })
+            // await fs.unlink(orig_file)
+        }
+    )
+}
+
 async function getServer(config: Config, state: PersistentState): Promise {
     const explicitPath = process.env.__RA_LSP_SERVER_DEBUG ?? config.serverPath;
     if (explicitPath) {
@@ -237,6 +278,12 @@ async function getServer(config: Config, state: PersistentState): Promise true).catch(_ => false)) {
+        await patchelf(dest)
+    }
+
     await state.updateServerVersion(config.package.version);
     return dest;
 }

From 125e4197d8af1eca498e0088aa37cf48e0b3827e Mon Sep 17 00:00:00 2001
From: Vladimir Serov 
Date: Thu, 21 May 2020 18:45:37 +0300
Subject: [PATCH 015/163] editors/vscode: removing original file after patching

---
 editors/code/src/main.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 0a234cb84737..0a2c30594b64 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -224,7 +224,7 @@ async function patchelf(dest: PathLike): Promise {
                     }
                 })
             })
-            // await fs.unlink(orig_file)
+            await fs.unlink(orig_file)
         }
     )
 }

From 8e0d776369f807c1d3f60abb899d2fe0d83570d4 Mon Sep 17 00:00:00 2001
From: Vladimir Serov 
Date: Thu, 21 May 2020 18:49:30 +0300
Subject: [PATCH 016/163] editor/vscode: lint

---
 editors/code/src/main.ts | 42 ++++++++++++++++++++--------------------
 1 file changed, 21 insertions(+), 21 deletions(-)

diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 0a2c30594b64..1c6e3093b558 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -192,13 +192,13 @@ async function bootstrapServer(config: Config, state: PersistentState): Promise<
 async function patchelf(dest: PathLike): Promise {
     await vscode.window.withProgress(
         {
-            location: vscode.ProgressLocation.Notification, 
+            location: vscode.ProgressLocation.Notification,
             title: "Patching rust-analysis for NixOS"
-        }, 
+        },
         async (progress, _) => {
-            let patch_path = path.join(os.tmpdir(), "patch-ra.nix")
-            progress.report({message: "Writing nix file", increment: 5})
-            await fs.writeFile(patch_path, `
+            const patchPath = path.join(os.tmpdir(), "patch-ra.nix");
+            progress.report({ message: "Writing nix file", increment: 5 });
+            await fs.writeFile(patchPath, `
             {src, pkgs ? import  {}}:
                 pkgs.stdenv.mkDerivation {
                     name = "rust-analyzer";
@@ -210,23 +210,23 @@ async function patchelf(dest: PathLike): Promise {
                     patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" $out
                     '';
                 }
-            `)
-            let orig_file = dest + "-orig"
-            await fs.rename(dest, orig_file)
-            progress.report({message: "Patching executable", increment: 20})
+            `);
+            const origFile = dest + "-orig";
+            await fs.rename(dest, origFile);
+            progress.report({ message: "Patching executable", increment: 20 });
             await new Promise((resolve, reject) => {
-                exec(`nix-build ${patch_path} --arg src '${orig_file}' -o ${dest}`,
-                (err, stdout, stderr) => {
-                    if (err != null) {
-                        reject(Error(stderr))
-                    } else {
-                        resolve(stdout)
-                    }
-                })
-            })
-            await fs.unlink(orig_file)
+                exec(`nix-build ${patchPath} --arg src '${origFile}' -o ${dest}`,
+                    (err, stdout, stderr) => {
+                        if (err != null) {
+                            reject(Error(stderr));
+                        } else {
+                            resolve(stdout);
+                        }
+                    });
+            });
+            await fs.unlink(origFile);
         }
-    )
+    );
 }
 
 async function getServer(config: Config, state: PersistentState): Promise {
@@ -281,7 +281,7 @@ async function getServer(config: Config, state: PersistentState): Promise true).catch(_ => false)) {
-        await patchelf(dest)
+        await patchelf(dest);
     }
 
     await state.updateServerVersion(config.package.version);

From d7331b2d5dcdb2002a45ac4afe64b4d801658df5 Mon Sep 17 00:00:00 2001
From: Cabia Rangris 
Date: Thu, 21 May 2020 17:50:28 +0200
Subject: [PATCH 017/163] Update editors/code/src/main.ts

Co-authored-by: Jeremy Kolb 
---
 editors/code/src/main.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 1c6e3093b558..bfa9980be4eb 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -193,7 +193,7 @@ async function patchelf(dest: PathLike): Promise {
     await vscode.window.withProgress(
         {
             location: vscode.ProgressLocation.Notification,
-            title: "Patching rust-analysis for NixOS"
+            title: "Patching rust-analyzer for NixOS"
         },
         async (progress, _) => {
             const patchPath = path.join(os.tmpdir(), "patch-ra.nix");

From 757292856b5d3653926e52dd4da473b062b9a832 Mon Sep 17 00:00:00 2001
From: Vladimir Serov 
Date: Thu, 21 May 2020 21:30:56 +0300
Subject: [PATCH 018/163] editors/vscode: patchelf-ing without intermediate
 files

---
 editors/code/src/main.ts | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 1c6e3093b558..dbb2411921bb 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -196,9 +196,7 @@ async function patchelf(dest: PathLike): Promise {
             title: "Patching rust-analysis for NixOS"
         },
         async (progress, _) => {
-            const patchPath = path.join(os.tmpdir(), "patch-ra.nix");
-            progress.report({ message: "Writing nix file", increment: 5 });
-            await fs.writeFile(patchPath, `
+            const expression = `
             {src, pkgs ? import  {}}:
                 pkgs.stdenv.mkDerivation {
                     name = "rust-analyzer";
@@ -210,12 +208,12 @@ async function patchelf(dest: PathLike): Promise {
                     patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" $out
                     '';
                 }
-            `);
+            `;
             const origFile = dest + "-orig";
             await fs.rename(dest, origFile);
             progress.report({ message: "Patching executable", increment: 20 });
             await new Promise((resolve, reject) => {
-                exec(`nix-build ${patchPath} --arg src '${origFile}' -o ${dest}`,
+                const handle = exec(`nix-build -E - --arg src '${origFile}' -o ${dest}`,
                     (err, stdout, stderr) => {
                         if (err != null) {
                             reject(Error(stderr));
@@ -223,6 +221,8 @@ async function patchelf(dest: PathLike): Promise {
                             resolve(stdout);
                         }
                     });
+                handle.stdin?.write(expression);
+                handle.stdin?.end();
             });
             await fs.unlink(origFile);
         }

From ec5162fa7f2344ea86e11237383cb9d5ce5707aa Mon Sep 17 00:00:00 2001
From: Vladimir Serov 
Date: Thu, 21 May 2020 21:32:27 +0300
Subject: [PATCH 019/163] editors/vscode: forgotten await in os check

---
 editors/code/src/main.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index dbb2411921bb..42685e0c76d2 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -280,7 +280,7 @@ async function getServer(config: Config, state: PersistentState): Promise true).catch(_ => false)) {
+    if (await fs.stat("/etc/nixos").then(_ => true).catch(_ => false)) {
         await patchelf(dest);
     }
 

From db926218b2082077750291f8426ddd28b284cd08 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Galil=C3=A9e=20=27Bill=27=20Enguehard?=
 
Date: Thu, 21 May 2020 12:06:29 +0200
Subject: [PATCH 020/163] Add extra newline after module path in hover info

Closes issue #3813
---
 crates/ra_ide/src/display.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs
index 722092de97d8..cb350808659b 100644
--- a/crates/ra_ide/src/display.rs
+++ b/crates/ra_ide/src/display.rs
@@ -83,7 +83,7 @@ pub(crate) fn rust_code_markup_with_doc(
 
     if let Some(mod_path) = mod_path {
         if !mod_path.is_empty() {
-            format_to!(buf, "{}\n", mod_path);
+            format_to!(buf, "{}\n\n", mod_path);
         }
     }
     format_to!(buf, "{}\n```", code);

From 2ab79c6f4d972796d3fd9f4b7fae3342a551f5e8 Mon Sep 17 00:00:00 2001
From: Jess Balint 
Date: Wed, 20 May 2020 18:37:09 -0500
Subject: [PATCH 021/163] Assist: replace anonymous lifetime with a named one

(fixes #4523)
---
 .../handlers/change_lifetime_anon_to_named.rs | 123 ++++++++++++++++++
 crates/ra_assists/src/lib.rs                  |   2 +
 docs/user/assists.md                          |  24 ++++
 3 files changed, 149 insertions(+)
 create mode 100644 crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs

diff --git a/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs b/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
new file mode 100644
index 000000000000..63f0a7dabfeb
--- /dev/null
+++ b/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
@@ -0,0 +1,123 @@
+use crate::{AssistContext, AssistId, Assists};
+use ra_syntax::{ast, ast::{TypeParamsOwner}, AstNode, SyntaxKind};
+
+/// Assist: change_lifetime_anon_to_named
+///
+/// Change an anonymous lifetime to a named lifetime.
+///
+/// ```
+/// impl Cursor<'_<|>> {
+///     fn node(self) -> &SyntaxNode {
+///         match self {
+///             Cursor::Replace(node) | Cursor::Before(node) => node,
+///         }
+///     }
+/// }
+/// ```
+/// ->
+/// ```
+/// impl<'a> Cursor<'a> {
+///     fn node(self) -> &SyntaxNode {
+///         match self {
+///             Cursor::Replace(node) | Cursor::Before(node) => node,
+///         }
+///     }
+/// }
+/// ```
+// TODO : How can we handle renaming any one of multiple anonymous lifetimes?
+pub(crate) fn change_lifetime_anon_to_named(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
+    let lifetime_token = ctx.find_token_at_offset(SyntaxKind::LIFETIME)?;
+    let lifetime_arg = ast::LifetimeArg::cast(lifetime_token.parent())?;
+    if lifetime_arg.syntax().text() != "'_" {
+        return None;
+    }
+    let next_token = lifetime_token.next_token()?;
+    if next_token.kind() != SyntaxKind::R_ANGLE {
+        // only allow naming the last anonymous lifetime
+        return None;
+    }
+    match lifetime_arg.syntax().ancestors().find_map(ast::ImplDef::cast) {
+        Some(impl_def) => {
+            // get the `impl` keyword so we know where to add the lifetime argument
+            let impl_kw = impl_def.syntax().first_child_or_token()?.into_token()?;
+            if impl_kw.kind() != SyntaxKind::IMPL_KW {
+                return None;
+            }
+            acc.add(
+                AssistId("change_lifetime_anon_to_named"),
+                "Give anonymous lifetime a name",
+                lifetime_arg.syntax().text_range(),
+                |builder| {
+                    match impl_def.type_param_list() {
+                        Some(type_params) => {
+                            builder.insert(
+                                (u32::from(type_params.syntax().text_range().end()) - 1).into(),
+                                ", 'a",
+                            );
+                        },
+                        None => {
+                            builder.insert(
+                                impl_kw.text_range().end(),
+                                "<'a>",
+                            );
+                        },
+                    }
+                    builder.replace(lifetime_arg.syntax().text_range(), "'a");
+                },
+            )
+        }
+        _ => None,
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::tests::{check_assist, check_assist_not_applicable};
+
+    #[test]
+    fn test_example_case() {
+        check_assist(
+            change_lifetime_anon_to_named,
+            r#"impl Cursor<'_<|>> {
+                fn node(self) -> &SyntaxNode {
+                    match self {
+                        Cursor::Replace(node) | Cursor::Before(node) => node,
+                    }
+                }
+            }"#,
+            r#"impl<'a> Cursor<'a> {
+                fn node(self) -> &SyntaxNode {
+                    match self {
+                        Cursor::Replace(node) | Cursor::Before(node) => node,
+                    }
+                }
+            }"#,
+        );
+    }
+
+    #[test]
+    fn test_example_case_simplified() {
+        check_assist(
+            change_lifetime_anon_to_named,
+            r#"impl Cursor<'_<|>> {"#,
+            r#"impl<'a> Cursor<'a> {"#,
+        );
+    }
+
+    #[test]
+    fn test_not_applicable() {
+        check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<'_><|> {"#);
+        check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<|><'_> {"#);
+        check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<'a<|>> {"#);
+    }
+
+    #[test]
+    fn test_with_type_parameter() {
+        check_assist(
+            change_lifetime_anon_to_named,
+            r#"impl Cursor>"#,
+            r#"impl Cursor"#,
+        );
+    }
+}
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 464bc03dde9e..3f8f7ffbfc52 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -112,6 +112,7 @@ mod handlers {
     mod add_turbo_fish;
     mod apply_demorgan;
     mod auto_import;
+    mod change_lifetime_anon_to_named;
     mod change_return_type_to_result;
     mod change_visibility;
     mod early_return;
@@ -151,6 +152,7 @@ mod handlers {
             add_turbo_fish::add_turbo_fish,
             apply_demorgan::apply_demorgan,
             auto_import::auto_import,
+            change_lifetime_anon_to_named::change_lifetime_anon_to_named,
             change_return_type_to_result::change_return_type_to_result,
             change_visibility::change_visibility,
             early_return::convert_to_guarded_return,
diff --git a/docs/user/assists.md b/docs/user/assists.md
index 4ad7ea59d2e9..29b64330e906 100644
--- a/docs/user/assists.md
+++ b/docs/user/assists.md
@@ -259,6 +259,30 @@ fn main() {
 }
 ```
 
+## `change_lifetime_anon_to_named`
+
+Change an anonymous lifetime to a named lifetime.
+
+```rust
+// BEFORE
+impl Cursor<'_<|>> {
+    fn node(self) -> &SyntaxNode {
+        match self {
+            Cursor::Replace(node) | Cursor::Before(node) => node,
+        }
+    }
+}
+
+// AFTER
+impl<'a> Cursor<'a> {
+    fn node(self) -> &SyntaxNode {
+        match self {
+            Cursor::Replace(node) | Cursor::Before(node) => node,
+        }
+    }
+}
+```
+
 ## `change_return_type_to_result`
 
 Change the function's return type to Result.

From 43339058e32e8bb0d218390b9df5b5a68fe57ca7 Mon Sep 17 00:00:00 2001
From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
Date: Fri, 22 May 2020 09:23:31 +0200
Subject: [PATCH 022/163] add support of feature flag for runnables #4464

Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
---
 crates/ra_cfg/src/cfg_expr.rs  | 17 +++++++----------
 crates/ra_ide/src/runnables.rs | 13 ++-----------
 2 files changed, 9 insertions(+), 21 deletions(-)

diff --git a/crates/ra_cfg/src/cfg_expr.rs b/crates/ra_cfg/src/cfg_expr.rs
index a4b201e0e017..98f44f56d039 100644
--- a/crates/ra_cfg/src/cfg_expr.rs
+++ b/crates/ra_cfg/src/cfg_expr.rs
@@ -35,14 +35,11 @@ impl CfgExpr {
     }
 
     /// Return minimal features needed
-    pub fn minimal_features_needed(&self) -> Option> {
+    pub fn minimal_features_needed(&self) -> Vec {
         let mut features = vec![];
         self.collect_minimal_features_needed(&mut features);
-        if features.is_empty() {
-            None
-        } else {
-            Some(features)
-        }
+
+        features
     }
 
     fn collect_minimal_features_needed(&self, features: &mut Vec) {
@@ -169,14 +166,14 @@ mod tests {
         let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
         let cfg_expr = parse_cfg(&subtree);
 
-        assert_eq!(cfg_expr.minimal_features_needed().unwrap(), vec![SmolStr::new("baz")]);
+        assert_eq!(cfg_expr.minimal_features_needed(), vec![SmolStr::new("baz")]);
 
         let (subtree, _) =
             get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#);
         let cfg_expr = parse_cfg(&subtree);
 
         assert_eq!(
-            cfg_expr.minimal_features_needed().unwrap(),
+            cfg_expr.minimal_features_needed(),
             vec![SmolStr::new("baz"), SmolStr::new("foo")]
         );
 
@@ -184,11 +181,11 @@ mod tests {
             get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#);
         let cfg_expr = parse_cfg(&subtree);
 
-        assert_eq!(cfg_expr.minimal_features_needed().unwrap(), vec![SmolStr::new("baz")]);
+        assert_eq!(cfg_expr.minimal_features_needed(), vec![SmolStr::new("baz")]);
 
         let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#);
         let cfg_expr = parse_cfg(&subtree);
 
-        assert!(cfg_expr.minimal_features_needed().is_none());
+        assert!(cfg_expr.minimal_features_needed().is_empty());
     }
 }
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 3a3d0b0ac124..a460370c5c32 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -190,17 +190,8 @@ fn runnable_mod(
 
 fn get_features_needed(attrs: Attrs) -> Option> {
     let cfg_expr = attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree));
-    let features_needed = cfg_expr.fold(vec![], |mut acc, cfg| {
-        if let Some(features_needed) = cfg.minimal_features_needed() {
-            acc.extend(features_needed);
-        }
-        acc
-    });
-    if features_needed.is_empty() {
-        None
-    } else {
-        Some(features_needed)
-    }
+    let features_needed = cfg_expr.map(|cfg| cfg.minimal_features_needed()).flatten().collect();
+    Some(features_needed).filter(|it: &Vec| !it.is_empty())
 }
 
 #[cfg(test)]

From 1fae96a8d4fb452216906f8909d421ad884fd929 Mon Sep 17 00:00:00 2001
From: Jess Balint 
Date: Fri, 22 May 2020 08:51:37 -0500
Subject: [PATCH 023/163] handle the case of conflicting lifetime param name

- and clean/format code
---
 .../handlers/change_lifetime_anon_to_named.rs | 43 ++++++++++++++++---
 1 file changed, 36 insertions(+), 7 deletions(-)

diff --git a/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs b/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
index 63f0a7dabfeb..8d8f7833f265 100644
--- a/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
+++ b/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
@@ -1,5 +1,6 @@
 use crate::{AssistContext, AssistId, Assists};
-use ra_syntax::{ast, ast::{TypeParamsOwner}, AstNode, SyntaxKind};
+use ra_syntax::{ast, ast::TypeParamsOwner, AstNode, SyntaxKind};
+use std::collections::HashSet;
 
 /// Assist: change_lifetime_anon_to_named
 ///
@@ -24,7 +25,7 @@ use ra_syntax::{ast, ast::{TypeParamsOwner}, AstNode, SyntaxKind};
 ///     }
 /// }
 /// ```
-// TODO : How can we handle renaming any one of multiple anonymous lifetimes?
+// FIXME: How can we handle renaming any one of multiple anonymous lifetimes?
 pub(crate) fn change_lifetime_anon_to_named(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let lifetime_token = ctx.find_token_at_offset(SyntaxKind::LIFETIME)?;
     let lifetime_arg = ast::LifetimeArg::cast(lifetime_token.parent())?;
@@ -43,6 +44,22 @@ pub(crate) fn change_lifetime_anon_to_named(acc: &mut Assists, ctx: &AssistConte
             if impl_kw.kind() != SyntaxKind::IMPL_KW {
                 return None;
             }
+            let new_lifetime_param = match impl_def.type_param_list() {
+                Some(type_params) => {
+                    let used_lifetime_params: HashSet<_> = type_params
+                        .lifetime_params()
+                        .map(|p| {
+                            let mut param_name = p.syntax().text().to_string();
+                            param_name.remove(0);
+                            param_name
+                        })
+                        .collect();
+                    "abcdefghijklmnopqrstuvwxyz"
+                        .chars()
+                        .find(|c| !used_lifetime_params.contains(&c.to_string()))?
+                }
+                None => 'a',
+            };
             acc.add(
                 AssistId("change_lifetime_anon_to_named"),
                 "Give anonymous lifetime a name",
@@ -52,17 +69,20 @@ pub(crate) fn change_lifetime_anon_to_named(acc: &mut Assists, ctx: &AssistConte
                         Some(type_params) => {
                             builder.insert(
                                 (u32::from(type_params.syntax().text_range().end()) - 1).into(),
-                                ", 'a",
+                                format!(", '{}", new_lifetime_param),
                             );
-                        },
+                        }
                         None => {
                             builder.insert(
                                 impl_kw.text_range().end(),
-                                "<'a>",
+                                format!("<'{}>", new_lifetime_param),
                             );
-                        },
+                        }
                     }
-                    builder.replace(lifetime_arg.syntax().text_range(), "'a");
+                    builder.replace(
+                        lifetime_arg.syntax().text_range(),
+                        format!("'{}", new_lifetime_param),
+                    );
                 },
             )
         }
@@ -120,4 +140,13 @@ mod tests {
             r#"impl Cursor"#,
         );
     }
+
+    #[test]
+    fn test_with_existing_lifetime_name_conflict() {
+        check_assist(
+            change_lifetime_anon_to_named,
+            r#"impl<'a, 'b> Cursor<'a, 'b, '_<|>>"#,
+            r#"impl<'a, 'b, 'c> Cursor<'a, 'b, 'c>"#,
+        );
+    }
 }

From 6594235dd80d337dd7bfaa8be876ba5e111526b1 Mon Sep 17 00:00:00 2001
From: Jess Balint 
Date: Fri, 22 May 2020 09:20:43 -0500
Subject: [PATCH 024/163] Remove doc using `cargo xtask codegen`.

---
 docs/user/assists.md | 24 ------------------------
 1 file changed, 24 deletions(-)

diff --git a/docs/user/assists.md b/docs/user/assists.md
index 29b64330e906..4ad7ea59d2e9 100644
--- a/docs/user/assists.md
+++ b/docs/user/assists.md
@@ -259,30 +259,6 @@ fn main() {
 }
 ```
 
-## `change_lifetime_anon_to_named`
-
-Change an anonymous lifetime to a named lifetime.
-
-```rust
-// BEFORE
-impl Cursor<'_<|>> {
-    fn node(self) -> &SyntaxNode {
-        match self {
-            Cursor::Replace(node) | Cursor::Before(node) => node,
-        }
-    }
-}
-
-// AFTER
-impl<'a> Cursor<'a> {
-    fn node(self) -> &SyntaxNode {
-        match self {
-            Cursor::Replace(node) | Cursor::Before(node) => node,
-        }
-    }
-}
-```
-
 ## `change_return_type_to_result`
 
 Change the function's return type to Result.

From 1f9e02c74e7822a490466a605fa384ce1b5e3afe Mon Sep 17 00:00:00 2001
From: Jess Balint 
Date: Fri, 22 May 2020 09:25:55 -0500
Subject: [PATCH 025/163] fix generated docs issue

---
 .../handlers/change_lifetime_anon_to_named.rs | 46 +++++++++----------
 crates/ra_assists/src/tests/generated.rs      | 25 ++++++++++
 docs/user/assists.md                          | 24 ++++++++++
 3 files changed, 72 insertions(+), 23 deletions(-)

diff --git a/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs b/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
index 8d8f7833f265..7101d9aaff3a 100644
--- a/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
+++ b/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
@@ -2,29 +2,29 @@ use crate::{AssistContext, AssistId, Assists};
 use ra_syntax::{ast, ast::TypeParamsOwner, AstNode, SyntaxKind};
 use std::collections::HashSet;
 
-/// Assist: change_lifetime_anon_to_named
-///
-/// Change an anonymous lifetime to a named lifetime.
-///
-/// ```
-/// impl Cursor<'_<|>> {
-///     fn node(self) -> &SyntaxNode {
-///         match self {
-///             Cursor::Replace(node) | Cursor::Before(node) => node,
-///         }
-///     }
-/// }
-/// ```
-/// ->
-/// ```
-/// impl<'a> Cursor<'a> {
-///     fn node(self) -> &SyntaxNode {
-///         match self {
-///             Cursor::Replace(node) | Cursor::Before(node) => node,
-///         }
-///     }
-/// }
-/// ```
+// Assist: change_lifetime_anon_to_named
+//
+// Change an anonymous lifetime to a named lifetime.
+//
+// ```
+// impl Cursor<'_<|>> {
+//     fn node(self) -> &SyntaxNode {
+//         match self {
+//             Cursor::Replace(node) | Cursor::Before(node) => node,
+//         }
+//     }
+// }
+// ```
+// ->
+// ```
+// impl<'a> Cursor<'a> {
+//     fn node(self) -> &SyntaxNode {
+//         match self {
+//             Cursor::Replace(node) | Cursor::Before(node) => node,
+//         }
+//     }
+// }
+// ```
 // FIXME: How can we handle renaming any one of multiple anonymous lifetimes?
 pub(crate) fn change_lifetime_anon_to_named(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
     let lifetime_token = ctx.find_token_at_offset(SyntaxKind::LIFETIME)?;
diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs
index 250e56a69624..abffbf97cfcf 100644
--- a/crates/ra_assists/src/tests/generated.rs
+++ b/crates/ra_assists/src/tests/generated.rs
@@ -268,6 +268,31 @@ pub mod std { pub mod collections { pub struct HashMap { } } }
     )
 }
 
+#[test]
+fn doctest_change_lifetime_anon_to_named() {
+    check_doc_test(
+        "change_lifetime_anon_to_named",
+        r#####"
+impl Cursor<'_<|>> {
+    fn node(self) -> &SyntaxNode {
+        match self {
+            Cursor::Replace(node) | Cursor::Before(node) => node,
+        }
+    }
+}
+"#####,
+        r#####"
+impl<'a> Cursor<'a> {
+    fn node(self) -> &SyntaxNode {
+        match self {
+            Cursor::Replace(node) | Cursor::Before(node) => node,
+        }
+    }
+}
+"#####,
+    )
+}
+
 #[test]
 fn doctest_change_return_type_to_result() {
     check_doc_test(
diff --git a/docs/user/assists.md b/docs/user/assists.md
index 4ad7ea59d2e9..a1058ecde977 100644
--- a/docs/user/assists.md
+++ b/docs/user/assists.md
@@ -259,6 +259,30 @@ fn main() {
 }
 ```
 
+## `change_lifetime_anon_to_named`
+
+Change an anonymous lifetime to a named lifetime.
+
+```rust
+// BEFORE
+impl Cursor<'_┃> {
+    fn node(self) -> &SyntaxNode {
+        match self {
+            Cursor::Replace(node) | Cursor::Before(node) => node,
+        }
+    }
+}
+
+// AFTER
+impl<'a> Cursor<'a> {
+    fn node(self) -> &SyntaxNode {
+        match self {
+            Cursor::Replace(node) | Cursor::Before(node) => node,
+        }
+    }
+}
+```
+
 ## `change_return_type_to_result`
 
 Change the function's return type to Result.

From d42fd8efb6019c08371007df68ef3a38c2e86a45 Mon Sep 17 00:00:00 2001
From: Jess Balint 
Date: Fri, 22 May 2020 10:15:59 -0500
Subject: [PATCH 026/163] use char range

---
 .../ra_assists/src/handlers/change_lifetime_anon_to_named.rs  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs b/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
index 7101d9aaff3a..67d50cfda5cd 100644
--- a/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
+++ b/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
@@ -54,8 +54,8 @@ pub(crate) fn change_lifetime_anon_to_named(acc: &mut Assists, ctx: &AssistConte
                             param_name
                         })
                         .collect();
-                    "abcdefghijklmnopqrstuvwxyz"
-                        .chars()
+                    (b'a'..=b'z')
+                        .map(char::from)
                         .find(|c| !used_lifetime_params.contains(&c.to_string()))?
                 }
                 None => 'a',

From 5043c9ce9c9d07263305ef0e560b8ce506593748 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Galil=C3=A9e=20=27Bill=27=20Enguehard?=
 
Date: Fri, 22 May 2020 19:04:39 +0200
Subject: [PATCH 027/163] Modify hover info to show module path separated by a
 line

---
 crates/ra_ide/src/display.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs
index cb350808659b..323505ef6b79 100644
--- a/crates/ra_ide/src/display.rs
+++ b/crates/ra_ide/src/display.rs
@@ -79,14 +79,14 @@ pub(crate) fn rust_code_markup_with_doc(
     doc: Option<&str>,
     mod_path: Option<&str>,
 ) -> String {
-    let mut buf = "```rust\n".to_owned();
+    let mut buf = "".to_owned();
 
     if let Some(mod_path) = mod_path {
         if !mod_path.is_empty() {
-            format_to!(buf, "{}\n\n", mod_path);
+            format_to!(buf, "{}\n___\n", mod_path);
         }
     }
-    format_to!(buf, "{}\n```", code);
+    format_to!(buf, "```rust\n\n{}\n```", code);
 
     if let Some(doc) = doc {
         format_to!(buf, "\n\n{}", doc);

From 910ac5a21399115ab5a4e1dc423da64fd91e7e15 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Galil=C3=A9e=20=27Bill=27=20Enguehard?=
 
Date: Fri, 22 May 2020 20:10:37 +0200
Subject: [PATCH 028/163] Fix formatting error when no modpath is present

---
 crates/ra_ide/src/display.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs
index 323505ef6b79..8bb312156878 100644
--- a/crates/ra_ide/src/display.rs
+++ b/crates/ra_ide/src/display.rs
@@ -79,14 +79,14 @@ pub(crate) fn rust_code_markup_with_doc(
     doc: Option<&str>,
     mod_path: Option<&str>,
 ) -> String {
-    let mut buf = "".to_owned();
+    let mut buf = String::new();
 
     if let Some(mod_path) = mod_path {
         if !mod_path.is_empty() {
-            format_to!(buf, "{}\n___\n", mod_path);
+            format_to!(buf, "{}\n___\n\n", mod_path);
         }
     }
-    format_to!(buf, "```rust\n\n{}\n```", code);
+    format_to!(buf, "```rust\n{}\n```", code);
 
     if let Some(doc) = doc {
         format_to!(buf, "\n\n{}", doc);

From d688215f25b8da06b825da5942763c0faf2f7454 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Galil=C3=A9e=20=27Bill=27=20Enguehard?=
 
Date: Fri, 22 May 2020 20:11:24 +0200
Subject: [PATCH 029/163] Update tests to accept new hover formatting

---
 crates/ra_ide/src/hover.rs | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index befa977c7fe7..1f4f6b848021 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -405,7 +405,7 @@ mod tests {
                 };
             }
         "#,
-            &["Foo\nfield_a: u32"],
+            &["Foo\n___\n\n```rust\nfield_a: u32"],
         );
 
         // Hovering over the field in the definition
@@ -422,7 +422,7 @@ mod tests {
                 };
             }
         "#,
-            &["Foo\nfield_a: u32"],
+            &["Foo\n___\n\n```rust\nfield_a: u32"],
         );
     }
 
@@ -475,7 +475,7 @@ fn main() {
             ",
         );
         let hover = analysis.hover(position).unwrap().unwrap();
-        assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\nSome"));
+        assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\n___\n\n```rust\nSome"));
 
         let (analysis, position) = single_file_with_position(
             "
@@ -503,6 +503,9 @@ fn main() {
         "#,
             &["
 Option
+___
+
+```rust
 None
 ```
 
@@ -524,6 +527,9 @@ The None variant
         "#,
             &["
 Option
+___
+
+```rust
 Some
 ```
 
@@ -606,7 +612,10 @@ fn func(foo: i32) { if true { <|>foo; }; }
             ",
         );
         let hover = analysis.hover(position).unwrap().unwrap();
-        assert_eq!(trim_markup_opt(hover.info.first()), Some("wrapper::Thing\nfn new() -> Thing"));
+        assert_eq!(
+            trim_markup_opt(hover.info.first()),
+            Some("wrapper::Thing\n___\n\n```rust\nfn new() -> Thing")
+        );
     }
 
     #[test]

From bfbc210bc1216b79e355eb70449caf08dc67d5ad Mon Sep 17 00:00:00 2001
From: Florian Diebold 
Date: Fri, 22 May 2020 18:15:53 +0200
Subject: [PATCH 030/163] Use Chalk's built-in representation of function item
 types

---
 crates/ra_hir_ty/src/db.rs                    |  5 +++
 crates/ra_hir_ty/src/lib.rs                   |  6 +++
 crates/ra_hir_ty/src/tests/traits.rs          | 42 +++++++++++++++++++
 crates/ra_hir_ty/src/traits/chalk.rs          | 39 +++++++++++++++--
 crates/ra_hir_ty/src/traits/chalk/interner.rs |  2 +
 crates/ra_hir_ty/src/traits/chalk/mapping.rs  | 26 ++++++++++--
 crates/ra_hir_ty/src/traits/chalk/tls.rs      | 18 +++++++-
 7 files changed, 128 insertions(+), 10 deletions(-)

diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs
index dfc6c7dd682a..0a8bb24ac239 100644
--- a/crates/ra_hir_ty/src/db.rs
+++ b/crates/ra_hir_ty/src/db.rs
@@ -76,6 +76,8 @@ pub trait HirDatabase: DefDatabase + Upcast {
     #[salsa::interned]
     fn intern_type_ctor(&self, type_ctor: TypeCtor) -> crate::TypeCtorId;
     #[salsa::interned]
+    fn intern_callable_def(&self, callable_def: CallableDef) -> crate::CallableDefId;
+    #[salsa::interned]
     fn intern_type_param_id(&self, param_id: TypeParamId) -> GlobalTypeParamId;
     #[salsa::interned]
     fn intern_chalk_impl(&self, impl_: Impl) -> crate::traits::GlobalImplId;
@@ -94,6 +96,9 @@ pub trait HirDatabase: DefDatabase + Upcast {
     #[salsa::invoke(crate::traits::chalk::impl_datum_query)]
     fn impl_datum(&self, krate: CrateId, impl_id: chalk::ImplId) -> Arc;
 
+    #[salsa::invoke(crate::traits::chalk::fn_def_datum_query)]
+    fn fn_def_datum(&self, krate: CrateId, fn_def_id: chalk::FnDefId) -> Arc;
+
     #[salsa::invoke(crate::traits::chalk::associated_ty_value_query)]
     fn associated_ty_value(
         &self,
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index e91c9be040a6..93cb45a648c5 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -159,6 +159,12 @@ pub enum TypeCtor {
 pub struct TypeCtorId(salsa::InternId);
 impl_intern_key!(TypeCtorId);
 
+/// This exists just for Chalk, because Chalk just has a single `FnDefId` where
+/// we have different IDs for struct and enum variant constructors.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
+pub struct CallableDefId(salsa::InternId);
+impl_intern_key!(CallableDefId);
+
 impl TypeCtor {
     pub fn num_ty_params(self, db: &dyn HirDatabase) -> usize {
         match self {
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index 6826610cb3b5..17c6460761ea 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -2643,6 +2643,48 @@ fn test() {
     );
 }
 
+#[test]
+fn builtin_fn_def_copy() {
+    assert_snapshot!(
+        infer_with_mismatches(r#"
+#[lang = "copy"]
+trait Copy {}
+
+fn foo() {}
+fn bar(T) -> T {}
+struct Struct(usize);
+enum Enum { Variant(usize) }
+
+trait Test { fn test(&self) -> bool; }
+impl Test for T {}
+
+fn test() {
+    foo.test();
+    bar.test();
+    Struct.test();
+    Enum::Variant.test();
+}
+"#, true),
+        // wrong result, because the built-in Copy impl for fn defs doesn't exist in Chalk yet
+        @r###"
+    42..44 '{}': ()
+    61..62 'T': {unknown}
+    69..71 '{}': ()
+    69..71: expected T, got ()
+    146..150 'self': &Self
+    202..282 '{     ...t(); }': ()
+    208..211 'foo': fn foo()
+    208..218 'foo.test()': {unknown}
+    224..227 'bar': fn bar<{unknown}>({unknown}) -> {unknown}
+    224..234 'bar.test()': {unknown}
+    240..246 'Struct': Struct(usize) -> Struct
+    240..253 'Struct.test()': {unknown}
+    259..272 'Enum::Variant': Variant(usize) -> Enum
+    259..279 'Enum::...test()': {unknown}
+    "###
+    );
+}
+
 #[test]
 fn builtin_sized() {
     assert_snapshot!(
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs
index e2f2a9ccbc64..5b0f12a3c7f3 100644
--- a/crates/ra_hir_ty/src/traits/chalk.rs
+++ b/crates/ra_hir_ty/src/traits/chalk.rs
@@ -14,7 +14,7 @@ use ra_db::{salsa::InternKey, CrateId};
 use super::{builtin, AssocTyValue, ChalkContext, Impl};
 use crate::{
     db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics,
-    DebruijnIndex, GenericPredicate, Substs, Ty, TypeCtor,
+    CallableDef, DebruijnIndex, GenericPredicate, Substs, Ty, TypeCtor,
 };
 use chalk_rust_ir::WellKnownTrait;
 use mapping::{convert_where_clauses, generic_predicate_to_inline_bound, make_binders};
@@ -54,10 +54,9 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> {
 
     fn fn_def_datum(
         &self,
-        _fn_def_id: chalk_ir::FnDefId,
+        fn_def_id: chalk_ir::FnDefId,
     ) -> Arc> {
-        // We don't yet provide any FnDefs to Chalk
-        unimplemented!()
+        self.db.fn_def_datum(self.krate, fn_def_id)
     }
 
     fn impls_for_trait(
@@ -405,6 +404,26 @@ fn type_alias_associated_ty_value(
     Arc::new(value)
 }
 
+pub(crate) fn fn_def_datum_query(
+    db: &dyn HirDatabase,
+    _krate: CrateId,
+    fn_def_id: FnDefId,
+) -> Arc {
+    let callable_def: CallableDef = from_chalk(db, fn_def_id);
+    let generic_params = generics(db.upcast(), callable_def.into());
+    let sig = db.callable_item_signature(callable_def);
+    let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
+    let where_clauses = convert_where_clauses(db, callable_def.into(), &bound_vars);
+    let bound = chalk_rust_ir::FnDefDatumBound {
+        // Note: Chalk doesn't actually use this information yet as far as I am aware, but we provide it anyway
+        argument_types: sig.value.params().iter().map(|ty| ty.clone().to_chalk(db)).collect(),
+        return_type: sig.value.ret().clone().to_chalk(db),
+        where_clauses,
+    };
+    let datum = FnDefDatum { id: fn_def_id, binders: make_binders(bound, sig.num_binders) };
+    Arc::new(datum)
+}
+
 impl From for crate::TypeCtorId {
     fn from(struct_id: AdtId) -> Self {
         struct_id.0
@@ -417,6 +436,18 @@ impl From for AdtId {
     }
 }
 
+impl From for crate::CallableDefId {
+    fn from(fn_def_id: FnDefId) -> Self {
+        InternKey::from_intern_id(fn_def_id.0)
+    }
+}
+
+impl From for FnDefId {
+    fn from(callable_def_id: crate::CallableDefId) -> Self {
+        chalk_ir::FnDefId(callable_def_id.as_intern_id())
+    }
+}
+
 impl From for crate::traits::GlobalImplId {
     fn from(impl_id: ImplId) -> Self {
         InternKey::from_intern_id(impl_id.0)
diff --git a/crates/ra_hir_ty/src/traits/chalk/interner.rs b/crates/ra_hir_ty/src/traits/chalk/interner.rs
index 0603728197e4..2a27f8ed8b3e 100644
--- a/crates/ra_hir_ty/src/traits/chalk/interner.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/interner.rs
@@ -20,6 +20,8 @@ pub type ImplId = chalk_ir::ImplId;
 pub type ImplDatum = chalk_rust_ir::ImplDatum;
 pub type AssociatedTyValueId = chalk_rust_ir::AssociatedTyValueId;
 pub type AssociatedTyValue = chalk_rust_ir::AssociatedTyValue;
+pub type FnDefId = chalk_ir::FnDefId;
+pub type FnDefDatum = chalk_rust_ir::FnDefDatum;
 
 impl chalk_ir::interner::Interner for Interner {
     type InternedType = Box>; // FIXME use Arc?
diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
index a83d82fd8bf4..7841a0e21865 100644
--- a/crates/ra_hir_ty/src/traits/chalk/mapping.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
@@ -15,8 +15,8 @@ use crate::{
     db::HirDatabase,
     primitive::{FloatBitness, FloatTy, IntBitness, IntTy, Signedness, Uncertain},
     traits::{builtin, AssocTyValue, Canonical, Impl, Obligation},
-    ApplicationTy, GenericPredicate, InEnvironment, ProjectionPredicate, ProjectionTy, Substs,
-    TraitEnvironment, TraitRef, Ty, TypeCtor,
+    ApplicationTy, CallableDef, GenericPredicate, InEnvironment, ProjectionPredicate, ProjectionTy,
+    Substs, TraitEnvironment, TraitRef, Ty, TypeCtor,
 };
 
 use super::interner::*;
@@ -217,11 +217,14 @@ impl ToChalk for TypeCtor {
             TypeCtor::Slice => TypeName::Slice,
             TypeCtor::Ref(mutability) => TypeName::Ref(mutability.to_chalk(db)),
             TypeCtor::Str => TypeName::Str,
+            TypeCtor::FnDef(callable_def) => {
+                let id = callable_def.to_chalk(db);
+                TypeName::FnDef(id)
+            }
             TypeCtor::Int(Uncertain::Unknown)
             | TypeCtor::Float(Uncertain::Unknown)
             | TypeCtor::Adt(_)
             | TypeCtor::Array
-            | TypeCtor::FnDef(_)
             | TypeCtor::FnPtr { .. }
             | TypeCtor::Never
             | TypeCtor::Closure { .. } => {
@@ -260,7 +263,10 @@ impl ToChalk for TypeCtor {
             TypeName::Ref(mutability) => TypeCtor::Ref(from_chalk(db, mutability)),
             TypeName::Str => TypeCtor::Str,
 
-            TypeName::FnDef(_) => unreachable!(),
+            TypeName::FnDef(fn_def_id) => {
+                let callable_def = from_chalk(db, fn_def_id);
+                TypeCtor::FnDef(callable_def)
+            }
 
             TypeName::Error => {
                 // this should not be reached, since we don't represent TypeName::Error with TypeCtor
@@ -347,6 +353,18 @@ impl ToChalk for Impl {
     }
 }
 
+impl ToChalk for CallableDef {
+    type Chalk = FnDefId;
+
+    fn to_chalk(self, db: &dyn HirDatabase) -> FnDefId {
+        db.intern_callable_def(self).into()
+    }
+
+    fn from_chalk(db: &dyn HirDatabase, fn_def_id: FnDefId) -> CallableDef {
+        db.lookup_intern_callable_def(fn_def_id.into())
+    }
+}
+
 impl ToChalk for TypeAliasId {
     type Chalk = AssocTypeId;
 
diff --git a/crates/ra_hir_ty/src/traits/chalk/tls.rs b/crates/ra_hir_ty/src/traits/chalk/tls.rs
index ebf402a0793a..d88828c7c0a0 100644
--- a/crates/ra_hir_ty/src/traits/chalk/tls.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/tls.rs
@@ -247,10 +247,24 @@ impl DebugContext<'_> {
 
     pub fn debug_fn_def_id(
         &self,
-        _fn_def_id: chalk_ir::FnDefId,
+        fn_def_id: chalk_ir::FnDefId,
         fmt: &mut fmt::Formatter<'_>,
     ) -> Result<(), fmt::Error> {
-        write!(fmt, "fn")
+        let def: CallableDef = from_chalk(self.0, fn_def_id);
+        let name = match def {
+            CallableDef::FunctionId(ff) => self.0.function_data(ff).name.clone(),
+            CallableDef::StructId(s) => self.0.struct_data(s).name.clone(),
+            CallableDef::EnumVariantId(e) => {
+                let enum_data = self.0.enum_data(e.parent);
+                enum_data.variants[e.local_id].name.clone()
+            }
+        };
+        match def {
+            CallableDef::FunctionId(_) => write!(fmt, "{{fn {}}}", name),
+            CallableDef::StructId(_) | CallableDef::EnumVariantId(_) => {
+                write!(fmt, "{{ctor {}}}", name)
+            }
+        }
     }
 
     pub fn debug_const(

From 194dd9eb0d44284f7e952a1e84296fcda4d90f5e Mon Sep 17 00:00:00 2001
From: Florian Diebold 
Date: Fri, 22 May 2020 19:13:17 +0200
Subject: [PATCH 031/163] Use Chalk's Ty::Function for function pointer types

Function pointers can be 'higher-ranked' over lifetimes, which is why they're
not an application type in Chalk, but since we don't model lifetimes it doesn't
matter for us yet.
---
 crates/ra_hir_ty/src/tests/traits.rs         | 32 ++++++++++++++++++++
 crates/ra_hir_ty/src/traits/chalk/mapping.rs | 27 ++++++++++++-----
 2 files changed, 51 insertions(+), 8 deletions(-)

diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index 17c6460761ea..0419bc751b20 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -2685,6 +2685,38 @@ fn test() {
     );
 }
 
+#[test]
+fn builtin_fn_ptr_copy() {
+    assert_snapshot!(
+        infer_with_mismatches(r#"
+#[lang = "copy"]
+trait Copy {}
+
+trait Test { fn test(&self) -> bool; }
+impl Test for T {}
+
+fn test(f1: fn(), f2: fn(usize) -> u8, f3: fn(u8, u8) -> &u8) {
+    f1.test();
+    f2.test();
+    f3.test();
+}
+"#, true),
+        @r###"
+    55..59 'self': &Self
+    109..111 'f1': fn()
+    119..121 'f2': fn(usize) -> u8
+    140..142 'f3': fn(u8, u8) -> &u8
+    163..211 '{     ...t(); }': ()
+    169..171 'f1': fn()
+    169..178 'f1.test()': bool
+    184..186 'f2': fn(usize) -> u8
+    184..193 'f2.test()': bool
+    199..201 'f3': fn(u8, u8) -> &u8
+    199..208 'f3.test()': bool
+    "###
+    );
+}
+
 #[test]
 fn builtin_sized() {
     assert_snapshot!(
diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
index 7841a0e21865..7082cb09573a 100644
--- a/crates/ra_hir_ty/src/traits/chalk/mapping.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
@@ -26,14 +26,19 @@ impl ToChalk for Ty {
     type Chalk = chalk_ir::Ty;
     fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Ty {
         match self {
-            Ty::Apply(apply_ty) => {
-                if let TypeCtor::Ref(m) = apply_ty.ctor {
-                    return ref_to_chalk(db, m, apply_ty.parameters);
+            Ty::Apply(apply_ty) => match apply_ty.ctor {
+                TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters),
+                TypeCtor::FnPtr { num_args: _ } => {
+                    let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner);
+                    chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: 0, substitution })
+                        .intern(&Interner)
                 }
-                let name = apply_ty.ctor.to_chalk(db);
-                let substitution = apply_ty.parameters.to_chalk(db);
-                chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner)
-            }
+                _ => {
+                    let name = apply_ty.ctor.to_chalk(db);
+                    let substitution = apply_ty.parameters.to_chalk(db);
+                    chalk_ir::ApplicationTy { name, substitution }.cast(&Interner).intern(&Interner)
+                }
+            },
             Ty::Projection(proj_ty) => {
                 let associated_ty_id = proj_ty.associated_ty.to_chalk(db);
                 let substitution = proj_ty.parameters.to_chalk(db);
@@ -93,7 +98,13 @@ impl ToChalk for Ty {
                 Ty::Projection(ProjectionTy { associated_ty, parameters })
             }
             chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(),
-            chalk_ir::TyData::Function(_) => unimplemented!(),
+            chalk_ir::TyData::Function(chalk_ir::Fn { num_binders: _, substitution }) => {
+                let parameters: Substs = from_chalk(db, substitution);
+                Ty::Apply(ApplicationTy {
+                    ctor: TypeCtor::FnPtr { num_args: (parameters.len() - 1) as u16 },
+                    parameters,
+                })
+            }
             chalk_ir::TyData::BoundVar(idx) => Ty::Bound(idx),
             chalk_ir::TyData::InferenceVar(_iv) => Ty::Unknown,
             chalk_ir::TyData::Dyn(where_clauses) => {

From d8af45472e5d44a8458b31a40e4584549cb990e7 Mon Sep 17 00:00:00 2001
From: kjeremy 
Date: Fri, 22 May 2020 17:13:14 -0400
Subject: [PATCH 032/163] Remove unnecessary clone that prevented clippy from
 moving on

---
 crates/ra_assists/src/handlers/reorder_fields.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs
index 30229edc2f24..897da283238f 100644
--- a/crates/ra_assists/src/handlers/reorder_fields.rs
+++ b/crates/ra_assists/src/handlers/reorder_fields.rs
@@ -23,7 +23,7 @@ use crate::{AssistContext, AssistId, Assists};
 // ```
 //
 pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
-    reorder::(acc, ctx.clone()).or_else(|| reorder::(acc, ctx))
+    reorder::(acc, ctx).or_else(|| reorder::(acc, ctx))
 }
 
 fn reorder(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {

From 7a46a9949059f24fac211636f95ee914db9a3117 Mon Sep 17 00:00:00 2001
From: kjeremy 
Date: Fri, 22 May 2020 17:26:31 -0400
Subject: [PATCH 033/163] And a few drive-bys

---
 crates/rust-analyzer/src/config.rs             | 6 ++----
 crates/rust-analyzer/src/main_loop/handlers.rs | 2 +-
 crates/rust-analyzer/src/to_proto.rs           | 2 +-
 3 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 0e4412ade006..c0f7c2c0c51b 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -269,10 +269,8 @@ impl Config {
             {
                 self.client_caps.hierarchical_symbols = value
             }
-            if let Some(value) = doc_caps
-                .code_action
-                .as_ref()
-                .and_then(|it| Some(it.code_action_literal_support.is_some()))
+            if let Some(value) =
+                doc_caps.code_action.as_ref().map(|it| it.code_action_literal_support.is_some())
             {
                 self.client_caps.code_action_literals = value;
             }
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index a9703e1d6f9d..ba685755668a 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -281,7 +281,7 @@ pub fn handle_document_symbol(
             kind: symbol.kind,
             deprecated: symbol.deprecated,
             location: Location::new(url.clone(), symbol.range),
-            container_name: container_name,
+            container_name,
         });
 
         for child in symbol.children.iter().flatten() {
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 461944ada6e4..7dd7d9416ffe 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -455,7 +455,7 @@ pub(crate) fn snippet_text_document_edit(
     let edits = source_file_edit
         .edit
         .into_iter()
-        .map(|it| snippet_text_edit(&line_index, line_endings, is_snippet, it.clone()))
+        .map(|it| snippet_text_edit(&line_index, line_endings, is_snippet, it))
         .collect();
     Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits })
 }

From 4967b811dda4eae3910514ea8788f0fab256ad8c Mon Sep 17 00:00:00 2001
From: Jess Balint 
Date: Fri, 22 May 2020 19:09:37 -0500
Subject: [PATCH 034/163] tweak syntax

---
 .../handlers/change_lifetime_anon_to_named.rs | 91 +++++++++----------
 1 file changed, 41 insertions(+), 50 deletions(-)

diff --git a/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs b/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
index 67d50cfda5cd..922213607051 100644
--- a/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
+++ b/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
@@ -37,57 +37,48 @@ pub(crate) fn change_lifetime_anon_to_named(acc: &mut Assists, ctx: &AssistConte
         // only allow naming the last anonymous lifetime
         return None;
     }
-    match lifetime_arg.syntax().ancestors().find_map(ast::ImplDef::cast) {
-        Some(impl_def) => {
-            // get the `impl` keyword so we know where to add the lifetime argument
-            let impl_kw = impl_def.syntax().first_child_or_token()?.into_token()?;
-            if impl_kw.kind() != SyntaxKind::IMPL_KW {
-                return None;
-            }
-            let new_lifetime_param = match impl_def.type_param_list() {
-                Some(type_params) => {
-                    let used_lifetime_params: HashSet<_> = type_params
-                        .lifetime_params()
-                        .map(|p| {
-                            let mut param_name = p.syntax().text().to_string();
-                            param_name.remove(0);
-                            param_name
-                        })
-                        .collect();
-                    (b'a'..=b'z')
-                        .map(char::from)
-                        .find(|c| !used_lifetime_params.contains(&c.to_string()))?
-                }
-                None => 'a',
-            };
-            acc.add(
-                AssistId("change_lifetime_anon_to_named"),
-                "Give anonymous lifetime a name",
-                lifetime_arg.syntax().text_range(),
-                |builder| {
-                    match impl_def.type_param_list() {
-                        Some(type_params) => {
-                            builder.insert(
-                                (u32::from(type_params.syntax().text_range().end()) - 1).into(),
-                                format!(", '{}", new_lifetime_param),
-                            );
-                        }
-                        None => {
-                            builder.insert(
-                                impl_kw.text_range().end(),
-                                format!("<'{}>", new_lifetime_param),
-                            );
-                        }
-                    }
-                    builder.replace(
-                        lifetime_arg.syntax().text_range(),
-                        format!("'{}", new_lifetime_param),
-                    );
-                },
-            )
-        }
-        _ => None,
+    let impl_def = lifetime_arg.syntax().ancestors().find_map(ast::ImplDef::cast)?;
+    // get the `impl` keyword so we know where to add the lifetime argument
+    let impl_kw = impl_def.syntax().first_child_or_token()?.into_token()?;
+    if impl_kw.kind() != SyntaxKind::IMPL_KW {
+        return None;
     }
+    let new_lifetime_param = match impl_def.type_param_list() {
+        Some(type_params) => {
+            let used_lifetime_params: HashSet<_> = type_params
+                .lifetime_params()
+                .map(|p| {
+                    let mut param_name = p.syntax().text().to_string();
+                    param_name.remove(0);
+                    param_name
+                })
+                .collect();
+            (b'a'..=b'z')
+                .map(char::from)
+                .find(|c| !used_lifetime_params.contains(&c.to_string()))?
+        }
+        None => 'a',
+    };
+    acc.add(
+        AssistId("change_lifetime_anon_to_named"),
+        "Give anonymous lifetime a name",
+        lifetime_arg.syntax().text_range(),
+        |builder| {
+            match impl_def.type_param_list() {
+                Some(type_params) => {
+                    builder.insert(
+                        (u32::from(type_params.syntax().text_range().end()) - 1).into(),
+                        format!(", '{}", new_lifetime_param),
+                    );
+                }
+                None => {
+                    builder
+                        .insert(impl_kw.text_range().end(), format!("<'{}>", new_lifetime_param));
+                }
+            }
+            builder.replace(lifetime_arg.syntax().text_range(), format!("'{}", new_lifetime_param));
+        },
+    )
 }
 
 #[cfg(test)]

From 6197a960dffa4c7e8c2f60168a476e5f39ba50aa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Galil=C3=A9e=20=27Bill=27=20Enguehard?=
 
Date: Sat, 23 May 2020 08:59:51 +0200
Subject: [PATCH 035/163] Fix resolve_proc_macro heavy test

---
 crates/rust-analyzer/tests/heavy_tests/main.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 4e94c37e1c16..738a9a8e3750 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -774,5 +774,5 @@ 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```""#)
+    assert_eq!(value, r#""foo::Bar\n___\n\n```rust\nfn bar()\n```""#)
 }

From 1739a542f89ebf207eb42fec9ffc31f365e50ff6 Mon Sep 17 00:00:00 2001
From: vsrs 
Date: Sat, 23 May 2020 15:20:46 +0300
Subject: [PATCH 036/163] Add `npm test` to CI

---
 .github/workflows/ci.yaml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 00f299ff1828..7cb89834e2aa 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -116,5 +116,8 @@ jobs:
     - run: npm run lint
       working-directory: ./editors/code
 
+    - run: npm test
+      working-directory: ./editors/code
+
     - run: npm run package --scripts-prepend-node-path
       working-directory: ./editors/code

From ca230627c630e3f7dcd55a26aadd161365d29628 Mon Sep 17 00:00:00 2001
From: vsrs <62505555+vsrs@users.noreply.github.com>
Date: Sat, 23 May 2020 15:37:07 +0300
Subject: [PATCH 037/163] try to run TypeScript CI on windows

---
 .github/workflows/ci.yaml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 7cb89834e2aa..bf56ac610530 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -97,7 +97,7 @@ jobs:
 
   typescript:
     name: TypeScript
-    runs-on: ubuntu-latest
+    runs-on: windows-latest
     steps:
     - name: Checkout repository
       uses: actions/checkout@v2
@@ -110,7 +110,7 @@ jobs:
     - run: npm ci
       working-directory: ./editors/code
 
-    - run: npm audit || { sleep 10 && npm audit; } || { sleep 30 && npm audit; }
+    - run: npm audit # || { sleep 10 && npm audit; } || { sleep 30 && npm audit; }
       working-directory: ./editors/code
 
     - run: npm run lint

From bddbe16d93ec74a689fcc352977acc9bf87260b0 Mon Sep 17 00:00:00 2001
From: vsrs <62505555+vsrs@users.noreply.github.com>
Date: Sat, 23 May 2020 16:04:14 +0300
Subject: [PATCH 038/163] Start xvfb manually on ubuntu

---
 .github/workflows/ci.yaml | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index bf56ac610530..3ec6da1d11ca 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -97,7 +97,7 @@ jobs:
 
   typescript:
     name: TypeScript
-    runs-on: windows-latest
+    runs-on: ubuntu-latest
     steps:
     - name: Checkout repository
       uses: actions/checkout@v2
@@ -116,8 +116,17 @@ jobs:
     - run: npm run lint
       working-directory: ./editors/code
 
+    - name: Start xvfb
+      run: |
+        sudo apt-get install xvfb
+        set -e
+        /usr/bin/Xvfb :10 -ac >> /tmp/Xvfb.out 2>&1 &
+        disown -ar
+      
     - run: npm test
       working-directory: ./editors/code
+      env:
+        DISPLAY: :10
 
     - run: npm run package --scripts-prepend-node-path
       working-directory: ./editors/code

From 12ea4a2ba2e85ba39533ccc4197ea7204f7a0e17 Mon Sep 17 00:00:00 2001
From: vsrs <62505555+vsrs@users.noreply.github.com>
Date: Sat, 23 May 2020 16:13:47 +0300
Subject: [PATCH 039/163] Try GabrielBB/xvfb to run vscode tests

---
 .github/workflows/ci.yaml | 16 +++++-----------
 1 file changed, 5 insertions(+), 11 deletions(-)

diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 3ec6da1d11ca..af71a3d54a6a 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -116,17 +116,11 @@ jobs:
     - run: npm run lint
       working-directory: ./editors/code
 
-    - name: Start xvfb
-      run: |
-        sudo apt-get install xvfb
-        set -e
-        /usr/bin/Xvfb :10 -ac >> /tmp/Xvfb.out 2>&1 &
-        disown -ar
-      
-    - run: npm test
-      working-directory: ./editors/code
-      env:
-        DISPLAY: :10
+    - name: Run vscode tests
+      uses: GabrielBB/xvfb-action@v1.2
+      with:
+        run: npm test
+        working-directory: ./editors/code
 
     - run: npm run package --scripts-prepend-node-path
       working-directory: ./editors/code

From a09b308b4e1d6d1be3ce55b76f33705f9357912b Mon Sep 17 00:00:00 2001
From: vsrs <62505555+vsrs@users.noreply.github.com>
Date: Sat, 23 May 2020 16:24:21 +0300
Subject: [PATCH 040/163] xvfb-action fix

---
 .github/workflows/ci.yaml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index af71a3d54a6a..e3e5a2c57d3b 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -110,7 +110,7 @@ jobs:
     - run: npm ci
       working-directory: ./editors/code
 
-    - run: npm audit # || { sleep 10 && npm audit; } || { sleep 30 && npm audit; }
+    - run: npm audit || { sleep 10 && npm audit; } || { sleep 30 && npm audit; }
       working-directory: ./editors/code
 
     - run: npm run lint
@@ -119,8 +119,8 @@ jobs:
     - name: Run vscode tests
       uses: GabrielBB/xvfb-action@v1.2
       with:
-        run: npm test
-        working-directory: ./editors/code
+        run: npm --prefix ./editors/code test
+        # working-directory: ./editors/code  # does not work: https://github.com/GabrielBB/xvfb-action/issues/8
 
     - run: npm run package --scripts-prepend-node-path
       working-directory: ./editors/code

From e3223a7fd3935fdbb86a8f99af35866cc9957d35 Mon Sep 17 00:00:00 2001
From: vsrs <62505555+vsrs@users.noreply.github.com>
Date: Sat, 23 May 2020 16:39:30 +0300
Subject: [PATCH 041/163] OS matrix for TypeScript

---
 .github/workflows/ci.yaml | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index e3e5a2c57d3b..0d231cb74172 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -97,7 +97,13 @@ jobs:
 
   typescript:
     name: TypeScript
-    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-latest, windows-latest, macos-latest]
+
+    runs-on: ${{ matrix.os }}
+    
     steps:
     - name: Checkout repository
       uses: actions/checkout@v2
@@ -111,6 +117,7 @@ jobs:
       working-directory: ./editors/code
 
     - run: npm audit || { sleep 10 && npm audit; } || { sleep 30 && npm audit; }
+      if: runner.os == 'Linux'
       working-directory: ./editors/code
 
     - run: npm run lint

From 1797b665a4dd82ba176b319c850a8875df327a5d Mon Sep 17 00:00:00 2001
From: vsrs <62505555+vsrs@users.noreply.github.com>
Date: Sat, 23 May 2020 17:07:25 +0300
Subject: [PATCH 042/163] vscode tests configuration for more verbose output

---
 .github/workflows/ci.yaml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 0d231cb74172..ed9191c49c37 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -125,6 +125,8 @@ jobs:
 
     - name: Run vscode tests
       uses: GabrielBB/xvfb-action@v1.2
+      env:
+        VSCODE_CLI: 1
       with:
         run: npm --prefix ./editors/code test
         # working-directory: ./editors/code  # does not work: https://github.com/GabrielBB/xvfb-action/issues/8

From 48d7c61e26398fa33b94e0e4bd0d2d1697ed4921 Mon Sep 17 00:00:00 2001
From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
Date: Sat, 23 May 2020 20:59:18 +0200
Subject: [PATCH 043/163] add support of feature flag for runnables #4464

Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
---
 Cargo.lock                                    |  3 +
 crates/ra_cfg/src/cfg_expr.rs                 | 55 -----------
 crates/ra_ide/src/runnables.rs                | 76 ++++++++-------
 crates/rust-analyzer/Cargo.toml               |  3 +
 crates/rust-analyzer/src/cargo_target_spec.rs | 13 ++-
 .../rust-analyzer/src/main_loop/handlers.rs   | 94 ++++++++++++++++++-
 6 files changed, 145 insertions(+), 99 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index c062366923dd..b406522d2404 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1387,17 +1387,20 @@ dependencies = [
  "lsp-types",
  "parking_lot",
  "pico-args",
+ "ra_cfg",
  "ra_db",
  "ra_flycheck",
  "ra_hir",
  "ra_hir_def",
  "ra_hir_ty",
  "ra_ide",
+ "ra_mbe",
  "ra_proc_macro_srv",
  "ra_prof",
  "ra_project_model",
  "ra_syntax",
  "ra_text_edit",
+ "ra_tt",
  "ra_vfs",
  "rand",
  "relative-path",
diff --git a/crates/ra_cfg/src/cfg_expr.rs b/crates/ra_cfg/src/cfg_expr.rs
index 98f44f56d039..85b100c6adfe 100644
--- a/crates/ra_cfg/src/cfg_expr.rs
+++ b/crates/ra_cfg/src/cfg_expr.rs
@@ -33,33 +33,6 @@ impl CfgExpr {
             CfgExpr::Not(pred) => pred.fold(query).map(|s| !s),
         }
     }
-
-    /// Return minimal features needed
-    pub fn minimal_features_needed(&self) -> Vec {
-        let mut features = vec![];
-        self.collect_minimal_features_needed(&mut features);
-
-        features
-    }
-
-    fn collect_minimal_features_needed(&self, features: &mut Vec) {
-        match self {
-            CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()),
-            CfgExpr::All(preds) => {
-                preds.iter().for_each(|cfg| cfg.collect_minimal_features_needed(features));
-            }
-            CfgExpr::Any(preds) => {
-                for cfg in preds {
-                    let len_features = features.len();
-                    cfg.collect_minimal_features_needed(features);
-                    if len_features != features.len() {
-                        break;
-                    }
-                }
-            }
-            _ => {}
-        }
-    }
 }
 
 pub fn parse_cfg(tt: &Subtree) -> CfgExpr {
@@ -160,32 +133,4 @@ mod tests {
             ]),
         );
     }
-
-    #[test]
-    fn test_cfg_expr_minimal_features_needed() {
-        let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
-        let cfg_expr = parse_cfg(&subtree);
-
-        assert_eq!(cfg_expr.minimal_features_needed(), vec![SmolStr::new("baz")]);
-
-        let (subtree, _) =
-            get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#);
-        let cfg_expr = parse_cfg(&subtree);
-
-        assert_eq!(
-            cfg_expr.minimal_features_needed(),
-            vec![SmolStr::new("baz"), SmolStr::new("foo")]
-        );
-
-        let (subtree, _) =
-            get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#);
-        let cfg_expr = parse_cfg(&subtree);
-
-        assert_eq!(cfg_expr.minimal_features_needed(), vec![SmolStr::new("baz")]);
-
-        let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#);
-        let cfg_expr = parse_cfg(&subtree);
-
-        assert!(cfg_expr.minimal_features_needed().is_empty());
-    }
 }
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index a460370c5c32..a96c5f157ba5 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -10,13 +10,14 @@ use ra_syntax::{
 
 use crate::FileId;
 use ast::DocCommentsOwner;
+use ra_cfg::CfgExpr;
 use std::fmt::Display;
 
 #[derive(Debug)]
 pub struct Runnable {
     pub range: TextRange,
     pub kind: RunnableKind,
-    pub features_needed: Option>,
+    pub cfg_exprs: Vec,
 }
 
 #[derive(Debug)]
@@ -118,9 +119,10 @@ fn runnable_fn(
     };
 
     let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def));
-    let features_needed = get_features_needed(attrs);
+    let cfg_exprs =
+        attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
 
-    Some(Runnable { range: fn_def.syntax().text_range(), kind, features_needed })
+    Some(Runnable { range: fn_def.syntax().text_range(), kind, cfg_exprs })
 }
 
 #[derive(Debug)]
@@ -183,15 +185,10 @@ fn runnable_mod(
         .join("::");
 
     let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module));
-    let features_needed = get_features_needed(attrs);
+    let cfg_exprs =
+        attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
 
-    Some(Runnable { range, kind: RunnableKind::TestMod { path }, features_needed })
-}
-
-fn get_features_needed(attrs: Attrs) -> Option> {
-    let cfg_expr = attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree));
-    let features_needed = cfg_expr.map(|cfg| cfg.minimal_features_needed()).flatten().collect();
-    Some(features_needed).filter(|it: &Vec| !it.is_empty())
+    Some(Runnable { range, kind: RunnableKind::TestMod { path }, cfg_exprs })
 }
 
 #[cfg(test)]
@@ -223,7 +220,7 @@ mod tests {
             Runnable {
                 range: 1..21,
                 kind: Bin,
-                features_needed: None,
+                cfg_exprs: [],
             },
             Runnable {
                 range: 22..46,
@@ -235,7 +232,7 @@ mod tests {
                         ignore: false,
                     },
                 },
-                features_needed: None,
+                cfg_exprs: [],
             },
             Runnable {
                 range: 47..81,
@@ -247,7 +244,7 @@ mod tests {
                         ignore: true,
                     },
                 },
-                features_needed: None,
+                cfg_exprs: [],
             },
         ]
         "###
@@ -275,7 +272,7 @@ mod tests {
             Runnable {
                 range: 1..21,
                 kind: Bin,
-                features_needed: None,
+                cfg_exprs: [],
             },
             Runnable {
                 range: 22..64,
@@ -284,7 +281,7 @@ mod tests {
                         "foo",
                     ),
                 },
-                features_needed: None,
+                cfg_exprs: [],
             },
         ]
         "###
@@ -315,7 +312,7 @@ mod tests {
             Runnable {
                 range: 1..21,
                 kind: Bin,
-                features_needed: None,
+                cfg_exprs: [],
             },
             Runnable {
                 range: 51..105,
@@ -324,7 +321,7 @@ mod tests {
                         "Data::foo",
                     ),
                 },
-                features_needed: None,
+                cfg_exprs: [],
             },
         ]
         "###
@@ -352,7 +349,7 @@ mod tests {
                 kind: TestMod {
                     path: "test_mod",
                 },
-                features_needed: None,
+                cfg_exprs: [],
             },
             Runnable {
                 range: 28..57,
@@ -364,7 +361,7 @@ mod tests {
                         ignore: false,
                     },
                 },
-                features_needed: None,
+                cfg_exprs: [],
             },
         ]
         "###
@@ -394,7 +391,7 @@ mod tests {
                 kind: TestMod {
                     path: "foo::test_mod",
                 },
-                features_needed: None,
+                cfg_exprs: [],
             },
             Runnable {
                 range: 46..79,
@@ -406,7 +403,7 @@ mod tests {
                         ignore: false,
                     },
                 },
-                features_needed: None,
+                cfg_exprs: [],
             },
         ]
         "###
@@ -438,7 +435,7 @@ mod tests {
                 kind: TestMod {
                     path: "foo::bar::test_mod",
                 },
-                features_needed: None,
+                cfg_exprs: [],
             },
             Runnable {
                 range: 68..105,
@@ -450,7 +447,7 @@ mod tests {
                         ignore: false,
                     },
                 },
-                features_needed: None,
+                cfg_exprs: [],
             },
         ]
         "###
@@ -482,11 +479,12 @@ mod tests {
                         ignore: false,
                     },
                 },
-                features_needed: Some(
-                    [
-                        "foo",
-                    ],
-                ),
+                cfg_exprs: [
+                    KeyValue {
+                        key: "feature",
+                        value: "foo",
+                    },
+                ],
             },
         ]
         "###
@@ -518,12 +516,20 @@ mod tests {
                         ignore: false,
                     },
                 },
-                features_needed: Some(
-                    [
-                        "foo",
-                        "bar",
-                    ],
-                ),
+                cfg_exprs: [
+                    All(
+                        [
+                            KeyValue {
+                                key: "feature",
+                                value: "foo",
+                            },
+                            KeyValue {
+                                key: "feature",
+                                value: "bar",
+                            },
+                        ],
+                    ),
+                ],
             },
         ]
         "###
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 9b2d29b1d57c..65b487db3b98 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -40,6 +40,7 @@ ra_project_model = { path = "../ra_project_model" }
 ra_syntax = { path = "../ra_syntax" }
 ra_text_edit = { path = "../ra_text_edit" }
 ra_vfs = "0.6.0"
+ra_cfg = { path = "../ra_cfg"}
 
 # This should only be used in CLI
 ra_db = { path = "../ra_db" }
@@ -55,6 +56,8 @@ winapi = "0.3.8"
 tempfile = "3.1.0"
 insta = "0.16.0"
 test_utils = { path = "../test_utils" }
+mbe = { path = "../ra_mbe", package = "ra_mbe" }
+tt = { path = "../ra_tt", package = "ra_tt" }
 
 [features]
 jemalloc = [ "ra_prof/jemalloc" ]
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index a2f85060ba37..441fb61df006 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -21,7 +21,7 @@ impl CargoTargetSpec {
     pub(crate) fn runnable_args(
         spec: Option,
         kind: &RunnableKind,
-        features_needed: &Option>,
+        features_needed: &Vec,
     ) -> Result<(Vec, Vec)> {
         let mut args = Vec::new();
         let mut extra_args = Vec::new();
@@ -76,12 +76,11 @@ impl CargoTargetSpec {
             }
         }
 
-        if let Some(features_needed) = features_needed {
-            features_needed.iter().for_each(|feature| {
-                args.push("--features".to_string());
-                args.push(feature.to_string());
-            });
-        }
+        features_needed.iter().for_each(|feature| {
+            args.push("--features".to_string());
+            args.push(feature.to_string());
+        });
+
         Ok((args, extra_args))
     }
 
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index cc9abd162bb1..5f6e845a8878 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -17,12 +17,13 @@ use lsp_types::{
     SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
     SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, TextEdit, Url, WorkspaceEdit,
 };
+use ra_cfg::CfgExpr;
 use ra_ide::{
     Assist, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
 };
 use ra_prof::profile;
 use ra_project_model::TargetKind;
-use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize};
+use ra_syntax::{AstNode, SmolStr, SyntaxKind, TextRange, TextSize};
 use rustc_hash::FxHashMap;
 use serde::{Deserialize, Serialize};
 use serde_json::to_value;
@@ -38,6 +39,7 @@ use crate::{
     world::WorldSnapshot,
     LspError, Result,
 };
+use hir::Attrs;
 
 pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result {
     let _p = profile("handle_analyzer_status");
@@ -1006,8 +1008,12 @@ fn to_lsp_runnable(
 ) -> Result {
     let spec = CargoTargetSpec::for_file(world, file_id)?;
     let target = spec.as_ref().map(|s| s.target.clone());
+    let mut features_needed = vec![];
+    for cfg_expr in &runnable.cfg_exprs {
+        collect_minimal_features_needed(cfg_expr, &mut features_needed);
+    }
     let (args, extra_args) =
-        CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.features_needed)?;
+        CargoTargetSpec::runnable_args(spec, &runnable.kind, &features_needed)?;
     let line_index = world.analysis().file_line_index(file_id)?;
     let label = match &runnable.kind {
         RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
@@ -1033,6 +1039,39 @@ fn to_lsp_runnable(
     })
 }
 
+fn get_features_needed(attrs: Attrs) -> Option> {
+    let cfg_expr = attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree));
+    let features_needed = cfg_expr
+        .map(|cfg| {
+            let mut min_features = vec![];
+            collect_minimal_features_needed(&cfg, &mut min_features);
+            min_features
+        })
+        .flatten()
+        .collect();
+    Some(features_needed).filter(|it: &Vec| !it.is_empty())
+}
+
+/// Fill minimal features needed
+fn collect_minimal_features_needed(cfg_expr: &CfgExpr, features: &mut Vec) {
+    match cfg_expr {
+        CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()),
+        CfgExpr::All(preds) => {
+            preds.iter().for_each(|cfg| collect_minimal_features_needed(cfg, features));
+        }
+        CfgExpr::Any(preds) => {
+            for cfg in preds {
+                let len_features = features.len();
+                collect_minimal_features_needed(cfg, features);
+                if len_features != features.len() {
+                    break;
+                }
+            }
+        }
+        _ => {}
+    }
+}
+
 pub fn handle_inlay_hints(
     world: WorldSnapshot,
     params: InlayHintsParams,
@@ -1169,3 +1208,54 @@ pub fn handle_semantic_tokens_range(
     let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
     Ok(Some(semantic_tokens.into()))
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use mbe::{ast_to_token_tree, TokenMap};
+    use ra_cfg::parse_cfg;
+    use ra_syntax::{
+        ast::{self, AstNode},
+        SmolStr,
+    };
+
+    fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
+        let source_file = ast::SourceFile::parse(input).ok().unwrap();
+        let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+        ast_to_token_tree(&tt).unwrap()
+    }
+
+    #[test]
+    fn test_cfg_expr_minimal_features_needed() {
+        let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
+        let cfg_expr = parse_cfg(&subtree);
+        let mut min_features = vec![];
+        collect_minimal_features_needed(&cfg_expr, &mut min_features);
+
+        assert_eq!(min_features, vec![SmolStr::new("baz")]);
+
+        let (subtree, _) =
+            get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#);
+        let cfg_expr = parse_cfg(&subtree);
+
+        let mut min_features = vec![];
+        collect_minimal_features_needed(&cfg_expr, &mut min_features);
+        assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]);
+
+        let (subtree, _) =
+            get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#);
+        let cfg_expr = parse_cfg(&subtree);
+
+        let mut min_features = vec![];
+        collect_minimal_features_needed(&cfg_expr, &mut min_features);
+        assert_eq!(min_features, vec![SmolStr::new("baz")]);
+
+        let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#);
+        let cfg_expr = parse_cfg(&subtree);
+
+        let mut min_features = vec![];
+        collect_minimal_features_needed(&cfg_expr, &mut min_features);
+        assert!(min_features.is_empty());
+    }
+}

From cd4ffc1945a3a1ca89776e9abdcd60b1896f356c Mon Sep 17 00:00:00 2001
From: Julian Wollersberger
 <24991778+Julian-Wollersberger@users.noreply.github.com>
Date: Sun, 24 May 2020 13:12:16 +0200
Subject: [PATCH 044/163] Update to rustc_lexer version 660. Change
 `unescape_*()` to `unescape_literal()`.

---
 Cargo.lock                         |  4 ++--
 crates/ra_syntax/Cargo.toml        |  2 +-
 crates/ra_syntax/src/ast/tokens.rs |  5 +++--
 crates/ra_syntax/src/validation.rs | 16 ++++++++--------
 4 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 007f05b4d03e..a511e0d28049 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1398,9 +1398,9 @@ dependencies = [
 
 [[package]]
 name = "rustc-ap-rustc_lexer"
-version = "656.0.0"
+version = "660.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cbba98ec46e96a4663197dfa8c0378752de2006e314e5400c0ca74929d6692f"
+checksum = "30760dbcc7667c9e0da561e980e24867ca7f4526ce060a3d7e6d9dcfeaae88d1"
 dependencies = [
  "unicode-xid",
 ]
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml
index c07ff488e4f0..a9a5cc7bc6ed 100644
--- a/crates/ra_syntax/Cargo.toml
+++ b/crates/ra_syntax/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
 [dependencies]
 itertools = "0.9.0"
 rowan = "0.10.0"
-rustc_lexer = { version = "656.0.0", package = "rustc-ap-rustc_lexer" }
+rustc_lexer = { version = "660.0.0", package = "rustc-ap-rustc_lexer" }
 rustc-hash = "1.1.0"
 arrayvec = "0.5.1"
 once_cell = "1.3.1"
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs
index 74906d8a62ef..3cd6d99c3889 100644
--- a/crates/ra_syntax/src/ast/tokens.rs
+++ b/crates/ra_syntax/src/ast/tokens.rs
@@ -6,6 +6,7 @@ use crate::{
     ast::{AstToken, Comment, RawString, String, Whitespace},
     TextRange, TextSize,
 };
+use rustc_lexer::unescape::{unescape_literal, Mode};
 
 impl Comment {
     pub fn kind(&self) -> CommentKind {
@@ -147,7 +148,7 @@ impl HasStringValue for String {
 
         let mut buf = std::string::String::with_capacity(text.len());
         let mut has_error = false;
-        rustc_lexer::unescape::unescape_str(text, &mut |_, unescaped_char| match unescaped_char {
+        unescape_literal(text, Mode::Str, &mut |_, unescaped_char| match unescaped_char {
             Ok(c) => buf.push(c),
             Err(_) => has_error = true,
         });
@@ -498,7 +499,7 @@ impl HasFormatSpecifier for String {
         let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start();
 
         let mut res = Vec::with_capacity(text.len());
-        rustc_lexer::unescape::unescape_str(text, &mut |range, unescaped_char| {
+        unescape_literal(text, Mode::Str, &mut |range, unescaped_char| {
             res.push((
                 TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap())
                     + offset,
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs
index d68cf0a82bf4..436ab033dc8b 100644
--- a/crates/ra_syntax/src/validation.rs
+++ b/crates/ra_syntax/src/validation.rs
@@ -2,15 +2,15 @@
 
 mod block;
 
-use std::convert::TryFrom;
-
-use rustc_lexer::unescape;
-
 use crate::{
     ast, match_ast, AstNode, SyntaxError,
     SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST_DEF, FN_DEF, INT_NUMBER, STRING, TYPE_ALIAS_DEF},
     SyntaxNode, SyntaxToken, TextSize, T,
 };
+use rustc_lexer::unescape::{
+    self, unescape_byte, unescape_byte_literal, unescape_char, unescape_literal, Mode,
+};
+use std::convert::TryFrom;
 
 fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str {
     use unescape::EscapeError as EE;
@@ -121,18 +121,18 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec) {
 
     match token.kind() {
         BYTE => {
-            if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape::unescape_byte) {
+            if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape_byte) {
                 push_err(2, e);
             }
         }
         CHAR => {
-            if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape::unescape_char) {
+            if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape_char) {
                 push_err(1, e);
             }
         }
         BYTE_STRING => {
             if let Some(without_quotes) = unquote(text, 2, '"') {
-                unescape::unescape_byte_str(without_quotes, &mut |range, char| {
+                unescape_byte_literal(without_quotes, Mode::ByteStr, &mut |range, char| {
                     if let Err(err) = char {
                         push_err(2, (range.start, err));
                     }
@@ -141,7 +141,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec) {
         }
         STRING => {
             if let Some(without_quotes) = unquote(text, 1, '"') {
-                unescape::unescape_str(without_quotes, &mut |range, char| {
+                unescape_literal(without_quotes, Mode::Str, &mut |range, char| {
                     if let Err(err) = char {
                         push_err(1, (range.start, err));
                     }

From ff9d553fe3af092e4ef5467f07cef62af27b868b Mon Sep 17 00:00:00 2001
From: Julian Wollersberger
 <24991778+Julian-Wollersberger@users.noreply.github.com>
Date: Sun, 24 May 2020 13:17:16 +0200
Subject: [PATCH 045/163] Some FIXMEs were outdated: * Done at line 243: "Add
 validation of `crate` keyword not appearing in the middle of the symbol path"
 * Already happened: "Remove validation of unterminated literals (it is
 already implemented in `tokenize()`)" * Happens in `unescape()`: "Add
 validation of character literal containing only a single char" * Missing:
 "raw string literals and raw byte string literals"

---
 crates/ra_syntax/src/validation.rs | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs
index 436ab033dc8b..fdec48fb0b88 100644
--- a/crates/ra_syntax/src/validation.rs
+++ b/crates/ra_syntax/src/validation.rs
@@ -81,10 +81,8 @@ fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str {
 
 pub(crate) fn validate(root: &SyntaxNode) -> Vec {
     // FIXME:
-    // * Add validation of character literal containing only a single char
-    // * Add validation of `crate` keyword not appearing in the middle of the symbol path
+    // * Add unescape validation of raw string literals and raw byte string literals
     // * Add validation of doc comments are being attached to nodes
-    // * Remove validation of unterminated literals (it is already implemented in `tokenize()`)
 
     let mut errors = Vec::new();
     for node in root.descendants() {

From 27ed376bc4dfed39295af650effe63007e443b6f Mon Sep 17 00:00:00 2001
From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
Date: Sun, 24 May 2020 13:34:34 +0200
Subject: [PATCH 046/163] add support of feature flag for runnables #4464

Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
---
 crates/ra_ide/src/runnables.rs                 |  2 +-
 crates/rust-analyzer/src/main_loop/handlers.rs | 14 --------------
 2 files changed, 1 insertion(+), 15 deletions(-)

diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index a96c5f157ba5..ed98e58e00ca 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -5,7 +5,7 @@ use itertools::Itertools;
 use ra_ide_db::RootDatabase;
 use ra_syntax::{
     ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner},
-    match_ast, SmolStr, SyntaxNode, TextRange,
+    match_ast, SyntaxNode, TextRange,
 };
 
 use crate::FileId;
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 5f6e845a8878..c51e4346aa88 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -39,7 +39,6 @@ use crate::{
     world::WorldSnapshot,
     LspError, Result,
 };
-use hir::Attrs;
 
 pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result {
     let _p = profile("handle_analyzer_status");
@@ -1039,19 +1038,6 @@ fn to_lsp_runnable(
     })
 }
 
-fn get_features_needed(attrs: Attrs) -> Option> {
-    let cfg_expr = attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree));
-    let features_needed = cfg_expr
-        .map(|cfg| {
-            let mut min_features = vec![];
-            collect_minimal_features_needed(&cfg, &mut min_features);
-            min_features
-        })
-        .flatten()
-        .collect();
-    Some(features_needed).filter(|it: &Vec| !it.is_empty())
-}
-
 /// Fill minimal features needed
 fn collect_minimal_features_needed(cfg_expr: &CfgExpr, features: &mut Vec) {
     match cfg_expr {

From 727ca80e4093e66aec1ac0a31aff524b4192584e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= 
Date: Fri, 22 May 2020 21:16:06 +0300
Subject: [PATCH 047/163] Bump rustc_lexer

---
 Cargo.lock | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index a511e0d28049..b41583579251 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -101,9 +101,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.0.53"
+version = "1.0.54"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "404b1fe4f65288577753b17e3b36a04596ee784493ec249bf81c7f2d2acd751c"
+checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311"
 
 [[package]]
 name = "cfg-if"
@@ -382,9 +382,9 @@ dependencies = [
 
 [[package]]
 name = "fst"
-version = "0.4.3"
+version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81f9cac32c1741cdf6b66be7dcf0d9c7f25ccf12f8aa84c16cfa31f9f14513b3"
+checksum = "a7293de202dbfe786c0b3fe6110a027836c5438ed06db7b715c9955ff4bfea51"
 
 [[package]]
 name = "fuchsia-zircon"
@@ -892,9 +892,9 @@ checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.13"
+version = "1.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53f5ffe53a6b28e37c9c1ce74893477864d64f74778a93a4beb43c8fa167f639"
+checksum = "70a50b9351bfa8d65a7d93ce712dc63d2fd15ddbf2c36990fc7cac344859c04f"
 dependencies = [
  "unicode-xid",
 ]
@@ -1595,9 +1595,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f"
 
 [[package]]
 name = "syn"
-version = "1.0.22"
+version = "1.0.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1425de3c33b0941002740a420b1a906a350b88d08b82b2c8a01035a3f9447bac"
+checksum = "95b5f192649e48a5302a13f2feb224df883b98933222369e4b3b0fe2a5447269"
 dependencies = [
  "proc-macro2",
  "quote",

From d18704ddd269c3bd25db142ac131a7ee86743356 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= 
Date: Fri, 22 May 2020 21:20:59 +0300
Subject: [PATCH 048/163] Bump some npm deps

---
 editors/code/package-lock.json | 62 +++++++++++++++++-----------------
 editors/code/package.json      | 16 ++++-----
 2 files changed, 39 insertions(+), 39 deletions(-)

diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index c322b02c8472..ee6b40a3bde4 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -117,9 +117,9 @@
             "dev": true
         },
         "@types/node": {
-            "version": "12.12.39",
-            "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.39.tgz",
-            "integrity": "sha512-pADGfwnDkr6zagDwEiCVE4yQrv7XDkoeVa4OfA9Ju/zRTk6YNDLGtQbkdL4/56mCQQCs4AhNrBIag6jrp7ZuOg==",
+            "version": "12.12.42",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.42.tgz",
+            "integrity": "sha512-R/9QdYFLL9dE9l5cWWzWIZByVGFd7lk7JVOJ7KD+E1SJ4gni7XJRLz9QTjyYQiHIqEAgku9VgxdLjMlhhUaAFg==",
             "dev": true
         },
         "@types/node-fetch": {
@@ -142,31 +142,31 @@
             }
         },
         "@types/vscode": {
-            "version": "1.45.0",
-            "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.45.0.tgz",
-            "integrity": "sha512-b0Gyir7sPBCqiKLygAhn/AYVfzWD+SMPkWltBrIuPEyTOxSU1wVApWY/FcxYO2EWTRacoubTl4+gvZf86RkecA==",
+            "version": "1.45.1",
+            "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.45.1.tgz",
+            "integrity": "sha512-0NO9qrrEJBO8FsqHCrFMgR2suKnwCsKBWvRSb2OzH5gs4i3QO5AhEMQYrSzDbU/wLPt7N617/rN9lPY213gmwg==",
             "dev": true
         },
         "@typescript-eslint/eslint-plugin": {
-            "version": "2.33.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.33.0.tgz",
-            "integrity": "sha512-QV6P32Btu1sCI/kTqjTNI/8OpCYyvlGjW5vD8MpTIg+HGE5S88HtT1G+880M4bXlvXj/NjsJJG0aGcVh0DdbeQ==",
+            "version": "2.34.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz",
+            "integrity": "sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ==",
             "dev": true,
             "requires": {
-                "@typescript-eslint/experimental-utils": "2.33.0",
+                "@typescript-eslint/experimental-utils": "2.34.0",
                 "functional-red-black-tree": "^1.0.1",
                 "regexpp": "^3.0.0",
                 "tsutils": "^3.17.1"
             }
         },
         "@typescript-eslint/experimental-utils": {
-            "version": "2.33.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.33.0.tgz",
-            "integrity": "sha512-qzPM2AuxtMrRq78LwyZa8Qn6gcY8obkIrBs1ehqmQADwkYzTE1Pb4y2W+U3rE/iFkSWcWHG2LS6MJfj6SmHApg==",
+            "version": "2.34.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz",
+            "integrity": "sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA==",
             "dev": true,
             "requires": {
                 "@types/json-schema": "^7.0.3",
-                "@typescript-eslint/typescript-estree": "2.33.0",
+                "@typescript-eslint/typescript-estree": "2.34.0",
                 "eslint-scope": "^5.0.0",
                 "eslint-utils": "^2.0.0"
             },
@@ -183,21 +183,21 @@
             }
         },
         "@typescript-eslint/parser": {
-            "version": "2.33.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.33.0.tgz",
-            "integrity": "sha512-AUtmwUUhJoH6yrtxZMHbRUEMsC2G6z5NSxg9KsROOGqNXasM71I8P2NihtumlWTUCRld70vqIZ6Pm4E5PAziEA==",
+            "version": "2.34.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.34.0.tgz",
+            "integrity": "sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA==",
             "dev": true,
             "requires": {
                 "@types/eslint-visitor-keys": "^1.0.0",
-                "@typescript-eslint/experimental-utils": "2.33.0",
-                "@typescript-eslint/typescript-estree": "2.33.0",
+                "@typescript-eslint/experimental-utils": "2.34.0",
+                "@typescript-eslint/typescript-estree": "2.34.0",
                 "eslint-visitor-keys": "^1.1.0"
             }
         },
         "@typescript-eslint/typescript-estree": {
-            "version": "2.33.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.33.0.tgz",
-            "integrity": "sha512-d8rY6/yUxb0+mEwTShCQF2zYQdLlqihukNfG9IUlLYz5y1CH6G/9XYbrxQLq3Z14RNvkCC6oe+OcFlyUpwUbkg==",
+            "version": "2.34.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz",
+            "integrity": "sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==",
             "dev": true,
             "requires": {
                 "debug": "^4.1.1",
@@ -2015,9 +2015,9 @@
             }
         },
         "rollup": {
-            "version": "2.10.0",
-            "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.10.0.tgz",
-            "integrity": "sha512-7BmpEfUN9P6esJzWIn3DmR//90mW6YwYB1t3y48LpF8ITpYtL8s1kEirMKqUu44dVH/6a/rs0EuwYVL3FuRDoA==",
+            "version": "2.10.7",
+            "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.10.7.tgz",
+            "integrity": "sha512-rofUSH2i4GymWhQq6bfRaSiVbz4LEB4h/7+AhuXCaeOSwQqClD0hINjs59j8SyfQwcqe83NcVJAY2kjp0h33bQ==",
             "dev": true,
             "requires": {
                 "fsevents": "~2.1.2"
@@ -2295,9 +2295,9 @@
             }
         },
         "tslib": {
-            "version": "1.12.0",
-            "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.12.0.tgz",
-            "integrity": "sha512-5rxCQkP0kytf4H1T4xz1imjxaUUPMvc5aWp0rJ/VMIN7ClRiH1FwFvBt8wOeMasp/epeUnmSW6CixSIePtiLqA==",
+            "version": "1.13.0",
+            "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
+            "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
             "dev": true
         },
         "tsutils": {
@@ -2341,9 +2341,9 @@
             }
         },
         "typescript": {
-            "version": "3.9.2",
-            "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.2.tgz",
-            "integrity": "sha512-q2ktq4n/uLuNNShyayit+DTobV2ApPEo/6so68JaD5ojvc/6GClBipedB9zNWYxRSAlZXAe405Rlijzl6qDiSw==",
+            "version": "3.9.3",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.3.tgz",
+            "integrity": "sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ==",
             "dev": true
         },
         "typescript-formatter": {
diff --git a/editors/code/package.json b/editors/code/package.json
index 1eebe06082b9..ce79f549773b 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -21,7 +21,7 @@
         "Programming Languages"
     ],
     "engines": {
-        "vscode": "^1.44.0"
+        "vscode": "^1.44.1"
     },
     "enableProposedApi": true,
     "scripts": {
@@ -43,17 +43,17 @@
         "@rollup/plugin-node-resolve": "^7.1.3",
         "@types/glob": "^7.1.1",
         "@types/mocha": "^7.0.2",
-        "@types/node": "^12.12.39",
+        "@types/node": "^12.12.42",
         "@types/node-fetch": "^2.5.7",
-        "@types/vscode": "^1.44.0",
-        "@typescript-eslint/eslint-plugin": "^2.33.0",
-        "@typescript-eslint/parser": "^2.33.0",
+        "@types/vscode": "^1.44.1",
+        "@typescript-eslint/eslint-plugin": "^2.34.0",
+        "@typescript-eslint/parser": "^2.34.0",
         "eslint": "^6.8.0",
         "glob": "^7.1.6",
         "mocha": "^7.1.2",
-        "rollup": "^2.10.0",
-        "tslib": "^1.12.0",
-        "typescript": "^3.9.2",
+        "rollup": "^2.10.7",
+        "tslib": "^1.13.0",
+        "typescript": "^3.9.3",
         "typescript-formatter": "^7.2.2",
         "vsce": "^1.75.0",
         "vscode-test": "^1.3.0"

From 6c5888d136120ce2f31ba8b0e8273273012e15af Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= 
Date: Fri, 22 May 2020 21:34:53 +0300
Subject: [PATCH 049/163] Bump more npm deps

---
 editors/code/package-lock.json | 574 +++++++++++++++++++--------------
 editors/code/package.json      |  14 +-
 2 files changed, 343 insertions(+), 245 deletions(-)

diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index ee6b40a3bde4..3b4a316820fe 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -13,21 +13,27 @@
                 "@babel/highlight": "^7.8.3"
             }
         },
+        "@babel/helper-validator-identifier": {
+            "version": "7.9.5",
+            "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz",
+            "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==",
+            "dev": true
+        },
         "@babel/highlight": {
-            "version": "7.8.3",
-            "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz",
-            "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==",
+            "version": "7.9.0",
+            "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.9.0.tgz",
+            "integrity": "sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==",
             "dev": true,
             "requires": {
+                "@babel/helper-validator-identifier": "^7.9.0",
                 "chalk": "^2.0.0",
-                "esutils": "^2.0.2",
                 "js-tokens": "^4.0.0"
             }
         },
         "@rollup/plugin-commonjs": {
-            "version": "11.1.0",
-            "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-11.1.0.tgz",
-            "integrity": "sha512-Ycr12N3ZPN96Fw2STurD21jMqzKwL9QuFhms3SD7KKRK7oaXUsBU9Zt0jL/rOPHiPYisI21/rXGO3jr9BnLHUA==",
+            "version": "12.0.0",
+            "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-12.0.0.tgz",
+            "integrity": "sha512-8+mDQt1QUmN+4Y9D3yCG8AJNewuTSLYPJVzKKUZ+lGeQrI+bV12Tc5HCyt2WdlnG6ihIL/DPbKRJlB40DX40mw==",
             "dev": true,
             "requires": {
                 "@rollup/pluginutils": "^3.0.8",
@@ -40,27 +46,29 @@
             }
         },
         "@rollup/plugin-node-resolve": {
-            "version": "7.1.3",
-            "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.3.tgz",
-            "integrity": "sha512-RxtSL3XmdTAE2byxekYLnx+98kEUOrPHF/KRVjLH+DEIHy6kjIw7YINQzn+NXiH/NTrQLAwYs0GWB+csWygA9Q==",
+            "version": "8.0.0",
+            "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-8.0.0.tgz",
+            "integrity": "sha512-5poJCChrkVggXXND/sQ7yNqwjUNT4fP31gpRWCnSNnlXuUXTCMHT33xZrTGxgjm5Rl18MHj7iEzlCT8rYWwQSA==",
             "dev": true,
             "requires": {
                 "@rollup/pluginutils": "^3.0.8",
                 "@types/resolve": "0.0.8",
                 "builtin-modules": "^3.1.0",
+                "deep-freeze": "^0.0.1",
+                "deepmerge": "^4.2.2",
                 "is-module": "^1.0.0",
                 "resolve": "^1.14.2"
             }
         },
         "@rollup/pluginutils": {
-            "version": "3.0.9",
-            "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.0.9.tgz",
-            "integrity": "sha512-TLZavlfPAZYI7v33wQh4mTP6zojne14yok3DNSLcjoG/Hirxfkonn6icP5rrNWRn8nZsirJBFFpijVOJzkUHDg==",
+            "version": "3.0.10",
+            "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.0.10.tgz",
+            "integrity": "sha512-d44M7t+PjmMrASHbhgpSbVgtL6EFyX7J4mYxwQ/c5eoaE6N2VgCgEcWVzNnwycIloti+/MpwFr8qfw+nRw00sw==",
             "dev": true,
             "requires": {
                 "@types/estree": "0.0.39",
                 "estree-walker": "^1.0.1",
-                "micromatch": "^4.0.2"
+                "picomatch": "^2.2.2"
             }
         },
         "@types/color-name": {
@@ -117,9 +125,9 @@
             "dev": true
         },
         "@types/node": {
-            "version": "12.12.42",
-            "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.42.tgz",
-            "integrity": "sha512-R/9QdYFLL9dE9l5cWWzWIZByVGFd7lk7JVOJ7KD+E1SJ4gni7XJRLz9QTjyYQiHIqEAgku9VgxdLjMlhhUaAFg==",
+            "version": "14.0.5",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.5.tgz",
+            "integrity": "sha512-90hiq6/VqtQgX8Sp0EzeIsv3r+ellbGj4URKj5j30tLlZvRUpnAe9YbYnjl3pJM93GyXU0tghHhvXHq+5rnCKA==",
             "dev": true
         },
         "@types/node-fetch": {
@@ -142,62 +150,60 @@
             }
         },
         "@types/vscode": {
-            "version": "1.45.1",
-            "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.45.1.tgz",
-            "integrity": "sha512-0NO9qrrEJBO8FsqHCrFMgR2suKnwCsKBWvRSb2OzH5gs4i3QO5AhEMQYrSzDbU/wLPt7N617/rN9lPY213gmwg==",
+            "version": "1.45.0",
+            "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.45.0.tgz",
+            "integrity": "sha512-b0Gyir7sPBCqiKLygAhn/AYVfzWD+SMPkWltBrIuPEyTOxSU1wVApWY/FcxYO2EWTRacoubTl4+gvZf86RkecA==",
             "dev": true
         },
         "@typescript-eslint/eslint-plugin": {
-            "version": "2.34.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz",
-            "integrity": "sha512-4zY3Z88rEE99+CNvTbXSyovv2z9PNOVffTWD2W8QF5s2prBQtwN2zadqERcrHpcR7O/+KMI3fcTAmUUhK/iQcQ==",
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.0.0.tgz",
+            "integrity": "sha512-lcZ0M6jD4cqGccYOERKdMtg+VWpoq3NSnWVxpc/AwAy0zhkUYVioOUZmfNqiNH8/eBNGhCn6HXd6mKIGRgNc1Q==",
             "dev": true,
             "requires": {
-                "@typescript-eslint/experimental-utils": "2.34.0",
+                "@typescript-eslint/experimental-utils": "3.0.0",
                 "functional-red-black-tree": "^1.0.1",
                 "regexpp": "^3.0.0",
+                "semver": "^7.3.2",
                 "tsutils": "^3.17.1"
-            }
-        },
-        "@typescript-eslint/experimental-utils": {
-            "version": "2.34.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.34.0.tgz",
-            "integrity": "sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA==",
-            "dev": true,
-            "requires": {
-                "@types/json-schema": "^7.0.3",
-                "@typescript-eslint/typescript-estree": "2.34.0",
-                "eslint-scope": "^5.0.0",
-                "eslint-utils": "^2.0.0"
             },
             "dependencies": {
-                "eslint-utils": {
-                    "version": "2.0.0",
-                    "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz",
-                    "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==",
-                    "dev": true,
-                    "requires": {
-                        "eslint-visitor-keys": "^1.1.0"
-                    }
+                "semver": {
+                    "version": "7.3.2",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
+                    "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
+                    "dev": true
                 }
             }
         },
+        "@typescript-eslint/experimental-utils": {
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.0.0.tgz",
+            "integrity": "sha512-BN0vmr9N79M9s2ctITtChRuP1+Dls0x/wlg0RXW1yQ7WJKPurg6X3Xirv61J2sjPif4F8SLsFMs5Nzte0WYoTQ==",
+            "dev": true,
+            "requires": {
+                "@types/json-schema": "^7.0.3",
+                "@typescript-eslint/typescript-estree": "3.0.0",
+                "eslint-scope": "^5.0.0",
+                "eslint-utils": "^2.0.0"
+            }
+        },
         "@typescript-eslint/parser": {
-            "version": "2.34.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.34.0.tgz",
-            "integrity": "sha512-03ilO0ucSD0EPTw2X4PntSIRFtDPWjrVq7C3/Z3VQHRC7+13YB55rcJI3Jt+YgeHbjUdJPcPa7b23rXCBokuyA==",
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.0.0.tgz",
+            "integrity": "sha512-8RRCA9KLxoFNO0mQlrLZA0reGPd/MsobxZS/yPFj+0/XgMdS8+mO8mF3BDj2ZYQj03rkayhSJtF1HAohQ3iylw==",
             "dev": true,
             "requires": {
                 "@types/eslint-visitor-keys": "^1.0.0",
-                "@typescript-eslint/experimental-utils": "2.34.0",
-                "@typescript-eslint/typescript-estree": "2.34.0",
+                "@typescript-eslint/experimental-utils": "3.0.0",
+                "@typescript-eslint/typescript-estree": "3.0.0",
                 "eslint-visitor-keys": "^1.1.0"
             }
         },
         "@typescript-eslint/typescript-estree": {
-            "version": "2.34.0",
-            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.34.0.tgz",
-            "integrity": "sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==",
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.0.0.tgz",
+            "integrity": "sha512-nevQvHyNghsfLrrByzVIH4ZG3NROgJ8LZlfh3ddwPPH4CH7W4GAiSx5qu+xHuX5pWsq6q/eqMc1io840ZhAnUg==",
             "dev": true,
             "requires": {
                 "debug": "^4.1.1",
@@ -218,9 +224,9 @@
             }
         },
         "acorn": {
-            "version": "7.1.1",
-            "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
-            "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
+            "version": "7.2.0",
+            "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz",
+            "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==",
             "dev": true
         },
         "acorn-jsx": {
@@ -239,9 +245,9 @@
             }
         },
         "ajv": {
-            "version": "6.12.0",
-            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
-            "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
+            "version": "6.12.2",
+            "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz",
+            "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==",
             "dev": true,
             "requires": {
                 "fast-deep-equal": "^3.1.1",
@@ -455,9 +461,9 @@
             }
         },
         "cli-width": {
-            "version": "2.2.0",
-            "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
-            "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
+            "version": "2.2.1",
+            "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz",
+            "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==",
             "dev": true
         },
         "cliui": {
@@ -471,6 +477,12 @@
                 "wrap-ansi": "^5.1.0"
             },
             "dependencies": {
+                "ansi-regex": {
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+                    "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+                    "dev": true
+                },
                 "emoji-regex": {
                     "version": "7.0.3",
                     "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
@@ -493,6 +505,15 @@
                         "is-fullwidth-code-point": "^2.0.0",
                         "strip-ansi": "^5.1.0"
                     }
+                },
+                "strip-ansi": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+                    "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^4.1.0"
+                    }
                 }
             }
         },
@@ -545,24 +566,14 @@
             "dev": true
         },
         "cross-spawn": {
-            "version": "6.0.5",
-            "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
-            "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+            "version": "7.0.2",
+            "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz",
+            "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==",
             "dev": true,
             "requires": {
-                "nice-try": "^1.0.4",
-                "path-key": "^2.0.1",
-                "semver": "^5.5.0",
-                "shebang-command": "^1.2.0",
-                "which": "^1.2.9"
-            },
-            "dependencies": {
-                "semver": {
-                    "version": "5.7.1",
-                    "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
-                    "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
-                    "dev": true
-                }
+                "path-key": "^3.1.0",
+                "shebang-command": "^2.0.0",
+                "which": "^2.0.1"
             }
         },
         "css-select": {
@@ -598,12 +609,24 @@
             "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
             "dev": true
         },
+        "deep-freeze": {
+            "version": "0.0.1",
+            "resolved": "https://registry.npmjs.org/deep-freeze/-/deep-freeze-0.0.1.tgz",
+            "integrity": "sha1-OgsABd4YZygZ39OM0x+RF5yJPoQ=",
+            "dev": true
+        },
         "deep-is": {
             "version": "0.1.3",
             "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
             "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
             "dev": true
         },
+        "deepmerge": {
+            "version": "4.2.2",
+            "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
+            "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
+            "dev": true
+        },
         "define-properties": {
             "version": "1.1.3",
             "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
@@ -759,22 +782,22 @@
             "dev": true
         },
         "eslint": {
-            "version": "6.8.0",
-            "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz",
-            "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==",
+            "version": "7.1.0",
+            "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.1.0.tgz",
+            "integrity": "sha512-DfS3b8iHMK5z/YLSme8K5cge168I8j8o1uiVmFCgnnjxZQbCGyraF8bMl7Ju4yfBmCuxD7shOF7eqGkcuIHfsA==",
             "dev": true,
             "requires": {
                 "@babel/code-frame": "^7.0.0",
                 "ajv": "^6.10.0",
-                "chalk": "^2.1.0",
-                "cross-spawn": "^6.0.5",
+                "chalk": "^4.0.0",
+                "cross-spawn": "^7.0.2",
                 "debug": "^4.0.1",
                 "doctrine": "^3.0.0",
                 "eslint-scope": "^5.0.0",
-                "eslint-utils": "^1.4.3",
+                "eslint-utils": "^2.0.0",
                 "eslint-visitor-keys": "^1.1.0",
-                "espree": "^6.1.2",
-                "esquery": "^1.0.1",
+                "espree": "^7.0.0",
+                "esquery": "^1.2.0",
                 "esutils": "^2.0.2",
                 "file-entry-cache": "^5.0.1",
                 "functional-red-black-tree": "^1.0.1",
@@ -787,27 +810,76 @@
                 "is-glob": "^4.0.0",
                 "js-yaml": "^3.13.1",
                 "json-stable-stringify-without-jsonify": "^1.0.1",
-                "levn": "^0.3.0",
+                "levn": "^0.4.1",
                 "lodash": "^4.17.14",
                 "minimatch": "^3.0.4",
-                "mkdirp": "^0.5.1",
                 "natural-compare": "^1.4.0",
-                "optionator": "^0.8.3",
+                "optionator": "^0.9.1",
                 "progress": "^2.0.0",
-                "regexpp": "^2.0.1",
-                "semver": "^6.1.2",
-                "strip-ansi": "^5.2.0",
-                "strip-json-comments": "^3.0.1",
+                "regexpp": "^3.1.0",
+                "semver": "^7.2.1",
+                "strip-ansi": "^6.0.0",
+                "strip-json-comments": "^3.1.0",
                 "table": "^5.2.3",
                 "text-table": "^0.2.0",
                 "v8-compile-cache": "^2.0.3"
             },
             "dependencies": {
-                "regexpp": {
+                "ansi-styles": {
+                    "version": "4.2.1",
+                    "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
+                    "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
+                    "dev": true,
+                    "requires": {
+                        "@types/color-name": "^1.1.1",
+                        "color-convert": "^2.0.1"
+                    }
+                },
+                "chalk": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz",
+                    "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-styles": "^4.1.0",
+                        "supports-color": "^7.1.0"
+                    }
+                },
+                "color-convert": {
                     "version": "2.0.1",
-                    "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
-                    "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
+                    "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+                    "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+                    "dev": true,
+                    "requires": {
+                        "color-name": "~1.1.4"
+                    }
+                },
+                "color-name": {
+                    "version": "1.1.4",
+                    "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+                    "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
                     "dev": true
+                },
+                "has-flag": {
+                    "version": "4.0.0",
+                    "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+                    "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+                    "dev": true
+                },
+                "semver": {
+                    "version": "7.3.2",
+                    "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
+                    "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
+                    "dev": true
+                },
+                "supports-color": {
+                    "version": "7.1.0",
+                    "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
+                    "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
+                    "dev": true,
+                    "requires": {
+                        "has-flag": "^4.0.0"
+                    }
                 }
             }
         },
@@ -822,9 +894,9 @@
             }
         },
         "eslint-utils": {
-            "version": "1.4.3",
-            "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz",
-            "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==",
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz",
+            "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==",
             "dev": true,
             "requires": {
                 "eslint-visitor-keys": "^1.1.0"
@@ -837,9 +909,9 @@
             "dev": true
         },
         "espree": {
-            "version": "6.2.1",
-            "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz",
-            "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==",
+            "version": "7.0.0",
+            "resolved": "https://registry.npmjs.org/espree/-/espree-7.0.0.tgz",
+            "integrity": "sha512-/r2XEx5Mw4pgKdyb7GNLQNsu++asx/dltf/CI8RFi9oGHxmQFgvLbc5Op4U6i8Oaj+kdslhJtVlEZeAqH5qOTw==",
             "dev": true,
             "requires": {
                 "acorn": "^7.1.1",
@@ -854,12 +926,20 @@
             "dev": true
         },
         "esquery": {
-            "version": "1.1.0",
-            "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.1.0.tgz",
-            "integrity": "sha512-MxYW9xKmROWF672KqjO75sszsA8Mxhw06YFeS5VHlB98KDHbOSurm3ArsjO60Eaf3QmGMCP1yn+0JQkNLo/97Q==",
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz",
+            "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==",
             "dev": true,
             "requires": {
-                "estraverse": "^4.0.0"
+                "estraverse": "^5.1.0"
+            },
+            "dependencies": {
+                "estraverse": {
+                    "version": "5.1.0",
+                    "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz",
+                    "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==",
+                    "dev": true
+                }
             }
         },
         "esrecurse": {
@@ -984,9 +1064,9 @@
             }
         },
         "flatted": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz",
-            "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==",
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
+            "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
             "dev": true
         },
         "form-data": {
@@ -1046,9 +1126,9 @@
             }
         },
         "glob-parent": {
-            "version": "5.1.0",
-            "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz",
-            "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==",
+            "version": "5.1.1",
+            "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+            "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
             "dev": true,
             "requires": {
                 "is-glob": "^4.0.1"
@@ -1267,15 +1347,6 @@
                     "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
                     "dev": true
                 },
-                "strip-ansi": {
-                    "version": "6.0.0",
-                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
-                    "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
-                    "dev": true,
-                    "requires": {
-                        "ansi-regex": "^5.0.0"
-                    }
-                },
                 "supports-color": {
                     "version": "7.1.0",
                     "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
@@ -1347,12 +1418,6 @@
             "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
             "dev": true
         },
-        "is-promise": {
-            "version": "2.1.0",
-            "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
-            "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
-            "dev": true
-        },
         "is-reference": {
             "version": "1.1.4",
             "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.4.tgz",
@@ -1393,9 +1458,9 @@
             "dev": true
         },
         "js-yaml": {
-            "version": "3.13.1",
-            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
-            "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+            "version": "3.14.0",
+            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
+            "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
             "dev": true,
             "requires": {
                 "argparse": "^1.0.7",
@@ -1421,13 +1486,13 @@
             "dev": true
         },
         "levn": {
-            "version": "0.3.0",
-            "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
-            "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+            "version": "0.4.1",
+            "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+            "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
             "dev": true,
             "requires": {
-                "prelude-ls": "~1.1.2",
-                "type-check": "~0.3.2"
+                "prelude-ls": "^1.2.1",
+                "type-check": "~0.4.0"
             }
         },
         "linkify-it": {
@@ -1510,16 +1575,6 @@
             "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=",
             "dev": true
         },
-        "micromatch": {
-            "version": "4.0.2",
-            "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
-            "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
-            "dev": true,
-            "requires": {
-                "braces": "^3.0.1",
-                "picomatch": "^2.0.5"
-            }
-        },
         "mime": {
             "version": "1.6.0",
             "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
@@ -1563,18 +1618,18 @@
             "dev": true
         },
         "mkdirp": {
-            "version": "0.5.3",
-            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz",
-            "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==",
+            "version": "0.5.5",
+            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+            "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
             "dev": true,
             "requires": {
                 "minimist": "^1.2.5"
             }
         },
         "mocha": {
-            "version": "7.1.2",
-            "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.2.tgz",
-            "integrity": "sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA==",
+            "version": "7.2.0",
+            "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz",
+            "integrity": "sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==",
             "dev": true,
             "requires": {
                 "ansi-colors": "3.2.3",
@@ -1626,13 +1681,14 @@
                         "path-is-absolute": "^1.0.0"
                     }
                 },
-                "mkdirp": {
-                    "version": "0.5.5",
-                    "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
-                    "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+                "js-yaml": {
+                    "version": "3.13.1",
+                    "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+                    "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
                     "dev": true,
                     "requires": {
-                        "minimist": "^1.2.5"
+                        "argparse": "^1.0.7",
+                        "esprima": "^4.0.0"
                     }
                 },
                 "ms": {
@@ -1655,6 +1711,15 @@
                     "requires": {
                         "has-flag": "^3.0.0"
                     }
+                },
+                "which": {
+                    "version": "1.3.1",
+                    "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+                    "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+                    "dev": true,
+                    "requires": {
+                        "isexe": "^2.0.0"
+                    }
                 }
             }
         },
@@ -1676,12 +1741,6 @@
             "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
             "dev": true
         },
-        "nice-try": {
-            "version": "1.0.5",
-            "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
-            "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
-            "dev": true
-        },
         "node-environment-flags": {
             "version": "1.0.6",
             "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz",
@@ -1773,17 +1832,17 @@
             }
         },
         "optionator": {
-            "version": "0.8.3",
-            "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
-            "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+            "version": "0.9.1",
+            "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
+            "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==",
             "dev": true,
             "requires": {
-                "deep-is": "~0.1.3",
-                "fast-levenshtein": "~2.0.6",
-                "levn": "~0.3.0",
-                "prelude-ls": "~1.1.2",
-                "type-check": "~0.3.2",
-                "word-wrap": "~1.2.3"
+                "deep-is": "^0.1.3",
+                "fast-levenshtein": "^2.0.6",
+                "levn": "^0.4.1",
+                "prelude-ls": "^1.2.1",
+                "type-check": "^0.4.0",
+                "word-wrap": "^1.2.3"
             }
         },
         "os": {
@@ -1886,9 +1945,9 @@
             "dev": true
         },
         "path-key": {
-            "version": "2.0.1",
-            "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
-            "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+            "version": "3.1.1",
+            "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+            "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
             "dev": true
         },
         "path-parse": {
@@ -1910,9 +1969,9 @@
             "dev": true
         },
         "prelude-ls": {
-            "version": "1.1.2",
-            "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
-            "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+            "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
             "dev": true
         },
         "progress": {
@@ -1981,9 +2040,9 @@
             "dev": true
         },
         "resolve": {
-            "version": "1.16.1",
-            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.16.1.tgz",
-            "integrity": "sha512-rmAglCSqWWMrrBv/XM6sW0NuRFiKViw/W4d9EbC4pt+49H8JwHy+mcGmALTEg504AUDcLTvb1T2q3E9AnmY+ig==",
+            "version": "1.17.0",
+            "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+            "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
             "dev": true,
             "requires": {
                 "path-parse": "^1.0.6"
@@ -2015,30 +2074,35 @@
             }
         },
         "rollup": {
-            "version": "2.10.7",
-            "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.10.7.tgz",
-            "integrity": "sha512-rofUSH2i4GymWhQq6bfRaSiVbz4LEB4h/7+AhuXCaeOSwQqClD0hINjs59j8SyfQwcqe83NcVJAY2kjp0h33bQ==",
+            "version": "2.10.9",
+            "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.10.9.tgz",
+            "integrity": "sha512-dY/EbjiWC17ZCUSyk14hkxATAMAShkMsD43XmZGWjLrgFj15M3Dw2kEkA9ns64BiLFm9PKN6vTQw8neHwK74eg==",
             "dev": true,
             "requires": {
                 "fsevents": "~2.1.2"
             }
         },
         "run-async": {
-            "version": "2.4.0",
-            "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz",
-            "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==",
-            "dev": true,
-            "requires": {
-                "is-promise": "^2.1.0"
-            }
+            "version": "2.4.1",
+            "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
+            "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
+            "dev": true
         },
         "rxjs": {
-            "version": "6.5.4",
-            "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz",
-            "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==",
+            "version": "6.5.5",
+            "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz",
+            "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==",
             "dev": true,
             "requires": {
                 "tslib": "^1.9.0"
+            },
+            "dependencies": {
+                "tslib": {
+                    "version": "1.13.0",
+                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
+                    "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
+                    "dev": true
+                }
             }
         },
         "safe-buffer": {
@@ -2065,18 +2129,18 @@
             "dev": true
         },
         "shebang-command": {
-            "version": "1.2.0",
-            "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
-            "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+            "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
             "dev": true,
             "requires": {
-                "shebang-regex": "^1.0.0"
+                "shebang-regex": "^3.0.0"
             }
         },
         "shebang-regex": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
-            "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+            "version": "3.0.0",
+            "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+            "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
             "dev": true
         },
         "sigmund": {
@@ -2086,9 +2150,9 @@
             "dev": true
         },
         "signal-exit": {
-            "version": "3.0.2",
-            "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
-            "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+            "version": "3.0.3",
+            "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
+            "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
             "dev": true
         },
         "slice-ansi": {
@@ -2131,17 +2195,6 @@
                 "emoji-regex": "^8.0.0",
                 "is-fullwidth-code-point": "^3.0.0",
                 "strip-ansi": "^6.0.0"
-            },
-            "dependencies": {
-                "strip-ansi": {
-                    "version": "6.0.0",
-                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
-                    "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
-                    "dev": true,
-                    "requires": {
-                        "ansi-regex": "^5.0.0"
-                    }
-                }
             }
         },
         "string.prototype.trimend": {
@@ -2196,26 +2249,18 @@
             }
         },
         "strip-ansi": {
-            "version": "5.2.0",
-            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-            "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+            "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
             "dev": true,
             "requires": {
-                "ansi-regex": "^4.1.0"
-            },
-            "dependencies": {
-                "ansi-regex": {
-                    "version": "4.1.0",
-                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-                    "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
-                    "dev": true
-                }
+                "ansi-regex": "^5.0.0"
             }
         },
         "strip-json-comments": {
-            "version": "3.0.1",
-            "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
-            "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz",
+            "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==",
             "dev": true
         },
         "supports-color": {
@@ -2239,6 +2284,12 @@
                 "string-width": "^3.0.0"
             },
             "dependencies": {
+                "ansi-regex": {
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+                    "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+                    "dev": true
+                },
                 "emoji-regex": {
                     "version": "7.0.3",
                     "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
@@ -2261,6 +2312,15 @@
                         "is-fullwidth-code-point": "^2.0.0",
                         "strip-ansi": "^5.1.0"
                     }
+                },
+                "strip-ansi": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+                    "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^4.1.0"
+                    }
                 }
             }
         },
@@ -2295,9 +2355,9 @@
             }
         },
         "tslib": {
-            "version": "1.13.0",
-            "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
-            "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.0.tgz",
+            "integrity": "sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g==",
             "dev": true
         },
         "tsutils": {
@@ -2307,6 +2367,14 @@
             "dev": true,
             "requires": {
                 "tslib": "^1.8.1"
+            },
+            "dependencies": {
+                "tslib": {
+                    "version": "1.13.0",
+                    "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
+                    "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==",
+                    "dev": true
+                }
             }
         },
         "tunnel": {
@@ -2316,12 +2384,12 @@
             "dev": true
         },
         "type-check": {
-            "version": "0.3.2",
-            "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
-            "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+            "version": "0.4.0",
+            "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+            "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
             "dev": true,
             "requires": {
-                "prelude-ls": "~1.1.2"
+                "prelude-ls": "^1.2.1"
             }
         },
         "type-fest": {
@@ -2480,9 +2548,9 @@
             }
         },
         "which": {
-            "version": "1.3.1",
-            "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
-            "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+            "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
             "dev": true,
             "requires": {
                 "isexe": "^2.0.0"
@@ -2553,6 +2621,12 @@
                 "strip-ansi": "^5.0.0"
             },
             "dependencies": {
+                "ansi-regex": {
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+                    "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+                    "dev": true
+                },
                 "emoji-regex": {
                     "version": "7.0.3",
                     "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
@@ -2575,6 +2649,15 @@
                         "is-fullwidth-code-point": "^2.0.0",
                         "strip-ansi": "^5.1.0"
                     }
+                },
+                "strip-ansi": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+                    "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^4.1.0"
+                    }
                 }
             }
         },
@@ -2623,6 +2706,12 @@
                 "yargs-parser": "^13.1.2"
             },
             "dependencies": {
+                "ansi-regex": {
+                    "version": "4.1.0",
+                    "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+                    "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+                    "dev": true
+                },
                 "emoji-regex": {
                     "version": "7.0.3",
                     "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
@@ -2645,6 +2734,15 @@
                         "is-fullwidth-code-point": "^2.0.0",
                         "strip-ansi": "^5.1.0"
                     }
+                },
+                "strip-ansi": {
+                    "version": "5.2.0",
+                    "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+                    "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+                    "dev": true,
+                    "requires": {
+                        "ansi-regex": "^4.1.0"
+                    }
                 }
             }
         },
diff --git a/editors/code/package.json b/editors/code/package.json
index ce79f549773b..8f5a06815ae3 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -39,20 +39,20 @@
         "vscode-languageclient": "7.0.0-next.1"
     },
     "devDependencies": {
-        "@rollup/plugin-commonjs": "^11.1.0",
-        "@rollup/plugin-node-resolve": "^7.1.3",
+        "@rollup/plugin-commonjs": "^12.0.0",
+        "@rollup/plugin-node-resolve": "^8.0.0",
         "@types/glob": "^7.1.1",
         "@types/mocha": "^7.0.2",
-        "@types/node": "^12.12.42",
+        "@types/node": "^14.0.5",
         "@types/node-fetch": "^2.5.7",
         "@types/vscode": "^1.44.1",
-        "@typescript-eslint/eslint-plugin": "^2.34.0",
-        "@typescript-eslint/parser": "^2.34.0",
-        "eslint": "^6.8.0",
+        "@typescript-eslint/eslint-plugin": "^3.0.0",
+        "@typescript-eslint/parser": "^3.0.0",
+        "eslint": "^7.0.0",
         "glob": "^7.1.6",
         "mocha": "^7.1.2",
         "rollup": "^2.10.7",
-        "tslib": "^1.13.0",
+        "tslib": "^2.0.0",
         "typescript": "^3.9.3",
         "typescript-formatter": "^7.2.2",
         "vsce": "^1.75.0",

From bfe8625e3b6fdf6cf081cae4f290c5b3b6174dc2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= 
Date: Sun, 24 May 2020 13:41:46 +0300
Subject: [PATCH 050/163] Bump more crates

---
 Cargo.lock | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index b41583579251..3fe133d65397 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -830,9 +830,9 @@ dependencies = [
 
 [[package]]
 name = "paste"
-version = "0.1.12"
+version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a229b1c58c692edcaa5b9b0948084f130f55d2dcc15b02fcc5340b2b4521476"
+checksum = "678f27e19361472a23717f11d229a7522ef64605baf0715c896a94b8b6b13a06"
 dependencies = [
  "paste-impl",
  "proc-macro-hack",
@@ -840,9 +840,9 @@ dependencies = [
 
 [[package]]
 name = "paste-impl"
-version = "0.1.12"
+version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e0bf239e447e67ff6d16a8bb5e4d4bd2343acf5066061c0e8e06ac5ba8ca68c"
+checksum = "149089128a45d8e377677b08873b4bad2a56618f80e4f28a83862ba250994a30"
 dependencies = [
  "proc-macro-hack",
  "proc-macro2",
@@ -858,9 +858,9 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
 
 [[package]]
 name = "petgraph"
-version = "0.5.0"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29c127eea4a29ec6c85d153c59dc1213f33ec74cead30fe4730aecc88cc1fd92"
+checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7"
 dependencies = [
  "fixedbitset",
  "indexmap",
@@ -892,9 +892,9 @@ checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.15"
+version = "1.0.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70a50b9351bfa8d65a7d93ce712dc63d2fd15ddbf2c36990fc7cac344859c04f"
+checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101"
 dependencies = [
  "unicode-xid",
 ]

From 733956617e53027fa27968f165a90422d49673b0 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Sun, 24 May 2020 15:42:11 +0200
Subject: [PATCH 051/163] Fix Cargo.lock

---
 Cargo.lock | 31 +++++++++++++++++--------------
 1 file changed, 17 insertions(+), 14 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index ca6c64166317..1f4c0cb5345e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -101,9 +101,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.0.53"
+version = "1.0.54"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "404b1fe4f65288577753b17e3b36a04596ee784493ec249bf81c7f2d2acd751c"
+checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311"
 
 [[package]]
 name = "cfg-if"
@@ -382,9 +382,9 @@ dependencies = [
 
 [[package]]
 name = "fst"
-version = "0.4.3"
+version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81f9cac32c1741cdf6b66be7dcf0d9c7f25ccf12f8aa84c16cfa31f9f14513b3"
+checksum = "a7293de202dbfe786c0b3fe6110a027836c5438ed06db7b715c9955ff4bfea51"
 
 [[package]]
 name = "fuchsia-zircon"
@@ -830,9 +830,9 @@ dependencies = [
 
 [[package]]
 name = "paste"
-version = "0.1.12"
+version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a229b1c58c692edcaa5b9b0948084f130f55d2dcc15b02fcc5340b2b4521476"
+checksum = "678f27e19361472a23717f11d229a7522ef64605baf0715c896a94b8b6b13a06"
 dependencies = [
  "paste-impl",
  "proc-macro-hack",
@@ -840,9 +840,9 @@ dependencies = [
 
 [[package]]
 name = "paste-impl"
-version = "0.1.12"
+version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e0bf239e447e67ff6d16a8bb5e4d4bd2343acf5066061c0e8e06ac5ba8ca68c"
+checksum = "149089128a45d8e377677b08873b4bad2a56618f80e4f28a83862ba250994a30"
 dependencies = [
  "proc-macro-hack",
  "proc-macro2",
@@ -858,9 +858,9 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
 
 [[package]]
 name = "petgraph"
-version = "0.5.0"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29c127eea4a29ec6c85d153c59dc1213f33ec74cead30fe4730aecc88cc1fd92"
+checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7"
 dependencies = [
  "fixedbitset",
  "indexmap",
@@ -892,9 +892,9 @@ checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.13"
+version = "1.0.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53f5ffe53a6b28e37c9c1ce74893477864d64f74778a93a4beb43c8fa167f639"
+checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101"
 dependencies = [
  "unicode-xid",
 ]
@@ -1598,9 +1598,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f"
 
 [[package]]
 name = "syn"
-version = "1.0.22"
+version = "1.0.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1425de3c33b0941002740a420b1a906a350b88d08b82b2c8a01035a3f9447bac"
+checksum = "95b5f192649e48a5302a13f2feb224df883b98933222369e4b3b0fe2a5447269"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1657,6 +1657,9 @@ name = "test_utils"
 version = "0.1.0"
 dependencies = [
  "difference",
+ "ra_cfg",
+ "relative-path",
+ "rustc-hash",
  "serde_json",
  "text-size",
 ]

From d1ff0145a9fe84f86192b2d88d5f0bd96a95a98f Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Sun, 24 May 2020 15:47:05 +0200
Subject: [PATCH 052/163] Fix tests

---
 crates/ra_ide/src/runnables.rs | 46 +++++++++++++++++-----------------
 1 file changed, 23 insertions(+), 23 deletions(-)

diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index ed98e58e00ca..6e7e47199c88 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -70,10 +70,10 @@ fn runnable_fn(
     let kind = if name_string == "main" {
         RunnableKind::Bin
     } else {
-        let test_id = if let Some(module) = sema.to_def(&fn_def).map(|def| def.module(sema.db)) {
-            let def = sema.to_def(&fn_def)?;
-            let impl_trait_name =
-                def.as_assoc_item(sema.db).and_then(|assoc_item| {
+        let test_id = match sema.to_def(&fn_def).map(|def| def.module(sema.db)) {
+            Some(module) => {
+                let def = sema.to_def(&fn_def)?;
+                let impl_trait_name = def.as_assoc_item(sema.db).and_then(|assoc_item| {
                     match assoc_item.container(sema.db) {
                         hir::AssocItemContainer::Trait(trait_item) => {
                             Some(trait_item.name(sema.db).to_string())
@@ -85,25 +85,25 @@ fn runnable_fn(
                     }
                 });
 
-            let path_iter = module
-                .path_to_root(sema.db)
-                .into_iter()
-                .rev()
-                .filter_map(|it| it.name(sema.db))
-                .map(|name| name.to_string());
+                let path_iter = module
+                    .path_to_root(sema.db)
+                    .into_iter()
+                    .rev()
+                    .filter_map(|it| it.name(sema.db))
+                    .map(|name| name.to_string());
 
-            let path = if let Some(impl_trait_name) = impl_trait_name {
-                path_iter
-                    .chain(std::iter::once(impl_trait_name))
-                    .chain(std::iter::once(name_string))
-                    .join("::")
-            } else {
-                path_iter.chain(std::iter::once(name_string)).join("::")
-            };
+                let path = if let Some(impl_trait_name) = impl_trait_name {
+                    path_iter
+                        .chain(std::iter::once(impl_trait_name))
+                        .chain(std::iter::once(name_string))
+                        .join("::")
+                } else {
+                    path_iter.chain(std::iter::once(name_string)).join("::")
+                };
 
-            TestId::Path(path)
-        } else {
-            TestId::Name(name_string)
+                TestId::Path(path)
+            }
+            None => TestId::Name(name_string),
         };
 
         if has_test_related_attribute(&fn_def) {
@@ -472,7 +472,7 @@ mod tests {
             Runnable {
                 range: 1..58,
                 kind: Test {
-                    test_id: Name(
+                    test_id: Path(
                         "test_foo1",
                     ),
                     attr: TestAttr {
@@ -509,7 +509,7 @@ mod tests {
             Runnable {
                 range: 1..80,
                 kind: Test {
-                    test_id: Name(
+                    test_id: Path(
                         "test_foo1",
                     ),
                     attr: TestAttr {

From e4af9f6d8a4587fc66d13db4c10ad9260d813ed3 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Sun, 24 May 2020 15:49:32 +0200
Subject: [PATCH 053/163] Reorgonise extensions docs

---
 docs/dev/lsp-extensions.md | 98 +++++++++++++++++++-------------------
 1 file changed, 50 insertions(+), 48 deletions(-)

diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 158d3c599a67..1cc51410b7f3 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -3,7 +3,9 @@
 This document describes LSP extensions used by rust-analyzer.
 It's a best effort document, when in doubt, consult the source (and send a PR with clarification ;-) ).
 We aim to upstream all non Rust-specific extensions to the protocol, but this is not a top priority.
-All capabilities are enabled via `experimental` field of `ClientCapabilities`.
+All capabilities are enabled via `experimental` field of `ClientCapabilities` or `ServerCapabilities`.
+Requests which we hope to upstream live under `experimental/` namespace.
+Requests, which are likely to always remain specific to `rust-analyzer` are under `rust-analyzer/` namespace.
 
 ## Snippet `TextEdit`
 
@@ -38,6 +40,53 @@ At the moment, rust-analyzer guarantees that only a single edit will have `Inser
 * Where exactly are `SnippetTextEdit`s allowed (only in code actions at the moment)?
 * Can snippets span multiple files (so far, no)?
 
+## `CodeAction` Groups
+
+**Issue:** https://github.com/microsoft/language-server-protocol/issues/994
+
+**Client Capability:** `{ "codeActionGroup": boolean }`
+
+If this capability is set, `CodeAction` returned from the server contain an additional field, `group`:
+
+```typescript
+interface CodeAction {
+    title: string;
+    group?: string;
+    ...
+}
+```
+
+All code-actions with the same `group` should be grouped under single (extendable) entry in lightbulb menu.
+The set of actions `[ { title: "foo" }, { group: "frobnicate", title: "bar" }, { group: "frobnicate", title: "baz" }]` should be rendered as
+
+```
+💡
+  +-------------+
+  | foo         |
+  +-------------+-----+
+  | frobnicate >| bar |
+  +-------------+-----+
+                | baz |
+                +-----+
+```
+
+Alternatively, selecting `frobnicate` could present a user with an additional menu to choose between `bar` and `baz`.
+
+### Example
+
+```rust
+fn main() {
+    let x: Entry/*cursor here*/ = todo!();
+}
+```
+
+Invoking code action at this position will yield two code actions for importing `Entry` from either `collections::HashMap` or `collection::BTreeMap`, grouped under a single "import" group.
+
+### Unresolved Questions
+
+* Is a fixed two-level structure enough?
+* Should we devise a general way to encode custom interaction protocols for GUI refactorings?
+
 ## Join Lines
 
 **Issue:** https://github.com/microsoft/language-server-protocol/issues/992
@@ -123,50 +172,3 @@ SSR with query `foo($a:expr, $b:expr) ==>> ($a).foo($b)` will transform, eg `foo
 
 * Probably needs search without replace mode
 * Needs a way to limit the scope to certain files.
-
-## `CodeAction` Groups
-
-**Issue:** https://github.com/microsoft/language-server-protocol/issues/994
-
-**Client Capability:** `{ "codeActionGroup": boolean }`
-
-If this capability is set, `CodeAction` returned from the server contain an additional field, `group`:
-
-```typescript
-interface CodeAction {
-    title: string;
-    group?: string;
-    ...
-}
-```
-
-All code-actions with the same `group` should be grouped under single (extendable) entry in lightbulb menu.
-The set of actions `[ { title: "foo" }, { group: "frobnicate", title: "bar" }, { group: "frobnicate", title: "baz" }]` should be rendered as
-
-```
-💡
-  +-------------+
-  | foo         |
-  +-------------+-----+
-  | frobnicate >| bar |
-  +-------------+-----+
-                | baz |
-                +-----+
-```
-
-Alternatively, selecting `frobnicate` could present a user with an additional menu to choose between `bar` and `baz`.
-
-### Example
-
-```rust
-fn main() {
-    let x: Entry/*cursor here*/ = todo!();
-}
-```
-
-Invoking code action at this position will yield two code actions for importing `Entry` from either `collections::HashMap` or `collection::BTreeMap`, grouped under a single "import" group.
-
-### Unresolved Questions
-
-* Is a fixed two-level structure enough?
-* Should we devise a general way to encode custom interaction protocols for GUI refactorings?

From 7e862626cc892662c3c95ba393483ac89c07e31a Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Sun, 24 May 2020 16:07:46 +0200
Subject: [PATCH 054/163] Fix bors config?

---
 bors.toml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/bors.toml b/bors.toml
index 0bc71860f2a9..13ce236df5b4 100644
--- a/bors.toml
+++ b/bors.toml
@@ -2,6 +2,8 @@ status = [
     "Rust (ubuntu-latest)",
     "Rust (windows-latest)",
     "Rust (macos-latest)",
-    "TypeScript"
+    "TypeScript (ubuntu-latest)",
+    "TypeScript (windows-latest)",
+    "TypeScript (macos-latest)",
 ]
 delete_merged_branches = true

From e2d36cb692f042f2051a8a88d271a297a3d333a4 Mon Sep 17 00:00:00 2001
From: Matthew Jasper 
Date: Thu, 21 May 2020 17:40:52 +0100
Subject: [PATCH 055/163] Highlight `true` and `false` as literals

---
 crates/ra_ide/src/snapshots/highlight_injection.html  | 1 +
 crates/ra_ide/src/snapshots/highlight_strings.html    | 1 +
 crates/ra_ide/src/snapshots/highlighting.html         | 3 ++-
 crates/ra_ide/src/snapshots/rainbow_highlighting.html | 1 +
 crates/ra_ide/src/syntax_highlighting.rs              | 1 +
 crates/ra_ide/src/syntax_highlighting/html.rs         | 1 +
 crates/ra_ide/src/syntax_highlighting/tags.rs         | 2 ++
 crates/rust-analyzer/src/semantic_tokens.rs           | 1 +
 crates/rust-analyzer/src/to_proto.rs                  | 1 +
 editors/code/package.json                             | 3 +++
 10 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html
index ea026d7a0464..68fc589bc785 100644
--- a/crates/ra_ide/src/snapshots/highlight_injection.html
+++ b/crates/ra_ide/src/snapshots/highlight_injection.html
@@ -17,6 +17,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .type_param         { color: #DFAF8F; }
 .attribute          { color: #94BFF3; }
 .numeric_literal    { color: #BFEBBF; }
+.bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
 .module             { color: #AFD8AF; }
 .variable           { color: #DCDCCC; }
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html
index 752b487e82fa..326744361c68 100644
--- a/crates/ra_ide/src/snapshots/highlight_strings.html
+++ b/crates/ra_ide/src/snapshots/highlight_strings.html
@@ -17,6 +17,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .type_param         { color: #DFAF8F; }
 .attribute          { color: #94BFF3; }
 .numeric_literal    { color: #BFEBBF; }
+.bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
 .module             { color: #AFD8AF; }
 .variable           { color: #DCDCCC; }
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index 2ceadf2fcbf7..198139220efc 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -17,6 +17,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .type_param         { color: #DFAF8F; }
 .attribute          { color: #94BFF3; }
 .numeric_literal    { color: #BFEBBF; }
+.bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
 .module             { color: #AFD8AF; }
 .variable           { color: #DCDCCC; }
@@ -64,7 +65,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     println!("Hello, {}!", 92);
 
     let mut vec = Vec::new();
-    if true {
+    if true {
         let x = 92;
         vec.push(Foo { x, y: 1 });
     }
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index 11e1f3e44e59..2a0294f719f5 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -17,6 +17,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .type_param         { color: #DFAF8F; }
 .attribute          { color: #94BFF3; }
 .numeric_literal    { color: #BFEBBF; }
+.bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
 .module             { color: #AFD8AF; }
 .variable           { color: #DCDCCC; }
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index b55cf748dc08..5f20fae80fcc 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -413,6 +413,7 @@ fn highlight_element(
                 | T![in] => h | HighlightModifier::ControlFlow,
                 T![for] if !is_child_of_impl(element) => h | HighlightModifier::ControlFlow,
                 T![unsafe] => h | HighlightModifier::Unsafe,
+                T![true] | T![false] => HighlightTag::BoolLiteral.into(),
                 _ => h,
             }
         }
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs
index ff0eeeb52c06..edfe61f39a13 100644
--- a/crates/ra_ide/src/syntax_highlighting/html.rs
+++ b/crates/ra_ide/src/syntax_highlighting/html.rs
@@ -76,6 +76,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .type_param         { color: #DFAF8F; }
 .attribute          { color: #94BFF3; }
 .numeric_literal    { color: #BFEBBF; }
+.bool_literal       { color: #BFE6EB; }
 .macro              { color: #94BFF3; }
 .module             { color: #AFD8AF; }
 .variable           { color: #DCDCCC; }
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index 33e6619ec789..09652a5b1cca 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -15,6 +15,7 @@ pub struct HighlightModifiers(u32);
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub enum HighlightTag {
     Attribute,
+    BoolLiteral,
     BuiltinType,
     ByteLiteral,
     CharLiteral,
@@ -60,6 +61,7 @@ impl HighlightTag {
     fn as_str(self) -> &'static str {
         match self {
             HighlightTag::Attribute => "attribute",
+            HighlightTag::BoolLiteral => "bool_literal",
             HighlightTag::BuiltinType => "builtin_type",
             HighlightTag::ByteLiteral => "byte_literal",
             HighlightTag::CharLiteral => "char_literal",
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 90a6257ee8e1..9b775871f0e8 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -36,6 +36,7 @@ macro_rules! define_semantic_token_types {
 
 define_semantic_token_types![
     (ATTRIBUTE, "attribute"),
+    (BOOLEAN, "boolean"),
     (BUILTIN_TYPE, "builtinType"),
     (ENUM_MEMBER, "enumMember"),
     (LIFETIME, "lifetime"),
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 672e47e41c35..81a347247cb6 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -295,6 +295,7 @@ fn semantic_token_type_and_modifiers(
         HighlightTag::ByteLiteral | HighlightTag::NumericLiteral => {
             lsp_types::SemanticTokenType::NUMBER
         }
+        HighlightTag::BoolLiteral => semantic_tokens::BOOLEAN,
         HighlightTag::CharLiteral | HighlightTag::StringLiteral => {
             lsp_types::SemanticTokenType::STRING
         }
diff --git a/editors/code/package.json b/editors/code/package.json
index 578ee8b0eafc..21039ced8858 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -644,6 +644,9 @@
                     "function.attribute": [
                         "entity.name.function.attribute.rust"
                     ],
+                    "boolean": [
+                        "constant.language.boolean.rust"
+                    ],
                     "builtinType": [
                         "support.type.primitive.rust"
                     ],

From 1895888aec1c87096809057b19a602e1cec9ada6 Mon Sep 17 00:00:00 2001
From: Matthew Jasper 
Date: Thu, 21 May 2020 17:44:45 +0100
Subject: [PATCH 056/163] Handle more cases in `highlight_name_by_syntax`

---
 crates/ra_ide/src/syntax_highlighting.rs | 30 +++++++++++++++---------
 1 file changed, 19 insertions(+), 11 deletions(-)

diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 5f20fae80fcc..61aeb28cbc73 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -481,23 +481,31 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight {
 }
 
 fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
-    let default = HighlightTag::Function.into();
+    let default = HighlightTag::UnresolvedReference;
 
     let parent = match name.syntax().parent() {
         Some(it) => it,
-        _ => return default,
+        _ => return default.into(),
     };
 
-    match parent.kind() {
-        STRUCT_DEF => HighlightTag::Struct.into(),
-        ENUM_DEF => HighlightTag::Enum.into(),
-        UNION_DEF => HighlightTag::Union.into(),
-        TRAIT_DEF => HighlightTag::Trait.into(),
-        TYPE_ALIAS_DEF => HighlightTag::TypeAlias.into(),
-        TYPE_PARAM => HighlightTag::TypeParam.into(),
-        RECORD_FIELD_DEF => HighlightTag::Field.into(),
+    let tag = match parent.kind() {
+        STRUCT_DEF => HighlightTag::Struct,
+        ENUM_DEF => HighlightTag::Enum,
+        UNION_DEF => HighlightTag::Union,
+        TRAIT_DEF => HighlightTag::Trait,
+        TYPE_ALIAS_DEF => HighlightTag::TypeAlias,
+        TYPE_PARAM => HighlightTag::TypeParam,
+        RECORD_FIELD_DEF => HighlightTag::Field,
+        MODULE => HighlightTag::Module,
+        FN_DEF => HighlightTag::Function,
+        CONST_DEF => HighlightTag::Constant,
+        STATIC_DEF => HighlightTag::Static,
+        ENUM_VARIANT => HighlightTag::EnumVariant,
+        BIND_PAT => HighlightTag::Local,
         _ => default,
-    }
+    };
+
+    tag.into()
 }
 
 fn highlight_injection(

From 0e814a3b5f5c7d034b0249cfb4391d9fcb9d8e42 Mon Sep 17 00:00:00 2001
From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
Date: Sun, 24 May 2020 16:47:35 +0200
Subject: [PATCH 057/163] fix textedit range returned for completion when left
 token is a keyword #4545

Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
---
 crates/ra_hir/src/code_model.rs               |  1 +
 .../completion/complete_unqualified_path.rs   | 35 +++++++++++++++++++
 .../src/completion/completion_context.rs      | 11 +++++-
 3 files changed, 46 insertions(+), 1 deletion(-)

diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 840cfdfc8238..7c015aa98a72 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -1363,6 +1363,7 @@ impl HirDisplay for Type {
 }
 
 /// For IDE only
+#[derive(Debug)]
 pub enum ScopeDef {
     ModuleDef(ModuleDef),
     MacroDef(MacroDef),
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index db791660a18a..417a92001013 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -297,6 +297,41 @@ mod tests {
         );
     }
 
+    #[test]
+    fn completes_bindings_from_for_with_in_prefix() {
+        assert_debug_snapshot!(
+            do_reference_completion(
+                r"
+                fn test() {
+                    for index in &[1, 2, 3] {
+                        let t = in<|>
+                    }
+                }
+                "
+            ),
+            @r###"
+        [
+            CompletionItem {
+                label: "index",
+                source_range: 107..107,
+                delete: 107..107,
+                insert: "index",
+                kind: Binding,
+            },
+            CompletionItem {
+                label: "test()",
+                source_range: 107..107,
+                delete: 107..107,
+                insert: "test()$0",
+                kind: Function,
+                lookup: "test",
+                detail: "fn test()",
+            },
+        ]
+        "###
+        );
+    }
+
     #[test]
     fn completes_generic_params() {
         assert_debug_snapshot!(
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index da336973c180..e8bf07d6eb48 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -169,7 +169,16 @@ impl<'a> CompletionContext<'a> {
         match self.token.kind() {
             // workaroud when completion is triggered by trigger characters.
             IDENT => self.original_token.text_range(),
-            _ => TextRange::empty(self.offset),
+            _ => {
+                // If we haven't characters between keyword and our cursor we take the keyword start range to edit
+                if self.token.kind().is_keyword()
+                    && self.offset == self.original_token.text_range().end()
+                {
+                    TextRange::empty(self.original_token.text_range().start())
+                } else {
+                    TextRange::empty(self.offset)
+                }
+            }
         }
     }
 

From 934227361623b258d833be20e464e1509cb432ad Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Sun, 24 May 2020 16:18:46 +0200
Subject: [PATCH 058/163] Document matchingBrace LSP request

---
 crates/rust-analyzer/src/lsp_ext.rs           | 12 ++---
 crates/rust-analyzer/src/main_loop.rs         |  4 +-
 .../rust-analyzer/src/main_loop/handlers.rs   |  8 ++--
 docs/dev/lsp-extensions.md                    | 47 ++++++++++++++++++-
 editors/code/src/commands/matching_brace.ts   |  4 +-
 editors/code/src/rust-analyzer-api.ts         |  7 ++-
 6 files changed, 63 insertions(+), 19 deletions(-)

diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index c25d90a504d5..b8b9e65ed35a 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -60,19 +60,19 @@ pub struct ExpandMacroParams {
     pub position: Option,
 }
 
-pub enum FindMatchingBrace {}
+pub enum MatchingBrace {}
 
-impl Request for FindMatchingBrace {
-    type Params = FindMatchingBraceParams;
+impl Request for MatchingBrace {
+    type Params = MatchingBraceParams;
     type Result = Vec;
-    const METHOD: &'static str = "rust-analyzer/findMatchingBrace";
+    const METHOD: &'static str = "experimental/matchingBrace";
 }
 
 #[derive(Deserialize, Serialize, Debug)]
 #[serde(rename_all = "camelCase")]
-pub struct FindMatchingBraceParams {
+pub struct MatchingBraceParams {
     pub text_document: TextDocumentIdentifier,
-    pub offsets: Vec,
+    pub positions: Vec,
 }
 
 pub enum ParentModule {}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 87795fffbd67..e28a32c260ba 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -509,8 +509,8 @@ fn on_request(
         .on_sync::(|s, p| {
             handlers::handle_selection_range(s.snapshot(), p)
         })?
-        .on_sync::(|s, p| {
-            handlers::handle_find_matching_brace(s.snapshot(), p)
+        .on_sync::(|s, p| {
+            handlers::handle_matching_brace(s.snapshot(), p)
         })?
         .on::(handlers::handle_analyzer_status)?
         .on::(handlers::handle_syntax_tree)?
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 2aaff3ea48ba..d73107968123 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -126,15 +126,15 @@ pub fn handle_selection_range(
     Ok(Some(res?))
 }
 
-pub fn handle_find_matching_brace(
+pub fn handle_matching_brace(
     world: WorldSnapshot,
-    params: lsp_ext::FindMatchingBraceParams,
+    params: lsp_ext::MatchingBraceParams,
 ) -> Result> {
-    let _p = profile("handle_find_matching_brace");
+    let _p = profile("handle_matching_brace");
     let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?;
     let line_index = world.analysis().file_line_index(file_id)?;
     let res = params
-        .offsets
+        .positions
         .into_iter()
         .map(|position| {
             let offset = from_proto::offset(&line_index, position);
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 1cc51410b7f3..9fa1c5fc2e43 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -95,7 +95,7 @@ Invoking code action at this position will yield two code actions for importing
 
 This request is send from client to server to handle "Join Lines" editor action.
 
-**Method:** `experimental/JoinLines`
+**Method:** `experimental/joinLines`
 
 **Request:**
 
@@ -172,3 +172,48 @@ SSR with query `foo($a:expr, $b:expr) ==>> ($a).foo($b)` will transform, eg `foo
 
 * Probably needs search without replace mode
 * Needs a way to limit the scope to certain files.
+
+## Matching Brace
+
+**Issue:** https://github.com/microsoft/language-server-protocol/issues/999
+
+**Server Capability:** `{ "matchingBrace": boolean }`
+
+This request is send from client to server to handle "Matching Brace" editor action.
+
+**Method:** `experimental/matchingBrace`
+
+**Request:**
+
+```typescript
+interface MatchingBraceParams {
+    textDocument: TextDocumentIdentifier,
+    /// Position for each cursor
+    positions: Position[],
+}
+```
+
+**Response:**
+
+```typescript
+Position[]
+```
+
+### Example
+
+```rust
+fn main() {
+    let x: Vec<()>/*cursor here*/ = vec![]
+}
+```
+
+`experimental/matchingBrace` yields the position of `<`.
+In many cases, matching braces can be handled by the editor.
+However, some cases (like disambiguating between generics and comparison operations) need a real parser.
+Moreover, it would be cool if editors didn't need to implement even basic language parsing
+
+### Unresolved Question
+
+* Should we return a a nested brace structure, to allow paredit-like actions of jump *out* of the current brace pair?
+  This is how `SelectionRange` request works.
+* Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs?
diff --git a/editors/code/src/commands/matching_brace.ts b/editors/code/src/commands/matching_brace.ts
index a60776e2d4d1..9c418b887c21 100644
--- a/editors/code/src/commands/matching_brace.ts
+++ b/editors/code/src/commands/matching_brace.ts
@@ -9,9 +9,9 @@ export function matchingBrace(ctx: Ctx): Cmd {
         const client = ctx.client;
         if (!editor || !client) return;
 
-        const response = await client.sendRequest(ra.findMatchingBrace, {
+        const response = await client.sendRequest(ra.matchingBrace, {
             textDocument: { uri: editor.document.uri.toString() },
-            offsets: editor.selections.map(s =>
+            positions: editor.selections.map(s =>
                 client.code2ProtocolConverter.asPosition(s.active),
             ),
         });
diff --git a/editors/code/src/rust-analyzer-api.ts b/editors/code/src/rust-analyzer-api.ts
index 73f36432f649..900c5cd5bce5 100644
--- a/editors/code/src/rust-analyzer-api.ts
+++ b/editors/code/src/rust-analyzer-api.ts
@@ -40,12 +40,11 @@ export interface ExpandedMacro {
 export const expandMacro = request>("expandMacro");
 
 
-export interface FindMatchingBraceParams {
+export interface MatchingBraceParams {
     textDocument: lc.TextDocumentIdentifier;
-    offsets: Vec;
+    positions: lc.Position[];
 }
-export const findMatchingBrace = request>("findMatchingBrace");
-
+export const matchingBrace = new lc.RequestType('experimental/matchingBrace');
 
 export interface PublishDecorationsParams {
     uri: string;

From dec4ba80236e1be15f011fd6b2f7f0ecb151fd31 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Sun, 24 May 2020 17:01:40 +0200
Subject: [PATCH 059/163] Document some rust-analyzer specific protocol
 extensions

---
 crates/rust-analyzer/src/lsp_ext.rs | 15 +++----
 docs/dev/lsp-extensions.md          | 62 +++++++++++++++++++++++++++++
 2 files changed, 70 insertions(+), 7 deletions(-)

diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index b8b9e65ed35a..c7e31c076300 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -38,13 +38,6 @@ pub struct SyntaxTreeParams {
     pub range: Option,
 }
 
-#[derive(Deserialize, Serialize, Debug)]
-#[serde(rename_all = "camelCase")]
-pub struct ExpandedMacro {
-    pub name: String,
-    pub expansion: String,
-}
-
 pub enum ExpandMacro {}
 
 impl Request for ExpandMacro {
@@ -60,6 +53,14 @@ pub struct ExpandMacroParams {
     pub position: Option,
 }
 
+#[derive(Deserialize, Serialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct ExpandedMacro {
+    pub name: String,
+    pub expansion: String,
+}
+
+
 pub enum MatchingBrace {}
 
 impl Request for MatchingBrace {
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 9fa1c5fc2e43..55035cfae183 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -217,3 +217,65 @@ Moreover, it would be cool if editors didn't need to implement even basic langua
 * Should we return a a nested brace structure, to allow paredit-like actions of jump *out* of the current brace pair?
   This is how `SelectionRange` request works.
 * Alternatively, should we perhaps flag certain `SelectionRange`s as being brace pairs?
+
+## Analyzer Status
+
+**Method:** `rust-analyzer/analyzerStatus`
+
+**Request:** `null`
+
+**Response:** `string`
+
+Returns internal status message, mostly for debugging purposes.
+
+## Collect Garbage
+
+**Method:** `rust-analyzer/collectGarbage`
+
+**Request:** `null`
+
+**Response:** `null`
+
+Frees some caches. For internal use, and is mostly broken at the moment.
+
+## Syntax Tree
+
+**Method:** `rust-analyzer/syntaxTree`
+
+**Request:**
+
+```typescript
+interface SyntaxTeeParams {
+    textDocument: TextDocumentIdentifier,
+    range?: Range,
+}
+```
+
+**Response:** `string`
+
+Returns textual representation of a parse tree for the file/selected region.
+Primarily for debugging, but very useful for all people working on rust-analyzer itself.
+
+## Expand Macro
+
+**Method:** `rust-analyzer/expandMacro`
+
+**Request:**
+
+```typescript
+interface ExpandMacroParams {
+    textDocument: TextDocumentIdentifier,
+    position?: Position,
+}
+```
+
+**Response:**
+
+```typescript
+interface ExpandedMacro {
+    name: string,
+    expansion: string,
+}
+```
+
+Expands macro call at a given position.

From 5276bfff819520cd27703b5d33a95d9674649e1e Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Sun, 24 May 2020 17:04:17 +0200
Subject: [PATCH 060/163] Fix formatting

---
 crates/rust-analyzer/src/lsp_ext.rs   | 1 -
 crates/rust-analyzer/src/main_loop.rs | 4 +---
 2 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index c7e31c076300..52e4fcbecae5 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -60,7 +60,6 @@ pub struct ExpandedMacro {
     pub expansion: String,
 }
 
-
 pub enum MatchingBrace {}
 
 impl Request for MatchingBrace {
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index e28a32c260ba..f1287d52cd32 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -509,9 +509,7 @@ fn on_request(
         .on_sync::(|s, p| {
             handlers::handle_selection_range(s.snapshot(), p)
         })?
-        .on_sync::(|s, p| {
-            handlers::handle_matching_brace(s.snapshot(), p)
-        })?
+        .on_sync::(|s, p| handlers::handle_matching_brace(s.snapshot(), p))?
         .on::(handlers::handle_analyzer_status)?
         .on::(handlers::handle_syntax_tree)?
         .on::(handlers::handle_expand_macro)?

From 5dab5e737909532e4a65390541393af6ee72f65b Mon Sep 17 00:00:00 2001
From: veetaha 
Date: Mon, 25 May 2020 03:47:33 +0300
Subject: [PATCH 061/163] Introduce `toggle inlay hints` vscode command

Users now can assign a shortcut for this command
via the general vscode
keybindings ui or `keybinding.json file`

Closes: #4599
---
 docs/user/features.md                           |  6 ++++++
 editors/code/package.json                       |  5 +++++
 editors/code/src/commands/index.ts              |  1 +
 editors/code/src/commands/toggle_inlay_hints.ts | 11 +++++++++++
 editors/code/src/config.ts                      |  2 +-
 editors/code/src/main.ts                        |  1 +
 6 files changed, 25 insertions(+), 1 deletion(-)
 create mode 100644 editors/code/src/commands/toggle_inlay_hints.ts

diff --git a/docs/user/features.md b/docs/user/features.md
index 340bce835565..12ecdec136a2 100644
--- a/docs/user/features.md
+++ b/docs/user/features.md
@@ -93,6 +93,12 @@ Shows internal statistic about memory usage of rust-analyzer.
 
 Show current rust-analyzer version.
 
+#### Toggle inlay hints
+
+Toggle inlay hints view for the current workspace.
+It is recommended to assign a shortcut for this command to quickly turn off
+inlay hints when they prevent you from reading/writing the code.
+
 #### Run Garbage Collection
 
 Manually triggers GC.
diff --git a/editors/code/package.json b/editors/code/package.json
index 21039ced8858..2f14eaebd258 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -166,6 +166,11 @@
                 "command": "rust-analyzer.serverVersion",
                 "title": "Show RA Version",
                 "category": "Rust Analyzer"
+            },
+            {
+                "command": "rust-analyzer.toggleInlayHints",
+                "title": "Toggle inlay hints",
+                "category": "Rust Analyzer"
             }
         ],
         "keybindings": [
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts
index abb53a248152..c2a232d5fd87 100644
--- a/editors/code/src/commands/index.ts
+++ b/editors/code/src/commands/index.ts
@@ -16,6 +16,7 @@ export * from './expand_macro';
 export * from './runnables';
 export * from './ssr';
 export * from './server_version';
+export * from './toggle_inlay_hints';
 
 export function collectGarbage(ctx: Ctx): Cmd {
     return async () => ctx.client.sendRequest(ra.collectGarbage, null);
diff --git a/editors/code/src/commands/toggle_inlay_hints.ts b/editors/code/src/commands/toggle_inlay_hints.ts
new file mode 100644
index 000000000000..7606af8d0cb0
--- /dev/null
+++ b/editors/code/src/commands/toggle_inlay_hints.ts
@@ -0,0 +1,11 @@
+import * as vscode from 'vscode';
+import { Ctx, Cmd } from '../ctx';
+
+export function toggleInlayHints(ctx: Ctx): Cmd {
+    return async () => {
+        await vscode
+            .workspace
+            .getConfiguration(`${ctx.config.rootSection}.inlayHints`)
+            .update('enable', !ctx.config.inlayHints.enable, vscode.ConfigurationTarget.Workspace);
+    };
+}
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index ee294fbe312c..e8abf8284eb7 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -8,7 +8,7 @@ export const NIGHTLY_TAG = "nightly";
 export class Config {
     readonly extensionId = "matklad.rust-analyzer";
 
-    private readonly rootSection = "rust-analyzer";
+    readonly rootSection = "rust-analyzer";
     private readonly requiresReloadOpts = [
         "serverPath",
         "cargo",
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 3405634f3395..0e5a206410e0 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -86,6 +86,7 @@ export async function activate(context: vscode.ExtensionContext) {
 
     ctx.registerCommand('ssr', commands.ssr);
     ctx.registerCommand('serverVersion', commands.serverVersion);
+    ctx.registerCommand('toggleInlayHints', commands.toggleInlayHints);
 
     // Internal commands which are invoked by the server.
     ctx.registerCommand('runSingle', commands.runSingle);

From 87aa746ed2820d401975534a33454d16c0034ee2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= 
Date: Mon, 25 May 2020 11:39:56 +0300
Subject: [PATCH 062/163] Add boolean literals to package.json

---
 editors/code/package.json | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/editors/code/package.json b/editors/code/package.json
index 2f14eaebd258..0e6755a3222a 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -586,6 +586,11 @@
                 "id": "attribute",
                 "description": "Style for attributes"
             },
+            {
+                "id": "boolean",
+                "description": "Style for boolean literals",
+                "superType": "keyword"
+            },
             {
                 "id": "builtinType",
                 "description": "Style for builtin types",

From 4a013ec62d2dfacaf6010b08c96947aa38481721 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Mon, 25 May 2020 10:59:54 +0200
Subject: [PATCH 063/163] Remove dead code

---
 editors/code/src/commands/index.ts    | 52 +-------------------------
 editors/code/src/commands/on_enter.ts |  2 +-
 editors/code/src/snippets.ts          | 52 ++++++++++++++++++++++++++
 editors/code/src/source_change.ts     | 54 ---------------------------
 4 files changed, 54 insertions(+), 106 deletions(-)
 create mode 100644 editors/code/src/snippets.ts
 delete mode 100644 editors/code/src/source_change.ts

diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts
index c2a232d5fd87..1ed8258d867e 100644
--- a/editors/code/src/commands/index.ts
+++ b/editors/code/src/commands/index.ts
@@ -3,8 +3,7 @@ import * as lc from 'vscode-languageclient';
 import * as ra from '../rust-analyzer-api';
 
 import { Ctx, Cmd } from '../ctx';
-import * as sourceChange from '../source_change';
-import { assert } from '../util';
+import { applySnippetWorkspaceEdit } from '../snippets';
 
 export * from './analyzer_status';
 export * from './matching_brace';
@@ -55,52 +54,3 @@ export function applySnippetWorkspaceEditCommand(_ctx: Ctx): Cmd {
         await applySnippetWorkspaceEdit(edit);
     };
 }
-
-export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) {
-    assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`);
-    const [uri, edits] = edit.entries()[0];
-
-    const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString());
-    if (!editor) return;
-
-    let selection: vscode.Selection | undefined = undefined;
-    let lineDelta = 0;
-    await editor.edit((builder) => {
-        for (const indel of edits) {
-            const parsed = parseSnippet(indel.newText);
-            if (parsed) {
-                const [newText, [placeholderStart, placeholderLength]] = parsed;
-                const prefix = newText.substr(0, placeholderStart);
-                const lastNewline = prefix.lastIndexOf('\n');
-
-                const startLine = indel.range.start.line + lineDelta + countLines(prefix);
-                const startColumn = lastNewline === -1 ?
-                    indel.range.start.character + placeholderStart
-                    : prefix.length - lastNewline - 1;
-                const endColumn = startColumn + placeholderLength;
-                selection = new vscode.Selection(
-                    new vscode.Position(startLine, startColumn),
-                    new vscode.Position(startLine, endColumn),
-                );
-                builder.replace(indel.range, newText);
-            } else {
-                lineDelta = countLines(indel.newText) - (indel.range.end.line - indel.range.start.line);
-                builder.replace(indel.range, indel.newText);
-            }
-        }
-    });
-    if (selection) editor.selection = selection;
-}
-
-function parseSnippet(snip: string): [string, [number, number]] | undefined {
-    const m = snip.match(/\$(0|\{0:([^}]*)\})/);
-    if (!m) return undefined;
-    const placeholder = m[2] ?? "";
-    const range: [number, number] = [m.index!!, placeholder.length];
-    const insert = snip.replace(m[0], placeholder);
-    return [insert, range];
-}
-
-function countLines(text: string): number {
-    return (text.match(/\n/g) || []).length;
-}
diff --git a/editors/code/src/commands/on_enter.ts b/editors/code/src/commands/on_enter.ts
index a7871c31eed0..0e4769633b77 100644
--- a/editors/code/src/commands/on_enter.ts
+++ b/editors/code/src/commands/on_enter.ts
@@ -2,7 +2,7 @@ import * as vscode from 'vscode';
 import * as ra from '../rust-analyzer-api';
 
 import { Cmd, Ctx } from '../ctx';
-import { applySnippetWorkspaceEdit } from '.';
+import { applySnippetWorkspaceEdit } from '../snippets';
 
 async function handleKeypress(ctx: Ctx) {
     const editor = ctx.activeRustEditor;
diff --git a/editors/code/src/snippets.ts b/editors/code/src/snippets.ts
new file mode 100644
index 000000000000..794530162dc0
--- /dev/null
+++ b/editors/code/src/snippets.ts
@@ -0,0 +1,52 @@
+import * as vscode from 'vscode';
+
+import { assert } from './util';
+
+export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) {
+    assert(edit.entries().length === 1, `bad ws edit: ${JSON.stringify(edit)}`);
+    const [uri, edits] = edit.entries()[0];
+
+    const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString());
+    if (!editor) return;
+
+    let selection: vscode.Selection | undefined = undefined;
+    let lineDelta = 0;
+    await editor.edit((builder) => {
+        for (const indel of edits) {
+            const parsed = parseSnippet(indel.newText);
+            if (parsed) {
+                const [newText, [placeholderStart, placeholderLength]] = parsed;
+                const prefix = newText.substr(0, placeholderStart);
+                const lastNewline = prefix.lastIndexOf('\n');
+
+                const startLine = indel.range.start.line + lineDelta + countLines(prefix);
+                const startColumn = lastNewline === -1 ?
+                    indel.range.start.character + placeholderStart
+                    : prefix.length - lastNewline - 1;
+                const endColumn = startColumn + placeholderLength;
+                selection = new vscode.Selection(
+                    new vscode.Position(startLine, startColumn),
+                    new vscode.Position(startLine, endColumn),
+                );
+                builder.replace(indel.range, newText);
+            } else {
+                lineDelta = countLines(indel.newText) - (indel.range.end.line - indel.range.start.line);
+                builder.replace(indel.range, indel.newText);
+            }
+        }
+    });
+    if (selection) editor.selection = selection;
+}
+
+function parseSnippet(snip: string): [string, [number, number]] | undefined {
+    const m = snip.match(/\$(0|\{0:([^}]*)\})/);
+    if (!m) return undefined;
+    const placeholder = m[2] ?? "";
+    const range: [number, number] = [m.index!!, placeholder.length];
+    const insert = snip.replace(m[0], placeholder);
+    return [insert, range];
+}
+
+function countLines(text: string): number {
+    return (text.match(/\n/g) || []).length;
+}
diff --git a/editors/code/src/source_change.ts b/editors/code/src/source_change.ts
deleted file mode 100644
index af8f1df51126..000000000000
--- a/editors/code/src/source_change.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import * as vscode from 'vscode';
-import * as lc from 'vscode-languageclient';
-import * as ra from './rust-analyzer-api';
-
-import { Ctx } from './ctx';
-
-export async function applySourceChange(ctx: Ctx, change: ra.SourceChange) {
-    const client = ctx.client;
-    if (!client) return;
-
-    const wsEdit = client.protocol2CodeConverter.asWorkspaceEdit(
-        change.workspaceEdit,
-    );
-    let created;
-    let moved;
-    if (change.workspaceEdit.documentChanges) {
-        for (const docChange of change.workspaceEdit.documentChanges) {
-            if (lc.CreateFile.is(docChange)) {
-                created = docChange.uri;
-            } else if (lc.RenameFile.is(docChange)) {
-                moved = docChange.newUri;
-            }
-        }
-    }
-    const toOpen = created || moved;
-    const toReveal = change.cursorPosition;
-    await vscode.workspace.applyEdit(wsEdit);
-    if (toOpen) {
-        const toOpenUri = vscode.Uri.parse(toOpen);
-        const doc = await vscode.workspace.openTextDocument(toOpenUri);
-        await vscode.window.showTextDocument(doc);
-    } else if (toReveal) {
-        const uri = client.protocol2CodeConverter.asUri(
-            toReveal.textDocument.uri,
-        );
-        const position = client.protocol2CodeConverter.asPosition(
-            toReveal.position,
-        );
-        const editor = vscode.window.activeTextEditor;
-        if (!editor || !editor.selection.isEmpty) {
-            return;
-        }
-
-        if (editor.document.uri !== uri) {
-            const doc = await vscode.workspace.openTextDocument(uri);
-            await vscode.window.showTextDocument(doc);
-        }
-        editor.selection = new vscode.Selection(position, position);
-        editor.revealRange(
-            new vscode.Range(position, position),
-            vscode.TextEditorRevealType.Default,
-        );
-    }
-}

From c2358365ad0b82d4a5f023192ee0ab2c198d6aee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= 
Date: Mon, 25 May 2020 11:51:56 +0300
Subject: [PATCH 064/163] Add self keyword semantic token type

---
 crates/ra_ide/src/snapshots/highlighting.html | 8 ++++----
 crates/ra_ide/src/syntax_highlighting.rs      | 1 +
 crates/ra_ide/src/syntax_highlighting/tags.rs | 2 ++
 crates/rust-analyzer/src/semantic_tokens.rs   | 1 +
 crates/rust-analyzer/src/to_proto.rs          | 1 +
 editors/code/package.json                     | 5 +++++
 6 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index 198139220efc..352e350955fc 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -35,12 +35,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 }
 
 trait Bar {
-    fn bar(&self) -> i32;
+    fn bar(&self) -> i32;
 }
 
 impl Bar for Foo {
-    fn bar(&self) -> i32 {
-        self.x
+    fn bar(&self) -> i32 {
+        self.x
     }
 }
 
@@ -92,7 +92,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 use Option::*;
 
 impl<T> Option<T> {
-    fn and<U>(self, other: Option<U>) -> Option<(T, U)> {
+    fn and<U>(self, other: Option<U>) -> Option<(T, U)> {
         match other {
             None => unimplemented!(),
             Nope => Nope,
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 61aeb28cbc73..8a995d779bae 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -414,6 +414,7 @@ fn highlight_element(
                 T![for] if !is_child_of_impl(element) => h | HighlightModifier::ControlFlow,
                 T![unsafe] => h | HighlightModifier::Unsafe,
                 T![true] | T![false] => HighlightTag::BoolLiteral.into(),
+                T![self] => HighlightTag::SelfKeyword.into(),
                 _ => h,
             }
         }
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index 09652a5b1cca..46c718c91e23 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -30,6 +30,7 @@ pub enum HighlightTag {
     Macro,
     Module,
     NumericLiteral,
+    SelfKeyword,
     SelfType,
     Static,
     StringLiteral,
@@ -76,6 +77,7 @@ impl HighlightTag {
             HighlightTag::Macro => "macro",
             HighlightTag::Module => "module",
             HighlightTag::NumericLiteral => "numeric_literal",
+            HighlightTag::SelfKeyword => "self_keyword",
             HighlightTag::SelfType => "self_type",
             HighlightTag::Static => "static",
             HighlightTag::StringLiteral => "string_literal",
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 9b775871f0e8..6f125c37cb99 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -40,6 +40,7 @@ define_semantic_token_types![
     (BUILTIN_TYPE, "builtinType"),
     (ENUM_MEMBER, "enumMember"),
     (LIFETIME, "lifetime"),
+    (SELF_KEYWORD, "selfKeyword"),
     (TYPE_ALIAS, "typeAlias"),
     (UNION, "union"),
     (UNRESOLVED_REFERENCE, "unresolvedReference"),
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 81a347247cb6..ebaad4d19b4e 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -274,6 +274,7 @@ fn semantic_token_type_and_modifiers(
         HighlightTag::TypeAlias => semantic_tokens::TYPE_ALIAS,
         HighlightTag::Trait => lsp_types::SemanticTokenType::INTERFACE,
         HighlightTag::BuiltinType => semantic_tokens::BUILTIN_TYPE,
+        HighlightTag::SelfKeyword => semantic_tokens::SELF_KEYWORD,
         HighlightTag::SelfType => lsp_types::SemanticTokenType::TYPE,
         HighlightTag::Field => lsp_types::SemanticTokenType::PROPERTY,
         HighlightTag::Function => lsp_types::SemanticTokenType::FUNCTION,
diff --git a/editors/code/package.json b/editors/code/package.json
index 2f14eaebd258..3127e6a30500 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -595,6 +595,11 @@
                 "id": "lifetime",
                 "description": "Style for lifetimes"
             },
+            {
+                "id": "selfKeyword",
+                "description": "Style for the self keyword",
+                "superType": "keyword"
+            },
             {
                 "id": "typeAlias",
                 "description": "Style for type aliases",

From 021b3da6721df7eaad2eb87024d2b0da28d60ade Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Mon, 25 May 2020 11:10:31 +0200
Subject: [PATCH 065/163] Flatten simple commands

---
 editors/code/src/commands/analyzer_status.ts  |  51 ----
 editors/code/src/commands/expand_macro.ts     |  66 -----
 editors/code/src/commands/index.ts            | 257 +++++++++++++++++-
 editors/code/src/commands/join_lines.ts       |  22 --
 editors/code/src/commands/matching_brace.ts   |  27 --
 editors/code/src/commands/on_enter.ts         |  35 ---
 editors/code/src/commands/parent_module.ts    |  29 --
 editors/code/src/commands/server_version.ts   |  15 -
 editors/code/src/commands/ssr.ts              |  30 --
 .../code/src/commands/toggle_inlay_hints.ts   |  11 -
 10 files changed, 242 insertions(+), 301 deletions(-)
 delete mode 100644 editors/code/src/commands/analyzer_status.ts
 delete mode 100644 editors/code/src/commands/expand_macro.ts
 delete mode 100644 editors/code/src/commands/join_lines.ts
 delete mode 100644 editors/code/src/commands/matching_brace.ts
 delete mode 100644 editors/code/src/commands/on_enter.ts
 delete mode 100644 editors/code/src/commands/parent_module.ts
 delete mode 100644 editors/code/src/commands/server_version.ts
 delete mode 100644 editors/code/src/commands/ssr.ts
 delete mode 100644 editors/code/src/commands/toggle_inlay_hints.ts

diff --git a/editors/code/src/commands/analyzer_status.ts b/editors/code/src/commands/analyzer_status.ts
deleted file mode 100644
index 09daa3402cf6..000000000000
--- a/editors/code/src/commands/analyzer_status.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import * as vscode from 'vscode';
-
-import * as ra from '../rust-analyzer-api';
-import { Ctx, Cmd } from '../ctx';
-
-// Shows status of rust-analyzer (for debugging)
-export function analyzerStatus(ctx: Ctx): Cmd {
-    let poller: NodeJS.Timer | undefined = undefined;
-    const tdcp = new TextDocumentContentProvider(ctx);
-
-    ctx.pushCleanup(
-        vscode.workspace.registerTextDocumentContentProvider(
-            'rust-analyzer-status',
-            tdcp,
-        ),
-    );
-
-    ctx.pushCleanup({
-        dispose() {
-            if (poller !== undefined) {
-                clearInterval(poller);
-            }
-        },
-    });
-
-    return async () => {
-        if (poller === undefined) {
-            poller = setInterval(() => tdcp.eventEmitter.fire(tdcp.uri), 1000);
-        }
-        const document = await vscode.workspace.openTextDocument(tdcp.uri);
-        return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true);
-    };
-}
-
-class TextDocumentContentProvider implements vscode.TextDocumentContentProvider {
-    readonly uri = vscode.Uri.parse('rust-analyzer-status://status');
-    readonly eventEmitter = new vscode.EventEmitter();
-
-    constructor(private readonly ctx: Ctx) {
-    }
-
-    provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult {
-        if (!vscode.window.activeTextEditor) return '';
-
-        return this.ctx.client.sendRequest(ra.analyzerStatus, null);
-    }
-
-    get onDidChange(): vscode.Event {
-        return this.eventEmitter.event;
-    }
-}
diff --git a/editors/code/src/commands/expand_macro.ts b/editors/code/src/commands/expand_macro.ts
deleted file mode 100644
index 23f2ef1d5e36..000000000000
--- a/editors/code/src/commands/expand_macro.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import * as vscode from 'vscode';
-import * as ra from '../rust-analyzer-api';
-
-import { Ctx, Cmd } from '../ctx';
-
-// Opens the virtual file that will show the syntax tree
-//
-// The contents of the file come from the `TextDocumentContentProvider`
-export function expandMacro(ctx: Ctx): Cmd {
-    const tdcp = new TextDocumentContentProvider(ctx);
-    ctx.pushCleanup(
-        vscode.workspace.registerTextDocumentContentProvider(
-            'rust-analyzer',
-            tdcp,
-        ),
-    );
-
-    return async () => {
-        const document = await vscode.workspace.openTextDocument(tdcp.uri);
-        tdcp.eventEmitter.fire(tdcp.uri);
-        return vscode.window.showTextDocument(
-            document,
-            vscode.ViewColumn.Two,
-            true,
-        );
-    };
-}
-
-function codeFormat(expanded: ra.ExpandedMacro): string {
-    let result = `// Recursive expansion of ${expanded.name}! macro\n`;
-    result += '// ' + '='.repeat(result.length - 3);
-    result += '\n\n';
-    result += expanded.expansion;
-
-    return result;
-}
-
-class TextDocumentContentProvider
-    implements vscode.TextDocumentContentProvider {
-    uri = vscode.Uri.parse('rust-analyzer://expandMacro/[EXPANSION].rs');
-    eventEmitter = new vscode.EventEmitter();
-
-    constructor(private readonly ctx: Ctx) {
-    }
-
-    async provideTextDocumentContent(_uri: vscode.Uri): Promise {
-        const editor = vscode.window.activeTextEditor;
-        const client = this.ctx.client;
-        if (!editor || !client) return '';
-
-        const position = editor.selection.active;
-
-        const expanded = await client.sendRequest(ra.expandMacro, {
-            textDocument: { uri: editor.document.uri.toString() },
-            position,
-        });
-
-        if (expanded == null) return 'Not available';
-
-        return codeFormat(expanded);
-    }
-
-    get onDidChange(): vscode.Event {
-        return this.eventEmitter.event;
-    }
-}
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands/index.ts
index 1ed8258d867e..1585912a2b77 100644
--- a/editors/code/src/commands/index.ts
+++ b/editors/code/src/commands/index.ts
@@ -4,18 +4,251 @@ import * as ra from '../rust-analyzer-api';
 
 import { Ctx, Cmd } from '../ctx';
 import { applySnippetWorkspaceEdit } from '../snippets';
+import { spawnSync } from 'child_process';
 
-export * from './analyzer_status';
-export * from './matching_brace';
-export * from './join_lines';
-export * from './on_enter';
-export * from './parent_module';
 export * from './syntax_tree';
-export * from './expand_macro';
 export * from './runnables';
-export * from './ssr';
-export * from './server_version';
-export * from './toggle_inlay_hints';
+
+export function analyzerStatus(ctx: Ctx): Cmd {
+    const tdcp = new class implements vscode.TextDocumentContentProvider {
+        readonly uri = vscode.Uri.parse('rust-analyzer-status://status');
+        readonly eventEmitter = new vscode.EventEmitter();
+
+        provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult {
+            if (!vscode.window.activeTextEditor) return '';
+
+            return ctx.client.sendRequest(ra.analyzerStatus, null);
+        }
+
+        get onDidChange(): vscode.Event {
+            return this.eventEmitter.event;
+        }
+    }();
+
+    let poller: NodeJS.Timer | undefined = undefined;
+
+    ctx.pushCleanup(
+        vscode.workspace.registerTextDocumentContentProvider(
+            'rust-analyzer-status',
+            tdcp,
+        ),
+    );
+
+    ctx.pushCleanup({
+        dispose() {
+            if (poller !== undefined) {
+                clearInterval(poller);
+            }
+        },
+    });
+
+    return async () => {
+        if (poller === undefined) {
+            poller = setInterval(() => tdcp.eventEmitter.fire(tdcp.uri), 1000);
+        }
+        const document = await vscode.workspace.openTextDocument(tdcp.uri);
+        return vscode.window.showTextDocument(document, vscode.ViewColumn.Two, true);
+    };
+}
+
+export function matchingBrace(ctx: Ctx): Cmd {
+    return async () => {
+        const editor = ctx.activeRustEditor;
+        const client = ctx.client;
+        if (!editor || !client) return;
+
+        const response = await client.sendRequest(ra.matchingBrace, {
+            textDocument: { uri: editor.document.uri.toString() },
+            positions: editor.selections.map(s =>
+                client.code2ProtocolConverter.asPosition(s.active),
+            ),
+        });
+        editor.selections = editor.selections.map((sel, idx) => {
+            const active = client.protocol2CodeConverter.asPosition(
+                response[idx],
+            );
+            const anchor = sel.isEmpty ? active : sel.anchor;
+            return new vscode.Selection(anchor, active);
+        });
+        editor.revealRange(editor.selection);
+    };
+}
+
+export function joinLines(ctx: Ctx): Cmd {
+    return async () => {
+        const editor = ctx.activeRustEditor;
+        const client = ctx.client;
+        if (!editor || !client) return;
+
+        const items: lc.TextEdit[] = await client.sendRequest(ra.joinLines, {
+            ranges: editor.selections.map((it) => client.code2ProtocolConverter.asRange(it)),
+            textDocument: { uri: editor.document.uri.toString() },
+        });
+        editor.edit((builder) => {
+            client.protocol2CodeConverter.asTextEdits(items).forEach((edit) => {
+                builder.replace(edit.range, edit.newText);
+            });
+        });
+    };
+}
+
+export function onEnter(ctx: Ctx): Cmd {
+    async function handleKeypress() {
+        const editor = ctx.activeRustEditor;
+        const client = ctx.client;
+
+        if (!editor || !client) return false;
+
+        const change = await client.sendRequest(ra.onEnter, {
+            textDocument: { uri: editor.document.uri.toString() },
+            position: client.code2ProtocolConverter.asPosition(
+                editor.selection.active,
+            ),
+        }).catch(_error => {
+            // client.logFailedRequest(OnEnterRequest.type, error);
+            return null;
+        });
+        if (!change) return false;
+
+        const workspaceEdit = client.protocol2CodeConverter.asWorkspaceEdit(change);
+        await applySnippetWorkspaceEdit(workspaceEdit);
+        return true;
+    }
+
+    return async () => {
+        if (await handleKeypress()) return;
+
+        await vscode.commands.executeCommand('default:type', { text: '\n' });
+    };
+}
+
+export function parentModule(ctx: Ctx): Cmd {
+    return async () => {
+        const editor = ctx.activeRustEditor;
+        const client = ctx.client;
+        if (!editor || !client) return;
+
+        const response = await client.sendRequest(ra.parentModule, {
+            textDocument: { uri: editor.document.uri.toString() },
+            position: client.code2ProtocolConverter.asPosition(
+                editor.selection.active,
+            ),
+        });
+        const loc = response[0];
+        if (loc == null) return;
+
+        const uri = client.protocol2CodeConverter.asUri(loc.uri);
+        const range = client.protocol2CodeConverter.asRange(loc.range);
+
+        const doc = await vscode.workspace.openTextDocument(uri);
+        const e = await vscode.window.showTextDocument(doc);
+        e.selection = new vscode.Selection(range.start, range.start);
+        e.revealRange(range, vscode.TextEditorRevealType.InCenter);
+    };
+}
+
+export function ssr(ctx: Ctx): Cmd {
+    return async () => {
+        const client = ctx.client;
+        if (!client) return;
+
+        const options: vscode.InputBoxOptions = {
+            value: "() ==>> ()",
+            prompt: "Enter request, for example 'Foo($a:expr) ==> Foo::new($a)' ",
+            validateInput: async (x: string) => {
+                try {
+                    await client.sendRequest(ra.ssr, { query: x, parseOnly: true });
+                } catch (e) {
+                    return e.toString();
+                }
+                return null;
+            }
+        };
+        const request = await vscode.window.showInputBox(options);
+        if (!request) return;
+
+        const edit = await client.sendRequest(ra.ssr, { query: request, parseOnly: false });
+
+        await vscode.workspace.applyEdit(client.protocol2CodeConverter.asWorkspaceEdit(edit));
+    };
+}
+
+export function serverVersion(ctx: Ctx): Cmd {
+    return async () => {
+        const { stdout } = spawnSync(ctx.serverPath, ["--version"], { encoding: "utf8" });
+        const commitHash = stdout.slice(`rust-analyzer `.length).trim();
+        const { releaseTag } = ctx.config.package;
+
+        void vscode.window.showInformationMessage(
+            `rust-analyzer version: ${releaseTag ?? "unreleased"} (${commitHash})`
+        );
+    };
+}
+
+export function toggleInlayHints(ctx: Ctx): Cmd {
+    return async () => {
+        await vscode
+            .workspace
+            .getConfiguration(`${ctx.config.rootSection}.inlayHints`)
+            .update('enable', !ctx.config.inlayHints.enable, vscode.ConfigurationTarget.Workspace);
+    };
+}
+
+// Opens the virtual file that will show the syntax tree
+//
+// The contents of the file come from the `TextDocumentContentProvider`
+export function expandMacro(ctx: Ctx): Cmd {
+    function codeFormat(expanded: ra.ExpandedMacro): string {
+        let result = `// Recursive expansion of ${expanded.name}! macro\n`;
+        result += '// ' + '='.repeat(result.length - 3);
+        result += '\n\n';
+        result += expanded.expansion;
+
+        return result;
+    }
+
+    const tdcp = new class implements vscode.TextDocumentContentProvider {
+        uri = vscode.Uri.parse('rust-analyzer://expandMacro/[EXPANSION].rs');
+        eventEmitter = new vscode.EventEmitter();
+        async provideTextDocumentContent(_uri: vscode.Uri): Promise {
+            const editor = vscode.window.activeTextEditor;
+            const client = ctx.client;
+            if (!editor || !client) return '';
+
+            const position = editor.selection.active;
+
+            const expanded = await client.sendRequest(ra.expandMacro, {
+                textDocument: { uri: editor.document.uri.toString() },
+                position,
+            });
+
+            if (expanded == null) return 'Not available';
+
+            return codeFormat(expanded);
+        }
+
+        get onDidChange(): vscode.Event {
+            return this.eventEmitter.event;
+        }
+    }();
+
+    ctx.pushCleanup(
+        vscode.workspace.registerTextDocumentContentProvider(
+            'rust-analyzer',
+            tdcp,
+        ),
+    );
+
+    return async () => {
+        const document = await vscode.workspace.openTextDocument(tdcp.uri);
+        tdcp.eventEmitter.fire(tdcp.uri);
+        return vscode.window.showTextDocument(
+            document,
+            vscode.ViewColumn.Two,
+            true,
+        );
+    };
+}
 
 export function collectGarbage(ctx: Ctx): Cmd {
     return async () => ctx.client.sendRequest(ra.collectGarbage, null);
@@ -35,12 +268,6 @@ export function showReferences(ctx: Ctx): Cmd {
     };
 }
 
-export function applySourceChange(ctx: Ctx): Cmd {
-    return async (change: ra.SourceChange) => {
-        await sourceChange.applySourceChange(ctx, change);
-    };
-}
-
 export function applyActionGroup(_ctx: Ctx): Cmd {
     return async (actions: { label: string; edit: vscode.WorkspaceEdit }[]) => {
         const selectedAction = await vscode.window.showQuickPick(actions);
diff --git a/editors/code/src/commands/join_lines.ts b/editors/code/src/commands/join_lines.ts
deleted file mode 100644
index 0bf1ee6e671e..000000000000
--- a/editors/code/src/commands/join_lines.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import * as ra from '../rust-analyzer-api';
-import * as lc from 'vscode-languageclient';
-
-import { Ctx, Cmd } from '../ctx';
-
-export function joinLines(ctx: Ctx): Cmd {
-    return async () => {
-        const editor = ctx.activeRustEditor;
-        const client = ctx.client;
-        if (!editor || !client) return;
-
-        const items: lc.TextEdit[] = await client.sendRequest(ra.joinLines, {
-            ranges: editor.selections.map((it) => client.code2ProtocolConverter.asRange(it)),
-            textDocument: { uri: editor.document.uri.toString() },
-        });
-        editor.edit((builder) => {
-            client.protocol2CodeConverter.asTextEdits(items).forEach((edit) => {
-                builder.replace(edit.range, edit.newText);
-            });
-        });
-    };
-}
diff --git a/editors/code/src/commands/matching_brace.ts b/editors/code/src/commands/matching_brace.ts
deleted file mode 100644
index 9c418b887c21..000000000000
--- a/editors/code/src/commands/matching_brace.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import * as vscode from 'vscode';
-import * as ra from '../rust-analyzer-api';
-
-import { Ctx, Cmd } from '../ctx';
-
-export function matchingBrace(ctx: Ctx): Cmd {
-    return async () => {
-        const editor = ctx.activeRustEditor;
-        const client = ctx.client;
-        if (!editor || !client) return;
-
-        const response = await client.sendRequest(ra.matchingBrace, {
-            textDocument: { uri: editor.document.uri.toString() },
-            positions: editor.selections.map(s =>
-                client.code2ProtocolConverter.asPosition(s.active),
-            ),
-        });
-        editor.selections = editor.selections.map((sel, idx) => {
-            const active = client.protocol2CodeConverter.asPosition(
-                response[idx],
-            );
-            const anchor = sel.isEmpty ? active : sel.anchor;
-            return new vscode.Selection(anchor, active);
-        });
-        editor.revealRange(editor.selection);
-    };
-}
diff --git a/editors/code/src/commands/on_enter.ts b/editors/code/src/commands/on_enter.ts
deleted file mode 100644
index 0e4769633b77..000000000000
--- a/editors/code/src/commands/on_enter.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import * as vscode from 'vscode';
-import * as ra from '../rust-analyzer-api';
-
-import { Cmd, Ctx } from '../ctx';
-import { applySnippetWorkspaceEdit } from '../snippets';
-
-async function handleKeypress(ctx: Ctx) {
-    const editor = ctx.activeRustEditor;
-    const client = ctx.client;
-
-    if (!editor || !client) return false;
-
-    const change = await client.sendRequest(ra.onEnter, {
-        textDocument: { uri: editor.document.uri.toString() },
-        position: client.code2ProtocolConverter.asPosition(
-            editor.selection.active,
-        ),
-    }).catch(_error => {
-        // client.logFailedRequest(OnEnterRequest.type, error);
-        return null;
-    });
-    if (!change) return false;
-
-    const workspaceEdit = client.protocol2CodeConverter.asWorkspaceEdit(change);
-    await applySnippetWorkspaceEdit(workspaceEdit);
-    return true;
-}
-
-export function onEnter(ctx: Ctx): Cmd {
-    return async () => {
-        if (await handleKeypress(ctx)) return;
-
-        await vscode.commands.executeCommand('default:type', { text: '\n' });
-    };
-}
diff --git a/editors/code/src/commands/parent_module.ts b/editors/code/src/commands/parent_module.ts
deleted file mode 100644
index 8f78ddd71c63..000000000000
--- a/editors/code/src/commands/parent_module.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import * as vscode from 'vscode';
-import * as ra from '../rust-analyzer-api';
-
-import { Ctx, Cmd } from '../ctx';
-
-export function parentModule(ctx: Ctx): Cmd {
-    return async () => {
-        const editor = ctx.activeRustEditor;
-        const client = ctx.client;
-        if (!editor || !client) return;
-
-        const response = await client.sendRequest(ra.parentModule, {
-            textDocument: { uri: editor.document.uri.toString() },
-            position: client.code2ProtocolConverter.asPosition(
-                editor.selection.active,
-            ),
-        });
-        const loc = response[0];
-        if (loc == null) return;
-
-        const uri = client.protocol2CodeConverter.asUri(loc.uri);
-        const range = client.protocol2CodeConverter.asRange(loc.range);
-
-        const doc = await vscode.workspace.openTextDocument(uri);
-        const e = await vscode.window.showTextDocument(doc);
-        e.selection = new vscode.Selection(range.start, range.start);
-        e.revealRange(range, vscode.TextEditorRevealType.InCenter);
-    };
-}
diff --git a/editors/code/src/commands/server_version.ts b/editors/code/src/commands/server_version.ts
deleted file mode 100644
index d64ac726e2cc..000000000000
--- a/editors/code/src/commands/server_version.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import * as vscode from "vscode";
-import { spawnSync } from "child_process";
-import { Ctx, Cmd } from '../ctx';
-
-export function serverVersion(ctx: Ctx): Cmd {
-    return async () => {
-        const { stdout } = spawnSync(ctx.serverPath, ["--version"], { encoding: "utf8" });
-        const commitHash = stdout.slice(`rust-analyzer `.length).trim();
-        const { releaseTag } = ctx.config.package;
-
-        void vscode.window.showInformationMessage(
-            `rust-analyzer version: ${releaseTag ?? "unreleased"} (${commitHash})`
-        );
-    };
-}
diff --git a/editors/code/src/commands/ssr.ts b/editors/code/src/commands/ssr.ts
deleted file mode 100644
index 5d40a64d28e9..000000000000
--- a/editors/code/src/commands/ssr.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import * as vscode from 'vscode';
-import * as ra from "../rust-analyzer-api";
-
-import { Ctx, Cmd } from '../ctx';
-
-export function ssr(ctx: Ctx): Cmd {
-    return async () => {
-        const client = ctx.client;
-        if (!client) return;
-
-        const options: vscode.InputBoxOptions = {
-            value: "() ==>> ()",
-            prompt: "Enter request, for example 'Foo($a:expr) ==> Foo::new($a)' ",
-            validateInput: async (x: string) => {
-                try {
-                    await client.sendRequest(ra.ssr, { query: x, parseOnly: true });
-                } catch (e) {
-                    return e.toString();
-                }
-                return null;
-            }
-        };
-        const request = await vscode.window.showInputBox(options);
-        if (!request) return;
-
-        const edit = await client.sendRequest(ra.ssr, { query: request, parseOnly: false });
-
-        await vscode.workspace.applyEdit(client.protocol2CodeConverter.asWorkspaceEdit(edit));
-    };
-}
diff --git a/editors/code/src/commands/toggle_inlay_hints.ts b/editors/code/src/commands/toggle_inlay_hints.ts
deleted file mode 100644
index 7606af8d0cb0..000000000000
--- a/editors/code/src/commands/toggle_inlay_hints.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import * as vscode from 'vscode';
-import { Ctx, Cmd } from '../ctx';
-
-export function toggleInlayHints(ctx: Ctx): Cmd {
-    return async () => {
-        await vscode
-            .workspace
-            .getConfiguration(`${ctx.config.rootSection}.inlayHints`)
-            .update('enable', !ctx.config.inlayHints.enable, vscode.ConfigurationTarget.Workspace);
-    };
-}

From 6058b8b0f6a24ad5b905d99d780a31b9e3d578d7 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Mon, 25 May 2020 12:02:30 +0200
Subject: [PATCH 066/163] Flatten commands.ts

---
 .../syntax_tree.ts => ast_inspector.ts}       | 88 +----------------
 .../src/{commands/index.ts => commands.ts}    | 97 ++++++++++++++++++-
 editors/code/src/main.ts                      |  1 -
 .../src/{commands/runnables.ts => run.ts}     | 26 ++---
 4 files changed, 103 insertions(+), 109 deletions(-)
 rename editors/code/src/{commands/syntax_tree.ts => ast_inspector.ts} (67%)
 rename editors/code/src/{commands/index.ts => commands.ts} (72%)
 rename editors/code/src/{commands/runnables.ts => run.ts} (88%)

diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/ast_inspector.ts
similarity index 67%
rename from editors/code/src/commands/syntax_tree.ts
rename to editors/code/src/ast_inspector.ts
index a5446c32724a..4fdd167bd5f7 100644
--- a/editors/code/src/commands/syntax_tree.ts
+++ b/editors/code/src/ast_inspector.ts
@@ -1,93 +1,15 @@
 import * as vscode from 'vscode';
-import * as ra from '../rust-analyzer-api';
-
-import { Ctx, Cmd, Disposable } from '../ctx';
-import { isRustDocument, RustEditor, isRustEditor, sleep } from '../util';
-
-const AST_FILE_SCHEME = "rust-analyzer";
-
-// Opens the virtual file that will show the syntax tree
-//
-// The contents of the file come from the `TextDocumentContentProvider`
-export function syntaxTree(ctx: Ctx): Cmd {
-    const tdcp = new TextDocumentContentProvider(ctx);
-
-    void new AstInspector(ctx);
-
-    ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider(AST_FILE_SCHEME, tdcp));
-    ctx.pushCleanup(vscode.languages.setLanguageConfiguration("ra_syntax_tree", {
-        brackets: [["[", ")"]],
-    }));
-
-    return async () => {
-        const editor = vscode.window.activeTextEditor;
-        const rangeEnabled = !!editor && !editor.selection.isEmpty;
-
-        const uri = rangeEnabled
-            ? vscode.Uri.parse(`${tdcp.uri.toString()}?range=true`)
-            : tdcp.uri;
-
-        const document = await vscode.workspace.openTextDocument(uri);
-
-        tdcp.eventEmitter.fire(uri);
-
-        void await vscode.window.showTextDocument(document, {
-            viewColumn: vscode.ViewColumn.Two,
-            preserveFocus: true
-        });
-    };
-}
-
-class TextDocumentContentProvider implements vscode.TextDocumentContentProvider {
-    readonly uri = vscode.Uri.parse('rust-analyzer://syntaxtree/tree.rast');
-    readonly eventEmitter = new vscode.EventEmitter();
-
-
-    constructor(private readonly ctx: Ctx) {
-        vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
-        vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions);
-    }
-
-    private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
-        if (isRustDocument(event.document)) {
-            // We need to order this after language server updates, but there's no API for that.
-            // Hence, good old sleep().
-            void sleep(10).then(() => this.eventEmitter.fire(this.uri));
-        }
-    }
-    private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
-        if (editor && isRustEditor(editor)) {
-            this.eventEmitter.fire(this.uri);
-        }
-    }
-
-    provideTextDocumentContent(uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult {
-        const rustEditor = this.ctx.activeRustEditor;
-        if (!rustEditor) return '';
-
-        // When the range based query is enabled we take the range of the selection
-        const range = uri.query === 'range=true' && !rustEditor.selection.isEmpty
-            ? this.ctx.client.code2ProtocolConverter.asRange(rustEditor.selection)
-            : null;
-
-        const params = { textDocument: { uri: rustEditor.document.uri.toString() }, range, };
-        return this.ctx.client.sendRequest(ra.syntaxTree, params, ct);
-    }
-
-    get onDidChange(): vscode.Event {
-        return this.eventEmitter.event;
-    }
-}
 
+import { Ctx, Disposable } from './ctx';
+import { RustEditor, isRustEditor } from './util';
 
 // FIXME: consider implementing this via the Tree View API?
 // https://code.visualstudio.com/api/extension-guides/tree-view
-class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, Disposable {
+export class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, Disposable {
     private readonly astDecorationType = vscode.window.createTextEditorDecorationType({
         borderColor: new vscode.ThemeColor('rust_analyzer.syntaxTreeBorder'),
         borderStyle: "solid",
         borderWidth: "2px",
-
     });
     private rustEditor: undefined | RustEditor;
 
@@ -113,7 +35,7 @@ class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, D
     });
 
     constructor(ctx: Ctx) {
-        ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: AST_FILE_SCHEME }, this));
+        ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: 'rust-analyzer' }, this));
         ctx.pushCleanup(vscode.languages.registerDefinitionProvider({ language: "rust" }, this));
         vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions);
         vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
@@ -146,7 +68,7 @@ class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, D
     }
 
     private findAstTextEditor(): undefined | vscode.TextEditor {
-        return vscode.window.visibleTextEditors.find(it => it.document.uri.scheme === AST_FILE_SCHEME);
+        return vscode.window.visibleTextEditors.find(it => it.document.uri.scheme === 'rust-analyzer');
     }
 
     private setRustEditor(newRustEditor: undefined | RustEditor) {
diff --git a/editors/code/src/commands/index.ts b/editors/code/src/commands.ts
similarity index 72%
rename from editors/code/src/commands/index.ts
rename to editors/code/src/commands.ts
index 1585912a2b77..573af5aa580d 100644
--- a/editors/code/src/commands/index.ts
+++ b/editors/code/src/commands.ts
@@ -1,13 +1,16 @@
 import * as vscode from 'vscode';
 import * as lc from 'vscode-languageclient';
-import * as ra from '../rust-analyzer-api';
+import * as ra from './rust-analyzer-api';
 
-import { Ctx, Cmd } from '../ctx';
-import { applySnippetWorkspaceEdit } from '../snippets';
+import { Ctx, Cmd } from './ctx';
+import { applySnippetWorkspaceEdit } from './snippets';
 import { spawnSync } from 'child_process';
+import { RunnableQuickPick, selectRunnable, createTask } from './run';
+import { AstInspector } from './ast_inspector';
+import { isRustDocument, sleep, isRustEditor } from './util';
 
-export * from './syntax_tree';
-export * from './runnables';
+export * from './ast_inspector';
+export * from './run';
 
 export function analyzerStatus(ctx: Ctx): Cmd {
     const tdcp = new class implements vscode.TextDocumentContentProvider {
@@ -194,6 +197,90 @@ export function toggleInlayHints(ctx: Ctx): Cmd {
     };
 }
 
+export function run(ctx: Ctx): Cmd {
+    let prevRunnable: RunnableQuickPick | undefined;
+
+    return async () => {
+        const item = await selectRunnable(ctx, prevRunnable);
+        if (!item) return;
+
+        item.detail = 'rerun';
+        prevRunnable = item;
+        const task = createTask(item.runnable);
+        return await vscode.tasks.executeTask(task);
+    };
+}
+
+// Opens the virtual file that will show the syntax tree
+//
+// The contents of the file come from the `TextDocumentContentProvider`
+export function syntaxTree(ctx: Ctx): Cmd {
+    const tdcp = new class implements vscode.TextDocumentContentProvider {
+        readonly uri = vscode.Uri.parse('rust-analyzer://syntaxtree/tree.rast');
+        readonly eventEmitter = new vscode.EventEmitter();
+        constructor() {
+            vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
+            vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions);
+        }
+
+        private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
+            if (isRustDocument(event.document)) {
+                // We need to order this after language server updates, but there's no API for that.
+                // Hence, good old sleep().
+                void sleep(10).then(() => this.eventEmitter.fire(this.uri));
+            }
+        }
+        private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
+            if (editor && isRustEditor(editor)) {
+                this.eventEmitter.fire(this.uri);
+            }
+        }
+
+        provideTextDocumentContent(uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult {
+            const rustEditor = ctx.activeRustEditor;
+            if (!rustEditor) return '';
+
+            // When the range based query is enabled we take the range of the selection
+            const range = uri.query === 'range=true' && !rustEditor.selection.isEmpty
+                ? ctx.client.code2ProtocolConverter.asRange(rustEditor.selection)
+                : null;
+
+            const params = { textDocument: { uri: rustEditor.document.uri.toString() }, range, };
+            return ctx.client.sendRequest(ra.syntaxTree, params, ct);
+        }
+
+        get onDidChange(): vscode.Event {
+            return this.eventEmitter.event;
+        }
+    };
+
+    void new AstInspector(ctx);
+
+    ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider('rust-analyzer', tdcp));
+    ctx.pushCleanup(vscode.languages.setLanguageConfiguration("ra_syntax_tree", {
+        brackets: [["[", ")"]],
+    }));
+
+    return async () => {
+        const editor = vscode.window.activeTextEditor;
+        const rangeEnabled = !!editor && !editor.selection.isEmpty;
+
+        const uri = rangeEnabled
+            ? vscode.Uri.parse(`${tdcp.uri.toString()}?range=true`)
+            : tdcp.uri;
+
+        const document = await vscode.workspace.openTextDocument(uri);
+
+        tdcp.eventEmitter.fire(uri);
+
+        void await vscode.window.showTextDocument(document, {
+            viewColumn: vscode.ViewColumn.Two,
+            preserveFocus: true
+        });
+    };
+}
+
+
 // Opens the virtual file that will show the syntax tree
 //
 // The contents of the file come from the `TextDocumentContentProvider`
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 0e5a206410e0..31ac81ee88d2 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -92,7 +92,6 @@ export async function activate(context: vscode.ExtensionContext) {
     ctx.registerCommand('runSingle', commands.runSingle);
     ctx.registerCommand('debugSingle', commands.debugSingle);
     ctx.registerCommand('showReferences', commands.showReferences);
-    ctx.registerCommand('applySourceChange', commands.applySourceChange);
     ctx.registerCommand('applySnippetWorkspaceEdit', commands.applySnippetWorkspaceEditCommand);
     ctx.registerCommand('applyActionGroup', commands.applyActionGroup);
 
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/run.ts
similarity index 88%
rename from editors/code/src/commands/runnables.ts
rename to editors/code/src/run.ts
index 0bd30fb077d7..8f0487d74a1e 100644
--- a/editors/code/src/commands/runnables.ts
+++ b/editors/code/src/run.ts
@@ -1,13 +1,13 @@
 import * as vscode from 'vscode';
 import * as lc from 'vscode-languageclient';
-import * as ra from '../rust-analyzer-api';
+import * as ra from './rust-analyzer-api';
 
-import { Ctx, Cmd } from '../ctx';
-import { startDebugSession, getDebugConfiguration } from '../debug';
+import { Ctx, Cmd } from './ctx';
+import { startDebugSession, getDebugConfiguration } from './debug';
 
 const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }];
 
-async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, debuggeeOnly = false, showButtons: boolean = true): Promise {
+export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, debuggeeOnly = false, showButtons: boolean = true): Promise {
     const editor = ctx.activeRustEditor;
     const client = ctx.client;
     if (!editor || !client) return;
@@ -83,20 +83,6 @@ async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, debugg
     });
 }
 
-export function run(ctx: Ctx): Cmd {
-    let prevRunnable: RunnableQuickPick | undefined;
-
-    return async () => {
-        const item = await selectRunnable(ctx, prevRunnable);
-        if (!item) return;
-
-        item.detail = 'rerun';
-        prevRunnable = item;
-        const task = createTask(item.runnable);
-        return await vscode.tasks.executeTask(task);
-    };
-}
-
 export function runSingle(ctx: Ctx): Cmd {
     return async (runnable: ra.Runnable) => {
         const editor = ctx.activeRustEditor;
@@ -165,7 +151,7 @@ export function newDebugConfig(ctx: Ctx): Cmd {
     };
 }
 
-class RunnableQuickPick implements vscode.QuickPickItem {
+export class RunnableQuickPick implements vscode.QuickPickItem {
     public label: string;
     public description?: string | undefined;
     public detail?: string | undefined;
@@ -184,7 +170,7 @@ interface CargoTaskDefinition extends vscode.TaskDefinition {
     env?: { [key: string]: string };
 }
 
-function createTask(spec: ra.Runnable): vscode.Task {
+export function createTask(spec: ra.Runnable): vscode.Task {
     const TASK_SOURCE = 'Rust';
     const definition: CargoTaskDefinition = {
         type: 'cargo',

From 76e170c3d0d0784c0e612c5849798c65a2034f29 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Mon, 25 May 2020 14:12:53 +0200
Subject: [PATCH 067/163] Less rust-analyzer specific onEnter

---
 crates/ra_ide/src/lib.rs                      |  3 +-
 crates/ra_ide/src/typing/on_enter.rs          | 11 ++--
 crates/rust-analyzer/src/caps.rs              |  1 +
 crates/rust-analyzer/src/lsp_ext.rs           |  4 +-
 .../rust-analyzer/src/main_loop/handlers.rs   | 14 +++--
 crates/rust-analyzer/src/to_proto.rs          | 12 +++++
 .../rust-analyzer/tests/heavy_tests/main.rs   | 46 +++++-----------
 docs/dev/lsp-extensions.md                    | 53 +++++++++++++++++++
 editors/code/src/commands.ts                  | 10 ++--
 editors/code/src/rust-analyzer-api.ts         |  3 +-
 editors/code/src/snippets.ts                  |  3 ++
 11 files changed, 105 insertions(+), 55 deletions(-)

diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 5ac002d82f0c..d983cd910023 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -309,7 +309,8 @@ impl Analysis {
 
     /// Returns an edit which should be applied when opening a new line, fixing
     /// up minor stuff like continuing the comment.
-    pub fn on_enter(&self, position: FilePosition) -> Cancelable> {
+    /// The edit will be a snippet (with `$0`).
+    pub fn on_enter(&self, position: FilePosition) -> Cancelable> {
         self.with_db(|db| typing::on_enter(&db, position))
     }
 
diff --git a/crates/ra_ide/src/typing/on_enter.rs b/crates/ra_ide/src/typing/on_enter.rs
index e7d64b4f68c2..a40d8af9c43d 100644
--- a/crates/ra_ide/src/typing/on_enter.rs
+++ b/crates/ra_ide/src/typing/on_enter.rs
@@ -11,9 +11,7 @@ use ra_syntax::{
 };
 use ra_text_edit::TextEdit;
 
-use crate::{SourceChange, SourceFileEdit};
-
-pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option {
+pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option {
     let parse = db.parse(position.file_id);
     let file = parse.tree();
     let comment = file
@@ -41,9 +39,7 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option bool {
@@ -90,9 +86,8 @@ mod tests {
         let (analysis, file_id) = single_file(&before);
         let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?;
 
-        assert_eq!(result.source_file_edits.len(), 1);
         let mut actual = before.to_string();
-        result.source_file_edits[0].edit.apply(&mut actual);
+        result.apply(&mut actual);
         Some(actual)
     }
 
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 780fc93174fa..d55cbb15fe88 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -85,6 +85,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
         experimental: Some(json!({
             "joinLines": true,
             "ssr": true,
+            "onEnter": true,
         })),
     }
 }
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 52e4fcbecae5..1cce1baa4552 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -102,8 +102,8 @@ pub enum OnEnter {}
 
 impl Request for OnEnter {
     type Params = lsp_types::TextDocumentPositionParams;
-    type Result = Option;
-    const METHOD: &'static str = "rust-analyzer/onEnter";
+    type Result = Option>;
+    const METHOD: &'static str = "experimental/onEnter";
 }
 
 pub enum Runnables {}
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index d73107968123..a13a0e1f523b 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -174,13 +174,17 @@ pub fn handle_join_lines(
 pub fn handle_on_enter(
     world: WorldSnapshot,
     params: lsp_types::TextDocumentPositionParams,
-) -> Result> {
+) -> Result>> {
     let _p = profile("handle_on_enter");
     let position = from_proto::file_position(&world, params)?;
-    match world.analysis().on_enter(position)? {
-        None => Ok(None),
-        Some(source_change) => to_proto::snippet_workspace_edit(&world, source_change).map(Some),
-    }
+    let edit = match world.analysis().on_enter(position)? {
+        None => return Ok(None),
+        Some(it) => it,
+    };
+    let line_index = world.analysis().file_line_index(position.file_id)?;
+    let line_endings = world.file_line_endings(position.file_id);
+    let edit = to_proto::snippet_text_edit_vec(&line_index, line_endings, true, edit);
+    Ok(Some(edit))
 }
 
 // Don't forget to add new trigger characters to `ServerCapabilities` in `caps.rs`.
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 81a347247cb6..39d58f1e01bd 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -135,6 +135,18 @@ pub(crate) fn text_edit_vec(
     text_edit.into_iter().map(|indel| self::text_edit(line_index, line_endings, indel)).collect()
 }
 
+pub(crate) fn snippet_text_edit_vec(
+    line_index: &LineIndex,
+    line_endings: LineEndings,
+    is_snippet: bool,
+    text_edit: TextEdit,
+) -> Vec {
+    text_edit
+        .into_iter()
+        .map(|indel| self::snippet_text_edit(line_index, line_endings, is_snippet, indel))
+        .collect()
+}
+
 pub(crate) fn completion_item(
     line_index: &LineIndex,
     line_endings: LineEndings,
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 738a9a8e3750..b1bfc968a8dc 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -473,23 +473,14 @@ fn main() {{}}
             text_document: server.doc_id("src/m0.rs"),
             position: Position { line: 0, character: 5 },
         },
-        json!({
-          "documentChanges": [
-            {
-              "edits": [
-                {
-                  "insertTextFormat": 2,
-                  "newText": "\n/// $0",
-                  "range": {
-                    "end": { "character": 5, "line": 0 },
-                    "start": { "character": 5, "line": 0 }
-                  }
-                }
-              ],
-              "textDocument": { "uri": "file:///[..]src/m0.rs", "version": null }
+        json!([{
+            "insertTextFormat": 2,
+            "newText": "\n/// $0",
+            "range": {
+            "end": { "character": 5, "line": 0 },
+            "start": { "character": 5, "line": 0 }
             }
-          ]
-        }),
+        }]),
     );
     let elapsed = start.elapsed();
     assert!(elapsed.as_millis() < 2000, "typing enter took {:?}", elapsed);
@@ -519,23 +510,14 @@ version = \"0.0.0\"
             text_document: server.doc_id("src/main.rs"),
             position: Position { line: 0, character: 8 },
         },
-        json!({
-          "documentChanges": [
-            {
-              "edits": [
-                {
-                  "insertTextFormat": 2,
-                  "newText": "\r\n/// $0",
-                  "range": {
-                    "end": { "line": 0, "character": 8 },
-                    "start": { "line": 0, "character": 8 }
-                  }
-                }
-              ],
-              "textDocument": { "uri": "file:///[..]src/main.rs", "version": null }
+        json!([{
+            "insertTextFormat": 2,
+            "newText": "\r\n/// $0",
+            "range": {
+            "end": { "line": 0, "character": 8 },
+            "start": { "line": 0, "character": 8 }
             }
-          ]
-        }),
+        }]),
     );
 }
 
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 55035cfae183..e4b9fb2c25e0 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -138,6 +138,59 @@ fn main() {
   Currently this is left to editor's discretion, but it might be useful to specify on the server via snippets.
   However, it then becomes unclear how it works with multi cursor.
 
+## On Enter
+
+**Issue:** https://github.com/microsoft/language-server-protocol/issues/1001
+
+**Server Capability:** `{ "onEnter": boolean }`
+
+This request is send from client to server to handle Enter keypress.
+
+**Method:** `experimental/onEnter`
+
+**Request:**: `TextDocumentPositionParams`
+
+**Response:**
+
+```typescript
+SnippetTextEdit[]
+```
+
+### Example
+
+```rust
+fn main() {
+    // Some /*cursor here*/ docs
+    let x = 92;
+}
+```
+
+`experimental/onEnter` returns the following snippet
+
+```rust
+fn main() {
+    // Some
+    // $0 docs
+    let x = 92;
+}
+```
+
+The primary goal of `onEnter` is to handle automatic indentation when opening a new line.
+This is not yet implemented.
+The secondary goal is to handle fixing up syntax, like continuing doc strings and comments, and escaping `\n` in string literals.
+
+As proper cursor positioning is raison-d'etat for `onEnter`, it uses `SnippetTextEdit`.
+
+### Unresolved Question
+
+* How to deal with synchronicity of the request?
+  One option is to require the client to block until the server returns the response.
+  Another option is to do a OT-style merging of edits from client and server.
+  A third option is to do a record-replay: client applies heuristic on enter immediatelly, then applies all user's keypresses.
+  When the server is ready with the response, the client rollbacks all the changes and applies the recorded actions on top of the correct response.
+* How to deal with multiple carets?
+* Should we extend this to arbitrary typed events and not just `onEnter`?
+
 ## Structural Search Replace (SSR)
 
 **Server Capability:** `{ "ssr": boolean }`
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 573af5aa580d..e080301405dd 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -3,7 +3,7 @@ import * as lc from 'vscode-languageclient';
 import * as ra from './rust-analyzer-api';
 
 import { Ctx, Cmd } from './ctx';
-import { applySnippetWorkspaceEdit } from './snippets';
+import { applySnippetWorkspaceEdit, applySnippetTextEdits } from './snippets';
 import { spawnSync } from 'child_process';
 import { RunnableQuickPick, selectRunnable, createTask } from './run';
 import { AstInspector } from './ast_inspector';
@@ -102,7 +102,7 @@ export function onEnter(ctx: Ctx): Cmd {
 
         if (!editor || !client) return false;
 
-        const change = await client.sendRequest(ra.onEnter, {
+        const lcEdits = await client.sendRequest(ra.onEnter, {
             textDocument: { uri: editor.document.uri.toString() },
             position: client.code2ProtocolConverter.asPosition(
                 editor.selection.active,
@@ -111,10 +111,10 @@ export function onEnter(ctx: Ctx): Cmd {
             // client.logFailedRequest(OnEnterRequest.type, error);
             return null;
         });
-        if (!change) return false;
+        if (!lcEdits) return false;
 
-        const workspaceEdit = client.protocol2CodeConverter.asWorkspaceEdit(change);
-        await applySnippetWorkspaceEdit(workspaceEdit);
+        const edits = client.protocol2CodeConverter.asTextEdits(lcEdits);
+        await applySnippetTextEdits(editor, edits);
         return true;
     }
 
diff --git a/editors/code/src/rust-analyzer-api.ts b/editors/code/src/rust-analyzer-api.ts
index 900c5cd5bce5..c10c0fa78942 100644
--- a/editors/code/src/rust-analyzer-api.ts
+++ b/editors/code/src/rust-analyzer-api.ts
@@ -67,8 +67,7 @@ export interface JoinLinesParams {
 }
 export const joinLines = new lc.RequestType('experimental/joinLines');
 
-
-export const onEnter = request>("onEnter");
+export const onEnter = new lc.RequestType('experimental/onEnter');
 
 export interface RunnablesParams {
     textDocument: lc.TextDocumentIdentifier;
diff --git a/editors/code/src/snippets.ts b/editors/code/src/snippets.ts
index 794530162dc0..bcb3f2cc7613 100644
--- a/editors/code/src/snippets.ts
+++ b/editors/code/src/snippets.ts
@@ -8,7 +8,10 @@ export async function applySnippetWorkspaceEdit(edit: vscode.WorkspaceEdit) {
 
     const editor = vscode.window.visibleTextEditors.find((it) => it.document.uri.toString() === uri.toString());
     if (!editor) return;
+    await applySnippetTextEdits(editor, edits);
+}
 
+export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vscode.TextEdit[]) {
     let selection: vscode.Selection | undefined = undefined;
     let lineDelta = 0;
     await editor.edit((builder) => {

From a30bdd9795770329e4562d8bfca60ebe2e52dea1 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Mon, 25 May 2020 14:56:26 +0200
Subject: [PATCH 068/163] Cleanup lsp extensions on the client side

---
 crates/rust-analyzer/src/lsp_ext.rs           |   2 +-
 .../rust-analyzer/src/main_loop/handlers.rs   |  11 +-
 docs/dev/lsp-extensions.md                    |   2 +-
 editors/code/src/commands.ts                  |   2 +-
 editors/code/src/debug.ts                     |   2 +-
 editors/code/src/inlay_hints.ts               |   2 +-
 editors/code/src/lsp_ext.ts                   |  84 ++++++++++++
 editors/code/src/run.ts                       |   2 +-
 editors/code/src/rust-analyzer-api.ts         | 123 ------------------
 9 files changed, 93 insertions(+), 137 deletions(-)
 create mode 100644 editors/code/src/lsp_ext.ts
 delete mode 100644 editors/code/src/rust-analyzer-api.ts

diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 1cce1baa4552..c571c62aed33 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -50,7 +50,7 @@ impl Request for ExpandMacro {
 #[serde(rename_all = "camelCase")]
 pub struct ExpandMacroParams {
     pub text_document: TextDocumentIdentifier,
-    pub position: Option,
+    pub position: Position,
 }
 
 #[derive(Deserialize, Serialize, Debug)]
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index a13a0e1f523b..3ccc95c23e04 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -72,15 +72,10 @@ pub fn handle_expand_macro(
     let _p = profile("handle_expand_macro");
     let file_id = from_proto::file_id(&world, ¶ms.text_document.uri)?;
     let line_index = world.analysis().file_line_index(file_id)?;
-    let offset = params.position.map(|p| from_proto::offset(&line_index, p));
+    let offset = from_proto::offset(&line_index, params.position);
 
-    match offset {
-        None => Ok(None),
-        Some(offset) => {
-            let res = world.analysis().expand_macro(FilePosition { file_id, offset })?;
-            Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion }))
-        }
-    }
+    let res = world.analysis().expand_macro(FilePosition { file_id, offset })?;
+    Ok(res.map(|it| lsp_ext::ExpandedMacro { name: it.name, expansion: it.expansion }))
 }
 
 pub fn handle_selection_range(
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index e4b9fb2c25e0..48147b17393c 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -318,7 +318,7 @@ Primarily for debugging, but very useful for all people working on rust-analyzer
 ```typescript
 interface ExpandMacroParams {
     textDocument: TextDocumentIdentifier,
-    position?: Position,
+    position: Position,
 }
 ```
 
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index e080301405dd..49e3845d5b17 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -1,6 +1,6 @@
 import * as vscode from 'vscode';
 import * as lc from 'vscode-languageclient';
-import * as ra from './rust-analyzer-api';
+import * as ra from './lsp_ext';
 
 import { Ctx, Cmd } from './ctx';
 import { applySnippetWorkspaceEdit, applySnippetTextEdits } from './snippets';
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts
index d3fe588e8319..027504ecd2a8 100644
--- a/editors/code/src/debug.ts
+++ b/editors/code/src/debug.ts
@@ -1,7 +1,7 @@
 import * as os from "os";
 import * as vscode from 'vscode';
 import * as path from 'path';
-import * as ra from './rust-analyzer-api';
+import * as ra from './lsp_ext';
 
 import { Cargo } from './cargo';
 import { Ctx } from "./ctx";
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts
index a2b07d003788..9e6d6045f349 100644
--- a/editors/code/src/inlay_hints.ts
+++ b/editors/code/src/inlay_hints.ts
@@ -1,6 +1,6 @@
 import * as lc from "vscode-languageclient";
 import * as vscode from 'vscode';
-import * as ra from './rust-analyzer-api';
+import * as ra from './lsp_ext';
 
 import { Ctx, Disposable } from './ctx';
 import { sendRequestWithRetry, isRustDocument, RustDocument, RustEditor, sleep } from './util';
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
new file mode 100644
index 000000000000..2a06632619b5
--- /dev/null
+++ b/editors/code/src/lsp_ext.ts
@@ -0,0 +1,84 @@
+/**
+ * This file mirrors `crates/rust-analyzer/src/req.rs` declarations.
+ */
+
+import * as lc from "vscode-languageclient";
+
+export const analyzerStatus = new lc.RequestType("rust-analyzer/analyzerStatus");
+
+export const collectGarbage = new lc.RequestType("rust-analyzer/collectGarbage");
+
+export interface SyntaxTreeParams {
+    textDocument: lc.TextDocumentIdentifier;
+    range: lc.Range | null;
+}
+export const syntaxTree = new lc.RequestType("rust-analyzer/syntaxTree");
+
+
+export interface ExpandMacroParams {
+    textDocument: lc.TextDocumentIdentifier;
+    position: lc.Position;
+}
+export interface ExpandedMacro {
+    name: string;
+    expansion: string;
+}
+export const expandMacro = new lc.RequestType("rust-analyzer/expandMacro");
+
+export interface MatchingBraceParams {
+    textDocument: lc.TextDocumentIdentifier;
+    positions: lc.Position[];
+}
+export const matchingBrace = new lc.RequestType("experimental/matchingBrace");
+
+export const parentModule = new lc.RequestType("rust-analyzer/parentModule");
+
+export interface JoinLinesParams {
+    textDocument: lc.TextDocumentIdentifier;
+    ranges: lc.Range[];
+}
+export const joinLines = new lc.RequestType("experimental/joinLines");
+
+export const onEnter = new lc.RequestType("experimental/onEnter");
+
+export interface RunnablesParams {
+    textDocument: lc.TextDocumentIdentifier;
+    position: lc.Position | null;
+}
+export interface Runnable {
+    range: lc.Range;
+    label: string;
+    bin: string;
+    args: string[];
+    extraArgs: string[];
+    env: { [key: string]: string };
+    cwd: string | null;
+}
+export const runnables = new lc.RequestType("rust-analyzer/runnables");
+
+export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint | InlayHint.ChainingHint;
+
+export namespace InlayHint {
+    export const enum Kind {
+        TypeHint = "TypeHint",
+        ParamHint = "ParameterHint",
+        ChainingHint = "ChainingHint",
+    }
+    interface Common {
+        range: lc.Range;
+        label: string;
+    }
+    export type TypeHint = Common & { kind: Kind.TypeHint };
+    export type ParamHint = Common & { kind: Kind.ParamHint };
+    export type ChainingHint = Common & { kind: Kind.ChainingHint };
+}
+export interface InlayHintsParams {
+    textDocument: lc.TextDocumentIdentifier;
+}
+export const inlayHints = new lc.RequestType("rust-analyzer/inlayHints");
+
+export interface SsrParams {
+    query: string;
+    parseOnly: boolean;
+}
+export const ssr = new lc.RequestType('experimental/ssr');
diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts
index 8f0487d74a1e..2a7a429cfa66 100644
--- a/editors/code/src/run.ts
+++ b/editors/code/src/run.ts
@@ -1,6 +1,6 @@
 import * as vscode from 'vscode';
 import * as lc from 'vscode-languageclient';
-import * as ra from './rust-analyzer-api';
+import * as ra from './lsp_ext';
 
 import { Ctx, Cmd } from './ctx';
 import { startDebugSession, getDebugConfiguration } from './debug';
diff --git a/editors/code/src/rust-analyzer-api.ts b/editors/code/src/rust-analyzer-api.ts
deleted file mode 100644
index c10c0fa78942..000000000000
--- a/editors/code/src/rust-analyzer-api.ts
+++ /dev/null
@@ -1,123 +0,0 @@
-/**
- * This file mirrors `crates/rust-analyzer/src/req.rs` declarations.
- */
-
-import * as lc from "vscode-languageclient";
-
-type Option = null | T;
-type Vec = T[];
-type FxHashMap = Record;
-
-function request(method: string) {
-    return new lc.RequestType(`rust-analyzer/${method}`);
-}
-function notification(method: string) {
-    return new lc.NotificationType(method);
-}
-
-
-export const analyzerStatus = request("analyzerStatus");
-
-
-export const collectGarbage = request("collectGarbage");
-
-
-export interface SyntaxTreeParams {
-    textDocument: lc.TextDocumentIdentifier;
-    range: Option;
-}
-export const syntaxTree = request("syntaxTree");
-
-
-export interface ExpandMacroParams {
-    textDocument: lc.TextDocumentIdentifier;
-    position: Option;
-}
-export interface ExpandedMacro {
-    name: string;
-    expansion: string;
-}
-export const expandMacro = request>("expandMacro");
-
-
-export interface MatchingBraceParams {
-    textDocument: lc.TextDocumentIdentifier;
-    positions: lc.Position[];
-}
-export const matchingBrace = new lc.RequestType('experimental/matchingBrace');
-
-export interface PublishDecorationsParams {
-    uri: string;
-    decorations: Vec;
-}
-export interface Decoration {
-    range: lc.Range;
-    tag: string;
-    bindingHash: Option;
-}
-export const decorationsRequest = request>("decorationsRequest");
-
-
-export const parentModule = request>("parentModule");
-
-
-export interface JoinLinesParams {
-    textDocument: lc.TextDocumentIdentifier;
-    ranges: lc.Range[];
-}
-export const joinLines = new lc.RequestType('experimental/joinLines');
-
-export const onEnter = new lc.RequestType('experimental/onEnter');
-
-export interface RunnablesParams {
-    textDocument: lc.TextDocumentIdentifier;
-    position: Option;
-}
-export interface Runnable {
-    range: lc.Range;
-    label: string;
-    bin: string;
-    args: Vec;
-    extraArgs: Vec;
-    env: FxHashMap;
-    cwd: Option;
-}
-export const runnables = request>("runnables");
-
-export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint | InlayHint.ChainingHint;
-
-export namespace InlayHint {
-    export const enum Kind {
-        TypeHint = "TypeHint",
-        ParamHint = "ParameterHint",
-        ChainingHint = "ChainingHint",
-    }
-    interface Common {
-        range: lc.Range;
-        label: string;
-    }
-    export type TypeHint = Common & { kind: Kind.TypeHint };
-    export type ParamHint = Common & { kind: Kind.ParamHint };
-    export type ChainingHint = Common & { kind: Kind.ChainingHint };
-}
-export interface InlayHintsParams {
-    textDocument: lc.TextDocumentIdentifier;
-}
-export const inlayHints = request>("inlayHints");
-
-
-export interface SsrParams {
-    query: string;
-    parseOnly: boolean;
-}
-export const ssr = new lc.RequestType('experimental/ssr');
-
-
-export const publishDecorations = notification("publishDecorations");
-
-
-export interface SourceChange {
-    label: string;
-    workspaceEdit: lc.WorkspaceEdit;
-    cursorPosition: Option;
-}

From 0ebb25b29b0988be89f42091fd373ea58d7ff9fb Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Mon, 25 May 2020 15:55:25 +0200
Subject: [PATCH 069/163] Document `parentModule` experimental LSP request

---
 crates/rust-analyzer/src/caps.rs              |  1 +
 crates/rust-analyzer/src/lsp_ext.rs           |  6 +--
 .../rust-analyzer/src/main_loop/handlers.rs   | 32 +++++----------
 crates/rust-analyzer/src/to_proto.rs          | 15 +++++--
 docs/dev/lsp-extensions.md                    | 40 ++++++++++++++++---
 editors/code/src/commands.ts                  |  6 +--
 editors/code/src/lsp_ext.ts                   |  2 +-
 7 files changed, 64 insertions(+), 38 deletions(-)

diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index d55cbb15fe88..345693524ea5 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -86,6 +86,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
             "joinLines": true,
             "ssr": true,
             "onEnter": true,
+            "parentModule": true,
         })),
     }
 }
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index c571c62aed33..acb1dacb6b66 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -3,7 +3,7 @@
 use std::{collections::HashMap, path::PathBuf};
 
 use lsp_types::request::Request;
-use lsp_types::{Location, Position, Range, TextDocumentIdentifier};
+use lsp_types::{Position, Range, TextDocumentIdentifier};
 use rustc_hash::FxHashMap;
 use serde::{Deserialize, Serialize};
 
@@ -79,8 +79,8 @@ pub enum ParentModule {}
 
 impl Request for ParentModule {
     type Params = lsp_types::TextDocumentPositionParams;
-    type Result = Vec;
-    const METHOD: &'static str = "rust-analyzer/parentModule";
+    type Result = Option;
+    const METHOD: &'static str = "experimental/parentModule";
 }
 
 pub enum JoinLines {}
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 3ccc95c23e04..1f910ff82b93 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -344,11 +344,8 @@ pub fn handle_goto_definition(
         None => return Ok(None),
         Some(it) => it,
     };
-    let res = to_proto::goto_definition_response(
-        &world,
-        FileRange { file_id: position.file_id, range: nav_info.range },
-        nav_info.info,
-    )?;
+    let src = FileRange { file_id: position.file_id, range: nav_info.range };
+    let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?;
     Ok(Some(res))
 }
 
@@ -362,11 +359,8 @@ pub fn handle_goto_implementation(
         None => return Ok(None),
         Some(it) => it,
     };
-    let res = to_proto::goto_definition_response(
-        &world,
-        FileRange { file_id: position.file_id, range: nav_info.range },
-        nav_info.info,
-    )?;
+    let src = FileRange { file_id: position.file_id, range: nav_info.range };
+    let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?;
     Ok(Some(res))
 }
 
@@ -380,26 +374,20 @@ pub fn handle_goto_type_definition(
         None => return Ok(None),
         Some(it) => it,
     };
-    let res = to_proto::goto_definition_response(
-        &world,
-        FileRange { file_id: position.file_id, range: nav_info.range },
-        nav_info.info,
-    )?;
+    let src = FileRange { file_id: position.file_id, range: nav_info.range };
+    let res = to_proto::goto_definition_response(&world, Some(src), nav_info.info)?;
     Ok(Some(res))
 }
 
 pub fn handle_parent_module(
     world: WorldSnapshot,
     params: lsp_types::TextDocumentPositionParams,
-) -> Result> {
+) -> Result> {
     let _p = profile("handle_parent_module");
     let position = from_proto::file_position(&world, params)?;
-    world
-        .analysis()
-        .parent_module(position)?
-        .into_iter()
-        .map(|it| to_proto::location(&world, it.file_range()))
-        .collect::>>()
+    let navs = world.analysis().parent_module(position)?;
+    let res = to_proto::goto_definition_response(&world, None, navs)?;
+    Ok(Some(res))
 }
 
 pub fn handle_runnables(
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 39d58f1e01bd..bb7594dbf309 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -403,13 +403,20 @@ pub(crate) fn location(world: &WorldSnapshot, frange: FileRange) -> Result,
     target: NavigationTarget,
 ) -> Result {
-    let src_location = location(world, src)?;
+    let origin_selection_range = match src {
+        Some(src) => {
+            let line_index = world.analysis().file_line_index(src.file_id)?;
+            let range = range(&line_index, src.range);
+            Some(range)
+        }
+        None => None,
+    };
     let (target_uri, target_range, target_selection_range) = location_info(world, target)?;
     let res = lsp_types::LocationLink {
-        origin_selection_range: Some(src_location.range),
+        origin_selection_range,
         target_uri,
         target_range,
         target_selection_range,
@@ -432,7 +439,7 @@ fn location_info(
 
 pub(crate) fn goto_definition_response(
     world: &WorldSnapshot,
-    src: FileRange,
+    src: Option,
     targets: Vec,
 ) -> Result {
     if world.config.client_caps.location_link {
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 48147b17393c..209f470eba50 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -87,6 +87,40 @@ Invoking code action at this position will yield two code actions for importing
 * Is a fixed two-level structure enough?
 * Should we devise a general way to encode custom interaction protocols for GUI refactorings?
 
+## Parent Module
+
+**Issue:** https://github.com/microsoft/language-server-protocol/issues/1002
+
+**Server Capability:** `{ "parentModule": boolean }`
+
+This request is send from client to server to handle "Goto Parent Module" editor action.
+
+**Method:** `experimental/parentModule`
+
+**Request:** `TextDocumentPositionParams`
+
+**Response:** `Location | Location[] | LocationLink[] | null`
+
+
+### Example
+
+```rust
+// src/main.rs
+mod foo;
+// src/foo.rs
+
+/* cursor here*/
+```
+
+`experimental/parentModule` returns a single `Link` to the `mod foo;` declaration.
+
+### Unresolved Question
+
+* An alternative would be to use a more general "gotoSuper" request, which would work for super methods, super classes and super modules.
+  This is the approach IntelliJ Rust is takeing.
+  However, experience shows that super module (which generally has a feeling of navigation between files) should be separate.
+  If you want super module, but the cursor happens to be inside an overriden function, the behavior with single "gotoSuper" request is surprising.
+
 ## Join Lines
 
 **Issue:** https://github.com/microsoft/language-server-protocol/issues/992
@@ -108,11 +142,7 @@ interface JoinLinesParams {
 }
 ```
 
-**Response:**
-
-```typescript
-TextEdit[]
-```
+**Response:** `TextEdit[]`
 
 ### Example
 
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 49e3845d5b17..86302db37c1e 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -138,10 +138,10 @@ export function parentModule(ctx: Ctx): Cmd {
             ),
         });
         const loc = response[0];
-        if (loc == null) return;
+        if (!loc) return;
 
-        const uri = client.protocol2CodeConverter.asUri(loc.uri);
-        const range = client.protocol2CodeConverter.asRange(loc.range);
+        const uri = client.protocol2CodeConverter.asUri(loc.targetUri);
+        const range = client.protocol2CodeConverter.asRange(loc.targetRange);
 
         const doc = await vscode.workspace.openTextDocument(uri);
         const e = await vscode.window.showTextDocument(doc);
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index 2a06632619b5..4da12eb3092a 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -31,7 +31,7 @@ export interface MatchingBraceParams {
 }
 export const matchingBrace = new lc.RequestType("experimental/matchingBrace");
 
-export const parentModule = new lc.RequestType("rust-analyzer/parentModule");
+export const parentModule = new lc.RequestType("experimental/parentModule");
 
 export interface JoinLinesParams {
     textDocument: lc.TextDocumentIdentifier;

From 5e66045a21aa575b2712eee56e7ebdd90965260f Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Mon, 25 May 2020 16:26:01 +0200
Subject: [PATCH 070/163] Try fixing release tags

closes #4319
---
 .github/actions/github-release/main.js | 2 +-
 .github/workflows/release.yaml         | 3 +++
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/.github/actions/github-release/main.js b/.github/actions/github-release/main.js
index 2c7eedbe290a..b499cd0fd837 100644
--- a/.github/actions/github-release/main.js
+++ b/.github/actions/github-release/main.js
@@ -16,7 +16,7 @@ async function runOnce() {
   const slug = process.env.GITHUB_REPOSITORY;
   const owner = slug.split('/')[0];
   const repo = slug.split('/')[1];
-  const sha = process.env.GITHUB_SHA;
+  const sha = process.env.HEAD_SHA;
 
   core.info(`files: ${files}`);
   core.info(`name: ${name}`);
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 29ac895493ac..11c6f0ac3f5f 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -88,6 +88,9 @@ jobs:
     - name: Checkout repository
       uses: actions/checkout@v2
 
+    - run: echo "::set-env name=HEAD_SHA::$(git rev-parse HEAD)"
+    - run: 'echo "HEAD_SHA: $HEAD_SHA"'
+
     - uses: actions/download-artifact@v1
       with:
         name: dist-macos-latest

From 3c2c8d7e5f7887351bf8e475f441609527679e5f Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Mon, 25 May 2020 16:31:19 +0200
Subject: [PATCH 071/163] Fix conflict between nightly branch and tag

---
 .github/workflows/release.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 11c6f0ac3f5f..1ae8ed1b63ef 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -6,7 +6,7 @@ on:
   push:
     branches:
     - release
-    - nightly
+    - trigger-nightly
 
 env:
   CARGO_INCREMENTAL: 0

From 6c676909dd94f48ab1e85427071b508f104f360f Mon Sep 17 00:00:00 2001
From: Yuki Kodama 
Date: Sun, 24 May 2020 18:09:42 +0900
Subject: [PATCH 072/163] Strip leading underscore

---
 crates/ra_ide/src/display/function_signature.rs | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
index 9572debd822c..4f054398a94e 100644
--- a/crates/ra_ide/src/display/function_signature.rs
+++ b/crates/ra_ide/src/display/function_signature.rs
@@ -187,6 +187,14 @@ impl FunctionSignature {
 
 impl From<&'_ ast::FnDef> for FunctionSignature {
     fn from(node: &ast::FnDef) -> FunctionSignature {
+        fn strip_leading_underscore(name: String) -> String {
+            if name.starts_with("_") {
+                name.get(1..).unwrap_or_default().to_string()
+            } else {
+                name
+            }
+        }
+
         fn param_list(node: &ast::FnDef) -> (bool, Vec, Vec) {
             let mut res = vec![];
             let mut res_types = vec![];
@@ -230,7 +238,7 @@ impl From<&'_ ast::FnDef> for FunctionSignature {
                     param_list
                         .params()
                         .map(|param| {
-                            Some(
+                            Some(strip_leading_underscore(
                                 param
                                     .pat()?
                                     .syntax()
@@ -238,7 +246,7 @@ impl From<&'_ ast::FnDef> for FunctionSignature {
                                     .find_map(ast::Name::cast)?
                                     .text()
                                     .to_string(),
-                            )
+                            ))
                         })
                         .map(|param| param.unwrap_or_default()),
                 );

From 4d13691ad192c20b2fb0f349582622cb5230da99 Mon Sep 17 00:00:00 2001
From: Yuki Kodama 
Date: Sun, 24 May 2020 22:39:09 +0900
Subject: [PATCH 073/163] Reflect test case

---
 crates/ra_ide/src/completion/presentation.rs | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 440ffa31d4e0..6b29f58d5158 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -641,7 +641,7 @@ mod tests {
         assert_debug_snapshot!(
             do_reference_completion(
                 r"
-                fn with_args(x: i32, y: String) {}
+                fn with_args(x: i32, y: String, _z: bool) {}
                 fn main() { with_<|> }
                 "
             ),
@@ -649,8 +649,8 @@ mod tests {
         [
             CompletionItem {
                 label: "main()",
-                source_range: 80..85,
-                delete: 80..85,
+                source_range: 90..95,
+                delete: 90..95,
                 insert: "main()$0",
                 kind: Function,
                 lookup: "main",
@@ -658,12 +658,12 @@ mod tests {
             },
             CompletionItem {
                 label: "with_args(…)",
-                source_range: 80..85,
-                delete: 80..85,
-                insert: "with_args(${1:x}, ${2:y})$0",
+                source_range: 90..95,
+                delete: 90..95,
+                insert: "with_args(${1:x}, ${2:y}, ${3:z})$0",
                 kind: Function,
                 lookup: "with_args",
-                detail: "fn with_args(x: i32, y: String)",
+                detail: "fn with_args(x: i32, y: String, _z: bool)",
                 trigger_call_info: true,
             },
         ]

From 378bfc3c8f1c63f44f972e11c9de00f9733b13c0 Mon Sep 17 00:00:00 2001
From: Yuki Kodama 
Date: Sun, 24 May 2020 23:01:26 +0900
Subject: [PATCH 074/163] Separate assertions

---
 crates/ra_ide/src/completion/presentation.rs | 72 ++++++++++++++++++--
 1 file changed, 65 insertions(+), 7 deletions(-)

diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 6b29f58d5158..5708ff2913c4 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -641,7 +641,7 @@ mod tests {
         assert_debug_snapshot!(
             do_reference_completion(
                 r"
-                fn with_args(x: i32, y: String, _z: bool) {}
+                fn with_args(x: i32, y: String) {}
                 fn main() { with_<|> }
                 "
             ),
@@ -649,8 +649,8 @@ mod tests {
         [
             CompletionItem {
                 label: "main()",
-                source_range: 90..95,
-                delete: 90..95,
+                source_range: 80..85,
+                delete: 80..85,
                 insert: "main()$0",
                 kind: Function,
                 lookup: "main",
@@ -658,12 +658,43 @@ mod tests {
             },
             CompletionItem {
                 label: "with_args(…)",
-                source_range: 90..95,
-                delete: 90..95,
-                insert: "with_args(${1:x}, ${2:y}, ${3:z})$0",
+                source_range: 80..85,
+                delete: 80..85,
+                insert: "with_args(${1:x}, ${2:y})$0",
                 kind: Function,
                 lookup: "with_args",
-                detail: "fn with_args(x: i32, y: String, _z: bool)",
+                detail: "fn with_args(x: i32, y: String)",
+                trigger_call_info: true,
+            },
+        ]
+        "###
+        );
+        assert_debug_snapshot!(
+            do_reference_completion(
+                r"
+                fn with_ignored_args(_foo: i32, b_a_r_: bool) {}
+                fn main() { with_<|> }
+                "
+            ),
+            @r###"
+        [
+            CompletionItem {
+                label: "main()",
+                source_range: 94..99,
+                delete: 94..99,
+                insert: "main()$0",
+                kind: Function,
+                lookup: "main",
+                detail: "fn main()",
+            },
+            CompletionItem {
+                label: "with_ignored_args(…)",
+                source_range: 94..99,
+                delete: 94..99,
+                insert: "with_ignored_args(${1:foo}, ${2:b_a_r_})$0",
+                kind: Function,
+                lookup: "with_ignored_args",
+                detail: "fn with_ignored_args(_foo: i32, b_a_r_: bool)",
                 trigger_call_info: true,
             },
         ]
@@ -695,6 +726,33 @@ mod tests {
         ]
         "###
         );
+        assert_debug_snapshot!(
+            do_reference_completion(
+                r"
+                struct S {}
+                impl S {
+                    fn foo_ignored_args(&self, _a: bool, b: i32) {}
+                }
+                fn bar(s: &S) {
+                    s.f<|>
+                }
+                "
+            ),
+            @r###"
+        [
+            CompletionItem {
+                label: "foo_ignored_args(…)",
+                source_range: 194..195,
+                delete: 194..195,
+                insert: "foo_ignored_args(${1:a}, ${2:b})$0",
+                kind: Method,
+                lookup: "foo_ignored_args",
+                detail: "fn foo_ignored_args(&self, _a: bool, b: i32)",
+                trigger_call_info: true,
+            },
+        ]
+        "###
+        );
     }
 
     #[test]

From 408d04764cd00a9f24fce15198ec26d7c74fb4d6 Mon Sep 17 00:00:00 2001
From: Yuki Kodama 
Date: Sun, 24 May 2020 23:43:44 +0900
Subject: [PATCH 075/163] Use built-in method

---
 crates/ra_ide/src/display/function_signature.rs | 16 +++++-----------
 1 file changed, 5 insertions(+), 11 deletions(-)

diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
index 4f054398a94e..fabc78f14088 100644
--- a/crates/ra_ide/src/display/function_signature.rs
+++ b/crates/ra_ide/src/display/function_signature.rs
@@ -187,14 +187,6 @@ impl FunctionSignature {
 
 impl From<&'_ ast::FnDef> for FunctionSignature {
     fn from(node: &ast::FnDef) -> FunctionSignature {
-        fn strip_leading_underscore(name: String) -> String {
-            if name.starts_with("_") {
-                name.get(1..).unwrap_or_default().to_string()
-            } else {
-                name
-            }
-        }
-
         fn param_list(node: &ast::FnDef) -> (bool, Vec, Vec) {
             let mut res = vec![];
             let mut res_types = vec![];
@@ -238,15 +230,17 @@ impl From<&'_ ast::FnDef> for FunctionSignature {
                     param_list
                         .params()
                         .map(|param| {
-                            Some(strip_leading_underscore(
+                            Some(
                                 param
                                     .pat()?
                                     .syntax()
                                     .descendants()
                                     .find_map(ast::Name::cast)?
                                     .text()
-                                    .to_string(),
-                            ))
+                                    .to_string()
+                                    .trim_start_matches('_')
+                                    .into(),
+                            )
                         })
                         .map(|param| param.unwrap_or_default()),
                 );

From 41d0f7f24ef924440ee710e134c01d6e47056c3f Mon Sep 17 00:00:00 2001
From: Yuki Kodama 
Date: Mon, 25 May 2020 00:14:56 +0900
Subject: [PATCH 076/163] Fix test to consider multiple underscores

---
 crates/ra_ide/src/completion/presentation.rs | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 5708ff2913c4..e59f730ff465 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -672,7 +672,7 @@ mod tests {
         assert_debug_snapshot!(
             do_reference_completion(
                 r"
-                fn with_ignored_args(_foo: i32, b_a_r_: bool) {}
+                fn with_ignored_args(_foo: i32, ___bar: bool, ho_ge_: String) {}
                 fn main() { with_<|> }
                 "
             ),
@@ -680,8 +680,8 @@ mod tests {
         [
             CompletionItem {
                 label: "main()",
-                source_range: 94..99,
-                delete: 94..99,
+                source_range: 110..115,
+                delete: 110..115,
                 insert: "main()$0",
                 kind: Function,
                 lookup: "main",
@@ -689,12 +689,12 @@ mod tests {
             },
             CompletionItem {
                 label: "with_ignored_args(…)",
-                source_range: 94..99,
-                delete: 94..99,
-                insert: "with_ignored_args(${1:foo}, ${2:b_a_r_})$0",
+                source_range: 110..115,
+                delete: 110..115,
+                insert: "with_ignored_args(${1:foo}, ${2:bar}, ${3:ho_ge_})$0",
                 kind: Function,
                 lookup: "with_ignored_args",
-                detail: "fn with_ignored_args(_foo: i32, b_a_r_: bool)",
+                detail: "fn with_ignored_args(_foo: i32, ___bar: bool, ho_ge_: String)",
                 trigger_call_info: true,
             },
         ]

From fd83f469e9643c7a9da2d9e4c796bd89c441458d Mon Sep 17 00:00:00 2001
From: Yuki Kodama 
Date: Tue, 26 May 2020 01:05:50 +0900
Subject: [PATCH 077/163] Trim at presentation layer

---
 crates/ra_ide/src/completion/presentation.rs    | 2 +-
 crates/ra_ide/src/display/function_signature.rs | 4 +---
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index e59f730ff465..61565c84fe2b 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -211,7 +211,7 @@ impl Completions {
             .parameter_names
             .iter()
             .skip(if function_signature.has_self_param { 1 } else { 0 })
-            .cloned()
+            .map(|name| name.trim_start_matches('_').into())
             .collect();
 
         builder = builder.add_call_parens(ctx, name, Params::Named(params));
diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs
index fabc78f14088..9572debd822c 100644
--- a/crates/ra_ide/src/display/function_signature.rs
+++ b/crates/ra_ide/src/display/function_signature.rs
@@ -237,9 +237,7 @@ impl From<&'_ ast::FnDef> for FunctionSignature {
                                     .descendants()
                                     .find_map(ast::Name::cast)?
                                     .text()
-                                    .to_string()
-                                    .trim_start_matches('_')
-                                    .into(),
+                                    .to_string(),
                             )
                         })
                         .map(|param| param.unwrap_or_default()),

From a5cc9a8a9ba1e6a0fc281e149881abdd3bd075c1 Mon Sep 17 00:00:00 2001
From: Jeremy Kolb 
Date: Mon, 25 May 2020 13:35:52 -0400
Subject: [PATCH 078/163] Fix some clippy perf warnings

---
 crates/ra_db/src/fixture.rs            | 4 ++--
 crates/ra_hir_def/src/lang_item.rs     | 2 +-
 crates/ra_hir_ty/src/traits/builtin.rs | 3 +--
 crates/ra_ide/src/ssr.rs               | 8 ++++----
 4 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs
index f6b50c67cf20..482a2f3e651e 100644
--- a/crates/ra_db/src/fixture.rs
+++ b/crates/ra_db/src/fixture.rs
@@ -267,8 +267,8 @@ impl From<&FixtureMeta> for ParsedMeta {
                 ParsedMeta::Root { path: path.to_owned() }
             }
             FixtureMeta::File(f) => Self::File(FileMeta {
-                path: f.path.to_owned().into(),
-                krate: f.crate_name.to_owned().into(),
+                path: f.path.to_owned(),
+                krate: f.crate_name.to_owned(),
                 deps: f.deps.to_owned(),
                 cfg: f.cfg.to_owned(),
                 edition: f
diff --git a/crates/ra_hir_def/src/lang_item.rs b/crates/ra_hir_def/src/lang_item.rs
index d962db3cc792..3516784b8d8d 100644
--- a/crates/ra_hir_def/src/lang_item.rs
+++ b/crates/ra_hir_def/src/lang_item.rs
@@ -164,7 +164,7 @@ impl LangItems {
         T: Into + Copy,
     {
         if let Some(lang_item_name) = lang_attr(db, item) {
-            self.items.entry(lang_item_name.clone()).or_insert_with(|| constructor(item));
+            self.items.entry(lang_item_name).or_insert_with(|| constructor(item));
         }
     }
 }
diff --git a/crates/ra_hir_ty/src/traits/builtin.rs b/crates/ra_hir_ty/src/traits/builtin.rs
index ccab246bfa71..88a422d2cb48 100644
--- a/crates/ra_hir_ty/src/traits/builtin.rs
+++ b/crates/ra_hir_ty/src/traits/builtin.rs
@@ -290,8 +290,7 @@ fn trait_object_unsize_impl_datum(
     let self_trait_ref = TraitRef { trait_, substs: self_substs };
     let where_clauses = vec![GenericPredicate::Implemented(self_trait_ref)];
 
-    let impl_substs =
-        Substs::builder(2).push(self_ty).push(Ty::Dyn(target_bounds.clone().into())).build();
+    let impl_substs = Substs::builder(2).push(self_ty).push(Ty::Dyn(target_bounds.into())).build();
 
     let trait_ref = TraitRef { trait_: unsize_trait, substs: impl_substs };
 
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 1873d1d0d0ab..130d3b4c3ba1 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -196,10 +196,10 @@ fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches {
     ) -> Option {
         let match_ = check_opt_nodes(pattern.path(), code.path(), placeholders, match_)?;
 
-        let mut pattern_fields =
-            pattern.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]);
-        let mut code_fields =
-            code.record_field_list().map(|x| x.fields().collect()).unwrap_or(vec![]);
+        let mut pattern_fields: Vec =
+            pattern.record_field_list().map(|x| x.fields().collect()).unwrap_or_default();
+        let mut code_fields: Vec =
+            code.record_field_list().map(|x| x.fields().collect()).unwrap_or_default();
 
         if pattern_fields.len() != code_fields.len() {
             return None;

From 1e823d6770385cece06ac8c2ee526cb31f4d4ea0 Mon Sep 17 00:00:00 2001
From: Yuki Kodama 
Date: Tue, 26 May 2020 02:36:34 +0900
Subject: [PATCH 079/163] Add question mark operator

---
 editors/code/rust.tmGrammar.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/editors/code/rust.tmGrammar.json b/editors/code/rust.tmGrammar.json
index cdcd557dc173..ab87cd39f608 100644
--- a/editors/code/rust.tmGrammar.json
+++ b/editors/code/rust.tmGrammar.json
@@ -207,7 +207,7 @@
 		{
 			"comment": "Miscellaneous operator",
 			"name": "keyword.operator.misc.rust",
-			"match": "(=>|::)"
+			"match": "(=>|::|\\?)"
 		},
 		{
 			"comment": "Comparison operator",

From a047f10839d64813213c70ed60c37c0a99f232b2 Mon Sep 17 00:00:00 2001
From: Aaron Loucks 
Date: Mon, 25 May 2020 23:18:45 -0400
Subject: [PATCH 080/163] Hover tooltip module name is monospace once again

The line separator is moved below the function signature to split
regions between the docs. This is very similar to how IntelliJ
displays tooltips. Adding an additional separator between the module
name and function signature currently has rendering issues.

Fixes #4594
Alternative to #4615
---
 crates/ra_ide/src/display.rs                   |  3 ++-
 crates/ra_ide/src/hover.rs                     | 16 +++++++++-------
 crates/rust-analyzer/tests/heavy_tests/main.rs |  2 +-
 3 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/crates/ra_ide/src/display.rs b/crates/ra_ide/src/display.rs
index 8bb312156878..827c094e7163 100644
--- a/crates/ra_ide/src/display.rs
+++ b/crates/ra_ide/src/display.rs
@@ -83,12 +83,13 @@ pub(crate) fn rust_code_markup_with_doc(
 
     if let Some(mod_path) = mod_path {
         if !mod_path.is_empty() {
-            format_to!(buf, "{}\n___\n\n", mod_path);
+            format_to!(buf, "```rust\n{}\n```\n\n", mod_path);
         }
     }
     format_to!(buf, "```rust\n{}\n```", code);
 
     if let Some(doc) = doc {
+        format_to!(buf, "\n___");
         format_to!(buf, "\n\n{}", doc);
     }
 
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 1f4f6b848021..3e721dcca392 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -405,7 +405,7 @@ mod tests {
                 };
             }
         "#,
-            &["Foo\n___\n\n```rust\nfield_a: u32"],
+            &["Foo\n```\n\n```rust\nfield_a: u32"],
         );
 
         // Hovering over the field in the definition
@@ -422,7 +422,7 @@ mod tests {
                 };
             }
         "#,
-            &["Foo\n___\n\n```rust\nfield_a: u32"],
+            &["Foo\n```\n\n```rust\nfield_a: u32"],
         );
     }
 
@@ -475,7 +475,7 @@ fn main() {
             ",
         );
         let hover = analysis.hover(position).unwrap().unwrap();
-        assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\n___\n\n```rust\nSome"));
+        assert_eq!(trim_markup_opt(hover.info.first()), Some("Option\n```\n\n```rust\nSome"));
 
         let (analysis, position) = single_file_with_position(
             "
@@ -503,11 +503,12 @@ fn main() {
         "#,
             &["
 Option
-___
+```
 
 ```rust
 None
 ```
+___
 
 The None variant
             "
@@ -527,11 +528,12 @@ The None variant
         "#,
             &["
 Option
-___
+```
 
 ```rust
 Some
 ```
+___
 
 The Some variant
             "
@@ -614,7 +616,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
         let hover = analysis.hover(position).unwrap().unwrap();
         assert_eq!(
             trim_markup_opt(hover.info.first()),
-            Some("wrapper::Thing\n___\n\n```rust\nfn new() -> Thing")
+            Some("wrapper::Thing\n```\n\n```rust\nfn new() -> Thing")
         );
     }
 
@@ -891,7 +893,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
                 fo<|>o();
             }
             ",
-            &["fn foo()\n```\n\n<- `\u{3000}` here"],
+            &["fn foo()\n```\n___\n\n<- `\u{3000}` here"],
         );
     }
 
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index b1bfc968a8dc..405ddb362969 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -756,5 +756,5 @@ pub fn foo(_input: TokenStream) -> TokenStream {
     });
 
     let value = res.get("contents").unwrap().get("value").unwrap().to_string();
-    assert_eq!(value, r#""foo::Bar\n___\n\n```rust\nfn bar()\n```""#)
+    assert_eq!(value, r#""```rust\nfoo::Bar\n```\n\n```rust\nfn bar()\n```""#)
 }

From bee4f8f9fee56bb00b462cdf4ad3ed317fed682f Mon Sep 17 00:00:00 2001
From: kjeremy 
Date: Tue, 26 May 2020 14:12:13 -0400
Subject: [PATCH 081/163] Pass trivially copy types as copy

---
 crates/ra_hir/src/code_model.rs               | 18 +++---
 crates/ra_hir_def/src/nameres/raw.rs          |  2 +-
 crates/ra_hir_expand/src/proc_macro.rs        |  2 +-
 .../src/completion/complete_trait_impl.rs     | 61 +++++++++----------
 4 files changed, 40 insertions(+), 43 deletions(-)

diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 840cfdfc8238..8d7937d947a4 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -532,7 +532,7 @@ impl Adt {
         Some(self.module(db).krate())
     }
 
-    pub fn name(&self, db: &dyn HirDatabase) -> Name {
+    pub fn name(self, db: &dyn HirDatabase) -> Name {
         match self {
             Adt::Struct(s) => s.name(db),
             Adt::Union(u) => u.name(db),
@@ -1018,15 +1018,15 @@ impl ImplDef {
         impls.lookup_impl_defs_for_trait(trait_.id).map(Self::from).collect()
     }
 
-    pub fn target_trait(&self, db: &dyn HirDatabase) -> Option {
+    pub fn target_trait(self, db: &dyn HirDatabase) -> Option {
         db.impl_data(self.id).target_trait.clone()
     }
 
-    pub fn target_type(&self, db: &dyn HirDatabase) -> TypeRef {
+    pub fn target_type(self, db: &dyn HirDatabase) -> TypeRef {
         db.impl_data(self.id).target_type.clone()
     }
 
-    pub fn target_ty(&self, db: &dyn HirDatabase) -> Type {
+    pub fn target_ty(self, db: &dyn HirDatabase) -> Type {
         let impl_data = db.impl_data(self.id);
         let resolver = self.id.resolver(db.upcast());
         let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
@@ -1038,23 +1038,23 @@ impl ImplDef {
         }
     }
 
-    pub fn items(&self, db: &dyn HirDatabase) -> Vec {
+    pub fn items(self, db: &dyn HirDatabase) -> Vec {
         db.impl_data(self.id).items.iter().map(|it| (*it).into()).collect()
     }
 
-    pub fn is_negative(&self, db: &dyn HirDatabase) -> bool {
+    pub fn is_negative(self, db: &dyn HirDatabase) -> bool {
         db.impl_data(self.id).is_negative
     }
 
-    pub fn module(&self, db: &dyn HirDatabase) -> Module {
+    pub fn module(self, db: &dyn HirDatabase) -> Module {
         self.id.lookup(db.upcast()).container.module(db.upcast()).into()
     }
 
-    pub fn krate(&self, db: &dyn HirDatabase) -> Crate {
+    pub fn krate(self, db: &dyn HirDatabase) -> Crate {
         Crate { id: self.module(db).id.krate }
     }
 
-    pub fn is_builtin_derive(&self, db: &dyn HirDatabase) -> Option> {
+    pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option> {
         let src = self.source(db);
         let item = src.file_id.is_builtin_derive(db.upcast())?;
         let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id);
diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs
index 4e628b14d921..f44baa57942f 100644
--- a/crates/ra_hir_def/src/nameres/raw.rs
+++ b/crates/ra_hir_def/src/nameres/raw.rs
@@ -175,7 +175,7 @@ pub(super) enum DefKind {
 }
 
 impl DefKind {
-    pub fn ast_id(&self) -> FileAstId {
+    pub fn ast_id(self) -> FileAstId {
         match self {
             DefKind::Function(it) => it.upcast(),
             DefKind::Struct(it, _) => it.upcast(),
diff --git a/crates/ra_hir_expand/src/proc_macro.rs b/crates/ra_hir_expand/src/proc_macro.rs
index 4e0e069c8c27..04c0260046bc 100644
--- a/crates/ra_hir_expand/src/proc_macro.rs
+++ b/crates/ra_hir_expand/src/proc_macro.rs
@@ -25,7 +25,7 @@ impl ProcMacroExpander {
     }
 
     pub fn expand(
-        &self,
+        self,
         db: &dyn AstDatabase,
         _id: LazyMacroId,
         tt: &tt::Subtree,
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index 039df03e0547..21c9316e6662 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -49,56 +49,53 @@ use crate::{
 pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
     if let Some((trigger, impl_def)) = completion_match(ctx) {
         match trigger.kind() {
-            SyntaxKind::NAME_REF => {
-                get_missing_assoc_items(&ctx.sema, &impl_def).iter().for_each(|item| match item {
+            SyntaxKind::NAME_REF => get_missing_assoc_items(&ctx.sema, &impl_def)
+                .into_iter()
+                .for_each(|item| match item {
                     hir::AssocItem::Function(fn_item) => {
-                        add_function_impl(&trigger, acc, ctx, &fn_item)
+                        add_function_impl(&trigger, acc, ctx, fn_item)
                     }
                     hir::AssocItem::TypeAlias(type_item) => {
-                        add_type_alias_impl(&trigger, acc, ctx, &type_item)
+                        add_type_alias_impl(&trigger, acc, ctx, type_item)
                     }
                     hir::AssocItem::Const(const_item) => {
-                        add_const_impl(&trigger, acc, ctx, &const_item)
+                        add_const_impl(&trigger, acc, ctx, const_item)
                     }
-                })
-            }
+                }),
 
             SyntaxKind::FN_DEF => {
-                for missing_fn in
-                    get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| {
-                        match item {
-                            hir::AssocItem::Function(fn_item) => Some(fn_item),
-                            _ => None,
-                        }
+                for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
+                    .into_iter()
+                    .filter_map(|item| match item {
+                        hir::AssocItem::Function(fn_item) => Some(fn_item),
+                        _ => None,
                     })
                 {
-                    add_function_impl(&trigger, acc, ctx, &missing_fn);
+                    add_function_impl(&trigger, acc, ctx, missing_fn);
                 }
             }
 
             SyntaxKind::TYPE_ALIAS_DEF => {
-                for missing_fn in
-                    get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| {
-                        match item {
-                            hir::AssocItem::TypeAlias(type_item) => Some(type_item),
-                            _ => None,
-                        }
+                for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
+                    .into_iter()
+                    .filter_map(|item| match item {
+                        hir::AssocItem::TypeAlias(type_item) => Some(type_item),
+                        _ => None,
                     })
                 {
-                    add_type_alias_impl(&trigger, acc, ctx, &missing_fn);
+                    add_type_alias_impl(&trigger, acc, ctx, missing_fn);
                 }
             }
 
             SyntaxKind::CONST_DEF => {
-                for missing_fn in
-                    get_missing_assoc_items(&ctx.sema, &impl_def).iter().filter_map(|item| {
-                        match item {
-                            hir::AssocItem::Const(const_item) => Some(const_item),
-                            _ => None,
-                        }
+                for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
+                    .into_iter()
+                    .filter_map(|item| match item {
+                        hir::AssocItem::Const(const_item) => Some(const_item),
+                        _ => None,
                     })
                 {
-                    add_const_impl(&trigger, acc, ctx, &missing_fn);
+                    add_const_impl(&trigger, acc, ctx, missing_fn);
                 }
             }
 
@@ -126,9 +123,9 @@ fn add_function_impl(
     fn_def_node: &SyntaxNode,
     acc: &mut Completions,
     ctx: &CompletionContext,
-    func: &hir::Function,
+    func: hir::Function,
 ) {
-    let signature = FunctionSignature::from_hir(ctx.db, *func);
+    let signature = FunctionSignature::from_hir(ctx.db, func);
 
     let fn_name = func.name(ctx.db).to_string();
 
@@ -167,7 +164,7 @@ fn add_type_alias_impl(
     type_def_node: &SyntaxNode,
     acc: &mut Completions,
     ctx: &CompletionContext,
-    type_alias: &hir::TypeAlias,
+    type_alias: hir::TypeAlias,
 ) {
     let alias_name = type_alias.name(ctx.db).to_string();
 
@@ -187,7 +184,7 @@ fn add_const_impl(
     const_def_node: &SyntaxNode,
     acc: &mut Completions,
     ctx: &CompletionContext,
-    const_: &hir::Const,
+    const_: hir::Const,
 ) {
     let const_name = const_.name(ctx.db).map(|n| n.to_string());
 

From 154e37684298d8775e7733b20d47eddaa04d883d Mon Sep 17 00:00:00 2001
From: kjeremy 
Date: Tue, 26 May 2020 14:32:56 -0400
Subject: [PATCH 082/163] Update crates

---
 Cargo.lock | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 1f4c0cb5345e..b3748b7248c1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -610,9 +610,9 @@ checksum = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
 
 [[package]]
 name = "libc"
-version = "0.2.70"
+version = "0.2.71"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f"
+checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
 
 [[package]]
 name = "libloading"
@@ -830,9 +830,9 @@ dependencies = [
 
 [[package]]
 name = "paste"
-version = "0.1.13"
+version = "0.1.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "678f27e19361472a23717f11d229a7522ef64605baf0715c896a94b8b6b13a06"
+checksum = "3431e8f72b90f8a7af91dec890d9814000cb371258e0ec7370d93e085361f531"
 dependencies = [
  "paste-impl",
  "proc-macro-hack",
@@ -840,9 +840,9 @@ dependencies = [
 
 [[package]]
 name = "paste-impl"
-version = "0.1.13"
+version = "0.1.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "149089128a45d8e377677b08873b4bad2a56618f80e4f28a83862ba250994a30"
+checksum = "25af5fc872ba284d8d84608bf8a0fa9b5376c96c23f503b007dfd9e34dde5606"
 dependencies = [
  "proc-macro-hack",
  "proc-macro2",
@@ -886,9 +886,9 @@ checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
 
 [[package]]
 name = "proc-macro-hack"
-version = "0.5.15"
+version = "0.5.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63"
+checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
 
 [[package]]
 name = "proc-macro2"
@@ -1598,9 +1598,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f"
 
 [[package]]
 name = "syn"
-version = "1.0.23"
+version = "1.0.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95b5f192649e48a5302a13f2feb224df883b98933222369e4b3b0fe2a5447269"
+checksum = "f14a640819f79b72a710c0be059dce779f9339ae046c8bef12c361d56702146f"
 dependencies = [
  "proc-macro2",
  "quote",

From 9a0353a81e9288bfa9f16393391bf4e160851c36 Mon Sep 17 00:00:00 2001
From: kjeremy 
Date: Tue, 26 May 2020 14:35:09 -0400
Subject: [PATCH 083/163] Update lexer

---
 Cargo.lock                  | 4 ++--
 crates/ra_syntax/Cargo.toml | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index b3748b7248c1..6f3d1f5645b0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1401,9 +1401,9 @@ dependencies = [
 
 [[package]]
 name = "rustc-ap-rustc_lexer"
-version = "660.0.0"
+version = "661.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30760dbcc7667c9e0da561e980e24867ca7f4526ce060a3d7e6d9dcfeaae88d1"
+checksum = "a6d88abd7c634b52557e46fc7ba47644f0cbe45c358c33f51c532d60d1da239e"
 dependencies = [
  "unicode-xid",
 ]
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml
index a9a5cc7bc6ed..a8ff2e74f5c6 100644
--- a/crates/ra_syntax/Cargo.toml
+++ b/crates/ra_syntax/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
 [dependencies]
 itertools = "0.9.0"
 rowan = "0.10.0"
-rustc_lexer = { version = "660.0.0", package = "rustc-ap-rustc_lexer" }
+rustc_lexer = { version = "661.0.0", package = "rustc-ap-rustc_lexer" }
 rustc-hash = "1.1.0"
 arrayvec = "0.5.1"
 once_cell = "1.3.1"

From 3c749b6224282e18db264b1e2e6f19f14b5c0a26 Mon Sep 17 00:00:00 2001
From: veetaha 
Date: Tue, 26 May 2020 22:51:04 +0300
Subject: [PATCH 084/163] Partially fix displaying inlay hints in Github PR
 diff views

---
 editors/code/src/util.ts | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts
index 127a9e911249..793c481fb686 100644
--- a/editors/code/src/util.ts
+++ b/editors/code/src/util.ts
@@ -74,10 +74,11 @@ export type RustDocument = vscode.TextDocument & { languageId: "rust" };
 export type RustEditor = vscode.TextEditor & { document: RustDocument };
 
 export function isRustDocument(document: vscode.TextDocument): document is RustDocument {
-    return document.languageId === 'rust'
-        // SCM diff views have the same URI as the on-disk document but not the same content
-        && document.uri.scheme !== 'git'
-        && document.uri.scheme !== 'svn';
+    // Prevent corrupted text (particularly via inlay hints) in diff views
+    // by allowing only `file` schemes
+    // unfortunately extensions that use diff views not always set this
+    // to something different than 'file' (see ongoing bug: #4608)
+    return document.languageId === 'rust' && document.uri.scheme === 'file';
 }
 
 export function isRustEditor(editor: vscode.TextEditor): editor is RustEditor {

From 36f97d39a461ae52489428867704590b78cd3257 Mon Sep 17 00:00:00 2001
From: Andrew Chin 
Date: Tue, 26 May 2020 18:45:14 -0400
Subject: [PATCH 085/163] Fix the `should_panic` snippet

Closes #4628
---
 crates/ra_ide/src/completion/complete_attribute.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/crates/ra_ide/src/completion/complete_attribute.rs b/crates/ra_ide/src/completion/complete_attribute.rs
index f17266221fd6..fb3f0b743ef2 100644
--- a/crates/ra_ide/src/completion/complete_attribute.rs
+++ b/crates/ra_ide/src/completion/complete_attribute.rs
@@ -112,7 +112,7 @@ const ATTRIBUTES: &[AttrCompletion] = &[
     AttrCompletion { label: "repr", snippet: Some("repr(${0:C})"), should_be_inner: false },
     AttrCompletion {
         label: "should_panic",
-        snippet: Some(r#"expected = "${0:reason}""#),
+        snippet: Some(r#"should_panic(expected = "${0:reason}")"#),
         should_be_inner: false,
     },
     AttrCompletion {
@@ -571,7 +571,7 @@ mod tests {
                 label: "should_panic",
                 source_range: 19..19,
                 delete: 19..19,
-                insert: "expected = \"${0:reason}\"",
+                insert: "should_panic(expected = \"${0:reason}\")",
                 kind: Attribute,
             },
             CompletionItem {
@@ -810,7 +810,7 @@ mod tests {
                 label: "should_panic",
                 source_range: 20..20,
                 delete: 20..20,
-                insert: "expected = \"${0:reason}\"",
+                insert: "should_panic(expected = \"${0:reason}\")",
                 kind: Attribute,
             },
             CompletionItem {

From bb415c1818fe75badbd70a87a4ae23923a3d3984 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Wed, 27 May 2020 12:20:47 +0200
Subject: [PATCH 086/163] Document inlay hints and runnables

We want to change those, but let's document what we have in meantime
---
 docs/dev/lsp-extensions.md | 63 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 209f470eba50..c898f3e93a8b 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -362,3 +362,66 @@ interface ExpandedMacro {
 ```
 
 Expands macro call at a given position.
+
+## Inlay Hints
+
+**Method:** `rust-analyzer/inlayHints`
+
+This request is send from client to server to render "inlay hints" -- virtual text inserted into editor to show things like inferred types.
+Generally, the client should re-query inlay hints after every modification.
+Note that we plan to move this request to `experimental/inlayHints`, as it is not really Rust-specific, but the current API is not necessary the right one.
+Upstream issue: https://github.com/microsoft/language-server-protocol/issues/956
+
+**Request:**
+
+```typescript
+interface InlayHintsParams {
+    textDocument: TextDocumentIdentifier,
+}
+```
+
+**Response:** `InlayHint[]`
+
+```typescript
+interface InlayHint {
+    kind: "TypeHint" | "ParameterHint" | "ChainingHints",
+    range: Range,
+    label: string,
+}
+```
+
+## Runnables
+
+**Method:** `rust-analyzer/runnables`
+
+This request is send from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`).
+Note that we plan to move this request to `experimental/runnables`, as it is not really Rust-specific, but the current API is not necessary the right one.
+Upstream issue: https://github.com/microsoft/language-server-protocol/issues/944
+
+**Request:**
+
+```typescript
+interface RunnablesParams {
+    textDocument: TextDocumentIdentifier;
+    /// If null, compute runnables for the whole file.
+    position?: Position;
+}
+```
+
+**Response:** `Runnable[]`
+
+```typescript
+interface Runnable {
+    /// The range this runnable is applicable for.
+    range: lc.Range;
+    /// The label to show in the UI.
+    label: string;
+    /// The following fields describe a process to spawn.
+    bin: string;
+    args: string[];
+    /// Args for cargo after `--`.
+    extraArgs: string[];
+    env: { [key: string]: string };
+    cwd: string | null;
+}
+```

From bfdcf73b9b0297b916d1c982ca95a005ceec7e86 Mon Sep 17 00:00:00 2001
From: Veetaha 
Date: Wed, 27 May 2020 14:04:57 +0300
Subject: [PATCH 087/163] typo

---
 docs/dev/lsp-extensions.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index c898f3e93a8b..68a5df27e7c3 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -384,7 +384,7 @@ interface InlayHintsParams {
 
 ```typescript
 interface InlayHint {
-    kind: "TypeHint" | "ParameterHint" | "ChainingHints",
+    kind: "TypeHint" | "ParameterHint" | "ChainingHint",
     range: Range,
     label: string,
 }

From 846cefa4917dc68a85b7b307be5d1890b83e1e2d Mon Sep 17 00:00:00 2001
From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
Date: Wed, 27 May 2020 15:15:19 +0200
Subject: [PATCH 088/163] fix textedit range returned for completion when left
 token is a keyword

Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
---
 crates/ra_ide/src/completion/complete_unqualified_path.rs | 1 +
 crates/ra_ide/src/completion/completion_context.rs        | 2 ++
 2 files changed, 3 insertions(+)

diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index 417a92001013..68032c37eda7 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -299,6 +299,7 @@ mod tests {
 
     #[test]
     fn completes_bindings_from_for_with_in_prefix() {
+        mark::check!(completes_bindings_from_for_with_in_prefix);
         assert_debug_snapshot!(
             do_reference_completion(
                 r"
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index e8bf07d6eb48..c4646b727c63 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -12,6 +12,7 @@ use ra_syntax::{
 use ra_text_edit::Indel;
 
 use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition};
+use test_utils::mark;
 
 /// `CompletionContext` is created early during completion to figure out, where
 /// exactly is the cursor, syntax-wise.
@@ -174,6 +175,7 @@ impl<'a> CompletionContext<'a> {
                 if self.token.kind().is_keyword()
                     && self.offset == self.original_token.text_range().end()
                 {
+                    mark::hit!(completes_bindings_from_for_with_in_prefix);
                     TextRange::empty(self.original_token.text_range().start())
                 } else {
                     TextRange::empty(self.offset)

From c011f04f55e82c5a057772fa3fb7a1c4d328c6d2 Mon Sep 17 00:00:00 2001
From: Cadu 
Date: Wed, 27 May 2020 13:15:19 -0300
Subject: [PATCH 089/163] Fixed missing newline on each field on "Missing
 structure fields".

---
 crates/ra_hir_ty/src/diagnostics.rs | 4 ++--
 crates/ra_ide/src/diagnostics.rs    | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index 41ac702724e8..2c7298714d8d 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -40,7 +40,7 @@ impl Diagnostic for MissingFields {
     fn message(&self) -> String {
         let mut buf = String::from("Missing structure fields:\n");
         for field in &self.missed_fields {
-            format_to!(buf, "- {}", field);
+            format_to!(buf, "- {}\n", field);
         }
         buf
     }
@@ -73,7 +73,7 @@ impl Diagnostic for MissingPatFields {
     fn message(&self) -> String {
         let mut buf = String::from("Missing structure fields:\n");
         for field in &self.missed_fields {
-            format_to!(buf, "- {}", field);
+            format_to!(buf, "- {}\n", field);
         }
         buf
     }
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index 3d83c0f71c5e..15dc50cf143d 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -664,7 +664,7 @@ mod tests {
         assert_debug_snapshot!(diagnostics, @r###"
         [
             Diagnostic {
-                message: "Missing structure fields:\n- b",
+                message: "Missing structure fields:\n- b\n",
                 range: 224..233,
                 severity: Error,
                 fix: Some(

From f3e04fbbabe2887542a9bb57fffcabc7cb50d406 Mon Sep 17 00:00:00 2001
From: vsrs 
Date: Wed, 27 May 2020 19:40:13 +0300
Subject: [PATCH 090/163] Add `inRustProject` when-clause for commands.

---
 editors/code/package.json | 66 ++++++++++++++++++++++++++++++++++++++-
 editors/code/src/main.ts  |  6 ++++
 editors/code/src/util.ts  |  5 +++
 3 files changed, 76 insertions(+), 1 deletion(-)

diff --git a/editors/code/package.json b/editors/code/package.json
index acf3ca4b5903..75dbafc05a3b 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -694,6 +694,70 @@
                     ]
                 }
             }
-        ]
+        ],
+        "menus": {
+            "commandPalette": [
+                {
+                    "command": "rust-analyzer.syntaxTree",
+                    "when": "inRustProject"
+                },
+                {
+                    "command": "rust-analyzer.expandMacro",
+                    "when": "inRustProject"
+                },
+                {
+                    "command": "rust-analyzer.matchingBrace",
+                    "when": "inRustProject"
+                },
+                {
+                    "command": "rust-analyzer.parentModule",
+                    "when": "inRustProject"
+                },
+                {
+                    "command": "rust-analyzer.joinLines",
+                    "when": "inRustProject"
+                },
+                {
+                    "command": "rust-analyzer.run",
+                    "when": "inRustProject"
+                },
+                {
+                    "command": "rust-analyzer.debug",
+                    "when": "inRustProject"
+                },
+                {
+                    "command": "rust-analyzer.newDebugConfig",
+                    "when": "inRustProject"
+                },
+                {
+                    "command": "rust-analyzer.analyzerStatus",
+                    "when": "inRustProject"
+                },
+                {
+                    "command": "rust-analyzer.collectGarbage",
+                    "when": "inRustProject"
+                },
+                {
+                    "command": "rust-analyzer.reload",
+                    "when": "inRustProject"
+                },
+                {
+                    "command": "rust-analyzer.onEnter",
+                    "when": "inRustProject"
+                },
+                {
+                    "command": "rust-analyzer.ssr",
+                    "when": "inRustProject"
+                },
+                {
+                    "command": "rust-analyzer.serverVersion",
+                    "when": "inRustProject"
+                },
+                {
+                    "command": "rust-analyzer.toggleInlayHints",
+                    "when": "inRustProject"
+                }
+            ]
+        }
     }
 }
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 31ac81ee88d2..b7337621cb61 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -12,10 +12,13 @@ import { log, assert, isValidExecutable } from './util';
 import { PersistentState } from './persistent_state';
 import { fetchRelease, download } from './net';
 import { activateTaskProvider } from './tasks';
+import { setContextValue } from './util';
 import { exec } from 'child_process';
 
 let ctx: Ctx | undefined;
 
+const RUST_PROJECT_CONTEXT_NAME = "inRustProject";
+
 export async function activate(context: vscode.ExtensionContext) {
     // Register a "dumb" onEnter command for the case where server fails to
     // start.
@@ -54,6 +57,8 @@ export async function activate(context: vscode.ExtensionContext) {
     // This a horribly, horribly wrong way to deal with this problem.
     ctx = await Ctx.create(config, context, serverPath, workspaceFolder.uri.fsPath);
 
+    setContextValue(RUST_PROJECT_CONTEXT_NAME, true);
+
     // Commands which invokes manually via command palette, shortcut, etc.
 
     // Reloading is inspired by @DanTup maneuver: https://github.com/microsoft/vscode/issues/45774#issuecomment-373423895
@@ -109,6 +114,7 @@ export async function activate(context: vscode.ExtensionContext) {
 }
 
 export async function deactivate() {
+    setContextValue(RUST_PROJECT_CONTEXT_NAME, undefined);
     await ctx?.client.stop();
     ctx = undefined;
 }
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts
index 793c481fb686..352ef9162f3d 100644
--- a/editors/code/src/util.ts
+++ b/editors/code/src/util.ts
@@ -94,3 +94,8 @@ export function isValidExecutable(path: string): boolean {
 
     return res.status === 0;
 }
+
+/** Sets ['when'](https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts) clause contexts */
+export function setContextValue(key: string, value: any): Thenable {
+    return vscode.commands.executeCommand('setContext', key, value);
+}

From c8a4bb1445e214dc7105de547bd999ad270e7b42 Mon Sep 17 00:00:00 2001
From: Florian Diebold 
Date: Wed, 27 May 2020 21:05:21 +0200
Subject: [PATCH 091/163] Upgrade Chalk

Chalk newly added TypeName::Never and Array; I implemented the conversion for
Never, but not Array since that expects a const argument.
---
 Cargo.lock                                    | 46 +++++++----------
 Cargo.toml                                    |  2 -
 crates/ra_hir_ty/Cargo.toml                   |  5 +-
 crates/ra_hir_ty/src/traits/chalk.rs          | 50 +++++++++----------
 crates/ra_hir_ty/src/traits/chalk/interner.rs | 14 +++---
 crates/ra_hir_ty/src/traits/chalk/mapping.rs  | 40 +++++++--------
 6 files changed, 71 insertions(+), 86 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 6f3d1f5645b0..1159d0f1d57d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -111,10 +111,18 @@ version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
 
+[[package]]
+name = "chalk-base"
+version = "0.10.1-dev"
+source = "git+https://github.com/rust-lang/chalk.git?rev=5a3b871ca17529ab5aa5787594fabad1634936cb#5a3b871ca17529ab5aa5787594fabad1634936cb"
+dependencies = [
+ "lazy_static",
+]
+
 [[package]]
 name = "chalk-derive"
 version = "0.10.1-dev"
-source = "git+https://github.com/rust-lang/chalk.git?rev=eaab84b394007d1bed15f5470409a6ea02900a96#eaab84b394007d1bed15f5470409a6ea02900a96"
+source = "git+https://github.com/rust-lang/chalk.git?rev=5a3b871ca17529ab5aa5787594fabad1634936cb#5a3b871ca17529ab5aa5787594fabad1634936cb"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -125,51 +133,32 @@ dependencies = [
 [[package]]
 name = "chalk-engine"
 version = "0.10.1-dev"
-source = "git+https://github.com/rust-lang/chalk.git?rev=eaab84b394007d1bed15f5470409a6ea02900a96#eaab84b394007d1bed15f5470409a6ea02900a96"
+source = "git+https://github.com/rust-lang/chalk.git?rev=5a3b871ca17529ab5aa5787594fabad1634936cb#5a3b871ca17529ab5aa5787594fabad1634936cb"
 dependencies = [
- "chalk-macros",
+ "chalk-base",
+ "chalk-derive",
+ "chalk-ir",
  "rustc-hash",
 ]
 
 [[package]]
 name = "chalk-ir"
 version = "0.10.1-dev"
-source = "git+https://github.com/rust-lang/chalk.git?rev=eaab84b394007d1bed15f5470409a6ea02900a96#eaab84b394007d1bed15f5470409a6ea02900a96"
+source = "git+https://github.com/rust-lang/chalk.git?rev=5a3b871ca17529ab5aa5787594fabad1634936cb#5a3b871ca17529ab5aa5787594fabad1634936cb"
 dependencies = [
+ "chalk-base",
  "chalk-derive",
- "chalk-engine",
- "chalk-macros",
-]
-
-[[package]]
-name = "chalk-macros"
-version = "0.10.1-dev"
-source = "git+https://github.com/rust-lang/chalk.git?rev=eaab84b394007d1bed15f5470409a6ea02900a96#eaab84b394007d1bed15f5470409a6ea02900a96"
-dependencies = [
- "lazy_static",
-]
-
-[[package]]
-name = "chalk-rust-ir"
-version = "0.10.1-dev"
-source = "git+https://github.com/rust-lang/chalk.git?rev=eaab84b394007d1bed15f5470409a6ea02900a96#eaab84b394007d1bed15f5470409a6ea02900a96"
-dependencies = [
- "chalk-derive",
- "chalk-engine",
- "chalk-ir",
- "chalk-macros",
 ]
 
 [[package]]
 name = "chalk-solve"
 version = "0.10.1-dev"
-source = "git+https://github.com/rust-lang/chalk.git?rev=eaab84b394007d1bed15f5470409a6ea02900a96#eaab84b394007d1bed15f5470409a6ea02900a96"
+source = "git+https://github.com/rust-lang/chalk.git?rev=5a3b871ca17529ab5aa5787594fabad1634936cb#5a3b871ca17529ab5aa5787594fabad1634936cb"
 dependencies = [
+ "chalk-base",
  "chalk-derive",
  "chalk-engine",
  "chalk-ir",
- "chalk-macros",
- "chalk-rust-ir",
  "ena",
  "itertools",
  "petgraph",
@@ -1036,7 +1025,6 @@ version = "0.1.0"
 dependencies = [
  "arrayvec",
  "chalk-ir",
- "chalk-rust-ir",
  "chalk-solve",
  "ena",
  "insta",
diff --git a/Cargo.toml b/Cargo.toml
index c034e24244e0..5278b5a1676c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,8 +22,6 @@ opt-level = 0
 opt-level = 0
 [profile.release.package.chalk-derive]
 opt-level = 0
-[profile.release.package.chalk-macros]
-opt-level = 0
 [profile.release.package.salsa-macros]
 opt-level = 0
 [profile.release.package.xtask]
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml
index b2de7fa34cc1..6afed58a11f0 100644
--- a/crates/ra_hir_ty/Cargo.toml
+++ b/crates/ra_hir_ty/Cargo.toml
@@ -27,9 +27,8 @@ test_utils = { path = "../test_utils" }
 
 scoped-tls = "1"
 
-chalk-solve =   { git = "https://github.com/rust-lang/chalk.git", rev = "eaab84b394007d1bed15f5470409a6ea02900a96" }
-chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "eaab84b394007d1bed15f5470409a6ea02900a96" }
-chalk-ir =      { git = "https://github.com/rust-lang/chalk.git", rev = "eaab84b394007d1bed15f5470409a6ea02900a96" }
+chalk-solve =   { git = "https://github.com/rust-lang/chalk.git", rev = "5a3b871ca17529ab5aa5787594fabad1634936cb" }
+chalk-ir =      { git = "https://github.com/rust-lang/chalk.git", rev = "5a3b871ca17529ab5aa5787594fabad1634936cb" }
 
 [dev-dependencies]
 insta = "0.16.0"
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs
index 5b0f12a3c7f3..61de3cc30c66 100644
--- a/crates/ra_hir_ty/src/traits/chalk.rs
+++ b/crates/ra_hir_ty/src/traits/chalk.rs
@@ -4,6 +4,7 @@ use std::sync::Arc;
 use log::debug;
 
 use chalk_ir::{fold::shift::Shift, GenericArg, TypeName};
+use chalk_solve::rust_ir::{self, WellKnownTrait};
 
 use hir_def::{
     lang_item::{lang_attr, LangItemTarget},
@@ -16,7 +17,6 @@ use crate::{
     db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics,
     CallableDef, DebruijnIndex, GenericPredicate, Substs, Ty, TypeCtor,
 };
-use chalk_rust_ir::WellKnownTrait;
 use mapping::{convert_where_clauses, generic_predicate_to_inline_bound, make_binders};
 
 pub use self::interner::*;
@@ -55,7 +55,7 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> {
     fn fn_def_datum(
         &self,
         fn_def_id: chalk_ir::FnDefId,
-    ) -> Arc> {
+    ) -> Arc> {
         self.db.fn_def_datum(self.krate, fn_def_id)
     }
 
@@ -112,7 +112,7 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> {
     }
     fn well_known_trait_id(
         &self,
-        well_known_trait: chalk_rust_ir::WellKnownTrait,
+        well_known_trait: rust_ir::WellKnownTrait,
     ) -> Option> {
         let lang_attr = lang_attr_from_well_known_trait(well_known_trait);
         let lang_items = self.db.crate_lang_items(self.krate);
@@ -133,13 +133,13 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> {
     fn opaque_ty_data(
         &self,
         _id: chalk_ir::OpaqueTyId,
-    ) -> Arc> {
+    ) -> Arc> {
         unimplemented!()
     }
 
     fn force_impl_for(
         &self,
-        _well_known: chalk_rust_ir::WellKnownTrait,
+        _well_known: rust_ir::WellKnownTrait,
         _ty: &chalk_ir::TyData,
     ) -> Option {
         // this method is mostly for rustc
@@ -150,6 +150,10 @@ impl<'a> chalk_solve::RustIrDatabase for ChalkContext<'a> {
         // FIXME: implement actual object safety
         true
     }
+
+    fn hidden_opaque_type(&self, _id: chalk_ir::OpaqueTyId) -> chalk_ir::Ty {
+        Ty::Unknown.to_chalk(self.db)
+    }
 }
 
 pub(crate) fn program_clauses_for_chalk_env_query(
@@ -188,7 +192,7 @@ pub(crate) fn associated_ty_data_query(
         .collect();
 
     let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars);
-    let bound_data = chalk_rust_ir::AssociatedTyDatumBound { bounds, where_clauses };
+    let bound_data = rust_ir::AssociatedTyDatumBound { bounds, where_clauses };
     let datum = AssociatedTyDatum {
         trait_id: trait_.to_chalk(db),
         id,
@@ -209,7 +213,7 @@ pub(crate) fn trait_datum_query(
     debug!("trait {:?} = {:?}", trait_id, trait_data.name);
     let generic_params = generics(db.upcast(), trait_.into());
     let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
-    let flags = chalk_rust_ir::TraitFlags {
+    let flags = rust_ir::TraitFlags {
         auto: trait_data.auto,
         upstream: trait_.lookup(db.upcast()).container.module(db.upcast()).krate != krate,
         non_enumerable: true,
@@ -221,7 +225,7 @@ pub(crate) fn trait_datum_query(
     let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars);
     let associated_ty_ids =
         trait_data.associated_types().map(|type_alias| type_alias.to_chalk(db)).collect();
-    let trait_datum_bound = chalk_rust_ir::TraitDatumBound { where_clauses };
+    let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses };
     let well_known =
         lang_attr(db.upcast(), trait_).and_then(|name| well_known_trait_from_lang_attr(&name));
     let trait_datum = TraitDatum {
@@ -271,12 +275,12 @@ pub(crate) fn struct_datum_query(
             convert_where_clauses(db, generic_def, &bound_vars)
         })
         .unwrap_or_else(Vec::new);
-    let flags = chalk_rust_ir::AdtFlags {
+    let flags = rust_ir::AdtFlags {
         upstream,
         // FIXME set fundamental flag correctly
         fundamental: false,
     };
-    let struct_datum_bound = chalk_rust_ir::AdtDatumBound {
+    let struct_datum_bound = rust_ir::AdtDatumBound {
         fields: Vec::new(), // FIXME add fields (only relevant for auto traits)
         where_clauses,
     };
@@ -316,9 +320,9 @@ fn impl_def_datum(
     let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
     let trait_ = trait_ref.trait_;
     let impl_type = if impl_id.lookup(db.upcast()).container.module(db.upcast()).krate == krate {
-        chalk_rust_ir::ImplType::Local
+        rust_ir::ImplType::Local
     } else {
-        chalk_rust_ir::ImplType::External
+        rust_ir::ImplType::External
     };
     let where_clauses = convert_where_clauses(db, impl_id.into(), &bound_vars);
     let negative = impl_data.is_negative;
@@ -331,13 +335,9 @@ fn impl_def_datum(
     );
     let trait_ref = trait_ref.to_chalk(db);
 
-    let polarity = if negative {
-        chalk_rust_ir::Polarity::Negative
-    } else {
-        chalk_rust_ir::Polarity::Positive
-    };
+    let polarity = if negative { rust_ir::Polarity::Negative } else { rust_ir::Polarity::Positive };
 
-    let impl_datum_bound = chalk_rust_ir::ImplDatumBound { trait_ref, where_clauses };
+    let impl_datum_bound = rust_ir::ImplDatumBound { trait_ref, where_clauses };
     let trait_data = db.trait_data(trait_);
     let associated_ty_value_ids = impl_data
         .items
@@ -395,8 +395,8 @@ fn type_alias_associated_ty_value(
         .associated_type_by_name(&type_alias_data.name)
         .expect("assoc ty value should not exist"); // validated when building the impl data as well
     let ty = db.ty(type_alias.into());
-    let value_bound = chalk_rust_ir::AssociatedTyValueBound { ty: ty.value.to_chalk(db) };
-    let value = chalk_rust_ir::AssociatedTyValue {
+    let value_bound = rust_ir::AssociatedTyValueBound { ty: ty.value.to_chalk(db) };
+    let value = rust_ir::AssociatedTyValue {
         impl_id: Impl::ImplDef(impl_id).to_chalk(db),
         associated_ty_id: assoc_ty.to_chalk(db),
         value: make_binders(value_bound, ty.num_binders),
@@ -414,7 +414,7 @@ pub(crate) fn fn_def_datum_query(
     let sig = db.callable_item_signature(callable_def);
     let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
     let where_clauses = convert_where_clauses(db, callable_def.into(), &bound_vars);
-    let bound = chalk_rust_ir::FnDefDatumBound {
+    let bound = rust_ir::FnDefDatumBound {
         // Note: Chalk doesn't actually use this information yet as far as I am aware, but we provide it anyway
         argument_types: sig.value.params().iter().map(|ty| ty.clone().to_chalk(db)).collect(),
         return_type: sig.value.ret().clone().to_chalk(db),
@@ -460,14 +460,14 @@ impl From for ImplId {
     }
 }
 
-impl From> for crate::traits::AssocTyValueId {
-    fn from(id: chalk_rust_ir::AssociatedTyValueId) -> Self {
+impl From> for crate::traits::AssocTyValueId {
+    fn from(id: rust_ir::AssociatedTyValueId) -> Self {
         Self::from_intern_id(id.0)
     }
 }
 
-impl From for chalk_rust_ir::AssociatedTyValueId {
+impl From for rust_ir::AssociatedTyValueId {
     fn from(assoc_ty_value_id: crate::traits::AssocTyValueId) -> Self {
-        chalk_rust_ir::AssociatedTyValueId(assoc_ty_value_id.as_intern_id())
+        rust_ir::AssociatedTyValueId(assoc_ty_value_id.as_intern_id())
     }
 }
diff --git a/crates/ra_hir_ty/src/traits/chalk/interner.rs b/crates/ra_hir_ty/src/traits/chalk/interner.rs
index 2a27f8ed8b3e..e27074ba6fad 100644
--- a/crates/ra_hir_ty/src/traits/chalk/interner.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/interner.rs
@@ -11,17 +11,17 @@ use std::{fmt, sync::Arc};
 pub struct Interner;
 
 pub type AssocTypeId = chalk_ir::AssocTypeId;
-pub type AssociatedTyDatum = chalk_rust_ir::AssociatedTyDatum;
+pub type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum;
 pub type TraitId = chalk_ir::TraitId;
-pub type TraitDatum = chalk_rust_ir::TraitDatum;
+pub type TraitDatum = chalk_solve::rust_ir::TraitDatum;
 pub type AdtId = chalk_ir::AdtId;
-pub type StructDatum = chalk_rust_ir::AdtDatum;
+pub type StructDatum = chalk_solve::rust_ir::AdtDatum;
 pub type ImplId = chalk_ir::ImplId;
-pub type ImplDatum = chalk_rust_ir::ImplDatum;
-pub type AssociatedTyValueId = chalk_rust_ir::AssociatedTyValueId;
-pub type AssociatedTyValue = chalk_rust_ir::AssociatedTyValue;
+pub type ImplDatum = chalk_solve::rust_ir::ImplDatum;
+pub type AssociatedTyValueId = chalk_solve::rust_ir::AssociatedTyValueId;
+pub type AssociatedTyValue = chalk_solve::rust_ir::AssociatedTyValue;
 pub type FnDefId = chalk_ir::FnDefId;
-pub type FnDefDatum = chalk_rust_ir::FnDefDatum;
+pub type FnDefDatum = chalk_solve::rust_ir::FnDefDatum;
 
 impl chalk_ir::interner::Interner for Interner {
     type InternedType = Box>; // FIXME use Arc?
diff --git a/crates/ra_hir_ty/src/traits/chalk/mapping.rs b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
index 7082cb09573a..5f6daf842be6 100644
--- a/crates/ra_hir_ty/src/traits/chalk/mapping.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/mapping.rs
@@ -7,6 +7,7 @@ use chalk_ir::{
     cast::Cast, fold::shift::Shift, interner::HasInterner, PlaceholderIndex, Scalar, TypeName,
     UniverseIndex,
 };
+use chalk_solve::rust_ir;
 
 use hir_def::{type_ref::Mutability, AssocContainerId, GenericDefId, Lookup, TypeAliasId};
 use ra_db::salsa::InternKey;
@@ -106,7 +107,7 @@ impl ToChalk for Ty {
                 })
             }
             chalk_ir::TyData::BoundVar(idx) => Ty::Bound(idx),
-            chalk_ir::TyData::InferenceVar(_iv) => Ty::Unknown,
+            chalk_ir::TyData::InferenceVar(_iv, _kind) => Ty::Unknown,
             chalk_ir::TyData::Dyn(where_clauses) => {
                 assert_eq!(where_clauses.bounds.binders.len(&Interner), 1);
                 let predicates = where_clauses
@@ -232,12 +233,13 @@ impl ToChalk for TypeCtor {
                 let id = callable_def.to_chalk(db);
                 TypeName::FnDef(id)
             }
+            TypeCtor::Never => TypeName::Never,
+
             TypeCtor::Int(Uncertain::Unknown)
             | TypeCtor::Float(Uncertain::Unknown)
             | TypeCtor::Adt(_)
             | TypeCtor::Array
             | TypeCtor::FnPtr { .. }
-            | TypeCtor::Never
             | TypeCtor::Closure { .. } => {
                 // other TypeCtors get interned and turned into a chalk StructId
                 let struct_id = db.intern_type_ctor(self).into();
@@ -273,13 +275,14 @@ impl ToChalk for TypeCtor {
             TypeName::Slice => TypeCtor::Slice,
             TypeName::Ref(mutability) => TypeCtor::Ref(from_chalk(db, mutability)),
             TypeName::Str => TypeCtor::Str,
+            TypeName::Never => TypeCtor::Never,
 
             TypeName::FnDef(fn_def_id) => {
                 let callable_def = from_chalk(db, fn_def_id);
                 TypeCtor::FnDef(callable_def)
             }
 
-            TypeName::Error => {
+            TypeName::Array | TypeName::Error => {
                 // this should not be reached, since we don't represent TypeName::Error with TypeCtor
                 unreachable!()
             }
@@ -508,7 +511,7 @@ where
 
     fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical {
         let parameter = chalk_ir::CanonicalVarKind::new(
-            chalk_ir::VariableKind::Ty,
+            chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General),
             chalk_ir::UniverseIndex::ROOT,
         );
         let value = self.value.to_chalk(db);
@@ -579,17 +582,17 @@ impl ToChalk for builtin::BuiltinImplData {
     type Chalk = ImplDatum;
 
     fn to_chalk(self, db: &dyn HirDatabase) -> ImplDatum {
-        let impl_type = chalk_rust_ir::ImplType::External;
+        let impl_type = rust_ir::ImplType::External;
         let where_clauses = self.where_clauses.into_iter().map(|w| w.to_chalk(db)).collect();
 
         let impl_datum_bound =
-            chalk_rust_ir::ImplDatumBound { trait_ref: self.trait_ref.to_chalk(db), where_clauses };
+            rust_ir::ImplDatumBound { trait_ref: self.trait_ref.to_chalk(db), where_clauses };
         let associated_ty_value_ids =
             self.assoc_ty_values.into_iter().map(|v| v.to_chalk(db)).collect();
-        chalk_rust_ir::ImplDatum {
+        rust_ir::ImplDatum {
             binders: make_binders(impl_datum_bound, self.num_vars),
             impl_type,
-            polarity: chalk_rust_ir::Polarity::Positive,
+            polarity: rust_ir::Polarity::Positive,
             associated_ty_value_ids,
         }
     }
@@ -604,9 +607,9 @@ impl ToChalk for builtin::BuiltinImplAssocTyValueData {
 
     fn to_chalk(self, db: &dyn HirDatabase) -> AssociatedTyValue {
         let ty = self.value.to_chalk(db);
-        let value_bound = chalk_rust_ir::AssociatedTyValueBound { ty };
+        let value_bound = rust_ir::AssociatedTyValueBound { ty };
 
-        chalk_rust_ir::AssociatedTyValue {
+        rust_ir::AssociatedTyValue {
             associated_ty_id: self.assoc_ty_id.to_chalk(db),
             impl_id: self.impl_.to_chalk(db),
             value: make_binders(value_bound, self.num_vars),
@@ -628,7 +631,7 @@ where
     chalk_ir::Binders::new(
         chalk_ir::VariableKinds::from(
             &Interner,
-            std::iter::repeat(chalk_ir::VariableKind::Ty).take(num_vars),
+            std::iter::repeat(chalk_ir::VariableKind::Ty(chalk_ir::TyKind::General)).take(num_vars),
         ),
         value,
     )
@@ -655,7 +658,7 @@ pub(super) fn generic_predicate_to_inline_bound(
     db: &dyn HirDatabase,
     pred: &GenericPredicate,
     self_ty: &Ty,
-) -> Option> {
+) -> Option> {
     // An InlineBound is like a GenericPredicate, except the self type is left out.
     // We don't have a special type for this, but Chalk does.
     match pred {
@@ -670,8 +673,8 @@ pub(super) fn generic_predicate_to_inline_bound(
                 .map(|ty| ty.clone().to_chalk(db).cast(&Interner))
                 .collect();
             let trait_bound =
-                chalk_rust_ir::TraitBound { trait_id: trait_ref.trait_.to_chalk(db), args_no_self };
-            Some(chalk_rust_ir::InlineBound::TraitBound(trait_bound))
+                rust_ir::TraitBound { trait_id: trait_ref.trait_.to_chalk(db), args_no_self };
+            Some(rust_ir::InlineBound::TraitBound(trait_bound))
         }
         GenericPredicate::Projection(proj) => {
             if &proj.projection_ty.parameters[0] != self_ty {
@@ -685,16 +688,13 @@ pub(super) fn generic_predicate_to_inline_bound(
                 .iter()
                 .map(|ty| ty.clone().to_chalk(db).cast(&Interner))
                 .collect();
-            let alias_eq_bound = chalk_rust_ir::AliasEqBound {
+            let alias_eq_bound = rust_ir::AliasEqBound {
                 value: proj.ty.clone().to_chalk(db),
-                trait_bound: chalk_rust_ir::TraitBound {
-                    trait_id: trait_.to_chalk(db),
-                    args_no_self,
-                },
+                trait_bound: rust_ir::TraitBound { trait_id: trait_.to_chalk(db), args_no_self },
                 associated_ty_id: proj.projection_ty.associated_ty.to_chalk(db),
                 parameters: Vec::new(), // FIXME we don't support generic associated types yet
             };
-            Some(chalk_rust_ir::InlineBound::AliasEqBound(alias_eq_bound))
+            Some(rust_ir::InlineBound::AliasEqBound(alias_eq_bound))
         }
         GenericPredicate::Error => None,
     }

From 232a1de222781877638fddfb5a483613a1c9ab2a Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Thu, 28 May 2020 10:19:01 +0200
Subject: [PATCH 092/163] Link forum topic

---
 README.md             | 8 ++++++--
 docs/user/readme.adoc | 4 ++--
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index 1c334d55e6d2..1e7c3e9b4344 100644
--- a/README.md
+++ b/README.md
@@ -27,9 +27,13 @@ If you want to **use** rust-analyzer's language server with your editor of
 choice, check [the manual](https://rust-analyzer.github.io/manual.html) folder. It also contains some tips & tricks to help
 you be more productive when using rust-analyzer.
 
-## Getting in touch
+## Communication
 
-We are on the rust-lang Zulip!
+For usage and troubleshooting requests, please use "IDEs and Editors" category of the Rust forum:
+
+https://users.rust-lang.org/c/ide/14
+
+For questions about development and implementation, join rls-2.0 working group on Zulip:
 
 https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frls-2.2E0
 
diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc
index 40ed54809146..9539d326d54a 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/readme.adoc
@@ -9,8 +9,6 @@
 :caution-caption: :fire:
 :warning-caption: :warning:
 
-
-
 // Master copy of this document lives in the https://github.com/rust-analyzer/rust-analyzer repository
 
 At its core, rust-analyzer is a *library* for semantic analysis of Rust code as it changes over time.
@@ -21,6 +19,8 @@ The LSP allows various code editors, like VS Code, Emacs or Vim, to implement se
 To improve this document, send a pull request against
 https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/readme.adoc[this file].
 
+If you have questions about using rust-analyzer, please ask them in the https://users.rust-lang.org/c/ide/14["`IDEs and Editors`"] topic of Rust users forum.
+
 == Installation
 
 In theory, one should be able to just install the <> and have it automatically work with any editor.

From 6d0f1e2e7209e85c1c558bd712d22e359f1f2786 Mon Sep 17 00:00:00 2001
From: vsrs <62505555+vsrs@users.noreply.github.com>
Date: Thu, 28 May 2020 16:04:42 +0300
Subject: [PATCH 093/163] Add `inRustProject` context documentation.

---
 docs/user/readme.adoc | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc
index 40ed54809146..0631ca8837b2 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/readme.adoc
@@ -65,6 +65,16 @@ The server binary is stored in:
 
 Note that we only support two most recent versions of VS Code.
 
+==== Special `when` clause context for keybindings.
+You may use `inRustProject` context to configure keybindings for rust projects only. For example:
+[source,json]
+----
+{ "key": "ctrl+shift+f5", "command": "workbench.action.debug.restart", "when": "inDebugMode && !inRustProject"},
+{ "key": "ctrl+shift+f5", "command": "rust-analyzer.debug", "when": "inRustProject"},
+{ "key": "ctrl+i", "command": "rust-analyzer.toggleInlayHints", "when": "inRustProject" }
+----
+More about `when` clause contexts https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts[here].
+
 ==== Updates
 
 The extension will be updated automatically as new versions become available. It will ask your permission to download the matching language server version binary if needed.

From 367487fe88dca78cffad5138673d5259f7f7ba6b Mon Sep 17 00:00:00 2001
From: robojumper 
Date: Thu, 28 May 2020 21:42:22 +0200
Subject: [PATCH 094/163] Support raw_ref_op's raw reference operator

---
 crates/ra_hir_def/src/body/lower.rs           |  19 +-
 crates/ra_hir_def/src/expr.rs                 |   3 +-
 crates/ra_hir_def/src/type_ref.rs             |  16 ++
 crates/ra_hir_ty/src/infer/expr.rs            |  37 ++--
 crates/ra_hir_ty/src/lib.rs                   |  18 +-
 crates/ra_hir_ty/src/tests/coercion.rs        |  17 +-
 crates/ra_hir_ty/src/tests/simple.rs          |  20 ++
 crates/ra_parser/src/grammar/expressions.rs   |  16 +-
 crates/ra_syntax/src/ast/generated/nodes.rs   |   3 +
 .../parser/inline/ok/0082_ref_expr.rast       | 172 +++++++++++++-----
 .../parser/inline/ok/0082_ref_expr.rs         |   6 +
 xtask/src/ast_src.rs                          |   4 +-
 12 files changed, 259 insertions(+), 72 deletions(-)

diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index e08d62dd6869..905c0cf5dad2 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -28,7 +28,7 @@ use crate::{
     },
     item_scope::BuiltinShadowMode,
     path::{GenericArgs, Path},
-    type_ref::{Mutability, TypeRef},
+    type_ref::{Mutability, Rawness, TypeRef},
     AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId,
     StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
 };
@@ -378,8 +378,21 @@ impl ExprCollector<'_> {
             }
             ast::Expr::RefExpr(e) => {
                 let expr = self.collect_expr_opt(e.expr());
-                let mutability = Mutability::from_mutable(e.mut_token().is_some());
-                self.alloc_expr(Expr::Ref { expr, mutability }, syntax_ptr)
+                let raw_tok = e.raw_token().is_some();
+                let mutability = if raw_tok {
+                    if e.mut_token().is_some() {
+                        Mutability::Mut
+                    } else if e.const_token().is_some() {
+                        Mutability::Shared
+                    } else {
+                        unreachable!("parser only remaps to raw_token() if matching mutability token follows")
+                    }
+                } else {
+                    Mutability::from_mutable(e.mut_token().is_some())
+                };
+                let rawness = Rawness::from_raw(raw_tok);
+
+                self.alloc_expr(Expr::Ref { expr, rawness, mutability }, syntax_ptr)
             }
             ast::Expr::PrefixExpr(e) => {
                 let expr = self.collect_expr_opt(e.expr());
diff --git a/crates/ra_hir_def/src/expr.rs b/crates/ra_hir_def/src/expr.rs
index a0cdad529b3f..f25c6f958079 100644
--- a/crates/ra_hir_def/src/expr.rs
+++ b/crates/ra_hir_def/src/expr.rs
@@ -19,7 +19,7 @@ use ra_syntax::ast::RangeOp;
 use crate::{
     builtin_type::{BuiltinFloat, BuiltinInt},
     path::{GenericArgs, Path},
-    type_ref::{Mutability, TypeRef},
+    type_ref::{Mutability, Rawness, TypeRef},
 };
 
 pub type ExprId = Idx;
@@ -110,6 +110,7 @@ pub enum Expr {
     },
     Ref {
         expr: ExprId,
+        rawness: Rawness,
         mutability: Mutability,
     },
     Box {
diff --git a/crates/ra_hir_def/src/type_ref.rs b/crates/ra_hir_def/src/type_ref.rs
index 5bdad9efdef3..86a77b7046d4 100644
--- a/crates/ra_hir_def/src/type_ref.rs
+++ b/crates/ra_hir_def/src/type_ref.rs
@@ -35,6 +35,22 @@ impl Mutability {
     }
 }
 
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum Rawness {
+    RawPtr,
+    Ref,
+}
+
+impl Rawness {
+    pub fn from_raw(is_raw: bool) -> Rawness {
+        if is_raw {
+            Rawness::RawPtr
+        } else {
+            Rawness::Ref
+        }
+    }
+}
+
 /// Compare ty::Ty
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub enum TypeRef {
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index b28724f0e946..54bab3476c7d 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -17,8 +17,8 @@ use crate::{
     autoderef, method_resolution, op,
     traits::InEnvironment,
     utils::{generics, variant_data, Generics},
-    ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Substs, TraitRef,
-    Ty, TypeCtor, Uncertain,
+    ApplicationTy, Binders, CallableDef, InferTy, IntTy, Mutability, Obligation, Rawness, Substs,
+    TraitRef, Ty, TypeCtor, Uncertain,
 };
 
 use super::{
@@ -350,19 +350,28 @@ impl<'a> InferenceContext<'a> {
                 // FIXME check the cast...
                 cast_ty
             }
-            Expr::Ref { expr, mutability } => {
-                let expectation =
-                    if let Some((exp_inner, exp_mutability)) = &expected.ty.as_reference() {
-                        if *exp_mutability == Mutability::Mut && *mutability == Mutability::Shared {
-                            // FIXME: throw type error - expected mut reference but found shared ref,
-                            // which cannot be coerced
-                        }
-                        Expectation::rvalue_hint(Ty::clone(exp_inner))
-                    } else {
-                        Expectation::none()
-                    };
+            Expr::Ref { expr, rawness, mutability } => {
+                let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) =
+                    &expected.ty.as_reference_or_ptr()
+                {
+                    if *exp_mutability == Mutability::Mut && *mutability == Mutability::Shared {
+                        // FIXME: throw type error - expected mut reference but found shared ref,
+                        // which cannot be coerced
+                    }
+                    if *exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr {
+                        // FIXME: throw type error - expected reference but found ptr,
+                        // which cannot be coerced
+                    }
+                    Expectation::rvalue_hint(Ty::clone(exp_inner))
+                } else {
+                    Expectation::none()
+                };
                 let inner_ty = self.infer_expr_inner(*expr, &expectation);
-                Ty::apply_one(TypeCtor::Ref(*mutability), inner_ty)
+                let ty = match rawness {
+                    Rawness::RawPtr => TypeCtor::RawPtr(*mutability),
+                    Rawness::Ref => TypeCtor::Ref(*mutability),
+                };
+                Ty::apply_one(ty, inner_ty)
             }
             Expr::Box { expr } => {
                 let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index 93cb45a648c5..9fa8d3bdc3f4 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -49,8 +49,10 @@ use std::sync::Arc;
 use std::{iter, mem};
 
 use hir_def::{
-    expr::ExprId, type_ref::Mutability, AdtId, AssocContainerId, DefWithBodyId, GenericDefId,
-    HasModule, Lookup, TraitId, TypeAliasId, TypeParamId,
+    expr::ExprId,
+    type_ref::{Mutability, Rawness},
+    AdtId, AssocContainerId, DefWithBodyId, GenericDefId, HasModule, Lookup, TraitId, TypeAliasId,
+    TypeParamId,
 };
 use ra_db::{impl_intern_key, salsa, CrateId};
 
@@ -709,6 +711,18 @@ impl Ty {
         }
     }
 
+    pub fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> {
+        match self {
+            Ty::Apply(ApplicationTy { ctor: TypeCtor::Ref(mutability), parameters }) => {
+                Some((parameters.as_single(), Rawness::Ref, *mutability))
+            }
+            Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(mutability), parameters }) => {
+                Some((parameters.as_single(), Rawness::RawPtr, *mutability))
+            }
+            _ => None,
+        }
+    }
+
     pub fn strip_references(&self) -> &Ty {
         let mut t: &Ty = self;
 
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs
index 2cc4f4bf964c..6f777ed8c937 100644
--- a/crates/ra_hir_ty/src/tests/coercion.rs
+++ b/crates/ra_hir_ty/src/tests/coercion.rs
@@ -116,15 +116,20 @@ fn infer_let_stmt_coerce() {
     assert_snapshot!(
         infer(r#"
 fn test() {
-    let x: &[i32] = &[1];
+    let x: &[isize] = &[1];
+    let x: *const [isize] = &[1];
 }
 "#),
         @r###"
-    11..40 '{     ...[1]; }': ()
-    21..22 'x': &[i32]
-    33..37 '&[1]': &[i32; _]
-    34..37 '[1]': [i32; _]
-    35..36 '1': i32
+    11..76 '{     ...[1]; }': ()
+    21..22 'x': &[isize]
+    35..39 '&[1]': &[isize; _]
+    36..39 '[1]': [isize; _]
+    37..38 '1': isize
+    49..50 'x': *const [isize]
+    69..73 '&[1]': &[isize; _]
+    70..73 '[1]': [isize; _]
+    71..72 '1': isize
     "###);
 }
 
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index fd2208af280e..f1db3416049f 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -384,6 +384,26 @@ fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) {
     );
 }
 
+#[test]
+fn infer_raw_ref() {
+    assert_snapshot!(
+        infer(r#"
+fn test(a: i32) {
+    &raw mut a;
+    &raw const a;
+}
+"#),
+        @r###"
+    9..10 'a': i32
+    17..54 '{     ...t a; }': ()
+    23..33 '&raw mut a': *mut i32
+    32..33 'a': i32
+    39..51 '&raw const a': *const i32
+    50..51 'a': i32
+    "###
+    );
+}
+
 #[test]
 fn infer_literals() {
     assert_snapshot!(
diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs
index 34f0397686fb..d6e8df32a14e 100644
--- a/crates/ra_parser/src/grammar/expressions.rs
+++ b/crates/ra_parser/src/grammar/expressions.rs
@@ -325,13 +325,27 @@ fn lhs(p: &mut Parser, r: Restrictions) -> Option<(CompletedMarker, BlockLike)>
     let kind = match p.current() {
         // test ref_expr
         // fn foo() {
+        //     // reference operator
         //     let _ = &1;
         //     let _ = &mut &f();
+        //     let _ = &raw;
+        //     let _ = &raw.0;
+        //     // raw reference operator
+        //     let _ = &raw mut foo;
+        //     let _ = &raw const foo;
         // }
         T![&] => {
             m = p.start();
             p.bump(T![&]);
-            p.eat(T![mut]);
+            if p.at(IDENT)
+                && p.at_contextual_kw("raw")
+                && (p.nth_at(1, T![mut]) || p.nth_at(1, T![const]))
+            {
+                p.bump_remap(T![raw]);
+                p.bump_any();
+            } else {
+                p.eat(T![mut]);
+            }
             REF_EXPR
         }
         // test unary_expr
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs
index cf6067e57c42..255402fbce32 100644
--- a/crates/ra_syntax/src/ast/generated/nodes.rs
+++ b/crates/ra_syntax/src/ast/generated/nodes.rs
@@ -1235,6 +1235,8 @@ impl CastExpr {
 /// ```
 /// ❰ &foo ❱;
 /// ❰ &mut bar ❱;
+/// ❰ &raw const bar ❱;
+/// ❰ &raw mut bar ❱;
 /// ```
 ///
 /// [Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#borrow-operators)
@@ -1247,6 +1249,7 @@ impl RefExpr {
     pub fn amp_token(&self) -> Option { support::token(&self.syntax, T![&]) }
     pub fn raw_token(&self) -> Option { support::token(&self.syntax, T![raw]) }
     pub fn mut_token(&self) -> Option { support::token(&self.syntax, T![mut]) }
+    pub fn const_token(&self) -> Option { support::token(&self.syntax, T![const]) }
     pub fn expr(&self) -> Option { support::child(&self.syntax) }
 }
 /// Prefix operator call. This is either `!` or `*` or `-`.
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rast b/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rast
index 7fe96e17db0c..58bdf7e34c64 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rast
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rast
@@ -1,5 +1,5 @@
-SOURCE_FILE@0..52
-  FN_DEF@0..51
+SOURCE_FILE@0..200
+  FN_DEF@0..199
     FN_KW@0..2 "fn"
     WHITESPACE@2..3 " "
     NAME@3..6
@@ -8,47 +8,131 @@ SOURCE_FILE@0..52
       L_PAREN@6..7 "("
       R_PAREN@7..8 ")"
     WHITESPACE@8..9 " "
-    BLOCK_EXPR@9..51
+    BLOCK_EXPR@9..199
       L_CURLY@9..10 "{"
       WHITESPACE@10..15 "\n    "
-      LET_STMT@15..26
-        LET_KW@15..18 "let"
-        WHITESPACE@18..19 " "
-        PLACEHOLDER_PAT@19..20
-          UNDERSCORE@19..20 "_"
-        WHITESPACE@20..21 " "
-        EQ@21..22 "="
-        WHITESPACE@22..23 " "
-        REF_EXPR@23..25
-          AMP@23..24 "&"
-          LITERAL@24..25
-            INT_NUMBER@24..25 "1"
-        SEMICOLON@25..26 ";"
-      WHITESPACE@26..31 "\n    "
-      LET_STMT@31..49
-        LET_KW@31..34 "let"
-        WHITESPACE@34..35 " "
-        PLACEHOLDER_PAT@35..36
-          UNDERSCORE@35..36 "_"
-        WHITESPACE@36..37 " "
-        EQ@37..38 "="
-        WHITESPACE@38..39 " "
-        REF_EXPR@39..48
-          AMP@39..40 "&"
-          MUT_KW@40..43 "mut"
-          WHITESPACE@43..44 " "
-          REF_EXPR@44..48
-            AMP@44..45 "&"
-            CALL_EXPR@45..48
-              PATH_EXPR@45..46
-                PATH@45..46
-                  PATH_SEGMENT@45..46
-                    NAME_REF@45..46
-                      IDENT@45..46 "f"
-              ARG_LIST@46..48
-                L_PAREN@46..47 "("
-                R_PAREN@47..48 ")"
-        SEMICOLON@48..49 ";"
-      WHITESPACE@49..50 "\n"
-      R_CURLY@50..51 "}"
-  WHITESPACE@51..52 "\n"
+      COMMENT@15..36 "// reference operator"
+      WHITESPACE@36..41 "\n    "
+      LET_STMT@41..52
+        LET_KW@41..44 "let"
+        WHITESPACE@44..45 " "
+        PLACEHOLDER_PAT@45..46
+          UNDERSCORE@45..46 "_"
+        WHITESPACE@46..47 " "
+        EQ@47..48 "="
+        WHITESPACE@48..49 " "
+        REF_EXPR@49..51
+          AMP@49..50 "&"
+          LITERAL@50..51
+            INT_NUMBER@50..51 "1"
+        SEMICOLON@51..52 ";"
+      WHITESPACE@52..57 "\n    "
+      LET_STMT@57..75
+        LET_KW@57..60 "let"
+        WHITESPACE@60..61 " "
+        PLACEHOLDER_PAT@61..62
+          UNDERSCORE@61..62 "_"
+        WHITESPACE@62..63 " "
+        EQ@63..64 "="
+        WHITESPACE@64..65 " "
+        REF_EXPR@65..74
+          AMP@65..66 "&"
+          MUT_KW@66..69 "mut"
+          WHITESPACE@69..70 " "
+          REF_EXPR@70..74
+            AMP@70..71 "&"
+            CALL_EXPR@71..74
+              PATH_EXPR@71..72
+                PATH@71..72
+                  PATH_SEGMENT@71..72
+                    NAME_REF@71..72
+                      IDENT@71..72 "f"
+              ARG_LIST@72..74
+                L_PAREN@72..73 "("
+                R_PAREN@73..74 ")"
+        SEMICOLON@74..75 ";"
+      WHITESPACE@75..80 "\n    "
+      LET_STMT@80..93
+        LET_KW@80..83 "let"
+        WHITESPACE@83..84 " "
+        PLACEHOLDER_PAT@84..85
+          UNDERSCORE@84..85 "_"
+        WHITESPACE@85..86 " "
+        EQ@86..87 "="
+        WHITESPACE@87..88 " "
+        REF_EXPR@88..92
+          AMP@88..89 "&"
+          PATH_EXPR@89..92
+            PATH@89..92
+              PATH_SEGMENT@89..92
+                NAME_REF@89..92
+                  IDENT@89..92 "raw"
+        SEMICOLON@92..93 ";"
+      WHITESPACE@93..98 "\n    "
+      LET_STMT@98..113
+        LET_KW@98..101 "let"
+        WHITESPACE@101..102 " "
+        PLACEHOLDER_PAT@102..103
+          UNDERSCORE@102..103 "_"
+        WHITESPACE@103..104 " "
+        EQ@104..105 "="
+        WHITESPACE@105..106 " "
+        REF_EXPR@106..112
+          AMP@106..107 "&"
+          FIELD_EXPR@107..112
+            PATH_EXPR@107..110
+              PATH@107..110
+                PATH_SEGMENT@107..110
+                  NAME_REF@107..110
+                    IDENT@107..110 "raw"
+            DOT@110..111 "."
+            NAME_REF@111..112
+              INT_NUMBER@111..112 "0"
+        SEMICOLON@112..113 ";"
+      WHITESPACE@113..118 "\n    "
+      COMMENT@118..143 "// raw reference oper ..."
+      WHITESPACE@143..148 "\n    "
+      LET_STMT@148..169
+        LET_KW@148..151 "let"
+        WHITESPACE@151..152 " "
+        PLACEHOLDER_PAT@152..153
+          UNDERSCORE@152..153 "_"
+        WHITESPACE@153..154 " "
+        EQ@154..155 "="
+        WHITESPACE@155..156 " "
+        REF_EXPR@156..168
+          AMP@156..157 "&"
+          RAW_KW@157..160 "raw"
+          WHITESPACE@160..161 " "
+          MUT_KW@161..164 "mut"
+          WHITESPACE@164..165 " "
+          PATH_EXPR@165..168
+            PATH@165..168
+              PATH_SEGMENT@165..168
+                NAME_REF@165..168
+                  IDENT@165..168 "foo"
+        SEMICOLON@168..169 ";"
+      WHITESPACE@169..174 "\n    "
+      LET_STMT@174..197
+        LET_KW@174..177 "let"
+        WHITESPACE@177..178 " "
+        PLACEHOLDER_PAT@178..179
+          UNDERSCORE@178..179 "_"
+        WHITESPACE@179..180 " "
+        EQ@180..181 "="
+        WHITESPACE@181..182 " "
+        REF_EXPR@182..196
+          AMP@182..183 "&"
+          RAW_KW@183..186 "raw"
+          WHITESPACE@186..187 " "
+          CONST_KW@187..192 "const"
+          WHITESPACE@192..193 " "
+          PATH_EXPR@193..196
+            PATH@193..196
+              PATH_SEGMENT@193..196
+                NAME_REF@193..196
+                  IDENT@193..196 "foo"
+        SEMICOLON@196..197 ";"
+      WHITESPACE@197..198 "\n"
+      R_CURLY@198..199 "}"
+  WHITESPACE@199..200 "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rs b/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rs
index 2dac6be95e75..c5262f4469b0 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rs
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0082_ref_expr.rs
@@ -1,4 +1,10 @@
 fn foo() {
+    // reference operator
     let _ = &1;
     let _ = &mut &f();
+    let _ = &raw;
+    let _ = &raw.0;
+    // raw reference operator
+    let _ = &raw mut foo;
+    let _ = &raw const foo;
 }
diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs
index 394a7bc889b9..d4621930ed65 100644
--- a/xtask/src/ast_src.rs
+++ b/xtask/src/ast_src.rs
@@ -1153,10 +1153,12 @@ pub(crate) const AST_SRC: AstSrc = AstSrc {
         /// ```
         /// ❰ &foo ❱;
         /// ❰ &mut bar ❱;
+        /// ❰ &raw const bar ❱;
+        /// ❰ &raw mut bar ❱;
         /// ```
         ///
         /// [Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#borrow-operators)
-        struct RefExpr: AttrsOwner { T![&], T![raw], T![mut], Expr }
+        struct RefExpr: AttrsOwner { T![&], T![raw], T![mut], T![const], Expr }
 
         /// Prefix operator call. This is either `!` or `*` or `-`.
         ///

From bd8aa04bae5280c9b5248413f2370f0773ac73aa Mon Sep 17 00:00:00 2001
From: Jess Balint 
Date: Thu, 28 May 2020 14:20:26 -0500
Subject: [PATCH 095/163] add support for naming anon lifetimes in function
 return type

---
 .../handlers/change_lifetime_anon_to_named.rs | 264 ++++++++++++++----
 1 file changed, 212 insertions(+), 52 deletions(-)

diff --git a/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs b/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
index 922213607051..999aec421b08 100644
--- a/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
+++ b/crates/ra_assists/src/handlers/change_lifetime_anon_to_named.rs
@@ -1,6 +1,10 @@
-use crate::{AssistContext, AssistId, Assists};
-use ra_syntax::{ast, ast::TypeParamsOwner, AstNode, SyntaxKind};
-use std::collections::HashSet;
+use crate::{assist_context::AssistBuilder, AssistContext, AssistId, Assists};
+use ast::{NameOwner, ParamList, TypeAscriptionOwner, TypeParamList, TypeRef};
+use ra_syntax::{ast, ast::TypeParamsOwner, AstNode, SyntaxKind, TextRange, TextSize};
+use rustc_hash::FxHashSet;
+
+static ASSIST_NAME: &str = "change_lifetime_anon_to_named";
+static ASSIST_LABEL: &str = "Give anonymous lifetime a name";
 
 // Assist: change_lifetime_anon_to_named
 //
@@ -26,59 +30,117 @@ use std::collections::HashSet;
 // }
 // ```
 // FIXME: How can we handle renaming any one of multiple anonymous lifetimes?
+// FIXME: should also add support for the case fun(f: &Foo) -> &<|>Foo
 pub(crate) fn change_lifetime_anon_to_named(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
-    let lifetime_token = ctx.find_token_at_offset(SyntaxKind::LIFETIME)?;
-    let lifetime_arg = ast::LifetimeArg::cast(lifetime_token.parent())?;
-    if lifetime_arg.syntax().text() != "'_" {
-        return None;
-    }
-    let next_token = lifetime_token.next_token()?;
-    if next_token.kind() != SyntaxKind::R_ANGLE {
+    let lifetime_token = ctx
+        .find_token_at_offset(SyntaxKind::LIFETIME)
+        .filter(|lifetime| lifetime.text() == "'_")?;
+    if let Some(fn_def) = lifetime_token.ancestors().find_map(ast::FnDef::cast) {
+        generate_fn_def_assist(acc, &fn_def, lifetime_token.text_range())
+    } else if let Some(impl_def) = lifetime_token.ancestors().find_map(ast::ImplDef::cast) {
         // only allow naming the last anonymous lifetime
-        return None;
+        lifetime_token.next_token().filter(|tok| tok.kind() == SyntaxKind::R_ANGLE)?;
+        generate_impl_def_assist(acc, &impl_def, lifetime_token.text_range())
+    } else {
+        None
     }
-    let impl_def = lifetime_arg.syntax().ancestors().find_map(ast::ImplDef::cast)?;
-    // get the `impl` keyword so we know where to add the lifetime argument
-    let impl_kw = impl_def.syntax().first_child_or_token()?.into_token()?;
-    if impl_kw.kind() != SyntaxKind::IMPL_KW {
-        return None;
-    }
-    let new_lifetime_param = match impl_def.type_param_list() {
-        Some(type_params) => {
-            let used_lifetime_params: HashSet<_> = type_params
-                .lifetime_params()
-                .map(|p| {
-                    let mut param_name = p.syntax().text().to_string();
-                    param_name.remove(0);
-                    param_name
-                })
-                .collect();
-            (b'a'..=b'z')
-                .map(char::from)
-                .find(|c| !used_lifetime_params.contains(&c.to_string()))?
+}
+
+/// Generate the assist for the fn def case
+fn generate_fn_def_assist(
+    acc: &mut Assists,
+    fn_def: &ast::FnDef,
+    lifetime_loc: TextRange,
+) -> Option<()> {
+    let param_list: ParamList = fn_def.param_list()?;
+    let new_lifetime_param = generate_unique_lifetime_param_name(&fn_def.type_param_list())?;
+    let end_of_fn_ident = fn_def.name()?.ident_token()?.text_range().end();
+    let self_param =
+        // use the self if it's a reference and has no explicit lifetime
+        param_list.self_param().filter(|p| p.lifetime_token().is_none() && p.amp_token().is_some());
+    // compute the location which implicitly has the same lifetime as the anonymous lifetime
+    let loc_needing_lifetime = if let Some(self_param) = self_param {
+        // if we have a self reference, use that
+        Some(self_param.self_token()?.text_range().start())
+    } else {
+        // otherwise, if there's a single reference parameter without a named liftime, use that
+        let fn_params_without_lifetime: Vec<_> = param_list
+            .params()
+            .filter_map(|param| match param.ascribed_type() {
+                Some(TypeRef::ReferenceType(ascribed_type))
+                    if ascribed_type.lifetime_token() == None =>
+                {
+                    Some(ascribed_type.amp_token()?.text_range().end())
+                }
+                _ => None,
+            })
+            .collect();
+        match fn_params_without_lifetime.len() {
+            1 => Some(fn_params_without_lifetime.into_iter().nth(0)?),
+            0 => None,
+            // multiple unnnamed is invalid. assist is not applicable
+            _ => return None,
         }
-        None => 'a',
     };
-    acc.add(
-        AssistId("change_lifetime_anon_to_named"),
-        "Give anonymous lifetime a name",
-        lifetime_arg.syntax().text_range(),
-        |builder| {
-            match impl_def.type_param_list() {
-                Some(type_params) => {
-                    builder.insert(
-                        (u32::from(type_params.syntax().text_range().end()) - 1).into(),
-                        format!(", '{}", new_lifetime_param),
-                    );
-                }
-                None => {
-                    builder
-                        .insert(impl_kw.text_range().end(), format!("<'{}>", new_lifetime_param));
-                }
-            }
-            builder.replace(lifetime_arg.syntax().text_range(), format!("'{}", new_lifetime_param));
-        },
-    )
+    acc.add(AssistId(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |builder| {
+        add_lifetime_param(fn_def, builder, end_of_fn_ident, new_lifetime_param);
+        builder.replace(lifetime_loc, format!("'{}", new_lifetime_param));
+        loc_needing_lifetime.map(|loc| builder.insert(loc, format!("'{} ", new_lifetime_param)));
+    })
+}
+
+/// Generate the assist for the impl def case
+fn generate_impl_def_assist(
+    acc: &mut Assists,
+    impl_def: &ast::ImplDef,
+    lifetime_loc: TextRange,
+) -> Option<()> {
+    let new_lifetime_param = generate_unique_lifetime_param_name(&impl_def.type_param_list())?;
+    let end_of_impl_kw = impl_def.impl_token()?.text_range().end();
+    acc.add(AssistId(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |builder| {
+        add_lifetime_param(impl_def, builder, end_of_impl_kw, new_lifetime_param);
+        builder.replace(lifetime_loc, format!("'{}", new_lifetime_param));
+    })
+}
+
+/// Given a type parameter list, generate a unique lifetime parameter name
+/// which is not in the list
+fn generate_unique_lifetime_param_name(
+    existing_type_param_list: &Option,
+) -> Option {
+    match existing_type_param_list {
+        Some(type_params) => {
+            let used_lifetime_params: FxHashSet<_> = type_params
+                .lifetime_params()
+                .map(|p| p.syntax().text().to_string()[1..].to_owned())
+                .collect();
+            (b'a'..=b'z').map(char::from).find(|c| !used_lifetime_params.contains(&c.to_string()))
+        }
+        None => Some('a'),
+    }
+}
+
+/// Add the lifetime param to `builder`. If there are type parameters in `type_params_owner`, add it to the end. Otherwise
+/// add new type params brackets with the lifetime parameter at `new_type_params_loc`.
+fn add_lifetime_param(
+    type_params_owner: &TypeParamsOwner,
+    builder: &mut AssistBuilder,
+    new_type_params_loc: TextSize,
+    new_lifetime_param: char,
+) {
+    match type_params_owner.type_param_list() {
+        // add the new lifetime parameter to an existing type param list
+        Some(type_params) => {
+            builder.insert(
+                (u32::from(type_params.syntax().text_range().end()) - 1).into(),
+                format!(", '{}", new_lifetime_param),
+            );
+        }
+        // create a new type param list containing only the new lifetime parameter
+        None => {
+            builder.insert(new_type_params_loc, format!("<'{}>", new_lifetime_param));
+        }
+    }
 }
 
 #[cfg(test)]
@@ -117,10 +179,36 @@ mod tests {
     }
 
     #[test]
-    fn test_not_applicable() {
+    fn test_example_case_cursor_after_tick() {
+        check_assist(
+            change_lifetime_anon_to_named,
+            r#"impl Cursor<'<|>_> {"#,
+            r#"impl<'a> Cursor<'a> {"#,
+        );
+    }
+
+    #[test]
+    fn test_example_case_cursor_before_tick() {
+        check_assist(
+            change_lifetime_anon_to_named,
+            r#"impl Cursor<<|>'_> {"#,
+            r#"impl<'a> Cursor<'a> {"#,
+        );
+    }
+
+    #[test]
+    fn test_not_applicable_cursor_position() {
         check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<'_><|> {"#);
         check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<|><'_> {"#);
+    }
+
+    #[test]
+    fn test_not_applicable_lifetime_already_name() {
         check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<'a<|>> {"#);
+        check_assist_not_applicable(
+            change_lifetime_anon_to_named,
+            r#"fn my_fun<'a>() -> X<'a<|>>"#,
+        );
     }
 
     #[test]
@@ -140,4 +228,76 @@ mod tests {
             r#"impl<'a, 'b, 'c> Cursor<'a, 'b, 'c>"#,
         );
     }
+
+    #[test]
+    fn test_function_return_value_anon_lifetime_param() {
+        check_assist(
+            change_lifetime_anon_to_named,
+            r#"fn my_fun() -> X<'_<|>>"#,
+            r#"fn my_fun<'a>() -> X<'a>"#,
+        );
+    }
+
+    #[test]
+    fn test_function_return_value_anon_reference_lifetime() {
+        check_assist(
+            change_lifetime_anon_to_named,
+            r#"fn my_fun() -> &'_<|> X"#,
+            r#"fn my_fun<'a>() -> &'a X"#,
+        );
+    }
+
+    #[test]
+    fn test_function_param_anon_lifetime() {
+        check_assist(
+            change_lifetime_anon_to_named,
+            r#"fn my_fun(x: X<'_<|>>)"#,
+            r#"fn my_fun<'a>(x: X<'a>)"#,
+        );
+    }
+
+    #[test]
+    fn test_function_add_lifetime_to_params() {
+        check_assist(
+            change_lifetime_anon_to_named,
+            r#"fn my_fun(f: &Foo) -> X<'_<|>>"#,
+            r#"fn my_fun<'a>(f: &'a Foo) -> X<'a>"#,
+        );
+    }
+
+    #[test]
+    fn test_function_add_lifetime_to_params_in_presence_of_other_lifetime() {
+        check_assist(
+            change_lifetime_anon_to_named,
+            r#"fn my_fun<'other>(f: &Foo, b: &'other Bar) -> X<'_<|>>"#,
+            r#"fn my_fun<'other, 'a>(f: &'a Foo, b: &'other Bar) -> X<'a>"#,
+        );
+    }
+
+    #[test]
+    fn test_function_not_applicable_without_self_and_multiple_unnamed_param_lifetimes() {
+        // this is not permitted under lifetime elision rules
+        check_assist_not_applicable(
+            change_lifetime_anon_to_named,
+            r#"fn my_fun(f: &Foo, b: &Bar) -> X<'_<|>>"#,
+        );
+    }
+
+    #[test]
+    fn test_function_add_lifetime_to_self_ref_param() {
+        check_assist(
+            change_lifetime_anon_to_named,
+            r#"fn my_fun<'other>(&self, f: &Foo, b: &'other Bar) -> X<'_<|>>"#,
+            r#"fn my_fun<'other, 'a>(&'a self, f: &Foo, b: &'other Bar) -> X<'a>"#,
+        );
+    }
+
+    #[test]
+    fn test_function_add_lifetime_to_param_with_non_ref_self() {
+        check_assist(
+            change_lifetime_anon_to_named,
+            r#"fn my_fun<'other>(self, f: &Foo, b: &'other Bar) -> X<'_<|>>"#,
+            r#"fn my_fun<'other, 'a>(self, f: &'a Foo, b: &'other Bar) -> X<'a>"#,
+        );
+    }
 }

From 7d0586cb15000193941f93d4b5281e56ef751edd Mon Sep 17 00:00:00 2001
From: Florian Diebold 
Date: Fri, 29 May 2020 16:03:06 +0200
Subject: [PATCH 096/163] Use first match branch in case of type mismatch, not
 last

The comment says this was intentional, but I do agree with #4304 that it makes
more sense the other way around (for if/else as well).

Fixes #4304.
---
 crates/ra_hir_ty/src/infer/coerce.rs | 4 +---
 crates/ra_hir_ty/src/tests/simple.rs | 2 +-
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/crates/ra_hir_ty/src/infer/coerce.rs b/crates/ra_hir_ty/src/infer/coerce.rs
index 2ee9adb16425..32c7c57cd61a 100644
--- a/crates/ra_hir_ty/src/infer/coerce.rs
+++ b/crates/ra_hir_ty/src/infer/coerce.rs
@@ -45,9 +45,7 @@ impl<'a> InferenceContext<'a> {
                 self.coerce_merge_branch(&ptr_ty1, &ptr_ty2)
             } else {
                 mark::hit!(coerce_merge_fail_fallback);
-                // For incompatible types, we use the latter one as result
-                // to be better recovery for `if` without `else`.
-                ty2.clone()
+                ty1.clone()
             }
         }
     }
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index fd2208af280e..daa9cc953f81 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -937,7 +937,7 @@ fn main(foo: Foo) {
     51..107 'if tru...     }': ()
     54..58 'true': bool
     59..67 '{      }': ()
-    73..107 'if fal...     }': ()
+    73..107 'if fal...     }': i32
     76..81 'false': bool
     82..107 '{     ...     }': i32
     92..95 'foo': Foo

From ab28f6c24909f2424c6f1d85d1518fa4269fbaae Mon Sep 17 00:00:00 2001
From: Florian Diebold 
Date: Fri, 29 May 2020 16:49:52 +0200
Subject: [PATCH 097/163] Upgrade Chalk

Fixes #4072.
---
 Cargo.lock                           | 20 +++--------
 crates/ra_hir_ty/Cargo.toml          |  4 +--
 crates/ra_hir_ty/src/tests/traits.rs | 54 +++++++++++++++++++++++++---
 3 files changed, 56 insertions(+), 22 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 1159d0f1d57d..def4ed45e04e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -111,18 +111,10 @@ version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
 
-[[package]]
-name = "chalk-base"
-version = "0.10.1-dev"
-source = "git+https://github.com/rust-lang/chalk.git?rev=5a3b871ca17529ab5aa5787594fabad1634936cb#5a3b871ca17529ab5aa5787594fabad1634936cb"
-dependencies = [
- "lazy_static",
-]
-
 [[package]]
 name = "chalk-derive"
 version = "0.10.1-dev"
-source = "git+https://github.com/rust-lang/chalk.git?rev=5a3b871ca17529ab5aa5787594fabad1634936cb#5a3b871ca17529ab5aa5787594fabad1634936cb"
+source = "git+https://github.com/rust-lang/chalk.git?rev=329b7f3fdd2431ed6f6778cde53f22374c7d094c#329b7f3fdd2431ed6f6778cde53f22374c7d094c"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -133,9 +125,8 @@ dependencies = [
 [[package]]
 name = "chalk-engine"
 version = "0.10.1-dev"
-source = "git+https://github.com/rust-lang/chalk.git?rev=5a3b871ca17529ab5aa5787594fabad1634936cb#5a3b871ca17529ab5aa5787594fabad1634936cb"
+source = "git+https://github.com/rust-lang/chalk.git?rev=329b7f3fdd2431ed6f6778cde53f22374c7d094c#329b7f3fdd2431ed6f6778cde53f22374c7d094c"
 dependencies = [
- "chalk-base",
  "chalk-derive",
  "chalk-ir",
  "rustc-hash",
@@ -144,18 +135,17 @@ dependencies = [
 [[package]]
 name = "chalk-ir"
 version = "0.10.1-dev"
-source = "git+https://github.com/rust-lang/chalk.git?rev=5a3b871ca17529ab5aa5787594fabad1634936cb#5a3b871ca17529ab5aa5787594fabad1634936cb"
+source = "git+https://github.com/rust-lang/chalk.git?rev=329b7f3fdd2431ed6f6778cde53f22374c7d094c#329b7f3fdd2431ed6f6778cde53f22374c7d094c"
 dependencies = [
- "chalk-base",
  "chalk-derive",
+ "lazy_static",
 ]
 
 [[package]]
 name = "chalk-solve"
 version = "0.10.1-dev"
-source = "git+https://github.com/rust-lang/chalk.git?rev=5a3b871ca17529ab5aa5787594fabad1634936cb#5a3b871ca17529ab5aa5787594fabad1634936cb"
+source = "git+https://github.com/rust-lang/chalk.git?rev=329b7f3fdd2431ed6f6778cde53f22374c7d094c#329b7f3fdd2431ed6f6778cde53f22374c7d094c"
 dependencies = [
- "chalk-base",
  "chalk-derive",
  "chalk-engine",
  "chalk-ir",
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml
index 6afed58a11f0..4b8dcdc07ff5 100644
--- a/crates/ra_hir_ty/Cargo.toml
+++ b/crates/ra_hir_ty/Cargo.toml
@@ -27,8 +27,8 @@ test_utils = { path = "../test_utils" }
 
 scoped-tls = "1"
 
-chalk-solve =   { git = "https://github.com/rust-lang/chalk.git", rev = "5a3b871ca17529ab5aa5787594fabad1634936cb" }
-chalk-ir =      { git = "https://github.com/rust-lang/chalk.git", rev = "5a3b871ca17529ab5aa5787594fabad1634936cb" }
+chalk-solve =   { git = "https://github.com/rust-lang/chalk.git", rev = "329b7f3fdd2431ed6f6778cde53f22374c7d094c" }
+chalk-ir =      { git = "https://github.com/rust-lang/chalk.git", rev = "329b7f3fdd2431ed6f6778cde53f22374c7d094c" }
 
 [dev-dependencies]
 insta = "0.16.0"
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index 0419bc751b20..e8778d419be7 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -2665,7 +2665,6 @@ fn test() {
     Enum::Variant.test();
 }
 "#, true),
-        // wrong result, because the built-in Copy impl for fn defs doesn't exist in Chalk yet
         @r###"
     42..44 '{}': ()
     61..62 'T': {unknown}
@@ -2674,13 +2673,13 @@ fn test() {
     146..150 'self': &Self
     202..282 '{     ...t(); }': ()
     208..211 'foo': fn foo()
-    208..218 'foo.test()': {unknown}
+    208..218 'foo.test()': bool
     224..227 'bar': fn bar<{unknown}>({unknown}) -> {unknown}
-    224..234 'bar.test()': {unknown}
+    224..234 'bar.test()': bool
     240..246 'Struct': Struct(usize) -> Struct
-    240..253 'Struct.test()': {unknown}
+    240..253 'Struct.test()': bool
     259..272 'Enum::Variant': Variant(usize) -> Enum
-    259..279 'Enum::...test()': {unknown}
+    259..279 'Enum::...test()': bool
     "###
     );
 }
@@ -2754,3 +2753,48 @@ fn test() {
     "###
     );
 }
+
+#[test]
+fn integer_range_iterate() {
+    let t = type_at(
+        r#"
+//- /main.rs crate:main deps:std
+fn test() {
+    for x in 0..100 { x<|>; }
+}
+
+//- /std.rs crate:std
+pub mod ops {
+    pub struct Range {
+        pub start: Idx,
+        pub end: Idx,
+    }
+}
+
+pub mod iter {
+    pub trait Iterator {
+        type Item;
+    }
+
+    pub trait IntoIterator {
+        type Item;
+        type IntoIter: Iterator;
+    }
+
+    impl IntoIterator for T where T: Iterator {
+        type Item = ::Item;
+        type IntoIter = Self;
+    }
+}
+
+trait Step {}
+impl Step for i32 {}
+impl Step for i64 {}
+
+impl iter::Iterator for ops::Range {
+    type Item = A;
+}
+"#,
+    );
+    assert_eq!(t, "i32");
+}

From 6f67a46a6a264ac7985a10ee19fbf9bbaef924bc Mon Sep 17 00:00:00 2001
From: Florian Diebold 
Date: Fri, 29 May 2020 17:35:57 +0200
Subject: [PATCH 098/163] Fix match ergonomics in closure parameters

Fixes #4476.
---
 crates/ra_hir_ty/src/infer/expr.rs     | 16 ++++++---
 crates/ra_hir_ty/src/tests/patterns.rs | 50 ++++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 5 deletions(-)

diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index 54bab3476c7d..78084cb573cb 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -140,13 +140,13 @@ impl<'a> InferenceContext<'a> {
 
                 let mut sig_tys = Vec::new();
 
-                for (arg_pat, arg_type) in args.iter().zip(arg_types.iter()) {
-                    let expected = if let Some(type_ref) = arg_type {
+                // collect explicitly written argument types
+                for arg_type in arg_types.iter() {
+                    let arg_ty = if let Some(type_ref) = arg_type {
                         self.make_ty(type_ref)
                     } else {
-                        Ty::Unknown
+                        self.table.new_type_var()
                     };
-                    let arg_ty = self.infer_pat(*arg_pat, &expected, BindingMode::default());
                     sig_tys.push(arg_ty);
                 }
 
@@ -158,7 +158,7 @@ impl<'a> InferenceContext<'a> {
                 sig_tys.push(ret_ty.clone());
                 let sig_ty = Ty::apply(
                     TypeCtor::FnPtr { num_args: sig_tys.len() as u16 - 1 },
-                    Substs(sig_tys.into()),
+                    Substs(sig_tys.clone().into()),
                 );
                 let closure_ty =
                     Ty::apply_one(TypeCtor::Closure { def: self.owner, expr: tgt_expr }, sig_ty);
@@ -168,6 +168,12 @@ impl<'a> InferenceContext<'a> {
                 // infer the body.
                 self.coerce(&closure_ty, &expected.ty);
 
+                // Now go through the argument patterns
+                for (arg_pat, arg_ty) in args.iter().zip(sig_tys) {
+                    let resolved = self.resolve_ty_as_possible(arg_ty);
+                    self.infer_pat(*arg_pat, &resolved, BindingMode::default());
+                }
+
                 let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
                 let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
 
diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs
index 0c5f972a2c6e..fe62587c0d70 100644
--- a/crates/ra_hir_ty/src/tests/patterns.rs
+++ b/crates/ra_hir_ty/src/tests/patterns.rs
@@ -520,3 +520,53 @@ fn main() {
         105..107 '()': ()
     ")
 }
+
+#[test]
+fn match_ergonomics_in_closure_params() {
+    assert_snapshot!(
+        infer(r#"
+#[lang = "fn_once"]
+trait FnOnce {
+    type Output;
+}
+
+fn foo U>(t: T, f: F) -> U { loop {} }
+
+fn test() {
+    foo(&(1, "a"), |&(x, y)| x); // normal, no match ergonomics
+    foo(&(1, "a"), |(x, y)| x);
+}
+"#),
+        @r###"
+    94..95 't': T
+    100..101 'f': F
+    111..122 '{ loop {} }': U
+    113..120 'loop {}': !
+    118..120 '{}': ()
+    134..233 '{     ... x); }': ()
+    140..143 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32
+    140..167 'foo(&(...y)| x)': i32
+    144..153 '&(1, "a")': &(i32, &str)
+    145..153 '(1, "a")': (i32, &str)
+    146..147 '1': i32
+    149..152 '"a"': &str
+    155..166 '|&(x, y)| x': |&(i32, &str)| -> i32
+    156..163 '&(x, y)': &(i32, &str)
+    157..163 '(x, y)': (i32, &str)
+    158..159 'x': i32
+    161..162 'y': &str
+    165..166 'x': i32
+    204..207 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32
+    204..230 'foo(&(...y)| x)': &i32
+    208..217 '&(1, "a")': &(i32, &str)
+    209..217 '(1, "a")': (i32, &str)
+    210..211 '1': i32
+    213..216 '"a"': &str
+    219..229 '|(x, y)| x': |&(i32, &str)| -> &i32
+    220..226 '(x, y)': (i32, &str)
+    221..222 'x': &i32
+    224..225 'y': &&str
+    228..229 'x': &i32
+    "###
+    );
+}

From 18aa4bcb0328393db0c20de0241e1e9606901144 Mon Sep 17 00:00:00 2001
From: Roland Ruckerbauer 
Date: Fri, 29 May 2020 21:17:14 +0200
Subject: [PATCH 099/163] Add semantic highlight to QUESTION token

Made it an operator with controlFlow modifier.
---
 crates/ra_ide/src/syntax_highlighting.rs      | 1 +
 crates/ra_ide/src/syntax_highlighting/tags.rs | 2 ++
 crates/rust-analyzer/src/to_proto.rs          | 1 +
 3 files changed, 4 insertions(+)

diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 8a995d779bae..cd6464b40575 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -391,6 +391,7 @@ fn highlight_element(
         INT_NUMBER | FLOAT_NUMBER => HighlightTag::NumericLiteral.into(),
         BYTE => HighlightTag::ByteLiteral.into(),
         CHAR => HighlightTag::CharLiteral.into(),
+        QUESTION => Highlight::new(HighlightTag::Operator) | HighlightModifier::ControlFlow,
         LIFETIME => {
             let h = Highlight::new(HighlightTag::Lifetime);
             match element.parent().map(|it| it.kind()) {
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index 46c718c91e23..1514531de2e3 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -42,6 +42,7 @@ pub enum HighlightTag {
     Local,
     UnresolvedReference,
     FormatSpecifier,
+    Operator,
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -89,6 +90,7 @@ impl HighlightTag {
             HighlightTag::Local => "variable",
             HighlightTag::UnresolvedReference => "unresolved_reference",
             HighlightTag::FormatSpecifier => "format_specifier",
+            HighlightTag::Operator => "operator",
         }
     }
 }
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 8e8e7033db76..2fbbb4e632d9 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -317,6 +317,7 @@ fn semantic_token_type_and_modifiers(
         HighlightTag::Keyword => lsp_types::SemanticTokenType::KEYWORD,
         HighlightTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE,
         HighlightTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER,
+        HighlightTag::Operator => lsp_types::SemanticTokenType::OPERATOR,
     };
 
     for modifier in highlight.modifiers.iter() {

From 49f674480a54823c81390c15e37eddfdab5ea7ec Mon Sep 17 00:00:00 2001
From: Stephan Seitz 
Date: Fri, 29 May 2020 23:41:04 +0200
Subject: [PATCH 100/163] Fix typo in docs/dev/lsp-extensions.md:
 automagiacally -> automagically

---
 docs/dev/lsp-extensions.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 68a5df27e7c3..dbc95be387c5 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -154,7 +154,7 @@ fn main() {
 }
 ```
 
-`experimental/joinLines` yields (curly braces are automagiacally removed)
+`experimental/joinLines` yields (curly braces are automagically removed)
 
 ```rust
 fn main() {

From a27ede88a3a9063a3a2265b668b1c41b589852f5 Mon Sep 17 00:00:00 2001
From: Roland Ruckerbauer 
Date: Sat, 30 May 2020 14:09:10 +0200
Subject: [PATCH 101/163] Fix problem with format string tokenization

Fixed by just not handling closing curlybrace escaping.
---
 crates/ra_syntax/src/ast/tokens.rs | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs
index 3cd6d99c3889..04b0a4480375 100644
--- a/crates/ra_syntax/src/ast/tokens.rs
+++ b/crates/ra_syntax/src/ast/tokens.rs
@@ -418,14 +418,9 @@ pub trait HasFormatSpecifier: AstToken {
 
                     let mut cloned = chars.clone().take(2);
                     let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied();
-                    let second = cloned.next().and_then(|next| next.1.as_ref().ok()).copied();
                     if first != Some('}') {
                         continue;
                     }
-                    if second == Some('}') {
-                        // Escaped format end specifier, `}}`
-                        continue;
-                    }
                     skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback);
                 }
                 _ => {

From 780c89959a2f7362eb51508d83863eeff9a49e4c Mon Sep 17 00:00:00 2001
From: Roland Ruckerbauer 
Date: Sat, 30 May 2020 18:35:11 +0200
Subject: [PATCH 102/163] Test case for format string highlighting of closing
 curlybrace

---
 crates/ra_ide/src/snapshots/highlight_strings.html | 1 +
 crates/ra_ide/src/syntax_highlighting/tests.rs     | 1 +
 2 files changed, 2 insertions(+)

diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html
index 326744361c68..41cddd0ff261 100644
--- a/crates/ra_ide/src/snapshots/highlight_strings.html
+++ b/crates/ra_ide/src/snapshots/highlight_strings.html
@@ -52,6 +52,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     println!("{argument}", argument = "test");   // => "test"
     println!("{name} {}", 1, name = 2);          // => "2 1"
     println!("{a} {c} {b}", a="a", b='b', c=3);  // => "a 3 b"
+    println!("{{{}}}", 2);                       // => "{2}"
     println!("Hello {:5}!", "x");
     println!("Hello {:1$}!", "x", 5);
     println!("Hello {1:0$}!", 5, "x");
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index eb43a23da682..7dc229cab73d 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -218,6 +218,7 @@ fn main() {
     println!("{argument}", argument = "test");   // => "test"
     println!("{name} {}", 1, name = 2);          // => "2 1"
     println!("{a} {c} {b}", a="a", b='b', c=3);  // => "a 3 b"
+    println!("{{{}}}", 2);                       // => "{2}"
     println!("Hello {:5}!", "x");
     println!("Hello {:1$}!", "x", 5);
     println!("Hello {1:0$}!", 5, "x");

From e1829d8959d9f48302fec42d64216c158db13744 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Sat, 30 May 2020 23:57:16 +0200
Subject: [PATCH 103/163] minor

---
 xtask/src/codegen/gen_assists_docs.rs | 144 +++++++++++++-------------
 1 file changed, 73 insertions(+), 71 deletions(-)

diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs
index 4bd6b5f0ca8c..20dcde820685 100644
--- a/xtask/src/codegen/gen_assists_docs.rs
+++ b/xtask/src/codegen/gen_assists_docs.rs
@@ -8,7 +8,7 @@ use crate::{
 };
 
 pub fn generate_assists_docs(mode: Mode) -> Result<()> {
-    let assists = collect_assists()?;
+    let assists = Assist::collect()?;
     generate_tests(&assists, mode)?;
     generate_docs(&assists, mode)?;
     Ok(())
@@ -22,81 +22,61 @@ struct Assist {
     after: String,
 }
 
-fn hide_hash_comments(text: &str) -> String {
-    text.split('\n') // want final newline
-        .filter(|&it| !(it.starts_with("# ") || it == "#"))
-        .map(|it| format!("{}\n", it))
-        .collect()
-}
-
-fn reveal_hash_comments(text: &str) -> String {
-    text.split('\n') // want final newline
-        .map(|it| {
-            if it.starts_with("# ") {
-                &it[2..]
-            } else if it == "#" {
-                ""
-            } else {
-                it
-            }
-        })
-        .map(|it| format!("{}\n", it))
-        .collect()
-}
-
-fn collect_assists() -> Result> {
-    let mut res = Vec::new();
-    for path in rust_files(&project_root().join(codegen::ASSISTS_DIR)) {
-        collect_file(&mut res, path.as_path())?;
-    }
-    res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id));
-    return Ok(res);
-
-    fn collect_file(acc: &mut Vec, path: &Path) -> Result<()> {
-        let text = fs::read_to_string(path)?;
-        let comment_blocks = extract_comment_blocks_with_empty_lines(&text);
-
-        for block in comment_blocks {
-            // FIXME: doesn't support blank lines yet, need to tweak
-            // `extract_comment_blocks` for that.
-            let mut lines = block.iter();
-            let first_line = lines.next().unwrap();
-            if !first_line.starts_with("Assist: ") {
-                continue;
-            }
-            let id = first_line["Assist: ".len()..].to_string();
-            assert!(
-                id.chars().all(|it| it.is_ascii_lowercase() || it == '_'),
-                "invalid assist id: {:?}",
-                id
-            );
-
-            let doc = take_until(lines.by_ref(), "```").trim().to_string();
-            assert!(
-                doc.chars().next().unwrap().is_ascii_uppercase() && doc.ends_with('.'),
-                "\n\n{}: assist docs should be proper sentences, with capitalization and a full stop at the end.\n\n{}\n\n",
-                id, doc,
-            );
-
-            let before = take_until(lines.by_ref(), "```");
-
-            assert_eq!(lines.next().unwrap().as_str(), "->");
-            assert_eq!(lines.next().unwrap().as_str(), "```");
-            let after = take_until(lines.by_ref(), "```");
-            acc.push(Assist { id, doc, before, after })
+impl Assist {
+    fn collect() -> Result> {
+        let mut res = Vec::new();
+        for path in rust_files(&project_root().join(codegen::ASSISTS_DIR)) {
+            collect_file(&mut res, path.as_path())?;
         }
+        res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id));
+        return Ok(res);
 
-        fn take_until<'a>(lines: impl Iterator, marker: &str) -> String {
-            let mut buf = Vec::new();
-            for line in lines {
-                if line == marker {
-                    break;
+        fn collect_file(acc: &mut Vec, path: &Path) -> Result<()> {
+            let text = fs::read_to_string(path)?;
+            let comment_blocks = extract_comment_blocks_with_empty_lines(&text);
+
+            for block in comment_blocks {
+                // FIXME: doesn't support blank lines yet, need to tweak
+                // `extract_comment_blocks` for that.
+                let mut lines = block.iter();
+                let first_line = lines.next().unwrap();
+                if !first_line.starts_with("Assist: ") {
+                    continue;
                 }
-                buf.push(line.clone());
+                let id = first_line["Assist: ".len()..].to_string();
+                assert!(
+                    id.chars().all(|it| it.is_ascii_lowercase() || it == '_'),
+                    "invalid assist id: {:?}",
+                    id
+                );
+
+                let doc = take_until(lines.by_ref(), "```").trim().to_string();
+                assert!(
+                    doc.chars().next().unwrap().is_ascii_uppercase() && doc.ends_with('.'),
+                    "\n\n{}: assist docs should be proper sentences, with capitalization and a full stop at the end.\n\n{}\n\n",
+                    id, doc,
+                );
+
+                let before = take_until(lines.by_ref(), "```");
+
+                assert_eq!(lines.next().unwrap().as_str(), "->");
+                assert_eq!(lines.next().unwrap().as_str(), "```");
+                let after = take_until(lines.by_ref(), "```");
+                acc.push(Assist { id, doc, before, after })
             }
-            buf.join("\n")
+
+            fn take_until<'a>(lines: impl Iterator, marker: &str) -> String {
+                let mut buf = Vec::new();
+                for line in lines {
+                    if line == marker {
+                        break;
+                    }
+                    buf.push(line.clone());
+                }
+                buf.join("\n")
+            }
+            Ok(())
         }
-        Ok(())
     }
 }
 
@@ -157,3 +137,25 @@ fn generate_docs(assists: &[Assist], mode: Mode) -> Result<()> {
 
     codegen::update(&project_root().join(codegen::ASSISTS_DOCS), &buf, mode)
 }
+
+fn hide_hash_comments(text: &str) -> String {
+    text.split('\n') // want final newline
+        .filter(|&it| !(it.starts_with("# ") || it == "#"))
+        .map(|it| format!("{}\n", it))
+        .collect()
+}
+
+fn reveal_hash_comments(text: &str) -> String {
+    text.split('\n') // want final newline
+        .map(|it| {
+            if it.starts_with("# ") {
+                &it[2..]
+            } else if it == "#" {
+                ""
+            } else {
+                it
+            }
+        })
+        .map(|it| format!("{}\n", it))
+        .collect()
+}

From 383247a9ae8202f20ce6f01d1429c1cd2a11d516 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Sun, 31 May 2020 00:33:37 +0200
Subject: [PATCH 104/163] Generalize

---
 xtask/src/codegen.rs                  | 20 ++++++++++++++++++--
 xtask/src/codegen/gen_assists_docs.rs | 10 +++-------
 2 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index b4907f4b2b08..2e8fd34943e5 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -61,8 +61,24 @@ fn extract_comment_blocks(text: &str) -> Vec> {
     do_extract_comment_blocks(text, false)
 }
 
-fn extract_comment_blocks_with_empty_lines(text: &str) -> Vec> {
-    do_extract_comment_blocks(text, true)
+fn extract_comment_blocks_with_empty_lines(tag: &str, text: &str) -> Vec {
+    assert!(tag.starts_with(char::is_uppercase));
+    let tag = format!("{}:", tag);
+    let mut res = Vec::new();
+    for mut block in do_extract_comment_blocks(text, true) {
+        let first = block.remove(0);
+        if first.starts_with(&tag) {
+            let id = first[tag.len()..].trim().to_string();
+            let block = CommentBlock { id, contents: block };
+            res.push(block);
+        }
+    }
+    res
+}
+
+struct CommentBlock {
+    id: String,
+    contents: Vec,
 }
 
 fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lines: bool) -> Vec> {
diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs
index 20dcde820685..6ebeb8aea4e7 100644
--- a/xtask/src/codegen/gen_assists_docs.rs
+++ b/xtask/src/codegen/gen_assists_docs.rs
@@ -33,22 +33,18 @@ impl Assist {
 
         fn collect_file(acc: &mut Vec, path: &Path) -> Result<()> {
             let text = fs::read_to_string(path)?;
-            let comment_blocks = extract_comment_blocks_with_empty_lines(&text);
+            let comment_blocks = extract_comment_blocks_with_empty_lines("Assist", &text);
 
             for block in comment_blocks {
                 // FIXME: doesn't support blank lines yet, need to tweak
                 // `extract_comment_blocks` for that.
-                let mut lines = block.iter();
-                let first_line = lines.next().unwrap();
-                if !first_line.starts_with("Assist: ") {
-                    continue;
-                }
-                let id = first_line["Assist: ".len()..].to_string();
+                let id = block.id;
                 assert!(
                     id.chars().all(|it| it.is_ascii_lowercase() || it == '_'),
                     "invalid assist id: {:?}",
                     id
                 );
+                let mut lines = block.contents.iter();
 
                 let doc = take_until(lines.by_ref(), "```").trim().to_string();
                 assert!(

From c8f27a4a886413a15a2a6af4a87b39b901c873a8 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Sun, 31 May 2020 01:54:54 +0200
Subject: [PATCH 105/163] Generate features docs from source

---
 crates/ra_ide/src/display/structure.rs        | 13 +++
 crates/ra_ide/src/extend_selection.rs         | 10 ++
 crates/ra_ide/src/goto_definition.rs          |  9 ++
 .../src/{impls.rs => goto_implementation.rs}  |  9 ++
 crates/ra_ide/src/goto_type_definition.rs     |  3 +
 crates/ra_ide/src/lib.rs                      |  4 +-
 crates/ra_ide/src/typing.rs                   |  7 ++
 crates/ra_ide_db/src/symbol_index.rs          | 21 ++++
 docs/user/features.md                         | 52 ----------
 docs/user/generated_features.adoc             | 98 +++++++++++++++++++
 docs/user/readme.adoc                         |  5 +-
 xtask/src/codegen.rs                          |  5 +-
 xtask/src/codegen/gen_feature_docs.rs         | 72 ++++++++++++++
 xtask/src/main.rs                             |  1 +
 xtask/tests/tidy.rs                           |  7 ++
 15 files changed, 258 insertions(+), 58 deletions(-)
 rename crates/ra_ide/src/{impls.rs => goto_implementation.rs} (96%)
 create mode 100644 docs/user/generated_features.adoc
 create mode 100644 xtask/src/codegen/gen_feature_docs.rs

diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs
index 967eee5d2328..3f59b89bb42e 100644
--- a/crates/ra_ide/src/display/structure.rs
+++ b/crates/ra_ide/src/display/structure.rs
@@ -18,6 +18,19 @@ pub struct StructureNode {
     pub deprecated: bool,
 }
 
+// Feature: File Structure
+//
+// Provides a tree of the symbols defined in the file. Can be used to
+//
+// * fuzzy search symbol in a file (super useful)
+// * draw breadcrumbs to describe the context around the cursor
+// * draw outline of the file
+//
+// |===
+// | Editor  | Shortcut
+//
+// | VS Code | kbd:[Ctrl+Shift+O]
+// |===
 pub fn file_structure(file: &SourceFile) -> Vec {
     let mut res = Vec::new();
     let mut stack = Vec::new();
diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs
index 554594a436ce..cee0a19e191d 100644
--- a/crates/ra_ide/src/extend_selection.rs
+++ b/crates/ra_ide/src/extend_selection.rs
@@ -14,6 +14,16 @@ use ra_syntax::{
 
 use crate::FileRange;
 
+// Feature: Extend Selection
+//
+// Extends the current selection to the encompassing syntactic construct
+// (expression, statement, item, module, etc). It works with multiple cursors.
+//
+// |===
+// | Editor  | Shortcut
+//
+// | VS Code | kbd:[Ctrl+Shift+→]
+// |===
 pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange {
     let sema = Semantics::new(db);
     let src = sema.parse(frange.file_id);
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index 90e85d419712..83ea5092c81b 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -17,6 +17,15 @@ use crate::{
     FilePosition, NavigationTarget, RangeInfo,
 };
 
+// Feature: Go To Definition
+//
+// Navigates to the definition of an identifier.
+//
+// |===
+// | Editor  | Shortcut
+//
+// | VS Code | kbd:[F12]
+// |===
 pub(crate) fn goto_definition(
     db: &RootDatabase,
     position: FilePosition,
diff --git a/crates/ra_ide/src/impls.rs b/crates/ra_ide/src/goto_implementation.rs
similarity index 96%
rename from crates/ra_ide/src/impls.rs
rename to crates/ra_ide/src/goto_implementation.rs
index ea2225f70724..a5a296d228b9 100644
--- a/crates/ra_ide/src/impls.rs
+++ b/crates/ra_ide/src/goto_implementation.rs
@@ -6,6 +6,15 @@ use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
 
 use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo};
 
+// Feature: Go To Implementation
+//
+// Navigates to the impl block of structs, enums or traits. Also implemented as a code lens.
+//
+// |===
+// | Editor  | Shortcut
+//
+// | VS Code | kbd:[Ctrl+F12]
+// |===
 pub(crate) fn goto_implementation(
     db: &RootDatabase,
     position: FilePosition,
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs
index a84637489e1f..eeadfa9ee702 100644
--- a/crates/ra_ide/src/goto_type_definition.rs
+++ b/crates/ra_ide/src/goto_type_definition.rs
@@ -5,6 +5,9 @@ use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffs
 
 use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo};
 
+// Feature: Go To Type Definition
+//
+// Navigates to the type of an identifier.
 pub(crate) fn goto_type_definition(
     db: &RootDatabase,
     position: FilePosition,
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index d983cd910023..12d5716e8359 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -23,6 +23,7 @@ mod completion;
 mod runnables;
 mod goto_definition;
 mod goto_type_definition;
+mod goto_implementation;
 mod extend_selection;
 mod hover;
 mod call_hierarchy;
@@ -30,7 +31,6 @@ mod call_info;
 mod syntax_highlighting;
 mod parent_module;
 mod references;
-mod impls;
 mod diagnostics;
 mod syntax_tree;
 mod folding_ranges;
@@ -373,7 +373,7 @@ impl Analysis {
         &self,
         position: FilePosition,
     ) -> Cancelable>>> {
-        self.with_db(|db| impls::goto_implementation(db, position))
+        self.with_db(|db| goto_implementation::goto_implementation(db, position))
     }
 
     /// Returns the type definitions for the symbol at `position`.
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs
index 39bb3b3579c0..67e2c33a000e 100644
--- a/crates/ra_ide/src/typing.rs
+++ b/crates/ra_ide/src/typing.rs
@@ -32,6 +32,13 @@ pub(crate) use on_enter::on_enter;
 
 pub(crate) const TRIGGER_CHARS: &str = ".=>";
 
+// Feature: On Typing Assists
+//
+// Some features trigger on typing certain characters:
+//
+// - typing `let =` tries to smartly add `;` if `=` is followed by an existing expression
+// - Enter inside comments automatically inserts `///`
+// - typing `.` in a chain method call auto-indents
 pub(crate) fn on_char_typed(
     db: &RootDatabase,
     position: FilePosition,
diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs
index 95be11134554..acc31fe3b51b 100644
--- a/crates/ra_ide_db/src/symbol_index.rs
+++ b/crates/ra_ide_db/src/symbol_index.rs
@@ -110,6 +110,27 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc
     Arc::new(SymbolIndex::new(symbols))
 }
 
+// Feature: Workspace Symbol
+//
+// Uses fuzzy-search to find types, modules and functions by name across your
+// project and dependencies. This is **the** most useful feature, which improves code
+// navigation tremendously. It mostly works on top of the built-in LSP
+// functionality, however `#` and `*` symbols can be used to narrow down the
+// search. Specifically,
+//
+// - `Foo` searches for `Foo` type in the current workspace
+// - `foo#` searches for `foo` function in the current workspace
+// - `Foo*` searches for `Foo` type among dependencies, including `stdlib`
+// - `foo#*` searches for `foo` function among dependencies
+//
+// That is, `#` switches from "types" to all symbols, `*` switches from the current
+// workspace to dependencies.
+//
+// |===
+// | Editor  | Shortcut
+//
+// | VS Code | kbd:[Ctrl+T]
+// |===
 pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec {
     /// Need to wrap Snapshot to provide `Clone` impl for `map_with`
     struct Snap(salsa::Snapshot);
diff --git a/docs/user/features.md b/docs/user/features.md
index 12ecdec136a2..4d940225249b 100644
--- a/docs/user/features.md
+++ b/docs/user/features.md
@@ -2,58 +2,6 @@ This document is an index of features that the rust-analyzer language server
 provides. Shortcuts are for the default VS Code layout. If there's no shortcut,
 you can use Ctrl+Shift+P to search for the corresponding action.
 
-### Workspace Symbol ctrl+t
-
-Uses fuzzy-search to find types, modules and functions by name across your
-project and dependencies. This is **the** most useful feature, which improves code
-navigation tremendously. It mostly works on top of the built-in LSP
-functionality, however `#` and `*` symbols can be used to narrow down the
-search. Specifically,
-
-- `Foo` searches for `Foo` type in the current workspace
-- `foo#` searches for `foo` function in the current workspace
-- `Foo*` searches for `Foo` type among dependencies, including `stdlib`
-- `foo#*` searches for `foo` function among dependencies
-
-That is, `#` switches from "types" to all symbols, `*` switches from the current
-workspace to dependencies.
-
-### Document Symbol ctrl+shift+o
-
-Provides a tree of the symbols defined in the file. Can be used to
-
-* fuzzy search symbol in a file (super useful)
-* draw breadcrumbs to describe the context around the cursor
-* draw outline of the file
-
-### On Typing Assists
-
-Some features trigger on typing certain characters:
-
-- typing `let =` tries to smartly add `;` if `=` is followed by an existing expression
-- Enter inside comments automatically inserts `///`
-- typing `.` in a chain method call auto-indents
-
-### Extend Selection
-
-Extends the current selection to the encompassing syntactic construct
-(expression, statement, item, module, etc). It works with multiple cursors. This
-is a relatively new feature of LSP:
-https://github.com/Microsoft/language-server-protocol/issues/613, check your
-editor's LSP library to see if this feature is supported.
-
-### Go to Definition
-
-Navigates to the definition of an identifier.
-
-### Go to Implementation
-
-Navigates to the impl block of structs, enums or traits. Also implemented as a code lens.
-
-### Go to Type Defintion
-
-Navigates to the type of an identifier.
-
 ### Commands ctrl+shift+p
 
 #### Run
diff --git a/docs/user/generated_features.adoc b/docs/user/generated_features.adoc
new file mode 100644
index 000000000000..e1eb5d88a8f6
--- /dev/null
+++ b/docs/user/generated_features.adoc
@@ -0,0 +1,98 @@
+=== Extend Selection
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/extend_selection.rs[extend_selection.rs]
+
+
+Extends the current selection to the encompassing syntactic construct
+(expression, statement, item, module, etc). It works with multiple cursors.
+
+|===
+| Editor  | Shortcut
+
+| VS Code | kbd:[Ctrl+Shift+→]
+|===
+
+
+=== File Structure
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/display/structure.rs[structure.rs]
+
+
+Provides a tree of the symbols defined in the file. Can be used to
+
+* fuzzy search symbol in a file (super useful)
+* draw breadcrumbs to describe the context around the cursor
+* draw outline of the file
+
+|===
+| Editor  | Shortcut
+
+| VS Code | kbd:[Ctrl+Shift+O]
+|===
+
+
+=== Go To Definition
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_definition.rs[goto_definition.rs]
+
+
+Navigates to the definition of an identifier.
+
+|===
+| Editor  | Shortcut
+
+| VS Code | kbd:[F12]
+|===
+
+
+=== Go To Implementation
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_implementation.rs[goto_implementation.rs]
+
+
+Navigates to the impl block of structs, enums or traits. Also implemented as a code lens.
+
+|===
+| Editor  | Shortcut
+
+| VS Code | kbd:[Ctrl+F12]
+|===
+
+
+=== Go To Type Definition
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_type_definition.rs[goto_type_definition.rs]
+
+
+Navigates to the type of an identifier.
+
+
+=== On Typing Assists
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/typing.rs[typing.rs]
+
+
+Some features trigger on typing certain characters:
+
+- typing `let =` tries to smartly add `;` if `=` is followed by an existing expression
+- Enter inside comments automatically inserts `///`
+- typing `.` in a chain method call auto-indents
+
+
+=== Workspace Symbol
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs[symbol_index.rs]
+
+
+Uses fuzzy-search to find types, modules and functions by name across your
+project and dependencies. This is **the** most useful feature, which improves code
+navigation tremendously. It mostly works on top of the built-in LSP
+functionality, however `#` and `*` symbols can be used to narrow down the
+search. Specifically,
+
+- `Foo` searches for `Foo` type in the current workspace
+- `foo#` searches for `foo` function in the current workspace
+- `Foo*` searches for `Foo` type among dependencies, including `stdlib`
+- `foo#*` searches for `foo` function among dependencies
+
+That is, `#` switches from "types" to all symbols, `*` switches from the current
+workspace to dependencies.
+
+|===
+| Editor  | Shortcut
+
+| VS Code | kbd:[Ctrl+T]
+|===
diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc
index 64bd0feb1332..7b159bfc6b7f 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/readme.adoc
@@ -8,6 +8,7 @@
 :important-caption: :heavy_exclamation_mark:
 :caution-caption: :fire:
 :warning-caption: :warning:
+:experimental:
 
 // Master copy of this document lives in the https://github.com/rust-analyzer/rust-analyzer repository
 
@@ -268,6 +269,6 @@ Gnome Builder currently has support for RLS, and there's no way to configure the
 1. Rename, symlink or copy the `rust-analyzer` binary to `rls` and place it somewhere Builder can find (in `PATH`, or under `~/.cargo/bin`).
 2. Enable the Rust Builder plugin.
 
-== Usage
+== Features
 
-See https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/features.md[features.md].
+include::./generated_features.adoc[]
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index 2e8fd34943e5..3e2a66979d9d 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -8,14 +8,15 @@
 mod gen_syntax;
 mod gen_parser_tests;
 mod gen_assists_docs;
+mod gen_feature_docs;
 
 use std::{mem, path::Path};
 
 use crate::{not_bash::fs2, Result};
 
 pub use self::{
-    gen_assists_docs::generate_assists_docs, gen_parser_tests::generate_parser_tests,
-    gen_syntax::generate_syntax,
+    gen_assists_docs::generate_assists_docs, gen_feature_docs::generate_feature_docs,
+    gen_parser_tests::generate_parser_tests, gen_syntax::generate_syntax,
 };
 
 const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar";
diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs
new file mode 100644
index 000000000000..58318564872e
--- /dev/null
+++ b/xtask/src/codegen/gen_feature_docs.rs
@@ -0,0 +1,72 @@
+//! Generates `assists.md` documentation.
+
+use std::{fmt, fs, path::PathBuf};
+
+use crate::{
+    codegen::{self, extract_comment_blocks_with_empty_lines, Mode},
+    project_root, rust_files, Result,
+};
+
+pub fn generate_feature_docs(mode: Mode) -> Result<()> {
+    let features = Feature::collect()?;
+    let contents = features.into_iter().map(|it| it.to_string()).collect::>().join("\n\n");
+
+    let dst = project_root().join("docs/user/generated_features.adoc");
+    codegen::update(&dst, &contents, mode)?;
+    Ok(())
+}
+
+#[derive(Debug)]
+struct Feature {
+    id: String,
+    path: PathBuf,
+    doc: String,
+}
+
+impl Feature {
+    fn collect() -> Result> {
+        let mut res = Vec::new();
+        for path in rust_files(&project_root()) {
+            collect_file(&mut res, path)?;
+        }
+        res.sort_by(|lhs, rhs| lhs.id.cmp(&rhs.id));
+        return Ok(res);
+
+        fn collect_file(acc: &mut Vec, path: PathBuf) -> Result<()> {
+            let text = fs::read_to_string(&path)?;
+            let comment_blocks = extract_comment_blocks_with_empty_lines("Feature", &text);
+
+            for block in comment_blocks {
+                let id = block.id;
+                assert!(
+                    id.split_ascii_whitespace().all(|it| it.starts_with(char::is_uppercase)),
+                    "bad feature: {}",
+                    id
+                );
+                let doc = block.contents.join("\n");
+                acc.push(Feature { id, path: path.clone(), doc })
+            }
+
+            Ok(())
+        }
+    }
+}
+
+impl fmt::Display for Feature {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        writeln!(f, "=== {}", self.id)?;
+        let path = self.path.strip_prefix(&project_root()).unwrap();
+        let name = self.path.file_name().unwrap();
+
+        //FIXME: generate line number as well
+        writeln!(
+            f,
+            "**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/{}[{}]",
+            path.display(),
+            name.to_str().unwrap(),
+        )?;
+
+        writeln!(f, "\n{}", self.doc)?;
+        Ok(())
+    }
+}
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index dff3ce4a1dd6..9d7cdd1145ae 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -75,6 +75,7 @@ FLAGS:
             codegen::generate_syntax(Mode::Overwrite)?;
             codegen::generate_parser_tests(Mode::Overwrite)?;
             codegen::generate_assists_docs(Mode::Overwrite)?;
+            codegen::generate_feature_docs(Mode::Overwrite)?;
             Ok(())
         }
         "format" => {
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs
index 2e9fcf07c5f2..06ff45d994bc 100644
--- a/xtask/tests/tidy.rs
+++ b/xtask/tests/tidy.rs
@@ -30,6 +30,13 @@ fn generated_assists_are_fresh() {
     }
 }
 
+#[test]
+fn generated_features_are_fresh() {
+    if let Err(error) = codegen::generate_feature_docs(Mode::Verify) {
+        panic!("{}. Please update features by running `cargo xtask codegen`", error);
+    }
+}
+
 #[test]
 fn check_code_formatting() {
     if let Err(error) = run_rustfmt(Mode::Verify) {

From 030d78345fa79af07f8ebd89a9d244576fac992b Mon Sep 17 00:00:00 2001
From: veetaha 
Date: Sat, 23 May 2020 04:58:22 +0300
Subject: [PATCH 106/163] Fix invoking cargo without consulting CARGO or
 standard installation paths

---
 Cargo.lock                                     | 1 +
 crates/rust-analyzer/Cargo.toml                | 1 +
 crates/rust-analyzer/src/main_loop/handlers.rs | 8 +++++++-
 editors/code/src/cargo.ts                      | 4 ++--
 editors/code/src/tasks.ts                      | 7 +++++--
 5 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index def4ed45e04e..7de784c1cde3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1364,6 +1364,7 @@ dependencies = [
  "ra_syntax",
  "ra_text_edit",
  "ra_tt",
+ "ra_toolchain",
  "ra_vfs",
  "rand",
  "relative-path",
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 65b487db3b98..2e49448cc6ca 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -48,6 +48,7 @@ hir = { path = "../ra_hir", package = "ra_hir" }
 hir_def = { path = "../ra_hir_def", package = "ra_hir_def" }
 hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" }
 ra_proc_macro_srv = { path = "../ra_proc_macro_srv" }
+ra_toolchain = { path = "../ra_toolchain" }
 
 [target.'cfg(windows)'.dependencies]
 winapi = "0.3.8"
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 1f910ff82b93..1b5b3325c04f 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -40,6 +40,7 @@ use crate::{
     world::WorldSnapshot,
     LspError, Result,
 };
+use anyhow::Context;
 
 pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result {
     let _p = profile("handle_analyzer_status");
@@ -982,10 +983,15 @@ fn to_lsp_runnable(
             target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
         }
     };
+    let cargo_path = ra_toolchain::cargo()
+        .to_str()
+        .context("Path to cargo executable contains invalid UTF8 characters")?
+        .to_owned();
+
     Ok(lsp_ext::Runnable {
         range: to_proto::range(&line_index, runnable.range),
         label,
-        bin: "cargo".to_string(),
+        bin: cargo_path,
         args,
         extra_args,
         env: {
diff --git a/editors/code/src/cargo.ts b/editors/code/src/cargo.ts
index a55b2f860f68..46cd3d777847 100644
--- a/editors/code/src/cargo.ts
+++ b/editors/code/src/cargo.ts
@@ -126,8 +126,8 @@ export class Cargo {
     }
 }
 
-// Mirrors `ra_env::get_path_for_executable` implementation
-function getCargoPathOrFail(): string {
+// Mirrors `ra_toolchain::cargo()` implementation
+export function getCargoPathOrFail(): string {
     const envVar = process.env.CARGO;
     const executableName = "cargo";
 
diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts
index 1366c76d6bda..c22d693623f8 100644
--- a/editors/code/src/tasks.ts
+++ b/editors/code/src/tasks.ts
@@ -1,4 +1,5 @@
 import * as vscode from 'vscode';
+import { getCargoPathOrFail } from "./cargo";
 
 // This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
 // our configuration should be compatible with it so use the same key.
@@ -24,6 +25,8 @@ class CargoTaskProvider implements vscode.TaskProvider {
         // set of tasks that always exist. These tasks cannot be removed in
         // tasks.json - only tweaked.
 
+        const cargoPath = getCargoPathOrFail();
+
         return [
             { command: 'build', group: vscode.TaskGroup.Build },
             { command: 'check', group: vscode.TaskGroup.Build },
@@ -46,7 +49,7 @@ class CargoTaskProvider implements vscode.TaskProvider {
                     `cargo ${command}`,
                     'rust',
                     // What to do when this command is executed.
-                    new vscode.ShellExecution('cargo', [command]),
+                    new vscode.ShellExecution(cargoPath, [command]),
                     // Problem matchers.
                     ['$rustc'],
                 );
@@ -80,4 +83,4 @@ class CargoTaskProvider implements vscode.TaskProvider {
 export function activateTaskProvider(target: vscode.WorkspaceFolder): vscode.Disposable {
     const provider = new CargoTaskProvider(target);
     return vscode.tasks.registerTaskProvider(TASK_TYPE, provider);
-}
\ No newline at end of file
+}

From a419cedb1cc661349a022262c8b03993e063252f Mon Sep 17 00:00:00 2001
From: veetaha 
Date: Sat, 23 May 2020 16:31:56 +0300
Subject: [PATCH 107/163] Fix tests, apply code review proposals

---
 Cargo.lock                                     |  1 +
 crates/ra_proc_macro_srv/Cargo.toml            |  1 +
 crates/ra_proc_macro_srv/src/tests/utils.rs    |  3 +--
 crates/rust-analyzer/src/main_loop/handlers.rs | 17 ++++++++++-------
 crates/rust-analyzer/tests/heavy_tests/main.rs | 14 +++++++++-----
 5 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 7de784c1cde3..e1da335f5f6e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1119,6 +1119,7 @@ dependencies = [
  "memmap",
  "ra_mbe",
  "ra_proc_macro",
+ "ra_toolchain",
  "ra_tt",
  "serde_derive",
  "test_utils",
diff --git a/crates/ra_proc_macro_srv/Cargo.toml b/crates/ra_proc_macro_srv/Cargo.toml
index bb3003278516..5821029450f5 100644
--- a/crates/ra_proc_macro_srv/Cargo.toml
+++ b/crates/ra_proc_macro_srv/Cargo.toml
@@ -22,3 +22,4 @@ cargo_metadata = "0.10.0"
 difference = "2.0.0"
 # used as proc macro test target
 serde_derive = "1.0.106"
+ra_toolchain = { path = "../ra_toolchain" }
diff --git a/crates/ra_proc_macro_srv/src/tests/utils.rs b/crates/ra_proc_macro_srv/src/tests/utils.rs
index 84348b5defce..8d85f2d8a775 100644
--- a/crates/ra_proc_macro_srv/src/tests/utils.rs
+++ b/crates/ra_proc_macro_srv/src/tests/utils.rs
@@ -2,7 +2,6 @@
 
 use crate::dylib;
 use crate::ProcMacroSrv;
-pub use difference::Changeset as __Changeset;
 use ra_proc_macro::ListMacrosTask;
 use std::str::FromStr;
 use test_utils::assert_eq_text;
@@ -13,7 +12,7 @@ mod fixtures {
 
     // Use current project metadata to get the proc-macro dylib path
     pub fn dylib_path(crate_name: &str, version: &str) -> std::path::PathBuf {
-        let command = Command::new("cargo")
+        let command = Command::new(ra_toolchain::cargo())
             .args(&["check", "--message-format", "json"])
             .output()
             .unwrap()
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 1b5b3325c04f..d42cfa30004e 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -427,7 +427,7 @@ pub fn handle_runnables(
                 res.push(lsp_ext::Runnable {
                     range: Default::default(),
                     label: format!("cargo {} -p {}", cmd, spec.package),
-                    bin: "cargo".to_string(),
+                    bin: cargo_path()?,
                     args: vec![cmd.to_string(), "--package".to_string(), spec.package.clone()],
                     extra_args: Vec::new(),
                     env: FxHashMap::default(),
@@ -439,7 +439,7 @@ pub fn handle_runnables(
             res.push(lsp_ext::Runnable {
                 range: Default::default(),
                 label: "cargo check --workspace".to_string(),
-                bin: "cargo".to_string(),
+                bin: cargo_path()?,
                 args: vec!["check".to_string(), "--workspace".to_string()],
                 extra_args: Vec::new(),
                 env: FxHashMap::default(),
@@ -450,6 +450,13 @@ pub fn handle_runnables(
     Ok(res)
 }
 
+fn cargo_path() -> Result {
+    Ok(ra_toolchain::cargo()
+        .to_str()
+        .context("Path to `cargo` executable contains invalid UTF8 characters")?
+        .to_owned())
+}
+
 pub fn handle_completion(
     world: WorldSnapshot,
     params: lsp_types::CompletionParams,
@@ -983,15 +990,11 @@ fn to_lsp_runnable(
             target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
         }
     };
-    let cargo_path = ra_toolchain::cargo()
-        .to_str()
-        .context("Path to cargo executable contains invalid UTF8 characters")?
-        .to_owned();
 
     Ok(lsp_ext::Runnable {
         range: to_proto::range(&line_index, runnable.range),
         label,
-        bin: cargo_path,
+        bin: cargo_path()?,
         args,
         extra_args,
         env: {
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 405ddb362969..a31580c867c3 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -58,6 +58,10 @@ use std::collections::Spam;
     eprintln!("completion took {:?}", completion_start.elapsed());
 }
 
+fn cargo_path() -> String {
+    ra_toolchain::cargo().to_str().unwrap().to_owned()
+}
+
 #[test]
 fn test_runnables_no_project() {
     if skip_slow_tests() {
@@ -79,7 +83,7 @@ fn foo() {
           {
             "args": [ "test" ],
             "extraArgs": [ "foo", "--nocapture" ],
-            "bin": "cargo",
+            "bin": cargo_path(),
             "env": { "RUST_BACKTRACE": "short" },
             "cwd": null,
             "label": "test foo",
@@ -91,7 +95,7 @@ fn foo() {
           {
             "args": ["check", "--workspace"],
             "extraArgs": [],
-            "bin": "cargo",
+            "bin": cargo_path(),
             "env": {},
             "cwd": null,
             "label": "cargo check --workspace",
@@ -141,7 +145,7 @@ fn main() {}
             {
               "args": [ "test", "--package", "foo", "--test", "spam" ],
               "extraArgs": [ "test_eggs", "--exact", "--nocapture" ],
-              "bin": "cargo",
+              "bin": cargo_path(),
               "env": { "RUST_BACKTRACE": "short" },
               "label": "test test_eggs",
               "range": {
@@ -153,7 +157,7 @@ fn main() {}
             {
               "args": [ "check", "--package", "foo" ],
               "extraArgs": [],
-              "bin": "cargo",
+              "bin": cargo_path(),
               "env": {},
               "label": "cargo check -p foo",
               "range": {
@@ -165,7 +169,7 @@ fn main() {}
             {
               "args": [ "test", "--package", "foo" ],
               "extraArgs": [],
-              "bin": "cargo",
+              "bin": cargo_path(),
               "env": {},
               "label": "cargo test -p foo",
               "range": {

From d605ec9c321392d9c7ee4b440c560e1e405d92e6 Mon Sep 17 00:00:00 2001
From: veetaha 
Date: Sun, 31 May 2020 05:13:08 +0300
Subject: [PATCH 108/163] Change Runnable.bin -> Runnable.kind

As per matklad, we now pass the responsibility for finding the binary to the frontend.
Also, added caching for finding the binary path to reduce
the amount of filesystem interactions.
---
 Cargo.lock                                    |   1 -
 crates/rust-analyzer/Cargo.toml               |   1 -
 crates/rust-analyzer/src/lsp_ext.rs           |  11 +-
 .../rust-analyzer/src/main_loop/handlers.rs   |  14 +-
 .../rust-analyzer/tests/heavy_tests/main.rs   |  14 +-
 docs/dev/lsp-extensions.md                    |   2 +-
 editors/code/src/debug.ts                     |   2 +-
 editors/code/src/lsp_ext.ts                   |   5 +-
 editors/code/src/run.ts                       |   3 +-
 editors/code/src/tasks.ts                     |   4 +-
 editors/code/src/{cargo.ts => toolchain.ts}   | 137 ++++++++++--------
 editors/code/src/util.ts                      |  18 +++
 editors/code/tests/unit/launch_config.test.ts |  14 +-
 13 files changed, 133 insertions(+), 93 deletions(-)
 rename editors/code/src/{cargo.ts => toolchain.ts} (53%)

diff --git a/Cargo.lock b/Cargo.lock
index e1da335f5f6e..07f18c7607ee 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1365,7 +1365,6 @@ dependencies = [
  "ra_syntax",
  "ra_text_edit",
  "ra_tt",
- "ra_toolchain",
  "ra_vfs",
  "rand",
  "relative-path",
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 2e49448cc6ca..65b487db3b98 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -48,7 +48,6 @@ hir = { path = "../ra_hir", package = "ra_hir" }
 hir_def = { path = "../ra_hir_def", package = "ra_hir_def" }
 hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" }
 ra_proc_macro_srv = { path = "../ra_proc_macro_srv" }
-ra_toolchain = { path = "../ra_toolchain" }
 
 [target.'cfg(windows)'.dependencies]
 winapi = "0.3.8"
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index acb1dacb6b66..173c23b9e5d0 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -121,12 +121,21 @@ pub struct RunnablesParams {
     pub position: Option,
 }
 
+// Must strictly correspond to the executable name
+#[derive(Serialize, Deserialize, Debug)]
+#[serde(rename_all = "lowercase")]
+pub enum RunnableKind {
+    Cargo,
+    Rustc,
+    Rustup,
+}
+
 #[derive(Deserialize, Serialize, Debug)]
 #[serde(rename_all = "camelCase")]
 pub struct Runnable {
     pub range: Range,
     pub label: String,
-    pub bin: String,
+    pub kind: RunnableKind,
     pub args: Vec,
     pub extra_args: Vec,
     pub env: FxHashMap,
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index d42cfa30004e..bc7c7f1ef5d5 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -40,7 +40,6 @@ use crate::{
     world::WorldSnapshot,
     LspError, Result,
 };
-use anyhow::Context;
 
 pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result {
     let _p = profile("handle_analyzer_status");
@@ -427,7 +426,7 @@ pub fn handle_runnables(
                 res.push(lsp_ext::Runnable {
                     range: Default::default(),
                     label: format!("cargo {} -p {}", cmd, spec.package),
-                    bin: cargo_path()?,
+                    kind: lsp_ext::RunnableKind::Cargo,
                     args: vec![cmd.to_string(), "--package".to_string(), spec.package.clone()],
                     extra_args: Vec::new(),
                     env: FxHashMap::default(),
@@ -439,7 +438,7 @@ pub fn handle_runnables(
             res.push(lsp_ext::Runnable {
                 range: Default::default(),
                 label: "cargo check --workspace".to_string(),
-                bin: cargo_path()?,
+                kind: lsp_ext::RunnableKind::Cargo,
                 args: vec!["check".to_string(), "--workspace".to_string()],
                 extra_args: Vec::new(),
                 env: FxHashMap::default(),
@@ -450,13 +449,6 @@ pub fn handle_runnables(
     Ok(res)
 }
 
-fn cargo_path() -> Result {
-    Ok(ra_toolchain::cargo()
-        .to_str()
-        .context("Path to `cargo` executable contains invalid UTF8 characters")?
-        .to_owned())
-}
-
 pub fn handle_completion(
     world: WorldSnapshot,
     params: lsp_types::CompletionParams,
@@ -994,7 +986,7 @@ fn to_lsp_runnable(
     Ok(lsp_ext::Runnable {
         range: to_proto::range(&line_index, runnable.range),
         label,
-        bin: cargo_path()?,
+        kind: lsp_ext::RunnableKind::Cargo,
         args,
         extra_args,
         env: {
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index a31580c867c3..8b473ff74cd1 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -58,10 +58,6 @@ use std::collections::Spam;
     eprintln!("completion took {:?}", completion_start.elapsed());
 }
 
-fn cargo_path() -> String {
-    ra_toolchain::cargo().to_str().unwrap().to_owned()
-}
-
 #[test]
 fn test_runnables_no_project() {
     if skip_slow_tests() {
@@ -83,7 +79,7 @@ fn foo() {
           {
             "args": [ "test" ],
             "extraArgs": [ "foo", "--nocapture" ],
-            "bin": cargo_path(),
+            "kind": "cargo",
             "env": { "RUST_BACKTRACE": "short" },
             "cwd": null,
             "label": "test foo",
@@ -95,7 +91,7 @@ fn foo() {
           {
             "args": ["check", "--workspace"],
             "extraArgs": [],
-            "bin": cargo_path(),
+            "kind": "cargo",
             "env": {},
             "cwd": null,
             "label": "cargo check --workspace",
@@ -145,7 +141,7 @@ fn main() {}
             {
               "args": [ "test", "--package", "foo", "--test", "spam" ],
               "extraArgs": [ "test_eggs", "--exact", "--nocapture" ],
-              "bin": cargo_path(),
+              "kind": "cargo",
               "env": { "RUST_BACKTRACE": "short" },
               "label": "test test_eggs",
               "range": {
@@ -157,7 +153,7 @@ fn main() {}
             {
               "args": [ "check", "--package", "foo" ],
               "extraArgs": [],
-              "bin": cargo_path(),
+              "kind": "cargo",
               "env": {},
               "label": "cargo check -p foo",
               "range": {
@@ -169,7 +165,7 @@ fn main() {}
             {
               "args": [ "test", "--package", "foo" ],
               "extraArgs": [],
-              "bin": cargo_path(),
+              "kind": "cargo",
               "env": {},
               "label": "cargo test -p foo",
               "range": {
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index dbc95be387c5..d06da355d39d 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -417,7 +417,7 @@ interface Runnable {
     /// The label to show in the UI.
     label: string;
     /// The following fields describe a process to spawn.
-    bin: string;
+    kind: "cargo" | "rustc" | "rustup";
     args: string[];
     /// Args for cargo after `--`.
     extraArgs: string[];
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts
index 027504ecd2a8..bdec5b7357fa 100644
--- a/editors/code/src/debug.ts
+++ b/editors/code/src/debug.ts
@@ -3,7 +3,7 @@ import * as vscode from 'vscode';
 import * as path from 'path';
 import * as ra from './lsp_ext';
 
-import { Cargo } from './cargo';
+import { Cargo } from './toolchain';
 import { Ctx } from "./ctx";
 
 const debugOutput = vscode.window.createOutputChannel("Debug");
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index 4da12eb3092a..3e0b606997fb 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -45,10 +45,13 @@ export interface RunnablesParams {
     textDocument: lc.TextDocumentIdentifier;
     position: lc.Position | null;
 }
+
+export type RunnableKind = "cargo" | "rustc" | "rustup";
+
 export interface Runnable {
     range: lc.Range;
     label: string;
-    bin: string;
+    kind: RunnableKind;
     args: string[];
     extraArgs: string[];
     env: { [key: string]: string };
diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts
index 2a7a429cfa66..401cb76af67d 100644
--- a/editors/code/src/run.ts
+++ b/editors/code/src/run.ts
@@ -1,6 +1,7 @@
 import * as vscode from 'vscode';
 import * as lc from 'vscode-languageclient';
 import * as ra from './lsp_ext';
+import * as toolchain from "./toolchain";
 
 import { Ctx, Cmd } from './ctx';
 import { startDebugSession, getDebugConfiguration } from './debug';
@@ -175,7 +176,7 @@ export function createTask(spec: ra.Runnable): vscode.Task {
     const definition: CargoTaskDefinition = {
         type: 'cargo',
         label: spec.label,
-        command: spec.bin,
+        command: toolchain.getPathForExecutable(spec.kind),
         args: spec.extraArgs ? [...spec.args, '--', ...spec.extraArgs] : spec.args,
         env: spec.env,
     };
diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts
index c22d693623f8..9748824df38f 100644
--- a/editors/code/src/tasks.ts
+++ b/editors/code/src/tasks.ts
@@ -1,5 +1,5 @@
 import * as vscode from 'vscode';
-import { getCargoPathOrFail } from "./cargo";
+import * as toolchain from "./toolchain";
 
 // This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
 // our configuration should be compatible with it so use the same key.
@@ -25,7 +25,7 @@ class CargoTaskProvider implements vscode.TaskProvider {
         // set of tasks that always exist. These tasks cannot be removed in
         // tasks.json - only tweaked.
 
-        const cargoPath = getCargoPathOrFail();
+        const cargoPath = toolchain.cargoPath();
 
         return [
             { command: 'build', group: vscode.TaskGroup.Build },
diff --git a/editors/code/src/cargo.ts b/editors/code/src/toolchain.ts
similarity index 53%
rename from editors/code/src/cargo.ts
rename to editors/code/src/toolchain.ts
index 46cd3d777847..80a7915e90e8 100644
--- a/editors/code/src/cargo.ts
+++ b/editors/code/src/toolchain.ts
@@ -1,9 +1,10 @@
 import * as cp from 'child_process';
 import * as os from 'os';
 import * as path from 'path';
+import * as fs from 'fs';
 import * as readline from 'readline';
 import { OutputChannel } from 'vscode';
-import { isValidExecutable } from './util';
+import { log, memoize } from './util';
 
 interface CompilationArtifact {
     fileName: string;
@@ -17,34 +18,35 @@ export interface ArtifactSpec {
     filter?: (artifacts: CompilationArtifact[]) => CompilationArtifact[];
 }
 
-export function artifactSpec(args: readonly string[]): ArtifactSpec {
-    const cargoArgs = [...args, "--message-format=json"];
-
-    // arguments for a runnable from the quick pick should be updated.
-    // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens
-    switch (cargoArgs[0]) {
-        case "run": cargoArgs[0] = "build"; break;
-        case "test": {
-            if (!cargoArgs.includes("--no-run")) {
-                cargoArgs.push("--no-run");
-            }
-            break;
-        }
-    }
-
-    const result: ArtifactSpec = { cargoArgs: cargoArgs };
-    if (cargoArgs[0] === "test") {
-        // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests
-        // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"}
-        result.filter = (artifacts) => artifacts.filter(it => it.isTest);
-    }
-
-    return result;
-}
-
 export class Cargo {
     constructor(readonly rootFolder: string, readonly output: OutputChannel) { }
 
+    // Made public for testing purposes
+    static artifactSpec(args: readonly string[]): ArtifactSpec {
+        const cargoArgs = [...args, "--message-format=json"];
+
+        // arguments for a runnable from the quick pick should be updated.
+        // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens
+        switch (cargoArgs[0]) {
+            case "run": cargoArgs[0] = "build"; break;
+            case "test": {
+                if (!cargoArgs.includes("--no-run")) {
+                    cargoArgs.push("--no-run");
+                }
+                break;
+            }
+        }
+
+        const result: ArtifactSpec = { cargoArgs: cargoArgs };
+        if (cargoArgs[0] === "test") {
+            // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests
+            // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"}
+            result.filter = (artifacts) => artifacts.filter(it => it.isTest);
+        }
+
+        return result;
+    }
+
     private async getArtifacts(spec: ArtifactSpec): Promise {
         const artifacts: CompilationArtifact[] = [];
 
@@ -77,7 +79,7 @@ export class Cargo {
     }
 
     async executableFromArgs(args: readonly string[]): Promise {
-        const artifacts = await this.getArtifacts(artifactSpec(args));
+        const artifacts = await this.getArtifacts(Cargo.artifactSpec(args));
 
         if (artifacts.length === 0) {
             throw new Error('No compilation artifacts');
@@ -94,14 +96,7 @@ export class Cargo {
         onStderrString: (data: string) => void
     ): Promise {
         return new Promise((resolve, reject) => {
-            let cargoPath;
-            try {
-                cargoPath = getCargoPathOrFail();
-            } catch (err) {
-                return reject(err);
-            }
-
-            const cargo = cp.spawn(cargoPath, cargoArgs, {
+            const cargo = cp.spawn(cargoPath(), cargoArgs, {
                 stdio: ['ignore', 'pipe', 'pipe'],
                 cwd: this.rootFolder
             });
@@ -126,26 +121,54 @@ export class Cargo {
     }
 }
 
-// Mirrors `ra_toolchain::cargo()` implementation
-export 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.`
-    );
+/** Mirrors `ra_toolchain::cargo()` implementation */
+export function cargoPath(): string {
+    return getPathForExecutable("cargo");
+}
+
+/** Mirrors `ra_toolchain::get_path_for_executable()` implementation */
+export const getPathForExecutable = memoize(
+    // We apply caching to decrease file-system interactions
+    (executableName: "cargo" | "rustc" | "rustup"): string => {
+        {
+            const envVar = process.env[executableName.toUpperCase()];
+            if (envVar) return envVar;
+        }
+
+        if (lookupInPath(executableName)) return executableName;
+
+        try {
+            // hmm, `os.homedir()` seems to be infallible
+            // it is not mentioned in docs and cannot be infered by the type signature...
+            const standardPath = path.join(os.homedir(), ".cargo", "bin", executableName);
+
+            if (isFile(standardPath)) return standardPath;
+        } catch (err) {
+            log.error("Failed to read the fs info", err);
+        }
+        return executableName;
+    }
+);
+
+function lookupInPath(exec: string): boolean {
+    const paths = process.env.PATH ?? "";;
+
+    const candidates = paths.split(path.delimiter).flatMap(dirInPath => {
+        const candidate = path.join(dirInPath, exec);
+        return os.type() === "Windows_NT"
+            ? [candidate, `${candidate}.exe`]
+            : [candidate];
+    });
+
+    return candidates.some(isFile);
+}
+
+function isFile(suspectPath: string): boolean {
+    // It is not mentionned in docs, but `statSync()` throws an error when
+    // the path doesn't exist
+    try {
+        return fs.statSync(suspectPath).isFile();
+    } catch {
+        return false;
+    }
 }
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts
index 352ef9162f3d..fe3fb71cd7dc 100644
--- a/editors/code/src/util.ts
+++ b/editors/code/src/util.ts
@@ -99,3 +99,21 @@ export function isValidExecutable(path: string): boolean {
 export function setContextValue(key: string, value: any): Thenable {
     return vscode.commands.executeCommand('setContext', key, value);
 }
+
+/**
+ * Returns a higher-order function that caches the results of invoking the
+ * underlying function.
+ */
+export function memoize(func: (this: TThis, arg: Param) => Ret) {
+    const cache = new Map();
+
+    return function(this: TThis, arg: Param) {
+        const cached = cache.get(arg);
+        if (cached) return cached;
+
+        const result = func.call(this, arg);
+        cache.set(arg, result);
+
+        return result;
+    };
+}
diff --git a/editors/code/tests/unit/launch_config.test.ts b/editors/code/tests/unit/launch_config.test.ts
index d5cf1b74eadd..68794d53ede1 100644
--- a/editors/code/tests/unit/launch_config.test.ts
+++ b/editors/code/tests/unit/launch_config.test.ts
@@ -1,25 +1,25 @@
 import * as assert from 'assert';
-import * as cargo from '../../src/cargo';
+import { Cargo } from '../../src/toolchain';
 
 suite('Launch configuration', () => {
 
     suite('Lens', () => {
         test('A binary', async () => {
-            const args = cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "pkg_name"]);
+            const args = Cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "pkg_name"]);
 
             assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]);
             assert.deepEqual(args.filter, undefined);
         });
 
         test('One of Multiple Binaries', async () => {
-            const args = cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "bin1"]);
+            const args = Cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "bin1"]);
 
             assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin1", "--message-format=json"]);
             assert.deepEqual(args.filter, undefined);
         });
 
         test('A test', async () => {
-            const args = cargo.artifactSpec(["test", "--package", "pkg_name", "--lib", "--no-run"]);
+            const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib", "--no-run"]);
 
             assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--no-run", "--message-format=json"]);
             assert.notDeepEqual(args.filter, undefined);
@@ -28,7 +28,7 @@ suite('Launch configuration', () => {
 
     suite('QuickPick', () => {
         test('A binary', async () => {
-            const args = cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "pkg_name"]);
+            const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "pkg_name"]);
 
             assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]);
             assert.deepEqual(args.filter, undefined);
@@ -36,14 +36,14 @@ suite('Launch configuration', () => {
 
 
         test('One of Multiple Binaries', async () => {
-            const args = cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "bin2"]);
+            const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "bin2"]);
 
             assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin2", "--message-format=json"]);
             assert.deepEqual(args.filter, undefined);
         });
 
         test('A test', async () => {
-            const args = cargo.artifactSpec(["test", "--package", "pkg_name", "--lib"]);
+            const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib"]);
 
             assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--message-format=json", "--no-run"]);
             assert.notDeepEqual(args.filter, undefined);

From 5dd65495699fb3d9c6e4b3c4f27b78a64d23e567 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Sun, 31 May 2020 09:28:53 +0200
Subject: [PATCH 109/163] Simplify

---
 xtask/src/codegen.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index 3e2a66979d9d..f47d54125f35 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -41,7 +41,7 @@ pub enum Mode {
 /// With verify = false,
 fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
     match fs2::read_to_string(path) {
-        Ok(ref old_contents) if normalize(old_contents) == normalize(contents) => {
+        Ok(old_contents) if normalize(&old_contents) == normalize(contents) => {
             return Ok(());
         }
         _ => (),

From c1161718792a1841841a51bc8450d37c4f1ff535 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Sun, 31 May 2020 09:29:15 +0200
Subject: [PATCH 110/163] Ignore HTML

---
 .gitignore | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore
index f835edef0062..dab51647db70 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,4 +7,4 @@ crates/*/target
 *.log
 *.iml
 .vscode/settings.json
-cargo-timing*.html
+*.html

From f593393ebb9bfa515caf168a9f037324eeb6edfe Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Sun, 31 May 2020 09:45:41 +0200
Subject: [PATCH 111/163] Specify actions

---
 crates/ra_ide/src/goto_definition.rs      |  2 +-
 crates/ra_ide/src/goto_implementation.rs  |  2 +-
 crates/ra_ide/src/goto_type_definition.rs |  8 ++++++-
 crates/ra_ide/src/runnables.rs            | 11 +++++++++
 docs/user/features.md                     |  6 -----
 docs/user/generated_features.adoc         | 27 ++++++++++++++++++++---
 xtask/src/codegen/gen_feature_docs.rs     | 25 ++++++++++++++++-----
 7 files changed, 64 insertions(+), 17 deletions(-)

diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index 83ea5092c81b..daeeac76f637 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -17,7 +17,7 @@ use crate::{
     FilePosition, NavigationTarget, RangeInfo,
 };
 
-// Feature: Go To Definition
+// Feature: Go to Definition
 //
 // Navigates to the definition of an identifier.
 //
diff --git a/crates/ra_ide/src/goto_implementation.rs b/crates/ra_ide/src/goto_implementation.rs
index a5a296d228b9..622a094e6001 100644
--- a/crates/ra_ide/src/goto_implementation.rs
+++ b/crates/ra_ide/src/goto_implementation.rs
@@ -6,7 +6,7 @@ use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
 
 use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo};
 
-// Feature: Go To Implementation
+// Feature: Go to Implementation
 //
 // Navigates to the impl block of structs, enums or traits. Also implemented as a code lens.
 //
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs
index eeadfa9ee702..e74a502ecb9a 100644
--- a/crates/ra_ide/src/goto_type_definition.rs
+++ b/crates/ra_ide/src/goto_type_definition.rs
@@ -5,9 +5,15 @@ use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffs
 
 use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo};
 
-// Feature: Go To Type Definition
+// Feature: Go to Type Definition
 //
 // Navigates to the type of an identifier.
+//
+// |===
+// | Editor  | Action Name
+//
+// | VS Code | **Go to Type Definition*
+// |===
 pub(crate) fn goto_type_definition(
     db: &RootDatabase,
     position: FilePosition,
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 6e7e47199c88..4bf2678e1046 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -44,6 +44,17 @@ pub enum RunnableKind {
     Bin,
 }
 
+// Feature: Run
+//
+// Shows a popup suggesting to run a test/benchmark/binary **at the current cursor
+// location**. Super useful for repeatedly running just a single test. Do bind this
+// to a shortcut!
+//
+// |===
+// | Editor  | Action Name
+//
+// | VS Code | **Rust Analyzer: Run**
+// |===
 pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec {
     let sema = Semantics::new(db);
     let source_file = sema.parse(file_id);
diff --git a/docs/user/features.md b/docs/user/features.md
index 4d940225249b..df8e73a20d5f 100644
--- a/docs/user/features.md
+++ b/docs/user/features.md
@@ -4,12 +4,6 @@ you can use Ctrl+Shift+P to search for the corresponding action.
 
 ### Commands ctrl+shift+p
 
-#### Run
-
-Shows a popup suggesting to run a test/benchmark/binary **at the current cursor
-location**. Super useful for repeatedly running just a single test. Do bind this
-to a shortcut!
-
 #### Parent Module
 
 Navigates to the parent module of the current module.
diff --git a/docs/user/generated_features.adoc b/docs/user/generated_features.adoc
index e1eb5d88a8f6..1f6fcc97410e 100644
--- a/docs/user/generated_features.adoc
+++ b/docs/user/generated_features.adoc
@@ -29,7 +29,7 @@ Provides a tree of the symbols defined in the file. Can be used to
 |===
 
 
-=== Go To Definition
+=== Go to Definition
 **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_definition.rs[goto_definition.rs]
 
 
@@ -42,7 +42,7 @@ Navigates to the definition of an identifier.
 |===
 
 
-=== Go To Implementation
+=== Go to Implementation
 **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_implementation.rs[goto_implementation.rs]
 
 
@@ -55,12 +55,18 @@ Navigates to the impl block of structs, enums or traits. Also implemented as a c
 |===
 
 
-=== Go To Type Definition
+=== Go to Type Definition
 **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/goto_type_definition.rs[goto_type_definition.rs]
 
 
 Navigates to the type of an identifier.
 
+|===
+| Editor  | Action Name
+
+| VS Code | **Go to Type Definition*
+|===
+
 
 === On Typing Assists
 **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/typing.rs[typing.rs]
@@ -73,6 +79,21 @@ Some features trigger on typing certain characters:
 - typing `.` in a chain method call auto-indents
 
 
+=== Run
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/runnables.rs[runnables.rs]
+
+
+Shows a popup suggesting to run a test/benchmark/binary **at the current cursor
+location**. Super useful for repeatedly running just a single test. Do bind this
+to a shortcut!
+
+|===
+| Editor  | Action Name
+
+| VS Code | **Rust Analyzer: Run**
+|===
+
+
 === Workspace Symbol
 **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs[symbol_index.rs]
 
diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs
index 58318564872e..170a3e88942a 100644
--- a/xtask/src/codegen/gen_feature_docs.rs
+++ b/xtask/src/codegen/gen_feature_docs.rs
@@ -38,11 +38,7 @@ impl Feature {
 
             for block in comment_blocks {
                 let id = block.id;
-                assert!(
-                    id.split_ascii_whitespace().all(|it| it.starts_with(char::is_uppercase)),
-                    "bad feature: {}",
-                    id
-                );
+                assert!(is_valid_feature_name(&id), "invalid feature name: {:?}", id);
                 let doc = block.contents.join("\n");
                 acc.push(Feature { id, path: path.clone(), doc })
             }
@@ -52,6 +48,25 @@ impl Feature {
     }
 }
 
+fn is_valid_feature_name(feature: &str) -> bool {
+    'word: for word in feature.split_whitespace() {
+        for &short in ["to"].iter() {
+            if word == short {
+                continue 'word;
+            }
+        }
+        for &short in ["To"].iter() {
+            if word == short {
+                return false;
+            }
+        }
+        if !word.starts_with(char::is_uppercase) {
+            return false;
+        }
+    }
+    true
+}
+
 impl fmt::Display for Feature {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         writeln!(f, "=== {}", self.id)?;

From 8915183d7da07a4b295e5e93a889dea4c15024a0 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Sun, 31 May 2020 09:59:38 +0200
Subject: [PATCH 112/163] Don't require module docs for Features and Assists

---
 .../src/handlers/add_from_impl_for_enum.rs    |  4 +-
 crates/ra_assists/src/tests/generated.rs      | 19 +++++++
 crates/ra_ide/src/display/structure.rs        |  6 +-
 crates/ra_ide/src/extend_selection.rs         |  2 -
 crates/ra_ide/src/goto_definition.rs          |  2 -
 crates/ra_ide/src/goto_implementation.rs      |  2 -
 crates/ra_ide/src/goto_type_definition.rs     |  2 -
 crates/ra_ide/src/join_lines.rs               | 11 +++-
 crates/ra_ide/src/matching_brace.rs           | 13 ++++-
 crates/ra_ide/src/parent_module.rs            | 12 +++-
 crates/ra_ide/src/runnables.rs                |  2 -
 crates/ra_ide/src/syntax_tree.rs              | 14 +++--
 docs/user/assists.md                          | 18 ++++++
 docs/user/features.md                         | 16 ------
 docs/user/generated_features.adoc             | 55 +++++++++++++++++++
 xtask/tests/tidy.rs                           |  7 ++-
 16 files changed, 140 insertions(+), 45 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 6a675e8126dd..776bddf918a9 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,9 +4,9 @@ use test_utils::mark;
 
 use crate::{utils::FamousDefs, AssistContext, AssistId, Assists};
 
-// Assist add_from_impl_for_enum
+// Assist: add_from_impl_for_enum
 //
-// Adds a From impl for an enum variant with one tuple field
+// Adds a From impl for an enum variant with one tuple field.
 //
 // ```
 // enum A { <|>One(u32) }
diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs
index 250e56a69624..4e0536828c26 100644
--- a/crates/ra_assists/src/tests/generated.rs
+++ b/crates/ra_assists/src/tests/generated.rs
@@ -58,6 +58,25 @@ fn main() {
     )
 }
 
+#[test]
+fn doctest_add_from_impl_for_enum() {
+    check_doc_test(
+        "add_from_impl_for_enum",
+        r#####"
+enum A { <|>One(u32) }
+"#####,
+        r#####"
+enum A { One(u32) }
+
+impl From for A {
+    fn from(v: u32) -> Self {
+        A::One(v)
+    }
+}
+"#####,
+    )
+}
+
 #[test]
 fn doctest_add_function() {
     check_doc_test(
diff --git a/crates/ra_ide/src/display/structure.rs b/crates/ra_ide/src/display/structure.rs
index 3f59b89bb42e..aad5a8e4db69 100644
--- a/crates/ra_ide/src/display/structure.rs
+++ b/crates/ra_ide/src/display/structure.rs
@@ -1,10 +1,6 @@
-//! FIXME: write short doc here
-
-use crate::TextRange;
-
 use ra_syntax::{
     ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner},
-    match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, WalkEvent,
+    match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, WalkEvent,
 };
 
 #[derive(Debug, Clone)]
diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs
index cee0a19e191d..a4bc93cdbaa7 100644
--- a/crates/ra_ide/src/extend_selection.rs
+++ b/crates/ra_ide/src/extend_selection.rs
@@ -1,5 +1,3 @@
-//! FIXME: write short doc here
-
 use std::iter::successors;
 
 use hir::Semantics;
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index daeeac76f637..a6c86e99c95e 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -1,5 +1,3 @@
-//! FIXME: write short doc here
-
 use hir::Semantics;
 use ra_ide_db::{
     defs::{classify_name, classify_name_ref},
diff --git a/crates/ra_ide/src/goto_implementation.rs b/crates/ra_ide/src/goto_implementation.rs
index 622a094e6001..0cec0657e416 100644
--- a/crates/ra_ide/src/goto_implementation.rs
+++ b/crates/ra_ide/src/goto_implementation.rs
@@ -1,5 +1,3 @@
-//! FIXME: write short doc here
-
 use hir::{Crate, ImplDef, Semantics};
 use ra_ide_db::RootDatabase;
 use ra_syntax::{algo::find_node_at_offset, ast, AstNode};
diff --git a/crates/ra_ide/src/goto_type_definition.rs b/crates/ra_ide/src/goto_type_definition.rs
index e74a502ecb9a..91a3097fbb36 100644
--- a/crates/ra_ide/src/goto_type_definition.rs
+++ b/crates/ra_ide/src/goto_type_definition.rs
@@ -1,5 +1,3 @@
-//! FIXME: write short doc here
-
 use ra_ide_db::RootDatabase;
 use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset};
 
diff --git a/crates/ra_ide/src/join_lines.rs b/crates/ra_ide/src/join_lines.rs
index af1ade8a1e12..5036c1fb0c62 100644
--- a/crates/ra_ide/src/join_lines.rs
+++ b/crates/ra_ide/src/join_lines.rs
@@ -1,5 +1,3 @@
-//! FIXME: write short doc here
-
 use itertools::Itertools;
 use ra_fmt::{compute_ws, extract_trivial_expression};
 use ra_syntax::{
@@ -11,6 +9,15 @@ use ra_syntax::{
 };
 use ra_text_edit::{TextEdit, TextEditBuilder};
 
+// Feature: Join Lines
+//
+// Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces.
+//
+// |===
+// | Editor  | Action Name
+//
+// | VS Code | **Rust Analyzer: Join lines**
+// |===
 pub fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit {
     let range = if range.is_empty() {
         let syntax = file.syntax();
diff --git a/crates/ra_ide/src/matching_brace.rs b/crates/ra_ide/src/matching_brace.rs
index b8534870604f..407a9636d1d1 100644
--- a/crates/ra_ide/src/matching_brace.rs
+++ b/crates/ra_ide/src/matching_brace.rs
@@ -1,7 +1,16 @@
-//! FIXME: write short doc here
-
 use ra_syntax::{ast::AstNode, SourceFile, SyntaxKind, TextSize, T};
 
+// Feature: Matching Brace
+//
+// If the cursor is on any brace (`<>(){}[]`) which is a part of a brace-pair,
+// moves cursor to the matching brace. It uses the actual parser to determine
+// braces, so it won't confuse generics with comparisons.
+//
+// |===
+// | Editor  | Action Name
+//
+// | VS Code | **Rust Analyzer: Find matching brace**
+// |===
 pub fn matching_brace(file: &SourceFile, offset: TextSize) -> Option {
     const BRACES: &[SyntaxKind] =
         &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>]];
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs
index a083fb1eb358..fa1535da5b0a 100644
--- a/crates/ra_ide/src/parent_module.rs
+++ b/crates/ra_ide/src/parent_module.rs
@@ -1,5 +1,3 @@
-//! FIXME: write short doc here
-
 use hir::Semantics;
 use ra_db::{CrateId, FileId, FilePosition};
 use ra_ide_db::RootDatabase;
@@ -11,6 +9,16 @@ use test_utils::mark;
 
 use crate::NavigationTarget;
 
+// Feature: Parent Module
+//
+// Navigates to the parent module of the current module.
+//
+// |===
+// | Editor  | Action Name
+//
+// | VS Code | **Rust Analyzer: Locate parent module**
+// |===
+
 /// This returns `Vec` because a module may be included from several places. We
 /// don't handle this case yet though, so the Vec has length at most one.
 pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec {
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 4bf2678e1046..286d45eee952 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -1,5 +1,3 @@
-//! FIXME: write short doc here
-
 use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
 use itertools::Itertools;
 use ra_ide_db::RootDatabase;
diff --git a/crates/ra_ide/src/syntax_tree.rs b/crates/ra_ide/src/syntax_tree.rs
index 86c70ff830b8..2192f509041c 100644
--- a/crates/ra_ide/src/syntax_tree.rs
+++ b/crates/ra_ide/src/syntax_tree.rs
@@ -1,5 +1,3 @@
-//! FIXME: write short doc here
-
 use ra_db::SourceDatabase;
 use ra_ide_db::RootDatabase;
 use ra_syntax::{
@@ -8,8 +6,16 @@ use ra_syntax::{
     SyntaxToken, TextRange, TextSize,
 };
 
-pub use ra_db::FileId;
-
+// Feature: Show Syntax Tree
+//
+// Shows the parse tree of the current file. It exists mostly for debugging
+// rust-analyzer itself.
+//
+// |===
+// | Editor  | Action Name
+//
+// | VS Code | **Rust Analyzer: Show Syntax Tree**
+// |===
 pub(crate) fn syntax_tree(
     db: &RootDatabase,
     file_id: FileId,
diff --git a/docs/user/assists.md b/docs/user/assists.md
index 4ad7ea59d2e9..b5d813b546f8 100644
--- a/docs/user/assists.md
+++ b/docs/user/assists.md
@@ -56,6 +56,24 @@ fn main() {
 }
 ```
 
+## `add_from_impl_for_enum`
+
+Adds a From impl for an enum variant with one tuple field.
+
+```rust
+// BEFORE
+enum A { ┃One(u32) }
+
+// AFTER
+enum A { One(u32) }
+
+impl From for A {
+    fn from(v: u32) -> Self {
+        A::One(v)
+    }
+}
+```
+
 ## `add_function`
 
 Adds a stub function with a signature matching the function under the cursor.
diff --git a/docs/user/features.md b/docs/user/features.md
index df8e73a20d5f..ba7ca15a6b4b 100644
--- a/docs/user/features.md
+++ b/docs/user/features.md
@@ -4,24 +4,8 @@ you can use Ctrl+Shift+P to search for the corresponding action.
 
 ### Commands ctrl+shift+p
 
-#### Parent Module
 
-Navigates to the parent module of the current module.
 
-#### Matching Brace
-
-If the cursor is on any brace (`<>(){}[]`) which is a part of a brace-pair,
-moves cursor to the matching brace. It uses the actual parser to determine
-braces, so it won't confuse generics with comparisons.
-
-#### Join Lines
-
-Join selected lines into one, smartly fixing up whitespace and trailing commas.
-
-#### Show Syntax Tree
-
-Shows the parse tree of the current file. It exists mostly for debugging
-rust-analyzer itself.
 
 #### Expand Macro Recursively
 
diff --git a/docs/user/generated_features.adoc b/docs/user/generated_features.adoc
index 1f6fcc97410e..bf0a36d01838 100644
--- a/docs/user/generated_features.adoc
+++ b/docs/user/generated_features.adoc
@@ -68,6 +68,34 @@ Navigates to the type of an identifier.
 |===
 
 
+=== Join Lines
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/join_lines.rs[join_lines.rs]
+
+
+Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces.
+
+|===
+| Editor  | Action Name
+
+| VS Code | **Rust Analyzer: Join lines**
+|===
+
+
+=== Matching Brace
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/matching_brace.rs[matching_brace.rs]
+
+
+If the cursor is on any brace (`<>(){}[]`) which is a part of a brace-pair,
+moves cursor to the matching brace. It uses the actual parser to determine
+braces, so it won't confuse generics with comparisons.
+
+|===
+| Editor  | Action Name
+
+| VS Code | **Rust Analyzer: Find matching brace**
+|===
+
+
 === On Typing Assists
 **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/typing.rs[typing.rs]
 
@@ -79,6 +107,19 @@ Some features trigger on typing certain characters:
 - typing `.` in a chain method call auto-indents
 
 
+=== Parent Module
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/parent_module.rs[parent_module.rs]
+
+
+Navigates to the parent module of the current module.
+
+|===
+| Editor  | Action Name
+
+| VS Code | **Rust Analyzer: Locate parent module**
+|===
+
+
 === Run
 **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/runnables.rs[runnables.rs]
 
@@ -94,6 +135,20 @@ to a shortcut!
 |===
 
 
+=== Show Syntax Tree
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_tree.rs[syntax_tree.rs]
+
+
+Shows the parse tree of the current file. It exists mostly for debugging
+rust-analyzer itself.
+
+|===
+| Editor  | Action Name
+
+| VS Code | **Rust Analyzer: Show Syntax Tree**
+|===
+
+
 === Workspace Symbol
 **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs[symbol_index.rs]
 
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs
index 06ff45d994bc..4ac5d929fc6b 100644
--- a/xtask/tests/tidy.rs
+++ b/xtask/tests/tidy.rs
@@ -102,7 +102,7 @@ impl TidyDocs {
     fn visit(&mut self, path: &Path, text: &str) {
         // Test hopefully don't really need comments, and for assists we already
         // have special comments which are source of doc tests and user docs.
-        if is_exclude_dir(path, &["tests", "test_data", "handlers"]) {
+        if is_exclude_dir(path, &["tests", "test_data"]) {
             return;
         }
 
@@ -117,9 +117,12 @@ impl TidyDocs {
 
         if first_line.starts_with("//!") {
             if first_line.contains("FIXME") {
-                self.contains_fixme.push(path.to_path_buf())
+                self.contains_fixme.push(path.to_path_buf());
             }
         } else {
+            if text.contains("// Feature:") || text.contains("// Assist:") {
+                return;
+            }
             self.missing_docs.push(path.display().to_string());
         }
 

From b795a07320e13bcbedb6435bcfddb3ecd0ed2bde Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Sun, 31 May 2020 10:14:36 +0200
Subject: [PATCH 113/163] Doc more features

---
 crates/ra_ide/src/expand_macro.rs | 11 +++++--
 crates/ra_ide/src/ssr.rs          | 24 ++++++++++++++--
 crates/ra_ide/src/status.rs       | 11 +++++--
 docs/user/features.md             | 48 -------------------------------
 docs/user/readme.adoc             |  7 +++++
 5 files changed, 47 insertions(+), 54 deletions(-)

diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs
index f536ba3e7865..54a47aac0684 100644
--- a/crates/ra_ide/src/expand_macro.rs
+++ b/crates/ra_ide/src/expand_macro.rs
@@ -1,5 +1,3 @@
-//! This modules implements "expand macro" functionality in the IDE
-
 use hir::Semantics;
 use ra_ide_db::RootDatabase;
 use ra_syntax::{
@@ -14,6 +12,15 @@ pub struct ExpandedMacro {
     pub expansion: String,
 }
 
+// Feature: Expand Macro Recursively
+//
+// Shows the full macro expansion of the macro at current cursor.
+//
+// |===
+// | Editor  | Action Name
+//
+// | VS Code | **Rust Analyzer: Expand macro recursively**
+// |===
 pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option {
     let sema = Semantics::new(db);
     let file = sema.parse(position.file_id);
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 130d3b4c3ba1..93e9aee1d01e 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -1,5 +1,3 @@
-//!  structural search replace
-
 use std::{collections::HashMap, iter::once, str::FromStr};
 
 use ra_db::{SourceDatabase, SourceDatabaseExt};
@@ -25,6 +23,28 @@ impl std::fmt::Display for SsrError {
 
 impl std::error::Error for SsrError {}
 
+// Feature: Structural Seach and Replace
+//
+// Search and replace with named wildcards that will match any expression.
+// The syntax for a structural search replace command is ` ==>> `.
+// A `$:expr` placeholder in the search pattern will match any expression and `$` will reference it in the replacement.
+// Available via the command `rust-analyzer.ssr`.
+//
+// ```rust
+// // Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)]
+//
+// // BEFORE
+// String::from(foo(y + 5, z))
+//
+// // AFTER
+// String::from((y + 5).foo(z))
+// ```
+//
+// |===
+// | Editor  | Action Name
+//
+// | VS Code | **Rust Analyzer: Structural Search Replace**
+// |===
 pub fn parse_search_replace(
     query: &str,
     parse_only: bool,
diff --git a/crates/ra_ide/src/status.rs b/crates/ra_ide/src/status.rs
index 30eb5c995e63..5b7992920071 100644
--- a/crates/ra_ide/src/status.rs
+++ b/crates/ra_ide/src/status.rs
@@ -1,5 +1,3 @@
-//! FIXME: write short doc here
-
 use std::{fmt, iter::FromIterator, sync::Arc};
 
 use hir::MacroFile;
@@ -26,6 +24,15 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
     db.query(hir::db::ParseMacroQuery).entries::()
 }
 
+// Feature: Status
+//
+// Shows internal statistic about memory usage of rust-analyzer.
+//
+// |===
+// | Editor  | Action Name
+//
+// | VS Code | **Rust Analyzer: Status**
+// |===
 pub(crate) fn status(db: &RootDatabase) -> String {
     let files_stats = db.query(FileTextQuery).entries::();
     let syntax_tree_stats = syntax_tree_stats(db);
diff --git a/docs/user/features.md b/docs/user/features.md
index ba7ca15a6b4b..ff8cb2d6ef67 100644
--- a/docs/user/features.md
+++ b/docs/user/features.md
@@ -5,60 +5,12 @@ you can use Ctrl+Shift+P to search for the corresponding action.
 ### Commands ctrl+shift+p
 
 
-
-
-#### Expand Macro Recursively
-
-Shows the full macro expansion of the macro at current cursor.
-
-#### Status
-
-Shows internal statistic about memory usage of rust-analyzer.
-
-#### Show RA Version
-
-Show current rust-analyzer version.
-
 #### Toggle inlay hints
 
 Toggle inlay hints view for the current workspace.
 It is recommended to assign a shortcut for this command to quickly turn off
 inlay hints when they prevent you from reading/writing the code.
 
-#### Run Garbage Collection
-
-Manually triggers GC.
-
-#### Start Cargo Watch
-
-Start `cargo watch` for live error highlighting. Will prompt to install if it's not already installed.
-
-#### Stop Cargo Watch
-
-Stop `cargo watch`.
-
-#### Structural Seach and Replace
-
-Search and replace with named wildcards that will match any expression.
-The syntax for a structural search replace command is ` ==>> `. A `$:expr` placeholder in the search pattern will match any expression and `$` will reference it in the replacement. Available via the command `rust-analyzer.ssr`.
-
-```rust
-// Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)]
-
-// BEFORE
-String::from(foo(y + 5, z))
-
-// AFTER
-String::from((y + 5).foo(z))
-```
-
-### Assists (Code Actions)
-
-Assists, or code actions, are small local refactorings, available in a particular context.
-They are usually triggered by a shortcut or by clicking a light bulb icon in the editor.
-
-See [assists.md](./assists.md) for the list of available assists.
-
 ### Magic Completions
 
 In addition to usual reference completion, rust-analyzer provides some ✨magic✨
diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc
index 7b159bfc6b7f..8cfa41144e71 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/readme.adoc
@@ -272,3 +272,10 @@ Gnome Builder currently has support for RLS, and there's no way to configure the
 == Features
 
 include::./generated_features.adoc[]
+
+== Assists (Code Actions)
+
+Assists, or code actions, are small local refactorings, available in a particular context.
+They are usually triggered by a shortcut or by clicking a light bulb icon in the editor.
+
+See [assists.md](./assists.md) for the list of available assists.

From fb469c3b31e7da962e91269b53b2f53d672cc4ba Mon Sep 17 00:00:00 2001
From: robojumper 
Date: Sat, 30 May 2020 02:06:58 +0200
Subject: [PATCH 114/163] labelled break test

---
 crates/ra_hir_ty/src/tests/simple.rs | 54 ++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index 839491b9e28c..beceb863434d 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -1943,3 +1943,57 @@ fn test() {
     "###
     );
 }
+
+#[test]
+fn infer_labelled_break_with_val() {
+    assert_snapshot!(
+        infer(r#"
+fn foo() {
+    let _x = || 'outer: loop {
+        let inner = 'inner: loop {
+            let i = Default::default();
+            if (break 'outer i) {
+                loop { break 'inner 5i8; };
+            } else if true {
+                break 'inner 6;
+            }
+            break 7;
+        };
+        break inner < 8;
+    };
+}
+"#),
+        @r###"
+    10..336 '{     ...  }; }': ()
+    20..22 '_x': || -> bool
+    25..333 '|| 'ou...     }': || -> bool
+    28..333 ''outer...     }': bool
+    41..333 '{     ...     }': ()
+    55..60 'inner': i32
+    63..301 ''inner...     }': i32
+    76..301 '{     ...     }': ()
+    94..95 'i': i32
+    98..114 'Defaul...efault': {unknown}
+    98..116 'Defaul...ault()': i32
+    130..270 'if (br...     }': ()
+    134..148 'break 'outer i': !
+    147..148 'i': i32
+    150..209 '{     ...     }': ()
+    168..194 'loop {...5i8; }': i8
+    173..194 '{ brea...5i8; }': ()
+    175..191 'break ...er 5i8': !
+    188..191 '5i8': i8
+    215..270 'if tru...     }': ()
+    218..222 'true': bool
+    223..270 '{     ...     }': ()
+    241..255 'break 'inner 6': !
+    254..255 '6': i32
+    283..290 'break 7': !
+    289..290 '7': i32
+    311..326 'break inner < 8': !
+    317..322 'inner': i32
+    317..326 'inner < 8': bool
+    325..326 '8': i32
+    "###
+    );
+}

From 1c6a2eb14a84c3a66972d1a6da429cca1aa8b40a Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Sun, 31 May 2020 11:29:19 +0200
Subject: [PATCH 115/163] Move the rest of the features to generated docs

---
 crates/ra_ide/src/completion.rs               |  47 ++++-
 .../ra_ide/src/completion/complete_postfix.rs |   6 +-
 crates/ra_ide/src/hover.rs                    | 116 ++++++-------
 crates/ra_ide/src/inlay_hints.rs              |  22 ++-
 crates/ra_ide/src/syntax_highlighting.rs      | 161 +++++++++---------
 crates/ra_ide/src/syntax_tree.rs              |   2 +-
 docs/user/features.md                         |  96 -----------
 docs/user/generated_features.adoc             | 143 ++++++++++++++++
 docs/user/readme.adoc                         |   1 +
 xtask/src/codegen/gen_feature_docs.rs         |   4 +-
 10 files changed, 358 insertions(+), 240 deletions(-)
 delete mode 100644 docs/user/features.md

diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index 191300704b55..d890b69d26fa 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -1,5 +1,3 @@
-//! FIXME: write short doc here
-
 mod completion_config;
 mod completion_item;
 mod completion_context;
@@ -35,6 +33,51 @@ pub use crate::completion::{
     completion_item::{CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat},
 };
 
+//FIXME: split the following feature into fine-grained features.
+
+// Feature: Magic Completions
+//
+// In addition to usual reference completion, rust-analyzer provides some ✨magic✨
+// completions as well:
+//
+// Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
+// is placed at the appropriate position. Even though `if` is easy to type, you
+// still want to complete it, to get ` { }` for free! `return` is inserted with a
+// space or `;` depending on the return type of the function.
+//
+// When completing a function call, `()` are automatically inserted. If a function
+// takes arguments, the cursor is positioned inside the parenthesis.
+//
+// There are postfix completions, which can be triggered by typing something like
+// `foo().if`. The word after `.` determines postfix completion. Possible variants are:
+//
+// - `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
+// - `expr.match` -> `match expr {}`
+// - `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
+// - `expr.ref` -> `&expr`
+// - `expr.refm` -> `&mut expr`
+// - `expr.not` -> `!expr`
+// - `expr.dbg` -> `dbg!(expr)`
+//
+// There also snippet completions:
+//
+// .Expressions
+// - `pd` -> `println!("{:?}")`
+// - `ppd` -> `println!("{:#?}")`
+//
+// .Items
+// - `tfn` -> `#[test] fn f(){}`
+// - `tmod` ->
+// ```rust
+// #[cfg(test)]
+// mod tests {
+//     use super::*;
+//
+//     #[test]
+//     fn test_fn() {}
+// }
+// ```
+
 /// Main entry point for completion. We run completion as a two-phase process.
 ///
 /// First, we look at the position and collect a so-called `CompletionContext.
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 02e660ca8e4b..59b58bf98b6b 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -1,12 +1,11 @@
 //! FIXME: write short doc here
-
+use ra_assists::utils::TryEnum;
 use ra_syntax::{
     ast::{self, AstNode},
     TextRange, TextSize,
 };
 use ra_text_edit::TextEdit;
 
-use super::completion_config::SnippetCap;
 use crate::{
     completion::{
         completion_context::CompletionContext,
@@ -14,7 +13,8 @@ use crate::{
     },
     CompletionItem,
 };
-use ra_assists::utils::TryEnum;
+
+use super::completion_config::SnippetCap;
 
 pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
     if !ctx.config.enable_postfix_completions {
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 3e721dcca392..d96cb5596918 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -1,10 +1,10 @@
-//! Logic for computing info that is displayed when the user hovers over any
-//! source code items (e.g. function call, struct field, variable symbol...)
+use std::iter::once;
 
 use hir::{
     Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef,
     ModuleSource, Semantics,
 };
+use itertools::Itertools;
 use ra_db::SourceDatabase;
 use ra_ide_db::{
     defs::{classify_name, classify_name_ref, Definition},
@@ -21,8 +21,6 @@ use crate::{
     display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel},
     FilePosition, RangeInfo,
 };
-use itertools::Itertools;
-use std::iter::once;
 
 /// Contains the results when hovering over an item
 #[derive(Debug, Default)]
@@ -62,6 +60,63 @@ impl HoverResult {
     }
 }
 
+// Feature: Hover
+//
+// Shows additional information, like type of an expression or documentation for definition when "focusing" code.
+// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
+pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option> {
+    let sema = Semantics::new(db);
+    let file = sema.parse(position.file_id).syntax().clone();
+    let token = pick_best(file.token_at_offset(position.offset))?;
+    let token = sema.descend_into_macros(token);
+
+    let mut res = HoverResult::new();
+
+    if let Some((node, name_kind)) = match_ast! {
+        match (token.parent()) {
+            ast::NameRef(name_ref) => {
+                classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition()))
+            },
+            ast::Name(name) => {
+                classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition()))
+            },
+            _ => None,
+        }
+    } {
+        let range = sema.original_range(&node).range;
+        res.extend(hover_text_from_name_kind(db, name_kind));
+
+        if !res.is_empty() {
+            return Some(RangeInfo::new(range, res));
+        }
+    }
+
+    let node = token
+        .ancestors()
+        .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
+
+    let ty = match_ast! {
+        match node {
+            ast::MacroCall(_it) => {
+                // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
+                // (e.g expanding a builtin macro). So we give up here.
+                return None;
+            },
+            ast::Expr(it) => {
+                sema.type_of_expr(&it)
+            },
+            ast::Pat(it) => {
+                sema.type_of_pat(&it)
+            },
+            _ => None,
+        }
+    }?;
+
+    res.extend(Some(rust_code_markup(&ty.display(db))));
+    let range = sema.original_range(&node).range;
+    Some(RangeInfo::new(range, res))
+}
+
 fn hover_text(
     docs: Option,
     desc: Option,
@@ -160,59 +215,6 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option Option> {
-    let sema = Semantics::new(db);
-    let file = sema.parse(position.file_id).syntax().clone();
-    let token = pick_best(file.token_at_offset(position.offset))?;
-    let token = sema.descend_into_macros(token);
-
-    let mut res = HoverResult::new();
-
-    if let Some((node, name_kind)) = match_ast! {
-        match (token.parent()) {
-            ast::NameRef(name_ref) => {
-                classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition()))
-            },
-            ast::Name(name) => {
-                classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition()))
-            },
-            _ => None,
-        }
-    } {
-        let range = sema.original_range(&node).range;
-        res.extend(hover_text_from_name_kind(db, name_kind));
-
-        if !res.is_empty() {
-            return Some(RangeInfo::new(range, res));
-        }
-    }
-
-    let node = token
-        .ancestors()
-        .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
-
-    let ty = match_ast! {
-        match node {
-            ast::MacroCall(_it) => {
-                // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
-                // (e.g expanding a builtin macro). So we give up here.
-                return None;
-            },
-            ast::Expr(it) => {
-                sema.type_of_expr(&it)
-            },
-            ast::Pat(it) => {
-                sema.type_of_pat(&it)
-            },
-            _ => None,
-        }
-    }?;
-
-    res.extend(Some(rust_code_markup(&ty.display(db))));
-    let range = sema.original_range(&node).range;
-    Some(RangeInfo::new(range, res))
-}
-
 fn pick_best(tokens: TokenAtOffset) -> Option {
     return tokens.max_by_key(priority);
     fn priority(n: &SyntaxToken) -> usize {
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index b391f903a80a..75bd3c96bb1f 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -1,5 +1,3 @@
-//! This module defines multiple types of inlay hints and their visibility
-
 use hir::{Adt, HirDisplay, Semantics, Type};
 use ra_ide_db::RootDatabase;
 use ra_prof::profile;
@@ -39,6 +37,26 @@ pub struct InlayHint {
     pub label: SmolStr,
 }
 
+// Feature: Inlay Hints
+//
+// rust-analyzer shows additional information inline with the source code.
+// Editors usually render this using read-only virtual text snippets interspersed with code.
+//
+// rust-analyzer shows hits for
+//
+// * types of local variables
+// * names of function arguments
+// * types of chained expressions
+//
+// **Note:** VS Code does not have native support for inlay hints https://github.com/microsoft/vscode/issues/16221[yet] and the hints are implemented using decorations.
+// This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird:
+// https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.com/rust-analyzer/rust-analyzer/issues/3453[2].
+//
+// |===
+// | Editor  | Action Name
+//
+// | VS Code | **Rust Analyzer: Toggle inlay hints*
+// |===
 pub(crate) fn inlay_hints(
     db: &RootDatabase,
     file_id: FileId,
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 8a995d779bae..3ab1f0a21573 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -1,5 +1,3 @@
-//! Implements syntax highlighting.
-
 mod tags;
 mod html;
 #[cfg(test)]
@@ -32,81 +30,15 @@ pub struct HighlightedRange {
     pub binding_hash: Option,
 }
 
-#[derive(Debug)]
-struct HighlightedRangeStack {
-    stack: Vec>,
-}
-
-/// We use a stack to implement the flattening logic for the highlighted
-/// syntax ranges.
-impl HighlightedRangeStack {
-    fn new() -> Self {
-        Self { stack: vec![Vec::new()] }
-    }
-
-    fn push(&mut self) {
-        self.stack.push(Vec::new());
-    }
-
-    /// Flattens the highlighted ranges.
-    ///
-    /// For example `#[cfg(feature = "foo")]` contains the nested ranges:
-    /// 1) parent-range: Attribute [0, 23)
-    /// 2) child-range: String [16, 21)
-    ///
-    /// The following code implements the flattening, for our example this results to:
-    /// `[Attribute [0, 16), String [16, 21), Attribute [21, 23)]`
-    fn pop(&mut self) {
-        let children = self.stack.pop().unwrap();
-        let prev = self.stack.last_mut().unwrap();
-        let needs_flattening = !children.is_empty()
-            && !prev.is_empty()
-            && prev.last().unwrap().range.contains_range(children.first().unwrap().range);
-        if !needs_flattening {
-            prev.extend(children);
-        } else {
-            let mut parent = prev.pop().unwrap();
-            for ele in children {
-                assert!(parent.range.contains_range(ele.range));
-                let mut cloned = parent.clone();
-                parent.range = TextRange::new(parent.range.start(), ele.range.start());
-                cloned.range = TextRange::new(ele.range.end(), cloned.range.end());
-                if !parent.range.is_empty() {
-                    prev.push(parent);
-                }
-                prev.push(ele);
-                parent = cloned;
-            }
-            if !parent.range.is_empty() {
-                prev.push(parent);
-            }
-        }
-    }
-
-    fn add(&mut self, range: HighlightedRange) {
-        self.stack
-            .last_mut()
-            .expect("during DFS traversal, the stack must not be empty")
-            .push(range)
-    }
-
-    fn flattened(mut self) -> Vec {
-        assert_eq!(
-            self.stack.len(),
-            1,
-            "after DFS traversal, the stack should only contain a single element"
-        );
-        let mut res = self.stack.pop().unwrap();
-        res.sort_by_key(|range| range.range.start());
-        // Check that ranges are sorted and disjoint
-        assert!(res
-            .iter()
-            .zip(res.iter().skip(1))
-            .all(|(left, right)| left.range.end() <= right.range.start()));
-        res
-    }
-}
-
+// Feature: Semantic Syntax Highlighting
+//
+// rust-analyzer highlights the code semantically.
+// For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait.
+// rust-analyzer does not specify colors directly, instead it assigns tag (like `struct`) and a set of modifiers (like `declaration`) to each token.
+// It's up to the client to map those to specific colors.
+//
+// The general rule is that a reference to an entity gets colored the same way as the entity itself.
+// We also give special modifier for `mut` and `&mut` local variables.
 pub(crate) fn highlight(
     db: &RootDatabase,
     file_id: FileId,
@@ -291,6 +223,81 @@ pub(crate) fn highlight(
     stack.flattened()
 }
 
+#[derive(Debug)]
+struct HighlightedRangeStack {
+    stack: Vec>,
+}
+
+/// We use a stack to implement the flattening logic for the highlighted
+/// syntax ranges.
+impl HighlightedRangeStack {
+    fn new() -> Self {
+        Self { stack: vec![Vec::new()] }
+    }
+
+    fn push(&mut self) {
+        self.stack.push(Vec::new());
+    }
+
+    /// Flattens the highlighted ranges.
+    ///
+    /// For example `#[cfg(feature = "foo")]` contains the nested ranges:
+    /// 1) parent-range: Attribute [0, 23)
+    /// 2) child-range: String [16, 21)
+    ///
+    /// The following code implements the flattening, for our example this results to:
+    /// `[Attribute [0, 16), String [16, 21), Attribute [21, 23)]`
+    fn pop(&mut self) {
+        let children = self.stack.pop().unwrap();
+        let prev = self.stack.last_mut().unwrap();
+        let needs_flattening = !children.is_empty()
+            && !prev.is_empty()
+            && prev.last().unwrap().range.contains_range(children.first().unwrap().range);
+        if !needs_flattening {
+            prev.extend(children);
+        } else {
+            let mut parent = prev.pop().unwrap();
+            for ele in children {
+                assert!(parent.range.contains_range(ele.range));
+                let mut cloned = parent.clone();
+                parent.range = TextRange::new(parent.range.start(), ele.range.start());
+                cloned.range = TextRange::new(ele.range.end(), cloned.range.end());
+                if !parent.range.is_empty() {
+                    prev.push(parent);
+                }
+                prev.push(ele);
+                parent = cloned;
+            }
+            if !parent.range.is_empty() {
+                prev.push(parent);
+            }
+        }
+    }
+
+    fn add(&mut self, range: HighlightedRange) {
+        self.stack
+            .last_mut()
+            .expect("during DFS traversal, the stack must not be empty")
+            .push(range)
+    }
+
+    fn flattened(mut self) -> Vec {
+        assert_eq!(
+            self.stack.len(),
+            1,
+            "after DFS traversal, the stack should only contain a single element"
+        );
+        let mut res = self.stack.pop().unwrap();
+        res.sort_by_key(|range| range.range.start());
+        // Check that ranges are sorted and disjoint
+        assert!(res
+            .iter()
+            .zip(res.iter().skip(1))
+            .all(|(left, right)| left.range.end() <= right.range.start()));
+        res
+    }
+}
+
 fn highlight_format_specifier(kind: FormatSpecifier) -> Option {
     Some(match kind {
         FormatSpecifier::Open
diff --git a/crates/ra_ide/src/syntax_tree.rs b/crates/ra_ide/src/syntax_tree.rs
index 2192f509041c..a341684fda20 100644
--- a/crates/ra_ide/src/syntax_tree.rs
+++ b/crates/ra_ide/src/syntax_tree.rs
@@ -1,4 +1,4 @@
-use ra_db::SourceDatabase;
+use ra_db::{FileId, SourceDatabase};
 use ra_ide_db::RootDatabase;
 use ra_syntax::{
     algo, AstNode, NodeOrToken, SourceFile,
diff --git a/docs/user/features.md b/docs/user/features.md
deleted file mode 100644
index ff8cb2d6ef67..000000000000
--- a/docs/user/features.md
+++ /dev/null
@@ -1,96 +0,0 @@
-This document is an index of features that the rust-analyzer language server
-provides. Shortcuts are for the default VS Code layout. If there's no shortcut,
-you can use Ctrl+Shift+P to search for the corresponding action.
-
-### Commands ctrl+shift+p
-
-
-#### Toggle inlay hints
-
-Toggle inlay hints view for the current workspace.
-It is recommended to assign a shortcut for this command to quickly turn off
-inlay hints when they prevent you from reading/writing the code.
-
-### Magic Completions
-
-In addition to usual reference completion, rust-analyzer provides some ✨magic✨
-completions as well:
-
-Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
-is placed at the appropriate position. Even though `if` is easy to type, you
-still want to complete it, to get ` { }` for free! `return` is inserted with a
-space or `;` depending on the return type of the function.
-
-When completing a function call, `()` are automatically inserted. If a function
-takes arguments, the cursor is positioned inside the parenthesis.
-
-There are postfix completions, which can be triggered by typing something like
-`foo().if`. The word after `.` determines postfix completion. Possible variants are:
-
-- `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
-- `expr.match` -> `match expr {}`
-- `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
-- `expr.ref` -> `&expr`
-- `expr.refm` -> `&mut expr`
-- `expr.not` -> `!expr`
-- `expr.dbg` -> `dbg!(expr)`
-
-There also snippet completions:
-
-#### Inside Expressions
-
-- `pd` -> `println!("{:?}")`
-- `ppd` -> `println!("{:#?}")`
-
-#### Inside Modules
-
-- `tfn` -> `#[test] fn f(){}`
-- `tmod` ->
-```rust
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_fn() {}
-}
-```
-
-### Code Highlighting
-
-Experimental feature to let rust-analyzer highlight Rust code instead of using the
-default highlighter.
-
-#### Rainbow Highlighting
-
-Experimental feature that, given code highlighting using rust-analyzer is
-active, will pick unique colors for identifiers.
-
-### Code hints
-
-Rust-analyzer has two types of hints to show the information about the code:
-
-* hover hints, appearing on hover on any element.
-
-These contain extended information on the hovered language item.
-
-* inlay hints, shown near the element hinted directly in the editor.
-
-Two types of inlay hints are displayed currently:
-
-* type hints, displaying the minimal information on the type of the expression (if the information is available)
-* method chaining hints, type information for multi-line method chains
-* parameter name hints, displaying the names of the parameters in the corresponding methods
-
-#### VS Code
-
-In VS Code, the following settings can be used to configure the inlay hints:
-
-* `rust-analyzer.inlayHints.typeHints` - enable hints for inferred types.
-* `rust-analyzer.inlayHints.chainingHints` - enable hints for inferred types on method chains.
-* `rust-analyzer.inlayHints.parameterHints` - enable hints for function parameters.
-* `rust-analyzer.inlayHints.maxLength` — shortens the hints if their length exceeds the value specified. If no value is specified (`null`), no shortening is applied.
-
-**Note:** VS Code does not have native support for inlay hints [yet](https://github.com/microsoft/vscode/issues/16221) and the hints are implemented using decorations.
-This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird:
-[1](https://github.com/rust-analyzer/rust-analyzer/issues/1623), [2](https://github.com/rust-analyzer/rust-analyzer/issues/3453).
diff --git a/docs/user/generated_features.adoc b/docs/user/generated_features.adoc
index bf0a36d01838..a806e3ff119a 100644
--- a/docs/user/generated_features.adoc
+++ b/docs/user/generated_features.adoc
@@ -1,3 +1,16 @@
+=== Expand Macro Recursively
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/expand_macro.rs[expand_macro.rs]
+
+
+Shows the full macro expansion of the macro at current cursor.
+
+|===
+| Editor  | Action Name
+
+| VS Code | **Rust Analyzer: Expand macro recursively**
+|===
+
+
 === Extend Selection
 **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/extend_selection.rs[extend_selection.rs]
 
@@ -68,6 +81,38 @@ Navigates to the type of an identifier.
 |===
 
 
+=== Hover
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/hover.rs[hover.rs]
+
+
+Shows additional information, like type of an expression or documentation for definition when "focusing" code.
+Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
+
+
+=== Inlay Hints
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/inlay_hints.rs[inlay_hints.rs]
+
+
+rust-analyzer shows additional information inline with the source code.
+Editors usually render this using read-only virtual text snippets interspersed with code.
+
+rust-analyzer shows hits for
+
+* types of local variables
+* names of function arguments
+* types of chained expressions
+
+**Note:** VS Code does not have native support for inlay hints https://github.com/microsoft/vscode/issues/16221[yet] and the hints are implemented using decorations.
+This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird:
+https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.com/rust-analyzer/rust-analyzer/issues/3453[2].
+
+|===
+| Editor  | Action Name
+
+| VS Code | **Rust Analyzer: Toggle inlay hints*
+|===
+
+
 === Join Lines
 **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/join_lines.rs[join_lines.rs]
 
@@ -81,6 +126,52 @@ Join selected lines into one, smartly fixing up whitespace, trailing commas, and
 |===
 
 
+=== Magic Completions
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/completion.rs[completion.rs]
+
+
+In addition to usual reference completion, rust-analyzer provides some ✨magic✨
+completions as well:
+
+Keywords like `if`, `else` `while`, `loop` are completed with braces, and cursor
+is placed at the appropriate position. Even though `if` is easy to type, you
+still want to complete it, to get ` { }` for free! `return` is inserted with a
+space or `;` depending on the return type of the function.
+
+When completing a function call, `()` are automatically inserted. If a function
+takes arguments, the cursor is positioned inside the parenthesis.
+
+There are postfix completions, which can be triggered by typing something like
+`foo().if`. The word after `.` determines postfix completion. Possible variants are:
+
+- `expr.if` -> `if expr {}` or `if let ... {}` for `Option` or `Result`
+- `expr.match` -> `match expr {}`
+- `expr.while` -> `while expr {}` or `while let ... {}` for `Option` or `Result`
+- `expr.ref` -> `&expr`
+- `expr.refm` -> `&mut expr`
+- `expr.not` -> `!expr`
+- `expr.dbg` -> `dbg!(expr)`
+
+There also snippet completions:
+
+.Expressions
+- `pd` -> `println!("{:?}")`
+- `ppd` -> `println!("{:#?}")`
+
+.Items
+- `tfn` -> `#[test] fn f(){}`
+- `tmod` ->
+```rust
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_fn() {}
+}
+```
+
+
 === Matching Brace
 **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/matching_brace.rs[matching_brace.rs]
 
@@ -135,6 +226,19 @@ to a shortcut!
 |===
 
 
+=== Semantic Syntax Highlighting
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_highlighting.rs[syntax_highlighting.rs]
+
+
+rust-analyzer highlights the code semantically.
+For example, `bar` in `foo::Bar` might be colored differently depending on whether `Bar` is an enum or a trait.
+rust-analyzer does not specify colors directly, instead it assigns tag (like `struct`) and a set of modifiers (like `declaration`) to each token.
+It's up to the client to map those to specific colors.
+
+The general rule is that a reference to an entity gets colored the same way as the entity itself.
+We also give special modifier for `mut` and `&mut` local variables.
+
+
 === Show Syntax Tree
 **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/syntax_tree.rs[syntax_tree.rs]
 
@@ -149,6 +253,45 @@ rust-analyzer itself.
 |===
 
 
+=== Status
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/status.rs[status.rs]
+
+
+Shows internal statistic about memory usage of rust-analyzer.
+
+|===
+| Editor  | Action Name
+
+| VS Code | **Rust Analyzer: Status**
+|===
+
+
+=== Structural Seach and Replace
+**Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide/src/ssr.rs[ssr.rs]
+
+
+Search and replace with named wildcards that will match any expression.
+The syntax for a structural search replace command is ` ==>> `.
+A `$:expr` placeholder in the search pattern will match any expression and `$` will reference it in the replacement.
+Available via the command `rust-analyzer.ssr`.
+
+```rust
+// Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)]
+
+// BEFORE
+String::from(foo(y + 5, z))
+
+// AFTER
+String::from((y + 5).foo(z))
+```
+
+|===
+| Editor  | Action Name
+
+| VS Code | **Rust Analyzer: Structural Search Replace**
+|===
+
+
 === Workspace Symbol
 **Source:** https://github.com/rust-analyzer/rust-analyzer/blob/master/crates/ra_ide_db/src/symbol_index.rs[symbol_index.rs]
 
diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc
index 8cfa41144e71..12def732710c 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/readme.adoc
@@ -8,6 +8,7 @@
 :important-caption: :heavy_exclamation_mark:
 :caution-caption: :fire:
 :warning-caption: :warning:
+:source-highlighter: rouge
 :experimental:
 
 // Master copy of this document lives in the https://github.com/rust-analyzer/rust-analyzer repository
diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs
index 170a3e88942a..a0c2ffef91ce 100644
--- a/xtask/src/codegen/gen_feature_docs.rs
+++ b/xtask/src/codegen/gen_feature_docs.rs
@@ -50,12 +50,12 @@ impl Feature {
 
 fn is_valid_feature_name(feature: &str) -> bool {
     'word: for word in feature.split_whitespace() {
-        for &short in ["to"].iter() {
+        for &short in ["to", "and"].iter() {
             if word == short {
                 continue 'word;
             }
         }
-        for &short in ["To"].iter() {
+        for &short in ["To", "And"].iter() {
             if word == short {
                 return false;
             }

From 1cd78a3355ea70d3070cabb00c80a5d195499752 Mon Sep 17 00:00:00 2001
From: robojumper 
Date: Sun, 31 May 2020 10:59:40 +0200
Subject: [PATCH 116/163] correctly infer labelled breaks

---
 crates/ra_hir_def/src/body/lower.rs         | 56 +++++++++++++++++----
 crates/ra_hir_def/src/body/scope.rs         |  4 +-
 crates/ra_hir_def/src/expr.rs               | 19 ++++---
 crates/ra_hir_expand/src/name.rs            |  5 ++
 crates/ra_hir_ty/src/infer.rs               | 11 ++++
 crates/ra_hir_ty/src/infer/expr.rs          | 45 +++++++++++------
 crates/ra_hir_ty/src/tests/simple.rs        | 20 ++++----
 crates/ra_syntax/src/ast/generated/nodes.rs |  1 +
 xtask/src/ast_src.rs                        |  2 +-
 9 files changed, 117 insertions(+), 46 deletions(-)

diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index 905c0cf5dad2..dc52c6bd9687 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -134,7 +134,7 @@ impl ExprCollector<'_> {
         self.make_expr(expr, Err(SyntheticSyntax))
     }
     fn empty_block(&mut self) -> ExprId {
-        self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None })
+        self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None, label: None })
     }
     fn missing_expr(&mut self) -> ExprId {
         self.alloc_expr_desugared(Expr::Missing)
@@ -215,7 +215,13 @@ impl ExprCollector<'_> {
             ast::Expr::BlockExpr(e) => self.collect_block(e),
             ast::Expr::LoopExpr(e) => {
                 let body = self.collect_block_opt(e.loop_body());
-                self.alloc_expr(Expr::Loop { body }, syntax_ptr)
+                self.alloc_expr(
+                    Expr::Loop {
+                        body,
+                        label: e.label().and_then(|l| l.lifetime_token()).map(|l| Name::new_lifetime(&l)),
+                    },
+                    syntax_ptr,
+                )
             }
             ast::Expr::WhileExpr(e) => {
                 let body = self.collect_block_opt(e.loop_body());
@@ -230,25 +236,47 @@ impl ExprCollector<'_> {
                             let pat = self.collect_pat(pat);
                             let match_expr = self.collect_expr_opt(condition.expr());
                             let placeholder_pat = self.missing_pat();
-                            let break_ = self.alloc_expr_desugared(Expr::Break { expr: None });
+                            let break_ =
+                                self.alloc_expr_desugared(Expr::Break { expr: None, label: None });
                             let arms = vec![
                                 MatchArm { pat, expr: body, guard: None },
                                 MatchArm { pat: placeholder_pat, expr: break_, guard: None },
                             ];
                             let match_expr =
                                 self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms });
-                            return self.alloc_expr(Expr::Loop { body: match_expr }, syntax_ptr);
+                            return self.alloc_expr(
+                                Expr::Loop {
+                                    body: match_expr,
+                                    label: e.label().and_then(|l| l.lifetime_token()).map(|l| Name::new_lifetime(&l)),
+                                },
+                                syntax_ptr,
+                            );
                         }
                     },
                 };
 
-                self.alloc_expr(Expr::While { condition, body }, syntax_ptr)
+                self.alloc_expr(
+                    Expr::While {
+                        condition,
+                        body,
+                        label: e.label().and_then(|l| l.lifetime_token()).map(|l| Name::new_lifetime(&l)),
+                    },
+                    syntax_ptr,
+                )
             }
             ast::Expr::ForExpr(e) => {
                 let iterable = self.collect_expr_opt(e.iterable());
                 let pat = self.collect_pat_opt(e.pat());
                 let body = self.collect_block_opt(e.loop_body());
-                self.alloc_expr(Expr::For { iterable, pat, body }, syntax_ptr)
+                self.alloc_expr(
+                    Expr::For {
+                        iterable,
+                        pat,
+                        body,
+                        label: e.label().and_then(|l| l.lifetime_token()).map(|l| Name::new_lifetime(&l)),
+                    },
+                    syntax_ptr,
+                )
             }
             ast::Expr::CallExpr(e) => {
                 let callee = self.collect_expr_opt(e.expr());
@@ -301,13 +329,18 @@ impl ExprCollector<'_> {
                     .unwrap_or(Expr::Missing);
                 self.alloc_expr(path, syntax_ptr)
             }
-            ast::Expr::ContinueExpr(_e) => {
-                // FIXME: labels
-                self.alloc_expr(Expr::Continue, syntax_ptr)
+            ast::Expr::ContinueExpr(e) => {
+                self.alloc_expr(
+                    Expr::Continue { label: e.lifetime_token().map(|l| Name::new_lifetime(&l)) },
+                    syntax_ptr,
+                )
             }
             ast::Expr::BreakExpr(e) => {
                 let expr = e.expr().map(|e| self.collect_expr(e));
-                self.alloc_expr(Expr::Break { expr }, syntax_ptr)
+                self.alloc_expr(
+                    Expr::Break { expr, label: e.lifetime_token().map(|l| Name::new_lifetime(&l)) },
+                    syntax_ptr,
+                )
             }
             ast::Expr::ParenExpr(e) => {
                 let inner = self.collect_expr_opt(e.expr());
@@ -529,7 +562,8 @@ impl ExprCollector<'_> {
             })
             .collect();
         let tail = block.expr().map(|e| self.collect_expr(e));
-        self.alloc_expr(Expr::Block { statements, tail }, syntax_node_ptr)
+        let label = block.label().and_then(|l| l.lifetime_token()).map(|t| Name::new_lifetime(&t));
+        self.alloc_expr(Expr::Block { statements, tail, label }, syntax_node_ptr)
     }
 
     fn collect_block_items(&mut self, block: &ast::BlockExpr) {
diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs
index 09e92b74e1ab..e48ff38f9604 100644
--- a/crates/ra_hir_def/src/body/scope.rs
+++ b/crates/ra_hir_def/src/body/scope.rs
@@ -138,10 +138,10 @@ fn compute_block_scopes(
 fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) {
     scopes.set_scope(expr, scope);
     match &body[expr] {
-        Expr::Block { statements, tail } => {
+        Expr::Block { statements, tail, .. } => {
             compute_block_scopes(&statements, *tail, body, scopes, scope);
         }
-        Expr::For { iterable, pat, body: body_expr } => {
+        Expr::For { iterable, pat, body: body_expr, .. } => {
             compute_expr_scopes(*iterable, body, scopes, scope);
             let scope = scopes.new_scope(scope);
             scopes.add_bindings(body, scope, *pat);
diff --git a/crates/ra_hir_def/src/expr.rs b/crates/ra_hir_def/src/expr.rs
index f25c6f958079..8683f6c7f647 100644
--- a/crates/ra_hir_def/src/expr.rs
+++ b/crates/ra_hir_def/src/expr.rs
@@ -52,18 +52,22 @@ pub enum Expr {
     Block {
         statements: Vec,
         tail: Option,
+        label: Option,
     },
     Loop {
         body: ExprId,
+        label: Option,
     },
     While {
         condition: ExprId,
         body: ExprId,
+        label: Option,
     },
     For {
         iterable: ExprId,
         pat: PatId,
         body: ExprId,
+        label: Option,
     },
     Call {
         callee: ExprId,
@@ -79,9 +83,12 @@ pub enum Expr {
         expr: ExprId,
         arms: Vec,
     },
-    Continue,
+    Continue {
+        label: Option,
+    },
     Break {
         expr: Option,
+        label: Option,
     },
     Return {
         expr: Option,
@@ -225,7 +232,7 @@ impl Expr {
                     f(*else_branch);
                 }
             }
-            Expr::Block { statements, tail } => {
+            Expr::Block { statements, tail, .. } => {
                 for stmt in statements {
                     match stmt {
                         Statement::Let { initializer, .. } => {
@@ -241,8 +248,8 @@ impl Expr {
                 }
             }
             Expr::TryBlock { body } => f(*body),
-            Expr::Loop { body } => f(*body),
-            Expr::While { condition, body } => {
+            Expr::Loop { body, .. } => f(*body),
+            Expr::While { condition, body, .. } => {
                 f(*condition);
                 f(*body);
             }
@@ -268,8 +275,8 @@ impl Expr {
                     f(arm.expr);
                 }
             }
-            Expr::Continue => {}
-            Expr::Break { expr } | Expr::Return { expr } => {
+            Expr::Continue { .. } => {},
+            Expr::Break { expr, .. } | Expr::Return { expr } => {
                 if let Some(expr) = expr {
                     f(*expr);
                 }
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index fecce224ee60..ea495cb11a2b 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -37,6 +37,11 @@ impl Name {
         Name(Repr::TupleField(idx))
     }
 
+    pub fn new_lifetime(lt: &ra_syntax::SyntaxToken) -> Name {
+        assert!(lt.kind() == ra_syntax::SyntaxKind::LIFETIME);
+        Name(Repr::Text(lt.text().clone()))
+    }
+
     /// Shortcut to create inline plain text name
     const fn new_inline_ascii(text: &[u8]) -> Name {
         Name::new_text(SmolStr::new_inline_from_ascii(text.len(), text))
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index 957d6e0b5792..dc77e88e50b2 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -219,6 +219,17 @@ struct InferenceContext<'a> {
 struct BreakableContext {
     pub may_break: bool,
     pub break_ty: Ty,
+    pub label: Option,
+}
+
+fn find_breakable<'c>(
+    ctxs: &'c mut [BreakableContext],
+    label: Option<&name::Name>,
+) -> Option<&'c mut BreakableContext> {
+    match label {
+        Some(_) => ctxs.iter_mut().rev().find(|ctx| ctx.label.as_ref() == label),
+        None => ctxs.last_mut(),
+    }
 }
 
 impl<'a> InferenceContext<'a> {
diff --git a/crates/ra_hir_ty/src/infer/expr.rs b/crates/ra_hir_ty/src/infer/expr.rs
index 78084cb573cb..4a98e2debff5 100644
--- a/crates/ra_hir_ty/src/infer/expr.rs
+++ b/crates/ra_hir_ty/src/infer/expr.rs
@@ -22,8 +22,8 @@ use crate::{
 };
 
 use super::{
-    BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic,
-    TypeMismatch,
+    find_breakable, BindingMode, BreakableContext, Diverges, Expectation, InferenceContext,
+    InferenceDiagnostic, TypeMismatch,
 };
 
 impl<'a> InferenceContext<'a> {
@@ -86,16 +86,20 @@ impl<'a> InferenceContext<'a> {
 
                 self.coerce_merge_branch(&then_ty, &else_ty)
             }
-            Expr::Block { statements, tail } => self.infer_block(statements, *tail, expected),
+            Expr::Block { statements, tail, .. } => {
+                // FIXME: Breakable block inference
+                self.infer_block(statements, *tail, expected)
+            }
             Expr::TryBlock { body } => {
                 let _inner = self.infer_expr(*body, expected);
                 // FIXME should be std::result::Result<{inner}, _>
                 Ty::Unknown
             }
-            Expr::Loop { body } => {
+            Expr::Loop { body, label } => {
                 self.breakables.push(BreakableContext {
                     may_break: false,
                     break_ty: self.table.new_type_var(),
+                    label: label.clone(),
                 });
                 self.infer_expr(*body, &Expectation::has_type(Ty::unit()));
 
@@ -110,8 +114,12 @@ impl<'a> InferenceContext<'a> {
                     Ty::simple(TypeCtor::Never)
                 }
             }
-            Expr::While { condition, body } => {
-                self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown });
+            Expr::While { condition, body, label } => {
+                self.breakables.push(BreakableContext {
+                    may_break: false,
+                    break_ty: Ty::Unknown,
+                    label: label.clone(),
+                });
                 // 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()));
@@ -120,10 +128,14 @@ impl<'a> InferenceContext<'a> {
                 self.diverges = Diverges::Maybe;
                 Ty::unit()
             }
-            Expr::For { iterable, body, pat } => {
+            Expr::For { iterable, body, pat, label } => {
                 let iterable_ty = self.infer_expr(*iterable, &Expectation::none());
 
-                self.breakables.push(BreakableContext { may_break: false, break_ty: Ty::Unknown });
+                self.breakables.push(BreakableContext {
+                    may_break: false,
+                    break_ty: Ty::Unknown,
+                    label: label.clone(),
+                });
                 let pat_ty =
                     self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
 
@@ -236,23 +248,24 @@ impl<'a> InferenceContext<'a> {
                 let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
                 self.infer_path(&resolver, p, tgt_expr.into()).unwrap_or(Ty::Unknown)
             }
-            Expr::Continue => Ty::simple(TypeCtor::Never),
-            Expr::Break { expr } => {
+            Expr::Continue { .. } => Ty::simple(TypeCtor::Never),
+            Expr::Break { expr, label } => {
                 let val_ty = if let Some(expr) = expr {
                     self.infer_expr(*expr, &Expectation::none())
                 } else {
                     Ty::unit()
                 };
 
-                let last_ty = if let Some(ctxt) = self.breakables.last() {
-                    ctxt.break_ty.clone()
-                } else {
-                    Ty::Unknown
-                };
+                let last_ty =
+                    if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
+                        ctxt.break_ty.clone()
+                    } else {
+                        Ty::Unknown
+                    };
 
                 let merged_type = self.coerce_merge_branch(&last_ty, &val_ty);
 
-                if let Some(ctxt) = self.breakables.last_mut() {
+                if let Some(ctxt) = find_breakable(&mut self.breakables, label.as_ref()) {
                     ctxt.break_ty = merged_type;
                     ctxt.may_break = true;
                 } else {
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index beceb863434d..88309157b78c 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -1969,17 +1969,17 @@ fn foo() {
     25..333 '|| 'ou...     }': || -> bool
     28..333 ''outer...     }': bool
     41..333 '{     ...     }': ()
-    55..60 'inner': i32
-    63..301 ''inner...     }': i32
+    55..60 'inner': i8
+    63..301 ''inner...     }': i8
     76..301 '{     ...     }': ()
-    94..95 'i': i32
+    94..95 'i': bool
     98..114 'Defaul...efault': {unknown}
-    98..116 'Defaul...ault()': i32
+    98..116 'Defaul...ault()': bool
     130..270 'if (br...     }': ()
     134..148 'break 'outer i': !
-    147..148 'i': i32
+    147..148 'i': bool
     150..209 '{     ...     }': ()
-    168..194 'loop {...5i8; }': i8
+    168..194 'loop {...5i8; }': !
     173..194 '{ brea...5i8; }': ()
     175..191 'break ...er 5i8': !
     188..191 '5i8': i8
@@ -1987,13 +1987,13 @@ fn foo() {
     218..222 'true': bool
     223..270 '{     ...     }': ()
     241..255 'break 'inner 6': !
-    254..255 '6': i32
+    254..255 '6': i8
     283..290 'break 7': !
-    289..290 '7': i32
+    289..290 '7': i8
     311..326 'break inner < 8': !
-    317..322 'inner': i32
+    317..322 'inner': i8
     317..326 'inner < 8': bool
-    325..326 '8': i32
+    325..326 '8': i8
     "###
     );
 }
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs
index 255402fbce32..cb430ca01352 100644
--- a/crates/ra_syntax/src/ast/generated/nodes.rs
+++ b/crates/ra_syntax/src/ast/generated/nodes.rs
@@ -1081,6 +1081,7 @@ pub struct BlockExpr {
 impl ast::AttrsOwner for BlockExpr {}
 impl ast::ModuleItemOwner for BlockExpr {}
 impl BlockExpr {
+    pub fn label(&self) -> Option