Repository: rust-analyzer/rust-analyzer
Pull request #6524 (2020-11-10 20:50:30)

Add support for loading rustc private crates
This PR presents a solution to the problem of making`rustc_private` crates visible to `rust-analyzer`. 
Currently developers add dependencies to those crates behind a `cfg(NOT_A_TARGET)` target to prevent `cargo` from building them.
This solution is unsatisfactory since it requires modifying `Cargo.toml` and causes problems for collaboration or CI. 

The proposed solution suggested by @matklad is to allow users to give RA a path where the `rustc` sources could be found and then load that like a normal workspace. 

This PR implements this solution by adding a `rustcSource` configuration item and adding the cargo metadata to the crate graph if it is provided. 

------

I have no idea how this should be tested, or if this code is generally tested at all. I've locally run the extension with these changes and it correctly loads the relevant crates on a `rustc_private` project of mine. 


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 4940110684..1a4a63550d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -139,9 +139,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.0.62"
+version = "1.0.61"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40"
+checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
 
 [[package]]
 name = "cfg"
@@ -398,11 +398,11 @@ dependencies = [
 
 [[package]]
 name = "filetime"
-version = "0.2.13"
+version = "0.2.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe"
+checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if 0.1.10",
  "libc",
  "redox_syscall",
  "winapi 0.3.9",
@@ -438,16 +438,6 @@ dependencies = [
  "toolchain",
 ]
 
-[[package]]
-name = "form_urlencoded"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00"
-dependencies = [
- "matches",
- "percent-encoding",
-]
-
 [[package]]
 name = "fsevent"
 version = "2.0.2"
@@ -622,7 +612,6 @@ dependencies = [
  "hir_expand",
  "itertools",
  "log",
- "once_cell",
  "profile",
  "rustc-hash",
  "scoped-tls",
@@ -725,9 +714,9 @@ dependencies = [
 
 [[package]]
 name = "inotify-sys"
-version = "0.1.4"
+version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4563555856585ab3180a5bf0b2f9f8d301a728462afffc8195b3f5394229c55"
+checksum = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0"
 dependencies = [
  "libc",
 ]
@@ -1064,9 +1053,9 @@ checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397"
 
 [[package]]
 name = "once_cell"
-version = "1.5.1"
+version = "1.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f53cef67919d7d247eb9a2f128ca9e522789967ef1eb4ccd8c71a95a8aedf596"
+checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad"
 
 [[package]]
 name = "oorandom"
@@ -1394,9 +1383,9 @@ dependencies = [
 
 [[package]]
 name = "rustc-ap-rustc_lexer"
-version = "688.0.0"
+version = "686.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebbdcc99bd015349093fcbae4780fda21416fec5d8843acfb3d1733e130cd4db"
+checksum = "a5b04cd2159495584d976d501c5394498470c2e94e4f0cebb8186562d407a678"
 dependencies = [
  "unicode-xid",
 ]
@@ -1899,11 +1888,10 @@ checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
 
 [[package]]
 name = "url"
-version = "2.2.0"
+version = "2.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e"
+checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
 dependencies = [
- "form_urlencoded",
  "idna",
  "matches",
  "percent-encoding",
diff --git a/crates/assists/src/handlers/remove_unused_param.rs b/crates/assists/src/handlers/remove_unused_param.rs
index 1ff5e92b04..5fccca54b8 100644
--- a/crates/assists/src/handlers/remove_unused_param.rs
+++ b/crates/assists/src/handlers/remove_unused_param.rs
@@ -73,8 +73,7 @@ fn process_usage(
     let source_file = ctx.sema.parse(usage.file_range.file_id);
     let call_expr: ast::CallExpr =
         find_node_at_range(source_file.syntax(), usage.file_range.range)?;
-    let call_expr_range = call_expr.expr()?.syntax().text_range();
-    if !call_expr_range.contains_range(usage.file_range.range) {
+    if call_expr.expr()?.syntax().text_range() != usage.file_range.range {
         return None;
     }
     let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
@@ -118,53 +117,6 @@ fn b() { foo(9, ) }
         );
     }
 
-    #[test]
-    fn remove_unused_qualified_call() {
-        check_assist(
-            remove_unused_param,
-            r#"
-mod bar { pub fn foo(x: i32, <|>y: i32) { x; } }
-fn b() { bar::foo(9, 2) }
-"#,
-            r#"
-mod bar { pub fn foo(x: i32) { x; } }
-fn b() { bar::foo(9) }
-"#,
-        );
-    }
-
-    #[test]
-    fn remove_unused_turbofished_func() {
-        check_assist(
-            remove_unused_param,
-            r#"
-pub fn foo<T>(x: T, <|>y: i32) { x; }
-fn b() { foo::<i32>(9, 2) }
-"#,
-            r#"
-pub fn foo<T>(x: T) { x; }
-fn b() { foo::<i32>(9) }
-"#,
-        );
-    }
-
-    #[test]
-    fn remove_unused_generic_unused_param_func() {
-        check_assist(
-            remove_unused_param,
-            r#"
-pub fn foo<T>(x: i32, <|>y: T) { x; }
-fn b() { foo::<i32>(9, 2) }
-fn b2() { foo(9, 2) }
-"#,
-            r#"
-pub fn foo<T>(x: i32) { x; }
-fn b() { foo::<i32>(9) }
-fn b2() { foo(9) }
-"#,
-        );
-    }
-
     #[test]
     fn keep_used() {
         mark::check!(keep_used);
@@ -176,37 +128,4 @@ fn main() { foo(9, 2) }
 "#,
         );
     }
-
-    #[test]
-    fn remove_across_files() {
-        check_assist(
-            remove_unused_param,
-            r#"
-//- /main.rs
-fn foo(x: i32, <|>y: i32) { x; }
-
-mod foo;
-
-//- /foo.rs
-use super::foo;
-
-fn bar() {
-    let _ = foo(1, 2);
-}
-"#,
-            r#"
-//- /main.rs
-fn foo(x: i32) { x; }
-
-mod foo;
-
-//- /foo.rs
-use super::foo;
-
-fn bar() {
-    let _ = foo(1);
-}
-"#,
-        )
-    }
 }
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs
index af3fc96b6c..84a0dffdd4 100644
--- a/crates/assists/src/utils/insert_use.rs
+++ b/crates/assists/src/utils/insert_use.rs
@@ -9,7 +9,7 @@ use syntax::{
         edit::{AstNodeEdit, IndentLevel},
         make, AstNode, PathSegmentKind, VisibilityOwner,
     },
-    AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
+    InsertPosition, SyntaxElement, SyntaxNode,
 };
 use test_utils::mark;
 
@@ -63,30 +63,27 @@ impl ImportScope {
         }
     }
 
-    fn insert_pos_after_last_inner_element(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) {
-        self.as_syntax_node()
-            .children_with_tokens()
-            .filter(|child| match child {
-                NodeOrToken::Node(node) => is_inner_attribute(node.clone()),
-                NodeOrToken::Token(token) => is_inner_comment(token.clone()),
+    fn insert_pos_after_inner_attribute(&self) -> (InsertPosition<SyntaxElement>, AddBlankLine) {
+        // check if the scope has inner attributes, we dont want to insert in front of them
+        match self
+            .as_syntax_node()
+            .children()
+            // no flat_map here cause we want to short circuit the iterator
+            .map(ast::Attr::cast)
+            .take_while(|attr| {
+                attr.as_ref().map(|attr| attr.kind() == ast::AttrKind::Inner).unwrap_or(false)
             })
             .last()
-            .map(|last_inner_element| {
-                (InsertPosition::After(last_inner_element.into()), AddBlankLine::BeforeTwice)
-            })
-            .unwrap_or_else(|| self.first_insert_pos())
+            .flatten()
+        {
+            Some(attr) => {
+                (InsertPosition::After(attr.syntax().clone().into()), AddBlankLine::BeforeTwice)
+            }
+            None => self.first_insert_pos(),
+        }
     }
 }
 
-fn is_inner_attribute(node: SyntaxNode) -> bool {
-    ast::Attr::cast(node).map(|attr| attr.kind()) == Some(ast::AttrKind::Inner)
-}
-
-fn is_inner_comment(token: SyntaxToken) -> bool {
-    ast::Comment::cast(token).and_then(|comment| comment.kind().doc)
-        == Some(ast::CommentPlacement::Inner)
-}
-
 /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
 pub(crate) fn insert_use<'a>(
     scope: &ImportScope,
@@ -561,7 +558,7 @@ fn find_insert_position(
                             (InsertPosition::After(node.into()), AddBlankLine::BeforeTwice)
                         }
                         // there are no imports in this file at all
-                        None => scope.insert_pos_after_last_inner_element(),
+                        None => scope.insert_pos_after_inner_attribute(),
                     },
                 }
             }
