Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/biome_js_type_info/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ biome_resolver = { workspace = true }
biome_rowan = { workspace = true }
camino = { workspace = true }
hashbrown = { workspace = true }
paste = "1.0"
rustc-hash = { workspace = true }

[dev-dependencies]
Expand Down
8 changes: 6 additions & 2 deletions crates/biome_js_type_info/src/format_type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -589,8 +589,12 @@ impl Format<FormatTypeContext> for TypeReference {
let level = resolved.level();
let id = resolved.id();
if level == TypeResolverLevel::Global {
if resolved.index() < NUM_PREDEFINED_TYPES {
write!(f, [token(global_type_name(id))])
// GlobalsResolverBuilder makes sure the type store is fully filled.
// Every global TypeId whose index is less than NUM_PREDEFINED_TYPES
// must have a name returned by global_type_name().
// GLOBAL_TYPE_MEMBERS ensures this invariant.
if let Some(name) = global_type_name(id) {
write!(f, [token(name)])
} else {
// Start counting from `NUM_PREDEFINED_TYPES` so
// snapshots remain stable even if we add new predefined
Expand Down
382 changes: 191 additions & 191 deletions crates/biome_js_type_info/src/globals.rs

Large diffs are not rendered by default.

55 changes: 55 additions & 0 deletions crates/biome_js_type_info/src/globals_builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//! Builder for constructing GlobalsResolver with forward references support.

use std::sync::Arc;

use crate::{NUM_PREDEFINED_TYPES, TypeData, TypeId, TypeStore};

use super::globals::GlobalsResolver;

/// Builder for constructing a GlobalsResolver
pub struct GlobalsResolverBuilder {
/// Types being built. None = reserved but not yet filled.
types: Vec<Option<TypeData>>,
}

impl GlobalsResolverBuilder {
pub fn with_capacity(capacity: usize) -> Self {
Self {
types: vec![None; capacity],
}
}

/// Fill a previously reserved type slot with actual type data.
pub fn set_type_data(&mut self, id: TypeId, data: TypeData) {
let index = id.index();
debug_assert!(
index < self.types.len(),
"TypeId {index} out of bounds (len: {})",
self.types.len()
);
debug_assert!(
self.types[index].is_none(),
"Type at index {index} already set"
);
self.types[index] = Some(data);
}

/// Build the final GlobalsResolver.
pub fn build(self) -> GlobalsResolver {
let types: Vec<Arc<TypeData>> = self
.types
.into_iter()
.map(|opt| Arc::new(opt.unwrap_or(TypeData::Unknown)))
.collect();

GlobalsResolver {
types: TypeStore::from_types(types),
}
}
}

impl Default for GlobalsResolverBuilder {
fn default() -> Self {
Self::with_capacity(NUM_PREDEFINED_TYPES)
}
}
116 changes: 116 additions & 0 deletions crates/biome_js_type_info/src/globals_ids.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//! Type ID constants for global types.
//!
//! TODO(tidefield): Implement a codegen for this file from TypeScript .d.ts files.

// use crate::define_global_type;
use crate::{ResolvedTypeId, TypeId};

use super::globals::GLOBAL_LEVEL;

// FIXME(tidefield): Get rid of the macro when implementing the codegen to improve compile time
// Right now, I'm preserving the names so that the snapshot tests don't break the snapshot tests
// to make sure I'm not breaking anything.
#[macro_export]
macro_rules! define_global_type {
($name:ident, $index:expr, $name_str:expr) => {
pub const $name: TypeId = TypeId::new($index);
paste::paste! {
Comment thread
tidefield marked this conversation as resolved.
pub const [<$name _NAME>]: &str = $name_str;
}
};
}
// define_global_type!(ARRAY_ID, 8, "Array"); // Creates ARRAY_ID and ARRAY_ID_NAME

// Type ID constants with their names defined together
define_global_type!(UNKNOWN_ID, 0, "unknown");
define_global_type!(UNDEFINED_ID, 1, "undefined");
define_global_type!(VOID_ID, 2, "void");
define_global_type!(CONDITIONAL_ID, 3, "conditional");
define_global_type!(NUMBER_ID, 4, "number");
define_global_type!(STRING_ID, 5, "string");
define_global_type!(INSTANCEOF_ARRAY_T_ID, 6, "instanceof Array<T>");
define_global_type!(INSTANCEOF_ARRAY_U_ID, 7, "instanceof Array<U>");
define_global_type!(ARRAY_ID, 8, "Array");
define_global_type!(ARRAY_FILTER_ID, 9, "Array.prototype.filter");
define_global_type!(ARRAY_FOREACH_ID, 10, "Array.prototype.forEach");
define_global_type!(ARRAY_MAP_ID, 11, "Array.prototype.map");
define_global_type!(GLOBAL_ID, 12, "globalThis");
define_global_type!(INSTANCEOF_PROMISE_ID, 13, "instanceof Promise");
define_global_type!(PROMISE_ID, 14, "Promise");
define_global_type!(PROMISE_CONSTRUCTOR_ID, 15, "Promise.constructor");
define_global_type!(PROMISE_CATCH_ID, 16, "Promise.prototype.catch");
define_global_type!(PROMISE_FINALLY_ID, 17, "Promise.prototype.finally");
define_global_type!(PROMISE_THEN_ID, 18, "Promise.prototype.then");
define_global_type!(PROMISE_ALL_ID, 19, "Promise.all");
define_global_type!(PROMISE_ALL_SETTLED_ID, 20, "Promise.allSettled");
define_global_type!(PROMISE_ANY_ID, 21, "Promise.any");
define_global_type!(PROMISE_RACE_ID, 22, "Promise.race");
define_global_type!(PROMISE_REJECT_ID, 23, "Promise.reject");
define_global_type!(PROMISE_RESOLVE_ID, 24, "Promise.resolve");
define_global_type!(PROMISE_TRY_ID, 25, "Promise.try");
define_global_type!(BIGINT_STRING_LITERAL_ID, 26, "\"bigint\"");
define_global_type!(BOOLEAN_STRING_LITERAL_ID, 27, "\"boolean\"");
define_global_type!(FUNCTION_STRING_LITERAL_ID, 28, "\"function\"");
define_global_type!(NUMBER_STRING_LITERAL_ID, 29, "\"number\"");
define_global_type!(OBJECT_STRING_LITERAL_ID, 30, "\"object\"");
define_global_type!(STRING_STRING_LITERAL_ID, 31, "\"string\"");
define_global_type!(SYMBOL_STRING_LITERAL_ID, 32, "\"symbol\"");
define_global_type!(UNDEFINED_STRING_LITERAL_ID, 33, "\"undefined\"");
define_global_type!(
TYPEOF_OPERATOR_RETURN_UNION_ID,
34,
"\"bigint\" | \"boolean\" | \"function\" | \"number\" | \"object\" | \"string\" | \"symbol\" | \"undefined\""
);
define_global_type!(T_ID, 35, "T");
define_global_type!(U_ID, 36, "U");
define_global_type!(CONDITIONAL_CALLBACK_ID, 37, "() => conditional");
define_global_type!(MAP_CALLBACK_ID, 38, "<U>(item: T) => U");
define_global_type!(VOID_CALLBACK_ID, 39, "() => void");
define_global_type!(FETCH_ID, 40, "fetch");
define_global_type!(INSTANCEOF_REGEXP_ID, 41, "instanceof RegExp");
define_global_type!(REGEXP_ID, 42, "RegExp");
define_global_type!(REGEXP_EXEC_ID, 43, "RegExp.exec");

/// Total number of predefined types.
/// Must be one more than the highest TypeId above.
pub const NUM_PREDEFINED_TYPES: usize = 44;

// Resolved type ID constants (TypeId wrapped with GlobalLevel)
pub const GLOBAL_UNKNOWN_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, UNKNOWN_ID);
pub const GLOBAL_UNDEFINED_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, UNDEFINED_ID);
pub const GLOBAL_VOID_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, VOID_ID);
pub const GLOBAL_CONDITIONAL_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, CONDITIONAL_ID);
pub const GLOBAL_NUMBER_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, NUMBER_ID);
pub const GLOBAL_STRING_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, STRING_ID);
pub const GLOBAL_ARRAY_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, ARRAY_ID);
pub const GLOBAL_GLOBAL_ID /* :smirk: */: ResolvedTypeId =
ResolvedTypeId::new(GLOBAL_LEVEL, GLOBAL_ID);
pub const GLOBAL_INSTANCEOF_PROMISE_ID: ResolvedTypeId =
ResolvedTypeId::new(GLOBAL_LEVEL, INSTANCEOF_PROMISE_ID);
pub const GLOBAL_PROMISE_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, PROMISE_ID);
pub const GLOBAL_PROMISE_CONSTRUCTOR_ID: ResolvedTypeId =
ResolvedTypeId::new(GLOBAL_LEVEL, PROMISE_CONSTRUCTOR_ID);
pub const GLOBAL_BIGINT_STRING_LITERAL_ID: ResolvedTypeId =
ResolvedTypeId::new(GLOBAL_LEVEL, BIGINT_STRING_LITERAL_ID);
pub const GLOBAL_BOOLEAN_STRING_LITERAL_ID: ResolvedTypeId =
ResolvedTypeId::new(GLOBAL_LEVEL, BOOLEAN_STRING_LITERAL_ID);
pub const GLOBAL_FUNCTION_STRING_LITERAL_ID: ResolvedTypeId =
ResolvedTypeId::new(GLOBAL_LEVEL, FUNCTION_STRING_LITERAL_ID);
pub const GLOBAL_NUMBER_STRING_LITERAL_ID: ResolvedTypeId =
ResolvedTypeId::new(GLOBAL_LEVEL, NUMBER_STRING_LITERAL_ID);
pub const GLOBAL_OBJECT_STRING_LITERAL_ID: ResolvedTypeId =
ResolvedTypeId::new(GLOBAL_LEVEL, OBJECT_STRING_LITERAL_ID);
pub const GLOBAL_STRING_STRING_LITERAL_ID: ResolvedTypeId =
ResolvedTypeId::new(GLOBAL_LEVEL, STRING_STRING_LITERAL_ID);
pub const GLOBAL_SYMBOL_STRING_LITERAL_ID: ResolvedTypeId =
ResolvedTypeId::new(GLOBAL_LEVEL, SYMBOL_STRING_LITERAL_ID);
pub const GLOBAL_UNDEFINED_STRING_LITERAL_ID: ResolvedTypeId =
ResolvedTypeId::new(GLOBAL_LEVEL, UNDEFINED_STRING_LITERAL_ID);
pub const GLOBAL_TYPEOF_OPERATOR_RETURN_UNION_ID: ResolvedTypeId =
ResolvedTypeId::new(GLOBAL_LEVEL, TYPEOF_OPERATOR_RETURN_UNION_ID);
pub const GLOBAL_T_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, T_ID);
pub const GLOBAL_U_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, U_ID);
pub const GLOBAL_FETCH_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, FETCH_ID);
pub const GLOBAL_INSTANCEOF_REGEXP_ID: ResolvedTypeId =
ResolvedTypeId::new(GLOBAL_LEVEL, INSTANCEOF_REGEXP_ID);
pub const GLOBAL_REGEXP_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, REGEXP_ID);
5 changes: 4 additions & 1 deletion crates/biome_js_type_info/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ mod conditionals;
mod flattening;
mod format_type_info;
mod globals;
mod globals_builder;
pub(crate) mod globals_ids;
mod helpers;
mod local_inference;
mod resolver;
Expand All @@ -13,7 +15,8 @@ mod type_store;

