Repository: rust-analyzer/rust-analyzer
Pull request #8820 (2021-05-13 09:31:24)

fix: Return absolute paths in find_path if crate start is ambiguous
bors r+


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index f9c34547ed..0e1234b721 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -319,12 +319,6 @@ version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fc4b29f4b9bb94bf267d57269fd0706d343a160937108e9619fe380645428abb"
 
-[[package]]
-name = "dot"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a74b6c4d4a1cff5f454164363c16b72fa12463ca6b31f4b5f2035a65fa3d5906"
-
 [[package]]
 name = "drop_bomb"
 version = "0.1.5"
@@ -594,7 +588,6 @@ version = "0.0.0"
 dependencies = [
  "cfg",
  "cov-mark",
- "dot",
  "either",
  "expect-test",
  "hir",
diff --git a/Cargo.toml b/Cargo.toml
index 498cf7d626..cf3013c080 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,4 @@
 [workspace]
-resolver = "2"
 members = ["xtask/", "lib/*", "crates/*"]
 
 [profile.dev]
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index aadd4e44ae..a2479016e5 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -9,7 +9,7 @@ use std::{
 use base_db::CrateId;
 use cfg::{CfgExpr, CfgOptions};
 use either::Either;
-use hir_expand::{hygiene::Hygiene, name::AsName, AstId, InFile};
+use hir_expand::{hygiene::Hygiene, name::AsName, AstId, AttrId, InFile};
 use itertools::Itertools;
 use la_arena::ArenaMap;
 use mbe::ast_to_token_tree;
@@ -101,13 +101,17 @@ impl RawAttrs {
         hygiene: &Hygiene,
     ) -> Self {
         let entries = collect_attrs(owner)
-            .flat_map(|(id, attr)| match attr {
-                Either::Left(attr) => Attr::from_src(db, attr, hygiene, id),
-                Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
-                    id,
-                    input: Some(AttrInput::Literal(SmolStr::new(doc))),
-                    path: Interned::new(ModPath::from(hir_expand::name!(doc))),
-                }),
+            .enumerate()
+            .flat_map(|(i, attr)| {
+                let index = AttrId(i as u32);
+                match attr {
+                    Either::Left(attr) => Attr::from_src(db, attr, hygiene, index),
+                    Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
+                        id: index,
+                        input: Some(AttrInput::Literal(SmolStr::new(doc))),
+                        path: Interned::new(ModPath::from(hir_expand::name!(doc))),
+                    }),
+                }
             })
             .collect::<Arc<_>>();
 
@@ -120,7 +124,6 @@ impl RawAttrs {
     }
 
     pub(crate) fn merge(&self, other: Self) -> Self {
-        // FIXME: This needs to fixup `AttrId`s
         match (&self.entries, &other.entries) {
             (None, None) => Self::EMPTY,
             (Some(entries), None) | (None, Some(entries)) => {
@@ -372,26 +375,39 @@ impl AttrsWithOwner {
 
                 let def_map = module.def_map(db);
                 let mod_data = &def_map[module.local_id];
-                match mod_data.declaration_source(db) {
+                let attrs = match mod_data.declaration_source(db) {
                     Some(it) => {
-                        let mut map = AttrSourceMap::new(InFile::new(it.file_id, &it.value));
+                        let mut attrs: Vec<_> = collect_attrs(&it.value as &dyn ast::AttrsOwner)
+                            .map(|attr| InFile::new(it.file_id, attr))
+                            .collect();
                         if let InFile { file_id, value: ModuleSource::SourceFile(file) } =
                             mod_data.definition_source(db)
                         {
-                            map.merge(AttrSourceMap::new(InFile::new(file_id, &file)));
+                            attrs.extend(
+                                collect_attrs(&file as &dyn ast::AttrsOwner)
+                                    .map(|attr| InFile::new(file_id, attr)),
+                            )
                         }
-                        return map;
+                        attrs
                     }
                     None => {
                         let InFile { file_id, value } = mod_data.definition_source(db);
-                        let attrs_owner = match &value {
-                            ModuleSource::SourceFile(file) => file as &dyn ast::AttrsOwner,
-                            ModuleSource::Module(module) => module as &dyn ast::AttrsOwner,
-                            ModuleSource::BlockExpr(block) => block as &dyn ast::AttrsOwner,
-                        };
-                        return AttrSourceMap::new(InFile::new(file_id, attrs_owner));
+                        match &value {
+                            ModuleSource::SourceFile(file) => {
+                                collect_attrs(file as &dyn ast::AttrsOwner)
+                            }
+                            ModuleSource::Module(module) => {
+                                collect_attrs(module as &dyn ast::AttrsOwner)
+                            }
+                            ModuleSource::BlockExpr(block) => {
+                                collect_attrs(block as &dyn ast::AttrsOwner)
+                            }
+                        }
+                        .map(|attr| InFile::new(file_id, attr))
+                        .collect()
                     }
-                }
+                };
+                return AttrSourceMap { attrs };
             }
             AttrDefId::FieldId(id) => {
                 let map = db.fields_attrs_source_map(id.parent);
@@ -446,7 +462,11 @@ impl AttrsWithOwner {
             },
         };
 
-        AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn AttrsOwner))
+        AttrSourceMap {
+            attrs: collect_attrs(&owner.value)
+                .map(|attr| InFile::new(owner.file_id, attr))
+                .collect(),
+        }
     }
 
     pub fn docs_with_rangemap(
@@ -498,7 +518,7 @@ impl AttrsWithOwner {
         if buf.is_empty() {
             None
         } else {
-            Some((Documentation(buf), DocsRangeMap { mapping, source_map: self.source_map(db) }))
+            Some((Documentation(buf), DocsRangeMap { mapping, source: self.source_map(db).attrs }))
         }
     }
 }
@@ -539,59 +559,27 @@ fn inner_attributes(
 }
 
 pub struct AttrSourceMap {
-    attrs: Vec<InFile<ast::Attr>>,
-    doc_comments: Vec<InFile<ast::Comment>>,
+    attrs: Vec<InFile<Either<ast::Attr, ast::Comment>>>,
 }
 
 impl AttrSourceMap {
-    fn new(owner: InFile<&dyn ast::AttrsOwner>) -> Self {
-        let mut attrs = Vec::new();
-        let mut doc_comments = Vec::new();
-        for (_, attr) in collect_attrs(owner.value) {
-            match attr {
-                Either::Left(attr) => attrs.push(owner.with_value(attr)),
-                Either::Right(comment) => doc_comments.push(owner.with_value(comment)),
-            }
-        }
-
-        Self { attrs, doc_comments }
-    }
-
-    fn merge(&mut self, other: Self) {
-        self.attrs.extend(other.attrs);
-        self.doc_comments.extend(other.doc_comments);
-    }
-
     /// Maps the lowered `Attr` back to its original syntax node.
     ///
     /// `attr` must come from the `owner` used for AttrSourceMap
     ///
     /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of
     /// the attribute represented by `Attr`.
-    pub fn source_of(&self, attr: &Attr) -> InFile<Either<ast::Attr, ast::Comment>> {
-        self.source_of_id(attr.id)
-    }
-
-    fn source_of_id(&self, id: AttrId) -> InFile<Either<ast::Attr, ast::Comment>> {
-        if id.is_doc_comment {
-            self.doc_comments
-                .get(id.ast_index as usize)
-                .unwrap_or_else(|| panic!("cannot find doc comment at index {:?}", id))
-                .clone()
-                .map(|attr| Either::Right(attr))
-        } else {
-            self.attrs
-                .get(id.ast_index as usize)
-                .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", id))
-                .clone()
-                .map(|attr| Either::Left(attr))
-        }
+    pub fn source_of(&self, attr: &Attr) -> InFile<&Either<ast::Attr, ast::Comment>> {
+        self.attrs
+            .get(attr.id.0 as usize)
+            .unwrap_or_else(|| panic!("cannot find `Attr` at index {:?}", attr.id))
+            .as_ref()
     }
 }
 
 /// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree.
 pub struct DocsRangeMap {
-    source_map: AttrSourceMap,
+    source: Vec<InFile<Either<ast::Attr, ast::Comment>>>,
     // (docstring-line-range, attr_index, attr-string-range)
     // a mapping from the text range of a line of the [`Documentation`] to the attribute index and
     // the original (untrimmed) syntax doc line
@@ -608,7 +596,7 @@ impl DocsRangeMap {
 
         let relative_range = range - line_docs_range.start();
 
-        let &InFile { file_id, value: ref source } = &self.source_map.source_of_id(idx);
+        let &InFile { file_id, value: ref source } = &self.source[idx.0 as usize];
         match source {
             Either::Left(_) => None, // FIXME, figure out a nice way to handle doc attributes here
             // as well as for whats done in syntax highlight doc injection
@@ -627,12 +615,6 @@ impl DocsRangeMap {
     }
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub(crate) struct AttrId {
-    is_doc_comment: bool,
-    pub(crate) ast_index: u32,
-}
-
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Attr {
     pub(crate) id: AttrId,
@@ -767,32 +749,22 @@ fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase
 
 fn collect_attrs(
     owner: &dyn ast::AttrsOwner,
-) -> impl Iterator<Item = (AttrId, Either<ast::Attr, ast::Comment>)> {
+) -> impl Iterator<Item = Either<ast::Attr, ast::Comment>> {
     let (inner_attrs, inner_docs) = inner_attributes(owner.syntax())
         .map_or((None, None), |(attrs, docs)| (Some(attrs), Some(docs)));
 
     let outer_attrs = owner.attrs().filter(|attr| attr.kind().is_outer());
-    let attrs =
-        outer_attrs.chain(inner_attrs.into_iter().flatten()).enumerate().map(|(idx, attr)| {
-            (
-                AttrId { ast_index: idx as u32, is_doc_comment: false },
-                attr.syntax().text_range().start(),
-                Either::Left(attr),
-            )
-        });
+    let attrs = outer_attrs
+        .chain(inner_attrs.into_iter().flatten())
+        .map(|attr| (attr.syntax().text_range().start(), Either::Left(attr)));
 
     let outer_docs =
         ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer);
-    let docs =
-        outer_docs.chain(inner_docs.into_iter().flatten()).enumerate().map(|(idx, docs_text)| {
-            (
-                AttrId { ast_index: idx as u32, is_doc_comment: true },
-                docs_text.syntax().text_range().start(),
-                Either::Right(docs_text),
-            )
-        });
+    let docs = outer_docs
+        .chain(inner_docs.into_iter().flatten())
+        .map(|docs_text| (docs_text.syntax().text_range().start(), Either::Right(docs_text)));
     // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
-    docs.chain(attrs).sorted_by_key(|&(_, offset, _)| offset).map(|(id, _, attr)| (id, attr))
+    docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).map(|(_, attr)| attr)
 }
 
 pub(crate) fn variants_attrs_source_map(
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 9f278d35b7..75dc19c117 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -205,7 +205,7 @@ impl ExprCollector<'_> {
         self.maybe_collect_expr(expr).unwrap_or_else(|| self.missing_expr())
     }
 
-    /// Returns `None` if and only if the expression is `#[cfg]`d out.
+    /// Returns `None` if the expression is `#[cfg]`d out.
     fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> {
         let syntax_ptr = AstPtr::new(&expr);
         self.check_cfg(&expr)?;
@@ -668,7 +668,7 @@ impl ExprCollector<'_> {
                 if self.check_cfg(&stmt).is_none() {
                     return;
                 }
-                let has_semi = stmt.semicolon_token().is_some();
+
                 // Note that macro could be expended to multiple statements
                 if let Some(ast::Expr::MacroCall(m)) = stmt.expr() {
                     let macro_ptr = AstPtr::new(&m);
@@ -685,19 +685,18 @@ impl ExprCollector<'_> {
                                 statements.statements().for_each(|stmt| this.collect_stmt(stmt));
                                 if let Some(expr) = statements.expr() {
                                     let expr = this.collect_expr(expr);
-                                    this.statements_in_scope
-                                        .push(Statement::Expr { expr, has_semi });
+                                    this.statements_in_scope.push(Statement::Expr(expr));
                                 }
                             }
                             None => {
                                 let expr = this.alloc_expr(Expr::Missing, syntax_ptr.clone());
-                                this.statements_in_scope.push(Statement::Expr { expr, has_semi });
+                                this.statements_in_scope.push(Statement::Expr(expr));
                             }
                         },
                     );
                 } else {
                     let expr = self.collect_expr_opt(stmt.expr());
-                    self.statements_in_scope.push(Statement::Expr { expr, has_semi });
+                    self.statements_in_scope.push(Statement::Expr(expr));
                 }
             }
             ast::Stmt::Item(item) => {
@@ -726,17 +725,8 @@ impl ExprCollector<'_> {
         let prev_statements = std::mem::take(&mut self.statements_in_scope);
 
         block.statements().for_each(|s| self.collect_stmt(s));
-        block.tail_expr().and_then(|e| {
-            let expr = self.maybe_collect_expr(e)?;
-            Some(self.statements_in_scope.push(Statement::Expr { expr, has_semi: false }))
-        });
-
-        let mut tail = None;
-        if let Some(Statement::Expr { expr, has_semi: false }) = self.statements_in_scope.last() {
-            tail = Some(*expr);
-            self.statements_in_scope.pop();
-        }
-        let tail = tail;
+
+        let tail = block.tail_expr().map(|e| self.collect_expr(e));
         let statements = std::mem::replace(&mut self.statements_in_scope, prev_statements);
         let syntax_node_ptr = AstPtr::new(&block.into());
         let expr_id = self.alloc_expr(
diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs
index 6764de3a75..bd7005ca69 100644
--- a/crates/hir_def/src/body/scope.rs
+++ b/crates/hir_def/src/body/scope.rs
@@ -157,7 +157,7 @@ fn compute_block_scopes(
                 scope = scopes.new_scope(scope);
                 scopes.add_bindings(body, scope, *pat);
             }
-            Statement::Expr { expr, .. } => {
+            Statement::Expr(expr) => {
                 scopes.set_scope(*expr, scope);
                 compute_expr_scopes(*expr, body, scopes, scope);
             }
diff --git a/crates/hir_def/src/expr.rs b/crates/hir_def/src/expr.rs
index 0c3b410802..b4ad984bd9 100644
--- a/crates/hir_def/src/expr.rs
+++ b/crates/hir_def/src/expr.rs
@@ -242,7 +242,7 @@ pub struct RecordLitField {
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum Statement {
     Let { pat: PatId, type_ref: Option<Interned<TypeRef>>, initializer: Option<ExprId> },
-    Expr { expr: ExprId, has_semi: bool },
+    Expr(ExprId),
 }
 
 impl Expr {
@@ -265,7 +265,7 @@ impl Expr {
                                 f(*expr);
                             }
                         }
-                        Statement::Expr { expr: expression, .. } => f(*expression),
+                        Statement::Expr(e) => f(*e),
                     }
                 }
                 if let Some(expr) = tail {
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs
index 858e880381..ee52794aa8 100644
--- a/crates/hir_def/src/find_path.rs
+++ b/crates/hir_def/src/find_path.rs
@@ -5,10 +5,10 @@ use std::iter;
 use hir_expand::name::{known, AsName, Name};
 use rustc_hash::FxHashSet;
 
-use crate::nameres::DefMap;
 use crate::{
     db::DefDatabase,
     item_scope::ItemInNs,
+    nameres::DefMap,
     path::{ModPath, PathKind},
     visibility::Visibility,
     ModuleDefId, ModuleId,
@@ -134,7 +134,16 @@ fn find_path_inner(
     for (name, def_id) in root_def_map.extern_prelude() {
         if item == ItemInNs::Types(*def_id) {
             let name = scope_name.unwrap_or_else(|| name.clone());
-            return Some(ModPath::from_segments(PathKind::Plain, vec![name]));
+
+            let name_already_occupied_in_type_ns = def_map
+                .with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
+                    def_map[local_id].scope.get(&name).take_types().filter(|&id| id != *def_id)
+                })
+                .is_some();
+            return Some(ModPath::from_segments(
+                if name_already_occupied_in_type_ns { PathKind::Abs } else { PathKind::Plain },
+                vec![name],
+            ));
         }
     }
 
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index a82ea5957e..e96ca953ff 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -62,14 +62,14 @@ use hir_expand::{
     ast_id_map::FileAstId,
     eager::{expand_eager_macro, ErrorEmitted, ErrorSink},
     hygiene::Hygiene,
-    AstId, FragmentKind, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
+    AstId, AttrId, FragmentKind, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId,
+    MacroDefKind,
 };
 use la_arena::Idx;
 use nameres::DefMap;
 use path::ModPath;
 use syntax::ast;
 
-use crate::attr::AttrId;
 use crate::builtin_type::BuiltinType;
 use item_tree::{
     Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, ModItem, Static, Struct, Trait,
@@ -753,7 +753,7 @@ fn derive_macro_as_call_id(
             MacroCallKind::Derive {
                 ast_id: item_attr.ast_id,
                 derive_name: last_segment.to_string(),
-                derive_attr_index: derive_attr.ast_index,
+                derive_attr,
             },
         )
         .into();
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index adfb78c942..e89136ed1d 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -13,14 +13,14 @@ use hir_expand::{
     builtin_macro::find_builtin_macro,
     name::{AsName, Name},
     proc_macro::ProcMacroExpander,
-    FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
+    AttrId, FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
 };
 use hir_expand::{InFile, MacroCallLoc};
 use rustc_hash::{FxHashMap, FxHashSet};
 use syntax::ast;
 
 use crate::{
-    attr::{AttrId, Attrs},
+    attr::Attrs,
     db::DefDatabase,
     derive_macro_as_call_id,
     intern::Interned,
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs
index b6a6d602f7..537c030286 100644
--- a/crates/hir_expand/src/builtin_derive.rs
+++ b/crates/hir_expand/src/builtin_derive.rs
@@ -269,7 +269,7 @@ mod tests {
     use expect_test::{expect, Expect};
     use name::AsName;
 
-    use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
+    use crate::{test_db::TestDB, AstId, AttrId, MacroCallId, MacroCallKind, MacroCallLoc};
 
     use super::*;
 
@@ -320,7 +320,7 @@ $0
             kind: MacroCallKind::Derive {
                 ast_id,
                 derive_name: name.to_string(),
-                derive_attr_index: 0,
+                derive_attr: AttrId(0),
             },
         };
 
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index c43d382adf..6647e57e7d 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -12,9 +12,9 @@ use syntax::{
 };
 
 use crate::{
-    ast_id_map::AstIdMap, hygiene::HygieneFrame, input::process_macro_input, BuiltinDeriveExpander,
-    BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId, HirFileId, HirFileIdRepr, LazyMacroId,
-    MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
+    ast_id_map::AstIdMap, hygiene::HygieneFrame, BuiltinDeriveExpander, BuiltinFnLikeExpander,
+    EagerCallLoc, EagerMacroId, HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc,
+    MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
 };
 
 /// Total limit on the number of tokens produced by any macro invocation.
@@ -267,16 +267,7 @@ fn parse_macro_expansion(
 
 fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option<Arc<(tt::Subtree, mbe::TokenMap)>> {
     let arg = db.macro_arg_text(id)?;
-    let (mut tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg));
-
-    if let MacroCallId::LazyMacro(id) = id {
-        let loc: MacroCallLoc = db.lookup_intern_macro(id);
-        if loc.def.is_proc_macro() {
-            // proc macros expect their inputs without parentheses, MBEs expect it with them included
-            tt.delimiter = None;
-        }
-    }
-
+    let (tt, tmap) = mbe::syntax_node_to_token_tree(&SyntaxNode::new_root(arg));
     Some(Arc::new((tt, tmap)))
 }
 
@@ -290,7 +281,6 @@ fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option<GreenNode> {
     };
     let loc = db.lookup_intern_macro(id);
     let arg = loc.kind.arg(db)?;
-    let arg = process_macro_input(db, arg, id);
     Some(arg.green().into())
 }
 
diff --git a/crates/hir_expand/src/input.rs b/crates/hir_expand/src/input.rs
deleted file mode 100644
index 112216859c..0000000000
--- a/crates/hir_expand/src/input.rs
+++ /dev/null
@@ -1,94 +0,0 @@
-//! Macro input conditioning.
-
-use syntax::{
-    ast::{self, AttrsOwner},
-    AstNode, SyntaxNode,
-};
-
-use crate::{
-    db::AstDatabase,
-    name::{name, AsName},
-    LazyMacroId, MacroCallKind, MacroCallLoc,
-};
-
-pub(crate) fn process_macro_input(
-    db: &dyn AstDatabase,
-    node: SyntaxNode,
-    id: LazyMacroId,
-) -> SyntaxNode {
-    let loc: MacroCallLoc = db.lookup_intern_macro(id);
-
-    match loc.kind {
-        MacroCallKind::FnLike { .. } => node,
-        MacroCallKind::Derive { derive_attr_index, .. } => {
-            let item = match ast::Item::cast(node.clone()) {
-                Some(item) => item,
-                None => return node,
-            };
-
-            remove_derives_up_to(item, derive_attr_index as usize).syntax().clone()
-        }
-    }
-}
-
-/// Removes `#[derive]` attributes from `item`, up to `attr_index`.
-fn remove_derives_up_to(item: ast::Item, attr_index: usize) -> ast::Item {
-    let item = item.clone_for_update();
-    for attr in item.attrs().take(attr_index + 1) {
-        if let Some(name) =
-            attr.path().and_then(|path| path.as_single_segment()).and_then(|seg| seg.name_ref())
-        {
-            if name.as_name() == name![derive] {
-                attr.syntax().detach();
-            }
-        }
-    }
-    item
-}
-
-#[cfg(test)]
-mod tests {
-    use base_db::fixture::WithFixture;
-    use base_db::SourceDatabase;
-    use expect_test::{expect, Expect};
-
-    use crate::test_db::TestDB;
-
-    use super::*;
-
-    fn test_remove_derives_up_to(attr: usize, ra_fixture: &str, expect: Expect) {
-        let (db, file_id) = TestDB::with_single_file(&ra_fixture);
-        let parsed = db.parse(file_id);
-
-        let mut items: Vec<_> =
-            parsed.syntax_node().descendants().filter_map(ast::Item::cast).collect();
-        assert_eq!(items.len(), 1);
-
-        let item = remove_derives_up_to(items.pop().unwrap(), attr);
-        expect.assert_eq(&item.to_string());
-    }
-
-    #[test]
-    fn remove_derive() {
-        test_remove_derives_up_to(
-            2,
-            r#"
-#[allow(unused)]
-#[derive(Copy)]
-#[derive(Hello)]
-#[derive(Clone)]
-struct A {
-    bar: u32
-}
-        "#,
-            expect![[r#"
-#[allow(unused)]
-
-
-#[derive(Clone)]
-struct A {
-    bar: u32
-}"#]],
-        );
-    }
-}
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 88cb16ca4d..80ab3aeee7 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -14,7 +14,6 @@ pub mod builtin_macro;
 pub mod proc_macro;
 pub mod quote;
 pub mod eager;
-mod input;
 
 use either::Either;
 
@@ -272,10 +271,6 @@ impl MacroDefId {
         };
         Either::Left(*id)
     }
-
-    pub fn is_proc_macro(&self) -> bool {
-        matches!(self.kind, MacroDefKind::ProcMacro(..))
-    }
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -297,21 +292,13 @@ pub struct MacroCallLoc {
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum MacroCallKind {
-    FnLike {
-        ast_id: AstId<ast::MacroCall>,
-        fragment: FragmentKind,
-    },
-    Derive {
-        ast_id: AstId<ast::Item>,
-        derive_name: String,
-        /// Syntactical index of the invoking `#[derive]` attribute.
-        ///
-        /// Outer attributes are counted first, then inner attributes. This does not support
-        /// out-of-line modules, which may have attributes spread across 2 files!
-        derive_attr_index: u32,
-    },
+    FnLike { ast_id: AstId<ast::MacroCall>, fragment: FragmentKind },
+    Derive { ast_id: AstId<ast::Item>, derive_name: String, derive_attr: AttrId },
 }
 
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct AttrId(pub u32);
+
 impl MacroCallKind {
     fn file_id(&self) -> HirFileId {
         match self {
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs
index d5643393ae..75e9508160 100644
--- a/crates/hir_expand/src/proc_macro.rs
+++ b/crates/hir_expand/src/proc_macro.rs
@@ -2,6 +2,7 @@
 
 use crate::db::AstDatabase;
 use base_db::{CrateId, ProcMacroId};
+use tt::buffer::{Cursor, TokenBuffer};
 
 #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
 pub struct ProcMacroExpander {
@@ -43,6 +44,9 @@ impl ProcMacroExpander {
                     .clone()
                     .ok_or_else(|| err!("No derive macro found."))?;
 
+                let tt = remove_derive_attrs(tt)
+                    .ok_or_else(|| err!("Fail to remove derive for custom derive"))?;
+
                 // Proc macros have access to the environment variables of the invoking crate.
                 let env = &krate_graph[calling_crate].env;
 
@@ -52,3 +56,101 @@ impl ProcMacroExpander {
         }
     }
 }
+
+fn eat_punct(cursor: &mut Cursor, c: char) -> bool {
+    if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = cursor.token_tree() {
+        if punct.char == c {
+            *cursor = cursor.bump();
+            return true;
+        }
+    }
+    false
+}
+
+fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool {
+    if let Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) = cursor.token_tree() {
+        if Some(kind) == subtree.delimiter_kind() {
+            *cursor = cursor.bump_subtree();
+            return true;
+        }
+    }
+    false
+}
+
+fn eat_ident(cursor: &mut Cursor, t: &str) -> bool {
+    if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(ident), _)) = cursor.token_tree() {
+        if t == ident.text.as_str() {
+            *cursor = cursor.bump();
+            return true;
+        }
+    }
+    false
+}
+
+fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> {
+    let buffer = TokenBuffer::from_tokens(&tt.token_trees);
+    let mut p = buffer.begin();
+    let mut result = tt::Subtree::default();
+
+    while !p.eof() {
+        let curr = p;
+
+        if eat_punct(&mut p, '#') {
+            eat_punct(&mut p, '!');
+            let parent = p;
+            if eat_subtree(&mut p, tt::DelimiterKind::Bracket) {
+                if eat_ident(&mut p, "derive") {
+                    p = parent.bump();
+                    continue;
+                }
+            }
+        }
+
+        result.token_trees.push(curr.token_tree()?.cloned());
+        p = curr.bump();
+    }
+
+    Some(result)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use test_utils::assert_eq_text;
+
+    #[test]
+    fn test_remove_derive_attrs() {
+        let tt = mbe::parse_to_token_tree(
+            r#"
+    #[allow(unused)]
+    #[derive(Copy)]
+    #[derive(Hello)]
+    struct A {
+        bar: u32
+    }
+"#,
+        )
+        .unwrap()
+        .0;
+        let result = format!("{:#?}", remove_derive_attrs(&tt).unwrap());
+
+        assert_eq_text!(
+            r#"
+SUBTREE $
+  PUNCH   # [alone] 0
+  SUBTREE [] 1
+    IDENT   allow 2
+    SUBTREE () 3
+      IDENT   unused 4
+  IDENT   struct 15
+  IDENT   A 16
+  SUBTREE {} 17
+    IDENT   bar 18
+    PUNCH   : [alone] 19
+    IDENT   u32 20
+"#
+            .trim(),
+            &result
+        );
+    }
+}
diff --git a/crates/hir_ty/src/consts.rs b/crates/hir_ty/src/consts.rs
deleted file mode 100644
index 0044b1cffc..0000000000
--- a/crates/hir_ty/src/consts.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-//! Handling of concrete const values
-
-/// A concrete constant value
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum ConstScalar {
-    // for now, we only support the trivial case of constant evaluating the length of an array
-    // Note that this is u64 because the target usize may be bigger than our usize
-    Usize(u64),
-
-    /// Case of an unknown value that rustc might know but we don't
-    Unknown,
-}
-
-impl std::fmt::Display for ConstScalar {
-    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
-        match self {
-            ConstScalar::Usize(us) => write!(fmt, "{}", us),
-            ConstScalar::Unknown => write!(fmt, "_"),
-        }
-    }
-}
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index 47709c1e8f..79602c3dde 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -83,7 +83,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
         if let Expr::Block { statements, tail, .. } = body_expr {
             if let Some(t) = tail {
                 self.validate_results_in_tail_expr(body.body_expr, *t, db);
-            } else if let Some(Statement::Expr { expr: id, .. }) = statements.last() {
+            } else if let Some(Statement::Expr(id)) = statements.last() {
                 self.validate_missing_tail_expr(body.body_expr, *id, db);
             }
         }
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index 8a42966973..1f6edf7a28 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -308,7 +308,7 @@ impl HirDisplay for Const {
                 let param_data = &generics.params.consts[id.local_id];
                 write!(f, "{}", param_data.name)
             }
-            ConstValue::Concrete(c) => write!(f, "{}", c.interned),
+            ConstValue::Concrete(_) => write!(f, "_"),
         }
     }
 }
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 2178ffd077..50497eecb0 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -3,7 +3,7 @@
 use std::iter::{repeat, repeat_with};
 use std::{mem, sync::Arc};
 
-use chalk_ir::{cast::Cast, fold::Shift, ConstData, Mutability, TyVariableKind};
+use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind};
 use hir_def::{
     expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp},
     path::{GenericArg, GenericArgs},
@@ -15,9 +15,7 @@ use stdx::always;
 use syntax::ast::RangeOp;
 
 use crate::{
-    autoderef,
-    consts::ConstScalar,
-    dummy_usize_const,
+    autoderef, dummy_usize_const,
     lower::lower_to_chalk_mutability,
     mapping::from_chalk,
     method_resolution, op,
@@ -25,7 +23,7 @@ use crate::{
     static_lifetime, to_chalk_trait_id,
     traits::FnTrait,
     utils::{generics, Generics},
-    AdtId, Binders, CallableDefId, ConstValue, FnPointer, FnSig, FnSubst, InEnvironment, Interner,
+    AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, InEnvironment, Interner,
     ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
 };
 
@@ -719,12 +717,11 @@ impl<'a> InferenceContext<'a> {
                     _ => self.table.new_type_var(),
                 };
 
-                let len = match array {
+                match array {
                     Array::ElementList(items) => {
                         for expr in items.iter() {
                             self.infer_expr_coerce(*expr, &Expectation::has_type(elem_ty.clone()));
                         }
-                        Some(items.len())
                     }
                     Array::Repeat { initializer, repeat } => {
                         self.infer_expr_coerce(
@@ -737,20 +734,10 @@ impl<'a> InferenceContext<'a> {
                                 TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner),
                             ),
                         );
-                        // FIXME: support length for Repeat array expressions
-                        None
                     }
-                };
+                }
 
-                let cd = ConstData {
-                    ty: TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner),
-                    value: ConstValue::Concrete(chalk_ir::ConcreteConst {
-                        interned: len
-                            .map(|len| ConstScalar::Usize(len as u64))
-                            .unwrap_or(ConstScalar::Unknown),
-                    }),
-                };
-                TyKind::Array(elem_ty, cd.intern(&Interner)).intern(&Interner)
+                TyKind::Array(elem_ty, dummy_usize_const()).intern(&Interner)
             }
             Expr::Literal(lit) => match lit {
                 Literal::Bool(..) => TyKind::Scalar(Scalar::Bool).intern(&Interner),
@@ -760,7 +747,6 @@ impl<'a> InferenceContext<'a> {
                 }
                 Literal::ByteString(..) => {
                     let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner);
-
                     let array_type =
                         TyKind::Array(byte_type, dummy_usize_const()).intern(&Interner);
                     TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(&Interner)
@@ -823,7 +809,7 @@ impl<'a> InferenceContext<'a> {
                     let ty = self.resolve_ty_as_possible(ty);
                     self.infer_pat(*pat, &ty, BindingMode::default());
                 }
-                Statement::Expr { expr, .. } => {
+                Statement::Expr(expr) => {
                     self.infer_expr(*expr, &Expectation::none());
                 }
             }
diff --git a/crates/hir_ty/src/interner.rs b/crates/hir_ty/src/interner.rs
index 4cbc9cd4fa..a1656115d6 100644
--- a/crates/hir_ty/src/interner.rs
+++ b/crates/hir_ty/src/interner.rs
@@ -1,7 +1,7 @@
 //! Implementation of the Chalk `Interner` trait, which allows customizing the
 //! representation of the various objects Chalk deals with (types, goals etc.).
 
-use crate::{chalk_db, consts::ConstScalar, tls, GenericArg};
+use crate::{chalk_db, tls, GenericArg};
 use base_db::salsa::InternId;
 use chalk_ir::{Goal, GoalData};
 use hir_def::{
@@ -31,7 +31,6 @@ impl_internable!(
     InternedWrapper<chalk_ir::TyData<Interner>>,
     InternedWrapper<chalk_ir::LifetimeData<Interner>>,
     InternedWrapper<chalk_ir::ConstData<Interner>>,
-    InternedWrapper<ConstScalar>,
     InternedWrapper<Vec<chalk_ir::CanonicalVarKind<Interner>>>,
     InternedWrapper<Vec<chalk_ir::ProgramClause<Interner>>>,
     InternedWrapper<Vec<chalk_ir::QuantifiedWhereClause<Interner>>>,
@@ -42,7 +41,7 @@ impl chalk_ir::interner::Interner for Interner {
     type InternedType = Interned<InternedWrapper<chalk_ir::TyData<Interner>>>;
     type InternedLifetime = Interned<InternedWrapper<chalk_ir::LifetimeData<Self>>>;
     type InternedConst = Interned<InternedWrapper<chalk_ir::ConstData<Self>>>;
-    type InternedConcreteConst = ConstScalar;
+    type InternedConcreteConst = ();
     type InternedGenericArg = chalk_ir::GenericArgData<Self>;
     type InternedGoal = Arc<GoalData<Self>>;
     type InternedGoals = Vec<Goal<Self>>;
@@ -246,15 +245,10 @@ impl chalk_ir::interner::Interner for Interner {
     fn const_eq(
         &self,
         _ty: &Self::InternedType,
-        c1: &Self::InternedConcreteConst,
-        c2: &Self::InternedConcreteConst,
+        _c1: &Self::InternedConcreteConst,
+        _c2: &Self::InternedConcreteConst,
     ) -> bool {
-        match (c1, c2) {
-            (&ConstScalar::Usize(a), &ConstScalar::Usize(b)) => a == b,
-            // we were previously assuming this to be true, I'm not whether true or false on
-            // unknown values is safer.
-            (_, _) => true,
-        }
+        true
     }
 
     fn intern_generic_arg(
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index d23eff513b..0505fa4aea 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -12,7 +12,6 @@ mod chalk_db;
 mod chalk_ext;
 mod infer;
 mod interner;
-mod consts;
 mod lower;
 mod mapping;
 mod op;
@@ -40,7 +39,7 @@ use chalk_ir::{
 };
 use hir_def::{expr::ExprId, type_ref::Rawness, TypeParamId};
 
-use crate::{consts::ConstScalar, db::HirDatabase, display::HirDisplay, utils::generics};
+use crate::{db::HirDatabase, display::HirDisplay, utils::generics};
 
 pub use autoderef::autoderef;
 pub use builder::TyBuilder;
@@ -251,9 +250,7 @@ pub fn dummy_usize_const() -> Const {
     let usize_ty = chalk_ir::TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner);
     chalk_ir::ConstData {
         ty: usize_ty,
-        value: chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst {
-            interned: ConstScalar::Unknown,
-        }),
+        value: chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: () }),
     }
     .intern(&Interner)
 }
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 9751b45e46..c99dd8d0a4 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -174,8 +174,6 @@ impl<'a> TyLoweringContext<'a> {
             }
             TypeRef::Array(inner) => {
                 let inner_ty = self.lower_ty(inner);
-                // FIXME: we don't have length info here because we don't store an expression for
-                // the length
                 TyKind::Array(inner_ty, dummy_usize_const()).intern(&Interner)
             }
             TypeRef::Slice(inner) => {
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index 88f3d09d34..f04bcf5316 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -20,7 +20,6 @@ oorandom = "11.1.2"
 pulldown-cmark-to-cmark = "6.0.0"
 pulldown-cmark = { version = "0.8.0", default-features = false }
 url = "2.1.1"
-dot = "0.1.4"
 
 stdx = { path = "../stdx", version = "0.0.0" }
 syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 960d169f48..e0bf660c40 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -1126,7 +1126,7 @@ fn main() {
             r#"
 fn main() {
     let data = &[1i32, 2, 3];
-      //^^^^ &[i32; 3]
+      //^^^^ &[i32; _]
     for i
 }"#,
         );
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index db08547d1d..8e5b720449 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -49,7 +49,6 @@ mod syntax_tree;
 mod typing;
 mod markdown_remove;
 mod doc_links;
-mod view_crate_graph;
 
 use std::sync::Arc;
 
@@ -288,11 +287,6 @@ impl Analysis {
         self.with_db(|db| view_hir::view_hir(&db, position))
     }
 
-    /// Renders the crate graph to GraphViz "dot" syntax.
-    pub fn view_crate_graph(&self) -> Cancelable<Result<String, String>> {
-        self.with_db(|db| view_crate_graph::view_crate_graph(&db))
-    }
-
     pub fn expand_macro(&self, position: FilePosition) -> Cancelable<Option<ExpandedMacro>> {
         self.with_db(|db| expand_macro::expand_macro(db, position))
     }
diff --git a/crates/ide/src/view_crate_graph.rs b/crates/ide/src/view_crate_graph.rs
deleted file mode 100644
index df6cc8aedd..0000000000
--- a/crates/ide/src/view_crate_graph.rs
+++ /dev/null
@@ -1,90 +0,0 @@
-use std::sync::Arc;
-
-use dot::{Id, LabelText};
-use ide_db::{
-    base_db::{CrateGraph, CrateId, Dependency, SourceDatabase, SourceDatabaseExt},
-    RootDatabase,
-};
-use rustc_hash::FxHashSet;
-
-// Feature: View Crate Graph
-//
-// Renders the currently loaded crate graph as an SVG graphic. Requires the `dot` tool, which
-// is part of graphviz, to be installed.
-//
-// Only workspace crates are included, no crates.io dependencies or sysroot crates.
-//
-// |===
-// | Editor  | Action Name
-//
-// | VS Code | **Rust Analyzer: View Crate Graph**
-// |===
-pub(crate) fn view_crate_graph(db: &RootDatabase) -> Result<String, String> {
-    let crate_graph = db.crate_graph();
-    let crates_to_render = crate_graph
-        .iter()
-        .filter(|krate| {
-            // Only render workspace crates
-            let root_id = db.file_source_root(crate_graph[*krate].root_file_id);
-            !db.source_root(root_id).is_library
-        })
-        .collect();
-    let graph = DotCrateGraph { graph: crate_graph, crates_to_render };
-
-    let mut dot = Vec::new();
-    dot::render(&graph, &mut dot).unwrap();
-    Ok(String::from_utf8(dot).unwrap())
-}
-
-struct DotCrateGraph {
-    graph: Arc<CrateGraph>,
-    crates_to_render: FxHashSet<CrateId>,
-}
-
-type Edge<'a> = (CrateId, &'a Dependency);
-
-impl<'a> dot::GraphWalk<'a, CrateId, Edge<'a>> for DotCrateGraph {
-    fn nodes(&'a self) -> dot::Nodes<'a, CrateId> {
-        self.crates_to_render.iter().copied().collect()
-    }
-
-    fn edges(&'a self) -> dot::Edges<'a, Edge<'a>> {
-        self.crates_to_render
-            .iter()
-            .flat_map(|krate| {
-                self.graph[*krate]
-                    .dependencies
-                    .iter()
-                    .filter(|dep| self.crates_to_render.contains(&dep.crate_id))
-                    .map(move |dep| (*krate, dep))
-            })
-            .collect()
-    }
-
-    fn source(&'a self, edge: &Edge<'a>) -> CrateId {
-        edge.0
-    }
-
-    fn target(&'a self, edge: &Edge<'a>) -> CrateId {
-        edge.1.crate_id
-    }
-}
-
-impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph {
-    fn graph_id(&'a self) -> Id<'a> {
-        Id::new("rust_analyzer_crate_graph").unwrap()
-    }
-
-    fn node_id(&'a self, n: &CrateId) -> Id<'a> {
-        Id::new(format!("_{}", n.0)).unwrap()
-    }
-
-    fn node_shape(&'a self, _node: &CrateId) -> Option<LabelText<'a>> {
-        Some(LabelText::LabelStr("box".into()))
-    }
-
-    fn node_label(&'a self, n: &CrateId) -> LabelText<'a> {
-        let name = self.graph[*n].display_name.as_ref().map_or("(unnamed crate)", |name| &*name);
-        LabelText::LabelStr(name.into())
-    }
-}
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs
index a454a2af32..506cc292c4 100644
--- a/crates/ide_assists/src/handlers/auto_import.rs
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -966,6 +966,32 @@ mod bar {
         println!("Hallo");
     }
 }
+"#,
+        );
+    }
+
+    #[test]
+    fn uses_abs_path_with_extern_crate_clash() {
+        check_assist(
+            auto_import,
+            r#"
+//- /main.rs crate:main deps:foo
+mod foo {}
+
+const _: () = {
+    Foo$0
+};
+//- /foo.rs crate:foo
+pub struct Foo
+"#,
+            r#"
+use ::foo::Foo;
+
+mod foo {}
+
+const _: () = {
+    Foo
+};
 "#,
         );
     }
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
index 8e21783919..66f274fa78 100644
--- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -151,37 +151,20 @@ fn create_struct_def(
     field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>,
     visibility: Option<ast::Visibility>,
 ) -> ast::Struct {
-    let pub_vis = make::visibility_pub();
-
-    let insert_pub = |node: &'_ SyntaxNode| {
-        let pub_vis = pub_vis.clone_for_update();
-        ted::insert(ted::Position::before(node), pub_vis.syntax());
-    };
-
-    // for fields without any existing visibility, use pub visibility
+    let pub_vis = Some(make::visibility_pub());
     let field_list = match field_list {
         Either::Left(field_list) => {
-            let field_list = field_list.clone_for_update();
-
-            field_list
-                .fields()
-                .filter(|field| field.visibility().is_none())
-                .filter_map(|field| field.name())
-                .for_each(|it| insert_pub(it.syntax()));
-
-            field_list.into()
+            make::record_field_list(field_list.fields().flat_map(|field| {
+                Some(make::record_field(pub_vis.clone(), field.name()?, field.ty()?))
+            }))
+            .into()
         }
-        Either::Right(field_list) => {
-            let field_list = field_list.clone_for_update();
-
+        Either::Right(field_list) => make::tuple_field_list(
             field_list
                 .fields()
-                .filter(|field| field.visibility().is_none())
-                .filter_map(|field| field.ty())
-                .for_each(|it| insert_pub(it.syntax()));
-
-            field_list.into()
-        }
+                .flat_map(|field| Some(make::tuple_field(pub_vis.clone(), field.ty()?))),
+        )
+        .into(),
     };
 
     make::struct_(visibility, variant_name, None, field_list).clone_for_update()
@@ -307,106 +290,6 @@ enum A { One(One) }"#,
             "enum A { $0One { foo: u32 } }",
             r#"struct One{ pub foo: u32 }
 
-enum A { One(One) }"#,
-        );
-    }
-
-    #[test]
-    fn test_extract_struct_keep_comments_and_attrs_one_field_named() {
-        check_assist(
-            extract_struct_from_enum_variant,
-            r#"
-enum A {
-    $0One {
-        // leading comment
-        /// doc comment
-        #[an_attr]
-        foo: u32
-        // trailing comment
-    }
-}"#,
-            r#"
-struct One{
-        // leading comment
-        /// doc comment
-        #[an_attr]
-        pub foo: u32
-        // trailing comment
-    }
-
-enum A {
-    One(One)
-}"#,
-        );
-    }
-
-    #[test]
-    fn test_extract_struct_keep_comments_and_attrs_several_fields_named() {
-        check_assist(
-            extract_struct_from_enum_variant,
-            r#"
-enum A {
-    $0One {
-        // comment
-        /// doc
-        #[attr]
-        foo: u32,
-        // comment
-        #[attr]
-        /// doc
-        bar: u32
-    }
-}"#,
-            r#"
-struct One{
-        // comment
-        /// doc
-        #[attr]
-        pub foo: u32,
-        // comment
-        #[attr]
-        /// doc
-        pub bar: u32
-    }
-
-enum A {
-    One(One)
-}"#,
-        );
-    }
-
-    #[test]
-    fn test_extract_struct_keep_comments_and_attrs_several_fields_tuple() {
-        check_assist(
-            extract_struct_from_enum_variant,
-            "enum A { $0One(/* comment */ #[attr] u32, /* another */ u32 /* tail */) }",
-            r#"
-struct One(/* comment */ #[attr] pub u32, /* another */ pub u32 /* tail */);
-
-enum A { One(One) }"#,
-        );
-    }
-
-    #[test]
-    fn test_extract_struct_keep_existing_visibility_named() {
-        check_assist(
-            extract_struct_from_enum_variant,
-            "enum A { $0One{ pub a: u32, pub(crate) b: u32, pub(super) c: u32, d: u32 } }",
-            r#"
-struct One{ pub a: u32, pub(crate) b: u32, pub(super) c: u32, pub d: u32 }
-
-enum A { One(One) }"#,
-        );
-    }
-
-    #[test]
-    fn test_extract_struct_keep_existing_visibility_tuple() {
-        check_assist(
-            extract_struct_from_enum_variant,
-            "enum A { $0One(pub u32, pub(crate) u32, pub(super) u32, u32) }",
-            r#"
-struct One(pub u32, pub(crate) u32, pub(super) u32, pub u32);
-
 enum A { One(One) }"#,
         );
     }
diff --git a/crates/ide_assists/src/handlers/pull_assignment_up.rs b/crates/ide_assists/src/handlers/pull_assignment_up.rs
index 3128faa68b..28d14b9c3f 100644
--- a/crates/ide_assists/src/handlers/pull_assignment_up.rs
+++ b/crates/ide_assists/src/handlers/pull_assignment_up.rs
@@ -60,12 +60,6 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Opti
         return None;
     };
 
-    if let Some(parent) = tgt.syntax().parent() {
-        if matches!(parent.kind(), syntax::SyntaxKind::BIN_EXPR | syntax::SyntaxKind::LET_STMT) {
-            return None;
-        }
-    }
-
     acc.add(
         AssistId("pull_assignment_up", AssistKind::RefactorExtract),
         "Pull assignment up",
@@ -80,13 +74,7 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Opti
             let tgt = edit.make_ast_mut(tgt);
 
             for (stmt, rhs) in assignments {
-                let mut stmt = stmt.syntax().clone();
-                if let Some(parent) = stmt.parent() {
-                    if ast::ExprStmt::cast(parent.clone()).is_some() {
-                        stmt = parent.clone();
-                    }
-                }
-                ted::replace(stmt, rhs.syntax());
+                ted::replace(stmt.syntax(), rhs.syntax());
             }
             let assign_expr = make::expr_assignment(collector.common_lhs, tgt.clone());
             let assign_stmt = make::expr_stmt(assign_expr);
@@ -99,7 +87,7 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Opti
 struct AssignmentsCollector<'a> {
     sema: &'a hir::Semantics<'a, ide_db::RootDatabase>,
     common_lhs: ast::Expr,
-    assignments: Vec<(ast::BinExpr, ast::Expr)>,
+    assignments: Vec<(ast::ExprStmt, ast::Expr)>,
 }
 
 impl<'a> AssignmentsCollector<'a> {
@@ -107,7 +95,6 @@ impl<'a> AssignmentsCollector<'a> {
         for arm in match_expr.match_arm_list()?.arms() {
             match arm.expr()? {
                 ast::Expr::BlockExpr(block) => self.collect_block(&block)?,
-                ast::Expr::BinExpr(expr) => self.collect_expr(&expr)?,
                 _ => return None,
             }
         }
@@ -127,28 +114,22 @@ impl<'a> AssignmentsCollector<'a> {
         }
     }
     fn collect_block(&mut self, block: &ast::BlockExpr) -> Option<()> {
-        let last_expr = block.tail_expr().or_else(|| {
-            if let ast::Stmt::ExprStmt(stmt) = block.statements().last()? {
-                stmt.expr()
-            } else {
-                None
-            }
-        })?;
-
-        if let ast::Expr::BinExpr(expr) = last_expr {
-            return self.collect_expr(&expr);
+        if block.tail_expr().is_some() {
+            return None;
         }
 
-        None
-    }
-
-    fn collect_expr(&mut self, expr: &ast::BinExpr) -> Option<()> {
-        if expr.op_kind()? == ast::BinOp::Assignment
-            && is_equivalent(self.sema, &expr.lhs()?, &self.common_lhs)
-        {
-            self.assignments.push((expr.clone(), expr.rhs()?));
-            return Some(());
+        let last_stmt = block.statements().last()?;
+        if let ast::Stmt::ExprStmt(stmt) = last_stmt {
+            if let ast::Expr::BinExpr(expr) = stmt.expr()? {
+                if expr.op_kind()? == ast::BinOp::Assignment
+                    && is_equivalent(self.sema, &expr.lhs()?, &self.common_lhs)
+                {
+                    self.assignments.push((stmt, expr.rhs()?));
+                    return Some(());
+                }
+            }
         }
+
         None
     }
 }
@@ -260,6 +241,7 @@ fn foo() {
     }
 
     #[test]
+    #[ignore]
     fn test_pull_assignment_up_assignment_expressions() {
         check_assist(
             pull_assignment_up,
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index 4a4996cf4d..b18699b771 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -119,32 +119,6 @@ pub struct RustAnalyzerPackageMetaData {
 pub struct PackageDependency {
     pub pkg: Package,
     pub name: String,
-    pub kind: DepKind,
-}
-
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub enum DepKind {
-    /// Available to the library, binary, and dev targets in the package (but not the build script).
-    Normal,
-    /// Available only to test and bench targets (and the library target, when built with `cfg(test)`).
-    Dev,
-    /// Available only to the build script target.
-    Build,
-}
-
-impl DepKind {
-    fn new(list: &[cargo_metadata::DepKindInfo]) -> Self {
-        for info in list {
-            match info.kind {
-                cargo_metadata::DependencyKind::Normal => return Self::Normal,
-                cargo_metadata::DependencyKind::Development => return Self::Dev,
-                cargo_metadata::DependencyKind::Build => return Self::Build,
-                cargo_metadata::DependencyKind::Unknown => continue,
-            }
-        }
-
-        Self::Normal
-    }
 }
 
 /// Information associated with a package's target
@@ -170,7 +144,6 @@ pub enum TargetKind {
     Example,
     Test,
     Bench,
-    BuildScript,
     Other,
 }
 
@@ -182,7 +155,6 @@ impl TargetKind {
                 "test" => TargetKind::Test,
                 "bench" => TargetKind::Bench,
                 "example" => TargetKind::Example,
-                "custom-build" => TargetKind::BuildScript,
                 "proc-macro" => TargetKind::Lib,
                 _ if kind.contains("lib") => TargetKind::Lib,
                 _ => continue,
@@ -329,11 +301,7 @@ impl CargoWorkspace {
                         continue;
                     }
                 };
-                let dep = PackageDependency {
-                    name: dep_node.name,
-                    pkg,
-                    kind: DepKind::new(&dep_node.dep_kinds),
-                };
+                let dep = PackageDependency { name: dep_node.name, pkg };
                 packages[source].dependencies.push(dep);
             }
             packages[source].active_features.extend(node.features);
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 607e62ea59..84c702fdf7 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -6,7 +6,6 @@ use std::{collections::VecDeque, fmt, fs, path::Path, process::Command};
 
 use anyhow::{Context, Result};
 use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro};
-use cargo_workspace::DepKind;
 use cfg::CfgOptions;
 use paths::{AbsPath, AbsPathBuf};
 use proc_macro_api::ProcMacroClient;
@@ -391,7 +390,6 @@ fn cargo_to_crate_graph(
                     &cfg_options,
                     proc_macro_loader,
                     file_id,
-                    &cargo[tgt].name,
                 );
                 if cargo[tgt].kind == TargetKind::Lib {
                     lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
@@ -408,25 +406,23 @@ fn cargo_to_crate_graph(
                     }
                 }
 
-                pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
+                pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
             }
         }
 
         // Set deps to the core, std and to the lib target of the current package
-        for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
+        for &from in pkg_crates.get(&pkg).into_iter().flatten() {
             if let Some((to, name)) = lib_tgt.clone() {
-                if to != *from && *kind != TargetKind::BuildScript {
-                    // (build script can not depend on its library target)
-
+                if to != from {
                     // For root projects with dashes in their name,
                     // cargo metadata does not do any normalization,
                     // so we do it ourselves currently
                     let name = CrateName::normalize_dashes(&name);
-                    add_dep(&mut crate_graph, *from, name, to);
+                    add_dep(&mut crate_graph, from, name, to);
                 }
             }
             for (name, krate) in public_deps.iter() {
-                add_dep(&mut crate_graph, *from, name.clone(), *krate);
+                add_dep(&mut crate_graph, from, name.clone(), *krate);
             }
         }
     }
@@ -437,17 +433,8 @@ fn cargo_to_crate_graph(
         for dep in cargo[pkg].dependencies.iter() {
             let name = CrateName::new(&dep.name).unwrap();
             if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
-                for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
-                    if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
-                        // Only build scripts may depend on build dependencies.
-                        continue;
-                    }
-                    if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
-                        // Build scripts may only depend on build dependencies.
-                        continue;
-                    }
-
-                    add_dep(&mut crate_graph, *from, name.clone(), to)
+                for &from in pkg_crates.get(&pkg).into_iter().flatten() {
+                    add_dep(&mut crate_graph, from, name.clone(), to)
                 }
             }
         }