@@ -833,67 +830,12 @@ use foo::bar;",
             "foo::bar",
             r"#![allow(unused_imports)]
 
-#![no_std]
 fn main() {}",
             r"#![allow(unused_imports)]
 
-#![no_std]
-
 use foo::bar;
-fn main() {}",
-        );
-    }
-
-    #[test]
-    fn inserts_after_single_line_inner_comments() {
-        check_none(
-            "foo::bar::Baz",
-            "//! Single line inner comments do not allow any code before them.",
-            r#"//! Single line inner comments do not allow any code before them.
-
-use foo::bar::Baz;"#,
-        );
-    }
 
-    #[test]
-    fn inserts_after_multiline_inner_comments() {
-        check_none(
-            "foo::bar::Baz",
-            r#"/*! Multiline inner comments do not allow any code before them. */
-
-/*! Still an inner comment, cannot place any code before. */
-fn main() {}"#,
-            r#"/*! Multiline inner comments do not allow any code before them. */
-
-/*! Still an inner comment, cannot place any code before. */
-
-use foo::bar::Baz;
-fn main() {}"#,
-        )
-    }
-
-    #[test]
-    fn inserts_after_all_inner_items() {
-        check_none(
-            "foo::bar::Baz",
-            r#"#![allow(unused_imports)]
-/*! Multiline line comment 2 */
-
-
-//! Single line comment 1
-#![no_std]
-//! Single line comment 2
-fn main() {}"#,
-            r#"#![allow(unused_imports)]
-/*! Multiline line comment 2 */
-
-
-//! Single line comment 1
-#![no_std]
-//! Single line comment 2
-
-use foo::bar::Baz;
-fn main() {}"#,
+fn main() {}",
         )
     }
 
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index cf5c38a235..fdc65a5c32 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -35,4 +35,3 @@ expect-test = "1.0"
 tracing = "0.1"
 tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] }
 tracing-tree = { version = "0.1.4" }
-once_cell = { version = "1.5.0", features = ["unstable"] }
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index d5f6a4025e..608a031d49 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -64,6 +64,9 @@ pub struct CargoConfig {
 
     /// rustc target
     pub target: Option<String>,
+
+    /// rustc private crate source
+    pub rustc_source: Option<AbsPathBuf>,
 }
 
 pub type Package = Idx<PackageData>;
diff --git a/crates/project_model/src/lib.rs b/crates/project_model/src/lib.rs
index e92cfea596..4531b19280 100644
--- a/crates/project_model/src/lib.rs
+++ b/crates/project_model/src/lib.rs
@@ -9,6 +9,7 @@ use std::{
     fmt,
     fs::{self, read_dir, ReadDir},
     io,
+    path::Component,
     process::Command,
 };
 
