Skip to content

Commit 31504f6

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 7eeac6d commit 31504f6

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
@@ -2401,9 +2401,9 @@ but no type in scope with that name."
24012401
discarded_location,
24022402
name,
24032403
type_with_name_in_scope,
2404+
imported_modules_with_same_public_variable_name,
24042405
} => {
24052406
let title = String::from("Unknown variable");
2406-
24072407
if let Some(ignored_location) = discarded_location {
24082408
let location = Location {
24092409
label: Label {
@@ -2433,17 +2433,32 @@ but no type in scope with that name."
24332433
let text = if *type_with_name_in_scope {
24342434
wrap_format!("`{name}` is a type, it cannot be used as a value.")
24352435
} else {
2436-
let is_first_char_uppercase =
2437-
name.chars().next().is_some_and(char::is_uppercase);
2438-
2439-
if is_first_char_uppercase {
2436+
let mut text = if name.starts_with(char::is_uppercase) {
24402437
wrap_format!(
2441-
"The custom type variant constructor \
2442-
`{name}` is not in scope here."
2438+
"The custom type variant constructor `{name}` is not in scope here."
24432439
)
2444-
} else {
2440+
}
2441+
else {
24452442
wrap_format!("The name `{name}` is not in scope here.")
2443+
};
2444+
2445+
// If there are some suggestions about variables in imported modules
2446+
// put a "consider" text after the main message
2447+
if !imported_modules_with_same_public_variable_name.is_empty() {
2448+
let consider_text = imported_modules_with_same_public_variable_name
2449+
.iter()
2450+
.fold(
2451+
String::from("Consider using one of these variables:\n\n"),
2452+
|mut acc, module_name| {
2453+
acc.push_str(&wrap_format!(" {module_name}.{name}\n"));
2454+
acc
2455+
}
2456+
);
2457+
text.push('\n');
2458+
text.push_str(&consider_text);
24462459
}
2460+
2461+
text
24472462
};
24482463

24492464
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
@@ -171,6 +171,9 @@ pub enum Error {
171171
/// this will contain its location.
172172
discarded_location: Option<SrcSpan>,
173173
type_with_name_in_scope: bool,
174+
/// Filled with the name of imported modules when the module has public value
175+
/// with the same name as this variable
176+
imported_modules_with_same_public_variable_name: Vec<EcoString>,
174177
},
175178

176179
UnknownType {
@@ -1293,6 +1296,7 @@ pub enum UnknownValueConstructorError {
12931296
name: EcoString,
12941297
variables: Vec<EcoString>,
12951298
type_with_name_in_scope: bool,
1299+
imported_modules_with_same_public_variable_name: Vec<EcoString>,
12961300
},
12971301

12981302
Module {
@@ -1318,12 +1322,14 @@ pub fn convert_get_value_constructor_error(
13181322
name,
13191323
variables,
13201324
type_with_name_in_scope,
1325+
imported_modules_with_same_public_variable_name,
13211326
} => Error::UnknownVariable {
13221327
location,
13231328
name,
13241329
variables,
13251330
discarded_location: None,
13261331
type_with_name_in_scope,
1332+
imported_modules_with_same_public_variable_name,
13271333
},
13281334

13291335
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
@@ -3527,6 +3527,15 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
35273527
.module_types
35283528
.keys()
35293529
.any(|typ| typ == name),
3530+
imported_modules_with_same_public_variable_name: self
3531+
.environment
3532+
.imported_modules
3533+
.iter()
3534+
.filter_map(|(module_name, (_, module))| {
3535+
module.get_public_value(name).map(|_| module_name)
3536+
})
3537+
.cloned()
3538+
.collect_vec(),
35303539
},
35313540
}
35323541
}

compiler-core/src/type_/pattern.rs

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

0 commit comments

Comments
 (0)