diff --git a/Cargo.lock b/Cargo.lock
index 4ae611a6bc0..82f910c1b70 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -945,6 +945,7 @@ dependencies = [
 name = "ra_cli"
 version = "0.1.0"
 dependencies = [
+ "anyhow",
  "crossbeam-channel",
  "env_logger",
  "itertools",
diff --git a/crates/ra_cli/Cargo.toml b/crates/ra_cli/Cargo.toml
index 03494a809fd..ce88a76b1d7 100644
--- a/crates/ra_cli/Cargo.toml
+++ b/crates/ra_cli/Cargo.toml
@@ -13,6 +13,7 @@ log = "0.4.5"
 pico-args = "0.3.0"
 rand = { version = "0.7.0", features = ["small_rng"] }
 rustc-hash = "1.0"
+anyhow = "1.0"
 
 hir = { path = "../ra_hir", package = "ra_hir" }
 hir_def = { path = "../ra_hir_def", package = "ra_hir_def" }
diff --git a/crates/ra_cli/src/analysis_bench.rs b/crates/ra_cli/src/analysis_bench.rs
index 3f10ed4002b..91fc55fe200 100644
--- a/crates/ra_cli/src/analysis_bench.rs
+++ b/crates/ra_cli/src/analysis_bench.rs
@@ -1,47 +1,17 @@
 //! FIXME: write short doc here
 
-use std::{
-    path::{Path, PathBuf},
-    str::FromStr,
-    sync::Arc,
-    time::Instant,
-};
+use std::{path::Path, sync::Arc, time::Instant};
 
+use anyhow::format_err;
 use ra_db::{
     salsa::{Database, Durability},
     FileId, SourceDatabaseExt,
 };
 use ra_ide::{Analysis, AnalysisChange, AnalysisHost, FilePosition, LineCol};
 
-use crate::{load_cargo::load_cargo, Result};
+use crate::{load_cargo::load_cargo, BenchWhat, Result, Verbosity};
 
-pub(crate) struct Position {
-    path: PathBuf,
-    line: u32,
-    column: u32,
-}
-
-impl FromStr for Position {
-    type Err = Box<dyn std::error::Error + Send + Sync>;
-    fn from_str(s: &str) -> Result<Self> {
-        let (path_line, column) = rsplit_at_char(s, ':')?;
-        let (path, line) = rsplit_at_char(path_line, ':')?;
-        Ok(Position { path: path.into(), line: line.parse()?, column: column.parse()? })
-    }
-}
-
-fn rsplit_at_char(s: &str, c: char) -> Result<(&str, &str)> {
-    let idx = s.rfind(':').ok_or_else(|| format!("no `{}` in {}", c, s))?;
-    Ok((&s[..idx], &s[idx + 1..]))
-}
-
-pub(crate) enum Op {
-    Highlight { path: PathBuf },
-    Complete(Position),
-    GotoDef(Position),
-}
-
-pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> {
+pub(crate) fn run(verbosity: Verbosity, path: &Path, what: BenchWhat) -> Result<()> {
     ra_prof::init();
 
     let start = Instant::now();
@@ -51,9 +21,9 @@ pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> {
     eprintln!("{:?}\n", start.elapsed());
 
     let file_id = {
-        let path = match &op {
-            Op::Highlight { path } => path,
-            Op::Complete(pos) | Op::GotoDef(pos) => &pos.path,
+        let path = match &what {
+            BenchWhat::Highlight { path } => path,
+            BenchWhat::Complete(pos) | BenchWhat::GotoDef(pos) => &pos.path,
         };
         let path = std::env::current_dir()?.join(path).canonicalize()?;
         roots
@@ -70,22 +40,22 @@ pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> {
                 }
                 None
             })
-            .ok_or_else(|| format!("Can't find {:?}", path))?
+            .ok_or_else(|| format_err!("Can't find {}", path.display()))?
     };
 
-    match &op {
-        Op::Highlight { .. } => {
+    match &what {
+        BenchWhat::Highlight { .. } => {
             let res = do_work(&mut host, file_id, |analysis| {
                 analysis.diagnostics(file_id).unwrap();
                 analysis.highlight_as_html(file_id, false).unwrap()
             });
-            if verbose {
+            if verbosity.is_verbose() {
                 println!("\n{}", res);
             }
         }
-        Op::Complete(pos) | Op::GotoDef(pos) => {
-            let is_completion = match op {
-                Op::Complete(..) => true,
+        BenchWhat::Complete(pos) | BenchWhat::GotoDef(pos) => {
+            let is_completion = match what {
+                BenchWhat::Complete(..) => true,
                 _ => false,
             };
 
@@ -98,13 +68,13 @@ pub(crate) fn run(verbose: bool, path: &Path, op: Op) -> Result<()> {
             if is_completion {
                 let res =
                     do_work(&mut host, file_id, |analysis| analysis.completions(file_postion));
-                if verbose {
+                if verbosity.is_verbose() {
                     println!("\n{:#?}", res);
                 }
             } else {
                 let res =
                     do_work(&mut host, file_id, |analysis| analysis.goto_definition(file_postion));
-                if verbose {
+                if verbosity.is_verbose() {
                     println!("\n{:#?}", res);
                 }
             }
diff --git a/crates/ra_cli/src/load_cargo.rs b/crates/ra_cli/src/load_cargo.rs
index 2d6433f184a..b9a4e6abad3 100644
--- a/crates/ra_cli/src/load_cargo.rs
+++ b/crates/ra_cli/src/load_cargo.rs
@@ -1,8 +1,6 @@
 //! FIXME: write short doc here
 
-use std::{collections::HashSet, error::Error, path::Path};
-
-use rustc_hash::FxHashMap;
+use std::{collections::HashSet, path::Path};
 
 use crossbeam_channel::{unbounded, Receiver};
 use ra_db::{CrateGraph, FileId, SourceRootId};
@@ -10,8 +8,9 @@ use ra_ide::{AnalysisChange, AnalysisHost, FeatureFlags};
 use ra_project_model::{get_rustc_cfg_options, PackageRoot, ProjectWorkspace};
 use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch};
 use ra_vfs_glob::RustPackageFilterBuilder;
+use rustc_hash::FxHashMap;
 
-type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>;
+use anyhow::Result;
 
 fn vfs_file_to_id(f: ra_vfs::VfsFile) -> FileId {
     FileId(f.0)
diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs
index 4a428faff89..4cf062f472d 100644
--- a/crates/ra_cli/src/main.rs
+++ b/crates/ra_cli/src/main.rs
@@ -5,14 +5,82 @@ mod analysis_stats;
 mod analysis_bench;
 mod progress_report;
 
-use std::{error::Error, fmt::Write, io::Read};
+use std::{fmt::Write, io::Read, path::PathBuf, str::FromStr};
 
 use pico_args::Arguments;
 use ra_ide::{file_structure, Analysis};
 use ra_prof::profile;
 use ra_syntax::{AstNode, SourceFile};
 
-type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>;
+use anyhow::{bail, format_err, Result};
+
+fn main() -> Result<()> {
+    env_logger::try_init()?;
+
+    let command = match Command::from_env_args()? {
+        Ok(it) => it,
+        Err(HelpPrinted) => return Ok(()),
+    };
+    match command {
+        Command::Parse { no_dump } => {
+            let _p = profile("parsing");
+            let file = file()?;
+            if !no_dump {
+                println!("{:#?}", file.syntax());
+            }
+            std::mem::forget(file);
+        }
+        Command::Symbols => {
+            let file = file()?;
+            for s in file_structure(&file) {
+                println!("{:?}", s);
+            }
+        }
+        Command::Highlight { rainbow } => {
+            let (analysis, file_id) = Analysis::from_single_file(read_stdin()?);
+            let html = analysis.highlight_as_html(file_id, rainbow).unwrap();
+            println!("{}", html);
+        }
+        Command::Stats { verbosity, randomize, memory_usage, only, with_deps, path } => {
+            analysis_stats::run(
+                verbosity,
+                memory_usage,
+                path.as_ref(),
+                only.as_ref().map(String::as_ref),
+                with_deps,
+                randomize,
+            )?;
+        }
+        Command::Bench { verbosity, path, what } => {
+            analysis_bench::run(verbosity, path.as_ref(), what)?;
+        }
+    }
+
+    Ok(())
+}
+
+enum Command {
+    Parse {
+        no_dump: bool,
+    },
+    Symbols,
+    Highlight {
+        rainbow: bool,
+    },
+    Stats {
+        verbosity: Verbosity,
+        randomize: bool,
+        memory_usage: bool,
+        only: Option<String>,
+        with_deps: bool,
+        path: PathBuf,
+    },
+    Bench {
+        verbosity: Verbosity,
+        path: PathBuf,
+        what: BenchWhat,
+    },
+}
 
 #[derive(Clone, Copy)]
 pub enum Verbosity {
@@ -37,17 +105,57 @@ impl Verbosity {
     }
 }
 
-fn main() -> Result<()> {
-    env_logger::try_init()?;
+enum BenchWhat {
+    Highlight { path: PathBuf },
+    Complete(Position),
+    GotoDef(Position),
+}
 
-    let mut matches = Arguments::from_env();
-    let subcommand = matches.subcommand()?.unwrap_or_default();
+pub(crate) struct Position {
+    path: PathBuf,
+    line: u32,
+    column: u32,
+}
 
-    match subcommand.as_str() {
-        "parse" => {
-            if matches.contains(["-h", "--help"]) {
-                eprintln!(
-                    "\
+impl FromStr for Position {
+    type Err = anyhow::Error;
+    fn from_str(s: &str) -> Result<Self> {
+        let (path_line, column) = rsplit_at_char(s, ':')?;
+        let (path, line) = rsplit_at_char(path_line, ':')?;
+        Ok(Position { path: path.into(), line: line.parse()?, column: column.parse()? })
+    }
+}
+
+fn rsplit_at_char(s: &str, c: char) -> Result<(&str, &str)> {
+    let idx = s.rfind(c).ok_or_else(|| format_err!("no `{}` in {}", c, s))?;
+    Ok((&s[..idx], &s[idx + 1..]))
+}
+
+struct HelpPrinted;
+
+impl Command {
+    fn from_env_args() -> Result<Result<Command, HelpPrinted>> {
+        let mut matches = Arguments::from_env();
+        let subcommand = matches.subcommand()?.unwrap_or_default();
+
+        let verbosity = match (
+            matches.contains(["-vv", "--spammy"]),
+            matches.contains(["-v", "--verbose"]),
+            matches.contains(["-q", "--quiet"]),
+        ) {
+            (true, _, true) => bail!("Invalid flags: -q conflicts with -vv"),
+            (true, _, false) => Verbosity::Spammy,
+            (false, false, false) => Verbosity::Normal,
+            (false, false, true) => Verbosity::Quiet,
+            (false, true, false) => Verbosity::Verbose,
+            (false, true, true) => bail!("Invalid flags: -q conflicts with -v"),
+        };
+
+        let command = match subcommand.as_str() {
+            "parse" => {
+                if matches.contains(["-h", "--help"]) {
+                    eprintln!(
+                        "\
 ra-cli-parse
 
 USAGE:
@@ -56,24 +164,18 @@ USAGE:
 FLAGS:
     -h, --help       Prints help inforamtion
         --no-dump"
-                );
-                return Ok(());
-            }
+                    );
+                    return Ok(Err(HelpPrinted));
+                }
 
-            let no_dump = matches.contains("--no-dump");
-            matches.finish().or_else(handle_extra_flags)?;
-
-            let _p = profile("parsing");
-            let file = file()?;
-            if !no_dump {
-                println!("{:#?}", file.syntax());
+                let no_dump = matches.contains("--no-dump");
+                matches.finish().or_else(handle_extra_flags)?;
+                Command::Parse { no_dump }
             }
-            std::mem::forget(file);
-        }
-        "symbols" => {
-            if matches.contains(["-h", "--help"]) {
-                eprintln!(
-                    "\
+            "symbols" => {
+                if matches.contains(["-h", "--help"]) {
+                    eprintln!(
+                        "\
 ra-cli-symbols
 
 USAGE:
@@ -81,21 +183,18 @@ USAGE:
 
 FLAGS:
     -h, --help    Prints help inforamtion"
-                );
-                return Ok(());
-            }
+                    );
+                    return Ok(Err(HelpPrinted));
+                }
 
-            matches.finish().or_else(handle_extra_flags)?;
+                matches.finish().or_else(handle_extra_flags)?;
 
-            let file = file()?;
-            for s in file_structure(&file) {
-                println!("{:?}", s);
+                Command::Symbols
             }
-        }
-        "highlight" => {
-            if matches.contains(["-h", "--help"]) {
-                eprintln!(
-                    "\
+            "highlight" => {
+                if matches.contains(["-h", "--help"]) {
+                    eprintln!(
+                        "\
 ra-cli-highlight
 
 USAGE:
@@ -104,21 +203,18 @@ USAGE:
 FLAGS:
     -h, --help       Prints help information
     -r, --rainbow"
-                );
-                return Ok(());
+                    );
+                    return Ok(Err(HelpPrinted));
+                }
+
+                let rainbow = matches.contains(["-r", "--rainbow"]);
+                matches.finish().or_else(handle_extra_flags)?;
+                Command::Highlight { rainbow }
             }
-
-            let rainbow_opt = matches.contains(["-r", "--rainbow"]);
-            matches.finish().or_else(handle_extra_flags)?;
-
-            let (analysis, file_id) = Analysis::from_single_file(read_stdin()?);
-            let html = analysis.highlight_as_html(file_id, rainbow_opt).unwrap();
-            println!("{}", html);
-        }
-        "analysis-stats" => {
-            if matches.contains(["-h", "--help"]) {
-                eprintln!(
-                    "\
+            "analysis-stats" => {
+                if matches.contains(["-h", "--help"]) {
+                    eprintln!(
+                        "\
 ra-cli-analysis-stats
 
 USAGE:
@@ -135,47 +231,28 @@ OPTIONS:
 
 ARGS:
     <PATH>"
-                );
-                return Ok(());
-            }
-
-            let verbosity = match (
-                matches.contains(["-vv", "--spammy"]),
-                matches.contains(["-v", "--verbose"]),
-                matches.contains(["-q", "--quiet"]),
-            ) {
-                (true, _, true) => Err("Invalid flags: -q conflicts with -vv")?,
-                (true, _, false) => Verbosity::Spammy,
-                (false, false, false) => Verbosity::Normal,
-                (false, false, true) => Verbosity::Quiet,
-                (false, true, false) => Verbosity::Verbose,
-                (false, true, true) => Err("Invalid flags: -q conflicts with -v")?,
-            };
-            let randomize = matches.contains("--randomize");
-            let memory_usage = matches.contains("--memory-usage");
-            let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?;
-            let with_deps: bool = matches.contains("--with-deps");
-            let path = {
-                let mut trailing = matches.free()?;
-                if trailing.len() != 1 {
-                    Err("Invalid flags")?;
+                    );
+                    return Ok(Err(HelpPrinted));
                 }
-                trailing.pop().unwrap()
-            };
 
-            analysis_stats::run(
-                verbosity,
-                memory_usage,
-                path.as_ref(),
-                only.as_ref().map(String::as_ref),
-                with_deps,
-                randomize,
-            )?;
-        }
-        "analysis-bench" => {
-            if matches.contains(["-h", "--help"]) {
-                eprintln!(
-                    "\
+                let randomize = matches.contains("--randomize");
+                let memory_usage = matches.contains("--memory-usage");
+                let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?;
+                let with_deps: bool = matches.contains("--with-deps");
+                let path = {
+                    let mut trailing = matches.free()?;
+                    if trailing.len() != 1 {
+                        bail!("Invalid flags");
+                    }
+                    trailing.pop().unwrap().into()
+                };
+
+                Command::Stats { verbosity, randomize, memory_usage, only, with_deps, path }
+            }
+            "analysis-bench" => {
+                if matches.contains(["-h", "--help"]) {
+                    eprintln!(
+                        "\
 ra_cli-analysis-bench
 
 USAGE:
@@ -191,29 +268,27 @@ OPTIONS:
 
 ARGS:
     <PATH>    Project to analyse"
-                );
-                return Ok(());
+                    );
+                    return Ok(Err(HelpPrinted));
+                }
+
+                let path: PathBuf = matches.opt_value_from_str("--path")?.unwrap_or_default();
+                let highlight_path: Option<String> = matches.opt_value_from_str("--highlight")?;
+                let complete_path: Option<Position> = matches.opt_value_from_str("--complete")?;
+                let goto_def_path: Option<Position> = matches.opt_value_from_str("--goto-def")?;
+                let what = match (highlight_path, complete_path, goto_def_path) {
+                    (Some(path), None, None) => BenchWhat::Highlight { path: path.into() },
+                    (None, Some(position), None) => BenchWhat::Complete(position),
+                    (None, None, Some(position)) => BenchWhat::GotoDef(position),
+                    _ => panic!(
+                        "exactly one of  `--highlight`, `--complete` or `--goto-def` must be set"
+                    ),
+                };
+                Command::Bench { verbosity, path, what }
             }
-
-            let verbose = matches.contains(["-v", "--verbose"]);
-            let path: String = matches.opt_value_from_str("--path")?.unwrap_or_default();
-            let highlight_path: Option<String> = matches.opt_value_from_str("--highlight")?;
-            let complete_path: Option<String> = matches.opt_value_from_str("--complete")?;
-            let goto_def_path: Option<String> = matches.opt_value_from_str("--goto-def")?;
-            let op = match (highlight_path, complete_path, goto_def_path) {
-                (Some(path), None, None) => analysis_bench::Op::Highlight { path: path.into() },
-                (None, Some(position), None) => analysis_bench::Op::Complete(position.parse()?),
-                (None, None, Some(position)) => analysis_bench::Op::GotoDef(position.parse()?),
-                _ => panic!(
-                    "exactly one of  `--highlight`, `--complete` or `--goto-def` must be set"
-                ),
-            };
-            matches.finish().or_else(handle_extra_flags)?;
-
-            analysis_bench::run(verbose, path.as_ref(), op)?;
-        }
-        _ => eprintln!(
-            "\
+            _ => {
+                eprintln!(
+                    "\
 ra-cli
 
 USAGE:
@@ -228,9 +303,12 @@ SUBCOMMANDS:
     highlight
     parse
     symbols"
-        ),
+                );
+                return Ok(Err(HelpPrinted));
+            }
+        };
+        Ok(Ok(command))
     }
-    Ok(())
 }
 
 fn handle_extra_flags(e: pico_args::Error) -> Result<()> {
@@ -240,9 +318,9 @@ fn handle_extra_flags(e: pico_args::Error) -> Result<()> {
             write!(&mut invalid_flags, "{}, ", flag)?;
         }
         let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2);
-        Err(format!("Invalid flags: {}", invalid_flags).into())
+        bail!("Invalid flags: {}", invalid_flags);
     } else {
-        Err(e.to_string().into())
+        bail!(e);
     }
 }
 
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml
index fdf81ed87c9..0066929c0c1 100644
--- a/crates/ra_lsp_server/Cargo.toml
+++ b/crates/ra_lsp_server/Cargo.toml
@@ -8,27 +8,28 @@ authors = ["rust-analyzer developers"]
 doctest = false
 
 [dependencies]
-threadpool = "1.7.1"
-relative-path = "1.0.0"
-serde_json = "1.0.34"
-serde = { version = "1.0.83", features = ["derive"] }
 crossbeam-channel = "0.4"
+either = "1.5"
+env_logger = { version = "0.7.1", default-features = false }
+jod-thread = "0.1.0"
 log = "0.4.3"
 lsp-types = { version = "0.70.0", features = ["proposed"] }
-rustc-hash = "1.0"
 parking_lot = "0.10.0"
-jod-thread = "0.1.0"
-ra_vfs = "0.5.0"
+relative-path = "1.0.0"
+rustc-hash = "1.0"
+serde = { version = "1.0.83", features = ["derive"] }
+serde_json = "1.0.34"
+threadpool = "1.7.1"
+
+lsp-server = "0.3.0"
+ra_cargo_watch = { path = "../ra_cargo_watch" }
+ra_ide = { path = "../ra_ide" }
+ra_prof = { path = "../ra_prof" }
+ra_project_model = { path = "../ra_project_model" }
 ra_syntax = { path = "../ra_syntax" }
 ra_text_edit = { path = "../ra_text_edit" }
-ra_ide = { path = "../ra_ide" }
-lsp-server = "0.3.0"
-ra_project_model = { path = "../ra_project_model" }
-ra_prof = { path = "../ra_prof" }
+ra_vfs = "0.5.0"
 ra_vfs_glob = { path = "../ra_vfs_glob" }
-env_logger = { version = "0.7.1", default-features = false }
-ra_cargo_watch = { path = "../ra_cargo_watch" }
-either = "1.5"
 
 [target.'cfg(windows)'.dependencies]
 winapi = "0.3"