Skip to content

Commit 1469529

Browse files
authored
Merge pull request #1047 from sbillig/pat-binding-dup
2 parents 0772d06 + 07940c0 commit 1469529

File tree

12 files changed

+193
-54
lines changed

12 files changed

+193
-54
lines changed

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

+37-8
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 `..` in pattern".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(),
@@ -943,7 +952,7 @@ impl<'db> BodyDiag<'db> {
943952
Self::DuplicatedRecordFieldBind { .. } => "duplicated record field binding".to_string(),
944953
Self::RecordFieldNotFound { .. } => "specified field not found".to_string(),
945954
Self::ExplicitLabelExpectedInRecord { .. } => "explicit label is required".to_string(),
946-
Self::MissingRecordFields { .. } => "all fields are not given".to_string(),
955+
Self::MissingRecordFields { .. } => "missing fields in record pattern".to_string(),
947956
Self::UndefinedVariable(..) => "undefined variable".to_string(),
948957
Self::ReturnedTypeMismatch { .. } => "returned type mismatch".to_string(),
949958
Self::TypeMustBeKnown(..) => "type must be known here".to_string(),
@@ -969,14 +978,14 @@ impl<'db> BodyDiag<'db> {
969978
format!("`{}` needs to be implemented for {ty}", trait_name.data(db))
970979
}
971980

972-
Self::NotCallable(..) => "not callable type is given in call expression".to_string(),
981+
Self::NotCallable(_, ty) => format!("expected function, found `{ty}`"),
973982

974983
Self::CallGenericArgNumMismatch { .. } => {
975984
"given generic argument number mismatch".to_string()
976985
}
977986

978-
Self::CallArgNumMismatch { .. } => "given argument number mismatch".to_string(),
979-
Self::CallArgLabelMismatch { .. } => "given argument label mismatch".to_string(),
987+
Self::CallArgNumMismatch { .. } => "argument number mismatch".to_string(),
988+
Self::CallArgLabelMismatch { .. } => "argument label mismatch".to_string(),
980989

981990
Self::AmbiguousInherentMethodCall { .. } => "ambiguous method call".to_string(),
982991

@@ -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(),
@@ -1135,7 +1164,7 @@ impl<'db> BodyDiag<'db> {
11351164
vec![
11361165
SubDiagnostic::new(
11371166
LabelStyle::Primary,
1138-
format!("duplicated field binding `{}`", name),
1167+
format!("duplicate field binding `{}`", name),
11391168
primary.resolve(db),
11401169
),
11411170
SubDiagnostic::new(
@@ -1285,7 +1314,7 @@ impl<'db> BodyDiag<'db> {
12851314
vec![
12861315
SubDiagnostic::new(
12871316
LabelStyle::Primary,
1288-
format!("`{}` cant be applied to `{}`", op, ty),
1317+
format!("`{}` can't be applied to `{}`", op, ty),
12891318
span.resolve(db),
12901319
),
12911320
SubDiagnostic::new(
@@ -1352,7 +1381,7 @@ impl<'db> BodyDiag<'db> {
13521381
Self::NotCallable(primary, ty) => {
13531382
vec![SubDiagnostic::new(
13541383
LabelStyle::Primary,
1355-
format!("`{ty}` is not callable"),
1384+
format!("call expression requires function; `{ty}` is not callable"),
13561385
primary.resolve(db),
13571386
)]
13581387
}

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
}

Diff for: crates/uitest/fixtures/ty_check/aug_assign.snap

+2-4
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/aug_assign.fe
4+
input_file: fixtures/ty_check/aug_assign.fe
55
---
66
error[8-0016]: `std::ops::SubAssign` trait is not implemented
77
┌─ aug_assign.fe:6:5
88
99
6f -= f
1010
^^^^^^
1111
│ │
12-
`-=` cant be applied to `Foo`
12+
`-=` can't be applied to `Foo`
1313
Try implementing `std::ops::SubAssign` for `Foo`
1414

1515
error[8-0018]: left-hand side of assignment is immutable
@@ -20,5 +20,3 @@ error[8-0018]: left-hand side of assignment is immutable
2020
6f -= f
2121
7f.x *= 1
2222
^^^ immutable assignment
23-
24-

Diff for: crates/uitest/fixtures/ty_check/binary.snap

+5-7
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/binary.fe
4+
input_file: fixtures/ty_check/binary.fe
55
---
66
error[8-0016]: `std::ops::Add` trait is not implemented
77
┌─ binary.fe:4:5
88
99
4f + f
1010
^^^^^
1111
│ │
12-
`+` cant be applied to `Foo`
12+
`+` can't be applied to `Foo`
1313
Try implementing `std::ops::Add` for `Foo`
1414

1515
error[8-0016]: `std::ops::And` trait is not implemented
@@ -18,7 +18,7 @@ error[8-0016]: `std::ops::And` trait is not implemented
1818
6 │ (f && f) || f
1919
^^^^^^
2020
│ │
21-
`&&` cant be applied to `Foo`
21+
`&&` can't be applied to `Foo`
2222
Try implementing `std::ops::And` for `Foo`
2323

2424
error[8-0016]: `std::ops::Eq` trait is not implemented
@@ -27,7 +27,7 @@ error[8-0016]: `std::ops::Eq` trait is not implemented
2727
7f == f
2828
^^^^^^
2929
│ │
30-
`==` cant be applied to `Foo`
30+
`==` can't be applied to `Foo`
3131
Try implementing `std::ops::Eq` for `Foo`
3232

3333
error[8-0016]: `std::ops::Ord` trait is not implemented
@@ -36,7 +36,5 @@ error[8-0016]: `std::ops::Ord` trait is not implemented
3636
8f < f
3737
^^^^^
3838
│ │
39-
`<` cant be applied to `Foo`
39+
`<` can't be applied to `Foo`
4040
Try implementing `std::ops::Ord` for `Foo`
41-
42-

Diff for: crates/uitest/fixtures/ty_check/call.fe

+6-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,9 @@ pub fn make_tuple<T, U>(_ t: T, _ u: U) -> (T, U) {
1515

1616
pub fn use_make_tuple() -> (i32, u32) {
1717
make_tuple<i32, u32>(false, 1)
18-
}
18+
}
19+
20+
fn f() {
21+
let x: u32 = 1
22+
x(100)
23+
}

Diff for: crates/uitest/fixtures/ty_check/call.snap

+12-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
source: crates/uitest/tests/ty_check.rs
33
expression: diags
4-
input_file: crates/uitest/fixtures/ty_check/call.fe
4+
input_file: fixtures/ty_check/call.fe
55
---
66
error[8-0000]: type mismatch
77
┌─ call.fe:9:12
@@ -15,7 +15,13 @@ error[8-0000]: type mismatch
1515
17make_tuple<i32, u32>(false, 1)
1616
│ ^^^^^ expected `i32`, but `bool` is given
1717

18-
error[8-0024]: given argument label mismatch
18+
error[8-0021]: expected function, found `u32`
19+
┌─ call.fe:22:5
20+
21+
22 │ x(100)
22+
│ ^ call expression requires function; `u32` is not callable
23+
24+
error[8-0024]: argument label mismatch
1925
┌─ call.fe:6:9
2026
2127
1pub fn add(x: i32, y: i32) -> i32 {
@@ -24,7 +30,7 @@ error[8-0024]: given argument label mismatch
2430
6 │ add(1, 2)
2531
│ ^ expected `x` label
2632

27-
error[8-0024]: given argument label mismatch
33+
error[8-0024]: argument label mismatch
2834
┌─ call.fe:6:12
2935
3036
1 │ pub fn add(x: i32, y: i32) -> i32 {
@@ -33,7 +39,7 @@ error[8-0024]: given argument label mismatch
3339
6 │ add(1, 2)
3440
│ ^ expected `y` label
3541

36-
error[8-0024]: given argument label mismatch
42+
error[8-0024]: argument label mismatch
3743
┌─ call.fe:7:9
3844
3945
1 │ pub fn add(x: i32, y: i32) -> i32 {
@@ -42,7 +48,7 @@ error[8-0024]: given argument label mismatch
4248
7 │ add(y: 1, x: 2)
4349
│ ^ expected `x` label, but `y` given
4450

45-
error[8-0024]: given argument label mismatch
51+
error[8-0024]: argument label mismatch
4652
┌─ call.fe:7:15
4753
4854
1 │ pub fn add(x: i32, y: i32) -> i32 {
@@ -51,13 +57,11 @@ error[8-0024]: given argument label mismatch
5157
7 │ add(y: 1, x: 2)
5258
│ ^ expected `y` label, but `x` given
5359

54-
error[8-0024]: given argument label mismatch
60+
error[8-0024]: argument label mismatch
5561
┌─ call.fe:8:15
5662
5763
1 │ pub fn add(x: i32, y: i32) -> i32 {
5864
--- function defined here
5965
·
6066
8 │ add(x: 1, z: 2)
6167
│ ^ expected `y` label, but `z` given
62-
63-

Diff for: crates/uitest/fixtures/ty_check/index.snap

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
source: crates/uitest/tests/ty_check.rs
33
expression: diags
4-
input_file: crates/uitest/fixtures/ty_check/index.fe
4+
input_file: fixtures/ty_check/index.fe
55
---
66
error[8-0000]: type mismatch
77
┌─ index.fe:4:7
@@ -15,7 +15,5 @@ error[8-0016]: `std::ops::Index` trait is not implemented
1515
6f[1]
1616
│ ^^^^
1717
│ │
18-
│ `[]` cant be applied to `Foo`
18+
│ `[]` can't be applied to `Foo`
1919
Try implementing `std::ops::Index` for `Foo`
20-
21-

0 commit comments

Comments
 (0)