report unexposed values

This commit is contained in:
Folkert 2021-02-07 01:17:46 +01:00
parent 802f821782
commit dc5eec189c
8 changed files with 114 additions and 13 deletions

View file

@ -1,6 +1,7 @@
use crate::procedure::References; use crate::procedure::References;
use inlinable_string::InlinableString; use inlinable_string::InlinableString;
use roc_collections::all::{MutMap, MutSet}; use roc_collections::all::{MutMap, MutSet};
use roc_module::ident::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};
@ -113,7 +114,7 @@ impl<'a> Env<'a> {
Ok(symbol) Ok(symbol)
} }
None => Err(RuntimeError::ValueNotExposed { None => Err(RuntimeError::ValueNotExposed {
module_name, module_name: ModuleName::from(module_name),
ident, ident,
region, region,
}), }),

View file

@ -2782,7 +2782,25 @@ pub fn with_hole<'a>(
use crate::layout::UnionVariant::*; use crate::layout::UnionVariant::*;
let arena = env.arena; let arena = env.arena;
let variant = crate::layout::union_sorted_tags(env.arena, variant_var, env.subs); let res_variant = crate::layout::union_sorted_tags(env.arena, variant_var, env.subs);
let variant = match res_variant {
Ok(cached) => cached,
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
return Stmt::RuntimeError(env.arena.alloc(format!(
"UnresolvedTypeVar {} line {}",
file!(),
line!()
)));
}
Err(LayoutProblem::Erroneous) => {
return Stmt::RuntimeError(env.arena.alloc(format!(
"Erroneous {} line {}",
file!(),
line!()
)));
}
};
match variant { match variant {
Never => unreachable!( Never => unreachable!(
@ -4581,9 +4599,23 @@ fn from_can_when<'a>(
} }
let opt_branches = to_opt_branches(env, region, branches, layout_cache); let opt_branches = to_opt_branches(env, region, branches, layout_cache);
let cond_layout = layout_cache let cond_layout = match layout_cache.from_var(env.arena, cond_var, env.subs) {
.from_var(env.arena, cond_var, env.subs) Ok(cached) => cached,
.unwrap_or_else(|err| panic!("TODO turn this into a RuntimeError {:?}", err)); Err(LayoutProblem::UnresolvedTypeVar(_)) => {
return Stmt::RuntimeError(env.arena.alloc(format!(
"UnresolvedTypeVar {} line {}",
file!(),
line!()
)));
}
Err(LayoutProblem::Erroneous) => {
return Stmt::RuntimeError(env.arena.alloc(format!(
"Erroneous {} line {}",
file!(),
line!()
)));
}
};
let ret_layout = layout_cache let ret_layout = layout_cache
.from_var(env.arena, expr_var, env.subs) .from_var(env.arena, expr_var, env.subs)
@ -6039,7 +6071,15 @@ fn from_can_pattern_help<'a>(
use crate::exhaustive::Union; use crate::exhaustive::Union;
use crate::layout::UnionVariant::*; use crate::layout::UnionVariant::*;
let variant = crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs); let res_variant = crate::layout::union_sorted_tags(env.arena, *whole_var, env.subs);
let variant = match res_variant {
Ok(cached) => cached,
Err(LayoutProblem::UnresolvedTypeVar(_)) => {
return Err(RuntimeError::UnresolvedTypeVar)
}
Err(LayoutProblem::Erroneous) => return Err(RuntimeError::ErroneousType),
};
let result = match variant { let result = match variant {
Never => unreachable!( Never => unreachable!(

View file

@ -1324,7 +1324,11 @@ impl<'a> WrappedVariant<'a> {
} }
} }
pub fn union_sorted_tags<'a>(arena: &'a Bump, var: Variable, subs: &Subs) -> UnionVariant<'a> { pub fn union_sorted_tags<'a>(
arena: &'a Bump,
var: Variable,
subs: &Subs,
) -> Result<UnionVariant<'a>, LayoutProblem> {
let var = let var =
if let Content::RecursionVar { structure, .. } = subs.get_without_compacting(var).content { if let Content::RecursionVar { structure, .. } = subs.get_without_compacting(var).content {
structure structure
@ -1338,10 +1342,11 @@ pub fn union_sorted_tags<'a>(arena: &'a Bump, var: Variable, subs: &Subs) -> Uni
let opt_rec_var = get_recursion_var(subs, var); let opt_rec_var = get_recursion_var(subs, var);
union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs) union_sorted_tags_help(arena, tags_vec, opt_rec_var, subs)
} }
Err((_, Content::Error)) => return Err(LayoutProblem::Erroneous),
Err(other) => panic!("invalid content in tag union variable: {:?}", other), Err(other) => panic!("invalid content in tag union variable: {:?}", other),
}; };
result Ok(result)
} }
fn get_recursion_var(subs: &Subs, var: Variable) -> Option<Variable> { fn get_recursion_var(subs: &Subs, var: Variable) -> Option<Variable> {

View file

@ -1,6 +1,6 @@
use inlinable_string::InlinableString; use inlinable_string::InlinableString;
use roc_collections::all::MutSet; use roc_collections::all::MutSet;
use roc_module::ident::{Ident, Lowercase, TagName}; use roc_module::ident::{Ident, Lowercase, ModuleName, TagName};
use roc_module::operator::BinOp; use roc_module::operator::BinOp;
use roc_module::symbol::{ModuleId, Symbol}; use roc_module::symbol::{ModuleId, Symbol};
use roc_parse::ast::Base; use roc_parse::ast::Base;
@ -117,9 +117,13 @@ pub enum RuntimeError {
UnsupportedPattern(Region), UnsupportedPattern(Region),
// Example: when 1 is 1.X -> 32 // Example: when 1 is 1.X -> 32
MalformedPattern(MalformedPatternProblem, Region), MalformedPattern(MalformedPatternProblem, Region),
UnresolvedTypeVar,
ErroneousType,
LookupNotInScope(Located<InlinableString>, MutSet<Box<str>>), LookupNotInScope(Located<InlinableString>, MutSet<Box<str>>),
ValueNotExposed { ValueNotExposed {
module_name: InlinableString, module_name: ModuleName,
ident: InlinableString, ident: InlinableString,
region: Region, region: Region,
}, },

View file

@ -344,6 +344,11 @@ fn pretty_runtime_error<'b>(
unreachable!("") unreachable!("")
} }
RuntimeError::UnresolvedTypeVar | RuntimeError::ErroneousType => {
// only generated during layout generation
unreachable!("")
}
RuntimeError::Shadowing { RuntimeError::Shadowing {
original_region, original_region,
shadow, shadow,
@ -443,7 +448,20 @@ fn pretty_runtime_error<'b>(
RuntimeError::UnsupportedPattern(_) => { RuntimeError::UnsupportedPattern(_) => {
todo!("unsupported patterns are currently not parsed!") todo!("unsupported patterns are currently not parsed!")
} }
RuntimeError::ValueNotExposed { .. } => todo!("value not exposed"), RuntimeError::ValueNotExposed { module_name, ident, region } => {
alloc.stack(vec![
alloc.concat(vec![
alloc.reflow("The "),
alloc.module_name(module_name),
alloc.reflow(" module does not expose a "),
alloc.string(ident.to_string()),
alloc.reflow(" value:"),
]),
alloc.region(region),
])
}
RuntimeError::ModuleNotImported { RuntimeError::ModuleNotImported {
module_name, module_name,
imported_modules, imported_modules,

View file

@ -1,6 +1,6 @@
use inlinable_string::InlinableString; use inlinable_string::InlinableString;
use roc_module::ident::Ident; use roc_module::ident::Ident;
use roc_module::ident::{Lowercase, TagName, Uppercase}; use roc_module::ident::{Lowercase, ModuleName, TagName, Uppercase};
use roc_module::symbol::{Interns, ModuleId, Symbol}; use roc_module::symbol::{Interns, ModuleId, Symbol};
use std::fmt; use std::fmt;
use std::path::PathBuf; use std::path::PathBuf;
@ -316,6 +316,17 @@ impl<'a> RocDocAllocator<'a> {
self.text(name).annotate(Annotation::Module) self.text(name).annotate(Annotation::Module)
} }
pub fn module_name(&'a self, name: ModuleName) -> DocBuilder<'a, Self, Annotation> {
let name = if name.is_empty() {
// Render the app module as "app"
"app".to_string()
} else {
name.as_str().to_string()
};
self.text(name).annotate(Annotation::Module)
}
pub fn inlinable_string(&'a self, s: InlinableString) -> DocBuilder<'a, Self, Annotation> { pub fn inlinable_string(&'a self, s: InlinableString) -> DocBuilder<'a, Self, Annotation> {
self.text(format!("{}", s)).annotate(Annotation::Module) self.text(format!("{}", s)).annotate(Annotation::Module)
} }

View file

@ -216,6 +216,27 @@ mod test_reporting {
.replace(UNDERLINE_CODE, "<underline>") .replace(UNDERLINE_CODE, "<underline>")
} }
#[test]
fn value_not_exposed() {
report_problem_as(
indoc!(
r#"
List.foobar 1 2
"#
),
indoc!(
r#"
SYNTAX PROBLEM
The List module does not expose a foobar value:
1 List.foobar 1 2
^^^^^^^^^^^
"#
),
)
}
#[test] #[test]
fn report_unused_def() { fn report_unused_def() {
report_problem_as( report_problem_as(

View file

@ -12,6 +12,7 @@ use inlinable_string::InlinableString;
use roc_can::expr::Recursive; use roc_can::expr::Recursive;
use roc_can::num::{finish_parsing_base, finish_parsing_float, finish_parsing_int}; use roc_can::num::{finish_parsing_base, finish_parsing_float, finish_parsing_int};
use roc_collections::all::{MutMap, MutSet}; use roc_collections::all::{MutMap, MutSet};
use roc_module::ident::ModuleName;
use roc_module::low_level::LowLevel; use roc_module::low_level::LowLevel;
use roc_module::operator::CalledVia; use roc_module::operator::CalledVia;
use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol}; use roc_module::symbol::{IdentIds, ModuleId, ModuleIds, Symbol};
@ -178,7 +179,7 @@ impl<'a> Env<'a> {
Ok(symbol) Ok(symbol)
} }
None => Err(RuntimeError::ValueNotExposed { None => Err(RuntimeError::ValueNotExposed {
module_name, module_name: ModuleName::from(module_name),
ident, ident,
region, region,
}), }),