diff --git a/crates/rome_js_analyze/tests/specs/style/noArguments.cjs b/crates/rome_js_analyze/tests/specs/style/noArguments.cjs index 2f4e14c5b3d..d49e3feb99f 100644 --- a/crates/rome_js_analyze/tests/specs/style/noArguments.cjs +++ b/crates/rome_js_analyze/tests/specs/style/noArguments.cjs @@ -1,5 +1,9 @@ function f() { console.log(arguments); + + for(let i = 0;i < arguments.length; ++i) { + console.log(arguments[i]); + } } function f() { diff --git a/crates/rome_js_analyze/tests/specs/style/noArguments.cjs.snap b/crates/rome_js_analyze/tests/specs/style/noArguments.cjs.snap index 8f3487c38db..310677f2f61 100644 --- a/crates/rome_js_analyze/tests/specs/style/noArguments.cjs.snap +++ b/crates/rome_js_analyze/tests/specs/style/noArguments.cjs.snap @@ -6,6 +6,10 @@ expression: noArguments.cjs ```js function f() { console.log(arguments); + + for(let i = 0;i < arguments.length; ++i) { + console.log(arguments[i]); + } } function f() { @@ -23,8 +27,41 @@ noArguments.cjs:2:17 lint/style/noArguments ━━━━━━━━━━━━ 1 │ function f() { > 2 │ console.log(arguments); │ ^^^^^^^^^ - 3 │ } - 4 │ + 3 │ + 4 │ for(let i = 0;i < arguments.length; ++i) { + + i arguments does not have Array.prototype methods and can be inconvenient to use. + + +``` + +``` +noArguments.cjs:4:23 lint/style/noArguments ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use the rest parameters instead of arguments. + + 2 │ console.log(arguments); + 3 │ + > 4 │ for(let i = 0;i < arguments.length; ++i) { + │ ^^^^^^^^^ + 5 │ console.log(arguments[i]); + 6 │ } + + i arguments does not have Array.prototype methods and can be inconvenient to use. + + +``` + +``` +noArguments.cjs:5:21 lint/style/noArguments ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use the rest parameters instead of arguments. + + 4 │ for(let i = 0;i < arguments.length; ++i) { + > 5 │ console.log(arguments[i]); + │ ^^^^^^^^^ + 6 │ } + 7 │ } i arguments does not have Array.prototype methods and can be inconvenient to use. diff --git a/crates/rome_js_analyze/tests/suppression/correctness/noUndeclaredVariables.ts.snap b/crates/rome_js_analyze/tests/suppression/correctness/noUndeclaredVariables.ts.snap index e7353b110c9..dd87ae65573 100644 --- a/crates/rome_js_analyze/tests/suppression/correctness/noUndeclaredVariables.ts.snap +++ b/crates/rome_js_analyze/tests/suppression/correctness/noUndeclaredVariables.ts.snap @@ -13,6 +13,27 @@ export type Invalid = ` ``` # Diagnostics +``` +noUndeclaredVariables.ts:1:50 lint/correctness/noUndeclaredVariables FIXABLE ━━━━━━━━━━━━━━━━━━━━━ + + ! The T variable is undeclared + + > 1 │ export type Invalid = `Hello ${T}` + │ ^ + 2 │ + 3 │ export type Invalid = ` + + i Safe fix: Suppress rule lint/correctness/noUndeclaredVariables + + 1 │ - export·type·Invalid·=·`Hello·${T}` + 1 │ + export·type·Invalid·=·`Hello·${//·rome-ignore·lint/correctness/noUndeclaredVariables:· + 2 │ + T}` + 2 3 │ + 3 4 │ export type Invalid = ` + + +``` + ``` noUndeclaredVariables.ts:5:7 lint/correctness/noUndeclaredVariables FIXABLE ━━━━━━━━━━━━━━━━━━━━━━ diff --git a/crates/rome_js_semantic/src/events.rs b/crates/rome_js_semantic/src/events.rs index ae37066e421..4b7272eca50 100644 --- a/crates/rome_js_semantic/src/events.rs +++ b/crates/rome_js_semantic/src/events.rs @@ -606,7 +606,7 @@ impl SemanticEventExtractor { if let Some(scope) = self.scopes.pop() { // Match references and declarations - for (name, references) in scope.references { + for (name, mut references) in scope.references { // If we know the declaration of these reference push the correct events... if let Some(declaration_at) = self.bindings.get(&name) { for reference in references { @@ -647,7 +647,8 @@ impl SemanticEventExtractor { } } else if let Some(parent) = self.scopes.last_mut() { // ... if not, promote these references to the parent scope ... - parent.references.insert(name, references); + let parent_references = parent.references.entry(name).or_default(); + parent_references.append(&mut references); } else { // ... or raise UnresolvedReference if this is the global scope. for reference in references { diff --git a/crates/rome_js_semantic/src/tests/assertions.rs b/crates/rome_js_semantic/src/tests/assertions.rs index 6f79d6b4fa1..ae1838f12ec 100644 --- a/crates/rome_js_semantic/src/tests/assertions.rs +++ b/crates/rome_js_semantic/src/tests/assertions.rs @@ -238,7 +238,7 @@ struct UniqueAssertion { } #[derive(Clone, Debug)] -struct UnmatchedAssertion { +struct UnresolvedReferenceAssertion { range: TextRange, } @@ -252,7 +252,7 @@ enum SemanticAssertion { AtScope(AtScopeAssertion), NoEvent(NoEventAssertion), Unique(UniqueAssertion), - Unmatched(UnmatchedAssertion), + UnresolvedReference(UnresolvedReferenceAssertion), } impl SemanticAssertion { @@ -335,9 +335,11 @@ impl SemanticAssertion { range: token.parent().unwrap().text_range(), })) } else if assertion_text.contains("/*?") { - Some(SemanticAssertion::Unmatched(UnmatchedAssertion { - range: token.parent().unwrap().text_range(), - })) + Some(SemanticAssertion::UnresolvedReference( + UnresolvedReferenceAssertion { + range: token.parent().unwrap().text_range(), + }, + )) } else { None } @@ -354,7 +356,7 @@ struct SemanticAssertions { scope_end_assertions: Vec, no_events: Vec, uniques: Vec, - unmatched: Vec, + unresolved_references: Vec, } impl SemanticAssertions { @@ -367,7 +369,7 @@ impl SemanticAssertions { let mut scope_end_assertions = vec![]; let mut no_events = vec![]; let mut uniques = vec![]; - let mut unmatched = vec![]; + let mut unresolved_references = vec![]; for node in root .syntax() @@ -419,8 +421,8 @@ impl SemanticAssertions { Some(SemanticAssertion::Unique(assertion)) => { uniques.push(assertion); } - Some(SemanticAssertion::Unmatched(assertion)) => { - unmatched.push(assertion); + Some(SemanticAssertion::UnresolvedReference(assertion)) => { + unresolved_references.push(assertion); } None => {} }; @@ -437,7 +439,7 @@ impl SemanticAssertions { scope_end_assertions, no_events, uniques, - unmatched, + unresolved_references, } } @@ -718,19 +720,35 @@ impl SemanticAssertions { } } - // Check every unmatched assertion + // Check every unresolved_reference assertion + let is_unresolved_reference = + |e: &SemanticEvent| matches!(e, SemanticEvent::UnresolvedReference { .. }); - for unmatched in self.unmatched.iter() { - match events_by_pos.get(&unmatched.range.start()) { + for unresolved_reference in self.unresolved_references.iter() { + match events_by_pos.get(&unresolved_reference.range.start()) { Some(v) => { let ok = v .iter() .any(|e| matches!(e, SemanticEvent::UnresolvedReference { .. })); if !ok { + show_all_events(test_name, code, events_by_pos, is_unresolved_reference); + show_unmatched_assertion( + test_name, + code, + unresolved_reference, + unresolved_reference.range, + ); panic!("No UnresolvedReference event found"); } } None => { + show_all_events(test_name, code, events_by_pos, is_unresolved_reference); + show_unmatched_assertion( + test_name, + code, + unresolved_reference, + unresolved_reference.range, + ); panic!("No UnresolvedReference event found"); } } @@ -738,6 +756,58 @@ impl SemanticAssertions { } } +fn show_unmatched_assertion( + test_name: &str, + code: &str, + assertion: &impl std::fmt::Debug, + assertion_range: TextRange, +) { + let diagnostic = TestSemanticDiagnostic::new( + format!("This assertion was not matched: {assertion:?}"), + assertion_range, + ); + let error = diagnostic + .with_file_path((test_name.to_string(), FileId::zero())) + .with_file_source_code(code); + + let mut console = EnvConsole::default(); + console.log(markup! { + {PrintDiagnostic::verbose(&error)} + }); +} + +fn show_all_events( + test_name: &str, + code: &str, + events_by_pos: HashMap>, + f: F, +) where + F: Fn(&SemanticEvent) -> bool, +{ + let mut console = EnvConsole::default(); + let mut all_events = vec![]; + for (_, events) in events_by_pos { + for e in events { + if f(&e) { + all_events.push(e); + } + } + } + + all_events.sort_by_key(|l| l.range().start()); + + for e in all_events { + let diagnostic = TestSemanticDiagnostic::new(format!("{e:?}"), e.range()); + let error = diagnostic + .with_file_path((test_name.to_string(), FileId::zero())) + .with_file_source_code(code); + + console.log(markup! { + {PrintDiagnostic::verbose(&error)} + }); + } +} + fn error_assertion_not_attached_to_a_declaration( code: &str, assertion_range: TextRange, diff --git a/crates/rome_js_semantic/src/tests/references.rs b/crates/rome_js_semantic/src/tests/references.rs index 6a069ec7eb3..e7375023730 100644 --- a/crates/rome_js_semantic/src/tests/references.rs +++ b/crates/rome_js_semantic/src/tests/references.rs @@ -194,8 +194,16 @@ assert_semantics! { } assert_semantics! { - ok_unmatched_reference, r#"a/*?*/"#, - ok_function_expression_read,"let f/*#F*/ = function g/*#G*/(){}; g/*?*/();", + ok_unresolved_reference, r#"a/*?*/"#, + ok_unresolved_function_expression_read,"let f/*#F*/ = function g/*#G*/(){}; g/*?*/();", + ok_unresolved_reference_arguments, + r#"function f() { + console.log(arguments/*?*/); + + for(let i = 0;i < arguments/*?*/.length; ++i) { + console.log(arguments/*?*/[i]); + } + }"#, } // Exports