truecourse 0.7.0-next.4 → 0.7.0-next.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/README.md +15 -3
- package/cli.mjs +1672 -749
- package/package.json +1 -1
- package/public/assets/{index-rfhWLMD4.js → index-xXZq2ACy.js} +232 -227
- package/public/index.html +1 -1
- package/roslyn-host/Humanizer.dll +0 -0
- package/roslyn-host/Microsoft.Bcl.AsyncInterfaces.dll +0 -0
- package/roslyn-host/Microsoft.Build.Locator.dll +0 -0
- package/roslyn-host/Microsoft.CodeAnalysis.CSharp.Workspaces.dll +0 -0
- package/roslyn-host/Microsoft.CodeAnalysis.CSharp.dll +0 -0
- package/roslyn-host/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.dll +0 -0
- package/roslyn-host/Microsoft.CodeAnalysis.Workspaces.MSBuild.dll +0 -0
- package/roslyn-host/Microsoft.CodeAnalysis.Workspaces.dll +0 -0
- package/roslyn-host/Microsoft.CodeAnalysis.dll +0 -0
- package/roslyn-host/Microsoft.Extensions.DependencyInjection.Abstractions.dll +0 -0
- package/roslyn-host/Microsoft.Extensions.Logging.Abstractions.dll +0 -0
- package/roslyn-host/Newtonsoft.Json.dll +0 -0
- package/roslyn-host/System.Composition.AttributedModel.dll +0 -0
- package/roslyn-host/System.Composition.Convention.dll +0 -0
- package/roslyn-host/System.Composition.Hosting.dll +0 -0
- package/roslyn-host/System.Composition.Runtime.dll +0 -0
- package/roslyn-host/System.Composition.TypedParts.dll +0 -0
- package/roslyn-host/System.IO.Pipelines.dll +0 -0
- package/roslyn-host/cs/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/cs/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/cs/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/cs/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/cs/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/csharp-roslyn-host.deps.json +545 -0
- package/roslyn-host/csharp-roslyn-host.dll +0 -0
- package/roslyn-host/csharp-roslyn-host.dll.config +623 -0
- package/roslyn-host/csharp-roslyn-host.pdb +0 -0
- package/roslyn-host/csharp-roslyn-host.runtimeconfig.json +16 -0
- package/roslyn-host/de/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/de/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/de/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/de/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/de/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/es/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/es/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/es/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/es/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/es/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/fr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/fr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/fr/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/fr/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/fr/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/it/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/it/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/it/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/it/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/it/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/ja/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/ja/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/ja/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/ja/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/ja/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/ko/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/ko/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/ko/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/ko/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/ko/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/pl/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/pl/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/pl/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/pl/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/pl/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/pt-BR/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/pt-BR/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/pt-BR/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/pt-BR/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/ru/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/ru/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/ru/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/ru/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/ru/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/tr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/tr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/tr/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/tr/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/tr/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/zh-Hans/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/zh-Hans/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/zh-Hans/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/zh-Hans/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/roslyn-host/zh-Hant/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/roslyn-host/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/roslyn-host/zh-Hant/Microsoft.CodeAnalysis.Workspaces.MSBuild.BuildHost.resources.dll +0 -0
- package/roslyn-host/zh-Hant/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/roslyn-host/zh-Hant/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/server.mjs +1224 -364
package/server.mjs
CHANGED
|
@@ -28008,7 +28008,12 @@ var init_result = __esm({
|
|
|
28008
28008
|
durationMs: external_exports.number().nonnegative(),
|
|
28009
28009
|
/** Present on `fail` / `error`. */
|
|
28010
28010
|
failure: GuardFailureDetailSchema.optional(),
|
|
28011
|
-
/**
|
|
28011
|
+
/**
|
|
28012
|
+
* Repo-relative pointer into `evidence/`; present on every EXECUTED outcome —
|
|
28013
|
+
* `pass` (the run transcript proving what ran) as well as `fail` / `error`.
|
|
28014
|
+
* Absent on non-executed `stale` / `orphaned`, and on pre-2026-07-08 runs whose
|
|
28015
|
+
* passes were never given a bundle (optional keeps those parsing + rendering).
|
|
28016
|
+
*/
|
|
28012
28017
|
evidencePath: external_exports.string().optional(),
|
|
28013
28018
|
/**
|
|
28014
28019
|
* Present when the bound section moved: the section kept its text but now
|
|
@@ -28404,6 +28409,153 @@ var init_guard = __esm({
|
|
|
28404
28409
|
}
|
|
28405
28410
|
});
|
|
28406
28411
|
|
|
28412
|
+
// packages/shared/dist/spec/overlap-resolution.js
|
|
28413
|
+
function coveringRelationInAreas(relations, a, b, areas) {
|
|
28414
|
+
return relations.find((r) => {
|
|
28415
|
+
const samePair = r.older === a && r.newer === b || r.older === b && r.newer === a;
|
|
28416
|
+
return samePair && (r.scope === void 0 || areas.includes(r.scope));
|
|
28417
|
+
});
|
|
28418
|
+
}
|
|
28419
|
+
function dedupeCrossAreaOverlaps(entries) {
|
|
28420
|
+
const byPair = /* @__PURE__ */ new Map();
|
|
28421
|
+
for (const e of entries) {
|
|
28422
|
+
const key4 = unorderedPairKey(e.overlap.docs[0], e.overlap.docs[1]);
|
|
28423
|
+
const list = byPair.get(key4);
|
|
28424
|
+
if (list)
|
|
28425
|
+
list.push(e);
|
|
28426
|
+
else
|
|
28427
|
+
byPair.set(key4, [e]);
|
|
28428
|
+
}
|
|
28429
|
+
const merged = [];
|
|
28430
|
+
for (const members of byPair.values()) {
|
|
28431
|
+
const parent = members.map((_, i) => i);
|
|
28432
|
+
const find = (i) => {
|
|
28433
|
+
while (parent[i] !== i) {
|
|
28434
|
+
parent[i] = parent[parent[i]];
|
|
28435
|
+
i = parent[i];
|
|
28436
|
+
}
|
|
28437
|
+
return i;
|
|
28438
|
+
};
|
|
28439
|
+
const union = (i, j) => {
|
|
28440
|
+
const ri = find(i);
|
|
28441
|
+
const rj = find(j);
|
|
28442
|
+
if (ri !== rj)
|
|
28443
|
+
parent[Math.max(ri, rj)] = Math.min(ri, rj);
|
|
28444
|
+
};
|
|
28445
|
+
const ptrOwner = /* @__PURE__ */ new Map();
|
|
28446
|
+
members.forEach((m, i) => {
|
|
28447
|
+
for (const ptr of sectionPointerKeys(m.overlap)) {
|
|
28448
|
+
const owner = ptrOwner.get(ptr);
|
|
28449
|
+
if (owner === void 0)
|
|
28450
|
+
ptrOwner.set(ptr, i);
|
|
28451
|
+
else
|
|
28452
|
+
union(owner, i);
|
|
28453
|
+
}
|
|
28454
|
+
});
|
|
28455
|
+
const components = /* @__PURE__ */ new Map();
|
|
28456
|
+
members.forEach((_, i) => {
|
|
28457
|
+
const root = find(i);
|
|
28458
|
+
const list = components.get(root);
|
|
28459
|
+
if (list)
|
|
28460
|
+
list.push(i);
|
|
28461
|
+
else
|
|
28462
|
+
components.set(root, [i]);
|
|
28463
|
+
});
|
|
28464
|
+
for (const idxs of components.values()) {
|
|
28465
|
+
const group = idxs.map((i) => members[i]);
|
|
28466
|
+
const rep = [...group].sort((x, y) => {
|
|
28467
|
+
const px = preambleCount(x.overlap);
|
|
28468
|
+
const py = preambleCount(y.overlap);
|
|
28469
|
+
if (px !== py)
|
|
28470
|
+
return px - py;
|
|
28471
|
+
if (x.area !== y.area)
|
|
28472
|
+
return x.area < y.area ? -1 : 1;
|
|
28473
|
+
return (x.overlap.note ?? "") < (y.overlap.note ?? "") ? -1 : 1;
|
|
28474
|
+
})[0];
|
|
28475
|
+
const span = /* @__PURE__ */ new Set();
|
|
28476
|
+
for (const g of group) {
|
|
28477
|
+
span.add(g.area);
|
|
28478
|
+
for (const a of g.overlap.areas ?? [])
|
|
28479
|
+
span.add(a);
|
|
28480
|
+
}
|
|
28481
|
+
merged.push({ area: rep.area, areas: [...span].sort(), overlap: rep.overlap });
|
|
28482
|
+
}
|
|
28483
|
+
}
|
|
28484
|
+
merged.sort((x, y) => {
|
|
28485
|
+
if (x.area !== y.area)
|
|
28486
|
+
return x.area < y.area ? -1 : 1;
|
|
28487
|
+
return unorderedPairKey(x.overlap.docs[0], x.overlap.docs[1]) < unorderedPairKey(y.overlap.docs[0], y.overlap.docs[1]) ? -1 : 1;
|
|
28488
|
+
});
|
|
28489
|
+
return merged;
|
|
28490
|
+
}
|
|
28491
|
+
function buildCorpusConflicts(corpus, decisions) {
|
|
28492
|
+
const userRelations = decisions.relations ?? [];
|
|
28493
|
+
const effectiveRels = [...corpus.relations, ...userRelations];
|
|
28494
|
+
const excludes = new Set(decisions.manualExcludes ?? []);
|
|
28495
|
+
const entries = [];
|
|
28496
|
+
for (const area of corpus.areas)
|
|
28497
|
+
for (const ov of area.overlaps)
|
|
28498
|
+
entries.push({ area: area.id, overlap: ov });
|
|
28499
|
+
const mergedOverlaps = dedupeCrossAreaOverlaps(entries);
|
|
28500
|
+
const flagged = [];
|
|
28501
|
+
for (const m of mergedOverlaps) {
|
|
28502
|
+
const [a, b] = m.overlap.docs;
|
|
28503
|
+
const relation = coveringRelationInAreas(effectiveRels, a, b, m.areas);
|
|
28504
|
+
const userRelation = coveringRelationInAreas(userRelations, a, b, m.areas);
|
|
28505
|
+
const excludedRef = excludes.has(a) ? a : excludes.has(b) ? b : void 0;
|
|
28506
|
+
flagged.push({
|
|
28507
|
+
area: m.area,
|
|
28508
|
+
areas: m.areas,
|
|
28509
|
+
a,
|
|
28510
|
+
b,
|
|
28511
|
+
note: m.overlap.note ?? "",
|
|
28512
|
+
resolved: relation !== void 0 || excludedRef !== void 0,
|
|
28513
|
+
synthesized: false,
|
|
28514
|
+
...relation ? { relation } : {},
|
|
28515
|
+
...userRelation ? { userRelation } : {},
|
|
28516
|
+
...excludedRef ? { excludedRef } : {}
|
|
28517
|
+
});
|
|
28518
|
+
}
|
|
28519
|
+
const coversFlagged = (r) => flagged.some((o) => {
|
|
28520
|
+
const samePair = o.a === r.older && o.b === r.newer || o.a === r.newer && o.b === r.older;
|
|
28521
|
+
return samePair && (r.scope === void 0 || o.areas.includes(r.scope));
|
|
28522
|
+
});
|
|
28523
|
+
const synthesized = userRelations.filter((r) => !coversFlagged(r)).map((r) => ({
|
|
28524
|
+
area: r.scope ?? "",
|
|
28525
|
+
areas: r.scope ? [r.scope] : [],
|
|
28526
|
+
a: r.older,
|
|
28527
|
+
b: r.newer,
|
|
28528
|
+
note: r.note ?? "",
|
|
28529
|
+
resolved: true,
|
|
28530
|
+
synthesized: true,
|
|
28531
|
+
relation: r,
|
|
28532
|
+
userRelation: r
|
|
28533
|
+
}));
|
|
28534
|
+
return [...flagged, ...synthesized];
|
|
28535
|
+
}
|
|
28536
|
+
function openConflicts(corpus, decisions) {
|
|
28537
|
+
return buildCorpusConflicts(corpus, decisions).filter((c) => !c.resolved);
|
|
28538
|
+
}
|
|
28539
|
+
var PREAMBLE_PTR, NUL, unorderedPairKey, sectionPointerKeys, preambleCount;
|
|
28540
|
+
var init_overlap_resolution = __esm({
|
|
28541
|
+
"packages/shared/dist/spec/overlap-resolution.js"() {
|
|
28542
|
+
"use strict";
|
|
28543
|
+
PREAMBLE_PTR = "\0preamble";
|
|
28544
|
+
NUL = "\0";
|
|
28545
|
+
unorderedPairKey = (a, b) => a < b ? `${a}${NUL}${b}` : `${b}${NUL}${a}`;
|
|
28546
|
+
sectionPointerKeys = (ov) => (ov.sections ?? []).map((s) => `${s.doc}${NUL}${s.heading ?? PREAMBLE_PTR}`);
|
|
28547
|
+
preambleCount = (ov) => (ov.sections ?? []).filter((s) => s.heading === null || s.heading === void 0).length;
|
|
28548
|
+
}
|
|
28549
|
+
});
|
|
28550
|
+
|
|
28551
|
+
// packages/shared/dist/spec/index.js
|
|
28552
|
+
var init_spec = __esm({
|
|
28553
|
+
"packages/shared/dist/spec/index.js"() {
|
|
28554
|
+
"use strict";
|
|
28555
|
+
init_overlap_resolution();
|
|
28556
|
+
}
|
|
28557
|
+
});
|
|
28558
|
+
|
|
28407
28559
|
// node_modules/.pnpm/ignore@7.0.5/node_modules/ignore/index.js
|
|
28408
28560
|
var require_ignore = __commonJS({
|
|
28409
28561
|
"node_modules/.pnpm/ignore@7.0.5/node_modules/ignore/index.js"(exports, module) {
|
|
@@ -28994,6 +29146,7 @@ var init_dist = __esm({
|
|
|
28994
29146
|
init_types2();
|
|
28995
29147
|
init_schemas();
|
|
28996
29148
|
init_guard();
|
|
29149
|
+
init_spec();
|
|
28997
29150
|
init_tcignore();
|
|
28998
29151
|
init_skip_dirs();
|
|
28999
29152
|
init_spec_scope();
|
|
@@ -44192,8 +44345,8 @@ function getMaxParameters(filePath) {
|
|
|
44192
44345
|
}
|
|
44193
44346
|
return 5;
|
|
44194
44347
|
}
|
|
44195
|
-
function isBootstrapEntry(
|
|
44196
|
-
return LANGUAGE_CONFIGS.some((c) => c.bootstrap.functionNames.includes(
|
|
44348
|
+
function isBootstrapEntry(functionName2, filePath) {
|
|
44349
|
+
return LANGUAGE_CONFIGS.some((c) => c.bootstrap.functionNames.includes(functionName2) && c.bootstrap.filePattern.test(filePath));
|
|
44197
44350
|
}
|
|
44198
44351
|
function getLanguageConfig(language) {
|
|
44199
44352
|
const config2 = LANGUAGE_CONFIGS.find((c) => c.name === language);
|
|
@@ -48928,9 +49081,6 @@ function buildCSharpSymbolIndex(files, rootPath) {
|
|
|
48928
49081
|
}
|
|
48929
49082
|
function contributeCSharpEdges(files, rootPath) {
|
|
48930
49083
|
const index = buildCSharpSymbolIndex(files, rootPath);
|
|
48931
|
-
if (index.stats.ambiguousRefs > 0) {
|
|
48932
|
-
console.warn(`[TrueCourse] C# symbol index: ${index.stats.ambiguousRefs} ambiguous type reference(s) skipped (same type name visible from multiple namespaces)`);
|
|
48933
|
-
}
|
|
48934
49084
|
return index.edges;
|
|
48935
49085
|
}
|
|
48936
49086
|
var TYPE_DECL_KINDS;
|
|
@@ -54259,12 +54409,9 @@ var init_lsp_client = __esm({
|
|
|
54259
54409
|
this.process.stdout.on("data", (chunk3) => this.onData(chunk3));
|
|
54260
54410
|
this.process.stderr.on("data", (chunk3) => {
|
|
54261
54411
|
});
|
|
54262
|
-
this.process.on("error", (err) =>
|
|
54263
|
-
|
|
54264
|
-
|
|
54265
|
-
}
|
|
54266
|
-
this.pendingRequests.clear();
|
|
54267
|
-
});
|
|
54412
|
+
this.process.on("error", (err) => this.rejectAllPending(err));
|
|
54413
|
+
this.process.stdin.on("error", (err) => this.rejectAllPending(new Error(`${this.config.name} LSP stdin error: ${err.message}`)));
|
|
54414
|
+
this.process.on("exit", (code, signal) => this.rejectAllPending(new Error(`${this.config.name} LSP server exited (${signal ?? `code ${code}`}) before responding`)));
|
|
54268
54415
|
const initResult = await this.sendRequest("initialize", {
|
|
54269
54416
|
processId: process.pid,
|
|
54270
54417
|
capabilities: {
|
|
@@ -54443,17 +54590,33 @@ var init_lsp_client = __esm({
|
|
|
54443
54590
|
// -------------------------------------------------------------------------
|
|
54444
54591
|
// Internal: JSON-RPC transport
|
|
54445
54592
|
// -------------------------------------------------------------------------
|
|
54593
|
+
/** Reject and clear every in-flight request — used when the server dies. */
|
|
54594
|
+
rejectAllPending(err) {
|
|
54595
|
+
if (this.pendingRequests.size === 0)
|
|
54596
|
+
return;
|
|
54597
|
+
for (const pending of this.pendingRequests.values())
|
|
54598
|
+
pending.reject(err);
|
|
54599
|
+
this.pendingRequests.clear();
|
|
54600
|
+
}
|
|
54446
54601
|
sendRequest(method, params) {
|
|
54447
54602
|
return new Promise((resolve11, reject) => {
|
|
54448
54603
|
const id = ++this.requestId;
|
|
54449
54604
|
this.pendingRequests.set(id, { resolve: resolve11, reject });
|
|
54450
54605
|
const msg = { jsonrpc: "2.0", id, method, params };
|
|
54451
|
-
|
|
54606
|
+
try {
|
|
54607
|
+
this.process.stdin.write(encodeMessage(msg));
|
|
54608
|
+
} catch (err) {
|
|
54609
|
+
this.pendingRequests.delete(id);
|
|
54610
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
54611
|
+
}
|
|
54452
54612
|
});
|
|
54453
54613
|
}
|
|
54454
54614
|
sendNotification(method, params) {
|
|
54455
54615
|
const msg = { jsonrpc: "2.0", method, params };
|
|
54456
|
-
|
|
54616
|
+
try {
|
|
54617
|
+
this.process.stdin.write(encodeMessage(msg));
|
|
54618
|
+
} catch {
|
|
54619
|
+
}
|
|
54457
54620
|
}
|
|
54458
54621
|
onData(chunk3) {
|
|
54459
54622
|
this.buffer = Buffer.concat([this.buffer, chunk3]);
|
|
@@ -64938,7 +65101,8 @@ var init_deterministic4 = __esm({
|
|
|
64938
65101
|
description: "Function parameter is never referenced in the function body \u2014 remove it or prefix with `_`.",
|
|
64939
65102
|
enabled: true,
|
|
64940
65103
|
severity: "low",
|
|
64941
|
-
type: "deterministic"
|
|
65104
|
+
type: "deterministic",
|
|
65105
|
+
engine: "roslyn-host"
|
|
64942
65106
|
},
|
|
64943
65107
|
{
|
|
64944
65108
|
key: "code-quality/deterministic/hardcoded-url",
|
|
@@ -76222,6 +76386,94 @@ var init_production_debug_enabled = __esm({
|
|
|
76222
76386
|
});
|
|
76223
76387
|
|
|
76224
76388
|
// packages/analyzer/dist/rules/security/visitors/javascript/insecure-random.js
|
|
76389
|
+
function isSecuritySensitiveName(name) {
|
|
76390
|
+
const lower = name.toLowerCase();
|
|
76391
|
+
return SECURITY_KEYWORDS.some((kw) => lower.includes(kw));
|
|
76392
|
+
}
|
|
76393
|
+
function securityContextName(callNode) {
|
|
76394
|
+
let parent = callNode.parent;
|
|
76395
|
+
while (parent) {
|
|
76396
|
+
switch (parent.type) {
|
|
76397
|
+
case "variable_declarator":
|
|
76398
|
+
return leafName(parent.childForFieldName("name"));
|
|
76399
|
+
case "assignment_expression":
|
|
76400
|
+
case "augmented_assignment_expression":
|
|
76401
|
+
return leafName(parent.childForFieldName("left"));
|
|
76402
|
+
case "pair":
|
|
76403
|
+
return leafName(parent.childForFieldName("key"));
|
|
76404
|
+
case "public_field_definition":
|
|
76405
|
+
case "field_definition":
|
|
76406
|
+
return leafName(parent.childForFieldName("property") ?? parent.childForFieldName("name"));
|
|
76407
|
+
// The value is returned from the enclosing function (e.g.
|
|
76408
|
+
// `function generateToken() { return Math.random()... }`) — the
|
|
76409
|
+
// function name is the security context.
|
|
76410
|
+
case "return_statement":
|
|
76411
|
+
return enclosingFunctionName(parent);
|
|
76412
|
+
// Expression-bodied arrow returns the value directly, e.g.
|
|
76413
|
+
// `const genToken = () => Math.random()...`.
|
|
76414
|
+
case "arrow_function":
|
|
76415
|
+
return functionName(parent);
|
|
76416
|
+
// Structural boundaries: the random value is discarded or otherwise
|
|
76417
|
+
// not bound to a name.
|
|
76418
|
+
case "expression_statement":
|
|
76419
|
+
case "statement_block":
|
|
76420
|
+
case "if_statement":
|
|
76421
|
+
case "for_statement":
|
|
76422
|
+
case "for_in_statement":
|
|
76423
|
+
case "while_statement":
|
|
76424
|
+
case "do_statement":
|
|
76425
|
+
case "switch_statement":
|
|
76426
|
+
case "function_declaration":
|
|
76427
|
+
case "function_expression":
|
|
76428
|
+
case "method_definition":
|
|
76429
|
+
return null;
|
|
76430
|
+
}
|
|
76431
|
+
parent = parent.parent;
|
|
76432
|
+
}
|
|
76433
|
+
return null;
|
|
76434
|
+
}
|
|
76435
|
+
function enclosingFunctionName(node2) {
|
|
76436
|
+
let p = node2.parent;
|
|
76437
|
+
while (p) {
|
|
76438
|
+
if (p.type === "function_declaration" || p.type === "generator_function_declaration" || p.type === "method_definition" || p.type === "arrow_function" || p.type === "function_expression") {
|
|
76439
|
+
return functionName(p);
|
|
76440
|
+
}
|
|
76441
|
+
p = p.parent;
|
|
76442
|
+
}
|
|
76443
|
+
return null;
|
|
76444
|
+
}
|
|
76445
|
+
function functionName(fn) {
|
|
76446
|
+
const nameField = fn.childForFieldName("name");
|
|
76447
|
+
if (nameField)
|
|
76448
|
+
return leafName(nameField);
|
|
76449
|
+
const p = fn.parent;
|
|
76450
|
+
if (!p)
|
|
76451
|
+
return null;
|
|
76452
|
+
if (p.type === "variable_declarator")
|
|
76453
|
+
return leafName(p.childForFieldName("name"));
|
|
76454
|
+
if (p.type === "pair")
|
|
76455
|
+
return leafName(p.childForFieldName("key"));
|
|
76456
|
+
if (p.type === "assignment_expression")
|
|
76457
|
+
return leafName(p.childForFieldName("left"));
|
|
76458
|
+
if (p.type === "public_field_definition" || p.type === "field_definition") {
|
|
76459
|
+
return leafName(p.childForFieldName("property") ?? p.childForFieldName("name"));
|
|
76460
|
+
}
|
|
76461
|
+
return null;
|
|
76462
|
+
}
|
|
76463
|
+
function leafName(node2) {
|
|
76464
|
+
if (!node2)
|
|
76465
|
+
return null;
|
|
76466
|
+
if (node2.type === "identifier" || node2.type === "property_identifier" || node2.type === "shorthand_property_identifier") {
|
|
76467
|
+
return node2.text;
|
|
76468
|
+
}
|
|
76469
|
+
if (node2.type === "member_expression") {
|
|
76470
|
+
return node2.childForFieldName("property")?.text ?? null;
|
|
76471
|
+
}
|
|
76472
|
+
if (node2.type === "string") {
|
|
76473
|
+
return node2.text.slice(1, -1);
|
|
76474
|
+
}
|
|
76475
|
+
return node2.text;
|
|
76476
|
+
}
|
|
76225
76477
|
function isFixtureOrTestFile(filePath) {
|
|
76226
76478
|
const lower = filePath.toLowerCase();
|
|
76227
76479
|
return /[\\/]seeds?[\\/]/.test(lower) || /[\\/](?:seed|seeds)\.(?:ts|tsx|js|jsx|mts|cts|mjs|cjs)$/.test(lower) || /\.seed\.(?:ts|tsx|js|jsx|mts|cts|mjs|cjs)$/.test(lower) || /[\\/]__tests__[\\/]/.test(lower) || /\.(?:test|spec)\.(?:ts|tsx|js|jsx|mts|cts|mjs|cjs)$/.test(lower);
|
|
@@ -76252,19 +76504,9 @@ var init_insecure_random = __esm({
|
|
|
76252
76504
|
if (/\.length\b/.test(parentText))
|
|
76253
76505
|
return null;
|
|
76254
76506
|
}
|
|
76255
|
-
|
|
76256
|
-
|
|
76257
|
-
|
|
76258
|
-
if (SECURITY_KEYWORDS.some((kw) => parentText.includes(kw))) {
|
|
76259
|
-
const immediateParent = node2.parent;
|
|
76260
|
-
if (immediateParent?.type === "binary_expression" && /\.length\b/.test(immediateParent.text)) {
|
|
76261
|
-
return null;
|
|
76262
|
-
}
|
|
76263
|
-
return makeViolation(this.ruleKey, node2, filePath, "high", "Insecure random number generator", "Math.random() is not cryptographically secure. Do not use it for tokens, keys, or secrets.", sourceCode, "Use crypto.randomBytes() or crypto.randomUUID() instead.");
|
|
76264
|
-
}
|
|
76265
|
-
if (parent.type === "expression_statement" || parent.type === "variable_declaration" || parent.type === "assignment_expression" || parent.type === "lexical_declaration")
|
|
76266
|
-
break;
|
|
76267
|
-
parent = parent.parent;
|
|
76507
|
+
const contextName = securityContextName(node2);
|
|
76508
|
+
if (contextName && isSecuritySensitiveName(contextName)) {
|
|
76509
|
+
return makeViolation(this.ruleKey, node2, filePath, "high", "Insecure random number generator", "Math.random() is not cryptographically secure. Do not use it for tokens, keys, or secrets.", sourceCode, "Use crypto.randomBytes() or crypto.randomUUID() instead.");
|
|
76268
76510
|
}
|
|
76269
76511
|
}
|
|
76270
76512
|
}
|
|
@@ -76693,6 +76935,11 @@ var init_unverified_cross_origin_message = __esm({
|
|
|
76693
76935
|
}
|
|
76694
76936
|
if (methodName3 !== "addEventListener")
|
|
76695
76937
|
return null;
|
|
76938
|
+
if (fn.type === "member_expression") {
|
|
76939
|
+
const receiver = fn.childForFieldName("object")?.text ?? "";
|
|
76940
|
+
if (receiver !== "window" && receiver !== "self" && receiver !== "globalThis")
|
|
76941
|
+
return null;
|
|
76942
|
+
}
|
|
76696
76943
|
const args = node2.childForFieldName("arguments");
|
|
76697
76944
|
if (!args)
|
|
76698
76945
|
return null;
|
|
@@ -77175,7 +77422,11 @@ var init_timing_attack_comparison = __esm({
|
|
|
77175
77422
|
return null;
|
|
77176
77423
|
const leftLeaf = leafIdentifier(left);
|
|
77177
77424
|
const rightLeaf = leafIdentifier(right);
|
|
77178
|
-
|
|
77425
|
+
const leftIsLiteral = left.type === "string" || left.type === "template_string";
|
|
77426
|
+
const rightIsLiteral = right.type === "string" || right.type === "template_string";
|
|
77427
|
+
const leftMatches = !leftIsLiteral && SENSITIVE_COMPARISON_PATTERNS.test(leftLeaf);
|
|
77428
|
+
const rightMatches = !rightIsLiteral && SENSITIVE_COMPARISON_PATTERNS.test(rightLeaf);
|
|
77429
|
+
if (leftMatches || rightMatches) {
|
|
77179
77430
|
if (sourceCode.includes("timingSafeEqual"))
|
|
77180
77431
|
return null;
|
|
77181
77432
|
return makeViolation(this.ruleKey, node2, filePath, "medium", "Timing attack via string comparison", `Using ${operator.text} to compare what may be a secret/token. This is vulnerable to timing attacks.`, sourceCode, "Use crypto.timingSafeEqual() for comparing secrets and tokens.");
|
|
@@ -77762,6 +78013,14 @@ var init_mixed_http_methods = __esm({
|
|
|
77762
78013
|
});
|
|
77763
78014
|
|
|
77764
78015
|
// packages/analyzer/dist/rules/security/visitors/javascript/process-signaling.js
|
|
78016
|
+
function isOwnedProcessPid(node2) {
|
|
78017
|
+
if (node2.type !== "member_expression")
|
|
78018
|
+
return false;
|
|
78019
|
+
if (node2.childForFieldName("property")?.text !== "pid")
|
|
78020
|
+
return false;
|
|
78021
|
+
const obj = node2.childForFieldName("object");
|
|
78022
|
+
return obj?.type === "member_expression" && obj.childForFieldName("property")?.text === "process";
|
|
78023
|
+
}
|
|
77765
78024
|
var processSignalingVisitor;
|
|
77766
78025
|
var init_process_signaling = __esm({
|
|
77767
78026
|
"packages/analyzer/dist/rules/security/visitors/javascript/process-signaling.js"() {
|
|
@@ -77789,6 +78048,8 @@ var init_process_signaling = __esm({
|
|
|
77789
78048
|
const pidArg = args.namedChildren[0];
|
|
77790
78049
|
if (!pidArg)
|
|
77791
78050
|
return null;
|
|
78051
|
+
if (isOwnedProcessPid(pidArg))
|
|
78052
|
+
return null;
|
|
77792
78053
|
if (pidArg.type !== "number") {
|
|
77793
78054
|
return makeViolation(this.ruleKey, node2, filePath, "high", "Sending signals to arbitrary processes", "process.kill() called with a non-literal PID. If the PID is user-controlled, this enables process manipulation.", sourceCode, "Validate and sanitize the PID before passing it to process.kill().");
|
|
77794
78055
|
}
|
|
@@ -79142,8 +79403,8 @@ var init_sql_injection2 = __esm({
|
|
|
79142
79403
|
if (!firstArg)
|
|
79143
79404
|
return null;
|
|
79144
79405
|
if (firstArg.type === "string" && firstArg.text.startsWith("f")) {
|
|
79145
|
-
const
|
|
79146
|
-
if (
|
|
79406
|
+
const hasInterpolation = firstArg.namedChildren.some((c) => c.type === "interpolation");
|
|
79407
|
+
if (hasInterpolation) {
|
|
79147
79408
|
const queryText = firstArg.text;
|
|
79148
79409
|
if (/:[a-z_]\w*|%s|%\(\w+\)s/.test(queryText))
|
|
79149
79410
|
return null;
|
|
@@ -79653,17 +79914,17 @@ var init_eval_usage2 = __esm({
|
|
|
79653
79914
|
funcName = attr.text;
|
|
79654
79915
|
const receiver = fn.childForFieldName("object");
|
|
79655
79916
|
if (receiver) {
|
|
79656
|
-
let
|
|
79917
|
+
let leafName3 = null;
|
|
79657
79918
|
if (receiver.type === "identifier") {
|
|
79658
|
-
|
|
79919
|
+
leafName3 = receiver.text;
|
|
79659
79920
|
} else if (receiver.type === "attribute") {
|
|
79660
79921
|
const attrChild = receiver.childForFieldName("attribute");
|
|
79661
79922
|
if (attrChild) {
|
|
79662
|
-
|
|
79923
|
+
leafName3 = attrChild.text.replace(/^_+/, "");
|
|
79663
79924
|
}
|
|
79664
79925
|
}
|
|
79665
|
-
if (
|
|
79666
|
-
const expectedSources = SAFE_RECEIVER_IMPORT_SOURCES[
|
|
79926
|
+
if (leafName3 && SAFE_METHOD_RECEIVERS.has(leafName3)) {
|
|
79927
|
+
const expectedSources = SAFE_RECEIVER_IMPORT_SOURCES[leafName3];
|
|
79667
79928
|
if (expectedSources) {
|
|
79668
79929
|
const sources = getPythonImportSources(node2);
|
|
79669
79930
|
for (const src of sources) {
|
|
@@ -83635,6 +83896,12 @@ var init_exclusions = __esm({
|
|
|
83635
83896
|
/^https?:\/\/(?!hooks\.slack\.com|[a-z0-9]+\.webhook\.office\.com)/,
|
|
83636
83897
|
// Docker / container image references
|
|
83637
83898
|
/^(?:docker|ghcr|quay|gcr|ecr)\.io\//,
|
|
83899
|
+
// PostHog public project API key (phc_…) — intentionally client-exposed,
|
|
83900
|
+
// embedded in frontend code to send analytics events. Its 40+ char base62
|
|
83901
|
+
// body collides with the generic Cohere-token shape, but it is not a
|
|
83902
|
+
// private credential. Private PostHog keys use the phx_/phs_ prefixes and
|
|
83903
|
+
// are deliberately NOT allowlisted here.
|
|
83904
|
+
/^phc_[A-Za-z0-9]{30,}$/,
|
|
83638
83905
|
// Common config patterns that aren't secrets
|
|
83639
83906
|
/^\*+$/,
|
|
83640
83907
|
// All asterisks (masked values)
|
|
@@ -85450,10 +85717,33 @@ var init_secret_scanner = __esm({
|
|
|
85450
85717
|
});
|
|
85451
85718
|
|
|
85452
85719
|
// packages/analyzer/dist/rules/security/visitors/universal.js
|
|
85720
|
+
function hasPasswordToken(text4) {
|
|
85721
|
+
const lower = text4.toLowerCase();
|
|
85722
|
+
return lower.includes("password") || lower.includes("passwd");
|
|
85723
|
+
}
|
|
85724
|
+
function isPasswordHashingContext(node2) {
|
|
85725
|
+
let stmt = node2.parent;
|
|
85726
|
+
while (stmt && !STATEMENT_NODE_TYPES2.has(stmt.type) && !FUNCTION_NODE_TYPES3.has(stmt.type)) {
|
|
85727
|
+
stmt = stmt.parent;
|
|
85728
|
+
}
|
|
85729
|
+
if (stmt && STATEMENT_NODE_TYPES2.has(stmt.type) && hasPasswordToken(stmt.text))
|
|
85730
|
+
return true;
|
|
85731
|
+
let fnNode = node2.parent;
|
|
85732
|
+
while (fnNode && !FUNCTION_NODE_TYPES3.has(fnNode.type))
|
|
85733
|
+
fnNode = fnNode.parent;
|
|
85734
|
+
if (fnNode) {
|
|
85735
|
+
const nameNode = fnNode.childForFieldName("name");
|
|
85736
|
+
const paramsNode = fnNode.childForFieldName("parameters") ?? fnNode.childForFieldName("parameter_list");
|
|
85737
|
+
const signature = `${nameNode?.text ?? ""} ${paramsNode?.text ?? ""}`;
|
|
85738
|
+
if (hasPasswordToken(signature))
|
|
85739
|
+
return true;
|
|
85740
|
+
}
|
|
85741
|
+
return false;
|
|
85742
|
+
}
|
|
85453
85743
|
function stripCommentMarkers(text4) {
|
|
85454
85744
|
return text4.replace(/^\/{2,3}\s?/, "").replace(/^\/\*\s?/, "").replace(/\s?\*\/$/, "").replace(/^\s*\*\s?/gm, "").replace(/^#\s?/gm, "").trim();
|
|
85455
85745
|
}
|
|
85456
|
-
var STRING_NODE_TYPES, hardcodedSecretVisitor, IPV4_REGEX, EXCLUDED_IPS, hardcodedIpVisitor, CLEARTEXT_PROTOCOLS, LOCALHOST_PREFIXES, clearTextProtocolVisitor, BIP39_COMMON_WORDS, hardcodedBlockchainMnemonicVisitor, DB_URL_PATTERN, DB_PASSWORD_KV_PATTERNS, NON_LITERAL_PASSWORD_SEGMENT, PLACEHOLDER_PASSWORDS, hardcodedDatabasePasswordVisitor, ldapUnauthenticatedVisitor, passwordStoredPlaintextVisitor, HASH_FUNCTIONS_NEEDING_SALT, unpredictableSaltMissingVisitor, sensitiveFileVisitor, hardcodedSecretInCommentVisitor, SECURITY_UNIVERSAL_VISITORS;
|
|
85746
|
+
var STRING_NODE_TYPES, hardcodedSecretVisitor, IPV4_REGEX, EXCLUDED_IPS, hardcodedIpVisitor, CLEARTEXT_PROTOCOLS, LOCALHOST_PREFIXES, clearTextProtocolVisitor, BIP39_COMMON_WORDS, hardcodedBlockchainMnemonicVisitor, DB_URL_PATTERN, DB_PASSWORD_KV_PATTERNS, NON_LITERAL_PASSWORD_SEGMENT, PLACEHOLDER_PASSWORDS, hardcodedDatabasePasswordVisitor, ldapUnauthenticatedVisitor, passwordStoredPlaintextVisitor, HASH_FUNCTIONS_NEEDING_SALT, STATEMENT_NODE_TYPES2, FUNCTION_NODE_TYPES3, unpredictableSaltMissingVisitor, sensitiveFileVisitor, hardcodedSecretInCommentVisitor, SECURITY_UNIVERSAL_VISITORS;
|
|
85457
85747
|
var init_universal = __esm({
|
|
85458
85748
|
"packages/analyzer/dist/rules/security/visitors/universal.js"() {
|
|
85459
85749
|
"use strict";
|
|
@@ -86633,6 +86923,29 @@ var init_universal = __esm({
|
|
|
86633
86923
|
"sha256",
|
|
86634
86924
|
"sha512"
|
|
86635
86925
|
]);
|
|
86926
|
+
STATEMENT_NODE_TYPES2 = /* @__PURE__ */ new Set([
|
|
86927
|
+
"expression_statement",
|
|
86928
|
+
"return_statement",
|
|
86929
|
+
"lexical_declaration",
|
|
86930
|
+
"variable_declaration",
|
|
86931
|
+
"variable_declarator",
|
|
86932
|
+
"local_declaration_statement",
|
|
86933
|
+
"assignment",
|
|
86934
|
+
"augmented_assignment",
|
|
86935
|
+
"assignment_expression",
|
|
86936
|
+
"augmented_assignment_expression"
|
|
86937
|
+
]);
|
|
86938
|
+
FUNCTION_NODE_TYPES3 = /* @__PURE__ */ new Set([
|
|
86939
|
+
"function_declaration",
|
|
86940
|
+
"function_expression",
|
|
86941
|
+
"arrow_function",
|
|
86942
|
+
"generator_function_declaration",
|
|
86943
|
+
"method_definition",
|
|
86944
|
+
"function_definition",
|
|
86945
|
+
"method_declaration",
|
|
86946
|
+
"local_function_statement",
|
|
86947
|
+
"constructor_declaration"
|
|
86948
|
+
]);
|
|
86636
86949
|
unpredictableSaltMissingVisitor = {
|
|
86637
86950
|
ruleKey: "security/deterministic/unpredictable-salt-missing",
|
|
86638
86951
|
nodeTypes: ["call_expression", "call", "invocation_expression"],
|
|
@@ -86650,21 +86963,12 @@ var init_universal = __esm({
|
|
|
86650
86963
|
}
|
|
86651
86964
|
if (!HASH_FUNCTIONS_NEEDING_SALT.has(methodName3))
|
|
86652
86965
|
return null;
|
|
86653
|
-
|
|
86654
|
-
|
|
86655
|
-
|
|
86656
|
-
|
|
86657
|
-
|
|
86658
|
-
|
|
86659
|
-
return makeViolation(this.ruleKey, node2, filePath, "high", "Missing salt in hash", `${methodName3}() called without a salt in a password context. This allows rainbow table attacks.`, sourceCode, "Use a password hashing function like bcrypt, argon2, or PBKDF2 which handle salting automatically.");
|
|
86660
|
-
}
|
|
86661
|
-
return null;
|
|
86662
|
-
}
|
|
86663
|
-
if (parent.type === "function_declaration" || parent.type === "function_definition" || parent.type === "method_definition" || parent.type === "program" || parent.type === "method_declaration" || parent.type === "compilation_unit")
|
|
86664
|
-
break;
|
|
86665
|
-
parent = parent.parent;
|
|
86666
|
-
}
|
|
86667
|
-
return null;
|
|
86966
|
+
const args = node2.childForFieldName("arguments");
|
|
86967
|
+
if (!args || args.namedChildren.length >= 2)
|
|
86968
|
+
return null;
|
|
86969
|
+
if (!isPasswordHashingContext(node2))
|
|
86970
|
+
return null;
|
|
86971
|
+
return makeViolation(this.ruleKey, node2, filePath, "high", "Missing salt in hash", `${methodName3}() called without a salt in a password context. This allows rainbow table attacks.`, sourceCode, "Use a password hashing function like bcrypt, argon2, or PBKDF2 which handle salting automatically.");
|
|
86668
86972
|
}
|
|
86669
86973
|
};
|
|
86670
86974
|
sensitiveFileVisitor = {
|
|
@@ -86757,7 +87061,7 @@ function isLiteralNode(n) {
|
|
|
86757
87061
|
}
|
|
86758
87062
|
return false;
|
|
86759
87063
|
}
|
|
86760
|
-
var JS_LANGUAGES, TS_LANGUAGES, COMPARISON_OPERATORS, CONSTANT_LITERALS, TERMINAL_TYPES, VALID_TYPEOF_VALUES, LITERAL_TYPES, ARRAY_METHODS_REQUIRING_RETURN, PRIMITIVE_TYPES, PURE_ARRAY_METHODS, KNOWN_ARG_ORDERS, VOID_RETURNING_METHODS, VOID_RETURNING_GLOBALS, NON_CONSTRUCTORS, PROTOTYPE_BUILTINS, NON_CALLABLE_GLOBALS, READ_ONLY_GLOBALS, RESTRICTED_NAMES, THROWABLE_TYPES;
|
|
87064
|
+
var JS_LANGUAGES, TS_LANGUAGES, COMPARISON_OPERATORS, CONSTANT_LITERALS, TERMINAL_TYPES, VALID_TYPEOF_VALUES, LITERAL_TYPES, ARRAY_METHODS_REQUIRING_RETURN, PRIMITIVE_TYPES, PURE_ARRAY_METHODS, SOCKET_IO_RECEIVERS, KNOWN_ARG_ORDERS, VOID_RETURNING_METHODS, VOID_RETURNING_GLOBALS, NON_CONSTRUCTORS, PROTOTYPE_BUILTINS, NON_CALLABLE_GLOBALS, READ_ONLY_GLOBALS, RESTRICTED_NAMES, THROWABLE_TYPES;
|
|
86761
87065
|
var init_helpers = __esm({
|
|
86762
87066
|
"packages/analyzer/dist/rules/bugs/visitors/javascript/_helpers.js"() {
|
|
86763
87067
|
"use strict";
|
|
@@ -86815,6 +87119,7 @@ var init_helpers = __esm({
|
|
|
86815
87119
|
"reduce",
|
|
86816
87120
|
"reduceRight"
|
|
86817
87121
|
]);
|
|
87122
|
+
SOCKET_IO_RECEIVERS = /* @__PURE__ */ new Set(["socket", "sockets"]);
|
|
86818
87123
|
KNOWN_ARG_ORDERS = [
|
|
86819
87124
|
{ fn: "startsWith", params: [["prefix", "str", "string", "start", "needle", "search"]] },
|
|
86820
87125
|
{ fn: "endsWith", params: [["suffix", "str", "string", "end", "needle", "search"]] },
|
|
@@ -87451,6 +87756,15 @@ var init_loss_of_precision = __esm({
|
|
|
87451
87756
|
const num = Number(text4);
|
|
87452
87757
|
if (!Number.isFinite(num))
|
|
87453
87758
|
return null;
|
|
87759
|
+
const parent = node2.parent;
|
|
87760
|
+
if (parent?.type === "arguments") {
|
|
87761
|
+
const call = parent.parent;
|
|
87762
|
+
if (call?.type === "call_expression") {
|
|
87763
|
+
const fn = call.childForFieldName("function");
|
|
87764
|
+
if (fn?.type === "identifier" && fn.text === "BigInt")
|
|
87765
|
+
return null;
|
|
87766
|
+
}
|
|
87767
|
+
}
|
|
87454
87768
|
if (!Number.isSafeInteger(num)) {
|
|
87455
87769
|
return makeViolation(this.ruleKey, node2, filePath, "high", "Loss of precision", `\`${text4}\` exceeds Number.MAX_SAFE_INTEGER and will lose precision at runtime.`, sourceCode, `Use BigInt (\`${text4}n\`) or restructure to avoid large integer literals.`);
|
|
87456
87770
|
}
|
|
@@ -89722,6 +90036,17 @@ var init_useless_increment = __esm({
|
|
|
89722
90036
|
});
|
|
89723
90037
|
|
|
89724
90038
|
// packages/analyzer/dist/rules/bugs/visitors/javascript/ignored-return-value.js
|
|
90039
|
+
function receiverBaseName(obj) {
|
|
90040
|
+
if (!obj)
|
|
90041
|
+
return null;
|
|
90042
|
+
if (obj.type === "identifier")
|
|
90043
|
+
return obj.text.toLowerCase();
|
|
90044
|
+
if (obj.type === "member_expression") {
|
|
90045
|
+
const prop = obj.childForFieldName("property");
|
|
90046
|
+
return prop ? prop.text.toLowerCase() : null;
|
|
90047
|
+
}
|
|
90048
|
+
return null;
|
|
90049
|
+
}
|
|
89725
90050
|
var ignoredReturnValueVisitor;
|
|
89726
90051
|
var init_ignored_return_value = __esm({
|
|
89727
90052
|
"packages/analyzer/dist/rules/bugs/visitors/javascript/ignored-return-value.js"() {
|
|
@@ -89742,6 +90067,11 @@ var init_ignored_return_value = __esm({
|
|
|
89742
90067
|
const prop = fn.childForFieldName("property");
|
|
89743
90068
|
if (!prop || !PURE_ARRAY_METHODS.has(prop.text))
|
|
89744
90069
|
return null;
|
|
90070
|
+
if (prop.text === "join") {
|
|
90071
|
+
const base = receiverBaseName(fn.childForFieldName("object"));
|
|
90072
|
+
if (base && SOCKET_IO_RECEIVERS.has(base))
|
|
90073
|
+
return null;
|
|
90074
|
+
}
|
|
89745
90075
|
return makeViolation(this.ruleKey, expr, filePath, "high", "Ignored return value", `The return value of \`.${prop.text}()\` is ignored \u2014 this method does not mutate the array in place; the result must be used.`, sourceCode, `Assign the result: \`const result = arr.${prop.text}(...)\`.`);
|
|
89746
90076
|
}
|
|
89747
90077
|
};
|
|
@@ -90403,7 +90733,12 @@ var init_global_this_usage = __esm({
|
|
|
90403
90733
|
let current = node2.parent;
|
|
90404
90734
|
while (current) {
|
|
90405
90735
|
const t = current.type;
|
|
90406
|
-
if (t === "function_declaration" || t === "function" ||
|
|
90736
|
+
if (t === "function_declaration" || t === "function" || // Modern tree-sitter-javascript names a `function () {}` expression
|
|
90737
|
+
// `function_expression` (only the legacy grammar used bare `function`).
|
|
90738
|
+
// Without it, `this` inside any function-expression callback / IIFE /
|
|
90739
|
+
// prototype-assigned method walks up to program scope and is wrongly
|
|
90740
|
+
// reported as a global reference.
|
|
90741
|
+
t === "function_expression" || t === "generator_function" || t === "generator_function_declaration" || t === "method_definition" || t === "class_declaration" || t === "class") {
|
|
90407
90742
|
return null;
|
|
90408
90743
|
}
|
|
90409
90744
|
if (t === "arrow_function") {
|
|
@@ -93069,6 +93404,9 @@ var init_missing_await = __esm({
|
|
|
93069
93404
|
return null;
|
|
93070
93405
|
if (!isInsideAsyncFunction(node2))
|
|
93071
93406
|
return null;
|
|
93407
|
+
const declaredName = node2.childForFieldName("name");
|
|
93408
|
+
if (declaredName?.type === "identifier" && /Promise$/.test(declaredName.text))
|
|
93409
|
+
return null;
|
|
93072
93410
|
const isPromise = typeQuery.isPromiseLike(filePath, value.startPosition.row, value.startPosition.column, value.endPosition.row, value.endPosition.column);
|
|
93073
93411
|
if (!isPromise)
|
|
93074
93412
|
return null;
|
|
@@ -93645,25 +93983,26 @@ var init_unbound_method = __esm({
|
|
|
93645
93983
|
let classBody = node2.parent;
|
|
93646
93984
|
while (classBody && classBody.type !== "class_body")
|
|
93647
93985
|
classBody = classBody.parent;
|
|
93648
|
-
if (classBody)
|
|
93649
|
-
|
|
93650
|
-
|
|
93651
|
-
|
|
93652
|
-
|
|
93653
|
-
|
|
93654
|
-
|
|
93655
|
-
|
|
93656
|
-
|
|
93657
|
-
|
|
93658
|
-
|
|
93659
|
-
|
|
93660
|
-
|
|
93661
|
-
|
|
93662
|
-
|
|
93663
|
-
|
|
93664
|
-
}
|
|
93986
|
+
if (!classBody)
|
|
93987
|
+
return null;
|
|
93988
|
+
let resolvesToMethod = false;
|
|
93989
|
+
for (const member of classBody.namedChildren) {
|
|
93990
|
+
if (member.type === "method_definition") {
|
|
93991
|
+
const nameNode = member.childForFieldName("name");
|
|
93992
|
+
if (nameNode?.text !== methodName3)
|
|
93993
|
+
continue;
|
|
93994
|
+
const isAccessor = member.children.some((c) => (c.text === "get" || c.text === "set") && c.type !== "property_identifier");
|
|
93995
|
+
resolvesToMethod = !isAccessor;
|
|
93996
|
+
break;
|
|
93997
|
+
}
|
|
93998
|
+
if (member.type === "public_field_definition" || member.type === "field_definition") {
|
|
93999
|
+
const nameNode = member.children.find((c) => c.type === "property_identifier" || c.type === "private_property_identifier");
|
|
94000
|
+
if (nameNode?.text === methodName3)
|
|
94001
|
+
return null;
|
|
93665
94002
|
}
|
|
93666
94003
|
}
|
|
94004
|
+
if (!resolvesToMethod)
|
|
94005
|
+
return null;
|
|
93667
94006
|
return makeViolation(this.ruleKey, node2, filePath, "high", "Unbound method reference", `\`this.${methodName3}\` is used as a value without binding \u2014 \`this\` will be lost when called as a callback.`, sourceCode, "Use `this.method.bind(this)`, an arrow wrapper `() => this.method()`, or declare as an arrow function property.");
|
|
93668
94007
|
}
|
|
93669
94008
|
};
|
|
@@ -93766,7 +94105,7 @@ var init_missing_error_boundary = __esm({
|
|
|
93766
94105
|
visit(node2, filePath, sourceCode) {
|
|
93767
94106
|
if (!sourceCode.includes("React") && !sourceCode.includes("react") && !sourceCode.includes("jsx"))
|
|
93768
94107
|
return null;
|
|
93769
|
-
const hasAsyncData = /(?<![.\w])useQuery\b/.test(sourceCode) || /(?<![.\w])useSWR\b/.test(sourceCode) || /\buseSuspenseQuery\b/.test(sourceCode) || /\buseSuspenseInfiniteQuery\b/.test(sourceCode)
|
|
94108
|
+
const hasAsyncData = /(?<![.\w])useQuery\b/.test(sourceCode) || /(?<![.\w])useSWR\b/.test(sourceCode) || /\buseSuspenseQuery\b/.test(sourceCode) || /\buseSuspenseInfiniteQuery\b/.test(sourceCode);
|
|
93770
94109
|
if (!hasAsyncData)
|
|
93771
94110
|
return null;
|
|
93772
94111
|
if (/\bErrorBoundary\b/.test(sourceCode))
|
|
@@ -106591,6 +106930,9 @@ var init_star_import = __esm({
|
|
|
106591
106930
|
if (sourceText === "zod" || sourceText.startsWith("@react-email/") || sourceText.startsWith("fumadocs-ui/") || sourceText.startsWith("fumadocs-core/")) {
|
|
106592
106931
|
return null;
|
|
106593
106932
|
}
|
|
106933
|
+
if (sourceText === "semver" || sourceText.startsWith("@sentry/")) {
|
|
106934
|
+
return null;
|
|
106935
|
+
}
|
|
106594
106936
|
if (sourceText.startsWith("./") || sourceText.startsWith("../"))
|
|
106595
106937
|
return null;
|
|
106596
106938
|
const nsImport = node2.namedChildren.find((c) => c.type === "import_clause")?.namedChildren.find((cc) => cc.type === "namespace_import");
|
|
@@ -108159,14 +108501,20 @@ var init_expression_complexity = __esm({
|
|
|
108159
108501
|
if (!expr)
|
|
108160
108502
|
return null;
|
|
108161
108503
|
let target = expr;
|
|
108162
|
-
while (target.type === "parenthesized_expression" && target.namedChildren[0]) {
|
|
108504
|
+
while ((target.type === "parenthesized_expression" || target.type === "as_expression" || target.type === "satisfies_expression" || target.type === "non_null_expression") && target.namedChildren[0]) {
|
|
108163
108505
|
target = target.namedChildren[0];
|
|
108164
108506
|
}
|
|
108507
|
+
if (target.type === "object" || target.type === "array")
|
|
108508
|
+
return null;
|
|
108165
108509
|
if (target.type === "arrow_function" || target.type === "function_expression" || target.type === "function_declaration" || target.type === "generator_function" || target.type === "generator_function_declaration")
|
|
108166
108510
|
return null;
|
|
108167
108511
|
if (target.type === "jsx_element" || target.type === "jsx_fragment" || target.type === "jsx_self_closing_element")
|
|
108168
108512
|
return null;
|
|
108169
108513
|
let operatorCount = 0;
|
|
108514
|
+
let hasLogical = false;
|
|
108515
|
+
let hasArithmetic = false;
|
|
108516
|
+
const LOGICAL_OPS = /* @__PURE__ */ new Set(["&&", "||", "??"]);
|
|
108517
|
+
const ARITHMETIC_OPS3 = /* @__PURE__ */ new Set(["+", "-", "*", "/", "%", "**", "&", "|", "^", "<<", ">>", ">>>"]);
|
|
108170
108518
|
const BINARY_TYPES = /* @__PURE__ */ new Set(["binary_expression", "logical_expression"]);
|
|
108171
108519
|
const FUNCTION_BOUNDARY_TYPES3 = /* @__PURE__ */ new Set([
|
|
108172
108520
|
"function_declaration",
|
|
@@ -108179,6 +108527,13 @@ var init_expression_complexity = __esm({
|
|
|
108179
108527
|
function countOps(n) {
|
|
108180
108528
|
if (BINARY_TYPES.has(n.type)) {
|
|
108181
108529
|
operatorCount++;
|
|
108530
|
+
const op = n.childForFieldName("operator")?.text;
|
|
108531
|
+
if (op) {
|
|
108532
|
+
if (LOGICAL_OPS.has(op))
|
|
108533
|
+
hasLogical = true;
|
|
108534
|
+
else if (ARITHMETIC_OPS3.has(op))
|
|
108535
|
+
hasArithmetic = true;
|
|
108536
|
+
}
|
|
108182
108537
|
}
|
|
108183
108538
|
for (let i = 0; i < n.childCount; i++) {
|
|
108184
108539
|
const child = n.child(i);
|
|
@@ -108191,6 +108546,8 @@ var init_expression_complexity = __esm({
|
|
|
108191
108546
|
}
|
|
108192
108547
|
countOps(expr);
|
|
108193
108548
|
if (operatorCount > 5) {
|
|
108549
|
+
if (hasLogical && !hasArithmetic)
|
|
108550
|
+
return null;
|
|
108194
108551
|
return makeViolation(this.ruleKey, node2, filePath, "medium", "Complex expression", `Expression has ${operatorCount} binary/logical operators (max 5). Break it into named variables for readability.`, sourceCode, "Split the expression into smaller, named intermediate variables.");
|
|
108195
108552
|
}
|
|
108196
108553
|
return null;
|
|
@@ -109015,12 +109372,73 @@ var init_strict_equality = __esm({
|
|
|
109015
109372
|
}
|
|
109016
109373
|
});
|
|
109017
109374
|
|
|
109018
|
-
// packages/analyzer/dist/rules/code-quality/visitors/
|
|
109019
|
-
|
|
109375
|
+
// packages/analyzer/dist/rules/code-quality/visitors/_shared/commented-out-code.js
|
|
109376
|
+
function stripLineMarker(line) {
|
|
109377
|
+
return line.replace(/^\s*(?:\/\/+|#+)\s?/, "").replace(/^\s*\*\/?\s?/, "").replace(/\*\/\s*$/, "").trim();
|
|
109378
|
+
}
|
|
109379
|
+
function looksLikeProse(line) {
|
|
109380
|
+
const words = line.match(/[A-Za-z][A-Za-z'-]+/g) ?? [];
|
|
109381
|
+
const codePunct = (line.match(/[;{}()=<>[\]]/g) ?? []).length;
|
|
109382
|
+
return words.length >= 4 && codePunct <= 1;
|
|
109383
|
+
}
|
|
109384
|
+
function looksLikeCode(line, opts) {
|
|
109385
|
+
if (/[;{}]\s*$/.test(line))
|
|
109386
|
+
return true;
|
|
109387
|
+
if (opts.colonTerminates && /:\s*$/.test(line))
|
|
109388
|
+
return true;
|
|
109389
|
+
if (/=>/.test(line))
|
|
109390
|
+
return true;
|
|
109391
|
+
if (/^[\w.]+\s*\([^)]*\)\s*;?\s*$/.test(line))
|
|
109392
|
+
return true;
|
|
109393
|
+
if (/^[\w.[\]]+\s*=\s*[^=].*$/.test(line) && !/==/.test(line))
|
|
109394
|
+
return true;
|
|
109395
|
+
return false;
|
|
109396
|
+
}
|
|
109397
|
+
function isCommentedOutCode(inner, opts) {
|
|
109398
|
+
let code = 0;
|
|
109399
|
+
let prose = 0;
|
|
109400
|
+
for (const raw of inner.split("\n")) {
|
|
109401
|
+
const line = stripLineMarker(raw);
|
|
109402
|
+
if (line.length === 0)
|
|
109403
|
+
continue;
|
|
109404
|
+
if (looksLikeProse(line))
|
|
109405
|
+
prose++;
|
|
109406
|
+
else if (looksLikeCode(line, opts))
|
|
109407
|
+
code++;
|
|
109408
|
+
}
|
|
109409
|
+
return code > 0 && code >= prose;
|
|
109410
|
+
}
|
|
109411
|
+
function lineCommentRunInner(node2, opts) {
|
|
109412
|
+
const isRunLine = (n) => n != null && n.type === "comment" && opts.isLineComment(n.text);
|
|
109413
|
+
if (!isRunLine(node2))
|
|
109414
|
+
return null;
|
|
109415
|
+
const prev = node2.previousSibling;
|
|
109416
|
+
if (isRunLine(prev) && prev.startPosition.row === node2.startPosition.row - 1)
|
|
109417
|
+
return null;
|
|
109418
|
+
const lines = [opts.strip(node2.text)];
|
|
109419
|
+
let cur = node2.nextSibling;
|
|
109420
|
+
let lastRow = node2.startPosition.row;
|
|
109421
|
+
while (isRunLine(cur) && cur.startPosition.row === lastRow + 1) {
|
|
109422
|
+
lines.push(opts.strip(cur.text));
|
|
109423
|
+
lastRow = cur.startPosition.row;
|
|
109424
|
+
cur = cur.nextSibling;
|
|
109425
|
+
}
|
|
109426
|
+
return lines.join("\n");
|
|
109427
|
+
}
|
|
109020
109428
|
var init_commented_out_code = __esm({
|
|
109429
|
+
"packages/analyzer/dist/rules/code-quality/visitors/_shared/commented-out-code.js"() {
|
|
109430
|
+
"use strict";
|
|
109431
|
+
}
|
|
109432
|
+
});
|
|
109433
|
+
|
|
109434
|
+
// packages/analyzer/dist/rules/code-quality/visitors/javascript/commented-out-code.js
|
|
109435
|
+
var isLineComment, commentedOutCodeVisitor;
|
|
109436
|
+
var init_commented_out_code2 = __esm({
|
|
109021
109437
|
"packages/analyzer/dist/rules/code-quality/visitors/javascript/commented-out-code.js"() {
|
|
109022
109438
|
"use strict";
|
|
109023
109439
|
init_types3();
|
|
109440
|
+
init_commented_out_code();
|
|
109441
|
+
isLineComment = (t) => t.startsWith("//");
|
|
109024
109442
|
commentedOutCodeVisitor = {
|
|
109025
109443
|
ruleKey: "code-quality/deterministic/commented-out-code",
|
|
109026
109444
|
languages: ["typescript", "tsx", "javascript"],
|
|
@@ -109029,21 +109447,19 @@ var init_commented_out_code = __esm({
|
|
|
109029
109447
|
const text4 = node2.text;
|
|
109030
109448
|
if (text4.startsWith("/**"))
|
|
109031
109449
|
return null;
|
|
109032
|
-
let inner
|
|
109033
|
-
if (
|
|
109034
|
-
inner =
|
|
109035
|
-
|
|
109036
|
-
|
|
109037
|
-
if (
|
|
109038
|
-
|
|
109039
|
-
|
|
109040
|
-
|
|
109041
|
-
|
|
109042
|
-
|
|
109043
|
-
|
|
109044
|
-
|
|
109045
|
-
const matchCount = codePatterns.filter((p) => p.test(inner)).length;
|
|
109046
|
-
if (matchCount >= 2) {
|
|
109450
|
+
let inner;
|
|
109451
|
+
if (isLineComment(text4)) {
|
|
109452
|
+
inner = lineCommentRunInner(node2, { isLineComment, strip: (t) => t.replace(/^\/\/+/, "") });
|
|
109453
|
+
if (inner === null)
|
|
109454
|
+
return null;
|
|
109455
|
+
} else if (text4.startsWith("/*")) {
|
|
109456
|
+
inner = text4.slice(2, -2);
|
|
109457
|
+
} else {
|
|
109458
|
+
return null;
|
|
109459
|
+
}
|
|
109460
|
+
if (inner.trim().length < 10)
|
|
109461
|
+
return null;
|
|
109462
|
+
if (isCommentedOutCode(inner, {})) {
|
|
109047
109463
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Commented-out code", "This comment appears to contain commented-out code. Remove it or track it in version control.", sourceCode, "Delete the commented-out code. If needed, it can be recovered from version control.");
|
|
109048
109464
|
}
|
|
109049
109465
|
return null;
|
|
@@ -109824,6 +110240,11 @@ var init_for_in_without_filter = __esm({
|
|
|
109824
110240
|
return true;
|
|
109825
110241
|
}
|
|
109826
110242
|
}
|
|
110243
|
+
if (n.type === "member_expression") {
|
|
110244
|
+
const prop = n.childForFieldName("property");
|
|
110245
|
+
if (prop?.text === "hasOwnProperty" || prop?.text === "hasOwn")
|
|
110246
|
+
return true;
|
|
110247
|
+
}
|
|
109827
110248
|
if (n.type === "string" && (n.text.includes("hasOwnProperty") || n.text.includes("hasOwn")))
|
|
109828
110249
|
return true;
|
|
109829
110250
|
for (let i = 0; i < n.childCount; i++) {
|
|
@@ -111191,6 +111612,10 @@ var init_selector_parameter = __esm({
|
|
|
111191
111612
|
languages: ["typescript", "tsx", "javascript"],
|
|
111192
111613
|
nodeTypes: JS_FUNCTION_TYPES,
|
|
111193
111614
|
visit(node2, filePath, sourceCode) {
|
|
111615
|
+
const fnParent = node2.parent;
|
|
111616
|
+
if ((node2.type === "arrow_function" || node2.type === "function_expression") && (fnParent?.type === "arguments" || fnParent?.type === "jsx_expression")) {
|
|
111617
|
+
return null;
|
|
111618
|
+
}
|
|
111194
111619
|
const params = node2.childForFieldName("parameters");
|
|
111195
111620
|
if (!params)
|
|
111196
111621
|
return null;
|
|
@@ -113273,6 +113698,15 @@ var init_meaningless_void_operator = __esm({
|
|
|
113273
113698
|
});
|
|
113274
113699
|
|
|
113275
113700
|
// packages/analyzer/dist/rules/code-quality/visitors/javascript/test-with-hardcoded-timeout.js
|
|
113701
|
+
function isTestFile(filePath) {
|
|
113702
|
+
const norm = filePath.replace(/\\/g, "/");
|
|
113703
|
+
const base = norm.slice(norm.lastIndexOf("/") + 1);
|
|
113704
|
+
if (/\.(test|spec)\.[cm]?[jt]sx?$/i.test(base))
|
|
113705
|
+
return true;
|
|
113706
|
+
if (/(^|\/)__tests?__\//.test(norm))
|
|
113707
|
+
return true;
|
|
113708
|
+
return false;
|
|
113709
|
+
}
|
|
113276
113710
|
var testWithHardcodedTimeoutVisitor;
|
|
113277
113711
|
var init_test_with_hardcoded_timeout = __esm({
|
|
113278
113712
|
"packages/analyzer/dist/rules/code-quality/visitors/javascript/test-with-hardcoded-timeout.js"() {
|
|
@@ -113283,7 +113717,7 @@ var init_test_with_hardcoded_timeout = __esm({
|
|
|
113283
113717
|
languages: ["typescript", "tsx", "javascript"],
|
|
113284
113718
|
nodeTypes: ["call_expression"],
|
|
113285
113719
|
visit(node2, filePath, sourceCode) {
|
|
113286
|
-
if (!
|
|
113720
|
+
if (!isTestFile(filePath))
|
|
113287
113721
|
return null;
|
|
113288
113722
|
const fn = node2.childForFieldName("function");
|
|
113289
113723
|
if (!fn)
|
|
@@ -114643,8 +115077,8 @@ var init_test_empty_file = __esm({
|
|
|
114643
115077
|
nodeTypes: ["program"],
|
|
114644
115078
|
visit(node2, filePath, sourceCode) {
|
|
114645
115079
|
const lowerPath = filePath.toLowerCase();
|
|
114646
|
-
const
|
|
114647
|
-
if (!
|
|
115080
|
+
const isTestFile6 = lowerPath.includes(".test.") || lowerPath.includes(".spec.") || lowerPath.includes("__tests__");
|
|
115081
|
+
if (!isTestFile6)
|
|
114648
115082
|
return null;
|
|
114649
115083
|
if (hasTestFunction(node2))
|
|
114650
115084
|
return null;
|
|
@@ -118350,7 +118784,7 @@ var init_javascript5 = __esm({
|
|
|
118350
118784
|
init_useless_computed_key();
|
|
118351
118785
|
init_useless_concat();
|
|
118352
118786
|
init_strict_equality();
|
|
118353
|
-
|
|
118787
|
+
init_commented_out_code2();
|
|
118354
118788
|
init_inverted_boolean();
|
|
118355
118789
|
init_prefer_single_boolean_return();
|
|
118356
118790
|
init_prefer_immediate_return();
|
|
@@ -119517,30 +119951,27 @@ var init_unused_variable2 = __esm({
|
|
|
119517
119951
|
});
|
|
119518
119952
|
|
|
119519
119953
|
// packages/analyzer/dist/rules/code-quality/visitors/python/commented-out-code.js
|
|
119520
|
-
var pythonCommentedOutCodeVisitor;
|
|
119521
|
-
var
|
|
119954
|
+
var isLineComment2, pythonCommentedOutCodeVisitor;
|
|
119955
|
+
var init_commented_out_code3 = __esm({
|
|
119522
119956
|
"packages/analyzer/dist/rules/code-quality/visitors/python/commented-out-code.js"() {
|
|
119523
119957
|
"use strict";
|
|
119524
119958
|
init_types3();
|
|
119959
|
+
init_commented_out_code();
|
|
119960
|
+
isLineComment2 = (t) => t.startsWith("#");
|
|
119525
119961
|
pythonCommentedOutCodeVisitor = {
|
|
119526
119962
|
ruleKey: "code-quality/deterministic/commented-out-code",
|
|
119527
119963
|
languages: ["python"],
|
|
119528
119964
|
nodeTypes: ["comment"],
|
|
119529
119965
|
visit(node2, filePath, sourceCode) {
|
|
119530
119966
|
const text4 = node2.text;
|
|
119531
|
-
if (!text4
|
|
119967
|
+
if (!isLineComment2(text4))
|
|
119532
119968
|
return null;
|
|
119533
|
-
const inner =
|
|
119534
|
-
if (inner
|
|
119969
|
+
const inner = lineCommentRunInner(node2, { isLineComment: isLineComment2, strip: (t) => t.replace(/^#+/, "") });
|
|
119970
|
+
if (inner === null)
|
|
119535
119971
|
return null;
|
|
119536
|
-
|
|
119537
|
-
|
|
119538
|
-
|
|
119539
|
-
/\w+\(.*\)/,
|
|
119540
|
-
/=\s*\w/
|
|
119541
|
-
];
|
|
119542
|
-
const matchCount = codePatterns.filter((p) => p.test(inner)).length;
|
|
119543
|
-
if (matchCount >= 2) {
|
|
119972
|
+
if (inner.trim().length < 10)
|
|
119973
|
+
return null;
|
|
119974
|
+
if (isCommentedOutCode(inner, { colonTerminates: true })) {
|
|
119544
119975
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Commented-out code", "This comment appears to contain commented-out code. Remove it or track it in version control.", sourceCode, "Delete the commented-out code. If needed, it can be recovered from version control.");
|
|
119545
119976
|
}
|
|
119546
119977
|
return null;
|
|
@@ -128533,8 +128964,8 @@ var init_test_not_discoverable = __esm({
|
|
|
128533
128964
|
return null;
|
|
128534
128965
|
const className = classNameNode.text;
|
|
128535
128966
|
const isTestClass = className.startsWith("Test") || className.endsWith("Test") || className.endsWith("Tests");
|
|
128536
|
-
const
|
|
128537
|
-
if (!isTestClass && !
|
|
128967
|
+
const isTestFile6 = isPythonTestFile(filePath);
|
|
128968
|
+
if (!isTestClass && !isTestFile6)
|
|
128538
128969
|
return null;
|
|
128539
128970
|
const body = node2.childForFieldName("body");
|
|
128540
128971
|
if (!body)
|
|
@@ -130303,7 +130734,7 @@ var init_python7 = __esm({
|
|
|
130303
130734
|
init_no_debugger2();
|
|
130304
130735
|
init_require_await2();
|
|
130305
130736
|
init_unused_variable2();
|
|
130306
|
-
|
|
130737
|
+
init_commented_out_code3();
|
|
130307
130738
|
init_abstract_class_without_abstract_method();
|
|
130308
130739
|
init_and_or_ternary();
|
|
130309
130740
|
init_assert_in_production();
|
|
@@ -131477,7 +131908,18 @@ function withLanguageSupport(rules, visitors) {
|
|
|
131477
131908
|
continue;
|
|
131478
131909
|
}
|
|
131479
131910
|
if (rule.engine === "roslyn-host" || rule.engine === "roslyn-workspace") {
|
|
131480
|
-
|
|
131911
|
+
if (language === "csharp") {
|
|
131912
|
+
support[language] = { status: "supported" };
|
|
131913
|
+
continue;
|
|
131914
|
+
}
|
|
131915
|
+
const hasExplicitForLang = explicitFamilies.get(rule.key)?.has(language) ?? false;
|
|
131916
|
+
const universalExcludesLang2 = universalExcludedFamilies.get(rule.key)?.has(language) ?? false;
|
|
131917
|
+
const hasUniversalForLang = universalRuleKeys.has(rule.key) && UNIVERSAL_VISITOR_FAMILIES.includes(language) && !universalExcludesLang2;
|
|
131918
|
+
if (hasExplicitForLang || hasUniversalForLang) {
|
|
131919
|
+
support[language] = { status: "supported" };
|
|
131920
|
+
continue;
|
|
131921
|
+
}
|
|
131922
|
+
support[language] = { status: "not-applicable", reason: "C# semantic rule (Roslyn host); no equivalent in this language" };
|
|
131481
131923
|
continue;
|
|
131482
131924
|
}
|
|
131483
131925
|
const hasExplicit = explicitFamilies.get(rule.key)?.has(language) ?? false;
|
|
@@ -140236,6 +140678,12 @@ function walkCSharp(node2, visit) {
|
|
|
140236
140678
|
walkCSharp(child, visit);
|
|
140237
140679
|
}
|
|
140238
140680
|
}
|
|
140681
|
+
function isCSharpTestPath(filePath) {
|
|
140682
|
+
return filePath.split(/[\\/]/).some((segment) => {
|
|
140683
|
+
const s = segment.toLowerCase();
|
|
140684
|
+
return /\.tests?$/.test(s) || s.endsWith(".testbase") || s.endsWith("testapp");
|
|
140685
|
+
});
|
|
140686
|
+
}
|
|
140239
140687
|
var FUNCTION_BOUNDARY_TYPES;
|
|
140240
140688
|
var init_csharp_helpers = __esm({
|
|
140241
140689
|
"packages/analyzer/dist/rules/_shared/csharp-helpers.js"() {
|
|
@@ -141650,7 +142098,7 @@ function isTypeConstantAccess(node2) {
|
|
|
141650
142098
|
const receiver = lastSegment(node2.childForFieldName("expression")?.text ?? "");
|
|
141651
142099
|
return /^[A-Z]/.test(receiver);
|
|
141652
142100
|
}
|
|
141653
|
-
function
|
|
142101
|
+
function leafName2(node2) {
|
|
141654
142102
|
if (node2.type === "identifier")
|
|
141655
142103
|
return node2.text;
|
|
141656
142104
|
if (node2.type === "member_access_expression")
|
|
@@ -141686,8 +142134,8 @@ var init_timing_attack_comparison2 = __esm({
|
|
|
141686
142134
|
return null;
|
|
141687
142135
|
if (isTypeConstantAccess(left) || isTypeConstantAccess(right))
|
|
141688
142136
|
return null;
|
|
141689
|
-
const leftLeaf =
|
|
141690
|
-
const rightLeaf =
|
|
142137
|
+
const leftLeaf = leafName2(left);
|
|
142138
|
+
const rightLeaf = leafName2(right);
|
|
141691
142139
|
if (leftLeaf === "Length" || rightLeaf === "Length")
|
|
141692
142140
|
return null;
|
|
141693
142141
|
if (ENUM_CONSTANT_PATTERN2.test(leftLeaf) || ENUM_CONSTANT_PATTERN2.test(rightLeaf))
|
|
@@ -141706,7 +142154,7 @@ var init_timing_attack_comparison2 = __esm({
|
|
|
141706
142154
|
});
|
|
141707
142155
|
|
|
141708
142156
|
// packages/analyzer/dist/rules/security/visitors/csharp/insecure-random.js
|
|
141709
|
-
function
|
|
142157
|
+
function isTestFile2(filePath) {
|
|
141710
142158
|
const fileName = filePath.replace(/^.*[\\/]/, "");
|
|
141711
142159
|
return /Tests?\.cs$/i.test(fileName) || /(?:TestSetup|TestFixture|Fixtures?|FunctionalTests|IntegrationTests|UnitTests|TestHelpers?)/.test(fileName);
|
|
141712
142160
|
}
|
|
@@ -141748,7 +142196,7 @@ var init_insecure_random3 = __esm({
|
|
|
141748
142196
|
languages: ["csharp"],
|
|
141749
142197
|
nodeTypes: ["object_creation_expression", "member_access_expression"],
|
|
141750
142198
|
visit(node2, filePath, sourceCode) {
|
|
141751
|
-
if (
|
|
142199
|
+
if (isTestFile2(filePath))
|
|
141752
142200
|
return null;
|
|
141753
142201
|
if (node2.type === "object_creation_expression") {
|
|
141754
142202
|
if (getCreatedTypeName(node2) !== "Random")
|
|
@@ -144219,11 +144667,14 @@ var init_empty_catch3 = __esm({
|
|
|
144219
144667
|
const stmt = tryStatements[0];
|
|
144220
144668
|
if (/\b(Parse|TryParse|Deserialize)\s*[<(]/.test(stmt.text))
|
|
144221
144669
|
return null;
|
|
144222
|
-
|
|
144223
|
-
if (expr?.type === "
|
|
144670
|
+
let expr = stmt.namedChildren[0];
|
|
144671
|
+
if (expr?.type === "await_expression")
|
|
144672
|
+
expr = expr.namedChildren[0];
|
|
144673
|
+
const isBestEffort = (name) => BEST_EFFORT_METHODS.has(name.replace(/Async$/, ""));
|
|
144674
|
+
if (expr?.type === "invocation_expression" && isBestEffort(getCSharpMethodName(expr))) {
|
|
144224
144675
|
return null;
|
|
144225
144676
|
}
|
|
144226
|
-
if (expr?.type === "conditional_access_expression" &&
|
|
144677
|
+
if (expr?.type === "conditional_access_expression" && isBestEffort((expr.text.match(/\.(\w+)\s*\([^()]*\)\s*$/) ?? [])[1] ?? "")) {
|
|
144227
144678
|
return null;
|
|
144228
144679
|
}
|
|
144229
144680
|
}
|
|
@@ -144287,6 +144738,8 @@ var init_self_assignment3 = __esm({
|
|
|
144287
144738
|
languages: ["csharp"],
|
|
144288
144739
|
nodeTypes: ["assignment_expression"],
|
|
144289
144740
|
visit(node2, filePath, sourceCode) {
|
|
144741
|
+
if (node2.parent?.type === "initializer_expression")
|
|
144742
|
+
return null;
|
|
144290
144743
|
const left = node2.childForFieldName("left");
|
|
144291
144744
|
const right = node2.childForFieldName("right");
|
|
144292
144745
|
const operator = node2.childForFieldName("operator");
|
|
@@ -145294,7 +145747,7 @@ var init_infinite_recursion2 = __esm({
|
|
|
145294
145747
|
return null;
|
|
145295
145748
|
if (node2.type === "property_declaration") {
|
|
145296
145749
|
const arrow = node2.childForFieldName("value");
|
|
145297
|
-
const arrowExpr = arrow?.namedChildren[0];
|
|
145750
|
+
const arrowExpr = arrow?.type === "arrow_expression_clause" ? arrow.namedChildren[0] : null;
|
|
145298
145751
|
if (arrowExpr?.type === "identifier" && arrowExpr.text === name) {
|
|
145299
145752
|
return makeViolation(this.ruleKey, node2, filePath, "critical", "Infinite recursion", `Property \`${name}\` returns itself \u2014 reading it recurses until StackOverflowException.`, sourceCode, "Return the backing field (or use an auto-property) instead of the property itself.");
|
|
145300
145753
|
}
|
|
@@ -147359,8 +147812,11 @@ var init_useless_finally2 = __esm({
|
|
|
147359
147812
|
});
|
|
147360
147813
|
|
|
147361
147814
|
// packages/analyzer/dist/rules/bugs/visitors/csharp/fstring-missing-placeholders.js
|
|
147362
|
-
function
|
|
147363
|
-
|
|
147815
|
+
function hasRealHole(node2) {
|
|
147816
|
+
if (node2.namedChildren.some((c) => c?.type === "interpolation"))
|
|
147817
|
+
return true;
|
|
147818
|
+
const stripped = node2.text.replace(/\{\{/g, "").replace(/\}\}/g, "");
|
|
147819
|
+
return stripped.includes("{");
|
|
147364
147820
|
}
|
|
147365
147821
|
var csharpFstringMissingPlaceholdersVisitor;
|
|
147366
147822
|
var init_fstring_missing_placeholders2 = __esm({
|
|
@@ -147372,7 +147828,7 @@ var init_fstring_missing_placeholders2 = __esm({
|
|
|
147372
147828
|
languages: ["csharp"],
|
|
147373
147829
|
nodeTypes: ["interpolated_string_expression"],
|
|
147374
147830
|
visit(node2, filePath, sourceCode) {
|
|
147375
|
-
if (
|
|
147831
|
+
if (hasRealHole(node2))
|
|
147376
147832
|
return null;
|
|
147377
147833
|
let top = node2;
|
|
147378
147834
|
while (top.parent?.type === "binary_expression" && top.parent.childForFieldName("operator")?.text === "+") {
|
|
@@ -147383,7 +147839,7 @@ var init_fstring_missing_placeholders2 = __esm({
|
|
|
147383
147839
|
const walk20 = (n) => {
|
|
147384
147840
|
if (siblingHasHoles)
|
|
147385
147841
|
return;
|
|
147386
|
-
if (n.type === "interpolated_string_expression" && n.id !== node2.id &&
|
|
147842
|
+
if (n.type === "interpolated_string_expression" && n.id !== node2.id && hasRealHole(n)) {
|
|
147387
147843
|
siblingHasHoles = true;
|
|
147388
147844
|
return;
|
|
147389
147845
|
}
|
|
@@ -148863,6 +149319,32 @@ var init_chained_orderby_loses_ordering = __esm({
|
|
|
148863
149319
|
});
|
|
148864
149320
|
|
|
148865
149321
|
// packages/analyzer/dist/rules/bugs/visitors/csharp/check-against-value-being-assigned.js
|
|
149322
|
+
function enclosingTypeDecl(node2) {
|
|
149323
|
+
let p = node2.parent;
|
|
149324
|
+
while (p) {
|
|
149325
|
+
if (p.type === "class_declaration" || p.type === "struct_declaration" || p.type === "record_declaration") {
|
|
149326
|
+
return p;
|
|
149327
|
+
}
|
|
149328
|
+
p = p.parent;
|
|
149329
|
+
}
|
|
149330
|
+
return null;
|
|
149331
|
+
}
|
|
149332
|
+
function selfMemberName(target) {
|
|
149333
|
+
if (target.type === "identifier")
|
|
149334
|
+
return target.text;
|
|
149335
|
+
if (target.type === "member_access_expression") {
|
|
149336
|
+
const obj = target.childForFieldName("expression");
|
|
149337
|
+
if (obj?.type === "this")
|
|
149338
|
+
return target.childForFieldName("name")?.text ?? null;
|
|
149339
|
+
}
|
|
149340
|
+
return null;
|
|
149341
|
+
}
|
|
149342
|
+
function enclosingTypeHasProperty(typeDecl, name) {
|
|
149343
|
+
const body = typeDecl.childForFieldName("body") ?? typeDecl.namedChildren.find((c) => c?.type === "declaration_list");
|
|
149344
|
+
if (!body)
|
|
149345
|
+
return false;
|
|
149346
|
+
return body.namedChildren.some((m) => m?.type === "property_declaration" && m.childForFieldName("name")?.text === name);
|
|
149347
|
+
}
|
|
148866
149348
|
function singleStatement(consequence) {
|
|
148867
149349
|
if (consequence.type === "block") {
|
|
148868
149350
|
const stmts = consequence.namedChildren.filter((c) => c?.type !== "comment");
|
|
@@ -148910,6 +149392,12 @@ var init_check_against_value_being_assigned = __esm({
|
|
|
148910
149392
|
const sameBA = condLeft.text === value.text && condRight.text === target.text;
|
|
148911
149393
|
if (!sameAB && !sameBA)
|
|
148912
149394
|
return null;
|
|
149395
|
+
const selfName = selfMemberName(target);
|
|
149396
|
+
if (selfName) {
|
|
149397
|
+
const typeDecl = enclosingTypeDecl(node2);
|
|
149398
|
+
if (typeDecl && enclosingTypeHasProperty(typeDecl, selfName))
|
|
149399
|
+
return null;
|
|
149400
|
+
}
|
|
148913
149401
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Redundant check against the value being assigned", `\`if (${condition.text}) ${assign.text};\` guards a plain assignment with a check against the same value \u2014 assigning unconditionally has the identical effect.`, sourceCode, "Remove the guard and assign unconditionally, or fix the condition if a different check was intended.");
|
|
148914
149402
|
}
|
|
148915
149403
|
};
|
|
@@ -149399,7 +149887,8 @@ var init_redundant_base_call = __esm({
|
|
|
149399
149887
|
const receiver = node2.childForFieldName("expression");
|
|
149400
149888
|
if (receiver?.type !== "base" && receiver?.text !== "base")
|
|
149401
149889
|
return null;
|
|
149402
|
-
const
|
|
149890
|
+
const nameNode = node2.childForFieldName("name");
|
|
149891
|
+
const memberName3 = nameNode?.type === "generic_name" ? nameNode.namedChildren.find((c) => c?.type === "identifier")?.text : nameNode?.text;
|
|
149403
149892
|
if (!memberName3)
|
|
149404
149893
|
return null;
|
|
149405
149894
|
const typeDecl = enclosingType(node2);
|
|
@@ -149734,6 +150223,19 @@ function baseEntryName(entry) {
|
|
|
149734
150223
|
}
|
|
149735
150224
|
return null;
|
|
149736
150225
|
}
|
|
150226
|
+
function baseEntryArity(entry) {
|
|
150227
|
+
const generic = entry.type === "generic_name" ? entry : entry.type === "qualified_name" ? entry.childForFieldName("name") : null;
|
|
150228
|
+
if (generic?.type !== "generic_name")
|
|
150229
|
+
return 0;
|
|
150230
|
+
const args = generic.namedChildren.find((c) => c?.type === "type_argument_list");
|
|
150231
|
+
return args ? args.namedChildren.filter((c) => c != null).length : 0;
|
|
150232
|
+
}
|
|
150233
|
+
function declaredArity(node2) {
|
|
150234
|
+
const params = node2.childForFieldName("type_parameters");
|
|
150235
|
+
if (!params)
|
|
150236
|
+
return 0;
|
|
150237
|
+
return params.namedChildren.filter((c) => c?.type === "type_parameter").length;
|
|
150238
|
+
}
|
|
149737
150239
|
var csharpRecursiveTypeInheritanceVisitor;
|
|
149738
150240
|
var init_recursive_type_inheritance = __esm({
|
|
149739
150241
|
"packages/analyzer/dist/rules/bugs/visitors/csharp/recursive-type-inheritance.js"() {
|
|
@@ -149755,6 +150257,8 @@ var init_recursive_type_inheritance = __esm({
|
|
|
149755
150257
|
return null;
|
|
149756
150258
|
if (baseEntryName(firstBase) !== typeName2)
|
|
149757
150259
|
return null;
|
|
150260
|
+
if (baseEntryArity(firstBase) !== declaredArity(node2))
|
|
150261
|
+
return null;
|
|
149758
150262
|
return makeViolation(this.ruleKey, firstBase, filePath, "high", "Recursive type inheritance", `\`${typeName2}\` lists itself as its own base type, producing a nonsensical recursive inheritance chain.`, sourceCode, "Remove the self reference from the base list.");
|
|
149759
150263
|
}
|
|
149760
150264
|
};
|
|
@@ -151982,6 +152486,20 @@ function isCSharpGeneratedSource(filePath, sourceCode) {
|
|
|
151982
152486
|
return true;
|
|
151983
152487
|
return sourceCode.slice(0, 600).includes("<auto-generated");
|
|
151984
152488
|
}
|
|
152489
|
+
function isCSharpEfMigration(node2) {
|
|
152490
|
+
const cls = getCSharpEnclosingClass(node2);
|
|
152491
|
+
if (!cls)
|
|
152492
|
+
return false;
|
|
152493
|
+
const baseList = cls.namedChildren.find((c) => c?.type === "base_list");
|
|
152494
|
+
if (!baseList)
|
|
152495
|
+
return false;
|
|
152496
|
+
return baseList.namedChildren.some((base) => {
|
|
152497
|
+
if (!base)
|
|
152498
|
+
return false;
|
|
152499
|
+
const simple = base.text.split(".").pop() ?? base.text;
|
|
152500
|
+
return simple === "Migration";
|
|
152501
|
+
});
|
|
152502
|
+
}
|
|
151985
152503
|
function isCSharpPrivateMember(node2) {
|
|
151986
152504
|
const accessibility = [];
|
|
151987
152505
|
for (const child of node2.children) {
|
|
@@ -154545,17 +155063,13 @@ var init_collapsible_if3 = __esm({
|
|
|
154545
155063
|
});
|
|
154546
155064
|
|
|
154547
155065
|
// packages/analyzer/dist/rules/code-quality/visitors/csharp/commented-out-code.js
|
|
154548
|
-
var
|
|
154549
|
-
var
|
|
155066
|
+
var isLineComment3, csharpCommentedOutCodeVisitor;
|
|
155067
|
+
var init_commented_out_code4 = __esm({
|
|
154550
155068
|
"packages/analyzer/dist/rules/code-quality/visitors/csharp/commented-out-code.js"() {
|
|
154551
155069
|
"use strict";
|
|
154552
155070
|
init_types3();
|
|
154553
|
-
|
|
154554
|
-
|
|
154555
|
-
/[;{}]\s*$/,
|
|
154556
|
-
/=>/,
|
|
154557
|
-
/\w+\(.*\)\s*[;{]?\s*$/
|
|
154558
|
-
];
|
|
155071
|
+
init_commented_out_code();
|
|
155072
|
+
isLineComment3 = (t) => t.startsWith("//") && !t.startsWith("///");
|
|
154559
155073
|
csharpCommentedOutCodeVisitor = {
|
|
154560
155074
|
ruleKey: "code-quality/deterministic/commented-out-code",
|
|
154561
155075
|
languages: ["csharp"],
|
|
@@ -154564,15 +155078,19 @@ var init_commented_out_code3 = __esm({
|
|
|
154564
155078
|
const text4 = node2.text;
|
|
154565
155079
|
if (text4.startsWith("///") || text4.startsWith("/**"))
|
|
154566
155080
|
return null;
|
|
154567
|
-
let inner
|
|
154568
|
-
if (
|
|
154569
|
-
inner =
|
|
154570
|
-
|
|
154571
|
-
|
|
154572
|
-
if (
|
|
155081
|
+
let inner;
|
|
155082
|
+
if (isLineComment3(text4)) {
|
|
155083
|
+
inner = lineCommentRunInner(node2, { isLineComment: isLineComment3, strip: (t) => t.replace(/^\/\/+/, "") });
|
|
155084
|
+
if (inner === null)
|
|
155085
|
+
return null;
|
|
155086
|
+
} else if (text4.startsWith("/*")) {
|
|
155087
|
+
inner = text4.slice(2, -2);
|
|
155088
|
+
} else {
|
|
155089
|
+
return null;
|
|
155090
|
+
}
|
|
155091
|
+
if (inner.trim().length < 10)
|
|
154573
155092
|
return null;
|
|
154574
|
-
|
|
154575
|
-
if (matchCount >= 2) {
|
|
155093
|
+
if (isCommentedOutCode(inner, {})) {
|
|
154576
155094
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Commented-out code", "This comment appears to contain commented-out code. Remove it or track it in version control.", sourceCode, "Delete the commented-out code. If needed, it can be recovered from version control.");
|
|
154577
155095
|
}
|
|
154578
155096
|
return null;
|
|
@@ -155280,6 +155798,13 @@ function csharpEmptyBodyIsIntentional(node2, body) {
|
|
|
155280
155798
|
}
|
|
155281
155799
|
if (isCSharpNoOpCallbackPosition(node2))
|
|
155282
155800
|
return true;
|
|
155801
|
+
let enclosingType3 = node2.parent;
|
|
155802
|
+
while (enclosingType3 && enclosingType3.type !== "class_declaration" && enclosingType3.type !== "struct_declaration" && enclosingType3.type !== "record_declaration" && enclosingType3.type !== "record_struct_declaration") {
|
|
155803
|
+
enclosingType3 = enclosingType3.parent;
|
|
155804
|
+
}
|
|
155805
|
+
const typeName2 = enclosingType3?.childForFieldName("name")?.text;
|
|
155806
|
+
if (typeName2 && /^(Null|Noop|NoOp|Empty)[A-Z0-9]/.test(typeName2))
|
|
155807
|
+
return true;
|
|
155283
155808
|
if (node2.type === "method_declaration") {
|
|
155284
155809
|
if (hasCSharpModifier(node2, "virtual") || hasCSharpModifier(node2, "override") || hasCSharpModifier(node2, "partial"))
|
|
155285
155810
|
return true;
|
|
@@ -155557,6 +156082,7 @@ var init_hardcoded_url2 = __esm({
|
|
|
155557
156082
|
"packages/analyzer/dist/rules/code-quality/visitors/csharp/hardcoded-url.js"() {
|
|
155558
156083
|
"use strict";
|
|
155559
156084
|
init_types3();
|
|
156085
|
+
init_csharp_helpers();
|
|
155560
156086
|
NAMESPACE_URI_HOSTS = /w3\.org|schema\.org|xmlns|openxmlformats|xmlsoap|purl\.org|tempuri\.org|schemas\.microsoft\.com|tools\.ietf\.org|datatracker\.ietf\.org|rfc-editor\.org/;
|
|
155561
156087
|
STABLE_API_HOSTS = /googleapis\.com|maps\.google|api\.stripe\.com|api\.twilio\.com|api\.sendgrid\.com|api\.github\.com|cdn\.|fonts\.googleapis|cloudflare|unpkg\.com|cdnjs\.cloudflare|jsdelivr\.net|nuget\.org/;
|
|
155562
156088
|
PLACEHOLDER_NAME = /default|placeholder|example|sample|template|fallback|demo|test|mock|stub|dummy/;
|
|
@@ -155568,6 +156094,8 @@ var init_hardcoded_url2 = __esm({
|
|
|
155568
156094
|
const text4 = node2.text;
|
|
155569
156095
|
if (!/https?:\/\/[a-zA-Z0-9]/.test(text4))
|
|
155570
156096
|
return null;
|
|
156097
|
+
if (isCSharpTestPath(filePath))
|
|
156098
|
+
return null;
|
|
155571
156099
|
if (text4.includes("example.com") || text4.includes("localhost") || text4.includes("127.0.0.1") || text4.includes("placeholder"))
|
|
155572
156100
|
return null;
|
|
155573
156101
|
if (NAMESPACE_URI_HOSTS.test(text4))
|
|
@@ -155588,6 +156116,42 @@ var init_hardcoded_url2 = __esm({
|
|
|
155588
156116
|
});
|
|
155589
156117
|
|
|
155590
156118
|
// packages/analyzer/dist/rules/code-quality/visitors/csharp/identical-functions.js
|
|
156119
|
+
function isTrivialExpr(e) {
|
|
156120
|
+
switch (e.type) {
|
|
156121
|
+
case "integer_literal":
|
|
156122
|
+
case "real_literal":
|
|
156123
|
+
case "boolean_literal":
|
|
156124
|
+
case "string_literal":
|
|
156125
|
+
case "verbatim_string_literal":
|
|
156126
|
+
case "null_literal":
|
|
156127
|
+
case "character_literal":
|
|
156128
|
+
case "identifier":
|
|
156129
|
+
case "this_expression":
|
|
156130
|
+
case "default_expression":
|
|
156131
|
+
case "member_access_expression":
|
|
156132
|
+
return true;
|
|
156133
|
+
case "invocation_expression": {
|
|
156134
|
+
const args = e.childForFieldName("arguments");
|
|
156135
|
+
if (!args)
|
|
156136
|
+
return true;
|
|
156137
|
+
return args.namedChildren.every((a) => {
|
|
156138
|
+
if (a?.type !== "argument")
|
|
156139
|
+
return true;
|
|
156140
|
+
const value = a.namedChildren.find((c) => c && c.type !== "name_colon");
|
|
156141
|
+
return !value || isTrivialExpr(value);
|
|
156142
|
+
});
|
|
156143
|
+
}
|
|
156144
|
+
default:
|
|
156145
|
+
return false;
|
|
156146
|
+
}
|
|
156147
|
+
}
|
|
156148
|
+
function isTrivialReturnBody(body) {
|
|
156149
|
+
const stmts = body.namedChildren.filter((c) => c && c.type !== "comment");
|
|
156150
|
+
if (stmts.length !== 1 || stmts[0]?.type !== "return_statement")
|
|
156151
|
+
return false;
|
|
156152
|
+
const expr = stmts[0].namedChildren.find((c) => c && c.type !== "comment");
|
|
156153
|
+
return !expr || isTrivialExpr(expr);
|
|
156154
|
+
}
|
|
155591
156155
|
var COMPARED_TYPES, csharpIdenticalFunctionsVisitor;
|
|
155592
156156
|
var init_identical_functions2 = __esm({
|
|
155593
156157
|
"packages/analyzer/dist/rules/code-quality/visitors/csharp/identical-functions.js"() {
|
|
@@ -155606,7 +156170,7 @@ var init_identical_functions2 = __esm({
|
|
|
155606
156170
|
const body = n.childForFieldName("body");
|
|
155607
156171
|
if (body && body.type === "block" && body.namedChildCount > 0) {
|
|
155608
156172
|
const onlyThrow = body.namedChildCount === 1 && body.namedChildren[0]?.type === "throw_statement";
|
|
155609
|
-
if (!onlyThrow) {
|
|
156173
|
+
if (!onlyThrow && !isTrivialReturnBody(body)) {
|
|
155610
156174
|
const normalized = body.text.replace(/\s+/g, " ").trim();
|
|
155611
156175
|
bodies.push({ body: normalized, fnNode: n });
|
|
155612
156176
|
}
|
|
@@ -155796,6 +156360,8 @@ var init_magic_number2 = __esm({
|
|
|
155796
156360
|
return null;
|
|
155797
156361
|
if (parentType === "argument" && parent.parent?.type === "bracketed_argument_list")
|
|
155798
156362
|
return null;
|
|
156363
|
+
if (parentType === "argument" && parent.childForFieldName("name"))
|
|
156364
|
+
return null;
|
|
155799
156365
|
if (parentType === "binary_expression" && TIME_FACTORS2.has(val)) {
|
|
155800
156366
|
const op = parent.childForFieldName("operator");
|
|
155801
156367
|
if (op?.text === "*") {
|
|
@@ -156117,6 +156683,8 @@ var init_no_extraneous_class2 = __esm({
|
|
|
156117
156683
|
return null;
|
|
156118
156684
|
if (node2.namedChildren.some((c) => c?.type === "base_list"))
|
|
156119
156685
|
return null;
|
|
156686
|
+
if (node2.namedChildren.some((c) => c?.type === "attribute_list"))
|
|
156687
|
+
return null;
|
|
156120
156688
|
const body = node2.childForFieldName("body");
|
|
156121
156689
|
if (!body)
|
|
156122
156690
|
return null;
|
|
@@ -156375,7 +156943,8 @@ var init_redundant_jump3 = __esm({
|
|
|
156375
156943
|
if (!grandparent)
|
|
156376
156944
|
return null;
|
|
156377
156945
|
if (node2.type === "return_statement") {
|
|
156378
|
-
|
|
156946
|
+
const hasExpression = node2.children.some((c) => c != null && c.type !== "return" && c.type !== ";");
|
|
156947
|
+
if (hasExpression)
|
|
156379
156948
|
return null;
|
|
156380
156949
|
if (!isCSharpFunctionBoundary(grandparent.type))
|
|
156381
156950
|
return null;
|
|
@@ -157740,6 +158309,8 @@ var init_too_many_lines3 = __esm({
|
|
|
157740
158309
|
const bodyNode = getCSharpFunctionBody(node2);
|
|
157741
158310
|
if (!bodyNode)
|
|
157742
158311
|
return null;
|
|
158312
|
+
if (isCSharpGeneratedSource(filePath, sourceCode) || isCSharpEfMigration(node2))
|
|
158313
|
+
return null;
|
|
157743
158314
|
const lineCount = bodyNode.endPosition.row - bodyNode.startPosition.row + 1;
|
|
157744
158315
|
if (lineCount > 50) {
|
|
157745
158316
|
const name = getCSharpFunctionName(node2);
|
|
@@ -158387,93 +158958,6 @@ var init_unused_collection2 = __esm({
|
|
|
158387
158958
|
}
|
|
158388
158959
|
});
|
|
158389
158960
|
|
|
158390
|
-
// packages/analyzer/dist/rules/code-quality/visitors/csharp/unused-function-parameter.js
|
|
158391
|
-
var csharpUnusedFunctionParameterVisitor;
|
|
158392
|
-
var init_unused_function_parameter2 = __esm({
|
|
158393
|
-
"packages/analyzer/dist/rules/code-quality/visitors/csharp/unused-function-parameter.js"() {
|
|
158394
|
-
"use strict";
|
|
158395
|
-
init_types3();
|
|
158396
|
-
init_csharp_helpers();
|
|
158397
|
-
csharpUnusedFunctionParameterVisitor = {
|
|
158398
|
-
ruleKey: "code-quality/deterministic/unused-function-parameter",
|
|
158399
|
-
languages: ["csharp"],
|
|
158400
|
-
nodeTypes: ["method_declaration", "constructor_declaration", "local_function_statement"],
|
|
158401
|
-
visit(node2, filePath, sourceCode) {
|
|
158402
|
-
const params = node2.childForFieldName("parameters");
|
|
158403
|
-
if (!params || params.namedChildCount === 0)
|
|
158404
|
-
return null;
|
|
158405
|
-
const body = node2.childForFieldName("body");
|
|
158406
|
-
if (!body)
|
|
158407
|
-
return null;
|
|
158408
|
-
if (body.type === "block" && body.namedChildCount === 0)
|
|
158409
|
-
return null;
|
|
158410
|
-
if (/NotImplementedException|NotSupportedException/.test(body.text))
|
|
158411
|
-
return null;
|
|
158412
|
-
if (body.type === "block" && body.namedChildCount === 1 && body.namedChildren[0]?.type === "return_statement" && /Task\.CompletedTask|Task\.FromResult|ValueTask\.CompletedTask/.test(body.namedChildren[0].text)) {
|
|
158413
|
-
return null;
|
|
158414
|
-
}
|
|
158415
|
-
if (node2.type === "method_declaration") {
|
|
158416
|
-
for (const mod of ["override", "virtual", "abstract", "partial", "extern"]) {
|
|
158417
|
-
if (hasCSharpModifier(node2, mod))
|
|
158418
|
-
return null;
|
|
158419
|
-
}
|
|
158420
|
-
if (node2.namedChildren.some((c) => c?.type === "explicit_interface_specifier"))
|
|
158421
|
-
return null;
|
|
158422
|
-
if (node2.childForFieldName("name")?.text === "Main")
|
|
158423
|
-
return null;
|
|
158424
|
-
}
|
|
158425
|
-
const paramList = [];
|
|
158426
|
-
for (const param of params.namedChildren) {
|
|
158427
|
-
if (!param || param.type !== "parameter")
|
|
158428
|
-
continue;
|
|
158429
|
-
if (param.children.some((c) => c?.type === "this" || c?.type === "modifier" && c.text === "this"))
|
|
158430
|
-
continue;
|
|
158431
|
-
if (param.childForFieldName("type")?.text === "CancellationToken")
|
|
158432
|
-
continue;
|
|
158433
|
-
const nameNode = param.childForFieldName("name");
|
|
158434
|
-
if (nameNode?.type === "identifier")
|
|
158435
|
-
paramList.push({ name: nameNode.text, node: param });
|
|
158436
|
-
}
|
|
158437
|
-
if (paramList.length === 0)
|
|
158438
|
-
return null;
|
|
158439
|
-
if (paramList.length === 2) {
|
|
158440
|
-
const first2 = params.namedChildren.find((c) => c?.type === "parameter");
|
|
158441
|
-
const firstType = first2?.childForFieldName("type")?.text;
|
|
158442
|
-
const secondType = paramList[1] ? paramList[1].node.childForFieldName("type")?.text : void 0;
|
|
158443
|
-
if ((firstType === "object" || firstType === "object?") && secondType?.endsWith("EventArgs"))
|
|
158444
|
-
return null;
|
|
158445
|
-
}
|
|
158446
|
-
const used = /* @__PURE__ */ new Set();
|
|
158447
|
-
function collectReads(n) {
|
|
158448
|
-
if (n.type === "identifier") {
|
|
158449
|
-
const parent = n.parent;
|
|
158450
|
-
const isMemberName = parent && (parent.type === "member_access_expression" || parent.type === "qualified_name" || parent.type === "member_binding_expression") && parent.childForFieldName("name")?.id === n.id;
|
|
158451
|
-
if (!isMemberName)
|
|
158452
|
-
used.add(n.text);
|
|
158453
|
-
}
|
|
158454
|
-
for (let i = 0; i < n.namedChildCount; i++) {
|
|
158455
|
-
const child = n.namedChild(i);
|
|
158456
|
-
if (child)
|
|
158457
|
-
collectReads(child);
|
|
158458
|
-
}
|
|
158459
|
-
}
|
|
158460
|
-
collectReads(body);
|
|
158461
|
-
const initializer = node2.namedChildren.find((c) => c?.type === "constructor_initializer");
|
|
158462
|
-
if (initializer)
|
|
158463
|
-
collectReads(initializer);
|
|
158464
|
-
for (const { name, node: paramNode } of paramList) {
|
|
158465
|
-
if (name.startsWith("_"))
|
|
158466
|
-
continue;
|
|
158467
|
-
if (!used.has(name)) {
|
|
158468
|
-
return makeViolation(this.ruleKey, paramNode, filePath, "low", `Unused parameter \`${name}\``, `Parameter \`${name}\` is never used in the method body. Remove it or rename it to \`_\` if the signature is fixed.`, sourceCode, `Remove unused parameter \`${name}\` or rename it to \`_${name}\`.`);
|
|
158469
|
-
}
|
|
158470
|
-
}
|
|
158471
|
-
return null;
|
|
158472
|
-
}
|
|
158473
|
-
};
|
|
158474
|
-
}
|
|
158475
|
-
});
|
|
158476
|
-
|
|
158477
158961
|
// packages/analyzer/dist/rules/code-quality/visitors/csharp/unused-private-member.js
|
|
158478
158962
|
var csharpUnusedPrivateMemberVisitor;
|
|
158479
158963
|
var init_unused_private_member2 = __esm({
|
|
@@ -158498,6 +158982,8 @@ var init_unused_private_member2 = __esm({
|
|
|
158498
158982
|
for (const member of body.namedChildren) {
|
|
158499
158983
|
if (!member)
|
|
158500
158984
|
continue;
|
|
158985
|
+
if (member.namedChildren.some((c) => c?.type === "explicit_interface_specifier"))
|
|
158986
|
+
continue;
|
|
158501
158987
|
if (!isCSharpPrivateMember(member))
|
|
158502
158988
|
continue;
|
|
158503
158989
|
if (getCSharpDeclAttributeNames(member).length > 0)
|
|
@@ -158895,8 +159381,17 @@ var init_abstract_class_public_constructor = __esm({
|
|
|
158895
159381
|
});
|
|
158896
159382
|
|
|
158897
159383
|
// packages/analyzer/dist/rules/code-quality/visitors/csharp/abstract-class-without-abstract-members.js
|
|
158898
|
-
function
|
|
158899
|
-
return body.namedChildren.some((c) => c && MEMBER_TYPES2.has(c.type) && hasCSharpModifier(c,
|
|
159384
|
+
function hasMemberWithModifier(body, modifier) {
|
|
159385
|
+
return body.namedChildren.some((c) => c != null && MEMBER_TYPES2.has(c.type) && hasCSharpModifier(c, modifier));
|
|
159386
|
+
}
|
|
159387
|
+
function hasProtectedConstructor(body) {
|
|
159388
|
+
return body.namedChildren.some((c) => c?.type === "constructor_declaration" && hasCSharpModifier(c, "protected"));
|
|
159389
|
+
}
|
|
159390
|
+
function hasBaseList(classDecl) {
|
|
159391
|
+
return classDecl.namedChildren.some((c) => c?.type === "base_list");
|
|
159392
|
+
}
|
|
159393
|
+
function hasExtensionSignal(classDecl, body) {
|
|
159394
|
+
return hasMemberWithModifier(body, "abstract") || hasMemberWithModifier(body, "virtual") || hasMemberWithModifier(body, "override") || hasProtectedConstructor(body) || hasBaseList(classDecl);
|
|
158900
159395
|
}
|
|
158901
159396
|
var MEMBER_TYPES2, csharpAbstractClassWithoutAbstractMembersVisitor;
|
|
158902
159397
|
var init_abstract_class_without_abstract_members = __esm({
|
|
@@ -158908,6 +159403,7 @@ var init_abstract_class_without_abstract_members = __esm({
|
|
|
158908
159403
|
"method_declaration",
|
|
158909
159404
|
"property_declaration",
|
|
158910
159405
|
"event_declaration",
|
|
159406
|
+
"event_field_declaration",
|
|
158911
159407
|
"indexer_declaration"
|
|
158912
159408
|
]);
|
|
158913
159409
|
csharpAbstractClassWithoutAbstractMembersVisitor = {
|
|
@@ -158922,10 +159418,10 @@ var init_abstract_class_without_abstract_members = __esm({
|
|
|
158922
159418
|
const body = node2.childForFieldName("body");
|
|
158923
159419
|
if (!body)
|
|
158924
159420
|
return null;
|
|
158925
|
-
if (
|
|
159421
|
+
if (hasExtensionSignal(node2, body))
|
|
158926
159422
|
return null;
|
|
158927
159423
|
const name = node2.childForFieldName("name")?.text ?? "class";
|
|
158928
|
-
return makeViolation(this.ruleKey, node2, filePath, "low", "Abstract class without abstract members", `Abstract class \`${name}\` declares no abstract members, so it should be a concrete
|
|
159424
|
+
return makeViolation(this.ruleKey, node2, filePath, "low", "Abstract class without abstract members", `Abstract class \`${name}\` declares no abstract members and no other reason to be abstract (no virtual/override member, no protected constructor, no base type), so it should be a concrete class or a static holder instead.`, sourceCode, "Add abstract members, drop the `abstract` modifier, or convert the type to an interface.");
|
|
158929
159425
|
}
|
|
158930
159426
|
};
|
|
158931
159427
|
}
|
|
@@ -159843,6 +160339,8 @@ var init_static_holder_type_has_constructor = __esm({
|
|
|
159843
160339
|
return null;
|
|
159844
160340
|
if (hasCSharpModifier(node2, "abstract"))
|
|
159845
160341
|
return null;
|
|
160342
|
+
if (node2.namedChildren.some((c) => c?.type === "base_list"))
|
|
160343
|
+
return null;
|
|
159846
160344
|
const body = node2.namedChildren.find((c) => c?.type === "declaration_list");
|
|
159847
160345
|
if (!body)
|
|
159848
160346
|
return null;
|
|
@@ -161726,6 +162224,15 @@ var init_extension_method_on_object = __esm({
|
|
|
161726
162224
|
});
|
|
161727
162225
|
|
|
161728
162226
|
// packages/analyzer/dist/rules/code-quality/visitors/csharp/generic-parameter-not-inferable.js
|
|
162227
|
+
function isInterfaceMember(method) {
|
|
162228
|
+
let current = method.parent;
|
|
162229
|
+
while (current) {
|
|
162230
|
+
if (TYPE_DECL_TYPES2.has(current.type))
|
|
162231
|
+
return current.type === "interface_declaration";
|
|
162232
|
+
current = current.parent;
|
|
162233
|
+
}
|
|
162234
|
+
return false;
|
|
162235
|
+
}
|
|
161729
162236
|
function identifiersIn2(node2) {
|
|
161730
162237
|
const used = /* @__PURE__ */ new Set();
|
|
161731
162238
|
if (!node2)
|
|
@@ -161747,14 +162254,23 @@ function parameterTypeIdentifiers(method) {
|
|
|
161747
162254
|
for (const id of identifiersIn2(param.childForFieldName("type")))
|
|
161748
162255
|
used.add(id);
|
|
161749
162256
|
}
|
|
162257
|
+
for (const id of identifiersIn2(params.childForFieldName("type")))
|
|
162258
|
+
used.add(id);
|
|
161750
162259
|
return used;
|
|
161751
162260
|
}
|
|
161752
|
-
var csharpGenericParameterNotInferableVisitor;
|
|
162261
|
+
var TYPE_DECL_TYPES2, csharpGenericParameterNotInferableVisitor;
|
|
161753
162262
|
var init_generic_parameter_not_inferable = __esm({
|
|
161754
162263
|
"packages/analyzer/dist/rules/code-quality/visitors/csharp/generic-parameter-not-inferable.js"() {
|
|
161755
162264
|
"use strict";
|
|
161756
162265
|
init_types3();
|
|
161757
162266
|
init_csharp_helpers();
|
|
162267
|
+
TYPE_DECL_TYPES2 = /* @__PURE__ */ new Set([
|
|
162268
|
+
"interface_declaration",
|
|
162269
|
+
"class_declaration",
|
|
162270
|
+
"struct_declaration",
|
|
162271
|
+
"record_declaration",
|
|
162272
|
+
"record_struct_declaration"
|
|
162273
|
+
]);
|
|
161758
162274
|
csharpGenericParameterNotInferableVisitor = {
|
|
161759
162275
|
ruleKey: "code-quality/deterministic/generic-parameter-not-inferable",
|
|
161760
162276
|
languages: ["csharp"],
|
|
@@ -161763,6 +162279,8 @@ var init_generic_parameter_not_inferable = __esm({
|
|
|
161763
162279
|
const typeParams = node2.namedChildren.find((c) => c?.type === "type_parameter_list");
|
|
161764
162280
|
if (!typeParams)
|
|
161765
162281
|
return null;
|
|
162282
|
+
if (isInterfaceMember(node2))
|
|
162283
|
+
return null;
|
|
161766
162284
|
const usedInParams = parameterTypeIdentifiers(node2);
|
|
161767
162285
|
const nameNode = node2.childForFieldName("name");
|
|
161768
162286
|
const usedInReturn = /* @__PURE__ */ new Set();
|
|
@@ -161774,15 +162292,13 @@ var init_generic_parameter_not_inferable = __esm({
|
|
|
161774
162292
|
for (const id of identifiersIn2(child))
|
|
161775
162293
|
usedInReturn.add(id);
|
|
161776
162294
|
}
|
|
161777
|
-
const usedInBody = identifiersIn2(node2.childForFieldName("body"));
|
|
161778
162295
|
for (const tp of typeParams.namedChildren) {
|
|
161779
162296
|
if (tp?.type !== "type_parameter")
|
|
161780
162297
|
continue;
|
|
161781
162298
|
const name = tp.namedChildren.find((c) => c?.type === "identifier")?.text;
|
|
161782
162299
|
if (!name)
|
|
161783
162300
|
continue;
|
|
161784
|
-
|
|
161785
|
-
if (!usedInParams.has(name) && usedElsewhere) {
|
|
162301
|
+
if (!usedInParams.has(name) && usedInReturn.has(name)) {
|
|
161786
162302
|
return makeViolation(this.ruleKey, tp, filePath, "low", "Type parameter not inferable", `Type parameter \`${name}\` appears in no method parameter, so callers must always specify it explicitly.`, sourceCode, `Use \`${name}\` in a parameter type, or accept that callers must specify it and consider a non-generic design.`);
|
|
161787
162303
|
}
|
|
161788
162304
|
}
|
|
@@ -161895,6 +162411,9 @@ var init_native_method_not_wrapped = __esm({
|
|
|
161895
162411
|
});
|
|
161896
162412
|
|
|
161897
162413
|
// packages/analyzer/dist/rules/code-quality/visitors/csharp/nested-generic-parameter.js
|
|
162414
|
+
function genericBaseName2(generic) {
|
|
162415
|
+
return generic.namedChildren.find((c) => c?.type === "identifier")?.text ?? "";
|
|
162416
|
+
}
|
|
161898
162417
|
function asGenericName(typeNode) {
|
|
161899
162418
|
if (!typeNode)
|
|
161900
162419
|
return null;
|
|
@@ -161910,16 +162429,19 @@ function hasNestedGeneric(typeNode) {
|
|
|
161910
162429
|
const generic = asGenericName(typeNode);
|
|
161911
162430
|
if (!generic)
|
|
161912
162431
|
return false;
|
|
162432
|
+
if (DELEGATE_OR_EXPRESSION_GENERICS.has(genericBaseName2(generic)))
|
|
162433
|
+
return false;
|
|
161913
162434
|
const args = generic.namedChildren.find((c) => c?.type === "type_argument_list");
|
|
161914
162435
|
if (!args)
|
|
161915
162436
|
return false;
|
|
161916
162437
|
return args.namedChildren.some((arg) => asGenericName(arg) !== null);
|
|
161917
162438
|
}
|
|
161918
|
-
var csharpNestedGenericParameterVisitor;
|
|
162439
|
+
var DELEGATE_OR_EXPRESSION_GENERICS, csharpNestedGenericParameterVisitor;
|
|
161919
162440
|
var init_nested_generic_parameter = __esm({
|
|
161920
162441
|
"packages/analyzer/dist/rules/code-quality/visitors/csharp/nested-generic-parameter.js"() {
|
|
161921
162442
|
"use strict";
|
|
161922
162443
|
init_types3();
|
|
162444
|
+
DELEGATE_OR_EXPRESSION_GENERICS = /* @__PURE__ */ new Set(["Func", "Action", "Predicate", "Expression"]);
|
|
161923
162445
|
csharpNestedGenericParameterVisitor = {
|
|
161924
162446
|
ruleKey: "code-quality/deterministic/nested-generic-parameter",
|
|
161925
162447
|
languages: ["csharp"],
|
|
@@ -162039,6 +162561,39 @@ function enclosingTypeBody(node2) {
|
|
|
162039
162561
|
const body = node2.parent;
|
|
162040
162562
|
return body?.type === "declaration_list" ? body : null;
|
|
162041
162563
|
}
|
|
162564
|
+
function isZeroArgCallTo(expr, getName) {
|
|
162565
|
+
if (expr?.type !== "invocation_expression")
|
|
162566
|
+
return false;
|
|
162567
|
+
const fn = expr.childForFieldName("function") ?? expr.namedChildren[0];
|
|
162568
|
+
const fnText = fn?.text;
|
|
162569
|
+
if (fnText !== getName && fnText !== `this.${getName}`)
|
|
162570
|
+
return false;
|
|
162571
|
+
const args = expr.childForFieldName("arguments") ?? expr.namedChildren.find((c) => c?.type === "argument_list");
|
|
162572
|
+
const argCount = args?.namedChildren.filter((c) => c?.type === "argument").length ?? 0;
|
|
162573
|
+
return argCount === 0;
|
|
162574
|
+
}
|
|
162575
|
+
function getterDelegatesTo(prop, getName) {
|
|
162576
|
+
const propArrow = prop.namedChildren.find((c) => c?.type === "arrow_expression_clause");
|
|
162577
|
+
if (propArrow)
|
|
162578
|
+
return isZeroArgCallTo(propArrow.namedChildren.find(Boolean), getName);
|
|
162579
|
+
const accessorList = prop.namedChildren.find((c) => c?.type === "accessor_list");
|
|
162580
|
+
if (!accessorList)
|
|
162581
|
+
return false;
|
|
162582
|
+
const getter = accessorList.namedChildren.find((a) => a?.type === "accessor_declaration" && a.children.some((c) => c?.text === "get"));
|
|
162583
|
+
if (!getter)
|
|
162584
|
+
return false;
|
|
162585
|
+
const getArrow = getter.namedChildren.find((c) => c?.type === "arrow_expression_clause");
|
|
162586
|
+
if (getArrow)
|
|
162587
|
+
return isZeroArgCallTo(getArrow.namedChildren.find(Boolean), getName);
|
|
162588
|
+
const block = getter.namedChildren.find((c) => c?.type === "block");
|
|
162589
|
+
if (block) {
|
|
162590
|
+
const stmts = block.namedChildren.filter((c) => c?.type !== "comment");
|
|
162591
|
+
if (stmts.length === 1 && stmts[0]?.type === "return_statement") {
|
|
162592
|
+
return isZeroArgCallTo(stmts[0].namedChildren.find(Boolean), getName);
|
|
162593
|
+
}
|
|
162594
|
+
}
|
|
162595
|
+
return false;
|
|
162596
|
+
}
|
|
162042
162597
|
var csharpPropertyNameMatchesGetMethodVisitor;
|
|
162043
162598
|
var init_property_name_matches_get_method = __esm({
|
|
162044
162599
|
"packages/analyzer/dist/rules/code-quality/visitors/csharp/property-name-matches-get-method.js"() {
|
|
@@ -162066,6 +162621,8 @@ var init_property_name_matches_get_method = __esm({
|
|
|
162066
162621
|
});
|
|
162067
162622
|
if (!hasGetMethod)
|
|
162068
162623
|
return null;
|
|
162624
|
+
if (getterDelegatesTo(node2, target))
|
|
162625
|
+
return null;
|
|
162069
162626
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Property name matches a Get method", `Property \`${propName}\` coexists with a parameterless \`${target}()\` method \u2014 a confusing duplication of how the value is exposed.`, sourceCode, `Keep one accessor: either the property \`${propName}\` or the method \`${target}\`, not both.`);
|
|
162070
162627
|
}
|
|
162071
162628
|
};
|
|
@@ -162073,6 +162630,50 @@ var init_property_name_matches_get_method = __esm({
|
|
|
162073
162630
|
});
|
|
162074
162631
|
|
|
162075
162632
|
// packages/analyzer/dist/rules/code-quality/visitors/csharp/public-const-versioning-hazard.js
|
|
162633
|
+
function constFieldNames(fieldDecl) {
|
|
162634
|
+
const names = [];
|
|
162635
|
+
const varDecl = fieldDecl.namedChildren.find((c) => c?.type === "variable_declaration");
|
|
162636
|
+
if (!varDecl)
|
|
162637
|
+
return names;
|
|
162638
|
+
for (const vd of varDecl.namedChildren) {
|
|
162639
|
+
if (vd?.type !== "variable_declarator")
|
|
162640
|
+
continue;
|
|
162641
|
+
const n = vd.childForFieldName("name")?.text ?? vd.namedChildren.find((c) => c?.type === "identifier")?.text;
|
|
162642
|
+
if (n)
|
|
162643
|
+
names.push(n);
|
|
162644
|
+
}
|
|
162645
|
+
return names;
|
|
162646
|
+
}
|
|
162647
|
+
function referencedBySiblingConst(node2, names) {
|
|
162648
|
+
let root = node2;
|
|
162649
|
+
while (root.parent)
|
|
162650
|
+
root = root.parent;
|
|
162651
|
+
let found = false;
|
|
162652
|
+
const scan3 = (n) => {
|
|
162653
|
+
if (found)
|
|
162654
|
+
return;
|
|
162655
|
+
if (n.type === "field_declaration" && n.id !== node2.id && hasCSharpModifier(n, "const")) {
|
|
162656
|
+
const varDecl = n.namedChildren.find((c) => c?.type === "variable_declaration");
|
|
162657
|
+
if (varDecl) {
|
|
162658
|
+
for (const vd of varDecl.namedChildren) {
|
|
162659
|
+
if (vd?.type !== "variable_declarator")
|
|
162660
|
+
continue;
|
|
162661
|
+
const declaredName = vd.childForFieldName("name");
|
|
162662
|
+
walkCSharp(vd, (id) => {
|
|
162663
|
+
if (id.type === "identifier" && id.id !== declaredName?.id && names.includes(id.text)) {
|
|
162664
|
+
found = true;
|
|
162665
|
+
}
|
|
162666
|
+
});
|
|
162667
|
+
}
|
|
162668
|
+
}
|
|
162669
|
+
}
|
|
162670
|
+
for (const c of n.namedChildren)
|
|
162671
|
+
if (c)
|
|
162672
|
+
scan3(c);
|
|
162673
|
+
};
|
|
162674
|
+
scan3(root);
|
|
162675
|
+
return found;
|
|
162676
|
+
}
|
|
162076
162677
|
var csharpPublicConstVersioningHazardVisitor;
|
|
162077
162678
|
var init_public_const_versioning_hazard = __esm({
|
|
162078
162679
|
"packages/analyzer/dist/rules/code-quality/visitors/csharp/public-const-versioning-hazard.js"() {
|
|
@@ -162088,6 +162689,9 @@ var init_public_const_versioning_hazard = __esm({
|
|
|
162088
162689
|
return null;
|
|
162089
162690
|
if (!hasCSharpModifier(node2, "public") && !hasCSharpModifier(node2, "protected"))
|
|
162090
162691
|
return null;
|
|
162692
|
+
const names = constFieldNames(node2);
|
|
162693
|
+
if (names.length > 0 && referencedBySiblingConst(node2, names))
|
|
162694
|
+
return null;
|
|
162091
162695
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Public constant versioning hazard", "A `public const` value is inlined into consumers at compile time and will not update unless they recompile \u2014 prefer `static readonly`.", sourceCode, "Replace the `public const` with a `public static readonly` field.");
|
|
162092
162696
|
}
|
|
162093
162697
|
};
|
|
@@ -162763,12 +163367,12 @@ var init_missing_access_modifier = __esm({
|
|
|
162763
163367
|
return null;
|
|
162764
163368
|
if (node2.parent?.parent?.type === "interface_declaration")
|
|
162765
163369
|
return null;
|
|
163370
|
+
if (node2.namedChildren.some((c) => c?.type === "explicit_interface_specifier"))
|
|
163371
|
+
return null;
|
|
162766
163372
|
if (node2.type === "method_declaration") {
|
|
162767
163373
|
const nm = node2.childForFieldName("name");
|
|
162768
163374
|
if (nm?.type === "qualified_name" || (nm?.text ?? "").includes("."))
|
|
162769
163375
|
return null;
|
|
162770
|
-
if (node2.namedChildren.some((c) => c?.type === "explicit_interface_specifier"))
|
|
162771
|
-
return null;
|
|
162772
163376
|
}
|
|
162773
163377
|
if (node2.type === "constructor_declaration" && mods.includes("static"))
|
|
162774
163378
|
return null;
|
|
@@ -162832,7 +163436,7 @@ function topLevelTypeIdentifiers(root) {
|
|
|
162832
163436
|
const body = m.namedChildren.find((c) => c?.type === "declaration_list");
|
|
162833
163437
|
if (body)
|
|
162834
163438
|
collectFrom(body);
|
|
162835
|
-
} else if (
|
|
163439
|
+
} else if (TYPE_DECL_TYPES3.has(m.type)) {
|
|
162836
163440
|
const id = m.childForFieldName("name");
|
|
162837
163441
|
if (id)
|
|
162838
163442
|
out.push(id);
|
|
@@ -162845,6 +163449,8 @@ function topLevelTypeIdentifiers(root) {
|
|
|
162845
163449
|
function nameMatches(fileName, typeName2) {
|
|
162846
163450
|
if (fileName === typeName2)
|
|
162847
163451
|
return true;
|
|
163452
|
+
if (typeName2 === `${fileName}Attribute`)
|
|
163453
|
+
return true;
|
|
162848
163454
|
const brace = fileName.indexOf("{");
|
|
162849
163455
|
if (brace > 0 && fileName.slice(0, brace) === typeName2)
|
|
162850
163456
|
return true;
|
|
@@ -162853,12 +163459,12 @@ function nameMatches(fileName, typeName2) {
|
|
|
162853
163459
|
return true;
|
|
162854
163460
|
return false;
|
|
162855
163461
|
}
|
|
162856
|
-
var
|
|
163462
|
+
var TYPE_DECL_TYPES3, csharpFilenameTypeMismatchVisitor;
|
|
162857
163463
|
var init_csharp_filename_type_mismatch = __esm({
|
|
162858
163464
|
"packages/analyzer/dist/rules/code-quality/visitors/csharp/csharp-filename-type-mismatch.js"() {
|
|
162859
163465
|
"use strict";
|
|
162860
163466
|
init_types3();
|
|
162861
|
-
|
|
163467
|
+
TYPE_DECL_TYPES3 = /* @__PURE__ */ new Set([
|
|
162862
163468
|
"class_declaration",
|
|
162863
163469
|
"struct_declaration",
|
|
162864
163470
|
"interface_declaration",
|
|
@@ -162872,13 +163478,18 @@ var init_csharp_filename_type_mismatch = __esm({
|
|
|
162872
163478
|
nodeTypes: ["compilation_unit"],
|
|
162873
163479
|
visit(node2, filePath, sourceCode) {
|
|
162874
163480
|
const base = filePath.split("/").pop() ?? "";
|
|
162875
|
-
const
|
|
163481
|
+
const lower = base.toLowerCase();
|
|
163482
|
+
const compoundExt = [".cshtml.cs", ".razor.cs"].find((ext2) => lower.endsWith(ext2));
|
|
163483
|
+
const fileName = compoundExt ? base.slice(0, base.length - compoundExt.length) : base.replace(/\.[^.]*$/, "");
|
|
162876
163484
|
if (!fileName)
|
|
162877
163485
|
return null;
|
|
163486
|
+
const migrationName = fileName.match(/^\d{14}_(.+)$/)?.[1];
|
|
163487
|
+
const candidateNames2 = migrationName ? [fileName, migrationName] : [fileName];
|
|
162878
163488
|
const identifiers = topLevelTypeIdentifiers(node2);
|
|
162879
163489
|
if (identifiers.length === 0)
|
|
162880
163490
|
return null;
|
|
162881
|
-
|
|
163491
|
+
const matches = (id) => candidateNames2.some((fn) => nameMatches(fn, id.text)) || compoundExt !== void 0 && id.text === `${fileName}Model`;
|
|
163492
|
+
if (identifiers.some(matches))
|
|
162882
163493
|
return null;
|
|
162883
163494
|
const first2 = identifiers[0];
|
|
162884
163495
|
return makeViolation(this.ruleKey, first2, filePath, "low", "File name does not match a contained type", `File '${fileName}' declares type '${first2.text}' but no top-level type matches the file name; name the file after a type so it is easy to locate.`, sourceCode, `Rename the file after a contained type, e.g. ${first2.text}.cs.`);
|
|
@@ -163331,7 +163942,7 @@ var init_csharp6 = __esm({
|
|
|
163331
163942
|
init_cognitive_complexity3();
|
|
163332
163943
|
init_collapsible_else_if2();
|
|
163333
163944
|
init_collapsible_if3();
|
|
163334
|
-
|
|
163945
|
+
init_commented_out_code4();
|
|
163335
163946
|
init_comparison_of_constant2();
|
|
163336
163947
|
init_console_log2();
|
|
163337
163948
|
init_contradictory_boolean_expression2();
|
|
@@ -163414,7 +164025,6 @@ var init_csharp6 = __esm({
|
|
|
163414
164025
|
init_unnecessary_namespace_qualifier2();
|
|
163415
164026
|
init_unsafe_any_usage2();
|
|
163416
164027
|
init_unused_collection2();
|
|
163417
|
-
init_unused_function_parameter2();
|
|
163418
164028
|
init_unused_private_member2();
|
|
163419
164029
|
init_unused_private_method2();
|
|
163420
164030
|
init_unused_private_nested_class2();
|
|
@@ -163673,7 +164283,6 @@ var init_csharp6 = __esm({
|
|
|
163673
164283
|
csharpUnnecessaryNamespaceQualifierVisitor,
|
|
163674
164284
|
csharpUnsafeAnyUsageVisitor,
|
|
163675
164285
|
csharpUnusedCollectionVisitor,
|
|
163676
|
-
csharpUnusedFunctionParameterVisitor,
|
|
163677
164286
|
csharpUnusedPrivateMemberVisitor,
|
|
163678
164287
|
csharpUnusedPrivateMethodVisitor,
|
|
163679
164288
|
csharpUnusedPrivateNestedClassVisitor,
|
|
@@ -165855,6 +166464,11 @@ function allElementsConstant(creation) {
|
|
|
165855
166464
|
return false;
|
|
165856
166465
|
return elements.every((e) => LITERAL_TYPES6.has(e.type));
|
|
165857
166466
|
}
|
|
166467
|
+
function isGeneratedOrMigrationFile(filePath) {
|
|
166468
|
+
if (/(^|[\\/])Migrations[\\/]/.test(filePath))
|
|
166469
|
+
return true;
|
|
166470
|
+
return /\.(designer|g|g\.i|generated)\.cs$/i.test(filePath);
|
|
166471
|
+
}
|
|
165858
166472
|
var LITERAL_TYPES6, csharpConstantArrayArgumentVisitor;
|
|
165859
166473
|
var init_constant_array_argument = __esm({
|
|
165860
166474
|
"packages/analyzer/dist/rules/performance/visitors/csharp/constant-array-argument.js"() {
|
|
@@ -165875,6 +166489,8 @@ var init_constant_array_argument = __esm({
|
|
|
165875
166489
|
languages: ["csharp"],
|
|
165876
166490
|
nodeTypes: ["array_creation_expression", "implicit_array_creation_expression"],
|
|
165877
166491
|
visit(node2, filePath, sourceCode) {
|
|
166492
|
+
if (isGeneratedOrMigrationFile(filePath))
|
|
166493
|
+
return null;
|
|
165878
166494
|
if (node2.parent?.type !== "argument")
|
|
165879
166495
|
return null;
|
|
165880
166496
|
if (!getCSharpEnclosingFunction(node2))
|
|
@@ -166663,7 +167279,36 @@ var init_missing_finally_cleanup2 = __esm({
|
|
|
166663
167279
|
});
|
|
166664
167280
|
|
|
166665
167281
|
// packages/analyzer/dist/rules/reliability/visitors/csharp/unsafe-json-parse.js
|
|
166666
|
-
|
|
167282
|
+
function bodyDeclaresMember(body, name) {
|
|
167283
|
+
for (const member of body.namedChildren) {
|
|
167284
|
+
if (!member)
|
|
167285
|
+
continue;
|
|
167286
|
+
if (member.type === "property_declaration") {
|
|
167287
|
+
if (member.childForFieldName("name")?.text === name)
|
|
167288
|
+
return true;
|
|
167289
|
+
} else if (member.type === "field_declaration") {
|
|
167290
|
+
const decl = member.namedChildren.find((c) => c?.type === "variable_declaration");
|
|
167291
|
+
for (const d of decl?.namedChildren ?? []) {
|
|
167292
|
+
if (d?.type === "variable_declarator" && d.childForFieldName("name")?.text === name)
|
|
167293
|
+
return true;
|
|
167294
|
+
}
|
|
167295
|
+
}
|
|
167296
|
+
}
|
|
167297
|
+
return false;
|
|
167298
|
+
}
|
|
167299
|
+
function enclosingTypeDeclaresMember(node2, name) {
|
|
167300
|
+
let current = node2.parent;
|
|
167301
|
+
while (current) {
|
|
167302
|
+
if (TYPE_DECLS2.has(current.type)) {
|
|
167303
|
+
const body = current.childForFieldName("body");
|
|
167304
|
+
if (body && bodyDeclaresMember(body, name))
|
|
167305
|
+
return true;
|
|
167306
|
+
}
|
|
167307
|
+
current = current.parent;
|
|
167308
|
+
}
|
|
167309
|
+
return false;
|
|
167310
|
+
}
|
|
167311
|
+
var JSON_PARSERS, TYPE_DECLS2, csharpUnsafeJsonParseVisitor;
|
|
166667
167312
|
var init_unsafe_json_parse3 = __esm({
|
|
166668
167313
|
"packages/analyzer/dist/rules/reliability/visitors/csharp/unsafe-json-parse.js"() {
|
|
166669
167314
|
"use strict";
|
|
@@ -166678,15 +167323,25 @@ var init_unsafe_json_parse3 = __esm({
|
|
|
166678
167323
|
JsonDocument: /* @__PURE__ */ new Set(["Parse", "ParseAsync"]),
|
|
166679
167324
|
JsonNode: /* @__PURE__ */ new Set(["Parse"])
|
|
166680
167325
|
};
|
|
167326
|
+
TYPE_DECLS2 = /* @__PURE__ */ new Set([
|
|
167327
|
+
"class_declaration",
|
|
167328
|
+
"struct_declaration",
|
|
167329
|
+
"record_declaration",
|
|
167330
|
+
"record_struct_declaration"
|
|
167331
|
+
]);
|
|
166681
167332
|
csharpUnsafeJsonParseVisitor = {
|
|
166682
167333
|
ruleKey: "reliability/deterministic/unsafe-json-parse",
|
|
166683
167334
|
languages: ["csharp"],
|
|
166684
167335
|
nodeTypes: ["invocation_expression"],
|
|
166685
167336
|
visit(node2, filePath, sourceCode) {
|
|
166686
|
-
const
|
|
167337
|
+
const rawReceiver = getCSharpReceiver(node2);
|
|
167338
|
+
const receiver = simpleTypeName5(rawReceiver);
|
|
166687
167339
|
const method = getCSharpMethodName(node2);
|
|
166688
167340
|
if (!JSON_PARSERS[receiver]?.has(method))
|
|
166689
167341
|
return null;
|
|
167342
|
+
const bareReceiver = rawReceiver.startsWith("this.") ? rawReceiver.slice(5) : rawReceiver;
|
|
167343
|
+
if (!bareReceiver.includes(".") && enclosingTypeDeclaresMember(node2, bareReceiver))
|
|
167344
|
+
return null;
|
|
166690
167345
|
const firstArg = getCSharpArguments(node2)[0];
|
|
166691
167346
|
if (firstArg?.type === "invocation_expression") {
|
|
166692
167347
|
const innerReceiver = simpleTypeName5(getCSharpReceiver(firstArg));
|
|
@@ -168393,18 +169048,10 @@ var init_logger_field_naming = __esm({
|
|
|
168393
169048
|
csharpLoggerFieldNamingVisitor = {
|
|
168394
169049
|
ruleKey: "style/deterministic/logger-field-naming",
|
|
168395
169050
|
languages: ["csharp"],
|
|
168396
|
-
nodeTypes: ["field_declaration"
|
|
169051
|
+
nodeTypes: ["field_declaration"],
|
|
168397
169052
|
visit(node2, filePath, sourceCode) {
|
|
168398
169053
|
if (isAutoGeneratedCSharpFile(sourceCode))
|
|
168399
169054
|
return null;
|
|
168400
|
-
if (node2.type === "property_declaration") {
|
|
168401
|
-
if (!isLoggerType(node2.childForFieldName("type")))
|
|
168402
|
-
return null;
|
|
168403
|
-
const name = node2.childForFieldName("name");
|
|
168404
|
-
if (!name || ACCEPTED.test(name.text))
|
|
168405
|
-
return null;
|
|
168406
|
-
return makeViolation(this.ruleKey, name, filePath, "low", "Logger does not follow the naming convention", `Logger '${name.text}' does not follow the convention for logger members (camelCase, optionally _-prefixed).`, sourceCode, `Rename the logger to a camelCase name such as '_logger'.`);
|
|
168407
|
-
}
|
|
168408
169055
|
const declaration = node2.namedChildren.find((c) => c?.type === "variable_declaration");
|
|
168409
169056
|
if (!declaration)
|
|
168410
169057
|
return null;
|
|
@@ -169212,15 +169859,28 @@ function isNestedInType(node2) {
|
|
|
169212
169859
|
const parent = node2.parent;
|
|
169213
169860
|
if (parent?.type !== "declaration_list")
|
|
169214
169861
|
return false;
|
|
169215
|
-
return parent.parent != null &&
|
|
169862
|
+
return parent.parent != null && TYPE_DECL_TYPES4.has(parent.parent.type);
|
|
169216
169863
|
}
|
|
169217
|
-
|
|
169864
|
+
function isConstantContainer(node2) {
|
|
169865
|
+
if (node2.type !== "class_declaration")
|
|
169866
|
+
return false;
|
|
169867
|
+
if (!hasCSharpModifier(node2, "static"))
|
|
169868
|
+
return false;
|
|
169869
|
+
const body = node2.childForFieldName("body");
|
|
169870
|
+
if (!body)
|
|
169871
|
+
return false;
|
|
169872
|
+
const members = body.namedChildren.filter((c) => c && c.type !== "comment");
|
|
169873
|
+
if (members.length === 0)
|
|
169874
|
+
return false;
|
|
169875
|
+
return members.every((m) => m?.type === "field_declaration" && (hasCSharpModifier(m, "const") || hasCSharpModifier(m, "static") && hasCSharpModifier(m, "readonly")));
|
|
169876
|
+
}
|
|
169877
|
+
var TYPE_DECL_TYPES4, csharpNestedTypePubliclyVisibleVisitor;
|
|
169218
169878
|
var init_nested_type_publicly_visible = __esm({
|
|
169219
169879
|
"packages/analyzer/dist/rules/architecture/visitors/csharp/nested-type-publicly-visible.js"() {
|
|
169220
169880
|
"use strict";
|
|
169221
169881
|
init_types3();
|
|
169222
169882
|
init_csharp_helpers();
|
|
169223
|
-
|
|
169883
|
+
TYPE_DECL_TYPES4 = /* @__PURE__ */ new Set([
|
|
169224
169884
|
"class_declaration",
|
|
169225
169885
|
"struct_declaration",
|
|
169226
169886
|
"interface_declaration",
|
|
@@ -169238,6 +169898,8 @@ var init_nested_type_publicly_visible = __esm({
|
|
|
169238
169898
|
return null;
|
|
169239
169899
|
if (getCSharpAttributeNames(node2).length > 0)
|
|
169240
169900
|
return null;
|
|
169901
|
+
if (isConstantContainer(node2))
|
|
169902
|
+
return null;
|
|
169241
169903
|
const name = node2.childForFieldName("name")?.text ?? "type";
|
|
169242
169904
|
return makeViolation(this.ruleKey, node2, filePath, "low", "Publicly visible nested type", `Nested type '${name}' is publicly visible, exposing an implementation detail of its containing type.`, sourceCode, `Make '${name}' private or internal, or lift it out to its own top-level type if it is a real part of the API.`);
|
|
169243
169905
|
}
|
|
@@ -169325,6 +169987,8 @@ var init_type_outside_namespace = __esm({
|
|
|
169325
169987
|
|
|
169326
169988
|
// packages/analyzer/dist/rules/architecture/visitors/csharp/_uri-helpers.js
|
|
169327
169989
|
function nameLooksLikeUri(name) {
|
|
169990
|
+
if (PREPOSITION_BEFORE_URI.test(name))
|
|
169991
|
+
return false;
|
|
169328
169992
|
if (URI_NAME_PATTERN.test(name))
|
|
169329
169993
|
return true;
|
|
169330
169994
|
return /(Url|Uri|Endpoint|Webhook)$/.test(name);
|
|
@@ -169332,11 +169996,12 @@ function nameLooksLikeUri(name) {
|
|
|
169332
169996
|
function isStringType(typeNode) {
|
|
169333
169997
|
return typeNode?.type === "predefined_type" && typeNode.text === "string";
|
|
169334
169998
|
}
|
|
169335
|
-
var URI_NAME_PATTERN;
|
|
169999
|
+
var URI_NAME_PATTERN, PREPOSITION_BEFORE_URI;
|
|
169336
170000
|
var init_uri_helpers = __esm({
|
|
169337
170001
|
"packages/analyzer/dist/rules/architecture/visitors/csharp/_uri-helpers.js"() {
|
|
169338
170002
|
"use strict";
|
|
169339
170003
|
URI_NAME_PATTERN = /(^|[^a-z])(url|uri|uris|urls|endpoint|endpoints|webhook|webhookurl|callbackurl|redirecturi|redirecturl|hreflink|baseurl|baseaddress)$/i;
|
|
170004
|
+
PREPOSITION_BEFORE_URI = /(In|As|Of|At|By|Within)(Url|Uri|Uris|Urls|Endpoint|Endpoints|Webhook)$/;
|
|
169340
170005
|
}
|
|
169341
170006
|
});
|
|
169342
170007
|
|
|
@@ -169374,13 +170039,22 @@ var init_uri_parameter_as_string = __esm({
|
|
|
169374
170039
|
});
|
|
169375
170040
|
|
|
169376
170041
|
// packages/analyzer/dist/rules/architecture/visitors/csharp/uri-property-as-string.js
|
|
169377
|
-
|
|
170042
|
+
function initializerProvesNotUri(valueNode) {
|
|
170043
|
+
if (!valueNode || !isCSharpStringNode(valueNode))
|
|
170044
|
+
return false;
|
|
170045
|
+
const text4 = getCSharpStringText(valueNode);
|
|
170046
|
+
if (text4 == null || text4.length === 0)
|
|
170047
|
+
return false;
|
|
170048
|
+
return !text4.includes("://");
|
|
170049
|
+
}
|
|
170050
|
+
var BINDING_ATTRIBUTES, csharpUriPropertyAsStringVisitor;
|
|
169378
170051
|
var init_uri_property_as_string = __esm({
|
|
169379
170052
|
"packages/analyzer/dist/rules/architecture/visitors/csharp/uri-property-as-string.js"() {
|
|
169380
170053
|
"use strict";
|
|
169381
170054
|
init_types3();
|
|
169382
170055
|
init_csharp_helpers();
|
|
169383
170056
|
init_uri_helpers();
|
|
170057
|
+
BINDING_ATTRIBUTES = /* @__PURE__ */ new Set(["BindProperty", "FromQuery", "FromRoute", "FromForm", "ModelBinder"]);
|
|
169384
170058
|
csharpUriPropertyAsStringVisitor = {
|
|
169385
170059
|
ruleKey: "architecture/deterministic/uri-property-as-string",
|
|
169386
170060
|
languages: ["csharp"],
|
|
@@ -169393,6 +170067,10 @@ var init_uri_property_as_string = __esm({
|
|
|
169393
170067
|
const name = node2.childForFieldName("name")?.text;
|
|
169394
170068
|
if (!name || !nameLooksLikeUri(name))
|
|
169395
170069
|
return null;
|
|
170070
|
+
if (getCSharpAttributeNames(node2).some((a) => BINDING_ATTRIBUTES.has(a)))
|
|
170071
|
+
return null;
|
|
170072
|
+
if (initializerProvesNotUri(node2.childForFieldName("value")))
|
|
170073
|
+
return null;
|
|
169396
170074
|
return makeViolation(this.ruleKey, node2.childForFieldName("type"), filePath, "low", "URI property typed as string", `Property '${name}' holds a URI but is typed as string; use System.Uri to get parsing and validation.`, sourceCode, `Change the type of '${name}' from string to System.Uri.`);
|
|
169397
170075
|
}
|
|
169398
170076
|
};
|
|
@@ -169430,6 +170108,21 @@ function isModelType(classDecl) {
|
|
|
169430
170108
|
const name = classDecl.childForFieldName("name")?.text ?? "";
|
|
169431
170109
|
return MODEL_NAME_SUFFIX.test(name);
|
|
169432
170110
|
}
|
|
170111
|
+
function isServerConstructed(classDecl, body) {
|
|
170112
|
+
for (const member of body.namedChildren) {
|
|
170113
|
+
if (member?.type === "constructor_declaration") {
|
|
170114
|
+
const params = member.childForFieldName("parameters");
|
|
170115
|
+
if (params && params.namedChildCount > 0)
|
|
170116
|
+
return true;
|
|
170117
|
+
}
|
|
170118
|
+
if (member?.type === "method_declaration" && hasCSharpModifier(member, "static")) {
|
|
170119
|
+
const mName = member.childForFieldName("name")?.text ?? "";
|
|
170120
|
+
if (mName === "Create" || mName.startsWith("From"))
|
|
170121
|
+
return true;
|
|
170122
|
+
}
|
|
170123
|
+
}
|
|
170124
|
+
return false;
|
|
170125
|
+
}
|
|
169433
170126
|
function flaggablePropertyType(typeNode) {
|
|
169434
170127
|
if (typeNode?.type !== "predefined_type")
|
|
169435
170128
|
return null;
|
|
@@ -169442,7 +170135,7 @@ var init_value_type_action_param_under_posting = __esm({
|
|
|
169442
170135
|
"use strict";
|
|
169443
170136
|
init_types3();
|
|
169444
170137
|
init_csharp_helpers();
|
|
169445
|
-
MODEL_NAME_SUFFIX = /(Request|
|
|
170138
|
+
MODEL_NAME_SUFFIX = /(Request|Input|Form|Command|Query|Payload)$/;
|
|
169446
170139
|
SCALAR_VALUE_TYPES = /* @__PURE__ */ new Set(["int", "long", "short", "byte", "uint", "ulong", "ushort", "sbyte", "decimal", "double", "float", "bool", "char"]);
|
|
169447
170140
|
csharpValueTypeActionParamUnderPostingVisitor = {
|
|
169448
170141
|
ruleKey: "architecture/deterministic/value-type-action-param-under-posting",
|
|
@@ -169454,6 +170147,8 @@ var init_value_type_action_param_under_posting = __esm({
|
|
|
169454
170147
|
const body = node2.childForFieldName("body");
|
|
169455
170148
|
if (!body)
|
|
169456
170149
|
return null;
|
|
170150
|
+
if (isServerConstructed(node2, body))
|
|
170151
|
+
return null;
|
|
169457
170152
|
for (const member of body.namedChildren) {
|
|
169458
170153
|
if (member?.type !== "property_declaration")
|
|
169459
170154
|
continue;
|
|
@@ -169481,6 +170176,22 @@ var init_value_type_action_param_under_posting = __esm({
|
|
|
169481
170176
|
});
|
|
169482
170177
|
|
|
169483
170178
|
// packages/analyzer/dist/rules/architecture/visitors/csharp/action-missing-producesresponsetype.js
|
|
170179
|
+
function excludedFromApiExplorer(node2) {
|
|
170180
|
+
for (const child of node2.children) {
|
|
170181
|
+
if (child?.type !== "attribute_list")
|
|
170182
|
+
continue;
|
|
170183
|
+
for (const attr of child.namedChildren) {
|
|
170184
|
+
if (attr?.type !== "attribute")
|
|
170185
|
+
continue;
|
|
170186
|
+
const name = (attr.childForFieldName("name")?.text ?? "").split(".").pop();
|
|
170187
|
+
if (name !== "ApiExplorerSettings")
|
|
170188
|
+
continue;
|
|
170189
|
+
if (/\bIgnoreApi\s*=\s*true\b/i.test(attr.text))
|
|
170190
|
+
return true;
|
|
170191
|
+
}
|
|
170192
|
+
}
|
|
170193
|
+
return false;
|
|
170194
|
+
}
|
|
169484
170195
|
function attributeNames7(node2) {
|
|
169485
170196
|
const names = [];
|
|
169486
170197
|
for (const child of node2.children) {
|
|
@@ -169528,6 +170239,8 @@ var init_action_missing_producesresponsetype = __esm({
|
|
|
169528
170239
|
const classAttrs = attributeNames7(cls);
|
|
169529
170240
|
if (classAttrs.some((a) => a.startsWith("ProducesResponseType") || a === "ProducesDefaultResponseType"))
|
|
169530
170241
|
return null;
|
|
170242
|
+
if (excludedFromApiExplorer(node2) || excludedFromApiExplorer(cls))
|
|
170243
|
+
return null;
|
|
169531
170244
|
const name = node2.childForFieldName("name");
|
|
169532
170245
|
return makeViolation(this.ruleKey, name ?? node2, filePath, "low", "Controller action has no [ProducesResponseType]", `Action '${name?.text ?? ""}' declares no [ProducesResponseType]; its response shapes and status codes are missing from the API description.`, sourceCode, "Annotate the action with [ProducesResponseType] for each status code it can return.");
|
|
169533
170246
|
}
|
|
@@ -171470,52 +172183,96 @@ var init_pyproject_checker = __esm({
|
|
|
171470
172183
|
// packages/analyzer/dist/roslyn-host-client.js
|
|
171471
172184
|
import { spawn as spawn6 } from "child_process";
|
|
171472
172185
|
import { existsSync as existsSync9 } from "fs";
|
|
172186
|
+
import { tmpdir } from "os";
|
|
171473
172187
|
import { dirname as dirname8, resolve as resolve9 } from "path";
|
|
171474
172188
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
171475
172189
|
function resolveRoslynHostBinary() {
|
|
171476
172190
|
const override = process.env[HOST_BINARY_ENV];
|
|
171477
172191
|
if (override)
|
|
171478
172192
|
return existsSync9(override) ? override : null;
|
|
171479
|
-
const
|
|
171480
|
-
|
|
171481
|
-
|
|
171482
|
-
|
|
171483
|
-
|
|
171484
|
-
|
|
172193
|
+
const rels = [
|
|
172194
|
+
"roslyn-host/csharp-roslyn-host.dll",
|
|
172195
|
+
"tools/csharp-roslyn-host/bin/Release/net8.0/csharp-roslyn-host"
|
|
172196
|
+
];
|
|
172197
|
+
let dir = dirname8(fileURLToPath4(import.meta.url));
|
|
172198
|
+
for (let i = 0; i < 12; i++) {
|
|
172199
|
+
for (const rel of rels) {
|
|
172200
|
+
const candidate = resolve9(dir, rel);
|
|
172201
|
+
if (existsSync9(candidate))
|
|
172202
|
+
return candidate;
|
|
172203
|
+
}
|
|
172204
|
+
const parent = dirname8(dir);
|
|
172205
|
+
if (parent === dir)
|
|
172206
|
+
break;
|
|
172207
|
+
dir = parent;
|
|
171485
172208
|
}
|
|
171486
172209
|
return null;
|
|
171487
172210
|
}
|
|
171488
172211
|
function invokeHost(bin, request) {
|
|
171489
172212
|
return new Promise((resolvePromise, reject) => {
|
|
171490
|
-
const
|
|
172213
|
+
const viaDotnet = bin.endsWith(".dll");
|
|
172214
|
+
const cmd = viaDotnet ? "dotnet" : bin;
|
|
172215
|
+
const args = viaDotnet ? [bin] : [];
|
|
172216
|
+
const child = spawn6(cmd, args, { stdio: ["pipe", "pipe", "pipe"], cwd: tmpdir() });
|
|
171491
172217
|
let stdout = "";
|
|
171492
172218
|
let stderr = "";
|
|
172219
|
+
let settled = false;
|
|
172220
|
+
const fail = (err) => {
|
|
172221
|
+
if (settled)
|
|
172222
|
+
return;
|
|
172223
|
+
settled = true;
|
|
172224
|
+
reject(err);
|
|
172225
|
+
};
|
|
172226
|
+
const succeed = (violations) => {
|
|
172227
|
+
if (settled)
|
|
172228
|
+
return;
|
|
172229
|
+
settled = true;
|
|
172230
|
+
resolvePromise(violations);
|
|
172231
|
+
};
|
|
171493
172232
|
child.stdout.setEncoding("utf8");
|
|
171494
172233
|
child.stderr.setEncoding("utf8");
|
|
171495
172234
|
child.stdout.on("data", (d) => stdout += d);
|
|
171496
172235
|
child.stderr.on("data", (d) => stderr += d);
|
|
171497
|
-
child.on("error", (e) =>
|
|
171498
|
-
child.on("
|
|
171499
|
-
|
|
171500
|
-
|
|
171501
|
-
|
|
172236
|
+
child.on("error", (e) => fail(new RoslynHostUnavailableError(`Failed to start the Roslyn host (${viaDotnet ? `dotnet ${bin}` : bin}): ${e.message}. Is the .NET runtime installed?`)));
|
|
172237
|
+
child.stdin.on("error", () => {
|
|
172238
|
+
});
|
|
172239
|
+
child.on("close", (code, signalName) => {
|
|
172240
|
+
if (code !== 0 || signalName) {
|
|
172241
|
+
const reason = stderr.trim() || stdout.trim() || "(no output)";
|
|
172242
|
+
fail(new RoslynHostUnavailableError(`The Roslyn host exited ${signalName ? `via ${signalName}` : `with code ${code}`} before responding. Is a compatible .NET runtime installed? Host output: ${reason}`));
|
|
172243
|
+
return;
|
|
172244
|
+
}
|
|
172245
|
+
const lines = stdout.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
172246
|
+
if (lines.length === 0) {
|
|
172247
|
+
fail(new Error(`Roslyn host produced no output.${stderr ? ` stderr: ${stderr}` : ""}`));
|
|
171502
172248
|
return;
|
|
171503
172249
|
}
|
|
171504
172250
|
let resp;
|
|
171505
|
-
|
|
171506
|
-
|
|
171507
|
-
|
|
171508
|
-
|
|
172251
|
+
for (const l of lines) {
|
|
172252
|
+
try {
|
|
172253
|
+
const parsed = JSON.parse(l);
|
|
172254
|
+
if (parsed && typeof parsed.ok === "boolean") {
|
|
172255
|
+
resp = parsed;
|
|
172256
|
+
break;
|
|
172257
|
+
}
|
|
172258
|
+
} catch {
|
|
172259
|
+
}
|
|
172260
|
+
}
|
|
172261
|
+
if (!resp) {
|
|
172262
|
+
fail(new Error(`Roslyn host returned invalid JSON: ${lines[0]}`));
|
|
171509
172263
|
return;
|
|
171510
172264
|
}
|
|
171511
172265
|
if (!resp.ok) {
|
|
171512
|
-
|
|
172266
|
+
fail(new Error(`Roslyn host error: ${resp.error ?? "unknown"}`));
|
|
171513
172267
|
return;
|
|
171514
172268
|
}
|
|
171515
|
-
|
|
172269
|
+
succeed(resp.violations ?? []);
|
|
171516
172270
|
});
|
|
171517
|
-
|
|
171518
|
-
|
|
172271
|
+
try {
|
|
172272
|
+
child.stdin.write(JSON.stringify(request) + "\n");
|
|
172273
|
+
child.stdin.end();
|
|
172274
|
+
} catch {
|
|
172275
|
+
}
|
|
171519
172276
|
});
|
|
171520
172277
|
}
|
|
171521
172278
|
function runRoslynWorkspace(projectPath, rules) {
|
|
@@ -178074,7 +178831,7 @@ var init_schemas2 = __esm({
|
|
|
178074
178831
|
// packages/core/dist/services/llm/cli-provider.js
|
|
178075
178832
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
178076
178833
|
import { join as join10 } from "node:path";
|
|
178077
|
-
import { tmpdir } from "node:os";
|
|
178834
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
178078
178835
|
import { randomUUID as randomUUID23 } from "node:crypto";
|
|
178079
178836
|
var import_cross_spawn, BaseCLIProvider, ClaudeCodeProvider;
|
|
178080
178837
|
var init_cli_provider = __esm({
|
|
@@ -178144,7 +178901,7 @@ var init_cli_provider = __esm({
|
|
|
178144
178901
|
constructor(transport) {
|
|
178145
178902
|
this.transport = transport;
|
|
178146
178903
|
if (process.env.TRUECOURSE_CLI_DEBUG) {
|
|
178147
|
-
this.debugDir = join10(
|
|
178904
|
+
this.debugDir = join10(tmpdir2(), "truecourse-cli-debug");
|
|
178148
178905
|
mkdirSync(this.debugDir, { recursive: true });
|
|
178149
178906
|
log.info(`[CLI] Debug output: ${this.debugDir}`);
|
|
178150
178907
|
}
|
|
@@ -178257,8 +179014,13 @@ var init_cli_provider = __esm({
|
|
|
178257
179014
|
unregisterChildProcess(this._repoId, child);
|
|
178258
179015
|
reject(new Error(`[CLI] Failed to spawn ${this.binaryName}: ${err.message}`));
|
|
178259
179016
|
});
|
|
178260
|
-
child.stdin.
|
|
178261
|
-
|
|
179017
|
+
child.stdin.on("error", () => {
|
|
179018
|
+
});
|
|
179019
|
+
try {
|
|
179020
|
+
child.stdin.write(prompt);
|
|
179021
|
+
child.stdin.end();
|
|
179022
|
+
} catch {
|
|
179023
|
+
}
|
|
178262
179024
|
});
|
|
178263
179025
|
}
|
|
178264
179026
|
/** Extract usage data from CLI JSON envelope if present. */
|
|
@@ -186525,7 +187287,7 @@ var DOMAIN_LABELS = {
|
|
|
186525
187287
|
"database": "Database",
|
|
186526
187288
|
"style": "Style"
|
|
186527
187289
|
};
|
|
186528
|
-
function buildAnalysisSteps(enabledCategories, enableLlmRules) {
|
|
187290
|
+
function buildAnalysisSteps(enabledCategories, enableLlmRules, hasCSharp) {
|
|
186529
187291
|
const steps = [
|
|
186530
187292
|
{ key: "parse", label: "Parsing repository" }
|
|
186531
187293
|
];
|
|
@@ -186536,6 +187298,9 @@ function buildAnalysisSteps(enabledCategories, enableLlmRules) {
|
|
|
186536
187298
|
for (const domain of activeDomains) {
|
|
186537
187299
|
steps.push({ key: domain, label: `${DOMAIN_LABELS[domain]} checks` });
|
|
186538
187300
|
}
|
|
187301
|
+
if (hasCSharp) {
|
|
187302
|
+
steps.push({ key: "csharp", label: "C# semantic analysis" });
|
|
187303
|
+
}
|
|
186539
187304
|
steps.push({ key: "persist", label: "Saving results" });
|
|
186540
187305
|
return steps;
|
|
186541
187306
|
}
|
|
@@ -186562,6 +187327,23 @@ var StepTracker = class {
|
|
|
186562
187327
|
this.emit();
|
|
186563
187328
|
}
|
|
186564
187329
|
}
|
|
187330
|
+
/**
|
|
187331
|
+
* Insert a step at runtime, just before `persist`, if it isn't already there.
|
|
187332
|
+
* Used for phases we can't declare up front — e.g. the C# semantic tier only
|
|
187333
|
+
* exists when the repo actually has C#, which isn't known until files are read.
|
|
187334
|
+
* No-op if the key already exists.
|
|
187335
|
+
*/
|
|
187336
|
+
ensureStep(key4, label) {
|
|
187337
|
+
if (this.steps.some((s) => s.key === key4))
|
|
187338
|
+
return;
|
|
187339
|
+
const persistIdx = this.steps.findIndex((s) => s.key === "persist");
|
|
187340
|
+
const step = { key: key4, label, status: "pending" };
|
|
187341
|
+
if (persistIdx === -1)
|
|
187342
|
+
this.steps.push(step);
|
|
187343
|
+
else
|
|
187344
|
+
this.steps.splice(persistIdx, 0, step);
|
|
187345
|
+
this.emit();
|
|
187346
|
+
}
|
|
186565
187347
|
setStatus(key4, status, detail) {
|
|
186566
187348
|
const step = this.steps.find((s) => s.key === key4);
|
|
186567
187349
|
if (step) {
|
|
@@ -195238,7 +196020,7 @@ import path17 from "node:path";
|
|
|
195238
196020
|
function writeEvidence(params) {
|
|
195239
196021
|
const dir = evidenceScenarioDir(params.repoRoot, params.runId, params.scenarioId);
|
|
195240
196022
|
fs18.mkdirSync(dir, { recursive: true });
|
|
195241
|
-
const
|
|
196023
|
+
const focus = params.failingStep != null ? params.steps.find((s) => s.index === params.failingStep) : params.steps[params.steps.length - 1];
|
|
195242
196024
|
const invocation = {
|
|
195243
196025
|
scenarioId: params.scenarioId,
|
|
195244
196026
|
title: params.title,
|
|
@@ -195258,11 +196040,11 @@ function writeEvidence(params) {
|
|
|
195258
196040
|
}))
|
|
195259
196041
|
};
|
|
195260
196042
|
writeFile(dir, "invocation.json", JSON.stringify(invocation, null, 2));
|
|
195261
|
-
if (
|
|
195262
|
-
writeFile(dir, "stdout.raw.txt",
|
|
195263
|
-
writeFile(dir, "stdout.txt",
|
|
195264
|
-
writeFile(dir, "stderr.raw.txt",
|
|
195265
|
-
writeFile(dir, "stderr.txt",
|
|
196043
|
+
if (focus) {
|
|
196044
|
+
writeFile(dir, "stdout.raw.txt", focus.rawStdout);
|
|
196045
|
+
writeFile(dir, "stdout.txt", focus.normStdout);
|
|
196046
|
+
writeFile(dir, "stderr.raw.txt", focus.rawStderr);
|
|
196047
|
+
writeFile(dir, "stderr.txt", focus.normStderr);
|
|
195266
196048
|
}
|
|
195267
196049
|
const diffLines = [];
|
|
195268
196050
|
if (params.outcome === "fail" && params.mismatch) {
|
|
@@ -195270,8 +196052,10 @@ function writeEvidence(params) {
|
|
|
195270
196052
|
diffLines.push(`expected: ${params.mismatch.expected}`);
|
|
195271
196053
|
diffLines.push(`actual: ${params.mismatch.actual}`, "");
|
|
195272
196054
|
diffLines.push(...params.mismatch.detail);
|
|
195273
|
-
} else if (params.infraMessage) {
|
|
196055
|
+
} else if (params.outcome === "error" && params.infraMessage) {
|
|
195274
196056
|
diffLines.push(`step ${params.failingStep} \u2014 infrastructure error`, "", params.infraMessage);
|
|
196057
|
+
} else if (params.outcome === "pass") {
|
|
196058
|
+
diffLines.push(`all ${params.steps.length} step${params.steps.length === 1 ? "" : "s"} met their expectations`);
|
|
195275
196059
|
}
|
|
195276
196060
|
writeFile(dir, "diff.txt", diffLines.join("\n") + "\n");
|
|
195277
196061
|
writeFile(dir, "files.txt", listSandboxFiles(params.sandboxCwd).join("\n") + "\n");
|
|
@@ -195385,7 +196169,7 @@ async function runScenario(scenario, ctx) {
|
|
|
195385
196169
|
if (capture.spawnError || capture.timedOut) {
|
|
195386
196170
|
const infra = capture.timedOut ? `step timed out after ${ctx.stepTimeoutMs}ms` : `failed to spawn: ${capture.spawnError}`;
|
|
195387
196171
|
records.push(toRecord(stepIndex, argv, step.stdin, repeat3, iteration, capture, normText));
|
|
195388
|
-
const
|
|
196172
|
+
const evidencePath2 = writeEvidence({
|
|
195389
196173
|
repoRoot: ctx.repoRoot,
|
|
195390
196174
|
runId: ctx.runId,
|
|
195391
196175
|
scenarioId: scenario.id,
|
|
@@ -195403,7 +196187,7 @@ async function runScenario(scenario, ctx) {
|
|
|
195403
196187
|
outcome: "error",
|
|
195404
196188
|
durationMs: Date.now() - start,
|
|
195405
196189
|
failure: { step: stepIndex, expected: "the step to run", actual: infra },
|
|
195406
|
-
evidencePath
|
|
196190
|
+
evidencePath: evidencePath2
|
|
195407
196191
|
};
|
|
195408
196192
|
}
|
|
195409
196193
|
const normStdout = normText(capture.stdout);
|
|
@@ -195418,7 +196202,7 @@ async function runScenario(scenario, ctx) {
|
|
|
195418
196202
|
});
|
|
195419
196203
|
if (mismatch) {
|
|
195420
196204
|
records.push(toRecord(stepIndex, argv, step.stdin, repeat3, iteration, capture, normText));
|
|
195421
|
-
const
|
|
196205
|
+
const evidencePath2 = writeEvidence({
|
|
195422
196206
|
repoRoot: ctx.repoRoot,
|
|
195423
196207
|
runId: ctx.runId,
|
|
195424
196208
|
scenarioId: scenario.id,
|
|
@@ -195440,7 +196224,7 @@ async function runScenario(scenario, ctx) {
|
|
|
195440
196224
|
expected: mismatch.expected,
|
|
195441
196225
|
actual: mismatch.actual
|
|
195442
196226
|
},
|
|
195443
|
-
evidencePath
|
|
196227
|
+
evidencePath: evidencePath2
|
|
195444
196228
|
};
|
|
195445
196229
|
}
|
|
195446
196230
|
}
|
|
@@ -195448,7 +196232,18 @@ async function runScenario(scenario, ctx) {
|
|
|
195448
196232
|
records.push(toRecord(stepIndex, argv, step.stdin, repeat3, repeat3, lastCapture, normText));
|
|
195449
196233
|
}
|
|
195450
196234
|
}
|
|
195451
|
-
|
|
196235
|
+
const evidencePath = ctx.capturePassEvidence ? writeEvidence({
|
|
196236
|
+
repoRoot: ctx.repoRoot,
|
|
196237
|
+
runId: ctx.runId,
|
|
196238
|
+
scenarioId: scenario.id,
|
|
196239
|
+
title: scenario.title,
|
|
196240
|
+
binds: scenario.binds,
|
|
196241
|
+
outcome: "pass",
|
|
196242
|
+
steps: records,
|
|
196243
|
+
sandboxCwd: sandbox.cwd,
|
|
196244
|
+
envPins: ENV_PINS
|
|
196245
|
+
}) : void 0;
|
|
196246
|
+
return { ...base, outcome: "pass", durationMs: Date.now() - start, ...evidencePath ? { evidencePath } : {} };
|
|
195452
196247
|
} finally {
|
|
195453
196248
|
sandbox.cleanup();
|
|
195454
196249
|
}
|
|
@@ -195741,13 +196536,15 @@ async function runGuard(opts) {
|
|
|
195741
196536
|
settled += 1;
|
|
195742
196537
|
opts.onScenarioSettled?.(settled, selected.length, result);
|
|
195743
196538
|
}
|
|
196539
|
+
const capturePassEvidence = opts.persist !== false;
|
|
195744
196540
|
const executed = await mapWithConcurrency(executable, concurrency, async ({ scenario, resolution }) => {
|
|
195745
196541
|
const outcome = await runScenario(scenario, {
|
|
195746
196542
|
repoRoot,
|
|
195747
196543
|
runId,
|
|
195748
196544
|
resolvedEntry,
|
|
195749
196545
|
recipeEnv: loaded.recipe.env,
|
|
195750
|
-
stepTimeoutMs
|
|
196546
|
+
stepTimeoutMs,
|
|
196547
|
+
capturePassEvidence
|
|
195751
196548
|
});
|
|
195752
196549
|
const result = resolution.kind === "remap" ? { ...outcome, remappedTo: resolution.section.anchor } : outcome;
|
|
195753
196550
|
settled += 1;
|
|
@@ -196365,7 +197162,16 @@ var OverlapSchema = external_exports.object({
|
|
|
196365
197162
|
/** Short note on what may disagree ("auth0_id vs auth0_sub"). */
|
|
196366
197163
|
note: external_exports.string().default(""),
|
|
196367
197164
|
/** The specific conflicting sections per doc (markdown headings), when known. */
|
|
196368
|
-
sections: external_exports.array(OverlapSectionSchema).default([])
|
|
197165
|
+
sections: external_exports.array(OverlapSectionSchema).default([]),
|
|
197166
|
+
/**
|
|
197167
|
+
* The area ids this dispute spans. Detection runs per area, so one disagreement
|
|
197168
|
+
* on a doc pair sharing several areas is flagged in each; the cross-area merge
|
|
197169
|
+
* collapses those to this single record and lists every area it spanned here, so
|
|
197170
|
+
* a resolution scoped to any of them clears the dispute everywhere. Empty for
|
|
197171
|
+
* older corpora written before the merge — the read layer recomputes the span
|
|
197172
|
+
* from the per-area placement of the (then-duplicated) records.
|
|
197173
|
+
*/
|
|
197174
|
+
areas: external_exports.array(external_exports.string()).default([])
|
|
196369
197175
|
});
|
|
196370
197176
|
var AreaSchema = external_exports.object({
|
|
196371
197177
|
/** Canonical area id, `product/concern`. */
|
|
@@ -197140,6 +197946,7 @@ init_zod();
|
|
|
197140
197946
|
import { createHash as createHash6 } from "node:crypto";
|
|
197141
197947
|
import fs28 from "node:fs";
|
|
197142
197948
|
init_transport();
|
|
197949
|
+
init_dist();
|
|
197143
197950
|
var DEFAULT_MAX_PAIRS_PER_AREA = 60;
|
|
197144
197951
|
async function flagOverlaps(repoRoot, areas, docs, opts = {}) {
|
|
197145
197952
|
const result = /* @__PURE__ */ new Map();
|
|
@@ -197211,7 +198018,7 @@ async function flagOverlaps(repoRoot, areas, docs, opts = {}) {
|
|
|
197211
198018
|
examineOne(repoRoot, pair.areaId, pair.a, pair.b, runner2).then((verdict) => {
|
|
197212
198019
|
if (verdict.overlap) {
|
|
197213
198020
|
const list = result.get(pair.areaId) ?? [];
|
|
197214
|
-
list.push({ docs: [pair.a.path, pair.b.path], note: verdict.note, sections: verdict.sections ?? [] });
|
|
198021
|
+
list.push({ docs: [pair.a.path, pair.b.path], note: verdict.note, sections: verdict.sections ?? [], areas: [] });
|
|
197215
198022
|
result.set(pair.areaId, list);
|
|
197216
198023
|
}
|
|
197217
198024
|
}).catch(() => {
|
|
@@ -197229,19 +198036,13 @@ async function flagOverlaps(repoRoot, areas, docs, opts = {}) {
|
|
|
197229
198036
|
};
|
|
197230
198037
|
launch();
|
|
197231
198038
|
});
|
|
197232
|
-
const
|
|
197233
|
-
|
|
197234
|
-
|
|
197235
|
-
|
|
197236
|
-
|
|
197237
|
-
|
|
197238
|
-
|
|
197239
|
-
return true;
|
|
197240
|
-
});
|
|
197241
|
-
if (kept.length === 0)
|
|
197242
|
-
result.delete(areaId);
|
|
197243
|
-
else
|
|
197244
|
-
result.set(areaId, kept);
|
|
198039
|
+
const entries = [...result].flatMap(([area, list]) => list.map((overlap) => ({ area, overlap })));
|
|
198040
|
+
const merged = dedupeCrossAreaOverlaps(entries);
|
|
198041
|
+
result.clear();
|
|
198042
|
+
for (const m of merged) {
|
|
198043
|
+
const list = result.get(m.area) ?? [];
|
|
198044
|
+
list.push({ ...m.overlap, areas: m.areas });
|
|
198045
|
+
result.set(m.area, list);
|
|
197245
198046
|
}
|
|
197246
198047
|
for (const [areaId, list] of result) {
|
|
197247
198048
|
list.sort((x, y) => x.docs.join() < y.docs.join() ? -1 : 1);
|
|
@@ -197249,11 +198050,6 @@ async function flagOverlaps(repoRoot, areas, docs, opts = {}) {
|
|
|
197249
198050
|
}
|
|
197250
198051
|
return result;
|
|
197251
198052
|
}
|
|
197252
|
-
function overlapDedupKey(ov) {
|
|
197253
|
-
const docs = [...ov.docs].sort().join("::");
|
|
197254
|
-
const secs = (ov.sections ?? []).map((s) => `${s.doc}|${s.heading}`).sort().join("::");
|
|
197255
|
-
return `${docs}##${secs}`;
|
|
197256
|
-
}
|
|
197257
198053
|
async function examineOne(repoRoot, areaId, a, b, runner2) {
|
|
197258
198054
|
const cacheKey = computeCacheKey4(areaId, a, b);
|
|
197259
198055
|
const cached = await readCache3(repoRoot, cacheKey);
|
|
@@ -205321,7 +206117,7 @@ var SKIP_DIRS2 = /* @__PURE__ */ new Set([
|
|
|
205321
206117
|
"obj",
|
|
205322
206118
|
".vs"
|
|
205323
206119
|
]);
|
|
205324
|
-
function
|
|
206120
|
+
function isTestFile3(filePath) {
|
|
205325
206121
|
const parts = filePath.replace(/\\/g, "/").split("/");
|
|
205326
206122
|
const fileName = parts[parts.length - 1] ?? "";
|
|
205327
206123
|
if (/\.(test|spec)\.[mc]?[jt]sx?$/.test(fileName))
|
|
@@ -205355,7 +206151,7 @@ async function eachParsedSource(rootDir, visit) {
|
|
|
205355
206151
|
}
|
|
205356
206152
|
if (!entry.isFile())
|
|
205357
206153
|
continue;
|
|
205358
|
-
if (
|
|
206154
|
+
if (isTestFile3(full))
|
|
205359
206155
|
continue;
|
|
205360
206156
|
const lang = EXT_TO_LANG[path33.extname(entry.name)];
|
|
205361
206157
|
if (!lang)
|
|
@@ -205435,19 +206231,19 @@ function readCsharpString(node2, source) {
|
|
|
205435
206231
|
}
|
|
205436
206232
|
if (node2.type === "interpolated_string_expression") {
|
|
205437
206233
|
let text4 = "";
|
|
205438
|
-
let
|
|
206234
|
+
let hasInterpolation = false;
|
|
205439
206235
|
for (let i = 0; i < node2.namedChildCount; i++) {
|
|
205440
206236
|
const c = node2.namedChild(i);
|
|
205441
206237
|
if (!c)
|
|
205442
206238
|
continue;
|
|
205443
206239
|
if (c.type === "interpolation") {
|
|
205444
206240
|
text4 += " /*INTERP*/ ";
|
|
205445
|
-
|
|
206241
|
+
hasInterpolation = true;
|
|
205446
206242
|
} else {
|
|
205447
206243
|
text4 += source.slice(c.startIndex, c.endIndex);
|
|
205448
206244
|
}
|
|
205449
206245
|
}
|
|
205450
|
-
return { text: text4, hasInterpolation
|
|
206246
|
+
return { text: text4, hasInterpolation };
|
|
205451
206247
|
}
|
|
205452
206248
|
if (node2.type === "binary_expression") {
|
|
205453
206249
|
const op = node2.childForFieldName("operator");
|
|
@@ -209370,14 +210166,14 @@ function getSqlText(node2, source) {
|
|
|
209370
210166
|
return { text: source.slice(node2.startIndex, node2.endIndex), hasInterpolation: false };
|
|
209371
210167
|
}
|
|
209372
210168
|
function extractFromSqlSource(sqlNode, filePath, source) {
|
|
209373
|
-
const { text: text4, hasInterpolation
|
|
209374
|
-
return buildQueriesFromSqlText(text4,
|
|
210169
|
+
const { text: text4, hasInterpolation } = getSqlText(sqlNode, source);
|
|
210170
|
+
return buildQueriesFromSqlText(text4, hasInterpolation, {
|
|
209375
210171
|
filePath,
|
|
209376
210172
|
lineStart: sqlNode.startPosition.row + 1,
|
|
209377
210173
|
lineEnd: sqlNode.endPosition.row + 1
|
|
209378
210174
|
});
|
|
209379
210175
|
}
|
|
209380
|
-
function buildQueriesFromSqlText(text4,
|
|
210176
|
+
function buildQueriesFromSqlText(text4, hasInterpolation, loc, adapterName = "raw-sql") {
|
|
209381
210177
|
if (!text4 || !/\bSELECT\b/i.test(text4))
|
|
209382
210178
|
return [];
|
|
209383
210179
|
const blocks = splitSelectBlocks(text4);
|
|
@@ -209396,7 +210192,7 @@ function buildQueriesFromSqlText(text4, hasInterpolation2, loc, adapterName = "r
|
|
|
209396
210192
|
else
|
|
209397
210193
|
unparseable.push({ reason: "unrecognised SQL predicate", raw: piece });
|
|
209398
210194
|
}
|
|
209399
|
-
if (
|
|
210195
|
+
if (hasInterpolation) {
|
|
209400
210196
|
unparseable.push({ reason: "string-interpolation substitution skipped", raw: "${...}" });
|
|
209401
210197
|
}
|
|
209402
210198
|
out.push({
|
|
@@ -209761,7 +210557,7 @@ function pySqlText(node2, source) {
|
|
|
209761
210557
|
function readPyStringNode(node2, source) {
|
|
209762
210558
|
if (node2.type === "concatenated_string") {
|
|
209763
210559
|
let text5 = "";
|
|
209764
|
-
let
|
|
210560
|
+
let hasInterpolation2 = false;
|
|
209765
210561
|
for (let i = 0; i < node2.namedChildCount; i++) {
|
|
209766
210562
|
const c = node2.namedChild(i);
|
|
209767
210563
|
if (!c)
|
|
@@ -209769,15 +210565,15 @@ function readPyStringNode(node2, source) {
|
|
|
209769
210565
|
const part = readPyStringNode(c, source);
|
|
209770
210566
|
if (part) {
|
|
209771
210567
|
text5 += part.text;
|
|
209772
|
-
|
|
210568
|
+
hasInterpolation2 = hasInterpolation2 || part.hasInterpolation;
|
|
209773
210569
|
}
|
|
209774
210570
|
}
|
|
209775
|
-
return { text: text5, hasInterpolation:
|
|
210571
|
+
return { text: text5, hasInterpolation: hasInterpolation2 };
|
|
209776
210572
|
}
|
|
209777
210573
|
if (node2.type !== "string")
|
|
209778
210574
|
return null;
|
|
209779
210575
|
let text4 = "";
|
|
209780
|
-
let
|
|
210576
|
+
let hasInterpolation = false;
|
|
209781
210577
|
for (let i = 0; i < node2.namedChildCount; i++) {
|
|
209782
210578
|
const c = node2.namedChild(i);
|
|
209783
210579
|
if (!c)
|
|
@@ -209786,10 +210582,10 @@ function readPyStringNode(node2, source) {
|
|
|
209786
210582
|
text4 += source.slice(c.startIndex, c.endIndex);
|
|
209787
210583
|
else if (c.type === "interpolation") {
|
|
209788
210584
|
text4 += " /*INTERP*/ ";
|
|
209789
|
-
|
|
210585
|
+
hasInterpolation = true;
|
|
209790
210586
|
}
|
|
209791
210587
|
}
|
|
209792
|
-
return { text: text4, hasInterpolation
|
|
210588
|
+
return { text: text4, hasInterpolation };
|
|
209793
210589
|
}
|
|
209794
210590
|
|
|
209795
210591
|
// packages/contract-verifier/dist/extractor/query/shared/python-orm.js
|
|
@@ -216899,7 +217695,7 @@ async function buildCodebaseScan(codeDir) {
|
|
|
216899
217695
|
|
|
216900
217696
|
// packages/contract-verifier/dist/extractor/index.js
|
|
216901
217697
|
var TS_EXT4 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
216902
|
-
function
|
|
217698
|
+
function isTestFile4(filePath) {
|
|
216903
217699
|
const parts = filePath.replace(/\\/g, "/").split("/");
|
|
216904
217700
|
const fileName = parts[parts.length - 1];
|
|
216905
217701
|
if (/\.(test|spec)\.(t|j)sx?$/.test(fileName))
|
|
@@ -216929,7 +217725,7 @@ async function extractOperationsFromDir(rootDir) {
|
|
|
216929
217725
|
const ext2 = path44.extname(entry.name);
|
|
216930
217726
|
if (!TS_EXT4.has(ext2))
|
|
216931
217727
|
continue;
|
|
216932
|
-
if (
|
|
217728
|
+
if (isTestFile4(full))
|
|
216933
217729
|
continue;
|
|
216934
217730
|
const source = fs43.readFileSync(full, "utf-8");
|
|
216935
217731
|
const lang = ext2 === ".ts" || ext2 === ".tsx" ? ext2 === ".tsx" ? "tsx" : "typescript" : "javascript";
|
|
@@ -226618,7 +227414,7 @@ var PROMPT_OVERHEAD_TOKENS = 500;
|
|
|
226618
227414
|
var TOKENS_PER_RULE = 50;
|
|
226619
227415
|
var TOKENS_PER_FILE_PATH = 25;
|
|
226620
227416
|
var MAX_CHARS_PER_BATCH = 1e5;
|
|
226621
|
-
function
|
|
227417
|
+
function isTestFile5(filePath) {
|
|
226622
227418
|
return TEST_PATTERNS.some((p) => filePath.includes(p));
|
|
226623
227419
|
}
|
|
226624
227420
|
function matchesFileFilter(fa, filter2, fileContent) {
|
|
@@ -226653,7 +227449,7 @@ function matchesFileFilter(fa, filter2, fileContent) {
|
|
|
226653
227449
|
return false;
|
|
226654
227450
|
}
|
|
226655
227451
|
if (filter2.isTestFile !== void 0) {
|
|
226656
|
-
if (filter2.isTestFile !==
|
|
227452
|
+
if (filter2.isTestFile !== isTestFile5(fa.filePath))
|
|
226657
227453
|
return false;
|
|
226658
227454
|
}
|
|
226659
227455
|
if (filter2.languages) {
|
|
@@ -231405,21 +232201,21 @@ function node(getModule) {
|
|
|
231405
232201
|
if (lineMatch) {
|
|
231406
232202
|
let object;
|
|
231407
232203
|
let method;
|
|
231408
|
-
let
|
|
232204
|
+
let functionName2;
|
|
231409
232205
|
let typeName2;
|
|
231410
232206
|
let methodName3;
|
|
231411
232207
|
if (lineMatch[1]) {
|
|
231412
|
-
|
|
231413
|
-
let methodStart =
|
|
231414
|
-
if (
|
|
232208
|
+
functionName2 = lineMatch[1];
|
|
232209
|
+
let methodStart = functionName2.lastIndexOf(".");
|
|
232210
|
+
if (functionName2[methodStart - 1] === ".") {
|
|
231415
232211
|
methodStart--;
|
|
231416
232212
|
}
|
|
231417
232213
|
if (methodStart > 0) {
|
|
231418
|
-
object =
|
|
231419
|
-
method =
|
|
232214
|
+
object = functionName2.slice(0, methodStart);
|
|
232215
|
+
method = functionName2.slice(methodStart + 1);
|
|
231420
232216
|
const objectEnd = object.indexOf(".Module");
|
|
231421
232217
|
if (objectEnd > 0) {
|
|
231422
|
-
|
|
232218
|
+
functionName2 = functionName2.slice(objectEnd + 1);
|
|
231423
232219
|
object = object.slice(0, objectEnd);
|
|
231424
232220
|
}
|
|
231425
232221
|
}
|
|
@@ -231431,11 +232227,11 @@ function node(getModule) {
|
|
|
231431
232227
|
}
|
|
231432
232228
|
if (method === "<anonymous>") {
|
|
231433
232229
|
methodName3 = void 0;
|
|
231434
|
-
|
|
232230
|
+
functionName2 = void 0;
|
|
231435
232231
|
}
|
|
231436
|
-
if (
|
|
232232
|
+
if (functionName2 === void 0) {
|
|
231437
232233
|
methodName3 = methodName3 || UNKNOWN_FUNCTION;
|
|
231438
|
-
|
|
232234
|
+
functionName2 = typeName2 ? `${typeName2}.${methodName3}` : methodName3;
|
|
231439
232235
|
}
|
|
231440
232236
|
let filename = lineMatch[2]?.startsWith("file://") ? lineMatch[2].slice(7) : lineMatch[2];
|
|
231441
232237
|
const isNative = lineMatch[5] === "native";
|
|
@@ -231448,7 +232244,7 @@ function node(getModule) {
|
|
|
231448
232244
|
return {
|
|
231449
232245
|
filename: filename ? decodeURI(filename) : void 0,
|
|
231450
232246
|
module: getModule ? getModule(filename) : void 0,
|
|
231451
|
-
function:
|
|
232247
|
+
function: functionName2,
|
|
231452
232248
|
lineno: _parseIntOrUndefined(lineMatch[3]),
|
|
231453
232249
|
colno: _parseIntOrUndefined(lineMatch[4]),
|
|
231454
232250
|
in_app: filenameIsInApp(filename || "", isNative),
|
|
@@ -231645,7 +232441,7 @@ function readToolVersion() {
|
|
|
231645
232441
|
if (cachedVersion)
|
|
231646
232442
|
return cachedVersion;
|
|
231647
232443
|
if (true) {
|
|
231648
|
-
cachedVersion = "0.7.0-next.
|
|
232444
|
+
cachedVersion = "0.7.0-next.6";
|
|
231649
232445
|
return cachedVersion;
|
|
231650
232446
|
}
|
|
231651
232447
|
try {
|
|
@@ -234043,7 +234839,7 @@ function findCSharpProjectFiles(repoPath) {
|
|
|
234043
234839
|
continue;
|
|
234044
234840
|
walk20(path63.join(dir, e.name), depth + 1);
|
|
234045
234841
|
} else if (e.isFile()) {
|
|
234046
|
-
if (e.name.endsWith(".sln"))
|
|
234842
|
+
if (e.name.endsWith(".sln") || e.name.endsWith(".slnx"))
|
|
234047
234843
|
solutions.push(path63.join(dir, e.name));
|
|
234048
234844
|
else if (e.name.endsWith(".csproj"))
|
|
234049
234845
|
projects.push(path63.join(dir, e.name));
|
|
@@ -234306,6 +235102,29 @@ async function runViolationPipeline(input) {
|
|
|
234306
235102
|
}
|
|
234307
235103
|
}
|
|
234308
235104
|
}
|
|
235105
|
+
{
|
|
235106
|
+
const detByDomain = /* @__PURE__ */ new Map();
|
|
235107
|
+
for (const v of allCodeViolations) {
|
|
235108
|
+
const d = v.ruleKey.split("/")[0];
|
|
235109
|
+
detByDomain.set(d, (detByDomain.get(d) ?? 0) + 1);
|
|
235110
|
+
}
|
|
235111
|
+
const archDet = serviceViolationResults.length + moduleViolationResults.length + methodViolationResults.length;
|
|
235112
|
+
if (archDet > 0)
|
|
235113
|
+
detByDomain.set("architecture", (detByDomain.get("architecture") ?? 0) + archDet);
|
|
235114
|
+
for (const domain of DOMAIN_ORDER) {
|
|
235115
|
+
const count = detByDomain.get(domain) ?? 0;
|
|
235116
|
+
tracker?.done(domain, count > 0 ? `${count} violations` : "Clean");
|
|
235117
|
+
}
|
|
235118
|
+
}
|
|
235119
|
+
const detViolationCount = allCodeViolations.length;
|
|
235120
|
+
let csharpStepActive = false;
|
|
235121
|
+
const startCsharpStep = (detail) => {
|
|
235122
|
+
if (!csharpStepActive) {
|
|
235123
|
+
tracker?.ensureStep("csharp", "C# semantic analysis");
|
|
235124
|
+
csharpStepActive = true;
|
|
235125
|
+
}
|
|
235126
|
+
tracker?.start("csharp", detail);
|
|
235127
|
+
};
|
|
234309
235128
|
const enabledHostRules = enabledCodeRules.filter((r) => r.engine === "roslyn-host");
|
|
234310
235129
|
if (enabledHostRules.length > 0) {
|
|
234311
235130
|
const csharpFiles = [];
|
|
@@ -234319,6 +235138,7 @@ async function runViolationPipeline(input) {
|
|
|
234319
235138
|
csharpFiles.push({ path: key4, text: fc.content });
|
|
234320
235139
|
}
|
|
234321
235140
|
if (csharpFiles.length > 0) {
|
|
235141
|
+
startCsharpStep(`Analyzing ${csharpFiles.length} C# files\u2026`);
|
|
234322
235142
|
const ruleByKey = new Map(enabledHostRules.map((r) => [r.key, r]));
|
|
234323
235143
|
const hostViolations = await runRoslynHost(csharpFiles, enabledHostRules.map((r) => r.key));
|
|
234324
235144
|
for (const v of hostViolations) {
|
|
@@ -234345,19 +235165,20 @@ async function runViolationPipeline(input) {
|
|
|
234345
235165
|
if (enabledWorkspaceRules.length > 0 && filesToScan.some((f2) => detectLanguage(f2.filePath) === "csharp")) {
|
|
234346
235166
|
const projectFiles = findCSharpProjectFiles(repoPath);
|
|
234347
235167
|
if (projectFiles.length === 0) {
|
|
234348
|
-
log.info("[Pipeline] C# present but no .csproj/.sln found \u2014 project-aware C# rules skipped");
|
|
235168
|
+
log.info("[Pipeline] C# present but no .csproj/.sln/.slnx found \u2014 project-aware C# rules skipped");
|
|
234349
235169
|
} else {
|
|
234350
235170
|
const ruleByKey = new Map(enabledWorkspaceRules.map((r) => [r.key, r]));
|
|
234351
235171
|
const ruleKeys = enabledWorkspaceRules.map((r) => r.key);
|
|
234352
|
-
for (const projectFile of projectFiles) {
|
|
235172
|
+
for (const [pi, projectFile] of projectFiles.entries()) {
|
|
235173
|
+
startCsharpStep(projectFiles.length > 1 ? `Loading project ${pi + 1}/${projectFiles.length}: ${path63.basename(projectFile)}\u2026` : `Loading project ${path63.basename(projectFile)}\u2026`);
|
|
234353
235174
|
let wsViolations;
|
|
234354
235175
|
try {
|
|
234355
235176
|
wsViolations = await runRoslynWorkspace(projectFile, ruleKeys);
|
|
234356
235177
|
} catch (err) {
|
|
235178
|
+
tracker?.error("csharp", `Project load failed: ${path63.basename(projectFile)}`);
|
|
234357
235179
|
if (err instanceof RoslynHostUnavailableError)
|
|
234358
235180
|
throw err;
|
|
234359
|
-
|
|
234360
|
-
continue;
|
|
235181
|
+
throw new Error(`Project-aware C# analysis failed for ${path63.basename(projectFile)}: ${err.message}. Restore the project (\`dotnet restore\`) and ensure its pinned .NET SDK is installed.`);
|
|
234361
235182
|
}
|
|
234362
235183
|
for (const v of wsViolations) {
|
|
234363
235184
|
const rule = ruleByKey.get(v.ruleKey);
|
|
@@ -234389,6 +235210,10 @@ async function runViolationPipeline(input) {
|
|
|
234389
235210
|
}
|
|
234390
235211
|
}
|
|
234391
235212
|
}
|
|
235213
|
+
if (csharpStepActive) {
|
|
235214
|
+
const csharpFindings = allCodeViolations.length - detViolationCount;
|
|
235215
|
+
tracker?.done("csharp", csharpFindings > 0 ? `${csharpFindings} violations` : "Clean");
|
|
235216
|
+
}
|
|
234392
235217
|
log.info(`[Pipeline] Code scan: ${allCodeViolations.length} violations from ${filesToScan.length} files (${enabledCodeRules.length} det rules, ${enabledLlmCodeRules.length} LLM rules)`);
|
|
234393
235218
|
if (enabledCodeRules.some((r) => r.key === "bugs/deterministic/invalid-pyproject-toml")) {
|
|
234394
235219
|
const pyprojectPath = path63.join(repoPath, "pyproject.toml");
|
|
@@ -234434,10 +235259,6 @@ async function runViolationPipeline(input) {
|
|
|
234434
235259
|
}
|
|
234435
235260
|
const totalDet = [...violationsByDomain.values()].reduce((a, b) => a + b, 0);
|
|
234436
235261
|
log.info(`[Pipeline] Totals: ${DOMAIN_ORDER.filter((d) => violationsByDomain.has(d)).map((d) => `${d}=${violationsByDomain.get(d)}`).join(", ")} (${totalDet})`);
|
|
234437
|
-
for (const domain of DOMAIN_ORDER) {
|
|
234438
|
-
const count = violationsByDomain.get(domain) ?? 0;
|
|
234439
|
-
tracker?.done(domain, count > 0 ? `${count} violations` : "Clean");
|
|
234440
|
-
}
|
|
234441
235262
|
onProgress?.({ step: "analyzing", percent: 84, detail: "Code checks done" });
|
|
234442
235263
|
const prevLlmCodeByFile = /* @__PURE__ */ new Map();
|
|
234443
235264
|
for (const cv of previousActiveCodeViolations) {
|
|
@@ -239312,7 +240133,41 @@ var guard_default = router13;
|
|
|
239312
240133
|
var import_express14 = __toESM(require_express2(), 1);
|
|
239313
240134
|
|
|
239314
240135
|
// packages/core/dist/commands/guard-in-process.js
|
|
240136
|
+
init_dist();
|
|
239315
240137
|
init_transport();
|
|
240138
|
+
var OpenConflictsError = class extends Error {
|
|
240139
|
+
conflicts;
|
|
240140
|
+
constructor(conflicts2) {
|
|
240141
|
+
super(formatOpenConflictsMessage(conflicts2));
|
|
240142
|
+
this.conflicts = conflicts2;
|
|
240143
|
+
this.name = "OpenConflictsError";
|
|
240144
|
+
}
|
|
240145
|
+
};
|
|
240146
|
+
function formatOpenConflictsMessage(conflicts2) {
|
|
240147
|
+
const lines = [
|
|
240148
|
+
`${conflicts2.length} open spec conflict${conflicts2.length === 1 ? "" : "s"} must be resolved before guard generate.`,
|
|
240149
|
+
"Extracting both sides of an unresolved overlap births a red finding that is really the dispute.",
|
|
240150
|
+
""
|
|
240151
|
+
];
|
|
240152
|
+
for (const c of conflicts2) {
|
|
240153
|
+
lines.push(` ${c.area}`);
|
|
240154
|
+
lines.push(` ${c.a} \u2194 ${c.b}`);
|
|
240155
|
+
if (c.note)
|
|
240156
|
+
lines.push(` ${c.note}`);
|
|
240157
|
+
}
|
|
240158
|
+
lines.push("");
|
|
240159
|
+
lines.push("Resolve them with `truecourse spec conflicts list` (or the dashboard Conflicts group), then re-run `truecourse guard generate`.");
|
|
240160
|
+
return lines.join("\n");
|
|
240161
|
+
}
|
|
240162
|
+
async function assertNoOpenConflicts(repoRoot) {
|
|
240163
|
+
const corpus = await getCorpus(repoRoot);
|
|
240164
|
+
if (!corpus)
|
|
240165
|
+
return;
|
|
240166
|
+
const decisions = await getDecisions(repoRoot);
|
|
240167
|
+
const open = openConflicts(corpus, decisions);
|
|
240168
|
+
if (open.length > 0)
|
|
240169
|
+
throw new OpenConflictsError(open);
|
|
240170
|
+
}
|
|
239316
240171
|
var GUARD_GENERATE_STEPS = [
|
|
239317
240172
|
{ key: "index", label: "Indexing sections" },
|
|
239318
240173
|
{ key: "extract", label: "Extracting claims" },
|
|
@@ -239348,6 +240203,7 @@ function resolveTransport2(options) {
|
|
|
239348
240203
|
}
|
|
239349
240204
|
async function guardGenerateInProcess(repoRoot, options = {}) {
|
|
239350
240205
|
const { tracker } = options;
|
|
240206
|
+
await assertNoOpenConflicts(repoRoot);
|
|
239351
240207
|
if (options.onLlmEstimate) {
|
|
239352
240208
|
const prices = await getModelPrices();
|
|
239353
240209
|
const estimate = await estimateGuardTokens(repoRoot, prices);
|
|
@@ -239596,6 +240452,10 @@ router14.post("/:id/guard/generate", async (req, res, next) => {
|
|
|
239596
240452
|
res.json({ cancelled: true });
|
|
239597
240453
|
return;
|
|
239598
240454
|
}
|
|
240455
|
+
if (e instanceof OpenConflictsError) {
|
|
240456
|
+
res.status(422).json({ error: e.message });
|
|
240457
|
+
return;
|
|
240458
|
+
}
|
|
239599
240459
|
emitSpecProgress(repoId, { step: "error", percent: 100, detail: e.message });
|
|
239600
240460
|
next(e);
|
|
239601
240461
|
} finally {
|