circle-ir 4.9.29 → 4.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analysis/config-loader.d.ts.map +1 -1
- package/dist/analysis/config-loader.js +23 -0
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +39 -0
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analysis/passes/param-source-flow-gate-pass.d.ts +53 -0
- package/dist/analysis/passes/param-source-flow-gate-pass.d.ts.map +1 -0
- package/dist/analysis/passes/param-source-flow-gate-pass.js +144 -0
- package/dist/analysis/passes/param-source-flow-gate-pass.js.map +1 -0
- package/dist/analysis/passes/scan-secrets-pass.d.ts +3 -3
- package/dist/analysis/passes/scan-secrets-pass.d.ts.map +1 -1
- package/dist/analysis/passes/scan-secrets-pass.js +31 -3
- package/dist/analysis/passes/scan-secrets-pass.js.map +1 -1
- package/dist/analyzer.d.ts +22 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +10 -1
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +233 -11
- package/dist/core/circle-ir-core.cjs +94 -3
- package/dist/core/circle-ir-core.js +94 -3
- package/dist/core/extractors/types.d.ts +28 -1
- package/dist/core/extractors/types.d.ts.map +1 -1
- package/dist/core/extractors/types.js +86 -3
- package/dist/core/extractors/types.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/navigation/index.d.ts +8 -0
- package/dist/navigation/index.d.ts.map +1 -0
- package/dist/navigation/index.js +6 -0
- package/dist/navigation/index.js.map +1 -0
- package/dist/navigation/navigation-index.d.ts +208 -0
- package/dist/navigation/navigation-index.d.ts.map +1 -0
- package/dist/navigation/navigation-index.js +968 -0
- package/dist/navigation/navigation-index.js.map +1 -0
- package/dist/navigation/types.d.ts +160 -0
- package/dist/navigation/types.d.ts.map +1 -0
- package/dist/navigation/types.js +17 -0
- package/dist/navigation/types.js.map +1 -0
- package/dist/types/index.d.ts +19 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -4458,7 +4458,7 @@ function detectLanguage(tree) {
|
|
|
4458
4458
|
if (pythonScore > jsScore && pythonScore > javaScore) return "python";
|
|
4459
4459
|
return jsScore > javaScore ? "javascript" : "java";
|
|
4460
4460
|
}
|
|
4461
|
-
function extractTypes(tree, cache, language) {
|
|
4461
|
+
function extractTypes(tree, cache, language, options) {
|
|
4462
4462
|
const effectiveLanguage = language ?? detectLanguage(tree);
|
|
4463
4463
|
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript" || effectiveLanguage === "tsx";
|
|
4464
4464
|
const isPython = effectiveLanguage === "python";
|
|
@@ -4478,7 +4478,7 @@ function extractTypes(tree, cache, language) {
|
|
|
4478
4478
|
if (effectiveLanguage === "csharp") {
|
|
4479
4479
|
return extractCSharpTypes(tree, cache);
|
|
4480
4480
|
}
|
|
4481
|
-
return extractJavaTypes(tree, cache);
|
|
4481
|
+
return extractJavaTypes(tree, cache, options);
|
|
4482
4482
|
}
|
|
4483
4483
|
function csharpMemberNodes(body2) {
|
|
4484
4484
|
if (!body2) return [];
|
|
@@ -4619,7 +4619,7 @@ function extractCSharpModifiers(node) {
|
|
|
4619
4619
|
}
|
|
4620
4620
|
return mods;
|
|
4621
4621
|
}
|
|
4622
|
-
function extractJavaTypes(tree, cache) {
|
|
4622
|
+
function extractJavaTypes(tree, cache, options) {
|
|
4623
4623
|
const types = [];
|
|
4624
4624
|
const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
|
|
4625
4625
|
for (const cls of classes) {
|
|
@@ -4633,8 +4633,76 @@ function extractJavaTypes(tree, cache) {
|
|
|
4633
4633
|
for (const enumDecl of enums) {
|
|
4634
4634
|
types.push(extractEnumInfo(enumDecl));
|
|
4635
4635
|
}
|
|
4636
|
+
if (!options?.navigationTypes) return types;
|
|
4637
|
+
for (const rec of getNodesFromCache(tree.rootNode, "record_declaration", cache)) {
|
|
4638
|
+
const info2 = extractClassInfo(rec);
|
|
4639
|
+
info2.is_record = true;
|
|
4640
|
+
for (const comp of recordComponents(rec)) {
|
|
4641
|
+
if (!info2.fields.some((f) => f.name === comp.name)) {
|
|
4642
|
+
info2.fields.push({
|
|
4643
|
+
name: comp.name,
|
|
4644
|
+
type: comp.type,
|
|
4645
|
+
modifiers: ["private", "final"],
|
|
4646
|
+
annotations: []
|
|
4647
|
+
});
|
|
4648
|
+
}
|
|
4649
|
+
if (!info2.methods.some((m) => m.name === comp.name)) {
|
|
4650
|
+
info2.methods.push({
|
|
4651
|
+
name: comp.name,
|
|
4652
|
+
return_type: comp.type,
|
|
4653
|
+
parameters: [],
|
|
4654
|
+
annotations: [],
|
|
4655
|
+
modifiers: ["public"],
|
|
4656
|
+
start_line: rec.startPosition.row + 1,
|
|
4657
|
+
end_line: rec.startPosition.row + 1
|
|
4658
|
+
});
|
|
4659
|
+
}
|
|
4660
|
+
}
|
|
4661
|
+
types.push(info2);
|
|
4662
|
+
}
|
|
4663
|
+
const declNodes = [
|
|
4664
|
+
...classes,
|
|
4665
|
+
...interfaces,
|
|
4666
|
+
...enums,
|
|
4667
|
+
...getNodesFromCache(tree.rootNode, "record_declaration", cache)
|
|
4668
|
+
];
|
|
4669
|
+
for (const t of types) {
|
|
4670
|
+
const node = declNodes.find(
|
|
4671
|
+
(n) => n.startPosition.row + 1 === t.start_line && getIdentifier(n, "name") === t.name
|
|
4672
|
+
);
|
|
4673
|
+
const path = node ? enclosingTypePath(node) : [];
|
|
4674
|
+
if (path.length > 0) t.enclosing_type = path.join(".");
|
|
4675
|
+
}
|
|
4636
4676
|
return types;
|
|
4637
4677
|
}
|
|
4678
|
+
function recordComponents(node) {
|
|
4679
|
+
const out2 = [];
|
|
4680
|
+
const params = node.childForFieldName("parameters");
|
|
4681
|
+
if (!params) return out2;
|
|
4682
|
+
for (let i2 = 0; i2 < params.childCount; i2++) {
|
|
4683
|
+
const c = params.child(i2);
|
|
4684
|
+
if (!c || c.type !== "formal_parameter") continue;
|
|
4685
|
+
const name2 = c.childForFieldName("name");
|
|
4686
|
+
const type = c.childForFieldName("type");
|
|
4687
|
+
if (name2) out2.push({ name: getNodeText(name2), type: type ? getNodeText(type) : null });
|
|
4688
|
+
}
|
|
4689
|
+
return out2;
|
|
4690
|
+
}
|
|
4691
|
+
function enclosingTypePath(node) {
|
|
4692
|
+
const DECLS = /* @__PURE__ */ new Set([
|
|
4693
|
+
"class_declaration",
|
|
4694
|
+
"interface_declaration",
|
|
4695
|
+
"enum_declaration",
|
|
4696
|
+
"record_declaration"
|
|
4697
|
+
]);
|
|
4698
|
+
const path = [];
|
|
4699
|
+
for (let p = node.parent; p; p = p.parent) {
|
|
4700
|
+
if (!DECLS.has(p.type)) continue;
|
|
4701
|
+
const n = getIdentifier(p, "name");
|
|
4702
|
+
if (n) path.unshift(n);
|
|
4703
|
+
}
|
|
4704
|
+
return path;
|
|
4705
|
+
}
|
|
4638
4706
|
function extractJavaScriptTypes(tree, cache) {
|
|
4639
4707
|
const types = [];
|
|
4640
4708
|
const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
|
|
@@ -13417,6 +13485,29 @@ var DEFAULT_SINKS = [
|
|
|
13417
13485
|
{ method: "deserialize", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
|
|
13418
13486
|
{ method: "from_str", class: "toml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13419
13487
|
{ method: "from_str", class: "ron", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13488
|
+
// cognium-dev#541 — the crates the removed classless rows were silently
|
|
13489
|
+
// covering. #484 replaced a classless `from_str` / `from_slice` with
|
|
13490
|
+
// class-scoped rows, but only for the four formats that already had entries;
|
|
13491
|
+
// every other serde data format lost its sink, and `rmp_serde::from_slice`
|
|
13492
|
+
// on bytes read straight off a `TcpStream` stopped being reported. Each row
|
|
13493
|
+
// below is a serde *data format* entry point — a function whose whole job is
|
|
13494
|
+
// to build a typed value out of untrusted bytes or text.
|
|
13495
|
+
{ method: "from_slice", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13496
|
+
{ method: "from_read", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13497
|
+
{ method: "from_read_ref", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13498
|
+
{ method: "from_slice", class: "serde_yaml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13499
|
+
{ method: "from_slice", class: "toml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13500
|
+
{ method: "from_reader", class: "ron", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13501
|
+
{ method: "from_bytes", class: "postcard", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13502
|
+
{ method: "from_slice", class: "serde_cbor", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13503
|
+
{ method: "from_reader", class: "serde_cbor", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13504
|
+
{ method: "from_reader", class: "ciborium", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13505
|
+
{ method: "from_str", class: "serde_xml_rs", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13506
|
+
{ method: "from_reader", class: "serde_xml_rs", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13507
|
+
{ method: "from_str", class: "quick_xml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13508
|
+
{ method: "from_reader", class: "quick_xml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13509
|
+
{ method: "from_slice", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
|
|
13510
|
+
{ method: "deserialize_from", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
|
|
13420
13511
|
// #294/#484: no classless `from_str` / `from_slice` deserialization sink.
|
|
13421
13512
|
// Those rows matched EVERY `T::from_str` / `from_slice` — `u32::from_str`,
|
|
13422
13513
|
// `Url::from_str`, `PublicKey::from_slice` — i.e. ordinary `FromStr` / byte
|
|
@@ -4392,7 +4392,7 @@ function detectLanguage(tree) {
|
|
|
4392
4392
|
if (pythonScore > jsScore && pythonScore > javaScore) return "python";
|
|
4393
4393
|
return jsScore > javaScore ? "javascript" : "java";
|
|
4394
4394
|
}
|
|
4395
|
-
function extractTypes(tree, cache, language) {
|
|
4395
|
+
function extractTypes(tree, cache, language, options) {
|
|
4396
4396
|
const effectiveLanguage = language ?? detectLanguage(tree);
|
|
4397
4397
|
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript" || effectiveLanguage === "tsx";
|
|
4398
4398
|
const isPython = effectiveLanguage === "python";
|
|
@@ -4412,7 +4412,7 @@ function extractTypes(tree, cache, language) {
|
|
|
4412
4412
|
if (effectiveLanguage === "csharp") {
|
|
4413
4413
|
return extractCSharpTypes(tree, cache);
|
|
4414
4414
|
}
|
|
4415
|
-
return extractJavaTypes(tree, cache);
|
|
4415
|
+
return extractJavaTypes(tree, cache, options);
|
|
4416
4416
|
}
|
|
4417
4417
|
function csharpMemberNodes(body2) {
|
|
4418
4418
|
if (!body2) return [];
|
|
@@ -4553,7 +4553,7 @@ function extractCSharpModifiers(node) {
|
|
|
4553
4553
|
}
|
|
4554
4554
|
return mods;
|
|
4555
4555
|
}
|
|
4556
|
-
function extractJavaTypes(tree, cache) {
|
|
4556
|
+
function extractJavaTypes(tree, cache, options) {
|
|
4557
4557
|
const types = [];
|
|
4558
4558
|
const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
|
|
4559
4559
|
for (const cls of classes) {
|
|
@@ -4567,8 +4567,76 @@ function extractJavaTypes(tree, cache) {
|
|
|
4567
4567
|
for (const enumDecl of enums) {
|
|
4568
4568
|
types.push(extractEnumInfo(enumDecl));
|
|
4569
4569
|
}
|
|
4570
|
+
if (!options?.navigationTypes) return types;
|
|
4571
|
+
for (const rec of getNodesFromCache(tree.rootNode, "record_declaration", cache)) {
|
|
4572
|
+
const info2 = extractClassInfo(rec);
|
|
4573
|
+
info2.is_record = true;
|
|
4574
|
+
for (const comp of recordComponents(rec)) {
|
|
4575
|
+
if (!info2.fields.some((f) => f.name === comp.name)) {
|
|
4576
|
+
info2.fields.push({
|
|
4577
|
+
name: comp.name,
|
|
4578
|
+
type: comp.type,
|
|
4579
|
+
modifiers: ["private", "final"],
|
|
4580
|
+
annotations: []
|
|
4581
|
+
});
|
|
4582
|
+
}
|
|
4583
|
+
if (!info2.methods.some((m) => m.name === comp.name)) {
|
|
4584
|
+
info2.methods.push({
|
|
4585
|
+
name: comp.name,
|
|
4586
|
+
return_type: comp.type,
|
|
4587
|
+
parameters: [],
|
|
4588
|
+
annotations: [],
|
|
4589
|
+
modifiers: ["public"],
|
|
4590
|
+
start_line: rec.startPosition.row + 1,
|
|
4591
|
+
end_line: rec.startPosition.row + 1
|
|
4592
|
+
});
|
|
4593
|
+
}
|
|
4594
|
+
}
|
|
4595
|
+
types.push(info2);
|
|
4596
|
+
}
|
|
4597
|
+
const declNodes = [
|
|
4598
|
+
...classes,
|
|
4599
|
+
...interfaces,
|
|
4600
|
+
...enums,
|
|
4601
|
+
...getNodesFromCache(tree.rootNode, "record_declaration", cache)
|
|
4602
|
+
];
|
|
4603
|
+
for (const t of types) {
|
|
4604
|
+
const node = declNodes.find(
|
|
4605
|
+
(n) => n.startPosition.row + 1 === t.start_line && getIdentifier(n, "name") === t.name
|
|
4606
|
+
);
|
|
4607
|
+
const path = node ? enclosingTypePath(node) : [];
|
|
4608
|
+
if (path.length > 0) t.enclosing_type = path.join(".");
|
|
4609
|
+
}
|
|
4570
4610
|
return types;
|
|
4571
4611
|
}
|
|
4612
|
+
function recordComponents(node) {
|
|
4613
|
+
const out2 = [];
|
|
4614
|
+
const params = node.childForFieldName("parameters");
|
|
4615
|
+
if (!params) return out2;
|
|
4616
|
+
for (let i2 = 0; i2 < params.childCount; i2++) {
|
|
4617
|
+
const c = params.child(i2);
|
|
4618
|
+
if (!c || c.type !== "formal_parameter") continue;
|
|
4619
|
+
const name2 = c.childForFieldName("name");
|
|
4620
|
+
const type = c.childForFieldName("type");
|
|
4621
|
+
if (name2) out2.push({ name: getNodeText(name2), type: type ? getNodeText(type) : null });
|
|
4622
|
+
}
|
|
4623
|
+
return out2;
|
|
4624
|
+
}
|
|
4625
|
+
function enclosingTypePath(node) {
|
|
4626
|
+
const DECLS = /* @__PURE__ */ new Set([
|
|
4627
|
+
"class_declaration",
|
|
4628
|
+
"interface_declaration",
|
|
4629
|
+
"enum_declaration",
|
|
4630
|
+
"record_declaration"
|
|
4631
|
+
]);
|
|
4632
|
+
const path = [];
|
|
4633
|
+
for (let p = node.parent; p; p = p.parent) {
|
|
4634
|
+
if (!DECLS.has(p.type)) continue;
|
|
4635
|
+
const n = getIdentifier(p, "name");
|
|
4636
|
+
if (n) path.unshift(n);
|
|
4637
|
+
}
|
|
4638
|
+
return path;
|
|
4639
|
+
}
|
|
4572
4640
|
function extractJavaScriptTypes(tree, cache) {
|
|
4573
4641
|
const types = [];
|
|
4574
4642
|
const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
|
|
@@ -13351,6 +13419,29 @@ var DEFAULT_SINKS = [
|
|
|
13351
13419
|
{ method: "deserialize", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
|
|
13352
13420
|
{ method: "from_str", class: "toml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13353
13421
|
{ method: "from_str", class: "ron", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13422
|
+
// cognium-dev#541 — the crates the removed classless rows were silently
|
|
13423
|
+
// covering. #484 replaced a classless `from_str` / `from_slice` with
|
|
13424
|
+
// class-scoped rows, but only for the four formats that already had entries;
|
|
13425
|
+
// every other serde data format lost its sink, and `rmp_serde::from_slice`
|
|
13426
|
+
// on bytes read straight off a `TcpStream` stopped being reported. Each row
|
|
13427
|
+
// below is a serde *data format* entry point — a function whose whole job is
|
|
13428
|
+
// to build a typed value out of untrusted bytes or text.
|
|
13429
|
+
{ method: "from_slice", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13430
|
+
{ method: "from_read", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13431
|
+
{ method: "from_read_ref", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13432
|
+
{ method: "from_slice", class: "serde_yaml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13433
|
+
{ method: "from_slice", class: "toml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13434
|
+
{ method: "from_reader", class: "ron", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13435
|
+
{ method: "from_bytes", class: "postcard", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13436
|
+
{ method: "from_slice", class: "serde_cbor", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13437
|
+
{ method: "from_reader", class: "serde_cbor", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13438
|
+
{ method: "from_reader", class: "ciborium", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13439
|
+
{ method: "from_str", class: "serde_xml_rs", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13440
|
+
{ method: "from_reader", class: "serde_xml_rs", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13441
|
+
{ method: "from_str", class: "quick_xml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13442
|
+
{ method: "from_reader", class: "quick_xml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
13443
|
+
{ method: "from_slice", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
|
|
13444
|
+
{ method: "deserialize_from", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
|
|
13354
13445
|
// #294/#484: no classless `from_str` / `from_slice` deserialization sink.
|
|
13355
13446
|
// Those rows matched EVERY `T::from_str` / `from_slice` — `u32::from_str`,
|
|
13356
13447
|
// `Url::from_str`, `PublicKey::from_slice` — i.e. ordinary `FromStr` / byte
|
|
@@ -4,8 +4,35 @@
|
|
|
4
4
|
import type { Tree } from 'web-tree-sitter';
|
|
5
5
|
import type { TypeInfo, SupportedLanguage } from '../../types/index.js';
|
|
6
6
|
import { type NodeCache } from '../parser.js';
|
|
7
|
+
/**
|
|
8
|
+
* Extra type information the navigation API needs and no existing consumer
|
|
9
|
+
* expects. Every field it adds is optional and absent unless asked for, so
|
|
10
|
+
* `extractTypes(tree, cache, language)` returns exactly what it always did.
|
|
11
|
+
*
|
|
12
|
+
* Two things are missing from the default Java extraction, both found by
|
|
13
|
+
* auditing the call sites the resolver cannot bind:
|
|
14
|
+
*
|
|
15
|
+
* - **A `record` yields no type at all.** `extractJavaTypes` walks
|
|
16
|
+
* `class_declaration`, `interface_declaration` and `enum_declaration`;
|
|
17
|
+
* `record_declaration` is not among them, so a record is invisible — not
|
|
18
|
+
* in `ir.types`, and its body's calls are not in `ir.calls` either.
|
|
19
|
+
* Records are ordinary Java 17+ code and cost 182 of 455 bindable misses
|
|
20
|
+
* in the 405-file sample, the single largest cause.
|
|
21
|
+
* - **Nesting is lost.** `Outer.Inner` is emitted as a type named `Inner`,
|
|
22
|
+
* so its fully-qualified name collides with a top-level `Inner` and an
|
|
23
|
+
* `import static app.Outer.Inner.make` cannot be matched to it.
|
|
24
|
+
*
|
|
25
|
+
* Both are fixed here rather than in the default path because `ir.types` is
|
|
26
|
+
* read by the SAST passes and by the method-scoping gate: adding a type, or
|
|
27
|
+
* changing one's identity, changes their results and belongs behind the SAST
|
|
28
|
+
* corpora gates, not in a navigation change.
|
|
29
|
+
*/
|
|
30
|
+
export interface TypeExtractionOptions {
|
|
31
|
+
/** Emit `record_declaration` types, and set `enclosing_type` on nested types. */
|
|
32
|
+
navigationTypes?: boolean;
|
|
33
|
+
}
|
|
7
34
|
/**
|
|
8
35
|
* Extract all type definitions from the tree.
|
|
9
36
|
*/
|
|
10
|
-
export declare function extractTypes(tree: Tree, cache?: NodeCache, language?: SupportedLanguage): TypeInfo[];
|
|
37
|
+
export declare function extractTypes(tree: Tree, cache?: NodeCache, language?: SupportedLanguage, options?: TypeExtractionOptions): TypeInfo[];
|
|
11
38
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/extractors/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAQ,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAwC,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9G,OAAO,EAA6C,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAgDzF;;GAEG;AACH,wBAAgB,YAAY,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/extractors/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAQ,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAwC,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9G,OAAO,EAA6C,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAgDzF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,IAAI,EACV,KAAK,CAAC,EAAE,SAAS,EACjB,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,OAAO,CAAC,EAAE,qBAAqB,GAC9B,QAAQ,EAAE,CAsBZ"}
|
|
@@ -49,7 +49,7 @@ function detectLanguage(tree) {
|
|
|
49
49
|
/**
|
|
50
50
|
* Extract all type definitions from the tree.
|
|
51
51
|
*/
|
|
52
|
-
export function extractTypes(tree, cache, language) {
|
|
52
|
+
export function extractTypes(tree, cache, language, options) {
|
|
53
53
|
const effectiveLanguage = language ?? detectLanguage(tree);
|
|
54
54
|
const isJavaScript = effectiveLanguage === 'javascript' || effectiveLanguage === 'typescript' || effectiveLanguage === 'tsx';
|
|
55
55
|
const isPython = effectiveLanguage === 'python';
|
|
@@ -69,7 +69,7 @@ export function extractTypes(tree, cache, language) {
|
|
|
69
69
|
if (effectiveLanguage === 'csharp') {
|
|
70
70
|
return extractCSharpTypes(tree, cache);
|
|
71
71
|
}
|
|
72
|
-
return extractJavaTypes(tree, cache);
|
|
72
|
+
return extractJavaTypes(tree, cache, options);
|
|
73
73
|
}
|
|
74
74
|
/**
|
|
75
75
|
* C# type extraction (Phase-0 spike — cognium-dev C#/.NET epic).
|
|
@@ -278,7 +278,7 @@ function extractCSharpModifiers(node) {
|
|
|
278
278
|
/**
|
|
279
279
|
* Extract Java types.
|
|
280
280
|
*/
|
|
281
|
-
function extractJavaTypes(tree, cache) {
|
|
281
|
+
function extractJavaTypes(tree, cache, options) {
|
|
282
282
|
const types = [];
|
|
283
283
|
// Extract classes
|
|
284
284
|
const classes = getNodesFromCache(tree.rootNode, 'class_declaration', cache);
|
|
@@ -295,8 +295,91 @@ function extractJavaTypes(tree, cache) {
|
|
|
295
295
|
for (const enumDecl of enums) {
|
|
296
296
|
types.push(extractEnumInfo(enumDecl));
|
|
297
297
|
}
|
|
298
|
+
if (!options?.navigationTypes)
|
|
299
|
+
return types;
|
|
300
|
+
// Records, which the default path omits entirely. A record's body holds
|
|
301
|
+
// ordinary method declarations, so `extractClassInfo` reads it unchanged;
|
|
302
|
+
// only the node type and the `is_record` marker differ.
|
|
303
|
+
for (const rec of getNodesFromCache(tree.rootNode, 'record_declaration', cache)) {
|
|
304
|
+
const info = extractClassInfo(rec);
|
|
305
|
+
info.is_record = true;
|
|
306
|
+
// A record's components are its fields and its accessors. Both are
|
|
307
|
+
// declared by the header, not written in the body, so neither
|
|
308
|
+
// `extractFields` nor `extractMethods` can see them.
|
|
309
|
+
for (const comp of recordComponents(rec)) {
|
|
310
|
+
if (!info.fields.some(f => f.name === comp.name)) {
|
|
311
|
+
info.fields.push({
|
|
312
|
+
name: comp.name,
|
|
313
|
+
type: comp.type,
|
|
314
|
+
modifiers: ['private', 'final'],
|
|
315
|
+
annotations: [],
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
if (!info.methods.some(m => m.name === comp.name)) {
|
|
319
|
+
info.methods.push({
|
|
320
|
+
name: comp.name,
|
|
321
|
+
return_type: comp.type,
|
|
322
|
+
parameters: [],
|
|
323
|
+
annotations: [],
|
|
324
|
+
modifiers: ['public'],
|
|
325
|
+
start_line: rec.startPosition.row + 1,
|
|
326
|
+
end_line: rec.startPosition.row + 1,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
types.push(info);
|
|
331
|
+
}
|
|
332
|
+
// Nesting, which the default path flattens. Walking every emitted type's
|
|
333
|
+
// ancestors is cheaper than threading the path through five extractors.
|
|
334
|
+
const declNodes = [
|
|
335
|
+
...classes,
|
|
336
|
+
...interfaces,
|
|
337
|
+
...enums,
|
|
338
|
+
...getNodesFromCache(tree.rootNode, 'record_declaration', cache),
|
|
339
|
+
];
|
|
340
|
+
for (const t of types) {
|
|
341
|
+
const node = declNodes.find(n => n.startPosition.row + 1 === t.start_line && getIdentifier(n, 'name') === t.name);
|
|
342
|
+
const path = node ? enclosingTypePath(node) : [];
|
|
343
|
+
if (path.length > 0)
|
|
344
|
+
t.enclosing_type = path.join('.');
|
|
345
|
+
}
|
|
298
346
|
return types;
|
|
299
347
|
}
|
|
348
|
+
/** The `(String host, int port)` header of a `record_declaration`. */
|
|
349
|
+
function recordComponents(node) {
|
|
350
|
+
const out = [];
|
|
351
|
+
const params = node.childForFieldName('parameters');
|
|
352
|
+
if (!params)
|
|
353
|
+
return out;
|
|
354
|
+
for (let i = 0; i < params.childCount; i++) {
|
|
355
|
+
const c = params.child(i);
|
|
356
|
+
if (!c || c.type !== 'formal_parameter')
|
|
357
|
+
continue;
|
|
358
|
+
const name = c.childForFieldName('name');
|
|
359
|
+
const type = c.childForFieldName('type');
|
|
360
|
+
if (name)
|
|
361
|
+
out.push({ name: getNodeText(name), type: type ? getNodeText(type) : null });
|
|
362
|
+
}
|
|
363
|
+
return out;
|
|
364
|
+
}
|
|
365
|
+
/** The names of the types a declaration sits inside, outermost first. */
|
|
366
|
+
function enclosingTypePath(node) {
|
|
367
|
+
const DECLS = new Set([
|
|
368
|
+
'class_declaration',
|
|
369
|
+
'interface_declaration',
|
|
370
|
+
'enum_declaration',
|
|
371
|
+
'record_declaration',
|
|
372
|
+
]);
|
|
373
|
+
const path = [];
|
|
374
|
+
for (let p = node.parent; p; p = p.parent) {
|
|
375
|
+
if (!DECLS.has(p.type))
|
|
376
|
+
continue;
|
|
377
|
+
const n = getIdentifier(p, 'name');
|
|
378
|
+
if (n)
|
|
379
|
+
path.unshift(n);
|
|
380
|
+
}
|
|
381
|
+
return path;
|
|
382
|
+
}
|
|
300
383
|
/**
|
|
301
384
|
* Extract JavaScript/TypeScript types.
|
|
302
385
|
*/
|