Skip to main content

microcad_lang/resolve/symbol/
mod.rs

1// Copyright © 2025-2026 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4mod iterators;
5mod symbol_definition;
6mod symbol_info;
7mod symbol_inner;
8mod symbol_map;
9mod symbols;
10
11use indexmap::IndexSet;
12pub use iterators::*;
13pub use symbol_definition::*;
14pub use symbol_info::*;
15pub(crate) use symbol_map::*;
16pub(crate) use symbols::*;
17
18use symbol_inner::*;
19
20use crate::{
21    builtin::*, rc::*, resolve::*, src_ref::*, syntax::*, tree_display::*, ty::*, value::*,
22};
23
24/// Symbol
25#[derive(Clone)]
26pub struct Symbol {
27    visibility: std::cell::RefCell<Visibility>,
28    src_ref: SrcRef,
29    inner: RcMut<SymbolInner>,
30}
31
32// creation
33impl Symbol {
34    /// Create new symbol without children.
35    /// # Arguments
36    /// - `visibility`: Visibility of the symbol
37    /// - `def`: Symbol definition
38    /// - `parent`: Symbol's parent symbol or none for root
39    pub(crate) fn new(def: SymbolDef, parent: Option<Symbol>) -> Self {
40        Symbol {
41            visibility: std::cell::RefCell::new(def.visibility()),
42            inner: RcMut::new(SymbolInner {
43                def,
44                parent,
45                ..Default::default()
46            }),
47            ..Default::default()
48        }
49    }
50
51    /// Create new symbol without children.
52    /// # Arguments
53    /// - `visibility`: Visibility of the symbol
54    /// - `def`: Symbol definition
55    /// - `parent`: Symbol's parent symbol or none for root
56    pub(super) fn new_with_visibility(
57        visibility: Visibility,
58        def: SymbolDef,
59        parent: Option<Symbol>,
60    ) -> Self {
61        Symbol {
62            visibility: std::cell::RefCell::new(visibility),
63            inner: RcMut::new(SymbolInner {
64                def,
65                parent,
66                ..Default::default()
67            }),
68            ..Default::default()
69        }
70    }
71
72    /// Create a symbol node for a built-in.
73    /// # Arguments
74    /// - `id`: Name of the symbol
75    /// - `parameters`: Optional parameter list
76    /// - `f`: The builtin function
77    pub(crate) fn new_builtin(builtin: Builtin) -> Symbol {
78        Symbol::new(SymbolDef::Builtin(Rc::new(builtin)), None)
79    }
80
81    /// New builtin function as symbol.
82    pub fn new_builtin_fn(
83        name: &'static str,
84        parameters: impl Iterator<Item = (Identifier, ParameterValue)>,
85        f: &'static BuiltinFn,
86        doc: Option<&'static str>,
87    ) -> Symbol {
88        Self::new_builtin(Builtin {
89            id: Identifier::no_ref(name),
90            parameters: parameters.collect(),
91            kind: BuiltinKind::Function,
92            f,
93            doc: doc.map(DocBlock::new_builtin),
94        })
95    }
96
97    /// Replace inner of a symbol with the inner of another.
98    pub(super) fn replace(&mut self, replacement: Symbol) {
99        replacement
100            .inner
101            .borrow()
102            .children
103            .iter()
104            .for_each(|(_, child)| child.inner.borrow_mut().parent = Some(self.clone()));
105        self.inner.replace(replacement.inner.take());
106    }
107}
108
109impl Symbol {
110    /// Search all ids which require target mode (e.g. `assert_valid`)
111    pub(super) fn search_target_mode_ids(&self) -> IdentifierSet {
112        self.riter()
113            .filter(|symbol| symbol.is_target_mode())
114            .map(|symbol| symbol.id())
115            .collect()
116    }
117
118    /// Return a list of unused private symbols
119    ///
120    /// Use this after eval for any useful result.
121    pub(crate) fn unused_private(&self) -> Symbols {
122        let used_in_module = &mut IndexSet::new();
123        let mut symbols: Symbols = self
124            .riter()
125            .skip(1) // skip root
126            .filter(|symbol| {
127                if let Some(in_module) = symbol.in_module()
128                    && symbol.is_used()
129                {
130                    used_in_module.insert(in_module);
131                }
132                symbol.is_unused_private()
133            })
134            .collect();
135
136        symbols.retain(|symbol| {
137            if let Some(in_module) = symbol.in_module() {
138                !used_in_module.contains(&in_module)
139            } else {
140                true
141            }
142        });
143        symbols.sort_by_key(|s| s.full_name());
144        symbols
145    }
146
147    /// Search a *symbol* by it's *qualified name* **and** within a *symbol* given by name.
148    ///
149    /// If both are found
150    /// # Arguments
151    /// - `name`: *qualified name* to search for.
152    /// - `within`: Searches in the *symbol* with this name too.
153    /// - `target`: What to search for
154    pub(crate) fn lookup_within_name(
155        &self,
156        name: &QualifiedName,
157        within: &QualifiedName,
158        target: LookupTarget,
159    ) -> ResolveResult<Symbol> {
160        self.lookup_within(name, &self.search(within, false)?, target)
161    }
162}
163
164// tree structure
165impl Symbol {
166    /// Get any child with the given `id`.
167    /// # Arguments
168    /// - `id`: Anticipated *id* of the possible child.
169    pub(super) fn get_child(&self, id: &Identifier) -> Option<Symbol> {
170        self.inner.borrow().children.get(id).cloned()
171    }
172
173    /// Add a new symbol to children.
174    pub(crate) fn add_symbol(&mut self, symbol: Symbol) -> ResolveResult<()> {
175        self.insert_symbol(symbol.id(), symbol.clone())
176    }
177
178    /// Add a new symbol to children with specific id.
179    pub(super) fn insert_symbol(&mut self, id: Identifier, symbol: Symbol) -> ResolveResult<()> {
180        log::trace!("insert symbol: {id}");
181        if let Some(symbol) = self.inner.borrow_mut().children.insert(id, symbol.clone()) {
182            Err(ResolveError::SymbolAlreadyDefined(symbol.full_name()))
183        } else {
184            Ok(())
185        }
186    }
187
188    /// Insert child and change parent of child to new parent.
189    /// # Arguments
190    /// - `parent`: New parent symbol (will be changed in child!).
191    /// - `child`: Child to insert
192    pub(crate) fn add_child(parent: &Symbol, child: Symbol) {
193        child.inner.borrow_mut().parent = Some(parent.clone());
194        let id = child.id();
195        parent.inner.borrow_mut().children.insert(id, child);
196    }
197
198    /// Initially set children.
199    ///
200    /// Panics if children already exist.
201    pub(super) fn set_children(&self, new_children: SymbolMap) {
202        assert!(self.inner.borrow().children.is_empty());
203        self.inner.borrow_mut().children = new_children;
204    }
205
206    /// Try to apply a FnMut for each child.
207    pub(crate) fn try_children<E: std::error::Error>(
208        &self,
209        f: impl FnMut((&Identifier, &Symbol)) -> Result<(), E>,
210    ) -> Result<(), E> {
211        self.inner.borrow().children.iter().try_for_each(f)
212    }
213
214    /// Apply a FnMut for each child.
215    pub fn with_children(&self, f: impl FnMut((&Identifier, &Symbol))) {
216        self.inner.borrow().children.iter().for_each(f)
217    }
218
219    /// Create a vector of cloned children.
220    fn public_children(&self, visibility: Visibility, src_ref: SrcRef) -> SymbolMap {
221        let inner = self.inner.borrow();
222
223        inner
224            .children
225            .values()
226            .filter(|symbol| {
227                if symbol.is_public() {
228                    true
229                } else {
230                    log::trace!("Skipping private symbol:\n{symbol:?}");
231                    false
232                }
233            })
234            .map(|symbol| symbol.clone_with(visibility.clone(), src_ref.clone()))
235            .map(|symbol| (symbol.id(), symbol))
236            .collect()
237    }
238
239    /// True if symbol has any children
240    pub(crate) fn is_empty(&self) -> bool {
241        self.inner.borrow().children.is_empty()
242    }
243
244    /// Get parent symbol.
245    pub(crate) fn get_parent(&self) -> Option<Symbol> {
246        self.inner.borrow().parent.clone()
247    }
248
249    /// Set new parent.
250    pub(super) fn set_parent(&mut self, parent: Symbol) {
251        self.inner.borrow_mut().parent = Some(parent);
252    }
253
254    /// Return iterator over symbol's children.
255    pub fn iter(&self) -> Children {
256        Children::new(self.clone())
257    }
258
259    /// Iterate recursively
260    pub fn riter(&self) -> RecurseChildren {
261        RecurseChildren::new(self.clone())
262    }
263}
264
265// visibility
266impl Symbol {
267    /// Return `true` if symbol's visibility is private
268    pub(super) fn visibility(&self) -> Visibility {
269        self.visibility.borrow().clone()
270    }
271
272    /// Return `true` if symbol's visibility set to is public.
273    pub(super) fn is_public(&self) -> bool {
274        matches!(self.visibility(), Visibility::Public)
275    }
276
277    pub(super) fn is_deleted(&self) -> bool {
278        matches!(self.visibility(), Visibility::Deleted)
279    }
280
281    pub(super) fn delete(&self) {
282        self.visibility.replace(Visibility::Deleted);
283    }
284
285    /// Clone this symbol but give the clone another visibility.
286    pub(crate) fn clone_with(&self, visibility: Visibility, src_ref: SrcRef) -> Self {
287        Self {
288            visibility: std::cell::RefCell::new(visibility),
289            src_ref,
290            inner: self.inner.clone(),
291        }
292    }
293
294    pub(crate) fn reset_visibility(&self) {
295        self.visibility
296            .replace(self.with_def(|def| def.visibility()));
297    }
298}
299
300// definition dependent
301impl Symbol {
302    /// Return the internal *id* of this symbol.
303    pub(crate) fn id(&self) -> Identifier {
304        self.inner.borrow().def.id()
305    }
306
307    /// check if a private symbol may be declared within this symbol
308    pub(super) fn can_const(&self) -> bool {
309        matches!(
310            self.inner.borrow().def,
311            SymbolDef::Module(..) | SymbolDef::SourceFile(..) | SymbolDef::Workbench(..)
312        )
313    }
314
315    /// check if a value on the stack may be declared within this symbol
316    pub(super) fn can_value(&self) -> bool {
317        matches!(
318            self.inner.borrow().def,
319            SymbolDef::Function(..) | SymbolDef::Workbench(..) | SymbolDef::SourceFile(..)
320        )
321    }
322
323    /// check if a property may be declared within this symbol
324    pub(super) fn can_prop(&self) -> bool {
325        matches!(self.inner.borrow().def, SymbolDef::Workbench(..))
326    }
327
328    /// check if a public assigment may be declared within this symbol
329    pub(super) fn can_public(&self) -> bool {
330        matches!(self.inner.borrow().def, SymbolDef::Module(..) | SymbolDef::SourceFile(..))
331    }
332
333    fn is_root(&self) -> bool {
334        matches!(self.inner.borrow().def, SymbolDef::Root)
335    }
336
337    pub(crate) fn is_source(&self) -> bool {
338        matches!(self.inner.borrow().def, SymbolDef::SourceFile(..))
339    }
340
341    pub(crate) fn is_module(&self) -> bool {
342        matches!(
343            self.inner.borrow().def,
344            SymbolDef::SourceFile(..) | SymbolDef::Module(..)
345        )
346    }
347
348    pub(crate) fn is_workbench(&self) -> bool {
349        matches!(self.inner.borrow().def, SymbolDef::Workbench(..))
350    }
351
352    /// Overwrite any value in this symbol
353    pub(crate) fn set_value(&self, new_value: Value) -> ResolveResult<()> {
354        let is_a_value = match &mut self.inner.borrow_mut().def {
355            SymbolDef::Constant(.., value) => {
356                *value = new_value;
357                true
358            }
359            _ => false,
360        };
361        match is_a_value {
362            true => Ok(()),
363            false => Err(ResolveError::NotAValue(self.full_name())),
364        }
365    }
366
367    /// Return file path of top level parent source file.
368    pub(super) fn source_path(&self) -> Option<std::path::PathBuf> {
369        if let SymbolDef::SourceFile(source_file) = &self.inner.borrow().def {
370            return source_file
371                .filename()
372                .parent()
373                .map(|path| path.to_path_buf());
374        }
375        self.get_parent().and_then(|parent| parent.source_path())
376    }
377
378    pub(super) fn is_resolvable(&self) -> bool {
379        matches!(
380            self.inner.borrow().def,
381            SymbolDef::SourceFile(..)
382                | SymbolDef::Module(..)
383                | SymbolDef::Workbench(..)
384                | SymbolDef::UseAll(..)
385                | SymbolDef::Alias(..)
386        ) && !self.is_deleted()
387    }
388
389    pub(super) fn is_link(&self) -> bool {
390        matches!(
391            self.inner.borrow().def,
392            SymbolDef::UseAll(..) | SymbolDef::Alias(..)
393        )
394    }
395
396    pub(super) fn is_alias(&self) -> bool {
397        matches!(self.inner.borrow().def, SymbolDef::Alias(..))
398    }
399
400    pub(super) fn get_link(&self) -> Option<QualifiedName> {
401        self.with_def(|def| match def {
402            SymbolDef::UseAll(_, name) | SymbolDef::Alias(.., name) => Some(name.clone()),
403            _ => None,
404        })
405    }
406
407    pub(super) fn has_links(&self) -> bool {
408        if self.is_link() {
409            true
410        } else {
411            self.inner
412                .borrow()
413                .children
414                .values()
415                .filter(|symbol| !symbol.is_deleted())
416                .any(|symbol| symbol.has_links())
417        }
418    }
419
420    /// Work with the symbol definition.
421    pub(crate) fn with_def<T>(&self, mut f: impl FnMut(&SymbolDef) -> T) -> T {
422        f(&self.inner.borrow().def)
423    }
424
425    /// Work with the mutable symbol definition.
426    pub(crate) fn with_def_mut<T>(&self, mut f: impl FnMut(&mut SymbolDef) -> T) -> T {
427        f(&mut self.inner.borrow_mut().def)
428    }
429}
430
431// check
432impl Symbol {
433    /// Mark this symbol as *checked*.
434    pub(super) fn set_check(&self) {
435        let _ = self.inner.borrow().checked.set(());
436    }
437
438    pub(super) fn is_checked(&self) -> bool {
439        self.inner.borrow().checked.get().is_some()
440    }
441
442    /// check names in symbol definition
443    pub(super) fn check(
444        &self,
445        context: &mut ResolveContext,
446        exclude_ids: &IdentifierSet,
447    ) -> ResolveResult<()> {
448        if !matches!(self.visibility.take(), Visibility::Deleted) {
449            // get names of symbol definitions
450            let names = match &self.inner.borrow().def {
451                SymbolDef::SourceFile(sf) => sf.names(),
452                SymbolDef::Module(m) => m.names(),
453                SymbolDef::Workbench(wb) => wb.names(),
454                SymbolDef::Function(f) => f.names(),
455                SymbolDef::Assignment(a) => a.names(),
456                SymbolDef::Alias(_, _, name) | SymbolDef::UseAll(_, name) => {
457                    log::error!("Resolve Context:\n{context:?}");
458                    return Err(ResolveError::ResolveCheckFailed(name.src_ref()));
459                }
460                _ => Default::default(),
461            };
462
463            if !names.is_empty() {
464                log::debug!("checking symbols:\n{names:?}");
465                // lookup names
466                names
467                    .iter()
468                    .filter(|name| {
469                        exclude_ids.contains(name.last().expect("symbol with empty name"))
470                    })
471                    // search in symbol table
472                    .try_for_each(|name| {
473                        match context.root.lookup(name, LookupTarget::Any) {
474                            Ok(_) => Ok::<_, ResolveError>(()),
475                            Err(err) => {
476                                // get name of current module
477                                let module = match context.root.search(&self.module_name(), false) {
478                                    Ok(module) => module,
479                                    Err(err) => {
480                                        context.error(&self.id(), err)?;
481                                        return Ok(());
482                                    }
483                                };
484                                // search within current module
485                                if context
486                                    .root
487                                    .lookup_within(name, &module, LookupTarget::Module)
488                                    .is_err()
489                                {
490                                    context.error(name, err)?;
491                                }
492                                Ok(())
493                            }
494                        }
495                    })?;
496            }
497
498            // check children
499            let children = self.inner.borrow().children.clone();
500            children
501                .values()
502                .try_for_each(|symbol| symbol.check(context, exclude_ids))
503        } else {
504            Ok(())
505        }
506    }
507
508    fn module_name(&self) -> QualifiedName {
509        match self.is_module() {
510            true => {
511                if let Some(parent) = &self.get_parent() {
512                    parent.module_name().with_suffix(&self.id())
513                } else {
514                    QualifiedName::from_id(self.id())
515                }
516            }
517            false => {
518                if let Some(parent) = &self.get_parent() {
519                    parent.module_name()
520                } else {
521                    unreachable!("root must be source file")
522                }
523            }
524        }
525    }
526
527    pub(crate) fn kind_str(&self) -> &'static str {
528        self.inner.borrow().def.kind_str()
529    }
530
531    pub(super) fn source_hash(&self) -> u64 {
532        self.inner.borrow().def.source_hash()
533    }
534}
535
536impl Symbol {
537    pub(crate) fn is_used(&self) -> bool {
538        self.inner.borrow().used.get().is_some()
539    }
540
541    /// Mark this symbol as *used*.
542    pub(crate) fn set_used(&self) {
543        let _ = self.inner.borrow().used.set(());
544    }
545
546    pub(crate) fn is_unused_private(&self) -> bool {
547        !self.is_used() && !self.is_public() && !self.is_deleted()
548    }
549
550    pub(crate) fn in_module(&self) -> Option<QualifiedName> {
551        if let Visibility::PrivateUse(module) = self.visibility() {
552            Some(module.clone())
553        } else {
554            None
555        }
556    }
557
558    /// Resolve aliases and use statements in this symbol.
559    pub(super) fn resolve(&self, context: &mut ResolveContext) -> ResolveResult<SymbolMap> {
560        log::trace!("resolving: {self}");
561
562        // retrieve symbols from any use statements
563        let mut from_self = {
564            let inner = self.inner.borrow();
565            match &inner.def {
566                SymbolDef::Alias(visibility, id, name) => {
567                    log::trace!("resolving use (as): {self} => {visibility}{id} ({name})");
568                    let symbol = context
569                        .root
570                        .lookup_within_opt(name, &inner.parent, LookupTarget::Any)?
571                        .clone_with(visibility.clone(), name.src_ref.clone());
572                    self.delete();
573                    [(id.clone(), symbol)].into_iter().collect()
574                }
575                SymbolDef::UseAll(visibility, name) => {
576                    let visibility = &if matches!(visibility, &Visibility::Private) {
577                        Visibility::PrivateUse(name.clone())
578                    } else {
579                        visibility.clone()
580                    };
581                    log::trace!("resolving use all: {self} => {visibility}{name}");
582                    let symbols = context
583                        .root
584                        .lookup_within_opt(name, &inner.parent, LookupTarget::Any)?
585                        .public_children(visibility.clone(), name.src_ref.clone());
586                    if !symbols.is_empty() {
587                        self.delete();
588                    }
589                    symbols
590                }
591                // skip others
592                _ => SymbolMap::default(),
593            }
594        };
595
596        let resolved = from_self.resolve_all(context)?;
597        from_self.extend(resolved.iter().map(|(k, v)| (k.clone(), v.clone())));
598        // collect symbols resolved from children
599        let from_children = self.inner.borrow().children.resolve_all(context)?;
600        self.inner
601            .borrow_mut()
602            .children
603            .extend(from_children.iter().map(|(k, v)| (k.clone(), v.clone())));
604        // return symbols collected from self
605        Ok(from_self)
606    }
607
608    /// Returns `true` if builtin symbol uses parameter of type Name
609    ///
610    /// (for assert_valid() and assert_invalid())
611    pub(crate) fn is_target_mode(&self) -> bool {
612        self.with_def(|def| match def {
613            SymbolDef::Builtin(builtin) => builtin
614                .parameters
615                .values()
616                .any(|param| param.ty() == Type::Target),
617            _ => false,
618        })
619    }
620
621    /// Search down the symbol tree for a qualified name.
622    /// # Arguments
623    /// - `name`: Name to search for.
624    pub(crate) fn search(&self, name: &QualifiedName, respect: bool) -> ResolveResult<Symbol> {
625        log::trace!("Searching {name} in {:?}", self.full_name());
626        if let Some(id) = name.first() {
627            if id.is_super() {
628                if let Some(parent) = self.get_parent() {
629                    return parent.search(&name[1..].iter().cloned().collect(), respect);
630                }
631            }
632        }
633        self.search_inner(name, true, respect)
634    }
635
636    fn search_inner(
637        &self,
638        name: &QualifiedName,
639        top_level: bool,
640        respect: bool,
641    ) -> ResolveResult<Symbol> {
642        if let Some(first) = name.first() {
643            if let Some(child) = self.get_child(first) {
644                if respect && !top_level && !child.is_public() {
645                    log::trace!("Symbol {:?} is private", child.full_name());
646                    Err(ResolveError::SymbolIsPrivate(child.full_name().clone()))
647                } else if name.is_single_identifier() && !child.is_deleted() {
648                    log::trace!("Found {name:?} in {:?}", self.full_name());
649                    self.set_used();
650                    Ok(child.clone())
651                } else {
652                    let name = &name.remove_first();
653                    child.search_inner(name, false, respect)
654                }
655            } else {
656                log::trace!("No child in {:?} while searching for {name:?}", self.id());
657                Err(ResolveError::SymbolNotFound(name.clone()))
658            }
659        } else {
660            log::warn!("Cannot search for an anonymous name");
661            Err(ResolveError::SymbolNotFound(name.clone()))
662        }
663    }
664
665    /// Print out symbols from that point.
666    /// # Arguments
667    /// - `f`: Output formatter
668    /// - `id`: Overwrite symbol's internal `id` with this one if given (e.g. when using in a map).
669    /// - `state`: TreeState
670    pub(super) fn print_symbol(
671        &self,
672        f: &mut impl std::fmt::Write,
673        id: Option<&Identifier>,
674        state: TreeState,
675        children: bool,
676    ) -> std::fmt::Result {
677        let self_id = &self.id();
678        let id = id.unwrap_or(self_id);
679        let def = &self.inner.borrow().def;
680        let full_name = self.full_name();
681        let visibility = self.visibility();
682        let hash = self.source_hash();
683        let depth = state.depth;
684        if state.debug && cfg!(feature = "ansi-color") {
685            let checked = if self.is_checked() { " ✓" } else { "" };
686            if self.is_used() {
687                write!(
688                    f,
689                    "{:depth$}{visibility:?}{id:?} {def:?} [{full_name:?}] #{hash:#x}{checked}",
690                    "",
691                )?;
692            } else {
693                color_print::cwrite!(
694                    f,
695                    "{:depth$}<#606060>{visibility:?}{id:?} {def:?} [{full_name:?}] #{hash:#x}</>{checked}",
696                    "",
697                )?;
698            }
699        } else {
700            write!(f, "{:depth$}{id} {def} [{full_name}]", "",)?;
701        }
702        if children {
703            writeln!(f)?;
704            self.try_children(|(id, child)| {
705                child.print_symbol(f, Some(id), state.indented(), true)
706            })?;
707        }
708        Ok(())
709    }
710
711    pub(super) fn set_src_ref(&mut self, src_ref: SrcRef) {
712        self.src_ref = src_ref;
713    }
714}
715
716impl FullyQualify for Symbol {
717    /// Get fully qualified name.
718    fn full_name(&self) -> QualifiedName {
719        let id = self.id();
720        match &self.get_parent() {
721            Some(parent) => {
722                let mut name = parent.full_name();
723                name.push(id);
724                name
725            }
726
727            None => {
728                let src_ref = id.src_ref();
729                QualifiedName::new(vec![id], src_ref)
730            }
731        }
732    }
733}
734
735impl SrcReferrer for Symbol {
736    fn src_ref(&self) -> SrcRef {
737        if self.src_ref.is_none() {
738            self.inner.borrow().src_ref()
739        } else {
740            self.src_ref.clone()
741        }
742    }
743}
744
745impl Default for Symbol {
746    fn default() -> Self {
747        Self {
748            src_ref: SrcRef(None),
749            visibility: std::cell::RefCell::new(Visibility::default()),
750            inner: RcMut::new(Default::default()),
751        }
752    }
753}
754
755impl PartialEq for Symbol {
756    fn eq(&self, other: &Self) -> bool {
757        // just compare the pointers - not the content
758        self.inner.as_ptr() == other.inner.as_ptr()
759    }
760}
761
762impl std::fmt::Display for Symbol {
763    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
764        self.print_symbol(f, None, TreeState::new_display(), false)
765    }
766}
767
768impl std::fmt::Debug for Symbol {
769    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
770        self.tree_print(f, TreeState::new_debug(0))
771    }
772}
773
774impl Info for Symbol {
775    fn info(&self) -> SymbolInfo {
776        self.with_def(|def| def.info())
777    }
778}
779
780impl TreeDisplay for Symbol {
781    fn tree_print(&self, f: &mut std::fmt::Formatter, state: TreeState) -> std::fmt::Result {
782        if self.is_root() {
783            self.try_children(|(_, symbol)| symbol.tree_print(f, TreeState::new_debug(1)))
784        } else {
785            self.print_symbol(f, Some(&self.id()), state, true)
786        }
787    }
788}
789
790#[test]
791fn test_symbol_resolve() {
792    let root = SourceFile::load_from_str(
793        Some("root"),
794        "",
795        "
796        use my; 
797        x = my::target;
798
799        use my::target; 
800        x = target;
801        ",
802    )
803    .expect("parse error");
804
805    let my = SourceFile::load_from_str(
806        Some("my"),
807        "",
808        "
809        pub const target = 1;
810        ",
811    )
812    .expect("parse error");
813
814    let mut context =
815        ResolveContext::test_create(root, ResolveMode::Symbolized).expect("resolve error");
816    context.test_add_file(my);
817    log::trace!("{context:?}");
818    context.resolve().expect("resolve error");
819}
820
821impl Lookup for Symbol {
822    /// Lookup a symbol from global symbols.
823    fn lookup(&self, name: &QualifiedName, target: LookupTarget) -> ResolveResult<Symbol> {
824        log::trace!(
825            "{lookup} for global symbol '{name:?}'",
826            lookup = crate::mark!(LOOKUP)
827        );
828        self.deny_super(name)?;
829
830        let symbol = match self.search(name, true) {
831            Ok(symbol) => {
832                if target.matches(&symbol) {
833                    symbol
834                } else {
835                    log::trace!(
836                        "{not_found} global symbol: {name:?}",
837                        not_found = crate::mark!(NOT_FOUND),
838                    );
839                    return Err(ResolveError::WrongTarget);
840                }
841            }
842            Err(err) => {
843                log::trace!(
844                    "{not_found} global symbol: {name:?}",
845                    not_found = crate::mark!(NOT_FOUND),
846                );
847                return Err(err)?;
848            }
849        };
850        symbol.set_check();
851        log::trace!(
852            "{found} global symbol: {symbol:?}",
853            found = crate::mark!(FOUND),
854        );
855        Ok(symbol)
856    }
857
858    fn ambiguity_error(ambiguous: QualifiedName, others: QualifiedNames) -> ResolveError {
859        ResolveError::AmbiguousSymbol(ambiguous, others)
860    }
861}