Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22144 from ChayimFriedman2/type-must-be-known
feat: Feature a "type annotations needed" diagnostic
Chayim Refael Friedman 4 weeks ago
parent db7ea41 · parent 4facc67 · commit 33d333a
-rw-r--r--crates/hir-ty/src/infer.rs54
-rw-r--r--crates/hir-ty/src/infer/callee.rs22
-rw-r--r--crates/hir-ty/src/infer/closure/analysis.rs8
-rw-r--r--crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs26
-rw-r--r--crates/hir-ty/src/infer/expr.rs72
-rw-r--r--crates/hir-ty/src/infer/pat.rs26
-rw-r--r--crates/hir-ty/src/infer/place_op.rs2
-rw-r--r--crates/hir-ty/src/infer/unify.rs10
-rw-r--r--crates/hir-ty/src/tests/never_type.rs4
-rw-r--r--crates/hir/src/diagnostics.rs11
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_unsafe.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/type_must_be_known.rs39
-rw-r--r--crates/ide-diagnostics/src/lib.rs2
13 files changed, 186 insertions, 92 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 82c5834e64..8b0702c483 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -401,6 +401,9 @@ pub enum InferenceDiagnostic {
InvalidLhsOfAssignment {
lhs: ExprId,
},
+ TypeMustBeKnown {
+ at_point: ExprOrPatId,
+ },
}
/// A mismatch between an expected and an inferred type.
@@ -1178,6 +1181,7 @@ pub(crate) struct InferenceContext<'body, 'db> {
deferred_call_resolutions: FxHashMap<ExprId, Vec<DeferredCallResolution<'db>>>,
diagnostics: Diagnostics,
+ vars_emitted_type_must_be_known_for: FxHashSet<Ty<'db>>,
}
#[derive(Clone, Debug)]
@@ -1267,6 +1271,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
deferred_cast_checks: Vec::new(),
inside_assignment: false,
diagnostics: Diagnostics::default(),
+ vars_emitted_type_must_be_known_for: FxHashSet::default(),
deferred_call_resolutions: FxHashMap::default(),
}
}
@@ -1581,6 +1586,33 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
&self.table.infer_ctxt
}
+ /// If `ty` is an error, returns an infer var instead. Otherwise, returns it.
+ ///
+ /// "Refreshing" types like this is useful for getting better types, but it is also
+ /// very dangerous: we might create duplicate diagnostics, for example if we try
+ /// to resolve it and fail. rustc doesn't do that for this reason (and is in general
+ /// more strict with how it uses error types; an error type in inputs will almost
+ /// always cause it to infer an error type in output, while we infer some type as much
+ /// as we can).
+ ///
+ /// Unfortunately, we cannot allow ourselves to do that. Not only we more often work
+ /// with incomplete code, we also have assists, for example "Generate constant", that
+ /// will assume the inferred type is the expected type even if the expression itself
+ /// cannot be inferred. Therefore, we choose a middle ground: refresh the type,
+ /// but if we return a new var, mark it so that no diagnostics will be issued on it.
+ fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> {
+ if ty.is_ty_error() {
+ let var = self.table.next_ty_var();
+
+ // Suppress future errors on this var. Add more things here when we add more diagnostics.
+ self.vars_emitted_type_must_be_known_for.insert(var);
+
+ var
+ } else {
+ ty
+ }
+ }
+
fn infer_body(&mut self, body_expr: ExprId) {
match self.return_coercion {
Some(_) => self.infer_return(body_expr),
@@ -1781,11 +1813,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.insert_type_vars(lt)
}
- /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it.
- fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> {
- self.table.insert_type_vars_shallow(ty)
- }
-
fn insert_type_vars<T>(&mut self, ty: T) -> T
where
T: TypeFoldable<DbInterner<'db>>,
@@ -1873,6 +1900,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.table.resolve_vars_if_possible(t)
}
+ pub(crate) fn structurally_resolve_type(&mut self, node: ExprOrPatId, ty: Ty<'db>) -> Ty<'db> {
+ let result = self.table.try_structurally_resolve_type(ty);
+ if result.is_ty_var() { self.type_must_be_known_at_this_point(node, ty) } else { result }
+ }
+
fn demand_eqtype(
&mut self,
id: ExprOrPatId,
@@ -1938,11 +1970,13 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
}
pub(crate) fn type_must_be_known_at_this_point(
- &self,
- _id: ExprOrPatId,
- _ty: Ty<'db>,
+ &mut self,
+ node: ExprOrPatId,
+ ty: Ty<'db>,
) -> Ty<'db> {
- // FIXME: Emit an diagnostic.
+ if self.vars_emitted_type_must_be_known_for.insert(ty) {
+ self.push_diagnostic(InferenceDiagnostic::TypeMustBeKnown { at_point: node });
+ }
self.types.types.error
}
@@ -2477,7 +2511,7 @@ impl<'db> Expectation<'db> {
fn adjust_for_branches(&self, table: &mut unify::InferenceTable<'db>) -> Expectation<'db> {
match *self {
Expectation::HasType(ety) => {
- let ety = table.structurally_resolve_type(ety);
+ let ety = table.try_structurally_resolve_type(ety);
if ety.is_ty_var() { Expectation::None } else { Expectation::HasType(ety) }
}
Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety),
diff --git a/crates/hir-ty/src/infer/callee.rs b/crates/hir-ty/src/infer/callee.rs
index 3d478912a3..a470c92124 100644
--- a/crates/hir-ty/src/infer/callee.rs
+++ b/crates/hir-ty/src/infer/callee.rs
@@ -47,9 +47,15 @@ impl<'db> InferenceContext<'_, 'db> {
let mut autoderef = GeneralAutoderef::new_from_inference_context(self, expr_ty);
let mut result = None;
+ let mut error_reported = false;
while result.is_none() && autoderef.next().is_some() {
- result =
- Self::try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &mut autoderef);
+ result = Self::try_overloaded_call_step(
+ call_expr,
+ callee_expr,
+ arg_exprs,
+ &mut autoderef,
+ &mut error_reported,
+ );
}
// FIXME: rustc does some ABI checks here, but the ABI mapping is in rustc_target and we don't have access to that crate.
@@ -65,10 +71,12 @@ impl<'db> InferenceContext<'_, 'db> {
self.infer_expr_no_expect(arg, ExprIsRead::Yes);
}
- self.push_diagnostic(InferenceDiagnostic::ExpectedFunction {
- call_expr,
- found: original_callee_ty.store(),
- });
+ if !error_reported {
+ self.push_diagnostic(InferenceDiagnostic::ExpectedFunction {
+ call_expr,
+ found: original_callee_ty.store(),
+ });
+ }
self.types.types.error
}
@@ -97,6 +105,7 @@ impl<'db> InferenceContext<'_, 'db> {
callee_expr: ExprId,
arg_exprs: &[ExprId],
autoderef: &mut InferenceContextAutoderef<'_, '_, 'db>,
+ error_reported: &mut bool,
) -> Option<CallStep<'db>> {
let final_ty = autoderef.final_ty();
let adjusted_ty = autoderef.ctx().table.try_structurally_resolve_type(final_ty);
@@ -211,6 +220,7 @@ impl<'db> InferenceContext<'_, 'db> {
autoderef
.ctx()
.type_must_be_known_at_this_point(callee_expr.into(), adjusted_ty);
+ *error_reported = true;
return None;
}
diff --git a/crates/hir-ty/src/infer/closure/analysis.rs b/crates/hir-ty/src/infer/closure/analysis.rs
index b0f5533b3b..31b6252475 100644
--- a/crates/hir-ty/src/infer/closure/analysis.rs
+++ b/crates/hir-ty/src/infer/closure/analysis.rs
@@ -741,7 +741,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
};
let Some(min_cap_list) = root_var_min_capture_list.get_mut(&var_hir_id) else {
- let mutability = self.determine_capture_mutability(&place);
+ let mutability = self.determine_capture_mutability(closure_def_id, &place);
let min_cap_list = vec![CapturedPlace { place, info: capture_info, mutability }];
root_var_min_capture_list.insert(var_hir_id, min_cap_list);
continue;
@@ -850,7 +850,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// Only need to insert when we don't have an ancestor in the existing min capture list
if !ancestor_found {
- let mutability = self.determine_capture_mutability(&place);
+ let mutability = self.determine_capture_mutability(closure_def_id, &place);
let captured_place =
CapturedPlace { place, info: updated_capture_info, mutability };
min_cap_list.push(captured_place);
@@ -1016,7 +1016,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
/// A captured place is mutable if
/// 1. Projections don't include a Deref of an immut-borrow, **and**
/// 2. PlaceBase is mut or projections include a Deref of a mut-borrow.
- fn determine_capture_mutability(&mut self, place: &Place) -> Mutability {
+ fn determine_capture_mutability(&mut self, closure_expr: ExprId, place: &Place) -> Mutability {
let var_hir_id = match place.base {
PlaceBase::Upvar { var_id, .. } => var_id,
_ => unreachable!(),
@@ -1029,7 +1029,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
};
for pointer_ty in place.deref_tys() {
- match self.table.structurally_resolve_type(pointer_ty).kind() {
+ match self.structurally_resolve_type(closure_expr.into(), pointer_ty).kind() {
// We don't capture derefs of raw ptrs
TyKind::RawPtr(_, _) => unreachable!(),
diff --git a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs
index bddb01ff99..f140b12491 100644
--- a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs
+++ b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs
@@ -797,7 +797,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
// Select just those fields of the `with`
// expression that will actually be used
- match self.cx.table.structurally_resolve_type(with_place.place.ty()).kind() {
+ match self.cx.structurally_resolve_type(with_expr.into(), with_place.place.ty()).kind() {
TyKind::Adt(adt, args) if adt.is_struct() => {
let AdtId::StructId(adt) = adt.def_id().0 else { unreachable!() };
let adt_fields = VariantId::from(adt).fields(self.cx.db).fields();
@@ -982,7 +982,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
// FIXME: Does the MIR code skip this read when matching on a ZST?
// If so, we can also skip it here.
read_discriminant(this);
- } else if this.is_multivariant_adt(place.place.ty()) {
+ } else if this.is_multivariant_adt(pat.into(), place.place.ty()) {
// Otherwise, this is a struct/enum variant, and so it's
// only a read if we need to read the discriminant.
read_discriminant(this);
@@ -1001,7 +1001,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
read_discriminant(this);
}
Pat::Record { .. } | Pat::TupleStruct { .. } => {
- if this.is_multivariant_adt(place.place.ty()) {
+ if this.is_multivariant_adt(pat.into(), place.place.ty()) {
read_discriminant(this);
}
}
@@ -1225,7 +1225,11 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
// a bind-by-ref means that the base_ty will be the type of the ident itself,
// but what we want here is the type of the underlying value being borrowed.
// So peel off one-level, turning the &T into T.
- match self.cx.table.structurally_resolve_type(base_ty).builtin_deref(false) {
+ match self
+ .cx
+ .structurally_resolve_type(pat.into(), base_ty)
+ .builtin_deref(false)
+ {
Some(ty) => Ok(ty),
None => {
debug!("By-ref binding of non-derefable type: {base_ty:?}");
@@ -1430,7 +1434,8 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
let place_ty = self.expr_ty(expr)?;
let base_ty = self.expr_ty_adjusted(base)?;
- let TyKind::Ref(region, _, mutbl) = self.cx.table.structurally_resolve_type(base_ty).kind()
+ let TyKind::Ref(region, _, mutbl) =
+ self.cx.structurally_resolve_type(base.into(), base_ty).kind()
else {
return Err(ErrorGuaranteed);
};
@@ -1447,7 +1452,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
) -> Result<PlaceWithOrigin> {
let base_curr_ty = base_place.place.ty();
let Some(deref_ty) =
- self.cx.table.structurally_resolve_type(base_curr_ty).builtin_deref(true)
+ self.cx.structurally_resolve_type(node, base_curr_ty).builtin_deref(true)
else {
debug!("explicit deref of non-derefable type: {:?}", base_curr_ty);
return Err(ErrorGuaranteed);
@@ -1474,7 +1479,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
/// Here `pat_hir_id` is the ExprId of the pattern itself.
fn total_fields_in_tuple(&mut self, pat_id: PatId) -> usize {
let ty = self.cx.result.pat_ty(pat_id);
- match self.cx.table.structurally_resolve_type(ty).kind() {
+ match self.cx.structurally_resolve_type(pat_id.into(), ty).kind() {
TyKind::Tuple(args) => args.len(),
_ => panic!("tuple pattern not applied to a tuple"),
}
@@ -1629,8 +1634,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
Pat::Slice { prefix: ref before, slice, suffix: ref after } => {
let Some(element_ty) = self
.cx
- .table
- .structurally_resolve_type(place_with_id.place.ty())
+ .structurally_resolve_type(pat.into(), place_with_id.place.ty())
.builtin_index()
else {
debug!("explicit index of non-indexable type {:?}", place_with_id);
@@ -1689,8 +1693,8 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
/// FIXME(never_patterns): update this comment once the aforementioned MIR builder
/// code is changed to be insensitive to inhhabitedness.
#[instrument(skip(self), level = "debug")]
- fn is_multivariant_adt(&mut self, ty: Ty<'db>) -> bool {
- if let TyKind::Adt(def, _) = self.cx.table.structurally_resolve_type(ty).kind() {
+ fn is_multivariant_adt(&mut self, node: ExprOrPatId, ty: Ty<'db>) -> bool {
+ if let TyKind::Adt(def, _) = self.cx.structurally_resolve_type(node, ty).kind() {
// Note that if a non-exhaustive SingleVariant is defined in another crate, we need
// to assume that more cases will be added to the variant in the future. This mean
// that we should handle non-exhaustive SingleVariant the same way we would handle
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index 79010fbe1b..e179ca4bd3 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -397,9 +397,7 @@ impl<'db> InferenceContext<'_, 'db> {
.1
}
&Expr::Loop { body, label } => {
- // FIXME: should be:
- // let ty = expected.coercion_target_type(&mut self.table);
- let ty = self.table.next_ty_var();
+ let ty = expected.coercion_target_type(&mut self.table);
let (breaks, ()) =
self.with_breakable_ctx(BreakableKind::Loop, Some(ty), label, |this| {
this.infer_expr(
@@ -732,7 +730,7 @@ impl<'db> InferenceContext<'_, 'db> {
let base_t = self.infer_expr_no_expect(*base, ExprIsRead::Yes);
let idx_t = self.infer_expr_no_expect(*index, ExprIsRead::Yes);
- let base_t = self.table.structurally_resolve_type(base_t);
+ let base_t = self.structurally_resolve_type((*base).into(), base_t);
match self.lookup_indexing(tgt_expr, *base, base_t, idx_t) {
Some((trait_index_ty, trait_element_ty)) => {
// two-phase not needed because index_ty is never mutable
@@ -890,7 +888,7 @@ impl<'db> InferenceContext<'_, 'db> {
// allows them to be inferred based on how they are used later in the
// function.
if is_input {
- let ty = this.table.structurally_resolve_type(ty);
+ let ty = this.structurally_resolve_type(expr.into(), ty);
match ty.kind() {
TyKind::FnDef(def, parameters) => {
let fnptr_ty = Ty::new_fn_ptr(
@@ -955,7 +953,6 @@ impl<'db> InferenceContext<'_, 'db> {
}
}
};
- // use a new type variable if we got unknown here
let ty = self.insert_type_vars_shallow(ty);
self.write_expr_ty(tgt_expr, ty);
if self.shallow_resolve(ty).is_never()
@@ -1304,7 +1301,7 @@ impl<'db> InferenceContext<'_, 'db> {
};
let mut oprnd_t = self.infer_expr_inner(oprnd, expected_inner, ExprIsRead::Yes);
- oprnd_t = self.table.structurally_resolve_type(oprnd_t);
+ oprnd_t = self.structurally_resolve_type(oprnd.into(), oprnd_t);
match unop {
UnaryOp::Deref => {
if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) {
@@ -1681,7 +1678,7 @@ impl<'db> InferenceContext<'_, 'db> {
) -> Ty<'db> {
// Field projections don't constitute reads.
let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::No);
- let receiver_ty = self.table.structurally_resolve_type(receiver_ty);
+ let receiver_ty = self.structurally_resolve_type(receiver.into(), receiver_ty);
if name.is_missing() {
// Bail out early, don't even try to look up field. Also, we don't issue an unresolved
@@ -1979,38 +1976,39 @@ impl<'db> InferenceContext<'_, 'db> {
.unwrap_or_default();
// If the arguments should be wrapped in a tuple (ex: closures), unwrap them here
- let (formal_input_tys, expected_input_tys) =
- if tuple_arguments == TupleArgumentsFlag::TupleArguments {
- let tuple_type = self.table.structurally_resolve_type(formal_input_tys[0]);
- match tuple_type.kind() {
- // We expected a tuple and got a tuple
- TyKind::Tuple(arg_types) => {
- // Argument length differs
- if arg_types.len() != provided_args.len() {
- // FIXME: Emit an error.
- }
- let expected_input_tys = match expected_input_tys {
- Some(expected_input_tys) => match expected_input_tys.first() {
- Some(ty) => match ty.kind() {
- TyKind::Tuple(tys) => Some(tys.iter().collect()),
- _ => None,
- },
- None => None,
- },
- None => None,
- };
- (arg_types.iter().collect(), expected_input_tys)
- }
- _ => {
- // Otherwise, there's a mismatch, so clear out what we're expecting, and set
- // our input types to err_args so we don't blow up the error messages
+ let (formal_input_tys, expected_input_tys) = if tuple_arguments
+ == TupleArgumentsFlag::TupleArguments
+ {
+ let tuple_type = self.structurally_resolve_type(call_expr.into(), formal_input_tys[0]);
+ match tuple_type.kind() {
+ // We expected a tuple and got a tuple
+ TyKind::Tuple(arg_types) => {
+ // Argument length differs
+ if arg_types.len() != provided_args.len() {
// FIXME: Emit an error.
- (vec![self.types.types.error; provided_args.len()], None)
}
+ let expected_input_tys = match expected_input_tys {
+ Some(expected_input_tys) => match expected_input_tys.first() {
+ Some(ty) => match ty.kind() {
+ TyKind::Tuple(tys) => Some(tys.iter().collect()),
+ _ => None,
+ },
+ None => None,
+ },
+ None => None,
+ };
+ (arg_types.iter().collect(), expected_input_tys)
}
- } else {
- (formal_input_tys.to_vec(), expected_input_tys)
- };
+ _ => {
+ // Otherwise, there's a mismatch, so clear out what we're expecting, and set
+ // our input types to err_args so we don't blow up the error messages
+ // FIXME: Emit an error.
+ (vec![self.types.types.error; provided_args.len()], None)
+ }
+ }
+ } else {
+ (formal_input_tys.to_vec(), expected_input_tys)
+ };
// If there are no external expectations at the call site, just use the types from the function defn
let expected_input_tys = if let Some(expected_input_tys) = expected_input_tys {
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index bba69c758a..3cd7461cf3 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -311,6 +311,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
};
let adjust_mode = self.calc_adjust_mode(pat, opt_path_res);
let ty = self.infer_pat_inner(pat_id, opt_path_res, adjust_mode, expected, pat_info);
+ let ty = self.insert_type_vars_shallow(ty);
self.write_pat_ty(pat_id, ty);
// If we implicitly inserted overloaded dereferences before matching check the pattern to
@@ -457,7 +458,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
ty
}
Pat::Lit(expr) => self.infer_lit_pat(expr, expected),
- Pat::Range { start: lhs, end: rhs, .. } => self.infer_range_pat(lhs, rhs, expected),
+ Pat::Range { start: lhs, end: rhs, .. } => {
+ self.infer_range_pat(pat, lhs, rhs, expected)
+ }
Pat::Bind { id: var_id, subpat } => {
self.infer_bind_pat(pat, var_id, subpat, expected, pat_info)
}
@@ -755,7 +758,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// types `[u8]` or `[u8; N]`, in order to type, e.g., `deref!(b"..."): Vec<u8>`.
let mut pat_ty = ty;
if matches!(literal, Literal::ByteString(_)) {
- let expected = self.table.structurally_resolve_type(expected);
+ let expected = self.structurally_resolve_type(expr.into(), expected);
match expected.kind() {
// Allow `b"...": &[u8]`
TyKind::Ref(_, inner_ty, _)
@@ -806,8 +809,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
fn infer_range_pat(
&mut self,
- lhs: Option<ExprId>,
- rhs: Option<ExprId>,
+ pat: PatId,
+ lhs_expr: Option<ExprId>,
+ rhs_expr: Option<ExprId>,
expected: Ty<'db>,
) -> Ty<'db> {
let mut calc_side = |opt_expr: Option<ExprId>| match opt_expr {
@@ -826,8 +830,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
Some((fail, ty, expr))
}
};
- let mut lhs = calc_side(lhs);
- let mut rhs = calc_side(rhs);
+ let mut lhs = calc_side(lhs_expr);
+ let mut rhs = calc_side(rhs_expr);
if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) {
// There exists a side that didn't meet our criteria that the end-point
@@ -854,7 +858,10 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// This check is needed if both sides are inference variables.
// We require types to be resolved here so that we emit inference failure
// rather than "_ is not a char or numeric".
- let ty = self.table.structurally_resolve_type(expected);
+ let ty = self.structurally_resolve_type(
+ lhs_expr.or(rhs_expr).map(ExprOrPatId::ExprId).unwrap_or(pat.into()),
+ expected,
+ );
if !(ty.is_numeric() || ty.is_char() || ty.references_error()) {
// FIXME: Emit an error.
return self.types.types.error;
@@ -1096,7 +1103,8 @@ https://doc.rust-lang.org/reference/types.html#trait-objects";
let mut expected_len = elements.len();
if ddpos.is_some() {
// Require known type only when `..` is present.
- if let TyKind::Tuple(tys) = self.table.structurally_resolve_type(expected).kind() {
+ if let TyKind::Tuple(tys) = self.structurally_resolve_type(pat.into(), expected).kind()
+ {
expected_len = tys.len();
}
}
@@ -1574,7 +1582,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects";
let _ = self.demand_eqtype(pat.into(), expected, resolved_arr_ty);
}
- let expected = self.table.structurally_resolve_type(expected);
+ let expected = self.structurally_resolve_type(pat.into(), expected);
debug!(?expected);
let (element_ty, opt_slice_ty, inferred) = match expected.kind() {
diff --git a/crates/hir-ty/src/infer/place_op.rs b/crates/hir-ty/src/infer/place_op.rs
index 1298b38097..1671b67d34 100644
--- a/crates/hir-ty/src/infer/place_op.rs
+++ b/crates/hir-ty/src/infer/place_op.rs
@@ -108,7 +108,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
index_ty: Ty<'db>,
) -> Option<(/*index type*/ Ty<'db>, /*element type*/ Ty<'db>)> {
let ty = autoderef.final_ty();
- let adjusted_ty = autoderef.ctx().table.structurally_resolve_type(ty);
+ let adjusted_ty = autoderef.ctx().structurally_resolve_type(base_expr.into(), ty);
debug!(
"try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
index_ty={:?})",
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index c28fea0ff1..be4d370c24 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -343,11 +343,6 @@ impl<'db> InferenceTable<'db> {
}
}
- pub(crate) fn structurally_resolve_type(&mut self, ty: Ty<'db>) -> Ty<'db> {
- self.try_structurally_resolve_type(ty)
- // FIXME: Err if it still contain infer vars.
- }
-
pub(crate) fn snapshot(&mut self) -> CombinedSnapshot {
self.infer_ctxt.start_snapshot()
}
@@ -433,11 +428,6 @@ impl<'db> InferenceTable<'db> {
self.infer_ctxt.insert_type_vars(ty)
}
- /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it.
- pub(super) fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> {
- if ty.is_ty_error() { self.next_ty_var() } else { ty }
- }
-
/// Whenever you lower a user-written type, you should call this.
pub(crate) fn process_user_written_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
self.process_remote_user_written_ty(ty)
diff --git a/crates/hir-ty/src/tests/never_type.rs b/crates/hir-ty/src/tests/never_type.rs
index 993293bb56..91273cd177 100644
--- a/crates/hir-ty/src/tests/never_type.rs
+++ b/crates/hir-ty/src/tests/never_type.rs
@@ -354,10 +354,10 @@ fn diverging_expression_3_break() {
11..85 '{ ...} }; }': ()
54..55 'x': u32
63..82 '{ loop...k; } }': u32
- 65..80 'loop { break; }': ()
+ 65..80 'loop { break; }': u32
70..80 '{ break; }': ()
72..77 'break': !
- 65..80: expected u32, got ()
+ 72..77: expected u32, got ()
97..343 '{ ...; }; }': ()
140..141 'x': u32
149..175 '{ for ...; }; }': u32
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 1009a8b31b..8f9465cf1b 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -8,7 +8,7 @@ use either::Either;
use hir_def::{
DefWithBodyId, GenericParamId, SyntheticSyntax,
expr_store::{
- ExprOrPatPtr, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast,
+ ExprOrPatPtr, ExprOrPatSource, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast,
hir_generic_arg_to_ast, hir_segment_to_ast_segment,
},
hir::ExprOrPatId,
@@ -108,6 +108,7 @@ diagnostics![AnyDiagnostic<'db> ->
IncorrectGenericsOrder,
MissingLifetime,
ElidedLifetimesInPath,
+ TypeMustBeKnown,
];
#[derive(Debug)]
@@ -444,6 +445,11 @@ pub struct ElidedLifetimesInPath {
pub hard_error: bool,
}
+#[derive(Debug)]
+pub struct TypeMustBeKnown {
+ pub at_point: ExprOrPatSource,
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GenericArgKind {
Lifetime,
@@ -800,6 +806,9 @@ impl<'db> AnyDiagnostic<'db> {
let lhs = expr_syntax(lhs)?;
InvalidLhsOfAssignment { lhs }.into()
}
+ &InferenceDiagnostic::TypeMustBeKnown { at_point } => {
+ TypeMustBeKnown { at_point: expr_or_pat_syntax(at_point)? }.into()
+ }
})
}
diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 6a37702fc5..3351f5dc1c 100644
--- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -485,7 +485,7 @@ fn main() {
let b = &raw const x.a;
- let tmp = Vec::from([1, 2, 3]);
+ let tmp = [1, 2, 3];
let c = &raw const tmp[x.a];
// ^^^ 💡 error: access to union field is unsafe and requires an unsafe function or block
diff --git a/crates/ide-diagnostics/src/handlers/type_must_be_known.rs b/crates/ide-diagnostics/src/handlers/type_must_be_known.rs
new file mode 100644
index 0000000000..4b72497408
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/type_must_be_known.rs
@@ -0,0 +1,39 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: type-must-be-known
+//
+// This diagnostic is triggered when rust-analyzer cannot infer some type.
+pub(crate) fn type_must_be_known(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::TypeMustBeKnown,
+) -> Diagnostic {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0282"),
+ "type annotations needed; type must be known at this point",
+ d.at_point.map(|it| it.into()),
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn smoke_test() {
+ check_diagnostics(
+ r#"
+fn foo() {
+ let var = loop {};
+ // ^^^ 💡 warn: unused variable
+ var();
+ // ^^^ error: type annotations needed; type must be known at this point
+ let var = loop {};
+ // ^^^ 💡 warn: unused variable
+ var[0];
+ // ^^^ error: type annotations needed; type must be known at this point
+}
+ "#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 74f0653660..a083447335 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -66,6 +66,7 @@ mod handlers {
pub(crate) mod trait_impl_orphan;
pub(crate) mod trait_impl_redundant_assoc_item;
pub(crate) mod type_mismatch;
+ pub(crate) mod type_must_be_known;
pub(crate) mod typed_hole;
pub(crate) mod undeclared_label;
pub(crate) mod unimplemented_builtin_macro;
@@ -481,6 +482,7 @@ pub fn semantic_diagnostics(
AnyDiagnostic::ElidedLifetimesInPath(d) => handlers::elided_lifetimes_in_path::elided_lifetimes_in_path(&ctx, &d),
AnyDiagnostic::GenericDefaultRefersToSelf(d) => handlers::generic_default_refers_to_self::generic_default_refers_to_self(&ctx, &d),
AnyDiagnostic::InvalidLhsOfAssignment(d) => handlers::invalid_lhs_of_assignment::invalid_lhs_of_assignment(&ctx, &d),
+ AnyDiagnostic::TypeMustBeKnown(d) => handlers::type_must_be_known::type_must_be_known(&ctx, &d),
};
res.push(d)
}