about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-04-15 20:17:26 +0000
committerbors <bors@rust-lang.org>2024-04-15 20:17:26 +0000
commit90cfa8035f98d3ab0f7f4f1e77f4f5e3b0a7370b (patch)
tree6c096eff50ab12113247f51d14d5259a22c0e568
parentb223860c0e4635e7b9788866422c5f5514518cf7 (diff)
parent531a270d918296c84a473e36b393cecdaa5b2e4e (diff)
downloadrust-90cfa8035f98d3ab0f7f4f1e77f4f5e3b0a7370b.tar.gz
rust-90cfa8035f98d3ab0f7f4f1e77f4f5e3b0a7370b.zip
Auto merge of #17078 - Veykril:diags-perf, r=Veykril
internal: Improve diagnostics performance
-rw-r--r--crates/base-db/src/lib.rs2
-rw-r--r--crates/hir-def/src/data.rs2
-rw-r--r--crates/hir-def/src/data/adt.rs11
-rw-r--r--crates/hir-def/src/db.rs5
-rw-r--r--crates/hir-def/src/item_tree.rs1
-rw-r--r--crates/hir-def/src/item_tree/lower.rs2
-rw-r--r--crates/hir-def/src/item_tree/pretty.rs2
-rw-r--r--crates/hir-ty/src/diagnostics/decl_check.rs2
-rw-r--r--crates/hir-ty/src/diagnostics/expr.rs93
-rw-r--r--crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs53
-rw-r--r--crates/hir-ty/src/diagnostics/unsafe_check.rs28
-rw-r--r--crates/hir-ty/src/infer.rs1
-rw-r--r--crates/hir-ty/src/inhabitedness.rs68
-rw-r--r--crates/hir-ty/src/mir/lower.rs2
-rw-r--r--crates/hir/src/lib.rs10
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_match_arms.rs3
-rw-r--r--crates/ide-diagnostics/src/lib.rs41
-rw-r--r--crates/rust-analyzer/src/integrated_benchmarks.rs8
18 files changed, 176 insertions, 158 deletions
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs
index a268b6a78fc..2b64a07a5a9 100644
--- a/crates/base-db/src/lib.rs
+++ b/crates/base-db/src/lib.rs
@@ -45,7 +45,7 @@ pub trait Upcast<T: ?Sized> {
 
 pub const DEFAULT_FILE_TEXT_LRU_CAP: usize = 16;
 pub const DEFAULT_PARSE_LRU_CAP: usize = 128;
-pub const DEFAULT_BORROWCK_LRU_CAP: usize = 1024;
+pub const DEFAULT_BORROWCK_LRU_CAP: usize = 2024;
 
 pub trait FileLoader {
     /// Text of the file.
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index b5317be288e..e3d750d33ca 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -510,6 +510,7 @@ pub struct ConstData {
     pub type_ref: Interned<TypeRef>,
     pub visibility: RawVisibility,
     pub rustc_allow_incoherent_impl: bool,
+    pub has_body: bool,
 }
 
 impl ConstData {
@@ -533,6 +534,7 @@ impl ConstData {
             type_ref: konst.type_ref.clone(),
             visibility,
             rustc_allow_incoherent_impl,
+            has_body: konst.has_body,
         })
     }
 }
diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs
index 5330db7c80c..0fe73418e51 100644
--- a/crates/hir-def/src/data/adt.rs
+++ b/crates/hir-def/src/data/adt.rs
@@ -26,7 +26,7 @@ use crate::{
     tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree},
     type_ref::TypeRef,
     visibility::RawVisibility,
-    EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId,
+    EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId, VariantId,
 };
 
 /// Note that we use `StructData` for unions as well!
@@ -378,6 +378,15 @@ impl VariantData {
             VariantData::Unit => StructKind::Unit,
         }
     }
+
+    #[allow(clippy::self_named_constructors)]
+    pub(crate) fn variant_data(db: &dyn DefDatabase, id: VariantId) -> Arc<VariantData> {
+        match id {
+            VariantId::StructId(it) => db.struct_data(it).variant_data.clone(),
+            VariantId::EnumVariantId(it) => db.enum_variant_data(it).variant_data.clone(),
+            VariantId::UnionId(it) => db.union_data(it).variant_data.clone(),
+        }
+    }
 }
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs
index 30d52d87f19..55ecabdc38e 100644
--- a/crates/hir-def/src/db.rs
+++ b/crates/hir-def/src/db.rs
@@ -12,7 +12,7 @@ use crate::{
     attr::{Attrs, AttrsWithOwner},
     body::{scope::ExprScopes, Body, BodySourceMap},
     data::{
-        adt::{EnumData, EnumVariantData, StructData},
+        adt::{EnumData, EnumVariantData, StructData, VariantData},
         ConstData, ExternCrateDeclData, FunctionData, ImplData, Macro2Data, MacroRulesData,
         ProcMacroData, StaticData, TraitAliasData, TraitData, TypeAliasData,
     },
@@ -128,6 +128,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
     ) -> (Arc<EnumVariantData>, DefDiagnostics);
 
     #[salsa::transparent]
