@@ -5,10 +5,10 @@ use biome_console::markup;
5
5
use biome_js_factory:: make;
6
6
use biome_js_semantic:: SemanticModel ;
7
7
use biome_js_syntax:: {
8
- binding_ext:: AnyJsBindingDeclaration , AnyJsExpression , AnyTsType , JsArrowFunctionExpression ,
9
- JsCallExpression , JsExpressionStatement , JsFunctionDeclaration , JsIdentifierExpression ,
10
- JsMethodClassMember , JsMethodObjectMember , JsStaticMemberExpression , JsSyntaxKind ,
11
- JsVariableDeclarator , TsReturnTypeAnnotation ,
8
+ binding_ext:: AnyJsBindingDeclaration , global_identifier , AnyJsExpression , AnyTsType ,
9
+ JsArrowFunctionExpression , JsCallExpression , JsExpressionStatement , JsFunctionDeclaration ,
10
+ JsIdentifierExpression , JsMethodClassMember , JsMethodObjectMember , JsStaticMemberExpression ,
11
+ JsSyntaxKind , JsVariableDeclarator , TsReturnTypeAnnotation ,
12
12
} ;
13
13
use biome_rowan:: { AstNode , AstSeparatedList , BatchMutationExt , SyntaxNodeCast , TriviaPieceKind } ;
14
14
@@ -376,6 +376,8 @@ fn is_handled_promise(js_call_expression: &JsCallExpression) -> Option<bool> {
376
376
/// async function returnsPromise(): Promise<void> {}
377
377
///
378
378
/// returnsPromise().then(() => null).catch(() => {});
379
+ ///
380
+ /// globalThis.Promise.reject('value').finally();
379
381
/// ```
380
382
///
381
383
/// Example TypeScript code that would return `false`:
@@ -389,46 +391,23 @@ fn is_member_expression_callee_a_promise(
389
391
model : & SemanticModel ,
390
392
) -> Option < bool > {
391
393
let expr = static_member_expr. object ( ) . ok ( ) ?;
394
+
395
+ if is_expression_an_promise ( & expr) {
396
+ return Some ( true ) ;
397
+ }
398
+
392
399
match expr {
393
400
AnyJsExpression :: JsCallExpression ( js_call_expr) => {
394
401
let callee = js_call_expr. callee ( ) . ok ( ) ?;
395
402
is_callee_a_promise ( & callee, model)
396
403
}
397
- AnyJsExpression :: JsIdentifierExpression ( js_ident_expr) => Some (
398
- is_expression_an_promise ( & js_ident_expr) . unwrap_or_default ( )
399
- || is_binding_a_promise ( & js_ident_expr, model) . unwrap_or_default ( ) ,
400
- ) ,
404
+ AnyJsExpression :: JsIdentifierExpression ( js_ident_expr) => {
405
+ is_binding_a_promise ( & js_ident_expr, model)
406
+ }
401
407
_ => Some ( false ) ,
402
408
}
403
409
}
404
410
405
- /// Checks if a `JsIdentifierExpression` represents a `Promise`.
406
- ///
407
- /// This function inspects a given `JsIdentifierExpression` to determine if it represents a `Promise`.
408
- /// It returns `Some(true)` if the identifier is `Promise`, `Some(false)` if it is not, and `None` if there is an error in the process.
409
- ///
410
- /// # Arguments
411
- ///
412
- /// * `js_ident_expr` - A reference to a `JsIdentifierExpression` to check.
413
- ///
414
- /// # Returns
415
- ///
416
- /// * `Some(true)` if the identifier is `Promise`.
417
- /// * `Some(false)` if the identifier is not `Promise`.
418
- /// * `None` if there is an error in the process.
419
- ///
420
- /// # Examples
421
- ///
422
- /// Example TypeScript code that would return `Some(true)`:
423
- /// ```typescript
424
- /// Promise.resolve('value').then(() => { })
425
- /// Promise.all([p1, p2, p3])
426
- /// ```
427
- fn is_expression_an_promise ( js_ident_expr : & JsIdentifierExpression ) -> Option < bool > {
428
- let js_reference_identifier = js_ident_expr. name ( ) . ok ( ) ?;
429
- Some ( js_reference_identifier. has_name ( "Promise" ) )
430
- }
431
-
432
411
/// Checks if the given `JsExpressionStatement` is within an async function.
433
412
///
434
413
/// This function traverses up the syntax tree from the given expression node
@@ -495,6 +474,8 @@ fn is_in_async_function(node: &JsExpressionStatement) -> bool {
495
474
/// }
496
475
///
497
476
/// const promise = new Promise((resolve) => resolve('value'));
477
+ ///
478
+ /// const promiseWithGlobalIdentifier = new window.Promise((resolve, reject) => resolve('value'));
498
479
/// ```
499
480
fn is_variable_initializer_a_promise (
500
481
js_variable_declarator : & JsVariableDeclarator ,
@@ -513,9 +494,7 @@ fn is_variable_initializer_a_promise(
513
494
) ,
514
495
AnyJsExpression :: JsNewExpression ( js_new_epr) => {
515
496
let any_js_expr = js_new_epr. callee ( ) . ok ( ) ?;
516
- let js_ident_expr = any_js_expr. as_js_identifier_expression ( ) ?;
517
- let reference = js_ident_expr. name ( ) . ok ( ) ?;
518
- Some ( reference. has_name ( "Promise" ) )
497
+ Some ( is_expression_an_promise ( & any_js_expr) )
519
498
}
520
499
_ => Some ( false ) ,
521
500
}
@@ -569,3 +548,35 @@ fn is_variable_annotation_a_promise(js_variable_declarator: &JsVariableDeclarato
569
548
_ => Some ( false ) ,
570
549
}
571
550
}
551
+
552
+ /// Checks if an expression is a `Promise`.
553
+ ///
554
+ /// This function inspects a given `AnyJsExpression` to determine if it represents a `Promise`,
555
+ /// either as a global identifier (e.g., `window.Promise`) or directly (e.g., `Promise.resolve`).
556
+ /// It returns `true` if the expression is a `Promise`, otherwise `false`.
557
+ ///
558
+ /// # Arguments
559
+ ///
560
+ /// * `expr` - A reference to an `AnyJsExpression` to check.
561
+ ///
562
+ /// # Returns
563
+ ///
564
+ /// * `true` if the expression is a `Promise`.
565
+ /// * `false` otherwise.
566
+ ///
567
+ /// # Examples
568
+ ///
569
+ /// Example TypeScript code that would return `true`:
570
+ /// ```typescript
571
+ /// window.Promise.resolve();
572
+ /// globalThis.Promise.resolve();
573
+ /// Promise.resolve('value').then(() => { });
574
+ /// Promise.all([p1, p2, p3]);
575
+ /// ```
576
+ ///
577
+ fn is_expression_an_promise ( expr : & AnyJsExpression ) -> bool {
578
+ if let Some ( ( _, value) ) = global_identifier ( expr) {
579
+ return value. text ( ) == "Promise" ;
580
+ }
581
+ false
582
+ }
0 commit comments