slopbrick 0.34.2 → 0.34.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/engine/worker.cjs +143 -22
- package/dist/engine/worker.js +143 -22
- package/dist/index.cjs +148 -27
- package/dist/index.js +148 -27
- package/package.json +1 -1
package/dist/engine/worker.cjs
CHANGED
|
@@ -34436,7 +34436,8 @@ var importPathMismatchRule = createRule({
|
|
|
34436
34436
|
|
|
34437
34437
|
// src/rules/cpp/c-style-cast.ts
|
|
34438
34438
|
var C_STYLE_CAST_REGEX = /\(\s*(?:int|long|short|char|float|double|bool|unsigned\s+\w+|signed\s+\w+|size_t|[A-Za-z_]\w*(?:\s*[*,&][^)]*)?)\s*\)\s*(\w+|[^a-zA-Z_])/g;
|
|
34439
|
-
var NAMED_CAST_PREFIX_REGEX = /\b(?:static|reinterpret|const|dynamic)_cast\s*<[^>]*>\s
|
|
34439
|
+
var NAMED_CAST_PREFIX_REGEX = /\b(?:static|reinterpret|const|dynamic)_cast\s*<[^>]*>\s*$/;
|
|
34440
|
+
var VOID_CAST_REGEX = /^\s*void\s*$/;
|
|
34440
34441
|
var cppCStyleCastRule = createRule({
|
|
34441
34442
|
id: "cpp/c-style-cast",
|
|
34442
34443
|
category: "typo",
|
|
@@ -34456,9 +34457,10 @@ var cppCStyleCastRule = createRule({
|
|
|
34456
34457
|
while ((m = C_STYLE_CAST_REGEX.exec(source)) !== null) {
|
|
34457
34458
|
const innerMatch = /\(\s*([^)]+?)\s*\)/.exec(m[0]) ?? [];
|
|
34458
34459
|
const inner = (innerMatch[1] ?? "").trim();
|
|
34460
|
+
if (VOID_CAST_REGEX.test(inner)) continue;
|
|
34459
34461
|
const looksLikeCast = /\b(?:int|long|short|char|float|double|bool|unsigned|signed|size_t)\b/.test(inner) || /[*&]/.test(inner);
|
|
34460
34462
|
if (!looksLikeCast) continue;
|
|
34461
|
-
const before = source.slice(Math.max(0, m.index -
|
|
34463
|
+
const before = source.slice(Math.max(0, m.index - 60), m.index);
|
|
34462
34464
|
if (NAMED_CAST_PREFIX_REGEX.test(before)) continue;
|
|
34463
34465
|
const line = source.slice(0, m.index).split("\n").length;
|
|
34464
34466
|
issues.push({
|
|
@@ -34469,7 +34471,7 @@ var cppCStyleCastRule = createRule({
|
|
|
34469
34471
|
message: `C-style cast at line ${line} \u2014 use static_cast / reinterpret_cast / const_cast`,
|
|
34470
34472
|
line,
|
|
34471
34473
|
column: 1,
|
|
34472
|
-
advice: "Use the named cast that matches the intent: `static_cast<int>(x)`, `reinterpret_cast<MyClass*>(p)`, `const_cast<...>(ref)`, or `dynamic_cast<Derived*>(base)` for runtime-checked downcasts. C-style casts silently pick whichever the compiler needs \u2014 `static_cast`, `reinterpret_cast`, OR `const_cast` \u2014 which makes them impossible to grep for and impossible to review. The C++ Core Guidelines (ES.49) call this out by name. AI agents reach for `(int)x` because of training-data C textbooks. Reference: cpp/c-style-cast v0.
|
|
34474
|
+
advice: "Use the named cast that matches the intent: `static_cast<int>(x)`, `reinterpret_cast<MyClass*>(p)`, `const_cast<...>(ref)`, or `dynamic_cast<Derived*>(base)` for runtime-checked downcasts. C-style casts silently pick whichever the compiler needs \u2014 `static_cast`, `reinterpret_cast`, OR `const_cast` \u2014 which makes them impossible to grep for and impossible to review. The C++ Core Guidelines (ES.49) call this out by name. AI agents reach for `(int)x` because of training-data C textbooks. Reference: cpp/c-style-cast v0.34.3 (refined regex selectivity)."
|
|
34473
34475
|
});
|
|
34474
34476
|
}
|
|
34475
34477
|
return issues;
|
|
@@ -34495,6 +34497,7 @@ var cppMagicNumbersRule = createRule({
|
|
|
34495
34497
|
if (!/\.(cpp|cc|cxx|h|hpp|hh|hxx|H)$/i.test(facts.filePath)) return issues;
|
|
34496
34498
|
const lines = source.split("\n");
|
|
34497
34499
|
const allowSet = /* @__PURE__ */ new Set([
|
|
34500
|
+
// v0.24 originals
|
|
34498
34501
|
"1024",
|
|
34499
34502
|
"65535",
|
|
34500
34503
|
"65536",
|
|
@@ -34513,17 +34516,42 @@ var cppMagicNumbersRule = createRule({
|
|
|
34513
34516
|
"2",
|
|
34514
34517
|
"3",
|
|
34515
34518
|
"4",
|
|
34516
|
-
"5"
|
|
34519
|
+
"5",
|
|
34520
|
+
// v0.34.4 additions
|
|
34521
|
+
"-1",
|
|
34522
|
+
// sentinel value for "not found" / "all bits set"
|
|
34523
|
+
"100",
|
|
34524
|
+
// percent literal, very common
|
|
34525
|
+
"0.0",
|
|
34526
|
+
"0.5",
|
|
34527
|
+
"1.0",
|
|
34528
|
+
"2.0",
|
|
34529
|
+
// common probability / ratio
|
|
34530
|
+
"4096",
|
|
34531
|
+
// page size, hash bucket count
|
|
34532
|
+
"2048",
|
|
34533
|
+
"512",
|
|
34534
|
+
// power-of-2 sizes
|
|
34535
|
+
"32",
|
|
34536
|
+
"64",
|
|
34537
|
+
"128",
|
|
34538
|
+
// bit widths, byte sizes
|
|
34539
|
+
"16",
|
|
34540
|
+
"8",
|
|
34541
|
+
// common small constants
|
|
34542
|
+
"50"
|
|
34543
|
+
// percentile literal
|
|
34517
34544
|
]);
|
|
34518
34545
|
for (let i = 0; i < lines.length; i++) {
|
|
34519
34546
|
const line = lines[i] ?? "";
|
|
34520
34547
|
if (!COMPARE_OR_RETURN_REGEX.test(line)) continue;
|
|
34548
|
+
const codeLine = line.replace(/\/\/.*$/, "").replace(/"(?:[^"\\]|\\.)*"/g, '""').replace(/'(?:[^'\\]|\\.)*'/g, "''");
|
|
34521
34549
|
let m;
|
|
34522
34550
|
MAGIC_NUMBER_REGEX.lastIndex = 0;
|
|
34523
|
-
while ((m = MAGIC_NUMBER_REGEX.exec(
|
|
34551
|
+
while ((m = MAGIC_NUMBER_REGEX.exec(codeLine)) !== null) {
|
|
34524
34552
|
const literal = m[0] ?? "";
|
|
34525
34553
|
if (allowSet.has(literal)) continue;
|
|
34526
|
-
if (/\b(?:constexpr|const)\s+\w+\s*=\s*$/.test(
|
|
34554
|
+
if (/\b(?:constexpr|const)\s+\w+\s*=\s*$/.test(codeLine.slice(0, m.index).trimEnd())) continue;
|
|
34527
34555
|
const prevLine = i > 0 ? lines[i - 1] ?? "" : "";
|
|
34528
34556
|
if (/\b(?:constexpr|const)\s+\w+\s*=\s*$/.test(prevLine.trim())) continue;
|
|
34529
34557
|
issues.push({
|
|
@@ -34534,7 +34562,7 @@ var cppMagicNumbersRule = createRule({
|
|
|
34534
34562
|
message: `magic number ${literal} at line ${i + 1} \u2014 name it: constexpr int MAX = ${literal};`,
|
|
34535
34563
|
line: i + 1,
|
|
34536
34564
|
column: 1,
|
|
34537
|
-
advice: 'Replace the bare literal with a named constant declared as `constexpr int MAX_SIZE = 1024;` (or `static constexpr`). The named constant lives next to its value, can be searched, and forces the reader to mean what they say. Magic numbers in comparisons hide intent ("7" against what?). AI agents produce magic-number-heavy code because their training-data examples rarely bother to name the constant. Reference: cpp/magic-numbers v0.
|
|
34565
|
+
advice: 'Replace the bare literal with a named constant declared as `constexpr int MAX_SIZE = 1024;` (or `static constexpr`). The named constant lives next to its value, can be searched, and forces the reader to mean what they say. Magic numbers in comparisons hide intent ("7" against what?). AI agents produce magic-number-heavy code because their training-data examples rarely bother to name the constant. Reference: cpp/magic-numbers v0.34.4 (expanded allowSet + string/comment exclusion).'
|
|
34538
34566
|
});
|
|
34539
34567
|
}
|
|
34540
34568
|
}
|
|
@@ -37187,7 +37215,6 @@ var javaSystemOutPrintlnRule = createRule({
|
|
|
37187
37215
|
});
|
|
37188
37216
|
|
|
37189
37217
|
// src/rules/java/thread-sleep-in-loop.ts
|
|
37190
|
-
var THREAD_SLEEP_REGEX = /\bThread\.sleep\s*\(/g;
|
|
37191
37218
|
var javaThreadSleepInLoopRule = createRule({
|
|
37192
37219
|
id: "java/thread-sleep-in-loop",
|
|
37193
37220
|
category: "perf",
|
|
@@ -37203,11 +37230,104 @@ var javaThreadSleepInLoopRule = createRule({
|
|
|
37203
37230
|
if (!source) return issues;
|
|
37204
37231
|
if (!/\.java$/i.test(facts.filePath)) return issues;
|
|
37205
37232
|
if (!/\bThread\.sleep\s*\(/.test(source)) return issues;
|
|
37206
|
-
|
|
37207
|
-
|
|
37208
|
-
|
|
37209
|
-
|
|
37210
|
-
|
|
37233
|
+
const braceStack = [];
|
|
37234
|
+
const loopSet = /* @__PURE__ */ new Set();
|
|
37235
|
+
let pendingLoopKeyword = null;
|
|
37236
|
+
let parenDepth = 0;
|
|
37237
|
+
const sleepEvents = [];
|
|
37238
|
+
let inString = false;
|
|
37239
|
+
let inLineComment = false;
|
|
37240
|
+
let inBlockComment = false;
|
|
37241
|
+
for (let i = 0; i < source.length; i++) {
|
|
37242
|
+
const c = source[i] ?? "";
|
|
37243
|
+
const next = source[i + 1] ?? "";
|
|
37244
|
+
const prev = source[i - 1] ?? "";
|
|
37245
|
+
if (inLineComment) {
|
|
37246
|
+
if (c === "\n") inLineComment = false;
|
|
37247
|
+
continue;
|
|
37248
|
+
}
|
|
37249
|
+
if (inBlockComment) {
|
|
37250
|
+
if (c === "*" && next === "/") {
|
|
37251
|
+
inBlockComment = false;
|
|
37252
|
+
i++;
|
|
37253
|
+
}
|
|
37254
|
+
continue;
|
|
37255
|
+
}
|
|
37256
|
+
if (inString) {
|
|
37257
|
+
if (c === "\\") {
|
|
37258
|
+
i++;
|
|
37259
|
+
continue;
|
|
37260
|
+
}
|
|
37261
|
+
if (c === inString) inString = false;
|
|
37262
|
+
continue;
|
|
37263
|
+
}
|
|
37264
|
+
if (c === "/" && next === "/") {
|
|
37265
|
+
inLineComment = true;
|
|
37266
|
+
i++;
|
|
37267
|
+
continue;
|
|
37268
|
+
}
|
|
37269
|
+
if (c === "/" && next === "*") {
|
|
37270
|
+
inBlockComment = true;
|
|
37271
|
+
i++;
|
|
37272
|
+
continue;
|
|
37273
|
+
}
|
|
37274
|
+
if (c === '"' || c === "'") {
|
|
37275
|
+
inString = c;
|
|
37276
|
+
continue;
|
|
37277
|
+
}
|
|
37278
|
+
if (c === "(") {
|
|
37279
|
+
parenDepth++;
|
|
37280
|
+
continue;
|
|
37281
|
+
}
|
|
37282
|
+
if (c === ")") {
|
|
37283
|
+
parenDepth = Math.max(0, parenDepth - 1);
|
|
37284
|
+
continue;
|
|
37285
|
+
}
|
|
37286
|
+
if (/[A-Za-z_]/.test(c) && !/[A-Za-z0-9_]/.test(prev)) {
|
|
37287
|
+
const next3 = source.slice(i, i + 3);
|
|
37288
|
+
const next5 = source.slice(i, i + 5);
|
|
37289
|
+
const next2 = source.slice(i, i + 2);
|
|
37290
|
+
const after3 = source[i + 3] ?? "";
|
|
37291
|
+
const after5 = source[i + 5] ?? "";
|
|
37292
|
+
const after2 = source[i + 2] ?? "";
|
|
37293
|
+
if (next3 === "for" && !/[A-Za-z0-9_]/.test(after3)) {
|
|
37294
|
+
pendingLoopKeyword = { kind: "for", idx: i };
|
|
37295
|
+
i += 2;
|
|
37296
|
+
continue;
|
|
37297
|
+
}
|
|
37298
|
+
if (next5 === "while" && !/[A-Za-z0-9_]/.test(after5)) {
|
|
37299
|
+
pendingLoopKeyword = { kind: "while", idx: i };
|
|
37300
|
+
i += 4;
|
|
37301
|
+
continue;
|
|
37302
|
+
}
|
|
37303
|
+
if (next2 === "do" && !/[A-Za-z0-9_]/.test(after2)) {
|
|
37304
|
+
pendingLoopKeyword = { kind: "do", idx: i };
|
|
37305
|
+
i += 1;
|
|
37306
|
+
continue;
|
|
37307
|
+
}
|
|
37308
|
+
}
|
|
37309
|
+
if (c === "{") {
|
|
37310
|
+
braceStack.push(i);
|
|
37311
|
+
if (pendingLoopKeyword && parenDepth === 0) {
|
|
37312
|
+
loopSet.add(i);
|
|
37313
|
+
pendingLoopKeyword = null;
|
|
37314
|
+
}
|
|
37315
|
+
} else if (c === "}") {
|
|
37316
|
+
const popped = braceStack.pop();
|
|
37317
|
+
if (popped !== void 0 && loopSet.has(popped)) {
|
|
37318
|
+
loopSet.delete(popped);
|
|
37319
|
+
}
|
|
37320
|
+
if (pendingLoopKeyword) {
|
|
37321
|
+
pendingLoopKeyword = null;
|
|
37322
|
+
}
|
|
37323
|
+
} else if (c === "T" && source.slice(i, i + 13) === "Thread.sleep(") {
|
|
37324
|
+
sleepEvents.push({ idx: i, loopDepth: loopSet.size });
|
|
37325
|
+
i += 12;
|
|
37326
|
+
}
|
|
37327
|
+
}
|
|
37328
|
+
for (const ev of sleepEvents) {
|
|
37329
|
+
if (ev.loopDepth === 0) continue;
|
|
37330
|
+
const line = source.slice(0, ev.idx).split("\n").length;
|
|
37211
37331
|
issues.push({
|
|
37212
37332
|
ruleId: "java/thread-sleep-in-loop",
|
|
37213
37333
|
category: "perf",
|
|
@@ -37216,7 +37336,7 @@ var javaThreadSleepInLoopRule = createRule({
|
|
|
37216
37336
|
message: `Thread.sleep() at line ${line}`,
|
|
37217
37337
|
line,
|
|
37218
37338
|
column: 1,
|
|
37219
|
-
advice: 'Use ScheduledExecutorService for periodic work, or BlockingQueue.take() for event-driven work. Thread.sleep in a loop is the classic "polling with sleep" anti-pattern \u2014 the thread blocks for the sleep duration each iteration. In server contexts this ties up Tomcat/Jetty/Netty threads. Reference: java/thread-sleep-in-loop v0.
|
|
37339
|
+
advice: 'Use ScheduledExecutorService for periodic work, or BlockingQueue.take() for event-driven work. Thread.sleep in a loop is the classic "polling with sleep" anti-pattern \u2014 the thread blocks for the sleep duration each iteration. In server contexts this ties up Tomcat/Jetty/Netty threads. Reference: java/thread-sleep-in-loop v0.34.6 (refined to require Thread.sleep inside the loop block, not just in the file).'
|
|
37220
37340
|
});
|
|
37221
37341
|
}
|
|
37222
37342
|
return issues;
|
|
@@ -37463,6 +37583,7 @@ var kotlinObjectSingletonMisuseRule = createRule({
|
|
|
37463
37583
|
// src/rules/kotlin/println-as-log.ts
|
|
37464
37584
|
var PRINTLN_REGEX = /\bprintln\s*\(/g;
|
|
37465
37585
|
var REAL_LOGGING_IMPORT_REGEX2 = /\bimport\s+(?:android\.util\.Log|org\.slf4j\.|io\.github\.oshai\.kotlinlogging|kotlin\.logging|co\.touchlab\.kermit|com\.github\.ajalt\.timber|org\.apache\.logging\.log4j)/;
|
|
37586
|
+
var TEST_FILE_REGEX = /(?:\/src\/test\/|\/test\/|\/Tests\/|\/Test\.kt|\/Tests\.kt|Tests\.kt$|Test\.kt$)/;
|
|
37466
37587
|
var kotlinPrintlnAsLogRule = createRule({
|
|
37467
37588
|
id: "kotlin/println-as-log",
|
|
37468
37589
|
category: "logic",
|
|
@@ -37477,7 +37598,7 @@ var kotlinPrintlnAsLogRule = createRule({
|
|
|
37477
37598
|
const source = facts.v2?._source;
|
|
37478
37599
|
if (!source) return issues;
|
|
37479
37600
|
if (!/\.kts?$/i.test(facts.filePath)) return issues;
|
|
37480
|
-
if (
|
|
37601
|
+
if (TEST_FILE_REGEX.test(facts.filePath)) return issues;
|
|
37481
37602
|
if (REAL_LOGGING_IMPORT_REGEX2.test(source)) return issues;
|
|
37482
37603
|
let m;
|
|
37483
37604
|
PRINTLN_REGEX.lastIndex = 0;
|
|
@@ -37491,7 +37612,7 @@ var kotlinPrintlnAsLogRule = createRule({
|
|
|
37491
37612
|
message: `println() as logger at line ${line}`,
|
|
37492
37613
|
line,
|
|
37493
37614
|
column: 1,
|
|
37494
|
-
advice: "Use a real logging library: slf4j (JVM), android.util.Log (Android), Timber (Android), kermit (multiplatform), or kotlin-logging. println() has no log level, no timestamp, no correlation ID, and cannot be filtered. Reference: kotlin/println-as-log v0.
|
|
37615
|
+
advice: "Use a real logging library: slf4j (JVM), android.util.Log (Android), Timber (Android), kermit (multiplatform), or kotlin-logging. println() has no log level, no timestamp, no correlation ID, and cannot be filtered. Reference: kotlin/println-as-log v0.34.5 (refined to skip test files for higher precision)."
|
|
37495
37616
|
});
|
|
37496
37617
|
}
|
|
37497
37618
|
return issues;
|
|
@@ -40514,7 +40635,7 @@ var swiftImplicitlyUnwrappedOptionalRule = createRule({
|
|
|
40514
40635
|
// src/rules/swift/print-debug.ts
|
|
40515
40636
|
var PRINT_REGEX = /\bprint\s*\(/g;
|
|
40516
40637
|
var DEFAULT_THRESHOLD2 = 1;
|
|
40517
|
-
var
|
|
40638
|
+
var TEST_FILE_REGEX2 = /(?:\/Tests\/|\/Tests\.swift|\/Test\.swift|Tests\.swift$|Test\.swift$)/;
|
|
40518
40639
|
var swiftPrintDebugRule = createRule({
|
|
40519
40640
|
id: "swift/print-debug",
|
|
40520
40641
|
category: "typo",
|
|
@@ -40529,7 +40650,7 @@ var swiftPrintDebugRule = createRule({
|
|
|
40529
40650
|
const source = facts.v2?._source;
|
|
40530
40651
|
if (!source) return issues;
|
|
40531
40652
|
if (!/\.swift$/i.test(facts.filePath)) return issues;
|
|
40532
|
-
if (
|
|
40653
|
+
if (TEST_FILE_REGEX2.test(facts.filePath)) return issues;
|
|
40533
40654
|
const matches = [];
|
|
40534
40655
|
let m;
|
|
40535
40656
|
PRINT_REGEX.lastIndex = 0;
|
|
@@ -45465,7 +45586,7 @@ var signal_strength_default = {
|
|
|
45465
45586
|
precision: 0.1268,
|
|
45466
45587
|
lastCalibratedAt: "2026-07-03T00:00:00Z",
|
|
45467
45588
|
verdict: "OK",
|
|
45468
|
-
_calibrationNote: "v0.29: v9 Kotlin calibration (2698 neg, 213 pos). ratio=1.84 (\u22651.5) \u2014 first rule in this entire session with a positive direction! Fires 1.84x more on post-2024 AI/demos than pre-2022 production. Precision=12.7% (below 50% USEFUL threshold); verdict=OK. The signal is real: post-2024 Kotlin code (especially AI-generated examples) uses println for output; pre-2022 production code uses slf4j/kermit. INSUFFICIENT_DATA: pos arm only 213 files. defaultOff: still set to true (verdict is OK but precision is below 50% \u2014 the guardrail expects OK/USEFUL rules to be defaultOff:false only when calibrated with a meaningful pos arm).",
|
|
45589
|
+
_calibrationNote: "v0.34.5: REFINED \u2014 rule now skips test files (JUnit4/5 + Android: *Tests.kt, *Test.kt, src/test/, test/ dirs). Per-file unique v9 Kotlin calibration (2698 neg, 213 pos): ratio=1.84 (\u22651.5, OK). The refinement mirrors v0.34.2's swift/print-debug fix: the previous exclusion only matched `\\/test\\/` and `.test.kts?$`, missing JUnit5's `FooTests.kt` naming. Expected post-refinement precision: 12.7% \u2192 25%+ by removing the JUnit assertion / debug-output fires that were ~50% of the FPs. Same direction as java/system-out-println (1.73 refined) and the v0.34.2 swift/print-debug pipeline. Full v9 re-calibration is part of the v0.35.0 re-measurement. v0.29: v9 Kotlin calibration (2698 neg, 213 pos). ratio=1.84 (\u22651.5) \u2014 first rule in this entire session with a positive direction! Fires 1.84x more on post-2024 AI/demos than pre-2022 production. Precision=12.7% (below 50% USEFUL threshold); verdict=OK. The signal is real: post-2024 Kotlin code (especially AI-generated examples) uses println for output; pre-2022 production code uses slf4j/kermit. INSUFFICIENT_DATA: pos arm only 213 files. defaultOff: still set to true (verdict is OK but precision is below 50% \u2014 the guardrail expects OK/USEFUL rules to be defaultOff:false only when calibrated with a meaningful pos arm).",
|
|
45469
45590
|
aiSpecific: false,
|
|
45470
45591
|
_v9Verdict: "OK",
|
|
45471
45592
|
_v9Lift: 1.84,
|
|
@@ -45529,7 +45650,7 @@ var signal_strength_default = {
|
|
|
45529
45650
|
precision: 0.1091,
|
|
45530
45651
|
lastCalibratedAt: "2026-07-03T00:00:00Z",
|
|
45531
45652
|
verdict: "DORMANT",
|
|
45532
|
-
_calibrationNote: "v0.30: v9 Java calibration (81891 neg, 10305 pos). ratio=0.97 (just below 1.0) \u2014 fires equally on both arms. Borderline era-confound: pre-2022 Java had more Thread.sleep in loops (less mature concurrency APIs); modern Java uses ScheduledExecutorService. Same direction as kotlin/runblocking-misuse (0.50). 2154 total fires \u2014 high absolute count, meaningful measurement. defaultOff.",
|
|
45653
|
+
_calibrationNote: "v0.34.6: REFINED \u2014 rule now requires Thread.sleep to be INSIDE the loop block (verified via brace-counting), not just in a file that happens to contain a for/while/do keyword. v0.30 baseline: 2154 total fires, ratio 0.97 (DORMANT). The refinement targets: (1) Thread.sleep in `main()` no longer fires if a different method has a `for` loop (the v0.30 heuristic fired on every Thread.sleep in the file); (2) Thread.sleep before/after a loop block (not inside it) no longer fires; (3) string literals containing `Thread.sleep(...)` are skipped via the string-state-machine. Expected post-refinement ratio: 1.5+ (positive-signal OK verdict) by removing the cross-method over-fires. Full v9 re-calibration is part of the v0.35.0 re-measurement. v0.30: v9 Java calibration (81891 neg, 10305 pos). ratio=0.97 (just below 1.0) \u2014 fires equally on both arms. Borderline era-confound: pre-2022 Java had more Thread.sleep in loops (less mature concurrency APIs); modern Java uses ScheduledExecutorService. Same direction as kotlin/runblocking-misuse (0.50). 2154 total fires \u2014 high absolute count, meaningful measurement. defaultOff.",
|
|
45533
45654
|
aiSpecific: false,
|
|
45534
45655
|
_v9Verdict: "DORMANT",
|
|
45535
45656
|
_v9Lift: 0.97,
|
|
@@ -45738,7 +45859,7 @@ var signal_strength_default = {
|
|
|
45738
45859
|
precision: 0.2325,
|
|
45739
45860
|
lastCalibratedAt: "2026-07-03T00:00:00Z",
|
|
45740
45861
|
verdict: "DORMANT",
|
|
45741
|
-
_calibrationNote: "v0.33: v9 C++ calibration (5107 neg, 1655 pos). Per-file unique: 874 TP, 2885 FP, ratio=0.93. Total fires were 32265 TP, 39954 FP \u2014 the regex is too broad and fires on every C-style cast including static_cast in template code. The per-file measurement (874/1655 = 53%) is more meaningful \u2014 both arms have similar proportions of files using C-style casts. INSUFFICIENT_DATA: pos arm 1655 files.",
|
|
45862
|
+
_calibrationNote: "v0.34.3: REFINED \u2014 NAMED_CAST_PREFIX_REGEX tightened (the old version required the lookback slice to end with `>(`, which never matched because the slice ends with `>`). Lookback increased 40\u219260 chars. Added `(void)x` discard-idom exclusion. v0.33 baseline: 874 TP / 2885 FP per-file (ratio 0.93, DORMANT). The refinement targets: (1) static_cast/const_cast/reinterpret_cast/dynamic_cast no longer fire when followed by whitespace before `(`; (2) class-type named casts like `static_cast<MyClass*>(p)` now correctly excluded (60-char lookback reaches the long typename); (3) `(void)x` deliberate discards are no longer flagged. Expected post-refinement ratio: 1.0-1.2 (still DORMANT but better-targeted). Full v9 re-calibration is part of the v0.35.0 re-measurement. v0.33: v9 C++ calibration (5107 neg, 1655 pos). Per-file unique: 874 TP, 2885 FP, ratio=0.93. Total fires were 32265 TP, 39954 FP \u2014 the regex is too broad and fires on every C-style cast including static_cast in template code. The per-file measurement (874/1655 = 53%) is more meaningful \u2014 both arms have similar proportions of files using C-style casts. INSUFFICIENT_DATA: pos arm 1655 files.",
|
|
45742
45863
|
aiSpecific: true,
|
|
45743
45864
|
_v7Verdict: "DORMANT",
|
|
45744
45865
|
_v7Lift: 1,
|
|
@@ -45784,7 +45905,7 @@ var signal_strength_default = {
|
|
|
45784
45905
|
precision: 0.2187,
|
|
45785
45906
|
lastCalibratedAt: "2026-07-03T00:00:00Z",
|
|
45786
45907
|
verdict: "DORMANT",
|
|
45787
|
-
_calibrationNote: "v0.33: v9 C++ calibration (5107 neg, 1655 pos). Per-file unique: 220 TP, 786 FP, ratio=0.86. Both arms have similar proportions of files with magic numbers \u2014 this is a general C++ anti-pattern that hasn't been eradicated. INSUFFICIENT_DATA: pos arm 1655 files.",
|
|
45908
|
+
_calibrationNote: "v0.34.4: REFINED \u2014 expanded allowSet with common constants (-1 sentinel, 100 percent literal, 0.5/1.0/2.0 ratios, 4096/2048/512/128/64/32/16/8 power-of-2 sizes, 50 percentile). Added string-literal and `//` comment stripping so substrings like `\"got 42 errors\"` and `// ticket #4242` no longer fire. v0.33 baseline: 220 TP / 786 FP per-file (ratio 0.86, DORMANT). The refinement targets: (1) sentinel `-1` literals are skipped because `1` is allowlisted and the `-` doesn't match MAGIC_NUMBER_REGEX anyway; (2) hex literals (`0xFF`) are skipped because the digit-only regex requires a word-boundary after digits, and `0x...` has none; (3) literals inside string/comment contexts no longer fire. Expected post-refinement ratio: 1.0-1.2 (still DORMANT but better-targeted). Full v9 re-calibration is part of the v0.35.0 re-measurement. v0.33: v9 C++ calibration (5107 neg, 1655 pos). Per-file unique: 220 TP, 786 FP, ratio=0.86. Both arms have similar proportions of files with magic numbers \u2014 this is a general C++ anti-pattern that hasn't been eradicated. INSUFFICIENT_DATA: pos arm 1655 files.",
|
|
45788
45909
|
aiSpecific: true,
|
|
45789
45910
|
_v7Verdict: "DORMANT",
|
|
45790
45911
|
_v7Lift: 1,
|
package/dist/engine/worker.js
CHANGED
|
@@ -34407,7 +34407,8 @@ var importPathMismatchRule = createRule({
|
|
|
34407
34407
|
|
|
34408
34408
|
// src/rules/cpp/c-style-cast.ts
|
|
34409
34409
|
var C_STYLE_CAST_REGEX = /\(\s*(?:int|long|short|char|float|double|bool|unsigned\s+\w+|signed\s+\w+|size_t|[A-Za-z_]\w*(?:\s*[*,&][^)]*)?)\s*\)\s*(\w+|[^a-zA-Z_])/g;
|
|
34410
|
-
var NAMED_CAST_PREFIX_REGEX = /\b(?:static|reinterpret|const|dynamic)_cast\s*<[^>]*>\s
|
|
34410
|
+
var NAMED_CAST_PREFIX_REGEX = /\b(?:static|reinterpret|const|dynamic)_cast\s*<[^>]*>\s*$/;
|
|
34411
|
+
var VOID_CAST_REGEX = /^\s*void\s*$/;
|
|
34411
34412
|
var cppCStyleCastRule = createRule({
|
|
34412
34413
|
id: "cpp/c-style-cast",
|
|
34413
34414
|
category: "typo",
|
|
@@ -34427,9 +34428,10 @@ var cppCStyleCastRule = createRule({
|
|
|
34427
34428
|
while ((m = C_STYLE_CAST_REGEX.exec(source)) !== null) {
|
|
34428
34429
|
const innerMatch = /\(\s*([^)]+?)\s*\)/.exec(m[0]) ?? [];
|
|
34429
34430
|
const inner = (innerMatch[1] ?? "").trim();
|
|
34431
|
+
if (VOID_CAST_REGEX.test(inner)) continue;
|
|
34430
34432
|
const looksLikeCast = /\b(?:int|long|short|char|float|double|bool|unsigned|signed|size_t)\b/.test(inner) || /[*&]/.test(inner);
|
|
34431
34433
|
if (!looksLikeCast) continue;
|
|
34432
|
-
const before = source.slice(Math.max(0, m.index -
|
|
34434
|
+
const before = source.slice(Math.max(0, m.index - 60), m.index);
|
|
34433
34435
|
if (NAMED_CAST_PREFIX_REGEX.test(before)) continue;
|
|
34434
34436
|
const line = source.slice(0, m.index).split("\n").length;
|
|
34435
34437
|
issues.push({
|
|
@@ -34440,7 +34442,7 @@ var cppCStyleCastRule = createRule({
|
|
|
34440
34442
|
message: `C-style cast at line ${line} \u2014 use static_cast / reinterpret_cast / const_cast`,
|
|
34441
34443
|
line,
|
|
34442
34444
|
column: 1,
|
|
34443
|
-
advice: "Use the named cast that matches the intent: `static_cast<int>(x)`, `reinterpret_cast<MyClass*>(p)`, `const_cast<...>(ref)`, or `dynamic_cast<Derived*>(base)` for runtime-checked downcasts. C-style casts silently pick whichever the compiler needs \u2014 `static_cast`, `reinterpret_cast`, OR `const_cast` \u2014 which makes them impossible to grep for and impossible to review. The C++ Core Guidelines (ES.49) call this out by name. AI agents reach for `(int)x` because of training-data C textbooks. Reference: cpp/c-style-cast v0.
|
|
34445
|
+
advice: "Use the named cast that matches the intent: `static_cast<int>(x)`, `reinterpret_cast<MyClass*>(p)`, `const_cast<...>(ref)`, or `dynamic_cast<Derived*>(base)` for runtime-checked downcasts. C-style casts silently pick whichever the compiler needs \u2014 `static_cast`, `reinterpret_cast`, OR `const_cast` \u2014 which makes them impossible to grep for and impossible to review. The C++ Core Guidelines (ES.49) call this out by name. AI agents reach for `(int)x` because of training-data C textbooks. Reference: cpp/c-style-cast v0.34.3 (refined regex selectivity)."
|
|
34444
34446
|
});
|
|
34445
34447
|
}
|
|
34446
34448
|
return issues;
|
|
@@ -34466,6 +34468,7 @@ var cppMagicNumbersRule = createRule({
|
|
|
34466
34468
|
if (!/\.(cpp|cc|cxx|h|hpp|hh|hxx|H)$/i.test(facts.filePath)) return issues;
|
|
34467
34469
|
const lines = source.split("\n");
|
|
34468
34470
|
const allowSet = /* @__PURE__ */ new Set([
|
|
34471
|
+
// v0.24 originals
|
|
34469
34472
|
"1024",
|
|
34470
34473
|
"65535",
|
|
34471
34474
|
"65536",
|
|
@@ -34484,17 +34487,42 @@ var cppMagicNumbersRule = createRule({
|
|
|
34484
34487
|
"2",
|
|
34485
34488
|
"3",
|
|
34486
34489
|
"4",
|
|
34487
|
-
"5"
|
|
34490
|
+
"5",
|
|
34491
|
+
// v0.34.4 additions
|
|
34492
|
+
"-1",
|
|
34493
|
+
// sentinel value for "not found" / "all bits set"
|
|
34494
|
+
"100",
|
|
34495
|
+
// percent literal, very common
|
|
34496
|
+
"0.0",
|
|
34497
|
+
"0.5",
|
|
34498
|
+
"1.0",
|
|
34499
|
+
"2.0",
|
|
34500
|
+
// common probability / ratio
|
|
34501
|
+
"4096",
|
|
34502
|
+
// page size, hash bucket count
|
|
34503
|
+
"2048",
|
|
34504
|
+
"512",
|
|
34505
|
+
// power-of-2 sizes
|
|
34506
|
+
"32",
|
|
34507
|
+
"64",
|
|
34508
|
+
"128",
|
|
34509
|
+
// bit widths, byte sizes
|
|
34510
|
+
"16",
|
|
34511
|
+
"8",
|
|
34512
|
+
// common small constants
|
|
34513
|
+
"50"
|
|
34514
|
+
// percentile literal
|
|
34488
34515
|
]);
|
|
34489
34516
|
for (let i = 0; i < lines.length; i++) {
|
|
34490
34517
|
const line = lines[i] ?? "";
|
|
34491
34518
|
if (!COMPARE_OR_RETURN_REGEX.test(line)) continue;
|
|
34519
|
+
const codeLine = line.replace(/\/\/.*$/, "").replace(/"(?:[^"\\]|\\.)*"/g, '""').replace(/'(?:[^'\\]|\\.)*'/g, "''");
|
|
34492
34520
|
let m;
|
|
34493
34521
|
MAGIC_NUMBER_REGEX.lastIndex = 0;
|
|
34494
|
-
while ((m = MAGIC_NUMBER_REGEX.exec(
|
|
34522
|
+
while ((m = MAGIC_NUMBER_REGEX.exec(codeLine)) !== null) {
|
|
34495
34523
|
const literal = m[0] ?? "";
|
|
34496
34524
|
if (allowSet.has(literal)) continue;
|
|
34497
|
-
if (/\b(?:constexpr|const)\s+\w+\s*=\s*$/.test(
|
|
34525
|
+
if (/\b(?:constexpr|const)\s+\w+\s*=\s*$/.test(codeLine.slice(0, m.index).trimEnd())) continue;
|
|
34498
34526
|
const prevLine = i > 0 ? lines[i - 1] ?? "" : "";
|
|
34499
34527
|
if (/\b(?:constexpr|const)\s+\w+\s*=\s*$/.test(prevLine.trim())) continue;
|
|
34500
34528
|
issues.push({
|
|
@@ -34505,7 +34533,7 @@ var cppMagicNumbersRule = createRule({
|
|
|
34505
34533
|
message: `magic number ${literal} at line ${i + 1} \u2014 name it: constexpr int MAX = ${literal};`,
|
|
34506
34534
|
line: i + 1,
|
|
34507
34535
|
column: 1,
|
|
34508
|
-
advice: 'Replace the bare literal with a named constant declared as `constexpr int MAX_SIZE = 1024;` (or `static constexpr`). The named constant lives next to its value, can be searched, and forces the reader to mean what they say. Magic numbers in comparisons hide intent ("7" against what?). AI agents produce magic-number-heavy code because their training-data examples rarely bother to name the constant. Reference: cpp/magic-numbers v0.
|
|
34536
|
+
advice: 'Replace the bare literal with a named constant declared as `constexpr int MAX_SIZE = 1024;` (or `static constexpr`). The named constant lives next to its value, can be searched, and forces the reader to mean what they say. Magic numbers in comparisons hide intent ("7" against what?). AI agents produce magic-number-heavy code because their training-data examples rarely bother to name the constant. Reference: cpp/magic-numbers v0.34.4 (expanded allowSet + string/comment exclusion).'
|
|
34509
34537
|
});
|
|
34510
34538
|
}
|
|
34511
34539
|
}
|
|
@@ -37158,7 +37186,6 @@ var javaSystemOutPrintlnRule = createRule({
|
|
|
37158
37186
|
});
|
|
37159
37187
|
|
|
37160
37188
|
// src/rules/java/thread-sleep-in-loop.ts
|
|
37161
|
-
var THREAD_SLEEP_REGEX = /\bThread\.sleep\s*\(/g;
|
|
37162
37189
|
var javaThreadSleepInLoopRule = createRule({
|
|
37163
37190
|
id: "java/thread-sleep-in-loop",
|
|
37164
37191
|
category: "perf",
|
|
@@ -37174,11 +37201,104 @@ var javaThreadSleepInLoopRule = createRule({
|
|
|
37174
37201
|
if (!source) return issues;
|
|
37175
37202
|
if (!/\.java$/i.test(facts.filePath)) return issues;
|
|
37176
37203
|
if (!/\bThread\.sleep\s*\(/.test(source)) return issues;
|
|
37177
|
-
|
|
37178
|
-
|
|
37179
|
-
|
|
37180
|
-
|
|
37181
|
-
|
|
37204
|
+
const braceStack = [];
|
|
37205
|
+
const loopSet = /* @__PURE__ */ new Set();
|
|
37206
|
+
let pendingLoopKeyword = null;
|
|
37207
|
+
let parenDepth = 0;
|
|
37208
|
+
const sleepEvents = [];
|
|
37209
|
+
let inString = false;
|
|
37210
|
+
let inLineComment = false;
|
|
37211
|
+
let inBlockComment = false;
|
|
37212
|
+
for (let i = 0; i < source.length; i++) {
|
|
37213
|
+
const c = source[i] ?? "";
|
|
37214
|
+
const next = source[i + 1] ?? "";
|
|
37215
|
+
const prev = source[i - 1] ?? "";
|
|
37216
|
+
if (inLineComment) {
|
|
37217
|
+
if (c === "\n") inLineComment = false;
|
|
37218
|
+
continue;
|
|
37219
|
+
}
|
|
37220
|
+
if (inBlockComment) {
|
|
37221
|
+
if (c === "*" && next === "/") {
|
|
37222
|
+
inBlockComment = false;
|
|
37223
|
+
i++;
|
|
37224
|
+
}
|
|
37225
|
+
continue;
|
|
37226
|
+
}
|
|
37227
|
+
if (inString) {
|
|
37228
|
+
if (c === "\\") {
|
|
37229
|
+
i++;
|
|
37230
|
+
continue;
|
|
37231
|
+
}
|
|
37232
|
+
if (c === inString) inString = false;
|
|
37233
|
+
continue;
|
|
37234
|
+
}
|
|
37235
|
+
if (c === "/" && next === "/") {
|
|
37236
|
+
inLineComment = true;
|
|
37237
|
+
i++;
|
|
37238
|
+
continue;
|
|
37239
|
+
}
|
|
37240
|
+
if (c === "/" && next === "*") {
|
|
37241
|
+
inBlockComment = true;
|
|
37242
|
+
i++;
|
|
37243
|
+
continue;
|
|
37244
|
+
}
|
|
37245
|
+
if (c === '"' || c === "'") {
|
|
37246
|
+
inString = c;
|
|
37247
|
+
continue;
|
|
37248
|
+
}
|
|
37249
|
+
if (c === "(") {
|
|
37250
|
+
parenDepth++;
|
|
37251
|
+
continue;
|
|
37252
|
+
}
|
|
37253
|
+
if (c === ")") {
|
|
37254
|
+
parenDepth = Math.max(0, parenDepth - 1);
|
|
37255
|
+
continue;
|
|
37256
|
+
}
|
|
37257
|
+
if (/[A-Za-z_]/.test(c) && !/[A-Za-z0-9_]/.test(prev)) {
|
|
37258
|
+
const next3 = source.slice(i, i + 3);
|
|
37259
|
+
const next5 = source.slice(i, i + 5);
|
|
37260
|
+
const next2 = source.slice(i, i + 2);
|
|
37261
|
+
const after3 = source[i + 3] ?? "";
|
|
37262
|
+
const after5 = source[i + 5] ?? "";
|
|
37263
|
+
const after2 = source[i + 2] ?? "";
|
|
37264
|
+
if (next3 === "for" && !/[A-Za-z0-9_]/.test(after3)) {
|
|
37265
|
+
pendingLoopKeyword = { kind: "for", idx: i };
|
|
37266
|
+
i += 2;
|
|
37267
|
+
continue;
|
|
37268
|
+
}
|
|
37269
|
+
if (next5 === "while" && !/[A-Za-z0-9_]/.test(after5)) {
|
|
37270
|
+
pendingLoopKeyword = { kind: "while", idx: i };
|
|
37271
|
+
i += 4;
|
|
37272
|
+
continue;
|
|
37273
|
+
}
|
|
37274
|
+
if (next2 === "do" && !/[A-Za-z0-9_]/.test(after2)) {
|
|
37275
|
+
pendingLoopKeyword = { kind: "do", idx: i };
|
|
37276
|
+
i += 1;
|
|
37277
|
+
continue;
|
|
37278
|
+
}
|
|
37279
|
+
}
|
|
37280
|
+
if (c === "{") {
|
|
37281
|
+
braceStack.push(i);
|
|
37282
|
+
if (pendingLoopKeyword && parenDepth === 0) {
|
|
37283
|
+
loopSet.add(i);
|
|
37284
|
+
pendingLoopKeyword = null;
|
|
37285
|
+
}
|
|
37286
|
+
} else if (c === "}") {
|
|
37287
|
+
const popped = braceStack.pop();
|
|
37288
|
+
if (popped !== void 0 && loopSet.has(popped)) {
|
|
37289
|
+
loopSet.delete(popped);
|
|
37290
|
+
}
|
|
37291
|
+
if (pendingLoopKeyword) {
|
|
37292
|
+
pendingLoopKeyword = null;
|
|
37293
|
+
}
|
|
37294
|
+
} else if (c === "T" && source.slice(i, i + 13) === "Thread.sleep(") {
|
|
37295
|
+
sleepEvents.push({ idx: i, loopDepth: loopSet.size });
|
|
37296
|
+
i += 12;
|
|
37297
|
+
}
|
|
37298
|
+
}
|
|
37299
|
+
for (const ev of sleepEvents) {
|
|
37300
|
+
if (ev.loopDepth === 0) continue;
|
|
37301
|
+
const line = source.slice(0, ev.idx).split("\n").length;
|
|
37182
37302
|
issues.push({
|
|
37183
37303
|
ruleId: "java/thread-sleep-in-loop",
|
|
37184
37304
|
category: "perf",
|
|
@@ -37187,7 +37307,7 @@ var javaThreadSleepInLoopRule = createRule({
|
|
|
37187
37307
|
message: `Thread.sleep() at line ${line}`,
|
|
37188
37308
|
line,
|
|
37189
37309
|
column: 1,
|
|
37190
|
-
advice: 'Use ScheduledExecutorService for periodic work, or BlockingQueue.take() for event-driven work. Thread.sleep in a loop is the classic "polling with sleep" anti-pattern \u2014 the thread blocks for the sleep duration each iteration. In server contexts this ties up Tomcat/Jetty/Netty threads. Reference: java/thread-sleep-in-loop v0.
|
|
37310
|
+
advice: 'Use ScheduledExecutorService for periodic work, or BlockingQueue.take() for event-driven work. Thread.sleep in a loop is the classic "polling with sleep" anti-pattern \u2014 the thread blocks for the sleep duration each iteration. In server contexts this ties up Tomcat/Jetty/Netty threads. Reference: java/thread-sleep-in-loop v0.34.6 (refined to require Thread.sleep inside the loop block, not just in the file).'
|
|
37191
37311
|
});
|
|
37192
37312
|
}
|
|
37193
37313
|
return issues;
|
|
@@ -37434,6 +37554,7 @@ var kotlinObjectSingletonMisuseRule = createRule({
|
|
|
37434
37554
|
// src/rules/kotlin/println-as-log.ts
|
|
37435
37555
|
var PRINTLN_REGEX = /\bprintln\s*\(/g;
|
|
37436
37556
|
var REAL_LOGGING_IMPORT_REGEX2 = /\bimport\s+(?:android\.util\.Log|org\.slf4j\.|io\.github\.oshai\.kotlinlogging|kotlin\.logging|co\.touchlab\.kermit|com\.github\.ajalt\.timber|org\.apache\.logging\.log4j)/;
|
|
37557
|
+
var TEST_FILE_REGEX = /(?:\/src\/test\/|\/test\/|\/Tests\/|\/Test\.kt|\/Tests\.kt|Tests\.kt$|Test\.kt$)/;
|
|
37437
37558
|
var kotlinPrintlnAsLogRule = createRule({
|
|
37438
37559
|
id: "kotlin/println-as-log",
|
|
37439
37560
|
category: "logic",
|
|
@@ -37448,7 +37569,7 @@ var kotlinPrintlnAsLogRule = createRule({
|
|
|
37448
37569
|
const source = facts.v2?._source;
|
|
37449
37570
|
if (!source) return issues;
|
|
37450
37571
|
if (!/\.kts?$/i.test(facts.filePath)) return issues;
|
|
37451
|
-
if (
|
|
37572
|
+
if (TEST_FILE_REGEX.test(facts.filePath)) return issues;
|
|
37452
37573
|
if (REAL_LOGGING_IMPORT_REGEX2.test(source)) return issues;
|
|
37453
37574
|
let m;
|
|
37454
37575
|
PRINTLN_REGEX.lastIndex = 0;
|
|
@@ -37462,7 +37583,7 @@ var kotlinPrintlnAsLogRule = createRule({
|
|
|
37462
37583
|
message: `println() as logger at line ${line}`,
|
|
37463
37584
|
line,
|
|
37464
37585
|
column: 1,
|
|
37465
|
-
advice: "Use a real logging library: slf4j (JVM), android.util.Log (Android), Timber (Android), kermit (multiplatform), or kotlin-logging. println() has no log level, no timestamp, no correlation ID, and cannot be filtered. Reference: kotlin/println-as-log v0.
|
|
37586
|
+
advice: "Use a real logging library: slf4j (JVM), android.util.Log (Android), Timber (Android), kermit (multiplatform), or kotlin-logging. println() has no log level, no timestamp, no correlation ID, and cannot be filtered. Reference: kotlin/println-as-log v0.34.5 (refined to skip test files for higher precision)."
|
|
37466
37587
|
});
|
|
37467
37588
|
}
|
|
37468
37589
|
return issues;
|
|
@@ -40485,7 +40606,7 @@ var swiftImplicitlyUnwrappedOptionalRule = createRule({
|
|
|
40485
40606
|
// src/rules/swift/print-debug.ts
|
|
40486
40607
|
var PRINT_REGEX = /\bprint\s*\(/g;
|
|
40487
40608
|
var DEFAULT_THRESHOLD2 = 1;
|
|
40488
|
-
var
|
|
40609
|
+
var TEST_FILE_REGEX2 = /(?:\/Tests\/|\/Tests\.swift|\/Test\.swift|Tests\.swift$|Test\.swift$)/;
|
|
40489
40610
|
var swiftPrintDebugRule = createRule({
|
|
40490
40611
|
id: "swift/print-debug",
|
|
40491
40612
|
category: "typo",
|
|
@@ -40500,7 +40621,7 @@ var swiftPrintDebugRule = createRule({
|
|
|
40500
40621
|
const source = facts.v2?._source;
|
|
40501
40622
|
if (!source) return issues;
|
|
40502
40623
|
if (!/\.swift$/i.test(facts.filePath)) return issues;
|
|
40503
|
-
if (
|
|
40624
|
+
if (TEST_FILE_REGEX2.test(facts.filePath)) return issues;
|
|
40504
40625
|
const matches = [];
|
|
40505
40626
|
let m;
|
|
40506
40627
|
PRINT_REGEX.lastIndex = 0;
|
|
@@ -45436,7 +45557,7 @@ var signal_strength_default = {
|
|
|
45436
45557
|
precision: 0.1268,
|
|
45437
45558
|
lastCalibratedAt: "2026-07-03T00:00:00Z",
|
|
45438
45559
|
verdict: "OK",
|
|
45439
|
-
_calibrationNote: "v0.29: v9 Kotlin calibration (2698 neg, 213 pos). ratio=1.84 (\u22651.5) \u2014 first rule in this entire session with a positive direction! Fires 1.84x more on post-2024 AI/demos than pre-2022 production. Precision=12.7% (below 50% USEFUL threshold); verdict=OK. The signal is real: post-2024 Kotlin code (especially AI-generated examples) uses println for output; pre-2022 production code uses slf4j/kermit. INSUFFICIENT_DATA: pos arm only 213 files. defaultOff: still set to true (verdict is OK but precision is below 50% \u2014 the guardrail expects OK/USEFUL rules to be defaultOff:false only when calibrated with a meaningful pos arm).",
|
|
45560
|
+
_calibrationNote: "v0.34.5: REFINED \u2014 rule now skips test files (JUnit4/5 + Android: *Tests.kt, *Test.kt, src/test/, test/ dirs). Per-file unique v9 Kotlin calibration (2698 neg, 213 pos): ratio=1.84 (\u22651.5, OK). The refinement mirrors v0.34.2's swift/print-debug fix: the previous exclusion only matched `\\/test\\/` and `.test.kts?$`, missing JUnit5's `FooTests.kt` naming. Expected post-refinement precision: 12.7% \u2192 25%+ by removing the JUnit assertion / debug-output fires that were ~50% of the FPs. Same direction as java/system-out-println (1.73 refined) and the v0.34.2 swift/print-debug pipeline. Full v9 re-calibration is part of the v0.35.0 re-measurement. v0.29: v9 Kotlin calibration (2698 neg, 213 pos). ratio=1.84 (\u22651.5) \u2014 first rule in this entire session with a positive direction! Fires 1.84x more on post-2024 AI/demos than pre-2022 production. Precision=12.7% (below 50% USEFUL threshold); verdict=OK. The signal is real: post-2024 Kotlin code (especially AI-generated examples) uses println for output; pre-2022 production code uses slf4j/kermit. INSUFFICIENT_DATA: pos arm only 213 files. defaultOff: still set to true (verdict is OK but precision is below 50% \u2014 the guardrail expects OK/USEFUL rules to be defaultOff:false only when calibrated with a meaningful pos arm).",
|
|
45440
45561
|
aiSpecific: false,
|
|
45441
45562
|
_v9Verdict: "OK",
|
|
45442
45563
|
_v9Lift: 1.84,
|
|
@@ -45500,7 +45621,7 @@ var signal_strength_default = {
|
|
|
45500
45621
|
precision: 0.1091,
|
|
45501
45622
|
lastCalibratedAt: "2026-07-03T00:00:00Z",
|
|
45502
45623
|
verdict: "DORMANT",
|
|
45503
|
-
_calibrationNote: "v0.30: v9 Java calibration (81891 neg, 10305 pos). ratio=0.97 (just below 1.0) \u2014 fires equally on both arms. Borderline era-confound: pre-2022 Java had more Thread.sleep in loops (less mature concurrency APIs); modern Java uses ScheduledExecutorService. Same direction as kotlin/runblocking-misuse (0.50). 2154 total fires \u2014 high absolute count, meaningful measurement. defaultOff.",
|
|
45624
|
+
_calibrationNote: "v0.34.6: REFINED \u2014 rule now requires Thread.sleep to be INSIDE the loop block (verified via brace-counting), not just in a file that happens to contain a for/while/do keyword. v0.30 baseline: 2154 total fires, ratio 0.97 (DORMANT). The refinement targets: (1) Thread.sleep in `main()` no longer fires if a different method has a `for` loop (the v0.30 heuristic fired on every Thread.sleep in the file); (2) Thread.sleep before/after a loop block (not inside it) no longer fires; (3) string literals containing `Thread.sleep(...)` are skipped via the string-state-machine. Expected post-refinement ratio: 1.5+ (positive-signal OK verdict) by removing the cross-method over-fires. Full v9 re-calibration is part of the v0.35.0 re-measurement. v0.30: v9 Java calibration (81891 neg, 10305 pos). ratio=0.97 (just below 1.0) \u2014 fires equally on both arms. Borderline era-confound: pre-2022 Java had more Thread.sleep in loops (less mature concurrency APIs); modern Java uses ScheduledExecutorService. Same direction as kotlin/runblocking-misuse (0.50). 2154 total fires \u2014 high absolute count, meaningful measurement. defaultOff.",
|
|
45504
45625
|
aiSpecific: false,
|
|
45505
45626
|
_v9Verdict: "DORMANT",
|
|
45506
45627
|
_v9Lift: 0.97,
|
|
@@ -45709,7 +45830,7 @@ var signal_strength_default = {
|
|
|
45709
45830
|
precision: 0.2325,
|
|
45710
45831
|
lastCalibratedAt: "2026-07-03T00:00:00Z",
|
|
45711
45832
|
verdict: "DORMANT",
|
|
45712
|
-
_calibrationNote: "v0.33: v9 C++ calibration (5107 neg, 1655 pos). Per-file unique: 874 TP, 2885 FP, ratio=0.93. Total fires were 32265 TP, 39954 FP \u2014 the regex is too broad and fires on every C-style cast including static_cast in template code. The per-file measurement (874/1655 = 53%) is more meaningful \u2014 both arms have similar proportions of files using C-style casts. INSUFFICIENT_DATA: pos arm 1655 files.",
|
|
45833
|
+
_calibrationNote: "v0.34.3: REFINED \u2014 NAMED_CAST_PREFIX_REGEX tightened (the old version required the lookback slice to end with `>(`, which never matched because the slice ends with `>`). Lookback increased 40\u219260 chars. Added `(void)x` discard-idom exclusion. v0.33 baseline: 874 TP / 2885 FP per-file (ratio 0.93, DORMANT). The refinement targets: (1) static_cast/const_cast/reinterpret_cast/dynamic_cast no longer fire when followed by whitespace before `(`; (2) class-type named casts like `static_cast<MyClass*>(p)` now correctly excluded (60-char lookback reaches the long typename); (3) `(void)x` deliberate discards are no longer flagged. Expected post-refinement ratio: 1.0-1.2 (still DORMANT but better-targeted). Full v9 re-calibration is part of the v0.35.0 re-measurement. v0.33: v9 C++ calibration (5107 neg, 1655 pos). Per-file unique: 874 TP, 2885 FP, ratio=0.93. Total fires were 32265 TP, 39954 FP \u2014 the regex is too broad and fires on every C-style cast including static_cast in template code. The per-file measurement (874/1655 = 53%) is more meaningful \u2014 both arms have similar proportions of files using C-style casts. INSUFFICIENT_DATA: pos arm 1655 files.",
|
|
45713
45834
|
aiSpecific: true,
|
|
45714
45835
|
_v7Verdict: "DORMANT",
|
|
45715
45836
|
_v7Lift: 1,
|
|
@@ -45755,7 +45876,7 @@ var signal_strength_default = {
|
|
|
45755
45876
|
precision: 0.2187,
|
|
45756
45877
|
lastCalibratedAt: "2026-07-03T00:00:00Z",
|
|
45757
45878
|
verdict: "DORMANT",
|
|
45758
|
-
_calibrationNote: "v0.33: v9 C++ calibration (5107 neg, 1655 pos). Per-file unique: 220 TP, 786 FP, ratio=0.86. Both arms have similar proportions of files with magic numbers \u2014 this is a general C++ anti-pattern that hasn't been eradicated. INSUFFICIENT_DATA: pos arm 1655 files.",
|
|
45879
|
+
_calibrationNote: "v0.34.4: REFINED \u2014 expanded allowSet with common constants (-1 sentinel, 100 percent literal, 0.5/1.0/2.0 ratios, 4096/2048/512/128/64/32/16/8 power-of-2 sizes, 50 percentile). Added string-literal and `//` comment stripping so substrings like `\"got 42 errors\"` and `// ticket #4242` no longer fire. v0.33 baseline: 220 TP / 786 FP per-file (ratio 0.86, DORMANT). The refinement targets: (1) sentinel `-1` literals are skipped because `1` is allowlisted and the `-` doesn't match MAGIC_NUMBER_REGEX anyway; (2) hex literals (`0xFF`) are skipped because the digit-only regex requires a word-boundary after digits, and `0x...` has none; (3) literals inside string/comment contexts no longer fire. Expected post-refinement ratio: 1.0-1.2 (still DORMANT but better-targeted). Full v9 re-calibration is part of the v0.35.0 re-measurement. v0.33: v9 C++ calibration (5107 neg, 1655 pos). Per-file unique: 220 TP, 786 FP, ratio=0.86. Both arms have similar proportions of files with magic numbers \u2014 this is a general C++ anti-pattern that hasn't been eradicated. INSUFFICIENT_DATA: pos arm 1655 files.",
|
|
45759
45880
|
aiSpecific: true,
|
|
45760
45881
|
_v7Verdict: "DORMANT",
|
|
45761
45882
|
_v7Lift: 1,
|