@@ -31,7 +32,7 @@ pub use proc_macro_api::ProcMacroClient;
 #[derive(Clone, Eq, PartialEq)]
 pub enum ProjectWorkspace {
     /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
-    Cargo { cargo: CargoWorkspace, sysroot: Sysroot },
+    Cargo { cargo: CargoWorkspace, sysroot: Sysroot, rustc: Option<CargoWorkspace> },
     /// Project workspace was manually specified using a `rust-project.json` file.
     Json { project: ProjectJson, sysroot: Option<Sysroot> },
 }
@@ -39,10 +40,14 @@ pub enum ProjectWorkspace {
 impl fmt::Debug for ProjectWorkspace {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
-            ProjectWorkspace::Cargo { cargo, sysroot } => f
+            ProjectWorkspace::Cargo { cargo, sysroot, rustc } => f
                 .debug_struct("Cargo")
                 .field("n_packages", &cargo.packages().len())
                 .field("n_sysroot_crates", &sysroot.crates().len())
+                .field(
+                    "n_rustc_compiler_crates",
+                    &rustc.as_ref().map_or(0, |rc| rc.packages().len()),
+                )
                 .finish(),
             ProjectWorkspace::Json { project, sysroot } => {
                 let mut debug_struct = f.debug_struct("Json");
@@ -200,7 +205,19 @@ impl ProjectWorkspace {
                 } else {
                     Sysroot::default()
                 };
-                ProjectWorkspace::Cargo { cargo, sysroot }
+
+                let rustc = if let Some(rustc_dir) = &cargo_config.rustc_source {
+                    Some(
+                        CargoWorkspace::from_cargo_metadata(&rustc_dir, cargo_config)
+                            .with_context(|| {
+                                format!("Failed to read Cargo metadata for Rust sources")
+                            })?,
+                    )
+                } else {
+                    None
+                };
+
+                ProjectWorkspace::Cargo { cargo, sysroot, rustc }
             }
         };
 
@@ -238,31 +255,43 @@ impl ProjectWorkspace {
                     })
                 }))
                 .collect::<Vec<_>>(),
-            ProjectWorkspace::Cargo { cargo, sysroot } => cargo
-                .packages()
-                .map(|pkg| {
-                    let is_member = cargo[pkg].is_member;
-                    let pkg_root = cargo[pkg].root().to_path_buf();
-
-                    let mut include = vec![pkg_root.clone()];
-                    include.extend(cargo[pkg].out_dir.clone());
-
-                    let mut exclude = vec![pkg_root.join(".git")];
-                    if is_member {
-                        exclude.push(pkg_root.join("target"));
-                    } else {
-                        exclude.push(pkg_root.join("tests"));
-                        exclude.push(pkg_root.join("examples"));
-                        exclude.push(pkg_root.join("benches"));
-                    }
-                    PackageRoot { is_member, include, exclude }
-                })
-                .chain(sysroot.crates().map(|krate| PackageRoot {
-                    is_member: false,
-                    include: vec![sysroot[krate].root_dir().to_path_buf()],
-                    exclude: Vec::new(),
-                }))
-                .collect(),
+            ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
+                let roots = cargo
+                    .packages()
+                    .map(|pkg| {
+                        let is_member = cargo[pkg].is_member;
+                        let pkg_root = cargo[pkg].root().to_path_buf();
+
+                        let mut include = vec![pkg_root.clone()];
+                        include.extend(cargo[pkg].out_dir.clone());
+
+                        let mut exclude = vec![pkg_root.join(".git")];
+                        if is_member {
+                            exclude.push(pkg_root.join("target"));
+                        } else {
+                            exclude.push(pkg_root.join("tests"));
+                            exclude.push(pkg_root.join("examples"));
+                            exclude.push(pkg_root.join("benches"));
+                        }
+                        PackageRoot { is_member, include, exclude }
+                    })
+                    .chain(sysroot.crates().map(|krate| PackageRoot {
+                        is_member: false,
+                        include: vec![sysroot[krate].root_dir().to_path_buf()],
+                        exclude: Vec::new(),
+                    }));
+                if let Some(rustc_packages) = rustc {
+                    roots
+                        .chain(rustc_packages.packages().map(|krate| PackageRoot {
+                            is_member: false,
+                            include: vec![rustc_packages[krate].root().to_path_buf()],
+                            exclude: Vec::new(),
+                        }))
+                        .collect()
+                } else {
+                    roots.collect()
+                }
+            }
         }
     }
 
@@ -273,7 +302,7 @@ impl ProjectWorkspace {
                 .filter_map(|(_, krate)| krate.proc_macro_dylib_path.as_ref())
                 .cloned()
                 .collect(),
-            ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => cargo
+            ProjectWorkspace::Cargo { cargo, sysroot: _sysroot, rustc: _rustc_crates } => cargo
                 .packages()
                 .filter_map(|pkg| cargo[pkg].proc_macro_dylib_path.as_ref())
                 .cloned()
@@ -284,8 +313,9 @@ impl ProjectWorkspace {
     pub fn n_packages(&self) -> usize {
         match self {
             ProjectWorkspace::Json { project, .. } => project.n_crates(),
-            ProjectWorkspace::Cargo { cargo, sysroot } => {
-                cargo.packages().len() + sysroot.crates().len()
+            ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
+                let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len());
+                cargo.packages().len() + sysroot.crates().len() + rustc_package_len
             }
         }
     }
@@ -365,7 +395,7 @@ impl ProjectWorkspace {
                     }
                 }
             }
