+17
-5
compiler-core/src/analyse.rs
+17
-5
compiler-core/src/analyse.rs
···
550
550
has_javascript_external: external_javascript.is_some(),
551
551
};
552
552
553
-
let typed_args = arguments
554
-
.into_iter()
555
-
.zip(&prereg_args_types)
556
-
.map(|(a, t)| a.set_type(t.clone()))
557
-
.collect_vec();
553
+
let mut typed_args = Vec::with_capacity(arguments.len());
554
+
for (argument, type_) in arguments.into_iter().zip(&prereg_args_types) {
555
+
let argument = argument.set_type(type_.clone());
556
+
match &argument.names {
557
+
ast::ArgNames::Named { .. } | ast::ArgNames::NamedLabelled { .. } => (),
558
+
ast::ArgNames::Discard { name, location }
559
+
| ast::ArgNames::LabelledDiscard {
560
+
name,
561
+
name_location: location,
562
+
..
563
+
} => {
564
+
let _ = environment.ignored_names.insert(name.clone(), *location);
565
+
}
566
+
}
567
+
568
+
typed_args.push(argument);
569
+
}
558
570
559
571
// We have already registered the function in the `register_value_from_function`
560
572
// method, but here we must set this as the current function again, so that anything
+33
-8
compiler-core/src/error.rs
+33
-8
compiler-core/src/error.rs
···
12
12
use crate::type_::{FieldAccessUsage, error::PatternMatchKind};
13
13
use crate::{ast::BinOp, parse::error::ParseErrorType, type_::Type};
14
14
use crate::{bit_array, diagnostic::Level, javascript, type_::UnifyErrorSituation};
15
-
use ecow::EcoString;
15
+
use ecow::{EcoString, eco_format};
16
16
use itertools::Itertools;
17
17
use pubgrub::Package;
18
18
use pubgrub::{DerivationTree, VersionSet};
···
2388
2388
TypeError::UnknownVariable {
2389
2389
location,
2390
2390
variables,
2391
+
ignored_variables,
2391
2392
name,
2392
2393
type_with_name_in_scope,
2393
2394
} => {
···
2402
2403
wrap_format!("The name `{name}` is not in scope here.")
2403
2404
}
2404
2405
};
2405
-
Diagnostic {
2406
-
title: "Unknown variable".into(),
2407
-
text,
2408
-
hint: None,
2409
-
level: Level::Error,
2410
-
location: Some(Location {
2406
+
2407
+
let ignored_variable = ignored_variables.get(&eco_format!("_{name}"));
2408
+
let location = if let Some(ignored_location) = ignored_variable {
2409
+
Location {
2410
+
label: Label {
2411
+
text: None,
2412
+
span: *location,
2413
+
},
2414
+
path: path.clone(),
2415
+
src: src.clone(),
2416
+
extra_labels: vec![
2417
+
ExtraLabel {
2418
+
src_info: None,
2419
+
label: Label {
2420
+
text: Some("Did you mean to use this ignored variable?".into()),
2421
+
span: *ignored_location,
2422
+
}
2423
+
}
2424
+
],
2425
+
}
2426
+
} else {
2427
+
Location {
2411
2428
label: Label {
2412
2429
text: did_you_mean(name, variables),
2413
2430
span: *location,
···
2415
2432
path: path.clone(),
2416
2433
src: src.clone(),
2417
2434
extra_labels: vec![],
2418
-
}),
2435
+
}
2436
+
};
2437
+
2438
+
Diagnostic {
2439
+
title: "Unknown variable".into(),
2440
+
text,
2441
+
hint: None,
2442
+
level: Level::Error,
2443
+
location: Some(location),
2419
2444
}
2420
2445
}
2421
2446
+18
-2
compiler-core/src/type_/environment.rs
+18
-2
compiler-core/src/type_/environment.rs
···
56
56
/// Values defined in the current function (or the prelude)
57
57
pub scope: im::HashMap<EcoString, ValueConstructor>,
58
58
59
+
// The names of all the ignored variables and arguments in scope:
60
+
// `let _var = 10` `pub fn main(_var) { todo }`.
61
+
pub ignored_names: HashMap<EcoString, SrcSpan>,
62
+
59
63
/// Types defined in the current module (or the prelude)
60
64
pub module_types: HashMap<EcoString, TypeConstructor>,
61
65
···
133
137
unqualified_imported_types: HashMap::new(),
134
138
accessors: prelude.accessors.clone(),
135
139
scope: prelude.values.clone().into(),
140
+
ignored_names: HashMap::new(),
136
141
importable_modules,
137
142
current_module,
138
143
local_variable_usages: vec![HashMap::new()],
···
148
153
#[derive(Debug)]
149
154
pub struct ScopeResetData {
150
155
local_values: im::HashMap<EcoString, ValueConstructor>,
156
+
ignored_names: HashMap<EcoString, SrcSpan>,
151
157
}
152
158
153
159
impl Environment<'_> {
···
170
176
171
177
pub fn open_new_scope(&mut self) -> ScopeResetData {
172
178
let local_values = self.scope.clone();
179
+
let ignored_names = self.ignored_names.clone();
173
180
self.local_variable_usages.push(HashMap::new());
174
-
ScopeResetData { local_values }
181
+
ScopeResetData {
182
+
local_values,
183
+
ignored_names,
184
+
}
175
185
}
176
186
177
187
pub fn close_scope(
···
180
190
was_successful: bool,
181
191
problems: &mut Problems,
182
192
) {
193
+
let ScopeResetData {
194
+
local_values,
195
+
ignored_names,
196
+
} = data;
197
+
183
198
let unused = self
184
199
.local_variable_usages
185
200
.pop()
···
192
207
if was_successful {
193
208
self.handle_unused_variables(unused, problems);
194
209
}
195
-
self.scope = data.local_values;
210
+
self.scope = local_values;
211
+
self.ignored_names = ignored_names;
196
212
}
197
213
198
214
pub fn next_uid(&mut self) -> u64 {
+3
-1
compiler-core/src/type_/error.rs
+3
-1
compiler-core/src/type_/error.rs
···
14
14
use num_bigint::BigInt;
15
15
#[cfg(test)]
16
16
use pretty_assertions::assert_eq;
17
-
use std::sync::Arc;
17
+
use std::{collections::HashMap, sync::Arc};
18
18
19
19
/// Errors and warnings discovered when compiling a module.
20
20
///
···
167
167
location: SrcSpan,
168
168
name: EcoString,
169
169
variables: Vec<EcoString>,
170
+
ignored_variables: HashMap<EcoString, SrcSpan>,
170
171
type_with_name_in_scope: bool,
171
172
},
172
173
···
1297
1298
location,
1298
1299
name,
1299
1300
variables,
1301
+
ignored_variables: HashMap::new(),
1300
1302
type_with_name_in_scope,
1301
1303
},
1302
1304
+11
compiler-core/src/type_/expression.rs
+11
compiler-core/src/type_/expression.rs
···
1008
1008
.map(|type_| self.type_from_ast(&type_))
1009
1009
.unwrap_or_else(|| Ok(self.new_unbound_var()))?;
1010
1010
1011
+
match &names {
1012
+
ArgNames::Named { .. } | ArgNames::NamedLabelled { .. } => (),
1013
+
ArgNames::Discard { name, .. } | ArgNames::LabelledDiscard { name, .. } => {
1014
+
let _ = self
1015
+
.environment
1016
+
.ignored_names
1017
+
.insert(name.clone(), location);
1018
+
}
1019
+
}
1020
+
1011
1021
// If we know the expected type of the argument from its contextual
1012
1022
// usage then unify the newly constructed type with the expected type.
1013
1023
// We do this here because then there is more type information for the
···
3469
3479
location: *location,
3470
3480
name: name.clone(),
3471
3481
variables: self.environment.local_value_names(),
3482
+
ignored_variables: self.environment.ignored_names.clone(),
3472
3483
type_with_name_in_scope: self
3473
3484
.environment
3474
3485
.module_types
+13
-6
compiler-core/src/type_/pattern.rs
+13
-6
compiler-core/src/type_/pattern.rs
···
586
586
match pattern {
587
587
Pattern::Discard { name, location, .. } => {
588
588
self.check_name_case(location, &name, Named::Discard);
589
+
let _ = self
590
+
.environment
591
+
.ignored_names
592
+
.insert(name.clone(), location);
589
593
Pattern::Discard {
590
594
type_,
591
595
name,
···
613
617
614
618
Pattern::VarUsage { name, location, .. } => {
615
619
let constructor = match self.variables.get_mut(&name) {
616
-
// If we've bound a variable in the current bit array pattern, we
617
-
// want to use that.
620
+
// If we've bound a variable in the current bit array pattern,
621
+
// we want to use that.
618
622
Some(variable) if variable.in_scope() => {
619
623
variable.usage = Usage::UsedInPattern;
620
624
ValueConstructor::local_variable(
···
631
635
location,
632
636
name: name.clone(),
633
637
variables: self.environment.local_value_names(),
638
+
ignored_variables: self.environment.ignored_names.clone(),
634
639
type_with_name_in_scope: self
635
640
.environment
636
641
.module_types
···
695
700
},
696
701
);
697
702
}
698
-
AssignName::Discard(_) => {
699
-
if let AssignName::Discard(right) = &right_side_assignment {
700
-
self.check_name_case(right_location, right, Named::Discard);
701
-
}
703
+
AssignName::Discard(right) => {
704
+
let _ = self
705
+
.environment
706
+
.ignored_names
707
+
.insert(right.clone(), right_location);
708
+
self.check_name_case(right_location, right, Named::Discard);
702
709
}
703
710
};
704
711
+67
compiler-core/src/type_/tests/errors.rs
+67
compiler-core/src/type_/tests/errors.rs
···
3125
3125
fn non_utf8_string_assignment() {
3126
3126
assert_error!(r#"let assert <<"Hello" as message:utf16>> = <<>>"#);
3127
3127
}
3128
+
3129
+
#[test]
3130
+
fn shadowed_function_argument() {
3131
+
assert_module_error!(
3132
+
"
3133
+
pub fn go(_x) {
3134
+
x + 1
3135
+
}
3136
+
"
3137
+
);
3138
+
}
3139
+
3140
+
#[test]
3141
+
fn shadowed_fn_argument() {
3142
+
assert_module_error!(
3143
+
"
3144
+
pub fn go(x) {
3145
+
fn(_y) {
3146
+
y + x
3147
+
}
3148
+
}
3149
+
"
3150
+
);
3151
+
}
3152
+
3153
+
#[test]
3154
+
fn shadowed_let_variable() {
3155
+
assert_module_error!(
3156
+
"
3157
+
pub fn go() {
3158
+
let _x = 1
3159
+
x + 1
3160
+
}
3161
+
"
3162
+
);
3163
+
}
3164
+
3165
+
#[test]
3166
+
fn shadowed_pattern_variable() {
3167
+
assert_module_error!(
3168
+
"
3169
+
pub type Wibble {
3170
+
Wibble(Int)
3171
+
}
3172
+
3173
+
pub fn go(x) {
3174
+
case x {
3175
+
Wibble(_y) -> y + 1
3176
+
}
3177
+
}
3178
+
"
3179
+
);
3180
+
}
3181
+
3182
+
#[test]
3183
+
fn do_not_suggest_ignored_variable_outside_of_current_scope() {
3184
+
assert_module_error!(
3185
+
"
3186
+
pub fn go() {
3187
+
let _ = {
3188
+
let _y = 1 // <- this shouldn't be highlighted!
3189
+
}
3190
+
y
3191
+
}
3192
+
"
3193
+
);
3194
+
}
+22
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__do_not_suggest_ignored_variable_outside_of_current_scope.snap
+22
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__do_not_suggest_ignored_variable_outside_of_current_scope.snap
···
1
+
---
2
+
source: compiler-core/src/type_/tests/errors.rs
3
+
expression: "\npub fn go() {\n let _ = {\n let _y = 1 // <- this shouldn't be highlighted!\n }\n y\n}\n"
4
+
---
5
+
----- SOURCE CODE
6
+
7
+
pub fn go() {
8
+
let _ = {
9
+
let _y = 1 // <- this shouldn't be highlighted!
10
+
}
11
+
y
12
+
}
13
+
14
+
15
+
----- ERROR
16
+
error: Unknown variable
17
+
┌─ /src/one/two.gleam:6:3
18
+
│
19
+
6 │ y
20
+
│ ^
21
+
22
+
The name `y` is not in scope here.
+23
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__shadowed_fn_argument.snap
+23
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__shadowed_fn_argument.snap
···
1
+
---
2
+
source: compiler-core/src/type_/tests/errors.rs
3
+
expression: "\npub fn go(x) {\n fn(_y) {\n y + x\n }\n}\n"
4
+
---
5
+
----- SOURCE CODE
6
+
7
+
pub fn go(x) {
8
+
fn(_y) {
9
+
y + x
10
+
}
11
+
}
12
+
13
+
14
+
----- ERROR
15
+
error: Unknown variable
16
+
┌─ /src/one/two.gleam:4:5
17
+
│
18
+
3 │ fn(_y) {
19
+
│ -- Did you mean to use this ignored variable?
20
+
4 │ y + x
21
+
│ ^
22
+
23
+
The name `y` is not in scope here.
+21
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__shadowed_function_argument.snap
+21
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__shadowed_function_argument.snap
···
1
+
---
2
+
source: compiler-core/src/type_/tests/errors.rs
3
+
expression: "\npub fn go(_x) {\n x + 1\n}\n"
4
+
---
5
+
----- SOURCE CODE
6
+
7
+
pub fn go(_x) {
8
+
x + 1
9
+
}
10
+
11
+
12
+
----- ERROR
13
+
error: Unknown variable
14
+
┌─ /src/one/two.gleam:3:3
15
+
│
16
+
2 │ pub fn go(_x) {
17
+
│ -- Did you mean to use this ignored variable?
18
+
3 │ x + 1
19
+
│ ^
20
+
21
+
The name `x` is not in scope here.
+22
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__shadowed_let_variable.snap
+22
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__shadowed_let_variable.snap
···
1
+
---
2
+
source: compiler-core/src/type_/tests/errors.rs
3
+
expression: "\npub fn go() {\n let _x = 1\n x + 1\n}\n"
4
+
---
5
+
----- SOURCE CODE
6
+
7
+
pub fn go() {
8
+
let _x = 1
9
+
x + 1
10
+
}
11
+
12
+
13
+
----- ERROR
14
+
error: Unknown variable
15
+
┌─ /src/one/two.gleam:4:3
16
+
│
17
+
3 │ let _x = 1
18
+
│ -- Did you mean to use this ignored variable?
19
+
4 │ x + 1
20
+
│ ^
21
+
22
+
The name `x` is not in scope here.
+27
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__shadowed_pattern_variable.snap
+27
compiler-core/src/type_/tests/snapshots/gleam_core__type___tests__errors__shadowed_pattern_variable.snap
···
1
+
---
2
+
source: compiler-core/src/type_/tests/errors.rs
3
+
expression: "\npub type Wibble {\n Wibble(Int)\n}\n\npub fn go(x) {\n case x {\n Wibble(_y) -> y + 1\n }\n}\n"
4
+
---
5
+
----- SOURCE CODE
6
+
7
+
pub type Wibble {
8
+
Wibble(Int)
9
+
}
10
+
11
+
pub fn go(x) {
12
+
case x {
13
+
Wibble(_y) -> y + 1
14
+
}
15
+
}
16
+
17
+
18
+
----- ERROR
19
+
error: Unknown variable
20
+
┌─ /src/one/two.gleam:8:19
21
+
│
22
+
8 │ Wibble(_y) -> y + 1
23
+
│ -- ^
24
+
│ │
25
+
│ Did you mean to use this ignored variable?
26
+
27
+
The name `y` is not in scope here.