11use biome_analyze:: { Rule , RuleDiagnostic , RuleSource , context:: RuleContext , declare_lint_rule} ;
22use biome_console:: markup;
33use biome_js_semantic:: SemanticModel ;
4- use biome_js_syntax:: { AnyJsBindingPattern , JsCatchClause , JsThrowStatement } ;
4+ use biome_js_syntax:: {
5+ AnyJsBindingPattern , AnyJsObjectMember , JsCatchClause , JsIdentifierBinding ,
6+ JsReferenceIdentifier , JsThrowStatement ,
7+ } ;
58use biome_rowan:: { AstNode , AstSeparatedList , TextRange } ;
69use biome_rule_options:: use_error_cause:: UseErrorCauseOptions ;
710
@@ -199,32 +202,59 @@ impl Rule for UseErrorCause {
199202 } ;
200203
201204 for member in obj_expr. members ( ) . iter ( ) . flatten ( ) {
202- if let Some ( prop) = member. as_js_property_object_member ( ) {
203- let is_cause_prop = prop
204- . name ( )
205- . ok ( )
206- . and_then ( |name_node| name_node. name ( ) )
207- . is_some_and ( |name| name == "cause" ) ;
205+ match member {
206+ AnyJsObjectMember :: JsPropertyObjectMember ( prop) => {
207+ let is_cause_prop = prop
208+ . name ( )
209+ . ok ( )
210+ . and_then ( |name_node| name_node. name ( ) )
211+ . is_some_and ( |name| name == "cause" ) ;
208212
209- if is_cause_prop && let Ok ( value) = prop. value ( ) {
210- match is_cause_value_correct_error (
211- & value,
212- identifier_binding,
213- model,
214- ) {
215- CauseValueCheckResult :: Correct => return None ,
216- CauseValueCheckResult :: Shadowed => {
217- return Some ( State :: ShadowedCause {
218- cause_range : value. range ( ) ,
219- catch_binding_range : identifier_binding. range ( ) ,
220- } ) ;
213+ if is_cause_prop && let Ok ( value) = prop. value ( ) {
214+ match is_cause_value_correct_error (
215+ & value,
216+ identifier_binding,
217+ model,
218+ ) {
219+ CauseValueCheckResult :: Correct => return None ,
220+ CauseValueCheckResult :: Shadowed => {
221+ return Some ( State :: ShadowedCause {
222+ cause_range : value. range ( ) ,
223+ catch_binding_range : identifier_binding. range ( ) ,
224+ } ) ;
225+ }
226+ CauseValueCheckResult :: Incorrect => {
227+ // Continue checking other properties, another `cause` might be present.
228+ // This is unlikely to be valid JS, but we handle it.
229+ }
221230 }
222- CauseValueCheckResult :: Incorrect => {
223- // Continue checking other properties, another `cause` might be present.
224- // This is unlikely to be valid JS, but we handle it.
231+ }
232+ }
233+ AnyJsObjectMember :: JsShorthandPropertyObjectMember ( prop) => {
234+ if let Ok ( cause_reference) = prop. name ( )
235+ && cause_reference
236+ . value_token ( )
237+ . is_ok_and ( |token| token. text_trimmed ( ) == "cause" )
238+ {
239+ match is_cause_reference_correct_error (
240+ & cause_reference,
241+ identifier_binding,
242+ model,
243+ ) {
244+ CauseValueCheckResult :: Correct => return None ,
245+ CauseValueCheckResult :: Shadowed => {
246+ return Some ( State :: ShadowedCause {
247+ cause_range : cause_reference. range ( ) ,
248+ catch_binding_range : identifier_binding. range ( ) ,
249+ } ) ;
250+ }
251+ CauseValueCheckResult :: Incorrect => {
252+ // Continue checking other properties, another `cause` might be present.
253+ }
225254 }
226255 }
227256 }
257+ _ => { }
228258 }
229259 }
230260
@@ -299,7 +329,7 @@ enum CauseValueCheckResult {
299329
300330fn is_cause_value_correct_error (
301331 value : & biome_js_syntax:: AnyJsExpression ,
302- catch_error_binding : & biome_js_syntax :: JsIdentifierBinding ,
332+ catch_error_binding : & JsIdentifierBinding ,
303333 model : & SemanticModel ,
304334) -> CauseValueCheckResult {
305335 let Some ( cause_identifier_expr) = value. as_js_identifier_expression ( ) else {
@@ -309,7 +339,25 @@ fn is_cause_value_correct_error(
309339 return CauseValueCheckResult :: Incorrect ;
310340 } ;
311341
312- let Some ( cause_binding) = model. binding ( & cause_reference) else {
342+ is_cause_reference_correct_error ( & cause_reference, catch_error_binding, model)
343+ }
344+
345+ fn is_cause_reference_correct_error (
346+ cause_reference : & JsReferenceIdentifier ,
347+ catch_error_binding : & JsIdentifierBinding ,
348+ model : & SemanticModel ,
349+ ) -> CauseValueCheckResult {
350+ let cause_name = cause_reference. value_token ( ) . ok ( ) ;
351+ let catch_name = catch_error_binding. name_token ( ) . ok ( ) ;
352+
353+ let Some ( cause_binding) = model. binding ( cause_reference) else {
354+ if cause_name. as_ref ( ) . map ( |t| t. text_trimmed ( ) )
355+ == catch_name. as_ref ( ) . map ( |t| t. text_trimmed ( ) )
356+ {
357+ // Fall back to textual equality when semantic binding data is unavailable.
358+ return CauseValueCheckResult :: Correct ;
359+ }
360+
313361 return CauseValueCheckResult :: Incorrect ;
314362 } ;
315363
@@ -318,12 +366,6 @@ fn is_cause_value_correct_error(
318366 if cause_binding == catch_binding {
319367 CauseValueCheckResult :: Correct
320368 } else {
321- let cause_name = cause_identifier_expr
322- . name ( )
323- . ok ( )
324- . and_then ( |n| n. value_token ( ) . ok ( ) ) ;
325- let catch_name = catch_error_binding. name_token ( ) . ok ( ) ;
326-
327369 if cause_name. as_ref ( ) . map ( |t| t. text_trimmed ( ) )
328370 == catch_name. as_ref ( ) . map ( |t| t. text_trimmed ( ) )
329371 {
0 commit comments