about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr.rs26
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data.rs42
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs125
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs173
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs213
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs129
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs35
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs27
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs15
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/src.rs19
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/display.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs92
-rw-r--r--src/tools/rust-analyzer/crates/span/src/ast_id.rs6
16 files changed, 494 insertions, 449 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
index b0f16ad6f4d..ba88495e14d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
@@ -20,7 +20,7 @@ use triomphe::Arc;
 
 use crate::{
     db::DefDatabase,
-    item_tree::{AttrOwner, Fields, ItemTreeNode},
+    item_tree::{AttrOwner, FieldParent, ItemTreeNode},
     lang_item::LangItem,
     nameres::{ModuleOrigin, ModuleSource},
     src::{HasChildSource, HasSource},
@@ -76,40 +76,36 @@ impl Attrs {
         let mut res = ArenaMap::default();
 
         let crate_graph = db.crate_graph();
-        let (fields, item_tree, krate) = match v {
+        let item_tree;
+        let (parent, fields, krate) = match v {
             VariantId::EnumVariantId(it) => {
                 let loc = it.lookup(db);
                 let krate = loc.parent.lookup(db).container.krate;
-                let item_tree = loc.id.item_tree(db);
+                item_tree = loc.id.item_tree(db);
                 let variant = &item_tree[loc.id.value];
-                (variant.fields.clone(), item_tree, krate)
+                (FieldParent::Variant(loc.id.value), &variant.fields, krate)
             }
             VariantId::StructId(it) => {
                 let loc = it.lookup(db);
                 let krate = loc.container.krate;
-                let item_tree = loc.id.item_tree(db);
+                item_tree = loc.id.item_tree(db);
                 let struct_ = &item_tree[loc.id.value];
-                (struct_.fields.clone(), item_tree, krate)
+                (FieldParent::Struct(loc.id.value), &struct_.fields, krate)
             }
             VariantId::UnionId(it) => {
                 let loc = it.lookup(db);
                 let krate = loc.container.krate;
-                let item_tree = loc.id.item_tree(db);
+                item_tree = loc.id.item_tree(db);
                 let union_ = &item_tree[loc.id.value];
-                (union_.fields.clone(), item_tree, krate)
+                (FieldParent::Union(loc.id.value), &union_.fields, krate)
             }
         };
 
-        let fields = match fields {
-            Fields::Record(fields) | Fields::Tuple(fields) => fields,
-            Fields::Unit => return Arc::new(res),
-        };
-
         let cfg_options = &crate_graph[krate].cfg_options;
 
         let mut idx = 0;
-        for field in fields {
-            let attrs = item_tree.attrs(db, krate, field.into());
+        for (id, _field) in fields.iter().enumerate() {
+            let attrs = item_tree.attrs(db, krate, AttrOwner::make_field_indexed(parent, id));
             if attrs.is_cfg_enabled(cfg_options) {
                 res.insert(Idx::from_raw(RawIdx::from(idx)), attrs);
                 idx += 1;
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
index 3486928e414..9e1bff98f8a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
@@ -11,7 +11,7 @@ use std::ops::{Deref, Index};
 use base_db::CrateId;
 use cfg::{CfgExpr, CfgOptions};
 use hir_expand::{name::Name, InFile};
-use la_arena::{Arena, ArenaMap};
+use la_arena::{Arena, ArenaMap, Idx, RawIdx};
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
 use span::MacroFileId;
@@ -24,6 +24,7 @@ use crate::{
     hir::{
         dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
     },
+    item_tree::AttrOwner,
     nameres::DefMap,
     path::{ModPath, Path},
     src::HasSource,
@@ -136,16 +137,23 @@ impl Body {
                     let data = db.function_data(f);
                     let f = f.lookup(db);
                     let src = f.source(db);
-                    params = src.value.param_list().map(|param_list| {
+                    params = src.value.param_list().map(move |param_list| {
                         let item_tree = f.id.item_tree(db);
                         let func = &item_tree[f.id.value];
                         let krate = f.container.module(db).krate;
                         let crate_graph = db.crate_graph();
                         (
                             param_list,
-                            func.params.clone().map(move |param| {
+                            (0..func.params.len()).map(move |idx| {
                                 item_tree
-                                    .attrs(db, krate, param.into())
+                                    .attrs(
+                                        db,
+                                        krate,
+                                        AttrOwner::Param(
+                                            f.id.value,
+                                            Idx::from_raw(RawIdx::from(idx as u32)),
+                                        ),
+                                    )
                                     .is_cfg_enabled(&crate_graph[krate].cfg_options)
                             }),
                         )
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
index 37506b6cdc2..a03140e2a92 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
@@ -7,6 +7,7 @@ use hir_expand::{
     name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefKind,
 };
 use intern::{sym, Interned, Symbol};
+use la_arena::{Idx, RawIdx};
 use smallvec::SmallVec;
 use syntax::{ast, Parse};
 use triomphe::Arc;
@@ -58,32 +59,15 @@ impl FunctionData {
 
         let crate_graph = db.crate_graph();
         let cfg_options = &crate_graph[krate].cfg_options;
-        let enabled_params = func
-            .params
-            .clone()
-            .filter(|&param| item_tree.attrs(db, krate, param.into()).is_cfg_enabled(cfg_options));
-
-        // If last cfg-enabled param is a `...` param, it's a varargs function.
-        let is_varargs = enabled_params
-            .clone()
-            .next_back()
-            .map_or(false, |param| item_tree[param].type_ref.is_none());
+        let attr_owner = |idx| {
+            item_tree::AttrOwner::Param(loc.id.value, Idx::from_raw(RawIdx::from(idx as u32)))
+        };
 
         let mut flags = func.flags;
-        if is_varargs {
-            flags |= FnFlags::IS_VARARGS;
-        }
         if flags.contains(FnFlags::HAS_SELF_PARAM) {
             // If there's a self param in the syntax, but it is cfg'd out, remove the flag.
-            let is_cfgd_out = match func.params.clone().next() {
-                Some(param) => {
-                    !item_tree.attrs(db, krate, param.into()).is_cfg_enabled(cfg_options)
-                }
-                None => {
-                    stdx::never!("fn HAS_SELF_PARAM but no parameters allocated");
-                    true
-                }
-            };
+            let is_cfgd_out =
+                !item_tree.attrs(db, krate, attr_owner(0usize)).is_cfg_enabled(cfg_options);
             if is_cfgd_out {
                 cov_mark::hit!(cfgd_out_self_param);
                 flags.remove(FnFlags::HAS_SELF_PARAM);
@@ -101,9 +85,14 @@ impl FunctionData {
 
         Arc::new(FunctionData {
             name: func.name.clone(),
-            params: enabled_params
-                .clone()
-                .filter_map(|id| item_tree[id].type_ref.clone())
+            params: func
+                .params
+                .iter()
+                .enumerate()
+                .filter(|&(idx, _)| {
+                    item_tree.attrs(db, krate, attr_owner(idx)).is_cfg_enabled(cfg_options)
+                })
+                .map(|(_, param)| param.type_ref.clone())
                 .collect(),
             ret_type: func.ret_type.clone(),
             attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()),
@@ -629,7 +618,8 @@ impl<'a> AssocItemCollector<'a> {
             if !attrs.is_cfg_enabled(self.expander.cfg_options()) {
                 self.diagnostics.push(DefDiagnostic::unconfigured_code(
                     self.module_id.local_id,
-                    InFile::new(self.expander.current_file_id(), item.ast_id(item_tree).erase()),
+                    tree_id,
+                    ModItem::from(item).into(),
                     attrs.cfg().unwrap(),
                     self.expander.cfg_options().clone(),
                 ));
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
index cc6e408b65b..827cce706be 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
@@ -7,7 +7,7 @@ use either::Either;
 
 use hir_expand::{
     name::{AsName, Name},
-    HirFileId, InFile,
+    InFile,
 };
 use intern::{sym, Interned};
 use la_arena::Arena;
@@ -18,7 +18,9 @@ use triomphe::Arc;
 use crate::{
     builtin_type::{BuiltinInt, BuiltinUint},
     db::DefDatabase,
-    item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId},
+    item_tree::{
+        AttrOwner, Field, FieldParent, FieldsShape, ItemTree, ModItem, RawVisibilityId, TreeId,
+    },
     lang_item::LangItem,
     lower::LowerCtx,
     nameres::diagnostics::{DefDiagnostic, DefDiagnostics},
@@ -211,20 +213,25 @@ impl StructData {
         }
 
         let strukt = &item_tree[loc.id.value];
-        let (variant_data, diagnostics) = lower_fields(
+        let (data, diagnostics) = lower_fields(
             db,
             krate,
-            loc.id.file_id(),
             loc.container.local_id,
+            loc.id.tree_id(),
             &item_tree,
             &db.crate_graph()[krate].cfg_options,
+            FieldParent::Struct(loc.id.value),
             &strukt.fields,
             None,
         );
         (
             Arc::new(StructData {
                 name: strukt.name.clone(),
-                variant_data: Arc::new(variant_data),
+                variant_data: Arc::new(match strukt.shape {
+                    FieldsShape::Record => VariantData::Record(data),
+                    FieldsShape::Tuple => VariantData::Tuple(data),
+                    FieldsShape::Unit => VariantData::Unit,
+                }),
                 repr,
                 visibility: item_tree[strukt.visibility].clone(),
                 flags,
@@ -256,20 +263,21 @@ impl StructData {
         }
 
         let union = &item_tree[loc.id.value];
-        let (variant_data, diagnostics) = lower_fields(
+        let (data, diagnostics) = lower_fields(
             db,
             krate,
-            loc.id.file_id(),
             loc.container.local_id,
+            loc.id.tree_id(),
             &item_tree,
             &db.crate_graph()[krate].cfg_options,
+            FieldParent::Union(loc.id.value),
             &union.fields,
             None,
         );
         (
             Arc::new(StructData {
                 name: union.name.clone(),
-                variant_data: Arc::new(variant_data),
+                variant_data: Arc::new(VariantData::Record(data)),
                 repr,
                 visibility: item_tree[union.visibility].clone(),
                 flags,
@@ -336,13 +344,14 @@ impl EnumVariantData {
         let item_tree = loc.id.item_tree(db);
         let variant = &item_tree[loc.id.value];
 
-        let (var_data, diagnostics) = lower_fields(
+        let (data, diagnostics) = lower_fields(
             db,
             krate,
-            loc.id.file_id(),
             container.local_id,
+            loc.id.tree_id(),
             &item_tree,
             &db.crate_graph()[krate].cfg_options,
+            FieldParent::Variant(loc.id.value),
             &variant.fields,
             Some(item_tree[loc.parent.lookup(db).id.value].visibility),
         );
@@ -350,7 +359,11 @@ impl EnumVariantData {
         (
             Arc::new(EnumVariantData {
                 name: variant.name.clone(),
-                variant_data: Arc::new(var_data),
+                variant_data: Arc::new(match variant.shape {
+                    FieldsShape::Record => VariantData::Record(data),
+                    FieldsShape::Tuple => VariantData::Tuple(data),
+                    FieldsShape::Unit => VariantData::Unit,
+                }),
             }),
             DefDiagnostics::new(diagnostics),
         )
@@ -396,21 +409,23 @@ pub enum StructKind {
     Unit,
 }
 
+// FIXME This is only used for mapping back source now?
 pub(crate) fn lower_struct(
     db: &dyn DefDatabase,
     trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
     ast: &InFile<ast::StructKind>,
     krate: CrateId,
     item_tree: &ItemTree,
-    fields: &Fields,
+    parent: FieldParent,
 ) -> StructKind {
     let ctx = LowerCtx::new(db, ast.file_id);
 
-    match (&ast.value, fields) {
-        (ast::StructKind::Tuple(fl), Fields::Tuple(fields)) => {
+    match &ast.value {
+        ast::StructKind::Tuple(fl) => {
             let cfg_options = &db.crate_graph()[krate].cfg_options;
-            for ((i, fd), item_tree_id) in fl.fields().enumerate().zip(fields.clone()) {
-                if !item_tree.attrs(db, krate, item_tree_id.into()).is_cfg_enabled(cfg_options) {
+            for (i, fd) in fl.fields().enumerate() {
+                let attrs = item_tree.attrs(db, krate, AttrOwner::make_field_indexed(parent, i));
+                if !attrs.is_cfg_enabled(cfg_options) {
                     continue;
                 }
 
@@ -427,10 +442,11 @@ pub(crate) fn lower_struct(
             }
             StructKind::Tuple
         }
-        (ast::StructKind::Record(fl), Fields::Record(fields)) => {
+        ast::StructKind::Record(fl) => {
             let cfg_options = &db.crate_graph()[krate].cfg_options;
-            for (fd, item_tree_id) in fl.fields().zip(fields.clone()) {
-                if !item_tree.attrs(db, krate, item_tree_id.into()).is_cfg_enabled(cfg_options) {
+            for (i, fd) in fl.fields().enumerate() {
+                let attrs = item_tree.attrs(db, krate, AttrOwner::make_field_indexed(parent, i));
+                if !attrs.is_cfg_enabled(cfg_options) {
                     continue;
                 }
 
@@ -454,65 +470,32 @@ pub(crate) fn lower_struct(
 fn lower_fields(
     db: &dyn DefDatabase,
     krate: CrateId,
-    current_file_id: HirFileId,
     container: LocalModuleId,
+    tree_id: TreeId,
     item_tree: &ItemTree,
     cfg_options: &CfgOptions,
-    fields: &Fields,
+    parent: FieldParent,
+    fields: &[Field],
     override_visibility: Option<RawVisibilityId>,
-) -> (VariantData, Vec<DefDiagnostic>) {
+) -> (Arena<FieldData>, Vec<DefDiagnostic>) {
     let mut diagnostics = Vec::new();
-    match fields {
-        Fields::Record(flds) => {
-            let mut arena = Arena::new();
-            for field_id in flds.clone() {
-                let attrs = item_tree.attrs(db, krate, field_id.into());
-                let field = &item_tree[field_id];
-                if attrs.is_cfg_enabled(cfg_options) {
-                    arena.alloc(lower_field(item_tree, field, override_visibility));
-                } else {
-                    diagnostics.push(DefDiagnostic::unconfigured_code(
-                        container,
-                        InFile::new(
-                            current_file_id,
-                            match field.ast_id {
-                                FieldAstId::Record(it) => it.erase(),
-                                FieldAstId::Tuple(it) => it.erase(),
-                            },
-                        ),
-                        attrs.cfg().unwrap(),
-                        cfg_options.clone(),
-                    ))
-                }
-            }
-            (VariantData::Record(arena), diagnostics)
-        }
-        Fields::Tuple(flds) => {
-            let mut arena = Arena::new();
-            for field_id in flds.clone() {
-                let attrs = item_tree.attrs(db, krate, field_id.into());
-                let field = &item_tree[field_id];
-                if attrs.is_cfg_enabled(cfg_options) {
-                    arena.alloc(lower_field(item_tree, field, override_visibility));
-                } else {
-                    diagnostics.push(DefDiagnostic::unconfigured_code(
-                        container,
-                        InFile::new(
-                            current_file_id,
-                            match field.ast_id {
-                                FieldAstId::Record(it) => it.erase(),
-                                FieldAstId::Tuple(it) => it.erase(),
-                            },
-                        ),
-                        attrs.cfg().unwrap(),
-                        cfg_options.clone(),
-                    ))
-                }
-            }
-            (VariantData::Tuple(arena), diagnostics)
+    let mut arena = Arena::new();
+    for (idx, field) in fields.iter().enumerate() {
+        let attr_owner = AttrOwner::make_field_indexed(parent, idx);
+        let attrs = item_tree.attrs(db, krate, attr_owner);
+        if attrs.is_cfg_enabled(cfg_options) {
+            arena.alloc(lower_field(item_tree, field, override_visibility));
+        } else {
+            diagnostics.push(DefDiagnostic::unconfigured_code(
+                container,
+                tree_id,
+                attr_owner,
+                attrs.cfg().unwrap(),
+                cfg_options.clone(),
+            ))
         }
-        Fields::Unit => (VariantData::Unit, diagnostics),
     }
+    (arena, diagnostics)
 }
 
 fn lower_field(
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
index 479beea4a9e..f6709680d0c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
@@ -47,7 +47,7 @@ use base_db::CrateId;
 use either::Either;
 use hir_expand::{attrs::RawAttrs, name::Name, ExpandTo, HirFileId, InFile};
 use intern::{Interned, Symbol};
-use la_arena::{Arena, Idx, IdxRange, RawIdx};
+use la_arena::{Arena, Idx, RawIdx};
 use once_cell::sync::OnceCell;
 use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
@@ -218,9 +218,7 @@ impl ItemTree {
                 extern_crates,
                 extern_blocks,
                 functions,
-                params,
                 structs,
-                fields,
                 unions,
                 enums,
                 variants,
@@ -241,9 +239,7 @@ impl ItemTree {
             extern_crates.shrink_to_fit();
             extern_blocks.shrink_to_fit();
             functions.shrink_to_fit();
-            params.shrink_to_fit();
             structs.shrink_to_fit();
-            fields.shrink_to_fit();
             unions.shrink_to_fit();
             enums.shrink_to_fit();
             variants.shrink_to_fit();
@@ -295,9 +291,7 @@ struct ItemTreeData {
     extern_crates: Arena<ExternCrate>,
     extern_blocks: Arena<ExternBlock>,
     functions: Arena<Function>,
-    params: Arena<Param>,
     structs: Arena<Struct>,
-    fields: Arena<Field>,
     unions: Arena<Union>,
     enums: Arena<Enum>,
     variants: Arena<Variant>,
@@ -315,7 +309,7 @@ struct ItemTreeData {
     vis: ItemVisibilities,
 }
 
-#[derive(Debug, Eq, PartialEq, Hash)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
 pub enum AttrOwner {
     /// Attributes on an item.
     ModItem(ModItem),
@@ -323,12 +317,28 @@ pub enum AttrOwner {
     TopLevel,
 
     Variant(FileItemTreeId<Variant>),
-    Field(Idx<Field>),
-    Param(Idx<Param>),
+    Field(FieldParent, ItemTreeFieldId),
+    Param(FileItemTreeId<Function>, ItemTreeParamId),
     TypeOrConstParamData(GenericModItem, LocalTypeOrConstParamId),
     LifetimeParamData(GenericModItem, LocalLifetimeParamId),
 }
 
+impl AttrOwner {
+    pub fn make_field_indexed(parent: FieldParent, idx: usize) -> Self {
+        AttrOwner::Field(parent, ItemTreeFieldId::from_raw(RawIdx::from_u32(idx as u32)))
+    }
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub enum FieldParent {
+    Struct(FileItemTreeId<Struct>),
+    Union(FileItemTreeId<Union>),
+    Variant(FileItemTreeId<Variant>),
+}
+
+pub type ItemTreeParamId = Idx<Param>;
+pub type ItemTreeFieldId = Idx<Field>;
+
 macro_rules! from_attrs {
     ( $( $var:ident($t:ty) ),+ $(,)? ) => {
         $(
@@ -341,12 +351,7 @@ macro_rules! from_attrs {
     };
 }
 
-from_attrs!(
-    ModItem(ModItem),
-    Variant(FileItemTreeId<Variant>),
-    Field(Idx<Field>),
-    Param(Idx<Param>),
-);
+from_attrs!(ModItem(ModItem), Variant(FileItemTreeId<Variant>));
 
 /// Trait implemented by all nodes in the item tree.
 pub trait ItemTreeNode: Clone {
@@ -365,7 +370,7 @@ pub trait GenericsItemTreeNode: ItemTreeNode {
 pub struct FileItemTreeId<N>(Idx<N>);
 
 impl<N> FileItemTreeId<N> {
-    pub fn range_iter(range: Range<Self>) -> impl Iterator<Item = Self> {
+    pub fn range_iter(range: Range<Self>) -> impl Iterator<Item = Self> + Clone {
         (range.start.index().into_raw().into_u32()..range.end.index().into_raw().into_u32())
             .map(RawIdx::from_u32)
             .map(Idx::from_raw)
@@ -417,18 +422,18 @@ impl TreeId {
         Self { file, block }
     }
 
-    pub(crate) fn item_tree(&self, db: &dyn DefDatabase) -> Arc<ItemTree> {
+    pub fn item_tree(&self, db: &dyn DefDatabase) -> Arc<ItemTree> {
         match self.block {
             Some(block) => db.block_item_tree(block),
             None => db.file_item_tree(self.file),
         }
     }
 
-    pub(crate) fn file_id(self) -> HirFileId {
+    pub fn file_id(self) -> HirFileId {
         self.file
     }
 
-    pub(crate) fn is_block(self) -> bool {
+    pub fn is_block(self) -> bool {
         self.block.is_some()
     }
 }
@@ -505,6 +510,27 @@ macro_rules! mod_items {
             )+
         }
 
+        impl ModItem {
+            pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::Item> {
+                match self {
+                    $(ModItem::$typ(it) => tree[it.index()].ast_id().upcast()),+
+                }
+            }
+        }
+
+        impl GenericModItem {
+            pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::AnyHasGenericParams> {
+                match self {
+                    $(
+                        $(
+                            #[cfg_attr(ignore_fragment, $generic_params)]
+                            GenericModItem::$typ(it) => tree[it.index()].ast_id().upcast(),
+                        )?
+                    )+
+                }
+            }
+        }
+
         impl From<GenericModItem> for ModItem {
             fn from(id: GenericModItem) -> ModItem {
                 match id {
@@ -596,22 +622,6 @@ mod_items! {
     Macro2 in macro_defs -> ast::MacroDef,
 }
 
-macro_rules! impl_index {
-    ( $($fld:ident: $t:ty),+ $(,)? ) => {
-        $(
-            impl Index<Idx<$t>> for ItemTree {
-                type Output = $t;
-
-                fn index(&self, index: Idx<$t>) -> &Self::Output {
-                    &self.data().$fld[index]
-                }
-            }
-        )+
-    };
-}
-
-impl_index!(fields: Field, variants: Variant, params: Param);
-
 impl Index<RawVisibilityId> for ItemTree {
     type Output = RawVisibility;
     fn index(&self, index: RawVisibilityId) -> &Self::Output {
@@ -723,7 +733,7 @@ pub struct Function {
     pub visibility: RawVisibilityId,
     pub explicit_generic_params: Interned<GenericParams>,
     pub abi: Option<Symbol>,
-    pub params: IdxRange<Param>,
+    pub params: Box<[Param]>,
     pub ret_type: Interned<TypeRef>,
     pub ast_id: FileAstId<ast::Fn>,
     pub(crate) flags: FnFlags,
@@ -731,15 +741,7 @@ pub struct Function {
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Param {
-    /// This is [`None`] for varargs
-    pub type_ref: Option<Interned<TypeRef>>,
-    pub ast_id: ParamAstId,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum ParamAstId {
-    Param(FileAstId<ast::Param>),
-    SelfParam(FileAstId<ast::SelfParam>),
+    pub type_ref: Interned<TypeRef>,
 }
 
 bitflags::bitflags! {
@@ -760,7 +762,8 @@ pub struct Struct {
     pub name: Name,
     pub visibility: RawVisibilityId,
     pub generic_params: Interned<GenericParams>,
-    pub fields: Fields,
+    pub fields: Box<[Field]>,
+    pub shape: FieldsShape,
     pub ast_id: FileAstId<ast::Struct>,
 }
 
@@ -769,7 +772,7 @@ pub struct Union {
     pub name: Name,
     pub visibility: RawVisibilityId,
     pub generic_params: Interned<GenericParams>,
-    pub fields: Fields,
+    pub fields: Box<[Field]>,
     pub ast_id: FileAstId<ast::Union>,
 }
 
@@ -782,6 +785,29 @@ pub struct Enum {
     pub ast_id: FileAstId<ast::Enum>,
 }
 
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Variant {
+    pub name: Name,
+    pub fields: Box<[Field]>,
+    pub shape: FieldsShape,
+    pub ast_id: FileAstId<ast::Variant>,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum FieldsShape {
+    Record,
+    Tuple,
+    Unit,
+}
+
+/// A single field of an enum variant or struct
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Field {
+    pub name: Name,
+    pub type_ref: Interned<TypeRef>,
+    pub visibility: RawVisibilityId,
+}
+
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Const {
     /// `None` for `const _: () = ();`
@@ -1039,28 +1065,6 @@ impl ModItem {
             &ModItem::Function(func) => Some(AssocItem::Function(func)),
         }
     }
-
-    pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::Item> {
-        match self {
-            ModItem::Use(it) => tree[it.index()].ast_id().upcast(),
-            ModItem::ExternCrate(it) => tree[it.index()].ast_id().upcast(),
-            ModItem::ExternBlock(it) => tree[it.index()].ast_id().upcast(),
-            ModItem::Function(it) => tree[it.index()].ast_id().upcast(),
-            ModItem::Struct(it) => tree[it.index()].ast_id().upcast(),
-            ModItem::Union(it) => tree[it.index()].ast_id().upcast(),
-            ModItem::Enum(it) => tree[it.index()].ast_id().upcast(),
-            ModItem::Const(it) => tree[it.index()].ast_id().upcast(),
-            ModItem::Static(it) => tree[it.index()].ast_id().upcast(),
-            ModItem::Trait(it) => tree[it.index()].ast_id().upcast(),
-            ModItem::TraitAlias(it) => tree[it.index()].ast_id().upcast(),
-            ModItem::Impl(it) => tree[it.index()].ast_id().upcast(),
-            ModItem::TypeAlias(it) => tree[it.index()].ast_id().upcast(),
-            ModItem::Mod(it) => tree[it.index()].ast_id().upcast(),
-            ModItem::MacroCall(it) => tree[it.index()].ast_id().upcast(),
-            ModItem::MacroRules(it) => tree[it.index()].ast_id().upcast(),
-            ModItem::Macro2(it) => tree[it.index()].ast_id().upcast(),
-        }
-    }
 }
 
 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
@@ -1099,32 +1103,3 @@ impl AssocItem {
         }
     }
 }
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Variant {
-    pub name: Name,
-    pub fields: Fields,
-    pub ast_id: FileAstId<ast::Variant>,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum Fields {
-    Record(IdxRange<Field>),
-    Tuple(IdxRange<Field>),
-    Unit,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum FieldAstId {
-    Record(FileAstId<ast::RecordField>),
-    Tuple(FileAstId<ast::TupleField>),
-}
-
-/// A single field of an enum variant or struct
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Field {
-    pub name: Name,
-    pub type_ref: Interned<TypeRef>,
-    pub visibility: RawVisibilityId,
-    pub ast_id: FieldAstId,
-}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
index 67092ae0c00..c9e9719c909 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
@@ -17,12 +17,12 @@ use crate::{
     db::DefDatabase,
     generics::{GenericParams, GenericParamsCollector, TypeParamData, TypeParamProvenance},
     item_tree::{
-        AssocItem, AttrOwner, Const, Either, Enum, ExternBlock, ExternCrate, Field, FieldAstId,
-        Fields, FileItemTreeId, FnFlags, Function, GenericArgs, GenericModItem, Idx, IdxRange,
-        Impl, ImportAlias, Interned, ItemTree, ItemTreeData, ItemTreeNode, Macro2, MacroCall,
-        MacroRules, Mod, ModItem, ModKind, ModPath, Mutability, Name, Param, ParamAstId, Path,
-        Range, RawAttrs, RawIdx, RawVisibilityId, Static, Struct, StructKind, Trait, TraitAlias,
-        TypeAlias, Union, Use, UseTree, UseTreeKind, Variant,
+        AssocItem, AttrOwner, Const, Either, Enum, ExternBlock, ExternCrate, Field, FieldParent,
+        FieldsShape, FileItemTreeId, FnFlags, Function, GenericArgs, GenericModItem, Idx, Impl,
+        ImportAlias, Interned, ItemTree, ItemTreeData, Macro2, MacroCall, MacroRules, Mod, ModItem,
+        ModKind, ModPath, Mutability, Name, Param, Path, Range, RawAttrs, RawIdx, RawVisibilityId,
+        Static, Struct, StructKind, Trait, TraitAlias, TypeAlias, Union, Use, UseTree, UseTreeKind,
+        Variant,
     },
     path::AssociatedTypeBinding,
     type_ref::{LifetimeRef, TraitBoundModifier, TraitRef, TypeBound, TypeRef},
@@ -30,7 +30,7 @@ use crate::{
     LocalLifetimeParamId, LocalTypeOrConstParamId,
 };
 
-fn id<N: ItemTreeNode>(index: Idx<N>) -> FileItemTreeId<N> {
+fn id<N>(index: Idx<N>) -> FileItemTreeId<N> {
     FileItemTreeId(index)
 }
 
@@ -193,82 +193,98 @@ impl<'a> Ctx<'a> {
         let visibility = self.lower_visibility(strukt);
         let name = strukt.name()?.as_name();
         let ast_id = self.source_ast_id_map.ast_id(strukt);
-        let fields = self.lower_fields(&strukt.kind());
+        let (fields, kind, attrs) = self.lower_fields(&strukt.kind());
         let generic_params = self.lower_generic_params(HasImplicitSelf::No, strukt);
-        let res = Struct { name, visibility, generic_params, fields, ast_id };
+        let res = Struct { name, visibility, generic_params, fields, shape: kind, ast_id };
         let id = id(self.data().structs.alloc(res));
+        for (idx, attr) in attrs {
+            self.add_attrs(
+                AttrOwner::Field(
+                    FieldParent::Struct(id),
+                    Idx::from_raw(RawIdx::from_u32(idx as u32)),
+                ),
+                attr,
+            );
+        }
         self.write_generic_params_attributes(id.into());
         Some(id)
     }
 
-    fn lower_fields(&mut self, strukt_kind: &ast::StructKind) -> Fields {
+    fn lower_fields(
+        &mut self,
+        strukt_kind: &ast::StructKind,
+    ) -> (Box<[Field]>, FieldsShape, Vec<(usize, RawAttrs)>) {
         match strukt_kind {
             ast::StructKind::Record(it) => {
-                let range = self.lower_record_fields(it);
-                Fields::Record(range)
+                let mut fields = vec![];
+                let mut attrs = vec![];
+
+                for (i, field) in it.fields().enumerate() {
+                    let data = self.lower_record_field(&field);
+                    fields.push(data);
+                    let attr = RawAttrs::new(self.db.upcast(), &field, self.span_map());
+                    if !attr.is_empty() {
+                        attrs.push((i, attr))
+                    }
+                }
+                (fields.into(), FieldsShape::Record, attrs)
             }
             ast::StructKind::Tuple(it) => {
-                let range = self.lower_tuple_fields(it);
-                Fields::Tuple(range)
-            }
-            ast::StructKind::Unit => Fields::Unit,
-        }
-    }
-
-    fn lower_record_fields(&mut self, fields: &ast::RecordFieldList) -> IdxRange<Field> {
-        let start = self.next_field_idx();
-        for field in fields.fields() {
-            if let Some(data) = self.lower_record_field(&field) {
-                let idx = self.data().fields.alloc(data);
-                self.add_attrs(
-                    idx.into(),
-                    RawAttrs::new(self.db.upcast(), &field, self.span_map()),
-                );
+                let mut fields = vec![];
+                let mut attrs = vec![];
+
+                for (i, field) in it.fields().enumerate() {
+                    let data = self.lower_tuple_field(i, &field);
+                    fields.push(data);
+                    let attr = RawAttrs::new(self.db.upcast(), &field, self.span_map());
+                    if !attr.is_empty() {
+                        attrs.push((i, attr))
+                    }
+                }
+                (fields.into(), FieldsShape::Tuple, attrs)
             }
+            ast::StructKind::Unit => (Box::default(), FieldsShape::Unit, Vec::default()),
         }
-        let end = self.next_field_idx();
-        IdxRange::new(start..end)
     }
 
-    fn lower_record_field(&mut self, field: &ast::RecordField) -> Option<Field> {
-        let name = field.name()?.as_name();
+    fn lower_record_field(&mut self, field: &ast::RecordField) -> Field {
+        let name = match field.name() {
+            Some(name) => name.as_name(),
+            None => Name::missing(),
+        };
         let visibility = self.lower_visibility(field);
         let type_ref = self.lower_type_ref_opt(field.ty());
-        let ast_id = FieldAstId::Record(self.source_ast_id_map.ast_id(field));
-        let res = Field { name, type_ref, visibility, ast_id };
-        Some(res)
-    }
 
-    fn lower_tuple_fields(&mut self, fields: &ast::TupleFieldList) -> IdxRange<Field> {
-        let start = self.next_field_idx();
-        for (i, field) in fields.fields().enumerate() {
-            let data = self.lower_tuple_field(i, &field);
-            let idx = self.data().fields.alloc(data);
-            self.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), &field, self.span_map()));
-        }
-        let end = self.next_field_idx();
-        IdxRange::new(start..end)
+        Field { name, type_ref, visibility }
     }
 
     fn lower_tuple_field(&mut self, idx: usize, field: &ast::TupleField) -> Field {
         let name = Name::new_tuple_field(idx);
         let visibility = self.lower_visibility(field);
         let type_ref = self.lower_type_ref_opt(field.ty());
-        let ast_id = FieldAstId::Tuple(self.source_ast_id_map.ast_id(field));
-        Field { name, type_ref, visibility, ast_id }
+        Field { name, type_ref, visibility }
     }
 
     fn lower_union(&mut self, union: &ast::Union) -> Option<FileItemTreeId<Union>> {
         let visibility = self.lower_visibility(union);
         let name = union.name()?.as_name();
         let ast_id = self.source_ast_id_map.ast_id(union);
-        let fields = match union.record_field_list() {
+        let (fields, _, attrs) = match union.record_field_list() {
             Some(record_field_list) => self.lower_fields(&StructKind::Record(record_field_list)),
-            None => Fields::Record(IdxRange::new(self.next_field_idx()..self.next_field_idx())),
+            None => (Box::default(), FieldsShape::Record, Vec::default()),
         };
         let generic_params = self.lower_generic_params(HasImplicitSelf::No, union);
         let res = Union { name, visibility, generic_params, fields, ast_id };
         let id = id(self.data().unions.alloc(res));
+        for (idx, attr) in attrs {
+            self.add_attrs(
+                AttrOwner::Field(
+                    FieldParent::Union(id),
+                    Idx::from_raw(RawIdx::from_u32(idx as u32)),
+                ),
+                attr,
+            );
+        }
         self.write_generic_params_attributes(id.into());
         Some(id)
     }
@@ -293,24 +309,35 @@ impl<'a> Ctx<'a> {
     fn lower_variants(&mut self, variants: &ast::VariantList) -> Range<FileItemTreeId<Variant>> {
         let start = self.next_variant_idx();
         for variant in variants.variants() {
-            if let Some(data) = self.lower_variant(&variant) {
-                let idx = self.data().variants.alloc(data);
-                self.add_attrs(
-                    id(idx).into(),
-                    RawAttrs::new(self.db.upcast(), &variant, self.span_map()),
-                );
-            }
+            let idx = self.lower_variant(&variant);
+            self.add_attrs(
+                id(idx).into(),
+                RawAttrs::new(self.db.upcast(), &variant, self.span_map()),
+            );
         }
         let end = self.next_variant_idx();
         FileItemTreeId(start)..FileItemTreeId(end)
     }
 
-    fn lower_variant(&mut self, variant: &ast::Variant) -> Option<Variant> {
-        let name = variant.name()?.as_name();
-        let fields = self.lower_fields(&variant.kind());
+    fn lower_variant(&mut self, variant: &ast::Variant) -> Idx<Variant> {
+        let name = match variant.name() {
+            Some(name) => name.as_name(),
+            None => Name::missing(),
+        };
+        let (fields, kind, attrs) = self.lower_fields(&variant.kind());
         let ast_id = self.source_ast_id_map.ast_id(variant);
-        let res = Variant { name, fields, ast_id };
-        Some(res)
+        let res = Variant { name, fields, shape: kind, ast_id };
+        let id = self.data().variants.alloc(res);
+        for (idx, attr) in attrs {
+            self.add_attrs(
+                AttrOwner::Field(
+                    FieldParent::Variant(FileItemTreeId(id)),
+                    Idx::from_raw(RawIdx::from_u32(idx as u32)),
+                ),
+                attr,
+            );
+        }
+        id
     }
 
     fn lower_function(&mut self, func: &ast::Fn) -> Option<FileItemTreeId<Function>> {
@@ -318,9 +345,20 @@ impl<'a> Ctx<'a> {
         let name = func.name()?.as_name();
 
         let mut has_self_param = false;
-        let start_param = self.next_param_idx();
+        let mut has_var_args = false;
+        let mut params = vec![];
+        let mut attrs = vec![];
+        let mut push_attr = |idx, attr: RawAttrs| {
+            if !attr.is_empty() {
+                attrs.push((idx, attr))
+            }
+        };
         if let Some(param_list) = func.param_list() {
             if let Some(self_param) = param_list.self_param() {
+                push_attr(
+                    params.len(),
+                    RawAttrs::new(self.db.upcast(), &self_param, self.span_map()),
+                );
                 let self_type = match self_param.ty() {
                     Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref),
                     None => {
@@ -342,40 +380,25 @@ impl<'a> Ctx<'a> {
                     }
                 };
                 let type_ref = Interned::new(self_type);
-                let ast_id = self.source_ast_id_map.ast_id(&self_param);
-                let idx = self.data().params.alloc(Param {
-                    type_ref: Some(type_ref),
-                    ast_id: ParamAstId::SelfParam(ast_id),
-                });
-                self.add_attrs(
-                    idx.into(),
-                    RawAttrs::new(self.db.upcast(), &self_param, self.span_map()),
-                );
+                params.push(Param { type_ref });
                 has_self_param = true;
             }
             for param in param_list.params() {
-                let ast_id = self.source_ast_id_map.ast_id(&param);
-                let idx = match param.dotdotdot_token() {
-                    Some(_) => self
-                        .data()
-                        .params
-                        .alloc(Param { type_ref: None, ast_id: ParamAstId::Param(ast_id) }),
+                push_attr(params.len(), RawAttrs::new(self.db.upcast(), &param, self.span_map()));
+                let param = match param.dotdotdot_token() {
+                    Some(_) => {
+                        has_var_args = true;
+                        Param { type_ref: Interned::new(TypeRef::Error) }
+                    }
                     None => {
                         let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty());
                         let ty = Interned::new(type_ref);
-                        self.data()
-                            .params
-                            .alloc(Param { type_ref: Some(ty), ast_id: ParamAstId::Param(ast_id) })
+                        Param { type_ref: ty }
                     }
                 };
-                self.add_attrs(
-                    idx.into(),
-                    RawAttrs::new(self.db.upcast(), &param, self.span_map()),
-                );
+                params.push(param);
             }
         }
-        let end_param = self.next_param_idx();
-        let params = IdxRange::new(start_param..end_param);
 
         let ret_type = match func.ret_type() {
             Some(rt) => match rt.ty() {
@@ -417,19 +440,25 @@ impl<'a> Ctx<'a> {
         if func.unsafe_token().is_some() {
             flags |= FnFlags::HAS_UNSAFE_KW;
         }
+        if has_var_args {
+            flags |= FnFlags::IS_VARARGS;
+        }
 
         let res = Function {
             name,
             visibility,
             explicit_generic_params: self.lower_generic_params(HasImplicitSelf::No, func),
             abi,
-            params,
+            params: params.into_boxed_slice(),
             ret_type: Interned::new(ret_type),
             ast_id,
             flags,
         };
 
         let id = id(self.data().functions.alloc(res));
+        for (idx, attr) in attrs {
+            self.add_attrs(AttrOwner::Param(id, Idx::from_raw(RawIdx::from_u32(idx as u32))), attr);
+        }
         self.write_generic_params_attributes(id.into());
         Some(id)
     }
@@ -725,21 +754,11 @@ impl<'a> Ctx<'a> {
         }
     }
 
-    fn next_field_idx(&self) -> Idx<Field> {
-        Idx::from_raw(RawIdx::from(
-            self.tree.data.as_ref().map_or(0, |data| data.fields.len() as u32),
-        ))
-    }
     fn next_variant_idx(&self) -> Idx<Variant> {
         Idx::from_raw(RawIdx::from(
             self.tree.data.as_ref().map_or(0, |data| data.variants.len() as u32),
         ))
     }
-    fn next_param_idx(&self) -> Idx<Param> {
-        Idx::from_raw(RawIdx::from(
-            self.tree.data.as_ref().map_or(0, |data| data.params.len() as u32),
-        ))
-    }
 }
 
 fn desugar_future_path(orig: TypeRef) -> Path {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
index 6283ae23b52..5eeb8607b96 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
@@ -2,16 +2,17 @@
 
 use std::fmt::{self, Write};
 
+use la_arena::{Idx, RawIdx};
 use span::ErasedFileAstId;
 
 use crate::{
     generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
     item_tree::{
-        AttrOwner, Const, DefDatabase, Enum, ExternBlock, ExternCrate, Field, FieldAstId, Fields,
-        FileItemTreeId, FnFlags, Function, GenericModItem, GenericParams, Impl, Interned, ItemTree,
-        Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, Param, ParamAstId, Path, RawAttrs,
-        RawVisibilityId, Static, Struct, Trait, TraitAlias, TypeAlias, TypeBound, TypeRef, Union,
-        Use, UseTree, UseTreeKind, Variant,
+        AttrOwner, Const, DefDatabase, Enum, ExternBlock, ExternCrate, Field, FieldParent,
+        FieldsShape, FileItemTreeId, FnFlags, Function, GenericModItem, GenericParams, Impl,
+        Interned, ItemTree, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, Param, Path,
+        RawAttrs, RawVisibilityId, Static, Struct, Trait, TraitAlias, TypeAlias, TypeBound,
+        TypeRef, Union, Use, UseTree, UseTreeKind, Variant,
     },
     pretty::{print_path, print_type_bounds, print_type_ref},
     visibility::RawVisibility,
@@ -118,19 +119,17 @@ impl Printer<'_> {
         };
     }
 
-    fn print_fields(&mut self, fields: &Fields) {
-        match fields {
-            Fields::Record(fields) => {
+    fn print_fields(&mut self, parent: FieldParent, kind: FieldsShape, fields: &[Field]) {
+        match kind {
+            FieldsShape::Record => {
                 self.whitespace();
                 w!(self, "{{");
                 self.indented(|this| {
-                    for field in fields.clone() {
-                        let Field { visibility, name, type_ref, ast_id } = &this.tree[field];
-                        this.print_ast_id(match ast_id {
-                            FieldAstId::Record(it) => it.erase(),
-                            FieldAstId::Tuple(it) => it.erase(),
-                        });
-                        this.print_attrs_of(field, "\n");
+                    for (idx, Field { name, type_ref, visibility }) in fields.iter().enumerate() {
+                        this.print_attrs_of(
+                            AttrOwner::Field(parent, Idx::from_raw(RawIdx::from(idx as u32))),
+                            "\n",
+                        );
                         this.print_visibility(*visibility);
                         w!(this, "{}: ", name.display(self.db.upcast()));
                         this.print_type_ref(type_ref);
@@ -139,16 +138,14 @@ impl Printer<'_> {
                 });
                 w!(self, "}}");
             }
-            Fields::Tuple(fields) => {
+            FieldsShape::Tuple => {
                 w!(self, "(");
                 self.indented(|this| {
-                    for field in fields.clone() {
-                        let Field { visibility, name, type_ref, ast_id } = &this.tree[field];
-                        this.print_ast_id(match ast_id {
-                            FieldAstId::Record(it) => it.erase(),
-                            FieldAstId::Tuple(it) => it.erase(),
-                        });
-                        this.print_attrs_of(field, "\n");
+                    for (idx, Field { name, type_ref, visibility }) in fields.iter().enumerate() {
+                        this.print_attrs_of(
+                            AttrOwner::Field(parent, Idx::from_raw(RawIdx::from(idx as u32))),
+                            "\n",
+                        );
                         this.print_visibility(*visibility);
                         w!(this, "{}: ", name.display(self.db.upcast()));
                         this.print_type_ref(type_ref);
@@ -157,24 +154,30 @@ impl Printer<'_> {
                 });
                 w!(self, ")");
             }
-            Fields::Unit => {}
+            FieldsShape::Unit => {}
         }
     }
 
-    fn print_fields_and_where_clause(&mut self, fields: &Fields, params: &GenericParams) {
-        match fields {
-            Fields::Record(_) => {
+    fn print_fields_and_where_clause(
+        &mut self,
+        parent: FieldParent,
+        kind: FieldsShape,
+        fields: &[Field],
+        params: &GenericParams,
+    ) {
+        match kind {
+            FieldsShape::Record => {
                 if self.print_where_clause(params) {
                     wln!(self);
                 }
-                self.print_fields(fields);
+                self.print_fields(parent, kind, fields);
             }
-            Fields::Unit => {
+            FieldsShape::Unit => {
                 self.print_where_clause(params);
-                self.print_fields(fields);
+                self.print_fields(parent, kind, fields);
             }
-            Fields::Tuple(_) => {
-                self.print_fields(fields);
+            FieldsShape::Tuple => {
+                self.print_fields(parent, kind, fields);
                 self.print_where_clause(params);
             }
         }
@@ -280,25 +283,20 @@ impl Printer<'_> {
                 w!(self, "(");
                 if !params.is_empty() {
                     self.indented(|this| {
-                        for param in params.clone() {
-                            this.print_attrs_of(param, "\n");
-                            let Param { type_ref, ast_id } = &this.tree[param];
-                            this.print_ast_id(match ast_id {
-                                ParamAstId::Param(it) => it.erase(),
-                                ParamAstId::SelfParam(it) => it.erase(),
-                            });
-                            match type_ref {
-                                Some(ty) => {
-                                    if flags.contains(FnFlags::HAS_SELF_PARAM) {
-                                        w!(this, "self: ");
-                                    }
-                                    this.print_type_ref(ty);
-                                    wln!(this, ",");
-                                }
-                                None => {
-                                    wln!(this, "...");
-                                }
-                            };
+                        for (idx, Param { type_ref }) in params.iter().enumerate() {
+                            this.print_attrs_of(
+                                AttrOwner::Param(it, Idx::from_raw(RawIdx::from(idx as u32))),
+                                "\n",
+                            );
+                            if idx == 0 && flags.contains(FnFlags::HAS_SELF_PARAM) {
+                                w!(this, "self: ");
+                            }
+                            if idx != params.len() {
+                                this.print_type_ref(type_ref);
+                            } else {
+                                wln!(this, "...");
+                            }
+                            wln!(this, ",");
                         }
                     });
                 }
@@ -312,13 +310,19 @@ impl Printer<'_> {
                 }
             }
             ModItem::Struct(it) => {
-                let Struct { visibility, name, fields, generic_params, ast_id } = &self.tree[it];
+                let Struct { visibility, name, fields, shape: kind, generic_params, ast_id } =
+                    &self.tree[it];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "struct {}", name.display(self.db.upcast()));
                 self.print_generic_params(generic_params, it.into());
-                self.print_fields_and_where_clause(fields, generic_params);
-                if matches!(fields, Fields::Record(_)) {
+                self.print_fields_and_where_clause(
+                    FieldParent::Struct(it),
+                    *kind,
+                    fields,
+                    generic_params,
+                );
+                if matches!(kind, FieldsShape::Record) {
                     wln!(self);
                 } else {
                     wln!(self, ";");
@@ -330,12 +334,13 @@ impl Printer<'_> {
                 self.print_visibility(*visibility);
                 w!(self, "union {}", name.display(self.db.upcast()));
                 self.print_generic_params(generic_params, it.into());
-                self.print_fields_and_where_clause(fields, generic_params);
-                if matches!(fields, Fields::Record(_)) {
-                    wln!(self);
-                } else {
-                    wln!(self, ";");
-                }
+                self.print_fields_and_where_clause(
+                    FieldParent::Union(it),
+                    FieldsShape::Record,
+                    fields,
+                    generic_params,
+                );
+                wln!(self);
             }
             ModItem::Enum(it) => {
                 let Enum { name, visibility, variants, generic_params, ast_id } = &self.tree[it];
@@ -346,11 +351,11 @@ impl Printer<'_> {
                 self.print_where_clause_and_opening_brace(generic_params);
                 self.indented(|this| {
                     for variant in FileItemTreeId::range_iter(variants.clone()) {
-                        let Variant { name, fields, ast_id } = &this.tree[variant];
+                        let Variant { name, fields, shape: kind, ast_id } = &this.tree[variant];
                         this.print_ast_id(ast_id.erase());
                         this.print_attrs_of(variant, "\n");
                         w!(this, "{}", name.display(self.db.upcast()));
-                        this.print_fields(fields);
+                        this.print_fields(FieldParent::Variant(variant), *kind, fields);
                         wln!(this, ",");
                     }
                 });
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
index 79bab11998b..c6930401a6f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
@@ -129,40 +129,34 @@ enum E {
             #[derive(Debug)]
             // AstId: 2
             pub(self) struct Struct {
-                // AstId: 6
                 #[doc = " fld docs"]
                 pub(self) fld: (),
             }
 
             // AstId: 3
             pub(self) struct Tuple(
-                // AstId: 7
                 #[attr]
                 pub(self) 0: u8,
             );
 
             // AstId: 4
             pub(self) union Ize {
-                // AstId: 8
                 pub(self) a: (),
-                // AstId: 9
                 pub(self) b: (),
             }
 
             // AstId: 5
             pub(self) enum E {
-                // AstId: 10
+                // AstId: 6
                 #[doc = " comment on Unit"]
                 Unit,
-                // AstId: 11
+                // AstId: 7
                 #[doc = " comment on Tuple"]
                 Tuple(
-                    // AstId: 13
                     pub(self) 0: u8,
                 ),
-                // AstId: 12
+                // AstId: 8
                 Struct {
-                    // AstId: 14
                     #[doc = " comment on a: u8"]
                     pub(self) a: u8,
                 },
@@ -201,9 +195,7 @@ trait Tr: SuperTrait + 'lifetime {
             // AstId: 3
             pub(self) fn f(
                 #[attr]
-                // AstId: 5
                 u8,
-                // AstId: 6
                 (),
             ) -> () { ... }
 
@@ -213,12 +205,11 @@ trait Tr: SuperTrait + 'lifetime {
                 Self: SuperTrait,
                 Self: 'lifetime
             {
-                // AstId: 8
+                // AstId: 6
                 pub(self) type Assoc: AssocBound = Default;
 
-                // AstId: 9
+                // AstId: 7
                 pub(self) fn method(
-                    // AstId: 10
                     self: &Self,
                 ) -> ();
             }
@@ -300,17 +291,11 @@ struct S {
         expect![[r#"
             // AstId: 1
             pub(self) struct S {
-                // AstId: 2
                 pub(self) a: self::Ty,
-                // AstId: 3
                 pub(self) b: super::SuperTy,
-                // AstId: 4
                 pub(self) c: super::super::SuperSuperTy,
-                // AstId: 5
                 pub(self) d: ::abs::Path,
-                // AstId: 6
                 pub(self) e: crate::Crate,
-                // AstId: 7
                 pub(self) f: plain::path::Ty,
             }
         "#]],
@@ -331,13 +316,9 @@ struct S {
         expect![[r#"
             // AstId: 1
             pub(self) struct S {
-                // AstId: 2
                 pub(self) a: Mixed::<'a, T, Item = (), OtherItem = u8>,
-                // AstId: 3
                 pub(self) b: Qualified::<Self=Fully>::Syntax,
-                // AstId: 4
                 pub(self) c: <TypeAnchored>::Path::<'a>,
-                // AstId: 5
                 pub(self) d: dyn for<'a> Trait::<'a>,
             }
         "#]],
@@ -371,15 +352,12 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
                 T: 'a,
                 T: 'b
             {
-                // AstId: 8
                 pub(self) field: &'a &'b T,
             }
 
             // AstId: 2
             pub(self) struct Tuple<T, U>(
-                // AstId: 9
                 pub(self) 0: T,
-                // AstId: 10
                 pub(self) 1: U,
             )
             where
@@ -393,9 +371,8 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
                 T: 'a,
                 T: 'b
             {
-                // AstId: 12
+                // AstId: 9
                 pub(self) fn f<G>(
-                    // AstId: 13
                     impl Copy,
                 ) -> impl Copy
                 where
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index 58aca964290..c51eea22dcb 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -23,7 +23,7 @@ use itertools::{izip, Itertools};
 use la_arena::Idx;
 use limit::Limit;
 use rustc_hash::{FxHashMap, FxHashSet};
-use span::{Edition, EditionedFileId, ErasedFileAstId, FileAstId, SyntaxContextId};
+use span::{Edition, EditionedFileId, FileAstId, SyntaxContextId};
 use syntax::ast;
 use triomphe::Arc;
 
@@ -32,8 +32,8 @@ use crate::{
     db::DefDatabase,
     item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports},
     item_tree::{
-        self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode,
-        Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId,
+        self, AttrOwner, ExternCrate, FieldsShape, FileItemTreeId, ImportKind, ItemTree,
+        ItemTreeId, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId,
     },
     macro_call_as_call_id, macro_call_as_call_id_with_eager,
     nameres::{
@@ -1580,10 +1580,7 @@ impl ModCollector<'_, '_> {
             let attrs = self.item_tree.attrs(db, krate, item.into());
             if let Some(cfg) = attrs.cfg() {
                 if !self.is_cfg_enabled(&cfg) {
-                    self.emit_unconfigured_diagnostic(
-                        InFile::new(self.file_id(), item.ast_id(self.item_tree).erase()),
-                        &cfg,
-                    );
+                    self.emit_unconfigured_diagnostic(self.tree_id, item.into(), &cfg);
                     return;
                 }
             }
@@ -1699,7 +1696,7 @@ impl ModCollector<'_, '_> {
                             .into(),
                         &it.name,
                         vis,
-                        !matches!(it.fields, Fields::Record(_)),
+                        !matches!(it.shape, FieldsShape::Record),
                     );
                 }
                 ModItem::Union(id) => {
@@ -1737,10 +1734,8 @@ impl ModCollector<'_, '_> {
                             match is_enabled {
                                 Err(cfg) => {
                                     self.emit_unconfigured_diagnostic(
-                                        InFile::new(
-                                            self.file_id(),
-                                            self.item_tree[variant.index()].ast_id.erase(),
-                                        ),
+                                        self.tree_id,
+                                        variant.into(),
                                         &cfg,
                                     );
                                     None
@@ -1956,7 +1951,8 @@ impl ModCollector<'_, '_> {
                         match is_enabled {
                             Err(cfg) => {
                                 self.emit_unconfigured_diagnostic(
-                                    ast_id.map(|it| it.erase()),
+                                    self.tree_id,
+                                    AttrOwner::TopLevel,
                                     &cfg,
                                 );
                             }
@@ -2402,10 +2398,11 @@ impl ModCollector<'_, '_> {
         self.def_collector.cfg_options.check(cfg) != Some(false)
     }
 
-    fn emit_unconfigured_diagnostic(&mut self, ast_id: InFile<ErasedFileAstId>, cfg: &CfgExpr) {
+    fn emit_unconfigured_diagnostic(&mut self, tree_id: TreeId, item: AttrOwner, cfg: &CfgExpr) {
         self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code(
             self.module_id,
-            ast_id,
+            tree_id,
+            item,
             cfg.clone(),
             self.def_collector.cfg_options.clone(),
         ));
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
index 4ab53d20b57..e1bd6966f39 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
@@ -4,12 +4,12 @@ use std::ops::Not;
 
 use base_db::CrateId;
 use cfg::{CfgExpr, CfgOptions};
-use hir_expand::{attrs::AttrId, ErasedAstId, MacroCallKind};
+use hir_expand::{attrs::AttrId, MacroCallKind};
 use la_arena::Idx;
 use syntax::ast;
 
 use crate::{
-    item_tree::{self, ItemTreeId},
+    item_tree::{self, AttrOwner, ItemTreeId, TreeId},
     nameres::LocalModuleId,
     path::ModPath,
     AstId,
@@ -29,7 +29,8 @@ pub enum DefDiagnosticKind {
         index: Idx<ast::UseTree>,
     },
     UnconfiguredCode {
-        ast: ErasedAstId,
+        tree: TreeId,
+        item: AttrOwner,
         cfg: CfgExpr,
         opts: CfgOptions,
     },
@@ -116,11 +117,15 @@ impl DefDiagnostic {
 
     pub fn unconfigured_code(
         container: LocalModuleId,
-        ast: ErasedAstId,
+        tree: TreeId,
+        item: AttrOwner,
         cfg: CfgExpr,
         opts: CfgOptions,
     ) -> Self {
-        Self { in_module: container, kind: DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } }
+        Self {
+            in_module: container,
+            kind: DefDiagnosticKind::UnconfiguredCode { tree, item, cfg, opts },
+        }
     }
 
     pub fn unresolved_proc_macro(
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
index e797d19223e..75cab137f78 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
@@ -17,7 +17,7 @@ use triomphe::Arc;
 use crate::{
     db::DefDatabase,
     item_scope::{ImportOrExternCrate, BUILTIN_SCOPE},
-    item_tree::Fields,
+    item_tree::FieldsShape,
     nameres::{sub_namespace_match, BlockInfo, BuiltinShadowMode, DefMap, MacroSubNs},
     path::{ModPath, PathKind},
     per_ns::PerNs,
@@ -381,11 +381,11 @@ impl DefMap {
                     .iter()
                     .find_map(|&variant| {
                         let variant_data = &tree[variant.lookup(db).id.value];
-                        (variant_data.name == *segment).then(|| match variant_data.fields {
-                            Fields::Record(_) => {
+                        (variant_data.name == *segment).then(|| match variant_data.shape {
+                            FieldsShape::Record => {
                                 PerNs::types(variant.into(), Visibility::Public, None)
                             }
-                            Fields::Tuple(_) | Fields::Unit => PerNs::both(
+                            FieldsShape::Tuple | FieldsShape::Unit => PerNs::both(
                                 variant.into(),
                                 variant.into(),
                                 Visibility::Public,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/src.rs b/src/tools/rust-analyzer/crates/hir-def/src/src.rs
index a0d2079e0d4..6db20870cf6 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/src.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/src.rs
@@ -6,9 +6,12 @@ use la_arena::ArenaMap;
 use syntax::{ast, AstNode, AstPtr};
 
 use crate::{
-    data::adt::lower_struct, db::DefDatabase, item_tree::ItemTreeNode, trace::Trace, GenericDefId,
-    ItemTreeLoc, LocalFieldId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup, UseId,
-    VariantId,
+    data::adt::lower_struct,
+    db::DefDatabase,
+    item_tree::{FieldParent, ItemTreeNode},
+    trace::Trace,
+    GenericDefId, ItemTreeLoc, LocalFieldId, LocalLifetimeParamId, LocalTypeOrConstParamId, Lookup,
+    UseId, VariantId,
 };
 
 pub trait HasSource {
@@ -124,13 +127,13 @@ impl HasChildSource<LocalFieldId> for VariantId {
 
     fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<LocalFieldId, Self::Value>> {
         let item_tree;
-        let (src, fields, container) = match *self {
+        let (src, parent, container) = match *self {
             VariantId::EnumVariantId(it) => {
                 let lookup = it.lookup(db);
                 item_tree = lookup.id.item_tree(db);
                 (
                     lookup.source(db).map(|it| it.kind()),
-                    &item_tree[lookup.id.value].fields,
+                    FieldParent::Variant(lookup.id.value),
                     lookup.parent.lookup(db).container,
                 )
             }
@@ -139,7 +142,7 @@ impl HasChildSource<LocalFieldId> for VariantId {
                 item_tree = lookup.id.item_tree(db);
                 (
                     lookup.source(db).map(|it| it.kind()),
-                    &item_tree[lookup.id.value].fields,
+                    FieldParent::Struct(lookup.id.value),
                     lookup.container,
                 )
             }
@@ -148,13 +151,13 @@ impl HasChildSource<LocalFieldId> for VariantId {
                 item_tree = lookup.id.item_tree(db);
                 (
                     lookup.source(db).map(|it| it.kind()),
-                    &item_tree[lookup.id.value].fields,
+                    FieldParent::Union(lookup.id.value),
                     lookup.container,
                 )
             }
         };
         let mut trace = Trace::new_for_map();
-        lower_struct(db, &mut trace, &src, container.krate, &item_tree, fields);
+        lower_struct(db, &mut trace, &src, container.krate, &item_tree, parent);
         src.with_value(trace.into_map())
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index 82f4ad01e01..284291bb9ad 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -812,7 +812,9 @@ impl<'a> InferenceContext<'a> {
                 None => self.err_ty(),
             };
 
-            param_tys.push(va_list_ty)
+            if let Some(ty) = param_tys.last_mut() {
+                *ty = va_list_ty;
+            }
         }
         let mut param_tys = param_tys.into_iter().chain(iter::repeat(self.table.new_type_var()));
         if let Some(self_param) = self.body.self_param {
diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs
index f80ccf84a2c..a40022c43a1 100644
--- a/src/tools/rust-analyzer/crates/hir/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/display.rs
@@ -99,7 +99,13 @@ impl HirDisplay for Function {
         }
 
         // FIXME: Use resolved `param.ty` once we no longer discard lifetimes
-        for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)).skip(skip_self) {
+        for (type_ref, param) in data
+            .params
+            .iter()
+            .zip(self.assoc_fn_params(db))
+            .take(data.params.len() - data.is_varargs() as usize)
+            .skip(skip_self)
+        {
             let local = param.as_local(db).map(|it| it.name(db));
             if !first {
                 f.write_str(", ")?;
@@ -114,7 +120,10 @@ impl HirDisplay for Function {
         }
 
         if data.is_varargs() {
-            f.write_str(", ...")?;
+            if !first {
+                f.write_str(", ")?;
+            }
+            f.write_str("...")?;
         }
 
         f.write_char(')')?;
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index dd9f1e88036..70f4a632fb3 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -44,7 +44,7 @@ use hir_def::{
     data::adt::VariantData,
     generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
     hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
-    item_tree::ItemTreeNode,
+    item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode},
     lang_item::LangItemTarget,
     layout::{self, ReprOptions, TargetDataLayout},
     nameres::{self, diagnostics::DefDiagnostic},
@@ -81,7 +81,7 @@ use rustc_hash::FxHashSet;
 use span::{Edition, EditionedFileId, FileId, MacroCallId};
 use stdx::{impl_from, never};
 use syntax::{
-    ast::{self, HasAttrs as _, HasName},
+    ast::{self, HasAttrs as _, HasGenericParams, HasName},
     format_smolstr, AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, ToSmolStr, T,
 };
 use triomphe::Arc;
@@ -906,12 +906,90 @@ fn emit_def_diagnostic_(
             );
         }
 
-        DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
-            let item = ast.to_ptr(db.upcast());
-            acc.push(
-                InactiveCode { node: ast.with_value(item), cfg: cfg.clone(), opts: opts.clone() }
+        DefDiagnosticKind::UnconfiguredCode { tree, item, cfg, opts } => {
+            let item_tree = tree.item_tree(db.upcast());
+            let ast_id_map = db.ast_id_map(tree.file_id());
+            // FIXME: This parses... We could probably store relative ranges for the children things
+            // here in the item tree?
+            (|| {
+                let process_field_list =
+                    |field_list: Option<_>, idx: ItemTreeFieldId| match field_list? {
+                        ast::FieldList::RecordFieldList(it) => Some(SyntaxNodePtr::new(
+                            it.fields().nth(idx.into_raw().into_u32() as usize)?.syntax(),
+                        )),
+                        ast::FieldList::TupleFieldList(it) => Some(SyntaxNodePtr::new(
+                            it.fields().nth(idx.into_raw().into_u32() as usize)?.syntax(),
+                        )),
+                    };
+                let ptr = match *item {
+                    AttrOwner::ModItem(it) => {
+                        ast_id_map.get(it.ast_id(&item_tree)).syntax_node_ptr()
+                    }
+                    AttrOwner::TopLevel => ast_id_map.root(),
+                    AttrOwner::Variant(it) => {
+                        ast_id_map.get(item_tree[it].ast_id).syntax_node_ptr()
+                    }
+                    AttrOwner::Field(FieldParent::Variant(parent), idx) => process_field_list(
+                        ast_id_map
+                            .get(item_tree[parent].ast_id)
+                            .to_node(&db.parse_or_expand(tree.file_id()))
+                            .field_list(),
+                        idx,
+                    )?,
+                    AttrOwner::Field(FieldParent::Struct(parent), idx) => process_field_list(
+                        ast_id_map
+                            .get(item_tree[parent.index()].ast_id)
+                            .to_node(&db.parse_or_expand(tree.file_id()))
+                            .field_list(),
+                        idx,
+                    )?,
+                    AttrOwner::Field(FieldParent::Union(parent), idx) => SyntaxNodePtr::new(
+                        ast_id_map
+                            .get(item_tree[parent.index()].ast_id)
+                            .to_node(&db.parse_or_expand(tree.file_id()))
+                            .record_field_list()?
+                            .fields()
+                            .nth(idx.into_raw().into_u32() as usize)?
+                            .syntax(),
+                    ),
+                    AttrOwner::Param(parent, idx) => SyntaxNodePtr::new(
+                        ast_id_map
+                            .get(item_tree[parent.index()].ast_id)
+                            .to_node(&db.parse_or_expand(tree.file_id()))
+                            .param_list()?
+                            .params()
+                            .nth(idx.into_raw().into_u32() as usize)?
+                            .syntax(),
+                    ),
+                    AttrOwner::TypeOrConstParamData(parent, idx) => SyntaxNodePtr::new(
+                        ast_id_map
+                            .get(parent.ast_id(&item_tree))
+                            .to_node(&db.parse_or_expand(tree.file_id()))
+                            .generic_param_list()?
+                            .type_or_const_params()
+                            .nth(idx.into_raw().into_u32() as usize)?
+                            .syntax(),
+                    ),
+                    AttrOwner::LifetimeParamData(parent, idx) => SyntaxNodePtr::new(
+                        ast_id_map
+                            .get(parent.ast_id(&item_tree))
+                            .to_node(&db.parse_or_expand(tree.file_id()))
+                            .generic_param_list()?
+                            .lifetime_params()
+                            .nth(idx.into_raw().into_u32() as usize)?
+                            .syntax(),
+                    ),
+                };
+                acc.push(
+                    InactiveCode {
+                        node: InFile::new(tree.file_id(), ptr),
+                        cfg: cfg.clone(),
+                        opts: opts.clone(),
+                    }
                     .into(),
-            );
+                );
+                Some(())
+            })();
         }
         DefDiagnosticKind::UnresolvedProcMacro { ast, krate } => {
             let (node, precise_location, macro_name, kind) = precise_macro_call_location(ast, db);
diff --git a/src/tools/rust-analyzer/crates/span/src/ast_id.rs b/src/tools/rust-analyzer/crates/span/src/ast_id.rs
index 332745aae6e..b61baa22446 100644
--- a/src/tools/rust-analyzer/crates/span/src/ast_id.rs
+++ b/src/tools/rust-analyzer/crates/span/src/ast_id.rs
@@ -80,13 +80,11 @@ macro_rules! register_ast_id_node {
 }
 register_ast_id_node! {
     impl AstIdNode for
-    Item,
+    Item, AnyHasGenericParams,
         Adt,
             Enum,
                 Variant,
             Struct,
-                RecordField,
-                TupleField,
             Union,
         AssocItem,
             Const,
@@ -104,7 +102,7 @@ register_ast_id_node! {
         Trait,
         TraitAlias,
         Use,
-    BlockExpr, ConstArg, Param, SelfParam
+    BlockExpr, ConstArg
 }
 
 /// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back.