Repository: rust-analyzer/rust-analyzer
Pull request #5029 (2020-06-24 11:49:03)

Be more explicit about absolute paths at various places


bors r+
🤖

bors[bot]: Merge conflict.

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 9ea1765cb5..91a9325490 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1174,6 +1174,7 @@ dependencies = [
  "anyhow",
  "cargo_metadata",
  "log",
+ "paths",
  "ra_arena",
  "ra_cfg",
  "ra_db",
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs
index 45b19c45a0..1b259682d9 100644
--- a/crates/paths/src/lib.rs
+++ b/crates/paths/src/lib.rs
@@ -28,6 +28,12 @@ impl AsRef<Path> for AbsPathBuf {
     }
 }
 
+impl AsRef<AbsPath> for AbsPathBuf {
+    fn as_ref(&self) -> &AbsPath {
+        self.as_path()
+    }
+}
+
 impl TryFrom<PathBuf> for AbsPathBuf {
     type Error = PathBuf;
     fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> {
@@ -45,9 +51,19 @@ impl TryFrom<&str> for AbsPathBuf {
     }
 }
 
+impl PartialEq<AbsPath> for AbsPathBuf {
+    fn eq(&self, other: &AbsPath) -> bool {
+        self.as_path() == other
+    }
+}
+
 impl AbsPathBuf {
+    pub fn assert(path: PathBuf) -> AbsPathBuf {
+        AbsPathBuf::try_from(path)
+            .unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display()))
+    }
     pub fn as_path(&self) -> &AbsPath {
-        AbsPath::new_unchecked(self.0.as_path())
+        AbsPath::assert(self.0.as_path())
     }
     pub fn pop(&mut self) -> bool {
         self.0.pop()
@@ -77,15 +93,19 @@ impl<'a> TryFrom<&'a Path> for &'a AbsPath {
         if !path.is_absolute() {
             return Err(path);
         }
-        Ok(AbsPath::new_unchecked(path))
+        Ok(AbsPath::assert(path))
     }
 }
 
 impl AbsPath {
-    fn new_unchecked(path: &Path) -> &AbsPath {
+    pub fn assert(path: &Path) -> &AbsPath {
+        assert!(path.is_absolute());
         unsafe { &*(path as *const Path as *const AbsPath) }
     }
 
+    pub fn parent(&self) -> Option<&AbsPath> {
+        self.0.parent().map(AbsPath::assert)
+    }
     pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf {
         self.as_ref().join(path).try_into().unwrap()
     }
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml
index e4a60f4c02..8189470144 100644
--- a/crates/ra_project_model/Cargo.toml
+++ b/crates/ra_project_model/Cargo.toml
@@ -18,6 +18,7 @@ ra_cfg = { path = "../ra_cfg" }
 ra_db = { path = "../ra_db" }
 ra_toolchain = { path = "../ra_toolchain" }
 ra_proc_macro =  { path = "../ra_proc_macro" }
+paths =  { path = "../paths" }
 
 serde = { version = "1.0.106", features = ["derive"] }
 serde_json = "1.0.48"
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index 4b7444039e..8ce63ab3c1 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -1,14 +1,10 @@
 //! FIXME: write short doc here
 
-use std::{
-    ffi::OsStr,
-    ops,
-    path::{Path, PathBuf},
-    process::Command,
-};
+use std::{ffi::OsStr, ops, path::Path, process::Command};
 
 use anyhow::{Context, Result};
 use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
+use paths::{AbsPath, AbsPathBuf};
 use ra_arena::{Arena, Idx};
 use ra_db::Edition;
 use rustc_hash::FxHashMap;
@@ -20,11 +16,14 @@ use rustc_hash::FxHashMap;
 /// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
 /// while this knows about `Packages` & `Targets`: purely cargo-related
 /// concepts.
+///
+/// We use absolute paths here, `cargo metadata` guarantees to always produce
+/// abs paths.
 #[derive(Debug, Clone)]
 pub struct CargoWorkspace {
     packages: Arena<PackageData>,
     targets: Arena<TargetData>,
-    workspace_root: PathBuf,
+    workspace_root: AbsPathBuf,
 }
 
 impl ops::Index<Package> for CargoWorkspace {
@@ -80,15 +79,15 @@ pub type Target = Idx<TargetData>;
 pub struct PackageData {
     pub version: String,
     pub name: String,
-    pub manifest: PathBuf,
+    pub manifest: AbsPathBuf,
     pub targets: Vec<Target>,
     pub is_member: bool,
     pub dependencies: Vec<PackageDependency>,
     pub edition: Edition,
     pub features: Vec<String>,
     pub cfgs: Vec<String>,
-    pub out_dir: Option<PathBuf>,
-    pub proc_macro_dylib_path: Option<PathBuf>,
+    pub out_dir: Option<AbsPathBuf>,
+    pub proc_macro_dylib_path: Option<AbsPathBuf>,
 }
 
 #[derive(Debug, Clone)]
@@ -101,7 +100,7 @@ pub struct PackageDependency {
 pub struct TargetData {
     pub package: Package,
     pub name: String,
-    pub root: PathBuf,
+    pub root: AbsPathBuf,
     pub kind: TargetKind,
     pub is_proc_macro: bool,
 }
@@ -135,7 +134,7 @@ impl TargetKind {
 }
 
 impl PackageData {
-    pub fn root(&self) -> &Path {
+    pub fn root(&self) -> &AbsPath {
         self.manifest.parent().unwrap()
     }
 }
@@ -193,7 +192,7 @@ impl CargoWorkspace {
             let pkg = packages.alloc(PackageData {
                 name,
                 version: version.to_string(),
-                manifest: manifest_path,
+                manifest: AbsPathBuf::assert(manifest_path),
                 targets: Vec::new(),
                 is_member,
                 edition,
@@ -210,7 +209,7 @@ impl CargoWorkspace {
                 let tgt = targets.alloc(TargetData {
                     package: pkg,
                     name: meta_tgt.name,
-                    root: meta_tgt.src_path.clone(),
+                    root: AbsPathBuf::assert(meta_tgt.src_path.clone()),
                     kind: TargetKind::new(meta_tgt.kind.as_slice()),
                     is_proc_macro,
                 });
@@ -246,16 +245,17 @@ impl CargoWorkspace {
             packages[source].features.extend(node.features);
         }
 
-        Ok(CargoWorkspace { packages, targets, workspace_root: meta.workspace_root })
+        let workspace_root = AbsPathBuf::assert(meta.workspace_root);
+        Ok(CargoWorkspace { packages, targets, workspace_root: workspace_root })
     }
 
     pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a {
         self.packages.iter().map(|(id, _pkg)| id)
     }
 
-    pub fn target_by_root(&self, root: &Path) -> Option<Target> {
+    pub fn target_by_root(&self, root: &AbsPath) -> Option<Target> {
         self.packages()
-            .filter_map(|pkg| self[pkg].targets.iter().find(|&&it| self[it].root == root))
+            .filter_map(|pkg| self[pkg].targets.iter().find(|&&it| &self[it].root == root))
             .next()
             .copied()
     }
@@ -279,8 +279,8 @@ impl CargoWorkspace {
 
 #[derive(Debug, Clone, Default)]
 pub struct ExternResources {
-    out_dirs: FxHashMap<PackageId, PathBuf>,
-    proc_dylib_paths: FxHashMap<PackageId, PathBuf>,
+    out_dirs: FxHashMap<PackageId, AbsPathBuf>,
+    proc_dylib_paths: FxHashMap<PackageId, AbsPathBuf>,
     cfgs: FxHashMap<PackageId, Vec<String>>,
 }
 
@@ -308,6 +308,7 @@ pub fn load_extern_resources(
         if let Ok(message) = message {
             match message {
                 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => {
+                    let out_dir = AbsPathBuf::assert(out_dir);
                     res.out_dirs.insert(package_id.clone(), out_dir);
                     res.cfgs.insert(package_id, cfgs);
                 }
@@ -317,7 +318,8 @@ pub fn load_extern_resources(
                         // Skip rmeta file
                         if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name))
                         {
-                            res.proc_dylib_paths.insert(package_id, filename.clone());
+                            let filename = AbsPathBuf::assert(filename.clone());
+                            res.proc_dylib_paths.insert(package_id, filename);
                         }
                     }
                 }
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index fe3e816894..ac88532f07 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -7,11 +7,12 @@ mod sysroot;
 use std::{
     fs::{read_dir, File, ReadDir},
     io::{self, BufReader},
-    path::{Path, PathBuf},
+    path::Path,
     process::{Command, Output},
 };
 
 use anyhow::{bail, Context, Result};
+use paths::{AbsPath, AbsPathBuf};
 use ra_cfg::CfgOptions;
 use ra_db::{CrateGraph, CrateName, Edition, Env, FileId};
 use rustc_hash::{FxHashMap, FxHashSet};
@@ -29,7 +30,7 @@ pub enum ProjectWorkspace {
     /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
     Cargo { cargo: CargoWorkspace, sysroot: Sysroot },
     /// Project workspace was manually specified using a `rust-project.json` file.
-    Json { project: JsonProject, project_location: PathBuf },
+    Json { project: JsonProject, project_location: AbsPathBuf },
 }
 
 /// `PackageRoot` describes a package root folder.
@@ -38,22 +39,22 @@ pub enum ProjectWorkspace {
 #[derive(Debug, Clone)]
 pub struct PackageRoot {
     /// Path to the root folder
-    path: PathBuf,
+    path: AbsPathBuf,
     /// Is a member of the current workspace
     is_member: bool,
-    out_dir: Option<PathBuf>,
+    out_dir: Option<AbsPathBuf>,
 }
 impl PackageRoot {
-    pub fn new_member(path: PathBuf) -> PackageRoot {
+    pub fn new_member(path: AbsPathBuf) -> PackageRoot {
         Self { path, is_member: true, out_dir: None }
     }
-    pub fn new_non_member(path: PathBuf) -> PackageRoot {
+    pub fn new_non_member(path: AbsPathBuf) -> PackageRoot {
         Self { path, is_member: false, out_dir: None }
     }
-    pub fn path(&self) -> &Path {
+    pub fn path(&self) -> &AbsPath {
         &self.path
     }
-    pub fn out_dir(&self) -> Option<&Path> {
+    pub fn out_dir(&self) -> Option<&AbsPath> {
         self.out_dir.as_deref()
     }
     pub fn is_member(&self) -> bool {
@@ -63,12 +64,12 @@ impl PackageRoot {
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
 pub enum ProjectManifest {
-    ProjectJson(PathBuf),
-    CargoToml(PathBuf),
+    ProjectJson(AbsPathBuf),
+    CargoToml(AbsPathBuf),
 }
 
 impl ProjectManifest {
-    pub fn from_manifest_file(path: PathBuf) -> Result<ProjectManifest> {
+    pub fn from_manifest_file(path: AbsPathBuf) -> Result<ProjectManifest> {
         if path.ends_with("rust-project.json") {
             return Ok(ProjectManifest::ProjectJson(path));
         }
@@ -78,7 +79,7 @@ impl ProjectManifest {
         bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
     }
 
-    pub fn discover_single(path: &Path) -> Result<ProjectManifest> {
+    pub fn discover_single(path: &AbsPath) -> Result<ProjectManifest> {
         let mut candidates = ProjectManifest::discover(path)?;
         let res = match candidates.pop() {
             None => bail!("no projects"),
@@ -91,23 +92,23 @@ impl ProjectManifest {
         Ok(res)
     }
 
-    pub fn discover(path: &Path) -> io::Result<Vec<ProjectManifest>> {
+    pub fn discover(path: &AbsPath) -> io::Result<Vec<ProjectManifest>> {
         if let Some(project_json) = find_in_parent_dirs(path, "rust-project.json") {
             return Ok(vec![ProjectManifest::ProjectJson(project_json)]);
         }
         return find_cargo_toml(path)
             .map(|paths| paths.into_iter().map(ProjectManifest::CargoToml).collect());
 
-        fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> {
+        fn find_cargo_toml(path: &AbsPath) -> io::Result<Vec<AbsPathBuf>> {
             match find_in_parent_dirs(path, "Cargo.toml") {
                 Some(it) => Ok(vec![it]),
                 None => Ok(find_cargo_toml_in_child_dir(read_dir(path)?)),
             }
         }
 
-        fn find_in_parent_dirs(path: &Path, target_file_name: &str) -> Option<PathBuf> {
+        fn find_in_parent_dirs(path: &AbsPath, target_file_name: &str) -> Option<AbsPathBuf> {
             if path.ends_with(target_file_name) {
-                return Some(path.to_owned());
+                return Some(path.to_path_buf());
             }
 
             let mut curr = Some(path);
@@ -123,17 +124,18 @@ impl ProjectManifest {
             None
         }
 
-        fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> {
+        fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<AbsPathBuf> {
             // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects
             entities
                 .filter_map(Result::ok)
                 .map(|it| it.path().join("Cargo.toml"))
                 .filter(|it| it.exists())
+                .map(AbsPathBuf::assert)
                 .collect()
         }
     }
 
-    pub fn discover_all(paths: &[impl AsRef<Path>]) -> Vec<ProjectManifest> {
+    pub fn discover_all(paths: &[impl AsRef<AbsPath>]) -> Vec<ProjectManifest> {
         let mut res = paths
             .iter()
             .filter_map(|it| ProjectManifest::discover(it.as_ref()).ok())
@@ -158,15 +160,12 @@ impl ProjectWorkspace {
                     format!("Failed to open json file {}", project_json.display())
                 })?;
                 let reader = BufReader::new(file);
-                let project_location = match project_json.parent() {
-                    Some(parent) => PathBuf::from(parent),
-                    None => PathBuf::new(),
-                };
+                let project_location = project_json.parent().unwrap().to_path_buf();
                 ProjectWorkspace::Json {
                     project: from_reader(reader).with_context(|| {
                         format!("Failed to deserialize json file {}", project_json.display())
                     })?,
-                    project_location: project_location,
+                    project_location,
                 }
             }
             ProjectManifest::CargoToml(cargo_toml) => {
@@ -218,13 +217,13 @@ impl ProjectWorkspace {
         }
     }
 
-    pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> {
+    pub fn proc_macro_dylib_paths(&self) -> Vec<AbsPathBuf> {
         match self {
-            ProjectWorkspace::Json { project, .. } => project
+            ProjectWorkspace::Json { project, project_location } => project
                 .crates
                 .iter()
                 .filter_map(|krate| krate.proc_macro_dylib_path.as_ref())
-                .cloned()
+                .map(|it| project_location.join(it))
                 .collect(),
             ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => cargo
                 .packages()
diff --git a/crates/ra_project_model/src/sysroot.rs b/crates/ra_project_model/src/sysroot.rs
index a8a196e64c..943ff92df8 100644
--- a/crates/ra_project_model/src/sysroot.rs
+++ b/crates/ra_project_model/src/sysroot.rs
@@ -1,15 +1,12 @@
 //! FIXME: write short doc here
 
-use std::{
-    env, ops,
-    path::{Path, PathBuf},
-    process::Command,
-};
+use std::{convert::TryFrom, env, ops, path::Path, process::Command};
 
-use anyhow::{bail, Result};
+use anyhow::{bail, format_err, Result};
 use ra_arena::{Arena, Idx};
 
 use crate::output;
+use paths::{AbsPath, AbsPathBuf};
 
 #[derive(Default, Debug, Clone)]
 pub struct Sysroot {
@@ -21,7 +18,7 @@ pub type SysrootCrate = Idx<SysrootCrateData>;
 #[derive(Debug, Clone)]
 pub struct SysrootCrateData {
     pub name: String,
-    pub root: PathBuf,
+    pub root: AbsPathBuf,
     pub deps: Vec<SysrootCrate>,
 }
 
@@ -53,7 +50,7 @@ impl Sysroot {
         self.crates.iter().map(|(id, _data)| id)
     }
 
-    pub fn discover(cargo_toml: &Path) -> Result<Sysroot> {
+    pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> {
         let src = get_or_install_rust_src(cargo_toml)?;
         let mut sysroot = Sysroot { crates: Arena::default() };
         for name in SYSROOT_CRATES.trim().lines() {
@@ -86,16 +83,18 @@ impl Sysroot {
     }
 }
 
-fn get_or_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> {
+fn get_or_install_rust_src(cargo_toml: &AbsPath) -> Result<AbsPathBuf> {
     if let Ok(path) = env::var("RUST_SRC_PATH") {
-        return Ok(path.into());
+        let path = AbsPathBuf::try_from(path.as_str())
+            .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?;
+        return Ok(path);
     }
     let current_dir = cargo_toml.parent().unwrap();
     let mut rustc = Command::new(ra_toolchain::rustc());
     rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
     let rustc_output = output(rustc)?;
     let stdout = String::from_utf8(rustc_output.stdout)?;
-    let sysroot_path = Path::new(stdout.trim());
+    let sysroot_path = AbsPath::assert(Path::new(stdout.trim()));
     let src_path = sysroot_path.join("lib/rustlib/src/rust/src");
 
     if !src_path.exists() {
@@ -116,7 +115,7 @@ fn get_or_install_rust_src(cargo_toml: &Path) -> Result<PathBuf> {
 }
 
 impl SysrootCrateData {
-    pub fn root_dir(&self) -> &Path {
+    pub fn root_dir(&self) -> &AbsPath {
         self.root.parent().unwrap()
     }
 }
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 99e3f7173d..45204d1a35 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -3,6 +3,8 @@
 //! Based on cli flags, either spawns an LSP server, or runs a batch analysis
 mod args;
 
+use std::convert::TryFrom;
+
 use lsp_server::Connection;
 use rust_analyzer::{
     cli,
@@ -10,9 +12,11 @@ use rust_analyzer::{
     from_json, Result,
 };
 
-use crate::args::HelpPrinted;
+use ra_db::AbsPathBuf;
 use ra_project_model::ProjectManifest;
 
+use crate::args::HelpPrinted;
+
 fn main() -> Result<()> {
     setup_logging()?;
     let args = match args::Args::parse()? {
@@ -20,6 +24,9 @@ fn main() -> Result<()> {
         Err(HelpPrinted) => return Ok(()),
     };
     match args.command {
+        args::Command::RunServer => run_server()?,
+        args::Command::ProcMacro => ra_proc_macro_srv::cli::run()?,
+
         args::Command::Parse { no_dump } => cli::parse(no_dump)?,
         args::Command::Symbols => cli::symbols()?,
         args::Command::Highlight { rainbow } => cli::highlight(rainbow)?,
@@ -41,7 +48,6 @@ fn main() -> Result<()> {
             load_output_dirs,
             with_proc_macro,
         )?,
-
         args::Command::Bench { path, what, load_output_dirs, with_proc_macro } => {
             cli::analysis_bench(
                 args.verbosity,
@@ -51,13 +57,9 @@ fn main() -> Result<()> {
                 with_proc_macro,
             )?
         }
-
         args::Command::Diagnostics { path, load_output_dirs, with_proc_macro, all } => {
             cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro, all)?
         }
-
-        args::Command::ProcMacro => run_proc_macro_srv()?,
-        args::Command::RunServer => run_server()?,
         args::Command::Version => println!("rust-analyzer {}", env!("REV")),
     }
     Ok(())
@@ -70,11 +72,6 @@ fn setup_logging() -> Result<()> {
     Ok(())
 }
 
-fn run_proc_macro_srv() -> Result<()> {
-    ra_proc_macro_srv::cli::run()?;
-    Ok(())
-}
-
 fn run_server() -> Result<()> {
     log::info!("lifecycle: server started");
 
@@ -103,14 +100,23 @@ fn run_server() -> Result<()> {
     }
 
     let config = {
-        let mut config = Config::default();
+        let root_path = match initialize_params
+            .root_uri
+            .and_then(|it| it.to_file_path().ok())
+            .and_then(|it| AbsPathBuf::try_from(it).ok())
+        {
+            Some(it) => it,
+            None => {
+                let cwd = std::env::current_dir()?;
+                AbsPathBuf::assert(cwd)
+            }
+        };
+
+        let mut config = Config::new(root_path);
         if let Some(value) = &initialize_params.initialization_options {
             config.update(value);
         }
         config.update_caps(&initialize_params.capabilities);
-        let cwd = std::env::current_dir()?;
-        config.root_path =
-            initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd);
 
         if config.linked_projects.is_empty() {
             let workspace_roots = initialize_params
@@ -119,6 +125,7 @@ fn run_server() -> Result<()> {
                     workspaces
                         .into_iter()
                         .filter_map(|it| it.uri.to_file_path().ok())
+                        .filter_map(|it| AbsPathBuf::try_from(it).ok())
                         .collect::<Vec<_>>()
                 })
                 .filter(|workspaces| !workspaces.is_empty())
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 00bbbaf407..e910db6eb5 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -16,7 +16,7 @@ pub fn load_cargo(
     load_out_dirs_from_check: bool,
     with_proc_macro: bool,
 ) -> Result<(AnalysisHost, vfs::Vfs)> {
-    let root = std::env::current_dir()?.join(root);
+    let root = AbsPathBuf::assert(std::env::current_dir()?.join(root));
     let root = ProjectManifest::discover_single(&root)?;
     let ws = ProjectWorkspace::load(
         root,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index aa2c4ae15d..0be34c43f0 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -11,6 +11,7 @@ use std::{ffi::OsString, path::PathBuf};
 
 use crate::diagnostics::DiagnosticsConfig;
 use lsp_types::ClientCapabilities;
+use ra_db::AbsPathBuf;
 use ra_flycheck::FlycheckConfig;
 use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig};
 use ra_project_model::{CargoConfig, JsonProject, ProjectManifest};
@@ -40,7 +41,7 @@ pub struct Config {
 
     pub with_sysroot: bool,
     pub linked_projects: Vec<LinkedProject>,
-    pub root_path: PathBuf,
+    pub root_path: AbsPathBuf,
 }
 
 #[derive(Debug, Clone)]
@@ -131,8 +132,8 @@ pub struct ClientCapsConfig {
     pub hover_actions: bool,
 }
 
-impl Default for Config {
-    fn default() -> Self {
+impl Config {
+    pub fn new(root_path: AbsPathBuf) -> Self {
         Config {
             client_caps: ClientCapsConfig::default(),
 
@@ -171,18 +172,16 @@ impl Default for Config {
             lens: LensConfig::default(),
             hover: HoverConfig::default(),
             linked_projects: Vec::new(),
-            root_path: PathBuf::new(),
+            root_path,
         }
     }
-}
 
-impl Config {
     #[rustfmt::skip]
     pub fn update(&mut self, value: &serde_json::Value) {
         log::info!("Config::update({:#})", value);
 
         let client_caps = self.client_caps.clone();
-        *self = Default::default();
+        *self = Config::new(self.root_path.clone());
         self.client_caps = client_caps;
 
         set(value, "/withSysroot", &mut self.with_sysroot);
@@ -279,9 +278,12 @@ impl Config {
                 self.linked_projects.clear();
                 for linked_project in linked_projects {
                     let linked_project = match linked_project {
-                        ManifestOrJsonProject::Manifest(it) => match ProjectManifest::from_manifest_file(it) {
-                            Ok(it) => it.into(),
-                            Err(_) => continue,
+                        ManifestOrJsonProject::Manifest(it) => {
+                            let path = self.root_path.join(it);
+                            match ProjectManifest::from_manifest_file(path) {
+                                Ok(it) => it.into(),
+                                Err(_) => continue,
+                            }
                         }
                         ManifestOrJsonProject::JsonProject(it) => it.into(),
                     };
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index e2ddb79332..f18694feb8 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -310,11 +310,10 @@ impl ProjectFolders {
 
             let mut file_set_roots: Vec<VfsPath> = vec![];
 
-            let path = AbsPathBuf::try_from(path).unwrap();
             let entry = if root.is_member() {
-                vfs::loader::Entry::local_cargo_package(path.clone())
+                vfs::loader::Entry::local_cargo_package(path.to_path_buf())
             } else {
-                vfs::loader::Entry::cargo_package_dependency(path.clone())
+                vfs::loader::Entry::cargo_package_dependency(path.to_path_buf())
             };
             res.load.push(entry);
             if root.is_member() {
@@ -329,7 +328,7 @@ impl ProjectFolders {
                 }
                 file_set_roots.push(out_dir.into());
             }
-            file_set_roots.push(path.into());
+            file_set_roots.push(path.to_path_buf().into());
 
             if root.is_member() {
                 local_filesets.push(fsc.len());


Test patch:
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs
index 8d88f992dd..49f194f7ef 100644
--- a/crates/rust-analyzer/tests/heavy_tests/support.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/support.rs
@@ -17,6 +17,7 @@ use serde_json::{to_string_pretty, Value};
 use tempfile::TempDir;
 use test_utils::{find_mismatch, Fixture};
 
+use ra_db::AbsPathBuf;
 use ra_project_model::ProjectManifest;
 use rust_analyzer::{
     config::{ClientCapsConfig, Config, FilesConfig, FilesWatcher, LinkedProject},
@@ -70,10 +71,11 @@ impl<'a> Project<'a> {
             fs::write(path.as_path(), entry.text.as_bytes()).unwrap();
         }
 
+        let tmp_dir_path = AbsPathBuf::assert(tmp_dir.path().to_path_buf());
         let mut roots =
-            self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect::<Vec<_>>();
+            self.roots.into_iter().map(|root| tmp_dir_path.join(root)).collect::<Vec<_>>();
         if roots.is_empty() {
-            roots.push(tmp_dir.path().to_path_buf());
+            roots.push(tmp_dir_path.clone());
         }
         let linked_projects = roots
             .into_iter()
@@ -91,7 +93,7 @@ impl<'a> Project<'a> {
             with_sysroot: self.with_sysroot,
             linked_projects,
             files: FilesConfig { watcher: FilesWatcher::Client, exclude: Vec::new() },
-            ..Config::default()
+            ..Config::new(tmp_dir_path)
         };
         if let Some(f) = &self.config {
             f(&mut config)