fixhunt

biome complexity/noUselessStringConcat

labelled safe
Runs / 2026-10-17T20-08-09-963Z-npda-biome-complexity_noUselessStringConcatbiome 2.5.152026-10-17 20:08 UTC8 programs × 2 round(s)16 programs, 3 fired, 3 fixed, 0 findings, 6 rejectedmodel cost $0.0029

Analysis Nemotron 3 Super

Flags unnecessary concatenation of two or more string or template literals (including numbers converted via ToString) that can be safely merged into a single literal.

Fix: Replaces the concatenation expression with a single string or template literal where all parts are evaluated at compile time via ToString conversion and joined without alteration.

10 hazards
  • Rewriting `"" + obj` to `"` + String(obj)` changes behavior if obj has a custom toString or valueOf with side effects.
  • Concatenation involving a getter like `"" + obj.prop` may evaluate the getter once instead of twice if the property access is duplicated in the original.
  • If any part of the concatenation is a function call with side effects (e.g., `"" + f()`), moving it into a literal context does not remove the call, but the rule does not fire on such cases so this is not a direct hazard of the fix.
  • The fix assumes all operands are primitive-compatible at compile time; if an operand is a symbol, the original throws a TypeError but the fix would produce invalid output (though the rule does not flag Symbol concatenation).
  • In strict mode, evaluating `"" + undefined` produces the string "undefined", same as the fix, so no behavioral change there.
  • Template literals in the original may contain escaped characters (e.g., `"a\\b"`) that are preserved correctly in the fix, so no hazard from escaping.
  • If the concatenation is part of a larger expression (e.g., `("a" + "b") + c`), replacing only the inner part preserves grouping and evaluation order.
  • Comments adjacent to or inside the concatenation (e.g., `"a" /* comment */ + "b"`) are not moved by the fix and may be lost if the tool strips whitespace during rewrite.
  • Automatic semicolon insertion is unaffected because the fix does not alter statement structure or line breaks.
  • The fix does not introduce new variables or change scope, so hoisting and temporal dead zone behavior is preserved.

Programs

idresulttitlehazard
r1-p001invalidComments Adjacent/Inside Concat Lostcomments adjacent to or inside the concatenation (e.g., \"a\" /* comment */ + \"b\") are not moved by the fix and may be lost if the tool strips whitespace during rewrite.
r1-p002invalidComments Attached to Removed/Moved Codecomments attached to the removed or moved code
r1-p003silent prediction offGetter Evaluated Once Instead of TwiceConcatenation involving a getter like \"" + obj.prop\" may evaluate the getter once instead of twice if the property access is duplicated in the original.
r1-p004fix ok prediction offTemplate Literal Escaping PreservedTemplate literals in the original may contain escaped characters (e.g., \"a\\b\") that are preserved correctly in the fix, so no hazard from escaping.
r1-p005invalidExceptions Inside Removed/Rewritten Codeexceptions thrown inside the code the fix removes or rewrites
r1-p006invalidASI and Semicolon-less Code Leading ( or [ASI and semicolon-less code, leading ( or [ on the next line
r1-p007invalidNo New Variables Introduced (Hoisting/TDZ Preserved)The fix does not introduce new variables or change scope, so hoisting and temporal dead zone behavior is preserved.
r1-p008invalidClass Fields, Static Blocks, Private Names, Super Callsclass fields, static blocks, private names, super calls
r2-p001silent prediction offGetter Side Effectsgetters, setters and Object.defineProperty side effects
r2-p002silentFunction Call Side Effects Not TriggeredIf any part of the concatenation is a function call with side effects (e.g., `` + f()), moving it into a literal context does not remove the call, but the rule does not fire on such cases so this is n
r2-p003fix okASI Unaffected by Fix StructureAutomatic semicolon insertion is unaffected because the fix does not alter statement structure or line breaks.
r2-p004silent prediction offStrict Mode Undefined CoercionIn strict mode, evaluating `` + undefined produces the string "undefined", same as the fix, so no behavioral change there.
r2-p005silent prediction offVar Hoisting in Loopvar hoisting and re-declaration inside loops or after use
r2-p006fix okStrict vs Sloppy Mode Semanticsstrict vs sloppy mode semantics
r2-p007silent prediction offGrouping Preserved in Larger ExpressionIf the concatenation is part of a larger expression (e.g., ("a" + "b") + c), replacing only the inner part preserves grouping and evaluation order.
r2-p008silent prediction offThis Binding in Arrow vs Functionthis binding: arrow vs function, methods extracted from objects, call/apply/bind

Sources

r1-p001 · Comments Adjacent/Inside Concat Lost (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-08-0)
1// Hazard 1: Comment inside concat lost if stripped
2const s = \"start\" /* middle */ + \"end\";
3console.log(s);
r1-p002 · Comments Attached to Removed/Moved Code (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-08-0)
1// Hazard 2: Comment on first part should remain after fix
2let msg = \"Hello\"; // greeting
3msg += \" \" + \"World\"; // space + world
4console.log(msg);
r1-p003 · Getter Evaluated Once Instead of Twice
1// Hazard 3: Getter called once vs twice
2const obj = {
3 get prop() {
4 console.log('getter called');
5 return 'value';
6 }
7};
8console.log('' + obj.prop + obj.prop);
getter called
getter called
valuevalue
r1-p004 · Template Literal Escaping Preserved
1// Hazard 4: Escaping in template literals - no issue expected
2const path = `C:\\Users\\name` + `\\file.txt`;
3console.log(path);
--- orig.js	2026-10-17 16:08:18.271817923 -0400
+++ fixed.js	2026-10-17 16:08:18.551807013 -0400
@@ -1,3 +1,3 @@
 // Hazard 4: Escaping in template literals - no issue expected
-const path = `C:\\Users\\name` + `\\file.txt`;
+const path = "C:\\Users\\name\\file.txt";
 console.log(path);
C:\Users\name\file.txt
r1-p005 · Exceptions Inside Removed/Rewritten Code (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-08-0)
1// Hazard 5: Exception in concatenation subexpression
2function risky() { throw new Error('fail'); }
3try {
4 const result = \"before\" + risky() + \"after\";
5} catch (e) {
6 console.log(e.constructor.name + ': ' + e.message);
7}
r1-p006 · ASI and Semicolon-less Code Leading ( or [ (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-08-0)
1// Hazard 6: ASI risk with leading (
2let x = 1
3let y = \"a\" + \"b\" + \"c\"
4(function(){ console.log('IIFE ran'); })()
5console.log(x + y)
r1-p007 · No New Variables Introduced (Hoisting/TDZ Preserved) (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-08-0)
1// Hazard 7: TDZ check - using let before declaration in same block
2if (true) {
3 console.log(typeof temp); // should be undefined due to TDZ
4 let temp = \"\" + \"value\";
5 console.log(temp);
6}
r1-p008 · Class Fields, Static Blocks, Private Names, Super Calls (generated program does not parse: /home/sun/dev/diaghunt/runs/2026-10-17T20-08-0)
1// Hazard 8: Class with private field and static block
2class Test {
3 #private = \"secret\";
4 static {
5 console.log(\"static\" + \" block\");
6 }
7 constructor() {
8 console.log(\"instance\" + \" created\");
9 }
10}
11new Test();
r2-p001 · Getter Side Effects
1const obj = { get prop() { console.log('getter'); return 'X'; } };
2let result = '' + obj.prop + '' + obj.prop;
3console.log(result);
getter
getter
XX
r2-p002 · Function Call Side Effects Not Triggered
1function f() { console.log('called'); return 'Y'; }
2let s = '' + f();
3console.log(s);
called
Y
r2-p003 · ASI Unaffected by Fix Structure
1let x = '\n'
2+ 'a' + 'b'
3console.log(x);
--- orig.js	2026-10-17 16:08:22.981635223 -0400
+++ fixed.js	2026-10-17 16:08:23.242625160 -0400
@@ -1,3 +1,2 @@
-let x = '\n'
-+ 'a' + 'b'
+let x = "\nab"
 console.log(x);
ab
r2-p004 · Strict Mode Undefined Coercion
1"use strict";
2let u = undefined;
3let s = '' + u;
4console.log(s);
undefined
r2-p005 · Var Hoisting in Loop
1for (var i = 0; i < 2; i++) {
2 var msg = 'i:' + i;
3 console.log(msg);
4}
5console.log(typeof msg);
i:0
i:1
string
r2-p006 · Strict vs Sloppy Mode Semantics
1"use strict";
2let s1 = 'a' + 'b';
3(function() {
4 'use sloppy';
5 let s2 = 'a' + 'b';
6 console.log(s1 === s2);
7})();
--- orig.js	2026-10-17 16:08:23.853601606 -0400
+++ fixed.js	2026-10-17 16:08:24.105591900 -0400
@@ -1,7 +1,7 @@
 "use strict";
-let s1 = 'a' + 'b';
+let s1 = "ab";
 (function() {
   'use sloppy';
-  let s2 = 'a' + 'b';
+  let s2 = "ab";
   console.log(s1 === s2);
 })();
true
r2-p007 · Grouping Preserved in Larger Expression
1let x = 'a';
2let y = 'b';
3let z = 'c';
4let r = (x + y) + z;
5console.log(r);
abc
r2-p008 · This Binding in Arrow vs Function
1const obj = { name: 'obj', getName: function() { return this.name; } };
2const fn = obj.getName;
3const arrowFn = () => this.name;
4let s1 = 'Hello ' + fn.call(obj);
5let s2 = 'Hello ' + arrowFn();
6console.log(s1, s2);