Skip to content

Commit 3e8ddad

Browse files
committed
Add imported module variables suggestions for unknown variables.
When a variable is not in the current scope, an `Error::UnknownVariable` is triggered. However the only suggestions were for variables in the scope with a similar name computed by the "did you mean" algorithm. With this commit, we also suggest public variables / identifiers (such as constants, functions or type variant constructor) in imported modules with the same name. For example with the following code: ```gleam import gleam/io pub fn main() -> Nil { println("Hello, World!") } ``` The suggestions are: ``` ┌─ /path/to/project/src/project.gleam:4:3 │ 4 │ println("Hello, World!") │ ^^^^^^^ The name `println` is not in scope here. Consider using one of these variables: io.println ``` However because we are only checking the name of the variable, we could have wrong suggestions, as in this case: ```gleam import gleam/float import gleam/int pub fn main() -> Nil { to_string(3) } ``` Here, it is clear that we want suggestions on a function named `to_string` and accepting one parameter of type `Int`, but this is not the case: ``` ┌─ /path/to/project/src/project.gleam:5:3 │ 5 │ to_string(3) │ ^^^^^^^^^ The name `to_string` is not in scope here. Consider using one of these implementations: float.to_string int.to_string ```
1 parent 157111d commit 3e8ddad

File tree

5 files changed

+61
-14
lines changed

5 files changed

+61
-14
lines changed

compiler-core/src/error.rs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2402,9 +2402,9 @@ but no type in scope with that name."
24022402
discarded_location,
24032403
name,
24042404
type_with_name_in_scope,
2405+
imported_modules_with_same_public_variable_name,
24052406
} => {
24062407
let title = String::from("Unknown variable");
2407-
24082408
if let Some(ignored_location) = discarded_location {
24092409
let location = Location {
24102410
label: Label {
@@ -2434,17 +2434,32 @@ but no type in scope with that name."
24342434
let text = if *type_with_name_in_scope {
24352435
wrap_format!("`{name}` is a type, it cannot be used as a value.")
24362436
} else {
2437-
let is_first_char_uppercase =
2438-
name.chars().next().is_some_and(char::is_uppercase);
2439-
2440-
if is_first_char_uppercase {
2437+
let mut text = if name.starts_with(char::is_uppercase) {
24412438
wrap_format!(
2442-
"The custom type variant constructor \
2443-
`{name}` is not in scope here."
2439+
"The custom type variant constructor `{name}` is not in scope here."
24442440
)
2445-
} else {
2441+
}
2442+
else {
24462443
wrap_format!("The name `{name}` is not in scope here.")
2444+
};
2445+
2446+
// If there are some suggestions about variables in imported modules
2447+
// put a "consider" text after the main message
2448+
if !imported_modules_with_same_public_variable_name.is_empty() {
2449+
let consider_text = imported_modules_with_same_public_variable_name
2450+
.iter()
2451+
.fold(
2452+
String::from("Consider using one of these variables:\n\n"),
2453+
|mut acc, module_name| {
2454+
acc.push_str(&wrap_format!(" {module_name}.{name}\n"));
2455+
acc
2456+
}
2457+
);
2458+
text.push('\n');
2459+
text.push_str(&consider_text);
24472460
}
2461+
2462+
text
24482463
};
24492464

24502465
Diagnostic {

compiler-core/src/type_/environment.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -476,14 +476,22 @@ impl Environment<'_> {
476476
name: &EcoString,
477477
) -> Result<&ValueConstructor, UnknownValueConstructorError> {
478478
match module {
479-
None => self.scope.get(name).ok_or_else(|| {
480-
let type_with_name_in_scope = self.module_types.keys().any(|type_| type_ == name);
481-
UnknownValueConstructorError::Variable {
479+
None => self
480+
.scope
481+
.get(name)
482+
.ok_or_else(|| UnknownValueConstructorError::Variable {
482483
name: name.clone(),
483484
variables: self.local_value_names(),
484-
type_with_name_in_scope,
485-
}
486-
}),
485+
type_with_name_in_scope: self.module_types.keys().any(|typ| typ == name),
486+
imported_modules_with_same_public_variable_name: self
487+
.imported_modules
488+
.iter()
489+
.filter_map(|(module_name, (_, module))| {
490+
module.get_public_value(name).map(|_| module_name)
491+
})
492+
.cloned()
493+
.collect_vec(),
494+
}),
487495

488496
Some(module_name) => {
489497
let (_, module) = self.imported_modules.get(module_name).ok_or_else(|| {

compiler-core/src/type_/error.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ pub enum Error {
165165
/// this will contain its location.
166166
discarded_location: Option<SrcSpan>,
167167
type_with_name_in_scope: bool,
168+
/// Filled with the name of imported modules when the module has public value
169+
/// with the same name as this variable
170+
imported_modules_with_same_public_variable_name: Vec<EcoString>,
168171
},
169172

170173
UnknownType {
@@ -1300,6 +1303,7 @@ pub enum UnknownValueConstructorError {
13001303
name: EcoString,
13011304
variables: Vec<EcoString>,
13021305
type_with_name_in_scope: bool,
1306+
imported_modules_with_same_public_variable_name: Vec<EcoString>,
13031307
},
13041308

13051309
Module {
@@ -1325,12 +1329,14 @@ pub fn convert_get_value_constructor_error(
13251329
name,
13261330
variables,
13271331
type_with_name_in_scope,
1332+
imported_modules_with_same_public_variable_name,
13281333
} => Error::UnknownVariable {
13291334
location,
13301335
name,
13311336
variables,
13321337
discarded_location: None,
13331338
type_with_name_in_scope,
1339+
imported_modules_with_same_public_variable_name,
13341340
},
13351341

13361342
UnknownValueConstructorError::Module { name, suggestions } => Error::UnknownModule {

compiler-core/src/type_/expression.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3634,6 +3634,15 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
36343634
.module_types
36353635
.keys()
36363636
.any(|typ| typ == name),
3637+
imported_modules_with_same_public_variable_name: self
3638+
.environment
3639+
.imported_modules
3640+
.iter()
3641+
.filter_map(|(module_name, (_, module))| {
3642+
module.get_public_value(name).map(|_| module_name)
3643+
})
3644+
.cloned()
3645+
.collect_vec(),
36373646
},
36383647
}
36393648
}

compiler-core/src/type_/pattern.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,15 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
12521252
.module_types
12531253
.keys()
12541254
.any(|type_| type_ == &name),
1255+
imported_modules_with_same_public_variable_name: self
1256+
.environment
1257+
.imported_modules
1258+
.iter()
1259+
.filter_map(|(module_name, (_, module))| {
1260+
module.get_public_value(&name).map(|_| module_name)
1261+
})
1262+
.cloned()
1263+
.collect_vec(),
12551264
});
12561265
}
12571266
},

0 commit comments

Comments
 (0)