Skip to main content

harn_parser/lexical/
call_resolution.rs

1use std::collections::{HashMap, HashSet};
2
3use crate::ast::{SNode, TypedParam};
4
5use super::{
6    module_scope_node_slices, parameter_scope, BindingId, BindingOwner, LexicalAnalysis,
7    MatchPatternCatalog, Scope,
8};
9
10/// Build the enum catalog with declarations imported into module scope.
11/// Local declarations are registered last because they shadow imported types.
12pub fn module_match_pattern_catalog_with_visible(
13    program: &[SNode],
14    visible_type_declarations: &[SNode],
15) -> MatchPatternCatalog {
16    let mut catalog = MatchPatternCatalog::default();
17    catalog.extend_declarations(visible_type_declarations);
18    for nodes in module_scope_node_slices(program) {
19        catalog.extend_declarations(nodes);
20    }
21    catalog
22}
23
24/// Return identifier-use spans that resolve to any lexical binding.
25///
26/// Unlike `resolved_identifier_bindings`, this includes callable names whose
27/// declaration identity is immaterial to the consumer. Source text lets the
28/// analysis also reach expression holes inside interpolated strings.
29pub fn lexically_resolved_identifier_spans(
30    params: &[TypedParam],
31    body: &[SNode],
32    source: Option<&str>,
33    match_patterns: &MatchPatternCatalog,
34) -> HashSet<(usize, usize)> {
35    analyze_callable(params, body, source, match_patterns).lexically_resolved
36}
37
38/// Resolve exact declarations, including defaults and interpolated expressions.
39pub fn resolved_identifier_bindings_with_source(
40    params: &[TypedParam],
41    body: &[SNode],
42    source: Option<&str>,
43    match_patterns: &MatchPatternCatalog,
44) -> HashMap<(usize, usize), BindingId> {
45    analyze_callable(params, body, source, match_patterns).resolved
46}
47
48fn analyze_callable<'source>(
49    params: &[TypedParam],
50    body: &[SNode],
51    source: Option<&'source str>,
52    match_patterns: &MatchPatternCatalog,
53) -> LexicalAnalysis<'source> {
54    let mut analysis = LexicalAnalysis::new_with_source(match_patterns, source);
55    // Defaults execute left to right: only earlier parameters are visible.
56    let mut visible_params = Scope::new();
57    for param in params {
58        if let Some(default) = &param.default_value {
59            analysis.walk_node(
60                default,
61                std::slice::from_ref(&visible_params),
62                false,
63                &BindingOwner::Current,
64            );
65        }
66        visible_params.extend(parameter_scope(
67            std::slice::from_ref(param),
68            &BindingOwner::Current,
69        ));
70    }
71    analysis.walk_body_with_bindings(
72        body,
73        Vec::new(),
74        false,
75        BindingOwner::Current,
76        parameter_scope(params, &BindingOwner::Current),
77    );
78    analysis
79}