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 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 while let Some(task_item) = self.tasks.pop_front() {
200 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 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(); }
228
229 let had_cleanup = self.tasks.get_cleanup_task(this_node).is_some();
231 let res = self.handle_task(task);
232
233 if !had_cleanup && self.tasks.get_cleanup_task(this_node).is_some() {
235 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(); }
244
245 match res {
246 Ok(()) => self.tasks.mark_done(this_node),
247 Err(e) => match e {
248 TriError::Soft(task) => {
249 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 => { }
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 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 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 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 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))] #[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 let add = pitem!(ptree, "{crate_}::{entry}::Add");
650 assert_eq!(add.as_use().unwrap().dst, pid!(ptree, "core::ops::Add"));
651
652 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 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 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 let i0 = pitem!(ptree, "{crate_}::{entry}::a::b::c::T0");
734 assert_eq!(i0.as_struct().unwrap().vis_node, pnode!(ptree, ""));
735
736 let i1 = pitem!(ptree, "{crate_}::{entry}::a::b::c::T1");
738 assert_eq!(i1.as_struct().unwrap().vis_node, ptree.crate_node());
739
740 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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))] #[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 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 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 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))] #[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 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 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 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 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 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 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 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 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 ¶ms[0],
1288 OwnedParam::Other { ty: OwnedType::Path { name, .. }, .. }
1289 if name == &format!("{crate_}::{entry}::T")
1290 ));
1291
1292 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}