From f78de3bb95acb996102a74b5b12d33054ba6d4c4 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sat, 18 Apr 2020 19:26:35 +0800 Subject: [PATCH 01/29] Ignore proc-macro in completion --- crates/ra_hir/src/code_model.rs | 11 +++++++++++ crates/ra_ide/src/completion/presentation.rs | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 3801fce23aeb..6e0d89466bdb 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -759,6 +759,17 @@ pub fn module(self, db: &dyn HirDatabase) -> Option { pub fn name(self, db: &dyn HirDatabase) -> Option { self.source(db).value.name().map(|it| it.as_name()) } + + /// Indicate it is a proc-macro + pub fn is_proc_macro(&self) -> bool { + match self.id.kind { + hir_expand::MacroDefKind::Declarative => false, + hir_expand::MacroDefKind::BuiltIn(_) => false, + hir_expand::MacroDefKind::BuiltInDerive(_) => false, + hir_expand::MacroDefKind::BuiltInEager(_) => false, + hir_expand::MacroDefKind::CustomDerive(_) => true, + } + } } /// Invariant: `inner.as_assoc_item(db).is_some()` diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 55f75b15aa1f..2189cef658e3 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs @@ -156,6 +156,12 @@ pub(crate) fn add_macro( name: Option, macro_: hir::MacroDef, ) { + // FIXME: Currently proc-macro do not have ast-node, + // such that it does not have source + if macro_.is_proc_macro() { + return; + } + let name = match name { Some(it) => it, None => return, From da18f1130756c4fdd611d95e4f98e553b4b65995 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sat, 18 Apr 2020 19:28:07 +0800 Subject: [PATCH 02/29] Split LIFETIME to two tokens in mbe --- crates/ra_mbe/src/mbe_expander/matcher.rs | 34 ++++++++++++++++----- crates/ra_mbe/src/subtree_source.rs | 32 +++++++++++++++++++- crates/ra_mbe/src/syntax_bridge.rs | 37 ++++++++++++++++++++--- 3 files changed, 89 insertions(+), 14 deletions(-) diff --git a/crates/ra_mbe/src/mbe_expander/matcher.rs b/crates/ra_mbe/src/mbe_expander/matcher.rs index 2579382da8da..9485c62b839a 100644 --- a/crates/ra_mbe/src/mbe_expander/matcher.rs +++ b/crates/ra_mbe/src/mbe_expander/matcher.rs @@ -202,6 +202,13 @@ fn eat_separator(&mut self, separator: &Separator) -> bool { } pub(crate) fn expect_tt(&mut self) -> Result { + match self.peek_n(0) { + Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) if punct.char == '\'' => { + return self.expect_lifetime(); + } + _ => (), + } + let tt = self.next().ok_or_else(|| ())?.clone(); let punct = match tt { tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if punct.spacing == tt::Spacing::Joint => { @@ -255,13 +262,21 @@ pub(crate) fn expect_tt(&mut self) -> Result { } } - pub(crate) fn expect_lifetime(&mut self) -> Result<&tt::Ident, ()> { - let ident = self.expect_ident()?; - // check if it start from "`" - if !ident.text.starts_with('\'') { + pub(crate) fn expect_lifetime(&mut self) -> Result { + let punct = self.expect_punct()?; + if punct.char != '\'' { return Err(()); } - Ok(ident) + let ident = self.expect_ident()?; + + Ok(tt::Subtree { + delimiter: None, + token_trees: vec![ + tt::Leaf::Punct(punct.clone()).into(), + tt::Leaf::Ident(ident.clone()).into(), + ], + } + .into()) } pub(crate) fn expect_fragment( @@ -274,7 +289,10 @@ pub(crate) struct OffsetTokenSink<'a> { } impl<'a> TreeSink for OffsetTokenSink<'a> { - fn token(&mut self, _kind: SyntaxKind, n_tokens: u8) { + fn token(&mut self, kind: SyntaxKind, mut n_tokens: u8) { + if kind == SyntaxKind::LIFETIME { + n_tokens = 2; + } for _ in 0..n_tokens { self.cursor = self.cursor.bump_subtree(); } @@ -286,7 +304,7 @@ fn error(&mut self, _error: ra_parser::ParseError) { } } - let buffer = TokenBuffer::new(self.inner.as_slice()); + let buffer = TokenBuffer::new(&self.inner.as_slice()); let mut src = SubtreeTokenSource::new(&buffer); let mut sink = OffsetTokenSink { cursor: buffer.begin(), error: false }; @@ -422,7 +440,7 @@ fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult input.expect_tt().map(Some).map_err(|()| err!()), "lifetime" => input .expect_lifetime() - .map(|ident| Some(tt::Leaf::Ident(ident.clone()).into())) + .map(|tt| Some(tt)) .map_err(|()| err!("expected lifetime")), "literal" => input .expect_literal() diff --git a/crates/ra_mbe/src/subtree_source.rs b/crates/ra_mbe/src/subtree_source.rs index 91e324db9084..46791efaaefb 100644 --- a/crates/ra_mbe/src/subtree_source.rs +++ b/crates/ra_mbe/src/subtree_source.rs @@ -50,6 +50,26 @@ fn mk_token(&self, pos: usize) -> Token { } fn get(&self, pos: usize) -> Ref> { + fn is_lifetime(c: Cursor) -> Option<(Cursor, SmolStr)> { + let tkn = c.token_tree(); + + if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = tkn { + if punct.char == '\'' { + let next = c.bump(); + if let Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) = next.token_tree() { + let res_cursor = next.bump(); + let text = SmolStr::new("'".to_string() + &ident.to_string()); + + return Some((res_cursor, text)); + } else { + panic!("Next token must be ident : {:#?}", next.token_tree()); + } + } + } + + None + } + if pos < self.cached.borrow().len() { return Ref::map(self.cached.borrow(), |c| &c[pos]); } @@ -63,6 +83,12 @@ fn get(&self, pos: usize) -> Ref> { continue; } + if let Some((curr, text)) = is_lifetime(cursor) { + cached.push(Some(TtToken { kind: LIFETIME, is_joint_to_next: false, text })); + self.cached_cursor.set(curr); + continue; + } + match cursor.token_tree() { Some(tt::TokenTree::Leaf(leaf)) => { cached.push(Some(convert_leaf(&leaf))); @@ -152,7 +178,11 @@ fn convert_ident(ident: &tt::Ident) -> TtToken { } fn convert_punct(p: tt::Punct) -> TtToken { - let kind = SyntaxKind::from_char(p.char).unwrap(); + let kind = match SyntaxKind::from_char(p.char) { + None => panic!("{:#?} is not a valid punct", p), + Some(kind) => kind, + }; + let text = { let mut buf = [0u8; 4]; let s: &str = p.char.encode_utf8(&mut buf); diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs index 31e9b22e7dc4..70899bc5d93f 100644 --- a/crates/ra_mbe/src/syntax_bridge.rs +++ b/crates/ra_mbe/src/syntax_bridge.rs @@ -271,7 +271,7 @@ struct RawConvertor<'a> { inner: std::slice::Iter<'a, RawToken>, } -trait SrcToken { +trait SrcToken: std::fmt::Debug { fn kind(&self) -> SyntaxKind; fn to_char(&self) -> Option; @@ -361,8 +361,12 @@ fn collect_leaf(&mut self, result: &mut Vec) { Some(next) if next.kind().is_punct() => tt::Spacing::Joint, _ => tt::Spacing::Alone, }; - let char = token.to_char().expect("Token from lexer must be single char"); - + let char = match token.to_char() { + Some(c) => c, + None => { + panic!("Token from lexer must be single char: token = {:#?}", token); + } + }; tt::Leaf::from(tt::Punct { char, spacing, id: self.id_alloc().alloc(range) }).into() } } else { @@ -373,9 +377,28 @@ macro_rules! make_leaf { } let leaf: tt::Leaf = match k { T![true] | T![false] => make_leaf!(Literal), - IDENT | LIFETIME => make_leaf!(Ident), + IDENT => make_leaf!(Ident), k if k.is_keyword() => make_leaf!(Ident), k if k.is_literal() => make_leaf!(Literal), + LIFETIME => { + let char_unit = TextUnit::from_usize(1); + let r = TextRange::offset_len(range.start(), char_unit); + let apostrophe = tt::Leaf::from(tt::Punct { + char: '\'', + spacing: tt::Spacing::Joint, + id: self.id_alloc().alloc(r), + }); + result.push(apostrophe.into()); + + let r = + TextRange::offset_len(range.start() + char_unit, range.len() - char_unit); + let ident = tt::Leaf::from(tt::Ident { + text: SmolStr::new(&token.to_text()[1..]), + id: self.id_alloc().alloc(r), + }); + result.push(ident.into()); + return; + } _ => return, }; @@ -455,6 +478,7 @@ fn new(node: &SyntaxNode, global_offset: TextUnit) -> Convertor { } } +#[derive(Debug)] enum SynToken { Ordiniary(SyntaxToken), Punch(SyntaxToken, TextUnit), @@ -592,11 +616,14 @@ fn delim_to_str(d: Option, closing: bool) -> SmolStr { } impl<'a> TreeSink for TtTreeSink<'a> { - fn token(&mut self, kind: SyntaxKind, n_tokens: u8) { + fn token(&mut self, kind: SyntaxKind, mut n_tokens: u8) { if kind == L_DOLLAR || kind == R_DOLLAR { self.cursor = self.cursor.bump_subtree(); return; } + if kind == LIFETIME { + n_tokens = 2; + } let mut last = self.cursor; for _ in 0..n_tokens { From 72bba9882889b2e20fd91e3c6c3a97debbbe6543 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sat, 18 Apr 2020 19:29:04 +0800 Subject: [PATCH 03/29] Merge empty delim subtree in proc-macro --- crates/ra_proc_macro_srv/src/rustc_server.rs | 11 ++++++++++- .../src/tests/fixtures/test_serialize_proc_macro.txt | 3 +-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/crates/ra_proc_macro_srv/src/rustc_server.rs b/crates/ra_proc_macro_srv/src/rustc_server.rs index ec0d356922d0..9fcfdc4504c9 100644 --- a/crates/ra_proc_macro_srv/src/rustc_server.rs +++ b/crates/ra_proc_macro_srv/src/rustc_server.rs @@ -76,7 +76,16 @@ fn extend>(&mut self, trees: I) { impl Extend for TokenStream { fn extend>(&mut self, streams: I) { for item in streams { - self.subtree.token_trees.extend(&mut item.into_iter()) + for tkn in item { + match tkn { + tt::TokenTree::Subtree(subtree) if subtree.delimiter.is_none() => { + self.subtree.token_trees.extend(subtree.token_trees); + } + _ => { + self.subtree.token_trees.push(tkn); + } + } + } } } } diff --git a/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt b/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt index 24507d98d76f..1f5d940fa0bc 100644 --- a/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt +++ b/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt @@ -25,8 +25,7 @@ SUBTREE $ SUBTREE () 4294967295 IDENT feature 4294967295 PUNCH = [alone] 4294967295 - SUBTREE $ - LITERAL "cargo-clippy" 0 + LITERAL "cargo-clippy" 0 PUNCH , [alone] 4294967295 IDENT allow 4294967295 SUBTREE () 4294967295 From 7adb681b1f8dbdc0205efbe22698c28ab9da6378 Mon Sep 17 00:00:00 2001 From: Josh Mcguigan Date: Thu, 16 Apr 2020 17:08:24 -0700 Subject: [PATCH 04/29] missing match arm diagnostic support enum record type --- crates/ra_hir_ty/src/_match.rs | 336 +++++++++++++++++++++++++++++--- crates/ra_hir_ty/src/test_db.rs | 36 +++- 2 files changed, 331 insertions(+), 41 deletions(-) diff --git a/crates/ra_hir_ty/src/_match.rs b/crates/ra_hir_ty/src/_match.rs index 688026a0408d..779e7857458a 100644 --- a/crates/ra_hir_ty/src/_match.rs +++ b/crates/ra_hir_ty/src/_match.rs @@ -235,10 +235,19 @@ fn from(pat_id: PatId) -> Self { } } +impl From<&PatId> for PatIdOrWild { + fn from(pat_id: &PatId) -> Self { + Self::PatId(*pat_id) + } +} + #[derive(Debug, Clone, Copy, PartialEq)] pub enum MatchCheckErr { NotImplemented, MalformedMatchArm, + /// Used when type inference cannot resolve the type of + /// a pattern or expression. + Unknown, } /// The return type of `is_useful` is either an indication of usefulness @@ -290,10 +299,14 @@ fn to_tail(&self) -> PatStack { Self::from_slice(&self.0[1..]) } - fn replace_head_with + Copy>(&self, pat_ids: &[T]) -> PatStack { + fn replace_head_with(&self, pats: I) -> PatStack + where + I: Iterator, + T: Into, + { let mut patterns: PatStackInner = smallvec![]; - for pat in pat_ids { - patterns.push((*pat).into()); + for pat in pats { + patterns.push(pat.into()); } for pat in &self.0[1..] { patterns.push(*pat); @@ -330,7 +343,7 @@ fn specialize_constructor( return Err(MatchCheckErr::NotImplemented); } - Some(self.replace_head_with(pat_ids)) + Some(self.replace_head_with(pat_ids.iter())) } (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => { match cx.body.exprs[lit_expr] { @@ -382,7 +395,7 @@ fn specialize_constructor( new_patterns.push((*pat_id).into()); } - Some(self.replace_head_with(&new_patterns)) + Some(self.replace_head_with(new_patterns.into_iter())) } else { return Err(MatchCheckErr::MalformedMatchArm); } @@ -390,13 +403,41 @@ fn specialize_constructor( // If there is no ellipsis in the tuple pattern, the number // of patterns must equal the constructor arity. if pat_ids.len() == constructor_arity { - Some(self.replace_head_with(pat_ids)) + Some(self.replace_head_with(pat_ids.into_iter())) } else { return Err(MatchCheckErr::MalformedMatchArm); } } } } + (Pat::Record { args: ref arg_patterns, .. }, Constructor::Enum(e)) => { + let pat_id = self.head().as_id().expect("we know this isn't a wild"); + if !enum_variant_matches(cx, pat_id, *e) { + None + } else { + match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() { + VariantData::Record(struct_field_arena) => { + // Here we treat any missing fields in the record as the wild pattern, as + // if the record has ellipsis. We want to do this here even if the + // record does not contain ellipsis, because it allows us to continue + // enforcing exhaustiveness for the rest of the match statement. + // + // Creating the diagnostic for the missing field in the pattern + // should be done in a different diagnostic. + let patterns = struct_field_arena.iter().map(|(_, struct_field)| { + arg_patterns + .iter() + .find(|pat| pat.name == struct_field.name) + .map(|pat| PatIdOrWild::from(pat.pat)) + .unwrap_or(PatIdOrWild::Wild) + }); + + Some(self.replace_head_with(patterns)) + } + _ => return Err(MatchCheckErr::Unknown), + } + } + } (Pat::Or(_), _) => return Err(MatchCheckErr::NotImplemented), (_, _) => return Err(MatchCheckErr::NotImplemented), }; @@ -655,8 +696,8 @@ fn arity(&self, cx: &MatchCheckCtx) -> MatchCheckResult { Constructor::Enum(e) => { match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() { VariantData::Tuple(struct_field_data) => struct_field_data.len(), + VariantData::Record(struct_field_data) => struct_field_data.len(), VariantData::Unit => 0, - _ => return Err(MatchCheckErr::NotImplemented), } } }; @@ -695,10 +736,10 @@ fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult Some(Constructor::Bool(val)), _ => return Err(MatchCheckErr::NotImplemented), }, - Pat::TupleStruct { .. } | Pat::Path(_) => { + Pat::TupleStruct { .. } | Pat::Path(_) | Pat::Record { .. } => { let pat_id = pat.as_id().expect("we already know this pattern is not a wild"); let variant_id = - cx.infer.variant_resolution_for_pat(pat_id).ok_or(MatchCheckErr::NotImplemented)?; + cx.infer.variant_resolution_for_pat(pat_id).ok_or(MatchCheckErr::Unknown)?; match variant_id { VariantId::EnumVariantId(enum_variant_id) => { Some(Constructor::Enum(enum_variant_id)) @@ -759,20 +800,22 @@ mod tests { pub(super) use insta::assert_snapshot; pub(super) use ra_db::fixture::WithFixture; - pub(super) use crate::test_db::TestDB; + pub(super) use crate::{diagnostics::MissingMatchArms, test_db::TestDB}; pub(super) fn check_diagnostic_message(content: &str) -> String { - TestDB::with_single_file(content).0.diagnostics().0 + TestDB::with_single_file(content).0.diagnostic::().0 } pub(super) fn check_diagnostic(content: &str) { - let diagnostic_count = TestDB::with_single_file(content).0.diagnostics().1; + let diagnostic_count = + TestDB::with_single_file(content).0.diagnostic::().1; assert_eq!(1, diagnostic_count, "no diagnostic reported"); } pub(super) fn check_no_diagnostic(content: &str) { - let diagnostic_count = TestDB::with_single_file(content).0.diagnostics().1; + let diagnostic_count = + TestDB::with_single_file(content).0.diagnostic::().1; assert_eq!(0, diagnostic_count, "expected no diagnostic, found one"); } @@ -1531,6 +1574,236 @@ fn test_fn() -> u32 { check_no_diagnostic(content); } + #[test] + fn enum_record_no_arms() { + let content = r" + enum Either { + A { foo: bool }, + B, + } + fn test_fn() { + let a = Either::A { foo: true }; + match a { + } + } + "; + + check_diagnostic(content); + } + + #[test] + fn enum_record_missing_arms() { + let content = r" + enum Either { + A { foo: bool }, + B, + } + fn test_fn() { + let a = Either::A { foo: true }; + match a { + Either::A { foo: true } => (), + } + } + "; + + check_diagnostic(content); + } + + #[test] + fn enum_record_no_diagnostic() { + let content = r" + enum Either { + A { foo: bool }, + B, + } + fn test_fn() { + let a = Either::A { foo: true }; + match a { + Either::A { foo: true } => (), + Either::A { foo: false } => (), + Either::B => (), + } + } + "; + + check_no_diagnostic(content); + } + + #[test] + fn enum_record_missing_field_no_diagnostic() { + let content = r" + enum Either { + A { foo: bool }, + B, + } + fn test_fn() { + let a = Either::B; + match a { + Either::A { } => (), + Either::B => (), + } + } + "; + + // When `Either::A` is missing a struct member, we don't want + // to fire the missing match arm diagnostic. This should fire + // some other diagnostic. + check_no_diagnostic(content); + } + + #[test] + fn enum_record_missing_field_missing_match_arm() { + let content = r" + enum Either { + A { foo: bool }, + B, + } + fn test_fn() { + let a = Either::B; + match a { + Either::A { } => (), + } + } + "; + + // Even though `Either::A` is missing fields, we still want to fire + // the missing arm diagnostic here, since we know `Either::B` is missing. + check_diagnostic(content); + } + + #[test] + fn enum_record_no_diagnostic_wild() { + let content = r" + enum Either { + A { foo: bool }, + B, + } + fn test_fn() { + let a = Either::A { foo: true }; + match a { + Either::A { foo: _ } => (), + Either::B => (), + } + } + "; + + check_no_diagnostic(content); + } + + #[test] + fn enum_record_fields_out_of_order_missing_arm() { + let content = r" + enum Either { + A { foo: bool, bar: () }, + B, + } + fn test_fn() { + let a = Either::A { foo: true }; + match a { + Either::A { bar: (), foo: false } => (), + Either::A { foo: true, bar: () } => (), + } + } + "; + + check_diagnostic(content); + } + + #[test] + fn enum_record_fields_out_of_order_no_diagnostic() { + let content = r" + enum Either { + A { foo: bool, bar: () }, + B, + } + fn test_fn() { + let a = Either::A { foo: true }; + match a { + Either::A { bar: (), foo: false } => (), + Either::A { foo: true, bar: () } => (), + Either::B => (), + } + } + "; + + check_no_diagnostic(content); + } + + #[test] + fn enum_record_ellipsis_missing_arm() { + let content = r" + enum Either { + A { foo: bool, bar: bool }, + B, + } + fn test_fn() { + match Either::B { + Either::A { foo: true, .. } => (), + Either::B => (), + } + } + "; + + check_diagnostic(content); + } + + #[test] + fn enum_record_ellipsis_no_diagnostic() { + let content = r" + enum Either { + A { foo: bool, bar: bool }, + B, + } + fn test_fn() { + let a = Either::A { foo: true }; + match a { + Either::A { foo: true, .. } => (), + Either::A { foo: false, .. } => (), + Either::B => (), + } + } + "; + + check_no_diagnostic(content); + } + + #[test] + fn enum_record_ellipsis_all_fields_missing_arm() { + let content = r" + enum Either { + A { foo: bool, bar: bool }, + B, + } + fn test_fn() { + let a = Either::B; + match a { + Either::A { .. } => (), + } + } + "; + + check_diagnostic(content); + } + + #[test] + fn enum_record_ellipsis_all_fields_no_diagnostic() { + let content = r" + enum Either { + A { foo: bool, bar: bool }, + B, + } + fn test_fn() { + let a = Either::B; + match a { + Either::A { .. } => (), + Either::B => (), + } + } + "; + + check_no_diagnostic(content); + } + #[test] fn enum_tuple_partial_ellipsis_no_diagnostic() { let content = r" @@ -1688,25 +1961,6 @@ fn test_fn() { check_no_diagnostic(content); } - #[test] - fn enum_record() { - let content = r" - enum Either { - A { foo: u32 }, - B, - } - fn test_fn() { - match Either::B { - Either::A { foo: 5 } => (), - } - } - "; - - // This is a false negative. - // We don't currently handle enum record types. - check_no_diagnostic(content); - } - #[test] fn internal_or() { let content = r" @@ -1796,4 +2050,22 @@ fn test_fn() { // We don't currently handle tuple patterns with ellipsis. check_no_diagnostic(content); } + + #[test] + fn struct_missing_arm() { + let content = r" + struct Foo { + a: bool, + } + fn test_fn(f: Foo) { + match f { + Foo { a: true } => {}, + } + } + "; + + // This is a false negative. + // We don't currently handle structs. + check_no_diagnostic(content); + } } diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs index 3a4d58bf9c4f..8498d3d966d0 100644 --- a/crates/ra_hir_ty/src/test_db.rs +++ b/crates/ra_hir_ty/src/test_db.rs @@ -12,7 +12,7 @@ }; use stdx::format_to; -use crate::{db::HirDatabase, expr::ExprValidator}; +use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator}; #[salsa::database( ra_db::SourceDatabaseExtStorage, @@ -104,10 +104,7 @@ pub fn module_for_file(&self, file_id: FileId) -> ModuleId { panic!("Can't find module for file") } - // FIXME: don't duplicate this - pub fn diagnostics(&self) -> (String, u32) { - let mut buf = String::new(); - let mut count = 0; + fn diag(&self, mut cb: F) { let crate_graph = self.crate_graph(); for krate in crate_graph.iter() { let crate_def_map = self.crate_def_map(krate); @@ -132,15 +129,36 @@ pub fn diagnostics(&self) -> (String, u32) { for f in fns { let infer = self.infer(f.into()); - let mut sink = DiagnosticSink::new(|d| { - format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message()); - count += 1; - }); + let mut sink = DiagnosticSink::new(&mut cb); infer.add_diagnostics(self, f, &mut sink); let mut validator = ExprValidator::new(f, infer, &mut sink); validator.validate_body(self); } } + } + + pub fn diagnostics(&self) -> (String, u32) { + let mut buf = String::new(); + let mut count = 0; + self.diag(|d| { + format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message()); + count += 1; + }); + (buf, count) + } + + /// Like `diagnostics`, but filtered for a single diagnostic. + pub fn diagnostic(&self) -> (String, u32) { + let mut buf = String::new(); + let mut count = 0; + self.diag(|d| { + // We want to filter diagnostics by the particular one we are testing for, to + // avoid surprising results in tests. + if d.downcast_ref::().is_some() { + format_to!(buf, "{:?}: {}\n", d.syntax_node(self).text(), d.message()); + count += 1; + }; + }); (buf, count) } } From ce674be2176e14e0d57c3a3e9edc315304aa2fde Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sun, 19 Apr 2020 02:45:17 +0800 Subject: [PATCH 05/29] Add mbe lifetime split test --- crates/ra_mbe/src/tests.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs index 5d1274d21faa..f2a726538c25 100644 --- a/crates/ra_mbe/src/tests.rs +++ b/crates/ra_mbe/src/tests.rs @@ -214,6 +214,33 @@ fn from(it: $v) -> $e { ); } +#[test] +fn test_lifetime_split() { + parse_macro( + r#" +macro_rules! foo { + ($($t:tt)*) => { $($t)*} +} +"#, + ) + .assert_expand( + r#"foo!(static bar: &'static str = "hello";);"#, + r#" +SUBTREE $ + IDENT static 17 + IDENT bar 18 + PUNCH : [alone] 19 + PUNCH & [alone] 20 + PUNCH ' [joint] 21 + IDENT static 22 + IDENT str 23 + PUNCH = [alone] 24 + LITERAL "hello" 25 + PUNCH ; [joint] 26 +"#, + ); +} + #[test] fn test_expr_order() { let expanded = parse_macro( From 21b98d585e0c84b14780166f6607e8f1b8560433 Mon Sep 17 00:00:00 2001 From: veetaha Date: Sat, 18 Apr 2020 21:46:24 +0300 Subject: [PATCH 06/29] Refucktor codegen --- xtask/src/codegen.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index 678b40133b0c..0e4dcb95a2df 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs @@ -9,9 +9,9 @@ mod gen_parser_tests; mod gen_assists_docs; -use std::{fs, mem, path::Path}; +use std::{mem, path::Path}; -use crate::Result; +use crate::{not_bash::fs2, Result}; pub use self::{ gen_assists_docs::generate_assists_docs, gen_parser_tests::generate_parser_tests, @@ -39,7 +39,7 @@ pub enum Mode { /// A helper to update file on disk if it has changed. /// With verify = false, fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { - match fs::read_to_string(path) { + match fs2::read_to_string(path) { Ok(ref old_contents) if normalize(old_contents) == normalize(contents) => { return Ok(()); } @@ -49,7 +49,7 @@ fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { anyhow::bail!("`{}` is not up-to-date", path.display()); } eprintln!("updating {}", path.display()); - fs::write(path, contents)?; + fs2::write(path, contents)?; return Ok(()); fn normalize(s: &str) -> String { @@ -65,7 +65,7 @@ fn extract_comment_blocks_with_empty_lines(text: &str) -> Vec> { do_extract_comment_blocks(text, true) } -fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lins: bool) -> Vec> { +fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lines: bool) -> Vec> { let mut res = Vec::new(); let prefix = "// "; @@ -73,7 +73,7 @@ fn do_extract_comment_blocks(text: &str, allow_blocks_with_empty_lins: bool) -> let mut block = vec![]; for line in lines { - if line == "//" && allow_blocks_with_empty_lins { + if line == "//" && allow_blocks_with_empty_lines { block.push(String::new()); continue; } From a1b5cf81ebcac15299cc612b49023bb418507027 Mon Sep 17 00:00:00 2001 From: Edwin Cheng Date: Sun, 19 Apr 2020 03:24:17 +0800 Subject: [PATCH 07/29] Convert bool to ident instead of literal in mbe --- crates/ra_mbe/src/mbe_expander/matcher.rs | 6 ++++- crates/ra_mbe/src/subtree_source.rs | 15 +++++------- crates/ra_mbe/src/syntax_bridge.rs | 2 +- crates/ra_mbe/src/tests.rs | 30 +++++++++++++++++++++++ crates/ra_mbe/src/tt_iter.rs | 8 +++--- 5 files changed, 47 insertions(+), 14 deletions(-) diff --git a/crates/ra_mbe/src/mbe_expander/matcher.rs b/crates/ra_mbe/src/mbe_expander/matcher.rs index 9485c62b839a..78f9efa1b692 100644 --- a/crates/ra_mbe/src/mbe_expander/matcher.rs +++ b/crates/ra_mbe/src/mbe_expander/matcher.rs @@ -187,7 +187,11 @@ fn eat_separator(&mut self, separator: &Separator) -> bool { _ => false, }, Separator::Literal(lhs) => match fork.expect_literal() { - Ok(rhs) => rhs.text == lhs.text, + Ok(rhs) => match rhs { + tt::Leaf::Literal(rhs) => rhs.text == lhs.text, + tt::Leaf::Ident(rhs) => rhs.text == lhs.text, + tt::Leaf::Punct(_) => false, + }, _ => false, }, Separator::Puncts(lhss) => lhss.iter().all(|lhs| match fork.expect_punct() { diff --git a/crates/ra_mbe/src/subtree_source.rs b/crates/ra_mbe/src/subtree_source.rs index 46791efaaefb..d7866452dd94 100644 --- a/crates/ra_mbe/src/subtree_source.rs +++ b/crates/ra_mbe/src/subtree_source.rs @@ -158,20 +158,17 @@ fn convert_literal(l: &tt::Literal) -> TtToken { let kind = lex_single_syntax_kind(&l.text) .map(|(kind, _error)| kind) .filter(|kind| kind.is_literal()) - .unwrap_or_else(|| match l.text.as_ref() { - "true" => T![true], - "false" => T![false], - _ => panic!("Fail to convert given literal {:#?}", &l), - }); + .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &l)); TtToken { kind, is_joint_to_next: false, text: l.text.clone() } } fn convert_ident(ident: &tt::Ident) -> TtToken { - let kind = if ident.text.starts_with('\'') { - LIFETIME - } else { - SyntaxKind::from_keyword(ident.text.as_str()).unwrap_or(IDENT) + let kind = match ident.text.as_ref() { + "true" => T![true], + "false" => T![false], + i if i.starts_with('\'') => LIFETIME, + _ => SyntaxKind::from_keyword(ident.text.as_str()).unwrap_or(IDENT), }; TtToken { kind, is_joint_to_next: false, text: ident.text.clone() } diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs index 70899bc5d93f..2b4390eb26ed 100644 --- a/crates/ra_mbe/src/syntax_bridge.rs +++ b/crates/ra_mbe/src/syntax_bridge.rs @@ -376,7 +376,7 @@ macro_rules! make_leaf { }; } let leaf: tt::Leaf = match k { - T![true] | T![false] => make_leaf!(Literal), + T![true] | T![false] => make_leaf!(Ident), IDENT => make_leaf!(Ident), k if k.is_keyword() => make_leaf!(Ident), k if k.is_literal() => make_leaf!(Literal), diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs index f2a726538c25..100ed41f234a 100644 --- a/crates/ra_mbe/src/tests.rs +++ b/crates/ra_mbe/src/tests.rs @@ -1015,6 +1015,36 @@ macro_rules! foo { .assert_expand_items(r#"foo!(u8 0);"#, r#"const VALUE : u8 = 0 ;"#); } +#[test] +fn test_boolean_is_ident() { + parse_macro( + r#" + macro_rules! foo { + ($lit0:literal, $lit1:literal) => { const VALUE: (bool,bool) = ($lit0,$lit1); }; + } +"#, + ) + .assert_expand( + r#"foo!(true,false);"#, + r#" +SUBTREE $ + IDENT const 14 + IDENT VALUE 15 + PUNCH : [alone] 16 + SUBTREE () 17 + IDENT bool 18 + PUNCH , [alone] 19 + IDENT bool 20 + PUNCH = [alone] 21 + SUBTREE () 22 + IDENT true 29 + PUNCH , [joint] 25 + IDENT false 31 + PUNCH ; [alone] 28 +"#, + ); +} + #[test] fn test_vis() { parse_macro( diff --git a/crates/ra_mbe/src/tt_iter.rs b/crates/ra_mbe/src/tt_iter.rs index 100184e66af6..46c4207185f6 100644 --- a/crates/ra_mbe/src/tt_iter.rs +++ b/crates/ra_mbe/src/tt_iter.rs @@ -40,9 +40,11 @@ pub(crate) fn expect_ident(&mut self) -> Result<&'a tt::Ident, ()> { } } - pub(crate) fn expect_literal(&mut self) -> Result<&'a tt::Literal, ()> { - match self.expect_leaf()? { - tt::Leaf::Literal(it) => Ok(it), + pub(crate) fn expect_literal(&mut self) -> Result<&'a tt::Leaf, ()> { + let it = self.expect_leaf()?; + match it { + tt::Leaf::Literal(_) => Ok(it), + tt::Leaf::Ident(ident) if ident.text == "true" || ident.text == "false" => Ok(it), _ => Err(()), } } From ca61356b01c8f0919443b3ccd5e543e06694466a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 18 Apr 2020 20:59:22 +0200 Subject: [PATCH 08/29] Add semantic tag for unresolved references This is a quick way to implement unresolved reference diagnostics. For example, adding to VS Code config "editor.tokenColorCustomizationsExperimental": { "unresolvedReference": "#FF0000" }, will highlight all unresolved refs in red. --- crates/ra_ide/src/snapshots/highlighting.html | 6 ++-- .../src/snapshots/rainbow_highlighting.html | 6 ++-- crates/ra_ide/src/syntax_highlighting.rs | 29 ++++++++++--------- crates/ra_ide/src/syntax_highlighting/tags.rs | 2 ++ crates/rust-analyzer/src/conv.rs | 5 +++- crates/rust-analyzer/src/semantic_tokens.rs | 3 ++ editors/code/package.json | 4 +++ 7 files changed, 34 insertions(+), 21 deletions(-) diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html index 214dcbb620c3..ccb1fc7516c9 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/src/snapshots/highlighting.html @@ -50,12 +50,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd fn main() { println!("Hello, {}!", 92); - let mut vec = Vec::new(); + let mut vec = Vec::new(); if true { let x = 92; - vec.push(Foo { x, y: 1 }); + vec.push(Foo { x, y: 1 }); } - unsafe { vec.set_len(0); } + unsafe { vec.set_len(0); } let mut x = 42; let y = &mut x; diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html index dddbfc0dd69a..3df82c45fbe9 100644 --- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html +++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html @@ -28,11 +28,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
fn main() {
     let hello = "hello";
-    let x = hello.to_string();
-    let y = hello.to_string();
+    let x = hello.to_string();
+    let y = hello.to_string();
 
     let x = "other color please!";
-    let y = x.to_string();
+    let y = x.to_string();
 }
 
 fn bar() {
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 7b15b82bdb6d..93d502875612 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -239,20 +239,21 @@ fn highlight_element(
         NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => return None,
         NAME_REF => {
             let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
-            let name_kind = classify_name_ref(sema, &name_ref)?;
-
-            match name_kind {
-                NameRefClass::Definition(def) => {
-                    if let Definition::Local(local) = &def {
-                        if let Some(name) = local.name(db) {
-                            let shadow_count =
-                                bindings_shadow_count.entry(name.clone()).or_default();
-                            binding_hash = Some(calc_binding_hash(&name, *shadow_count))
-                        }
-                    };
-                    highlight_name(db, def)
-                }
-                NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(),
+            match classify_name_ref(sema, &name_ref) {
+                Some(name_kind) => match name_kind {
+                    NameRefClass::Definition(def) => {
+                        if let Definition::Local(local) = &def {
+                            if let Some(name) = local.name(db) {
+                                let shadow_count =
+                                    bindings_shadow_count.entry(name.clone()).or_default();
+                                binding_hash = Some(calc_binding_hash(&name, *shadow_count))
+                            }
+                        };
+                        highlight_name(db, def)
+                    }
+                    NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(),
+                },
+                None => HighlightTag::UnresolvedReference.into(),
             }
         }
 
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index e8b138e1a7a6..f2c42165454e 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -38,6 +38,7 @@ pub enum HighlightTag {
     TypeParam,
     Union,
     Local,
+    UnresolvedReference,
 }
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -79,6 +80,7 @@ fn as_str(self) -> &'static str {
             HighlightTag::TypeParam => "type_param",
             HighlightTag::Union => "union",
             HighlightTag::Local => "variable",
+            HighlightTag::UnresolvedReference => "unresolved_reference",
         }
     }
 }
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs
index b2b1cb625885..8d2360cc8dbe 100644
--- a/crates/rust-analyzer/src/conv.rs
+++ b/crates/rust-analyzer/src/conv.rs
@@ -24,7 +24,9 @@
     world::WorldSnapshot,
     Result,
 };
