fixhunt

biome complexity/useFlatMap

labelled safe
Runs / 2026-10-17T20-12-00-746Z-4q9e-biome-complexity_useFlatMapbiome 2.5.152026-10-17 20:12 UTC8 programs × 2 round(s)15 programs, 11 fired, 11 fixed, 1 findings, 0 rejectedmodel cost $0.03

Analysis Nemotron 3 Super

The rule flags uses of .map() followed by .flat() where flat() has no argument or an explicit depth of 1, suggesting replacement with .flatMap() to avoid creating an intermediate array.

Fix: Replace the chained .map().flat() or .map().flat(1) call with a single .flatMap() call using the same mapping function, preserving all arguments and surrounding syntax.

8 hazards
  • If the map callback contains side effects that depend on intermediate array indices, removing the intermediate array may alter behavior if those indices are used elsewhere.
  • If the flat depth is omitted but the map returns nested arrays deeper than one level, flatMap only flattens one level, potentially leaving extra nesting compared to the original.
  • If the map function returns holes (empty slots), .flat() removes them while .flatMap() preserves them, changing array length and indices.
  • If the map function uses 'this' and the context is bound via .map(callback, thisArg), the fix must preserve the thisArg or it will be lost.
  • If the chain includes .map().flat() after a non-array method like .filter(), the fix only applies to the map-flat pair and must not disturb surrounding chain links.
  • If the map callback throws an exception, the timing of when that exception occurs relative to subsequent operations is unchanged, but any reliance on the intermediate array's existence for debugging or inspection is removed.
  • If the code relies on the intermediate array being a distinct object in memory (e.g., for identity checks via ===), replacing it with flatMap's internal optimization breaks such checks.
  • If the map function returns undefined, .flat() skips it while .flatMap() also skips it, so no change, but if it returns null, both treat it as a single element, so still safe—however, if the map returns a non-array scalar, both behave identically in flattening one level.

Findings

programoraclelabeljudgestatussummary
r1-p006fix-changes-behaviorlabelled safeprogram-bug · lowrelatedThe program fails both before and after the fix because `arguments` is an array-like object without `map` or `flatMap` methods. The rewrite does not change the…

Programs

idresulttitlehazard
r1-p001fix okVar Hoisting and Re-declarationvar hoisting and re-declaration inside loops or after use
r1-p002silent prediction offHoles Preservation DifferenceIf the map function returns holes (empty slots), .flat() removes them while .flatMap() preserves them, changing array length and indices.
r1-p003fix okChain with Filter Before Map-FlatIf the chain includes .map().flat() after a non-array method like .filter(), the fix only applies to the map-flat pair and must not disturb surrounding chain links.
r1-p004silent prediction offIntermediate Array Identity CheckIf the code relies on the intermediate array being a distinct object in memory (e.g., for identity checks via ===), replacing it with flatMap's internal optimization breaks such checks.
r1-p005fix okGetter/Setter Side Effectsgetters, setters and Object.defineProperty side effects
r1-p006fix-changes-behaviorArguments Object and Parametersarguments object, function.length, default parameters, rest parameters
r1-p007fix okClass Fields and Super Callsclass fields, static blocks, private names, super calls
r1-p008fix okStrict vs Sloppy Mode Semanticsstrict vs sloppy mode semantics
r2-p001fix okHole preservation differenceIf the map function returns holes (empty slots), .flat() removes them while .flatMap() preserves them, changing array length and indices.
r2-p002fix okClass fields and super callsclass fields, static blocks, private names, super calls
r2-p003silent prediction offThisArg binding in mapIf the map function uses 'this' and the context is bound via .map(callback, thisArg), the fix must preserve the thisArg or it will be lost.
r2-p004fix okNumeric/string coercion edge casesnumeric/string coercion: + vs template literal, objects with toString/valueOf, Symbol, BigInt
r2-p005silentAsync/await ordering and generator interactionasync/await ordering, generators and yield position
r2-p006fix okArguments object and parameter nuancesarguments object, function.length, default parameters, rest parameters
r2-p007fix okBlock-scoped function declarationsfunction declarations inside blocks, switch cases, labeled statements

Sources

