-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Expand file tree
/
Copy pathOverriddenOrHiddenMembersHelpers.cs
More file actions
1076 lines (960 loc) · 57.5 KB
/
OverriddenOrHiddenMembersHelpers.cs
File metadata and controls
1076 lines (960 loc) · 57.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
/// <summary>
/// Encapsulates the MakeOverriddenOrHiddenMembers functionality for methods, properties (including indexers),
/// and events.
/// </summary>
internal static class OverriddenOrHiddenMembersHelpers
{
internal static OverriddenOrHiddenMembersResult MakeOverriddenOrHiddenMembers(this MethodSymbol member)
{
return MakeOverriddenOrHiddenMembersWorker(member);
}
internal static OverriddenOrHiddenMembersResult MakeOverriddenOrHiddenMembers(this PropertySymbol member)
{
return MakeOverriddenOrHiddenMembersWorker(member);
}
internal static OverriddenOrHiddenMembersResult MakeOverriddenOrHiddenMembers(this EventSymbol member)
{
return MakeOverriddenOrHiddenMembersWorker(member);
}
/// <summary>
/// Walk up the type hierarchy from ContainingType and list members that this
/// member either overrides (accessible members with the same signature, if this
/// member is declared "override") or hides (accessible members with the same name
/// but different kinds, plus members that would be in the overrides list if
/// this member were not declared "override").
///
/// Members in the overridden list may be non-virtual or may have different
/// accessibilities, types, accessors, etc. They are really candidates to be
/// overridden.
///
/// Members in the hidden list are definitely hidden.
///
/// Members in the runtime overridden list are indistinguishable from the members
/// in the overridden list from the point of view of the runtime (see
/// FindOtherOverriddenMethodsInContainingType for details).
/// </summary>
/// <remarks>
/// In the presence of non-C# types, the meaning of "same signature" is rather
/// complicated. If this member isn't from source, then it refers to the runtime's
/// notion of signature (i.e. including return type, custom modifiers, etc).
/// If this member is from source, then the process is (conceptually) as follows.
///
/// 1) Walk up the type hierarchy, recording all matching members with the same
/// signature, ignoring custom modifiers and return type. Stop if a hidden
/// member is encountered.
/// 2) Apply the following "tie-breaker" rules until you have at most one member,
/// a) Prefer members in more derived types.
/// b) Prefer an exact custom modifier match (i.e. none, for a source member).
/// c) Prefer fewer custom modifiers (values/positions don't matter, just count).
/// d) Prefer earlier in GetMembers order (within the same type).
/// 3) If a member remains, search its containing type for other members that
/// have the same C# signature (overridden members) or runtime signature
/// (runtime overridden members).
///
/// In metadata, properties participate in overriding only through their accessors.
/// That is, property/event accessors may implicitly or explicitly override other methods
/// and a property/event can be considered to override another property/event if its accessors
/// override those of the other property/event.
/// This implementation (like Dev10) will not follow that approach. Instead, it is
/// based on spec section 10.7.5, which treats properties as entities in their own
/// right. If all property/event accessors have conventional names in metadata and nothing
/// "unusual" is done with explicit overriding, this approach should produce the same
/// results as an implementation based on accessor overriding.
/// </remarks>
private static OverriddenOrHiddenMembersResult MakeOverriddenOrHiddenMembersWorker(Symbol member)
{
Debug.Assert(member.Kind == SymbolKind.Method || member.Kind == SymbolKind.Property || member.Kind == SymbolKind.Event);
if (!CanOverrideOrHide(member))
{
return OverriddenOrHiddenMembersResult.Empty;
}
if (member.IsAccessor())
{
// Accessors are handled specially - see MakePropertyAccessorOverriddenOrHiddenMembers for details.
MethodSymbol accessor = member as MethodSymbol;
Symbol associatedPropertyOrEvent = accessor.AssociatedSymbol;
if ((object)associatedPropertyOrEvent != null)
{
if (associatedPropertyOrEvent.Kind == SymbolKind.Property)
{
return MakePropertyAccessorOverriddenOrHiddenMembers(accessor, (PropertySymbol)associatedPropertyOrEvent);
}
else
{
Debug.Assert(associatedPropertyOrEvent.Kind == SymbolKind.Event);
return MakeEventAccessorOverriddenOrHiddenMembers(accessor, (EventSymbol)associatedPropertyOrEvent);
}
}
}
Debug.Assert(!member.IsAccessor());
NamedTypeSymbol containingType = member.ContainingType;
// NOTE: In other areas of the compiler, we check whether the member is from a specific compilation.
// We could do the same thing here, but that would mean that callers of the public API would have
// to pass in a Compilation object when asking about overriding or hiding. This extra cost eliminates
// the small benefit of getting identical answers from "imported" symbols, regardless of whether they
// are imported as source or metadata symbols.
//
// We believe that source and metadata behaviors agree for correct code, modulo accomodations for
// runtime bugs (such as https://github.com/dotnet/roslyn/issues/45453) on older platforms.
// In incorrect code,
// the source behavior is somewhat more generous (e.g. accepting a method with the wrong return type),
// but we do not guarantee that incorrect source will be treated in the same way as incorrect metadata.
bool memberIsFromSomeCompilation = member.Dangerous_IsFromSomeCompilation;
if (containingType.IsInterface)
{
return MakeInterfaceOverriddenOrHiddenMembers(member, memberIsFromSomeCompilation);
}
ArrayBuilder<Symbol> hiddenBuilder;
ImmutableArray<Symbol> overriddenMembers;
FindOverriddenOrHiddenMembers(member, containingType, memberIsFromSomeCompilation, out hiddenBuilder, out overriddenMembers);
ImmutableArray<Symbol> hiddenMembers = hiddenBuilder == null ? ImmutableArray<Symbol>.Empty : hiddenBuilder.ToImmutableAndFree();
return OverriddenOrHiddenMembersResult.Create(overriddenMembers, hiddenMembers);
}
private static void FindOverriddenOrHiddenMembers(Symbol member, NamedTypeSymbol containingType, bool memberIsFromSomeCompilation,
out ArrayBuilder<Symbol> hiddenBuilder,
out ImmutableArray<Symbol> overriddenMembers)
{
Symbol bestMatch = null;
hiddenBuilder = null;
// A specific override exact match candidate, if one is known. This supports covariant returns, for which signature
// matching is not sufficient. This member is treated as being as good as an exact match.
Symbol knownOverriddenMember = member switch
{
MethodSymbol method => KnownOverriddenClassMethod(method),
PEPropertySymbol { GetMethod: PEMethodSymbol { ExplicitlyOverriddenClassMethod: { AssociatedSymbol: PropertySymbol overriddenProperty } } } => overriddenProperty,
RetargetingPropertySymbol { GetMethod: RetargetingMethodSymbol { ExplicitlyOverriddenClassMethod: { AssociatedSymbol: PropertySymbol overriddenProperty } } } => overriddenProperty,
_ => null
};
for (NamedTypeSymbol currType = containingType.BaseTypeNoUseSiteDiagnostics;
(object)currType != null && (object)bestMatch == null && hiddenBuilder == null;
currType = currType.BaseTypeNoUseSiteDiagnostics)
{
bool unused;
FindOverriddenOrHiddenMembersInType(
member,
memberIsFromSomeCompilation,
containingType,
knownOverriddenMember,
currType,
out bestMatch,
out unused,
out hiddenBuilder);
}
// Based on bestMatch, find other methods that will be overridden, hidden, or runtime overridden
// (in bestMatch.ContainingType).
FindRelatedMembers(member.IsOverride, memberIsFromSomeCompilation, member.Kind, bestMatch, out overriddenMembers, ref hiddenBuilder);
}
public static Symbol FindFirstHiddenMemberIfAny(Symbol member, bool memberIsFromSomeCompilation)
{
ArrayBuilder<Symbol> hiddenBuilder;
FindOverriddenOrHiddenMembers(member, member.ContainingType, memberIsFromSomeCompilation, out hiddenBuilder,
overriddenMembers: out _);
Symbol result = hiddenBuilder?.FirstOrDefault();
hiddenBuilder?.Free();
return result;
}
/// <summary>
/// Compute a candidate overridden method when a method knows what method it is intended to
/// override. This makes a particular difference when covariant returns are used, in which
/// case the signature matching rules would not compute the correct overridden method.
/// </summary>
private static MethodSymbol KnownOverriddenClassMethod(MethodSymbol method) =>
method switch
{
PEMethodSymbol m => m.ExplicitlyOverriddenClassMethod,
RetargetingMethodSymbol m => m.ExplicitlyOverriddenClassMethod,
_ => null
};
/// <summary>
/// In the CLI, accessors are just regular methods and their overriding/hiding rules are the same as for
/// regular methods. In C#, however, accessors are intimately connected with their corresponding properties.
/// Rather than walking up the type hierarchy from the containing type of this accessor, looking for members
/// with the same name, MakePropertyAccessorOverriddenOrHiddenMembers delegates to the associated property.
/// For an accessor to hide a member, the hidden member must be a corresponding accessor on a property hidden
/// by the associated property. For an accessor to override a member, the overridden member must be a
/// corresponding accessor on a property (directly or indirectly) overridden by the associated property.
///
/// Example 1:
///
/// public class A { public virtual int P { get; set; } }
/// public class B : A { public override int P { get { return 1; } } } //get only
/// public class C : B { public override int P { set { } } } // set only
///
/// C.P.set overrides A.P.set because C.P.set is the setter of C.P, which overrides B.P,
/// which overrides A.P, which has A.P.set as a setter.
///
/// Example 2:
///
/// public class A { public virtual int P { get; set; } }
/// public class B : A { public new virtual int P { get { return 1; } } } //get only
/// public class C : B { public override int P { set { } } } // set only
///
/// C.P.set does not override any method because C.P overrides B.P, which has no setter
/// and does not override a property.
/// </summary>
/// <param name="accessor">This accessor.</param>
/// <param name="associatedProperty">The property associated with this accessor.</param>
/// <returns>Members overridden or hidden by this accessor.</returns>
/// <remarks>
/// This method is intended to return values consistent with the definition of C#, which
/// may differ from the actual meaning at runtime.
///
/// Note: we don't need a different path for interfaces - Property.OverriddenOrHiddenMembers handles that.
/// </remarks>
private static OverriddenOrHiddenMembersResult MakePropertyAccessorOverriddenOrHiddenMembers(MethodSymbol accessor, PropertySymbol associatedProperty)
{
Debug.Assert(accessor.IsAccessor());
Debug.Assert((object)associatedProperty != null);
bool accessorIsGetter = accessor.MethodKind == MethodKind.PropertyGet;
MethodSymbol overriddenAccessor = null;
ArrayBuilder<Symbol> hiddenBuilder = null;
OverriddenOrHiddenMembersResult hiddenOrOverriddenByProperty = associatedProperty.OverriddenOrHiddenMembers;
foreach (Symbol hiddenByProperty in hiddenOrOverriddenByProperty.HiddenMembers)
{
if (hiddenByProperty.Kind == SymbolKind.Property)
{
// If we're looking at the associated property of this method (vs a property
// it overrides), then record the corresponding accessor (if any) as hidden.
PropertySymbol propertyHiddenByProperty = (PropertySymbol)hiddenByProperty;
MethodSymbol correspondingAccessor = accessorIsGetter ? propertyHiddenByProperty.GetMethod : propertyHiddenByProperty.SetMethod;
if ((object)correspondingAccessor != null)
{
AccessOrGetInstance(ref hiddenBuilder).Add(correspondingAccessor);
}
}
}
if (hiddenOrOverriddenByProperty.OverriddenMembers.Any())
{
// CONSIDER: Do something more sensible if there are multiple overridden members? Already an error case.
PropertySymbol propertyOverriddenByProperty = (PropertySymbol)hiddenOrOverriddenByProperty.OverriddenMembers[0];
MethodSymbol correspondingAccessor = accessorIsGetter ?
propertyOverriddenByProperty.GetOwnOrInheritedGetMethod() :
propertyOverriddenByProperty.GetOwnOrInheritedSetMethod();
if ((object)correspondingAccessor != null)
{
overriddenAccessor = correspondingAccessor;
}
}
// There's a detailed comment in MakeOverriddenOrHiddenMembersWorker(Symbol) concerning why this predicate is appropriate.
bool accessorIsFromSomeCompilation = accessor.Dangerous_IsFromSomeCompilation;
ImmutableArray<Symbol> overriddenAccessors = ImmutableArray<Symbol>.Empty;
if ((object)overriddenAccessor != null && IsOverriddenSymbolAccessible(overriddenAccessor, accessor.ContainingType) &&
isAccessorOverride(accessor, overriddenAccessor))
{
FindRelatedMembers(
accessor.IsOverride, accessorIsFromSomeCompilation, accessor.Kind, overriddenAccessor, out overriddenAccessors, ref hiddenBuilder);
}
ImmutableArray<Symbol> hiddenMembers = hiddenBuilder == null ? ImmutableArray<Symbol>.Empty : hiddenBuilder.ToImmutableAndFree();
return OverriddenOrHiddenMembersResult.Create(overriddenAccessors, hiddenMembers);
bool isAccessorOverride(MethodSymbol accessor, MethodSymbol overriddenAccessor)
{
if (accessorIsFromSomeCompilation)
{
return MemberSignatureComparer.CSharpAccessorOverrideComparer.Equals(accessor, overriddenAccessor); //NB: custom comparer
}
if (overriddenAccessor.Equals(KnownOverriddenClassMethod(accessor), TypeCompareKind.AllIgnoreOptions))
{
return true;
}
return MemberSignatureComparer.RuntimeSignatureComparer.Equals(accessor, overriddenAccessor);
}
}
/// <summary>
/// In the CLI, accessors are just regular methods and their overriding/hiding rules are the same as for
/// regular methods. In C#, however, accessors are intimately connected with their corresponding events.
/// Rather than walking up the type hierarchy from the containing type of this accessor, looking for members
/// with the same name, MakeEventAccessorOverriddenOrHiddenMembers delegates to the associated event.
/// For an accessor to hide a member, the hidden member must be a corresponding accessor on a event hidden
/// by the associated event. For an accessor to override a member, the overridden member must be a
/// corresponding accessor on a event (directly or indirectly) overridden by the associated event.
/// </summary>
/// <param name="accessor">This accessor.</param>
/// <param name="associatedEvent">The event associated with this accessor.</param>
/// <returns>Members overridden or hidden by this accessor.</returns>
/// <remarks>
/// This method is intended to return values consistent with the definition of C#, which
/// may differ from the actual meaning at runtime.
///
/// Note: we don't need a different path for interfaces - Event.OverriddenOrHiddenMembers handles that.
///
/// CONSIDER: It is an error for an event to have only one accessor. Currently, we mimic the behavior for
/// properties, for consistency, but an alternative approach would be to say that nothing is overridden.
///
/// CONSIDER: is there a way to share code with MakePropertyAccessorOverriddenOrHiddenMembers?
/// </remarks>
private static OverriddenOrHiddenMembersResult MakeEventAccessorOverriddenOrHiddenMembers(MethodSymbol accessor, EventSymbol associatedEvent)
{
Debug.Assert(accessor.IsAccessor());
Debug.Assert((object)associatedEvent != null);
bool accessorIsAdder = accessor.MethodKind == MethodKind.EventAdd;
MethodSymbol overriddenAccessor = null;
ArrayBuilder<Symbol> hiddenBuilder = null;
OverriddenOrHiddenMembersResult hiddenOrOverriddenByEvent = associatedEvent.OverriddenOrHiddenMembers;
foreach (Symbol hiddenByEvent in hiddenOrOverriddenByEvent.HiddenMembers)
{
if (hiddenByEvent.Kind == SymbolKind.Event)
{
// If we're looking at the associated event of this method (vs a event
// it overrides), then record the corresponding accessor (if any) as hidden.
EventSymbol eventHiddenByEvent = (EventSymbol)hiddenByEvent;
MethodSymbol correspondingAccessor = accessorIsAdder ? eventHiddenByEvent.AddMethod : eventHiddenByEvent.RemoveMethod;
if ((object)correspondingAccessor != null)
{
AccessOrGetInstance(ref hiddenBuilder).Add(correspondingAccessor);
}
}
}
if (hiddenOrOverriddenByEvent.OverriddenMembers.Any())
{
// CONSIDER: Do something more sensible if there are multiple overridden members? Already an error case.
EventSymbol eventOverriddenByEvent = (EventSymbol)hiddenOrOverriddenByEvent.OverriddenMembers[0];
MethodSymbol correspondingAccessor = eventOverriddenByEvent.GetOwnOrInheritedAccessor(accessorIsAdder);
if ((object)correspondingAccessor != null)
{
overriddenAccessor = correspondingAccessor;
}
}
// There's a detailed comment in MakeOverriddenOrHiddenMembersWorker(Symbol) concerning why this predicate is appropriate.
bool accessorIsFromSomeCompilation = accessor.Dangerous_IsFromSomeCompilation;
ImmutableArray<Symbol> overriddenAccessors = ImmutableArray<Symbol>.Empty;
if ((object)overriddenAccessor != null && IsOverriddenSymbolAccessible(overriddenAccessor, accessor.ContainingType) &&
(accessorIsFromSomeCompilation
? MemberSignatureComparer.CSharpAccessorOverrideComparer.Equals(accessor, overriddenAccessor) //NB: custom comparer
: MemberSignatureComparer.RuntimeSignatureComparer.Equals(accessor, overriddenAccessor)))
{
FindRelatedMembers(
accessor.IsOverride, accessorIsFromSomeCompilation, accessor.Kind, overriddenAccessor, out overriddenAccessors, ref hiddenBuilder);
}
ImmutableArray<Symbol> hiddenMembers = hiddenBuilder == null ? ImmutableArray<Symbol>.Empty : hiddenBuilder.ToImmutableAndFree();
return OverriddenOrHiddenMembersResult.Create(overriddenAccessors, hiddenMembers);
}
/// <summary>
/// There are two key reasons why interface overriding/hiding is different from class overriding/hiding:
/// 1) interface members never override other members; and
/// 2) interfaces can extend multiple interfaces.
/// The first difference doesn't require any special handling - as long as the members have IsOverride=false,
/// the code for class overriding/hiding does the right thing.
/// The second difference is more problematic. For one thing, an interface member can hide a different member in
/// each base interface. We only report the first one, but we need to expose all of them in the API. More importantly,
/// multiple inheritance raises the possibility of diamond inheritance. Spec section 13.2.5, Interface member access,
/// says: "The intuitive rule for hiding in multiple-inheritance interfaces is simply this: If a member is hidden in any
/// access path, it is hidden in all access paths." For example, consider the following interfaces:
///
/// interface I0 { void M(); }
/// interface I1 : I0 { void M(); }
/// interface I2 : I0, I1 { void M(); }
///
/// I2.M does not hide I0.M, because it is already hidden by I1.M. To make this work, we need to traverse the graph
/// of ancestor interfaces in topological order and flag ones later in the enumeration that are hidden along some path.
/// </summary>
/// <remarks>
/// See SymbolPreparer::checkIfaceHiding.
/// </remarks>
internal static OverriddenOrHiddenMembersResult MakeInterfaceOverriddenOrHiddenMembers(Symbol member, bool memberIsFromSomeCompilation)
{
Debug.Assert(!member.IsAccessor());
NamedTypeSymbol containingType = member.ContainingType;
Debug.Assert(containingType.IsInterfaceType());
PooledHashSet<NamedTypeSymbol> membersOfOtherKindsHidden = PooledHashSet<NamedTypeSymbol>.GetInstance();
PooledHashSet<NamedTypeSymbol> allMembersHidden = PooledHashSet<NamedTypeSymbol>.GetInstance(); // Implies membersOfOtherKindsHidden.
ArrayBuilder<Symbol> hiddenBuilder = null;
foreach (NamedTypeSymbol currType in containingType.AllInterfacesNoUseSiteDiagnostics) // NB: topologically sorted
{
if (allMembersHidden.Contains(currType))
{
continue;
}
Symbol currTypeBestMatch;
bool currTypeHasSameKindNonMatch;
ArrayBuilder<Symbol> currTypeHiddenBuilder;
FindOverriddenOrHiddenMembersInType(
member,
memberIsFromSomeCompilation,
containingType,
knownOverriddenMember: null,
currType,
out currTypeBestMatch,
out currTypeHasSameKindNonMatch,
out currTypeHiddenBuilder);
bool haveBestMatch = (object)currTypeBestMatch != null;
if (haveBestMatch)
{
// If our base interface contains a matching member of the same kind,
// then we don't need to look any further up this subtree.
foreach (var hidden in currType.AllInterfacesNoUseSiteDiagnostics)
{
allMembersHidden.Add(hidden);
}
AccessOrGetInstance(ref hiddenBuilder).Add(currTypeBestMatch);
}
if (currTypeHiddenBuilder != null)
{
// If our base interface contains a matching member of a different kind, then
// it will hide all members that aren't of that kind further up the chain.
// As a result, nothing of our kind will be visible and we can stop looking.
if (!membersOfOtherKindsHidden.Contains(currType))
{
if (!haveBestMatch)
{
foreach (var hidden in currType.AllInterfacesNoUseSiteDiagnostics)
{
allMembersHidden.Add(hidden);
}
}
AccessOrGetInstance(ref hiddenBuilder).AddRange(currTypeHiddenBuilder);
}
currTypeHiddenBuilder.Free();
}
else if (currTypeHasSameKindNonMatch && !haveBestMatch)
{
// If our base interface contains a (non-matching) member of the same kind, then
// it will hide all members that aren't of that kind further up the chain.
foreach (var hidden in currType.AllInterfacesNoUseSiteDiagnostics)
{
membersOfOtherKindsHidden.Add(hidden);
}
}
}
membersOfOtherKindsHidden.Free();
allMembersHidden.Free();
// Based on bestMatch, find other methods that will be overridden, hidden, or runtime overridden
// (in bestMatch.ContainingType).
ImmutableArray<Symbol> overriddenMembers = ImmutableArray<Symbol>.Empty;
if (hiddenBuilder != null)
{
ArrayBuilder<Symbol> hiddenAndRelatedBuilder = null;
foreach (Symbol hidden in hiddenBuilder)
{
FindRelatedMembers(member.IsOverride, memberIsFromSomeCompilation, member.Kind, hidden, out overriddenMembers, ref hiddenAndRelatedBuilder);
Debug.Assert(overriddenMembers.Length == 0);
}
hiddenBuilder.Free();
hiddenBuilder = hiddenAndRelatedBuilder;
}
Debug.Assert(overriddenMembers.IsEmpty);
ImmutableArray<Symbol> hiddenMembers = hiddenBuilder == null ? ImmutableArray<Symbol>.Empty : hiddenBuilder.ToImmutableAndFree();
return OverriddenOrHiddenMembersResult.Create(overriddenMembers, hiddenMembers);
}
/// <summary>
/// Look for overridden or hidden members in a specific type.
/// </summary>
/// <param name="member">Member that is hiding or overriding.</param>
/// <param name="memberIsFromSomeCompilation">True if member is from the current compilation.</param>
/// <param name="memberContainingType">The type that contains member (member.ContainingType).</param>
/// <param name="knownOverriddenMember">The known overridden member (e.g. in the presence of a metadata methodimpl).</param>
/// <param name="currType">The type to search.</param>
/// <param name="currTypeBestMatch">
/// A member with the same signature if currTypeHasExactMatch is true,
/// a member with (a minimal number of) different custom modifiers if there is one,
/// and null otherwise.</param>
/// <param name="currTypeHasSameKindNonMatch">True if there's a member with the same name and kind that is not a match.</param>
/// <param name="hiddenBuilder">Hidden members (same name, different kind) will be added to this builder.</param>
/// <remarks>
/// There is some similarity between this member and TypeSymbol.FindPotentialImplicitImplementationMemberDeclaredInType.
/// When making changes to this member, think about whether or not they should also be applied in TypeSymbol.
///
/// In incorrect or imported code, it is possible that both currTypeBestMatch and hiddenBuilder will be populated.
/// </remarks>
private static void FindOverriddenOrHiddenMembersInType(
Symbol member,
bool memberIsFromSomeCompilation,
NamedTypeSymbol memberContainingType,
Symbol knownOverriddenMember,
NamedTypeSymbol currType,
out Symbol currTypeBestMatch,
out bool currTypeHasSameKindNonMatch,
out ArrayBuilder<Symbol> hiddenBuilder)
{
Debug.Assert(!member.IsAccessor());
currTypeBestMatch = null;
currTypeHasSameKindNonMatch = false;
hiddenBuilder = null;
bool currTypeHasExactMatch = false;
int minCustomModifierCount = int.MaxValue;
IEqualityComparer<Symbol> exactMatchComparer = memberIsFromSomeCompilation
? MemberSignatureComparer.CSharpCustomModifierOverrideComparer
: MemberSignatureComparer.RuntimePlusRefOutSignatureComparer;
IEqualityComparer<Symbol> fallbackComparer = memberIsFromSomeCompilation
? MemberSignatureComparer.CSharpOverrideComparer
: MemberSignatureComparer.RuntimeSignatureComparer;
SymbolKind memberKind = member.Kind;
int memberArity = member.GetMemberArity();
foreach (Symbol otherMember in currType.GetMembers(member.Name))
{
if (!IsOverriddenSymbolAccessible(otherMember, memberContainingType))
{
//do nothing
}
else if (otherMember.IsAccessor() && !((MethodSymbol)otherMember).IsIndexedPropertyAccessor())
{
//Indexed property accessors can be overridden or hidden by non-accessors.
//do nothing - no interaction between accessors and non-accessors
}
else if (otherMember.Kind != memberKind)
{
// NOTE: generic methods can hide things with arity 0.
// From CSemanticChecker::FindSymHiddenByMethPropAgg
int otherMemberArity = otherMember.GetMemberArity();
if (otherMemberArity == memberArity || (memberKind == SymbolKind.Method && otherMemberArity == 0))
{
AddHiddenMemberIfApplicable(ref hiddenBuilder, memberKind, otherMember);
}
}
else if (!currTypeHasExactMatch)
{
switch (memberKind)
{
case SymbolKind.Field:
currTypeHasExactMatch = true;
currTypeBestMatch = otherMember;
break;
case SymbolKind.NamedType:
if (otherMember.GetMemberArity() == memberArity)
{
currTypeHasExactMatch = true;
currTypeBestMatch = otherMember;
}
break;
default:
if (otherMember.Equals(knownOverriddenMember, TypeCompareKind.AllIgnoreOptions))
{
currTypeHasExactMatch = true;
currTypeBestMatch = otherMember;
}
// We do not perform signature matching in the presence of a methodimpl
else if (knownOverriddenMember == null)
{
if (exactMatchComparer.Equals(member, otherMember))
{
currTypeHasExactMatch = true;
currTypeBestMatch = otherMember;
}
else if (fallbackComparer.Equals(member, otherMember))
{
// If this method is from source, we'll also consider methods that match
// without regard to custom modifiers. If there's more than one, we'll
// choose the one with the fewest custom modifiers.
int methodCustomModifierCount = CustomModifierCount(otherMember);
if (methodCustomModifierCount < minCustomModifierCount)
{
Debug.Assert(memberIsFromSomeCompilation || minCustomModifierCount == int.MaxValue, "Metadata members require exact custom modifier matches.");
minCustomModifierCount = methodCustomModifierCount;
currTypeBestMatch = otherMember;
}
}
else
{
currTypeHasSameKindNonMatch = true;
}
}
break;
}
}
}
switch (memberKind)
{
case SymbolKind.Field:
case SymbolKind.NamedType:
break;
default:
// If the member is from metadata, then even a fallback match is an exact match.
// We just declined to set the flag at the time in case there was a "better" exact match.
// Having said that, there's no reason to update the flag, since no-one will consume it.
// if (!memberIsFromSomeCompilation && ((object)currTypeBestMatch != null)) currTypeHasExactMatch = true;
// There's a special case where we have to go back and fix up our best match.
// If member is from source, then it has no custom modifiers (at least,
// until it copies them from the member it overrides, which we're trying to
// compute now). Therefore, an exact match will be one that has no custom
// modifiers in/on its parameters. The best match may, however, have custom
// modifiers in/on its (return) type, since that wasn't considered during the
// signature comparison. If that is the case, then we need to make sure that
// there isn't another inexact match (i.e. same signature ignoring custom
// modifiers) with fewer custom modifiers.
// NOTE: If member is constructed, then it has already inherited custom modifiers
// from the underlying member and this cleanup is unnecessary. That's why we're
// checking member.IsDefinition in addition to memberIsFromSomeCompilation.
if (currTypeHasExactMatch && memberIsFromSomeCompilation && member.IsDefinition && TypeOrReturnTypeHasCustomModifiers(currTypeBestMatch))
{
#if DEBUG
// If there were custom modifiers on the parameters, then the match wouldn't have been
// exact and so we would already have applied the custom modifier count as a tie-breaker.
foreach (ParameterSymbol param in currTypeBestMatch.GetParameters())
{
Debug.Assert(!(param.TypeWithAnnotations.CustomModifiers.Any() || param.RefCustomModifiers.Any()));
Debug.Assert(!param.Type.HasCustomModifiers(flagNonDefaultArraySizesOrLowerBounds: false));
}
#endif
Symbol minCustomModifierMatch = currTypeBestMatch;
foreach (Symbol otherMember in currType.GetMembers(member.Name))
{
if (otherMember.Kind == currTypeBestMatch.Kind && !ReferenceEquals(otherMember, currTypeBestMatch))
{
if (MemberSignatureComparer.CSharpOverrideComparer.Equals(otherMember, currTypeBestMatch))
{
int customModifierCount = CustomModifierCount(otherMember);
if (customModifierCount < minCustomModifierCount)
{
minCustomModifierCount = customModifierCount;
minCustomModifierMatch = otherMember;
}
}
}
}
currTypeBestMatch = minCustomModifierMatch;
}
break;
}
}
/// <summary>
/// If representative member is non-null and is contained in a constructed type, then find
/// other members in the same type with the same signature. If this is an override member,
/// add them to the overridden and runtime overridden lists. Otherwise, add them to the
/// hidden list.
/// </summary>
private static void FindRelatedMembers(
bool isOverride,
bool overridingMemberIsFromSomeCompilation,
SymbolKind overridingMemberKind,
Symbol representativeMember,
out ImmutableArray<Symbol> overriddenMembers,
ref ArrayBuilder<Symbol> hiddenBuilder)
{
overriddenMembers = ImmutableArray<Symbol>.Empty;
if ((object)representativeMember != null)
{
bool needToSearchForRelated = representativeMember.Kind != SymbolKind.Field && representativeMember.Kind != SymbolKind.NamedType &&
(!representativeMember.ContainingType.IsDefinition || representativeMember.IsIndexer());
if (isOverride)
{
if (needToSearchForRelated)
{
ArrayBuilder<Symbol> overriddenBuilder = ArrayBuilder<Symbol>.GetInstance();
overriddenBuilder.Add(representativeMember);
FindOtherOverriddenMethodsInContainingType(representativeMember, overridingMemberIsFromSomeCompilation, overriddenBuilder);
overriddenMembers = overriddenBuilder.ToImmutableAndFree();
}
else
{
overriddenMembers = ImmutableArray.Create<Symbol>(representativeMember);
}
}
else
{
AddHiddenMemberIfApplicable(ref hiddenBuilder, overridingMemberKind, representativeMember);
if (needToSearchForRelated)
{
FindOtherHiddenMembersInContainingType(overridingMemberKind, representativeMember, ref hiddenBuilder);
}
}
}
}
/// <summary>
/// Some kinds of methods are not considered to be hideable by certain kinds of members.
/// Specifically, methods, properties, and types cannot hide constructors, destructors,
/// operators, conversions, or accessors.
/// </summary>
private static void AddHiddenMemberIfApplicable(ref ArrayBuilder<Symbol> hiddenBuilder, SymbolKind hidingMemberKind, Symbol hiddenMember)
{
Debug.Assert((object)hiddenMember != null);
if (hiddenMember.Kind != SymbolKind.Method || ((MethodSymbol)hiddenMember).CanBeHiddenByMemberKind(hidingMemberKind))
{
AccessOrGetInstance(ref hiddenBuilder).Add(hiddenMember);
}
}
private static ArrayBuilder<T> AccessOrGetInstance<T>(ref ArrayBuilder<T> builder)
{
if (builder == null)
{
builder = ArrayBuilder<T>.GetInstance();
}
return builder;
}
/// <summary>
/// Having found the best member to override, we want to find members with the same signature on the
/// best member's containing type.
/// </summary>
/// <param name="representativeMember">
/// The member that we consider to be overridden (may have different custom modifiers from the overriding member).
/// Assumed to already be in the overridden and runtime overridden lists.
/// </param>
/// <param name="overridingMemberIsFromSomeCompilation">
/// If the best match was based on the custom modifier count, rather than the custom modifiers themselves
/// (because the overriding member is in the current compilation), then we should use the count when determining
/// whether the override is ambiguous.
/// </param>
/// <param name="overriddenBuilder">
/// If the declaring type is constructed, it's possible that two (or more) members have the same signature
/// (including custom modifiers). Return a list of such members so that we can report the ambiguity.
/// </param>
private static void FindOtherOverriddenMethodsInContainingType(Symbol representativeMember, bool overridingMemberIsFromSomeCompilation, ArrayBuilder<Symbol> overriddenBuilder)
{
Debug.Assert((object)representativeMember != null);
Debug.Assert(representativeMember.Kind == SymbolKind.Property || !representativeMember.ContainingType.IsDefinition);
int representativeCustomModifierCount = -1;
foreach (Symbol otherMember in representativeMember.ContainingType.GetMembers(representativeMember.Name))
{
if (otherMember.Kind == representativeMember.Kind)
{
if (otherMember != representativeMember)
{
// NOTE: If the overriding member is from source, then we compared *counts* of custom modifiers, rather
// than actually comparing custom modifiers. Hence, we should do the same thing when looking for
// ambiguous overrides.
if (overridingMemberIsFromSomeCompilation)
{
if (representativeCustomModifierCount < 0)
{
representativeCustomModifierCount = representativeMember.CustomModifierCount();
}
if (MemberSignatureComparer.CSharpOverrideComparer.Equals(otherMember, representativeMember) &&
otherMember.CustomModifierCount() == representativeCustomModifierCount)
{
overriddenBuilder.Add(otherMember);
}
}
else
{
if (MemberSignatureComparer.CSharpCustomModifierOverrideComparer.Equals(otherMember, representativeMember))
{
overriddenBuilder.Add(otherMember);
}
}
}
}
}
}
/// <summary>
/// Having found that we are hiding a method with exactly the same signature
/// (including custom modifiers), we want to find methods with the same signature
/// on the declaring type because they will also be hidden.
/// (If the declaring type is constructed, it's possible that two or more
/// methods have the same signature (including custom modifiers).)
/// (If the representative member is an indexer, it's possible that two or more
/// properties have the same signature (including custom modifiers, even in a
/// non-generic type).
/// </summary>
/// <param name="hidingMemberKind">
/// This kind of the hiding member.
/// </param>
/// <param name="representativeMember">
/// The member that we consider to be hidden (must have exactly the same custom modifiers as the hiding member).
/// Assumed to already be in hiddenBuilder.
/// </param>
/// <param name="hiddenBuilder">
/// Will have all other members with the same signature (including custom modifiers) as
/// representativeMember added.
/// </param>
private static void FindOtherHiddenMembersInContainingType(SymbolKind hidingMemberKind, Symbol representativeMember, ref ArrayBuilder<Symbol> hiddenBuilder)
{
Debug.Assert((object)representativeMember != null);
Debug.Assert(representativeMember.Kind != SymbolKind.Field);
Debug.Assert(representativeMember.Kind != SymbolKind.NamedType);
Debug.Assert(representativeMember.Kind == SymbolKind.Property || !representativeMember.ContainingType.IsDefinition);
IEqualityComparer<Symbol> comparer = MemberSignatureComparer.CSharpCustomModifierOverrideComparer;
foreach (Symbol otherMember in representativeMember.ContainingType.GetMembers(representativeMember.Name))
{
if (otherMember.Kind == representativeMember.Kind)
{
if (otherMember != representativeMember && comparer.Equals(otherMember, representativeMember))
{
AddHiddenMemberIfApplicable(ref hiddenBuilder, hidingMemberKind, otherMember);
}
}
}
}
private static bool CanOverrideOrHide(Symbol member)
{
switch (member.Kind)
{
case SymbolKind.Property:
case SymbolKind.Event:
// Explicit interface impls don't override or hide.
return !member.IsExplicitInterfaceImplementation();
case SymbolKind.Method:
MethodSymbol methodSymbol = (MethodSymbol)member;
return MethodSymbol.CanOverrideOrHide(methodSymbol.MethodKind) && ReferenceEquals(methodSymbol, methodSymbol.ConstructedFrom);
default:
throw ExceptionUtilities.UnexpectedValue(member.Kind);
}
}
private static bool TypeOrReturnTypeHasCustomModifiers(Symbol member)
{
switch (member.Kind)
{
case SymbolKind.Method:
MethodSymbol method = (MethodSymbol)member;
var methodReturnType = method.ReturnTypeWithAnnotations;
return methodReturnType.CustomModifiers.Any() || method.RefCustomModifiers.Any() ||
methodReturnType.Type.HasCustomModifiers(flagNonDefaultArraySizesOrLowerBounds: false);
case SymbolKind.Property:
PropertySymbol property = (PropertySymbol)member;
var propertyType = property.TypeWithAnnotations;
return propertyType.CustomModifiers.Any() || property.RefCustomModifiers.Any() ||
propertyType.Type.HasCustomModifiers(flagNonDefaultArraySizesOrLowerBounds: false);
case SymbolKind.Event:
EventSymbol @event = (EventSymbol)member;
return @event.Type.HasCustomModifiers(flagNonDefaultArraySizesOrLowerBounds: false); //can't have custom modifiers on (vs in) type
default:
throw ExceptionUtilities.UnexpectedValue(member.Kind);
}
}
private static int CustomModifierCount(Symbol member)
{
switch (member.Kind)
{
case SymbolKind.Method:
MethodSymbol method = (MethodSymbol)member;
return method.CustomModifierCount();
case SymbolKind.Property:
PropertySymbol property = (PropertySymbol)member;
return property.CustomModifierCount();
case SymbolKind.Event:
EventSymbol @event = (EventSymbol)member;
return @event.Type.CustomModifierCount();
default:
throw ExceptionUtilities.UnexpectedValue(member.Kind);
}
}
/// <summary>
/// Determine if this method requires a methodimpl table entry to inform the runtime of the override relationship.
/// </summary>
/// <param name="warnAmbiguous">True if we should produce an ambiguity warning per https://github.com/dotnet/roslyn/issues/45453 .</param>
internal static bool RequiresExplicitOverride(this MethodSymbol method, out bool warnAmbiguous)
{
warnAmbiguous = false;
if (!method.IsOverride)
return false;
MethodSymbol csharpOverriddenMethod = method.OverriddenMethod;
if (csharpOverriddenMethod is null)
return false;
MethodSymbol runtimeOverriddenMethod = method.GetFirstRuntimeOverriddenMethodIgnoringNewSlot(out bool wasAmbiguous);
if (csharpOverriddenMethod == runtimeOverriddenMethod && !wasAmbiguous)
return false;
// See https://github.com/dotnet/roslyn/issues/45453. No need to warn when the runtime
// supports covariant returns because any methodimpl we produce to identify the specific
// overridden method is unambiguously understood by the runtime.
if (method.ContainingAssembly.RuntimeSupportsCovariantReturnsOfClasses)
return true;
// If the method was declared as a covariant return, there will be a compile-time error since the runtime
// does not support covariant returns. In this case we do not warn about runtime ambiguity and pretend that
// we can use a methodimpl (even though it is of a form not supported by the runtime and would result in a
// loader error) so that the symbol APIs produce the most useful result.
if (!method.ReturnType.Equals(csharpOverriddenMethod.ReturnType, TypeCompareKind.AllIgnoreOptions))
return true;
// Due to https://github.com/dotnet/runtime/issues/38119 the methodimpl would
// appear to the runtime to be ambiguous in some cases.
bool methodimplWouldBeAmbiguous = csharpOverriddenMethod.MethodHasRuntimeCollision();
if (!methodimplWouldBeAmbiguous)
return true;
Debug.Assert(runtimeOverriddenMethod is { });
// We produce the warning when a methodimpl would be required but would be ambiguous to the runtime.
// However, if there was a duplicate definition for the runtime signature of the overridden
// method where it was originally declared, that would have been an error. In that case we suppress
// the warning as a cascaded diagnostic.
bool originalOverriddenMethodWasAmbiguous =
csharpOverriddenMethod.IsDefinition || csharpOverriddenMethod.OriginalDefinition.MethodHasRuntimeCollision();
warnAmbiguous = !originalOverriddenMethodWasAmbiguous;
bool overriddenMethodContainedInSameTypeAsRuntimeOverriddenMethod =
csharpOverriddenMethod.ContainingType.Equals(runtimeOverriddenMethod.ContainingType, TypeCompareKind.CLRSignatureCompareOptions);
// If the overridden method is on a different (e.g. base) type compared to the runtime overridden
// method, then the runtime overridden method could not possibly resolve correctly to the overridden method.
// In this case we might as well produce a methodimpl. At least it has a chance of being correctly resolved
// by the runtime, where the runtime resolution without the methodimpl would definitely be wrong.
if (!overriddenMethodContainedInSameTypeAsRuntimeOverriddenMethod)
return true;
// This is the historical test, preserved since the days of the native compiler in case it turns out to affect compatibility.
// However, this test cannot be true in a program free of errors.
return csharpOverriddenMethod != runtimeOverriddenMethod && method.IsAccessor() != runtimeOverriddenMethod.IsAccessor();
}
internal static bool MethodHasRuntimeCollision(this MethodSymbol method)
{
foreach (Symbol otherMethod in method.ContainingType.GetMembers(method.Name))
{
if (otherMethod != method && MemberSignatureComparer.RuntimeSignatureComparer.Equals(otherMethod, method))
{
return true;
}
}
return false;
}