@@ -29,25 +29,47 @@ private readonly record struct TypeArgumentInfo(
29
29
30
30
private readonly TypeCacheHashtable _typeCacheHashtable ;
31
31
32
+ private readonly Logger _logger ;
33
+
32
34
public CompilerGeneratedState ( ILProvider ilProvider , Logger logger )
33
35
{
34
- _typeCacheHashtable = new TypeCacheHashtable ( ilProvider , logger ) ;
36
+ _typeCacheHashtable = new TypeCacheHashtable ( ilProvider ) ;
37
+ _logger = logger ;
35
38
}
36
39
37
40
private sealed class TypeCacheHashtable : LockFreeReaderHashtable < MetadataType , TypeCache >
38
41
{
39
42
private ILProvider _ilProvider ;
40
- private Logger ? _logger ;
41
43
42
- public TypeCacheHashtable ( ILProvider ilProvider , Logger logger ) => ( _ilProvider , _logger ) = ( ilProvider , logger ) ;
44
+ public TypeCacheHashtable ( ILProvider ilProvider ) => _ilProvider = ilProvider ;
43
45
44
46
protected override bool CompareKeyToValue ( MetadataType key , TypeCache value ) => key == value . Type ;
45
47
protected override bool CompareValueToValue ( TypeCache value1 , TypeCache value2 ) => value1 . Type == value2 . Type ;
46
48
protected override int GetKeyHashCode ( MetadataType key ) => key . GetHashCode ( ) ;
47
49
protected override int GetValueHashCode ( TypeCache value ) => value . Type . GetHashCode ( ) ;
48
50
49
51
protected override TypeCache CreateValueFromKey ( MetadataType key )
50
- => new TypeCache ( key , _logger , _ilProvider ) ;
52
+ => new TypeCache ( key , _ilProvider ) ;
53
+
54
+ public TypeCache GetOrCreateValue ( MetadataType key , out bool created )
55
+ {
56
+ TypeCache existingValue ;
57
+ created = false ;
58
+ if ( TryGetValue ( key , out existingValue ) )
59
+ return existingValue ;
60
+
61
+ var newValue = CreateValueFromKey ( key ) ;
62
+ if ( TryAdd ( newValue ) )
63
+ {
64
+ created = true ;
65
+ return newValue ;
66
+ }
67
+
68
+ if ( ! TryGetValue ( key , out existingValue ) )
69
+ throw new InvalidOperationException ( ) ;
70
+
71
+ return existingValue ;
72
+ }
51
73
}
52
74
53
75
private sealed class TypeCache
@@ -64,14 +86,26 @@ private sealed class TypeCache
64
86
// or null if the type has no methods with compiler-generated members.
65
87
private Dictionary < MethodDesc , List < TypeSystemEntity > > ? _compilerGeneratedMembers ;
66
88
89
+ // Stores a list of warnings to be emitted at the end of the cache construction
90
+ private List < ( MessageOrigin , DiagnosticId , string [ ] ) > ? _warnings ;
91
+
92
+ internal void LogWarnings ( Logger ? logger )
93
+ {
94
+ if ( _warnings == null || logger == null )
95
+ return ;
96
+
97
+ foreach ( var ( origin , id , messageArgs ) in _warnings )
98
+ logger . LogWarning ( origin , id , messageArgs ) ;
99
+ }
100
+
67
101
/// <summary>
68
102
/// Walks the type and its descendents to find Roslyn-compiler generated
69
103
/// code and gather information to map it back to original user code. If
70
104
/// a compiler-generated type is passed in directly, this method will walk
71
105
/// up and find the nearest containing user type. Returns the nearest user type,
72
106
/// or null if none was found.
73
107
/// </summary>
74
- internal TypeCache ( MetadataType type , Logger ? logger , ILProvider ilProvider )
108
+ internal TypeCache ( MetadataType type , ILProvider ilProvider )
75
109
{
76
110
Debug . Assert ( type == type . GetTypeDefinition ( ) ) ;
77
111
Debug . Assert ( ! CompilerGeneratedNames . IsStateMachineOrDisplayClass ( type . Name ) ) ;
@@ -82,6 +116,15 @@ internal TypeCache(MetadataType type, Logger? logger, ILProvider ilProvider)
82
116
var userDefinedMethods = new HashSet < MethodDesc > ( ) ;
83
117
var generatedTypeToTypeArgs = new Dictionary < MetadataType , TypeArgumentInfo > ( ) ;
84
118
119
+ // We delay actually logging the warnings until the compiler-generated type info is
120
+ // populated for this type, because the type info is needed to determine whether a warning
121
+ // is suppressed.
122
+ void AddWarning ( MessageOrigin origin , DiagnosticId id , params string [ ] messageArgs )
123
+ {
124
+ _warnings ??= new List < ( MessageOrigin , DiagnosticId , string [ ] ) > ( ) ;
125
+ _warnings . Add ( ( origin , id , messageArgs ) ) ;
126
+ }
127
+
85
128
void ProcessMethod ( MethodDesc method )
86
129
{
87
130
Debug . Assert ( method == method . GetTypicalMethodDefinition ( ) ) ;
@@ -139,7 +182,7 @@ referencedMethod.OwningType is MetadataType generatedType &&
139
182
if ( ! generatedTypeToTypeArgs . TryAdd ( generatedType , new TypeArgumentInfo ( method , null ) ) )
140
183
{
141
184
var alreadyAssociatedMethod = generatedTypeToTypeArgs [ generatedType ] . CreatingMethod ;
142
- logger ? . LogWarning ( new MessageOrigin ( method ) , DiagnosticId . MethodsAreAssociatedWithUserMethod , method . GetDisplayName ( ) , alreadyAssociatedMethod . GetDisplayName ( ) , generatedType . GetDisplayName ( ) ) ;
185
+ AddWarning ( new MessageOrigin ( method ) , DiagnosticId . MethodsAreAssociatedWithUserMethod , method . GetDisplayName ( ) , alreadyAssociatedMethod . GetDisplayName ( ) , generatedType . GetDisplayName ( ) ) ;
143
186
}
144
187
continue ;
145
188
}
@@ -207,7 +250,7 @@ referencedMethod.OwningType is MetadataType generatedType &&
207
250
if ( ! _compilerGeneratedTypeToUserCodeMethod . TryAdd ( stateMachineType , method ) )
208
251
{
209
252
var alreadyAssociatedMethod = _compilerGeneratedTypeToUserCodeMethod [ stateMachineType ] ;
210
- logger ? . LogWarning ( new MessageOrigin ( method ) , DiagnosticId . MethodsAreAssociatedWithStateMachine , method . GetDisplayName ( ) , alreadyAssociatedMethod . GetDisplayName ( ) , stateMachineType . GetDisplayName ( ) ) ;
253
+ AddWarning ( new MessageOrigin ( method ) , DiagnosticId . MethodsAreAssociatedWithStateMachine , method . GetDisplayName ( ) , alreadyAssociatedMethod . GetDisplayName ( ) , stateMachineType . GetDisplayName ( ) ) ;
211
254
}
212
255
// Already warned above if multiple methods map to the same type
213
256
// Fill in null for argument providers now, the real providers will be filled in later
@@ -263,7 +306,7 @@ referencedMethod.OwningType is MetadataType generatedType &&
263
306
if ( ! _compilerGeneratedMethodToUserCodeMethod . TryAdd ( nestedFunction , userDefinedMethod ) )
264
307
{
265
308
var alreadyAssociatedMethod = _compilerGeneratedMethodToUserCodeMethod [ nestedFunction ] ;
266
- logger ? . LogWarning ( new MessageOrigin ( userDefinedMethod ) , DiagnosticId . MethodsAreAssociatedWithUserMethod , userDefinedMethod . GetDisplayName ( ) , alreadyAssociatedMethod . GetDisplayName ( ) , nestedFunction . GetDisplayName ( ) ) ;
309
+ AddWarning ( new MessageOrigin ( userDefinedMethod ) , DiagnosticId . MethodsAreAssociatedWithUserMethod , userDefinedMethod . GetDisplayName ( ) , alreadyAssociatedMethod . GetDisplayName ( ) , nestedFunction . GetDisplayName ( ) ) ;
267
310
}
268
311
break ;
269
312
case MetadataType stateMachineType :
@@ -295,7 +338,7 @@ referencedMethod.OwningType is MetadataType generatedType &&
295
338
{
296
339
var method = info . CreatingMethod ;
297
340
var alreadyAssociatedMethod = _generatedTypeToTypeArgumentInfo [ generatedType ] . CreatingMethod ;
298
- logger ? . LogWarning ( new MessageOrigin ( method ) , DiagnosticId . MethodsAreAssociatedWithUserMethod , method . GetDisplayName ( ) , alreadyAssociatedMethod . GetDisplayName ( ) , generatedType . GetDisplayName ( ) ) ;
341
+ AddWarning ( new MessageOrigin ( method ) , DiagnosticId . MethodsAreAssociatedWithUserMethod , method . GetDisplayName ( ) , alreadyAssociatedMethod . GetDisplayName ( ) , generatedType . GetDisplayName ( ) ) ;
299
342
}
300
343
}
301
344
}
@@ -575,7 +618,10 @@ public static bool TryGetStateMachineType(MethodDesc method, [NotNullWhen(true)]
575
618
if ( userType is null )
576
619
return null ;
577
620
578
- return _typeCacheHashtable . GetOrCreateValue ( userType ) ;
621
+ var typeCache = _typeCacheHashtable . GetOrCreateValue ( userType , out bool created ) ;
622
+ if ( created )
623
+ typeCache . LogWarnings ( _logger ) ;
624
+ return typeCache ;
579
625
}
580
626
581
627
private static TypeDesc ? GetFirstConstructorArgumentAsType ( CustomAttributeValue < TypeDesc > attribute )
0 commit comments