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
5 changes: 5 additions & 0 deletions .changeset/spotty-glasses-buy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@biomejs/biome": patch
---

Fixed [#11310](https://github.com/biomejs/biome/issues/11310): Restored the performance of [`noMisusedPromises`](https://biomejs.dev/linter/rules/no-misused-promises/) and [`noFloatingPromises`](https://biomejs.dev/linter/rules/no-floating-promises/) when analyzed expressions share deep imported type paths.
20 changes: 20 additions & 0 deletions crates/biome_db/src/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,26 @@ where
})
}

/// Counts `WillExecute` events for the tracked function named `query_name`.
///
/// This supports assertions for internal queries whose function item is not
/// visible to an integration test.
pub fn function_query_will_execute_count_by_name(
db: &dyn salsa::Database,
query_name: &str,
events: &[Event],
) -> usize {
events
.iter()
.filter(|event| {
let salsa::EventKind::WillExecute { database_key } = event.kind else {
return false;
};
db.ingredient_debug_name(database_key.ingredient_index()) == query_name
})
.count()
}

fn find_will_execute_event<'a, Q, I>(
db: &dyn salsa::Database,
query: Q,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* should generate diagnostics */

import { wrappedLoad } from "./wrapper";

const values = [1, 2, 3];

values.forEach((value) => wrappedLoad(value));
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: index.ts
---
# Input
```ts
/* should generate diagnostics */

import { wrappedLoad } from "./wrapper";

const values = [1, 2, 3];

values.forEach((value) => wrappedLoad(value));

```

# Diagnostics
```
index.ts:7:16 lint/nursery/noMisusedPromises ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

i This function returns a Promise, but no return value was expected.

5 │ const values = [1, 2, 3];
6 │
> 7 │ values.forEach((value) => wrappedLoad(value));
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
8 │

i This may not have the desired result if you expect the Promise to be `await`-ed.

i This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.


```
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/* should not generate diagnostics */

export async function load(value: number): Promise<number> {
return value;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: load.ts
---
# Input
```ts
/* should not generate diagnostics */

export async function load(value: number): Promise<number> {
return value;
}

```
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* should not generate diagnostics */

import { load } from "./load";

export function wrappedLoad(value: number) {
return load(value);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: wrapper.ts
---
# Input
```ts
/* should not generate diagnostics */

import { load } from "./load";

export function wrappedLoad(value: number) {
return load(value);
}

```
1 change: 1 addition & 0 deletions crates/biome_module_graph/src/db/queries/type_inference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub use interned::{
BindingTypeInput, CallArgumentTypeInput, CallExpressionTypeInput, ExpressionTypeInput,
LocalTypeInput, NormalizeTypeInput,
};
pub(crate) use interned::{BindingTypeWithImportBudgetInput, LocalTypeWithImportBudgetInput};
pub use lookups::{
find_member_type, find_value_member_type, infer_binding_type, infer_expression_type,
infer_local_type, resolve_callable_type,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@ pub struct LocalTypeInput<'db> {
pub type_id: InferredLocalTypeId,
}

/// Interned input for [`super::infer_binding_type_with_import_budget`].
#[salsa::interned]
#[derive(Debug)]
pub(crate) struct BindingTypeWithImportBudgetInput<'db> {
pub lookup: BindingTypeInput<'db>,
pub remaining: u8,
}

/// Interned input for [`super::infer_local_type_with_import_budget`].
#[salsa::interned]
#[derive(Debug)]
pub(crate) struct LocalTypeWithImportBudgetInput<'db> {
pub lookup: LocalTypeInput<'db>,
pub remaining: u8,
}

/// Interned input for [`super::infer_call_expression_type`].
#[salsa::interned]
#[derive(Debug)]
Expand Down
38 changes: 18 additions & 20 deletions crates/biome_module_graph/src/db/queries/type_inference/lookups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
//! same local-handle resolution path, allowing callers to inspect one type
//! without resolving every type collected for the module.

use super::{BindingTypeInput, ExpressionTypeInput, LocalTypeInput};
use super::{
BindingTypeInput, BindingTypeWithImportBudgetInput, ExpressionTypeInput, LocalTypeInput,
LocalTypeWithImportBudgetInput,
};
use crate::ModuleDb;
use crate::db::type_inference::{
ImportResolution, ResolutionCtx, find_member_type_on_demand as find_member_type_impl,
find_value_member_type_on_demand as find_value_member_type_impl, resolve_local_type_on_demand,
};
use crate::module_graph::{ModuleInfo, ModuleInfoKind};
use crate::module_graph::ModuleInfoKind;
use crate::type_inference::profiling::{
TypeInferenceProfileOrigin, TypeInferenceQueryKind, execute_query,
};
Expand Down Expand Up @@ -51,8 +54,7 @@ pub fn infer_expression_type<'db>(
}