r1-p001 · Var Hoisting and Re-declaration
1for (var i = 0; i < 2; i++) {
2 var data = [[i], [i+1]];
3 var result = data.map(x => x).flat();
4 console.log(result.length);
5}
6console.log(typeof data);
--- orig.js	2026-10-17 16:12:12.090676088 -0400
+++ fixed.js	2026-10-17 16:12:12.359669828 -0400
@@ -1,6 +1,6 @@
 for (var i = 0; i < 2; i++) {
   var data = [[i], [i+1]];
-  var result = data.map(x => x).flat();
+  var result = data.flatMap(x => x);
   console.log(result.length);
 }
 console.log(typeof data);
2
2
object
r1-p002 · Holes Preservation Difference
1const arr = [1, , 3];
2const mapped = arr.map((x, i) => i === 1 ? undefined : [x]);
3const flattened = mapped.flat();
4console.log(flattened.length, flattened.join(','));
5try {
6 const flatMapped = arr.flatMap((x, i) => i === 1 ? undefined : [x]);
7 console.log(flatMapped.length, flatMapped.join(','));
8} catch (e) { console.log(e.name + ': ' + e.message); }
2 1,3
2 1,3
r1-p003 · Chain with Filter Before Map-Flat
1const nums = [1, 2, 3, 4];
2const result = nums.filter(n => n % 2 === 0).map(n => [n, n*10]).flat();
3console.log(result.join('-'));
4// After fix: .flatMap should preserve filter behavior
--- orig.js	2026-10-17 16:12:12.755660612 -0400
+++ fixed.js	2026-10-17 16:12:13.014654585 -0400
@@ -1,4 +1,4 @@
 const nums = [1, 2, 3, 4];
-const result = nums.filter(n => n % 2 === 0).map(n => [n, n*10]).flat();
+const result = nums.filter(n => n % 2 === 0).flatMap(n => [n, n*10]);
 console.log(result.join('-'));
 // After fix: .flatMap should preserve filter behavior
2-20-4-40
r1-p004 · Intermediate Array Identity Check
1const src = [[1], [2]];
2const mapped = src.map(x => x);
3const flattened = mapped.flat();
4console.log(mapped === src, mapped === flattened);
5// After fix, no intermediate array -> mapped would be undefined
false false
r1-p005 · Getter/Setter Side Effects
1let count = 0;
2const obj = { get arr() { count++; return [[1], [2]]; } };
3const result = obj.arr.map(x => x).flat();
4console.log(count, result.length);
5// After fix, getter called once instead of twice if map and flat were separate?
--- orig.js	2026-10-17 16:12:13.401645600 -0400
+++ fixed.js	2026-10-17 16:12:13.658639632 -0400
@@ -1,5 +1,5 @@
 let count = 0;
 const obj = { get arr() { count++; return [[1], [2]]; } };
-const result = obj.arr.map(x => x).flat();
+const result = obj.arr.flatMap(x => x);
 console.log(count, result.length);
 // After fix, getter called once instead of twice if map and flat were separate?
1 2
r1-p006 · Arguments Object and Parameters
1function process(a, b = [], ...rest) {
2 return arguments.map(x => [x]).flat();
3}
4const res = process(1, 2, 3);
5console.log(res.length, res.join(','));
--- orig.js	2026-10-17 16:12:13.830635639 -0400
+++ fixed.js	2026-10-17 16:12:14.086629700 -0400
@@ -1,5 +1,5 @@
 function process(a, b = [], ...rest) {
-  return arguments.map(x => [x]).flat();
+  return arguments.flatMap(x => [x]);
 }
 const res = process(1, 2, 3);
 console.log(res.length, res.join(','));
r1-p007 · Class Fields and Super Calls
1class Base {
2 static items = [[1], [2]];
3 getData() { return Base.items.map(x => x).flat(); }
4}
5class Der extends Base {
6 getData() { return super.getData().length; }
7}
8console.log(new Der().getData());
--- orig.js	2026-10-17 16:12:14.245626016 -0400
+++ fixed.js	2026-10-17 16:12:14.506619970 -0400
@@ -1,6 +1,6 @@
 class Base {
   static items = [[1], [2]];
-  getData() { return Base.items.map(x => x).flat(); }
+  getData() { return Base.items.flatMap(x => x); }
 }
 class Der extends Base {
   getData() { return super.getData().length; }
2
r1-p008 · Strict vs Sloppy Mode Semantics
1'use strict';
2(function() {
3 var data = [[1], [2], [3]];
4 try {
5 delete data[0];
6 const result = data.map(x => x).flat();
7 console.log(result.length);
8 } catch (e) { console.log(e.name); }
9})();
10
11(function() {
12 'use sloppy';
13 var data2 = [[1], [2], [3]];
14 delete data2[0];
15 const result = data2.map(x => x).flat();
16 console.log(result.length);
17})();
--- orig.js	2026-10-17 16:12:14.671616148 -0400
+++ fixed.js	2026-10-17 16:12:14.931610125 -0400
@@ -3,7 +3,7 @@
   var data = [[1], [2], [3]];
   try {
     delete data[0];
-    const result = data.map(x => x).flat();
+    const result = data.flatMap(x => x);
     console.log(result.length);
   } catch (e) { console.log(e.name); }
 })();
