Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/diagnostics/expr.rs')
-rw-r--r--crates/hir-ty/src/diagnostics/expr.rs187
1 files changed, 100 insertions, 87 deletions
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index cc531f076d..0eca0c09d6 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -5,7 +5,6 @@
use std::fmt;
use base_db::Crate;
-use chalk_solve::rust_ir::AdtKind;
use either::Either;
use hir_def::{
AdtId, AssocItemId, DefWithBodyId, HasModule, ItemContainerId, Lookup,
@@ -16,6 +15,7 @@ use intern::sym;
use itertools::Itertools;
use rustc_hash::FxHashSet;
use rustc_pattern_analysis::constructor::Constructor;
+use rustc_type_ir::inherent::{AdtDef, IntoKind};
use syntax::{
AstNode,
ast::{self, UnaryOp},
@@ -25,13 +25,17 @@ use triomphe::Arc;
use typed_arena::Arena;
use crate::{
- Adjust, InferenceResult, Interner, TraitEnvironment, Ty, TyExt, TyKind,
+ Adjust, InferenceResult, TraitEnvironment,
db::HirDatabase,
diagnostics::match_check::{
self,
pat_analysis::{self, DeconstructedPat, MatchCheckCtx, WitnessPat},
},
display::{DisplayTarget, HirDisplay},
+ next_solver::{
+ DbInterner, Ty, TyKind, TypingMode,
+ infer::{DbInternerInferExt, InferCtxt},
+ },
};
pub(crate) use hir_def::{
@@ -75,24 +79,41 @@ impl BodyValidationDiagnostic {
let infer = db.infer(owner);
let body = db.body(owner);
let env = db.trait_environment_for_body(owner);
- let mut validator =
- ExprValidator { owner, body, infer, diagnostics: Vec::new(), validate_lints, env };
- validator.validate_body(db);
+ let interner = DbInterner::new_with(db, Some(env.krate), env.block);
+ let infcx =
+ interner.infer_ctxt().build(TypingMode::typeck_for_body(interner, owner.into()));
+ let mut validator = ExprValidator {
+ owner,
+ body,
+ infer,
+ diagnostics: Vec::new(),
+ validate_lints,
+ env,
+ infcx,
+ };
+ validator.validate_body();
validator.diagnostics
}
}
-struct ExprValidator {
+struct ExprValidator<'db> {
owner: DefWithBodyId,
body: Arc<Body>,
- infer: Arc<InferenceResult>,
- env: Arc<TraitEnvironment>,
+ infer: Arc<InferenceResult<'db>>,
+ env: Arc<TraitEnvironment<'db>>,
diagnostics: Vec<BodyValidationDiagnostic>,
validate_lints: bool,
+ infcx: InferCtxt<'db>,
}
-impl ExprValidator {
- fn validate_body(&mut self, db: &dyn HirDatabase) {
+impl<'db> ExprValidator<'db> {
+ #[inline]
+ fn db(&self) -> &'db dyn HirDatabase {
+ self.infcx.interner.db
+ }
+
+ fn validate_body(&mut self) {
+ let db = self.db();
let mut filter_map_next_checker = None;
// we'll pass &mut self while iterating over body.exprs, so they need to be disjoint
let body = Arc::clone(&self.body);
@@ -114,19 +135,19 @@ impl ExprValidator {
match expr {
Expr::Match { expr, arms } => {
- self.validate_match(id, *expr, arms, db);
+ self.validate_match(id, *expr, arms);
}
Expr::Call { .. } | Expr::MethodCall { .. } => {
- self.validate_call(db, id, expr, &mut filter_map_next_checker);
+ self.validate_call(id, expr, &mut filter_map_next_checker);
}
Expr::Closure { body: body_expr, .. } => {
self.check_for_trailing_return(*body_expr, &body);
}
Expr::If { .. } => {
- self.check_for_unnecessary_else(id, expr, db);
+ self.check_for_unnecessary_else(id, expr);
}
Expr::Block { .. } | Expr::Async { .. } | Expr::Unsafe { .. } => {
- self.validate_block(db, expr);
+ self.validate_block(expr);
}
_ => {}
}
@@ -147,10 +168,9 @@ impl ExprValidator {
fn validate_call(
&mut self,
- db: &dyn HirDatabase,
call_id: ExprId,
expr: &Expr,
- filter_map_next_checker: &mut Option<FilterMapNextChecker>,
+ filter_map_next_checker: &mut Option<FilterMapNextChecker<'db>>,
) {
if !self.validate_lints {
return;
@@ -166,8 +186,9 @@ impl ExprValidator {
None => return,
};
- let checker = filter_map_next_checker
- .get_or_insert_with(|| FilterMapNextChecker::new(&self.owner.resolver(db), db));
+ let checker = filter_map_next_checker.get_or_insert_with(|| {
+ FilterMapNextChecker::new(&self.owner.resolver(self.db()), self.db())
+ });
if checker.check(call_id, receiver, &callee).is_some() {
self.diagnostics.push(BodyValidationDiagnostic::ReplaceFilterMapNextWithFindMap {
@@ -176,26 +197,20 @@ impl ExprValidator {
}
if let Some(receiver_ty) = self.infer.type_of_expr_with_adjust(*receiver) {
- checker.prev_receiver_ty = Some(receiver_ty.clone());
+ checker.prev_receiver_ty = Some(receiver_ty);
}
}
}
- fn validate_match(
- &mut self,
- match_expr: ExprId,
- scrutinee_expr: ExprId,
- arms: &[MatchArm],
- db: &dyn HirDatabase,
- ) {
+ fn validate_match(&mut self, match_expr: ExprId, scrutinee_expr: ExprId, arms: &[MatchArm]) {
let Some(scrut_ty) = self.infer.type_of_expr_with_adjust(scrutinee_expr) else {
return;
};
- if scrut_ty.contains_unknown() {
+ if scrut_ty.references_non_lt_error() {
return;
}
- let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db, self.env.clone());
+ let cx = MatchCheckCtx::new(self.owner.module(self.db()), &self.infcx, self.env.clone());
let pattern_arena = Arena::new();
let mut m_arms = Vec::with_capacity(arms.len());
@@ -206,7 +221,7 @@ impl ExprValidator {
let Some(pat_ty) = self.infer.type_of_pat_with_adjust(arm.pat) else {
return;
};
- if pat_ty.contains_unknown() {
+ if pat_ty.references_non_lt_error() {
return;
}
@@ -230,7 +245,7 @@ impl ExprValidator {
// 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 pat = self.lower_pattern(&cx, arm.pat, &mut has_lowering_errors);
let m_arm = pat_analysis::MatchArm {
pat: pattern_arena.alloc(pat),
has_guard: arm.guard.is_some(),
@@ -246,15 +261,12 @@ impl ExprValidator {
return;
}
- let known_valid_scrutinee = Some(self.is_known_valid_scrutinee(scrutinee_expr, db));
- let report = match cx.compute_match_usefulness(
- m_arms.as_slice(),
- scrut_ty.clone(),
- known_valid_scrutinee,
- ) {
- Ok(report) => report,
- Err(()) => return,
- };
+ let known_valid_scrutinee = Some(self.is_known_valid_scrutinee(scrutinee_expr));
+ let report =
+ match cx.compute_match_usefulness(m_arms.as_slice(), scrut_ty, known_valid_scrutinee) {
+ Ok(report) => report,
+ Err(()) => return,
+ };
// FIXME Report unreachable arms
// https://github.com/rust-lang/rust/blob/f31622a50/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200
@@ -268,7 +280,7 @@ impl ExprValidator {
scrut_ty,
witnesses,
m_arms.is_empty(),
- self.owner.krate(db),
+ self.owner.krate(self.db()),
),
});
}
@@ -279,7 +291,9 @@ impl ExprValidator {
// While the above function in rustc uses thir exprs, r-a doesn't have them.
// So, the logic here is getting same result as "hir lowering + match with lowered thir"
// with "hir only"
- fn is_known_valid_scrutinee(&self, scrutinee_expr: ExprId, db: &dyn HirDatabase) -> bool {
+ fn is_known_valid_scrutinee(&self, scrutinee_expr: ExprId) -> bool {
+ let db = self.db();
+
if self
.infer
.expr_adjustments
@@ -299,22 +313,18 @@ impl ExprValidator {
);
value_or_partial.is_none_or(|v| !matches!(v, ValueNs::StaticId(_)))
}
- Expr::Field { expr, .. } => match self.infer.type_of_expr[*expr].kind(Interner) {
- TyKind::Adt(adt, ..)
- if db.adt_datum(self.owner.krate(db), *adt).kind == AdtKind::Union =>
- {
- false
- }
- _ => self.is_known_valid_scrutinee(*expr, db),
+ Expr::Field { expr, .. } => match self.infer.type_of_expr[*expr].kind() {
+ TyKind::Adt(adt, ..) if matches!(adt.def_id().0, AdtId::UnionId(_)) => false,
+ _ => self.is_known_valid_scrutinee(*expr),
},
- Expr::Index { base, .. } => self.is_known_valid_scrutinee(*base, db),
- Expr::Cast { expr, .. } => self.is_known_valid_scrutinee(*expr, db),
+ Expr::Index { base, .. } => self.is_known_valid_scrutinee(*base),
+ Expr::Cast { expr, .. } => self.is_known_valid_scrutinee(*expr),
Expr::Missing => false,
_ => true,
}
}
- fn validate_block(&mut self, db: &dyn HirDatabase, expr: &Expr) {
+ fn validate_block(&mut self, expr: &Expr) {
let (Expr::Block { statements, .. }
| Expr::Async { statements, .. }
| Expr::Unsafe { statements, .. }) = expr
@@ -322,7 +332,7 @@ impl ExprValidator {
return;
};
let pattern_arena = Arena::new();
- let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db, self.env.clone());
+ let cx = MatchCheckCtx::new(self.owner.module(self.db()), &self.infcx, self.env.clone());
for stmt in &**statements {
let &Statement::Let { pat, initializer, else_branch: None, .. } = stmt else {
continue;
@@ -332,12 +342,12 @@ impl ExprValidator {
}
let Some(initializer) = initializer else { continue };
let Some(ty) = self.infer.type_of_expr_with_adjust(initializer) else { continue };
- if ty.contains_unknown() {
+ if ty.references_non_lt_error() {
continue;
}
let mut have_errors = false;
- let deconstructed_pat = self.lower_pattern(&cx, pat, db, &mut have_errors);
+ let deconstructed_pat = self.lower_pattern(&cx, pat, &mut have_errors);
// optimization, wildcard trivially hold
if have_errors || matches!(deconstructed_pat.ctor(), Constructor::Wildcard) {
@@ -349,7 +359,7 @@ impl ExprValidator {
has_guard: false,
arm_data: (),
};
- let report = match cx.compute_match_usefulness(&[match_arm], ty.clone(), None) {
+ let report = match cx.compute_match_usefulness(&[match_arm], ty, None) {
Ok(v) => v,
Err(e) => {
debug!(?e, "match usefulness error");
@@ -365,21 +375,20 @@ impl ExprValidator {
ty,
witnesses,
false,
- self.owner.krate(db),
+ self.owner.krate(self.db()),
),
});
}
}
}
- fn lower_pattern<'p>(
+ fn lower_pattern<'a>(
&self,
- cx: &MatchCheckCtx<'p>,
+ cx: &MatchCheckCtx<'a, 'db>,
pat: PatId,
- db: &dyn HirDatabase,
have_errors: &mut bool,
- ) -> DeconstructedPat<'p> {
- let mut patcx = match_check::PatCtxt::new(db, &self.infer, &self.body);
+ ) -> DeconstructedPat<'a, 'db> {
+ let mut patcx = match_check::PatCtxt::new(self.db(), &self.infer, &self.body);
let pattern = patcx.lower_pattern(pat);
let pattern = cx.lower_pat(&pattern);
if !patcx.errors.is_empty() {
@@ -423,7 +432,7 @@ impl ExprValidator {
}
}
- fn check_for_unnecessary_else(&mut self, id: ExprId, expr: &Expr, db: &dyn HirDatabase) {
+ fn check_for_unnecessary_else(&mut self, id: ExprId, expr: &Expr) {
if !self.validate_lints {
return;
}
@@ -442,11 +451,11 @@ impl ExprValidator {
&& last_then_expr_ty.is_never()
{
// Only look at sources if the then branch diverges and we have an else branch.
- let source_map = db.body_with_source_map(self.owner).1;
+ let source_map = self.db().body_with_source_map(self.owner).1;
let Ok(source_ptr) = source_map.expr_syntax(id) else {
return;
};
- let root = source_ptr.file_syntax(db);
+ let root = source_ptr.file_syntax(self.db());
let either::Left(ast::Expr::IfExpr(if_expr)) = source_ptr.value.to_node(&root)
else {
return;
@@ -480,15 +489,15 @@ impl ExprValidator {
}
}
-struct FilterMapNextChecker {
+struct FilterMapNextChecker<'db> {
filter_map_function_id: Option<hir_def::FunctionId>,
next_function_id: Option<hir_def::FunctionId>,
prev_filter_map_expr_id: Option<ExprId>,
- prev_receiver_ty: Option<chalk_ir::Ty<Interner>>,
+ prev_receiver_ty: Option<Ty<'db>>,
}
-impl FilterMapNextChecker {
- fn new(resolver: &hir_def::resolver::Resolver<'_>, db: &dyn HirDatabase) -> Self {
+impl<'db> FilterMapNextChecker<'db> {
+ fn new(resolver: &hir_def::resolver::Resolver<'db>, db: &'db dyn HirDatabase) -> Self {
// Find and store the FunctionIds for Iterator::filter_map and Iterator::next
let (next_function_id, filter_map_function_id) = match LangItem::IteratorNext
.resolve_function(db, resolver.krate())
@@ -528,15 +537,15 @@ impl FilterMapNextChecker {
return None;
}
- if *function_id == self.next_function_id? {
- if let Some(prev_filter_map_expr_id) = self.prev_filter_map_expr_id {
- let is_dyn_trait = self
- .prev_receiver_ty
- .as_ref()
- .is_some_and(|it| it.strip_references().dyn_trait().is_some());
- if *receiver_expr_id == prev_filter_map_expr_id && !is_dyn_trait {
- return Some(());
- }
+ if *function_id == self.next_function_id?
+ && let Some(prev_filter_map_expr_id) = self.prev_filter_map_expr_id
+ {
+ let is_dyn_trait = self
+ .prev_receiver_ty
+ .as_ref()
+ .is_some_and(|it| it.strip_references().dyn_trait().is_some());
+ if *receiver_expr_id == prev_filter_map_expr_id && !is_dyn_trait {
+ return Some(());
}
}
@@ -547,7 +556,7 @@ impl FilterMapNextChecker {
pub fn record_literal_missing_fields(
db: &dyn HirDatabase,
- infer: &InferenceResult,
+ infer: &InferenceResult<'_>,
id: ExprId,
expr: &Expr,
) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> {
@@ -577,7 +586,7 @@ pub fn record_literal_missing_fields(
pub fn record_pattern_missing_fields(
db: &dyn HirDatabase,
- infer: &InferenceResult,
+ infer: &InferenceResult<'_>,
id: PatId,
pat: &Pat,
) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> {
@@ -605,8 +614,8 @@ pub fn record_pattern_missing_fields(
Some((variant_def, missed_fields, exhaustive))
}
-fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResult) -> bool {
- fn walk(pat: PatId, body: &Body, infer: &InferenceResult, has_type_mismatches: &mut bool) {
+fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResult<'_>) -> bool {
+ 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 => (),
@@ -628,15 +637,19 @@ fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResul
!has_type_mismatches
}
-fn missing_match_arms<'p>(
- cx: &MatchCheckCtx<'p>,
- scrut_ty: &Ty,
- witnesses: Vec<WitnessPat<'p>>,
+fn missing_match_arms<'a, 'db>(
+ cx: &MatchCheckCtx<'a, 'db>,
+ scrut_ty: Ty<'a>,
+ witnesses: Vec<WitnessPat<'a, 'db>>,
arms_is_empty: bool,
krate: Crate,
) -> String {
- struct DisplayWitness<'a, 'p>(&'a WitnessPat<'p>, &'a MatchCheckCtx<'p>, DisplayTarget);
- impl fmt::Display for DisplayWitness<'_, '_> {
+ struct DisplayWitness<'a, 'b, 'db>(
+ &'a WitnessPat<'b, 'db>,
+ &'a MatchCheckCtx<'b, 'db>,
+ DisplayTarget,
+ );
+ impl fmt::Display for DisplayWitness<'_, '_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let DisplayWitness(witness, cx, display_target) = *self;
let pat = cx.hoist_witness_pat(witness);