Skip to content

Commit ec7126c

Browse files
authored
fix: gritql mismatch import pattern (#6248)
1 parent 050047f commit ec7126c

File tree

8 files changed

+74
-12
lines changed

8 files changed

+74
-12
lines changed

.changeset/modern-meals-end.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
"@biomejs/biome": patch
3+
---
4+
5+
Fixed grit pattern matching for different kinds of import statements.
6+
7+
The grit pattern `import $imports from "foo"` will match the following code:
8+
9+
```ts
10+
import bar from "foo"
11+
import { bar } from "foo"
12+
import { bar, baz } from "foo"
13+
```
14+
15+

crates/biome_grit_patterns/src/grit_context.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use grit_pattern_matcher::pattern::{
2020
use grit_util::error::GritPatternError;
2121
use grit_util::{AnalysisLogs, FileOrigin, InputRanges, MatchRanges, error::GritResult};
2222
use path_absolutize::Absolutize;
23+
use std::collections::BTreeSet;
2324
use std::path::PathBuf;
2425
use std::sync::{Arc, Mutex};
2526

@@ -163,8 +164,15 @@ impl<'a> ExecContext<'a, GritQueryContext> for GritExecContext<'a> {
163164
let (variables, ranges, suppressed) =
164165
state.bindings_history_to_ranges(&self.lang, self.name());
165166

167+
// Deduplicate ranges to avoid duplicate matches
168+
let unique_ranges: Vec<_> = ranges
169+
.into_iter()
170+
.collect::<BTreeSet<_>>()
171+
.into_iter()
172+
.collect();
173+
166174
let input_ranges = InputRanges {
167-
ranges,
175+
ranges: unique_ranges,
168176
variables,
169177
suppressed,
170178
};

crates/biome_grit_patterns/src/grit_node_patterns.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,33 @@ use grit_pattern_matcher::pattern::{
1212
use grit_util::error::GritResult;
1313
use grit_util::{AnalysisLogs, Language};
1414

15+
/// Check if two syntax kinds are compatible for import pattern matching
16+
fn are_import_kinds_compatible(
17+
pattern_kind: GritTargetSyntaxKind,
18+
node_kind: GritTargetSyntaxKind,
19+
) -> bool {
20+
let Some(pattern_js_kind) = pattern_kind.as_js_kind() else {
21+
return false;
22+
};
23+
let Some(node_js_kind) = node_kind.as_js_kind() else {
24+
return false;
25+
};
26+
27+
use biome_js_syntax::JsSyntaxKind::*;
28+
29+
// Only allow specific import transformations to avoid over-matching
30+
match (pattern_js_kind, node_js_kind) {
31+
// Default import pattern can match named import
32+
(JS_IMPORT_DEFAULT_CLAUSE, JS_IMPORT_NAMED_CLAUSE) => true,
33+
// Named import pattern can match default import
34+
(JS_IMPORT_NAMED_CLAUSE, JS_IMPORT_DEFAULT_CLAUSE) => true,
35+
// Default import specifier can match named import specifiers
36+
(JS_DEFAULT_IMPORT_SPECIFIER, JS_NAMED_IMPORT_SPECIFIERS) => true,
37+
(JS_NAMED_IMPORT_SPECIFIERS, JS_DEFAULT_IMPORT_SPECIFIER) => true,
38+
_ => false,
39+
}
40+
}
41+
1542
#[derive(Clone, Debug)]
1643
pub struct GritNodePattern {
1744
pub kind: GritTargetSyntaxKind,
@@ -59,7 +86,7 @@ impl Matcher<GritQueryContext> for GritNodePattern {
5986
);
6087
}
6188

62-
if node.kind() != self.kind {
89+
if node.kind() != self.kind && !are_import_kinds_compatible(self.kind, node.kind()) {
6390
return Ok(false);
6491
}
6592
if self.args.is_empty() {

crates/biome_grit_patterns/tests/quick_test.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@ use biome_js_syntax::JsFileSource;
1010
#[test]
1111
fn test_query() {
1212
let parse_grit_result = parse_grit(
13-
"`console.log($arg)` => . where {
14-
log(message=\"This is a debug log\", variable=$arg),
15-
}
16-
",
13+
"
14+
`import $what from $where`
15+
",
1716
);
1817
if !parse_grit_result.diagnostics().is_empty() {
1918
panic!("Cannot parse query:\n{:?}", parse_grit_result.diagnostics());
@@ -31,12 +30,11 @@ fn test_query() {
3130
println!("Diagnostics from compiling query:\n{:?}", query.diagnostics);
3231
}
3332

34-
let body = r#"console.log("grape");"#;
33+
let body = r#"import { PrismaClient } from "@prisma/client/runtime";"#;
3534

36-
let file = GritTargetFile::new(
37-
"test.js",
38-
parse(body, JsFileSource::tsx(), JsParserOptions::default()).into(),
39-
);
35+
let parsed = parse(body, JsFileSource::js_module(), JsParserOptions::default());
36+
37+
let file = GritTargetFile::new("test.js", parsed.into());
4038
let GritQueryResult { effects, logs, .. } =
4139
query.execute(file).expect("could not execute query");
4240

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
`import $import from "foo"`
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
source: crates/biome_grit_patterns/tests/spec_tests.rs
3+
expression: import_patterns
4+
---
5+
SnapshotResult {
6+
messages: [],
7+
matched_ranges: [
8+
"2:1-2:27",
9+
],
10+
rewritten_files: [],
11+
created_files: [],
12+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Named import
2+
import { baz } from "foo";

crates/biome_grit_patterns/tests/specs/ts/withinUntilClause.snap

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ SnapshotResult {
66
messages: [],
77
matched_ranges: [
88
"8:5-10:8",
9-
"8:5-10:8",
109
],
1110
rewritten_files: [],
1211
created_files: [],

0 commit comments

Comments
 (0)