let reference = js_info.raw_expressions.get(&expression)?.clone();
let mut ctx =
ResolutionCtx::new(db, module, &js_info, ImportResolution::on_demand(module));
let mut ctx = ResolutionCtx::new(db, module, &js_info, ImportResolution::on_demand());
Some(ctx.resolve(&reference))
},
)
Expand Down Expand Up @@ -80,7 +82,7 @@ pub fn infer_binding_type<'db>(
TypeInferenceQueryKind::Lookups,
TypeInferenceProfileOrigin::exact(module, range),
"infer_binding_type",
|| infer_binding_type_impl(db, input, ImportResolution::on_demand(module)),
|| infer_binding_type_impl(db, input, ImportResolution::on_demand()),
)
}

Expand All @@ -107,28 +109,28 @@ pub fn infer_local_type<'db>(
TypeInferenceQueryKind::Lookups,
TypeInferenceProfileOrigin::document(module),
"infer_local_type",
|| infer_local_type_impl(db, input, ImportResolution::on_demand(module)),
|| infer_local_type_impl(db, input, ImportResolution::on_demand()),
)
}

#[salsa::tracked(cycle_result=infer_binding_type_with_import_budget_cycle_result)]
pub(crate) fn infer_binding_type_with_import_budget<'db>(
db: &'db dyn ModuleDb,
input: BindingTypeInput<'db>,
root: ModuleInfo,
remaining: u8,
input: BindingTypeWithImportBudgetInput<'db>,
) -> Option<InferredTypeData<'db>> {
infer_binding_type_impl(db, input, ImportResolution::OnDemand { root, remaining })
let lookup = input.lookup(db);
let remaining = input.remaining(db);
infer_binding_type_impl(db, lookup, ImportResolution::OnDemand { remaining })
}

#[salsa::tracked(cycle_result=infer_local_type_with_import_budget_cycle_result)]
pub(crate) fn infer_local_type_with_import_budget<'db>(
db: &'db dyn ModuleDb,
input: LocalTypeInput<'db>,
root: ModuleInfo,
remaining: u8,
input: LocalTypeWithImportBudgetInput<'db>,
) -> Option<InferredTypeData<'db>> {
infer_local_type_impl(db, input, ImportResolution::OnDemand { root, remaining })
let lookup = input.lookup(db);
let remaining = input.remaining(db);
infer_local_type_impl(db, lookup, ImportResolution::OnDemand { remaining })
}

fn infer_binding_type_impl<'db>(
Expand Down Expand Up @@ -199,19 +201,15 @@ fn infer_local_type_cycle_result<'db>(
fn infer_binding_type_with_import_budget_cycle_result<'db>(
_db: &'db dyn ModuleDb,
_id: salsa::Id,
_input: BindingTypeInput<'db>,
_root: ModuleInfo,
_remaining: u8,
_input: BindingTypeWithImportBudgetInput<'db>,
) -> Option<InferredTypeData<'db>> {
Some(InferredTypeData::Unknown)
}

fn infer_local_type_with_import_budget_cycle_result<'db>(
_db: &'db dyn ModuleDb,
_id: salsa::Id,
_input: LocalTypeInput<'db>,
_root: ModuleInfo,
_remaining: u8,
_input: LocalTypeWithImportBudgetInput<'db>,
) -> Option<InferredTypeData<'db>> {
Some(InferredTypeData::Unknown)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,7 @@ pub fn infer_module_types<'db>(
);
whole_module
});
let result =
resolve_raw_types(db, module, &js_info, ImportResolution::on_demand(module));
let result = resolve_raw_types(db, module, &js_info, ImportResolution::on_demand());
if let Some(whole_module) = whole_module {
whole_module.complete();
}
Expand Down Expand Up @@ -179,8 +178,9 @@ pub(crate) fn inference_module_sccs(

// #region EXTERNAL INFERENCE ENTRY POINTS

// The scheduler remains untracked because `infer_module_types` caches each
// module result while this explicit work list prevents recursive stack growth.
// The scheduler is shared by tracked and untracked entry points. Its module
// queries cache inferred tables while this explicit work list prevents
// recursive stack growth.
/// Infers a module after preparing its resolved static import and re-export
/// dependencies.
///
Expand Down Expand Up @@ -213,16 +213,26 @@ pub fn infer_module_types_bottom_up<'db>(
pub(crate) fn infer_module_types_bottom_up_for_import_depth<'db>(
db: &'db dyn ModuleDb,
module: ModuleInfo,
implementation: TypeInferenceCodeReference,
) -> Option<&'db InferredModuleTypes<'db>> {
prepare_module_types_bottom_up_for_import_depth(db, module)
.then(|| infer_module_types_from_tables(db, module, module))?
}

