mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
Suggest typo fixes for non-existing module values
This commit is contained in:
parent
c156f61b0f
commit
214b8a30a9
4 changed files with 77 additions and 24 deletions
|
@ -1,6 +1,6 @@
|
||||||
use crate::procedure::References;
|
use crate::procedure::References;
|
||||||
use roc_collections::all::{MutMap, MutSet};
|
use roc_collections::all::{MutMap, MutSet};
|
||||||
use roc_module::ident::{Ident, ModuleName};
|
use roc_module::ident::{Ident, Lowercase, ModuleName};
|
||||||
use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
|
use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
|
||||||
use roc_problem::can::{Problem, RuntimeError};
|
use roc_problem::can::{Problem, RuntimeError};
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
|
@ -99,23 +99,42 @@ impl<'a> Env<'a> {
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match self
|
match self.dep_idents.get(&module_id) {
|
||||||
.dep_idents
|
Some(exposed_ids) => match exposed_ids.get_id(&ident) {
|
||||||
.get(&module_id)
|
Some(ident_id) => {
|
||||||
.and_then(|exposed_ids| exposed_ids.get_id(&ident))
|
let symbol = Symbol::new(module_id, *ident_id);
|
||||||
{
|
|
||||||
Some(ident_id) => {
|
|
||||||
let symbol = Symbol::new(module_id, *ident_id);
|
|
||||||
|
|
||||||
self.qualified_lookups.insert(symbol);
|
self.qualified_lookups.insert(symbol);
|
||||||
|
|
||||||
Ok(symbol)
|
Ok(symbol)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let exposed_values = exposed_ids
|
||||||
|
.idents()
|
||||||
|
.filter(|(_, ident)| {
|
||||||
|
ident
|
||||||
|
.as_ref()
|
||||||
|
.chars()
|
||||||
|
.next()
|
||||||
|
.filter(|c| c.is_lowercase())
|
||||||
|
.is_some()
|
||||||
|
})
|
||||||
|
.map(|(_, ident)| Lowercase::from(ident.as_ref()))
|
||||||
|
.collect();
|
||||||
|
Err(RuntimeError::ValueNotExposed {
|
||||||
|
module_name,
|
||||||
|
ident,
|
||||||
|
region,
|
||||||
|
exposed_values,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
panic!(
|
||||||
|
"Module {} exists, but is not recorded in dep_idents",
|
||||||
|
module_name
|
||||||
|
)
|
||||||
}
|
}
|
||||||
None => Err(RuntimeError::ValueNotExposed {
|
|
||||||
module_name,
|
|
||||||
ident,
|
|
||||||
region,
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,6 +139,7 @@ pub enum RuntimeError {
|
||||||
module_name: ModuleName,
|
module_name: ModuleName,
|
||||||
ident: Ident,
|
ident: Ident,
|
||||||
region: Region,
|
region: Region,
|
||||||
|
exposed_values: Vec<Lowercase>,
|
||||||
},
|
},
|
||||||
ModuleNotImported {
|
ModuleNotImported {
|
||||||
module_name: ModuleName,
|
module_name: ModuleName,
|
||||||
|
|
|
@ -6,6 +6,7 @@ use roc_problem::can::{BadPattern, FloatErrorKind, IntErrorKind, Problem, Runtim
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::error::r#type::suggest;
|
||||||
use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder, Severity};
|
use crate::report::{Annotation, Report, RocDocAllocator, RocDocBuilder, Severity};
|
||||||
use ven_pretty::DocAllocator;
|
use ven_pretty::DocAllocator;
|
||||||
|
|
||||||
|
@ -874,16 +875,36 @@ fn pretty_runtime_error<'b>(
|
||||||
module_name,
|
module_name,
|
||||||
ident,
|
ident,
|
||||||
region,
|
region,
|
||||||
|
exposed_values,
|
||||||
} => {
|
} => {
|
||||||
|
let mut suggestions = suggest::sort(ident.as_ref(), exposed_values);
|
||||||
|
suggestions.truncate(4);
|
||||||
|
|
||||||
|
let did_you_mean = if suggestions.is_empty() {
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("In fact, it look like "),
|
||||||
|
alloc.module_name(module_name.clone()),
|
||||||
|
alloc.reflow(" doesn't expose any values!"),
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
let qualified_suggestions = suggestions
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| alloc.string(module_name.to_string() + "." + v.as_str()));
|
||||||
|
alloc.stack(vec![
|
||||||
|
alloc.reflow("Did you mean one of these?"),
|
||||||
|
alloc.vcat(qualified_suggestions).indent(4),
|
||||||
|
])
|
||||||
|
};
|
||||||
doc = alloc.stack(vec![
|
doc = alloc.stack(vec![
|
||||||
alloc.concat(vec![
|
alloc.concat(vec![
|
||||||
alloc.reflow("The "),
|
alloc.reflow("The "),
|
||||||
alloc.module_name(module_name),
|
alloc.module_name(module_name),
|
||||||
alloc.reflow(" module does not expose a "),
|
alloc.reflow(" module does not expose `"),
|
||||||
alloc.string(ident.to_string()),
|
alloc.string(ident.to_string()),
|
||||||
alloc.reflow(" value:"),
|
alloc.reflow("`:"),
|
||||||
]),
|
]),
|
||||||
alloc.region(region),
|
alloc.region(region),
|
||||||
|
did_you_mean,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
title = VALUE_NOT_EXPOSED;
|
title = VALUE_NOT_EXPOSED;
|
||||||
|
@ -1176,8 +1197,6 @@ fn not_found<'b>(
|
||||||
thing: &'b str,
|
thing: &'b str,
|
||||||
options: MutSet<Box<str>>,
|
options: MutSet<Box<str>>,
|
||||||
) -> RocDocBuilder<'b> {
|
) -> RocDocBuilder<'b> {
|
||||||
use crate::error::r#type::suggest;
|
|
||||||
|
|
||||||
let mut suggestions = suggest::sort(
|
let mut suggestions = suggest::sort(
|
||||||
name.as_inline_str().as_str(),
|
name.as_inline_str().as_str(),
|
||||||
options.iter().map(|v| v.as_ref()).collect(),
|
options.iter().map(|v| v.as_ref()).collect(),
|
||||||
|
|
|
@ -298,17 +298,24 @@ mod test_reporting {
|
||||||
report_problem_as(
|
report_problem_as(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
List.foobar 1 2
|
List.isempty 1 2
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
── NOT EXPOSED ─────────────────────────────────────────────────────────────────
|
── NOT EXPOSED ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
The List module does not expose a foobar value:
|
The List module does not expose `isempty`:
|
||||||
|
|
||||||
1│ List.foobar 1 2
|
1│ List.isempty 1 2
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Did you mean one of these?
|
||||||
|
|
||||||
|
List.isEmpty
|
||||||
|
List.set
|
||||||
|
List.get
|
||||||
|
List.keepIf
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -5624,10 +5631,17 @@ mod test_reporting {
|
||||||
r#"
|
r#"
|
||||||
── NOT EXPOSED ─────────────────────────────────────────────────────────────────
|
── NOT EXPOSED ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
The Num module does not expose a if value:
|
The Num module does not expose `if`:
|
||||||
|
|
||||||
1│ Num.if
|
1│ Num.if
|
||||||
^^^^^^
|
^^^^^^
|
||||||
|
|
||||||
|
Did you mean one of these?
|
||||||
|
|
||||||
|
Num.sin
|
||||||
|
Num.div
|
||||||
|
Num.abs
|
||||||
|
Num.neg
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue