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.js
CHANGED
|
@@ -36997,6 +36997,48 @@ var javaArraylistVsLinkedlistRule = createRule({
|
|
|
36997
36997
|
}
|
|
36998
36998
|
});
|
|
36999
36999
|
|
|
37000
|
+
// src/rules/java/builder-overuse.ts
|
|
37001
|
+
var BUILDER_ANNOTATION_REGEX = /@Builder\b/;
|
|
37002
|
+
var FIELD_DECL_REGEX = /(?:private|public|protected)\s+(?:final\s+)?[\w<>,\s]+\s+(\w+)\s*[=;]/g;
|
|
37003
|
+
var DEFAULT_FIELD_COUNT_CAP = 3;
|
|
37004
|
+
var javaBuilderOveruseRule = createRule({
|
|
37005
|
+
id: "java/builder-overuse",
|
|
37006
|
+
category: "typo",
|
|
37007
|
+
severity: "low",
|
|
37008
|
+
aiSpecific: true,
|
|
37009
|
+
description: "@Builder on a class with few fields \u2014 plain constructor is simpler",
|
|
37010
|
+
create(_context) {
|
|
37011
|
+
return { fieldCountCap: DEFAULT_FIELD_COUNT_CAP };
|
|
37012
|
+
},
|
|
37013
|
+
analyze(context, facts) {
|
|
37014
|
+
const issues = [];
|
|
37015
|
+
const source = facts.v2?._source;
|
|
37016
|
+
if (!source) return issues;
|
|
37017
|
+
if (!/\.java$/i.test(facts.filePath)) return issues;
|
|
37018
|
+
BUILDER_ANNOTATION_REGEX.lastIndex = 0;
|
|
37019
|
+
const builderMatch = BUILDER_ANNOTATION_REGEX.exec(source);
|
|
37020
|
+
if (!builderMatch) return issues;
|
|
37021
|
+
const firstBuilderLine = source.slice(0, builderMatch.index).split("\n").length;
|
|
37022
|
+
FIELD_DECL_REGEX.lastIndex = 0;
|
|
37023
|
+
let fieldCount = 0;
|
|
37024
|
+
while (FIELD_DECL_REGEX.exec(source) !== null) {
|
|
37025
|
+
fieldCount++;
|
|
37026
|
+
}
|
|
37027
|
+
if (fieldCount > context.fieldCountCap) return issues;
|
|
37028
|
+
issues.push({
|
|
37029
|
+
ruleId: "java/builder-overuse",
|
|
37030
|
+
category: "typo",
|
|
37031
|
+
severity: "low",
|
|
37032
|
+
aiSpecific: true,
|
|
37033
|
+
message: `@Builder on a class with ${fieldCount} field(s) \u2014 plain constructor is simpler`,
|
|
37034
|
+
line: firstBuilderLine,
|
|
37035
|
+
column: 1,
|
|
37036
|
+
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."
|
|
37037
|
+
});
|
|
37038
|
+
return issues;
|
|
37039
|
+
}
|
|
37040
|
+
});
|
|
37041
|
+
|
|
37000
37042
|
// src/rules/java/empty-catch-block.ts
|
|
37001
37043
|
var SINGLE_LINE_EMPTY_CATCH_REGEX = /catch\s*\([^)]*\)\s*\{\s*\}/g;
|
|
37002
37044
|
var javaEmptyCatchBlockRule = createRule({
|
|
@@ -37032,6 +37074,58 @@ var javaEmptyCatchBlockRule = createRule({
|
|
|
37032
37074
|
}
|
|
37033
37075
|
});
|
|
37034
37076
|
|
|
37077
|
+
// src/rules/java/immutable-collection-preference.ts
|
|
37078
|
+
var IMMUTABLE_REGEX = /\b(List|Map|Set)\.of\s*\(/g;
|
|
37079
|
+
var MUTABLE_REGEX = /new\s+(ArrayList|HashMap|HashSet|LinkedList|TreeMap)\s*[<(]/g;
|
|
37080
|
+
var DEFAULT_IMMUTABLE_THRESHOLD = 5;
|
|
37081
|
+
var DEFAULT_MUTABLE_CAP = 1;
|
|
37082
|
+
var javaImmutableCollectionPreferenceRule = createRule({
|
|
37083
|
+
id: "java/immutable-collection-preference",
|
|
37084
|
+
category: "typo",
|
|
37085
|
+
severity: "low",
|
|
37086
|
+
aiSpecific: true,
|
|
37087
|
+
description: "Immutable factory method over-use \u2014 prefer mutable when collection will be modified",
|
|
37088
|
+
create(_context) {
|
|
37089
|
+
return {
|
|
37090
|
+
immutableThreshold: DEFAULT_IMMUTABLE_THRESHOLD,
|
|
37091
|
+
mutableCap: DEFAULT_MUTABLE_CAP
|
|
37092
|
+
};
|
|
37093
|
+
},
|
|
37094
|
+
analyze(context, facts) {
|
|
37095
|
+
const issues = [];
|
|
37096
|
+
const source = facts.v2?._source;
|
|
37097
|
+
if (!source) return issues;
|
|
37098
|
+
if (!/\.java$/i.test(facts.filePath)) return issues;
|
|
37099
|
+
IMMUTABLE_REGEX.lastIndex = 0;
|
|
37100
|
+
let immutableCount = 0;
|
|
37101
|
+
let firstIdx = 0;
|
|
37102
|
+
let m;
|
|
37103
|
+
while ((m = IMMUTABLE_REGEX.exec(source)) !== null) {
|
|
37104
|
+
immutableCount++;
|
|
37105
|
+
if (firstIdx === 0) firstIdx = m.index;
|
|
37106
|
+
}
|
|
37107
|
+
if (immutableCount < context.immutableThreshold) return issues;
|
|
37108
|
+
MUTABLE_REGEX.lastIndex = 0;
|
|
37109
|
+
let mutableCount = 0;
|
|
37110
|
+
while ((m = MUTABLE_REGEX.exec(source)) !== null) {
|
|
37111
|
+
mutableCount++;
|
|
37112
|
+
}
|
|
37113
|
+
if (mutableCount > context.mutableCap) return issues;
|
|
37114
|
+
const line = source.slice(0, firstIdx).split("\n").length;
|
|
37115
|
+
issues.push({
|
|
37116
|
+
ruleId: "java/immutable-collection-preference",
|
|
37117
|
+
category: "typo",
|
|
37118
|
+
severity: "low",
|
|
37119
|
+
aiSpecific: true,
|
|
37120
|
+
message: `${immutableCount} immutable factory calls (List/Map/Set.of) with only ${mutableCount} mutable collection \u2014 likely over-preferring immutability`,
|
|
37121
|
+
line,
|
|
37122
|
+
column: 1,
|
|
37123
|
+
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."
|
|
37124
|
+
});
|
|
37125
|
+
return issues;
|
|
37126
|
+
}
|
|
37127
|
+
});
|
|
37128
|
+
|
|
37035
37129
|
// src/rules/java/legacy-date-api.ts
|
|
37036
37130
|
var LEGACY_IMPORT_REGEX = /^import\s+(?:static\s+)?java\.(?:util|sql)\.(?:Date|Calendar|GregorianCalendar)\s*;/gm;
|
|
37037
37131
|
var LEGACY_USAGE_REGEX = /\bnew\s+(?:Date|GregorianCalendar)\s*\(/g;
|
|
@@ -37101,6 +37195,52 @@ var javaLegacyDateApiRule = createRule({
|
|
|
37101
37195
|
}
|
|
37102
37196
|
});
|
|
37103
37197
|
|
|
37198
|
+
// src/rules/java/optional-overuse.ts
|
|
37199
|
+
var OR_ELSE_THROW_REGEX = /\.orElseThrow\s*\(/g;
|
|
37200
|
+
var NULL_CHECK_REGEX = /Objects\.requireNonNull\s*\(|if\s*\([^)]*==\s*null\)\s*(?:throw|return)/g;
|
|
37201
|
+
var DEFAULT_THRESHOLD = 2;
|
|
37202
|
+
var javaOptionalOveruseRule = createRule({
|
|
37203
|
+
id: "java/optional-overuse",
|
|
37204
|
+
category: "typo",
|
|
37205
|
+
severity: "low",
|
|
37206
|
+
aiSpecific: true,
|
|
37207
|
+
description: "Optional chain over-use \u2014 null check or Objects.requireNonNull is faster",
|
|
37208
|
+
create(_context) {
|
|
37209
|
+
return { threshold: DEFAULT_THRESHOLD };
|
|
37210
|
+
},
|
|
37211
|
+
analyze(context, facts) {
|
|
37212
|
+
const issues = [];
|
|
37213
|
+
const source = facts.v2?._source;
|
|
37214
|
+
if (!source) return issues;
|
|
37215
|
+
if (!/\.java$/i.test(facts.filePath)) return issues;
|
|
37216
|
+
OR_ELSE_THROW_REGEX.lastIndex = 0;
|
|
37217
|
+
let orElseThrowCount = 0;
|
|
37218
|
+
let firstIdx = 0;
|
|
37219
|
+
let m;
|
|
37220
|
+
while ((m = OR_ELSE_THROW_REGEX.exec(source)) !== null) {
|
|
37221
|
+
orElseThrowCount++;
|
|
37222
|
+
if (firstIdx === 0) firstIdx = m.index;
|
|
37223
|
+
}
|
|
37224
|
+
if (orElseThrowCount < context.threshold) return issues;
|
|
37225
|
+
NULL_CHECK_REGEX.lastIndex = 0;
|
|
37226
|
+
const nullCheckCount = (source.match(NULL_CHECK_REGEX) ?? []).length;
|
|
37227
|
+
const optionalRatio = orElseThrowCount / Math.max(orElseThrowCount + nullCheckCount, 1);
|
|
37228
|
+
if (optionalRatio < 0.6) return issues;
|
|
37229
|
+
const line = source.slice(0, firstIdx).split("\n").length;
|
|
37230
|
+
issues.push({
|
|
37231
|
+
ruleId: "java/optional-overuse",
|
|
37232
|
+
category: "typo",
|
|
37233
|
+
severity: "low",
|
|
37234
|
+
aiSpecific: true,
|
|
37235
|
+
message: `${orElseThrowCount} .orElseThrow() calls with ${nullCheckCount} null checks \u2014 Optional chain over-use`,
|
|
37236
|
+
line,
|
|
37237
|
+
column: 1,
|
|
37238
|
+
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.'
|
|
37239
|
+
});
|
|
37240
|
+
return issues;
|
|
37241
|
+
}
|
|
37242
|
+
});
|
|
37243
|
+
|
|
37104
37244
|
// src/rules/java/raw-type-overuse.ts
|
|
37105
37245
|
var RAW_TYPE_REGEX = /\b(List|Map|Set|Collection|Iterable)\s+(?![<A-Z])(\w+)/g;
|
|
37106
37246
|
var javaRawTypeOveruseRule = createRule({
|
|
@@ -37137,6 +37277,68 @@ var javaRawTypeOveruseRule = createRule({
|
|
|
37137
37277
|
}
|
|
37138
37278
|
});
|
|
37139
37279
|
|
|
37280
|
+
// src/rules/java/stream-overuse.ts
|
|
37281
|
+
var STREAM_OPS = [
|
|
37282
|
+
".map(",
|
|
37283
|
+
".filter(",
|
|
37284
|
+
".flatMap(",
|
|
37285
|
+
".collect(",
|
|
37286
|
+
".reduce(",
|
|
37287
|
+
".sorted(",
|
|
37288
|
+
".distinct(",
|
|
37289
|
+
".limit(",
|
|
37290
|
+
".skip(",
|
|
37291
|
+
".anyMatch(",
|
|
37292
|
+
".allMatch(",
|
|
37293
|
+
".noneMatch(",
|
|
37294
|
+
".findFirst(",
|
|
37295
|
+
".findAny("
|
|
37296
|
+
];
|
|
37297
|
+
var DEFAULT_CHAIN_THRESHOLD = 3;
|
|
37298
|
+
var javaStreamOveruseRule = createRule({
|
|
37299
|
+
id: "java/stream-overuse",
|
|
37300
|
+
category: "typo",
|
|
37301
|
+
severity: "low",
|
|
37302
|
+
aiSpecific: true,
|
|
37303
|
+
description: "Stream API chain over-use \u2014 for-loop is faster for simple transformations",
|
|
37304
|
+
create(_context) {
|
|
37305
|
+
return { chainThreshold: DEFAULT_CHAIN_THRESHOLD };
|
|
37306
|
+
},
|
|
37307
|
+
analyze(context, facts) {
|
|
37308
|
+
const issues = [];
|
|
37309
|
+
const source = facts.v2?._source;
|
|
37310
|
+
if (!source) return issues;
|
|
37311
|
+
if (!/\.java$/i.test(facts.filePath)) return issues;
|
|
37312
|
+
const lines = source.split("\n");
|
|
37313
|
+
const streamOpPattern = new RegExp(STREAM_OPS.map((op) => op.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"), "g");
|
|
37314
|
+
let firstOffendingLine = 0;
|
|
37315
|
+
let firstOffendingCount = 0;
|
|
37316
|
+
for (let i = 0; i < lines.length; i++) {
|
|
37317
|
+
const line = lines[i] ?? "";
|
|
37318
|
+
if (line.trim().startsWith("//") || line.trim().startsWith("*")) continue;
|
|
37319
|
+
const matches = line.match(streamOpPattern);
|
|
37320
|
+
if (matches && matches.length >= context.chainThreshold) {
|
|
37321
|
+
if (firstOffendingLine === 0) {
|
|
37322
|
+
firstOffendingLine = i + 1;
|
|
37323
|
+
firstOffendingCount = matches.length;
|
|
37324
|
+
}
|
|
37325
|
+
}
|
|
37326
|
+
}
|
|
37327
|
+
if (firstOffendingLine === 0) return issues;
|
|
37328
|
+
issues.push({
|
|
37329
|
+
ruleId: "java/stream-overuse",
|
|
37330
|
+
category: "typo",
|
|
37331
|
+
severity: "low",
|
|
37332
|
+
aiSpecific: true,
|
|
37333
|
+
message: `${firstOffendingCount} stream operations on a single line \u2014 for-loop is faster`,
|
|
37334
|
+
line: firstOffendingLine,
|
|
37335
|
+
column: 1,
|
|
37336
|
+
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."
|
|
37337
|
+
});
|
|
37338
|
+
return issues;
|
|
37339
|
+
}
|
|
37340
|
+
});
|
|
37341
|
+
|
|
37140
37342
|
// src/rules/java/string-concat-loop.ts
|
|
37141
37343
|
var STRING_CONCAT_REGEX = /(\b\w+)\s*=\s*\1\s*\+\s*[^;]+;|(\b\w+)\s*\+=\s*['"`]/g;
|
|
37142
37344
|
var javaStringConcatLoopRule = createRule({
|
|
@@ -37175,7 +37377,7 @@ var javaStringConcatLoopRule = createRule({
|
|
|
37175
37377
|
|
|
37176
37378
|
// src/rules/java/system-out-println.ts
|
|
37177
37379
|
var PRINTLN_REGEX = /System\.out\.println\s*\(/g;
|
|
37178
|
-
var
|
|
37380
|
+
var DEFAULT_THRESHOLD2 = 1;
|
|
37179
37381
|
var javaSystemOutPrintlnRule = createRule({
|
|
37180
37382
|
id: "java/system-out-println",
|
|
37181
37383
|
category: "typo",
|
|
@@ -37183,7 +37385,7 @@ var javaSystemOutPrintlnRule = createRule({
|
|
|
37183
37385
|
aiSpecific: true,
|
|
37184
37386
|
description: "System.out.println in production code \u2014 use a logger (SLF4J, Log4j, etc.)",
|
|
37185
37387
|
create(_context) {
|
|
37186
|
-
return { threshold:
|
|
37388
|
+
return { threshold: DEFAULT_THRESHOLD2 };
|
|
37187
37389
|
},
|
|
37188
37390
|
analyze(context, facts) {
|
|
37189
37391
|
const issues = [];
|
|
@@ -37216,6 +37418,56 @@ var javaSystemOutPrintlnRule = createRule({
|
|
|
37216
37418
|
}
|
|
37217
37419
|
});
|
|
37218
37420
|
|
|
37421
|
+
// src/rules/java/verbose-javadoc.ts
|
|
37422
|
+
var TAG_REGEX = /@(param|return|throws)\b/g;
|
|
37423
|
+
var DEFAULT_TAG_THRESHOLD = 3;
|
|
37424
|
+
var DEFAULT_BODY_LENGTH_CAP = 5;
|
|
37425
|
+
var javaVerboseJavadocRule = createRule({
|
|
37426
|
+
id: "java/verbose-javadoc",
|
|
37427
|
+
category: "typo",
|
|
37428
|
+
severity: "low",
|
|
37429
|
+
aiSpecific: true,
|
|
37430
|
+
description: "Excessive Javadoc tags on trivial methods \u2014 over-documentation is an AI fingerprint",
|
|
37431
|
+
create(_context) {
|
|
37432
|
+
return {
|
|
37433
|
+
tagThreshold: DEFAULT_TAG_THRESHOLD,
|
|
37434
|
+
bodyLengthCap: DEFAULT_BODY_LENGTH_CAP
|
|
37435
|
+
};
|
|
37436
|
+
},
|
|
37437
|
+
analyze(context, facts) {
|
|
37438
|
+
const issues = [];
|
|
37439
|
+
const source = facts.v2?._source;
|
|
37440
|
+
if (!source) return issues;
|
|
37441
|
+
if (!/\.java$/i.test(facts.filePath)) return issues;
|
|
37442
|
+
TAG_REGEX.lastIndex = 0;
|
|
37443
|
+
let tagCount = 0;
|
|
37444
|
+
let firstTagLine = 0;
|
|
37445
|
+
let m;
|
|
37446
|
+
while ((m = TAG_REGEX.exec(source)) !== null) {
|
|
37447
|
+
tagCount++;
|
|
37448
|
+
if (firstTagLine === 0) {
|
|
37449
|
+
firstTagLine = source.slice(0, m.index).split("\n").length;
|
|
37450
|
+
}
|
|
37451
|
+
}
|
|
37452
|
+
if (tagCount < context.tagThreshold) return issues;
|
|
37453
|
+
const lineCount = source.split("\n").length;
|
|
37454
|
+
const tagDensity = tagCount / Math.max(lineCount, 1);
|
|
37455
|
+
if (tagDensity < 0.05) return issues;
|
|
37456
|
+
if (lineCount > 200) return issues;
|
|
37457
|
+
issues.push({
|
|
37458
|
+
ruleId: "java/verbose-javadoc",
|
|
37459
|
+
category: "typo",
|
|
37460
|
+
severity: "low",
|
|
37461
|
+
aiSpecific: true,
|
|
37462
|
+
message: `${tagCount} Javadoc tags in ${lineCount} lines (density ${(tagDensity * 100).toFixed(1)}%) \u2014 likely over-documented`,
|
|
37463
|
+
line: firstTagLine,
|
|
37464
|
+
column: 1,
|
|
37465
|
+
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."
|
|
37466
|
+
});
|
|
37467
|
+
return issues;
|
|
37468
|
+
}
|
|
37469
|
+
});
|
|
37470
|
+
|
|
37219
37471
|
// src/rules/kotlin/coroutine-global-scope.ts
|
|
37220
37472
|
var GLOBAL_SCOPE_REGEX = /GlobalScope\s*\.\s*(?:launch|async|runBlocking)\s*[({]/g;
|
|
37221
37473
|
var kotlinCoroutineGlobalScopeRule = createRule({
|
|
@@ -37360,7 +37612,7 @@ var kotlinObjectSingletonMisuseRule = createRule({
|
|
|
37360
37612
|
|
|
37361
37613
|
// src/rules/kotlin/println-debug.ts
|
|
37362
37614
|
var PRINTLN_REGEX2 = /^\s*println\s*\(/gm;
|
|
37363
|
-
var
|
|
37615
|
+
var DEFAULT_THRESHOLD3 = 1;
|
|
37364
37616
|
var kotlinPrintlnDebugRule = createRule({
|
|
37365
37617
|
id: "kotlin/println-debug",
|
|
37366
37618
|
category: "typo",
|
|
@@ -37368,7 +37620,7 @@ var kotlinPrintlnDebugRule = createRule({
|
|
|
37368
37620
|
aiSpecific: true,
|
|
37369
37621
|
description: "println(...) in production code \u2014 use Timber, android.util.Log, or an SLF4J facade",
|
|
37370
37622
|
create(_context) {
|
|
37371
|
-
return { threshold:
|
|
37623
|
+
return { threshold: DEFAULT_THRESHOLD3 };
|
|
37372
37624
|
},
|
|
37373
37625
|
analyze(context, facts) {
|
|
37374
37626
|
const issues = [];
|
|
@@ -37811,19 +38063,24 @@ var DEFAULT_CONFIG3 = {
|
|
|
37811
38063
|
* - `tests/rules/**` — rule test files contain expected-issue
|
|
37812
38064
|
* assertions, also meta-code.
|
|
37813
38065
|
*
|
|
37814
|
-
* Three patterns
|
|
37815
|
-
*
|
|
37816
|
-
*
|
|
37817
|
-
*
|
|
38066
|
+
* Three patterns (broadened in v0.25.1 from the v0.25.0 narrow
|
|
38067
|
+
* set, which missed `tests/engine/**`, `tests/cli/**`, and
|
|
38068
|
+
* `snippet/**`), ~80 issues removed per self-scan. Combined
|
|
38069
|
+
* with the v0.25.0 graded security cap, this restores the
|
|
38070
|
+
* v9 plan's "security ≥ 80" criterion (unachievable in
|
|
38071
|
+
* v0.24.0 due to 90 self-scan FPs collapsing the score to 0).
|
|
37818
38072
|
*
|
|
37819
38073
|
* Set `selfScan: { excludePaths: [] }` in `slopbrick.config.mjs`
|
|
37820
38074
|
* to opt out and scan every file (legacy behavior).
|
|
37821
38075
|
*/
|
|
37822
38076
|
selfScan: {
|
|
37823
38077
|
excludePaths: [
|
|
37824
|
-
"src/rules/**",
|
|
37825
|
-
|
|
37826
|
-
"
|
|
38078
|
+
"**/src/rules/**",
|
|
38079
|
+
// rule definitions are meta-code
|
|
38080
|
+
"**/snippet/**",
|
|
38081
|
+
// RULE_HINTS examples are intentional bad code
|
|
38082
|
+
"**/tests/**"
|
|
38083
|
+
// all test files (fixtures + unit + integration)
|
|
37827
38084
|
]
|
|
37828
38085
|
}
|
|
37829
38086
|
};
|
|
@@ -40293,7 +40550,7 @@ var swiftImplicitlyUnwrappedOptionalRule = createRule({
|
|
|
40293
40550
|
|
|
40294
40551
|
// src/rules/swift/print-debug.ts
|
|
40295
40552
|
var PRINT_REGEX = /\bprint\s*\(/g;
|
|
40296
|
-
var
|
|
40553
|
+
var DEFAULT_THRESHOLD4 = 1;
|
|
40297
40554
|
var swiftPrintDebugRule = createRule({
|
|
40298
40555
|
id: "swift/print-debug",
|
|
40299
40556
|
category: "typo",
|
|
@@ -40301,7 +40558,7 @@ var swiftPrintDebugRule = createRule({
|
|
|
40301
40558
|
aiSpecific: true,
|
|
40302
40559
|
description: "print(...) in production Swift \u2014 use Logger (os.log) for level-controlled output",
|
|
40303
40560
|
create(_context) {
|
|
40304
|
-
return { threshold:
|
|
40561
|
+
return { threshold: DEFAULT_THRESHOLD4 };
|
|
40305
40562
|
},
|
|
40306
40563
|
analyze(context, facts) {
|
|
40307
40564
|
const issues = [];
|
|
@@ -42957,11 +43214,16 @@ var builtinRules = [
|
|
|
42957
43214
|
goNilSliceVsEmptyRule,
|
|
42958
43215
|
goStructTagInconsistencyRule,
|
|
42959
43216
|
javaArraylistVsLinkedlistRule,
|
|
43217
|
+
javaBuilderOveruseRule,
|
|
42960
43218
|
javaEmptyCatchBlockRule,
|
|
43219
|
+
javaImmutableCollectionPreferenceRule,
|
|
42961
43220
|
javaLegacyDateApiRule,
|
|
43221
|
+
javaOptionalOveruseRule,
|
|
42962
43222
|
javaRawTypeOveruseRule,
|
|
43223
|
+
javaStreamOveruseRule,
|
|
42963
43224
|
javaStringConcatLoopRule,
|
|
42964
43225
|
javaSystemOutPrintlnRule,
|
|
43226
|
+
javaVerboseJavadocRule,
|
|
42965
43227
|
kotlinCoroutineGlobalScopeRule,
|
|
42966
43228
|
kotlinDataClassDefaultsOveruseRule,
|
|
42967
43229
|
kotlinObjectSingletonMisuseRule,
|
|
@@ -43827,13 +44089,13 @@ var signal_strength_default = {
|
|
|
43827
44089
|
defaultOff: true
|
|
43828
44090
|
},
|
|
43829
44091
|
"java/arraylist-vs-linkedlist": {
|
|
43830
|
-
recall:
|
|
43831
|
-
fpRate:
|
|
43832
|
-
ratio:
|
|
43833
|
-
precision: 0,
|
|
43834
|
-
lastCalibratedAt: "2026-07-
|
|
44092
|
+
recall: 35e-4,
|
|
44093
|
+
fpRate: 94e-4,
|
|
44094
|
+
ratio: 0.37,
|
|
44095
|
+
precision: 0.0446,
|
|
44096
|
+
lastCalibratedAt: "2026-07-02T19:34:00Z",
|
|
43835
44097
|
verdict: "DORMANT",
|
|
43836
|
-
_calibrationNote: "v0.20: new rule (new LinkedList<>() \u2014 use ArrayList
|
|
44098
|
+
_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).",
|
|
43837
44099
|
aiSpecific: true,
|
|
43838
44100
|
_v7Verdict: "DORMANT",
|
|
43839
44101
|
_v7Lift: 1,
|
|
@@ -43842,16 +44104,21 @@ var signal_strength_default = {
|
|
|
43842
44104
|
_v7Precision: 0,
|
|
43843
44105
|
_v8Verdict: "DORMANT",
|
|
43844
44106
|
_v8Lift: 1,
|
|
44107
|
+
_v9Verdict: "DORMANT",
|
|
44108
|
+
_v9Lift: 4.74,
|
|
44109
|
+
_v9Recall: 35e-4,
|
|
44110
|
+
_v9FpRate: 94e-4,
|
|
44111
|
+
_v9Precision: 0.0446,
|
|
43845
44112
|
defaultOff: true
|
|
43846
44113
|
},
|
|
43847
44114
|
"java/empty-catch-block": {
|
|
43848
|
-
recall:
|
|
43849
|
-
fpRate: 0,
|
|
43850
|
-
ratio:
|
|
43851
|
-
precision:
|
|
43852
|
-
lastCalibratedAt: "2026-07-
|
|
44115
|
+
recall: 8e-3,
|
|
44116
|
+
fpRate: 0.1118,
|
|
44117
|
+
ratio: 0.07,
|
|
44118
|
+
precision: 89e-4,
|
|
44119
|
+
lastCalibratedAt: "2026-07-02T19:34:00Z",
|
|
43853
44120
|
verdict: "DORMANT",
|
|
43854
|
-
_calibrationNote: "v0.
|
|
44121
|
+
_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.",
|
|
43855
44122
|
aiSpecific: true,
|
|
43856
44123
|
_v7Verdict: "DORMANT",
|
|
43857
44124
|
_v7Lift: 1,
|
|
@@ -43860,16 +44127,21 @@ var signal_strength_default = {
|
|
|
43860
44127
|
_v7Precision: 0,
|
|
43861
44128
|
_v8Verdict: "DORMANT",
|
|
43862
44129
|
_v8Lift: 1,
|
|
44130
|
+
_v9Verdict: "DORMANT",
|
|
44131
|
+
_v9Lift: 0.08,
|
|
44132
|
+
_v9Recall: 8e-3,
|
|
44133
|
+
_v9FpRate: 0.1118,
|
|
44134
|
+
_v9Precision: 89e-4,
|
|
43863
44135
|
defaultOff: true
|
|
43864
44136
|
},
|
|
43865
44137
|
"java/legacy-date-api": {
|
|
43866
|
-
recall: 0,
|
|
43867
|
-
fpRate: 0,
|
|
43868
|
-
ratio:
|
|
43869
|
-
precision: 0,
|
|
43870
|
-
lastCalibratedAt: "2026-07-
|
|
44138
|
+
recall: 0.0285,
|
|
44139
|
+
fpRate: 0.0511,
|
|
44140
|
+
ratio: 0.56,
|
|
44141
|
+
precision: 0.0657,
|
|
44142
|
+
lastCalibratedAt: "2026-07-02T19:34:00Z",
|
|
43871
44143
|
verdict: "DORMANT",
|
|
43872
|
-
_calibrationNote: "v0.
|
|
44144
|
+
_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.",
|
|
43873
44145
|
aiSpecific: true,
|
|
43874
44146
|
_v7Verdict: "DORMANT",
|
|
43875
44147
|
_v7Lift: 1,
|
|
@@ -43878,16 +44150,101 @@ var signal_strength_default = {
|
|
|
43878
44150
|
_v7Precision: 0,
|
|
43879
44151
|
_v8Verdict: "DORMANT",
|
|
43880
44152
|
_v8Lift: 1,
|
|
44153
|
+
_v9Verdict: "DORMANT",
|
|
44154
|
+
_v9Lift: 1.29,
|
|
44155
|
+
_v9Recall: 0.0285,
|
|
44156
|
+
_v9FpRate: 0.0511,
|
|
44157
|
+
_v9Precision: 0.0657,
|
|
43881
44158
|
defaultOff: true
|
|
43882
44159
|
},
|
|
43883
|
-
"java/
|
|
44160
|
+
"java/verbose-javadoc": {
|
|
43884
44161
|
recall: 0,
|
|
43885
44162
|
fpRate: 0,
|
|
43886
44163
|
ratio: 1,
|
|
43887
44164
|
precision: 0,
|
|
43888
|
-
lastCalibratedAt: "2026-07-
|
|
44165
|
+
lastCalibratedAt: "2026-07-03T00:00:00Z",
|
|
44166
|
+
verdict: "DORMANT",
|
|
44167
|
+
_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.",
|
|
44168
|
+
aiSpecific: true,
|
|
44169
|
+
_v9Verdict: "DORMANT",
|
|
44170
|
+
_v9Lift: 1,
|
|
44171
|
+
_v9Recall: 0,
|
|
44172
|
+
_v9FpRate: 0,
|
|
44173
|
+
_v9Precision: 0,
|
|
44174
|
+
defaultOff: true
|
|
44175
|
+
},
|
|
44176
|
+
"java/optional-overuse": {
|
|
44177
|
+
recall: 0,
|
|
44178
|
+
fpRate: 0,
|
|
44179
|
+
ratio: 1,
|
|
44180
|
+
precision: 0,
|
|
44181
|
+
lastCalibratedAt: "2026-07-03T00:00:00Z",
|
|
44182
|
+
verdict: "DORMANT",
|
|
44183
|
+
_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.",
|
|
44184
|
+
aiSpecific: true,
|
|
44185
|
+
_v9Verdict: "DORMANT",
|
|
44186
|
+
_v9Lift: 1,
|
|
44187
|
+
_v9Recall: 0,
|
|
44188
|
+
_v9FpRate: 0,
|
|
44189
|
+
_v9Precision: 0,
|
|
44190
|
+
defaultOff: true
|
|
44191
|
+
},
|
|
44192
|
+
"java/immutable-collection-preference": {
|
|
44193
|
+
recall: 0,
|
|
44194
|
+
fpRate: 0,
|
|
44195
|
+
ratio: 1,
|
|
44196
|
+
precision: 0,
|
|
44197
|
+
lastCalibratedAt: "2026-07-03T00:00:00Z",
|
|
44198
|
+
verdict: "DORMANT",
|
|
44199
|
+
_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.",
|
|
44200
|
+
aiSpecific: true,
|
|
44201
|
+
_v9Verdict: "DORMANT",
|
|
44202
|
+
_v9Lift: 1,
|
|
44203
|
+
_v9Recall: 0,
|
|
44204
|
+
_v9FpRate: 0,
|
|
44205
|
+
_v9Precision: 0,
|
|
44206
|
+
defaultOff: true
|
|
44207
|
+
},
|
|
44208
|
+
"java/builder-overuse": {
|
|
44209
|
+
recall: 0,
|
|
44210
|
+
fpRate: 0,
|
|
44211
|
+
ratio: 1,
|
|
44212
|
+
precision: 0,
|
|
44213
|
+
lastCalibratedAt: "2026-07-03T00:00:00Z",
|
|
44214
|
+
verdict: "DORMANT",
|
|
44215
|
+
_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.",
|
|
44216
|
+
aiSpecific: true,
|
|
44217
|
+
_v9Verdict: "DORMANT",
|
|
44218
|
+
_v9Lift: 1,
|
|
44219
|
+
_v9Recall: 0,
|
|
44220
|
+
_v9FpRate: 0,
|
|
44221
|
+
_v9Precision: 0,
|
|
44222
|
+
defaultOff: true
|
|
44223
|
+
},
|
|
44224
|
+
"java/stream-overuse": {
|
|
44225
|
+
recall: 0,
|
|
44226
|
+
fpRate: 0,
|
|
44227
|
+
ratio: 1,
|
|
44228
|
+
precision: 0,
|
|
44229
|
+
lastCalibratedAt: "2026-07-03T00:00:00Z",
|
|
44230
|
+
verdict: "DORMANT",
|
|
44231
|
+
_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.",
|
|
44232
|
+
aiSpecific: true,
|
|
44233
|
+
_v9Verdict: "DORMANT",
|
|
44234
|
+
_v9Lift: 1,
|
|
44235
|
+
_v9Recall: 0,
|
|
44236
|
+
_v9FpRate: 0,
|
|
44237
|
+
_v9Precision: 0,
|
|
44238
|
+
defaultOff: true
|
|
44239
|
+
},
|
|
44240
|
+
"java/raw-type-overuse": {
|
|
44241
|
+
recall: 0.1093,
|
|
44242
|
+
fpRate: 0.2041,
|
|
44243
|
+
ratio: 0.54,
|
|
44244
|
+
precision: 0.0632,
|
|
44245
|
+
lastCalibratedAt: "2026-07-02T19:34:00Z",
|
|
43889
44246
|
verdict: "DORMANT",
|
|
43890
|
-
_calibrationNote: "v0.20:
|
|
44247
|
+
_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.",
|
|
43891
44248
|
aiSpecific: true,
|
|
43892
44249
|
_v7Verdict: "DORMANT",
|
|
43893
44250
|
_v7Lift: 1,
|
|
@@ -43896,16 +44253,21 @@ var signal_strength_default = {
|
|
|
43896
44253
|
_v7Precision: 0,
|
|
43897
44254
|
_v8Verdict: "DORMANT",
|
|
43898
44255
|
_v8Lift: 1,
|
|
44256
|
+
_v9Verdict: "DORMANT",
|
|
44257
|
+
_v9Lift: 0.31,
|
|
44258
|
+
_v9Recall: 0.1093,
|
|
44259
|
+
_v9FpRate: 0.2041,
|
|
44260
|
+
_v9Precision: 0.0632,
|
|
43899
44261
|
defaultOff: true
|
|
43900
44262
|
},
|
|
43901
44263
|
"java/string-concat-loop": {
|
|
43902
|
-
recall:
|
|
43903
|
-
fpRate: 0,
|
|
43904
|
-
ratio:
|
|
43905
|
-
precision: 0,
|
|
43906
|
-
lastCalibratedAt: "2026-07-
|
|
44264
|
+
recall: 46e-4,
|
|
44265
|
+
fpRate: 0.0358,
|
|
44266
|
+
ratio: 0.13,
|
|
44267
|
+
precision: 0.0158,
|
|
44268
|
+
lastCalibratedAt: "2026-07-02T19:34:00Z",
|
|
43907
44269
|
verdict: "DORMANT",
|
|
43908
|
-
_calibrationNote: "v0.
|
|
44270
|
+
_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.",
|
|
43909
44271
|
aiSpecific: true,
|
|
43910
44272
|
_v7Verdict: "DORMANT",
|
|
43911
44273
|
_v7Lift: 1,
|
|
@@ -43914,16 +44276,21 @@ var signal_strength_default = {
|
|
|
43914
44276
|
_v7Precision: 0,
|
|
43915
44277
|
_v8Verdict: "DORMANT",
|
|
43916
44278
|
_v8Lift: 1,
|
|
44279
|
+
_v9Verdict: "DORMANT",
|
|
44280
|
+
_v9Lift: 0.44,
|
|
44281
|
+
_v9Recall: 46e-4,
|
|
44282
|
+
_v9FpRate: 0.0358,
|
|
44283
|
+
_v9Precision: 0.0158,
|
|
43917
44284
|
defaultOff: true
|
|
43918
44285
|
},
|
|
43919
44286
|
"java/system-out-println": {
|
|
43920
|
-
recall: 0,
|
|
43921
|
-
fpRate: 0,
|
|
43922
|
-
ratio:
|
|
43923
|
-
precision: 0,
|
|
43924
|
-
lastCalibratedAt: "2026-07-
|
|
44287
|
+
recall: 0.1503,
|
|
44288
|
+
fpRate: 0.2533,
|
|
44289
|
+
ratio: 0.59,
|
|
44290
|
+
precision: 0.0695,
|
|
44291
|
+
lastCalibratedAt: "2026-07-02T19:34:00Z",
|
|
43925
44292
|
verdict: "DORMANT",
|
|
43926
|
-
_calibrationNote: "v0.
|
|
44293
|
+
_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.",
|
|
43927
44294
|
aiSpecific: true,
|
|
43928
44295
|
_v7Verdict: "DORMANT",
|
|
43929
44296
|
_v7Lift: 1,
|
|
@@ -43932,6 +44299,11 @@ var signal_strength_default = {
|
|
|
43932
44299
|
_v7Precision: 0,
|
|
43933
44300
|
_v8Verdict: "DORMANT",
|
|
43934
44301
|
_v8Lift: 1,
|
|
44302
|
+
_v9Verdict: "DORMANT",
|
|
44303
|
+
_v9Lift: 0.27,
|
|
44304
|
+
_v9Recall: 0.1503,
|
|
44305
|
+
_v9FpRate: 0.2533,
|
|
44306
|
+
_v9Precision: 0.0695,
|
|
43935
44307
|
defaultOff: true
|
|
43936
44308
|
},
|
|
43937
44309
|
"layout/forced-layout": {
|