pub use conditionals::*;
pub use flattening::MAX_FLATTEN_DEPTH;
pub use globals::{GLOBAL_RESOLVER, GLOBAL_UNKNOWN_ID, GlobalsResolver, NUM_PREDEFINED_TYPES};
pub use globals::{GLOBAL_RESOLVER, GlobalsResolver};
pub use globals_ids::{GLOBAL_UNKNOWN_ID, NUM_PREDEFINED_TYPES};
pub use resolver::*;
pub use r#type::Type;
pub use type_data::*;
Expand Down
8 changes: 6 additions & 2 deletions crates/biome_js_type_info/src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,12 @@ pub struct ResolvedTypeId(ResolverId, TypeId);
impl Debug for ResolvedTypeId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.0.level() == TypeResolverLevel::Global {
if self.1.index() < NUM_PREDEFINED_TYPES {
f.write_str(global_type_name(self.1))
// GlobalsResolverBuilder makes sure the type store is fully filled.
// Every global TypeId whose index less than NUM_PREDEFINED_TYPES
// must have a name returned by global_type_name().
// GLOBAL_TYPE_MEMBERS ensures this invariant.
if let Some(name) = global_type_name(self.1) {
f.write_str(name)
} else {
let id = self.1.index() - NUM_PREDEFINED_TYPES;
f.write_fmt(format_args!("Global TypeId({id})"))
Expand Down