mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-24 20:42:29 +00:00
Report missing params
This commit is contained in:
parent
bc6a84a215
commit
922b1c44ef
16 changed files with 286 additions and 93 deletions
|
@ -689,11 +689,11 @@ impl Constraints {
|
|||
|
||||
pub fn import_params(
|
||||
&mut self,
|
||||
type_index: TypeOrVar,
|
||||
opt_type_index: Option<TypeOrVar>,
|
||||
module_id: ModuleId,
|
||||
region: Region,
|
||||
) -> Constraint {
|
||||
Constraint::ImportParams(type_index, module_id, region)
|
||||
Constraint::ImportParams(opt_type_index, module_id, region)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -797,7 +797,7 @@ pub enum Constraint {
|
|||
CheckCycle(Index<Cycle>, IllegalCycleMark),
|
||||
|
||||
IngestedFile(TypeOrVar, Box<PathBuf>, Arc<Vec<u8>>),
|
||||
ImportParams(TypeOrVar, ModuleId, Region),
|
||||
ImportParams(Option<TypeOrVar>, ModuleId, Region),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
|
|
|
@ -302,6 +302,7 @@ fn deep_copy_expr_help<C: CopyEnv>(env: &mut C, copied: &mut Vec<Variable>, expr
|
|||
sub!(*var),
|
||||
*module_id,
|
||||
),
|
||||
MissingImportParams(module_id, region) => MissingImportParams(*module_id, *region),
|
||||
&AbilityMember(sym, specialization, specialization_var) => {
|
||||
AbilityMember(sym, specialization, sub!(specialization_var))
|
||||
}
|
||||
|
|
|
@ -210,6 +210,9 @@ fn expr<'a>(c: &Ctx, p: EPrec, f: &'a Arena<'a>, e: &'a Expr) -> DocBuilder<'a,
|
|||
pp_sym(c, f, *sym)
|
||||
}
|
||||
ImportParams(loc_expr, _, _) => expr(c, p, f, &loc_expr.value),
|
||||
MissingImportParams(module_id, _) => {
|
||||
text!(f, "<missing params for {:?}>", module_id)
|
||||
}
|
||||
When {
|
||||
loc_cond, branches, ..
|
||||
} => maybe_paren!(
|
||||
|
|
|
@ -171,7 +171,7 @@ enum PendingValueDef<'a> {
|
|||
symbol: Symbol,
|
||||
loc_pattern: Loc<Pattern>,
|
||||
module_id: ModuleId,
|
||||
params: ast::Collection<'a, Loc<AssignedField<'a, ast::Expr<'a>>>>,
|
||||
opt_provided: Option<ast::Collection<'a, Loc<AssignedField<'a, ast::Expr<'a>>>>>,
|
||||
},
|
||||
/// Ingested file
|
||||
IngestedFile(
|
||||
|
@ -191,7 +191,7 @@ impl PendingValueDef<'_> {
|
|||
loc_pattern,
|
||||
symbol: _,
|
||||
module_id: _,
|
||||
params: _,
|
||||
opt_provided: _,
|
||||
} => loc_pattern,
|
||||
PendingValueDef::IngestedFile(loc_pattern, _, _) => loc_pattern,
|
||||
}
|
||||
|
@ -1141,25 +1141,32 @@ fn canonicalize_value_defs<'a>(
|
|||
PendingValue::ExpectFx(pending_expect) => {
|
||||
pending_expect_fx.push(pending_expect);
|
||||
}
|
||||
PendingValue::ModuleImport {
|
||||
PendingValue::ModuleImport(PendingModuleImport {
|
||||
module_id,
|
||||
region,
|
||||
exposed_symbols,
|
||||
params,
|
||||
} => {
|
||||
}) => {
|
||||
imports_introduced.push(IntroducedImport {
|
||||
module_id,
|
||||
region,
|
||||
exposed_symbols,
|
||||
});
|
||||
|
||||
if let Some((symbol, loc_pattern, params)) = params {
|
||||
pending_value_defs.push(PendingValueDef::ImportParams {
|
||||
match params {
|
||||
PendingModuleImportParams::None => {}
|
||||
PendingModuleImportParams::Required {
|
||||
symbol,
|
||||
loc_pattern,
|
||||
module_id,
|
||||
params,
|
||||
});
|
||||
opt_provided,
|
||||
} => {
|
||||
pending_value_defs.push(PendingValueDef::ImportParams {
|
||||
symbol,
|
||||
loc_pattern,
|
||||
module_id,
|
||||
opt_provided,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
PendingValue::InvalidIngestedFile => { /* skip */ }
|
||||
|
@ -2406,26 +2413,36 @@ fn canonicalize_pending_value_def<'a>(
|
|||
symbol,
|
||||
loc_pattern,
|
||||
module_id,
|
||||
params,
|
||||
opt_provided,
|
||||
} => {
|
||||
let (record, can_output) =
|
||||
canonicalize_record(env, var_store, scope, loc_pattern.region, params);
|
||||
|
||||
// Insert a reference to the record so that we don't report it as unused
|
||||
// If the whole module is unused, we'll report that separately
|
||||
output
|
||||
.references
|
||||
.insert_value_lookup(symbol, QualifiedReference::Unqualified);
|
||||
|
||||
let references = DefReferences::Value(can_output.references.clone());
|
||||
output.union(can_output);
|
||||
let (expr, references) = match opt_provided {
|
||||
Some(params) => {
|
||||
let (record, can_output) =
|
||||
canonicalize_record(env, var_store, scope, loc_pattern.region, params);
|
||||
|
||||
let loc_record = Loc::at(loc_pattern.region, record);
|
||||
let references = can_output.references.clone();
|
||||
output.union(can_output);
|
||||
|
||||
let loc_expr = Loc::at(
|
||||
loc_pattern.region,
|
||||
Expr::ImportParams(Box::new(loc_record), var_store.fresh(), module_id),
|
||||
);
|
||||
let loc_record = Loc::at(loc_pattern.region, record);
|
||||
|
||||
let expr =
|
||||
Expr::ImportParams(Box::new(loc_record), var_store.fresh(), module_id);
|
||||
|
||||
(expr, references)
|
||||
}
|
||||
None => (
|
||||
Expr::MissingImportParams(module_id, loc_pattern.region),
|
||||
References::new(),
|
||||
),
|
||||
};
|
||||
|
||||
let loc_expr = Loc::at(loc_pattern.region, expr);
|
||||
|
||||
let def = single_can_def(
|
||||
loc_pattern,
|
||||
|
@ -2437,7 +2454,7 @@ fn canonicalize_pending_value_def<'a>(
|
|||
|
||||
DefOutput {
|
||||
output,
|
||||
references,
|
||||
references: DefReferences::Value(references),
|
||||
def,
|
||||
}
|
||||
}
|
||||
|
@ -2982,16 +2999,7 @@ enum PendingValue<'a> {
|
|||
Dbg(PendingExpectOrDbg<'a>),
|
||||
Expect(PendingExpectOrDbg<'a>),
|
||||
ExpectFx(PendingExpectOrDbg<'a>),
|
||||
ModuleImport {
|
||||
module_id: ModuleId,
|
||||
region: Region,
|
||||
exposed_symbols: Vec<(Symbol, Region)>,
|
||||
params: Option<(
|
||||
Symbol,
|
||||
Loc<Pattern>,
|
||||
ast::Collection<'a, Loc<AssignedField<'a, ast::Expr<'a>>>>,
|
||||
)>,
|
||||
},
|
||||
ModuleImport(PendingModuleImport<'a>),
|
||||
SignatureDefMismatch,
|
||||
InvalidIngestedFile,
|
||||
ImportNameConflict,
|
||||
|
@ -3002,6 +3010,24 @@ struct PendingExpectOrDbg<'a> {
|
|||
preceding_comment: Region,
|
||||
}
|
||||
|
||||
struct PendingModuleImport<'a> {
|
||||
module_id: ModuleId,
|
||||
region: Region,
|
||||
exposed_symbols: Vec<(Symbol, Region)>,
|
||||
params: PendingModuleImportParams<'a>,
|
||||
}
|
||||
|
||||
enum PendingModuleImportParams<'a> {
|
||||
/// The module does not require any params
|
||||
None,
|
||||
/// The module requires params, they may or may not have been provided
|
||||
Required {
|
||||
symbol: Symbol,
|
||||
loc_pattern: Loc<Pattern>,
|
||||
opt_provided: Option<ast::Collection<'a, Loc<AssignedField<'a, ast::Expr<'a>>>>>,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct IntroducedImport {
|
||||
module_id: ModuleId,
|
||||
region: Region,
|
||||
|
@ -3152,42 +3178,40 @@ fn to_pending_value_def<'a>(
|
|||
None => module_name.clone(),
|
||||
};
|
||||
|
||||
let mut params_sym = None;
|
||||
|
||||
let params = module_import.params.and_then(|params| {
|
||||
let params = if env.modules_expecting_params.contains(&module_id) {
|
||||
let name_str = name_with_alias.as_str();
|
||||
let sym_region = module_import.params.map(|p| p.params.region).unwrap_or(region);
|
||||
|
||||
let params_region = params.params.region;
|
||||
match scope.introduce_str(format!("#{name_str}").as_str(), sym_region) {
|
||||
Ok(symbol) => {
|
||||
let loc_pattern = Loc::at(sym_region, Pattern::Identifier(symbol));
|
||||
|
||||
match scope.introduce_str(format!("#{name_str}").as_str(), params_region) {
|
||||
Ok(sym) => {
|
||||
params_sym = Some(sym);
|
||||
|
||||
let loc_pattern = Loc::at(params_region, Pattern::Identifier(sym));
|
||||
Some((sym, loc_pattern, params.params.value))
|
||||
PendingModuleImportParams::Required {
|
||||
symbol,
|
||||
loc_pattern,
|
||||
opt_provided: module_import.params.map(|p| p.params.value),
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
Err(_) => {
|
||||
// Ignore conflict, it will be handled as a duplicate import
|
||||
None
|
||||
}
|
||||
PendingModuleImportParams::None
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
match (module_import.params, env.modules_expecting_params.contains(&module_id)) {
|
||||
(None, true) => {
|
||||
env.problems.push(Problem::MissingParams {
|
||||
module_id,
|
||||
region,
|
||||
});
|
||||
}
|
||||
(Some(import_params), false) => {
|
||||
} else {
|
||||
if let Some(params) = module_import.params {
|
||||
env.problems.push(Problem::UnexpectedParams {
|
||||
module_id,
|
||||
region: import_params.params.region,
|
||||
region: params.params.region,
|
||||
});
|
||||
}
|
||||
(None, false) | (Some(_), true) => { /* All good */}
|
||||
}
|
||||
|
||||
PendingModuleImportParams::None
|
||||
};
|
||||
|
||||
let params_sym = match params {
|
||||
PendingModuleImportParams::Required { symbol, .. } => Some(symbol),
|
||||
PendingModuleImportParams::None => None,
|
||||
};
|
||||
|
||||
if let Err(existing_import) =
|
||||
scope
|
||||
|
@ -3257,12 +3281,12 @@ fn to_pending_value_def<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
PendingValue::ModuleImport {
|
||||
PendingValue::ModuleImport(PendingModuleImport {
|
||||
module_id,
|
||||
region,
|
||||
exposed_symbols,
|
||||
params,
|
||||
}
|
||||
})
|
||||
}
|
||||
IngestedFileImport(ingested_file) => {
|
||||
let loc_name = ingested_file.name.item;
|
||||
|
|
|
@ -186,6 +186,9 @@ pub enum Expr {
|
|||
|
||||
/// Module params expression in import
|
||||
ImportParams(Box<Loc<Expr>>, Variable, ModuleId),
|
||||
/// The imported module requires params but none were provided
|
||||
/// We delay this error until solve, so we can report the expected type
|
||||
MissingImportParams(ModuleId, Region),
|
||||
|
||||
/// The "crash" keyword
|
||||
Crash {
|
||||
|
@ -340,6 +343,7 @@ impl Expr {
|
|||
Self::TupleAccess { index, .. } => Category::TupleAccess(*index),
|
||||
Self::RecordUpdate { .. } => Category::Record,
|
||||
Self::ImportParams(loc_expr, _, _) => loc_expr.value.category(),
|
||||
Self::MissingImportParams(_, _) => Category::Unknown,
|
||||
Self::Tag {
|
||||
name, arguments, ..
|
||||
} => Category::TagApply {
|
||||
|
@ -1966,6 +1970,7 @@ pub fn inline_calls(var_store: &mut VarStore, expr: Expr) -> Expr {
|
|||
| other @ TypedHole { .. }
|
||||
| other @ ForeignCall { .. }
|
||||
| other @ OpaqueWrapFunction(_)
|
||||
| other @ MissingImportParams(_, _)
|
||||
| other @ Crash { .. } => other,
|
||||
|
||||
List {
|
||||
|
@ -3270,6 +3275,7 @@ pub(crate) fn get_lookup_symbols(expr: &Expr) -> Vec<ExpectLookup> {
|
|||
| Expr::EmptyRecord
|
||||
| Expr::TypedHole(_)
|
||||
| Expr::RuntimeError(_)
|
||||
| Expr::MissingImportParams(_, _)
|
||||
| Expr::OpaqueWrapFunction(_) => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1149,6 +1149,7 @@ fn fix_values_captured_in_closure_expr(
|
|||
| TypedHole { .. }
|
||||
| RuntimeError(_)
|
||||
| ZeroArgumentTag { .. }
|
||||
| MissingImportParams(_, _)
|
||||
| RecordAccessor { .. } => {}
|
||||
|
||||
List { loc_elems, .. } => {
|
||||
|
|
|
@ -404,6 +404,7 @@ pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr, var: Variable) {
|
|||
}
|
||||
Expr::TypedHole(_) => { /* terminal */ }
|
||||
Expr::RuntimeError(..) => { /* terminal */ }
|
||||
Expr::MissingImportParams(..) => { /* terminal */ }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue