Skip to content

Commit

Permalink
Add missing check for duplicated binding in the same pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
Y-Nak committed Aug 21, 2024
1 parent bd7343c commit d34f878
Show file tree
Hide file tree
Showing 25 changed files with 240 additions and 98 deletions.
89 changes: 59 additions & 30 deletions crates/hir-analysis/src/ty/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,11 @@ pub enum BodyDiag<'db> {
TypeMismatch(DynLazySpan<'db>, String, String),
InfiniteOccurrence(DynLazySpan<'db>),

DuplicatedBinding {
primary: DynLazySpan<'db>,
conflicat_with: DynLazySpan<'db>,
name: IdentId<'db>,
},
DuplicatedRestPat(DynLazySpan<'db>),

InvalidPathDomainInPat {
Expand Down Expand Up @@ -878,43 +883,47 @@ impl<'db> BodyDiag<'db> {
match self {
Self::TypeMismatch(..) => 0,
Self::InfiniteOccurrence(..) => 1,
Self::DuplicatedRestPat(..) => 2,
Self::InvalidPathDomainInPat { .. } => 3,
Self::UnitVariantExpected { .. } => 4,
Self::TupleVariantExpected { .. } => 5,
Self::RecordExpected { .. } => 6,
Self::MismatchedFieldCount { .. } => 7,
Self::DuplicatedRecordFieldBind { .. } => 8,
Self::RecordFieldNotFound { .. } => 9,
Self::ExplicitLabelExpectedInRecord { .. } => 10,
Self::MissingRecordFields { .. } => 11,
Self::UndefinedVariable(..) => 12,
Self::ReturnedTypeMismatch { .. } => 13,
Self::TypeMustBeKnown(..) => 14,
Self::AccessedFieldNotFound { .. } => 15,
Self::OpsTraitNotImplemented { .. } => 16,
Self::NonAssignableExpr(..) => 17,
Self::ImmutableAssignment { .. } => 18,
Self::LoopControlOutsideOfLoop { .. } => 19,
Self::TraitNotImplemented { .. } => 20,
Self::NotCallable(..) => 21,
Self::CallGenericArgNumMismatch { .. } => 22,
Self::CallArgNumMismatch { .. } => 23,
Self::CallArgLabelMismatch { .. } => 24,
Self::AmbiguousInherentMethodCall { .. } => 25,
Self::AmbiguousTrait { .. } => 26,
Self::AmbiguousTraitInst { .. } => 27,
Self::InvisibleAmbiguousTrait { .. } => 28,
Self::MethodNotFound { .. } => 29,
Self::NotValue { .. } => 30,
Self::TypeAnnotationNeeded { .. } => 31,
Self::DuplicatedBinding { .. } => 2,
Self::DuplicatedRestPat(..) => 3,
Self::InvalidPathDomainInPat { .. } => 4,
Self::UnitVariantExpected { .. } => 5,
Self::TupleVariantExpected { .. } => 6,
Self::RecordExpected { .. } => 7,
Self::MismatchedFieldCount { .. } => 8,
Self::DuplicatedRecordFieldBind { .. } => 9,
Self::RecordFieldNotFound { .. } => 10,
Self::ExplicitLabelExpectedInRecord { .. } => 11,
Self::MissingRecordFields { .. } => 12,
Self::UndefinedVariable(..) => 13,
Self::ReturnedTypeMismatch { .. } => 14,
Self::TypeMustBeKnown(..) => 15,
Self::AccessedFieldNotFound { .. } => 16,
Self::OpsTraitNotImplemented { .. } => 17,
Self::NonAssignableExpr(..) => 18,
Self::ImmutableAssignment { .. } => 19,
Self::LoopControlOutsideOfLoop { .. } => 20,
Self::TraitNotImplemented { .. } => 21,
Self::NotCallable(..) => 22,
Self::CallGenericArgNumMismatch { .. } => 23,
Self::CallArgNumMismatch { .. } => 24,
Self::CallArgLabelMismatch { .. } => 25,
Self::AmbiguousInherentMethodCall { .. } => 26,
Self::AmbiguousTrait { .. } => 27,
Self::AmbiguousTraitInst { .. } => 28,
Self::InvisibleAmbiguousTrait { .. } => 29,
Self::MethodNotFound { .. } => 30,
Self::NotValue { .. } => 31,
Self::TypeAnnotationNeeded { .. } => 32,
}
}

fn message(&self, db: &dyn HirDb) -> String {
match self {
Self::TypeMismatch(_, _, _) => "type mismatch".to_string(),
Self::InfiniteOccurrence(_) => "infinite sized type found".to_string(),
Self::DuplicatedBinding { name, .. } => {
format!("duplicated bidning `{}` in the same pattern", name.data(db))
}
Self::DuplicatedRestPat(_) => "duplicated `..` found".to_string(),
Self::InvalidPathDomainInPat { .. } => "invalid item is given here".to_string(),
Self::UnitVariantExpected { .. } => "expected unit variant".to_string(),
Expand Down Expand Up @@ -990,6 +999,26 @@ impl<'db> BodyDiag<'db> {
span.resolve(db),
)],

Self::DuplicatedBinding {
primary,
conflicat_with,
name,
} => {
let name = name.data(db.as_hir_db());
vec![
SubDiagnostic::new(
LabelStyle::Primary,
format!("`{name}` is already defined in the same pattern",),
primary.resolve(db),
),
SubDiagnostic::new(
LabelStyle::Secondary,
format!("`{name}` is defined here"),
conflicat_with.resolve(db),
),
]
}

Self::DuplicatedRestPat(span) => vec![SubDiagnostic::new(
LabelStyle::Primary,
"`..` can be used only once".to_string(),
Expand Down
66 changes: 61 additions & 5 deletions crates/hir-analysis/src/ty/ty_check/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,17 +177,38 @@ impl<'db> TyCheckEnv<'db> {
self.pat_ty.insert(pat, ty);
}

/// Register a pending binding which will be added when `flush_pending_vars`
/// is called.
/// Registers a new pending binding.
///
/// This function adds a binding to the list of pending variables. If a
/// binding with the same name already exists, it returns the existing
/// binding. Otherwise, it returns `None`.
///
/// To flush pending bindings to the designated scope, call
/// [`flush_pending_bindings`] in the scope.
///
/// # Arguments
///
/// * `name` - The identifier of the variable.
/// * `binding` - The local binding to be registered.
///
/// # Returns
///
/// * `Some(LocalBinding)` if a binding with the same name already exists.
/// * `None` if the binding was successfully registered.
pub(super) fn register_pending_binding(
&mut self,
name: IdentId<'db>,
binding: LocalBinding<'db>,
) {
self.pending_vars.insert(name, binding);
) -> Option<LocalBinding<'db>> {
self.pending_vars.insert(name, binding)
}

/// Flush pending bindings to the current scope environment.
/// Flushes all pending variable bindings into the current variable
/// environment.
///
/// This function moves all pending bindings from the `pending_vars` map
/// into the latest `BlockEnv` in `var_env`. After this operation, the
/// `pending_vars` map will be empty.
pub(super) fn flush_pending_bindings(&mut self) {
let var_env = self.var_env.last_mut().unwrap();
for (name, binding) in self.pending_vars.drain() {
Expand All @@ -199,6 +220,25 @@ impl<'db> TyCheckEnv<'db> {
self.pending_confirmations.push((inst, span))
}

/// Completes the type checking environment by finalizing pending trait
/// confirmations, folding types with the unification table, and collecting
/// diagnostics.
///
/// # Arguments
///
/// * `table` - A mutable reference to the unification table used for type
/// unification.
///
/// # Returns
///
/// * A tuple containing the `TypedBody` and a vector of `FuncBodyDiag`.
///
/// The `TypedBody` includes the body of the function, pattern types,
/// expression types, and callables, all of which have been folded with
/// the unification table.
///
/// The vector of `FuncBodyDiag` contains diagnostics related to function
/// bodies, such as ambiguous trait instances.
pub(super) fn finish(
mut self,
table: &mut UnificationTable<'db>,
Expand Down Expand Up @@ -249,6 +289,22 @@ impl<'db> TyCheckEnv<'db> {
&self.var_env[idx]
}

/// Performs pending trait confirmations and collects diagnostics.
///
/// This function attempts to satisfy all pending trait confirmations by
/// iteratively probing and unifying trait instances until a fixed point
/// is reached. If any trait instance remains ambiguous, a diagnostic is
/// generated and added to the diagnostics vector.
///
/// # Arguments
///
/// * `prober` - A mutable reference to the [`Prober`] used for type
/// unification and probing.
///
/// # Returns
///
/// * A vector of `FuncBodyDiag` containing diagnostics related to ambiguous
/// trait instances.
fn perform_pending_confirmation(
&self,
prober: &mut Prober<'db, '_>,
Expand Down
14 changes: 12 additions & 2 deletions crates/hir-analysis/src/ty/ty_check/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,19 @@ impl<'db> TyChecker<'db> {
}
}

ResolvedPathInBody::Binding(ident, _) | ResolvedPathInBody::NewBinding(ident) => {
ResolvedPathInBody::Binding(name, _) | ResolvedPathInBody::NewBinding(name) => {
let binding = LocalBinding::local(pat, *is_mut);
self.env.register_pending_binding(ident, binding);
if let Some(LocalBinding::Local {
pat: conflict_with, ..
}) = self.env.register_pending_binding(name, binding)
{
let diag = BodyDiag::DuplicatedBinding {
primary: span.into(),
conflicat_with: conflict_with.lazy_span(self.body()).into(),
name,
};
self.push_diag(diag);
}
self.fresh_ty()
}

Expand Down
4 changes: 1 addition & 3 deletions crates/uitest/fixtures/ty/trait_bound/coinductive.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ source: crates/uitest/tests/ty.rs
expression: diags
input_file: crates/uitest/fixtures/ty/trait_bound/coinductive.fe
---
error[8-0031]: type annotation is needed
error[8-0032]: type annotation is needed
┌─ coinductive.fe:35:9
35let v = Vec::new()
^
│ │
type annotation is needed
consider giving `: Vec<_>` here


4 changes: 2 additions & 2 deletions crates/uitest/fixtures/ty_check/array.snap
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ error[8-0000]: type mismatch
5let mut x: [i32; 5] = [1; 10]
^^^^^^^ expected `[i32; 5]`, but `[i32; 10]` is given

error[8-0031]: type annotation is needed
error[8-0032]: type annotation is needed
┌─ array.fe:3:14
3let z = [1, false, 1]
Expand All @@ -36,7 +36,7 @@ error[8-0031]: type annotation is needed
type annotation is needed
no default type is provided for an integer type. consider giving integer type

error[8-0031]: type annotation is needed
error[8-0032]: type annotation is needed
┌─ array.fe:6:14
6let x = [1; false]
Expand Down
10 changes: 5 additions & 5 deletions crates/uitest/fixtures/ty_check/assign.snap
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,37 @@ error[8-0000]: type mismatch
36t = 1
│ ^ expected `T`, but `{integer}` is given

error[8-0017]: not assignable left-hand side of assignment
error[8-0018]: not assignable left-hand side of assignment
┌─ assign.fe:31:5
311 = 1
│ ^ cant assign to this expression

error[8-0018]: left-hand side of assignment is immutable
error[8-0019]: left-hand side of assignment is immutable
┌─ assign.fe:21:9
20fn set_inner(self, x: i32, y: u32) {
│ ---- try changing to `mut self`
21 │ self.inner = Inner { x, y }
│ ^^^^^^^^^^ immutable assignment

error[8-0018]: left-hand side of assignment is immutable
error[8-0019]: left-hand side of assignment is immutable
┌─ assign.fe:26:5
25 │ fn foo<T>(x: i32, mut y: u32, opt: Option<T>, opt2: Option<i32>) {
- try changing to `mut x`
26x = 1
^ immutable assignment

error[8-0018]: left-hand side of assignment is immutable
error[8-0019]: left-hand side of assignment is immutable
┌─ assign.fe:43:13
42Some(x) => {
- try changing to `mut x`
43x = 1
^ immutable assignment

error[8-0031]: type annotation is needed
error[8-0032]: type annotation is needed
┌─ assign.fe:31:5
311 = 1
Expand Down
4 changes: 2 additions & 2 deletions crates/uitest/fixtures/ty_check/aug_assign.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ source: crates/uitest/tests/ty_check.rs
expression: diags
input_file: crates/uitest/fixtures/ty_check/aug_assign.fe
---
error[8-0016]: `std::ops::SubAssign` trait is not implemented
error[8-0017]: `std::ops::SubAssign` trait is not implemented
┌─ aug_assign.fe:6:5
6f -= f
Expand All @@ -12,7 +12,7 @@ error[8-0016]: `std::ops::SubAssign` trait is not implemented
`-=` cant be applied to `Foo`
Try implementing `std::ops::SubAssign` for `Foo`

error[8-0018]: left-hand side of assignment is immutable
error[8-0019]: left-hand side of assignment is immutable
┌─ aug_assign.fe:7:5
5fn foo(f: Foo) {
Expand Down
8 changes: 4 additions & 4 deletions crates/uitest/fixtures/ty_check/binary.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ source: crates/uitest/tests/ty_check.rs
expression: diags
input_file: crates/uitest/fixtures/ty_check/binary.fe
---
error[8-0016]: `std::ops::Add` trait is not implemented
error[8-0017]: `std::ops::Add` trait is not implemented
┌─ binary.fe:4:5
4f + f
Expand All @@ -12,7 +12,7 @@ error[8-0016]: `std::ops::Add` trait is not implemented
`+` cant be applied to `Foo`
Try implementing `std::ops::Add` for `Foo`

error[8-0016]: `std::ops::And` trait is not implemented
error[8-0017]: `std::ops::And` trait is not implemented
┌─ binary.fe:6:6
6 │ (f && f) || f
Expand All @@ -21,7 +21,7 @@ error[8-0016]: `std::ops::And` trait is not implemented
`&&` cant be applied to `Foo`
Try implementing `std::ops::And` for `Foo`

error[8-0016]: `std::ops::Eq` trait is not implemented
error[8-0017]: `std::ops::Eq` trait is not implemented
┌─ binary.fe:7:5
7f == f
Expand All @@ -30,7 +30,7 @@ error[8-0016]: `std::ops::Eq` trait is not implemented
`==` cant be applied to `Foo`
Try implementing `std::ops::Eq` for `Foo`

error[8-0016]: `std::ops::Ord` trait is not implemented
error[8-0017]: `std::ops::Ord` trait is not implemented
┌─ binary.fe:8:5
8f < f
Expand Down
Loading

0 comments on commit d34f878

Please sign in to comment.