@@ -12,6 +12,6 @@
   'use sloppy';
   var data2 = [[1], [2], [3]];
   delete data2[0];
-  const result = data2.map(x => x).flat();
+  const result = data2.flatMap(x => x);
   console.log(result.length);
 })();
2
2
r2-p001 · Hole preservation difference
1const a = [1, , 3];
2const mapped = a.map(x => x === 2 ? [] : [x]);
3console.log('mapped:', mapped);
4const flatResult = mapped.flat();
5console.log('flat():', flatResult, 'length:', flatResult.length);
6a.map(x => x === 2 ? [] : [x]).flat(); // expect_trigger
7const flatMapResult = a.map(x => x === 2 ? [] : [x]).flatMap(x => x);
8console.log('flatMap():', flatMapResult, 'length:', flatMapResult.length);
--- orig.js	2026-10-17 16:12:25.971357806 -0400
+++ fixed.js	2026-10-17 16:12:26.250351516 -0400
@@ -3,6 +3,6 @@
 console.log('mapped:', mapped);
 const flatResult = mapped.flat();
 console.log('flat():', flatResult, 'length:', flatResult.length);
-a.map(x => x === 2 ? [] : [x]).flat(); // expect_trigger
+a.flatMap(x => x === 2 ? [] : [x]); // expect_trigger
 const flatMapResult = a.map(x => x === 2 ? [] : [x]).flatMap(x => x);
 console.log('flatMap():', flatMapResult, 'length:', flatMapResult.length);
