Skip to content

Commit 72e97f1

Browse files
Merge pull request #13 from chinmay-sawant/bug-11-fix
Refactor relation building to use indexed variant for parallel processing
2 parents 97d06ad + 7587cb4 commit 72e97f1

File tree

2 files changed

+74
-43
lines changed

2 files changed

+74
-43
lines changed

cmd/analyzer/relations.go

Lines changed: 72 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,42 @@ type OutRelation struct {
2424
// If includeExternal is true, all calls are included in the relations, including external module functions.
2525
// We still defensively exclude relations that have zero called entries to preserve prior semantics unless includeExternal is true.
2626
func BuildRelations(functions []FunctionInfo, includeExternal bool) []OutRelation {
27+
// Build indices from the full set provided (sequential usage builds on the same set)
28+
funcMap, suffixMap := buildGlobalIndices(functions)
29+
30+
out := make([]OutRelation, 0, len(functions))
31+
for _, f := range functions {
32+
if len(f.Calls) == 0 && !includeExternal {
33+
continue // skip functions with no user-defined calls (previous behaviour)
34+
}
35+
rel := buildRelationForFunction(f, includeExternal, funcMap, suffixMap)
36+
if len(rel.Called) > 0 || includeExternal {
37+
// Include the relation if it has calls OR if we're including all functions
38+
out = append(out, rel)
39+
}
40+
}
41+
return out
42+
}
43+
44+
// BuildRelationsIndexed builds relations for a subset of functions using indices built from a full set.
45+
// Useful for parallel processing where each worker processes a chunk but needs global resolution.
46+
func BuildRelationsIndexed(subset []FunctionInfo, includeExternal bool, fullIndex []FunctionInfo) []OutRelation {
47+
funcMap, suffixMap := buildGlobalIndices(fullIndex)
48+
out := make([]OutRelation, 0, len(subset))
49+
for _, f := range subset {
50+
if len(f.Calls) == 0 && !includeExternal {
51+
continue
52+
}
53+
rel := buildRelationForFunction(f, includeExternal, funcMap, suffixMap)
54+
if len(rel.Called) > 0 || includeExternal {
55+
out = append(out, rel)
56+
}
57+
}
58+
return out
59+
}
60+
61+
// buildGlobalIndices creates lookup maps for exact and suffix-based resolution.
62+
func buildGlobalIndices(functions []FunctionInfo) (map[string]FunctionInfo, map[string]FunctionInfo) {
2763
// index by name for quick lookup
2864
funcMap := make(map[string]FunctionInfo, len(functions))
2965
// Also create an index by suffix for external function matching
@@ -45,60 +81,54 @@ func BuildRelations(functions []FunctionInfo, includeExternal bool) []OutRelatio
4581
}
4682
}
4783
}
84+
return funcMap, suffixMap
85+
}
4886

49-
out := make([]OutRelation, 0, len(functions))
50-
for _, f := range functions {
51-
if len(f.Calls) == 0 && !includeExternal {
52-
continue // skip functions with no user-defined calls (previous behaviour)
53-
}
54-
rel := OutRelation{Name: f.Name, Line: f.Line, FilePath: f.FilePath}
55-
for _, cname := range f.Calls {
56-
if cf, ok := funcMap[cname]; ok {
57-
// Function exists in our codebase (including external modules when scanned)
87+
// buildRelationForFunction builds OutRelation for a single function using provided indices.
88+
func buildRelationForFunction(f FunctionInfo, includeExternal bool, funcMap, suffixMap map[string]FunctionInfo) OutRelation {
89+
rel := OutRelation{Name: f.Name, Line: f.Line, FilePath: f.FilePath}
90+
for _, cname := range f.Calls {
91+
if cf, ok := funcMap[cname]; ok {
92+
// Function exists in our codebase (including external modules when scanned)
93+
rel.Called = append(rel.Called, OutCalled{Name: cf.Name, Line: cf.Line, FilePath: cf.FilePath})
94+
} else if includeExternal {
95+
// Try to match with external functions by suffix
96+
if cf, ok := suffixMap[cname]; ok {
5897
rel.Called = append(rel.Called, OutCalled{Name: cf.Name, Line: cf.Line, FilePath: cf.FilePath})
59-
} else if includeExternal {
60-
// Try to match with external functions by suffix
61-
if cf, ok := suffixMap[cname]; ok {
62-
rel.Called = append(rel.Called, OutCalled{Name: cf.Name, Line: cf.Line, FilePath: cf.FilePath})
63-
} else {
64-
// Try more flexible matching for method calls
65-
matched := false
66-
var matchedFunction FunctionInfo
98+
} else {
99+
// Try more flexible matching for method calls
100+
matched := false
101+
var matchedFunction FunctionInfo
102+
103+
// First try: exact suffix match with dot notation
104+
for fullName, cf := range funcMap {
105+
if strings.HasSuffix(fullName, "."+cname) {
106+
rel.Called = append(rel.Called, OutCalled{Name: cf.Name, Line: cf.Line, FilePath: cf.FilePath})
107+
matched = true
108+
break
109+
}
110+
}
67111

68-
// First try: exact suffix match with dot notation
112+
// Second try: if not matched, try partial matching
113+
if !matched {
69114
for fullName, cf := range funcMap {
70-
if strings.HasSuffix(fullName, "."+cname) {
71-
rel.Called = append(rel.Called, OutCalled{Name: cf.Name, Line: cf.Line, FilePath: cf.FilePath})
115+
if strings.Contains(fullName, cname) {
116+
matchedFunction = cf
72117
matched = true
73118
break
74119
}
75120
}
76-
77-
// Second try: if not matched, try partial matching
78-
if !matched {
79-
for fullName, cf := range funcMap {
80-
if strings.Contains(fullName, cname) {
81-
matchedFunction = cf
82-
matched = true
83-
break
84-
}
85-
}
86-
if matched {
87-
rel.Called = append(rel.Called, OutCalled{Name: matchedFunction.Name, Line: matchedFunction.Line, FilePath: matchedFunction.FilePath})
88-
}
121+
if matched {
122+
rel.Called = append(rel.Called, OutCalled{Name: matchedFunction.Name, Line: matchedFunction.Line, FilePath: matchedFunction.FilePath})
89123
}
124+
}
90125

91-
if !matched {
92-
// External function call not found in scanned modules - include with placeholder info
93-
rel.Called = append(rel.Called, OutCalled{Name: cname, Line: 0, FilePath: "external"})
94-
}
126+
if !matched {
127+
// External function call not found in scanned modules - include with placeholder info
128+
rel.Called = append(rel.Called, OutCalled{Name: cname, Line: 0, FilePath: "external"})
95129
}
96130
}
97131
}
98-
if len(rel.Called) > 0 || includeExternal {
99-
// Include the relation if it has calls OR if we're including all functions
100-
out = append(out, rel)
101-
}
102132
}
103-
return out
133+
return rel
104134
}

cmd/server/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,8 @@ func buildRelationsParallel(functions []analyzer.FunctionInfo, includeExternal b
359359
go func(start, end, workerIndex int) {
360360
defer wg.Done()
361361
chunkFunctions := functions[start:end]
362-
chunkRelations := analyzer.BuildRelations(chunkFunctions, includeExternal)
362+
// Use indexed variant so each chunk can resolve calls against the full set
363+
chunkRelations := analyzer.BuildRelationsIndexed(chunkFunctions, includeExternal, functions)
363364
resultChan <- relationChunk{relations: chunkRelations, index: workerIndex}
364365
}(start, end, i)
365366
}

0 commit comments

Comments
 (0)