#[salsa::tracked]
fn prepare_module_types_bottom_up_for_import_depth(db: &dyn ModuleDb, module: ModuleInfo) -> bool {
let whole_module = start_whole_module_inference_at(
TypeInferenceWholeModuleReason::ImportDepthLimit,
TypeInferenceProfileOrigin::Inherited,
implementation,
TypeInferenceCodeReference::new(
file!(),
line!(),
"prepare_module_types_bottom_up_for_import_depth",
),
);
let result = infer_module_types_bottom_up_impl(db, module, ModuleInferenceMode::FromTables);
let prepared =
infer_module_types_bottom_up_impl(db, module, ModuleInferenceMode::FromTables).is_some();
whole_module.complete();
result
prepared
}

fn infer_module_types_bottom_up_impl<'db>(
Expand Down
40 changes: 14 additions & 26 deletions crates/biome_module_graph/src/db/type_inference/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ use super::{
resolver::{MAX_RAW_TYPE_RESOLUTION_DEPTH, ResolutionCtx},
};
use crate::db::queries::{
BindingTypeInput, LocalTypeInput, SymbolFromModuleInfo, infer_binding_type,
BindingTypeInput, BindingTypeWithImportBudgetInput, LocalTypeInput,
LocalTypeWithImportBudgetInput, SymbolFromModuleInfo, infer_binding_type,
infer_binding_type_with_import_budget, infer_local_type, infer_local_type_with_import_budget,
infer_module_types_bottom_up_for_import_depth, inference_module_sccs, namespace_export_names,
resolved_export_origin,
};
use crate::module_graph::{ModuleInfo, ModuleInfoKind};
use crate::type_inference::TypeInferenceCodeReference;
use crate::{JsExport, JsImport, JsOwnExport, ModuleDb, ModuleGraphGeneration, ResolvedPath};
use biome_js_type_info::{
GlobalTypeId, ImportSymbol, Path, ResolvedTypeId, TypeImportQualifier, TypeReference,
Expand Down Expand Up @@ -328,12 +328,7 @@ pub(in crate::db) fn resolve_export_type_on_demand<'db>(
return None;
}

let ctx = ResolutionCtx::new(
db,
module,
&js_info,
super::ImportResolution::on_demand(module),
);
let ctx = ResolutionCtx::new(db, module, &js_info, super::ImportResolution::on_demand());
Some(ctx.resolve_export_name_on_demand(module, name))
}

Expand Down Expand Up @@ -407,24 +402,16 @@ impl<'db> ResolutionCtx<'db, '_> {
.map_or(InferredTypeData::Unknown, |types| {
self.resolve_import_symbol_from_tables(module, types, symbol)
}),
super::ImportResolution::OnDemand { root, remaining } => {
super::ImportResolution::OnDemand { remaining } => {
let sccs = inference_module_sccs(self.db, ModuleGraphGeneration::get(self.db));
if module == self.module || sccs.contains_cycle_between(self.module, module) {
return InferredTypeData::Unknown;
}
if remaining == 0 {
return infer_module_types_bottom_up_for_import_depth(
self.db,
module,
TypeInferenceCodeReference::new(
file!(),
line!(),
"ResolutionCtx::resolve_import_symbol",
),
)
.map_or(InferredTypeData::Unknown, |types| {
self.resolve_import_symbol_from_tables(module, types, symbol)
});
return infer_module_types_bottom_up_for_import_depth(self.db, module)
.map_or(InferredTypeData::Unknown, |types| {
self.resolve_import_symbol_from_tables(module, types, symbol)
});
}

let ModuleInfoKind::Js(js_info) = module.kind(self.db) else {
Expand All @@ -438,7 +425,6 @@ impl<'db> ResolutionCtx<'db, '_> {
module,
&js_info,
super::ImportResolution::OnDemand {
root,
remaining: remaining - 1,
},
);
Expand Down Expand Up @@ -854,8 +840,9 @@ fn inferred_type_from_binding_on_demand<'db>(

let input = BindingTypeInput::new(db, module, range);
match import_resolution {
super::ImportResolution::OnDemand { root, remaining } => {
infer_binding_type_with_import_budget(db, input, root, remaining)
super::ImportResolution::OnDemand { remaining } => {
let input = BindingTypeWithImportBudgetInput::new(db, input, remaining);
infer_binding_type_with_import_budget(db, input)
}
super::ImportResolution::FromTables { .. } | super::ImportResolution::CycleFallback(_) => {
infer_binding_type(db, input)
Expand Down Expand Up @@ -921,8 +908,9 @@ fn inferred_type_from_resolved_id_on_demand<'db>(
} else {
let input = LocalTypeInput::new(db, module, local_type_id);
match import_resolution {
super::ImportResolution::OnDemand { root, remaining } => {
infer_local_type_with_import_budget(db, input, root, remaining)
super::ImportResolution::OnDemand { remaining } => {
let input = LocalTypeWithImportBudgetInput::new(db, input, remaining);
infer_local_type_with_import_budget(db, input)
}
super::ImportResolution::FromTables { .. }
| super::ImportResolution::CycleFallback(_) => infer_local_type(db, input),
Expand Down
Loading
Loading