diff --git a/Cargo.lock b/Cargo.lock index 3826ae1c6f15..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", @@ -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", 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_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_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/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_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_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_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_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_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/_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/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/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_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) } } 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) '()': () + ") +} 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, diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index bb12a1bdc0cd..f8dac1d54178 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs @@ -161,6 +161,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, 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_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/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/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
 ];
diff --git a/crates/ra_mbe/src/mbe_expander/matcher.rs b/crates/ra_mbe/src/mbe_expander/matcher.rs
index 2579382da8da..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() {
@@ -202,6 +206,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 +266,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 +293,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 +308,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 +444,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..d7866452dd94 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)));
@@ -132,27 +158,28 @@ 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() }
 }
 
 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..2b4390eb26ed 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 {
@@ -372,10 +376,29 @@ macro_rules! make_leaf {
                 };
             }
             let leaf: tt::Leaf = match k {
-                T![true] | T![false] => make_leaf!(Literal),
-                IDENT | LIFETIME => make_leaf!(Ident),
+                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),
+                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 {
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs
index 5d1274d21faa..100ed41f234a 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(
@@ -988,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(()),
         }
     }
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..673f80a7ab02 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},
@@ -28,66 +28,11 @@ pub(crate) struct ProcMacroProcessThread {
     handle: jod_thread::JoinHandle<()>,
 }
 
-struct Task {
-    req: Request,
-    result_tx: Sender>,
-}
-
-struct Process {
-    path: PathBuf,
-    child: Child,
-}
-
-impl Drop for Process {
-    fn drop(&mut self) {
-        let _ = self.child.kill();
-    }
-}
-
-impl Process {
-    fn run(process_path: &Path, args: I) -> Result
-    where
-        I: IntoIterator,
-        S: AsRef,
-    {
-        let child = Command::new(process_path.clone())
-            .args(args)
-            .stdin(Stdio::piped())
-            .stdout(Stdio::piped())
-            .stderr(Stdio::null())
-            .spawn()?;
-
-        Ok(Process { path: process_path.into(), child })
-    }
-
-    fn restart(&mut self) -> Result<(), io::Error> {
-        let _ = self.child.kill();
-        self.child = Command::new(self.path.clone())
-            .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: &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);
@@ -197,11 +142,62 @@ 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,
     req: Request,
-) -> Result, io::Error> {
+) -> io::Result> {
     req.write(&mut writer)?;
     Ok(Response::read(&mut reader)?)
 }
diff --git a/crates/ra_proc_macro/src/rpc.rs b/crates/ra_proc_macro/src/rpc.rs
index 66b3f55db370..4ce485926369 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.
+    /// Name of macro to expand.
     ///
-    /// 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`).
+    /// 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.
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..d202eb0fde42 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 {
     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(|s| is_derive_registrar_symbol(s)).map(&str::to_owned)
         }
+        Object::PE(pe) => pe
+            .exports
+            .iter()
+            .flat_map(|s| s.name)
+            .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)?;
-            let name = exports
+            exports
                 .iter()
                 .map(|s| {
                     // In macos doc:
@@ -59,11 +53,11 @@ fn find_registrar_symbol(file: &Path) -> Result, IoError> {
                     }
                 })
                 .find(|s| is_derive_registrar_symbol(s))
-                .map(|s| s.to_string());
-            Ok(name)
+                .map(&str::to_owned)
         }
-        _ => Ok(None),
-    }
+        _ => return Ok(None),
+    };
+    return 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.display()))
+        })?;
 
         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.display(), 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..3aca859db34b 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 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)
 
@@ -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.display(), 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 ec0d356922d0..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;
@@ -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
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/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/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()?);
             }
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"
                 );
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/conv.rs b/crates/rust-analyzer/src/conv.rs
index f47d931fdac8..d0218dcbf78b 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;
@@ -381,6 +383,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/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index f2ad453fafe4..8e1744bf9026 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
@@ -184,7 +181,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,
diff --git a/docs/user/readme.adoc b/docs/user/readme.adoc
index abd126340c9b..13ab2acc21f3 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,19 +110,21 @@ 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]
 ----
 $ 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
 
-`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)
@@ -156,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
@@ -183,11 +185,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 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'`, see the <>.
 
 == Usage
 
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": [
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;
         }