-use semantic_tokens::{ATTRIBUTE, BUILTIN_TYPE, ENUM_MEMBER, LIFETIME, TYPE_ALIAS, UNION};
+use semantic_tokens::{
+    ATTRIBUTE, BUILTIN_TYPE, ENUM_MEMBER, LIFETIME, TYPE_ALIAS, UNION, UNRESOLVED_REFERENCE,
+};
 
 pub trait Conv {
     type Output;
@@ -373,6 +375,7 @@ fn conv(self) -> Self::Output {
             HighlightTag::Comment => SemanticTokenType::COMMENT,
             HighlightTag::Attribute => ATTRIBUTE,
             HighlightTag::Keyword => SemanticTokenType::KEYWORD,
+            HighlightTag::UnresolvedReference => UNRESOLVED_REFERENCE,
         };
 
         for modifier in self.modifiers.iter() {
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 865fa3b1c56b..10fe696f6f10 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -10,6 +10,8 @@
 pub(crate) const LIFETIME: SemanticTokenType = SemanticTokenType::new("lifetime");
 pub(crate) const TYPE_ALIAS: SemanticTokenType = SemanticTokenType::new("typeAlias");
 pub(crate) const UNION: SemanticTokenType = SemanticTokenType::new("union");
+pub(crate) const UNRESOLVED_REFERENCE: SemanticTokenType =
+    SemanticTokenType::new("unresolvedReference");
 
 pub(crate) const CONSTANT: SemanticTokenModifier = SemanticTokenModifier::new("constant");
 pub(crate) const CONTROL_FLOW: SemanticTokenModifier = SemanticTokenModifier::new("controlFlow");
@@ -43,6 +45,7 @@
     LIFETIME,
     TYPE_ALIAS,
     UNION,
+    UNRESOLVED_REFERENCE,
 ];
 
 pub(crate) const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
diff --git a/editors/code/package.json b/editors/code/package.json
index 5ce59e54a969..79410ad1094d 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -517,6 +517,10 @@
                 "id": "union",
                 "description": "Style for C-style untagged unions",
                 "superType": "type"
+            },
+            {
+                "id": "unresolvedReference",
+                "description": "Style for names which can not be resolved due to compilation errors"
             }
         ],
         "semanticTokenModifiers": [

From fa2ea8f494d8434da705dc0e0f047f3bd7503af9 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Sat, 18 Apr 2020 22:05:06 +0200
Subject: [PATCH 09/29] Fix goto definition for record patterns

---
 crates/ra_hir/src/semantics.rs       |  4 ++++
 crates/ra_hir/src/source_analyzer.rs | 11 +++++++++++
 crates/ra_hir_ty/src/infer.rs        |  4 ++++
 crates/ra_hir_ty/src/infer/pat.rs    |  6 ++++++
 crates/ra_ide/src/goto_definition.rs | 28 ++++++++++++++++++++++------
 crates/ra_ide_db/src/defs.rs         |  9 +++++++++
 crates/ra_ide_db/src/marks.rs        |  1 +
 7 files changed, 57 insertions(+), 6 deletions(-)

diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 0b477f0e9ea7..5d6edc45c8ce 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -195,6 +195,10 @@ pub fn resolve_record_field(
         self.analyze(field.syntax()).resolve_record_field(self.db, field)
     }
 
+    pub fn resolve_record_field_pat(&self, field: &ast::RecordFieldPat) -> Option {
+        self.analyze(field.syntax()).resolve_record_field_pat(self.db, field)
+    }
+
     pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option {
         let sa = self.analyze(macro_call.syntax());
         let macro_call = self.find_file(macro_call.syntax().clone()).with_value(macro_call);
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index 23af400b8bc6..0ed6d0958065 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -95,6 +95,7 @@ fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option {
     }
 
     fn pat_id(&self, pat: &ast::Pat) -> Option {
+        // FIXME: macros, see `expr_id`
         let src = InFile { file_id: self.file_id, value: pat };
         self.body_source_map.as_ref()?.node_pat(src)
     }
@@ -167,6 +168,16 @@ pub(crate) fn resolve_record_field(
         Some((struct_field.into(), local))
     }
 
+    pub(crate) fn resolve_record_field_pat(
+        &self,
+        _db: &dyn HirDatabase,
+        field: &ast::RecordFieldPat,
+    ) -> Option {
+        let pat_id = self.pat_id(&field.pat()?)?;
+        let struct_field = self.infer.as_ref()?.record_field_pat_resolution(pat_id)?;
+        Some(struct_field.into())
+    }
+
     pub(crate) fn resolve_macro_call(
         &self,
         db: &dyn HirDatabase,
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index b6d9b3438e7d..dfb6a435fff1 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -127,6 +127,7 @@ pub struct InferenceResult {
     field_resolutions: FxHashMap,
     /// For each field in record literal, records the field it resolves to.
     record_field_resolutions: FxHashMap,
+    record_field_pat_resolutions: FxHashMap,
     /// For each struct literal, records the variant it resolves to.
     variant_resolutions: FxHashMap,
     /// For each associated item record what it resolves to
@@ -147,6 +148,9 @@ pub fn field_resolution(&self, expr: ExprId) -> Option {
     pub fn record_field_resolution(&self, expr: ExprId) -> Option {
         self.record_field_resolutions.get(&expr).copied()
     }
+    pub fn record_field_pat_resolution(&self, pat: PatId) -> Option {
+        self.record_field_pat_resolutions.get(&pat).copied()
+    }
     pub fn variant_resolution_for_expr(&self, id: ExprId) -> Option {
         self.variant_resolutions.get(&id.into()).copied()
     }
diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs
index 8ec4d4ace084..7c2ad4384165 100644
--- a/crates/ra_hir_ty/src/infer/pat.rs
+++ b/crates/ra_hir_ty/src/infer/pat.rs
@@ -7,6 +7,7 @@
     expr::{BindingAnnotation, Pat, PatId, RecordFieldPat},
     path::Path,
     type_ref::Mutability,
+    StructFieldId,
 };
 use hir_expand::name::Name;
 use test_utils::tested_by;
@@ -67,6 +68,11 @@ fn infer_record_pat(
         let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
         for subpat in subpats {
             let matching_field = var_data.as_ref().and_then(|it| it.field(&subpat.name));
+            if let Some(local_id) = matching_field {
+                let field_def = StructFieldId { parent: def.unwrap(), local_id };
+                self.result.record_field_pat_resolutions.insert(subpat.pat, field_def);
+            }
+
             let expected_ty =
                 matching_field.map_or(Ty::Unknown, |field| field_tys[field].clone().subst(&substs));
             let expected_ty = self.normalize_associated_types_in(expected_ty);
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index 8aed94d162ab..9998ca5a3d35 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -62,10 +62,9 @@ pub(crate) enum ReferenceResult {
 
 impl ReferenceResult {
     fn to_vec(self) -> Vec {
-        use self::ReferenceResult::*;
         match self {
-            Exact(target) => vec![target],
-            Approximate(vec) => vec,
+            ReferenceResult::Exact(target) => vec![target],
+            ReferenceResult::Approximate(vec) => vec,
         }
     }
 }
@@ -74,8 +73,6 @@ pub(crate) fn reference_definition(
     sema: &Semantics,
     name_ref: &ast::NameRef,
 ) -> ReferenceResult {
-    use self::ReferenceResult::*;
-
     let name_kind = classify_name_ref(sema, name_ref);
     if let Some(def) = name_kind {
         let def = def.definition();
@@ -91,7 +88,7 @@ pub(crate) fn reference_definition(
         .into_iter()
         .map(|s| s.to_nav(sema.db))
         .collect();
-    Approximate(navs)
+    ReferenceResult::Approximate(navs)
 }
 
 #[cfg(test)]
@@ -398,6 +395,25 @@ fn bar() -> Foo {
         );
     }
 
+    #[test]
+    fn goto_def_for_record_pat_fields() {
+        covers!(ra_ide_db::goto_def_for_record_field_pats);
+        check_goto(
+            r"
+            //- /lib.rs
+            struct Foo {
+                spam: u32,
+            }
+
+            fn bar(foo: Foo) -> Foo {
+                let Foo { spam<|>: _, } = foo
+            }
+            ",
+            "spam RECORD_FIELD_DEF FileId(1) [17; 26) [17; 21)",
+            "spam: u32|spam",
+        );
+    }
+
     #[test]
     fn goto_def_for_record_fields_macros() {
         check_goto(
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs
index 49a8c74fba7b..785613b82c10 100644
--- a/crates/ra_ide_db/src/defs.rs
+++ b/crates/ra_ide_db/src/defs.rs
@@ -180,6 +180,7 @@ fn classify_name_inner(sema: &Semantics, name: &ast::Name) -> Opti
     }
 }
 
+#[derive(Debug)]
 pub enum NameRefClass {
     Definition(Definition),
     FieldShorthand { local: Local, field: Definition },
@@ -229,6 +230,14 @@ pub fn classify_name_ref(
         }
     }
 
+    if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent.clone()) {
+        tested_by!(goto_def_for_record_field_pats; force);
+        if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) {
+            let field = Definition::StructField(field);
+            return Some(NameRefClass::Definition(field));
+        }
+    }
+
     if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
         tested_by!(goto_def_for_macros; force);
         if let Some(macro_def) = sema.resolve_macro_call(¯o_call) {
diff --git a/crates/ra_ide_db/src/marks.rs b/crates/ra_ide_db/src/marks.rs
index 4f0a22af0316..03b4be21c974 100644
--- a/crates/ra_ide_db/src/marks.rs
+++ b/crates/ra_ide_db/src/marks.rs
@@ -6,5 +6,6 @@
     goto_def_for_fields
     goto_def_for_record_fields
     goto_def_for_field_init_shorthand
+    goto_def_for_record_field_pats
     search_filters_by_range
 ];

From b79fd82559642f4fe504c0c382b86d57c666be1d Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Sat, 18 Apr 2020 22:16:04 +0200
Subject: [PATCH 10/29] Correctly infer types in guard expressions

The root cause was that we forgot to add bindings from the arm to the
guard expression

closes #3980
---
 crates/ra_hir_def/src/body/scope.rs    |  4 ++++
 crates/ra_hir_ty/src/tests/patterns.rs | 26 ++++++++++++++++++++++++++
 2 files changed, 30 insertions(+)

diff --git a/crates/ra_hir_def/src/body/scope.rs b/crates/ra_hir_def/src/body/scope.rs
index 4d489f69204e..fe4137176f14 100644
--- a/crates/ra_hir_def/src/body/scope.rs
+++ b/crates/ra_hir_def/src/body/scope.rs
@@ -157,6 +157,10 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
             for arm in arms {
                 let scope = scopes.new_scope(scope);
                 scopes.add_bindings(body, scope, arm.pat);
+                if let Some(guard) = arm.guard {
+                    scopes.set_scope(guard, scope);
+                    compute_expr_scopes(guard, body, scopes, scope);
+                }
                 scopes.set_scope(arm.expr, scope);
                 compute_expr_scopes(arm.expr, body, scopes, scope);
             }
diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs
index 07cbc521a3f2..6ea51d5d314e 100644
--- a/crates/ra_hir_ty/src/tests/patterns.rs
+++ b/crates/ra_hir_ty/src/tests/patterns.rs
@@ -455,3 +455,29 @@ fn test() {
     "###
     );
 }
+
+#[test]
+fn infer_guard() {
+    assert_snapshot!(
+        infer(r#"
+struct S;
+impl S { fn foo(&self) -> bool { false } }
+
+fn main() {
+    match S {
+        s if s.foo() => (),
+    }
+}
+    "#), @"
+        [28; 32) 'self': &S
+        [42; 51) '{ false }': bool
+        [44; 49) 'false': bool
+        [65; 116) '{     ...   } }': ()
+        [71; 114) 'match ...     }': ()
+        [77; 78) 'S': S
+        [89; 90) 's': S
+        [94; 95) 's': S
+        [94; 101) 's.foo()': bool
+        [105; 107) '()': ()
+    ")
+}

From 5c760a96b6b241b2a7aef5e67baea708a8c97fcc Mon Sep 17 00:00:00 2001
From: Jeremy Kolb 
Date: Sun, 19 Apr 2020 13:17:14 -0400
Subject: [PATCH 11/29] Update regex

---
 Cargo.lock | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 3826ae1c6f15..6404d242963f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1288,9 +1288,9 @@ checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
 
 [[package]]
 name = "regex"
-version = "1.3.6"
+version = "1.3.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3"
+checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692"
 dependencies = [
  "aho-corasick",
  "memchr",

From d7f3d858add197f91969c69b1d4a14dbcb801f03 Mon Sep 17 00:00:00 2001
From: Jeremy Kolb 
Date: Sun, 19 Apr 2020 15:15:49 -0400
Subject: [PATCH 12/29] Some clippy fixes

---
 crates/ra_assists/src/handlers/add_from_impl_for_enum.rs | 2 +-
 crates/ra_assists/src/handlers/introduce_variable.rs     | 2 +-
 crates/ra_assists/src/handlers/merge_imports.rs          | 2 +-
 crates/ra_db/src/fixture.rs                              | 2 +-
 crates/ra_db/src/input.rs                                | 2 +-
 crates/ra_hir_def/src/body/lower.rs                      | 8 +++-----
 crates/ra_hir_expand/src/ast_id_map.rs                   | 2 +-
 crates/ra_hir_expand/src/builtin_macro.rs                | 8 ++++----
 crates/ra_ide/src/extend_selection.rs                    | 2 +-
 crates/ra_project_model/src/cargo_workspace.rs           | 3 +--
 crates/rust-analyzer/src/world.rs                        | 2 +-
 11 files changed, 16 insertions(+), 19 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 864373aa5d58..0621487e8f7e 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
@@ -98,7 +98,7 @@ fn already_has_from_impl(
     };
     let var_ty = hir_enum_var.fields(sema.db)[0].signature_ty(sema.db);
 
-    e_ty.impls_trait(sema.db, from_trait, &[var_ty.clone()])
+    e_ty.impls_trait(sema.db, from_trait, &[var_ty])
 }
 
 #[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs
index 8d0f7e922a1d..8c09e6bcd063 100644
--- a/crates/ra_assists/src/handlers/introduce_variable.rs
+++ b/crates/ra_assists/src/handlers/introduce_variable.rs
@@ -124,7 +124,7 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> {
             }
         }
 
-        if ast::Stmt::cast(node.clone().into()).is_some() {
+        if ast::Stmt::cast(node.clone()).is_some() {
             return Some((node, false));
         }
 
diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs
index ef0ce058617a..4be1238f146d 100644
--- a/crates/ra_assists/src/handlers/merge_imports.rs
+++ b/crates/ra_assists/src/handlers/merge_imports.rs
@@ -30,7 +30,7 @@ pub(crate) fn merge_imports(ctx: AssistCtx) -> Option {
             .filter_map(|dir| neighbor(&use_item, dir))
             .filter_map(|it| Some((it.clone(), it.use_tree()?)))
             .find_map(|(use_item, use_tree)| {
-                Some((try_merge_trees(&tree, &use_tree)?, use_item.clone()))
+                Some((try_merge_trees(&tree, &use_tree)?, use_item))
             })?;
 
         rewriter.replace_ast(&tree, &merged);
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs
index 7777ce81e78f..8248684eeac1 100644
--- a/crates/ra_db/src/fixture.rs
+++ b/crates/ra_db/src/fixture.rs
@@ -235,7 +235,7 @@ fn parse_meta(meta: &str) -> ParsedMeta {
             "env" => {
                 for key in value.split(',') {
                     if let Some((k, v)) = split1(key, '=') {
-                        env.set(k.into(), v.into());
+                        env.set(k, v.into());
                     }
                 }
             }
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index 5ddce98c650e..ab14e2d5e64d 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -327,7 +327,7 @@ pub fn extern_path(&self, path: impl AsRef) -> Option<(ExternSourceId, Rel
         self.extern_paths.iter().find_map(|(root_path, id)| {
             if let Ok(rel_path) = path.strip_prefix(root_path) {
                 let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
-                Some((id.clone(), rel_path))
+                Some((*id, rel_path))
             } else {
                 None
             }
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index 82a52804d50d..0caedd8d852e 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -473,16 +473,14 @@ fn collect_block(&mut self, expr: ast::BlockExpr) -> ExprId {
         self.collect_block_items(&block);
         let statements = block
             .statements()
-            .filter_map(|s| match s {
+            .map(|s| match s {
                 ast::Stmt::LetStmt(stmt) => {
                     let pat = self.collect_pat_opt(stmt.pat());
                     let type_ref = stmt.ascribed_type().map(TypeRef::from_ast);
                     let initializer = stmt.initializer().map(|e| self.collect_expr(e));
-                    Some(Statement::Let { pat, type_ref, initializer })
-                }
-                ast::Stmt::ExprStmt(stmt) => {
-                    Some(Statement::Expr(self.collect_expr_opt(stmt.expr())))
+                    Statement::Let { pat, type_ref, initializer }
                 }
+                ast::Stmt::ExprStmt(stmt) => Statement::Expr(self.collect_expr_opt(stmt.expr())),
             })
             .collect();
         let tail = block.expr().map(|e| self.collect_expr(e));
diff --git a/crates/ra_hir_expand/src/ast_id_map.rs b/crates/ra_hir_expand/src/ast_id_map.rs
index a3ca302c2efa..d19569245e0b 100644
--- a/crates/ra_hir_expand/src/ast_id_map.rs
+++ b/crates/ra_hir_expand/src/ast_id_map.rs
@@ -66,7 +66,7 @@ pub(crate) fn from_source(node: &SyntaxNode) -> AstIdMap {
         // change parent's id. This means that, say, adding a new function to a
         // trait does not change ids of top-level items, which helps caching.
         bfs(node, |it| {
-            if let Some(module_item) = ast::ModuleItem::cast(it.clone()) {
+            if let Some(module_item) = ast::ModuleItem::cast(it) {
                 res.alloc(module_item.syntax());
             }
         });
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index f9d3787f6373..3da137f2e65f 100644
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -301,7 +301,7 @@ fn relative_file(db: &dyn AstDatabase, call_id: MacroCallId, path: &str) -> Opti
     }
 
     // Extern paths ?
-    let krate = db.relevant_crates(call_site).get(0)?.clone();
+    let krate = *db.relevant_crates(call_site).get(0)?;
     let (extern_source_id, relative_file) =
         db.crate_graph()[krate].extern_source.extern_path(path)?;
 
@@ -329,7 +329,7 @@ fn include_expand(
 
     // FIXME:
     // Handle include as expression
-    let res = parse_to_token_tree(&db.file_text(file_id.into()))
+    let res = parse_to_token_tree(&db.file_text(file_id))
         .ok_or_else(|| mbe::ExpandError::ConversionError)?
         .0;
 
@@ -340,7 +340,7 @@ fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Optio
     let call_id: MacroCallId = arg_id.into();
     let original_file = call_id.as_file().original_file(db);
 
-    let krate = db.relevant_crates(original_file).get(0)?.clone();
+    let krate = *db.relevant_crates(original_file).get(0)?;
     db.crate_graph()[krate].env.get(key)
 }
 
@@ -447,7 +447,7 @@ fn expand_builtin_macro(ra_fixture: &str) -> String {
                     file_id: file_id.into(),
                 };
 
-                let id: MacroCallId = db.intern_eager_expansion(eager.into()).into();
+                let id: MacroCallId = db.intern_eager_expansion(eager).into();
                 id.as_file()
             }
         };
diff --git a/crates/ra_ide/src/extend_selection.rs b/crates/ra_ide/src/extend_selection.rs
index f5a06335128a..753d2ef6a0a0 100644
--- a/crates/ra_ide/src/extend_selection.rs
+++ b/crates/ra_ide/src/extend_selection.rs
@@ -96,7 +96,7 @@ fn try_extend_selection(
         return Some(node.text_range());
     }
 
-    let node = shallowest_node(&node.into());
+    let node = shallowest_node(&node);
 
     if node.parent().map(|n| list_kinds.contains(&n.kind())) == Some(true) {
         if let Some(range) = extend_list_item(&node) {
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index b50cda06fdce..84008b2e344d 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -303,8 +303,7 @@ pub fn load_extern_resources(
                     if message.target.kind.contains(&"proc-macro".to_string()) {
                         let package_id = message.package_id;
                         // Skip rmeta file
-                        if let Some(filename) =
-                            message.filenames.iter().filter(|name| is_dylib(name)).next()
+                        if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name))
                         {
                             res.proc_dylib_paths.insert(package_id, filename.clone());
                         }
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index f2ad453fafe4..369578fb6cf4 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -184,7 +184,7 @@ pub fn new(
         let mut analysis_host = AnalysisHost::new(lru_capacity);
         analysis_host.apply_change(change);
         WorldState {
-            config: config,
+            config,
             roots: folder_roots,
             workspaces: Arc::new(workspaces),
             analysis_host,

From 8a04372fec5f26a0650395a1e420fea062b3a7ab Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Mon, 20 Apr 2020 16:34:01 +0200
Subject: [PATCH 13/29] Fix panic in split_imports assist

The fix is admittedly quit literally just papering over.

Long-term, I see two more principled approaches:

* we switch to a fully tree-based impl, without parse . to_string
  step; with this approach, there shouldn't be any panics. The results
  might be nonsensical, but so was the original input.

* we preserve the invariant that re-parsing constructed node is an
  identity, and make all the `make_xxx` method return an `Option`.

closes #4044
---
 crates/ra_assists/src/handlers/split_import.rs | 7 ++++++-
 crates/ra_syntax/src/algo.rs                   | 8 ++++++--
 crates/ra_syntax/src/ast/edit.rs               | 6 +++++-
 3 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/crates/ra_assists/src/handlers/split_import.rs b/crates/ra_assists/src/handlers/split_import.rs
index d9244f22d697..f25826796317 100644
--- a/crates/ra_assists/src/handlers/split_import.rs
+++ b/crates/ra_assists/src/handlers/split_import.rs
@@ -37,7 +37,7 @@ pub(crate) fn split_import(ctx: AssistCtx) -> Option {
 
 #[cfg(test)]
 mod tests {
-    use crate::helpers::{check_assist, check_assist_target};
+    use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target};
 
     use super::*;
 
@@ -63,4 +63,9 @@ fn split_import_works_with_trees() {
     fn split_import_target() {
         check_assist_target(split_import, "use crate::<|>db::{RootDatabase, FileSymbol}", "::");
     }
+
+    #[test]
+    fn issue4044() {
+        check_assist_not_applicable(split_import, "use crate::<|>:::self;")
+    }
 }
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs
index ea41bf85d166..06df8495c968 100644
--- a/crates/ra_syntax/src/algo.rs
+++ b/crates/ra_syntax/src/algo.rs
@@ -10,8 +10,8 @@
 use rustc_hash::FxHashMap;
 
 use crate::{
-    AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken,
-    TextRange, TextUnit,
+    AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxNodePtr,
+    SyntaxToken, TextRange, TextUnit,
 };
 
 /// Returns ancestors of the node at the offset, sorted by length. This should
@@ -90,6 +90,10 @@ pub fn neighbor(me: &T, direction: Direction) -> Option {
     me.syntax().siblings(direction).skip(1).find_map(T::cast)
 }
 
+pub fn has_errors(node: &SyntaxNode) -> bool {
+    node.children().any(|it| it.kind() == SyntaxKind::ERROR)
+}
+
 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
 pub enum InsertPosition {
     First,
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs
index 9e5411ee58a6..26e4576ffe99 100644
--- a/crates/ra_syntax/src/ast/edit.rs
+++ b/crates/ra_syntax/src/ast/edit.rs
@@ -307,7 +307,11 @@ pub fn split_prefix(&self, prefix: &ast::Path) -> ast::UseTree {
 
         fn split_path_prefix(prefix: &ast::Path) -> Option {
             let parent = prefix.parent_path()?;
-            let mut res = make::path_unqualified(parent.segment()?);
+            let segment = parent.segment()?;
+            if algo::has_errors(segment.syntax()) {
+                return None;
+            }
+            let mut res = make::path_unqualified(segment);
             for p in iter::successors(parent.parent_path(), |it| it.parent_path()) {
                 res = make::path_qualified(res, p.segment()?);
             }

From 0be68a482581861f4218e0a759e2da71ee19fce6 Mon Sep 17 00:00:00 2001
From: Florian Diebold 
Date: Sat, 18 Apr 2020 13:36:35 +0200
Subject: [PATCH 14/29] Update Chalk, and cache Chalk env elaboration through a
 query

This should fix some of the worst performance problems.
---
 Cargo.lock                               |  12 +-
 crates/ra_hir_ty/Cargo.toml              |   6 +-
 crates/ra_hir_ty/src/db.rs               |   7 ++
 crates/ra_hir_ty/src/traits.rs           |   2 +-
 crates/ra_hir_ty/src/traits/chalk.rs     | 145 +++++++++++++++++++----
 crates/ra_hir_ty/src/traits/chalk/tls.rs |  35 +++++-
 6 files changed, 171 insertions(+), 36 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 6404d242963f..37455bc57ba4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -114,7 +114,7 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
 [[package]]
 name = "chalk-derive"
 version = "0.1.0"
-source = "git+https://github.com/rust-lang/chalk.git?rev=28cef6ff403d403e6ad2f3d27d944e9ffac1bce8#28cef6ff403d403e6ad2f3d27d944e9ffac1bce8"
+source = "git+https://github.com/rust-lang/chalk.git?rev=2c072cc830d04af5f10b390e6643327f85108282#2c072cc830d04af5f10b390e6643327f85108282"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -125,7 +125,7 @@ dependencies = [
 [[package]]
 name = "chalk-engine"
 version = "0.9.0"
-source = "git+https://github.com/rust-lang/chalk.git?rev=28cef6ff403d403e6ad2f3d27d944e9ffac1bce8#28cef6ff403d403e6ad2f3d27d944e9ffac1bce8"
+source = "git+https://github.com/rust-lang/chalk.git?rev=2c072cc830d04af5f10b390e6643327f85108282#2c072cc830d04af5f10b390e6643327f85108282"
 dependencies = [
  "chalk-macros",
  "rustc-hash",
@@ -134,7 +134,7 @@ dependencies = [
 [[package]]
 name = "chalk-ir"
 version = "0.1.0"
-source = "git+https://github.com/rust-lang/chalk.git?rev=28cef6ff403d403e6ad2f3d27d944e9ffac1bce8#28cef6ff403d403e6ad2f3d27d944e9ffac1bce8"
+source = "git+https://github.com/rust-lang/chalk.git?rev=2c072cc830d04af5f10b390e6643327f85108282#2c072cc830d04af5f10b390e6643327f85108282"
 dependencies = [
  "chalk-derive",
  "chalk-engine",
@@ -144,7 +144,7 @@ dependencies = [
 [[package]]
 name = "chalk-macros"
 version = "0.1.1"
-source = "git+https://github.com/rust-lang/chalk.git?rev=28cef6ff403d403e6ad2f3d27d944e9ffac1bce8#28cef6ff403d403e6ad2f3d27d944e9ffac1bce8"
+source = "git+https://github.com/rust-lang/chalk.git?rev=2c072cc830d04af5f10b390e6643327f85108282#2c072cc830d04af5f10b390e6643327f85108282"
 dependencies = [
  "lazy_static",
 ]
@@ -152,7 +152,7 @@ dependencies = [
 [[package]]
 name = "chalk-rust-ir"
 version = "0.1.0"
-source = "git+https://github.com/rust-lang/chalk.git?rev=28cef6ff403d403e6ad2f3d27d944e9ffac1bce8#28cef6ff403d403e6ad2f3d27d944e9ffac1bce8"
+source = "git+https://github.com/rust-lang/chalk.git?rev=2c072cc830d04af5f10b390e6643327f85108282#2c072cc830d04af5f10b390e6643327f85108282"
 dependencies = [
  "chalk-derive",
  "chalk-engine",
@@ -163,7 +163,7 @@ dependencies = [
 [[package]]
 name = "chalk-solve"
 version = "0.1.0"
-source = "git+https://github.com/rust-lang/chalk.git?rev=28cef6ff403d403e6ad2f3d27d944e9ffac1bce8#28cef6ff403d403e6ad2f3d27d944e9ffac1bce8"
+source = "git+https://github.com/rust-lang/chalk.git?rev=2c072cc830d04af5f10b390e6643327f85108282#2c072cc830d04af5f10b390e6643327f85108282"
 dependencies = [
  "chalk-derive",
  "chalk-engine",
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml
index 177bdbcb0eb6..04d3cd6a2795 100644
--- a/crates/ra_hir_ty/Cargo.toml
+++ b/crates/ra_hir_ty/Cargo.toml
@@ -27,9 +27,9 @@ test_utils = { path = "../test_utils" }
 
 scoped-tls = "1"
 
-chalk-solve =   { git = "https://github.com/rust-lang/chalk.git", rev = "28cef6ff403d403e6ad2f3d27d944e9ffac1bce8" }
-chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "28cef6ff403d403e6ad2f3d27d944e9ffac1bce8" }
-chalk-ir =      { git = "https://github.com/rust-lang/chalk.git", rev = "28cef6ff403d403e6ad2f3d27d944e9ffac1bce8" }
+chalk-solve =   { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" }
+chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" }
+chalk-ir =      { git = "https://github.com/rust-lang/chalk.git", rev = "2c072cc830d04af5f10b390e6643327f85108282" }
 
 [dev-dependencies]
 insta = "0.16.0"
diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs
index 33da16b487a9..9e5dfeab3cdd 100644
--- a/crates/ra_hir_ty/src/db.rs
+++ b/crates/ra_hir_ty/src/db.rs
@@ -107,6 +107,13 @@ fn trait_solve(
         krate: CrateId,
         goal: crate::Canonical>,
     ) -> Option;
+
+    #[salsa::invoke(crate::traits::chalk::program_clauses_for_chalk_env_query)]
+    fn program_clauses_for_chalk_env(
+        &self,
+        krate: CrateId,
+        env: chalk_ir::Environment,
+    ) -> chalk_ir::ProgramClauses;
 }
 
 fn infer_wait(db: &impl HirDatabase, def: DefWithBodyId) -> Arc {
diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs
index 05791a84868f..6bc6d474c978 100644
--- a/crates/ra_hir_ty/src/traits.rs
+++ b/crates/ra_hir_ty/src/traits.rs
@@ -225,7 +225,7 @@ fn solution_from_chalk(
                 None => unimplemented!(),
             })
             .collect();
-        let result = Canonical { value, num_vars: subst.binders.len() };
+        let result = Canonical { value, num_vars: subst.binders.len(&Interner) };
         SolutionVariables(result)
     };
     match solution {
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs
index e00a82db2a89..1ccb7c3b4ab8 100644
--- a/crates/ra_hir_ty/src/traits/chalk.rs
+++ b/crates/ra_hir_ty/src/traits/chalk.rs
@@ -4,8 +4,8 @@
 use log::debug;
 
 use chalk_ir::{
-    cast::Cast, fold::shift::Shift, Goal, GoalData, Parameter, PlaceholderIndex, TypeName,
-    UniverseIndex,
+    cast::Cast, fold::shift::Shift, interner::HasInterner, Goal, GoalData, Parameter,
+    PlaceholderIndex, TypeName, UniverseIndex,
 };
 
 use hir_def::{AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId};
@@ -33,8 +33,10 @@ impl chalk_ir::interner::Interner for Interner {
     type InternedGoals = Vec>;
     type InternedSubstitution = Vec>;
     type InternedProgramClause = chalk_ir::ProgramClauseData;
-    type InternedProgramClauses = Vec>;
+    type InternedProgramClauses = Arc<[chalk_ir::ProgramClause]>;
     type InternedQuantifiedWhereClauses = Vec>;
+    type InternedParameterKinds = Vec>;
+    type InternedCanonicalVarKinds = Vec>;
     type Identifier = TypeAliasId;
     type DefId = InternId;
 
@@ -60,6 +62,27 @@ fn debug_alias(
         tls::with_current_program(|prog| Some(prog?.debug_alias(alias, fmt)))
     }
 
+    fn debug_projection_ty(
+        proj: &chalk_ir::ProjectionTy,
+        fmt: &mut fmt::Formatter<'_>,
+    ) -> Option {
+        tls::with_current_program(|prog| Some(prog?.debug_projection_ty(proj, fmt)))
+    }
+
+    fn debug_opaque_ty(
+        opaque_ty: &chalk_ir::OpaqueTy,
+        fmt: &mut fmt::Formatter<'_>,
+    ) -> Option {
+        tls::with_current_program(|prog| Some(prog?.debug_opaque_ty(opaque_ty, fmt)))
+    }
+
+    fn debug_opaque_ty_id(
+        opaque_ty_id: chalk_ir::OpaqueTyId,
+        fmt: &mut fmt::Formatter<'_>,
+    ) -> Option {
+        tls::with_current_program(|prog| Some(prog?.debug_opaque_ty_id(opaque_ty_id, fmt)))
+    }
+
     fn debug_ty(ty: &chalk_ir::Ty, fmt: &mut fmt::Formatter<'_>) -> Option {
         tls::with_current_program(|prog| Some(prog?.debug_ty(ty, fmt)))
     }
@@ -202,15 +225,15 @@ fn program_clause_data<'a>(
     fn intern_program_clauses(
         &self,
         data: impl IntoIterator>,
-    ) -> Vec> {
+    ) -> Arc<[chalk_ir::ProgramClause]> {
         data.into_iter().collect()
     }
 
     fn program_clauses_data<'a>(
         &self,
-        clauses: &'a Vec>,
+        clauses: &'a Arc<[chalk_ir::ProgramClause]>,
     ) -> &'a [chalk_ir::ProgramClause] {
-        clauses
+        &clauses
     }
 
     fn intern_quantified_where_clauses(
@@ -226,6 +249,34 @@ fn quantified_where_clauses_data<'a>(
     ) -> &'a [chalk_ir::QuantifiedWhereClause] {
         clauses
     }
+
+    fn intern_parameter_kinds(
+        &self,
+        data: impl IntoIterator>,
+    ) -> Self::InternedParameterKinds {
+        data.into_iter().collect()
+    }
+
+    fn parameter_kinds_data<'a>(
+        &self,
+        parameter_kinds: &'a Self::InternedParameterKinds,
+    ) -> &'a [chalk_ir::ParameterKind<()>] {
+        ¶meter_kinds
+    }
+
+    fn intern_canonical_var_kinds(
+        &self,
+        data: impl IntoIterator>,
+    ) -> Self::InternedCanonicalVarKinds {
+        data.into_iter().collect()
+    }
+
+    fn canonical_var_kinds_data<'a>(
+        &self,
+        canonical_var_kinds: &'a Self::InternedCanonicalVarKinds,
+    ) -> &'a [chalk_ir::ParameterKind] {
+        &canonical_var_kinds
+    }
 }
 
 impl chalk_ir::interner::HasInterner for Interner {
@@ -268,9 +319,12 @@ fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Ty {
             Ty::Projection(proj_ty) => {
                 let associated_ty_id = proj_ty.associated_ty.to_chalk(db);
                 let substitution = proj_ty.parameters.to_chalk(db);
-                chalk_ir::AliasTy { associated_ty_id, substitution }
-                    .cast(&Interner)
-                    .intern(&Interner)
+                chalk_ir::AliasTy::Projection(chalk_ir::ProjectionTy {
+                    associated_ty_id,
+                    substitution,
+                })
+                .cast(&Interner)
+                .intern(&Interner)
             }
             Ty::Placeholder(id) => {
                 let interned_id = db.intern_type_param_id(id);
@@ -314,16 +368,17 @@ fn from_chalk(db: &dyn HirDatabase, chalk: chalk_ir::Ty) -> Self {
                 );
                 Ty::Placeholder(db.lookup_intern_type_param_id(interned_id))
             }
-            chalk_ir::TyData::Alias(proj) => {
+            chalk_ir::TyData::Alias(chalk_ir::AliasTy::Projection(proj)) => {
                 let associated_ty = from_chalk(db, proj.associated_ty_id);
                 let parameters = from_chalk(db, proj.substitution);
                 Ty::Projection(ProjectionTy { associated_ty, parameters })
             }
+            chalk_ir::TyData::Alias(chalk_ir::AliasTy::Opaque(_)) => unimplemented!(),
             chalk_ir::TyData::Function(_) => unimplemented!(),
             chalk_ir::TyData::BoundVar(idx) => Ty::Bound(idx),
             chalk_ir::TyData::InferenceVar(_iv) => Ty::Unknown,
             chalk_ir::TyData::Dyn(where_clauses) => {
-                assert_eq!(where_clauses.bounds.binders.len(), 1);
+                assert_eq!(where_clauses.bounds.binders.len(&Interner), 1);
                 let predicates = where_clauses
                     .bounds
                     .skip_binders()
@@ -404,6 +459,7 @@ fn from_chalk(db: &dyn HirDatabase, type_name: TypeName) -> TypeCtor {
         match type_name {
             TypeName::Struct(struct_id) => db.lookup_intern_type_ctor(struct_id.into()),
             TypeName::AssociatedType(type_id) => TypeCtor::AssociatedType(from_chalk(db, type_id)),
+            TypeName::OpaqueType(_) => unreachable!(),
             TypeName::Error => {
                 // this should not be reached, since we don't represent TypeName::Error with TypeCtor
                 unreachable!()
@@ -460,7 +516,8 @@ fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::QuantifiedWhereClause {
                 let ty = projection_pred.ty.to_chalk(db).shifted_in(&Interner);
-                let alias = projection_pred.projection_ty.to_chalk(db).shifted_in(&Interner);
+                let projection = projection_pred.projection_ty.to_chalk(db).shifted_in(&Interner);
+                let alias = chalk_ir::AliasTy::Projection(projection);
                 make_binders(chalk_ir::WhereClause::AliasEq(chalk_ir::AliasEq { alias, ty }), 0)
             }
             GenericPredicate::Error => panic!("tried passing GenericPredicate::Error to Chalk"),
@@ -481,7 +538,13 @@ fn from_chalk(
                 GenericPredicate::Implemented(from_chalk(db, tr))
             }
             chalk_ir::WhereClause::AliasEq(projection_eq) => {
-                let projection_ty = from_chalk(db, projection_eq.alias);
+                let projection_ty = from_chalk(
+                    db,
+                    match projection_eq.alias {
+                        chalk_ir::AliasTy::Projection(p) => p,
+                        _ => unimplemented!(),
+                    },
+                );
                 let ty = from_chalk(db, projection_eq.ty);
                 GenericPredicate::Projection(super::ProjectionPredicate { projection_ty, ty })
             }
@@ -490,10 +553,10 @@ fn from_chalk(
 }
 
 impl ToChalk for ProjectionTy {
-    type Chalk = chalk_ir::AliasTy;
+    type Chalk = chalk_ir::ProjectionTy;
 
-    fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasTy {
-        chalk_ir::AliasTy {
+    fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::ProjectionTy {
+        chalk_ir::ProjectionTy {
             associated_ty_id: self.associated_ty.to_chalk(db),
             substitution: self.parameters.to_chalk(db),
         }
@@ -501,7 +564,7 @@ fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasTy {
 
     fn from_chalk(
         db: &dyn HirDatabase,
-        projection_ty: chalk_ir::AliasTy,
+        projection_ty: chalk_ir::ProjectionTy,
     ) -> ProjectionTy {
         ProjectionTy {
             associated_ty: from_chalk(db, projection_ty.associated_ty_id),
@@ -514,7 +577,10 @@ impl ToChalk for super::ProjectionPredicate {
     type Chalk = chalk_ir::AliasEq;
 
     fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::AliasEq {
-        chalk_ir::AliasEq { alias: self.projection_ty.to_chalk(db), ty: self.ty.to_chalk(db) }
+        chalk_ir::AliasEq {
+            alias: chalk_ir::AliasTy::Projection(self.projection_ty.to_chalk(db)),
+            ty: self.ty.to_chalk(db),
+        }
     }
 
     fn from_chalk(_db: &dyn HirDatabase, _normalize: chalk_ir::AliasEq) -> Self {
@@ -540,17 +606,24 @@ fn from_chalk(_db: &dyn HirDatabase, _goal: chalk_ir::DomainGoal) -> S
 impl ToChalk for Canonical
 where
     T: ToChalk,
+    T::Chalk: HasInterner,
 {
     type Chalk = chalk_ir::Canonical;
 
     fn to_chalk(self, db: &dyn HirDatabase) -> chalk_ir::Canonical {
         let parameter = chalk_ir::ParameterKind::Ty(chalk_ir::UniverseIndex::ROOT);
         let value = self.value.to_chalk(db);
-        chalk_ir::Canonical { value, binders: vec![parameter; self.num_vars] }
+        chalk_ir::Canonical {
+            value,
+            binders: chalk_ir::CanonicalVarKinds::from(&Interner, vec![parameter; self.num_vars]),
+        }
     }
 
     fn from_chalk(db: &dyn HirDatabase, canonical: chalk_ir::Canonical) -> Canonical {
-        Canonical { num_vars: canonical.binders.len(), value: from_chalk(db, canonical.value) }
+        Canonical {
+            num_vars: canonical.binders.len(&Interner),
+            value: from_chalk(db, canonical.value),
+        }
     }
 }
 
@@ -649,9 +722,15 @@ fn from_chalk(
     }
 }
 
-fn make_binders(value: T, num_vars: usize) -> chalk_ir::Binders {
+fn make_binders(value: T, num_vars: usize) -> chalk_ir::Binders
+where
+    T: HasInterner,
+{
     chalk_ir::Binders::new(
-        std::iter::repeat(chalk_ir::ParameterKind::Ty(())).take(num_vars).collect(),
+        chalk_ir::ParameterKinds::from(
+            &Interner,
+            std::iter::repeat(chalk_ir::ParameterKind::Ty(())).take(num_vars),
+        ),
         value,
     )
 }
@@ -799,6 +878,28 @@ fn well_known_trait_id(
         // FIXME tell Chalk about well-known traits (here and in trait_datum)
         None
     }
+
+    fn program_clauses_for_env(
+        &self,
+        environment: &chalk_ir::Environment,
+    ) -> chalk_ir::ProgramClauses {
+        self.db.program_clauses_for_chalk_env(self.krate, environment.clone())
+    }
+
+    fn opaque_ty_data(
+        &self,
+        _id: chalk_ir::OpaqueTyId,
+    ) -> Arc> {
+        unimplemented!()
+    }
+}
+
+pub(crate) fn program_clauses_for_chalk_env_query(
+    db: &dyn HirDatabase,
+    krate: CrateId,
+    environment: chalk_ir::Environment,
+) -> chalk_ir::ProgramClauses {
+    chalk_solve::program_clauses_for_env(&ChalkContext { db, krate }, &environment)
 }
 
 pub(crate) fn associated_ty_data_query(
diff --git a/crates/ra_hir_ty/src/traits/chalk/tls.rs b/crates/ra_hir_ty/src/traits/chalk/tls.rs
index fa8e4d1ad182..4867cb17ed1e 100644
--- a/crates/ra_hir_ty/src/traits/chalk/tls.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/tls.rs
@@ -121,19 +121,38 @@ pub fn debug_assoc_type_id(
         write!(fmt, "{}::{}", trait_data.name, type_alias_data.name)
     }
 
-    pub fn debug_alias(
+    pub fn debug_opaque_ty_id(
         &self,
-        alias: &AliasTy,
+        opaque_ty_id: chalk_ir::OpaqueTyId,
         fmt: &mut fmt::Formatter<'_>,
     ) -> Result<(), fmt::Error> {
-        let type_alias: TypeAliasId = from_chalk(self.0, alias.associated_ty_id);
+        fmt.debug_struct("OpaqueTyId").field("index", &opaque_ty_id.0).finish()
+    }
+
+    pub fn debug_alias(
+        &self,
+        alias_ty: &AliasTy,
+        fmt: &mut fmt::Formatter<'_>,
+    ) -> Result<(), fmt::Error> {
+        match alias_ty {
+            AliasTy::Projection(projection_ty) => self.debug_projection_ty(projection_ty, fmt),
+            AliasTy::Opaque(opaque_ty) => self.debug_opaque_ty(opaque_ty, fmt),
+        }
+    }
+
+    pub fn debug_projection_ty(
+        &self,
+        projection_ty: &chalk_ir::ProjectionTy,
+        fmt: &mut fmt::Formatter<'_>,
+    ) -> Result<(), fmt::Error> {
+        let type_alias: TypeAliasId = from_chalk(self.0, projection_ty.associated_ty_id);
         let type_alias_data = self.0.type_alias_data(type_alias);
         let trait_ = match type_alias.lookup(self.0.upcast()).container {
             AssocContainerId::TraitId(t) => t,
             _ => panic!("associated type not in trait"),
         };
         let trait_data = self.0.trait_data(trait_);
-        let params = alias.substitution.parameters(&Interner);
+        let params = projection_ty.substitution.parameters(&Interner);
         write!(fmt, "<{:?} as {}", ¶ms[0], trait_data.name,)?;
         if params.len() > 1 {
             write!(
@@ -145,6 +164,14 @@ pub fn debug_alias(
         write!(fmt, ">::{}", type_alias_data.name)
     }
 
+    pub fn debug_opaque_ty(
+        &self,
+        opaque_ty: &chalk_ir::OpaqueTy,
+        fmt: &mut fmt::Formatter<'_>,
+    ) -> Result<(), fmt::Error> {
+        write!(fmt, "{:?}", opaque_ty.opaque_ty_id)
+    }
+
     pub fn debug_ty(
         &self,
         ty: &chalk_ir::Ty,

From d3019164dcbb46f8c369ed4efff79de5a42a95a8 Mon Sep 17 00:00:00 2001
From: veetaha 
Date: Mon, 20 Apr 2020 21:26:10 +0300
Subject: [PATCH 15/29] ra_proc_macro: cleanups here and there

---
 crates/ra_proc_macro/src/lib.rs              |  17 ++-
 crates/ra_proc_macro/src/msg.rs              |  37 +++----
 crates/ra_proc_macro/src/process.rs          |  27 ++---
 crates/ra_proc_macro/src/rpc.rs              |  16 +--
 crates/ra_proc_macro_srv/src/cli.rs          |  58 ++++------
 crates/ra_proc_macro_srv/src/dylib.rs        | 107 ++++++++-----------
 crates/ra_proc_macro_srv/src/lib.rs          |  36 +++----
 crates/ra_proc_macro_srv/src/rustc_server.rs |   2 +-
 crates/ra_proc_macro_srv/src/tests/utils.rs  |   2 +-
 crates/rust-analyzer/src/bin/main.rs         |   4 +-
 crates/rust-analyzer/src/cli/load_cargo.rs   |   2 +-
 crates/rust-analyzer/src/world.rs            |  23 ++--
 12 files changed, 141 insertions(+), 190 deletions(-)

diff --git a/crates/ra_proc_macro/src/lib.rs b/crates/ra_proc_macro/src/lib.rs
index b200fd12633e..004943b9e065 100644
--- a/crates/ra_proc_macro/src/lib.rs
+++ b/crates/ra_proc_macro/src/lib.rs
@@ -2,7 +2,7 @@
 //!
 //! We separate proc-macro expanding logic to an extern program to allow
 //! different implementations (e.g. wasm or dylib loading). And this crate
-//! is used to provide basic infrastructure  for communication between two
+//! is used to provide basic infrastructure for communication between two
 //! processes: Client (RA itself), Server (the external program)
 
 mod rpc;
@@ -13,6 +13,7 @@
 use ra_tt::{SmolStr, Subtree};
 use std::{
     ffi::OsStr,
+    io,
     path::{Path, PathBuf},
     sync::Arc,
 };
@@ -57,14 +58,10 @@ pub struct ProcMacroClient {
 }
 
 impl ProcMacroClient {
-    pub fn extern_process(
-        process_path: &Path,
-        args: I,
-    ) -> Result
-    where
-        I: IntoIterator,
-        S: AsRef,
-    {
+    pub fn extern_process(
+        process_path: PathBuf,
+        args: impl IntoIterator>,
+    ) -> io::Result {
         let (thread, process) = ProcMacroProcessSrv::run(process_path, args)?;
         Ok(ProcMacroClient {
             kind: ProcMacroClientKind::Process { process: Arc::new(process), thread },
@@ -84,7 +81,7 @@ pub fn by_dylib_path(
             ProcMacroClientKind::Process { process, .. } => {
                 let macros = match process.find_proc_macros(dylib_path) {
                     Err(err) => {
-                        eprintln!("Fail to find proc macro. Error: {:#?}", err);
+                        eprintln!("Failed to find proc macros. Error: {:#?}", err);
                         return vec![];
                     }
                     Ok(macros) => macros,
diff --git a/crates/ra_proc_macro/src/msg.rs b/crates/ra_proc_macro/src/msg.rs
index aa95bcc8f7bc..95d9b8804e14 100644
--- a/crates/ra_proc_macro/src/msg.rs
+++ b/crates/ra_proc_macro/src/msg.rs
@@ -1,4 +1,4 @@
-//! Defines messages for cross-process message based on `ndjson` wire protocol
+//! Defines messages for cross-process message passing based on `ndjson` wire protocol
 
 use std::{
     convert::TryFrom,
@@ -31,7 +31,7 @@ impl TryFrom for $ty {
             fn try_from(value: Response) -> Result {
                 match value {
                     Response::$tag(res) => Ok(res),
-                    _ => Err("Fail to convert from response"),
+                    _ => Err(concat!("Failed to convert response to ", stringify!($tag))),
                 }
             }
         }
@@ -53,18 +53,16 @@ pub enum ErrorCode {
     ExpansionError,
 }
 
-pub trait Message: Sized + Serialize + DeserializeOwned {
-    fn read(r: &mut impl BufRead) -> io::Result> {
-        let text = match read_json(r)? {
-            None => return Ok(None),
-            Some(text) => text,
-        };
-        let msg = serde_json::from_str(&text)?;
-        Ok(Some(msg))
+pub trait Message: Serialize + DeserializeOwned {
+    fn read(inp: &mut impl BufRead) -> io::Result> {
+        Ok(match read_json(inp)? {
+            None => None,
+            Some(text) => Some(serde_json::from_str(&text)?),
+        })
     }
-    fn write(self, w: &mut impl Write) -> io::Result<()> {
+    fn write(self, out: &mut impl Write) -> io::Result<()> {
         let text = serde_json::to_string(&self)?;
-        write_json(w, &text)
+        write_json(out, &text)
     }
 }
 
@@ -73,15 +71,12 @@ impl Message for Response {}
 
 fn read_json(inp: &mut impl BufRead) -> io::Result> {
     let mut buf = String::new();
-    if inp.read_line(&mut buf)? == 0 {
-        return Ok(None);
-    }
-    // Remove ending '\n'
-    let buf = &buf[..buf.len() - 1];
-    if buf.is_empty() {
-        return Ok(None);
-    }
-    Ok(Some(buf.to_string()))
+    inp.read_line(&mut buf)?;
+    buf.pop(); // Remove traling '\n'
+    Ok(match buf.len() {
+        0 => None,
+        _ => Some(buf),
+    })
 }
 
 fn write_json(out: &mut impl Write, msg: &str) -> io::Result<()> {
diff --git a/crates/ra_proc_macro/src/process.rs b/crates/ra_proc_macro/src/process.rs
index f851570bca9f..b1ebf46a1bc9 100644
--- a/crates/ra_proc_macro/src/process.rs
+++ b/crates/ra_proc_macro/src/process.rs
@@ -45,24 +45,23 @@ fn drop(&mut self) {
 }
 
 impl Process {
-    fn run(process_path: &Path, args: I) -> Result
-    where
-        I: IntoIterator,
-        S: AsRef,
-    {
-        let child = Command::new(process_path.clone())
+    fn run(
+        process_path: PathBuf,
+        args: impl IntoIterator>,
+    ) -> Result {
+        let child = Command::new(&process_path)
             .args(args)
             .stdin(Stdio::piped())
             .stdout(Stdio::piped())
             .stderr(Stdio::null())
             .spawn()?;
 
-        Ok(Process { path: process_path.into(), child })
+        Ok(Process { path: process_path, child })
     }
 
     fn restart(&mut self) -> Result<(), io::Error> {
         let _ = self.child.kill();
-        self.child = Command::new(self.path.clone())
+        self.child = Command::new(&self.path)
             .stdin(Stdio::piped())
             .stdout(Stdio::piped())
             .stderr(Stdio::null())
@@ -80,14 +79,10 @@ fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> {
 }
 
 impl ProcMacroProcessSrv {
-    pub fn run(
-        process_path: &Path,
-        args: I,
-    ) -> Result<(ProcMacroProcessThread, ProcMacroProcessSrv), io::Error>
-    where
-        I: IntoIterator,
-        S: AsRef,
-    {
+    pub fn run(
+        process_path: PathBuf,
+        args: impl IntoIterator>,
+    ) -> io::Result<(ProcMacroProcessThread, ProcMacroProcessSrv)> {
         let process = Process::run(process_path, args)?;
 
         let (task_tx, task_rx) = bounded(0);
diff --git a/crates/ra_proc_macro/src/rpc.rs b/crates/ra_proc_macro/src/rpc.rs
index 66b3f55db370..e97b9086056b 100644
--- a/crates/ra_proc_macro/src/rpc.rs
+++ b/crates/ra_proc_macro/src/rpc.rs
@@ -1,9 +1,9 @@
-//! Data struture serialization related stuffs for RPC
+//! Data struture serialization related stuff for RPC
 //!
-//! Define all necessary rpc serialization data structure,
-//! which include ra_tt related data and some task messages.
-//! Although adding Serialize and Deserialize trait to ra_tt directly seem to be much easier,
-//! we deliberately duplicate the ra_tt struct with #[serde(with = "XXDef")]
+//! Defines all necessary rpc serialization data structures,
+//! which includes `ra_tt` related data and some task messages.
+//! Although adding `Serialize` and `Deserialize` traits to `ra_tt` directly seems
+//! to be much easier, we deliberately duplicate `ra_tt` structs with `#[serde(with = "XXDef")]`
 //! for separation of code responsibility.
 
 use ra_tt::{
@@ -34,15 +34,15 @@ pub struct ListMacrosResult {
 pub struct ExpansionTask {
     /// Argument of macro call.
     ///
-    /// In custom derive that would be a struct or enum; in attribute-like macro - underlying
+    /// In custom derive this will be a struct or enum; in attribute-like macro - underlying
     /// item; in function-like macro - the macro body.
     #[serde(with = "SubtreeDef")]
     pub macro_body: Subtree,
 
-    /// Names of macros to expand.
+    /// Names of macros to expand. // TODO: are they comma-separated?
     ///
     /// In custom derive those are names of derived traits (`Serialize`, `Getters`, etc.). In
-    /// attribute-like and functiona-like macros - single name of macro itself (`show_streams`).
+    /// attribute-like and function-like macros - single name of macro itself (`show_streams`).
     pub macro_name: String,
 
     /// Possible attributes for the attribute-like macros.
diff --git a/crates/ra_proc_macro_srv/src/cli.rs b/crates/ra_proc_macro_srv/src/cli.rs
index c771f2b38911..5f1f3ba3c7d8 100644
--- a/crates/ra_proc_macro_srv/src/cli.rs
+++ b/crates/ra_proc_macro_srv/src/cli.rs
@@ -2,55 +2,43 @@
 
 use crate::{expand_task, list_macros};
 use ra_proc_macro::msg::{self, Message};
-
 use std::io;
 
-fn read_request() -> Result, io::Error> {
-    let stdin = io::stdin();
-    let mut stdin = stdin.lock();
-    msg::Request::read(&mut stdin)
-}
-
-fn write_response(res: Result) -> Result<(), io::Error> {
-    let msg: msg::Response = match res {
-        Ok(res) => res,
-        Err(err) => msg::Response::Error(msg::ResponseError {
-            code: msg::ErrorCode::ExpansionError,
-            message: err,
-        }),
-    };
-
-    let stdout = io::stdout();
-    let mut stdout = stdout.lock();
-    msg.write(&mut stdout)
-}
-
 pub fn run() {
     loop {
         let req = match read_request() {
             Err(err) => {
-                eprintln!("Read message error on ra_proc_macro_srv: {}", err.to_string());
+                eprintln!("Read message error on ra_proc_macro_srv: {}", err);
                 continue;
             }
             Ok(None) => continue,
             Ok(Some(req)) => req,
         };
 
-        match req {
-            msg::Request::ListMacro(task) => {
-                if let Err(err) =
-                    write_response(list_macros(&task).map(|it| msg::Response::ListMacro(it)))
-                {
-                    eprintln!("Write message error on list macro: {}", err);
-                }
-            }
+        let res = match req {
+            msg::Request::ListMacro(task) => Ok(msg::Response::ListMacro(list_macros(&task))),
             msg::Request::ExpansionMacro(task) => {
-                if let Err(err) =
-                    write_response(expand_task(&task).map(|it| msg::Response::ExpansionMacro(it)))
-                {
-                    eprintln!("Write message error on expansion macro: {}", err);
-                }
+                expand_task(&task).map(msg::Response::ExpansionMacro)
             }
+        };
+
+        let msg = res.unwrap_or_else(|err| {
+            msg::Response::Error(msg::ResponseError {
+                code: msg::ErrorCode::ExpansionError,
+                message: err,
+            })
+        });
+
+        if let Err(err) = write_response(msg) {
+            eprintln!("Write message error: {}", err);
         }
     }
 }
+
+fn read_request() -> io::Result> {
+    msg::Request::read(&mut io::stdin().lock())
+}
+
+fn write_response(msg: msg::Response) -> io::Result<()> {
+    msg.write(&mut io::stdout().lock())
+}
diff --git a/crates/ra_proc_macro_srv/src/dylib.rs b/crates/ra_proc_macro_srv/src/dylib.rs
index 16bd7466e386..f7a86a5327e8 100644
--- a/crates/ra_proc_macro_srv/src/dylib.rs
+++ b/crates/ra_proc_macro_srv/src/dylib.rs
@@ -9,43 +9,37 @@
 use memmap::Mmap;
 use ra_proc_macro::ProcMacroKind;
 
-use std::io::Error as IoError;
-use std::io::ErrorKind as IoErrorKind;
+use std::io;
 
 const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";
 
-fn invalid_data_err(e: impl Into>) -> IoError {
-    IoError::new(IoErrorKind::InvalidData, e)
+fn invalid_data_err(e: impl Into>) -> io::Error {
+    io::Error::new(io::ErrorKind::InvalidData, e)
 }
 
-fn is_derive_registrar_symbol(symbol: &str) -> bool {
+fn is_derive_registrar_symbol(symbol: &&str) -> bool {
     symbol.contains(NEW_REGISTRAR_SYMBOL)
 }
 
-fn find_registrar_symbol(file: &Path) -> Result, IoError> {
+fn find_registrar_symbol(file: &Path) -> io::Result> {
     let file = File::open(file)?;
     let buffer = unsafe { Mmap::map(&file)? };
     let object = Object::parse(&buffer).map_err(invalid_data_err)?;
 
-    match object {
+    let name = match object {
         Object::Elf(elf) => {
             let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?;
-            let name =
-                symbols.iter().find(|s| is_derive_registrar_symbol(s)).map(|s| s.to_string());
-            Ok(name)
-        }
-        Object::PE(pe) => {
-            let name = pe
-                .exports
-                .iter()
-                .flat_map(|s| s.name)
-                .find(|s| is_derive_registrar_symbol(s))
-                .map(|s| s.to_string());
-            Ok(name)
+            symbols.into_iter().find(is_derive_registrar_symbol).map(&str::to_owned)
         }
+        Object::PE(pe) => pe
+            .exports
+            .iter()
+            .flat_map(|s| s.name)
+            .find(is_derive_registrar_symbol)
+            .map(&str::to_owned),
         Object::Mach(Mach::Binary(binary)) => {
             let exports = binary.exports().map_err(invalid_data_err)?;
-            let name = exports
+            exports
                 .iter()
                 .map(|s| {
                     // In macos doc:
@@ -58,12 +52,12 @@ fn find_registrar_symbol(file: &Path) -> Result, IoError> {
                         &s.name
                     }
                 })
-                .find(|s| is_derive_registrar_symbol(s))
-                .map(|s| s.to_string());
-            Ok(name)
+                .find(is_derive_registrar_symbol)
+                .map(&str::to_owned)
         }
-        _ => Ok(None),
-    }
+        _ => return Ok(None),
+    };
+    Ok(name)
 }
 
 /// Loads dynamic library in platform dependent manner.
@@ -93,15 +87,16 @@ fn load_library(file: &Path) -> Result {
 }
 
 struct ProcMacroLibraryLibloading {
-    // Hold the dylib to prevent it for unloadeding
+    // Hold the dylib to prevent it from unloading
     _lib: Library,
     exported_macros: Vec,
 }
 
 impl ProcMacroLibraryLibloading {
-    fn open(file: &Path) -> Result {
-        let symbol_name = find_registrar_symbol(file)?
-            .ok_or(invalid_data_err(format!("Cannot find registrar symbol in file {:?}", file)))?;
+    fn open(file: &Path) -> io::Result {
+        let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| {
+            invalid_data_err(format!("Cannot find registrar symbol in file {:?}", file))
+        })?;
 
         let lib = load_library(file).map_err(invalid_data_err)?;
         let exported_macros = {
@@ -121,18 +116,16 @@ pub struct Expander {
 }
 
 impl Expander {
-    pub fn new>(lib: &P) -> Result {
-        let mut libs = vec![];
-        /* Some libraries for dynamic loading require canonicalized path (even when it is
-        already absolute
-        */
-        let lib =
-            lib.as_ref().canonicalize().expect(&format!("Cannot canonicalize {:?}", lib.as_ref()));
+    pub fn new(lib: &Path) -> Result {
+        // Some libraries for dynamic loading require canonicalized path even when it is
+        // already absolute
+        let lib = lib
+            .canonicalize()
+            .unwrap_or_else(|err| panic!("Cannot canonicalize {:?}: {:?}", lib, err));
 
         let library = ProcMacroLibraryImpl::open(&lib).map_err(|e| e.to_string())?;
-        libs.push(library);
 
-        Ok(Expander { libs })
+        Ok(Expander { libs: vec![library] })
     }
 
     pub fn expand(
@@ -176,7 +169,6 @@ pub fn expand(
                             parsed_attributes,
                             parsed_body,
                         );
-
                         return res.map(|it| it.subtree);
                     }
                     _ => continue,
@@ -187,26 +179,21 @@ pub fn expand(
         Err(bridge::PanicMessage::String("Nothing to expand".to_string()))
     }
 
-    pub fn list_macros(&self) -> Result, bridge::PanicMessage> {
-        let mut result = vec![];
-
-        for lib in &self.libs {
-            for proc_macro in &lib.exported_macros {
-                let res = match proc_macro {
-                    bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
-                        (trait_name.to_string(), ProcMacroKind::CustomDerive)
-                    }
-                    bridge::client::ProcMacro::Bang { name, .. } => {
-                        (name.to_string(), ProcMacroKind::FuncLike)
-                    }
-                    bridge::client::ProcMacro::Attr { name, .. } => {
-                        (name.to_string(), ProcMacroKind::Attr)
-                    }
-                };
-                result.push(res);
-            }
-        }
-
-        Ok(result)
+    pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
+        self.libs
+            .iter()
+            .flat_map(|it| &it.exported_macros)
+            .map(|proc_macro| match proc_macro {
+                bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
+                    (trait_name.to_string(), ProcMacroKind::CustomDerive)
+                }
+                bridge::client::ProcMacro::Bang { name, .. } => {
+                    (name.to_string(), ProcMacroKind::FuncLike)
+                }
+                bridge::client::ProcMacro::Attr { name, .. } => {
+                    (name.to_string(), ProcMacroKind::Attr)
+                }
+            })
+            .collect()
     }
 }
diff --git a/crates/ra_proc_macro_srv/src/lib.rs b/crates/ra_proc_macro_srv/src/lib.rs
index c62b0ed893c0..7faf36834258 100644
--- a/crates/ra_proc_macro_srv/src/lib.rs
+++ b/crates/ra_proc_macro_srv/src/lib.rs
@@ -3,10 +3,10 @@
 //! This library is able to call compiled Rust custom derive dynamic libraries on arbitrary code.
 //! The general idea here is based on https://github.com/fedochet/rust-proc-macro-expander.
 //!
-//! But we change some several design for fitting RA needs:
+//! But we adapt it to better fit RA needs:
 //!
-//! * We use `ra_tt` for proc-macro `TokenStream` server, it is easy to manipute and interact with
-//!   RA then proc-macro2 token stream.
+//! * We use `ra_tt` for proc-macro `TokenStream` server, it is easy to manipulate and interact with
+//!   RA than `proc-macro2` token stream.
 //! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable`
 //!   rustc rather than `unstable`. (Although in gerenal ABI compatibility is still an issue)
 
@@ -21,36 +21,28 @@
 
 use proc_macro::bridge::client::TokenStream;
 use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask};
+use std::path::Path;
 
 pub(crate) fn expand_task(task: &ExpansionTask) -> Result {
-    let expander = dylib::Expander::new(&task.lib)
-        .expect(&format!("Cannot expand with provided libraries: ${:?}", &task.lib));
+    let expander = create_expander(&task.lib);
 
     match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) {
         Ok(expansion) => Ok(ExpansionResult { expansion }),
         Err(msg) => {
-            let reason = format!(
-                "Cannot perform expansion for {}: error {:?}!",
-                &task.macro_name,
-                msg.as_str()
-            );
-            Err(reason)
+            Err(format!("Cannot perform expansion for {}: error {:?}", &task.macro_name, msg))
         }
     }
 }
 
-pub(crate) fn list_macros(task: &ListMacrosTask) -> Result {
-    let expander = dylib::Expander::new(&task.lib)
-        .expect(&format!("Cannot expand with provided libraries: ${:?}", &task.lib));
+pub(crate) fn list_macros(task: &ListMacrosTask) -> ListMacrosResult {
+    let expander = create_expander(&task.lib);
 
-    match expander.list_macros() {
-        Ok(macros) => Ok(ListMacrosResult { macros }),
-        Err(msg) => {
-            let reason =
-                format!("Cannot perform expansion for {:?}: error {:?}!", &task.lib, msg.as_str());
-            Err(reason)
-        }
-    }
+    ListMacrosResult { macros: expander.list_macros() }
+}
+
+fn create_expander(lib: &Path) -> dylib::Expander {
+    dylib::Expander::new(lib)
+        .unwrap_or_else(|err| panic!("Cannot create expander for {:?}: {:?}", lib, err))
 }
 
 pub mod cli;
diff --git a/crates/ra_proc_macro_srv/src/rustc_server.rs b/crates/ra_proc_macro_srv/src/rustc_server.rs
index 9fcfdc4504c9..f481d70b22f5 100644
--- a/crates/ra_proc_macro_srv/src/rustc_server.rs
+++ b/crates/ra_proc_macro_srv/src/rustc_server.rs
@@ -6,7 +6,7 @@
 //! The original idea from fedochet is using proc-macro2 as backend,
 //! we use ra_tt instead for better intergation with RA.
 //!
-//! FIXME: No span and source file informatin is implemented yet
+//! FIXME: No span and source file information is implemented yet
 
 use crate::proc_macro::bridge::{self, server};
 use ra_tt as tt;
diff --git a/crates/ra_proc_macro_srv/src/tests/utils.rs b/crates/ra_proc_macro_srv/src/tests/utils.rs
index 1ee409449222..2139ec7a4dac 100644
--- a/crates/ra_proc_macro_srv/src/tests/utils.rs
+++ b/crates/ra_proc_macro_srv/src/tests/utils.rs
@@ -60,6 +60,6 @@ pub fn list(crate_name: &str, version: &str) -> Vec {
     let path = fixtures::dylib_path(crate_name, version);
     let task = ListMacrosTask { lib: path };
 
-    let res = list_macros(&task).unwrap();
+    let res = list_macros(&task);
     res.macros.into_iter().map(|(name, kind)| format!("{} [{:?}]", name, kind)).collect()
 }
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 28b67cfe2aa4..e8d5dad6577c 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -51,7 +51,7 @@ fn main() -> Result<()> {
             cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro, all)?
         }
 
-        args::Command::ProcMacro => run_proc_macro_sv()?,
+        args::Command::ProcMacro => run_proc_macro_srv()?,
         args::Command::RunServer => run_server()?,
         args::Command::Version => println!("rust-analyzer {}", env!("REV")),
     }
@@ -65,7 +65,7 @@ fn setup_logging() -> Result<()> {
     Ok(())
 }
 
-fn run_proc_macro_sv() -> Result<()> {
+fn run_proc_macro_srv() -> Result<()> {
     ra_proc_macro_srv::cli::run();
     Ok(())
 }
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 762f776fea85..d0a71120aafb 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -76,7 +76,7 @@ pub(crate) fn load_cargo(
         ProcMacroClient::dummy()
     } else {
         let path = std::env::current_exe()?;
-        ProcMacroClient::extern_process(&path, &["proc-macro"]).unwrap()
+        ProcMacroClient::extern_process(path, &["proc-macro"]).unwrap()
     };
     let host = load(&source_roots, ws, &mut vfs, receiver, extern_dirs, &proc_macro_client);
     Ok((host, source_roots))
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index f2ad453fafe4..d50f3f3d4b6f 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -148,20 +148,17 @@ pub fn new(
 
         let proc_macro_client = match &config.proc_macro_srv {
             None => ProcMacroClient::dummy(),
-            Some((path, args)) => {
-                let path = std::path::Path::new(path);
-                match ProcMacroClient::extern_process(path, args) {
-                    Ok(it) => it,
-                    Err(err) => {
-                        log::error!(
-                            "Fail to run ra_proc_macro_srv from path {}, error : {}",
-                            path.to_string_lossy(),
-                            err
-                        );
-                        ProcMacroClient::dummy()
-                    }
+            Some((path, args)) => match ProcMacroClient::extern_process(path.into(), args) {
+                Ok(it) => it,
+                Err(err) => {
+                    log::error!(
+                        "Fail to run ra_proc_macro_srv from path {}, error: {:?}",
+                        path,
+                        err
+                    );
+                    ProcMacroClient::dummy()
                 }
-            }
+            },
         };
 
         workspaces

From 982af2286a57667263f6cf0abb80b5e20b198047 Mon Sep 17 00:00:00 2001
From: Veetaha 
Date: Mon, 20 Apr 2020 21:50:06 +0300
Subject: [PATCH 16/29] Display path not debug it
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-Authored-By: Laurențiu Nicola 
---
 crates/ra_proc_macro_srv/src/lib.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crates/ra_proc_macro_srv/src/lib.rs b/crates/ra_proc_macro_srv/src/lib.rs
index 7faf36834258..17845870356d 100644
--- a/crates/ra_proc_macro_srv/src/lib.rs
+++ b/crates/ra_proc_macro_srv/src/lib.rs
@@ -42,7 +42,7 @@ pub(crate) fn list_macros(task: &ListMacrosTask) -> ListMacrosResult {
 
 fn create_expander(lib: &Path) -> dylib::Expander {
     dylib::Expander::new(lib)
-        .unwrap_or_else(|err| panic!("Cannot create expander for {:?}: {:?}", lib, err))
+        .unwrap_or_else(|err| panic!("Cannot create expander for {}: {}", lib.display(), err))
 }
 
 pub mod cli;

From 0f5b1fef5ea0a34e2760383182e9683cdbd7d49e Mon Sep 17 00:00:00 2001
From: Veetaha 
Date: Mon, 20 Apr 2020 21:54:43 +0300
Subject: [PATCH 17/29] Display path not debug it
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-Authored-By: Laurențiu Nicola 
---
 crates/ra_proc_macro_srv/src/dylib.rs | 14 +++++++-------
 crates/ra_proc_macro_srv/src/lib.rs   |  2 +-
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/crates/ra_proc_macro_srv/src/dylib.rs b/crates/ra_proc_macro_srv/src/dylib.rs
index f7a86a5327e8..d202eb0fde42 100644
--- a/crates/ra_proc_macro_srv/src/dylib.rs
+++ b/crates/ra_proc_macro_srv/src/dylib.rs
@@ -17,7 +17,7 @@ fn invalid_data_err(e: impl Into>) -> i
     io::Error::new(io::ErrorKind::InvalidData, e)
 }
 
-fn is_derive_registrar_symbol(symbol: &&str) -> bool {
+fn is_derive_registrar_symbol(symbol: &str) -> bool {
     symbol.contains(NEW_REGISTRAR_SYMBOL)
 }
 
@@ -29,13 +29,13 @@ fn find_registrar_symbol(file: &Path) -> io::Result> {
     let name = match object {
         Object::Elf(elf) => {
             let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?;
-            symbols.into_iter().find(is_derive_registrar_symbol).map(&str::to_owned)
+            symbols.into_iter().find(|s| is_derive_registrar_symbol(s)).map(&str::to_owned)
         }
         Object::PE(pe) => pe
             .exports
             .iter()
             .flat_map(|s| s.name)
-            .find(is_derive_registrar_symbol)
+            .find(|s| is_derive_registrar_symbol(s))
             .map(&str::to_owned),
         Object::Mach(Mach::Binary(binary)) => {
             let exports = binary.exports().map_err(invalid_data_err)?;
@@ -52,12 +52,12 @@ fn find_registrar_symbol(file: &Path) -> io::Result> {
                         &s.name
                     }
                 })
-                .find(is_derive_registrar_symbol)
+                .find(|s| is_derive_registrar_symbol(s))
                 .map(&str::to_owned)
         }
         _ => return Ok(None),
     };
-    Ok(name)
+    return Ok(name);
 }
 
 /// Loads dynamic library in platform dependent manner.
@@ -95,7 +95,7 @@ struct ProcMacroLibraryLibloading {
 impl ProcMacroLibraryLibloading {
     fn open(file: &Path) -> io::Result {
         let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| {
-            invalid_data_err(format!("Cannot find registrar symbol in file {:?}", file))
+            invalid_data_err(format!("Cannot find registrar symbol in file {}", file.display()))
         })?;
 
         let lib = load_library(file).map_err(invalid_data_err)?;
@@ -121,7 +121,7 @@ pub fn new(lib: &Path) -> Result {
         // already absolute
         let lib = lib
             .canonicalize()
-            .unwrap_or_else(|err| panic!("Cannot canonicalize {:?}: {:?}", lib, err));
+            .unwrap_or_else(|err| panic!("Cannot canonicalize {}: {:?}", lib.display(), err));
 
         let library = ProcMacroLibraryImpl::open(&lib).map_err(|e| e.to_string())?;
 
diff --git a/crates/ra_proc_macro_srv/src/lib.rs b/crates/ra_proc_macro_srv/src/lib.rs
index 17845870356d..f64e05a2d966 100644
--- a/crates/ra_proc_macro_srv/src/lib.rs
+++ b/crates/ra_proc_macro_srv/src/lib.rs
@@ -42,7 +42,7 @@ pub(crate) fn list_macros(task: &ListMacrosTask) -> ListMacrosResult {
 
 fn create_expander(lib: &Path) -> dylib::Expander {
     dylib::Expander::new(lib)
-        .unwrap_or_else(|err| panic!("Cannot create expander for {}: {}", lib.display(), err))
+        .unwrap_or_else(|err| panic!("Cannot create expander for {}: {:?}", lib.display(), err))
 }
 
 pub mod cli;

From 834960d841b667ad1c102ceeec8fbb20aaae099d Mon Sep 17 00:00:00 2001
From: Veetaha 
Date: Mon, 20 Apr 2020 22:07:47 +0300
Subject: [PATCH 18/29] Fix typo
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-Authored-By: Laurențiu Nicola 
---
 crates/ra_proc_macro_srv/src/lib.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/crates/ra_proc_macro_srv/src/lib.rs b/crates/ra_proc_macro_srv/src/lib.rs
index f64e05a2d966..3aca859db34b 100644
--- a/crates/ra_proc_macro_srv/src/lib.rs
+++ b/crates/ra_proc_macro_srv/src/lib.rs
@@ -5,7 +5,7 @@
 //!
 //! But we adapt it to better fit RA needs:
 //!
-//! * We use `ra_tt` for proc-macro `TokenStream` server, it is easy to manipulate and interact with
+//! * We use `ra_tt` for proc-macro `TokenStream` server, it is easier to manipulate and interact with
 //!   RA than `proc-macro2` token stream.
 //! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable`
 //!   rustc rather than `unstable`. (Although in gerenal ABI compatibility is still an issue)

From d8ca817456ed60b0163d3f81c58f6db9e6372e5f Mon Sep 17 00:00:00 2001
From: veetaha 
Date: Mon, 20 Apr 2020 22:24:10 +0300
Subject: [PATCH 19/29] Fix doc comment

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

diff --git a/crates/ra_proc_macro/src/rpc.rs b/crates/ra_proc_macro/src/rpc.rs
index e97b9086056b..4ce485926369 100644
--- a/crates/ra_proc_macro/src/rpc.rs
+++ b/crates/ra_proc_macro/src/rpc.rs
@@ -39,10 +39,10 @@ pub struct ExpansionTask {
     #[serde(with = "SubtreeDef")]
     pub macro_body: Subtree,
 
-    /// Names of macros to expand. // TODO: are they comma-separated?
+    /// Name of macro to expand.
     ///
-    /// In custom derive those are names of derived traits (`Serialize`, `Getters`, etc.). In
-    /// attribute-like and function-like macros - single name of macro itself (`show_streams`).
+    /// In custom derive this is the name of the derived trait (`Serialize`, `Getters`, etc.).
+    /// In attribute-like and function-like macros - single name of macro itself (`show_streams`).
     pub macro_name: String,
 
     /// Possible attributes for the attribute-like macros.

From fc460b1e423c2c510a10539f8289af548685676c Mon Sep 17 00:00:00 2001
From: veetaha 
Date: Mon, 20 Apr 2020 22:42:36 +0300
Subject: [PATCH 20/29] Migrate to Result -> io::Result

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

diff --git a/crates/ra_proc_macro/src/process.rs b/crates/ra_proc_macro/src/process.rs
index b1ebf46a1bc9..e24944af4b9a 100644
--- a/crates/ra_proc_macro/src/process.rs
+++ b/crates/ra_proc_macro/src/process.rs
@@ -48,7 +48,7 @@ impl Process {
     fn run(
         process_path: PathBuf,
         args: impl IntoIterator>,
-    ) -> Result {
+    ) -> io::Result {
         let child = Command::new(&process_path)
             .args(args)
             .stdin(Stdio::piped())
@@ -59,7 +59,7 @@ fn run(
         Ok(Process { path: process_path, child })
     }
 
-    fn restart(&mut self) -> Result<(), io::Error> {
+    fn restart(&mut self) -> io::Result<()> {
         let _ = self.child.kill();
         self.child = Command::new(&self.path)
             .stdin(Stdio::piped())
@@ -196,7 +196,7 @@ fn send_request(
     mut writer: &mut impl Write,
     mut reader: &mut impl BufRead,
     req: Request,
-) -> Result, io::Error> {
+) -> io::Result> {
     req.write(&mut writer)?;
     Ok(Response::read(&mut reader)?)
 }

From bd350108b0d1be67e86b93a94c324317a00b57cd Mon Sep 17 00:00:00 2001
From: Edwin Cheng 
Date: Tue, 21 Apr 2020 04:57:55 +0800
Subject: [PATCH 21/29] Fix restart missing arguments in proc-macro-srv

---
 crates/ra_proc_macro/src/process.rs | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/crates/ra_proc_macro/src/process.rs b/crates/ra_proc_macro/src/process.rs
index e24944af4b9a..97ba196b8a2a 100644
--- a/crates/ra_proc_macro/src/process.rs
+++ b/crates/ra_proc_macro/src/process.rs
@@ -9,7 +9,7 @@
 use io::{BufRead, BufReader};
 use std::{
     convert::{TryFrom, TryInto},
-    ffi::OsStr,
+    ffi::{OsStr, OsString},
     io::{self, Write},
     path::{Path, PathBuf},
     process::{Child, Command, Stdio},
@@ -35,6 +35,7 @@ struct Task {
 
 struct Process {
     path: PathBuf,
+    args: Vec,
     child: Child,
 }
 
@@ -46,22 +47,25 @@ fn drop(&mut self) {
 
 impl Process {
     fn run(
-        process_path: PathBuf,
+        path: PathBuf,
         args: impl IntoIterator>,
     ) -> io::Result {
-        let child = Command::new(&process_path)
-            .args(args)
+        let args = args.into_iter().map(|s| s.as_ref().into()).collect();
+
+        let child = Command::new(&path)
+            .args(&args)
             .stdin(Stdio::piped())
             .stdout(Stdio::piped())
             .stderr(Stdio::null())
             .spawn()?;
 
-        Ok(Process { path: process_path, child })
+        Ok(Process { path, args, child })
     }
 
     fn restart(&mut self) -> io::Result<()> {
         let _ = self.child.kill();
         self.child = Command::new(&self.path)
+            .args(&self.args)
             .stdin(Stdio::piped())
             .stdout(Stdio::piped())
             .stderr(Stdio::null())

From ce382e6a79edf9c00d4e7d7c3834cde7577e6517 Mon Sep 17 00:00:00 2001
From: Edwin Cheng 
Date: Tue, 21 Apr 2020 05:22:17 +0800
Subject: [PATCH 22/29] Refactor a bit

---
 crates/ra_proc_macro/src/process.rs | 105 ++++++++++++++--------------
 1 file changed, 51 insertions(+), 54 deletions(-)

diff --git a/crates/ra_proc_macro/src/process.rs b/crates/ra_proc_macro/src/process.rs
index 97ba196b8a2a..673f80a7ab02 100644
--- a/crates/ra_proc_macro/src/process.rs
+++ b/crates/ra_proc_macro/src/process.rs
@@ -28,60 +28,6 @@ pub(crate) struct ProcMacroProcessThread {
     handle: jod_thread::JoinHandle<()>,
 }
 
-struct Task {
-    req: Request,
-    result_tx: Sender>,
-}
-
-struct Process {
-    path: PathBuf,
-    args: Vec,
-    child: Child,
-}
-
-impl Drop for Process {
-    fn drop(&mut self) {
-        let _ = self.child.kill();
-    }
-}
-
-impl Process {
-    fn run(
-        path: PathBuf,
-        args: impl IntoIterator>,
-    ) -> io::Result {
-        let args = args.into_iter().map(|s| s.as_ref().into()).collect();
-
-        let child = Command::new(&path)
-            .args(&args)
-            .stdin(Stdio::piped())
-            .stdout(Stdio::piped())
-            .stderr(Stdio::null())
-            .spawn()?;
-
-        Ok(Process { path, args, child })
-    }
-
-    fn restart(&mut self) -> io::Result<()> {
-        let _ = self.child.kill();
-        self.child = Command::new(&self.path)
-            .args(&self.args)
-            .stdin(Stdio::piped())
-            .stdout(Stdio::piped())
-            .stderr(Stdio::null())
-            .spawn()?;
-        Ok(())
-    }
-
-    fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> {
-        let stdin = self.child.stdin.take()?;
-        let stdout = self.child.stdout.take()?;
-        let read = BufReader::new(stdout);
-
-        Some((stdin, read))
-    }
-}
-
 impl ProcMacroProcessSrv {
     pub fn run(
         process_path: PathBuf,
@@ -196,6 +142,57 @@ fn client_loop(task_rx: Receiver, mut process: Process) {
     }
 }
 
+struct Task {
+    req: Request,
+    result_tx: Sender>,
+}
+
+struct Process {
+    path: PathBuf,
+    args: Vec,
+    child: Child,
+}
+
+impl Drop for Process {
+    fn drop(&mut self) {
+        let _ = self.child.kill();
+    }
+}
+
+impl Process {
+    fn run(
+        path: PathBuf,
+        args: impl IntoIterator>,
+    ) -> io::Result {
+        let args = args.into_iter().map(|s| s.as_ref().into()).collect();
+        let child = mk_child(&path, &args)?;
+        Ok(Process { path, args, child })
+    }
+
+    fn restart(&mut self) -> io::Result<()> {
+        let _ = self.child.kill();
+        self.child = mk_child(&self.path, &self.args)?;
+        Ok(())
+    }
+
+    fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> {
+        let stdin = self.child.stdin.take()?;
+        let stdout = self.child.stdout.take()?;
+        let read = BufReader::new(stdout);
+
+        Some((stdin, read))
+    }
+}
+
+fn mk_child(path: &Path, args: impl IntoIterator>) -> io::Result {
+    Command::new(&path)
+        .args(args)
+        .stdin(Stdio::piped())
+        .stdout(Stdio::piped())
+        .stderr(Stdio::null())
+        .spawn()
+}
+
 fn send_request(
     mut writer: &mut impl Write,
     mut reader: &mut impl BufRead,

From f964bbd7351cbf26f01cd9114b549037fb155a27 Mon Sep 17 00:00:00 2001
From: Nikolai Morin 
Date: Tue, 21 Apr 2020 09:20:14 +0200
Subject: [PATCH 23/29] More detailed Sublime Text install instructions
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* People might typically jump directly to their editor and wonder where the part about installing rust-analyzer is – at least I did. I added a link to the relevant section for ST.
* Make ST instructions more detailed and user friendly (especially beginners), include troubleshooting tips.
* Minor grammar improvements throughout.
---
 docs/user/readme.adoc | 37 +++++++++++++++++++++++--------------
 1 file changed, 23 insertions(+), 14 deletions(-)

diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc
index abd126340c9b..7898ece0b6a5 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/readme.adoc
@@ -14,9 +14,9 @@
 // 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.
-This manual focuses on a specific usage of the library -- the implementation of
-https://microsoft.github.io/language-server-protocol/[Language Server Protocol].
-LSP allows various code editors, like VS Code, Emacs or Vim, to implement semantic features like completion or goto definition by talking to an external language server process.
+This manual focuses on a specific usage of the library -- running it as part of a server that implements the
+https://microsoft.github.io/language-server-protocol/[Language Server Protocol] (LSP).
+The LSP allows various code editors, like VS Code, Emacs or Vim, to implement semantic features like completion or goto definition by talking to an external language server process.
 
 To improve this document, send a pull request against
 https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/readme.adoc[this file].
@@ -26,7 +26,7 @@ https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/user/readme.adoc
 In theory, one should be able to just install the server binary and have it automatically work with any editor.
 We are not there yet, so some editor specific setup is required.
 
-Additionally, rust-analyzer needs sources of the standard library.
+Additionally, rust-analyzer needs the sources of the standard library.
 If the source code is not present, rust-analyzer will attempt to install it automatically.
 
 To add the sources manually, run the following command:
@@ -38,7 +38,7 @@ $ rustup component add rust-src
 === VS Code
 
 This is the best supported editor at the moment.
-rust-analyzer plugin for VS Code is maintained
+The rust-analyzer plugin for VS Code is maintained
 https://github.com/rust-analyzer/rust-analyzer/tree/master/editors/code[in tree].
 
 You can install the latest release of the plugin from
@@ -74,7 +74,7 @@ We ship nightly releases for VS Code. To help us out with testing the newest cod
 { "rust-analyzer.updates.channel": "nightly" }
 ----
 
-You will be prompted to install the `nightly` extension version. Just click `Download now` and from that moment you will get automatic updates each 24 hours.
+You will be prompted to install the `nightly` extension version. Just click `Download now` and from that moment you will get automatic updates every 24 hours.
 
 If you don't want to be asked for `Download now` every day when the new nightly version is released add the following to your `settings.json`:
 [source,json]
@@ -110,10 +110,10 @@ Here are some useful self-diagnostic commands:
 
 === Language Server Binary
 
-Other editors generally require `rust-analyzer` binary to be in `$PATH`.
-You can download the pre-built binary from
-https://github.com/rust-analyzer/rust-analyzer/releases[releases]
-page, or you can install it from source using the following command:
+Other editors generally require the `rust-analyzer` binary to be in `$PATH`.
+You can download the pre-built binary from the https://github.com/rust-analyzer/rust-analyzer/releases[releases] page. Typically, you then need to rename the binary for your platform, e.g. `rust-analyzer-mac` if you're on Mac OS, to `rust-analzyer` and make it executable in addition to moving it into a directory in your `$PATH`.
+
+Alternatively, you can install it from source using the following command:
 
 [source,bash]
 ----
@@ -122,7 +122,7 @@ $ cargo xtask install --server
 
 ==== Arch Linux
 
-`rust-analyzer` binary can be installed from AUR (Arch User Repository):
+The `rust-analyzer` binary can be installed from AUR (Arch User Repository):
 
 - https://aur.archlinux.org/packages/rust-analyzer-bin[`rust-analyzer-bin`] (binary from GitHub releases)
 - https://aur.archlinux.org/packages/rust-analyzer[`rust-analyzer`] (built from latest tagged source)
@@ -183,11 +183,20 @@ Once `neovim/nvim-lsp` is installed, use `+lua require'nvim_lsp'.rust_analyzer.s
 
 === Sublime Text 3
 
-Prerequisites:
+Prerequisites: You have installed the <>.
 
-`LSP` package.
+You also need the `LSP` package. To install it:
 
-Invoke the command palette (`ctrl+shift+p`) and type LSP enable to locally/globally enable the rust-analyzer LSP (type LSP enable, then choose either locally or globally, then select rust-analyzer)
+1. If you've never installed a Sublime Text package, install Package Control:
+   * Open the command palette (Win/Linux: `ctrl+shift+p`, Mac: `cmd+shift+p`)
+   * Type `Install Package Control`, press enter
+2. In the command palette, run `Package control: Install package`, and in the list that pops up, type `LSP` and press enter.
+
+Finally, with your Rust project open, in the command palette, run `LSP: Enable Language Server In Project` or `LSP: Enable Language Server Globally`, then select `rust-analyzer` in the list that pops up enable to enable the rust-analyzer LSP. The latter means that rust-analzyer is enabled by default in Rust projects.
+
+If it worked, you should see "rust-analzyer, Line X, Column Y" on the left side of the bottom bar, and after waiting a bit, functionality like tooltips on hovering over variables should become available.
+
+If you get an error saying `No such file or directory: 'rust-analyzer'` even though the binary is on your `$PATH`, there is likely a problem where Sublime doesn't see the same `$PATH` as your shell, see https://github.com/rust-analyzer/rust-analyzer/issues/1811[this issue]. On Unix, if you installed Rust with `rustup`, moving the binary to `$HOME/.cargo/bin` should help.
 
 == Usage
 

From 22fd891d45ba9eaa0fefccdd4e7cb902efc4636b Mon Sep 17 00:00:00 2001
From: Nikolai Morin 
Date: Tue, 21 Apr 2020 10:13:33 +0200
Subject: [PATCH 24/29] Update docs/user/readme.adoc
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-Authored-By: Laurențiu Nicola 
---
 docs/user/readme.adoc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc
index 7898ece0b6a5..ac9b66b4a8f5 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/readme.adoc
@@ -192,7 +192,7 @@ You also need the `LSP` package. To install it:
    * Type `Install Package Control`, press enter
 2. In the command palette, run `Package control: Install package`, and in the list that pops up, type `LSP` and press enter.
 
-Finally, with your Rust project open, in the command palette, run `LSP: Enable Language Server In Project` or `LSP: Enable Language Server Globally`, then select `rust-analyzer` in the list that pops up enable to enable the rust-analyzer LSP. The latter means that rust-analzyer is enabled by default in Rust projects.
+Finally, with your Rust project open, in the command palette, run `LSP: Enable Language Server In Project` or `LSP: Enable Language Server Globally`, then select `rust-analyzer` in the list that pops up to enable the rust-analyzer LSP. The latter means that rust-analzyer is enabled by default in Rust projects.
 
 If it worked, you should see "rust-analzyer, Line X, Column Y" on the left side of the bottom bar, and after waiting a bit, functionality like tooltips on hovering over variables should become available.
 

From d22349de0a66700db0792d8213812a4c5867559a Mon Sep 17 00:00:00 2001
From: Nikolai Morin 
Date: Tue, 21 Apr 2020 10:15:49 +0200
Subject: [PATCH 25/29] Update docs/user/readme.adoc
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-Authored-By: Laurențiu Nicola 
---
 docs/user/readme.adoc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc
index ac9b66b4a8f5..4b5294ca905b 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/readme.adoc
@@ -196,7 +196,7 @@ Finally, with your Rust project open, in the command palette, run `LSP: Enable L
 
 If it worked, you should see "rust-analzyer, Line X, Column Y" on the left side of the bottom bar, and after waiting a bit, functionality like tooltips on hovering over variables should become available.
 
-If you get an error saying `No such file or directory: 'rust-analyzer'` even though the binary is on your `$PATH`, there is likely a problem where Sublime doesn't see the same `$PATH` as your shell, see https://github.com/rust-analyzer/rust-analyzer/issues/1811[this issue]. On Unix, if you installed Rust with `rustup`, moving the binary to `$HOME/.cargo/bin` should help.
+If you get an error saying `No such file or directory: 'rust-analyzer'` even though the binary is on your `$PATH`, the likely explanation is that Sublime doesn't see the same `$PATH` as the shell, see https://github.com/rust-analyzer/rust-analyzer/issues/1811[this issue]. On Unix, running the editor from a shell or changing the `.desktop` file to set the environment, should help.
 
 == Usage
 

From c0e9a1a476b7cd6e60e58e91f3497555f5134bbd Mon Sep 17 00:00:00 2001
From: Nikolai Morin 
Date: Tue, 21 Apr 2020 10:16:30 +0200
Subject: [PATCH 26/29] Delete comma

---
 docs/user/readme.adoc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc
index 4b5294ca905b..5f03f068acdf 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/readme.adoc
@@ -196,7 +196,7 @@ Finally, with your Rust project open, in the command palette, run `LSP: Enable L
 
 If it worked, you should see "rust-analzyer, Line X, Column Y" on the left side of the bottom bar, and after waiting a bit, functionality like tooltips on hovering over variables should become available.
 
-If you get an error saying `No such file or directory: 'rust-analyzer'` even though the binary is on your `$PATH`, the likely explanation is that Sublime doesn't see the same `$PATH` as the shell, see https://github.com/rust-analyzer/rust-analyzer/issues/1811[this issue]. On Unix, running the editor from a shell or changing the `.desktop` file to set the environment, should help.
+If you get an error saying `No such file or directory: 'rust-analyzer'` even though the binary is on your `$PATH`, the likely explanation is that Sublime doesn't see the same `$PATH` as the shell, see https://github.com/rust-analyzer/rust-analyzer/issues/1811[this issue]. On Unix, running the editor from a shell or changing the `.desktop` file to set the environment should help.
 
 == Usage
 

From 427f634de3fe6a746adf5de41eeb290c5ac321ef Mon Sep 17 00:00:00 2001
From: Nikolai Morin 
Date: Tue, 21 Apr 2020 11:17:12 +0200
Subject: [PATCH 27/29] Move the PATH issue up to the non-editor specific
 section

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

diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc
index 5f03f068acdf..77cdd5e1c384 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/readme.adoc
@@ -120,6 +120,8 @@ Alternatively, you can install it from source using the following command:
 $ cargo xtask install --server
 ----
 
+If your editor can't find the binary even though the binary is on your `$PATH`, the likely explanation is that it doesn't see the same `$PATH` as the shell, see https://github.com/rust-analyzer/rust-analyzer/issues/1811[this issue]. On Unix, running the editor from a shell or changing the `.desktop` file to set the environment should help.
+
 ==== Arch Linux
 
 The `rust-analyzer` binary can be installed from AUR (Arch User Repository):
@@ -196,7 +198,7 @@ Finally, with your Rust project open, in the command palette, run `LSP: Enable L
 
 If it worked, you should see "rust-analzyer, Line X, Column Y" on the left side of the bottom bar, and after waiting a bit, functionality like tooltips on hovering over variables should become available.
 
-If you get an error saying `No such file or directory: 'rust-analyzer'` even though the binary is on your `$PATH`, the likely explanation is that Sublime doesn't see the same `$PATH` as the shell, see https://github.com/rust-analyzer/rust-analyzer/issues/1811[this issue]. On Unix, running the editor from a shell or changing the `.desktop` file to set the environment should help.
+If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <>.
 
 == Usage
 

From 0f49fd818b67ece2c33d818d68af6ad5c00dd51b Mon Sep 17 00:00:00 2001
From: Heyward Fann 
Date: Tue, 21 Apr 2020 17:54:13 +0800
Subject: [PATCH 28/29] docs(readme): improve user docs

cleanup unavailable configurations/commands
---
 docs/user/readme.adoc | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc
index 77cdd5e1c384..13ab2acc21f3 100644
--- a/docs/user/readme.adoc
+++ b/docs/user/readme.adoc
@@ -158,8 +158,8 @@ The are several LSP client implementations for vim:
 2. Run `:CocInstall coc-rust-analyzer` to install
    https://github.com/fannheyward/coc-rust-analyzer[coc-rust-analyzer],
    this extension implements _most_ of the features supported in the VSCode extension:
-   * same configurations as VSCode extension, `rust-analyzer.serverPath`, `rust-analyzer.enableCargoWatchOnStartup` etc.
-   * same commands too, `rust-analyzer.analyzerStatus`, `rust-analyzer.startCargoWatch` etc.
+   * same configurations as VSCode extension, `rust-analyzer.serverPath`, `rust-analyzer.cargo.features` etc.
+   * same commands too, `rust-analyzer.analyzerStatus`, `rust-analyzer.ssr` etc.
    * highlighting and inlay_hints are not implemented yet
 
 ==== LanguageClient-neovim

From d9bed8aa45bc9b206f48d837e6737ee24535d7a6 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov 
Date: Tue, 21 Apr 2020 12:06:25 +0200
Subject: [PATCH 29/29] Cleanup args a bit

---
 crates/rust-analyzer/src/bin/args.rs | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/crates/rust-analyzer/src/bin/args.rs b/crates/rust-analyzer/src/bin/args.rs
index 5e19253a65cc..b14409c39c03 100644
--- a/crates/rust-analyzer/src/bin/args.rs
+++ b/crates/rust-analyzer/src/bin/args.rs
@@ -84,7 +84,7 @@ pub(crate) fn parse() -> Result> {
                 if matches.contains(["-h", "--help"]) {
                     eprintln!(
                         "\
-ra-cli-parse
+rust-analyzer parse
 
 USAGE:
     rust-analyzer parse [FLAGS]
@@ -104,7 +104,7 @@ pub(crate) fn parse() -> Result> {
                 if matches.contains(["-h", "--help"]) {
                     eprintln!(
                         "\
-ra-cli-symbols
+rust-analyzer symbols
 
 USAGE:
     rust-analyzer highlight [FLAGS]
@@ -123,7 +123,7 @@ pub(crate) fn parse() -> Result> {
                 if matches.contains(["-h", "--help"]) {
                     eprintln!(
                         "\
-ra-cli-highlight
+rust-analyzer highlight
 
 USAGE:
     rust-analyzer highlight [FLAGS]
@@ -143,7 +143,7 @@ pub(crate) fn parse() -> Result> {
                 if matches.contains(["-h", "--help"]) {
                     eprintln!(
                         "\
-ra-cli-analysis-stats
+rust-analyzer analysis-stats
 
 USAGE:
     rust-analyzer analysis-stats [FLAGS] [OPTIONS] [PATH]
@@ -193,7 +193,7 @@ pub(crate) fn parse() -> Result> {
                 if matches.contains(["-h", "--help"]) {
                     eprintln!(
                         "\
-rust-analyzer-analysis-bench
+rust-analyzer analysis-bench
 
 USAGE:
     rust-analyzer analysis-bench [FLAGS] [OPTIONS]
@@ -236,7 +236,7 @@ pub(crate) fn parse() -> Result> {
                 if matches.contains(["-h", "--help"]) {
                     eprintln!(
                         "\
-ra-cli-diagnostics
+rust-analyzer diagnostics
 
 USAGE:
     rust-analyzer diagnostics [FLAGS] [PATH]
@@ -269,7 +269,7 @@ pub(crate) fn parse() -> Result> {
             _ => {
                 eprintln!(
                     "\
-ra-cli
+rust-analyzer
 
 USAGE:
     rust-analyzer 
@@ -281,6 +281,8 @@ pub(crate) fn parse() -> Result> {
     analysis-bench
     analysis-stats
     highlight
+    diagnostics
+    proc-macro
     parse
     symbols"
                 );