slopbrick 0.25.0 → 0.26.0
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 +418 -46
- package/dist/engine/worker.js +418 -46
- package/dist/index.cjs +467 -50
- package/dist/index.js +467 -50
- package/package.json +1 -1
package/dist/engine/worker.cjs
CHANGED
|
@@ -37026,6 +37026,48 @@ var javaArraylistVsLinkedlistRule = createRule({
|
|
|
37026
37026
|
}
|
|
37027
37027
|
});
|
|
37028
37028
|
|
|
37029
|
+
// src/rules/java/builder-overuse.ts
|
|
37030
|
+
var BUILDER_ANNOTATION_REGEX = /@Builder\b/;
|
|
37031
|
+
var FIELD_DECL_REGEX = /(?:private|public|protected)\s+(?:final\s+)?[\w<>,\s]+\s+(\w+)\s*[=;]/g;
|
|
37032
|
+
var DEFAULT_FIELD_COUNT_CAP = 3;
|
|
37033
|
+
var javaBuilderOveruseRule = createRule({
|
|
37034
|
+
id: "java/builder-overuse",
|
|
37035
|
+
category: "typo",
|
|
37036
|
+
severity: "low",
|
|
37037
|
+
aiSpecific: true,
|
|
37038
|
+
description: "@Builder on a class with few fields \u2014 plain constructor is simpler",
|
|
37039
|
+
create(_context) {
|
|
37040
|
+
return { fieldCountCap: DEFAULT_FIELD_COUNT_CAP };
|
|
37041
|
+
},
|
|
37042
|
+
analyze(context, facts) {
|
|
37043
|
+
const issues = [];
|
|
37044
|
+
const source = facts.v2?._source;
|
|
37045
|
+
if (!source) return issues;
|
|
37046
|
+
if (!/\.java$/i.test(facts.filePath)) return issues;
|
|
37047
|
+
BUILDER_ANNOTATION_REGEX.lastIndex = 0;
|
|
37048
|
+
const builderMatch = BUILDER_ANNOTATION_REGEX.exec(source);
|
|
37049
|
+
if (!builderMatch) return issues;
|
|
37050
|
+
const firstBuilderLine = source.slice(0, builderMatch.index).split("\n").length;
|
|
37051
|
+
FIELD_DECL_REGEX.lastIndex = 0;
|
|
37052
|
+
let fieldCount = 0;
|
|
37053
|
+
while (FIELD_DECL_REGEX.exec(source) !== null) {
|
|
37054
|
+
fieldCount++;
|
|
37055
|
+
}
|
|
37056
|
+
if (fieldCount > context.fieldCountCap) return issues;
|
|
37057
|
+
issues.push({
|
|
37058
|
+
ruleId: "java/builder-overuse",
|
|
37059
|
+
category: "typo",
|
|
37060
|
+
severity: "low",
|
|
37061
|
+
aiSpecific: true,
|
|
37062
|
+
message: `@Builder on a class with ${fieldCount} field(s) \u2014 plain constructor is simpler`,
|
|
37063
|
+
line: firstBuilderLine,
|
|
37064
|
+
column: 1,
|
|
37065
|
+
advice: "Use a plain constructor for classes with \u2264 3 fields. AI agents default to @Builder because their training data emphasizes the pattern. Builder adds Lombok dependency and a Builder inner class for every annotated class. Reference: java/builder-overuse v0.26.0."
|
|
37066
|
+
});
|
|
37067
|
+
return issues;
|
|
37068
|
+
}
|
|
37069
|
+
});
|
|
37070
|
+
|
|
37029
37071
|
// src/rules/java/empty-catch-block.ts
|
|
37030
37072
|
var SINGLE_LINE_EMPTY_CATCH_REGEX = /catch\s*\([^)]*\)\s*\{\s*\}/g;
|
|
37031
37073
|
var javaEmptyCatchBlockRule = createRule({
|
|
@@ -37061,6 +37103,58 @@ var javaEmptyCatchBlockRule = createRule({
|
|
|
37061
37103
|
}
|
|
37062
37104
|
});
|
|
37063
37105
|
|
|
37106
|
+
// src/rules/java/immutable-collection-preference.ts
|
|
37107
|
+
var IMMUTABLE_REGEX = /\b(List|Map|Set)\.of\s*\(/g;
|
|
37108
|
+
var MUTABLE_REGEX = /new\s+(ArrayList|HashMap|HashSet|LinkedList|TreeMap)\s*[<(]/g;
|
|
37109
|
+
var DEFAULT_IMMUTABLE_THRESHOLD = 5;
|
|
37110
|
+
var DEFAULT_MUTABLE_CAP = 1;
|
|
37111
|
+
var javaImmutableCollectionPreferenceRule = createRule({
|
|
37112
|
+
id: "java/immutable-collection-preference",
|
|
37113
|
+
category: "typo",
|
|
37114
|
+
severity: "low",
|
|
37115
|
+
aiSpecific: true,
|
|
37116
|
+
description: "Immutable factory method over-use \u2014 prefer mutable when collection will be modified",
|
|
37117
|
+
create(_context) {
|
|
37118
|
+
return {
|
|
37119
|
+
immutableThreshold: DEFAULT_IMMUTABLE_THRESHOLD,
|
|
37120
|
+
mutableCap: DEFAULT_MUTABLE_CAP
|
|
37121
|
+
};
|
|
37122
|
+
},
|
|
37123
|
+
analyze(context, facts) {
|
|
37124
|
+
const issues = [];
|
|
37125
|
+
const source = facts.v2?._source;
|
|
37126
|
+
if (!source) return issues;
|
|
37127
|
+
if (!/\.java$/i.test(facts.filePath)) return issues;
|
|
37128
|
+
IMMUTABLE_REGEX.lastIndex = 0;
|
|
37129
|
+
let immutableCount = 0;
|
|
37130
|
+
let firstIdx = 0;
|
|
37131
|
+
let m;
|
|
37132
|
+
while ((m = IMMUTABLE_REGEX.exec(source)) !== null) {
|
|
37133
|
+
immutableCount++;
|
|
37134
|
+
if (firstIdx === 0) firstIdx = m.index;
|
|
37135
|
+
}
|
|
37136
|
+
if (immutableCount < context.immutableThreshold) return issues;
|
|
37137
|
+
MUTABLE_REGEX.lastIndex = 0;
|
|
37138
|
+
let mutableCount = 0;
|
|
37139
|
+
while ((m = MUTABLE_REGEX.exec(source)) !== null) {
|
|
37140
|
+
mutableCount++;
|
|
37141
|
+
}
|
|
37142
|
+
if (mutableCount > context.mutableCap) return issues;
|
|
37143
|
+
const line = source.slice(0, firstIdx).split("\n").length;
|
|
37144
|
+
issues.push({
|
|
37145
|
+
ruleId: "java/immutable-collection-preference",
|
|
37146
|
+
category: "typo",
|
|
37147
|
+
severity: "low",
|
|
37148
|
+
aiSpecific: true,
|
|
37149
|
+
message: `${immutableCount} immutable factory calls (List/Map/Set.of) with only ${mutableCount} mutable collection \u2014 likely over-preferring immutability`,
|
|
37150
|
+
line,
|
|
37151
|
+
column: 1,
|
|
37152
|
+
advice: "Prefer mutable collections (new ArrayList<>()) when the collection will be modified later. AI agents default to List.of/Map.of/Set.of because their training data emphasizes functional-style Java. Reference: java/immutable-collection-preference v0.26.0."
|
|
37153
|
+
});
|
|
37154
|
+
return issues;
|
|
37155
|
+
}
|
|
37156
|
+
});
|
|
37157
|
+
|
|
37064
37158
|
// src/rules/java/legacy-date-api.ts
|
|
37065
37159
|
var LEGACY_IMPORT_REGEX = /^import\s+(?:static\s+)?java\.(?:util|sql)\.(?:Date|Calendar|GregorianCalendar)\s*;/gm;
|
|
37066
37160
|
var LEGACY_USAGE_REGEX = /\bnew\s+(?:Date|GregorianCalendar)\s*\(/g;
|
|
@@ -37130,6 +37224,52 @@ var javaLegacyDateApiRule = createRule({
|
|
|
37130
37224
|
}
|
|
37131
37225
|
});
|
|
37132
37226
|
|
|
37227
|
+
// src/rules/java/optional-overuse.ts
|
|
37228
|
+
var OR_ELSE_THROW_REGEX = /\.orElseThrow\s*\(/g;
|
|
37229
|
+
var NULL_CHECK_REGEX = /Objects\.requireNonNull\s*\(|if\s*\([^)]*==\s*null\)\s*(?:throw|return)/g;
|
|
37230
|
+
var DEFAULT_THRESHOLD = 2;
|
|
37231
|
+
var javaOptionalOveruseRule = createRule({
|
|
37232
|
+
id: "java/optional-overuse",
|
|
37233
|
+
category: "typo",
|
|
37234
|
+
severity: "low",
|
|
37235
|
+
aiSpecific: true,
|
|
37236
|
+
description: "Optional chain over-use \u2014 null check or Objects.requireNonNull is faster",
|
|
37237
|
+
create(_context) {
|
|
37238
|
+
return { threshold: DEFAULT_THRESHOLD };
|
|
37239
|
+
},
|
|
37240
|
+
analyze(context, facts) {
|
|
37241
|
+
const issues = [];
|
|
37242
|
+
const source = facts.v2?._source;
|
|
37243
|
+
if (!source) return issues;
|
|
37244
|
+
if (!/\.java$/i.test(facts.filePath)) return issues;
|
|
37245
|
+
OR_ELSE_THROW_REGEX.lastIndex = 0;
|
|
37246
|
+
let orElseThrowCount = 0;
|
|
37247
|
+
let firstIdx = 0;
|
|
37248
|
+
let m;
|
|
37249
|
+
while ((m = OR_ELSE_THROW_REGEX.exec(source)) !== null) {
|
|
37250
|
+
orElseThrowCount++;
|
|
37251
|
+
if (firstIdx === 0) firstIdx = m.index;
|
|
37252
|
+
}
|
|
37253
|
+
if (orElseThrowCount < context.threshold) return issues;
|
|
37254
|
+
NULL_CHECK_REGEX.lastIndex = 0;
|
|
37255
|
+
const nullCheckCount = (source.match(NULL_CHECK_REGEX) ?? []).length;
|
|
37256
|
+
const optionalRatio = orElseThrowCount / Math.max(orElseThrowCount + nullCheckCount, 1);
|
|
37257
|
+
if (optionalRatio < 0.6) return issues;
|
|
37258
|
+
const line = source.slice(0, firstIdx).split("\n").length;
|
|
37259
|
+
issues.push({
|
|
37260
|
+
ruleId: "java/optional-overuse",
|
|
37261
|
+
category: "typo",
|
|
37262
|
+
severity: "low",
|
|
37263
|
+
aiSpecific: true,
|
|
37264
|
+
message: `${orElseThrowCount} .orElseThrow() calls with ${nullCheckCount} null checks \u2014 Optional chain over-use`,
|
|
37265
|
+
line,
|
|
37266
|
+
column: 1,
|
|
37267
|
+
advice: 'Use Objects.requireNonNull(x, "msg") for null checks; reserve Optional for return values. AI agents default to Optional chains because their training data emphasizes null-safety. Real Java code uses null checks in hot paths. Reference: java/optional-overuse v0.26.0.'
|
|
37268
|
+
});
|
|
37269
|
+
return issues;
|
|
37270
|
+
}
|
|
37271
|
+
});
|
|
37272
|
+
|
|
37133
37273
|
// src/rules/java/raw-type-overuse.ts
|
|
37134
37274
|
var RAW_TYPE_REGEX = /\b(List|Map|Set|Collection|Iterable)\s+(?![<A-Z])(\w+)/g;
|
|
37135
37275
|
var javaRawTypeOveruseRule = createRule({
|
|
@@ -37166,6 +37306,68 @@ var javaRawTypeOveruseRule = createRule({
|
|
|
37166
37306
|
}
|
|
37167
37307
|
});
|
|
37168
37308
|
|
|
37309
|
+
// src/rules/java/stream-overuse.ts
|
|
37310
|
+
var STREAM_OPS = [
|
|
37311
|
+
".map(",
|
|
37312
|
+
".filter(",
|
|
37313
|
+
".flatMap(",
|
|
37314
|
+
".collect(",
|
|
37315
|
+
".reduce(",
|
|
37316
|
+
".sorted(",
|
|
37317
|
+
".distinct(",
|
|
37318
|
+
".limit(",
|
|
37319
|
+
".skip(",
|
|
37320
|
+
".anyMatch(",
|
|
37321
|
+
".allMatch(",
|
|
37322
|
+
".noneMatch(",
|
|
37323
|
+
".findFirst(",
|
|
37324
|
+
".findAny("
|
|
37325
|
+
];
|
|
37326
|
+
var DEFAULT_CHAIN_THRESHOLD = 3;
|
|
37327
|
+
var javaStreamOveruseRule = createRule({
|
|
37328
|
+
id: "java/stream-overuse",
|
|
37329
|
+
category: "typo",
|
|
37330
|
+
severity: "low",
|
|
37331
|
+
aiSpecific: true,
|
|
37332
|
+
description: "Stream API chain over-use \u2014 for-loop is faster for simple transformations",
|
|
37333
|
+
create(_context) {
|
|
37334
|
+
return { chainThreshold: DEFAULT_CHAIN_THRESHOLD };
|
|
37335
|
+
},
|
|
37336
|
+
analyze(context, facts) {
|
|
37337
|
+
const issues = [];
|
|
37338
|
+
const source = facts.v2?._source;
|
|
37339
|
+
if (!source) return issues;
|
|
37340
|
+
if (!/\.java$/i.test(facts.filePath)) return issues;
|
|
37341
|
+
const lines = source.split("\n");
|
|
37342
|
+
const streamOpPattern = new RegExp(STREAM_OPS.map((op) => op.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"), "g");
|
|
37343
|
+
let firstOffendingLine = 0;
|
|
37344
|
+
let firstOffendingCount = 0;
|
|
37345
|
+
for (let i = 0; i < lines.length; i++) {
|
|
37346
|
+
const line = lines[i] ?? "";
|
|
37347
|
+
if (line.trim().startsWith("//") || line.trim().startsWith("*")) continue;
|
|
37348
|
+
const matches = line.match(streamOpPattern);
|
|
37349
|
+
if (matches && matches.length >= context.chainThreshold) {
|
|
37350
|
+
if (firstOffendingLine === 0) {
|
|
37351
|
+
firstOffendingLine = i + 1;
|
|
37352
|
+
firstOffendingCount = matches.length;
|
|
37353
|
+
}
|
|
37354
|
+
}
|
|
37355
|
+
}
|
|
37356
|
+
if (firstOffendingLine === 0) return issues;
|
|
37357
|
+
issues.push({
|
|
37358
|
+
ruleId: "java/stream-overuse",
|
|
37359
|
+
category: "typo",
|
|
37360
|
+
severity: "low",
|
|
37361
|
+
aiSpecific: true,
|
|
37362
|
+
message: `${firstOffendingCount} stream operations on a single line \u2014 for-loop is faster`,
|
|
37363
|
+
line: firstOffendingLine,
|
|
37364
|
+
column: 1,
|
|
37365
|
+
advice: "Prefer a for-loop for simple transformations. AI agents default to Stream API chains because their training data emphasizes functional-style Java. Stream chains allocate intermediate objects; for small collections, a for-loop is faster and more readable. Reference: java/stream-overuse v0.26.0."
|
|
37366
|
+
});
|
|
37367
|
+
return issues;
|
|
37368
|
+
}
|
|
37369
|
+
});
|
|
37370
|
+
|
|
37169
37371
|
// src/rules/java/string-concat-loop.ts
|
|
37170
37372
|
var STRING_CONCAT_REGEX = /(\b\w+)\s*=\s*\1\s*\+\s*[^;]+;|(\b\w+)\s*\+=\s*['"`]/g;
|
|
37171
37373
|
var javaStringConcatLoopRule = createRule({
|
|
@@ -37204,7 +37406,7 @@ var javaStringConcatLoopRule = createRule({
|
|
|
37204
37406
|
|
|
37205
37407
|
// src/rules/java/system-out-println.ts
|
|
37206
37408
|
var PRINTLN_REGEX = /System\.out\.println\s*\(/g;
|
|
37207
|
-
var
|
|
37409
|
+
var DEFAULT_THRESHOLD2 = 1;
|
|
37208
37410
|
var javaSystemOutPrintlnRule = createRule({
|
|
37209
37411
|
id: "java/system-out-println",
|
|
37210
37412
|
category: "typo",
|
|
@@ -37212,7 +37414,7 @@ var javaSystemOutPrintlnRule = createRule({
|
|
|
37212
37414
|
aiSpecific: true,
|
|
37213
37415
|
description: "System.out.println in production code \u2014 use a logger (SLF4J, Log4j, etc.)",
|
|
37214
37416
|
create(_context) {
|
|
37215
|
-
return { threshold:
|
|
37417
|
+
return { threshold: DEFAULT_THRESHOLD2 };
|
|
37216
37418
|
},
|
|
37217
37419
|
analyze(context, facts) {
|
|
37218
37420
|
const issues = [];
|
|
@@ -37245,6 +37447,56 @@ var javaSystemOutPrintlnRule = createRule({
|
|
|
37245
37447
|
}
|
|
37246
37448
|
});
|
|
37247
37449
|
|
|
37450
|
+
// src/rules/java/verbose-javadoc.ts
|
|
37451
|
+
var TAG_REGEX = /@(param|return|throws)\b/g;
|
|
37452
|
+
var DEFAULT_TAG_THRESHOLD = 3;
|
|
37453
|
+
var DEFAULT_BODY_LENGTH_CAP = 5;
|
|
37454
|
+
var javaVerboseJavadocRule = createRule({
|
|
37455
|
+
id: "java/verbose-javadoc",
|
|
37456
|
+
category: "typo",
|
|
37457
|
+
severity: "low",
|
|
37458
|
+
aiSpecific: true,
|
|
37459
|
+
description: "Excessive Javadoc tags on trivial methods \u2014 over-documentation is an AI fingerprint",
|
|
37460
|
+
create(_context) {
|
|
37461
|
+
return {
|
|
37462
|
+
tagThreshold: DEFAULT_TAG_THRESHOLD,
|
|
37463
|
+
bodyLengthCap: DEFAULT_BODY_LENGTH_CAP
|
|
37464
|
+
};
|
|
37465
|
+
},
|
|
37466
|
+
analyze(context, facts) {
|
|
37467
|
+
const issues = [];
|
|
37468
|
+
const source = facts.v2?._source;
|
|
37469
|
+
if (!source) return issues;
|
|
37470
|
+
if (!/\.java$/i.test(facts.filePath)) return issues;
|
|
37471
|
+
TAG_REGEX.lastIndex = 0;
|
|
37472
|
+
let tagCount = 0;
|
|
37473
|
+
let firstTagLine = 0;
|
|
37474
|
+
let m;
|
|
37475
|
+
while ((m = TAG_REGEX.exec(source)) !== null) {
|
|
37476
|
+
tagCount++;
|
|
37477
|
+
if (firstTagLine === 0) {
|
|
37478
|
+
firstTagLine = source.slice(0, m.index).split("\n").length;
|
|
37479
|
+
}
|
|
37480
|
+
}
|
|
37481
|
+
if (tagCount < context.tagThreshold) return issues;
|
|
37482
|
+
const lineCount = source.split("\n").length;
|
|
37483
|
+
const tagDensity = tagCount / Math.max(lineCount, 1);
|
|
37484
|
+
if (tagDensity < 0.05) return issues;
|
|
37485
|
+
if (lineCount > 200) return issues;
|
|
37486
|
+
issues.push({
|
|
37487
|
+
ruleId: "java/verbose-javadoc",
|
|
37488
|
+
category: "typo",
|
|
37489
|
+
severity: "low",
|
|
37490
|
+
aiSpecific: true,
|
|
37491
|
+
message: `${tagCount} Javadoc tags in ${lineCount} lines (density ${(tagDensity * 100).toFixed(1)}%) \u2014 likely over-documented`,
|
|
37492
|
+
line: firstTagLine,
|
|
37493
|
+
column: 1,
|
|
37494
|
+
advice: "Skip Javadoc on trivial methods (getters, setters, builders). AI agents default to over-documentation because their training data has countless textbook Javadoc examples. Real Java code limits Javadoc to public API surface. Reference: java/verbose-javadoc v0.26.0."
|
|
37495
|
+
});
|
|
37496
|
+
return issues;
|
|
37497
|
+
}
|
|
37498
|
+
});
|
|
37499
|
+
|
|
37248
37500
|
// src/rules/kotlin/coroutine-global-scope.ts
|
|
37249
37501
|
var GLOBAL_SCOPE_REGEX = /GlobalScope\s*\.\s*(?:launch|async|runBlocking)\s*[({]/g;
|
|
37250
37502
|
var kotlinCoroutineGlobalScopeRule = createRule({
|
|
@@ -37389,7 +37641,7 @@ var kotlinObjectSingletonMisuseRule = createRule({
|
|
|
37389
37641
|
|
|
37390
37642
|
// src/rules/kotlin/println-debug.ts
|
|
37391
37643
|
var PRINTLN_REGEX2 = /^\s*println\s*\(/gm;
|
|
37392
|
-
var
|
|
37644
|
+
var DEFAULT_THRESHOLD3 = 1;
|
|
37393
37645
|
var kotlinPrintlnDebugRule = createRule({
|
|
37394
37646
|
id: "kotlin/println-debug",
|
|
37395
37647
|
category: "typo",
|
|
@@ -37397,7 +37649,7 @@ var kotlinPrintlnDebugRule = createRule({
|
|
|
37397
37649
|
aiSpecific: true,
|
|
37398
37650
|
description: "println(...) in production code \u2014 use Timber, android.util.Log, or an SLF4J facade",
|
|
37399
37651
|
create(_context) {
|
|
37400
|
-
return { threshold:
|
|
37652
|
+
return { threshold: DEFAULT_THRESHOLD3 };
|
|
37401
37653
|
},
|
|
37402
37654
|
analyze(context, facts) {
|
|
37403
37655
|
const issues = [];
|
|
@@ -37840,19 +38092,24 @@ var DEFAULT_CONFIG3 = {
|
|
|
37840
38092
|
* - `tests/rules/**` — rule test files contain expected-issue
|
|
37841
38093
|
* assertions, also meta-code.
|
|
37842
38094
|
*
|
|
37843
|
-
* Three patterns
|
|
37844
|
-
*
|
|
37845
|
-
*
|
|
37846
|
-
*
|
|
38095
|
+
* Three patterns (broadened in v0.25.1 from the v0.25.0 narrow
|
|
38096
|
+
* set, which missed `tests/engine/**`, `tests/cli/**`, and
|
|
38097
|
+
* `snippet/**`), ~80 issues removed per self-scan. Combined
|
|
38098
|
+
* with the v0.25.0 graded security cap, this restores the
|
|
38099
|
+
* v9 plan's "security ≥ 80" criterion (unachievable in
|
|
38100
|
+
* v0.24.0 due to 90 self-scan FPs collapsing the score to 0).
|
|
37847
38101
|
*
|
|
37848
38102
|
* Set `selfScan: { excludePaths: [] }` in `slopbrick.config.mjs`
|
|
37849
38103
|
* to opt out and scan every file (legacy behavior).
|
|
37850
38104
|
*/
|
|
37851
38105
|
selfScan: {
|
|
37852
38106
|
excludePaths: [
|
|
37853
|
-
"src/rules/**",
|
|
37854
|
-
|
|
37855
|
-
"
|
|
38107
|
+
"**/src/rules/**",
|
|
38108
|
+
// rule definitions are meta-code
|
|
38109
|
+
"**/snippet/**",
|
|
38110
|
+
// RULE_HINTS examples are intentional bad code
|
|
38111
|
+
"**/tests/**"
|
|
38112
|
+
// all test files (fixtures + unit + integration)
|
|
37856
38113
|
]
|
|
37857
38114
|
}
|
|
37858
38115
|
};
|
|
@@ -40322,7 +40579,7 @@ var swiftImplicitlyUnwrappedOptionalRule = createRule({
|
|
|
40322
40579
|
|
|
40323
40580
|
// src/rules/swift/print-debug.ts
|
|
40324
40581
|
var PRINT_REGEX = /\bprint\s*\(/g;
|
|
40325
|
-
var
|
|
40582
|
+
var DEFAULT_THRESHOLD4 = 1;
|
|
40326
40583
|
var swiftPrintDebugRule = createRule({
|
|
40327
40584
|
id: "swift/print-debug",
|
|
40328
40585
|
category: "typo",
|
|
@@ -40330,7 +40587,7 @@ var swiftPrintDebugRule = createRule({
|
|
|
40330
40587
|
aiSpecific: true,
|
|
40331
40588
|
description: "print(...) in production Swift \u2014 use Logger (os.log) for level-controlled output",
|
|
40332
40589
|
create(_context) {
|
|
40333
|
-
return { threshold:
|
|
40590
|
+
return { threshold: DEFAULT_THRESHOLD4 };
|
|
40334
40591
|
},
|
|
40335
40592
|
analyze(context, facts) {
|
|
40336
40593
|
const issues = [];
|
|
@@ -42986,11 +43243,16 @@ var builtinRules = [
|
|
|
42986
43243
|
goNilSliceVsEmptyRule,
|
|
42987
43244
|
goStructTagInconsistencyRule,
|
|
42988
43245
|
javaArraylistVsLinkedlistRule,
|
|
43246
|
+
javaBuilderOveruseRule,
|
|
42989
43247
|
javaEmptyCatchBlockRule,
|
|
43248
|
+
javaImmutableCollectionPreferenceRule,
|
|
42990
43249
|
javaLegacyDateApiRule,
|
|
43250
|
+
javaOptionalOveruseRule,
|
|
42991
43251
|
javaRawTypeOveruseRule,
|
|
43252
|
+
javaStreamOveruseRule,
|
|
42992
43253
|
javaStringConcatLoopRule,
|
|
42993
43254
|
javaSystemOutPrintlnRule,
|
|
43255
|
+
javaVerboseJavadocRule,
|
|
42994
43256
|
kotlinCoroutineGlobalScopeRule,
|
|
42995
43257
|
kotlinDataClassDefaultsOveruseRule,
|
|
42996
43258
|
kotlinObjectSingletonMisuseRule,
|
|
@@ -43856,13 +44118,13 @@ var signal_strength_default = {
|
|
|
43856
44118
|
defaultOff: true
|
|
43857
44119
|
},
|
|
43858
44120
|
"java/arraylist-vs-linkedlist": {
|
|
43859
|
-
recall:
|
|
43860
|
-
fpRate:
|
|
43861
|
-
ratio:
|
|
43862
|
-
precision: 0,
|
|
43863
|
-
lastCalibratedAt: "2026-07-
|
|
44121
|
+
recall: 35e-4,
|
|
44122
|
+
fpRate: 94e-4,
|
|
44123
|
+
ratio: 0.37,
|
|
44124
|
+
precision: 0.0446,
|
|
44125
|
+
lastCalibratedAt: "2026-07-02T19:34:00Z",
|
|
43864
44126
|
verdict: "DORMANT",
|
|
43865
|
-
_calibrationNote: "v0.20: new rule (new LinkedList<>() \u2014 use ArrayList
|
|
44127
|
+
_calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files = 92196 total. v9 TP=36, FP=771, P=4.46%, FPR=0.94%, recall=0.35%, ratio=0.37, lift=P/FPR=4.74. Rule fires more on legacy enterprise Java (neg) than on AI/ML-integration Java (pos) because modern Java idiom defaults to ArrayList. v0.20: new rule (new LinkedList<>() \u2014 use ArrayList, Effective Java Item 28).",
|
|
43866
44128
|
aiSpecific: true,
|
|
43867
44129
|
_v7Verdict: "DORMANT",
|
|
43868
44130
|
_v7Lift: 1,
|
|
@@ -43871,16 +44133,21 @@ var signal_strength_default = {
|
|
|
43871
44133
|
_v7Precision: 0,
|
|
43872
44134
|
_v8Verdict: "DORMANT",
|
|
43873
44135
|
_v8Lift: 1,
|
|
44136
|
+
_v9Verdict: "DORMANT",
|
|
44137
|
+
_v9Lift: 4.74,
|
|
44138
|
+
_v9Recall: 35e-4,
|
|
44139
|
+
_v9FpRate: 94e-4,
|
|
44140
|
+
_v9Precision: 0.0446,
|
|
43874
44141
|
defaultOff: true
|
|
43875
44142
|
},
|
|
43876
44143
|
"java/empty-catch-block": {
|
|
43877
|
-
recall:
|
|
43878
|
-
fpRate: 0,
|
|
43879
|
-
ratio:
|
|
43880
|
-
precision:
|
|
43881
|
-
lastCalibratedAt: "2026-07-
|
|
44144
|
+
recall: 8e-3,
|
|
44145
|
+
fpRate: 0.1118,
|
|
44146
|
+
ratio: 0.07,
|
|
44147
|
+
precision: 89e-4,
|
|
44148
|
+
lastCalibratedAt: "2026-07-02T19:34:00Z",
|
|
43882
44149
|
verdict: "DORMANT",
|
|
43883
|
-
_calibrationNote: "v0.
|
|
44150
|
+
_calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=82, FP=9159, P=0.89%, FPR=11.18%, recall=0.80%, ratio=0.07, lift=P/FPR=0.08. Strongly INVERTED: empty catch blocks are 14x more common in legacy enterprise Java than in AI/Java-modern pos repos. Heuristic likely needs to be tightened (e.g., require at least a comment) before it can serve as a meaningful discriminator. v0.20: new rule.",
|
|
43884
44151
|
aiSpecific: true,
|
|
43885
44152
|
_v7Verdict: "DORMANT",
|
|
43886
44153
|
_v7Lift: 1,
|
|
@@ -43889,16 +44156,21 @@ var signal_strength_default = {
|
|
|
43889
44156
|
_v7Precision: 0,
|
|
43890
44157
|
_v8Verdict: "DORMANT",
|
|
43891
44158
|
_v8Lift: 1,
|
|
44159
|
+
_v9Verdict: "DORMANT",
|
|
44160
|
+
_v9Lift: 0.08,
|
|
44161
|
+
_v9Recall: 8e-3,
|
|
44162
|
+
_v9FpRate: 0.1118,
|
|
44163
|
+
_v9Precision: 89e-4,
|
|
43892
44164
|
defaultOff: true
|
|
43893
44165
|
},
|
|
43894
44166
|
"java/legacy-date-api": {
|
|
43895
|
-
recall: 0,
|
|
43896
|
-
fpRate: 0,
|
|
43897
|
-
ratio:
|
|
43898
|
-
precision: 0,
|
|
43899
|
-
lastCalibratedAt: "2026-07-
|
|
44167
|
+
recall: 0.0285,
|
|
44168
|
+
fpRate: 0.0511,
|
|
44169
|
+
ratio: 0.56,
|
|
44170
|
+
precision: 0.0657,
|
|
44171
|
+
lastCalibratedAt: "2026-07-02T19:34:00Z",
|
|
43900
44172
|
verdict: "DORMANT",
|
|
43901
|
-
_calibrationNote: "v0.
|
|
44173
|
+
_calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=294, FP=4181, P=6.57%, FPR=5.11%, recall=2.85%, ratio=0.56, lift=P/FPR=1.29. INVERTED-to-NOISY: legacy date API is more common in older enterprise Java (neg) than in modern AI/Java repos (pos). The heuristic correctly flags the legacy pattern, but the legacy pattern is over-represented in the neg baseline (older codebases). v0.20: new rule.",
|
|
43902
44174
|
aiSpecific: true,
|
|
43903
44175
|
_v7Verdict: "DORMANT",
|
|
43904
44176
|
_v7Lift: 1,
|
|
@@ -43907,16 +44179,101 @@ var signal_strength_default = {
|
|
|
43907
44179
|
_v7Precision: 0,
|
|
43908
44180
|
_v8Verdict: "DORMANT",
|
|
43909
44181
|
_v8Lift: 1,
|
|
44182
|
+
_v9Verdict: "DORMANT",
|
|
44183
|
+
_v9Lift: 1.29,
|
|
44184
|
+
_v9Recall: 0.0285,
|
|
44185
|
+
_v9FpRate: 0.0511,
|
|
44186
|
+
_v9Precision: 0.0657,
|
|
43910
44187
|
defaultOff: true
|
|
43911
44188
|
},
|
|
43912
|
-
"java/
|
|
44189
|
+
"java/verbose-javadoc": {
|
|
43913
44190
|
recall: 0,
|
|
43914
44191
|
fpRate: 0,
|
|
43915
44192
|
ratio: 1,
|
|
43916
44193
|
precision: 0,
|
|
43917
|
-
lastCalibratedAt: "2026-07-
|
|
44194
|
+
lastCalibratedAt: "2026-07-03T00:00:00Z",
|
|
44195
|
+
verdict: "DORMANT",
|
|
44196
|
+
_calibrationNote: "v0.26.0: new positive AI-signal rule. Triggers when a small Java file (< 200 lines) has 3+ Javadoc tags with density >= 0.05. Calibration pending v9 Java corpus re-run; placeholder values until then. v0.20.0: anti-pattern design was wrong (humans write more Javadoc on trivial methods because their training data has it). v0.26.0: redesigned as positive signal (AI over-documents trivial methods). Reference: java/verbose-javadoc v0.26.0.",
|
|
44197
|
+
aiSpecific: true,
|
|
44198
|
+
_v9Verdict: "DORMANT",
|
|
44199
|
+
_v9Lift: 1,
|
|
44200
|
+
_v9Recall: 0,
|
|
44201
|
+
_v9FpRate: 0,
|
|
44202
|
+
_v9Precision: 0,
|
|
44203
|
+
defaultOff: true
|
|
44204
|
+
},
|
|
44205
|
+
"java/optional-overuse": {
|
|
44206
|
+
recall: 0,
|
|
44207
|
+
fpRate: 0,
|
|
44208
|
+
ratio: 1,
|
|
44209
|
+
precision: 0,
|
|
44210
|
+
lastCalibratedAt: "2026-07-03T00:00:00Z",
|
|
44211
|
+
verdict: "DORMANT",
|
|
44212
|
+
_calibrationNote: "v0.26.0: new positive AI-signal rule. Triggers when a Java file has 2+ .orElseThrow() calls AND 0 Objects.requireNonNull()/null checks (optionalRatio > 0.6). Calibration pending v9 Java corpus re-run; placeholder values until then. v0.26.0: redesigned as positive signal (AI chains Optional.ofNullable().orElseThrow() where null checks would be cleaner). Reference: java/optional-overuse v0.26.0.",
|
|
44213
|
+
aiSpecific: true,
|
|
44214
|
+
_v9Verdict: "DORMANT",
|
|
44215
|
+
_v9Lift: 1,
|
|
44216
|
+
_v9Recall: 0,
|
|
44217
|
+
_v9FpRate: 0,
|
|
44218
|
+
_v9Precision: 0,
|
|
44219
|
+
defaultOff: true
|
|
44220
|
+
},
|
|
44221
|
+
"java/immutable-collection-preference": {
|
|
44222
|
+
recall: 0,
|
|
44223
|
+
fpRate: 0,
|
|
44224
|
+
ratio: 1,
|
|
44225
|
+
precision: 0,
|
|
44226
|
+
lastCalibratedAt: "2026-07-03T00:00:00Z",
|
|
44227
|
+
verdict: "DORMANT",
|
|
44228
|
+
_calibrationNote: "v0.26.0: new positive AI-signal rule. Triggers when a Java file has 5+ List.of/Map.of/Set.of calls AND < 1 new ArrayList/HashMap/HashSet/LinkedList/TreeMap calls. Calibration pending v9 Java corpus re-run; placeholder values until then. v0.26.0: redesigned as positive signal (AI defaults to immutable factory methods). Reference: java/immutable-collection-preference v0.26.0.",
|
|
44229
|
+
aiSpecific: true,
|
|
44230
|
+
_v9Verdict: "DORMANT",
|
|
44231
|
+
_v9Lift: 1,
|
|
44232
|
+
_v9Recall: 0,
|
|
44233
|
+
_v9FpRate: 0,
|
|
44234
|
+
_v9Precision: 0,
|
|
44235
|
+
defaultOff: true
|
|
44236
|
+
},
|
|
44237
|
+
"java/builder-overuse": {
|
|
44238
|
+
recall: 0,
|
|
44239
|
+
fpRate: 0,
|
|
44240
|
+
ratio: 1,
|
|
44241
|
+
precision: 0,
|
|
44242
|
+
lastCalibratedAt: "2026-07-03T00:00:00Z",
|
|
44243
|
+
verdict: "DORMANT",
|
|
44244
|
+
_calibrationNote: "v0.26.0: new positive AI-signal rule. Triggers when @Builder annotation (Lombok) is used on a class with <= 3 fields. Calibration pending v9 Java corpus re-run; placeholder values until then. v0.26.0: redesigned as positive signal (AI defaults to Builder pattern even for simple data classes). Reference: java/builder-overuse v0.26.0.",
|
|
44245
|
+
aiSpecific: true,
|
|
44246
|
+
_v9Verdict: "DORMANT",
|
|
44247
|
+
_v9Lift: 1,
|
|
44248
|
+
_v9Recall: 0,
|
|
44249
|
+
_v9FpRate: 0,
|
|
44250
|
+
_v9Precision: 0,
|
|
44251
|
+
defaultOff: true
|
|
44252
|
+
},
|
|
44253
|
+
"java/stream-overuse": {
|
|
44254
|
+
recall: 0,
|
|
44255
|
+
fpRate: 0,
|
|
44256
|
+
ratio: 1,
|
|
44257
|
+
precision: 0,
|
|
44258
|
+
lastCalibratedAt: "2026-07-03T00:00:00Z",
|
|
44259
|
+
verdict: "DORMANT",
|
|
44260
|
+
_calibrationNote: "v0.26.0: new positive AI-signal rule. Triggers when a single line has 3+ Stream API operations (.map, .filter, .flatMap, .collect, .reduce, .sorted, .distinct, .limit, .skip, .anyMatch, .allMatch, .noneMatch, .findFirst, .findAny). Calibration pending v9 Java corpus re-run; placeholder values until then. v0.26.0: redesigned as positive signal (AI chains Stream API for everything). Reference: java/stream-overuse v0.26.0.",
|
|
44261
|
+
aiSpecific: true,
|
|
44262
|
+
_v9Verdict: "DORMANT",
|
|
44263
|
+
_v9Lift: 1,
|
|
44264
|
+
_v9Recall: 0,
|
|
44265
|
+
_v9FpRate: 0,
|
|
44266
|
+
_v9Precision: 0,
|
|
44267
|
+
defaultOff: true
|
|
44268
|
+
},
|
|
44269
|
+
"java/raw-type-overuse": {
|
|
44270
|
+
recall: 0.1093,
|
|
44271
|
+
fpRate: 0.2041,
|
|
44272
|
+
ratio: 0.54,
|
|
44273
|
+
precision: 0.0632,
|
|
44274
|
+
lastCalibratedAt: "2026-07-02T19:34:00Z",
|
|
43918
44275
|
verdict: "DORMANT",
|
|
43919
|
-
_calibrationNote: "v0.20:
|
|
44276
|
+
_calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=1126, FP=16712, P=6.32%, FPR=20.41%, recall=10.93%, ratio=0.54, lift=P/FPR=0.31. Strongly INVERTED: raw types (no generics) are 2x more common in legacy enterprise Java (neg) than in modern Java repos (pos). Heuristic is correctly identifying the legacy anti-pattern, but it's an age marker more than an AI-fingerprint. v0.20: new rule.",
|
|
43920
44277
|
aiSpecific: true,
|
|
43921
44278
|
_v7Verdict: "DORMANT",
|
|
43922
44279
|
_v7Lift: 1,
|
|
@@ -43925,16 +44282,21 @@ var signal_strength_default = {
|
|
|
43925
44282
|
_v7Precision: 0,
|
|
43926
44283
|
_v8Verdict: "DORMANT",
|
|
43927
44284
|
_v8Lift: 1,
|
|
44285
|
+
_v9Verdict: "DORMANT",
|
|
44286
|
+
_v9Lift: 0.31,
|
|
44287
|
+
_v9Recall: 0.1093,
|
|
44288
|
+
_v9FpRate: 0.2041,
|
|
44289
|
+
_v9Precision: 0.0632,
|
|
43928
44290
|
defaultOff: true
|
|
43929
44291
|
},
|
|
43930
44292
|
"java/string-concat-loop": {
|
|
43931
|
-
recall:
|
|
43932
|
-
fpRate: 0,
|
|
43933
|
-
ratio:
|
|
43934
|
-
precision: 0,
|
|
43935
|
-
lastCalibratedAt: "2026-07-
|
|
44293
|
+
recall: 46e-4,
|
|
44294
|
+
fpRate: 0.0358,
|
|
44295
|
+
ratio: 0.13,
|
|
44296
|
+
precision: 0.0158,
|
|
44297
|
+
lastCalibratedAt: "2026-07-02T19:34:00Z",
|
|
43936
44298
|
verdict: "DORMANT",
|
|
43937
|
-
_calibrationNote: "v0.
|
|
44299
|
+
_calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=47, FP=2933, P=1.58%, FPR=3.58%, recall=0.46%, ratio=0.13, lift=P/FPR=0.44. Strongly INVERTED: string concatenation in a loop is 8x more common in legacy enterprise Java (neg) than in AI/ML/Java-modern pos repos. Modern Java training data and modern IDE warnings discourage the pattern. v0.20: new rule.",
|
|
43938
44300
|
aiSpecific: true,
|
|
43939
44301
|
_v7Verdict: "DORMANT",
|
|
43940
44302
|
_v7Lift: 1,
|
|
@@ -43943,16 +44305,21 @@ var signal_strength_default = {
|
|
|
43943
44305
|
_v7Precision: 0,
|
|
43944
44306
|
_v8Verdict: "DORMANT",
|
|
43945
44307
|
_v8Lift: 1,
|
|
44308
|
+
_v9Verdict: "DORMANT",
|
|
44309
|
+
_v9Lift: 0.44,
|
|
44310
|
+
_v9Recall: 46e-4,
|
|
44311
|
+
_v9FpRate: 0.0358,
|
|
44312
|
+
_v9Precision: 0.0158,
|
|
43946
44313
|
defaultOff: true
|
|
43947
44314
|
},
|
|
43948
44315
|
"java/system-out-println": {
|
|
43949
|
-
recall: 0,
|
|
43950
|
-
fpRate: 0,
|
|
43951
|
-
ratio:
|
|
43952
|
-
precision: 0,
|
|
43953
|
-
lastCalibratedAt: "2026-07-
|
|
44316
|
+
recall: 0.1503,
|
|
44317
|
+
fpRate: 0.2533,
|
|
44318
|
+
ratio: 0.59,
|
|
44319
|
+
precision: 0.0695,
|
|
44320
|
+
lastCalibratedAt: "2026-07-02T19:34:00Z",
|
|
43954
44321
|
verdict: "DORMANT",
|
|
43955
|
-
_calibrationNote: "v0.
|
|
44322
|
+
_calibrationNote: "v9 Java corpus calibration (v0.25.0, 2026-07-02): 81891 neg + 10305 pos Java files. v9 TP=1549, FP=20737, P=6.95%, FPR=25.32%, recall=15.03%, ratio=0.59, lift=P/FPR=0.27. INVERTED: System.out.println is more common in legacy enterprise Java (neg) than in modern AI/Java repos (pos). Modern Java defaults to SLF4J/Log4j, AI-generated Java starts with a logger import. The pattern is the opposite of the v0.20 design intent (which assumed println was the AI-fingerprint signal). v0.25.x: consider gating this rule by Java era (recent file mtime) or making it an age-fingerprint rather than AI-fingerprint. v0.20: new rule.",
|
|
43956
44323
|
aiSpecific: true,
|
|
43957
44324
|
_v7Verdict: "DORMANT",
|
|
43958
44325
|
_v7Lift: 1,
|
|
@@ -43961,6 +44328,11 @@ var signal_strength_default = {
|
|
|
43961
44328
|
_v7Precision: 0,
|
|
43962
44329
|
_v8Verdict: "DORMANT",
|
|
43963
44330
|
_v8Lift: 1,
|
|
44331
|
+
_v9Verdict: "DORMANT",
|
|
44332
|
+
_v9Lift: 0.27,
|
|
44333
|
+
_v9Recall: 0.1503,
|
|
44334
|
+
_v9FpRate: 0.2533,
|
|
44335
|
+
_v9Precision: 0.0695,
|
|
43964
44336
|
defaultOff: true
|
|
43965
44337
|
},
|
|
43966
44338
|
"layout/forced-layout": {
|