8000 perf: faster function parameters in inference (#6823) · biomejs/biome@eebc48e · GitHub
[go: up one dir, main page]

Skip to content

Commit eebc48e

Browse files
authored
perf: faster function parameters in inference (#6823)
1 parent a0ae867 commit eebc48e

25 files changed

+16550
-123
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@biomejs/biome": patch
3+
---
4+
5+
Improved [#6172](https://github.com/biomejs/biome/issues/6172): Optimised the way function arguments are stored in Biome's type inference. This led to about 10% performance improvement in `RedisCommander.d.ts` and about 2% on `@next/font` type definitions.

crates/biome_js_analyze/src/lint/nursery/no_misused_promises.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ fn find_misused_promise_returning_callback(
275275
{
276276
let callee_ty = ctx.type_of_expression(&call_expression.callee().ok()?);
277277
let function = callee_ty.as_function()?;
278-
callee_ty.resolve(&function.parameters.get(argument_index)?.ty)?
278+
callee_ty.resolve(function.parameters.get(argument_index)?.ty())?
279279
} else if let Some(new_expression) = argument_list
280280
.syntax()
281281
.ancestors()
@@ -290,7 +290,7 @@ fn find_misused_promise_returning_callback(
290290
.find(|member| member.kind == TypeMemberKind::Constructor)?;
291291
let constructor_ty = callee_ty.resolve(&constructor.ty)?;
292292
let constructor = constructor_ty.as_function()?;
293-
constructor_ty.resolve(&constructor.parameters.get(argument_index)?.ty)?
293+
constructor_ty.resolve(constructor.parameters.get(argument_index)?.ty())?
294294
} else {
295295
return None;
296296
};

crates/biome_js_type_info/src/flattening/expressions.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ fn infer_generic_arg(
470470
// If the parameter's type directly references the target, we replace all
471471
// the target's references to the generic with references to the concrete
472472
// argument type.
473-
if param.ty == *target_reference {
473+
if param.ty() == target_reference {
474474
target.update_all_references(|reference| {
475475
if reference == generic_reference {
476476
*reference = concrete_reference.clone();
@@ -480,7 +480,7 @@ fn infer_generic_arg(
480480
}
481481

482482
// Otherwise, we proceed by looking into the parameter type itself...
483-
let resolved_param = resolver.resolve_and_get(&param.ty)?;
483+
let resolved_param = resolver.resolve_and_get(param.ty())?;
484484

485485
// If the parameter type is a function, ie. callback, we try to infer from
486486
// the callback's return type.

crates/biome_js_type_info/src/format_type_info.rs

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ use crate::globals::global_type_name;
22
use crate::{
33
CallArgumentType, Class, DestructureField, Function, FunctionParameter,
44
FunctionParameterBinding, GenericTypeParameter, ImportSymbol, Interface, Literal,
5-
MergedReference, NUM_PREDEFINED_TYPES, Object, ObjectLiteral, ReturnType, Type, TypeData,
6-
TypeId, TypeImportQualifier, TypeInstance, TypeMember, TypeMemberKind, TypeReference,
7-
TypeReferenceQualifier, TypeResolverLevel, TypeofAwaitExpression, TypeofExpression, Union,
5+
MergedReference, NUM_PREDEFINED_TYPES, NamedFunctionParameter, Object, ObjectLiteral,
6+
PatternFunctionParameter, ReturnType, Type, TypeData, TypeId, TypeImportQualifier,
7+
TypeInstance, TypeMember, TypeMemberKind, TypeReference, TypeReferenceQualifier,
8+
TypeResolverLevel, TypeofAwaitExpression, TypeofExpression, Union,
89
};
910
use biome_formatter::prelude::*;
1011
use biome_formatter::{
@@ -242,6 +243,38 @@ impl Format<FormatTypeContext> for ReturnType {
242243
}
243244

244245
impl Format<FormatTypeContext> for FunctionParameter {
246+
fn fmt(&self, f: &mut Formatter<FormatTypeContext>) -> FormatResult<()> {
247+
match self {
248+
Self::Named(named) => write!(f, [named]),
249+
Self::Pattern(pattern) => write!(f, [pattern]),
250+
}
251+
}
252+
}
253+
254+
impl Format<FormatTypeContext> for NamedFunctionParameter {
255+
fn fmt(&self, f: &mut Formatter<FormatTypeContext>) -> FormatResult<()> {
256+
let optional = format_with(|f| {
257+
if self.is_optional {
258+
write!(f, [&format_args![text("optional")]])
259+
} else {
260+
write!(f, [&format_args![text("required")]])
261+
}
262+
});
263+
write!(
264+
f,
265+
[&group(&block_indent(&format_args![
266+
optional,
267+
space(),
268+
self.name,
269+
text(":"),
270+
space(),
271+
&self.ty,
272+
]))]
273+
)
274+
}
275+
}
276+
277+
impl Format<FormatTypeContext> for PatternFunctionParameter {
245278
fn fmt(&self, f: &mut Formatter<FormatTypeContext>) -> FormatResult<()> {
246279
let bindings = format_with(|f| {
247280
if !self.bindings.is_empty() {
@@ -264,11 +297,10 @@ impl Format<FormatTypeContext> for FunctionParameter {
264297
f,
265298
[&group(&format_args![
266299
text("..."),
267-
self.name.as_ref().unwrap_or(&Text::Static("(unnamed)")),
300+
bindings,
268301
text(":"),
269302
space(),
270303
&self.ty,
271-
bindings
272304
])]
273305
)
274306
} else {
@@ -284,11 +316,10 @@ impl Format<FormatTypeContext> for FunctionParameter {
284316
[&group(&block_indent(&format_args![
285317
optional,
286318
space(),
287-
self.name.as_ref().unwrap_or(&Text::Static("(unnamed)")),
319+
bindings,
288320
text(":"),
289321
space(),
290-
&self.ty,
291-
bindings
322+
&self.ty
292323
]))]
293324
)
294325
}
@@ -892,7 +923,7 @@ impl Format<FormatTypeContext> for FmtFunctionParameterBindings<'_> {
892923
format_with(|f| write!(f, [&format_args![text(","), soft_line_break_or_space()]]));
893924
let mut joiner = f.join_with(separator);
894925
for part in self.0 {
895-
joiner.entry(&format_args![&part.name, text(":"), &part.ty]);
926+
joiner.entry(&format_args![&part.name, text(":"), space(), &part.ty]);
896927
}
897928
joiner.finish()
898929
});

crates/biome_js_type_info/src/globals.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ use biome_js_syntax::AnyJsExpression;
1111
use biome_rowan::Text;
1212

1313
use crate::{
14-
Class, Function, FunctionParameter, GenericTypeParameter, Literal, Resolvable,
15-
ResolvedTypeData, ResolvedTypeId, ReturnType, ScopeId, TypeData, TypeId, TypeInstance,
16-
TypeMember, TypeMemberKind, TypeReference, TypeReferenceQualifier, TypeResolver,
14+
Class, Function, FunctionParameter, GenericTypeParameter, Literal, PatternFunctionParameter,
15+
Resolvable, ResolvedTypeData, ResolvedTypeId, ReturnType, ScopeId, TypeData, TypeId,
16+
TypeInstance, TypeMember, TypeMemberKind, TypeReference, TypeReferenceQualifier, TypeResolver,
1717
TypeResolverLevel, TypeStore, Union, flattening::MAX_FLATTEN_DEPTH,
1818
};
1919

@@ -191,13 +191,12 @@ impl Default for GlobalsResolver {
191191
is_async: false,
192192
type_parameters,
193193
name: Some(Text::Static(global_type_name(id))),
194-
parameters: [FunctionParameter {
195-
name: None,
194+
parameters: [FunctionParameter::Pattern(PatternFunctionParameter {
196195
bindings: Default::default(),
197196
is_optional: false,
198197
is_rest: false,
199198
ty: ResolvedTypeId::new(TypeResolverLevel::Global, param_type_id).into(),
200-
}]
199+
})]
201200
.into(),
202201
return_type: ReturnType::Type(
203202
ResolvedTypeId::new(TypeResolverLevel::Global, return_type_id).into(),
@@ -292,13 +291,12 @@ impl Default for GlobalsResolver {
292291
is_async: false,
293292
type_parameters: Default::default(),
294293
name: Some(Text::Static(global_type_name(PROMISE_CONSTRUCTOR_ID))),
295-
parameters: [FunctionParameter {
296-
name: None,
294+
parameters: [FunctionParameter::Pattern(PatternFunctionParameter {
297295
bindings: Default::default(),
298296
is_optional: false,
299297
is_rest: false,
300298
ty: ResolvedTypeId::new(GLOBAL_LEVEL, VOID_CALLBACK_ID).into(),
301-
}]
299+
})]
302300
.into(),
303301
return_type: ReturnType::Type(GLOBAL_VOID_ID.into()),
304302
}),
@@ -351,13 +349,12 @@ impl Default for GlobalsResolver {
351349
is_async: false,
352350
type_parameters: Default::default(),
353351
name: Some(Text::Static(global_type_name(MAP_CALLBACK_ID))),
354-
parameters: [FunctionParameter {
355-
name: None,
352+
parameters: [FunctionParameter::Pattern(PatternFunctionParameter {
356353
ty: GLOBAL_U_ID.into(),
357354
bindings: Default::default(),
358355
is_optional: false,
359356
is_rest: false,
360-
}]
357+
})]
361358
.into(),
362359
return_type: ReturnType::Type(GLOBAL_U_ID.into()),
363360
}),

crates/biome_js_type_info/src/local_inference.rs

Lines changed: 36 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ use crate::literal::{BooleanLiteral, NumberLiteral, StringLiteral};
2929
use crate::{
3030
AssertsReturnType, CallArgumentType, Class, Constructor, DestructureField, Function,
3131
FunctionParameter, FunctionParameterBinding, GLOBAL_UNKNOWN_ID, GenericTypeParameter,
32-
Interface, Literal, Module, Namespace, Object, PredicateReturnType, ResolvedTypeId, ReturnType,
33-
ScopeId, Tuple, TupleElementType, TypeData, TypeInstance, TypeMember, TypeMemberKind,
34-
TypeOperator, TypeOperatorType, TypeReference, TypeReferenceQualifier, TypeResolver,
32+
Interface, Literal, Module, NamedFunctionParameter, Namespace, Object,
33+
PatternFunctionParameter, PredicateReturnType, ResolvedTypeId, ReturnType, ScopeId, Tuple,
34+
TupleElementType, TypeData, TypeInstance, TypeMember, TypeMemberKind, TypeOperator,
35+
TypeOperatorType, TypeReference, TypeReferenceQualifier, TypeResolver,
3536
TypeofAdditionExpression, TypeofBitwiseNotExpression, TypeofCallExpression,
3637
TypeofConditionalExpression, TypeofDestructureExpression, TypeofExpression,
3738
TypeofLogicalAndExpression, TypeofLogicalOrExpression, TypeofNewExpression,
@@ -806,20 +807,13 @@ impl TypeData {
806807
Ok(AnyJsArrowFunctionParameters::AnyJsBinding(binding)) => {
807808
let name = binding
808809
.as_js_identifier_binding()
809-
.and_then(|binding| text_from_token(binding.name_token()));
810-
Box::new([FunctionParameter {
811-
bindings: name
812-
.iter()
813-
.map(|name| FunctionParameterBinding {
814-
name: name.clone(),
815-
ty: TypeReference::Unknown,
816-
})
817-
.collect(),
810+
.and_then(|binding| text_from_token(binding.name_token()))
811+
.unwrap_or_default();
812+
Box::new([FunctionParameter::Named(NamedFunctionParameter {
818813
name,
819814
ty: TypeReference::Unknown,
820815
is_optional: false,
821-
is_rest: false,
822-
}])
816+
})])
823817
}
824818
Ok(AnyJsArrowFunctionParameters::JsParameters(params)) => {
825819
function_params_from_js_params(resolver, scope_id, Ok(params))
@@ -1422,13 +1416,12 @@ impl FunctionParameter {
14221416
AnyJsParameter::AnyJsFormalParameter(AnyJsFormalParameter::JsFormalParameter(
14231417
param,
14241418
)) => Self::from_js_formal_parameter(resolver, scope_id, param),
1425-
AnyJsParameter::AnyJsFormalParameter(_) => Self {
1426-
name: None,
1419+
AnyJsParameter::AnyJsFormalParameter(_) => Self::Pattern(PatternFunctionParameter {
14271420
ty: TypeReference::Unknown,
14281421
bindings: [].into(),
14291422
is_optional: false,
14301423
is_rest: false,
1431-
},
1424+
}),
14321425
AnyJsParameter::JsRestParameter(param) => {
14331426
let ty = param
14341427
.type_annotation()
@@ -1444,25 +1437,22 @@ impl FunctionParameter {
14441437
)
14451438
})
14461439
.unwrap_or_default();
1447-
Self {
1448-
name: None,
1440+
Self::Pattern(PatternFunctionParameter {
14491441
ty: resolver.reference_to_owned_data(ty),
14501442
bindings,
14511443
is_optional: false,
14521444
is_rest: true,
1453-
}
1445+
})
14541446
}
1455-
AnyJsParameter::TsThisParameter(param) => Self {
1456-
name: Some(Text::Static("this")),
1447+
AnyJsParameter::TsThisParameter(param) => Self::Named(NamedFunctionParameter {
1448+
name: Text::Static("this"),
14571449
ty: param
14581450
.type_annotation()
14591451
.and_then(|annotation| annotation.ty().ok())
14601452
.map(|ty| TypeReference::from_any_ts_type(resolver, scope_id, &ty))
14611453
.unwrap_or_default(),
1462-
bindings: [].into(),
14631454
is_optional: false,
1464-
is_rest: false,
1465-
},
1455+
}),
14661456
}
14671457
}
14681458

@@ -1484,21 +1474,28 @@ impl FunctionParameter {
14841474
.and_then(|annotation| annotation.ty().ok())
14851475
.map(|ty| TypeData::from_any_ts_type(resolver, scope_id, &ty))
14861476
.unwrap_or_default();
1487-
let bindings = param
1488-
.binding()
1489-
.ok()
1490-
.and_then(|binding| {
1491-
FunctionParameterBinding::bindings_from_any_js_binding_pattern_of_type(
1492-
resolver, scope_id, &binding, &ty,
1493-
)
1477+
if let Some(name) = name {
1478+
Self::Named(NamedFunctionParameter {
1479+
name,
1480+
ty: resolver.reference_to_owned_data(ty),
1481+
is_optional: param.question_mark_token().is_some(),
1482+
})
1483+
} else {
1484+
let bindings = param
1485+
.binding()
1486+
.ok()
1487+
.and_then(|binding| {
1488+
FunctionParameterBinding::bindings_from_any_js_binding_pattern_of_type(
1489+
resolver, scope_id, &binding, &ty,
1490+
)
1491+
})
1492+
.unwrap_or_default();
1493+
Self::Pattern(PatternFunctionParameter {
1494+
bindings,
1495+
ty: resolver.reference_to_owned_data(ty),
1496+
is_optional: param.question_mark_token().is_some(),
1497+
is_rest: false,
14941498
})
1495-
.unwrap_or_default();
1496-
Self {
1497-
name,
1498-
ty: resolver.reference_to_owned_data(ty),
1499-
bindings,
1500-
is_optional: param.question_mark_token().is_some(),
1501-
is_rest: false,
15021499
}
15031500
}
15041501

crates/biome_js_type_info/src/type_info.rs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -718,20 +718,48 @@ impl Function {
718718

719719
/// Definition of a function argument.
720720
#[derive(Clone, Debug, Eq, Hash, PartialEq, Resolvable)]
721-
pub struct FunctionParameter {
722-
/// Name of the argument, if specified in the definition.
723-
pub name: Option<Text>,
721+
pub enum FunctionParameter {
722+
Named(NamedFunctionParameter),
723+
Pattern(PatternFunctionParameter),
724+
}
725+
726+
impl FunctionParameter {
727+
pub fn ty(&self) -> &TypeReference {
728+
match self {
729+
Self::Named(named) => &named.ty,
730+
Self::Pattern(pattern) => &pattern.ty,
731+
}
732+
}
733+
}
734+
735+
/// A plain function parameter where the name of the parameter is also the name
736+
/// of the binding.
737+
#[derive(Clone, Debug, Eq, Hash, PartialEq, Resolvable)]
738+
pub struct NamedFunctionParameter {
739+
/// Name of the parameter.
740+
pub name: Text,
724741

725-
/// Type of the argument.
742+
/// Type of the parameter.
726743
pub ty: TypeReference,
727744

745+
/// Whether the parameter is optional or not.
746+
pub is_optional: bool,
747+
}
748+
749+
/// A function parameter that is bound to either one or more positional
750+
/// parameters, and which may or may not be destructured into multiple bindings.
751+
#[derive(Clone, Debug, Eq, Hash, PartialEq, Resolvable)]
752+
pub struct PatternFunctionParameter {
728753
/// Bindings created for the parameter within the function body.
729754
pub bindings: Box<[FunctionParameterBinding]>,
730755

731-
/// Whether the argument is optional or not.
756+
/// Type of the parameter.
757+
pub ty: TypeReference,
758+
759+
/// Whether the parameter is optional or not.
732760
pub is_optional: bool,
733761

734-
/// Whether this is a rest argument (`...`) or not.
762+
/// Whether this is a rest parameter (`...`) or not.
735763
pub is_rest: bool,
736764
}
737765

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
@@ -25,7 +25,7 @@ Global TypeId(1) => Call unresolved reference "resolve" (scope ID: 0)(Global Typ
2525
Global TypeId(2) => sync Function {
2626
accepts: {
2727
params: [
28-
required resolve: unknown (bindings: resolve:unknown)
28+
required resolve: unknown
2929
]
3030
type_args: []
3131
}

crates/biome_js_type_info/tests/snapshots/infer_flattened_type_of_destructured_interface_field.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ function bar({ foo }: Foo) {}
1919
sync Function "bar" {
2020
accepts: {
2121
params: [
22-
required (unnamed): Module(0) TypeId(1) (bindings: foo:Module(0) TypeId(2))
22+
required (bindings: foo: Module(0) TypeId(2)): Module(0) TypeId(1)
2323
]
2424
type_args: []
2525
}

0 commit comments

Comments
 (0)
0