|
| 1 | +// Anchor helpers — Plan §1.2 follow-on. |
| 2 | +// |
| 3 | +// Many regex detectors emit cross-file "imports" / "depends_on" edges |
| 4 | +// using the source file path and the imported name as endpoints. Both |
| 5 | +// endpoints were free-form strings with no matching CodeNode, so every |
| 6 | +// such edge got dropped at GraphBuilder.Snapshot's phantom filter. |
| 7 | +// |
| 8 | +// EnsureFileAnchor and EnsureExternalAnchor materialize anchor nodes so |
| 9 | +// the edges survive. The GraphBuilder dedup map collapses the per-file |
| 10 | +// and per-external nodes across files at zero extra cost (every Python |
| 11 | +// file importing "requests" gets one shared py:external:requests node). |
| 12 | +package base |
| 13 | + |
| 14 | +import ( |
| 15 | + "github.com/randomcodespace/codeiq/go/internal/detector" |
| 16 | + "github.com/randomcodespace/codeiq/go/internal/model" |
| 17 | +) |
| 18 | + |
| 19 | +// EnsureFileAnchor returns the canonical ID of the file-as-module anchor |
| 20 | +// node for ctx.FilePath and appends the node to nodes once. Caller must |
| 21 | +// pass the same `seen` map across invocations within a single detector |
| 22 | +// run (or nil for one-shot calls). |
| 23 | +// |
| 24 | +// langPrefix scopes the anchor namespace ("py" for Python, "ts" for |
| 25 | +// TypeScript, etc.) so cross-language detectors don't collide on the |
| 26 | +// same path. |
| 27 | +// |
| 28 | +// Detector source/confidence are stamped onto the anchor — pick a |
| 29 | +// confidence that's at-or-below the actual emission detector so the |
| 30 | +// merge rule (higher wins) doesn't accidentally demote a high-confidence |
| 31 | +// emission later. |
| 32 | +func EnsureFileAnchor(ctx *detector.Context, langPrefix, detectorName string, conf model.Confidence, nodes *[]*model.CodeNode, seen map[string]bool) string { |
| 33 | + id := langPrefix + ":file:" + ctx.FilePath |
| 34 | + if seen != nil && seen[id] { |
| 35 | + return id |
| 36 | + } |
| 37 | + if seen != nil { |
| 38 | + seen[id] = true |
| 39 | + } |
| 40 | + n := model.NewCodeNode(id, model.NodeModule, ctx.FilePath) |
| 41 | + n.FilePath = ctx.FilePath |
| 42 | + n.Source = detectorName |
| 43 | + n.Confidence = conf |
| 44 | + n.Properties["module_type"] = langPrefix + "_file" |
| 45 | + *nodes = append(*nodes, n) |
| 46 | + return id |
| 47 | +} |
| 48 | + |
| 49 | +// EnsureExternalAnchor returns the canonical ID of an external module / |
| 50 | +// package / image target and appends it to nodes once per unique name. |
| 51 | +// idPrefix scopes the namespace ("py:external", "rust:external", |
| 52 | +// "docker:image", etc.). |
| 53 | +func EnsureExternalAnchor(name, idPrefix, detectorName string, conf model.Confidence, nodes *[]*model.CodeNode, seen map[string]bool) string { |
| 54 | + id := idPrefix + ":" + name |
| 55 | + if seen != nil && seen[id] { |
| 56 | + return id |
| 57 | + } |
| 58 | + if seen != nil { |
| 59 | + seen[id] = true |
| 60 | + } |
| 61 | + n := model.NewCodeNode(id, model.NodeExternal, name) |
| 62 | + n.Source = detectorName |
| 63 | + n.Confidence = conf |
| 64 | + n.Properties["module"] = name |
| 65 | + *nodes = append(*nodes, n) |
| 66 | + return id |
| 67 | +} |
0 commit comments