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 { 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, pub visibility: RawVisibility, pub rustc_allow_incoherent_impl: bool, + pub has_body: bool, } impl ConstData { @@ -533,6 +534,7 @@ pub(crate) fn const_data_query(db: &dyn DefDatabase, konst: ConstId) -> Arc StructKind { VariantData::Unit => StructKind::Unit, } } + + #[allow(clippy::self_named_constructors)] + pub(crate) fn variant_data(db: &dyn DefDatabase, id: VariantId) -> Arc { + 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 @@ 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, }, @@ -127,6 +127,9 @@ fn enum_variant_data_with_diagnostics( id: EnumVariantId, ) -> (Arc, DefDiagnostics); + #[salsa::transparent] + #[salsa::invoke(VariantData::variant_data)] + fn variant_data(&self, id: VariantId) -> Arc; #[salsa::transparent] #[salsa::invoke(ImplData::impl_data_query)] fn impl_data(&self, e: ImplId) -> Arc; 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, pub ast_id: FileAstId, + 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 @@ fn lower_const(&mut self, konst: &ast::Const) -> FileItemTreeId { 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 @@ fn print_mod_item(&mut self, item: ModItem) { 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 { - 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_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 @@ fn validate_match( 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 @@ fn validate_block(&mut self, db: &dyn HirDatabase, expr: &Expr) { 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 @@ pub(crate) fn compute_match_usefulness( } 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 @@ fn ctor_sub_tys<'a>( 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 @@ fn ctors_for_ty( 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 @@ fn write_variant_name( } 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 @@ }; pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec { - 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 { } 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>, 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 @@ 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 @@ fn interner(&self) -> Interner { impl UninhabitedFrom<'_> { fn visit_adt(&mut self, adt: AdtId, subst: &Substitution) -> ControlFlow { - 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 @@ fn visit_adt(&mut self, adt: AdtId, subst: &Substitution) -> ControlFlow ControlFlow { - 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, ty: &Binders, subst: &Substitution, - is_enum: bool, ) -> ControlFlow { - 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 @@ fn current_loop_end(&mut self) -> Result { } 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 @@ pub fn diagnostics( acc: &mut Vec, 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 @@ pub fn diagnostics( 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 @@ pub fn diagnostics( acc: &mut Vec, 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 @@ pub fn diagnostics( 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::>(); + if diagnostics_of_range.is_empty() { + return res; + } + let mut rustc_stack: FxHashMap> = FxHashMap::default(); let mut clippy_stack: FxHashMap> = 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>, diagnostics_of_range: &mut FxHashMap, &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");