-            ProjectWorkspace::Cargo { cargo, sysroot } => {
+            ProjectWorkspace::Cargo { cargo, sysroot, rustc } => {
                 let (public_deps, libproc_macro) =
                     sysroot_to_crate_graph(&mut crate_graph, sysroot, target, load);
 
@@ -373,50 +403,25 @@ impl ProjectWorkspace {
                 cfg_options.extend(get_rustc_cfg_options(target));
 
                 let mut pkg_to_lib_crate = FxHashMap::default();
-                let mut pkg_crates = FxHashMap::default();
 
                 // Add test cfg for non-sysroot crates
                 cfg_options.insert_atom("test".into());
                 cfg_options.insert_atom("debug_assertions".into());
 
+                let mut pkg_crates = FxHashMap::default();
+
                 // Next, create crates for each package, target pair
                 for pkg in cargo.packages() {
                     let mut lib_tgt = None;
                     for &tgt in cargo[pkg].targets.iter() {
-                        let root = cargo[tgt].root.as_path();
-                        if let Some(file_id) = load(root) {
-                            let edition = cargo[pkg].edition;
-                            let cfg_options = {
-                                let mut opts = cfg_options.clone();
-                                for feature in cargo[pkg].features.iter() {
-                                    opts.insert_key_value("feature".into(), feature.into());
-                                }
-                                opts.extend(cargo[pkg].cfgs.iter().cloned());
-                                opts
-                            };
-                            let mut env = Env::default();
-                            if let Some(out_dir) = &cargo[pkg].out_dir {
-                                // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
-                                if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
-                                    env.set("OUT_DIR", out_dir);
-                                }
-                            }
-                            let proc_macro = cargo[pkg]
-                                .proc_macro_dylib_path
-                                .as_ref()
-                                .map(|it| proc_macro_client.by_dylib_path(&it))
-                                .unwrap_or_default();
-
-                            let display_name =
-                                CrateDisplayName::from_canonical_name(cargo[pkg].name.clone());
-                            let crate_id = crate_graph.add_crate_root(
-                                file_id,
-                                edition,
-                                Some(display_name),
-                                cfg_options,
-                                env,
-                                proc_macro.clone(),
-                            );
+                        if let Some(crate_id) = add_target_crate_root(
+                            &mut crate_graph,
+                            &cargo[pkg],
+                            &cargo[tgt],
+                            &cfg_options,
+                            proc_macro_client,
+                            load,
+                        ) {
                             if cargo[tgt].kind == TargetKind::Lib {
                                 lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
                                 pkg_to_lib_crate.insert(pkg, crate_id);
@@ -484,6 +489,92 @@ impl ProjectWorkspace {
                         }
                     }
                 }
+
+                let mut rustc_pkg_crates = FxHashMap::default();
+
+                // If the user provided a path to rustc sources, we add all the rustc_private crates
+                // and create dependencies on them for the crates in the current workspace
+                if let Some(rustc_workspace) = rustc {
+                    for pkg in rustc_workspace.packages() {
+                        for &tgt in rustc_workspace[pkg].targets.iter() {
+                            if rustc_workspace[tgt].kind != TargetKind::Lib {
+                                continue;
+                            }
+                            // Exclude alloc / core / std
+                            if rustc_workspace[tgt]
+                                .root
+                                .components()
+                                .any(|c| c == Component::Normal("library".as_ref()))
+                            {
+                                continue;
+                            }
+
+                            if let Some(crate_id) = add_target_crate_root(
+                                &mut crate_graph,
+                                &rustc_workspace[pkg],
+                                &rustc_workspace[tgt],
+                                &cfg_options,
+                                proc_macro_client,
+                                load,
+                            ) {
+                                pkg_to_lib_crate.insert(pkg, crate_id);
+                                // Add dependencies on the core / std / alloc for rustc
+                                for (name, krate) in public_deps.iter() {
+                                    if let Err(_) =
+                                        crate_graph.add_dep(crate_id, name.clone(), *krate)
+                                    {
+                                        log::error!(
+                                            "cyclic dependency on {} for {}",
+                                            name,
+                                            &cargo[pkg].name
+                                        )
+                                    }
+                                }
+                                rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
+                            }
+                        }
+                    }
+                    // Now add a dep edge from all targets of upstream to the lib
+                    // target of downstream.
+                    for pkg in rustc_workspace.packages() {
+                        for dep in rustc_workspace[pkg].dependencies.iter() {
+                            let name = CrateName::new(&dep.name).unwrap();
+                            if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
+                                for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() {
+                                    if let Err(_) = crate_graph.add_dep(from, name.clone(), to) {
+                                        log::error!(
+                                            "cyclic dependency {} -> {}",
+                                            &rustc_workspace[pkg].name,
+                                            &rustc_workspace[dep.pkg].name
+                                        )
+                                    }
+                                }
+                            }
+                        }
+                    }
+
+                    // Add dependencies for all the crates of the current workspace to rustc_private libraries
+                    for dep in rustc_workspace.packages() {
+                        let name = CrateName::normalize_dashes(&rustc_workspace[dep].name);
+
+                        if let Some(&to) = pkg_to_lib_crate.get(&dep) {
+                            for pkg in cargo.packages() {
+                                if !cargo[pkg].is_member {
+                                    continue;
+                                }
+                                for &from in pkg_crates.get(&pkg).into_iter().flatten() {
+                                    if let Err(_) = crate_graph.add_dep(from, name.clone(), to) {
+                                        log::error!(
+                                            "cyclic dependency {} -> {}",
+                                            &cargo[pkg].name,
+                                            &rustc_workspace[dep].name
+                                        )
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
             }
         }
         if crate_graph.patch_cfg_if() {
@@ -537,6 +628,52 @@ fn utf8_stdout(mut cmd: Command) -> Result<String> {
     Ok(stdout.trim().to_string())
 }
 
+fn add_target_crate_root(
+    crate_graph: &mut CrateGraph,
+    pkg: &cargo_workspace::PackageData,
+    tgt: &cargo_workspace::TargetData,
+    cfg_options: &CfgOptions,
+    proc_macro_client: &ProcMacroClient,
+    load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+) -> Option<CrateId> {
+    let root = tgt.root.as_path();
+    if let Some(file_id) = load(root) {
+        let edition = pkg.edition;
+        let cfg_options = {
+            let mut opts = cfg_options.clone();
+            for feature in pkg.features.iter() {
+                opts.insert_key_value("feature".into(), feature.into());
+            }
+            opts.extend(pkg.cfgs.iter().cloned());
+            opts
+        };
+        let mut env = Env::default();
+        if let Some(out_dir) = &pkg.out_dir {
+            // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
+            if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
+                env.set("OUT_DIR", out_dir);
+            }
+        }
+        let proc_macro = pkg
+            .proc_macro_dylib_path
+            .as_ref()
+            .map(|it| proc_macro_client.by_dylib_path(&it))
+            .unwrap_or_default();
+
+        let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone());
+        let crate_id = crate_graph.add_crate_root(
+            file_id,
+            edition,
+            Some(display_name),
+            cfg_options,
+            env,
+            proc_macro.clone(),
+        );
+
+        return Some(crate_id);
+    }
+    None
+}
 fn sysroot_to_crate_graph(
     crate_graph: &mut CrateGraph,
     sysroot: &Sysroot,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index b4c738272b..372180ab58 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -7,7 +7,7 @@
 //! configure the server itself, feature flags are passed into analysis, and
 //! tweak things like automatic insertion of `()` in completions.
 
-use std::{ffi::OsString, path::PathBuf};
+use std::{convert::TryFrom, ffi::OsString, path::PathBuf};
 
 use flycheck::FlycheckConfig;
 use hir::PrefixKind;
@@ -227,12 +227,25 @@ impl Config {
         self.notifications =
             NotificationsConfig { cargo_toml_not_found: data.notifications_cargoTomlNotFound };
         self.cargo_autoreload = data.cargo_autoreload;
+
+        let rustc_source = if let Some(rustc_source) = data.rustcSource {
+            let rustpath: PathBuf = rustc_source.into();
+            AbsPathBuf::try_from(rustpath)
+                .map_err(|_| {
+                    log::error!("rustc source directory must be an absolute path");
+                })
+                .ok()
+        } else {
+            None
+        };
+
         self.cargo = CargoConfig {
             no_default_features: data.cargo_noDefaultFeatures,
             all_features: data.cargo_allFeatures,
             features: data.cargo_features.clone(),
             load_out_dirs_from_check: data.cargo_loadOutDirsFromCheck,
             target: data.cargo_target.clone(),
+            rustc_source: rustc_source,
         };
         self.runnables = RunnablesConfig {
             override_cargo: data.runnables_overrideCargo,
@@ -385,13 +398,10 @@ impl Config {
             }
 
             if let Some(code_action) = &doc_caps.code_action {
-                match (code_action.data_support, &code_action.resolve_support) {
-                    (Some(true), Some(resolve_support)) => {
-                        if resolve_support.properties.iter().any(|it| it == "edit") {
-                            self.client_caps.code_action_resolve = true;
-                        }
+                if let Some(resolve_support) = &code_action.resolve_support {
+                    if resolve_support.properties.iter().any(|it| it == "edit") {
+                        self.client_caps.code_action_resolve = true;
                     }
-                    _ => (),
                 }
             }
         }
@@ -535,5 +545,6 @@ config_data! {
         rustfmt_overrideCommand: Option<Vec<String>> = None,
 
         withSysroot: bool = true,
+        rustcSource : Option<String> = None,
     }
 }
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 0eabd51bdb..11c8d0e5f8 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -246,7 +246,9 @@ impl GlobalState {
             .iter()
             .enumerate()
             .filter_map(|(id, w)| match w {
-                ProjectWorkspace::Cargo { cargo, sysroot: _ } => Some((id, cargo.workspace_root())),
+                ProjectWorkspace::Cargo { cargo, sysroot: _, rustc: _ } => {
+                    Some((id, cargo.workspace_root()))
+                }
                 ProjectWorkspace::Json { project, .. } => {
                     // Enable flychecks for json projects if a custom flycheck command was supplied
                     // in the workspace configuration.
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 374ed59105..59d89f47d1 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -1,5 +1,8 @@
 //! Missing batteries for standard libraries.
-use std::time::Instant;
+use std::{
+    sync::atomic::{AtomicUsize, Ordering},
+    time::Instant,
+};
 
 mod macros;
 pub mod panic_context;
@@ -147,6 +150,31 @@ where
     left
 }
 
+pub struct RacyFlag(AtomicUsize);
+
+impl RacyFlag {
+    pub const fn new() -> RacyFlag {
+        RacyFlag(AtomicUsize::new(!0))
+    }
+
+    pub fn get(&self, init: impl FnMut() -> bool) -> bool {
+        let mut init = Some(init);
+        self.get_impl(&mut || init.take().map_or(false, |mut f| f()))
+    }
+
+    fn get_impl(&self, init: &mut dyn FnMut() -> bool) -> bool {
+        match self.0.load(Ordering::Relaxed) {
+            0 => false,
+            1 => true,
+            _ => {
+                let res = init();
+                self.0.store(if res { 1 } else { 0 }, Ordering::Relaxed);
+                res
+            }
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 1fe907753d..61d2acb49c 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
 [dependencies]
 itertools = "0.9.0"
 rowan = "0.10.0"
-rustc_lexer = { version = "688.0.0", package = "rustc-ap-rustc_lexer" }
+rustc_lexer = { version = "686.0.0", package = "rustc-ap-rustc_lexer" }
 rustc-hash = "1.1.0"
 arrayvec = "0.5.1"
 once_cell = "1.3.1"
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs
index 7844f9ed63..8a0e3d27b2 100644
--- a/crates/syntax/src/ast.rs
+++ b/crates/syntax/src/ast.rs
@@ -115,22 +115,7 @@ fn test_doc_comment_none() {
 }
 
 #[test]
-fn test_outer_doc_comment_of_items() {
-    let file = SourceFile::parse(
-        r#"
-        /// doc
-        // non-doc
-        mod foo {}
-        "#,
-    )
-    .ok()
-    .unwrap();
-    let module = file.syntax().descendants().find_map(Module::cast).unwrap();
-    assert_eq!("doc", module.doc_comment_text().unwrap());
-}
-
-#[test]
-fn test_inner_doc_comment_of_items() {
+fn test_doc_comment_of_items() {
     let file = SourceFile::parse(
         r#"
         //! doc
@@ -141,7 +126,7 @@ fn test_inner_doc_comment_of_items() {
     .ok()
     .unwrap();
     let module = file.syntax().descendants().find_map(Module::cast).unwrap();
-    assert!(module.doc_comment_text().is_none());
+    assert_eq!("doc", module.doc_comment_text().unwrap());
 }
 
 #[test]
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index 2661c753e2..e4e512f2e0 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -14,15 +14,16 @@ use crate::{
 
 impl ast::Comment {
     pub fn kind(&self) -> CommentKind {
-        CommentKind::from_text(self.text())
+        kind_by_prefix(self.text())
     }
 
     pub fn prefix(&self) -> &'static str {
-        let &(prefix, _kind) = CommentKind::BY_PREFIX
-            .iter()
-            .find(|&(prefix, kind)| self.kind() == *kind && self.text().starts_with(prefix))
-            .unwrap();
-        prefix
+        for (prefix, k) in COMMENT_PREFIX_TO_KIND.iter() {
+            if *k == self.kind() && self.text().starts_with(prefix) {
+                return prefix;
+            }
+        }
+        unreachable!()
     }
 }
 
@@ -54,25 +55,29 @@ pub enum CommentPlacement {
     Outer,
 }
 
-impl CommentKind {
-    const BY_PREFIX: [(&'static str, CommentKind); 8] = [
-        ("/**/", CommentKind { shape: CommentShape::Block, doc: None }),
-        ("////", CommentKind { shape: CommentShape::Line, doc: None }),
-        ("///", CommentKind { shape: CommentShape::Line, doc: Some(CommentPlacement::Outer) }),
-        ("//!", CommentKind { shape: CommentShape::Line, doc: Some(CommentPlacement::Inner) }),
-        ("/**", CommentKind { shape: CommentShape::Block, doc: Some(CommentPlacement::Outer) }),
-        ("/*!", CommentKind { shape: CommentShape::Block, doc: Some(CommentPlacement::Inner) }),
-        ("//", CommentKind { shape: CommentShape::Line, doc: None }),
-        ("/*", CommentKind { shape: CommentShape::Block, doc: None }),
-    ];
+const COMMENT_PREFIX_TO_KIND: &[(&str, CommentKind)] = {
+    use {CommentPlacement::*, CommentShape::*};
+    &[
+        ("////", CommentKind { shape: Line, doc: None }),
+        ("///", CommentKind { shape: Line, doc: Some(Outer) }),
+        ("//!", CommentKind { shape: Line, doc: Some(Inner) }),
+        ("/**", CommentKind { shape: Block, doc: Some(Outer) }),
+        ("/*!", CommentKind { shape: Block, doc: Some(Inner) }),
+        ("//", CommentKind { shape: Line, doc: None }),
+        ("/*", CommentKind { shape: Block, doc: None }),
+    ]
+};
 
-    pub(crate) fn from_text(text: &str) -> CommentKind {
-        let &(_prefix, kind) = CommentKind::BY_PREFIX
-            .iter()
-            .find(|&(prefix, _kind)| text.starts_with(prefix))
-            .unwrap();
-        kind
+fn kind_by_prefix(text: &str) -> CommentKind {
+    if text == "/**/" {
+        return CommentKind { shape: CommentShape::Block, doc: None };
+    }
+    for (prefix, kind) in COMMENT_PREFIX_TO_KIND.iter() {
+        if text.starts_with(prefix) {
+            return *kind;
+        }
     }
+    panic!("bad comment text: {:?}", text)
 }
 
 impl ast::Whitespace {
diff --git a/crates/syntax/src/parsing/text_tree_sink.rs b/crates/syntax/src/parsing/text_tree_sink.rs
index 997bc5d288..c1b5f246d1 100644
--- a/crates/syntax/src/parsing/text_tree_sink.rs
+++ b/crates/syntax/src/parsing/text_tree_sink.rs
@@ -5,7 +5,6 @@ use std::mem;
 use parser::{ParseError, TreeSink};
 
 use crate::{
-    ast,
     parsing::Token,
     syntax_node::GreenNode,
     SmolStr, SyntaxError,
@@ -154,22 +153,24 @@ fn n_attached_trivias<'a>(
 
             while let Some((i, (kind, text))) = trivias.next() {
                 match kind {
-                    WHITESPACE if text.contains("\n\n") => {
-                        // we check whether the next token is a doc-comment
-                        // and skip the whitespace in this case
-                        if let Some((COMMENT, peek_text)) = trivias.peek().map(|(_, pair)| pair) {
-                            let comment_kind = ast::CommentKind::from_text(peek_text);
-                            if comment_kind.doc == Some(ast::CommentPlacement::Outer) {
-                                continue;
+                    WHITESPACE => {
+                        if text.contains("\n\n") {
+                            // we check whether the next token is a doc-comment
+                            // and skip the whitespace in this case
+                            if let Some((peek_kind, peek_text)) =
+                                trivias.peek().map(|(_, pair)| pair)
+                            {
+                                if *peek_kind == COMMENT
+                                    && peek_text.starts_with("///")
+                                    && !peek_text.starts_with("////")
+                                {
+                                    continue;
+                                }
                             }
+                            break;
                         }
-                        break;
                     }
                     COMMENT => {
-                        let comment_kind = ast::CommentKind::from_text(text);
-                        if comment_kind.doc == Some(ast::CommentPlacement::Inner) {
-                            break;
-                        }
                         res = i + 1;
                     }
                     _ => (),
diff --git a/editors/code/package.json b/editors/code/package.json
index eccafccdd9..6db78a99a3 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -687,6 +687,14 @@
                     },
                     "default": [],
                     "description": "Additional arguments to be passed to cargo for runnables such as tests or binaries.\nFor example, it may be '--release'"
+                },
+                "rust-analyzer.rustcSource": {
+                    "type": [
+                        "null",
+                        "string"
+                    ],
+                    "default": null,
+                    "description": "Path to the rust compiler sources, for usage in rustc_private projects."
                 }
             }
         },


Test patch:
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs
index 0a400cb705..104ef334c6 100644
--- a/crates/hir_ty/src/tests.rs
+++ b/crates/hir_ty/src/tests.rs
@@ -22,8 +22,7 @@ use hir_def::{
     AssocItemId, DefWithBodyId, LocalModuleId, Lookup, ModuleDefId,
 };
 use hir_expand::{db::AstDatabase, InFile};
-use once_cell::race::OnceBool;
-use stdx::format_to;
+use stdx::{format_to, RacyFlag};
 use syntax::{
     algo,
     ast::{self, AstNode},
@@ -41,8 +40,8 @@ use crate::{
 // `env UPDATE_EXPECT=1 cargo test -p hir_ty` to update the snapshots.
 
 fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
-    static ENABLE: OnceBool = OnceBool::new();
-    if !ENABLE.get_or_init(|| env::var("CHALK_DEBUG").is_ok()) {
+    static ENABLE: RacyFlag = RacyFlag::new();
+    if !ENABLE.get(|| env::var("CHALK_DEBUG").is_ok()) {
         return None;
     }
 
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
index 5b2e5187ac..58d47d32a4 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt
@@ -1,13 +1,6 @@
 [
     MappedRustDiagnostic {
-        url: Url {
-            scheme: "file",
-            host: None,
-            port: None,
-            path: "/test/compiler/mir/tagset.rs",
-            query: None,
-            fragment: None,
-        },
+        url: "file:///test/compiler/mir/tagset.rs",
         diagnostic: Diagnostic {
             range: Range {
                 start: Position {
@@ -36,14 +29,7 @@
                 [
                     DiagnosticRelatedInformation {
                         location: Location {
-                            uri: Url {
-                                scheme: "file",
-                                host: None,
-                                port: None,
-                                path: "/test/compiler/lib.rs",
-                                query: None,
-                                fragment: None,
-                            },
+                            uri: "file:///test/compiler/lib.rs",
                             range: Range {
                                 start: Position {
                                     line: 0,
@@ -59,14 +45,7 @@
                     },
                     DiagnosticRelatedInformation {
                         location: Location {
-                            uri: Url {
-                                scheme: "file",
-                                host: None,
-                                port: None,
-                                path: "/test/compiler/mir/tagset.rs",
-                                query: None,
-                                fragment: None,
-                            },
+                            uri: "file:///test/compiler/mir/tagset.rs",
                             range: Range {
                                 start: Position {
                                     line: 41,
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt
index 116f0ff734..6aa26bf63a 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/handles_macro_location.txt
@@ -1,13 +1,6 @@
 [
     MappedRustDiagnostic {
-        url: Url {
-            scheme: "file",
-            host: None,
-            port: None,
-            path: "/test/src/main.rs",
-            query: None,
-            fragment: None,
-        },
+        url: "file:///test/src/main.rs",
         diagnostic: Diagnostic {
             range: Range {
                 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
index bbec6a7964..7aaffaba25 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt
@@ -1,13 +1,6 @@
 [
     MappedRustDiagnostic {
-        url: Url {
-            scheme: "file",
-            host: None,
-            port: None,
-            path: "/test/crates/hir_def/src/data.rs",
-            query: None,
-            fragment: None,
-        },
+        url: "file:///test/crates/hir_def/src/data.rs",
         diagnostic: Diagnostic {
             range: Range {
                 start: Position {
@@ -32,14 +25,7 @@
                 [
                     DiagnosticRelatedInformation {
                         location: Location {
-                            uri: Url {
-                                scheme: "file",
-                                host: None,
-                                port: None,
-                                path: "/test/crates/hir_def/src/path.rs",
-                                query: None,
-                                fragment: None,
-                            },
+                            uri: "file:///test/crates/hir_def/src/path.rs",
                             range: Range {
                                 start: Position {
                                     line: 264,
@@ -61,14 +47,7 @@
         fixes: [],
     },
     MappedRustDiagnostic {
-        url: Url {
-            scheme: "file",
-            host: None,
-            port: None,
-            path: "/test/crates/hir_def/src/path.rs",
-            query: None,
-            fragment: None,
-        },
+        url: "file:///test/crates/hir_def/src/path.rs",
         diagnostic: Diagnostic {
             range: Range {
                 start: Position {
@@ -93,14 +72,7 @@
                 [
                     DiagnosticRelatedInformation {
                         location: Location {
-                            uri: Url {
-                                scheme: "file",
-                                host: None,
-                                port: None,
-                                path: "/test/crates/hir_def/src/data.rs",
-                                query: None,
-                                fragment: None,
-                            },
+                            uri: "file:///test/crates/hir_def/src/data.rs",
                             range: Range {
                                 start: Position {
                                     line: 79,
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt
index 2cbf657e59..5842134200 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_incompatible_type_for_trait.txt
@@ -1,13 +1,6 @@
 [
     MappedRustDiagnostic {
-        url: Url {
-            scheme: "file",
-            host: None,
-            port: None,
-            path: "/test/compiler/ty/list_iter.rs",
-            query: None,
-            fragment: None,
-        },
+        url: "file:///test/compiler/ty/list_iter.rs",
         diagnostic: Diagnostic {
             range: Range {
                 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt
index 1142dc2ac6..2610e4e209 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_mismatched_type.txt
@@ -1,13 +1,6 @@
 [
     MappedRustDiagnostic {
-        url: Url {
-            scheme: "file",
-            host: None,
-            port: None,
-            path: "/test/runtime/compiler_support.rs",
-            query: None,
-            fragment: None,
-        },
+        url: "file:///test/runtime/compiler_support.rs",
         diagnostic: Diagnostic {
             range: Range {
                 start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
index c709de95ff..a8d85f0087 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt
@@ -1,13 +1,6 @@
 [
     MappedRustDiagnostic {
-        url: Url {
-            scheme: "file",
-            host: None,
-            port: None,
-            path: "/test/driver/subcommand/repl.rs",
-            query: None,
-            fragment: None,
-        },
+        url: "file:///test/driver/subcommand/repl.rs",
         diagnostic: Diagnostic {
             range: Range {
                 start: Position {
@@ -53,14 +46,7 @@
                     SnippetWorkspaceEdit {
                         changes: Some(
                             {
-                                Url {
-                                    scheme: "file",
-                                    host: None,
-                                    port: None,
-                                    path: "/test/driver/subcommand/repl.rs",
-                                    query: None,
-                                    fragment: None,
-                                }: [
+                                "file:///test/driver/subcommand/repl.rs": [
                                     TextEdit {
                                         range: Range {
                                             start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
index 632f438d7a..0b89373766 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt
@@ -1,13 +1,6 @@
 [
     MappedRustDiagnostic {
-        url: Url {
-            scheme: "file",
-            host: None,
-            port: None,
-            path: "/test/driver/subcommand/repl.rs",
-            query: None,
-            fragment: None,
-        },
+        url: "file:///test/driver/subcommand/repl.rs",
         diagnostic: Diagnostic {
             range: Range {
                 start: Position {
@@ -53,14 +46,7 @@
                     SnippetWorkspaceEdit {
                         changes: Some(
                             {
-                                Url {
-                                    scheme: "file",
-                                    host: None,
-                                    port: None,
-                                    path: "/test/driver/subcommand/repl.rs",
-                                    query: None,
-                                    fragment: None,
-                                }: [
+                                "file:///test/driver/subcommand/repl.rs": [
                                     TextEdit {
                                         range: Range {
                                             start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
index c0b79428d8..7fa9dee62a 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt
@@ -1,13 +1,6 @@
 [
     MappedRustDiagnostic {
-        url: Url {
-            scheme: "file",
-            host: None,
-            port: None,
-            path: "/test/driver/subcommand/repl.rs",
-            query: None,
-            fragment: None,
-        },
+        url: "file:///test/driver/subcommand/repl.rs",
         diagnostic: Diagnostic {
             range: Range {
                 start: Position {
@@ -53,14 +46,7 @@
                     SnippetWorkspaceEdit {
                         changes: Some(
                             {
-                                Url {
-                                    scheme: "file",
-                                    host: None,
-                                    port: None,
-                                    path: "/test/driver/subcommand/repl.rs",
-                                    query: None,
-                                    fragment: None,
-                                }: [
+                                "file:///test/driver/subcommand/repl.rs": [
                                     TextEdit {
                                         range: Range {
                                             start: Position {
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt
index 782c72dbdc..ba1b98b335 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt
@@ -1,13 +1,6 @@
 [
     MappedRustDiagnostic {
-        url: Url {
-            scheme: "file",
-            host: None,
-            port: None,
-            path: "/test/compiler/ty/select.rs",
-            query: None,
-            fragment: None,
-        },
+        url: "file:///test/compiler/ty/select.rs",
         diagnostic: Diagnostic {
             range: Range {
                 start: Position {
@@ -36,14 +29,7 @@
                 [
                     DiagnosticRelatedInformation {
                         location: Location {
-                            uri: Url {
-                                scheme: "file",
-                                host: None,
-                                port: None,
-                                path: "/test/compiler/ty/select.rs",
-                                query: None,
-                                fragment: None,
-                            },
+                            uri: "file:///test/compiler/ty/select.rs",
                             range: Range {
                                 start: Position {
                                     line: 218,
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
index d3f27ab6a5..3c97b20841 100644
--- a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
+++ b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt
@@ -1,13 +1,6 @@
 [
     MappedRustDiagnostic {
-        url: Url {
-            scheme: "file",
-            host: None,
-            port: None,
-            path: "/test/src/main.rs",
-            query: None,
-            fragment: None,
-        },
+        url: "file:///test/src/main.rs",
         diagnostic: Diagnostic {
             range: Range {
                 start: Position {
@@ -36,14 +29,7 @@
                 [
                     DiagnosticRelatedInformation {
                         location: Location {
-                            uri: Url {
-                                scheme: "file",
-                                host: None,
-                                port: None,
-                                path: "/test/src/main.rs",
-                                query: None,
-                                fragment: None,
-                            },
+                            uri: "file:///test/src/main.rs",
                             range: Range {
                                 start: Position {
                                     line: 2,
@@ -75,14 +61,7 @@
                     SnippetWorkspaceEdit {
                         changes: Some(
                             {
-                                Url {
-                                    scheme: "file",
-                                    host: None,
-                                    port: None,
-                                    path: "/test/src/main.rs",
-                                    query: None,
-                                    fragment: None,
-                                }: [
+                                "file:///test/src/main.rs": [
                                     TextEdit {
                                         range: Range {
                                             start: Position {
diff --git a/crates/syntax/test_data/parser/ok/0037_mod.rast b/crates/syntax/test_data/parser/ok/0037_mod.rast
index 35577272e5..1d5d94bdea 100644
--- a/crates/syntax/test_data/parser/ok/0037_mod.rast
+++ b/crates/syntax/test_data/parser/ok/0037_mod.rast
@@ -1,9 +1,9 @@
 SOURCE_FILE@0..93
   COMMENT@0..60 "// https://github.com ..."
   WHITESPACE@60..62 "\n\n"
-  COMMENT@62..70 "//! docs"
-  WHITESPACE@70..71 "\n"
-  MODULE@71..93
+  MODULE@62..93
+    COMMENT@62..70 "//! docs"
+    WHITESPACE@70..71 "\n"
     COMMENT@71..82 "// non-docs"
     WHITESPACE@82..83 "\n"
     MOD_KW@83..86 "mod"