@@ -4,11 +4,15 @@ namespace CSharpCodeAnalyst.Exploration;
4
4
5
5
public class CodeGraphExplorer : ICodeGraphExplorer
6
6
{
7
+ private List < Dependency > _allDependencies = [ ] ;
7
8
private CodeGraph ? _codeGraph ;
8
9
9
10
public void LoadCodeGraph ( CodeGraph graph )
10
11
{
11
12
_codeGraph = graph ;
13
+
14
+ // Clear all cached data
15
+ _allDependencies = [ ] ;
12
16
}
13
17
14
18
public List < CodeElement > GetElements ( List < string > ids )
@@ -33,6 +37,28 @@ public List<CodeElement> GetElements(List<string> ids)
33
37
return elements ;
34
38
}
35
39
40
+ public SearchResult FindParents ( List < string > ids )
41
+ {
42
+ if ( _codeGraph is null )
43
+ {
44
+ return new SearchResult ( [ ] , [ ] ) ;
45
+ }
46
+
47
+ var parents = new HashSet < CodeElement > ( ) ;
48
+ foreach ( var id in ids )
49
+ {
50
+ if ( _codeGraph . Nodes . TryGetValue ( id , out var element ) )
51
+ {
52
+ if ( element . Parent is not null )
53
+ {
54
+ parents . Add ( element . Parent ) ;
55
+ }
56
+ }
57
+ }
58
+
59
+ return new SearchResult ( parents , [ ] ) ;
60
+ }
61
+
36
62
/// <summary>
37
63
/// Returns all dependencies that link the given nodes (ids).
38
64
/// </summary>
@@ -62,13 +88,14 @@ public Invocation FindIncomingCalls(string id)
62
88
63
89
var method = _codeGraph . Nodes [ id ] ;
64
90
65
- var callDependencies = GetCallDependencies ( ) ;
66
- var calls = callDependencies . Where ( call => call . TargetId == method . Id ) . ToArray ( ) ;
91
+ var allCalls = GetDependencies ( d => d . Type == DependencyType . Calls ) ;
92
+ var calls = allCalls . Where ( call => call . TargetId == method . Id ) . ToArray ( ) ;
67
93
var methods = calls . Select ( d => _codeGraph . Nodes [ d . SourceId ] ) ;
68
94
69
95
return new Invocation ( methods , calls ) ;
70
96
}
71
97
98
+
72
99
public Invocation FindIncomingCallsRecursive ( string id )
73
100
{
74
101
ArgumentNullException . ThrowIfNull ( id ) ;
@@ -83,10 +110,10 @@ public Invocation FindIncomingCallsRecursive(string id)
83
110
var processingQueue = new Queue < CodeElement > ( ) ;
84
111
processingQueue . Enqueue ( method ) ;
85
112
86
- var allCalls = new HashSet < Dependency > ( ) ;
87
- var allMethods = new HashSet < CodeElement > ( ) ;
113
+ var foundCalls = new HashSet < Dependency > ( ) ;
114
+ var foundMethods = new HashSet < CodeElement > ( ) ;
88
115
89
- var callDependencies = GetCallDependencies ( ) ;
116
+ var allCalls = GetDependencies ( d => d . Type == DependencyType . Calls ) ;
90
117
91
118
var processed = new HashSet < string > ( ) ;
92
119
while ( processingQueue . Any ( ) )
@@ -97,19 +124,74 @@ public Invocation FindIncomingCallsRecursive(string id)
97
124
continue ;
98
125
}
99
126
100
- var calls = callDependencies . Where ( call => call . TargetId == element . Id ) . ToArray ( ) ;
101
- allCalls . UnionWith ( calls ) ;
127
+ var calls = allCalls . Where ( call => call . TargetId == element . Id ) . ToArray ( ) ;
128
+ foundCalls . UnionWith ( calls ) ;
102
129
103
130
var methods = calls . Select ( d => _codeGraph . Nodes [ d . SourceId ] ) . ToArray ( ) ;
104
- allMethods . UnionWith ( methods ) ;
131
+ foundMethods . UnionWith ( methods ) ;
105
132
106
133
foreach ( var methodToExplore in methods )
107
134
{
108
135
processingQueue . Enqueue ( _codeGraph . Nodes [ methodToExplore . Id ] ) ;
109
136
}
110
137
}
111
138
112
- return new Invocation ( allMethods , allCalls ) ;
139
+ return new Invocation ( foundMethods , foundCalls ) ;
140
+ }
141
+
142
+
143
+ public SearchResult FollowIncomingCallsRecursive ( string id )
144
+ {
145
+ ArgumentNullException . ThrowIfNull ( id ) ;
146
+
147
+ if ( _codeGraph is null )
148
+ {
149
+ return new SearchResult ( [ ] , [ ] ) ;
150
+ }
151
+
152
+ var allImplementsAndOverrides =
153
+ GetDependencies ( d => d . Type is DependencyType . Implements or DependencyType . Overrides ) ;
154
+ var allCalls = GetDependencies ( d => d . Type == DependencyType . Calls ) ;
155
+
156
+ var method = _codeGraph . Nodes [ id ] ;
157
+
158
+ var processingQueue = new Queue < CodeElement > ( ) ;
159
+ processingQueue . Enqueue ( method ) ;
160
+
161
+ var foundDependencies = new HashSet < Dependency > ( ) ;
162
+ var foundElements = new HashSet < CodeElement > ( ) ;
163
+
164
+
165
+ var processed = new HashSet < string > ( ) ;
166
+ while ( processingQueue . Any ( ) )
167
+ {
168
+ var element = processingQueue . Dequeue ( ) ;
169
+ if ( ! processed . Add ( element . Id ) )
170
+ {
171
+ continue ;
172
+ }
173
+
174
+ // Calls
175
+ var calls = allCalls . Where ( call => call . TargetId == element . Id ) . ToArray ( ) ;
176
+ foundDependencies . UnionWith ( calls ) ;
177
+ var callSources = calls . Select ( d => _codeGraph . Nodes [ d . SourceId ] ) . ToHashSet ( ) ;
178
+ foundElements . UnionWith ( callSources ) ;
179
+
180
+ // Abstractions. Sometimes the abstractions is called.
181
+ var abstractions = allImplementsAndOverrides . Where ( d => d . SourceId == element . Id ) . ToArray ( ) ;
182
+ foundDependencies . UnionWith ( abstractions ) ;
183
+ var abstractionTargets = abstractions . Select ( d => _codeGraph . Nodes [ d . TargetId ] ) . ToHashSet ( ) ;
184
+ foundElements . UnionWith ( abstractionTargets ) ;
185
+
186
+ // Follow new leads
187
+ var methodsToExplore = abstractionTargets . Union ( callSources ) ;
188
+ foreach ( var methodToExplore in methodsToExplore )
189
+ {
190
+ processingQueue . Enqueue ( _codeGraph . Nodes [ methodToExplore . Id ] ) ;
191
+ }
192
+ }
193
+
194
+ return new SearchResult ( foundElements , foundDependencies ) ;
113
195
}
114
196
115
197
/// <summary>
@@ -264,18 +346,24 @@ public SearchResult FindIncomingDependencies(string id)
264
346
return new SearchResult ( elements , dependencies ) ;
265
347
}
266
348
267
- private List < Dependency > GetCallDependencies ( )
349
+ private List < Dependency > GetCachedDependencies ( )
268
350
{
269
351
if ( _codeGraph is null )
270
352
{
271
353
return [ ] ;
272
354
}
273
355
274
- var callDependencies = _codeGraph . Nodes . Values
275
- . SelectMany ( node => node . Dependencies )
276
- . Where ( d => d . Type == DependencyType . Calls )
277
- . ToList ( ) ;
278
- return callDependencies ;
356
+ if ( _allDependencies . Count == 0 )
357
+ {
358
+ _allDependencies = _codeGraph . GetAllDependencies ( ) . ToList ( ) ;
359
+ }
360
+
361
+ return _allDependencies ;
362
+ }
363
+
364
+ private List < Dependency > GetDependencies ( Func < Dependency , bool > filter )
365
+ {
366
+ return GetCachedDependencies ( ) . Where ( filter ) . ToList ( ) ;
279
367
}
280
368
281
369
private HashSet < Dependency > FindInheritsAndImplementsRelationships ( )
0 commit comments