Skip to content

Commit 46a659f

Browse files
Y-Naksbillig
authored andcommitted
Add missing check for duplicated binding in the same pattern
1 parent 4e24af3 commit 46a659f

File tree

6 files changed

+151
-13
lines changed

6 files changed

+151
-13
lines changed

Diff for: crates/hir-analysis/src/ty/diagnostics.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,11 @@ pub enum BodyDiag<'db> {
544544
TypeMismatch(DynLazySpan<'db>, String, String),
545545
InfiniteOccurrence(DynLazySpan<'db>),
546546

547+
DuplicatedBinding {
548+
primary: DynLazySpan<'db>,
549+
conflicat_with: DynLazySpan<'db>,
550+
name: IdentId<'db>,
551+
},
547552
DuplicatedRestPat(DynLazySpan<'db>),
548553

549554
InvalidPathDomainInPat {
@@ -927,14 +932,18 @@ impl<'db> BodyDiag<'db> {
927932
Self::MethodNotFound { .. } => 29,
928933
Self::NotValue { .. } => 30,
929934
Self::TypeAnnotationNeeded { .. } => 31,
935+
Self::DuplicatedBinding { .. } => 32,
930936
}
931937
}
932938

933939
fn message(&self, db: &dyn HirDb) -> String {
934940
match self {
935941
Self::TypeMismatch(_, _, _) => "type mismatch".to_string(),
936942
Self::InfiniteOccurrence(_) => "infinite sized type found".to_string(),
937-
Self::DuplicatedRestPat(_) => "duplicated `..` found".to_string(),
943+
Self::DuplicatedBinding { name, .. } => {
944+
format!("duplicate binding `{}` in pattern", name.data(db))
945+
}
946+
Self::DuplicatedRestPat(_) => "duplicate `..` found".to_string(),
938947
Self::InvalidPathDomainInPat { .. } => "invalid item is given here".to_string(),
939948
Self::UnitVariantExpected { .. } => "expected unit variant".to_string(),
940949
Self::TupleVariantExpected { .. } => "expected tuple variant".to_string(),
@@ -1009,6 +1018,26 @@ impl<'db> BodyDiag<'db> {
10091018
span.resolve(db),
10101019
)],
10111020

1021+
Self::DuplicatedBinding {
1022+
primary,
1023+
conflicat_with,
1024+
name,
1025+
} => {
1026+
let name = name.data(db.as_hir_db());
1027+
vec![
1028+
SubDiagnostic::new(
1029+
LabelStyle::Primary,
1030+
format!("`{name}` is defined again here",),
1031+
primary.resolve(db),
1032+
),
1033+
SubDiagnostic::new(
1034+
LabelStyle::Secondary,
1035+
format!("first definition of `{name}` in this pattern"),
1036+
conflicat_with.resolve(db),
1037+
),
1038+
]
1039+
}
1040+
10121041
Self::DuplicatedRestPat(span) => vec![SubDiagnostic::new(
10131042
LabelStyle::Primary,
10141043
"`..` can be used only once".to_string(),

Diff for: crates/hir-analysis/src/ty/ty_check/env.rs

+61-5
Original file line numberDiff line numberDiff line change
@@ -177,17 +177,38 @@ impl<'db> TyCheckEnv<'db> {
177177
self.pat_ty.insert(pat, ty);
178178
}
179179

180-
/// Register a pending binding which will be added when `flush_pending_vars`
181-
/// is called.
180+
/// Registers a new pending binding.
181+
///
182+
/// This function adds a binding to the list of pending variables. If a
183+
/// binding with the same name already exists, it returns the existing
184+
/// binding. Otherwise, it returns `None`.
185+
///
186+
/// To flush pending bindings to the designated scope, call
187+
/// [`flush_pending_bindings`] in the scope.
188+
///
189+
/// # Arguments
190+
///
191+
/// * `name` - The identifier of the variable.
192+
/// * `binding` - The local binding to be registered.
193+
///
194+
/// # Returns
195+
///
196+
/// * `Some(LocalBinding)` if a binding with the same name already exists.
197+
/// * `None` if the binding was successfully registered.
182198
pub(super) fn register_pending_binding(
183199
&mut self,
184200
name: IdentId<'db>,
185201
binding: LocalBinding<'db>,
186-
) {
187-
self.pending_vars.insert(name, binding);
202+
) -> Option<LocalBinding<'db>> {
203+
self.pending_vars.insert(name, binding)
188204
}
189205

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

223+
/// Completes the type checking environment by finalizing pending trait
224+
/// confirmations, folding types with the unification table, and collecting
225+
/// diagnostics.
226+
///
227+
/// # Arguments
228+
///
229+
/// * `table` - A mutable reference to the unification table used for type
230+
/// unification.
231+
///
232+
/// # Returns
233+
///
234+
/// * A tuple containing the `TypedBody` and a vector of `FuncBodyDiag`.
235+
///
236+
/// The `TypedBody` includes the body of the function, pattern types,
237+
/// expression types, and callables, all of which have been folded with
238+
/// the unification table.
239+
///
240+
/// The vector of `FuncBodyDiag` contains diagnostics related to function
241+
/// bodies, such as ambiguous trait instances.
202242
pub(super) fn finish(
203243
mut self,
204244
table: &mut UnificationTable<'db>,
@@ -249,6 +289,22 @@ impl<'db> TyCheckEnv<'db> {
249289
&self.var_env[idx]
250290
}
251291

292+
/// Performs pending trait confirmations and collects diagnostics.
293+
///
294+
/// This function attempts to satisfy all pending trait confirmations by
295+
/// iteratively probing and unifying trait instances until a fixed point
296+
/// is reached. If any trait instance remains ambiguous, a diagnostic is
297+
/// generated and added to the diagnostics vector.
298+
///
299+
/// # Arguments
300+
///
301+
/// * `prober` - A mutable reference to the [`Prober`] used for type
302+
/// unification and probing.
303+
///
304+
/// # Returns
305+
///
306+
/// * A vector of `FuncBodyDiag` containing diagnostics related to ambiguous
307+
/// trait instances.
252308
fn perform_pending_confirmation(
253309
&self,
254310
prober: &mut Prober<'db, '_>,

Diff for: crates/hir-analysis/src/ty/ty_check/pat.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,19 @@ impl<'db> TyChecker<'db> {
140140
}
141141
}
142142
_ => {
143+
let name = *path.ident(self.db.as_hir_db()).unwrap();
143144
let binding = LocalBinding::local(pat, *is_mut);
144-
self.env.register_pending_binding(
145-
*path.ident(self.db.as_hir_db()).unwrap(),
146-
binding,
147-
);
145+
if let Some(LocalBinding::Local {
146+
pat: conflict_with, ..
147+
}) = self.env.register_pending_binding(name, binding)
148+
{
149+
let diag = BodyDiag::DuplicatedBinding {
150+
primary: span.into(),
151+
conflicat_with: conflict_with.lazy_span(self.body()).into(),
152+
name,
153+
};
154+
self.push_diag(diag);
155+
}
148156
self.fresh_ty()
149157
}
150158
}
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
pub enum E {
2+
V(i32, u32)
3+
}
4+
5+
fn foo(f: E) {
6+
let E::V(x, x) = f
7+
8+
match f {
9+
E::V(x, x) => ()
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
source: crates/uitest/tests/ty_check.rs
3+
expression: diags
4+
input_file: fixtures/ty_check/pat/duplicated_binding.fe
5+
---
6+
error[8-0032]: duplicate binding `x` in pattern
7+
┌─ duplicated_binding.fe:6:17
8+
9+
6let E::V(x, x) = f
10+
- ^ `x` is defined again here
11+
│ │
12+
first definition of `x` in this pattern
13+
14+
error[8-0032]: duplicate binding `x` in pattern
15+
┌─ duplicated_binding.fe:9:17
16+
17+
9E::V(x, x) => ()
18+
- ^ `x` is defined again here
19+
│ │
20+
first definition of `x` in this pattern

Diff for: crates/uitest/fixtures/ty_check/pat/record.snap

+17-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
---
22
source: crates/uitest/tests/ty_check.rs
33
expression: diags
4-
input_file: crates/uitest/fixtures/ty_check/pat/record.fe
4+
input_file: fixtures/ty_check/pat/record.fe
55
---
6-
error[8-0002]: duplicated `..` found
6+
error[8-0002]: duplicate `..` found
77
┌─ record.fe:13:24
88
99
13let Foo {x, .., y, ..}
1010
^^ `..` can be used only once
1111

12-
error[8-0002]: duplicated `..` found
12+
error[8-0002]: duplicate `..` found
1313
┌─ record.fe:19:33
1414
1515
19let Bar::Variant {x, .., y, ..}
@@ -79,4 +79,18 @@ error[8-0011]: all fields are not given
7979
missing `x, y`
8080
Consider using `Bar::Variant { x, y }` instead
8181

82+
error[8-0032]: duplicate binding `x` in pattern
83+
┌─ record.fe:12:17
84+
85+
12let Foo {x, x}
86+
- ^ `x` is defined again here
87+
│ │
88+
first definition of `x` in this pattern
8289

90+
error[8-0032]: duplicate binding `x` in pattern
91+
┌─ record.fe:18:26
92+
93+
18let Bar::Variant {x, x}
94+
- ^ `x` is defined again here
95+
│ │
96+
first definition of `x` in this pattern

0 commit comments

Comments
 (0)