Suggest typo fixes for non-existing module values

This commit is contained in:
ayazhafiz 2021-11-17 22:39:24 -05:00
parent c156f61b0f
commit 214b8a30a9
4 changed files with 77 additions and 24 deletions

View file

@ -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,11 +99,8 @@ 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)
.and_then(|exposed_ids| exposed_ids.get_id(&ident))
{
Some(ident_id) => { Some(ident_id) => {
let symbol = Symbol::new(module_id, *ident_id); let symbol = Symbol::new(module_id, *ident_id);
@ -111,11 +108,33 @@ impl<'a> Env<'a> {
Ok(symbol) Ok(symbol)
} }
None => Err(RuntimeError::ValueNotExposed { 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, module_name,
ident, ident,
region, region,
}), exposed_values,
})
}
},
None => {
panic!(
"Module {} exists, but is not recorded in dep_idents",
module_name
)
}
} }
} }
} }

View file

@ -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,

View file

@ -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(),

View file

@ -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
"# "#
), ),
) )