Repository: rust-analyzer/rust-analyzer
Pull request #3131 (2020-02-13 20:50:42)

vscode: simplified config and to removed one source of truth of default values
Though not intended initially, the implementation of config design is alike [dart's one](https://github.com/Dart-Code/Dart-Code/blob/master/src/extension/config.ts) as pointed by @matklad in PM.


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index f1651edaad..45804c087e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1247,7 +1247,6 @@ dependencies = [
 name = "ra_project_model"
 version = "0.1.0"
 dependencies = [
- "anyhow",
  "cargo_metadata",
  "log",
  "ra_arena",
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index c4aea2a067..1fb701da5f 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -1,18 +1,10 @@
+use ra_ide_db::imports_locator::ImportsLocator;
+use ra_syntax::ast::{self, AstNode};
+
 use crate::{
     assist_ctx::{Assist, AssistCtx},
     insert_use_statement, AssistId,
 };
-use hir::{
-    db::HirDatabase, AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution,
-    SourceAnalyzer, Trait, Type,
-};
-use ra_ide_db::{imports_locator::ImportsLocator, RootDatabase};
-use ra_prof::profile;
-use ra_syntax::{
-    ast::{self, AstNode},
-    SyntaxNode,
-};
-use rustc_hash::FxHashSet;
 use std::collections::BTreeSet;
 
 // Assist: auto_import
@@ -35,24 +27,52 @@ use std::collections::BTreeSet;
 // # pub mod std { pub mod collections { pub struct HashMap { } } }
 // ```
 pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
-    let auto_import_assets = AutoImportAssets::new(&ctx)?;
-    let proposed_imports = auto_import_assets.search_for_imports(ctx.db);
-    if proposed_imports.is_empty() {
+    let path_under_caret: ast::Path = ctx.find_node_at_offset()?;
+    if path_under_caret.syntax().ancestors().find_map(ast::UseItem::cast).is_some() {
         return None;
     }
 
-    let assist_group_name = if proposed_imports.len() == 1 {
-        format!("Import `{}`", proposed_imports.iter().next().unwrap())
-    } else {
-        auto_import_assets.get_import_group_message()
+    let module = path_under_caret.syntax().ancestors().find_map(ast::Module::cast);
+    let position = match module.and_then(|it| it.item_list()) {
+        Some(item_list) => item_list.syntax().clone(),
+        None => {
+            let current_file =
+                path_under_caret.syntax().ancestors().find_map(ast::SourceFile::cast)?;
+            current_file.syntax().clone()
+        }
     };
-    let mut group = ctx.add_assist_group(assist_group_name);
+    let source_analyzer = ctx.source_analyzer(&position, None);
+    let module_with_name_to_import = source_analyzer.module()?;
+
+    let name_ref_to_import =
+        path_under_caret.syntax().descendants().find_map(ast::NameRef::cast)?;
+    if source_analyzer
+        .resolve_path(ctx.db, &name_ref_to_import.syntax().ancestors().find_map(ast::Path::cast)?)
+        .is_some()
+    {
+        return None;
+    }
+
+    let name_to_import = name_ref_to_import.syntax().to_string();
+    let proposed_imports = ImportsLocator::new(ctx.db)
+        .find_imports(&name_to_import)
+        .into_iter()
+        .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def))
+        .filter(|use_path| !use_path.segments.is_empty())
+        .take(20)
+        .collect::<BTreeSet<_>>();
+
+    if proposed_imports.is_empty() {
+        return None;
+    }
+
+    let mut group = ctx.add_assist_group(format!("Import {}", name_to_import));
     for import in proposed_imports {
         group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), |edit| {
-            edit.target(auto_import_assets.syntax_under_caret.text_range());
+            edit.target(path_under_caret.syntax().text_range());
             insert_use_statement(
-                &auto_import_assets.syntax_under_caret,
-                &auto_import_assets.syntax_under_caret,
+                &position,
+                path_under_caret.syntax(),
                 &import,
                 edit.text_edit_builder(),
             );
@@ -61,233 +81,12 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
     group.finish()
 }
 
-struct AutoImportAssets {
-    import_candidate: ImportCandidate,
-    module_with_name_to_import: Module,
-    syntax_under_caret: SyntaxNode,
-}
-
-impl AutoImportAssets {
-    fn new(ctx: &AssistCtx) -> Option<Self> {
-        if let Some(path_under_caret) = ctx.find_node_at_offset::<ast::Path>() {
-            Self::for_regular_path(path_under_caret, &ctx)
-        } else {
-            Self::for_method_call(ctx.find_node_at_offset()?, &ctx)
-        }
-    }
-
-    fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option<Self> {
-        let syntax_under_caret = method_call.syntax().to_owned();
-        let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None);
-        let module_with_name_to_import = source_analyzer.module()?;
-        Some(Self {
-            import_candidate: ImportCandidate::for_method_call(
-                &method_call,
-                &source_analyzer,
-                ctx.db,
-            )?,
-            module_with_name_to_import,
-            syntax_under_caret,
-        })
-    }
-
-    fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistCtx) -> Option<Self> {
-        let syntax_under_caret = path_under_caret.syntax().to_owned();
-        if syntax_under_caret.ancestors().find_map(ast::UseItem::cast).is_some() {
-            return None;
-        }
-
-        let source_analyzer = ctx.source_analyzer(&syntax_under_caret, None);
-        let module_with_name_to_import = source_analyzer.module()?;
-        Some(Self {
-            import_candidate: ImportCandidate::for_regular_path(
-                &path_under_caret,
-                &source_analyzer,
-                ctx.db,
-            )?,
-            module_with_name_to_import,
-            syntax_under_caret,
-        })
-    }
-
-    fn get_search_query(&self) -> &str {
-        match &self.import_candidate {
-            ImportCandidate::UnqualifiedName(name) => name,
-            ImportCandidate::QualifierStart(qualifier_start) => qualifier_start,
-            ImportCandidate::TraitAssocItem(_, trait_assoc_item_name) => trait_assoc_item_name,
-            ImportCandidate::TraitMethod(_, trait_method_name) => trait_method_name,
-        }
-    }
-
-    fn get_import_group_message(&self) -> String {
-        match &self.import_candidate {
-            ImportCandidate::UnqualifiedName(name) => format!("Import {}", name),
-            ImportCandidate::QualifierStart(qualifier_start) => {
-                format!("Import {}", qualifier_start)
-            }
-            ImportCandidate::TraitAssocItem(_, trait_assoc_item_name) => {
-                format!("Import a trait for item {}", trait_assoc_item_name)
-            }
-            ImportCandidate::TraitMethod(_, trait_method_name) => {
-                format!("Import a trait for method {}", trait_method_name)
-            }
-        }
-    }
-
-    fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> {
-        let _p = profile("auto_import::search_for_imports");
-        let current_crate = self.module_with_name_to_import.krate();
-        ImportsLocator::new(db)
-            .find_imports(&self.get_search_query())
-            .into_iter()
-            .filter_map(|module_def| match &self.import_candidate {
-                ImportCandidate::TraitAssocItem(assoc_item_type, _) => {
-                    let located_assoc_item = match module_def {
-                        ModuleDef::Function(located_function) => located_function
-                            .as_assoc_item(db)
-                            .map(|assoc| assoc.container(db))
-                            .and_then(Self::assoc_to_trait),
-                        ModuleDef::Const(located_const) => located_const
-                            .as_assoc_item(db)
-                            .map(|assoc| assoc.container(db))
-                            .and_then(Self::assoc_to_trait),
-                        _ => None,
-                    }?;
-
-                    let mut trait_candidates = FxHashSet::default();
-                    trait_candidates.insert(located_assoc_item.into());
-
-                    assoc_item_type
-                        .iterate_path_candidates(
-                            db,
-                            current_crate,
-                            &trait_candidates,
-                            None,
-                            |_, assoc| Self::assoc_to_trait(assoc.container(db)),
-                        )
-                        .map(ModuleDef::from)
-                }
-                ImportCandidate::TraitMethod(function_callee, _) => {
-                    let located_assoc_item =
-                        if let ModuleDef::Function(located_function) = module_def {
-                            located_function
-                                .as_assoc_item(db)
-                                .map(|assoc| assoc.container(db))
-                                .and_then(Self::assoc_to_trait)
-                        } else {
-                            None
-                        }?;
-
-                    let mut trait_candidates = FxHashSet::default();
-                    trait_candidates.insert(located_assoc_item.into());
-
-                    function_callee
-                        .iterate_method_candidates(
-                            db,
-                            current_crate,
-                            &trait_candidates,
-                            None,
-                            |_, function| {
-                                Self::assoc_to_trait(function.as_assoc_item(db)?.container(db))
-                            },
-                        )
-                        .map(ModuleDef::from)
-                }
-                _ => Some(module_def),
-            })
-            .filter_map(|module_def| self.module_with_name_to_import.find_use_path(db, module_def))
-            .filter(|use_path| !use_path.segments.is_empty())
-            .take(20)
-            .collect::<BTreeSet<_>>()
-    }
-
-    fn assoc_to_trait(assoc: AssocItemContainer) -> Option<Trait> {
-        if let AssocItemContainer::Trait(extracted_trait) = assoc {
-            Some(extracted_trait)
-        } else {
-            None
-        }
-    }
-}
-
-#[derive(Debug)]
-enum ImportCandidate {
-    /// Simple name like 'HashMap'
-    UnqualifiedName(String),
-    /// First part of the qualified name.
-    /// For 'std::collections::HashMap', that will be 'std'.
-    QualifierStart(String),
-    /// A trait associated function (with no self parameter) or associated constant.
-    /// For 'test_mod::TestEnum::test_function', `Type` is the `test_mod::TestEnum` expression type
-    /// and `String` is the `test_function`
-    TraitAssocItem(Type, String),
-    /// A trait method with self parameter.
-    /// For 'test_enum.test_method()', `Type` is the `test_enum` expression type
-    /// and `String` is the `test_method`
-    TraitMethod(Type, String),
-}
-
-impl ImportCandidate {
-    fn for_method_call(
-        method_call: &ast::MethodCallExpr,
-        source_analyzer: &SourceAnalyzer,
-        db: &impl HirDatabase,
-    ) -> Option<Self> {
-        if source_analyzer.resolve_method_call(method_call).is_some() {
-            return None;
-        }
-        Some(Self::TraitMethod(
-            source_analyzer.type_of(db, &method_call.expr()?)?,
-            method_call.name_ref()?.syntax().to_string(),
-        ))
-    }
-
-    fn for_regular_path(
-        path_under_caret: &ast::Path,
-        source_analyzer: &SourceAnalyzer,
-        db: &impl HirDatabase,
-    ) -> Option<Self> {
-        if source_analyzer.resolve_path(db, path_under_caret).is_some() {
-            return None;
-        }
-
-        let segment = path_under_caret.segment()?;
-        if let Some(qualifier) = path_under_caret.qualifier() {
-            let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?;
-            let qualifier_start_path =
-                qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?;
-            if let Some(qualifier_start_resolution) =
-                source_analyzer.resolve_path(db, &qualifier_start_path)
-            {
-                let qualifier_resolution = if qualifier_start_path == qualifier {
-                    qualifier_start_resolution
-                } else {
-                    source_analyzer.resolve_path(db, &qualifier)?
-                };
-                if let PathResolution::Def(ModuleDef::Adt(assoc_item_path)) = qualifier_resolution {
-                    Some(ImportCandidate::TraitAssocItem(
-                        assoc_item_path.ty(db),
-                        segment.syntax().to_string(),
-                    ))
-                } else {
-                    None
-                }
-            } else {
-                Some(ImportCandidate::QualifierStart(qualifier_start.syntax().to_string()))
-            }
-        } else {
-            Some(ImportCandidate::UnqualifiedName(
-                segment.syntax().descendants().find_map(ast::NameRef::cast)?.syntax().to_string(),
-            ))
-        }
-    }
-}
-
 #[cfg(test)]
 mod tests {
-    use super::*;
     use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target};
 
+    use super::*;
+
     #[test]
     fn applicable_when_found_an_import() {
         check_assist(
@@ -491,303 +290,4 @@ mod tests {
             ",
         );
     }
-
-    #[test]
-    fn not_applicable_for_imported_function() {
-        check_assist_not_applicable(
-            auto_import,
-            r"
-            pub mod test_mod {
-                pub fn test_function() {}
-            }
-
-            use test_mod::test_function;
-            fn main() {
-                test_function<|>
-            }
-            ",
-        );
-    }
-
-    #[test]
-    fn associated_struct_function() {
-        check_assist(
-            auto_import,
-            r"
-            mod test_mod {
-                pub struct TestStruct {}
-                impl TestStruct {
-                    pub fn test_function() {}
-                }
-            }
-
-            fn main() {
-                TestStruct::test_function<|>
-            }
-            ",
-            r"
-            use test_mod::TestStruct;
-
-            mod test_mod {
-                pub struct TestStruct {}
-                impl TestStruct {
-                    pub fn test_function() {}
-                }
-            }
-
-            fn main() {
-                TestStruct::test_function<|>
-            }
-            ",
-        );
-    }
-
-    #[test]
-    fn associated_struct_const() {
-        check_assist(
-            auto_import,
-            r"
-            mod test_mod {
-                pub struct TestStruct {}
-                impl TestStruct {
-                    const TEST_CONST: u8 = 42;
-                }
-            }
-
-            fn main() {
-                TestStruct::TEST_CONST<|>
-            }
-            ",
-            r"
-            use test_mod::TestStruct;
-
-            mod test_mod {
-                pub struct TestStruct {}
-                impl TestStruct {
-                    const TEST_CONST: u8 = 42;
-                }
-            }
-
-            fn main() {
-                TestStruct::TEST_CONST<|>
-            }
-            ",
-        );
-    }
-
-    #[test]
-    fn associated_trait_function() {
-        check_assist(
-            auto_import,
-            r"
-            mod test_mod {
-                pub trait TestTrait {
-                    fn test_function();
-                }
-                pub struct TestStruct {}
-                impl TestTrait for TestStruct {
-                    fn test_function() {}
-                }
-            }
-
-            fn main() {
-                test_mod::TestStruct::test_function<|>
-            }
-            ",
-            r"
-            use test_mod::TestTrait;
-
-            mod test_mod {
-                pub trait TestTrait {
-                    fn test_function();
-                }
-                pub struct TestStruct {}
-                impl TestTrait for TestStruct {
-                    fn test_function() {}
-                }
-            }
-
-            fn main() {
-                test_mod::TestStruct::test_function<|>
-            }
-            ",
-        );
-    }
-
-    #[test]
-    fn not_applicable_for_imported_trait_for_function() {
-        check_assist_not_applicable(
-            auto_import,
-            r"
-            mod test_mod {
-                pub trait TestTrait {
-                    fn test_function();
-                }
-                pub trait TestTrait2 {
-                    fn test_function();
-                }
-                pub enum TestEnum {
-                    One,
-                    Two,
-                }
-                impl TestTrait2 for TestEnum {
-                    fn test_function() {}
-                }
-                impl TestTrait for TestEnum {
-                    fn test_function() {}
-                }
-            }
-
-            use test_mod::TestTrait2;
-            fn main() {
-                test_mod::TestEnum::test_function<|>;
-            }
-            ",
-        )
-    }
-
-    #[test]
-    fn associated_trait_const() {
-        check_assist(
-            auto_import,
-            r"
-            mod test_mod {
-                pub trait TestTrait {
-                    const TEST_CONST: u8;
-                }
-                pub struct TestStruct {}
-                impl TestTrait for TestStruct {
-                    const TEST_CONST: u8 = 42;
-                }
-            }
-
-            fn main() {
-                test_mod::TestStruct::TEST_CONST<|>
-            }
-            ",
-            r"
-            use test_mod::TestTrait;
-
-            mod test_mod {
-                pub trait TestTrait {
-                    const TEST_CONST: u8;
-                }
-                pub struct TestStruct {}
-                impl TestTrait for TestStruct {
-                    const TEST_CONST: u8 = 42;
-                }
-            }
-
-            fn main() {
-                test_mod::TestStruct::TEST_CONST<|>
-            }
-            ",
-        );
-    }
-
-    #[test]
-    fn not_applicable_for_imported_trait_for_const() {
-        check_assist_not_applicable(
-            auto_import,
-            r"
-            mod test_mod {
-                pub trait TestTrait {
-                    const TEST_CONST: u8;
-                }
-                pub trait TestTrait2 {
-                    const TEST_CONST: f64;
-                }
-                pub enum TestEnum {
-                    One,
-                    Two,
-                }
-                impl TestTrait2 for TestEnum {
-                    const TEST_CONST: f64 = 42.0;
-                }
-                impl TestTrait for TestEnum {
-                    const TEST_CONST: u8 = 42;
-                }
-            }
-
-            use test_mod::TestTrait2;
-            fn main() {
-                test_mod::TestEnum::TEST_CONST<|>;
-            }
-            ",
-        )
-    }
-
-    #[test]
-    fn trait_method() {
-        check_assist(
-            auto_import,
-            r"
-            mod test_mod {
-                pub trait TestTrait {
-                    fn test_method(&self);
-                }
-                pub struct TestStruct {}
-                impl TestTrait for TestStruct {
-                    fn test_method(&self) {}
-                }
-            }
-
-            fn main() {
-                let test_struct = test_mod::TestStruct {};
-                test_struct.test_meth<|>od()
-            }
-            ",
-            r"
-            use test_mod::TestTrait;
-
-            mod test_mod {
-                pub trait TestTrait {
-                    fn test_method(&self);
-                }
-                pub struct TestStruct {}
-                impl TestTrait for TestStruct {
-                    fn test_method(&self) {}
-                }
-            }
-
-            fn main() {
-                let test_struct = test_mod::TestStruct {};
-                test_struct.test_meth<|>od()
-            }
-            ",
-        );
-    }
-
-    #[test]
-    fn not_applicable_for_imported_trait_for_method() {
-        check_assist_not_applicable(
-            auto_import,
-            r"
-            mod test_mod {
-                pub trait TestTrait {
-                    fn test_method(&self);
-                }
-                pub trait TestTrait2 {
-                    fn test_method(&self);
-                }
-                pub enum TestEnum {
-                    One,
-                    Two,
-                }
-                impl TestTrait2 for TestEnum {
-                    fn test_method(&self) {}
-                }
-                impl TestTrait for TestEnum {
-                    fn test_method(&self) {}
-                }
-            }
-
-            use test_mod::TestTrait2;
-            fn main() {
-                let one = test_mod::TestEnum::One;
-                one.test<|>_method();
-            }
-            ",
-        )
-    }
 }
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index a56b8ab042..4fb679f6dd 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -818,7 +818,7 @@ impl TypeParam {
     }
 }
 
-// FIXME: rename from `ImplBlock` to `Impl`
+// FIXME: rename to `ImplBlock`
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
 pub struct ImplBlock {
     pub(crate) id: ImplId,
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 2e598fdcdf..59c86bbfa4 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -758,7 +758,7 @@ pub fn handle_code_lens(
     // Gather runnables
     for runnable in world.analysis().runnables(file_id)? {
         let title = match &runnable.kind {
-            RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => "▶️\u{fe0e}Run Test",
+            RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => "▶️Run Test",
             RunnableKind::Bench { .. } => "Run Bench",
             RunnableKind::Bin => "Run",
         }
diff --git a/crates/ra_parser/src/grammar/params.rs b/crates/ra_parser/src/grammar/params.rs
index 272661b1da..ed4f93347a 100644
--- a/crates/ra_parser/src/grammar/params.rs
+++ b/crates/ra_parser/src/grammar/params.rs
@@ -114,11 +114,8 @@ fn value_parameter(p: &mut Parser, flavor: Flavor) {
         // test fn_pointer_param_ident_path
         // type Foo = fn(Bar::Baz);
         // type Qux = fn(baz: Bar::Baz);
-
-        // test fn_pointer_unnamed_arg
-        // type Foo = fn(_: bar);
         Flavor::FnPointer => {
-            if (p.at(IDENT) || p.at(UNDERSCORE)) && p.nth(1) == T![:] && !p.nth_at(1, T![::]) {
+            if p.at(IDENT) && p.nth(1) == T![:] && !p.nth_at(1, T![::]) {
                 patterns::pattern_single(p);
                 types::ascription(p);
             } else {
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml
index 653d5bd145..69edc3c66b 100644
--- a/crates/ra_project_model/Cargo.toml
+++ b/crates/ra_project_model/Cargo.toml
@@ -19,5 +19,3 @@ ra_cfg = { path = "../ra_cfg" }
 
 serde = { version = "1.0.89", features = ["derive"] }
 serde_json = "1.0.39"
-
-anyhow = "1.0.26"
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index 22d226a745..60cb8c1eb2 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -2,13 +2,14 @@
 
 use std::path::{Path, PathBuf};
 
-use anyhow::{Context, Result};
 use cargo_metadata::{CargoOpt, MetadataCommand};
 use ra_arena::{impl_arena_id, Arena, RawId};
 use ra_db::Edition;
 use rustc_hash::FxHashMap;
 use serde::Deserialize;
 
+use crate::Result;
+
 /// `CargoWorkspace` represents the logical structure of, well, a Cargo
 /// workspace. It pretty closely mirrors `cargo metadata` output.
 ///
@@ -170,9 +171,7 @@ impl CargoWorkspace {
         if let Some(parent) = cargo_toml.parent() {
             meta.current_dir(parent);
         }
-        let meta = meta.exec().with_context(|| {
-            format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display())
-        })?;
+        let meta = meta.exec().map_err(|e| format!("cargo metadata failed: {}", e))?;
         let mut pkg_by_id = FxHashMap::default();
         let mut packages = Arena::default();
         let mut targets = Arena::default();
@@ -182,9 +181,7 @@ impl CargoWorkspace {
         for meta_pkg in meta.packages {
             let cargo_metadata::Package { id, edition, name, manifest_path, .. } = meta_pkg;
             let is_member = ws_members.contains(&id);
-            let edition = edition
-                .parse::<Edition>()
-                .with_context(|| format!("Failed to parse edition {}", edition))?;
+            let edition = edition.parse::<Edition>()?;
             let pkg = packages.alloc(PackageData {
                 name,
                 manifest: manifest_path,
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index fef405b7f5..bc1d15406b 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -12,7 +12,6 @@ use std::{
     process::Command,
 };
 
-use anyhow::{bail, Context, Result};
 use ra_cfg::CfgOptions;
 use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId};
 use rustc_hash::FxHashMap;
@@ -24,6 +23,8 @@ pub use crate::{
     sysroot::Sysroot,
 };
 
+pub type Result<T> = ::std::result::Result<T, Box<dyn Error + Send + Sync>>;
+
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub struct CargoTomlNotFoundError(pub PathBuf);
 
@@ -80,36 +81,15 @@ impl ProjectWorkspace {
     ) -> Result<ProjectWorkspace> {
         match find_rust_project_json(path) {
             Some(json_path) => {
-                let file = File::open(&json_path)
-                    .with_context(|| format!("Failed to open json file {}", json_path.display()))?;
+                let file = File::open(json_path)?;
                 let reader = BufReader::new(file);
-                Ok(ProjectWorkspace::Json {
-                    project: from_reader(reader).with_context(|| {
-                        format!("Failed to deserialize json file {}", json_path.display())
-                    })?,
-                })
+                Ok(ProjectWorkspace::Json { project: from_reader(reader)? })
             }
             None => {
-                let cargo_toml = find_cargo_toml(path).with_context(|| {
-                    format!("Failed to find Cargo.toml for path {}", path.display())
-                })?;
-                let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features)
-                    .with_context(|| {
-                        format!(
-                            "Failed to read Cargo metadata from Cargo.toml file {}",
-                            cargo_toml.display()
-                        )
-                    })?;
-                let sysroot = if with_sysroot {
-                    Sysroot::discover(&cargo_toml).with_context(|| {
-                        format!(
-                            "Failed to find sysroot for Cargo.toml file {}",
-                            cargo_toml.display()
-                        )
-                    })?
-                } else {
-                    Sysroot::default()
-                };
+                let cargo_toml = find_cargo_toml(path)?;
+                let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features)?;
+                let sysroot =
+                    if with_sysroot { Sysroot::discover(&cargo_toml)? } else { Sysroot::default() };
                 Ok(ProjectWorkspace::Cargo { cargo, sysroot })
             }
         }
@@ -423,20 +403,11 @@ pub fn get_rustc_cfg_options() -> CfgOptions {
         }
     }
 
-    match (|| -> Result<String> {
+    match (|| -> Result<_> {
         // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here.
-        let output = Command::new("rustc")
-            .args(&["--print", "cfg", "-O"])
-            .output()
-            .context("Failed to get output from rustc --print cfg -O")?;
+        let output = Command::new("rustc").args(&["--print", "cfg", "-O"]).output()?;
         if !output.status.success() {
-            bail!(
-                "rustc --print cfg -O exited with exit code ({})",
-                output
-                    .status
-                    .code()
-                    .map_or(String::from("no exit code"), |code| format!("{}", code))
-            );
+            Err("failed to get rustc cfgs")?;
         }
         Ok(String::from_utf8(output.stdout)?)
     })() {
diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs
index 7b9cc899c0..a23265fc08 100644
--- a/crates/ra_project_model/src/sysroot.rs
+++ b/crates/ra_project_model/src/sysroot.rs
@@ -1,6 +1,5 @@
 //! FIXME: write short doc here
 
-use anyhow::{anyhow, bail, Context, Result};
 use std::{
     env,
     path::{Path, PathBuf},
@@ -9,6 +8,8 @@ use std::{
 
 use ra_arena::{impl_arena_id, Arena, RawId};
 
+use crate::Result;
+
 #[derive(Default, Debug, Clone)]
 pub struct Sysroot {
     crates: Arena<SysrootCrate, SysrootCrateData>,
@@ -50,7 +51,7 @@ impl Sysroot {
         let src = try_find_src_path(cargo_toml)?;
 
         if !src.exists() {
-            Err(anyhow!(
+            Err(format!(
                 "can't load standard library from sysroot\n\
                  {}\n\
                  (discovered via `rustc --print sysroot`)\n\
@@ -99,14 +100,9 @@ fn try_find_src_path(cargo_toml: &Path) -> Result<PathBuf> {
         .current_dir(cargo_toml.parent().unwrap())
         .args(&["--print", "sysroot"])
         .output()
-        .context("rustc --print sysroot failed")?;
+        .map_err(|e| format!("rustc --print sysroot failed: {}", e))?;
     if !rustc_output.status.success() {
-        match rustc_output.status.code() {
-            Some(code) => {
-                bail!("failed to locate sysroot: rustc --print sysroot exited with code {}", code)
-            }
-            None => bail!("failed to locate sysroot: rustc --print sysroot terminated by signal"),
-        };
+        Err("failed to locate sysroot")?;
     }
     let stdout = String::from_utf8(rustc_output.stdout)?;
     let sysroot_path = Path::new(stdout.trim());
diff --git a/editors/code/package.json b/editors/code/package.json
index f687eb8d45..12d32cef72 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -181,6 +181,9 @@
                 },
                 "rust-analyzer.excludeGlobs": {
                     "type": "array",
+                    "items": {
+                        "type": "string"
+                    },
                     "default": [],
                     "description": "Paths to exclude from analysis"
                 },
@@ -196,6 +199,9 @@
                 },
                 "rust-analyzer.cargo-watch.arguments": {
                     "type": "array",
+                    "items": {
+                        "type": "string"
+                    },
                     "description": "`cargo-watch` arguments. (e.g: `--features=\"shumway,pdf\"` will run as `cargo watch -x \"check --features=\"shumway,pdf\"\"` )",
                     "default": []
                 },
@@ -241,6 +247,7 @@
                 "rust-analyzer.maxInlayHintLength": {
                     "type": "number",
                     "default": 20,
+                    "exclusiveMinimum": 0,
                     "description": "Maximum length for inlay hints"
                 },
                 "rust-analyzer.cargoFeatures.noDefaultFeatures": {
@@ -255,6 +262,9 @@
                 },
                 "rust-analyzer.cargoFeatures.features": {
                     "type": "array",
+                    "items": {
+                        "type": "string"
+                    },
                     "default": [],
                     "description": "List of features to activate"
                 }
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 2e3d4aba2d..4484b21674 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -1,6 +1,6 @@
 import * as lc from 'vscode-languageclient';
+import * as vscode from 'vscode';
 
-import { window, workspace } from 'vscode';
 import { Config } from './config';
 import { ensureLanguageServerBinary } from './installation/language_server';
 
@@ -8,32 +8,34 @@ export async function createClient(config: Config): Promise<null | lc.LanguageCl
     // '.' Is the fallback if no folder is open
     // TODO?: Workspace folders support Uri's (eg: file://test.txt).
     // It might be a good idea to test if the uri points to a file.
-    const workspaceFolderPath = workspace.workspaceFolders?.[0]?.uri.fsPath ?? '.';
+    const workspaceFolderPath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? '.';
 
-    const raLspServerPath = await ensureLanguageServerBinary(config.langServerSource);
-    if (!raLspServerPath) return null;
+    const langServerPath = await ensureLanguageServerBinary(config.langServerBinarySource);
+    if (!langServerPath) return null;
 
     const run: lc.Executable = {
-        command: raLspServerPath,
+        command: langServerPath,
         options: { cwd: workspaceFolderPath },
     };
     const serverOptions: lc.ServerOptions = {
         run,
         debug: run,
     };
-    const traceOutputChannel = window.createOutputChannel(
+    const traceOutputChannel = vscode.window.createOutputChannel(
         'Rust Analyzer Language Server Trace',
     );
+    const cargoWatchOpts = config.cargoWatchOptions;
+
     const clientOptions: lc.LanguageClientOptions = {
         documentSelector: [{ scheme: 'file', language: 'rust' }],
         initializationOptions: {
             publishDecorations: true,
             lruCapacity: config.lruCapacity,
             maxInlayHintLength: config.maxInlayHintLength,
-            cargoWatchEnable: config.cargoWatchOptions.enable,
-            cargoWatchArgs: config.cargoWatchOptions.arguments,
-            cargoWatchCommand: config.cargoWatchOptions.command,
-            cargoWatchAllTargets: config.cargoWatchOptions.allTargets,
+            cargoWatchEnable: cargoWatchOpts.enable,
+            cargoWatchArgs: cargoWatchOpts.arguments,
+            cargoWatchCommand: cargoWatchOpts.command,
+            cargoWatchAllTargets: cargoWatchOpts.allTargets,
             excludeGlobs: config.excludeGlobs,
             useClientWatching: config.useClientWatching,
             featureFlags: config.featureFlags,
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 4188454368..8cd89e1196 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -16,45 +16,49 @@ export interface CargoFeatures {
     allFeatures: boolean;
     features: string[];
 }
-
 export class Config {
-    langServerSource!: null | BinarySource;
+    private static readonly rootSection = "rust-analyzer";
+    private static readonly requiresReloadOpts = [
+        "cargoFeatures",
+        "cargo-watch",
+    ]
+    .map(opt => `${Config.rootSection}.${opt}`);
+
+    private cfg!: vscode.WorkspaceConfiguration;
+
+    constructor(private readonly ctx: vscode.ExtensionContext) {
+        vscode.workspace.onDidChangeConfiguration(this.onConfigChange, this, ctx.subscriptions);
+        this.refreshConfig();
+    }
 
-    highlightingOn = true;
-    rainbowHighlightingOn = false;
-    enableEnhancedTyping = true;
-    lruCapacity: null | number = null;
-    displayInlayHints = true;
-    maxInlayHintLength: null | number = null;
-    excludeGlobs: string[] = [];
-    useClientWatching = true;
-    featureFlags: Record<string, boolean> = {};
-    // for internal use
-    withSysroot: null | boolean = null;
-    cargoWatchOptions: CargoWatchOptions = {
-        enable: true,
-        arguments: [],
-        command: '',
-        allTargets: true,
-    };
-    cargoFeatures: CargoFeatures = {
-        noDefaultFeatures: false,
-        allFeatures: true,
-        features: [],
-    };
 
-    private prevEnhancedTyping: null | boolean = null;
-    private prevCargoFeatures: null | CargoFeatures = null;
-    private prevCargoWatchOptions: null | CargoWatchOptions = null;
+    private refreshConfig() {
+        this.cfg = vscode.workspace.getConfiguration(Config.rootSection);
+        console.log("Using configuration:", this.cfg);
+    }
+
+    private async onConfigChange(event: vscode.ConfigurationChangeEvent) {
+        this.refreshConfig();
+
+        const requiresReloadOpt = Config.requiresReloadOpts.find(
+            opt => event.affectsConfiguration(opt)
+        );
+
+        if (!requiresReloadOpt) return;
 
-    constructor(ctx: vscode.ExtensionContext) {
-        vscode.workspace.onDidChangeConfiguration(_ => this.refresh(ctx), null, ctx.subscriptions);
-        this.refresh(ctx);
+        const userResponse = await vscode.window.showInformationMessage(
+            `Changing "${requiresReloadOpt}" requires a reload`,
+            "Reload now"
+        );
+
+        if (userResponse === "Reload now") {
+            vscode.commands.executeCommand("workbench.action.reloadWindow");
+        }
     }
 
-    private static expandPathResolving(path: string) {
-        if (path.startsWith('~/')) {
-            return path.replace('~', os.homedir());
+    private static replaceTildeWithHomeDir(path: string) {
+        if (path.startsWith("~/")) {
+            return os.homedir() + path.slice("~".length);
         }
         return path;
     }
@@ -64,17 +68,14 @@ export class Config {
      * `platform` on GitHub releases. (It is also stored under the same name when
      * downloaded by the extension).
      */
-    private static prebuiltLangServerFileName(
-        platform: NodeJS.Platform,
-        arch: string
-    ): null | string {
+    get prebuiltLangServerFileName(): null | string {
         // See possible `arch` values here:
         // https://nodejs.org/api/process.html#process_process_arch
 
-        switch (platform) {
+        switch (process.platform) {
 
             case "linux": {
-                switch (arch) {
+                switch (process.arch) {
                     case "arm":
                     case "arm64": return null;
 
@@ -97,28 +98,23 @@ export class Config {
         }
     }
 
-    private static langServerBinarySource(
-        ctx: vscode.ExtensionContext,
-        config: vscode.WorkspaceConfiguration
-    ): null | BinarySource {
-        const langServerPath = RA_LSP_DEBUG ?? config.get<null | string>("raLspServerPath");
+    get langServerBinarySource(): null | BinarySource {
+        const langServerPath = RA_LSP_DEBUG ?? this.cfg.get<null | string>("raLspServerPath");
 
         if (langServerPath) {
             return {
                 type: BinarySource.Type.ExplicitPath,
-                path: Config.expandPathResolving(langServerPath)
+                path: Config.replaceTildeWithHomeDir(langServerPath)
             };
         }
 
-        const prebuiltBinaryName = Config.prebuiltLangServerFileName(
-            process.platform, process.arch
-        );
+        const prebuiltBinaryName = this.prebuiltLangServerFileName;
 
         if (!prebuiltBinaryName) return null;
 
         return {
             type: BinarySource.Type.GithubRelease,
-            dir: ctx.globalStoragePath,
+            dir:  this.ctx.globalStoragePath,
             file: prebuiltBinaryName,
             repo: {
                 name: "rust-analyzer",
@@ -127,158 +123,35 @@ export class Config {
         };
     }
 
+    // We don't do runtime config validation here for simplicity. More on stackoverflow:
+    // https://stackoverflow.com/questions/60135780/what-is-the-best-way-to-type-check-the-configuration-for-vscode-extension
 
-    // FIXME: revisit the logic for `if (.has(...)) config.get(...)` set default
-    // values only in one place (i.e. remove default values from non-readonly members declarations)
-    private refresh(ctx: vscode.ExtensionContext) {
-        const config = vscode.workspace.getConfiguration('rust-analyzer');
-
-        let requireReloadMessage = null;
-
-        if (config.has('highlightingOn')) {
-            this.highlightingOn = config.get('highlightingOn') as boolean;
-        }
+    get highlightingOn()        { return this.cfg.get("highlightingOn") as boolean; }
+    get rainbowHighlightingOn() { return this.cfg.get("rainbowHighlightingOn") as boolean; }
+    get lruCapacity()           { return this.cfg.get("lruCapacity") as null | number; }
+    get displayInlayHints()     { return this.cfg.get("displayInlayHints") as boolean; }
+    get maxInlayHintLength()    { return this.cfg.get("maxInlayHintLength") as number; }
+    get excludeGlobs()          { return this.cfg.get("excludeGlobs") as string[]; }
+    get useClientWatching()     { return this.cfg.get("useClientWatching") as boolean; }
+    get featureFlags()          { return this.cfg.get("featureFlags") as Record<string, boolean>; }
 
-        if (config.has('rainbowHighlightingOn')) {
-            this.rainbowHighlightingOn = config.get(
-                'rainbowHighlightingOn',
-            ) as boolean;
-        }
-
-        if (config.has('enableEnhancedTyping')) {
-            this.enableEnhancedTyping = config.get(
-                'enableEnhancedTyping',
-            ) as boolean;
-
-            if (this.prevEnhancedTyping === null) {
-                this.prevEnhancedTyping = this.enableEnhancedTyping;
-            }
-        } else if (this.prevEnhancedTyping === null) {
-            this.prevEnhancedTyping = this.enableEnhancedTyping;
-        }
-
-        if (this.prevEnhancedTyping !== this.enableEnhancedTyping) {
-            requireReloadMessage =
-                'Changing enhanced typing setting requires a reload';
-            this.prevEnhancedTyping = this.enableEnhancedTyping;
-        }
-
-        this.langServerSource = Config.langServerBinarySource(ctx, config);
-
-        if (config.has('cargo-watch.enable')) {
-            this.cargoWatchOptions.enable = config.get<boolean>(
-                'cargo-watch.enable',
-                true,
-            );
-        }
-
-        if (config.has('cargo-watch.arguments')) {
-            this.cargoWatchOptions.arguments = config.get<string[]>(
-                'cargo-watch.arguments',
-                [],
-            );
-        }
-
-        if (config.has('cargo-watch.command')) {
-            this.cargoWatchOptions.command = config.get<string>(
-                'cargo-watch.command',
-                '',
-            );
-        }
-
-        if (config.has('cargo-watch.allTargets')) {
-            this.cargoWatchOptions.allTargets = config.get<boolean>(
-                'cargo-watch.allTargets',
-                true,
-            );
-        }
-
-        if (config.has('lruCapacity')) {
-            this.lruCapacity = config.get('lruCapacity') as number;
-        }
-
-        if (config.has('displayInlayHints')) {
-            this.displayInlayHints = config.get('displayInlayHints') as boolean;
-        }
-        if (config.has('maxInlayHintLength')) {
-            this.maxInlayHintLength = config.get(
-                'maxInlayHintLength',
-            ) as number;
-        }
-        if (config.has('excludeGlobs')) {
-            this.excludeGlobs = config.get('excludeGlobs') || [];
-        }
-        if (config.has('useClientWatching')) {
-            this.useClientWatching = config.get('useClientWatching') || true;
-        }
-        if (config.has('featureFlags')) {
-            this.featureFlags = config.get('featureFlags') || {};
-        }
-        if (config.has('withSysroot')) {
-            this.withSysroot = config.get('withSysroot') || false;
-        }
-
-        if (config.has('cargoFeatures.noDefaultFeatures')) {
-            this.cargoFeatures.noDefaultFeatures = config.get(
-                'cargoFeatures.noDefaultFeatures',
-                false,
-            );
-        }
-        if (config.has('cargoFeatures.allFeatures')) {
-            this.cargoFeatures.allFeatures = config.get(
-                'cargoFeatures.allFeatures',
-                true,
-            );
-        }
-        if (config.has('cargoFeatures.features')) {
-            this.cargoFeatures.features = config.get(
-                'cargoFeatures.features',
-                [],
-            );
-        }
-
-        if (
-            this.prevCargoFeatures !== null &&
-            (this.cargoFeatures.allFeatures !==
-                this.prevCargoFeatures.allFeatures ||
-                this.cargoFeatures.noDefaultFeatures !==
-                this.prevCargoFeatures.noDefaultFeatures ||
-                this.cargoFeatures.features.length !==
-                this.prevCargoFeatures.features.length ||
-                this.cargoFeatures.features.some(
-                    (v, i) => v !== this.prevCargoFeatures!.features[i],
-                ))
-        ) {
-            requireReloadMessage = 'Changing cargo features requires a reload';
-        }
-        this.prevCargoFeatures = { ...this.cargoFeatures };
-
-        if (this.prevCargoWatchOptions !== null) {
-            const changed =
-                this.cargoWatchOptions.enable !== this.prevCargoWatchOptions.enable ||
-                this.cargoWatchOptions.command !== this.prevCargoWatchOptions.command ||
-                this.cargoWatchOptions.allTargets !== this.prevCargoWatchOptions.allTargets ||
-                this.cargoWatchOptions.arguments.length !== this.prevCargoWatchOptions.arguments.length ||
-                this.cargoWatchOptions.arguments.some(
-                    (v, i) => v !== this.prevCargoWatchOptions!.arguments[i],
-                );
-            if (changed) {
-                requireReloadMessage = 'Changing cargo-watch options requires a reload';
-            }
-        }
-        this.prevCargoWatchOptions = { ...this.cargoWatchOptions };
+    get cargoWatchOptions(): CargoWatchOptions {
+        return {
+            enable:     this.cfg.get("cargo-watch.enable") as boolean,
+            arguments:  this.cfg.get("cargo-watch.arguments") as string[],
+            allTargets: this.cfg.get("cargo-watch.allTargets") as boolean,
+            command:    this.cfg.get("cargo-watch.command") as string,
+        };
+    }
 
-        if (requireReloadMessage !== null) {
-            const reloadAction = 'Reload now';
-            vscode.window
-                .showInformationMessage(requireReloadMessage, reloadAction)
-                .then(selectedAction => {
-                    if (selectedAction === reloadAction) {
-                        vscode.commands.executeCommand(
-                            'workbench.action.reloadWindow',
-                        );
-                    }
-                });
-        }
+    get cargoFeatures(): CargoFeatures {
+        return {
+            noDefaultFeatures: this.cfg.get("cargoFeatures.noDefaultFeatures") as boolean,
+            allFeatures:       this.cfg.get("cargoFeatures.allFeatures") as boolean,
+            features:          this.cfg.get("cargoFeatures.features") as string[],
+        };
     }
+
+    // for internal use
+    get withSysroot() { return this.cfg.get("withSysroot", false); }
 }
diff --git a/editors/code/src/status_display.ts b/editors/code/src/status_display.ts
index 51dbf388bb..993e79d703 100644
--- a/editors/code/src/status_display.ts
+++ b/editors/code/src/status_display.ts
@@ -66,9 +66,9 @@ class StatusDisplay implements Disposable {
 
     refreshLabel() {
         if (this.packageName) {
-            this.statusBarItem!.text = `${spinnerFrames[this.i]} cargo ${this.command} [${this.packageName}]`;
+            this.statusBarItem.text = `${spinnerFrames[this.i]} cargo ${this.command} [${this.packageName}]`;
         } else {
-            this.statusBarItem!.text = `${spinnerFrames[this.i]} cargo ${this.command}`;
+            this.statusBarItem.text = `${spinnerFrames[this.i]} cargo ${this.command}`;
         }
     }
 


Test patch:
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0157_fn_pointer_unnamed_arg.rs b/crates/ra_syntax/test_data/parser/inline/ok/0157_fn_pointer_unnamed_arg.rs
deleted file mode 100644
index 1ebbe5b035..0000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0157_fn_pointer_unnamed_arg.rs
+++ /dev/null
@@ -1,1 +0,0 @@
-type Foo = fn(_: bar);
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0157_fn_pointer_unnamed_arg.txt b/crates/ra_syntax/test_data/parser/inline/ok/0157_fn_pointer_unnamed_arg.txt
deleted file mode 100644
index 52d8f21a47..0000000000
--- a/crates/ra_syntax/test_data/parser/inline/ok/0157_fn_pointer_unnamed_arg.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-SOURCE_FILE@[0; 23)
-  TYPE_ALIAS_DEF@[0; 22)
-    TYPE_KW@[0; 4) "type"
-    WHITESPACE@[4; 5) " "
-    NAME@[5; 8)
-      IDENT@[5; 8) "Foo"
-    WHITESPACE@[8; 9) " "
-    EQ@[9; 10) "="
-    WHITESPACE@[10; 11) " "
-    FN_POINTER_TYPE@[11; 21)
-      FN_KW@[11; 13) "fn"
-      PARAM_LIST@[13; 21)
-        L_PAREN@[13; 14) "("
-        PARAM@[14; 20)
-          PLACEHOLDER_PAT@[14; 15)
-            UNDERSCORE@[14; 15) "_"
-          COLON@[15; 16) ":"
-          WHITESPACE@[16; 17) " "
-          PATH_TYPE@[17; 20)
-            PATH@[17; 20)
-              PATH_SEGMENT@[17; 20)
-                NAME_REF@[17; 20)
-                  IDENT@[17; 20) "bar"
-        R_PAREN@[20; 21) ")"
-    SEMI@[21; 22) ";"
-  WHITESPACE@[22; 23) "\n"