Skip to main content

syn_sem/semantic/analyze/
semantics.rs

1use super::{
2    find_known::KnownLibFinder,
3    handler,
4    infer_eval::Inspector,
5    resolve::shorten_chain,
6    task::{GenerateTask, Task, TaskId, TaskItem, TaskQueue, TaskResolveUse},
7};
8use crate::{
9    ds::vec::BoxedSlice,
10    etc::{abs_fs::AbstractFiles, known, util::FiniteLoop},
11    semantic::{
12        entry::{context::ConfigLoad, GlobalCx},
13        eval::Evaluated,
14        logic::Logic,
15        tree::{PathId, PrivItem, PrivPathTree, PubPathTree, SynToPath, Type, TypeId, TypeScalar},
16    },
17    syntax::{common::SynId, SyntaxTree},
18    Result, TriError, TriResult, TriResultHelper,
19};
20use std::{
21    iter,
22    path::{Path as StdPath, PathBuf},
23    sync::Arc,
24};
25
26pub struct Analyzer<'gcx> {
27    gcx: &'gcx GlobalCx<'gcx>,
28    files: AbstractFiles,
29    stree: SyntaxTree,
30    ptree: PubPathTree<'gcx>,
31    s2p: SynToPath,
32    evaluated: Evaluated<'gcx>,
33    logic: Logic<'gcx>,
34    type_inspector: Inspector<'gcx>,
35    known_finder: KnownLibFinder,
36    tasks: TaskQueue<'gcx>,
37}
38
39impl<'gcx> Analyzer<'gcx> {
40    pub fn new(gcx: &'gcx GlobalCx<'gcx>) -> Self {
41        Self {
42            gcx,
43            files: AbstractFiles::default(),
44            stree: SyntaxTree::new(),
45            ptree: PubPathTree::new(PrivPathTree::new()),
46            s2p: SynToPath::new(),
47            evaluated: Evaluated::new(),
48            logic: Logic::new(gcx),
49            type_inspector: Inspector::new(gcx),
50            known_finder: KnownLibFinder::new(),
51            tasks: TaskQueue::new(),
52        }
53    }
54
55    pub fn get_global_context(&self) -> &GlobalCx<'gcx> {
56        self.gcx
57    }
58
59    pub fn add_virtual_file<P, C>(&mut self, path: P, code: C) -> Option<Arc<str>>
60    where
61        P: Into<PathBuf>,
62        C: Into<Arc<str>>,
63    {
64        self.files.insert_virtual_file(path.into(), code.into())
65    }
66
67    pub fn add_virtual_item<P: Into<PathBuf>>(
68        &mut self,
69        path: P,
70        item: &syn::Item,
71    ) -> Option<Arc<str>> {
72        let code = quote::quote!(#item).to_string();
73        self.add_virtual_file(path, code)
74    }
75
76    pub fn set_known_library<N, P>(&mut self, name: N, path: P) -> Result<()>
77    where
78        N: Into<Box<str>>,
79        P: AsRef<StdPath>,
80    {
81        self.files
82            .set_known_library(name.into(), path.as_ref())
83            .map(|_| ())
84    }
85
86    pub fn analyze(self, entry: impl AsRef<StdPath>) -> Result<Semantics<'gcx>> {
87        fn inner<'gcx>(this: Analyzer<'gcx>, entry: &StdPath) -> Result<Semantics<'gcx>> {
88            let fpath = entry.to_path_buf();
89            let npath = this.files.to_name_path(entry)?;
90            let entry_task = Task::construct_path_tree_for_file(fpath, npath);
91
92            let mut sem = Semantics {
93                gcx: this.gcx,
94                files: this.files,
95                stree: this.stree,
96                ptree: this.ptree,
97                s2p: this.s2p,
98                evaluated: this.evaluated,
99                logic: this.logic,
100                type_inspector: this.type_inspector,
101                known_finder: this.known_finder,
102                tasks: this.tasks,
103            };
104
105            let mut cx = sem.as_analyze_cx();
106            cx.add_default()?;
107            cx.run_task_loop_with_task(entry_task)?;
108            Ok(sem)
109        }
110        inner(self, entry.as_ref())
111    }
112}
113
114#[derive(Debug)]
115pub struct Semantics<'gcx> {
116    gcx: &'gcx GlobalCx<'gcx>,
117    pub files: AbstractFiles,
118    pub stree: SyntaxTree,
119    pub ptree: PubPathTree<'gcx>,
120    pub s2p: SynToPath,
121    pub evaluated: Evaluated<'gcx>,
122    pub logic: Logic<'gcx>,
123    type_inspector: Inspector<'gcx>,
124    known_finder: KnownLibFinder,
125    tasks: TaskQueue<'gcx>,
126}
127
128impl<'gcx> Semantics<'gcx> {
129    pub fn monomorphize_impl(&mut self, item_impl: SynId, self_ty: Option<TypeId>) -> Result<()> {
130        let task = Task::monomorphize_impl(item_impl, self_ty);
131        self.as_analyze_cx().run_task_loop_with_task(task)
132    }
133
134    pub fn get_type_of_expr(&mut self, expr: SynId) -> Option<TypeId> {
135        // Gets infer type if the expression has been inferred.
136        let syn = expr.as_any().downcast_ref::<syn::Expr>()?;
137        let infer_ty = self.type_inspector.inferer.get_type(syn)?.clone();
138
139        let base = crate::helper::ptree::find_base_node_of_expr(&self.stree, &self.s2p, expr)?;
140
141        let mut dummy_tasks = TaskQueue::new();
142        let infer_helper = self.type_inspector.as_infer_helper(
143            self.gcx,
144            &self.stree,
145            &self.ptree.inner,
146            &self.s2p,
147            &self.evaluated,
148            &mut self.logic,
149            &mut dummy_tasks,
150            base,
151        );
152
153        TypeId::from_infer_type(infer_ty, &infer_helper).ok()
154    }
155
156    fn as_analyze_cx(&mut self) -> AnalyzeCx<'_, 'gcx> {
157        AnalyzeCx {
158            gcx: self.gcx,
159            files: &mut self.files,
160            stree: &mut self.stree,
161            ptree: &mut self.ptree.inner,
162            s2p: &mut self.s2p,
163            evaluated: &mut self.evaluated,
164            type_inspector: &mut self.type_inspector,
165            logic: &mut self.logic,
166            known_finder: &mut self.known_finder,
167            tasks: &mut self.tasks,
168        }
169    }
170}
171
172struct AnalyzeCx<'a, 'gcx> {
173    gcx: &'gcx GlobalCx<'gcx>,
174    files: &'a mut AbstractFiles,
175    stree: &'a mut SyntaxTree,
176    ptree: &'a mut PrivPathTree<'gcx>,
177    s2p: &'a mut SynToPath,
178    evaluated: &'a mut Evaluated<'gcx>,
179    logic: &'a mut Logic<'gcx>,
180    type_inspector: &'a mut Inspector<'gcx>,
181    known_finder: &'a mut KnownLibFinder,
182    tasks: &'a mut TaskQueue<'gcx>,
183}
184
185impl<'gcx> AnalyzeCx<'_, 'gcx> {
186    fn run_task_loop_with_task(&mut self, task: Task<'gcx>) -> Result<()> {
187        let _ = self.tasks.push_back(task);
188        self.run_task_loop()
189    }
190
191    fn run_task_loop(&mut self) -> Result<()> {
192        const LOOP_ID: &str = "analyze-loop";
193        FiniteLoop::set_limit(LOOP_ID, 10);
194        FiniteLoop::reset(LOOP_ID);
195
196        let mut cx_tasks = Vec::new();
197
198        // Main task handling loop
199        while let Some(task_item) = self.tasks.pop_front() {
200            // Panics if infinite loop detected
201            let key = iter::once(TaskId::from(&task_item.task))
202                .chain(self.tasks.iter().map(TaskId::from));
203            FiniteLoop::assert(LOOP_ID, key, || {
204                let tasks = iter::once(&task_item.task)
205                    .chain(&*self.tasks)
206                    .collect::<BoxedSlice<_>>();
207                panic!("infinite loop detected: remaining tasks\n{:?}", tasks);
208            });
209
210            let TaskItem {
211                task,
212                this_node,
213                parent_node,
214            } = task_item;
215
216            // Task is going to be processed in this order.
217            // 1. Setup - Some tasks need some setup procedures. We search all setup tasks from
218            //    root node to current node(task), then process them in the order.
219            // 2. Process - Processes this task.
220            // 3. Cleanup - Some tasks need some cleanup procedures. We search all cleanup tasks
221            //    from current node(task) to root node, then process them in the order.
222
223            // 1. Sets up
224            self.tasks.append_setup_tasks(this_node, &mut cx_tasks);
225            for setup_task in cx_tasks.drain(..) {
226                self.call_task_handler(setup_task).unwrap(); // never fails
227            }
228
229            // 2. Processes the task.
230            let had_cleanup = self.tasks.get_cleanup_task(this_node).is_some();
231            let res = self.handle_task(task);
232
233            // 3. Cleans up
234            if !had_cleanup && self.tasks.get_cleanup_task(this_node).is_some() {
235                // If current task just made cleanup task, it is for children tasks, not for the
236                // current task. So we skip it.
237                self.tasks.append_cleanup_tasks(parent_node, &mut cx_tasks);
238            } else {
239                self.tasks.append_cleanup_tasks(this_node, &mut cx_tasks);
240            }
241            for cleanup_task in cx_tasks.drain(..) {
242                self.call_task_handler(cleanup_task).unwrap(); // never fails
243            }
244
245            match res {
246                Ok(()) => self.tasks.mark_done(this_node),
247                Err(e) => match e {
248                    TriError::Soft(task) => {
249                        // Retries the task again
250                        self.tasks.push_back_force(TaskItem {
251                            task,
252                            this_node,
253                            parent_node,
254                        });
255                    }
256                    TriError::Hard(e) => return Err(e),
257                },
258            }
259        }
260
261        shorten_chain(self.ptree);
262
263        debug_assert_eq!(self.ptree.unresolved().len(), 0);
264        debug_assert!(self.tasks.is_empty());
265        self.tasks.reset();
266        Ok(())
267    }
268
269    fn handle_task(&mut self, task: Task<'gcx>) -> TriResult<(), Task<'gcx>> {
270        let res = self.call_task_handler(task);
271
272        fn append_task<'gcx, T: GenerateTask<'gcx>>(
273            t: &T,
274            pid: PathId,
275            tasks: &mut TaskQueue<'gcx>,
276        ) {
277            for task in t.generate_task(pid) {
278                let _ = tasks.push_back(task);
279            }
280        }
281
282        let mut found_raw_use = false;
283
284        for pid in self.ptree.unresolved() {
285            match &self.ptree[pid] {
286                PrivItem::RawConst(raw) => append_task(raw, pid, self.tasks),
287                PrivItem::RawEnum(raw) => append_task(raw, pid, self.tasks),
288                PrivItem::RawField(raw) => append_task(raw, pid, self.tasks),
289                PrivItem::RawFn(raw) => append_task(raw, pid, self.tasks),
290                PrivItem::RawLocal(raw) => append_task(raw, pid, self.tasks),
291                PrivItem::RawMod(raw) => append_task(raw, pid, self.tasks),
292                PrivItem::RawStruct(raw) => append_task(raw, pid, self.tasks),
293                PrivItem::RawTrait(raw) => append_task(raw, pid, self.tasks),
294                PrivItem::RawTypeAlias(raw) => append_task(raw, pid, self.tasks),
295                PrivItem::RawUse(raw) => {
296                    append_task(raw, pid, self.tasks);
297                    found_raw_use = true;
298                }
299                PrivItem::RawVariant(raw) => append_task(raw, pid, self.tasks),
300
301                PrivItem::Block(_)
302                | PrivItem::Const(_)
303                | PrivItem::Enum(_)
304                | PrivItem::Field(_)
305                | PrivItem::Fn(_)
306                | PrivItem::Local(_)
307                | PrivItem::Mod(_)
308                | PrivItem::Struct(_)
309                | PrivItem::Trait(_)
310                | PrivItem::TypeAlias(_)
311                | PrivItem::Use(_)
312                | PrivItem::Variant(_)
313                | PrivItem::None => { /* No need to resolve further */ }
314            }
315        }
316
317        if found_raw_use {
318            for task in TaskResolveUse::tasks_for_all() {
319                let _ = self.tasks.push_back(task);
320            }
321        }
322
323        res
324    }
325
326    fn call_task_handler(&mut self, task: Task<'gcx>) -> TriResult<(), Task<'gcx>> {
327        match task {
328            Task::ConstructPathTree(inner) => handler::TaskConstructPathTreeHandler {
329                files: self.files,
330                stree: self.stree,
331                ptree: self.ptree,
332                s2p: self.s2p,
333                tasks: self.tasks,
334            }
335            .handle_task(inner)
336            .map_soft_err(Task::ConstructPathTree),
337            Task::FindKnownLib(inner) => {
338                handler::TaskFindKnownLibHandler {
339                    files: self.files,
340                    known_finder: self.known_finder,
341                    tasks: self.tasks,
342                }
343                .handle_task(inner);
344                Ok(())
345            }
346            Task::LoadLogic(inner) => handler::TaskLoadLogicHandler {
347                gcx: self.gcx,
348                ptree: self.ptree,
349                s2p: self.s2p,
350                evaluated: self.evaluated,
351                type_inspector: self.type_inspector,
352                logic: self.logic,
353                tasks: self.tasks,
354            }
355            .handle_task(inner)
356            .map_soft_err(Task::LoadLogic),
357            Task::Resolve(inner) => handler::TaskResolveHandler {
358                gcx: self.gcx,
359                stree: self.stree,
360                ptree: self.ptree,
361                s2p: self.s2p,
362                evaluated: self.evaluated,
363                type_inspector: self.type_inspector,
364                logic: self.logic,
365                tasks: self.tasks,
366            }
367            .handle_task(inner)
368            .map_soft_err(Task::Resolve),
369            Task::FixType(inner) => handler::TaskFixTypeHandler {
370                gcx: self.gcx,
371                stree: self.stree,
372                ptree: self.ptree,
373                s2p: self.s2p,
374                evaluted: self.evaluated,
375                type_inspector: self.type_inspector,
376                logic: self.logic,
377                tasks: self.tasks,
378            }
379            .handle_task(inner)
380            .map_soft_err(Task::FixType),
381            Task::EvalConst(inner) => handler::TaskEvalConstHandler {
382                gcx: self.gcx,
383                stree: self.stree,
384                ptree: self.ptree,
385                s2p: self.s2p,
386                evaluated: self.evaluated,
387                type_inspector: self.type_inspector,
388                logic: self.logic,
389                tasks: self.tasks,
390            }
391            .handle_task(inner)
392            .map_soft_err(Task::EvalConst),
393            Task::EvalExpr(inner) => handler::TaskEvalExprHandler {
394                gcx: self.gcx,
395                stree: self.stree,
396                ptree: self.ptree,
397                s2p: self.s2p,
398                evaluated: self.evaluated,
399                type_inspector: self.type_inspector,
400                logic: self.logic,
401                tasks: self.tasks,
402            }
403            .handle_task(inner)
404            .map_soft_err(Task::EvalExpr),
405            Task::Monomorphize(inner) => handler::TaskMonomorphizeHandler {
406                gcx: self.gcx,
407                stree: self.stree,
408                ptree: self.ptree,
409                s2p: self.s2p,
410                inferer: &mut self.type_inspector.inferer,
411                tasks: self.tasks,
412            }
413            .handle_task(inner)
414            .map_soft_err(Task::Monomorphize),
415            Task::Dyn(inner) => handler::TaskDynHandler { gcx: self.gcx }
416                .handle_task(inner)
417                .map_soft_err(Task::Dyn),
418        }
419    }
420
421    fn add_default(&mut self) -> Result<()> {
422        // Adds default scalar types such as "i32" and "u32".
423        for name in known::scalar_names() {
424            let scalar = TypeScalar::from_type_name(name).unwrap();
425            self.ptree.insert_type(Type::Scalar(scalar));
426        }
427
428        // Appends a task for reading default well known libraries such as "core" and "std" if
429        // the configuration allows.
430        let config = self.gcx.get_config();
431        for (name, code, flag) in [
432            ("core", known::LIB_CORE_CODE, ConfigLoad::CORE),
433            ("std", known::LIB_STD_CODE, ConfigLoad::STD),
434        ] {
435            if !config.load.contains(flag) {
436                continue;
437            }
438            // Registers the well known library code.
439            let fpath: PathBuf = name.into();
440            let code: Arc<str> = code.into();
441            self.files.insert_virtual_file(fpath.clone(), code);
442            self.files.set_known_library(name.into(), name.as_ref())?;
443
444            // Adds a task for the library file.
445            let npath: String = name.to_owned();
446            let task = Task::construct_path_tree_for_file(fpath, npath);
447            let _ = self.tasks.push_back(task);
448        }
449        self.run_task_loop()
450    }
451}
452
453#[cfg(test)]
454mod tests {
455    use super::*;
456    use crate::{
457        etc::util,
458        pid, pitem, pnode,
459        semantic::tree::{ArrayLen, OwnedParam, OwnedType, PathId, PubItem},
460        Config, ConfigLoad, GetOwned,
461    };
462
463    fn prepare<'gcx, 'a>(
464        gcx: &'gcx GlobalCx<'gcx>,
465        files: impl IntoIterator<Item = (&'a str, &'a str)>,
466    ) -> Analyzer<'gcx> {
467        syn_locator::enable_thread_local(true);
468        syn_locator::clear();
469
470        util::set_crate_name(util::cargo_crate_name());
471
472        let mut analyzer = Analyzer::new(gcx);
473        for (path, code) in files {
474            analyzer.add_virtual_file(path, code);
475        }
476        analyzer
477    }
478
479    mod import {
480        use super::*;
481        use crate::semantic::tree::NodeIndex;
482
483        #[test]
484        fn test_cascading_import() {
485            let entry = "/mod.rs";
486            let code = r#"
487            mod a {
488                pub mod b {
489                    pub struct C;
490                }
491            }
492            use b::C;
493            use a::b;
494            "#;
495
496            let gcx = GlobalCx::default();
497            gcx.configure(Config {
498                load: ConfigLoad::empty(),
499            });
500            let sem = prepare(&gcx, [(entry, code)]).analyze(entry).unwrap();
501            let ptree = &sem.ptree;
502            let crate_ = ptree.crate_name();
503
504            let b = pitem!(ptree, "{crate_}::b");
505            assert_eq!(b.as_use().unwrap().dst, pid!(ptree, "{crate_}::a::b"));
506
507            let c = pitem!(ptree, "{crate_}::C");
508            assert_eq!(c.as_use().unwrap().dst, pid!(ptree, "{crate_}::a::b::C"));
509        }
510
511        #[test]
512        fn test_recursive_import() {
513            let entry = "/mod.rs";
514            let code = r#"
515            mod a {
516                pub struct A;
517                struct AA;
518                pub use super::b::*;
519            }
520            mod b {
521                pub struct B;
522                struct BB;
523                pub use super::c::*;
524            }
525            mod c {
526                pub struct C;
527                struct CC;
528                pub use super::a::*;
529            }
530            "#;
531
532            let gcx = GlobalCx::default();
533            gcx.configure(Config {
534                load: ConfigLoad::empty(),
535            });
536            let sem = prepare(&gcx, [(entry, code)]).analyze(entry).unwrap();
537            let ptree = &sem.ptree;
538            let crate_ = ptree.crate_name();
539
540            let node_a = pnode!(ptree, "{crate_}::a::A");
541            let node_b = pnode!(ptree, "{crate_}::b::B");
542            let node_c = pnode!(ptree, "{crate_}::c::C");
543
544            assert_eq!(ptree.node(node_a).iter().count(), 1);
545            assert_eq!(ptree.node(node_b).iter().count(), 1);
546            assert_eq!(ptree.node(node_c).iter().count(), 1);
547
548            let pid_a = node_a.to_path_id(0);
549            let pid_b = node_b.to_path_id(0);
550            let pid_c = node_c.to_path_id(0);
551
552            let test = |key: &str, expected_dst: PathId| {
553                let node = pnode!(ptree, "{crate_}::{key}");
554                let node = ptree.node(node);
555                let mut values = node.iter();
556
557                if values.clone().count() != 1 {
558                    panic!("error at key: `{key}`");
559                }
560
561                let (_, value) = values.next().unwrap();
562                assert_eq!(value.as_use().unwrap().dst, expected_dst);
563            };
564
565            test("a::B", pid_b);
566            test("a::C", pid_c);
567            test("b::A", pid_a);
568            test("b::C", pid_c);
569            test("c::A", pid_a);
570            test("c::B", pid_b);
571
572            let root = PubPathTree::ROOT;
573            assert!(ptree.search(root, "crate::a::BB").is_none());
574            assert!(ptree.search(root, "crate::a::CC").is_none());
575            assert!(ptree.search(root, "crate::b::AA").is_none());
576            assert!(ptree.search(root, "crate::b::CC").is_none());
577            assert!(ptree.search(root, "crate::c::AA").is_none());
578            assert!(ptree.search(root, "crate::c::BB").is_none());
579        }
580
581        #[test]
582        fn test_import_multiple_namespaces() {
583            let entry = "/mod.rs";
584            let code = r#"
585            mod a {
586                pub mod x {}  // Type namespace
587                pub fn x() {} // Value namespace
588            }
589
590            mod b {
591                pub mod x {}  // Type namespace
592                pub fn x() {} // Value namespace
593            }
594
595            use a::x;     // brings 'mod' and 'fn' inside 'a'
596            pub use a::*; // same as above
597            use b::*;     // brings 'mod' and 'fn' inside 'b'
598            "#;
599
600            let gcx = GlobalCx::default();
601            gcx.configure(Config {
602                load: ConfigLoad::empty(),
603            });
604            let sem = prepare(&gcx, [(entry, code)]).analyze(entry).unwrap();
605            let ptree = &sem.ptree;
606            let crate_ = ptree.crate_name();
607
608            let vis_node = pnode!(ptree, "{crate_}");
609            let node_a_x = pnode!(ptree, "{crate_}::a::x");
610            let node_b_x = pnode!(ptree, "{crate_}::b::x");
611
612            let mut expected = vec![
613                (NodeIndex(0), node_a_x.to_path_id(0)),
614                (NodeIndex(0), node_a_x.to_path_id(1)),
615                (vis_node, node_b_x.to_path_id(0)),
616                (vis_node, node_b_x.to_path_id(1)),
617            ];
618            expected.sort_unstable();
619
620            let node_x = pnode!(ptree, "{crate_}::x");
621            let mut use_values = ptree
622                .node(node_x)
623                .iter()
624                .filter_map(|(_, item)| match item {
625                    PubItem::Use(v) => Some((v.vis_node, v.dst)),
626                    _ => None,
627                })
628                .collect::<Vec<_>>();
629            use_values.sort_unstable();
630
631            assert_eq!(use_values, expected);
632        }
633
634        #[cfg(not(miri))] // Loading "core" takes too much time
635        #[test]
636        fn test_import_well_known_library() {
637            let entry = "test";
638            let code = r#"
639            use ops::Add;
640            use std::ops;
641            "#;
642
643            let gcx = GlobalCx::default();
644            let sem = prepare(&gcx, [(entry, code)]).analyze(entry).unwrap();
645            let ptree = &sem.ptree;
646            let crate_ = ptree.crate_name();
647
648            // use ops::Add;
649            let add = pitem!(ptree, "{crate_}::{entry}::Add");
650            assert_eq!(add.as_use().unwrap().dst, pid!(ptree, "core::ops::Add"));
651
652            // use std::ops;
653            let ops = pitem!(ptree, "{crate_}::{entry}::ops");
654            assert_eq!(ops.as_use().unwrap().dst, pid!(ptree, "core::ops"));
655        }
656
657        #[test]
658        fn test_import_custom_known_library() {
659            let known = "known";
660            let known_code = r#"
661            pub struct T;
662            impl T {
663                pub const U: u32 = 0;
664                pub const fn new() -> Self { Self }
665            }
666            "#;
667
668            let entry = "test";
669            let entry_code = r#"
670            fn f() {
671                let a = known::T::U;
672                let b = known::T::new();
673            }
674            "#;
675
676            let gcx = GlobalCx::default();
677            gcx.configure(Config {
678                load: ConfigLoad::empty(),
679            });
680            let mut analyzer = prepare(&gcx, [(known, known_code), (entry, entry_code)]);
681            analyzer.set_known_library(known, known).unwrap();
682            let sem = analyzer.analyze(entry).unwrap();
683            let ptree = &sem.ptree;
684            let crate_ = ptree.crate_name();
685
686            // let a = known::T::U;
687            let ia = pitem!(ptree, "{crate_}::{entry}::f::{{0}}::a");
688            let ta = ptree.get_owned(ia.as_local().unwrap().tid);
689            assert_eq!(
690                ta,
691                OwnedType::Path {
692                    name: "u32".into(),
693                    params: [].into()
694                }
695            );
696
697            // let b = known::T::new();
698            let ib = pitem!(ptree, "{crate_}::{entry}::f::{{0}}::b");
699            let tb = ptree.get_owned(ib.as_local().unwrap().tid);
700            assert!(matches!(tb, OwnedType::Path { name, .. } if name == "known::T"));
701        }
702    }
703
704    mod visibility {
705        use super::*;
706
707        #[test]
708        fn test_various_pub_visibility() {
709            let entry = "test";
710            let code = r#"
711            mod a {
712                mod b {
713                    pub mod c {
714                        pub struct T0; // Visible at root
715                        pub(crate) struct T1; // Visible at crate
716                        pub(in super::super) struct T2; // Visible at 'a'
717                        pub(super) struct T3; // Visible at 'b'
718                        struct T4; // Visible at 'c'
719                    }
720                }
721            }
722            "#;
723
724            let gcx = GlobalCx::default();
725            gcx.configure(Config {
726                load: ConfigLoad::empty(),
727            });
728            let sem = prepare(&gcx, [(entry, code)]).analyze(entry).unwrap();
729            let ptree = &sem.ptree;
730            let crate_ = ptree.crate_name();
731
732            // pub struct T0; // Visible at root
733            let i0 = pitem!(ptree, "{crate_}::{entry}::a::b::c::T0");
734            assert_eq!(i0.as_struct().unwrap().vis_node, pnode!(ptree, ""));
735
736            // pub(crate) struct T1; // Visible at crate
737            let i1 = pitem!(ptree, "{crate_}::{entry}::a::b::c::T1");
738            assert_eq!(i1.as_struct().unwrap().vis_node, ptree.crate_node());
739
740            // pub(in super::super) struct T2; // Visible at 'a'
741            let i2 = pitem!(ptree, "{crate_}::{entry}::a::b::c::T2");
742            assert_eq!(
743                i2.as_struct().unwrap().vis_node,
744                pnode!(ptree, "{crate_}::{entry}::a")
745            );
746
747            // pub(super) struct T3; // Visible at 'b'
748            let i3 = pitem!(ptree, "{crate_}::{entry}::a::b::c::T3");
749            assert_eq!(
750                i3.as_struct().unwrap().vis_node,
751                pnode!(ptree, "{crate_}::{entry}::a::b")
752            );
753
754            // struct T4; // Visible at 'c'
755            let i4 = pitem!(ptree, "{crate_}::{entry}::a::b::c::T4");
756            assert_eq!(
757                i4.as_struct().unwrap().vis_node,
758                pnode!(ptree, "{crate_}::{entry}::a::b::c")
759            );
760        }
761
762        #[test]
763        fn test_choosing_visible_item() {
764            let entry = "test";
765            let code = r#"
766            mod a { struct A; }
767            mod b { pub struct A; }
768            mod c { struct A; }
769            mod d {
770                use super::{a::*, b::*, c::*};
771
772                struct T {
773                    a: A, // b::A
774                }
775            }
776            "#;
777
778            let gcx = GlobalCx::default();
779            gcx.configure(Config {
780                load: ConfigLoad::empty(),
781            });
782            let sem = prepare(&gcx, [(entry, code)]).analyze(entry).unwrap();
783            let ptree = &sem.ptree;
784            let crate_ = ptree.crate_name();
785
786            let field = pitem!(ptree, "{crate_}::{entry}::d::T::a");
787            let ty = ptree.get_owned(field.as_field().unwrap().tid);
788            assert_eq!(
789                ty,
790                OwnedType::Path {
791                    name: format!("{crate_}::{entry}::b::A"),
792                    params: [OwnedParam::Self_].into(),
793                }
794            );
795        }
796    }
797
798    mod chain {
799        use super::*;
800
801        #[test]
802        fn test_shorten_type_alias_chain() {
803            let entry = "test";
804            let code = r#"
805            mod a { pub struct A; }
806            mod b { pub type B = super::a::A; }
807            mod c { pub type C = (super::a::A, super::b::B); }
808            mod d { pub type D = [super::c::C; 1]; }
809            "#;
810
811            let gcx = GlobalCx::default();
812            gcx.configure(Config {
813                load: ConfigLoad::empty(),
814            });
815            let sem = prepare(&gcx, [(entry, code)]).analyze(entry).unwrap();
816            let ptree = &sem.ptree;
817            let crate_ = ptree.crate_name();
818
819            // mod a { pub struct A; }
820            let ia = pitem!(ptree, "{crate_}::{entry}::a::A");
821            let ta = ptree.get_owned(ia.as_struct().unwrap().tid);
822            assert_eq!(
823                ta,
824                OwnedType::Path {
825                    name: format!("{crate_}::{entry}::a::A"),
826                    params: [OwnedParam::Self_].into(),
827                }
828            );
829
830            // mod b { pub type B = super::a::A; }
831            let ib = pitem!(ptree, "{crate_}::{entry}::b::B");
832            let tb = ptree.get_owned(ib.as_type_alias().unwrap().tid);
833            assert_eq!(tb, ta);
834
835            // mod c { pub type C = (super::a::A, super::b::B); }
836            let ic = pitem!(ptree, "{crate_}::{entry}::c::C");
837            let tc = ptree.get_owned(ic.as_type_alias().unwrap().tid);
838            assert_eq!(tc, OwnedType::Tuple([ta.clone(), ta.clone()].into()));
839
840            // mod d { pub type D = [super::c::C; 1]; }
841            let id = pitem!(ptree, "{crate_}::{entry}::d::D");
842            let td = ptree.get_owned(id.as_type_alias().unwrap().tid);
843            assert_eq!(
844                td,
845                OwnedType::Array {
846                    elem: Box::new(tc.clone()),
847                    len: ArrayLen::Fixed(1)
848                }
849            );
850        }
851
852        #[test]
853        fn test_shorten_mixed_use_type_alias_chain() {
854            let entry = "test";
855            let code = r#"
856            mod a { pub struct A; }
857            mod b { pub type B = super::a::A; }
858            mod c { pub use super::b::B as C; }
859            mod d { pub type D = super::c::C; }
860            mod e { pub use super::d::D; }
861            "#;
862
863            let gcx = GlobalCx::default();
864            gcx.configure(Config {
865                load: ConfigLoad::empty(),
866            });
867            let sem = prepare(&gcx, [(entry, code)]).analyze(entry).unwrap();
868            let ptree = &sem.ptree;
869            let crate_ = ptree.crate_name();
870
871            // mod a { pub struct A; }
872            let ia = pitem!(ptree, "{crate_}::{entry}::a::A");
873            let ta = ptree.get_owned(ia.as_struct().unwrap().tid);
874            assert_eq!(
875                ta,
876                OwnedType::Path {
877                    name: format!("{crate_}::{entry}::a::A"),
878                    params: [OwnedParam::Self_].into(),
879                }
880            );
881
882            // mod b { pub type B = super::a::A; }
883            let ib = pitem!(ptree, "{crate_}::{entry}::b::B");
884            let tb = ptree.get_owned(ib.as_type_alias().unwrap().tid);
885            assert_eq!(tb, ta);
886
887            // mod c { pub use super::b::B as C; }
888            let ic = pitem!(ptree, "{crate_}::{entry}::c::C");
889            assert_eq!(
890                ic.as_use().unwrap().dst,
891                pid!(ptree, "{crate_}::{entry}::a::A")
892            );
893
894            // mod d { pub type D = super::c::C; }
895            let id = pitem!(ptree, "{crate_}::{entry}::d::D");
896            let td = ptree.get_owned(id.as_type_alias().unwrap().tid);
897            assert_eq!(td, ta);
898
899            // mod e { pub use super::d::D; }
900            let ie = pitem!(ptree, "{crate_}::{entry}::e::D");
901            assert_eq!(
902                ie.as_use().unwrap().dst,
903                pid!(ptree, "{crate_}::{entry}::a::A")
904            );
905        }
906    }
907
908    mod local {
909        use super::*;
910
911        #[test]
912        fn test_local_fn_param() {
913            let entry = "test";
914            let code = r#"
915            struct T { a: f32, b: f64 }
916
917            fn f0(a: i32, b: i64) {}
918            fn f1(a: [i32; 1]) {}
919            fn f2(T { a, b }: T) {}
920            fn f3(a: (i32, i64)) {}
921            "#;
922
923            let gcx = GlobalCx::default();
924            gcx.configure(Config {
925                load: ConfigLoad::empty(),
926            });
927            let sem = prepare(&gcx, [(entry, code)]).analyze(entry).unwrap();
928            let ptree = &sem.ptree;
929            let crate_ = ptree.crate_name();
930
931            // fn f0(a: i32, b: i64) {}
932            let ia = pitem!(ptree, "{crate_}::{entry}::f0::a");
933            let ta = ptree.get_owned(ia.as_local().unwrap().tid);
934            assert_eq!(
935                ta,
936                OwnedType::Path {
937                    name: "i32".into(),
938                    params: [].into()
939                }
940            );
941
942            let ib = pitem!(ptree, "{crate_}::{entry}::f0::b");
943            let tb = ptree.get_owned(ib.as_local().unwrap().tid);
944            assert_eq!(
945                tb,
946                OwnedType::Path {
947                    name: "i64".into(),
948                    params: [].into()
949                }
950            );
951
952            // fn f1(a: [i32; 1]) {}
953            let ia = pitem!(ptree, "{crate_}::{entry}::f1::a");
954            let ta = ptree.get_owned(ia.as_local().unwrap().tid);
955            assert_eq!(
956                ta,
957                OwnedType::Array {
958                    elem: Box::new(OwnedType::Path {
959                        name: "i32".into(),
960                        params: [].into()
961                    }),
962                    len: ArrayLen::Fixed(1),
963                }
964            );
965
966            // struct T { a: f32, b: f64 }
967            // fn f2(T { a, b }: T) {}
968            let ia = pitem!(ptree, "{crate_}::{entry}::f2::a");
969            let ta = ptree.get_owned(ia.as_local().unwrap().tid);
970            assert_eq!(
971                ta,
972                OwnedType::Path {
973                    name: "f32".into(),
974                    params: [].into()
975                }
976            );
977
978            let ib = pitem!(ptree, "{crate_}::{entry}::f2::b");
979            let tb = ptree.get_owned(ib.as_local().unwrap().tid);
980            assert_eq!(
981                tb,
982                OwnedType::Path {
983                    name: "f64".into(),
984                    params: [].into()
985                }
986            );
987
988            // fn f3(a: (i32, i64)) {}
989            let ia = pitem!(ptree, "{crate_}::{entry}::f3::a");
990            let ta = ptree.get_owned(ia.as_local().unwrap().tid);
991            assert_eq!(
992                ta,
993                OwnedType::Tuple(
994                    [
995                        OwnedType::Path {
996                            name: "i32".into(),
997                            params: [].into()
998                        },
999                        OwnedType::Path {
1000                            name: "i64".into(),
1001                            params: [].into()
1002                        }
1003                    ]
1004                    .into()
1005                )
1006            );
1007        }
1008
1009        #[test]
1010        fn test_local_determined_by_fn_input_output() {
1011            let entry = "test";
1012            let code = r#"
1013            fn f() {
1014                fn f(a: u32) -> i32 { a as i32 }
1015                let a = 0;
1016                let b = f(a);
1017            }
1018            "#;
1019
1020            let gcx = GlobalCx::default();
1021            gcx.configure(Config {
1022                load: ConfigLoad::empty(),
1023            });
1024            let sem = prepare(&gcx, [(entry, code)]).analyze(entry).unwrap();
1025            let ptree = &sem.ptree;
1026            let crate_ = ptree.crate_name();
1027
1028            let ia = pitem!(ptree, "{crate_}::{entry}::f::{{0}}::a");
1029            let ta = ptree.get_owned(ia.as_local().unwrap().tid);
1030            assert_eq!(
1031                ta,
1032                OwnedType::Path {
1033                    name: "u32".into(),
1034                    params: [].into()
1035                }
1036            );
1037
1038            let ib = pitem!(ptree, "{crate_}::{entry}::f::{{0}}::b");
1039            let tb = ptree.get_owned(ib.as_local().unwrap().tid);
1040            assert_eq!(
1041                tb,
1042                OwnedType::Path {
1043                    name: "i32".into(),
1044                    params: [].into()
1045                }
1046            );
1047        }
1048
1049        #[test]
1050        fn test_local_determined_by_struct() {
1051            let entry = "test";
1052            let code = r#"
1053            fn f() {
1054                struct A(i32);
1055                let a = A(0);
1056            }
1057            "#;
1058
1059            let gcx = GlobalCx::default();
1060            gcx.configure(Config {
1061                load: ConfigLoad::empty(),
1062            });
1063            let sem = prepare(&gcx, [(entry, code)]).analyze(entry).unwrap();
1064            let ptree = &sem.ptree;
1065            let crate_ = ptree.crate_name();
1066
1067            let ia = pitem!(ptree, "{crate_}::{entry}::f::{{0}}::a");
1068            let ta = ptree.get_owned(ia.as_local().unwrap().tid);
1069            assert_eq!(
1070                ta,
1071                OwnedType::Path {
1072                    name: format!("{crate_}::{entry}::f::{{0}}::A"),
1073                    params: [
1074                        OwnedParam::Self_,
1075                        OwnedParam::Other {
1076                            name: "1".into(),
1077                            ty: OwnedType::Path {
1078                                name: "i32".into(),
1079                                params: [].into()
1080                            },
1081                        }
1082                    ]
1083                    .into(),
1084                }
1085            );
1086        }
1087    }
1088
1089    mod etc {
1090        use super::*;
1091
1092        #[cfg(not(miri))] // Loading "core" takes too much time
1093        #[test]
1094        fn test_array_length_in_type() {
1095            let entry = "test";
1096            let code = r#"
1097            // Complex array length
1098            const A: [i32; f0() + 2] = [0, 0, 0];
1099            const fn f0() -> usize { 1 }
1100            // Array length in a local type
1101            fn f1() {
1102                let a: [i32; 2] = [1, 2];
1103                let b = [0; 1];
1104            }
1105            "#;
1106
1107            let gcx = GlobalCx::default();
1108            let sem = prepare(&gcx, [(entry, code)]).analyze(entry).unwrap();
1109            let ptree = &sem.ptree;
1110            let crate_ = ptree.crate_name();
1111
1112            // const A: [i32; f0() + 2] = [0, 0, 0];
1113            let ia = pitem!(ptree, "{crate_}::{entry}::A");
1114            let ta = ptree.get_owned(ia.as_const().unwrap().type_id());
1115            let OwnedType::Array { elem, len } = ta else {
1116                panic!("{ta:?} is not an array");
1117            };
1118            assert_eq!(
1119                *elem,
1120                OwnedType::Path {
1121                    name: "i32".into(),
1122                    params: [].into()
1123                }
1124            );
1125            assert_eq!(len, ArrayLen::Fixed(3));
1126
1127            // let a: [i32; 2] = [1, 2];
1128            let ia = pitem!(ptree, "{crate_}::{entry}::f1::{{0}}::a");
1129            let ta = ptree.get_owned(ia.as_local().unwrap().tid);
1130            let OwnedType::Array { elem, len } = ta else {
1131                panic!("{ta:?} is not an array");
1132            };
1133            assert_eq!(
1134                *elem,
1135                OwnedType::Path {
1136                    name: "i32".into(),
1137                    params: [].into()
1138                }
1139            );
1140            assert_eq!(len, ArrayLen::Fixed(2));
1141
1142            // let b = [0; 1];
1143            let ib = pitem!(ptree, "{crate_}::{entry}::f1::{{0}}::b");
1144            let ta = ptree.get_owned(ib.as_local().unwrap().tid);
1145            let OwnedType::Array { elem, len } = ta else {
1146                panic!("{ta:?} is not an array");
1147            };
1148            assert_eq!(
1149                *elem,
1150                OwnedType::Path {
1151                    name: "int".into(),
1152                    params: [].into()
1153                }
1154            );
1155            assert_eq!(len, ArrayLen::Fixed(1));
1156        }
1157
1158        #[cfg(not(miri))] // Loading "core" takes too much time
1159        #[test]
1160        fn test_constant() {
1161            use crate::{semantic::eval, Intern};
1162
1163            let entry = "test";
1164            let code = r#"
1165            // A constant determined by function call.
1166            const A: i32 = double(2) + 1;
1167            const fn double(i: i32) -> i32 { i * 2 }
1168
1169            // A constant determined by associated function call.
1170            const B: T = T::new(1);
1171            struct T(u32);
1172            impl T { const fn new(value: u32) -> Self { Self(value) } }
1173
1174            // A zero sized constant determined by associated function call.
1175            const C: U = U::new();
1176            struct U;
1177            impl U { const fn new() -> Self { Self } }
1178
1179            // A constant in a function.
1180            fn f() { const I: i32 = A + 3; }
1181            "#;
1182
1183            let gcx = GlobalCx::default();
1184            let sem = prepare(&gcx, [(entry, code)]).analyze(entry).unwrap();
1185            let ptree = &sem.ptree;
1186            let evaluated = &sem.evaluated;
1187            let crate_ = ptree.crate_name();
1188
1189            // const A: i32 = double(2) + 1;
1190            let pid = pid!(ptree, "{crate_}::{entry}::A");
1191            let v = evaluated.get_mapped_value_by_path_id(pid).unwrap();
1192            assert_eq!(v, &eval::Value::Scalar(eval::Scalar::I32(5)));
1193
1194            // const B: T = T::new(1);
1195            let pid = pid!(ptree, "{crate_}::{entry}::B");
1196            let v = evaluated.get_mapped_value_by_path_id(pid).unwrap();
1197            assert_eq!(
1198                v,
1199                &eval::Value::Composed(vec![eval::Field {
1200                    name: gcx.intern_str("0"),
1201                    value: eval::Value::Scalar(eval::Scalar::U32(1))
1202                }])
1203            );
1204
1205            // const C: U = U::new();
1206            let pid = pid!(ptree, "{crate_}::{entry}::C");
1207            let v = evaluated.get_mapped_value_by_path_id(pid).unwrap();
1208            assert_eq!(v, &eval::Value::Composed(vec![]));
1209
1210            // const I: i32 = A + 3;
1211            let pid = pid!(ptree, "{crate_}::{entry}::f::{{0}}::I");
1212            let v = evaluated.get_mapped_value_by_path_id(pid).unwrap();
1213            assert_eq!(v, &eval::Value::Scalar(eval::Scalar::I32(8)));
1214        }
1215
1216        #[test]
1217        fn test_impl_block() {
1218            let entry = "test";
1219            let code = r#"
1220            struct T;
1221
1222            impl T {
1223                fn f0(self) {}
1224                fn f1(&self) {}
1225                fn f2(&mut self) {}
1226                fn f3() -> Self { Self }
1227            }
1228
1229            mod a {
1230                mod b {
1231                    use super::super::T;
1232                    impl T {
1233                        pub(super) fn g0(self) {}
1234                    }
1235                }
1236            }
1237            "#;
1238
1239            let gcx = GlobalCx::default();
1240            gcx.configure(Config {
1241                load: ConfigLoad::empty(),
1242            });
1243            let sem = prepare(&gcx, [(entry, code)]).analyze(entry).unwrap();
1244            let ptree = &sem.ptree;
1245            let crate_ = ptree.crate_name();
1246
1247            // fn f0(self) {}
1248            let i0 = pitem!(ptree, "{crate_}::{entry}::T::f0::self");
1249            let t0 = ptree.get_owned(i0.as_local().unwrap().tid);
1250            assert!(matches!(
1251                t0,
1252                OwnedType::Path { name, .. } if name == format!("{crate_}::{entry}::T")
1253            ));
1254
1255            // fn f1(&self) {}
1256            let i1 = pitem!(ptree, "{crate_}::{entry}::T::f1::self");
1257            let t1 = ptree.get_owned(i1.as_local().unwrap().tid);
1258            let OwnedType::Ref { elem } = t1 else {
1259                panic!("{t1:?} is not a reference");
1260            };
1261            assert!(matches!(
1262                *elem,
1263                OwnedType::Path { name, .. } if name == format!("{crate_}::{entry}::T")
1264            ));
1265
1266            // fn f2(&mut self) {}
1267            let i2 = pitem!(ptree, "{crate_}::{entry}::T::f2::self");
1268            let t2 = ptree.get_owned(i2.as_local().unwrap().tid);
1269            let OwnedType::Ref { elem } = t2 else {
1270                panic!("{t2:?} is not a reference");
1271            };
1272            let OwnedType::Mut { elem } = *elem else {
1273                panic!("{elem:?} is not a mutable");
1274            };
1275            assert!(matches!(
1276                *elem,
1277                OwnedType::Path { name, .. } if name == format!("{crate_}::{entry}::T")
1278            ));
1279
1280            // fn f3() -> Self { Self }
1281            let i3 = pitem!(ptree, "{crate_}::{entry}::T::f3");
1282            let t3 = ptree.get_owned(i3.as_fn().unwrap().tid);
1283            let OwnedType::Path { params, .. } = t3 else {
1284                panic!("{t3:?} is not a path");
1285            };
1286            assert!(matches!(
1287                &params[0],
1288                OwnedParam::Other { ty: OwnedType::Path { name, .. }, .. }
1289                if name == &format!("{crate_}::{entry}::T")
1290            ));
1291
1292            // pub(super) fn g0(self) {}
1293            let g0 = pitem!(ptree, "{crate_}::{entry}::T::g0");
1294            assert_eq!(
1295                g0.as_fn().unwrap().vis_node,
1296                pnode!(ptree, "{crate_}::{entry}::a")
1297            );
1298
1299            let i0 = pitem!(ptree, "{crate_}::{entry}::T::g0::self");
1300            let t0 = ptree.get_owned(i0.as_local().unwrap().tid);
1301            assert!(matches!(
1302                t0,
1303                OwnedType::Path { name, .. } if name == format!("{crate_}::{entry}::T")
1304            ));
1305        }
1306    }
1307}