@@ -34,11 +34,25 @@ public MethodInfo(TypeInfo typeInfo, IMethodSymbol symbol, MethodDeclarationSynt
34
34
35
35
public bool Missing { get ; set ; }
36
36
37
- public Dictionary < ReferenceLocation , MethodInfo > InvokedBy { get ; } = new Dictionary < ReferenceLocation , MethodInfo > ( ) ;
37
+ public HashSet < MethodInfo > InvokedBy { get ; } = new HashSet < MethodInfo > ( ) ;
38
38
39
- public HashSet < MethodInfo > Dependencies { get ; } = new HashSet < MethodInfo > ( ) ;
39
+ /// <summary>
40
+ /// Implementation/derived/base/interface methods
41
+ /// </summary>
42
+ public HashSet < MethodInfo > RelatedMethods { get ; } = new HashSet < MethodInfo > ( ) ;
43
+
44
+ /// <summary>
45
+ /// Related and invoked by methods
46
+ /// </summary>
47
+ public IEnumerable < MethodInfo > Dependencies
48
+ {
49
+ get { return InvokedBy . Union ( RelatedMethods ) ; }
50
+ }
40
51
41
- public HashSet < IMethodSymbol > ExternalDependencies { get ; } = new HashSet < IMethodSymbol > ( ) ;
52
+ /// <summary>
53
+ /// External Base/derivered or interface/implementation methods
54
+ /// </summary>
55
+ public HashSet < IMethodSymbol > ExternalRelatedMethods { get ; } = new HashSet < IMethodSymbol > ( ) ;
42
56
43
57
// async methods that have the same parameters and name and are external
44
58
public HashSet < IMethodSymbol > ExternalAsyncMethods { get ; } = new HashSet < IMethodSymbol > ( ) ;
@@ -47,6 +61,13 @@ public MethodInfo(TypeInfo typeInfo, IMethodSymbol symbol, MethodDeclarationSynt
47
61
/// When true the method will not be generated. Example would be a test method (never invoked in code) that does not have any async calls
48
62
/// or a method that is only called by that test method. If there are some references (methods invoked) we need to check them as they can be
49
63
///
64
+ /// Steps:
65
+ /// 1. Check if the method is missing or implements an abstract method
66
+ /// a) If true then we cannot ignore this method
67
+ /// 3. Calculate the Ignore property for all dependant methods
68
+ /// a) If there is a method that is not ignored then this wont be too
69
+ /// i) Calculate ignore property for all current references
70
+ /// 2. Calculate the Ignore property for all current references
50
71
/// </summary>
51
72
public void CalculateIgnore ( int deep = 0 , HashSet < MethodInfo > processedMethodInfos = null )
52
73
{
@@ -59,75 +80,78 @@ public void CalculateIgnore(int deep = 0, HashSet<MethodInfo> processedMethodInf
59
80
throw new Exception ( "_ignoreCalculating" ) ;
60
81
}
61
82
_ignoreCalculating = true ;
62
-
63
-
83
+
84
+
64
85
if ( processedMethodInfos == null )
65
86
{
66
87
processedMethodInfos = new HashSet < MethodInfo > { this } ;
67
88
}
68
-
69
- deep ++ ;
70
- if ( deep > 20 )
89
+ else
71
90
{
72
- Ignore = false ;
73
- _ignoreCalculating = false ;
74
- _ignoreCalculated = true ;
75
- return ;
91
+ processedMethodInfos . Add ( this ) ;
76
92
}
77
- /*
78
- foreach (var refResult in ReferenceResults)
79
- {
80
- refResult.CalculateIgnore(deep, processedMethodInfos);
81
- }*/
82
93
83
- if ( Missing || IsRequired )
94
+ deep ++ ;
95
+ if ( deep > 100 )
84
96
{
85
- Ignore = false ;
97
+ foreach ( var refResult in ReferenceResults )
98
+ {
99
+ refResult . CalculateIgnore ( deep , processedMethodInfos ) ;
100
+ }
86
101
_ignoreCalculating = false ;
87
102
_ignoreCalculated = true ;
88
103
return ;
89
104
}
90
- var config = TypeInfo . NamespaceInfo . DocumentInfo . ProjectInfo . Configuration ;
91
-
92
- //if (TypeInfo.TypeTransformation == TypeTransformation.NewType)
93
- //{
94
-
95
- //}
96
105
97
- // If method is used elsewhere then we should generante the method (TODO: check if methods that invoke this one are ignored... problem with circular calls)
98
- if ( InvokedBy . Any ( ) )
106
+ if ( Missing || ImplementsAbstractMethod )
99
107
{
100
- Ignore = false ;
108
+ foreach ( var refResult in ReferenceResults )
109
+ {
110
+ refResult . CalculateIgnore ( deep , processedMethodInfos ) ;
111
+ }
101
112
_ignoreCalculating = false ;
102
113
_ignoreCalculated = true ;
103
114
return ;
104
115
}
105
116
106
- /*
107
- foreach (var methodInfo in InvokedBy.Values)
117
+ // Calculate the ignore property for all dependent methods.
118
+ // If any of above methods is required then this will be too
119
+ foreach ( var methodInfo in Dependencies )
108
120
{
109
121
if ( methodInfo == this )
110
122
{
111
123
continue ;
112
124
}
125
+ // If we detect a circular dependency we assume that the method is required
126
+ if ( methodInfo . _ignoreCalculating )
127
+ {
128
+ foreach ( var refResult in ReferenceResults )
129
+ {
130
+ refResult . CalculateIgnore ( deep , processedMethodInfos ) ;
131
+ }
132
+ _ignoreCalculating = false ;
133
+ _ignoreCalculated = true ;
134
+ return ;
135
+ }
113
136
114
137
methodInfo . CalculateIgnore ( deep , processedMethodInfos ) ;
115
138
if ( methodInfo . Ignore )
116
139
{
117
140
continue ;
118
141
}
119
- Ignore = false;
142
+ foreach ( var refResult in ReferenceResults )
143
+ {
144
+ refResult . CalculateIgnore ( deep , processedMethodInfos ) ;
145
+ }
146
+ _ignoreCalculating = false ;
147
+ _ignoreCalculated = true ;
120
148
return ;
121
- }*/
149
+ }
122
150
123
151
var dependencies = ReferenceResults
124
- . Union ( GetAllDependencies ( ) . ToList ( ) . SelectMany ( o => o . ReferenceResults ) )
125
- . ToList ( ) ; /*
126
- if (!dependencies.Any() && config.CanGenerateMethod != null)
127
- {
128
- return !config.CanGenerateMethod(this);
129
- }
130
- */
152
+ . Union ( GetAllRelatedMethods ( ) . ToList ( ) . SelectMany ( o => o . ReferenceResults ) )
153
+ . ToList ( ) ;
154
+
131
155
foreach ( var refResult in dependencies )
132
156
{
133
157
// if the reference cannot be async we should preserve the original method. TODO: what if the method has dependecies that are async
@@ -136,23 +160,6 @@ public void CalculateIgnore(int deep = 0, HashSet<MethodInfo> processedMethodInf
136
160
refResult . MethodInfo . CanBeAsnyc = false ;
137
161
}
138
162
refResult . CalculateIgnore ( deep , processedMethodInfos ) ;
139
-
140
- /*
141
- var ignore = false;
142
- if (refResult.MethodInfo != null)
143
- {
144
- if (processedMethodInfos.Contains(refResult.MethodInfo))
145
- {
146
- continue;
147
- }
148
- processedMethodInfos.Add(refResult.MethodInfo);
149
- ignore |= refResult.MethodInfo.CanIgnore(deep, processedMethodInfos);
150
- }
151
- ignore |= refResult.UserIgnore;
152
- if (!ignore)
153
- {
154
- return false;
155
- }*/
156
163
}
157
164
Ignore = dependencies . All ( o => o . Ignore ) ;
158
165
_ignoreCalculating = false ;
@@ -163,7 +170,7 @@ public bool HasReferences
163
170
{
164
171
get
165
172
{
166
- return References . Any ( ) || GetAllDependencies ( ) . Any ( o => o . References . Any ( ) ) ;
173
+ return References . Any ( ) || GetAllRelatedMethods ( ) . Any ( o => o . References . Any ( ) ) ;
167
174
}
168
175
}
169
176
@@ -179,13 +186,13 @@ public bool HasRequiredExternalMethods(int deep = 0, HashSet<MethodInfo> process
179
186
processedMethodInfos = new HashSet < MethodInfo > { this } ;
180
187
}
181
188
182
- if ( ExternalDependencies . Select ( o => o . ContainingType . GetFullName ( ) )
189
+ if ( ExternalRelatedMethods . Select ( o => o . ContainingType . GetFullName ( ) )
183
190
. Except ( ExternalAsyncMethods . Select ( o => o . ContainingType . GetFullName ( ) ) )
184
191
. Any ( ) )
185
192
{
186
193
return true ;
187
194
}
188
- foreach ( var depednency in GetAllDependencies ( ) )
195
+ foreach ( var depednency in GetAllRelatedMethods ( ) )
189
196
{
190
197
if ( processedMethodInfos . Contains ( depednency ) )
191
198
{
@@ -200,15 +207,15 @@ public bool HasRequiredExternalMethods(int deep = 0, HashSet<MethodInfo> process
200
207
return false ;
201
208
}
202
209
203
- public bool IsRequired
210
+ public bool ImplementsAbstractMethod
204
211
{
205
212
get { return OverridenMethod != null && OverridenMethod . IsAbstract ; }
206
213
}
207
214
208
215
public IMethodSymbol OverridenMethod { get ; set ; }
209
216
210
217
/// <summary>
211
- /// references that are candidates to be async
218
+ /// References to other methods that are invoked inside this method and are candidates to be async
212
219
/// </summary>
213
220
internal HashSet < ReferenceLocation > References { get ; } = new HashSet < ReferenceLocation > ( ) ;
214
221
@@ -217,6 +224,29 @@ public bool IsRequired
217
224
/// </summary>
218
225
public HashSet < ReferenceLocation > TypeReferences { get ; } = new HashSet < ReferenceLocation > ( ) ;
219
226
227
+ public IEnumerable < MethodInfo > GetAllRelatedMethods ( )
228
+ {
229
+ var deps = new HashSet < MethodInfo > ( ) ;
230
+ var depsQueue = new Queue < MethodInfo > ( RelatedMethods ) ;
231
+ while ( depsQueue . Count > 0 )
232
+ {
233
+ var dependency = depsQueue . Dequeue ( ) ;
234
+ if ( deps . Contains ( dependency ) )
235
+ {
236
+ continue ;
237
+ }
238
+ deps . Add ( dependency ) ;
239
+ foreach ( var subDependency in dependency . RelatedMethods )
240
+ {
241
+ if ( ! deps . Contains ( subDependency ) )
242
+ {
243
+ depsQueue . Enqueue ( subDependency ) ;
244
+ }
245
+ }
246
+ yield return dependency ;
247
+ }
248
+ }
249
+
220
250
public IEnumerable < MethodInfo > GetAllDependencies ( )
221
251
{
222
252
var deps = new HashSet < MethodInfo > ( ) ;
@@ -231,11 +261,10 @@ public IEnumerable<MethodInfo> GetAllDependencies()
231
261
deps . Add ( dependency ) ;
232
262
foreach ( var subDependency in dependency . Dependencies )
233
263
{
234
- if ( deps . Contains ( subDependency ) )
264
+ if ( ! deps . Contains ( subDependency ) )
235
265
{
236
- continue ;
266
+ depsQueue . Enqueue ( subDependency ) ;
237
267
}
238
- depsQueue . Enqueue ( subDependency ) ;
239
268
}
240
269
yield return dependency ;
241
270
}
@@ -559,17 +588,39 @@ public void PostAnalyze()
559
588
{
560
589
TypeInfo . MethodInfos . Remove ( Node ) ;
561
590
}
591
+
592
+ // Remove all references as the method wont be transformed to asnyc
593
+ if ( ! CanBeAsnyc && ! RelatedMethods . Any ( ) && ! Missing )
594
+ {
595
+ foreach ( var invoked in InvokedBy )
596
+ {
597
+ var toRemove = invoked . ReferenceResults . Where ( o => o . MethodInfo == this ) . ToList ( ) ;
598
+ while ( toRemove . Count > 0 )
599
+ {
600
+ invoked . ReferenceResults . Remove ( toRemove [ 0 ] ) ;
601
+ toRemove . RemoveAt ( 0 ) ;
602
+ }
603
+ }
604
+ }
562
605
}
563
606
564
607
// check if all the references can be converted to async
565
608
public void Analyze ( )
566
609
{
610
+ var solutionConfig = TypeInfo . NamespaceInfo . DocumentInfo . ProjectInfo . SolutionInfo . Configuration ;
611
+
567
612
foreach ( var reference in References )
568
613
{
569
614
ReferenceResults . Add ( AnalyzeReference ( reference ) ) ;
570
615
}
571
616
CanBeAsnyc = ReferenceResults . Any ( o => o . CanBeAsync ) ;
572
617
618
+ // TODO: check if this is correct
619
+ if ( TypeInfo . TypeTransformation == TypeTransformation . Partial && GetAllRelatedMethods ( ) . ToList ( ) . Any ( o => o . InvokedBy . Any ( ) ) )
620
+ {
621
+ Missing = true ;
622
+ }
623
+
573
624
var counter = new MethodStatementsCounterVisitior ( ) ;
574
625
counter . Visit ( Node . Body ) ;
575
626
@@ -582,7 +633,6 @@ public void Analyze()
582
633
IsEmpty = true ;
583
634
}
584
635
585
- var solutionConfig = TypeInfo . NamespaceInfo . DocumentInfo . ProjectInfo . SolutionInfo . Configuration ;
586
636
if ( Node . AttributeLists
587
637
. SelectMany ( o => o . Attributes . Where ( a => a . Name . ToString ( ) == "MethodImpl" ) )
588
638
. Any ( o => o . ArgumentList . Arguments . Any ( a => a . Expression . ToString ( ) == "MethodImplOptions.Synchronized" ) ) )
0 commit comments