mapped: [ [ 1 ], <1 empty item>, [ 3 ] ]
flat(): [ 1, 3 ] length: 2
flatMap(): [ 1, 3 ] length: 2
r2-p002 · Class fields and super calls
1class Base { constructor() { this.x = 1; } static y = 2; }
2class Derived extends Base { #z = 3; static { console.log('static block'); }
3 getZ() { return this.#z + super.x; }
4 process() { return [this].map(i => [i.getZ()]).flat(); }
5}
6const d = new Derived();
7console.log('process result:', d.process());
8d.process(); // expect_trigger
--- orig.js	2026-10-17 16:12:26.411347887 -0400
+++ fixed.js	2026-10-17 16:12:26.672342004 -0400
@@ -1,7 +1,7 @@
 class Base { constructor() { this.x = 1; } static y = 2; }
 class Derived extends Base { #z = 3; static { console.log('static block'); }
   getZ() { return this.#z + super.x; }
-  process() { return [this].map(i => [i.getZ()]).flat(); }
+  process() { return [this].flatMap(i => [i.getZ()]); }
 }
 const d = new Derived();
 console.log('process result:', d.process());
static block
process result: [ NaN ]
r2-p003 · ThisArg binding in map
1const obj = { multiplier: 2 };
2function mapper(x) { return [x * this.multiplier]; }
3const arr = [1, 2, 3];
4const resultWithThis = arr.map(mapper, obj).flat();
5console.log('with thisArg:', resultWithThis);
6arr.map(mapper, obj).flat(); // expect_trigger
7const broken = arr.map(mapper).flatMap(x => x);
8console.log('without thisArg:', broken);
with thisArg: [ 2, 4, 6 ]
r2-p004 · Numeric/string coercion edge cases
1const weird = {
2 valueOf() { return 42; },
3 toString() { return 'hello'; }
4};
5const sym = Symbol('test');
6const big = BigInt(123);
7const arr = [weird, sym, big];
8const mapped = arr.map(x => [x, String(x)]);
9console.log('mapped:', mapped);
10const flattened = mapped.flat();
11console.log('flat result:', flattened);
12arr.map(x => [x, String(x)]).flat(); // expect_trigger
13const flatMapped = arr.map(x => [x, String(x)]).flatMap(x => x);
14console.log('flatMap result:', flatMapped);
--- orig.js	2026-10-17 16:12:27.071333013 -0400
+++ fixed.js	2026-10-17 16:12:27.346326828 -0400
@@ -9,6 +9,6 @@
 console.log('mapped:', mapped);
 const flattened = mapped.flat();
 console.log('flat result:', flattened);
-arr.map(x => [x, String(x)]).flat(); // expect_trigger
+arr.flatMap(x => [x, String(x)]); // expect_trigger
 const flatMapped = arr.map(x => [x, String(x)]).flatMap(x => x);
 console.log('flatMap result:', flatMapped);
mapped: [
  [
    { valueOf: [Function: valueOf], toString: [Function: toString] },
    'hello'
  ],
  [ Symbol(test), 'Symbol(test)' ],
  [ 123n, '123' ]
]
flat result: [
  { valueOf: [Function: valueOf], toString: [Function: toString] },
  'hello',
  Symbol(test),
  'Symbol(test)',
  123n,
  '123'
]
flatMap result: [
  { valueOf: [Function: valueOf], toString: [Function: toString] },
  'hello',
  Symbol(test),
  'Symbol(test)',
  123n,
  '123'
]
r2-p005 · Async/await ordering and generator interaction
1async function* gen() { yield 1; yield* [2, 3]; }
2(async () => {
3 const arr = [];
4 for await (const x of gen()) arr.push(x);
5 console.log('collected:', arr);
6 const mapped = arr.map(x => [x, x * 2]);
7 console.log('mapped:', mapped);
8 const result = mapped.flat();
9 console.log('flat:', result);
10})();
collected: [ 1, 2, 3 ]
mapped: [ [ 1, 2 ], [ 2, 4 ], [ 3, 6 ] ]
flat: [ 1, 2, 2, 4, 3, 6 ]
r2-p006 · Arguments object and parameter nuances
1function mapper(a, b = 10, ...rest) {
2 console.log('args.length:', arguments.length, 'b:', b, 'rest:', rest);
3 return [a + b];
4}
5const arr = [5];
6console.log('calling map:');
7const result = arr.map(mapper).flat();
8console.log('flat result:', result);
9arr.map(mapper).flat(); // expect_trigger
10const flatMapResult = arr.map(mapper).flatMap(x => x);
11console.log('flatMap result:', flatMapResult);
--- orig.js	2026-10-17 16:12:27.761317495 -0400
+++ fixed.js	2026-10-17 16:12:28.039311245 -0400
@@ -4,8 +4,8 @@
 }
 const arr = [5];
 console.log('calling map:');
-const result = arr.map(mapper).flat();
+const result = arr.flatMap(mapper);
 console.log('flat result:', result);
-arr.map(mapper).flat(); // expect_trigger
+arr.flatMap(mapper); // expect_trigger
 const flatMapResult = arr.map(mapper).flatMap(x => x);
 console.log('flatMap result:', flatMapResult);
calling map:
args.length: 3 b: 0 rest: [ [ 5 ] ]
flat result: [ 5 ]
args.length: 3 b: 0 rest: [ [ 5 ] ]
args.length: 3 b: 0 rest: [ [ 5 ] ]
flatMap result: [ 5 ]
r2-p007 · Block-scoped function declarations
1function test() {
2 if (true) {
3 function mapFn(x) { return [x, 0]; }
4 const arr = [1, 2];
5 console.log('mapped:', arr.map(mapFn));
6 const flat = arr.map(mapFn).flat();
7 console.log('flat:', flat);
8 arr.map(mapFn).flat(); // expect_trigger
9 }
10 switch (1) {
11 case 1:
12 function caseFn(x) { return [x * 2]; }
13 break;
14 }
15}
16test();
--- orig.js	2026-10-17 16:12:28.211307386 -0400
+++ fixed.js	2026-10-17 16:12:28.487301193 -0400
@@ -3,9 +3,9 @@
     function mapFn(x) { return [x, 0]; }
     const arr = [1, 2];
     console.log('mapped:', arr.map(mapFn));
-    const flat = arr.map(mapFn).flat();
+    const flat = arr.flatMap(mapFn);
     console.log('flat:', flat);
-    arr.map(mapFn).flat(); // expect_trigger
+    arr.flatMap(mapFn); // expect_trigger
   }
   switch (1) {
     case 1:
mapped: [ [ 1, 0 ], [ 2, 0 ] ]
flat: [ 1, 0, 2, 0 ]