Skip to content

Commit 2a229e4

Browse files
tidefieldautofix-ci[bot]arendjr
authored
chore: refactor global type system to accommodate further extensions (#8253)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Arend van Beelen jr. <[email protected]>
1 parent 0a6b6fb commit 2a229e4

File tree

8 files changed

+380
-196
lines changed

8 files changed

+380
-196
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/biome_js_type_info/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ biome_resolver = { workspace = true }
2222
biome_rowan = { workspace = true }
2323
camino = { workspace = true }
2424
hashbrown = { workspace = true }
25+
paste = "1.0"
2526
rustc-hash = { workspace = true }
2627

2728
[dev-dependencies]

crates/biome_js_type_info/src/format_type_info.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -589,8 +589,12 @@ impl Format<FormatTypeContext> for TypeReference {
589589
let level = resolved.level();
590590
let id = resolved.id();
591591
if level == TypeResolverLevel::Global {
592-
if resolved.index() < NUM_PREDEFINED_TYPES {
593-
write!(f, [token(global_type_name(id))])
592+
// GlobalsResolverBuilder makes sure the type store is fully filled.
593+
// Every global TypeId whose index is less than NUM_PREDEFINED_TYPES
594+
// must have a name returned by global_type_name().
595+
// GLOBAL_TYPE_MEMBERS ensures this invariant.
596+
if let Some(name) = global_type_name(id) {
597+
write!(f, [token(name)])
594598
} else {
595599
// Start counting from `NUM_PREDEFINED_TYPES` so
596600
// snapshots remain stable even if we add new predefined

crates/biome_js_type_info/src/globals.rs

Lines changed: 191 additions & 191 deletions
Large diffs are not rendered by default.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//! Builder for constructing GlobalsResolver with forward references support.
2+
3+
use std::sync::Arc;
4+
5+
use crate::{NUM_PREDEFINED_TYPES, TypeData, TypeId, TypeStore};
6+
7+
use super::globals::GlobalsResolver;
8+
9+
/// Builder for constructing a GlobalsResolver
10+
pub struct GlobalsResolverBuilder {
11+
/// Types being built. None = reserved but not yet filled.
12+
types: Vec<Option<TypeData>>,
13+
}
14+
15+
impl GlobalsResolverBuilder {
16+
pub fn with_capacity(capacity: usize) -> Self {
17+
Self {
18+
types: vec![None; capacity],
19+
}
20+
}
21+
22+
/// Fill a previously reserved type slot with actual type data.
23+
pub fn set_type_data(&mut self, id: TypeId, data: TypeData) {
24+
let index = id.index();
25+
debug_assert!(
26+
index < self.types.len(),
27+
"TypeId {index} out of bounds (len: {})",
28+
self.types.len()
29+
);
30+
debug_assert!(
31+
self.types[index].is_none(),
32+
"Type at index {index} already set"
33+
);
34+
self.types[index] = Some(data);
35+
}
36+
37+
/// Build the final GlobalsResolver.
38+
pub fn build(self) -> GlobalsResolver {
39+
let types: Vec<Arc<TypeData>> = self
40+
.types
41+
.into_iter()
42+
.map(|opt| Arc::new(opt.unwrap_or(TypeData::Unknown)))
43+
.collect();
44+
45+
GlobalsResolver {
46+
types: TypeStore::from_types(types),
47+
}
48+
}
49+
}
50+
51+
impl Default for GlobalsResolverBuilder {
52+
fn default() -> Self {
53+
Self::with_capacity(NUM_PREDEFINED_TYPES)
54+
}
55+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
//! Type ID constants for global types.
2+
//!
3+
//! TODO(tidefield): Implement a codegen for this file from TypeScript .d.ts files.
4+
5+
// use crate::define_global_type;
6+
use crate::{ResolvedTypeId, TypeId};
7+
8+
use super::globals::GLOBAL_LEVEL;
9+
10+
// FIXME(tidefield): Get rid of the macro when implementing the codegen to improve compile time
11+
// Right now, I'm preserving the names so that the snapshot tests don't break the snapshot tests
12+
// to make sure I'm not breaking anything.
13+
#[macro_export]
14+
macro_rules! define_global_type {
15+
($name:ident, $index:expr, $name_str:expr) => {
16+
pub const $name: TypeId = TypeId::new($index);
17+
paste::paste! {
18+
pub const [<$name _NAME>]: &str = $name_str;
19+
}
20+
};
21+
}
22+
// define_global_type!(ARRAY_ID, 8, "Array"); // Creates ARRAY_ID and ARRAY_ID_NAME
23+
24+
// Type ID constants with their names defined together
25+
define_global_type!(UNKNOWN_ID, 0, "unknown");
26+
define_global_type!(UNDEFINED_ID, 1, "undefined");
27+
define_global_type!(VOID_ID, 2, "void");
28+
define_global_type!(CONDITIONAL_ID, 3, "conditional");
29+
define_global_type!(NUMBER_ID, 4, "number");
30+
define_global_type!(STRING_ID, 5, "string");
31+
define_global_type!(INSTANCEOF_ARRAY_T_ID, 6, "instanceof Array<T>");
32+
define_global_type!(INSTANCEOF_ARRAY_U_ID, 7, "instanceof Array<U>");
33+
define_global_type!(ARRAY_ID, 8, "Array");
34+
define_global_type!(ARRAY_FILTER_ID, 9, "Array.prototype.filter");
35+
define_global_type!(ARRAY_FOREACH_ID, 10, "Array.prototype.forEach");
36+
define_global_type!(ARRAY_MAP_ID, 11, "Array.prototype.map");
37+
define_global_type!(GLOBAL_ID, 12, "globalThis");
38+
define_global_type!(INSTANCEOF_PROMISE_ID, 13, "instanceof Promise");
39+
define_global_type!(PROMISE_ID, 14, "Promise");
40+
define_global_type!(PROMISE_CONSTRUCTOR_ID, 15, "Promise.constructor");
41+
define_global_type!(PROMISE_CATCH_ID, 16, "Promise.prototype.catch");
42+
define_global_type!(PROMISE_FINALLY_ID, 17, "Promise.prototype.finally");
43+
define_global_type!(PROMISE_THEN_ID, 18, "Promise.prototype.then");
44+
define_global_type!(PROMISE_ALL_ID, 19, "Promise.all");
45+
define_global_type!(PROMISE_ALL_SETTLED_ID, 20, "Promise.allSettled");
46+
define_global_type!(PROMISE_ANY_ID, 21, "Promise.any");
47+
define_global_type!(PROMISE_RACE_ID, 22, "Promise.race");
48+
define_global_type!(PROMISE_REJECT_ID, 23, "Promise.reject");
49+
define_global_type!(PROMISE_RESOLVE_ID, 24, "Promise.resolve");
50+
define_global_type!(PROMISE_TRY_ID, 25, "Promise.try");
51+
define_global_type!(BIGINT_STRING_LITERAL_ID, 26, "\"bigint\"");
52+
define_global_type!(BOOLEAN_STRING_LITERAL_ID, 27, "\"boolean\"");
53+
define_global_type!(FUNCTION_STRING_LITERAL_ID, 28, "\"function\"");
54+
define_global_type!(NUMBER_STRING_LITERAL_ID, 29, "\"number\"");
55+
define_global_type!(OBJECT_STRING_LITERAL_ID, 30, "\"object\"");
56+
define_global_type!(STRING_STRING_LITERAL_ID, 31, "\"string\"");
57+
define_global_type!(SYMBOL_STRING_LITERAL_ID, 32, "\"symbol\"");
58+
define_global_type!(UNDEFINED_STRING_LITERAL_ID, 33, "\"undefined\"");
59+
define_global_type!(
60+
TYPEOF_OPERATOR_RETURN_UNION_ID,
61+
34,
62+
"\"bigint\" | \"boolean\" | \"function\" | \"number\" | \"object\" | \"string\" | \"symbol\" | \"undefined\""
63+
);
64+
define_global_type!(T_ID, 35, "T");
65+
define_global_type!(U_ID, 36, "U");
66+
define_global_type!(CONDITIONAL_CALLBACK_ID, 37, "() => conditional");
67+
define_global_type!(MAP_CALLBACK_ID, 38, "<U>(item: T) => U");
68+
define_global_type!(VOID_CALLBACK_ID, 39, "() => void");
69+
define_global_type!(FETCH_ID, 40, "fetch");
70+
define_global_type!(INSTANCEOF_REGEXP_ID, 41, "instanceof RegExp");
71+
define_global_type!(REGEXP_ID, 42, "RegExp");
72+
define_global_type!(REGEXP_EXEC_ID, 43, "RegExp.exec");
73+
74+
/// Total number of predefined types.
75+
/// Must be one more than the highest TypeId above.
76+
pub const NUM_PREDEFINED_TYPES: usize = 44;
77+
78+
// Resolved type ID constants (TypeId wrapped with GlobalLevel)
79+
pub const GLOBAL_UNKNOWN_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, UNKNOWN_ID);
80+
pub const GLOBAL_UNDEFINED_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, UNDEFINED_ID);
81+
pub const GLOBAL_VOID_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, VOID_ID);
82+
pub const GLOBAL_CONDITIONAL_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, CONDITIONAL_ID);
83+
pub const GLOBAL_NUMBER_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, NUMBER_ID);
84+
pub const GLOBAL_STRING_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, STRING_ID);
85+
pub const GLOBAL_ARRAY_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, ARRAY_ID);
86+
pub const GLOBAL_GLOBAL_ID /* :smirk: */: ResolvedTypeId =
87+
ResolvedTypeId::new(GLOBAL_LEVEL, GLOBAL_ID);
88+
pub const GLOBAL_INSTANCEOF_PROMISE_ID: ResolvedTypeId =
89+
ResolvedTypeId::new(GLOBAL_LEVEL, INSTANCEOF_PROMISE_ID);
90+
pub const GLOBAL_PROMISE_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, PROMISE_ID);
91+
pub const GLOBAL_PROMISE_CONSTRUCTOR_ID: ResolvedTypeId =
92+
ResolvedTypeId::new(GLOBAL_LEVEL, PROMISE_CONSTRUCTOR_ID);
93+
pub const GLOBAL_BIGINT_STRING_LITERAL_ID: ResolvedTypeId =
94+
ResolvedTypeId::new(GLOBAL_LEVEL, BIGINT_STRING_LITERAL_ID);
95+
pub const GLOBAL_BOOLEAN_STRING_LITERAL_ID: ResolvedTypeId =
96+
ResolvedTypeId::new(GLOBAL_LEVEL, BOOLEAN_STRING_LITERAL_ID);
97+
pub const GLOBAL_FUNCTION_STRING_LITERAL_ID: ResolvedTypeId =
98+
ResolvedTypeId::new(GLOBAL_LEVEL, FUNCTION_STRING_LITERAL_ID);
99+
pub const GLOBAL_NUMBER_STRING_LITERAL_ID: ResolvedTypeId =
100+
ResolvedTypeId::new(GLOBAL_LEVEL, NUMBER_STRING_LITERAL_ID);
101+
pub const GLOBAL_OBJECT_STRING_LITERAL_ID: ResolvedTypeId =
102+
ResolvedTypeId::new(GLOBAL_LEVEL, OBJECT_STRING_LITERAL_ID);
103+
pub const GLOBAL_STRING_STRING_LITERAL_ID: ResolvedTypeId =
104+
ResolvedTypeId::new(GLOBAL_LEVEL, STRING_STRING_LITERAL_ID);
105+
pub const GLOBAL_SYMBOL_STRING_LITERAL_ID: ResolvedTypeId =
106+
ResolvedTypeId::new(GLOBAL_LEVEL, SYMBOL_STRING_LITERAL_ID);
107+
pub const GLOBAL_UNDEFINED_STRING_LITERAL_ID: ResolvedTypeId =
108+
ResolvedTypeId::new(GLOBAL_LEVEL, UNDEFINED_STRING_LITERAL_ID);
109+
pub const GLOBAL_TYPEOF_OPERATOR_RETURN_UNION_ID: ResolvedTypeId =
110+
ResolvedTypeId::new(GLOBAL_LEVEL, TYPEOF_OPERATOR_RETURN_UNION_ID);
111+
pub const GLOBAL_T_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, T_ID);
112+
pub const GLOBAL_U_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, U_ID);
113+
pub const GLOBAL_FETCH_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, FETCH_ID);
114+
pub const GLOBAL_INSTANCEOF_REGEXP_ID: ResolvedTypeId =
115+
ResolvedTypeId::new(GLOBAL_LEVEL, INSTANCEOF_REGEXP_ID);
116+
pub const GLOBAL_REGEXP_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, REGEXP_ID);

