Skip to content

Commit 7d963f3

Browse files
authored
chore: add regex globals (#8166)
1 parent bf1a836 commit 7d963f3

File tree

34 files changed

+202
-107
lines changed

34 files changed

+202
-107
lines changed

crates/biome_js_type_info/src/format_type_info.rs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -788,23 +788,50 @@ impl Format<FormatTypeContext> for Interface {
788788

789789
impl Format<FormatTypeContext> for Literal {
790790
fn fmt(&self, f: &mut Formatter<FormatTypeContext>) -> FormatResult<()> {
791-
write!(f, [&format_args![token("value:"), space()]])?;
792791
match self {
793-
Self::BigInt(bigint_text) => write!(f, [text(bigint_text, TextSize::default())]),
792+
Self::BigInt(bigint_text) => write!(
793+
f,
794+
[
795+
token("bigint:"),
796+
space(),
797+
text(bigint_text, TextSize::default())
798+
]
799+
),
794800
Self::Boolean(lit) => write!(
795801
f,
796-
[text(
797-
lit.as_bool().to_string().as_str(),
798-
TextSize::default()
799-
)]
802+
[
803+
token("bool:"),
804+
space(),
805+
text(lit.as_bool().to_string().as_str(), TextSize::default())
806+
]
800807
),
801808
Self::Number(lit) => {
802-
write!(f, [text(lit.as_str(), TextSize::default())])
809+
write!(
810+
f,
811+
[
812+
token("number:"),
813+
space(),
814+
text(lit.as_str(), TextSize::default())
815+
]
816+
)
803817
}
804818
Self::Object(obj) => write!(f, [&obj]),
805-
Self::RegExp(regexp_text) => write!(f, [text(regexp_text, TextSize::default())]),
806-
Self::String(lit) => write!(f, [text(lit.as_str(), TextSize::default())]),
807-
Self::Template(template_text) => write!(f, [text(template_text, TextSize::default())]),
819+
Self::RegExp(regex) => write!(
820+
f,
821+
[token("regex:"), space(), text(regex, TextSize::default())]
822+
),
823+
Self::String(lit) => write!(
824+
f,
825+
[
826+
token("string:"),
827+
space(),
828+
text(lit.as_str(), TextSize::default())
829+
]
830+
),
831+
Self::Template(tmpl) => write!(
832+
f,
833+
[token("string:"), space(), text(tmpl, TextSize::default())]
834+
),
808835
}
809836
}
810837
}

crates/biome_js_type_info/src/globals.rs

Lines changed: 62 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Hardcoded global definitions.
22
3-
// FIXME: Implement inference from type definitions.
3+
// FIXME: Implement inference from type definitions: https://github.com/biomejs/biome/issues/5977
44

55
use std::{
66
borrow::Cow,
@@ -59,22 +59,25 @@ pub const PROMISE_RACE_ID: TypeId = TypeId::new(22);
5959
pub const PROMISE_REJECT_ID: TypeId = TypeId::new(23);
6060
pub const PROMISE_RESOLVE_ID: TypeId = TypeId::new(24);
6161
pub const PROMISE_TRY_ID: TypeId = TypeId::new(25);
62-
pub const BIGINT_STRING_LITERAL_ID: TypeId = TypeId::new(26);
63-
pub const BOOLEAN_STRING_LITERAL_ID: TypeId = TypeId::new(27);
64-
pub const FUNCTION_STRING_LITERAL_ID: TypeId = TypeId::new(28);
65-
pub const NUMBER_STRING_LITERAL_ID: TypeId = TypeId::new(29);
66-
pub const OBJECT_STRING_LITERAL_ID: TypeId = TypeId::new(30);
67-
pub const STRING_STRING_LITERAL_ID: TypeId = TypeId::new(31);
68-
pub const SYMBOL_STRING_LITERAL_ID: TypeId = TypeId::new(32);
69-
pub const UNDEFINED_STRING_LITERAL_ID: TypeId = TypeId::new(33);
70-
pub const TYPEOF_OPERATOR_RETURN_UNION_ID: TypeId = TypeId::new(34);
71-
pub const T_ID: TypeId = TypeId::new(35);
72-
pub const U_ID: TypeId = TypeId::new(36);
73-
pub const CONDITIONAL_CALLBACK_ID: TypeId = TypeId::new(37);
74-
pub const MAP_CALLBACK_ID: TypeId = TypeId::new(38);
75-
pub const VOID_CALLBACK_ID: TypeId = TypeId::new(39);
76-
pub const FETCH_ID: TypeId = TypeId::new(40);
77-
pub const NUM_PREDEFINED_TYPES: usize = 41; // Must be one more than the highest `TypeId` above.
62+
pub const INSTANCEOF_REGEXP_ID: TypeId = TypeId::new(26);
63+
pub const REGEXP_ID: TypeId = TypeId::new(27);
64+
pub const REGEXP_EXEC_ID: TypeId = TypeId::new(28);
65+
pub const BIGINT_STRING_LITERAL_ID: TypeId = TypeId::new(29);
66+
pub const BOOLEAN_STRING_LITERAL_ID: TypeId = TypeId::new(30);
67+
pub const FUNCTION_STRING_LITERAL_ID: TypeId = TypeId::new(31);
68+
pub const NUMBER_STRING_LITERAL_ID: TypeId = TypeId::new(32);
69+
pub const OBJECT_STRING_LITERAL_ID: TypeId = TypeId::new(33);
70+
pub const STRING_STRING_LITERAL_ID: TypeId = TypeId::new(34);
71+
pub const SYMBOL_STRING_LITERAL_ID: TypeId = TypeId::new(35);
72+
pub const UNDEFINED_STRING_LITERAL_ID: TypeId = TypeId::new(36);
73+
pub const TYPEOF_OPERATOR_RETURN_UNION_ID: TypeId = TypeId::new(37);
74+
pub const T_ID: TypeId = TypeId::new(38);
75+
pub const U_ID: TypeId = TypeId::new(39);
76+
pub const CONDITIONAL_CALLBACK_ID: TypeId = TypeId::new(40);
77+
pub const MAP_CALLBACK_ID: TypeId = TypeId::new(41);
78+
pub const VOID_CALLBACK_ID: TypeId = TypeId::new(42);
79+
pub const FETCH_ID: TypeId = TypeId::new(43);
80+
pub const NUM_PREDEFINED_TYPES: usize = 44; // Must be one more than the highest `TypeId` above.
7881

7982
pub const GLOBAL_UNKNOWN_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, UNKNOWN_ID);
8083
pub const GLOBAL_UNDEFINED_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, UNDEFINED_ID);
@@ -83,6 +86,9 @@ pub const GLOBAL_CONDITIONAL_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEV
8386
pub const GLOBAL_NUMBER_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, NUMBER_ID);
8487
pub const GLOBAL_STRING_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, STRING_ID);
8588
pub const GLOBAL_ARRAY_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, ARRAY_ID);
89+
pub const GLOBAL_INSTANCEOF_REGEXP_ID: ResolvedTypeId =
90+
ResolvedTypeId::new(GLOBAL_LEVEL, INSTANCEOF_REGEXP_ID);
91+
pub const GLOBAL_REGEXP_ID: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, REGEXP_ID);
8692
pub const GLOBAL_GLOBAL_ID /* :smirk: */: ResolvedTypeId = ResolvedTypeId::new(GLOBAL_LEVEL, GLOBAL_ID);
8793
pub const GLOBAL_INSTANCEOF_PROMISE_ID: ResolvedTypeId =
8894
ResolvedTypeId::new(GLOBAL_LEVEL, INSTANCEOF_PROMISE_ID);
@@ -140,24 +146,27 @@ pub fn global_type_name(id: TypeId) -> &'static str {
140146
23 => "Promise.reject",
141147
24 => "Promise.resolve",
142148
25 => "Promise.try",
143-
26 => "\"bigint\"",
144-
27 => "\"boolean\"",
145-
28 => "\"function\"",
146-
29 => "\"number\"",
147-
30 => "\"object\"",
148-
31 => "\"string\"",
149-
32 => "\"symbol\"",
150-
33 => "\"undefined\"",
151-
34 => {
149+
26 => "instanceof RegExp",
150+
27 => "RegExp",
151+
28 => "RegExp.exec",
152+
29 => "\"bigint\"",
153+
30 => "\"boolean\"",
154+
31 => "\"function\"",
155+
32 => "\"number\"",
156+
33 => "\"object\"",
157+
34 => "\"string\"",
158+
35 => "\"symbol\"",
159+
36 => "\"undefined\"",
160+
37 => {
152161
"\"bigint\" | \"boolean\" | \"function\" | \"number\" | \"object\" \
153162
| \"string\" | \"symbol\" | \"undefined\""
154163
}
155-
35 => "T",
156-
36 => "U",
157-
37 => "() => conditional",
158-
38 => "<U>(item: T) => U",
159-
39 => "() => void",
160-
40 => "fetch",
164+
38 => "T",
165+
39 => "U",
166+
40 => "() => conditional",
167+
41 => "<U>(item: T) => U",
168+
42 => "() => void",
169+
43 => "fetch",
161170
_ => "inferred type",
162171
}
163172
}
@@ -215,6 +224,16 @@ impl Default for GlobalsResolver {
215224
})
216225
};
217226

