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.rs137
1 files changed, 96 insertions, 41 deletions
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index 93772ca452..be29926a38 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -7,7 +7,9 @@ use std::fmt;
use base_db::Crate;
use either::Either;
use hir_def::{
- AdtId, AssocItemId, DefWithBodyId, HasModule, ItemContainerId, Lookup,
+ AdtId, AssocItemId, AttrDefId, CallableDefId, DefWithBodyId, HasModule, ItemContainerId,
+ Lookup,
+ attrs::AttrFlags,
lang_item::LangItems,
resolver::{HasResolver, ValueNs},
};
@@ -33,7 +35,7 @@ use crate::{
},
display::{DisplayTarget, HirDisplay},
next_solver::{
- DbInterner, ParamEnv, Ty, TyKind, TypingMode,
+ CallableIdWrapper, DbInterner, ParamEnv, Ty, TyKind, TypingMode,
infer::{DbInternerInferExt, InferCtxt},
},
};
@@ -67,6 +69,9 @@ pub enum BodyValidationDiagnostic {
RemoveUnnecessaryElse {
if_expr: ExprId,
},
+ UnusedMustUse {
+ expr: ExprId,
+ },
}
impl BodyValidationDiagnostic {
@@ -328,52 +333,71 @@ impl<'db> ExprValidator<'db> {
let pattern_arena = Arena::new();
let cx = MatchCheckCtx::new(self.owner.module(self.db()), &self.infcx, self.env);
for stmt in &**statements {
- let &Statement::Let { pat, initializer, else_branch: None, .. } = stmt else {
- continue;
+ let diag = match *stmt {
+ Statement::Expr { expr: stmt_expr, has_semi: true } if self.validate_lints => {
+ self.check_unused_must_use(stmt_expr)
+ }
+ Statement::Let { pat, initializer, else_branch: None, .. } => {
+ self.check_non_exhaustive_let(&cx, &pattern_arena, pat, initializer)
+ }
+ _ => None,
};
- if self.infer.type_mismatch_for_pat(pat).is_some() {
- continue;
- }
- let Some(initializer) = initializer else { continue };
- let Some(ty) = self.infer.type_of_expr_with_adjust(initializer) else { continue };
- if ty.references_non_lt_error() {
- continue;
+ if let Some(diag) = diag {
+ self.diagnostics.push(diag);
}
+ }
+ }
- let mut have_errors = false;
- let deconstructed_pat = self.lower_pattern(&cx, pat, &mut have_errors);
+ fn check_non_exhaustive_let<'a>(
+ &self,
+ cx: &MatchCheckCtx<'a, 'db>,
+ pattern_arena: &'a Arena<DeconstructedPat<'a, 'db>>,
+ pat: PatId,
+ initializer: Option<ExprId>,
+ ) -> Option<BodyValidationDiagnostic> {
+ if self.infer.type_mismatch_for_pat(pat).is_some() {
+ return None;
+ }
+ let initializer = initializer?;
+ let ty = self.infer.type_of_expr_with_adjust(initializer)?;
+ if ty.references_non_lt_error() {
+ return None;
+ }
- // optimization, wildcard trivially hold
- if have_errors || matches!(deconstructed_pat.ctor(), Constructor::Wildcard) {
- continue;
- }
+ let mut have_errors = false;
+ let deconstructed_pat = self.lower_pattern(cx, pat, &mut have_errors);
- let match_arm = rustc_pattern_analysis::MatchArm {
- pat: pattern_arena.alloc(deconstructed_pat),
- has_guard: false,
- arm_data: (),
- };
- let report = match cx.compute_match_usefulness(&[match_arm], ty, None) {
- Ok(v) => v,
- Err(e) => {
- debug!(?e, "match usefulness error");
- continue;
- }
- };
- let witnesses = report.non_exhaustiveness_witnesses;
- if !witnesses.is_empty() {
- self.diagnostics.push(BodyValidationDiagnostic::NonExhaustiveLet {
- pat,
- uncovered_patterns: missing_match_arms(
- &cx,
- ty,
- witnesses,
- false,
- self.owner.krate(self.db()),
- ),
- });
+ // optimization, wildcard trivially hold
+ if have_errors || matches!(deconstructed_pat.ctor(), Constructor::Wildcard) {
+ return None;
+ }
+
+ let match_arm = rustc_pattern_analysis::MatchArm {
+ pat: pattern_arena.alloc(deconstructed_pat),
+ has_guard: false,
+ arm_data: (),
+ };
+ let report = match cx.compute_match_usefulness(&[match_arm], ty, None) {
+ Ok(v) => v,
+ Err(e) => {
+ debug!(?e, "match usefulness error");
+ return None;
}
+ };
+ let witnesses = report.non_exhaustiveness_witnesses;
+ if witnesses.is_empty() {
+ return None;
}
+ Some(BodyValidationDiagnostic::NonExhaustiveLet {
+ pat,
+ uncovered_patterns: missing_match_arms(
+ cx,
+ ty,
+ witnesses,
+ false,
+ self.owner.krate(self.db()),
+ ),
+ })
}
fn lower_pattern<'a>(
@@ -391,6 +415,37 @@ impl<'db> ExprValidator<'db> {
pattern
}
+ fn check_unused_must_use(&self, expr: ExprId) -> Option<BodyValidationDiagnostic> {
+ let db = self.db();
+ let must_use_fn = match &self.body[expr] {
+ Expr::Call { callee, .. } => {
+ let callee_ty = self.infer.expr_ty(*callee);
+ if let TyKind::FnDef(CallableIdWrapper(CallableDefId::FunctionId(func)), _) =
+ callee_ty.kind()
+ {
+ AttrFlags::query(db, AttrDefId::FunctionId(func))
+ .contains(AttrFlags::IS_MUST_USE)
+ } else {
+ false
+ }
+ }
+ Expr::MethodCall { .. } => {
+ self.infer.method_resolution(expr).is_some_and(|(func, _)| {
+ AttrFlags::query(db, AttrDefId::FunctionId(func))
+ .contains(AttrFlags::IS_MUST_USE)
+ })
+ }
+ _ => return None,
+ };
+ let must_use_ty =
+ self.infer.type_of_expr_with_adjust(expr).is_some_and(|ty| match ty.kind() {
+ TyKind::Adt(adt, _) => AttrFlags::query(db, AttrDefId::AdtId(adt.def_id()))
+ .contains(AttrFlags::IS_MUST_USE),
+ _ => false,
+ });
+ (must_use_fn || must_use_ty).then_some(BodyValidationDiagnostic::UnusedMustUse { expr })
+ }
+
fn check_for_trailing_return(&mut self, body_expr: ExprId, body: &Body) {
if !self.validate_lints {
return;