Repository: biomejs/biome (language: rust)

Issue:
🐛 `noUnusedVariables` makes the semantic model panics on invalid code
### Environment information

```block
Main
```


### What happened?

Given the following file named `main,js`:

```js
c;
const c = 0;
```

Execute the following command:

```shell
biome lint --only=correctness/noUnusedVariables main.js
````

And then notice the panics:

```shell
Biome encountered an unexpected error

This is a bug in Biome, not an error in your code, and we would appreciate it if you could report it to https://github.com/biomejs/biome/issues/ along with the following information to help us fixing the issue:

Source Location: crates/biome_js_semantic/src/semantic_model/model.rs:138:21
Thread Name: biome::worker_4
Message: internal error: entered unreachable code: Expected global scope not present

main.js internalError/panic  INTERNAL  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  ✖ processing panicked: internal error: entered unreachable code: Expected global scope not present
  
  ⚠ This diagnostic was derived from an internal Biome error. Potential bug, please report it if necessary.
  

Checked 0 files in 1558µs. No fixes applied.
internalError/io ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  ✖ No files were processed in the specified paths.
```

### Expected result

Biome should not panic.

### Code of Conduct

- [X] I agree to follow Biome's Code of Conduct


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 10b3283d1cc7..de1864582c7b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -105,6 +105,8 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b
 
   Contributed by @Conaclos
 
+- [noUnusedVariables](https://biomejs.dev/linter/rules/no-unused-variables/) no longer panics when suggesting the renaming of a variable at the start of a file ([#4114](https://github.com/biomejs/biome/issues/4114)). Contributed by @Conaclos
+
 - [noUselessEscapeInRegex](https://biomejs.dev/linter/rules/no-useless-escape-in-regex/) no longer panics on regexes that start with an empty character class. Contributed by @Conaclos
 
 - [noUselessStringConcat](https://biomejs.dev/linter/rules/no-useless-string-concat/) no longer panics when it encounters malformed code. Contributed by @Conaclos
diff --git a/crates/biome_js_semantic/src/semantic_model/model.rs b/crates/biome_js_semantic/src/semantic_model/model.rs
index 436aad7771f4..97e34d49bb11 100644
--- a/crates/biome_js_semantic/src/semantic_model/model.rs
+++ b/crates/biome_js_semantic/src/semantic_model/model.rs
@@ -124,13 +124,16 @@ impl SemanticModelData {
 
     /// Returns the [ScopeId] which the syntax is part of.
     pub(crate) fn scope(&self, range: TextRange) -> ScopeId {
+        // Seeking an interval in `self.scope_by_range` require a non-empty interval
+        debug_assert!(range.len() > 0.into(), "the range must not be empty.");
         let start = range.start().into();
         let end = range.end().into();
         let scopes = self
             .scope_by_range
+            // Find overlapping intervals
             .find(start, end)
+            // Only take intersecting intervals
             .filter(|x| !(start < x.start || end > x.stop));
-
         // We always want the most tight scope
         match scopes.map(|x| x.val).max() {
             Some(val) => val,
diff --git a/crates/biome_js_semantic/src/semantic_model/reference.rs b/crates/biome_js_semantic/src/semantic_model/reference.rs
index 105d89813584..25ac83b610af 100644
--- a/crates/biome_js_semantic/src/semantic_model/reference.rs
+++ b/crates/biome_js_semantic/src/semantic_model/reference.rs
@@ -58,9 +58,12 @@ impl Reference {
 
     /// Returns the scope of this reference
     pub fn scope(&self) -> Scope {
-        let id = self
-            .data
-            .scope(TextRange::new(self.range_start(), self.range_start()));
+        let start = self.range_start();
+        let id = self.data.scope(TextRange::new(
+            start,
+            // SAFETY: A reference name has at least a length of 1 byte.
+            start + TextSize::from(1),
+        ));
         Scope {
             data: self.data.clone(),
             id,


Test patch:
diff --git a/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/issue4114.js b/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/issue4114.js
new file mode 100644
index 000000000000..963a7b32417a
--- /dev/null
+++ b/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/issue4114.js
@@ -0,0 +1,2 @@
+c;
+const c = 0;
\ No newline at end of file
diff --git a/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/issue4114.js.snap b/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/issue4114.js.snap
new file mode 100644
index 000000000000..bc22f1dfa9a8
--- /dev/null
+++ b/crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/issue4114.js.snap
@@ -0,0 +1,31 @@
+---
+source: crates/biome_js_analyze/tests/spec_tests.rs
+expression: issue4114.js
+---
+# Input
+```jsx
+c;
+const c = 0;
+```
+
+# Diagnostics
+```
+issue4114.js:2:7 lint/correctness/noUnusedVariables  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+  ! This variable is unused.
+  
+    1 │ c;
+  > 2 │ const c = 0;
+      │       ^
+  
+  i Unused variables usually are result of incomplete refactoring, typos and other source of bugs.
+  
+  i Unsafe fix: If this is intentional, prepend c with an underscore.
+  
+    1   │ - c;
+    2   │ - const·c·=·0;
+      1 │ + _c;
+      2 │ + const·_c·=·0;
+  
+
+```