227+
let regexp_method_definition = |id: TypeId| {
228+
TypeData::from(Function {
229+
is_async: false,
230+
type_parameters: Default::default(),
231+
name: Some(Text::new_static(global_type_name(id))),
232+
parameters: Default::default(),
233+
return_type: ReturnType::Type(GLOBAL_INSTANCEOF_REGEXP_ID.into()),
234+
})
235+
};
236+
218237
let string_literal = |value: &'static str| -> TypeData {
219238
TypeData::from(Literal::String(Text::new_static(value).into()))
220239
};
@@ -311,6 +330,15 @@ impl Default for GlobalsResolver {
311330
promise_method_definition(PROMISE_REJECT_ID),
312331
promise_method_definition(PROMISE_RESOLVE_ID),
313332
promise_method_definition(PROMISE_TRY_ID),
333+
TypeData::instance_of(TypeReference::from(GLOBAL_REGEXP_ID)),
334+
TypeData::Class(Box::new(Class {
335+
name: Some(Text::new_static("RegExp")),
336+
type_parameters: Box::default(),
337+
extends: None,
338+
implements: [].into(),
339+
members: Box::new([method("exec", REGEXP_EXEC_ID)]),
340+
})),
341+
regexp_method_definition(REGEXP_EXEC_ID),
314342
string_literal("bigint"),
315343
string_literal("boolean"),
316344
string_literal("function"),
@@ -454,6 +482,8 @@ impl TypeResolver for GlobalsResolver {
454482
Some(GLOBAL_ARRAY_ID)
455483
} else if qualifier.is_promise() && !qualifier.has_known_type_parameters() {
456484
Some(GLOBAL_PROMISE_ID)
485+
} else if qualifier.is_regex() && !qualifier.has_known_type_parameters() {
486+
Some(GLOBAL_REGEXP_ID)
457487
} else if !qualifier.type_only
458488
&& let Some(ident) = qualifier.path.identifier()
459489
{

crates/biome_js_type_info/src/type_data.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,6 +1402,17 @@ impl TypeReferenceQualifier {
14021402
self.path.is_identifier("Promise")
14031403
}
14041404

1405+
/// Checks whether this type qualifier references the `RegExp` type.
1406+
///
1407+
/// This method simply checks whether the reference is for a literal
1408+
/// `RegExp`, without considering whether another symbol named `RegExp` is
1409+
/// in scope. It can be used _after_ type resolution has failed to find a
1410+
/// `RegExp` symbol in scope, but should not be used _instead of_ such type
1411+
/// resolution.
1412+
pub fn is_regex(&self) -> bool {
1413+
self.path.is_identifier("RegExp")
1414+
}
1415+
14051416
pub fn with_excluded_binding_id(mut self, binding_id: BindingId) -> Self {
14061417
self.excluded_binding_id = Some(binding_id);
14071418
self

crates/biome_js_type_info/tests/local_inference.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,17 @@ fn infer_type_of_object_member_expression() {
3434
);
3535
}
3636

37+
#[test]
38+
fn infer_type_of_regex() {
39+
const CODE: &str = r#"/ab+c/"#;
40+
41+
let root = parse_ts(CODE);
42+
let expr = get_expression(&root);
43+
let mut resolver = GlobalsResolver::default();
44+
let ty = TypeData::from_any_js_expression(&mut resolver, ScopeId::GLOBAL, &expr);
45+
assert_type_data_snapshot(CODE, &ty, &resolver, "infer_type_of_regex");
46+
}
47+
3748
#[test]
3849
fn infer_type_of_typeof_expression() {
3950
const CODE: &str = r#"typeof foo"#;

crates/biome_js_type_info/tests/snapshots/infer_flattened_type_from_direct_promise_instance.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ instanceof Promise
1818
## Registered types
1919

2020
```
21-
Global TypeId(0) => value: value
21+
Global TypeId(0) => string: value
2222
2323
Global TypeId(1) => Call unresolved reference "resolve" (scope ID: 0)(Global TypeId(0))
2424

crates/biome_js_type_info/tests/snapshots/infer_flattened_type_from_static_promise_function.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ instanceof Promise
2020
```
2121
Global TypeId(0) => Promise.resolve
2222
23-
Global TypeId(1) => value: value
23+
Global TypeId(1) => string: value
2424
```

crates/biome_js_type_info/tests/snapshots/infer_flattened_type_of_typeof_expression.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ typeof foo;
2020
## Registered types
2121

2222
```
23-
Thin TypeId(0) => value: foo
23+
Thin TypeId(0) => string: foo
2424
25-
Global TypeId(0) => value: foo
25+
Global TypeId(0) => string: foo
2626
```

crates/biome_js_type_info/tests/snapshots/infer_resolved_type_from_direct_promise_instance.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ new Promise
1818
## Registered types
1919

2020
```
21-
Global TypeId(0) => value: value
21+
Global TypeId(0) => string: value
2222
2323
Global TypeId(1) => Call unresolved reference "resolve" (scope ID: 0)(Global TypeId(0))
2424

crates/biome_js_type_info/tests/snapshots/infer_resolved_type_from_static_promise_function.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@ Call Global TypeId(0)(Global TypeId(1))
2020
```
2121
Global TypeId(0) => Promise.resolve
2222
23-
Global TypeId(1) => value: value
23+
Global TypeId(1) => string: value
2424
```

crates/biome_js_type_info/tests/snapshots/infer_type_of_binary_expression_eq.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ const a = 1 === 1;
1212
## Result
1313

1414
```
15-
a => value: true
15+
a => bool: true
1616
1717
```
1818

1919
## Registered types
2020

2121
```
22-
Global TypeId(0) => value: true
22+
Global TypeId(0) => bool: true
2323
```

0 commit comments

Comments
 (0)