+    #[salsa::invoke(VariantData::variant_data)]
+    fn variant_data(&self, id: VariantId) -> Arc<VariantData>;
+    #[salsa::transparent]
     #[salsa::invoke(ImplData::impl_data_query)]
     fn impl_data(&self, e: ImplId) -> Arc<ImplData>;
 
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs
index 585e93ce21e..610480736cc 100644
--- a/crates/hir-def/src/item_tree.rs
+++ b/crates/hir-def/src/item_tree.rs
@@ -716,6 +716,7 @@ pub struct Const {
     pub visibility: RawVisibilityId,
     pub type_ref: Interned<TypeRef>,
     pub ast_id: FileAstId<ast::Const>,
+    pub has_body: bool,
 }
 
 #[derive(Debug, Clone, Eq, PartialEq)]
diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs
index f02163cbe44..4b5ef56d782 100644
--- a/crates/hir-def/src/item_tree/lower.rs
+++ b/crates/hir-def/src/item_tree/lower.rs
@@ -446,7 +446,7 @@ impl<'a> Ctx<'a> {
         let type_ref = self.lower_type_ref_opt(konst.ty());
         let visibility = self.lower_visibility(konst);
         let ast_id = self.source_ast_id_map.ast_id(konst);
-        let res = Const { name, visibility, type_ref, ast_id };
+        let res = Const { name, visibility, type_ref, ast_id, has_body: konst.body().is_some() };
         id(self.data().consts.alloc(res))
     }
 
diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs
index 0c84057950b..cef2a3fb866 100644
--- a/crates/hir-def/src/item_tree/pretty.rs
+++ b/crates/hir-def/src/item_tree/pretty.rs
@@ -357,7 +357,7 @@ impl Printer<'_> {
                 wln!(self, "}}");
             }
             ModItem::Const(it) => {
-                let Const { name, visibility, type_ref, ast_id } = &self.tree[it];
+                let Const { name, visibility, type_ref, ast_id, has_body: _ } = &self.tree[it];
                 self.print_ast_id(ast_id.erase());
                 self.print_visibility(*visibility);
                 w!(self, "const ");
diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs
index 38eb3371e3c..ecbb1d4c60e 100644
--- a/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -43,7 +43,7 @@ mod allow {
 }
 
 pub fn incorrect_case(db: &dyn HirDatabase, owner: ModuleDefId) -> Vec<IncorrectCase> {
-    let _p = tracing::span!(tracing::Level::INFO, "validate_module_item").entered();
+    let _p = tracing::span!(tracing::Level::INFO, "incorrect_case").entered();
     let mut validator = DeclValidator::new(db);
     validator.validate_item(owner);
     validator.sink
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index 20b0da441da..a5a42c52af0 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -11,6 +11,7 @@ use hir_def::{ItemContainerId, Lookup};
 use hir_expand::name;
 use itertools::Itertools;
 use rustc_hash::FxHashSet;
+use rustc_pattern_analysis::constructor::Constructor;
 use syntax::{ast, AstNode};
 use tracing::debug;
 use triomphe::Arc;
@@ -190,45 +191,45 @@ impl ExprValidator {
         let pattern_arena = Arena::new();
         let mut m_arms = Vec::with_capacity(arms.len());
         let mut has_lowering_errors = false;
+        // Note: Skipping the entire diagnostic rather than just not including a faulty match arm is
+        // preferred to avoid the chance of false positives.
         for arm in arms {
-            if let Some(pat_ty) = self.infer.type_of_pat.get(arm.pat) {
-                // We only include patterns whose type matches the type
-                // of the scrutinee expression. If we had an InvalidMatchArmPattern
-                // diagnostic or similar we could raise that in an else
-                // block here.
-                //
-                // When comparing the types, we also have to consider that rustc
-                // will automatically de-reference the scrutinee expression type if
-                // necessary.
-                //
-                // FIXME we should use the type checker for this.
-                if (pat_ty == scrut_ty
-                    || scrut_ty
-                        .as_reference()
-                        .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty)
-                        .unwrap_or(false))
-                    && types_of_subpatterns_do_match(arm.pat, &self.body, &self.infer)
-                {
-                    // If we had a NotUsefulMatchArm diagnostic, we could
-                    // check the usefulness of each pattern as we added it
-                    // to the matrix here.
-                    let pat = self.lower_pattern(&cx, arm.pat, db, &mut has_lowering_errors);
-                    let m_arm = pat_analysis::MatchArm {
-                        pat: pattern_arena.alloc(pat),
-                        has_guard: arm.guard.is_some(),
-                        arm_data: (),
-                    };
-                    m_arms.push(m_arm);
-                    if !has_lowering_errors {
-                        continue;
-                    }
+            let Some(pat_ty) = self.infer.type_of_pat.get(arm.pat) else {
+                return;
+            };
+
+            // We only include patterns whose type matches the type
+            // of the scrutinee expression. If we had an InvalidMatchArmPattern
+            // diagnostic or similar we could raise that in an else
+            // block here.
+            //
+            // When comparing the types, we also have to consider that rustc
+            // will automatically de-reference the scrutinee expression type if
+            // necessary.
+            //
+            // FIXME we should use the type checker for this.
+            if (pat_ty == scrut_ty
+                || scrut_ty
+                    .as_reference()
+                    .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty)
+                    .unwrap_or(false))
+                && types_of_subpatterns_do_match(arm.pat, &self.body, &self.infer)
+            {
+                // If we had a NotUsefulMatchArm diagnostic, we could
+                // check the usefulness of each pattern as we added it
+                // to the matrix here.
+                let pat = self.lower_pattern(&cx, arm.pat, db, &mut has_lowering_errors);
+                let m_arm = pat_analysis::MatchArm {
+                    pat: pattern_arena.alloc(pat),
+                    has_guard: arm.guard.is_some(),
+                    arm_data: (),
+                };
+                m_arms.push(m_arm);
+                if !has_lowering_errors {
+                    continue;
                 }
             }
-
-            // If we can't resolve the type of a pattern, or the pattern type doesn't
-            // fit the match expression, we skip this diagnostic. Skipping the entire
-            // diagnostic rather than just not including this match arm is preferred
-            // to avoid the chance of false positives.
+            // If the pattern type doesn't fit the match expression, we skip this diagnostic.
             cov_mark::hit!(validate_match_bailed_out);
             return;
         }
@@ -266,15 +267,17 @@ impl ExprValidator {
 
             let mut have_errors = false;
             let deconstructed_pat = self.lower_pattern(&cx, pat, db, &mut have_errors);
+
+            // optimization, wildcard trivially hold
+            if have_errors || matches!(deconstructed_pat.ctor(), Constructor::Wildcard) {
+                continue;
+            }
+
             let match_arm = rustc_pattern_analysis::MatchArm {
                 pat: pattern_arena.alloc(deconstructed_pat),
                 has_guard: false,
                 arm_data: (),
             };
-            if have_errors {
-                continue;
-            }
-
             let report = match cx.compute_match_usefulness(&[match_arm], ty.clone()) {
                 Ok(v) => v,
                 Err(e) => {
@@ -531,8 +534,16 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul
     fn walk(pat: PatId, body: &Body, infer: &InferenceResult, has_type_mismatches: &mut bool) {
         match infer.type_mismatch_for_pat(pat) {
             Some(_) => *has_type_mismatches = true,
+            None if *has_type_mismatches => (),
             None => {
-                body[pat].walk_child_pats(|subpat| walk(subpat, body, infer, has_type_mismatches))
+                let pat = &body[pat];
+                if let Pat::ConstBlock(expr) | Pat::Lit(expr) = *pat {
+                    *has_type_mismatches |= infer.type_mismatch_for_expr(expr).is_some();
+                    if *has_type_mismatches {
+                        return;
+                    }
+                }
+                pat.walk_child_pats(|subpat| walk(subpat, body, infer, has_type_mismatches))
             }
         }
     }
diff --git a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
index 0b24fe72276..c171dbc1700 100644
--- a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -1,9 +1,9 @@
 //! Interface with `rustc_pattern_analysis`.
 
 use std::fmt;
-use tracing::debug;
 
 use hir_def::{DefWithBodyId, EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId};
+use once_cell::unsync::Lazy;
 use rustc_hash::FxHashMap;
 use rustc_pattern_analysis::{
     constructor::{Constructor, ConstructorSet, VariantVisibility},
@@ -91,20 +91,13 @@ impl<'p> MatchCheckCtx<'p> {
     }
 
     fn is_uninhabited(&self, ty: &Ty) -> bool {
-        is_ty_uninhabited_from(ty, self.module, self.db)
+        is_ty_uninhabited_from(self.db, ty, self.module)
     }
 
-    /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
-    fn is_foreign_non_exhaustive_enum(&self, ty: &Ty) -> bool {
-        match ty.as_adt() {
-            Some((adt @ hir_def::AdtId::EnumId(_), _)) => {
-                let has_non_exhaustive_attr =
-                    self.db.attrs(adt.into()).by_key("non_exhaustive").exists();
-                let is_local = adt.module(self.db.upcast()).krate() == self.module.krate();
-                has_non_exhaustive_attr && !is_local
-            }
-            _ => false,
-        }
+    /// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`.
+    fn is_foreign_non_exhaustive(&self, adt: hir_def::AdtId) -> bool {
+        let is_local = adt.krate(self.db.upcast()) == self.module.krate();
+        !is_local && self.db.attrs(adt.into()).by_key("non_exhaustive").exists()
     }
 
     fn variant_id_for_adt(
@@ -376,24 +369,21 @@ impl<'p> PatCx for MatchCheckCtx<'p> {
                         single(subst_ty)
                     } else {
                         let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap();
-                        let (adt, _) = ty.as_adt().unwrap();
 
-                        let adt_is_local =
-                            variant.module(self.db.upcast()).krate() == self.module.krate();
                         // Whether we must not match the fields of this variant exhaustively.
-                        let is_non_exhaustive =
-                            self.db.attrs(variant.into()).by_key("non_exhaustive").exists()
-                                && !adt_is_local;
-                        let visibilities = self.db.field_visibilities(variant);
+                        let is_non_exhaustive = Lazy::new(|| self.is_foreign_non_exhaustive(adt));
+                        let visibilities = Lazy::new(|| self.db.field_visibilities(variant));
 
                         self.list_variant_fields(ty, variant)
                             .map(move |(fid, ty)| {
-                                let is_visible = matches!(adt, hir_def::AdtId::EnumId(..))
-                                    || visibilities[fid]
-                                        .is_visible_from(self.db.upcast(), self.module);
+                                let is_visible = || {
+                                    matches!(adt, hir_def::AdtId::EnumId(..))
+                                        || visibilities[fid]
+                                            .is_visible_from(self.db.upcast(), self.module)
+                                };
                                 let is_uninhabited = self.is_uninhabited(&ty);
                                 let private_uninhabited =
-                                    is_uninhabited && (!is_visible || is_non_exhaustive);
+                                    is_uninhabited && (!is_visible() || *is_non_exhaustive);
                                 (ty, PrivateUninhabitedField(private_uninhabited))
                             })
                             .collect()
@@ -445,17 +435,20 @@ impl<'p> PatCx for MatchCheckCtx<'p> {
             TyKind::Scalar(Scalar::Char) => unhandled(),
             TyKind::Scalar(Scalar::Int(..) | Scalar::Uint(..)) => unhandled(),
             TyKind::Array(..) | TyKind::Slice(..) => unhandled(),
-            TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), subst) => {
-                let enum_data = cx.db.enum_data(*enum_id);
-                let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty);
+            &TyKind::Adt(AdtId(adt @ hir_def::AdtId::EnumId(enum_id)), ref subst) => {
+                let enum_data = cx.db.enum_data(enum_id);
+                let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive(adt);
 
                 if enum_data.variants.is_empty() && !is_declared_nonexhaustive {
                     ConstructorSet::NoConstructors
                 } else {
-                    let mut variants = FxHashMap::default();
+                    let mut variants = FxHashMap::with_capacity_and_hasher(
+                        enum_data.variants.len(),
+                        Default::default(),
+                    );
                     for (i, &(variant, _)) in enum_data.variants.iter().enumerate() {
                         let is_uninhabited =
-                            is_enum_variant_uninhabited_from(variant, subst, cx.module, cx.db);
+                            is_enum_variant_uninhabited_from(cx.db, variant, subst, cx.module);
                         let visibility = if is_uninhabited {
                             VariantVisibility::Empty
                         } else {
@@ -506,7 +499,7 @@ impl<'p> PatCx for MatchCheckCtx<'p> {
     }
 
     fn bug(&self, fmt: fmt::Arguments<'_>) {
-        debug!("{}", fmt)
+        never!("{}", fmt)
     }
 
     fn complexity_exceeded(&self) -> Result<(), Self::Error> {
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index cbca0e801d4..f13ad30c2a2 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -4,7 +4,7 @@
 use hir_def::{
     body::Body,
     hir::{Expr, ExprId, UnaryOp},
-    resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
+    resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
     DefWithBodyId,
 };
 
@@ -13,9 +13,9 @@ use crate::{
 };
 
 pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
-    let infer = db.infer(def);
-    let mut res = Vec::new();
+    let _p = tracing::span!(tracing::Level::INFO, "missing_unsafe",);
 
+    let mut res = Vec::new();
     let is_unsafe = match def {
         DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(),
         DefWithBodyId::StaticId(_)
@@ -28,6 +28,7 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
     }
 
     let body = db.body(def);
+    let infer = db.infer(def);
     unsafe_expressions(db, &infer, def, &body, body.body_expr, &mut |expr| {
         if !expr.inside_unsafe_block {
             res.push(expr.expr);
@@ -51,14 +52,24 @@ pub fn unsafe_expressions(
     current: ExprId,
     unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr),
 ) {
-    walk_unsafe(db, infer, def, body, current, false, unsafe_expr_cb)
+    walk_unsafe(
+        db,
+        infer,
+        body,
+        &mut resolver_for_expr(db.upcast(), def, current),
+        def,
+        current,
+        false,
+        unsafe_expr_cb,
+    )
 }
 
 fn walk_unsafe(
     db: &dyn HirDatabase,
     infer: &InferenceResult,
-    def: DefWithBodyId,
     body: &Body,
+    resolver: &mut Resolver,
+    def: DefWithBodyId,
     current: ExprId,
     inside_unsafe_block: bool,
     unsafe_expr_cb: &mut dyn FnMut(UnsafeExpr),
@@ -73,13 +84,14 @@ fn walk_unsafe(
             }
         }
         Expr::Path(path) => {
-            let resolver = resolver_for_expr(db.upcast(), def, current);
+            let g = resolver.update_to_inner_scope(db.upcast(), def, current);
             let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path);
             if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
                 if db.static_data(id).mutable {
                     unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
                 }
             }
+            resolver.reset_to_guard(g);
         }
         Expr::MethodCall { .. } => {
             if infer
@@ -97,13 +109,13 @@ fn walk_unsafe(
         }
         Expr::Unsafe { .. } => {
             return expr.walk_child_exprs(|child| {
-                walk_unsafe(db, infer, def, body, child, true, unsafe_expr_cb);
+                walk_unsafe(db, infer, body, resolver, def, child, true, unsafe_expr_cb);
             });
         }
         _ => {}
     }
 
     expr.walk_child_exprs(|child| {
-        walk_unsafe(db, infer, def, body, child, inside_unsafe_block, unsafe_expr_cb);
+        walk_unsafe(db, infer, body, resolver, def, child, inside_unsafe_block, unsafe_expr_cb);
     });
 }
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 3aacf7d07f9..281386e1364 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -432,6 +432,7 @@ pub struct InferenceResult {
     /// Whether there are any type-mismatching errors in the result.
     pub(crate) has_errors: bool,
     /// Interned common types to return references to.
+    // FIXME: Move this into `InferenceContext`
     standard_types: InternedStandardTypes,
     /// Stores the types which were implicitly dereferenced in pattern binding modes.
     pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
diff --git a/crates/hir-ty/src/inhabitedness.rs b/crates/hir-ty/src/inhabitedness.rs
index 532b650e8ff..f0a28d322c4 100644
--- a/crates/hir-ty/src/inhabitedness.rs
+++ b/crates/hir-ty/src/inhabitedness.rs
@@ -5,42 +5,36 @@ use chalk_ir::{
     visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
     DebruijnIndex,
 };
-use hir_def::{
-    attr::Attrs, data::adt::VariantData, visibility::Visibility, AdtId, EnumVariantId, HasModule,
-    ModuleId, VariantId,
-};
+use hir_def::{visibility::Visibility, AdtId, EnumVariantId, HasModule, ModuleId, VariantId};
 use rustc_hash::FxHashSet;
 
 use crate::{
     consteval::try_const_usize, db::HirDatabase, Binders, Interner, Substitution, Ty, TyKind,
 };
 
+// FIXME: Turn this into a query, it can be quite slow
 /// Checks whether a type is visibly uninhabited from a particular module.
-pub(crate) fn is_ty_uninhabited_from(ty: &Ty, target_mod: ModuleId, db: &dyn HirDatabase) -> bool {
+pub(crate) fn is_ty_uninhabited_from(db: &dyn HirDatabase, ty: &Ty, target_mod: ModuleId) -> bool {
+    let _p = tracing::span!(tracing::Level::INFO, "is_ty_uninhabited_from", ?ty);
     let mut uninhabited_from =
         UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
     let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
     inhabitedness == BREAK_VISIBLY_UNINHABITED
 }
 
+// FIXME: Turn this into a query, it can be quite slow
 /// Checks whether a variant is visibly uninhabited from a particular module.
 pub(crate) fn is_enum_variant_uninhabited_from(
+    db: &dyn HirDatabase,
     variant: EnumVariantId,
     subst: &Substitution,
     target_mod: ModuleId,
-    db: &dyn HirDatabase,
 ) -> bool {
-    let is_local = variant.module(db.upcast()).krate() == target_mod.krate();
+    let _p = tracing::span!(tracing::Level::INFO, "is_enum_variant_uninhabited_from",);
 
     let mut uninhabited_from =
         UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
-    let inhabitedness = uninhabited_from.visit_variant(
-        variant.into(),
-        &db.enum_variant_data(variant).variant_data,
-        subst,
-        &db.attrs(variant.into()),
-        is_local,
-    );
+    let inhabitedness = uninhabited_from.visit_variant(variant.into(), subst);
     inhabitedness == BREAK_VISIBLY_UNINHABITED
 }
 
@@ -98,34 +92,18 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
 
 impl UninhabitedFrom<'_> {
     fn visit_adt(&mut self, adt: AdtId, subst: &Substitution) -> ControlFlow<VisiblyUninhabited> {
-        let attrs = self.db.attrs(adt.into());
-        let adt_non_exhaustive = attrs.by_key("non_exhaustive").exists();
-        let is_local = adt.module(self.db.upcast()).krate() == self.target_mod.krate();
-        if adt_non_exhaustive && !is_local {
-            return CONTINUE_OPAQUELY_INHABITED;
-        }
-
         // An ADT is uninhabited iff all its variants uninhabited.
         match adt {
             // rustc: For now, `union`s are never considered uninhabited.
             AdtId::UnionId(_) => CONTINUE_OPAQUELY_INHABITED,
-            AdtId::StructId(s) => {
-                let struct_data = self.db.struct_data(s);
-                self.visit_variant(s.into(), &struct_data.variant_data, subst, &attrs, is_local)
-            }
+            AdtId::StructId(s) => self.visit_variant(s.into(), subst),
             AdtId::EnumId(e) => {
                 let enum_data = self.db.enum_data(e);
 
                 for &(variant, _) in enum_data.variants.iter() {
-                    let variant_inhabitedness = self.visit_variant(
-                        variant.into(),
-                        &self.db.enum_variant_data(variant).variant_data,
-                        subst,
-                        &self.db.attrs(variant.into()),
-                        is_local,
-                    );
+                    let variant_inhabitedness = self.visit_variant(variant.into(), subst);
                     match variant_inhabitedness {
-                        Break(VisiblyUninhabited) => continue,
+                        Break(VisiblyUninhabited) => (),
                         Continue(()) => return CONTINUE_OPAQUELY_INHABITED,
                     }
                 }
@@ -137,34 +115,36 @@ impl UninhabitedFrom<'_> {
     fn visit_variant(
         &mut self,
         variant: VariantId,
-        variant_data: &VariantData,
         subst: &Substitution,
-        attrs: &Attrs,
-        is_local: bool,
     ) -> ControlFlow<VisiblyUninhabited> {
-        let non_exhaustive_field_list = attrs.by_key("non_exhaustive").exists();
-        if non_exhaustive_field_list && !is_local {
+        let is_local = variant.krate(self.db.upcast()) == self.target_mod.krate();
+        if !is_local && self.db.attrs(variant.into()).by_key("non_exhaustive").exists() {
+            return CONTINUE_OPAQUELY_INHABITED;
+        }
+
+        let variant_data = self.db.variant_data(variant);
+        let fields = variant_data.fields();
+        if fields.is_empty() {
             return CONTINUE_OPAQUELY_INHABITED;
         }
 
         let is_enum = matches!(variant, VariantId::EnumVariantId(..));
         let field_tys = self.db.field_types(variant);
-        let field_vis = self.db.field_visibilities(variant);
+        let field_vis = if is_enum { None } else { Some(self.db.field_visibilities(variant)) };
 
-        for (fid, _) in variant_data.fields().iter() {
-            self.visit_field(field_vis[fid], &field_tys[fid], subst, is_enum)?;
+        for (fid, _) in fields.iter() {
+            self.visit_field(field_vis.as_ref().map(|it| it[fid]), &field_tys[fid], subst)?;
         }
         CONTINUE_OPAQUELY_INHABITED
     }
 
     fn visit_field(
         &mut self,
-        vis: Visibility,
+        vis: Option<Visibility>,
         ty: &Binders<Ty>,
         subst: &Substitution,
-        is_enum: bool,
     ) -> ControlFlow<VisiblyUninhabited> {
-        if is_enum || vis.is_visible_from(self.db.upcast(), self.target_mod) {
+        if vis.map_or(true, |it| it.is_visible_from(self.db.upcast(), self.target_mod)) {
             let ty = ty.clone().substitute(Interner, subst);
             ty.visit_with(self, DebruijnIndex::INNERMOST)
         } else {
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index c65dfd32853..151f65cfbb8 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -1706,7 +1706,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
     }
 
     fn is_uninhabited(&self, expr_id: ExprId) -> bool {
-        is_ty_uninhabited_from(&self.infer[expr_id], self.owner.module(self.db.upcast()), self.db)
+        is_ty_uninhabited_from(self.db, &self.infer[expr_id], self.owner.module(self.db.upcast()))
     }
 
     /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 6bffb0c5e7b..12f5a89caaa 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -548,8 +548,7 @@ impl Module {
         acc: &mut Vec<AnyDiagnostic>,
         style_lints: bool,
     ) {
-        let name = self.name(db);
-        let _p = tracing::span!(tracing::Level::INFO, "Module::diagnostics", ?name);
+        let _p = tracing::span!(tracing::Level::INFO, "Module::diagnostics", name = ?self.name(db));
         let def_map = self.id.def_map(db.upcast());
         for diag in def_map.diagnostics() {
             if diag.in_module != self.id.local_id {
@@ -684,7 +683,7 @@ impl Module {
                 let items = &db.trait_data(trait_.into()).items;
                 let required_items = items.iter().filter(|&(_, assoc)| match *assoc {
                     AssocItemId::FunctionId(it) => !db.function_data(it).has_body(),
-                    AssocItemId::ConstId(id) => Const::from(id).value(db).is_none(),
+                    AssocItemId::ConstId(id) => !db.const_data(id).has_body,
                     AssocItemId::TypeAliasId(it) => db.type_alias_data(it).type_ref.is_none(),
                 });
                 impl_assoc_items_scratch.extend(db.impl_data(impl_def.id).items.iter().filter_map(
@@ -1628,7 +1627,6 @@ impl DefWithBody {
         acc: &mut Vec<AnyDiagnostic>,
         style_lints: bool,
     ) {
-        db.unwind_if_cancelled();
         let krate = self.module(db).id.krate();
 
         let (body, source_map) = db.body_with_source_map(self.into());
@@ -1762,7 +1760,9 @@ impl DefWithBody {
                         need_mut = &mir::MutabilityReason::Not;
                     }
                     let local = Local { parent: self.into(), binding_id };
-                    match (need_mut, local.is_mut(db)) {
+                    let is_mut = body[binding_id].mode == BindingAnnotation::Mutable;
+
+                    match (need_mut, is_mut) {
                         (mir::MutabilityReason::Unused, _) => {
                             let should_ignore = matches!(body[binding_id].name.as_str(), Some(it) if it.starts_with('_'));
                             if !should_ignore {
diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
index 045154614f8..6d0119fb57c 100644
--- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
@@ -317,7 +317,8 @@ fn main() {
     #[test]
     fn mismatched_types_issue_15883() {
         // Check we don't panic.
-        check_diagnostics_no_bails(
+        cov_mark::check!(validate_match_bailed_out);
+        check_diagnostics(
             r#"
 //- minicore: option
 fn main() {
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 270cf844c65..c3ced36a696 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -320,13 +320,11 @@ pub fn diagnostics(
     let module = sema.file_to_module_def(file_id);
 
     let ctx = DiagnosticsContext { config, sema, resolve };
-    if module.is_none() {
-        handlers::unlinked_file::unlinked_file(&ctx, &mut res, file_id);
-    }
 
     let mut diags = Vec::new();
-    if let Some(m) = module {
-        m.diagnostics(db, &mut diags, config.style_lints);
+    match module {
+        Some(m) => m.diagnostics(db, &mut diags, config.style_lints),
+        None => handlers::unlinked_file::unlinked_file(&ctx, &mut res, file_id),
     }
 
     for diag in diags {
@@ -409,6 +407,11 @@ pub fn diagnostics(
         res.push(d)
     }
 
+    res.retain(|d| {
+        !(ctx.config.disabled.contains(d.code.as_str())
+            || ctx.config.disable_experimental && d.experimental)
+    });
+
     let mut diagnostics_of_range = res
         .iter_mut()
         .filter_map(|it| {
@@ -421,9 +424,14 @@ pub fn diagnostics(
         })
         .collect::<FxHashMap<_, _>>();
 
+    if diagnostics_of_range.is_empty() {
+        return res;
+    }
+
     let mut rustc_stack: FxHashMap<String, Vec<Severity>> = FxHashMap::default();
     let mut clippy_stack: FxHashMap<String, Vec<Severity>> = FxHashMap::default();
 
+    // FIXME: This becomes quite expensive for big files
     handle_lint_attributes(
         &ctx.sema,
         parse.syntax(),
@@ -432,11 +440,7 @@ pub fn diagnostics(
         &mut diagnostics_of_range,
     );
 
-    res.retain(|d| {
-        d.severity != Severity::Allow
-            && !ctx.config.disabled.contains(d.code.as_str())
-            && !(ctx.config.disable_experimental && d.experimental)
-    });
+    res.retain(|d| d.severity != Severity::Allow);
 
     res
 }
@@ -476,6 +480,7 @@ fn handle_lint_attributes(
     clippy_stack: &mut FxHashMap<String, Vec<Severity>>,
     diagnostics_of_range: &mut FxHashMap<InFile<SyntaxNode>, &mut Diagnostic>,
 ) {
+    let _g = tracing::span!(tracing::Level::INFO, "handle_lint_attributes").entered();
     let file_id = sema.hir_file_for(root);
     let preorder = root.preorder();
     for ev in preorder {
@@ -486,24 +491,24 @@ fn handle_lint_attributes(
                         stack.push(severity);
                     });
                 }
-                if let Some(x) =
+                if let Some(it) =
                     diagnostics_of_range.get_mut(&InFile { file_id, value: node.clone() })
                 {
                     const EMPTY_LINTS: &[&str] = &[];
-                    let (names, stack) = match x.code {
+                    let (names, stack) = match it.code {
                         DiagnosticCode::RustcLint(name) => (
-                            RUSTC_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |x| &**x),
+                            RUSTC_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |it| &**it),
                             &mut *rustc_stack,
                         ),
                         DiagnosticCode::Clippy(name) => (
-                            CLIPPY_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |x| &**x),
+                            CLIPPY_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |it| &**it),
                             &mut *clippy_stack,
                         ),
                         _ => continue,
                     };
                     for &name in names {
-                        if let Some(s) = stack.get(name).and_then(|x| x.last()) {
-                            x.severity = *s;
+                        if let Some(s) = stack.get(name).and_then(|it| it.last()) {
+                            it.severity = *s;
                         }
                     }
                 }
@@ -571,8 +576,8 @@ fn parse_lint_attribute(
         if let Some(lint) = lint.as_single_name_ref() {
             job(rustc_stack.entry(lint.to_string()).or_default(), severity);
         }
-        if let Some(tool) = lint.qualifier().and_then(|x| x.as_single_name_ref()) {
-            if let Some(name_ref) = &lint.segment().and_then(|x| x.name_ref()) {
+        if let Some(tool) = lint.qualifier().and_then(|it| it.as_single_name_ref()) {
+            if let Some(name_ref) = &lint.segment().and_then(|it| it.name_ref()) {
                 if tool.to_string() == "clippy" {
                     job(clippy_stack.entry(name_ref.to_string()).or_default(), severity);
                 }
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs
index 2731e845f35..7b385ca9d96 100644
--- a/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -40,7 +40,7 @@ fn integrated_highlighting_benchmark() {
     };
     let load_cargo_config = LoadCargoConfig {
         load_out_dirs_from_check: true,
-        with_proc_macro_server: ProcMacroServerChoice::None,
+        with_proc_macro_server: ProcMacroServerChoice::Sysroot,
         prefill_caches: false,
     };
 
@@ -100,7 +100,7 @@ fn integrated_completion_benchmark() {
     };
     let load_cargo_config = LoadCargoConfig {
         load_out_dirs_from_check: true,
-        with_proc_macro_server: ProcMacroServerChoice::None,
+        with_proc_macro_server: ProcMacroServerChoice::Sysroot,
         prefill_caches: true,
     };
 
@@ -262,7 +262,7 @@ fn integrated_diagnostics_benchmark() {
     };
     let load_cargo_config = LoadCargoConfig {
         load_out_dirs_from_check: true,
-        with_proc_macro_server: ProcMacroServerChoice::None,
+        with_proc_macro_server: ProcMacroServerChoice::Sysroot,
         prefill_caches: true,
     };
 
@@ -300,7 +300,7 @@ fn integrated_diagnostics_benchmark() {
         .diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id)
         .unwrap();
 
-    let _g = crate::tracing::hprof::init("*>1");
+    let _g = crate::tracing::hprof::init("*");
 
     {
         let _it = stdx::timeit("change");