Skip to content

Commit 490e07a

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 a81b912 commit 490e07a

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
@@ -481,14 +481,22 @@ impl Environment<'_> {
481481
name: &EcoString,
482482
) -> Result<&ValueConstructor, UnknownValueConstructorError> {
483483
match module {
484-
None => self.scope.get(name).ok_or_else(|| {
485-
let type_with_name_in_scope = self.module_types.keys().any(|type_| type_ == name);
486-
UnknownValueConstructorError::Variable {
484+
None => self
485+
.scope
486+
.get(name)
487+
.ok_or_else(|| UnknownValueConstructorError::Variable {
487488
name: name.clone(),
488489
variables: self.local_value_names(),
489-
type_with_name_in_scope,
490-
}
491-
}),
490+
type_with_name_in_scope: self.module_types.keys().any(|typ| typ == name),
491+
imported_modules_with_same_public_variable_name: self
492+
.imported_modules
493+
.iter()
494+
.filter_map(|(module_name, (_, module))| {
495+
module.get_public_value(name).map(|_| module_name)
496+
})
497+
.cloned()
498+
.collect_vec(),
499+
}),
492500

493501
Some(module_name) => {
494502
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 {
@@ -1314,6 +1317,7 @@ pub enum UnknownValueConstructorError {
13141317
name: EcoString,
13151318
variables: Vec<EcoString>,
13161319
type_with_name_in_scope: bool,
1320+
imported_modules_with_same_public_variable_name: Vec<EcoString>,
13171321
},
13181322

13191323
Module {
@@ -1339,12 +1343,14 @@ pub fn convert_get_value_constructor_error(
13391343
name,
13401344
variables,
13411345
type_with_name_in_scope,
1346+
imported_modules_with_same_public_variable_name,
13421347
} => Error::UnknownVariable {
13431348
location,
13441349
name,
13451350
variables,
13461351
discarded_location: None,
13471352
type_with_name_in_scope,
1353+
imported_modules_with_same_public_variable_name,
13481354
},
13491355

13501356
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
@@ -3647,6 +3647,15 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
36473647
.module_types
36483648
.keys()
36493649
.any(|typ| typ == name),
3650+
imported_modules_with_same_public_variable_name: self
3651+
.environment
3652+
.imported_modules
3653+
.iter()
3654+
.filter_map(|(module_name, (_, module))| {
3655+
module.get_public_value(name).map(|_| module_name)
3656+
})
3657+
.cloned()
3658+
.collect_vec(),
36503659
},
36513660
}
36523661
}

compiler-core/src/type_/pattern.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,15 @@ impl<'a, 'b> PatternTyper<'a, 'b> {
12591259
.module_types
12601260
.keys()
12611261
.any(|type_| type_ == &name),
1262+
imported_modules_with_same_public_variable_name: self
1263+
.environment
1264+
.imported_modules
1265+
.iter()
1266+
.filter_map(|(module_name, (_, module))| {
1267+
module.get_public_value(&name).map(|_| module_name)
1268+
})
1269+
.cloned()
1270+
.collect_vec(),
12621271
});
12631272
}
12641273
},

0 commit comments

Comments
 (0)