crates/biome_js_type_info/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ mod conditionals;
44
mod flattening;
55
mod format_type_info;
66
mod globals;
7+
mod globals_builder;
8+
pub(crate) mod globals_ids;
79
mod helpers;
810
mod local_inference;
911
mod resolver;
@@ -13,7 +15,8 @@ mod type_store;
1315

1416
pub use conditionals::*;
1517
pub use flattening::MAX_FLATTEN_DEPTH;
16-
pub use globals::{GLOBAL_RESOLVER, GLOBAL_UNKNOWN_ID, GlobalsResolver, NUM_PREDEFINED_TYPES};
18+
pub use globals::{GLOBAL_RESOLVER, GlobalsResolver};
19+
pub use globals_ids::{GLOBAL_UNKNOWN_ID, NUM_PREDEFINED_TYPES};
1720
pub use resolver::*;
1821
pub use r#type::Type;
1922
pub use type_data::*;

crates/biome_js_type_info/src/resolver.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,12 @@ pub struct ResolvedTypeId(ResolverId, TypeId);
3434
impl Debug for ResolvedTypeId {
3535
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3636
if self.0.level() == TypeResolverLevel::Global {
37-
if self.1.index() < NUM_PREDEFINED_TYPES {
38-
f.write_str(global_type_name(self.1))
37+
// GlobalsResolverBuilder makes sure the type store is fully filled.
38+
// Every global TypeId whose index less than NUM_PREDEFINED_TYPES
39+
// must have a name returned by global_type_name().
40+
// GLOBAL_TYPE_MEMBERS ensures this invariant.
41+
if let Some(name) = global_type_name(self.1) {
42+
f.write_str(name)
3943
} else {
4044
let id = self.1.index() - NUM_PREDEFINED_TYPES;
4145
f.write_fmt(format_args!("Global TypeId({id})"))

0 commit comments

Comments
 (0)