hermes_sema/ids.rs
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8//! Typed ids for sema entities, backed by `hermes_ast::SemaId` (lib.rs:16).
9//! Port of the identity discipline used by `hermes::sema::Decl`,
10//! `hermes::sema::LexicalScope`, and `hermes::sema::FunctionInfo`
11//! (`include/hermes/Sema/SemContext.h`): those C++ classes are allocated
12//! once and referenced thereafter by (typed) pointer; here they are
13//! referenced by a typed, `u32`-sized index instead, since the AST side
14//! only has an opaque `SemaId` slot (`hermes_ast::node_child`) to store one in.
15//!
16//! **Stability: stable.** Part of the result model, alongside
17//! [`crate::sem_context`]; see the crate doc.
18
19use hermes_ast::SemaId;
20
21/// Declares a `u32` newtype that converts to/from [`SemaId`].
22macro_rules! declare_sema_id {
23 ($(#[$doc:meta])* $name:ident) => {
24 $(#[$doc])*
25 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
26 pub struct $name(u32);
27
28 impl $name {
29 /// Wraps a raw `SemaId` produced elsewhere (e.g. stored on an
30 /// AST node) as this specific id type.
31 pub fn from_sema_id(id: SemaId) -> Self {
32 $name(id.0)
33 }
34
35 /// Converts back to the untyped `SemaId` the AST side stores.
36 pub fn sema_id(self) -> SemaId {
37 SemaId(self.0)
38 }
39
40 /// Returns the `usize` index for side-table indexing (e.g. a
41 /// `Vec<...>` of per-id data owned by `SemContext`).
42 pub fn index(self) -> usize {
43 self.0 as usize
44 }
45 }
46 };
47}
48
49declare_sema_id!(
50 /// Identity of a `Decl` (`hermes::sema::Decl`, SemContext.h:54).
51 DeclId
52);
53declare_sema_id!(
54 /// Identity of a `LexicalScope` (`hermes::sema::LexicalScope`,
55 /// SemContext.h:230).
56 ScopeId
57);
58declare_sema_id!(
59 /// Identity of a `FunctionInfo` (`hermes::sema::FunctionInfo`,
60 /// SemContext.h:291).
61 FunctionInfoId
62);
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn decl_id_round_trips_through_sema_id() {
70 let decl = DeclId::from_sema_id(SemaId(7));
71 let sema = decl.sema_id();
72 assert_eq!(sema, SemaId(7));
73 let back = DeclId::from_sema_id(sema);
74 assert_eq!(back, decl);
75 assert_eq!(back.index(), 7);
76 }
77
78 #[test]
79 fn scope_id_round_trips_through_sema_id() {
80 let scope = ScopeId::from_sema_id(SemaId(3));
81 assert_eq!(scope.sema_id(), SemaId(3));
82 assert_eq!(scope.index(), 3);
83 }
84
85 #[test]
86 fn function_info_id_round_trips_through_sema_id() {
87 let f = FunctionInfoId::from_sema_id(SemaId(42));
88 assert_eq!(f.sema_id(), SemaId(42));
89 assert_eq!(f.index(), 42);
90 }
91
92 #[test]
93 fn ids_are_copy_eq_hash_debug() {
94 // Compile-time check that the derives requested by the brief are
95 // present: Copy (no `.clone()` needed), Eq, Hash (usable as a map
96 // key), Debug (formattable).
97 use std::collections::HashSet;
98 let a = DeclId::from_sema_id(SemaId(1));
99 let b = a; // Copy
100 let mut set = HashSet::new();
101 set.insert(a);
102 assert!(set.contains(&b));
103 assert_eq!(format!("{a:?}"), format!("{b:?}"));
104 }
105}