@@ -484,7 +471,7 @@ fn handle_rustc_crates(
     pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
     public_deps: &[(CrateName, CrateId)],
     cargo: &CargoWorkspace,
-    pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
+    pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<CrateId>>,
 ) {
     let mut rustc_pkg_crates = FxHashMap::default();
     // The root package of the rustc-dev component is rustc_driver, so we match that
@@ -518,7 +505,6 @@ fn handle_rustc_crates(
                         &cfg_options,
                         proc_macro_loader,
                         file_id,
-                        &rustc_workspace[tgt].name,
                     );
                     pkg_to_lib_crate.insert(pkg, crate_id);
                     // Add dependencies on core / std / alloc for this crate
@@ -553,13 +539,13 @@ fn handle_rustc_crates(
                 if !package.metadata.rustc_private {
                     continue;
                 }
-                for (from, _) in pkg_crates.get(&pkg).into_iter().flatten() {
+                for &from in pkg_crates.get(&pkg).into_iter().flatten() {
                     // Avoid creating duplicate dependencies
                     // This avoids the situation where `from` depends on e.g. `arrayvec`, but
                     // `rust_analyzer` thinks that it should use the one from the `rustcSource`
                     // instead of the one from `crates.io`
-                    if !crate_graph[*from].dependencies.iter().any(|d| d.name == name) {
-                        add_dep(crate_graph, *from, name.clone(), to);
+                    if !crate_graph[from].dependencies.iter().any(|d| d.name == name) {
+                        add_dep(crate_graph, from, name.clone(), to);
                     }
                 }
             }
@@ -574,7 +560,6 @@ fn add_target_crate_root(
     cfg_options: &CfgOptions,
     proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>,
     file_id: FileId,
-    cargo_name: &str,
 ) -> CrateId {
     let edition = pkg.edition;
     let cfg_options = {
@@ -601,7 +586,7 @@ fn add_target_crate_root(
         .map(|it| proc_macro_loader(&it))
         .unwrap_or_default();
 
-    let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
+    let display_name = CrateDisplayName::from_canonical_name(pkg.name.clone());
     let crate_id = crate_graph.add_crate_root(
         file_id,
         edition,
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index f4cd43448d..909c215322 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -159,7 +159,7 @@ impl CargoTargetSpec {
             TargetKind::Lib => {
                 buf.push("--lib".to_string());
             }
-            TargetKind::Other | TargetKind::BuildScript => (),
+            TargetKind::Other => (),
         }
     }
 }
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 551013aa9c..f6e40f8722 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -3,8 +3,8 @@
 //! `ide` crate.
 
 use std::{
-    io::{Read, Write as _},
-    process::{self, Command, Stdio},
+    io::Write as _,
+    process::{self, Stdio},
 };
 
 use ide::{
@@ -117,24 +117,6 @@ pub(crate) fn handle_view_hir(
     Ok(res)
 }
 
-pub(crate) fn handle_view_crate_graph(snap: GlobalStateSnapshot, (): ()) -> Result<String> {
-    let _p = profile::span("handle_view_crate_graph");
-    let dot = snap.analysis.view_crate_graph()??;
-
-    // We shell out to `dot` to render to SVG, as there does not seem to be a pure-Rust renderer.
-    let child = Command::new("dot")
-        .arg("-Tsvg")
-        .stdin(Stdio::piped())
-        .stdout(Stdio::piped())
-        .spawn()
-        .map_err(|err| format!("failed to spawn `dot`: {}", err))?;
-    child.stdin.unwrap().write_all(dot.as_bytes())?;
-
-    let mut svg = String::new();
-    child.stdout.unwrap().read_to_string(&mut svg)?;
-    Ok(svg)
-}
-
 pub(crate) fn handle_expand_macro(
     snap: GlobalStateSnapshot,
     params: lsp_ext::ExpandMacroParams,
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 3bd0980580..b8835a5349 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -61,14 +61,6 @@ impl Request for ViewHir {
     const METHOD: &'static str = "rust-analyzer/viewHir";
 }
 
-pub enum ViewCrateGraph {}
-
-impl Request for ViewCrateGraph {
-    type Params = ();
-    type Result = String;
-    const METHOD: &'static str = "rust-analyzer/viewCrateGraph";
-}
-
 pub enum ExpandMacro {}
 
 impl Request for ExpandMacro {
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index c7bd7eee1a..ce7ece559f 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -513,7 +513,6 @@ impl GlobalState {
             .on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)
             .on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
             .on::<lsp_ext::ViewHir>(handlers::handle_view_hir)
-            .on::<lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph)
             .on::<lsp_ext::ExpandMacro>(handlers::handle_expand_macro)
             .on::<lsp_ext::ParentModule>(handlers::handle_parent_module)
             .on::<lsp_ext::Runnables>(handlers::handle_runnables)
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 8fcd72d5d5..f0f981802e 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
 <!---
-lsp_ext.rs hash: 6e57fc1b345b00e9
+lsp_ext.rs hash: 28a9d5a24b7ca396
 
 If you need to change the above hash to make the test pass, please check if you
 need to adjust this doc as well and ping this issue:
@@ -486,16 +486,6 @@ Primarily for debugging, but very useful for all people working on rust-analyzer
 Returns a textual representation of the HIR of the function containing the cursor.
 For debugging or when working on rust-analyzer itself.
 
-## View Crate Graph
-
-**Method:** `rust-analyzer/viewCrateGraph`
-
-**Request:** `null`
-
-**Response:** `string`
-
-Renders rust-analyzer's crate graph as an SVG image.
-
 ## Expand Macro
 
 **Method:** `rust-analyzer/expandMacro`
diff --git a/editors/code/package.json b/editors/code/package.json
index 0f38a1673a..f35d30898f 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -109,11 +109,6 @@
                 "title": "View Hir",
                 "category": "Rust Analyzer"
             },
-            {
-                "command": "rust-analyzer.viewCrateGraph",
-                "title": "View Crate Graph",
-                "category": "Rust Analyzer"
-            },
             {
                 "command": "rust-analyzer.expandMacro",
                 "title": "Expand macro recursively",
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 8ab259af22..4092435dbe 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -429,14 +429,6 @@ export function viewHir(ctx: Ctx): Cmd {
     };
 }
 
-export function viewCrateGraph(ctx: Ctx): Cmd {
-    return async () => {
-        const panel = vscode.window.createWebviewPanel("rust-analyzer.crate-graph", "rust-analyzer crate graph", vscode.ViewColumn.Two);
-        const svg = await ctx.client.sendRequest(ra.viewCrateGraph);
-        panel.webview.html = svg;
-    };
-}
-
 // Opens the virtual file that will show the syntax tree
 //
 // The contents of the file come from the `TextDocumentContentProvider`
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index aa745a65ce..f78de894b5 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -27,8 +27,6 @@ export const syntaxTree = new lc.RequestType<SyntaxTreeParams, string, void>("ru
 
 export const viewHir = new lc.RequestType<lc.TextDocumentPositionParams, string, void>("rust-analyzer/viewHir");
 
-export const viewCrateGraph = new lc.RequestType0<string, void>("rust-analyzer/viewCrateGraph");
-
 export interface ExpandMacroParams {
     textDocument: lc.TextDocumentIdentifier;
     position: lc.Position;
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 516322d035..643fb643f3 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -106,7 +106,6 @@ async function tryActivate(context: vscode.ExtensionContext) {
     ctx.registerCommand('parentModule', commands.parentModule);
     ctx.registerCommand('syntaxTree', commands.syntaxTree);
     ctx.registerCommand('viewHir', commands.viewHir);
-    ctx.registerCommand('viewCrateGraph', commands.viewCrateGraph);
     ctx.registerCommand('expandMacro', commands.expandMacro);
     ctx.registerCommand('run', commands.run);
     ctx.registerCommand('copyRunCommandLine', commands.copyRunCommandLine);


Test patch:
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs
index aad3d610e2..63d9d4e0b7 100644
--- a/crates/hir_ty/src/tests/coercion.rs
+++ b/crates/hir_ty/src/tests/coercion.rs
@@ -55,7 +55,7 @@ fn coerce_places() {
         impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
         impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
         "#,
-        expect![[r#"
+        expect![[r"
             30..31 '_': &[T]
             44..55 '{ loop {} }': T
             46..53 'loop {}': !
@@ -72,8 +72,8 @@ fn coerce_places() {
             165..170 'gen()': *mut [U; _]
             185..419 '{     ...rr); }': ()
             195..198 'arr': &[u8; _]
-            211..215 '&[1]': &[u8; 1]
-            212..215 '[1]': [u8; 1]
+            211..215 '&[1]': &[u8; _]
+            212..215 '[1]': [u8; _]
             213..214 '1': u8
             226..227 'a': &[u8]
             236..239 'arr': &[u8; _]
@@ -90,7 +90,7 @@ fn coerce_places() {
             302..314 'S { a: arr }': S<&[u8]>
             309..312 'arr': &[u8; _]
             325..326 'e': [&[u8]; _]
-            340..345 '[arr]': [&[u8]; 1]
+            340..345 '[arr]': [&[u8]; _]
             341..344 'arr': &[u8; _]
             355..356 'f': [&[u8]; _]
             370..378 '[arr; 2]': [&[u8]; _]
@@ -100,7 +100,7 @@ fn coerce_places() {
             406..416 '(arr, arr)': (&[u8], &[u8])
             407..410 'arr': &[u8; _]
             412..415 'arr': &[u8; _]
-        "#]],
+        "]],
     );
 }
 
@@ -113,17 +113,17 @@ fn infer_let_stmt_coerce() {
             let x: *const [isize] = &[1];
         }
         ",
-        expect![[r#"
+        expect![[r"
             10..75 '{     ...[1]; }': ()
             20..21 'x': &[isize]
-            34..38 '&[1]': &[isize; 1]
-            35..38 '[1]': [isize; 1]
+            34..38 '&[1]': &[isize; _]
+            35..38 '[1]': [isize; _]
             36..37 '1': isize
             48..49 'x': *const [isize]
-            68..72 '&[1]': &[isize; 1]
-            69..72 '[1]': [isize; 1]
+            68..72 '&[1]': &[isize; _]
+            69..72 '[1]': [isize; _]
             70..71 '1': isize
-        "#]],
+        "]],
     );
 }
 
@@ -208,7 +208,7 @@ fn infer_if_coerce() {
         #[lang = "unsize"]
         pub trait Unsize<T: ?Sized> {}
         "#,
-        expect![[r#"
+        expect![[r"
             10..11 'x': &[T]
             27..38 '{ loop {} }': &[T]
             29..36 'loop {}': !
@@ -220,14 +220,14 @@ fn infer_if_coerce() {
             71..96 '{     ...     }': &[i32]
             81..84 'foo': fn foo<i32>(&[i32]) -> &[i32]
             81..90 'foo(&[1])': &[i32]
-            85..89 '&[1]': &[i32; 1]
-            86..89 '[1]': [i32; 1]
+            85..89 '&[1]': &[i32; _]
+            86..89 '[1]': [i32; _]
             87..88 '1': i32
-            102..122 '{     ...     }': &[i32; 1]
-            112..116 '&[1]': &[i32; 1]
-            113..116 '[1]': [i32; 1]
+            102..122 '{     ...     }': &[i32; _]
+            112..116 '&[1]': &[i32; _]
+            113..116 '[1]': [i32; _]
             114..115 '1': i32
-        "#]],
+        "]],
     );
 }
 
@@ -254,7 +254,7 @@ fn infer_if_else_coerce() {
         impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
         impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
         "#,
-        expect![[r#"
+        expect![[r"
             10..11 'x': &[T]
             27..38 '{ loop {} }': &[T]
             29..36 'loop {}': !
@@ -263,17 +263,17 @@ fn infer_if_else_coerce() {
             59..60 'x': &[i32]
             63..122 'if tru...     }': &[i32]
             66..70 'true': bool
-            71..91 '{     ...     }': &[i32; 1]
-            81..85 '&[1]': &[i32; 1]
-            82..85 '[1]': [i32; 1]
+            71..91 '{     ...     }': &[i32; _]
+            81..85 '&[1]': &[i32; _]
+            82..85 '[1]': [i32; _]
             83..84 '1': i32
             97..122 '{     ...     }': &[i32]
             107..110 'foo': fn foo<i32>(&[i32]) -> &[i32]
             107..116 'foo(&[1])': &[i32]
-            111..115 '&[1]': &[i32; 1]
-            112..115 '[1]': [i32; 1]
+            111..115 '&[1]': &[i32; _]
+            112..115 '[1]': [i32; _]
             113..114 '1': i32
-        "#]],
+        "]],
     )
 }
 
@@ -295,7 +295,7 @@ fn infer_match_first_coerce() {
         #[lang = "unsize"]
         pub trait Unsize<T: ?Sized> {}
         "#,
-        expect![[r#"
+        expect![[r"
             10..11 'x': &[T]
             27..38 '{ loop {} }': &[T]
             29..36 'loop {}': !
@@ -309,19 +309,19 @@ fn infer_match_first_coerce() {
             87..88 '2': i32
             92..95 'foo': fn foo<i32>(&[i32]) -> &[i32]
             92..101 'foo(&[2])': &[i32]
-            96..100 '&[2]': &[i32; 1]
-            97..100 '[2]': [i32; 1]
+            96..100 '&[2]': &[i32; _]
+            97..100 '[2]': [i32; _]
             98..99 '2': i32
             111..112 '1': i32
             111..112 '1': i32
-            116..120 '&[1]': &[i32; 1]
-            117..120 '[1]': [i32; 1]
+            116..120 '&[1]': &[i32; _]
+            117..120 '[1]': [i32; _]
             118..119 '1': i32
             130..131 '_': i32
-            135..139 '&[3]': &[i32; 1]
-            136..139 '[3]': [i32; 1]
+            135..139 '&[3]': &[i32; _]
+            136..139 '[3]': [i32; _]
             137..138 '3': i32
-        "#]],
+    "]],
     );
 }
 
@@ -348,7 +348,7 @@ fn infer_match_second_coerce() {
         impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
         impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
         "#,
-        expect![[r#"
+        expect![[r"
             10..11 'x': &[T]
             27..38 '{ loop {} }': &[T]
             29..36 'loop {}': !
@@ -360,21 +360,21 @@ fn infer_match_second_coerce() {
             75..76 'i': i32
             87..88 '1': i32
             87..88 '1': i32
-            92..96 '&[1]': &[i32; 1]
-            93..96 '[1]': [i32; 1]
+            92..96 '&[1]': &[i32; _]
+            93..96 '[1]': [i32; _]
             94..95 '1': i32
             106..107 '2': i32
             106..107 '2': i32
             111..114 'foo': fn foo<i32>(&[i32]) -> &[i32]
             111..120 'foo(&[2])': &[i32]
-            115..119 '&[2]': &[i32; 1]
-            116..119 '[2]': [i32; 1]
+            115..119 '&[2]': &[i32; _]
+            116..119 '[2]': [i32; _]
             117..118 '2': i32
             130..131 '_': i32
-            135..139 '&[3]': &[i32; 1]
-            136..139 '[3]': [i32; 1]
+            135..139 '&[3]': &[i32; _]
+            136..139 '[3]': [i32; _]
             137..138 '3': i32
-        "#]],
+    "]],
     );
 }
 
@@ -685,15 +685,15 @@ fn coerce_unsize_array() {
             let f: &[usize] = &[1, 2, 3];
         }
         "#,
-        expect![[r#"
+        expect![[r"
             161..198 '{     ... 3]; }': ()
             171..172 'f': &[usize]
-            185..195 '&[1, 2, 3]': &[usize; 3]
-            186..195 '[1, 2, 3]': [usize; 3]
+            185..195 '&[1, 2, 3]': &[usize; _]
+            186..195 '[1, 2, 3]': [usize; _]
             187..188 '1': usize
             190..191 '2': usize
             193..194 '3': usize
-        "#]],
+        "]],
     );
 }
 
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs
index 33305f208f..f514b3efe6 100644
--- a/crates/hir_ty/src/tests/patterns.rs
+++ b/crates/hir_ty/src/tests/patterns.rs
@@ -243,8 +243,8 @@ fn infer_pattern_match_slice() {
         expect![[r#"
             10..209 '{     ...   } }': ()
             20..25 'slice': &[f64]
-            36..42 '&[0.0]': &[f64; 1]
-            37..42 '[0.0]': [f64; 1]
+            36..42 '&[0.0]': &[f64; _]
+            37..42 '[0.0]': [f64; _]
             38..41 '0.0': f64
             48..207 'match ...     }': ()
             54..59 'slice': &[f64]
@@ -346,7 +346,7 @@ fn infer_pattern_match_arr() {
         expect![[r#"
             10..179 '{     ...   } }': ()
             20..23 'arr': [f64; _]
-            36..46 '[0.0, 1.0]': [f64; 2]
+            36..46 '[0.0, 1.0]': [f64; _]
             37..40 '0.0': f64
             42..45 '1.0': f64
             52..177 'match ...     }': ()
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index 4318617124..d14f5c9bb1 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -99,7 +99,7 @@ fn recursive_vars() {
             10..47 '{     ...&y]; }': ()
             20..21 'y': &{unknown}
             24..31 'unknown': &{unknown}
-            37..44 '[y, &y]': [&&{unknown}; 2]
+            37..44 '[y, &y]': [&&{unknown}; _]
             38..39 'y': &{unknown}
             41..43 '&y': &&{unknown}
             42..43 'y': &{unknown}
@@ -123,7 +123,7 @@ fn recursive_vars_2() {
             24..31 'unknown': &&{unknown}
             41..42 'y': &&{unknown}
             45..52 'unknown': &&{unknown}
-            58..76 '[(x, y..., &x)]': [(&&&{unknown}, &&&{unknown}); 2]
+            58..76 '[(x, y..., &x)]': [(&&&{unknown}, &&&{unknown}); _]
             59..65 '(x, y)': (&&&{unknown}, &&&{unknown})
             60..61 'x': &&{unknown}
             63..64 'y': &&{unknown}
@@ -175,8 +175,8 @@ fn infer_std_crash_2() {
         "#,
         expect![[r#"
             22..52 '{     ...n']; }': ()
-            28..49 '&[0, b...b'\n']': &[u8; 4]
-            29..49 '[0, b'...b'\n']': [u8; 4]
+            28..49 '&[0, b...b'\n']': &[u8; _]
+            29..49 '[0, b'...b'\n']': [u8; _]
             30..31 '0': u8
             33..38 'b'\n'': u8
             40..41 '1': u8
@@ -336,8 +336,8 @@ fn infer_array_macro_call() {
         expect![[r#"
             !0..4 '0u32': u32
             44..69 '{     ...()]; }': ()
-            54..55 'a': [u32; 1]
-            58..66 '[bar!()]': [u32; 1]
+            54..55 'a': [u32; _]
+            58..66 '[bar!()]': [u32; _]
         "#]],
     );
 }
@@ -1050,52 +1050,3 @@ fn test() {
         "#]],
     );
 }
-
-#[test]
-fn cfg_tail() {
-    // https://github.com/rust-analyzer/rust-analyzer/issues/8378
-    check_infer(
-        r#"
-        fn fake_tail(){
-            { "first" }
-            #[cfg(never)] 9
-        }
-        fn multiple_fake(){
-            { "fake" }
-            { "fake" }
-            { "second" }
-            #[cfg(never)] { 11 }
-            #[cfg(never)] 12;
-            #[cfg(never)] 13
-        }
-        fn no_normal_tail(){
-            { "third" }
-            #[cfg(never)] 14;
-            #[cfg(never)] 15;
-        }
-        fn no_actual_tail(){
-            { "fourth" };
-            #[cfg(never)] 14;
-            #[cfg(never)] 15
-        }
-        "#,
-        expect![[r#"
-            14..53 '{     ...)] 9 }': &str
-            20..31 '{ "first" }': &str
-            22..29 '"first"': &str
-            72..190 '{     ...] 13 }': &str
-            78..88 '{ "fake" }': &str
-            80..86 '"fake"': &str
-            93..103 '{ "fake" }': &str
-            95..101 '"fake"': &str
-            108..120 '{ "second" }': &str
-            110..118 '"second"': &str
-            210..273 '{     ... 15; }': &str
-            216..227 '{ "third" }': &str
-            218..225 '"third"': &str
-            293..357 '{     ...] 15 }': ()
-            299..311 '{ "fourth" }': &str
-            301..309 '"fourth"': &str
-        "#]],
-    )
-}
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index 8b09f2e4a0..0eefd70f22 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -11,7 +11,7 @@ fn test() {
     let x = box 1;
     let t = (x, box x, box &1, box [1]);
     t;
-} //^ (Box<i32>, Box<Box<i32>>, Box<&i32>, Box<[i32; 1]>)
+} //^ (Box<i32>, Box<Box<i32>>, Box<&i32>, Box<[i32; _]>)
 
 //- /std.rs crate:std
 #[prelude_import] use prelude::*;
@@ -36,7 +36,7 @@ fn test() {
     let x = box 1;
     let t = (x, box x, box &1, box [1]);
     t;
-} //^ (Box<i32, {unknown}>, Box<Box<i32, {unknown}>, {unknown}>, Box<&i32, {unknown}>, Box<[i32; 1], {unknown}>)
+} //^ (Box<i32, {unknown}>, Box<Box<i32, {unknown}>, {unknown}>, Box<&i32, {unknown}>, Box<[i32; _], {unknown}>)
 
 //- /std.rs crate:std
 #[prelude_import] use prelude::*;
@@ -1266,55 +1266,55 @@ fn infer_array() {
             8..9 'x': &str
             17..18 'y': isize
             27..292 '{     ... []; }': ()
-            37..38 'a': [&str; 1]
-            41..44 '[x]': [&str; 1]
+            37..38 'a': [&str; _]
+            41..44 '[x]': [&str; _]
             42..43 'x': &str
-            54..55 'b': [[&str; 1]; 2]
-            58..64 '[a, a]': [[&str; 1]; 2]
-            59..60 'a': [&str; 1]
-            62..63 'a': [&str; 1]
-            74..75 'c': [[[&str; 1]; 2]; 2]
-            78..84 '[b, b]': [[[&str; 1]; 2]; 2]
-            79..80 'b': [[&str; 1]; 2]
-            82..83 'b': [[&str; 1]; 2]
-            95..96 'd': [isize; 4]
-            99..111 '[y, 1, 2, 3]': [isize; 4]
+            54..55 'b': [[&str; _]; _]
+            58..64 '[a, a]': [[&str; _]; _]
+            59..60 'a': [&str; _]
+            62..63 'a': [&str; _]
+            74..75 'c': [[[&str; _]; _]; _]
+            78..84 '[b, b]': [[[&str; _]; _]; _]
+            79..80 'b': [[&str; _]; _]
+            82..83 'b': [[&str; _]; _]
+            95..96 'd': [isize; _]
+            99..111 '[y, 1, 2, 3]': [isize; _]
             100..101 'y': isize
             103..104 '1': isize
             106..107 '2': isize
             109..110 '3': isize
-            121..122 'd': [isize; 4]
-            125..137 '[1, y, 2, 3]': [isize; 4]
+            121..122 'd': [isize; _]
+            125..137 '[1, y, 2, 3]': [isize; _]
             126..127 '1': isize
             129..130 'y': isize
             132..133 '2': isize
             135..136 '3': isize
-            147..148 'e': [isize; 1]
-            151..154 '[y]': [isize; 1]
+            147..148 'e': [isize; _]
+            151..154 '[y]': [isize; _]
             152..153 'y': isize
-            164..165 'f': [[isize; 4]; 2]
-            168..174 '[d, d]': [[isize; 4]; 2]
-            169..170 'd': [isize; 4]
-            172..173 'd': [isize; 4]
-            184..185 'g': [[isize; 1]; 2]
-            188..194 '[e, e]': [[isize; 1]; 2]
-            189..190 'e': [isize; 1]
-            192..193 'e': [isize; 1]
-            205..206 'h': [i32; 2]
-            209..215 '[1, 2]': [i32; 2]
+            164..165 'f': [[isize; _]; _]
+            168..174 '[d, d]': [[isize; _]; _]
+            169..170 'd': [isize; _]
+            172..173 'd': [isize; _]
+            184..185 'g': [[isize; _]; _]
+            188..194 '[e, e]': [[isize; _]; _]
+            189..190 'e': [isize; _]
+            192..193 'e': [isize; _]
+            205..206 'h': [i32; _]
+            209..215 '[1, 2]': [i32; _]
             210..211 '1': i32
             213..214 '2': i32
-            225..226 'i': [&str; 2]
-            229..239 '["a", "b"]': [&str; 2]
+            225..226 'i': [&str; _]
+            229..239 '["a", "b"]': [&str; _]
             230..233 '"a"': &str
             235..238 '"b"': &str
-            250..251 'b': [[&str; 1]; 2]
-            254..264 '[a, ["b"]]': [[&str; 1]; 2]
-            255..256 'a': [&str; 1]
-            258..263 '["b"]': [&str; 1]
+            250..251 'b': [[&str; _]; _]
+            254..264 '[a, ["b"]]': [[&str; _]; _]
+            255..256 'a': [&str; _]
+            258..263 '["b"]': [&str; _]
             259..262 '"b"': &str
             274..275 'x': [u8; _]
-            287..289 '[]': [u8; 0]
+            287..289 '[]': [u8; _]
         "#]],
     );
 }
@@ -2429,20 +2429,20 @@ fn infer_operator_overload() {
             394..395 '1': i32
             406..408 'V2': V2([f32; _]) -> V2
             406..416 'V2([x, y])': V2
-            409..415 '[x, y]': [f32; 2]
+            409..415 '[x, y]': [f32; _]
             410..411 'x': f32
             413..414 'y': f32
             436..519 '{     ... vb; }': ()
             446..448 'va': V2
             451..453 'V2': V2([f32; _]) -> V2
             451..465 'V2([0.0, 1.0])': V2
-            454..464 '[0.0, 1.0]': [f32; 2]
+            454..464 '[0.0, 1.0]': [f32; _]
             455..458 '0.0': f32
             460..463 '1.0': f32
             475..477 'vb': V2
             480..482 'V2': V2([f32; _]) -> V2
             480..494 'V2([0.0, 1.0])': V2
-            483..493 '[0.0, 1.0]': [f32; 2]
+            483..493 '[0.0, 1.0]': [f32; _]
             484..487 '0.0': f32
             489..492 '1.0': f32
             505..506 'r': V2
@@ -2593,8 +2593,8 @@ fn test() {
             658..661 'vec': Vec<i32, Global>
             664..679 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global>
             664..691 '<[_]>:...1i32])': Vec<i32, Global>
-            680..690 'box [1i32]': Box<[i32; 1], Global>
-            684..690 '[1i32]': [i32; 1]
+            680..690 'box [1i32]': Box<[i32; _], Global>
+            684..690 '[1i32]': [i32; _]
             685..689 '1i32': i32
         "#]],
     )
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 47a1455fd0..ffc7c8ef4d 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -531,7 +531,7 @@ fn indexing_arrays() {
         expect![[r#"
             10..26 '{ &mut...[2]; }': ()
             12..23 '&mut [9][2]': &mut {unknown}
-            17..20 '[9]': [i32; 1]
+            17..20 '[9]': [i32; _]
             17..23 '[9][2]': {unknown}
             18..19 '9': i32
             21..22 '2': i32