1use crate::error::ResolveError;
2use crate::module::{ModuleGraph, load_module_graph};
3use crate::prelude::prelude_symbols;
4use crate::stdlib::stdlib_symbols;
5use crate::symbols::{Symbol, SymbolKey, SymbolKind, Visibility, collect_module_symbols};
6use crate::warning::ResolveWarning;
7use crisp_ast::Span;
8use crisp_ast::expr::{Block, Expr, ExprKind, Stmt};
9use crisp_ast::ident::Ident;
10use crisp_ast::item::{Item, SourceFile, UseDecl};
11use crisp_ast::pat::{Pat, PatKind};
12use crisp_ast::ty::{Type, TypeBound, TypeKind};
13use crisp_manifest::{read_manifest, resolve_dependencies};
14use std::collections::{BTreeMap, HashMap, HashSet};
15use std::path::Path;
16
17#[derive(Debug, Clone)]
18pub struct ResolvedBinding {
19 pub local_name: String,
20 pub symbol: SymbolKey,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct ResolvedRustImport {
28 pub crisp_module: String,
29 pub crate_name: String,
30 pub item: String,
31 pub local_name: String,
32}
33
34#[derive(Debug, Clone)]
35pub struct ResolvedModule {
36 pub module_path: String,
37 pub file: String,
38 pub imports: Vec<ResolvedBinding>,
39 pub scope: Vec<String>,
40}
41
42#[derive(Debug, Clone)]
43pub struct ResolvedCrate {
44 pub crate_root: String,
45 pub modules: Vec<ResolvedModule>,
46 pub symbol_count: usize,
47 pub rust_imports: Vec<ResolvedRustImport>,
48 pub warnings: Vec<ResolveWarning>,
49}
50
51pub struct Resolver {
52 graph: ModuleGraph,
53 global: BTreeMap<SymbolKey, Symbol>,
54 rust_deps: HashSet<String>,
56 unmarked_deps: HashSet<String>,
58 rust_imports: Vec<ResolvedRustImport>,
59 warnings: Vec<ResolveWarning>,
60}
61
62impl Resolver {
63 pub fn resolve_crate(crate_root: &Path) -> Result<ResolvedCrate, ResolveError> {
64 let graph = load_module_graph(crate_root)?;
65 let (rust_deps, unmarked_deps) = load_dep_sets(crate_root)?;
66 let mut resolver = Self::new(graph, rust_deps, unmarked_deps)?;
67 resolver.run()
68 }
69
70 fn new(
71 graph: ModuleGraph,
72 rust_deps: HashSet<String>,
73 unmarked_deps: HashSet<String>,
74 ) -> Result<Self, ResolveError> {
75 let mut global = BTreeMap::new();
76 for sym in prelude_symbols().into_iter().chain(stdlib_symbols()) {
77 global.insert(sym.key.clone(), sym);
78 }
79 for (module_path, node) in &graph.modules {
80 for sym in collect_module_symbols(module_path, &node.ast.items) {
81 if let Some(prev) = global.get(&sym.key) {
82 return Err(ResolveError::DuplicateDef {
83 name: sym.key.name.clone(),
84 module: sym.key.module.clone(),
85 span: sym.span.merge(prev.span),
86 });
87 }
88 global.insert(sym.key.clone(), sym);
89 }
90 }
91 Ok(Self {
92 graph,
93 global,
94 rust_deps,
95 unmarked_deps,
96 rust_imports: Vec::new(),
97 warnings: Vec::new(),
98 })
99 }
100
101 fn run(&mut self) -> Result<ResolvedCrate, ResolveError> {
102 let mut resolved_modules = Vec::new();
103 for (module_path, node) in &self.graph.modules.clone() {
104 let imports = self.resolve_module_imports(module_path, &node.ast)?;
105 let scope: Vec<String> = imports.iter().map(|b| b.local_name.clone()).collect();
106 self.check_module_references(module_path, &node.ast, &imports)?;
107 resolved_modules.push(ResolvedModule {
108 module_path: module_path.clone(),
109 file: node.path.display().to_string(),
110 imports,
111 scope,
112 });
113 }
114 Ok(ResolvedCrate {
115 crate_root: self.graph.crate_root.display().to_string(),
116 modules: resolved_modules,
117 symbol_count: self.global.len(),
118 rust_imports: self.rust_imports.clone(),
119 warnings: self.warnings.clone(),
120 })
121 }
122
123 fn resolve_module_imports(
124 &mut self,
125 current: &str,
126 file: &SourceFile,
127 ) -> Result<Vec<ResolvedBinding>, ResolveError> {
128 let mut scope: HashMap<String, SymbolKey> = HashMap::new();
129 let mut bindings = Vec::new();
130
131 for sym in prelude_symbols().into_iter().chain(stdlib_symbols()) {
132 scope.insert(sym.key.name.clone(), sym.key.clone());
133 bindings.push(ResolvedBinding {
134 local_name: sym.key.name.clone(),
135 symbol: sym.key.clone(),
136 });
137 }
138
139 for sym in collect_module_symbols(current, &file.items) {
140 scope.insert(sym.key.name.clone(), sym.key.clone());
141 if !bindings.iter().any(|b| b.local_name == sym.key.name) {
142 bindings.push(ResolvedBinding {
143 local_name: sym.key.name.clone(),
144 symbol: sym.key.clone(),
145 });
146 }
147 }
148
149 for item in &file.items {
150 let Item::Use(use_decl) = item else {
151 continue;
152 };
153 self.apply_use(current, use_decl, &mut scope, &mut bindings)?;
154 }
155
156 Ok(bindings)
157 }
158
159 fn apply_use(
160 &mut self,
161 current: &str,
162 decl: &UseDecl,
163 scope: &mut HashMap<String, SymbolKey>,
164 bindings: &mut Vec<ResolvedBinding>,
165 ) -> Result<(), ResolveError> {
166 if decl.path.first().is_some_and(|p| p.name == "rust") {
168 let path_str = decl
169 .path
170 .iter()
171 .map(|p| p.name.as_str())
172 .collect::<Vec<_>>()
173 .join(".");
174 if decl.path.len() != 2 {
175 return Err(ResolveError::RustUsePathInvalid {
176 path: path_str,
177 span: decl.span,
178 });
179 }
180 let crate_name = decl.path[1].name.clone();
181 return self.bind_rust_crate(current, &crate_name, decl, scope, bindings);
182 }
183
184 if let Some(target_module) = self.lookup_crisp_module(current, &decl.path) {
186 let joined = decl
187 .path
188 .iter()
189 .map(|p| p.name.as_str())
190 .collect::<Vec<_>>()
191 .join(".");
192 if self.rust_deps.contains(&joined) {
193 self.warnings.push(ResolveWarning::ModuleShadowsRustDep {
194 name: joined.clone(),
195 span: decl.span,
196 });
197 }
198 return self.bind_crisp_use(target_module, decl, scope, bindings);
199 }
200
201 if decl.path.len() == 1 {
203 let crate_name = decl.path[0].name.clone();
204 if self.rust_deps.contains(&crate_name) || self.unmarked_deps.contains(&crate_name) {
205 return self.bind_rust_crate(current, &crate_name, decl, scope, bindings);
206 }
207 }
208
209 Err(ResolveError::ModuleNotFound {
210 path: decl
211 .path
212 .iter()
213 .map(|p| p.name.as_str())
214 .collect::<Vec<_>>()
215 .join("."),
216 })
217 }
218
219 fn bind_crisp_use(
220 &self,
221 target_module: String,
222 decl: &UseDecl,
223 scope: &mut HashMap<String, SymbolKey>,
224 bindings: &mut Vec<ResolvedBinding>,
225 ) -> Result<(), ResolveError> {
226 let span = decl.span;
227 if let Some(imports) = &decl.imports {
228 for imp in imports {
229 let sym = self.lookup_export(&target_module, &imp.name.name)?;
230 let local = imp
231 .alias
232 .as_ref()
233 .map(|a| a.name.clone())
234 .unwrap_or_else(|| imp.name.name.clone());
235 self.insert_binding(&local, sym.key.clone(), scope, bindings, span)?;
236 }
237 } else {
238 let exported: Vec<_> = self
239 .global
240 .values()
241 .filter(|s| s.key.module == target_module && s.is_exported() && !s.from_prelude)
242 .cloned()
243 .collect();
244 for sym in exported {
245 self.insert_binding(&sym.key.name, sym.key.clone(), scope, bindings, span)?;
246 }
247 }
248 let _ = decl.is_pub;
249 Ok(())
250 }
251
252 fn bind_rust_crate(
253 &mut self,
254 current: &str,
255 crate_name: &str,
256 decl: &UseDecl,
257 scope: &mut HashMap<String, SymbolKey>,
258 bindings: &mut Vec<ResolvedBinding>,
259 ) -> Result<(), ResolveError> {
260 if self.unmarked_deps.contains(crate_name) && !self.rust_deps.contains(crate_name) {
261 return Err(ResolveError::RustCrateNotMarked {
262 name: crate_name.to_string(),
263 span: decl.span,
264 });
265 }
266 if !self.rust_deps.contains(crate_name) {
267 return Err(ResolveError::RustCrateNotFound {
268 name: crate_name.to_string(),
269 span: decl.span,
270 });
271 }
272 let Some(imports) = &decl.imports else {
273 return Err(ResolveError::RustImportNeedsList {
274 name: crate_name.to_string(),
275 span: decl.span,
276 });
277 };
278
279 let module = format!("rust.{crate_name}");
280 for imp in imports {
281 let key = SymbolKey {
282 module: module.clone(),
283 name: imp.name.name.clone(),
284 };
285 self.global.entry(key.clone()).or_insert_with(|| Symbol {
286 key: key.clone(),
287 kind: SymbolKind::RustFn,
288 visibility: Visibility::Public,
289 span: imp.span,
290 from_prelude: false,
291 });
292 let local = imp
293 .alias
294 .as_ref()
295 .map(|a| a.name.clone())
296 .unwrap_or_else(|| imp.name.name.clone());
297 self.insert_binding(&local, key, scope, bindings, decl.span)?;
298 self.rust_imports.push(ResolvedRustImport {
299 crisp_module: current.to_string(),
300 crate_name: crate_name.to_string(),
301 item: imp.name.name.clone(),
302 local_name: local,
303 });
304 }
305 Ok(())
306 }
307
308 fn lookup_crisp_module(&self, current: &str, path: &[Ident]) -> Option<String> {
310 self.resolve_use_path(current, path).ok()
311 }
312
313 fn insert_binding(
314 &self,
315 local: &str,
316 key: SymbolKey,
317 scope: &mut HashMap<String, SymbolKey>,
318 bindings: &mut Vec<ResolvedBinding>,
319 span: Span,
320 ) -> Result<(), ResolveError> {
321 if let Some(prev) = scope.get(local) {
322 if prev != &key {
323 return Err(ResolveError::AmbiguousImport {
324 name: local.to_string(),
325 span,
326 });
327 }
328 return Ok(());
329 }
330 scope.insert(local.to_string(), key.clone());
331 bindings.push(ResolvedBinding {
332 local_name: local.to_string(),
333 symbol: key,
334 });
335 Ok(())
336 }
337
338 fn resolve_use_path(&self, current: &str, path: &[Ident]) -> Result<String, ResolveError> {
339 if path.is_empty() {
340 return Err(ResolveError::ModuleNotFound {
341 path: "(empty)".to_string(),
342 });
343 }
344 let joined = path
345 .iter()
346 .map(|p| p.name.as_str())
347 .collect::<Vec<_>>()
348 .join(".");
349 if path.first().map(|p| p.name.as_str()) == Some("std") {
350 return Ok(joined);
351 }
352 if self.graph.modules.contains_key(&joined) {
353 return Ok(joined);
354 }
355 if self.graph.modules.contains_key(&joined) {
357 return Ok(joined);
358 }
359 let current_dir = current.rsplit_once('.').map(|(d, _)| d).unwrap_or("");
361 let candidate = if current_dir.is_empty() {
362 joined.clone()
363 } else {
364 format!("{current_dir}.{joined}")
365 };
366 if self.graph.modules.contains_key(&candidate) {
367 return Ok(candidate);
368 }
369 Err(ResolveError::ModuleNotFound { path: joined })
370 }
371
372 fn lookup_export(&self, module: &str, name: &str) -> Result<&Symbol, ResolveError> {
373 let key = SymbolKey {
374 module: module.to_string(),
375 name: name.to_string(),
376 };
377 let sym = self
378 .global
379 .get(&key)
380 .ok_or_else(|| ResolveError::NotExported {
381 name: name.to_string(),
382 module: module.to_string(),
383 span: Span::default(),
384 })?;
385 if !sym.is_exported() {
386 return Err(ResolveError::PrivateImport {
387 name: name.to_string(),
388 module: module.to_string(),
389 span: sym.span,
390 });
391 }
392 Ok(sym)
393 }
394
395 fn check_module_references(
396 &self,
397 current: &str,
398 file: &SourceFile,
399 imports: &[ResolvedBinding],
400 ) -> Result<(), ResolveError> {
401 let scope: HashMap<String, SymbolKey> = imports
402 .iter()
403 .map(|b| (b.local_name.clone(), b.symbol.clone()))
404 .collect();
405 for item in &file.items {
406 match item {
407 Item::Function(f) => {
408 let mut local = scope_with_generics(&scope, &f.generics);
409 for p in &f.params {
410 local.insert(
411 p.name.name.clone(),
412 SymbolKey {
413 module: "_param".to_string(),
414 name: p.name.name.clone(),
415 },
416 );
417 if let Some(ty) = &p.ty {
418 self.check_type(&local, ty)?;
419 }
420 }
421 if let Some(ty) = &f.ret_type {
422 self.check_type(&local, ty)?;
423 }
424 self.check_expr(&local, &f.body)?;
425 }
426 Item::TypeDef(t) => {
427 let local = scope_with_generics(&scope, &t.generics);
428 self.check_type_def(&local, t)?;
429 }
430 Item::Const(c) => self.check_expr(&scope, &c.value)?,
431 Item::Test(t) => self.check_block(&scope, &t.body)?,
432 Item::TestCompileFail(_) => {}
433 Item::Impl(i) => {
434 if let Some(tn) = &i.trait_name {
435 self.check_name(&scope, &tn.name, tn.span)?;
436 }
437 for arg in &i.trait_args {
438 self.check_type(&scope, arg)?;
439 }
440 self.check_type(&scope, &i.ty)?;
441 for f in &i.items {
442 let mut local = scope_with_generics(&scope, &f.generics);
443 for p in &f.params {
444 local.insert(
445 p.name.name.clone(),
446 SymbolKey {
447 module: "_param".to_string(),
448 name: p.name.name.clone(),
449 },
450 );
451 if let Some(ty) = &p.ty {
452 self.check_type(&local, ty)?;
453 }
454 }
455 if let Some(ty) = &f.ret_type {
456 self.check_type(&local, ty)?;
457 }
458 self.check_expr(&local, &f.body)?;
459 }
460 }
461 Item::TraitDef(t) => {
462 let trait_scope = scope_with_generics(&scope, &t.generics);
463 for item in &t.items {
464 let mut local = trait_scope.clone();
465 for p in &item.params {
466 local.insert(
467 p.name.name.clone(),
468 SymbolKey {
469 module: "_param".to_string(),
470 name: p.name.name.clone(),
471 },
472 );
473 if let Some(ty) = &p.ty {
474 self.check_type(&local, ty)?;
475 }
476 }
477 if let Some(ty) = &item.ret_type {
478 self.check_type(&local, ty)?;
479 }
480 if let Some(body) = &item.default_body {
481 self.check_expr(&local, body)?;
482 }
483 }
484 }
485 Item::ShapeDef(s) => {
486 let local = scope_with_generics(&scope, &s.generics);
487 for f in &s.fields {
488 match f {
489 crisp_ast::item::ShapeField::Data { ty, .. } => {
490 self.check_type(&local, ty)?;
491 }
492 crisp_ast::item::ShapeField::Method {
493 params, ret_type, ..
494 } => {
495 for p in params {
496 if let Some(ty) = &p.ty {
497 self.check_type(&local, ty)?;
498 }
499 }
500 self.check_type(&local, ret_type)?;
501 }
502 }
503 }
504 }
505 Item::Use(_) | Item::Extern(_) => {}
506 }
507 }
508 let _ = current;
509 Ok(())
510 }
511
512 fn check_type_def(
513 &self,
514 scope: &HashMap<String, SymbolKey>,
515 t: &crisp_ast::item::TypeDef,
516 ) -> Result<(), ResolveError> {
517 use crisp_ast::item::TypeBody;
518 match &t.body {
519 TypeBody::Struct(fields) => {
520 for f in fields {
521 self.check_type(scope, &f.ty)?;
522 if let Some(def) = &f.default {
523 self.check_expr(scope, def)?;
524 }
525 }
526 }
527 TypeBody::Enum(variants) => {
528 for v in variants {
529 for ty in &v.fields {
530 self.check_type(scope, ty)?;
531 }
532 }
533 }
534 TypeBody::Alias(ty) => self.check_type(scope, ty)?,
535 }
536 Ok(())
537 }
538
539 fn check_type(
540 &self,
541 scope: &HashMap<String, SymbolKey>,
542 ty: &Type,
543 ) -> Result<(), ResolveError> {
544 match &ty.kind {
545 TypeKind::Named(id) => {
546 self.check_name(scope, &id.name, id.span)?;
547 self.reject_shape_type(scope, &id.name, id.span)
548 }
549 TypeKind::Option(inner) | TypeKind::Slice(inner) | TypeKind::Ref { inner, .. } => {
550 self.check_type(scope, inner)
551 }
552 TypeKind::Tuple(types) => {
553 for t in types {
554 self.check_type(scope, t)?;
555 }
556 Ok(())
557 }
558 TypeKind::Array { elem, .. } => self.check_type(scope, elem),
559 TypeKind::Fn { params, ret } => {
560 for p in params {
561 self.check_type(scope, p)?;
562 }
563 self.check_type(scope, ret)
564 }
565 TypeKind::Constrained { inner, bounds } => {
566 for b in bounds {
567 match b {
568 TypeBound::Shape(id) => {
569 self.check_name(scope, &id.name, id.span)?;
570 if let Some(key) = scope.get(&id.name)
572 && let Some(sym) = self.global.get(key)
573 && sym.kind != SymbolKind::Shape
574 {
575 return Err(ResolveError::UnresolvedName {
576 name: id.name.clone(),
577 span: id.span,
578 message: format!(
579 "[E0035] `{name}` is not a shape",
580 name = id.name
581 ),
582 hint: Some("shape bounds require a `shape` definition".into()),
583 });
584 }
585 }
586 TypeBound::Trait(id) => {
587 self.check_name(scope, &id.name, id.span)?;
588 }
589 }
590 }
591 self.check_type(scope, inner)
592 }
593 TypeKind::Never | TypeKind::Unit => Ok(()),
594 TypeKind::Generic { base, args } => {
595 self.check_type(scope, base)?;
596 for a in args {
597 self.check_type(scope, a)?;
598 }
599 Ok(())
600 }
601 }
602 }
603
604 fn check_block(
605 &self,
606 scope: &HashMap<String, SymbolKey>,
607 block: &Block,
608 ) -> Result<(), ResolveError> {
609 let mut local = scope.clone();
610 for stmt in &block.stmts {
611 match stmt {
612 Stmt::Bind { pat, value, .. } => {
613 self.check_expr(&local, value)?;
614 self.bind_pat(&mut local, pat)?;
615 }
616 Stmt::Assign { target, value } => {
617 self.check_name(&local, &target.name, target.span)?;
618 self.check_expr(&local, value)?;
619 }
620 Stmt::Expr(e) => self.check_expr(&local, e)?,
621 }
622 }
623 if let Some(tail) = &block.tail {
624 self.check_expr(&local, tail)?;
625 }
626 Ok(())
627 }
628
629 fn bind_pat(
630 &self,
631 scope: &mut HashMap<String, SymbolKey>,
632 pat: &Pat,
633 ) -> Result<(), ResolveError> {
634 match &pat.kind {
635 PatKind::Ident(id) => {
636 scope.insert(
637 id.name.clone(),
638 SymbolKey {
639 module: "_local".to_string(),
640 name: id.name.clone(),
641 },
642 );
643 }
644 PatKind::Wildcard => {}
645 PatKind::Tuple(pats) => {
646 for p in pats {
647 self.bind_pat(scope, p)?;
648 }
649 }
650 PatKind::Slice { prefix, rest } => {
651 for p in prefix {
652 self.bind_pat(scope, p)?;
653 }
654 if let Some(id) = rest {
655 scope.insert(
656 id.name.clone(),
657 SymbolKey {
658 module: "_local".to_string(),
659 name: id.name.clone(),
660 },
661 );
662 }
663 }
664 PatKind::Struct { fields, .. } => {
665 for f in fields {
666 if let Some(p) = &f.pat {
667 self.bind_pat(scope, p)?;
668 }
669 }
670 }
671 PatKind::Enum { args, .. } => {
672 for p in args {
673 self.bind_pat(scope, p)?;
674 }
675 }
676 PatKind::Literal(_) => {}
677 PatKind::Type { inner, .. } => self.bind_pat(scope, inner)?,
678 }
679 Ok(())
680 }
681
682 fn check_expr(
683 &self,
684 scope: &HashMap<String, SymbolKey>,
685 expr: &Expr,
686 ) -> Result<(), ResolveError> {
687 match &expr.kind {
688 ExprKind::Ident(id) if crisp_ast::is_hole_ident(&id.name) => Ok(()),
689 ExprKind::Ident(id) => self.check_name(scope, &id.name, id.span),
690 ExprKind::Block(b) => self.check_block(scope, b),
691 ExprKind::If {
692 cond,
693 then_branch,
694 else_branch,
695 } => {
696 self.check_expr(scope, cond)?;
697 self.check_expr(scope, then_branch)?;
698 if let Some(e) = else_branch {
699 self.check_expr(scope, e)?;
700 }
701 Ok(())
702 }
703 ExprKind::Match { scrutinee, arms } => {
704 self.check_expr(scope, scrutinee)?;
705 let mut local = scope.clone();
706 for arm in arms {
707 self.bind_pat(&mut local, &arm.pat)?;
708 if let Some(g) = &arm.guard {
709 self.check_expr(&local, g)?;
710 }
711 self.check_expr(&local, &arm.body)?;
712 }
713 Ok(())
714 }
715 ExprKind::For { pat, iter, body } => {
716 self.check_expr(scope, iter)?;
717 let mut local = scope.clone();
718 self.bind_pat(&mut local, pat)?;
719 self.check_expr(&local, body)
720 }
721 ExprKind::While { cond, body } => {
722 self.check_expr(scope, cond)?;
723 self.check_expr(scope, body)
724 }
725 ExprKind::Loop(body)
726 | ExprKind::Async(body)
727 | ExprKind::Await(body)
728 | ExprKind::Spawn(body)
729 | ExprKind::Unsafe(body)
730 | ExprKind::Try(body) => self.check_expr(scope, body),
731 ExprKind::Break(Some(v)) => self.check_expr(scope, v),
732 ExprKind::Lambda { params, body } => {
733 let mut local = scope.clone();
734 for p in params {
735 local.insert(
736 p.name.name.clone(),
737 SymbolKey {
738 module: "_local".to_string(),
739 name: p.name.name.clone(),
740 },
741 );
742 if let Some(ty) = &p.ty {
743 self.check_type(&local, ty)?;
744 }
745 }
746 self.check_expr(&local, body)
747 }
748 ExprKind::Call { func, args } => {
749 self.check_expr(scope, func)?;
750 for a in args {
751 self.check_expr(scope, a)?;
752 }
753 Ok(())
754 }
755 ExprKind::MethodCall { receiver, args, .. } => {
756 self.check_expr(scope, receiver)?;
757 for a in args {
758 self.check_expr(scope, a)?;
759 }
760 Ok(())
761 }
762 ExprKind::Field { base, .. } => self.check_expr(scope, base),
763 ExprKind::Index { base, index } => {
764 self.check_expr(scope, base)?;
765 self.check_expr(scope, index)
766 }
767 ExprKind::IndexAssign { base, index, value } => {
768 self.check_expr(scope, base)?;
769 self.check_expr(scope, index)?;
770 self.check_expr(scope, value)
771 }
772 ExprKind::Array(elems) => {
773 for e in elems {
774 self.check_expr(scope, e)?;
775 }
776 Ok(())
777 }
778 ExprKind::Unary { expr, .. }
779 | ExprKind::Cast { expr, .. }
780 | ExprKind::Throw(expr)
781 | ExprKind::Return(Some(expr)) => self.check_expr(scope, expr),
782 ExprKind::Binary { left, right, .. } | ExprKind::Pipe { left, right, .. } => {
783 self.check_expr(scope, left)?;
784 self.check_expr(scope, right)
785 }
786 ExprKind::Assign { target, value } => {
787 self.check_name(scope, &target.name, target.span)?;
788 self.check_expr(scope, value)
789 }
790 ExprKind::Bind { pat, value, .. } => {
791 self.check_expr(scope, value)?;
792 let mut local = scope.clone();
793 self.bind_pat(&mut local, pat)
794 }
795 ExprKind::StructLit { name, fields } => {
796 self.check_name(scope, &name.name, name.span)?;
797 for f in fields {
798 self.check_expr(scope, &f.value)?;
799 }
800 Ok(())
801 }
802 ExprKind::Str(parts) => {
803 for part in &parts.0 {
804 if let crisp_ast::expr::StringPart::Expr(e) = part {
805 self.check_expr(scope, e)?;
806 }
807 }
808 Ok(())
809 }
810 ExprKind::Catch { body, arms } => {
811 self.check_expr(scope, body)?;
812 let mut local = scope.clone();
813 for arm in arms {
814 self.bind_pat(&mut local, &arm.pat)?;
815 self.check_expr(&local, &arm.body)?;
816 }
817 Ok(())
818 }
819 ExprKind::Int(_)
820 | ExprKind::Float(_)
821 | ExprKind::Bool(_)
822 | ExprKind::Char(_)
823 | ExprKind::Unit
824 | ExprKind::Break(None)
825 | ExprKind::Continue
826 | ExprKind::Return(None) => Ok(()),
827 }
828 }
829
830 fn check_name(
831 &self,
832 scope: &HashMap<String, SymbolKey>,
833 name: &str,
834 span: Span,
835 ) -> Result<(), ResolveError> {
836 if scope.contains_key(name) {
837 return Ok(());
838 }
839 if name == "Self" || name.starts_with('_') {
840 return Ok(());
841 }
842 let hint = self.unresolved_hint(scope, name);
843 let message = match &hint {
844 Some(h) => format!("[E0035] unresolved name `{name}`\nhelp: {h}"),
845 None => format!("[E0035] unresolved name `{name}`"),
846 };
847 Err(ResolveError::UnresolvedName {
848 name: name.to_string(),
849 span,
850 message,
851 hint,
852 })
853 }
854
855 fn unresolved_hint(&self, scope: &HashMap<String, SymbolKey>, name: &str) -> Option<String> {
856 let _ = scope;
857 let mut modules: Vec<&str> = self
858 .global
859 .values()
860 .filter(|s| s.key.name == name && !s.from_prelude)
861 .map(|s| s.key.module.as_str())
862 .collect();
863 modules.sort_unstable();
864 modules.dedup();
865 if modules.is_empty() {
866 return None;
867 }
868 let module = modules[0];
869 Some(format!(
870 "`{name}` is defined in module `{module}`; add `use {module} {{ {name} }}` \
871(sibling modules are not visible by filename order alone)"
872 ))
873 }
874
875 fn reject_shape_type(
876 &self,
877 _scope: &HashMap<String, SymbolKey>,
878 _name: &str,
879 _span: Span,
880 ) -> Result<(), ResolveError> {
881 Ok(())
883 }
884}
885
886fn scope_with_generics(
887 scope: &HashMap<String, SymbolKey>,
888 generics: &[Ident],
889) -> HashMap<String, SymbolKey> {
890 let mut local = scope.clone();
891 for g in generics {
892 local.insert(
893 g.name.clone(),
894 SymbolKey {
895 module: "_generic".to_string(),
896 name: g.name.clone(),
897 },
898 );
899 }
900 local
901}
902
903fn load_dep_sets(crate_root: &Path) -> Result<(HashSet<String>, HashSet<String>), ResolveError> {
904 let manifest = read_manifest(crate_root).map_err(|e| ResolveError::Manifest {
905 root: crate_root.display().to_string(),
906 message: e.to_string(),
907 })?;
908 let deps = resolve_dependencies(&manifest);
909 let mut rust_deps = HashSet::new();
910 let mut unmarked_deps = HashSet::new();
911 for dep in deps {
912 if dep.rust {
913 rust_deps.insert(dep.name);
914 } else {
915 unmarked_deps.insert(dep.name);
916 }
917 }
918 for (name, spec) in &manifest.dependencies {
919 use crisp_manifest::DependencySpec;
920 match spec {
921 DependencySpec::Version(_) => {
922 unmarked_deps.insert(name.clone());
923 }
924 DependencySpec::Detailed { rust, .. } if !*rust => {
925 unmarked_deps.insert(name.clone());
926 }
927 _ => {}
928 }
929 }
930 Ok((rust_deps, unmarked_deps))
931}