SourceMemberContainerTypeSymbol.vb 211.6 KB
Newer Older
1
' Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
P
Pilchie 已提交
2 3

Imports System.Collections.Immutable
4
Imports System.Linq
P
Pilchie 已提交
5 6 7 8 9 10 11 12
Imports System.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax

Namespace Microsoft.CodeAnalysis.VisualBasic.Symbols

    ''' <summary>
13
    ''' Represents a named type symbol whose members are declared in source.
P
Pilchie 已提交
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
    ''' </summary>
    Friend MustInherit Class SourceMemberContainerTypeSymbol
        Inherits InstanceTypeSymbol

        ''' <summary>
        ''' Holds information about a SourceType in a compact form.
        ''' </summary>
        <Flags>
        Friend Enum SourceTypeFlags As UShort
            [Private] = CUShort(Accessibility.Private)
            [Protected] = CUShort(Accessibility.Protected)
            [Friend] = CUShort(Accessibility.Friend)
            ProtectedFriend = CUShort(Accessibility.ProtectedOrFriend)
            [Public] = CUShort(Accessibility.Public)
            AccessibilityMask = &H7

            [Class] = CUShort(TypeKind.Class) << TypeKindShift
            [Structure] = CUShort(TypeKind.Structure) << TypeKindShift
            [Interface] = CUShort(TypeKind.Interface) << TypeKindShift
            [Enum] = CUShort(TypeKind.Enum) << TypeKindShift
            [Delegate] = CUShort(TypeKind.Delegate) << TypeKindShift
            [Module] = CUShort(TypeKind.Module) << TypeKindShift
            Submission = CUShort(TypeKind.Submission) << TypeKindShift
            TypeKindMask = &HF0
            TypeKindShift = 4

            [MustInherit] = 1 << 8
            [NotInheritable] = 1 << 9
            [Shadows] = 1 << 10
            [Partial] = 1 << 11
        End Enum

        ' Flags about the type
        Private ReadOnly m_flags As SourceTypeFlags

        ' Misc flags defining the state of this symbol (StateFlags)
        Protected m_lazyState As Integer

        <Flags>
        Protected Enum StateFlags As Integer
            FlattenedMembersIsSortedMask = &H1   ' Set if "m_lazyMembersFlattened" is sorted.
            ReportedVarianceDiagnostics = &H2    ' Set if variance diagnostics have been reported.
            ReportedBaseClassConstraintsDiagnostics = &H4    ' Set if base class constraints diagnostics have been reported.
            ReportedInterfacesConstraintsDiagnostics = &H8    ' Set if constraints diagnostics for base/implemented interfaces have been reported.
        End Enum

        ' Containing symbol
        Private ReadOnly m_containingSymbol As NamespaceOrTypeSymbol

        ' Containing source module
        Protected ReadOnly m_containingModule As SourceModuleSymbol

        ' The declaration for this type.
        Private ReadOnly m_declaration As MergedTypeDeclaration

        ' The name of the type, might be different than m_decl.Name depending on lexical sort order.
        Private ReadOnly m_name As String

        ' The name of the default property if any.
        ' GetMembersAndInitializers must be called before accessing field.
        Private m_defaultPropertyName As String

        ' The different kinds of members of this type
        Private m_lazyMembersAndInitializers As MembersAndInitializers

        ' Maps names to nested type symbols.
        Private Shared ReadOnly m_emptyTypeMembers As New Dictionary(Of String, ImmutableArray(Of NamedTypeSymbol))(IdentifierComparison.Comparer)
        Private m_lazyTypeMembers As Dictionary(Of String, ImmutableArray(Of NamedTypeSymbol))

        ' An array of members in declaration order.
        Private m_lazyMembersFlattened As ImmutableArray(Of Symbol)

        ' Type parameters (Nothing if not created yet)
        Private m_lazyTypeParameters As ImmutableArray(Of TypeParameterSymbol)

        Private m_lazyEmitExtensionAttribute As ThreeState = ThreeState.Unknown
        Private m_lazyContainsExtensionMethods As ThreeState = ThreeState.Unknown
        Private m_lazyAnyMemberHasAttributes As ThreeState = ThreeState.Unknown

        Private m_lazyStructureCycle As Integer = ThreeState.Unknown  ' Interlocked

        Private m_lazyLexicalSortKey As LexicalSortKey = LexicalSortKey.NotInitialized

#Region "Construction"

        ' Create the type symbol and associated type parameter symbols. Most information
        ' is deferred until later.
        Protected Sub New(declaration As MergedTypeDeclaration,
                          containingSymbol As NamespaceOrTypeSymbol,
                          containingModule As SourceModuleSymbol)

            m_containingModule = containingModule
            m_containingSymbol = containingSymbol
            m_declaration = declaration
            m_name = GetBestName(declaration, containingModule.ContainingSourceAssembly.DeclaringCompilation)
            m_flags = ComputeTypeFlags(declaration, containingSymbol.IsNamespace)
        End Sub

        ' Figure out the "right" name spelling, it should come from lexically first declaration.
A
angocke 已提交
113
        Private Shared Function GetBestName(declaration As MergedTypeDeclaration, compilation As VisualBasicCompilation) As String
P
Pilchie 已提交
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
            Dim declarations As ImmutableArray(Of SingleTypeDeclaration) = declaration.Declarations
            Dim best As SingleTypeDeclaration = declarations(0)

            For i As Integer = 1 To declarations.Length - 1
                Dim bestLocation As Location = best.Location
                If compilation.FirstSourceLocation(bestLocation, declarations(i).Location) IsNot bestLocation Then
                    best = declarations(i)
                End If
            Next

            Return best.Name
        End Function

        ''' <summary>
        ''' Compute the type flags from the declaration.
        ''' This function DOES NOT diagnose errors in the modifiers. Given the set of modifiers,
        ''' it produces the flags, even in the case of potentially conflicting modifiers. We have to
        ''' return some answer even in the case of errors.
        ''' </summary>
        Private Function ComputeTypeFlags(declaration As MergedTypeDeclaration, isTopLevel As Boolean) As SourceTypeFlags
            Dim mergedModifiers As DeclarationModifiers = DeclarationModifiers.None
            For i = 0 To declaration.Declarations.Length - 1
                mergedModifiers = mergedModifiers Or declaration.Declarations(i).Modifiers
            Next

            Dim modifiers = mergedModifiers
            Dim flags As SourceTypeFlags = 0

            ' compute type kind, inheritability
            Select Case declaration.Kind
                Case DeclarationKind.Class
                    flags = SourceTypeFlags.Class
                    If (modifiers And DeclarationModifiers.NotInheritable) <> 0 Then
                        flags = flags Or SourceTypeFlags.NotInheritable
                    ElseIf (modifiers And DeclarationModifiers.MustInherit) <> 0 Then
                        flags = flags Or SourceTypeFlags.MustInherit
                    End If

                Case DeclarationKind.Script, DeclarationKind.ImplicitClass
                    flags = SourceTypeFlags.Class Or SourceTypeFlags.NotInheritable

                Case DeclarationKind.Submission
                    flags = SourceTypeFlags.Submission Or SourceTypeFlags.NotInheritable

                Case DeclarationKind.Structure
                    flags = SourceTypeFlags.Structure Or SourceTypeFlags.NotInheritable

                Case DeclarationKind.Interface
                    flags = SourceTypeFlags.Interface Or SourceTypeFlags.MustInherit

                Case DeclarationKind.Enum
                    flags = SourceTypeFlags.Enum Or SourceTypeFlags.NotInheritable

                Case DeclarationKind.Delegate,
                    DeclarationKind.EventSyntheticDelegate
                    flags = SourceTypeFlags.Delegate Or SourceTypeFlags.NotInheritable

                Case DeclarationKind.Module
                    flags = SourceTypeFlags.Module Or SourceTypeFlags.NotInheritable

                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(declaration.Kind)
            End Select

            ' compute accessibility
            If isTopLevel Then
                ' top-level types (types in namespaces) can only be Friend or Public, and the default is friend
                If (modifiers And DeclarationModifiers.Friend) <> 0 Then
                    flags = flags Or SourceTypeFlags.Friend
                ElseIf (modifiers And DeclarationModifiers.Public) <> 0 Then
                    flags = flags Or SourceTypeFlags.Public
                Else
                    flags = flags Or SourceTypeFlags.Friend
                End If
            Else
                ' Nested types (including types in modules) can be any accessibility, and the default is public
                If (modifiers And DeclarationModifiers.Private) <> 0 Then
                    flags = flags Or SourceTypeFlags.Private
                ElseIf (modifiers And (DeclarationModifiers.Protected Or DeclarationModifiers.Friend)) =
                                      (DeclarationModifiers.Protected Or DeclarationModifiers.Friend) Then
                    flags = flags Or SourceTypeFlags.ProtectedFriend
                ElseIf (modifiers And DeclarationModifiers.Protected) <> 0 Then
                    flags = flags Or SourceTypeFlags.Protected
                ElseIf (modifiers And DeclarationModifiers.Friend) <> 0 Then
                    flags = flags Or SourceTypeFlags.Friend
                Else
                    flags = flags Or SourceTypeFlags.Public
                End If
            End If

            ' Compute partial
            If (modifiers And DeclarationModifiers.Partial) <> 0 Then
                flags = flags Or SourceTypeFlags.Partial
            End If

            ' Compute Shadows
            If (modifiers And DeclarationModifiers.Shadows) <> 0 Then
                flags = flags Or SourceTypeFlags.Shadows
            End If
            Return flags
        End Function

        Public Shared Function Create(declaration As MergedTypeDeclaration,
                                      containingSymbol As NamespaceOrTypeSymbol,
                                      containingModule As SourceModuleSymbol) As SourceMemberContainerTypeSymbol

            Dim kind = declaration.SyntaxReferences.First.SyntaxTree.GetEmbeddedKind()

222
            If kind <> EmbeddedSymbolKind.None Then
P
Pilchie 已提交
223 224 225 226
                Return New EmbeddedSymbolManager.EmbeddedNamedTypeSymbol(declaration, containingSymbol, containingModule, kind)
            End If

            Select Case declaration.Kind
227 228 229
                Case DeclarationKind.ImplicitClass,
                    DeclarationKind.Script,
                    DeclarationKind.Submission
P
Pilchie 已提交
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
                    Return New ImplicitNamedTypeSymbol(declaration, containingSymbol, containingModule)

                Case Else
                    Dim type = New SourceNamedTypeSymbol(declaration, containingSymbol, containingModule)

                    ' In case Vb Core Runtime is being embedded, we should mark attribute 
                    ' 'Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute'
                    ' as being referenced if the named type just created is a module
                    If type.TypeKind = TypeKind.Module Then
                        type.DeclaringCompilation.EmbeddedSymbolManager.RegisterModuleDeclaration()
                    End If

                    Return type
            End Select
        End Function

        ' Create a nested type with the given declaration.
        Private Function CreateNestedType(declaration As MergedTypeDeclaration) As NamedTypeSymbol
#If DEBUG Then
            ' Ensure that the type declaration is either from user code or embedded
            ' code, but not merged accross embedded code/user code boundary.
            Dim embedded = EmbeddedSymbolKind.Unset
            For Each ref In declaration.SyntaxReferences
                Dim refKind = ref.SyntaxTree.GetEmbeddedKind()
                If embedded <> EmbeddedSymbolKind.Unset Then
                    Debug.Assert(embedded = refKind)
                Else
                    embedded = refKind
                End If
            Next
            Debug.Assert(embedded <> EmbeddedSymbolKind.Unset)
#End If

            If declaration.Kind = DeclarationKind.Delegate Then
                Debug.Assert(Not declaration.SyntaxReferences.First.SyntaxTree.IsEmbeddedSyntaxTree)
                Return New SourceNamedTypeSymbol(declaration, Me, m_containingModule)
            ElseIf declaration.Kind = DeclarationKind.EventSyntheticDelegate Then
                Debug.Assert(Not declaration.SyntaxReferences.First.SyntaxTree.IsEmbeddedSyntaxTree)
                Return New SynthesizedEventDelegateSymbol(declaration.SyntaxReferences(0), Me)
            Else
                Return Create(declaration, Me, m_containingModule)
            End If
        End Function
#End Region

#Region "Completion"

        ''' <summary>
        ''' Force all declaration errors to be generated.
        ''' </summary>
        Friend NotOverridable Overrides Sub GenerateDeclarationErrors(cancellationToken As CancellationToken)
            GenerateAllDeclarationErrorsImpl(cancellationToken)
        End Sub

        Protected Overridable Sub GenerateAllDeclarationErrorsImpl(cancellationToken As CancellationToken)
            cancellationToken.ThrowIfCancellationRequested()

            Dim membersAndInitializers = GetMembersAndInitializers()

            cancellationToken.ThrowIfCancellationRequested()

            For Each member In Me.GetMembers()
                ' we already visited types
                If member.Kind <> SymbolKind.NamedType Then
                    member.GenerateDeclarationErrors(cancellationToken)
                End If
            Next

            cancellationToken.ThrowIfCancellationRequested()
            Dim unused1 = BaseTypeNoUseSiteDiagnostics

            cancellationToken.ThrowIfCancellationRequested()
            Dim unused2 = InterfacesNoUseSiteDiagnostics

            cancellationToken.ThrowIfCancellationRequested()
            Dim unused3 = ExplicitInterfaceImplementationMap

            cancellationToken.ThrowIfCancellationRequested()
            Dim typeParams = TypeParameters
            If Not typeParams.IsEmpty Then
                TypeParameterSymbol.EnsureAllConstraintsAreResolved(typeParams)
            End If

            cancellationToken.ThrowIfCancellationRequested()
            Dim unused4 = GetAttributes()

            cancellationToken.ThrowIfCancellationRequested()
            BindAllMemberAttributes(cancellationToken)

            cancellationToken.ThrowIfCancellationRequested()
            GenerateVarianceDiagnostics()
        End Sub

        Private Sub GenerateVarianceDiagnostics()
            If (m_lazyState And StateFlags.ReportedVarianceDiagnostics) <> 0 Then
                Return
            End If

            Dim diagnostics As DiagnosticBag = Nothing
            Dim infosBuffer As ArrayBuilder(Of DiagnosticInfo) = Nothing

            Select Case Me.TypeKind
                Case TypeKind.Interface
                    GenerateVarianceDiagnosticsForInterface(diagnostics, infosBuffer)

                Case TypeKind.Delegate
                    GenerateVarianceDiagnosticsForDelegate(diagnostics, infosBuffer)

                Case TypeKind.Class, TypeKind.Enum, TypeKind.Structure
                    ReportNestingIntoVariantInterface(diagnostics)

                Case TypeKind.Module, TypeKind.Submission
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(Me.TypeKind)
            End Select

            m_containingModule.AtomicSetFlagAndStoreDiagnostics(m_lazyState,
                                                                StateFlags.ReportedVarianceDiagnostics,
                                                                0,
                                                                diagnostics,
                                                                CompilationStage.Declare)

            If diagnostics IsNot Nothing Then
                diagnostics.Free()
            End If

            If infosBuffer IsNot Nothing Then
                ' all diagnostics were reported to diagnostic bag:
                Debug.Assert(infosBuffer.Count = 0)
                infosBuffer.Free()
            End If
        End Sub

        Private Sub ReportNestingIntoVariantInterface(<[In], Out> ByRef diagnostics As DiagnosticBag)
            If Not m_containingSymbol.IsType Then
                Return
            End If

            ' Check for illegal nesting into variant interface.
            Dim container = DirectCast(m_containingSymbol, NamedTypeSymbol)

            Do
                If Not container.IsInterfaceType() Then
                    Debug.Assert(Not container.IsDelegateType())
                    ' The same validation will be performed for the container and 
                    ' there is no reason to duplicate the same errors, if any, on this type.
                    container = Nothing
                    Exit Do
                End If

                If container.TypeParameters.HaveVariance() Then
                    ' We are inside of a variant interface
                    Exit Do
                End If

                ' This interface isn't variant, but its containing interface might be.
                container = container.ContainingType
            Loop While container IsNot Nothing

            If container IsNot Nothing Then
                Debug.Assert(container.IsInterfaceType() AndAlso container.HasVariance())
                If diagnostics Is Nothing Then
                    diagnostics = DiagnosticBag.GetInstance()
                End If

                diagnostics.Add(New VBDiagnostic(ErrorFactory.ErrorInfo(ERRID.ERR_VarianceInterfaceNesting), Locations(0)))
            End If
        End Sub

        Private Sub GenerateVarianceDiagnosticsForInterface(
            <[In], Out> ByRef diagnostics As DiagnosticBag,
            <[In], Out> ByRef infosBuffer As ArrayBuilder(Of DiagnosticInfo)
        )
            ' Dev10 didn't do this shortcut, but I and Lucian believe that the checks below
            ' can be safely skipped for an invariant interface.
            If Not Me.HasVariance() Then
                Return
            End If

            ' Variance spec $2:
            ' An interface I is valid if and only if
            '  * every method signature in I is valid and has no variant generic parameters (no variant generic
            '    parameters part is handled by SourceMethodSymbol.BindTypeParameterConstraints), and
            '  * every property in I is valid, and
            '  * every event in I is valid, and
            '  * every immediate base interface type of I is valid covariantly, and
            '  * the interface is either invariant or it lacks nested classes and structs, and
            '  * every nested type is valid.
            '
            ' A property "Property Foo as T" is valid if and only if either
            '  * The property is read-only and T is valid covariantly, or
            '  * The property is write-only and T is valid invariantly, or
            '  * The property is readable and writable and T is invariant.
            '
            ' An event "Event e as D" is valid if and only if
            '  * the delegate type D is valid contravariantly
            '
            ' An event "Event e(Signature)" is valid if and only if
            '  * it is not contained (not even nested) in a variant interface, 
            '    this is handled by SynthesizedEventDelegateSymbol.
            '
            ' The test that nested types are valid isn't needed, since GenerateVarianceDiagnostics
            ' will anyways be called on them all. (and for any invalid nested type, we report the
            ' error on it, rather than on this container.)
            '
            ' The check that an interface lacks nested classes and structs is done inside
            ' ReportNestingIntoVariantInterface. Why? Because we have to look for indirectly
            ' nested classes/structs, not just immediate ones. And it seemed nicer for classes/structs
            ' to look UP for variant containers, rather than for interfaces to look DOWN for class/struct contents.

            For Each batch As ImmutableArray(Of Symbol) In GetMembersAndInitializers().Members.Values
                For Each member As Symbol In batch
                    If Not member.IsImplicitlyDeclared Then
                        Select Case member.Kind
                            Case SymbolKind.Method
                                GenerateVarianceDiagnosticsForMethod(DirectCast(member, MethodSymbol), diagnostics, infosBuffer)
                                Debug.Assert(Not HaveDiagnostics(infosBuffer))

                            Case SymbolKind.Property
                                GenerateVarianceDiagnosticsForProperty(DirectCast(member, PropertySymbol), diagnostics, infosBuffer)
                                Debug.Assert(Not HaveDiagnostics(infosBuffer))

                            Case SymbolKind.Event
                                GenerateVarianceDiagnosticsForEvent(DirectCast(member, EventSymbol), diagnostics, infosBuffer)
                                Debug.Assert(Not HaveDiagnostics(infosBuffer))

                        End Select
                    End If
                Next
            Next

            ' 3. every immediate base interface is valid covariantly.
            ' Actually, the only type that's invalid covariantly and allowable as a base interface,
            ' is a generic instantiation X(T1,...) where we've instantiated it wrongly (e.g. given it "Out Ti"
            ' for a generic parameter that was declared as an "In"). Look what happens:
            ' Interface IZoo(Of In T)     | Dim x as IZoo(Of Animal)
            '   Inherits IReadOnly(Of T)  | Dim y as IZoo(Of Mammal) = x   ' through contravariance of IZoo
            ' End Interface               | Dim z as IReadOnly(Of Mammal) = y  ' through inheritance from IZoo
            ' Now we might give "z" to someone who's expecting to read only Mammals, even though we know the zoo
            ' contains all kinds of animals.
            For Each implemented As NamedTypeSymbol In Me.InterfacesNoUseSiteDiagnostics
                If Not implemented.IsErrorType() Then
                    Debug.Assert(Not HaveDiagnostics(infosBuffer))
                    GenerateVarianceDiagnosticsForType(implemented, VarianceKind.Out, VarianceContext.Complex, infosBuffer)
                    If HaveDiagnostics(infosBuffer) Then
                        ReportDiagnostics(diagnostics, GetInheritsOrImplementsLocation(implemented, getInherits:=True), infosBuffer)
                    End If
                End If
            Next
        End Sub

        ' Gets the implements location for a particular interface, which must be implemented but might be indirectly implemented.
        ' Also gets the direct interface it was inherited through
483 484
        Private Function GetImplementsLocation(implementedInterface As NamedTypeSymbol, ByRef directInterface As NamedTypeSymbol) As Location
            Debug.Assert(Me.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics.Contains(implementedInterface))
P
Pilchie 已提交
485 486

            ' Find the directly implemented interface that "implementedIface" was inherited through.
487
            directInterface = Nothing
P
Pilchie 已提交
488 489

            For Each iface In Me.InterfacesNoUseSiteDiagnostics
490 491
                If iface = implementedInterface Then
                    directInterface = iface
P
Pilchie 已提交
492
                    Exit For
493 494
                ElseIf directInterface Is Nothing AndAlso iface.ImplementsInterface(implementedInterface, useSiteDiagnostics:=Nothing) Then
                    directInterface = iface
P
Pilchie 已提交
495 496 497
                End If
            Next

498
            Debug.Assert(directInterface IsNot Nothing)
P
Pilchie 已提交
499

500
            Return GetInheritsOrImplementsLocation(directInterface, Me.IsInterfaceType())
P
Pilchie 已提交
501 502
        End Function

503
        Private Function GetImplementsLocation(implementedInterface As NamedTypeSymbol) As Location
P
Pilchie 已提交
504
            Dim dummy As NamedTypeSymbol = Nothing
505
            Return GetImplementsLocation(implementedInterface, dummy)
P
Pilchie 已提交
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 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265
        End Function

        Protected MustOverride Function GetInheritsOrImplementsLocation(base As NamedTypeSymbol, getInherits As Boolean) As Location

        Private Sub GenerateVarianceDiagnosticsForDelegate(
            <[In], Out> ByRef diagnostics As DiagnosticBag,
            <[In], Out> ByRef infosBuffer As ArrayBuilder(Of DiagnosticInfo)
        )
            ' Dev10 didn't do this shortcut, but I and Lucian believe that the checks below
            ' can be safely skipped for an invariant interface.
            If Not Me.HasVariance() Then
                Return
            End If

            ' Variance spec $2
            ' A delegate "Delegate Function/Sub Foo(Of T1, ... Tn)Signature" is valid if and only if
            '  * the signature is valid.
            '
            ' That delegate becomes "Class Foo(Of T1, ... Tn) : Function Invoke(...) As ... : End Class
            ' So we just need to pick up the "Invoke" method and check that it's valid.
            ' NB. that delegates can have variance in their generic params, and hence so can e.g. "Class Foo(Of Out T1)"
            ' This is the only place in the CLI where a class can have variant generic params.
            '
            ' Note: delegates that are synthesized from events are already dealt with in
            ' SynthesizedEventDelegateSymbol.GenerateAllDeclarationErrors, so we won't run into them here.

            Dim invoke As MethodSymbol = Me.DelegateInvokeMethod

            If invoke IsNot Nothing Then
                GenerateVarianceDiagnosticsForMethod(invoke, diagnostics, infosBuffer)
            End If
        End Sub

        Private Shared Sub ReportDiagnostics(
            <[In], Out> ByRef diagnostics As DiagnosticBag,
            location As Location,
            infos As ArrayBuilder(Of DiagnosticInfo)
        )
            If diagnostics Is Nothing Then
                diagnostics = DiagnosticBag.GetInstance()
            End If

            For Each info In infos
                diagnostics.Add(info, location)
            Next

            infos.Clear()
        End Sub

        Private Shared Function HaveDiagnostics(diagnostics As ArrayBuilder(Of DiagnosticInfo)) As Boolean
            Return diagnostics IsNot Nothing AndAlso diagnostics.Count > 0
        End Function

        ''' <summary>
        ''' Following enum is used just to help give more specific error messages.
        ''' </summary>
        Private Enum VarianceContext
            ' We'll give specific error messages in these simple contexts:
            [ByVal]
            [ByRef]
            [Return]
            [Constraint]
            Nullable
            ReadOnlyProperty
            WriteOnlyProperty
            [Property]

            ' Otherwise (e.g. nested inside a generic) we use the following as a catch-all:
            Complex
        End Enum

        Private Sub GenerateVarianceDiagnosticsForType(
            type As TypeSymbol,
            requiredVariance As VarianceKind,
            context As VarianceContext,
            <[In], Out> ByRef diagnostics As ArrayBuilder(Of DiagnosticInfo)
        )
            GenerateVarianceDiagnosticsForTypeRecursively(type, requiredVariance, context, Nothing, 0, diagnostics)
        End Sub

        Private Shared Sub AppendVarianceDiagnosticInfo(
            <[In], Out> ByRef diagnostics As ArrayBuilder(Of DiagnosticInfo),
            info As DiagnosticInfo
        )
            If diagnostics Is Nothing Then
                diagnostics = ArrayBuilder(Of DiagnosticInfo).GetInstance()
            End If

            diagnostics.Add(info)
        End Sub

        Private Structure VarianceDiagnosticsTargetTypeParameter
            Public ReadOnly ConstructedType As NamedTypeSymbol
            Private ReadOnly m_TypeParameterIndex As Integer

            Public ReadOnly Property TypeParameter As TypeParameterSymbol
                Get
                    Return ConstructedType.TypeParameters(m_TypeParameterIndex)
                End Get
            End Property

            Public Sub New(constructedType As NamedTypeSymbol, typeParameterIndex As Integer)
                Debug.Assert(typeParameterIndex >= 0 AndAlso typeParameterIndex < constructedType.Arity)
                Me.ConstructedType = constructedType
                m_TypeParameterIndex = typeParameterIndex
            End Sub
        End Structure

        Private Sub GenerateVarianceDiagnosticsForTypeRecursively(
            type As TypeSymbol,
            requiredVariance As VarianceKind,
            context As VarianceContext,
            typeParameterInfo As VarianceDiagnosticsTargetTypeParameter,
            constructionDepth As Integer,
            <[In], Out> ByRef diagnostics As ArrayBuilder(Of DiagnosticInfo)
        )
            ' Variance spec $2:
            '
            ' A type T is valid invariantly if and only if:
            '  * it is valid covariantly, and
            '  * it is valid contravariantly.
            '
            ' A type T is valid covariantly if and only if one of the following hold: either
            '  * T is a generic parameter which was not declared contravariant, or
            '  * T is an array type U() where U is valid covariantly, or
            '  * T is a construction X1(Of T11...)...Xn(Of Tn1...) of some generic struct/class/interface/delegate Xn
            '    declared as X1(Of X11...)...Xn(Of Xn1...) such that for each i and j,
            '      - if Xij was declared covariant then Tij is valid covariantly
            '      - if Xij was declared contravariant then Tij is valid contravariantly
            '      - if Xij was declared invariant then Tij is valid invariantly
            '  * or T is a non-generic struct/class/interface/delegate/enum.
            '
            ' A type T is valid contravariantly if and only if one of the following hold: either
            '  * T is a generic parameter which was not declared covariant, or
            '  * T is an array type U() where U is valid contravariantly, or
            '  * T is a construction X1(Of T11...)...Xn(Of Tn1...) of some generic struct/class/interface/delegate Xn
            '    declared as X1(Of X11...)...Xn(Of Xn1...) such that for each i and j,
            '      - if Xij was declared covariant then Tij is valid contravariantly
            '      - if Xij was declared contravariant then Tij is valid covariantly
            '      - if Xij was declared invariant then Tij is valid invariantly
            '  * or T is a non-generic struct/class/interface/delegate/enum.
            '
            '
            ' In all cases, if a type fails a variance validity check, then it ultimately failed
            ' because somewhere there were one or more generic parameters "T" which were declared with
            ' the wrong kind of variance. In particular, they were either declared In when they'd have
            ' to be Out or InOut, or they were declared Out when they'd have to be In or InOut.
            ' We mark all these as errors.
            '
            ' BUT... CLS restrictions say that in any generic type, all nested types first copy their
            ' containers's generic parameters. This restriction is embodied in the BCSYM structure.
            '    SOURCE:                        BCSYM:                               IL:
            '    Interface I(Of Out T1)         Interface"I"/genericparams=T1        .interface I(Of Out T1)
            '      Interface J : End Interface    Interface"J"/no genericparams         .interface J(Of Out T1)
            '      Sub f(ByVal x as J)            ... GenericTypeBinding(J,args=[],       .proc f(x As J(Of T1))
            '    End Interface                                     parentargs=I[T1])
            ' Observe that, by construction, any time we use a nested type like J in a contravariant position
            ' then it's bound to be invalid. If we simply applied the previous paragraph then we'd emit a
            ' confusing error to the user like "J is invalid because T1 is an Out parameter". So we want
            ' to do a better job of reporting errors. In particular,
            '   * If we are checking a GenericTypeBinding (e.g. x as J(Of T1)) for contravariant validity, look up
            '     to find the outermost ancester binding (e.g. parentargs=I[T1]) which is of a variant interface.
            '     If this is also the outermost variant container of the current context, then it's an error.

            Select Case type.Kind
                Case SymbolKind.TypeParameter
                    ' 1. if T is a generic parameter which was declared wrongly
                    Dim typeParam = DirectCast(type, TypeParameterSymbol)

                    If (typeParam.Variance = VarianceKind.Out AndAlso requiredVariance <> VarianceKind.Out) OrElse
                        (typeParam.Variance = VarianceKind.In AndAlso requiredVariance <> VarianceKind.In) Then

                        ' The error is either because we have an "Out" param and Out is inappropriate here,
                        ' or we used an "In" param and In is inappropriate here. This flag says which:
                        Dim inappropriateOut As Boolean = (typeParam.Variance = VarianceKind.Out)

                        ' OKAY, so now we need to report an error. Simple enough, but we've tried to give helpful
                        ' context-specific error messages to the user, and so the code has to work through a lot
                        ' of special cases.

                        Select Case context
                            Case VarianceContext.ByVal
                                ' "Type '|1' cannot be used as a ByVal parameter type because '|1' is an 'Out' type parameter."
                                Debug.Assert(inappropriateOut, "unexpected: an variance error in ByVal must be due to an inappropriate out")
                                AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceOutByValDisallowed1, type.Name))

                            Case VarianceContext.ByRef
                                ' "Type '|1' cannot be used in this context because 'In' and 'Out' type parameters cannot be used for ByRef parameter types, and '|1' is an 'Out/In' type parameter."
                                AppendVarianceDiagnosticInfo(diagnostics,
                                                             ErrorFactory.ErrorInfo(If(inappropriateOut,
                                                                                       ERRID.ERR_VarianceOutByRefDisallowed1,
                                                                                       ERRID.ERR_VarianceInByRefDisallowed1),
                                                                                    type.Name))

                            Case VarianceContext.Return
                                ' "Type '|1' cannot be used as a return type because '|1' is an 'In' type parameter."
                                Debug.Assert(Not inappropriateOut, "unexpected: a variance error in Return Type must be due to an inappropriate in")
                                AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceInReturnDisallowed1, type.Name))

                            Case VarianceContext.Constraint
                                ' "Type '|1' cannot be used as a generic type constraint because '|1' is an 'Out' type parameter."
                                Debug.Assert(inappropriateOut, "unexpected: a variance error in Constraint must be due to an inappropriate out")
                                AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceOutConstraintDisallowed1, type.Name))

                            Case VarianceContext.Nullable
                                ' "Type '|1' cannot be used in '|2' because 'In' and 'Out' type parameters cannot be made nullable, and '|1' is an 'In/Out' type parameter."
                                AppendVarianceDiagnosticInfo(diagnostics,
                                                             ErrorFactory.ErrorInfo(If(inappropriateOut,
                                                                                       ERRID.ERR_VarianceOutNullableDisallowed2,
                                                                                       ERRID.ERR_VarianceInNullableDisallowed2),
                                                                                    type.Name,
                                                                                    CustomSymbolDisplayFormatter.QualifiedName(typeParameterInfo.ConstructedType)))

                            Case VarianceContext.ReadOnlyProperty
                                ' "Type '|1' cannot be used as a ReadOnly property type because '|1' is an 'In' type parameter."
                                Debug.Assert(Not inappropriateOut, "unexpected: a variance error in ReadOnlyProperty must be due to an inappropriate in")
                                AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceInReadOnlyPropertyDisallowed1, type.Name))

                            Case VarianceContext.WriteOnlyProperty
                                ' "Type '|1' cannot be used as a WriteOnly property type because '|1' is an 'Out' type parameter."
                                Debug.Assert(inappropriateOut, "unexpected: a variance error in WriteOnlyProperty must be due to an inappropriate out")
                                AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceOutWriteOnlyPropertyDisallowed1, type.Name))

                            Case VarianceContext.Property
                                ' "Type '|1' cannot be used as a property type in this context because '|1' is an 'Out/In' type parameter and the property is not marked ReadOnly/WriteOnly.")
                                AppendVarianceDiagnosticInfo(diagnostics,
                                                             ErrorFactory.ErrorInfo(If(inappropriateOut,
                                                                                       ERRID.ERR_VarianceOutPropertyDisallowed1,
                                                                                       ERRID.ERR_VarianceInPropertyDisallowed1),
                                                                                    type.Name))

                            Case VarianceContext.Complex

                                ' Otherwise, we're in "VarianceContextComplex" property. And so the error message needs
                                ' to spell out precisely where in the context we are:
                                ' "Type '|1' cannot be used in this context because '|1' is an 'Out|In' type parameter."
                                ' "Type '|1' cannot be used for the '|2' in '|3' in this context because '|1' is an 'Out|In' type parameter."
                                ' "Type '|1' cannot be used in '|2' in this context because '|1' is an 'Out|In' type parameter."
                                ' "Type '|1' cannot be used in '|2' for the '|3' in '|4' in this context because '|1' is an 'Out' type parameter."
                                ' We need the "in '|2' here" clause when ErrorBindingIsNested, to show which instantiation we're talking about.
                                ' We need the "for the '|3' in '|4'" when ErrorBinding->GetGenericParamCount()>1

                                If typeParameterInfo.ConstructedType Is Nothing Then
                                    ' "Type '|1' cannot be used in this context because '|1' is an 'Out|In' type parameter."
                                    ' Used for simple errors where the erroneous generic-param is NOT inside a generic binding:
                                    ' e.g. "Sub f(ByVal a as O)" for some parameter declared as "Out O"
                                    ' gives the error "An 'Out' parameter like 'O' cannot be user here".
                                    AppendVarianceDiagnosticInfo(diagnostics,
                                                                 ErrorFactory.ErrorInfo(If(inappropriateOut,
                                                                                           ERRID.ERR_VarianceOutParamDisallowed1,
                                                                                           ERRID.ERR_VarianceInParamDisallowed1),
                                                                                        type.Name))
                                ElseIf constructionDepth <= 1 Then
                                    If typeParameterInfo.ConstructedType.Arity <= 1 Then
                                        ' "Type '|1' cannot be used in this context because '|1' is an 'Out|In' type parameter."
                                        ' e.g. "Sub f(ByVal a As IEnumerable(Of O))" yields
                                        ' "An 'Out' parameter like 'O' cannot be used here."
                                        AppendVarianceDiagnosticInfo(diagnostics,
                                                                     ErrorFactory.ErrorInfo(If(inappropriateOut,
                                                                                               ERRID.ERR_VarianceOutParamDisallowed1,
                                                                                               ERRID.ERR_VarianceInParamDisallowed1),
                                                                                            type.Name))
                                    Else
                                        Debug.Assert(typeParameterInfo.ConstructedType.Arity > 1)
                                        ' "Type '|1' cannot be used for the '|2' in '|3' in this context because '|1' is an 'Out|In' type parameter."
                                        ' e.g. "Sub f(ByVal a As IDoubleEnumerable(Of O,I)) yields
                                        ' "An 'Out' parameter like 'O' cannot be used for type parameter 'T1' of 'IDoubleEnumerable(Of T1,T2)'."
                                        AppendVarianceDiagnosticInfo(diagnostics,
                                                                     ErrorFactory.ErrorInfo(If(inappropriateOut,
                                                                                               ERRID.ERR_VarianceOutParamDisallowedForGeneric3,
                                                                                               ERRID.ERR_VarianceInParamDisallowedForGeneric3),
                                                                                            type.Name,
                                                                                            typeParameterInfo.TypeParameter.Name,
                                                                                            CustomSymbolDisplayFormatter.QualifiedName(typeParameterInfo.ConstructedType.OriginalDefinition)))
                                    End If
                                Else
                                    Debug.Assert(constructionDepth > 1)
                                    If typeParameterInfo.ConstructedType.Arity <= 1 Then
                                        ' "Type '|1' cannot be used in '|2' in this context because '|1' is an 'Out|In' type parameter."
                                        ' e.g. "Sub f(ByVal a as Func(Of IEnumerable(Of O), IEnumerable(Of O))" yields
                                        ' "In 'IEnumerable(Of O)' here, an 'Out' parameter like 'O' cannot be used."
                                        AppendVarianceDiagnosticInfo(diagnostics,
                                                                     ErrorFactory.ErrorInfo(If(inappropriateOut,
                                                                                               ERRID.ERR_VarianceOutParamDisallowedHere2,
                                                                                               ERRID.ERR_VarianceInParamDisallowedHere2),
                                                                                            type.Name,
                                                                                            CustomSymbolDisplayFormatter.QualifiedName(typeParameterInfo.ConstructedType)))

                                    Else
                                        Debug.Assert(typeParameterInfo.ConstructedType.Arity > 1)
                                        ' "Type '|1' cannot be used in '|2' for the '|3' in '|4' in this context because '|1' is an 'Out' type parameter."
                                        ' e.g. "Sub f(ByVal a as IEnumerable(Of Func(Of O,O))" yields
                                        ' "In 'Func(Of O,O)' here, an 'Out' parameter like 'O' cannot be used for type parameter 'Tresult' of 'Func(Of Tresult,T)'."
                                        AppendVarianceDiagnosticInfo(diagnostics,
                                                                     ErrorFactory.ErrorInfo(If(inappropriateOut,
                                                                                               ERRID.ERR_VarianceOutParamDisallowedHereForGeneric4,
                                                                                               ERRID.ERR_VarianceInParamDisallowedHereForGeneric4),
                                                                                            type.Name,
                                                                                            CustomSymbolDisplayFormatter.QualifiedName(typeParameterInfo.ConstructedType),
                                                                                            typeParameterInfo.TypeParameter.Name,
                                                                                            CustomSymbolDisplayFormatter.QualifiedName(typeParameterInfo.ConstructedType.OriginalDefinition)))

                                    End If
                                End If

                            Case Else
                                Throw ExceptionUtilities.UnexpectedValue(context)
                        End Select
                    End If

                Case SymbolKind.ArrayType
                    ' 2. if T is an array U():
                    GenerateVarianceDiagnosticsForTypeRecursively(DirectCast(type, ArrayTypeSymbol).ElementType,
                                                                  requiredVariance,
                                                                  context,
                                                                  typeParameterInfo,
                                                                  constructionDepth,
                                                                  diagnostics)

                Case SymbolKind.NamedType
                    Dim namedType = DirectCast(type, NamedTypeSymbol)

                    If Not namedType.IsGenericType Then
                        Return
                    End If

                    ' 3. T is a construction X1(Of T11...)...Xn(Of Tn1...) of some generic struct/class/interface/delegate X1(Of X11...)...Xn(Of Xn1...)
                    ' Special check, discussed above, for better error-reporting when we find a generic binding in an
                    ' illegal contravariant position
                    If requiredVariance <> VarianceKind.Out Then
                        Dim outermostVarianceContainerOfType As NamedTypeSymbol = Nothing
                        Dim container As NamedTypeSymbol = type.ContainingType

                        While container IsNot Nothing
                            If container.TypeParameters.HaveVariance() Then
                                outermostVarianceContainerOfType = container.OriginalDefinition
                            End If

                            container = container.ContainingType
                        End While

                        Dim outermostVarianceContainerOfContext As NamedTypeSymbol = Nothing
                        container = Me

                        Do
                            If container.TypeParameters.HaveVariance() Then
                                outermostVarianceContainerOfContext = container
                            End If

                            container = container.ContainingType
                        Loop While container IsNot Nothing

                        If outermostVarianceContainerOfType IsNot Nothing AndAlso outermostVarianceContainerOfType Is outermostVarianceContainerOfContext Then
                            ' ERRID_VarianceTypeDisallowed2.               "Type '|1' cannot be used in this context because both the context and the definition of '|1' are nested within type '|2', and '|2' has 'In' or 'Out' type parameters. Consider moving '|1' outside of '|2'."
                            ' ERRID_VarianceTypeDisallowedForGeneric4.     "Type '|1' cannot be used for the '|3' in '|4' in this context because both the context and the definition of '|1' are nested within type '|2', and '|2' has 'In' or 'Out' type parameters. Consider moving '|1' outside of '|2'."
                            ' ERRID_VarianceTypeDisallowedHere3.           "Type '|1' cannot be used in '|3' in this context because both the context and the definition of '|1' are nested within type '|2', and '|2' has 'In' or 'Out' type parameters. Consider moving '|1' outside of '|2'."
                            ' ERRID_VarianceTypeDisallowedHereForGeneric5. "Type '|1' cannot be used for the '|4' of '|5' in '|3' in this context because both the context and the definition of '|1' are nested within type '|2', and '|2' has 'In' or 'Out' type parameters. Consider moving '|1' outside of '|2'."
                            If typeParameterInfo.ConstructedType Is Nothing Then
                                AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceTypeDisallowed2,
                                                                                                 CustomSymbolDisplayFormatter.ShortNameWithTypeArgs(type.OriginalDefinition),
                                                                                                 CustomSymbolDisplayFormatter.QualifiedName(outermostVarianceContainerOfType)))
                            ElseIf constructionDepth <= 1 Then
                                If typeParameterInfo.ConstructedType.Arity <= 1 Then
                                    AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceTypeDisallowed2,
                                                                                                     CustomSymbolDisplayFormatter.ShortNameWithTypeArgs(type.OriginalDefinition),
                                                                                                     CustomSymbolDisplayFormatter.QualifiedName(outermostVarianceContainerOfType)))
                                Else
                                    Debug.Assert(typeParameterInfo.ConstructedType.Arity > 1)
                                    AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceTypeDisallowedForGeneric4,
                                                                                                     CustomSymbolDisplayFormatter.ShortNameWithTypeArgs(type.OriginalDefinition),
                                                                                                     CustomSymbolDisplayFormatter.QualifiedName(outermostVarianceContainerOfType),
                                                                                                     typeParameterInfo.TypeParameter.Name,
                                                                                                     CustomSymbolDisplayFormatter.QualifiedName(typeParameterInfo.ConstructedType.OriginalDefinition)))
                                End If
                            Else
                                Debug.Assert(constructionDepth > 1)
                                If typeParameterInfo.ConstructedType.Arity <= 1 Then
                                    AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceTypeDisallowedHere3,
                                                                                                     CustomSymbolDisplayFormatter.ShortNameWithTypeArgs(type.OriginalDefinition),
                                                                                                     CustomSymbolDisplayFormatter.QualifiedName(outermostVarianceContainerOfType),
                                                                                                     CustomSymbolDisplayFormatter.QualifiedName(typeParameterInfo.ConstructedType)))
                                Else
                                    Debug.Assert(typeParameterInfo.ConstructedType.Arity > 1)
                                    AppendVarianceDiagnosticInfo(diagnostics, ErrorFactory.ErrorInfo(ERRID.ERR_VarianceTypeDisallowedHereForGeneric5,
                                                                                                     CustomSymbolDisplayFormatter.ShortNameWithTypeArgs(type.OriginalDefinition),
                                                                                                     CustomSymbolDisplayFormatter.QualifiedName(outermostVarianceContainerOfType),
                                                                                                     CustomSymbolDisplayFormatter.QualifiedName(typeParameterInfo.ConstructedType),
                                                                                                     typeParameterInfo.TypeParameter.Name,
                                                                                                     CustomSymbolDisplayFormatter.QualifiedName(typeParameterInfo.ConstructedType.OriginalDefinition)))
                                End If
                            End If

                            Return
                        End If
                    End If

                    ' The general code below will catch the case of nullables "T?" or "Nullable(Of T)", which require T to
                    ' be inviarant. But we want more specific error reporting for this case, so we check for it first.
                    If namedType.IsNullableType() Then
                        Debug.Assert(namedType.TypeParameters(0).Variance = VarianceKind.None, "unexpected: a nullable type should have one generic parameter with no variance")
                        If namedType.TypeArgumentsNoUseSiteDiagnostics(0).IsValueType Then
                            GenerateVarianceDiagnosticsForTypeRecursively(namedType.TypeArgumentsNoUseSiteDiagnostics(0),
                                                                          VarianceKind.None,
                                                                          VarianceContext.Nullable,
                                                                          New VarianceDiagnosticsTargetTypeParameter(namedType, 0),
                                                                          constructionDepth,
                                                                          diagnostics)
                        End If

                        Return
                    End If

                    ' "Type" will refer to the last generic binding, Xn(Of Tn1...). So we have to check all the way up to X1.
                    Do
                        For argumentIndex As Integer = 0 To namedType.Arity - 1
                            ' nb. the InvertVariance() here is the only difference between covariantly-valid and contravariantly-valid
                            ' for generic constructions.
                            Dim argumentRequiredVariance As VarianceKind

                            Select Case requiredVariance
                                Case VarianceKind.In
                                    Select Case namedType.TypeParameters(argumentIndex).Variance
                                        Case VarianceKind.In
                                            argumentRequiredVariance = VarianceKind.Out
                                        Case VarianceKind.Out
                                            argumentRequiredVariance = VarianceKind.In
                                        Case Else
                                            argumentRequiredVariance = VarianceKind.None
                                    End Select
                                Case VarianceKind.Out
                                    argumentRequiredVariance = namedType.TypeParameters(argumentIndex).Variance
                                Case Else
                                    argumentRequiredVariance = VarianceKind.None
                            End Select

                            GenerateVarianceDiagnosticsForTypeRecursively(namedType.TypeArgumentsNoUseSiteDiagnostics(argumentIndex),
                                                                          argumentRequiredVariance,
                                                                          VarianceContext.Complex,
                                                                          New VarianceDiagnosticsTargetTypeParameter(namedType, argumentIndex),
                                                                          constructionDepth + 1,
                                                                          diagnostics)
                        Next

                        namedType = namedType.ContainingType
                    Loop While namedType IsNot Nothing

                Case SymbolKind.ErrorType
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(type.Kind)
            End Select
        End Sub

        Private Sub GenerateVarianceDiagnosticsForMethod(
            method As MethodSymbol,
            <[In], Out> ByRef diagnostics As DiagnosticBag,
            <[In], Out> ByRef infosBuffer As ArrayBuilder(Of DiagnosticInfo)
        )
            Debug.Assert(Not HaveDiagnostics(infosBuffer))

            Select Case method.MethodKind
                Case MethodKind.EventAdd, MethodKind.EventRemove, MethodKind.PropertyGet, MethodKind.PropertySet
                    Return
            End Select

            GenerateVarianceDiagnosticsForParameters(method.Parameters, diagnostics, infosBuffer)

            ' Return type is valid covariantly
            Debug.Assert(Not HaveDiagnostics(infosBuffer))
            GenerateVarianceDiagnosticsForType(method.ReturnType, VarianceKind.Out, VarianceContext.Return, infosBuffer)
            If HaveDiagnostics(infosBuffer) Then
                Dim location As location
                Dim syntax As MethodBaseSyntax = method.GetDeclaringSyntaxNode(Of MethodBaseSyntax)()

                If syntax Is Nothing AndAlso method.MethodKind = MethodKind.DelegateInvoke Then
                    syntax = method.ContainingType.GetDeclaringSyntaxNode(Of MethodBaseSyntax)()
                End If

                Dim asClause As AsClauseSyntax = If(syntax IsNot Nothing, syntax.AsClauseInternal, Nothing)

                If asClause IsNot Nothing Then
                    location = asClause.Type.GetLocation()
                Else
                    location = method.Locations(0)
                End If

                ReportDiagnostics(diagnostics, location, infosBuffer)
            End If

            GenerateVarianceDiagnosticsForConstraints(method.TypeParameters, diagnostics, infosBuffer)
        End Sub

        Private Sub GenerateVarianceDiagnosticsForParameters(
            parameters As ImmutableArray(Of ParameterSymbol),
            <[In], Out> ByRef diagnostics As DiagnosticBag,
            <[In], Out> ByRef infosBuffer As ArrayBuilder(Of DiagnosticInfo)
        )
            Debug.Assert(Not HaveDiagnostics(infosBuffer))

            ' Each ByVal Pi is valid contravariantly, and each ByRef Pi is valid invariantly
            For Each param As ParameterSymbol In parameters
                Dim requiredVariance As VarianceKind
                Dim context As VarianceContext

                If param.IsByRef Then
                    requiredVariance = VarianceKind.None
                    context = VarianceContext.ByRef
                Else
                    requiredVariance = VarianceKind.In
                    context = VarianceContext.ByVal
                End If

                GenerateVarianceDiagnosticsForType(param.Type, requiredVariance, context, infosBuffer)
                If HaveDiagnostics(infosBuffer) Then
                    Dim location As location
                    Dim syntax As ParameterSyntax = param.GetDeclaringSyntaxNode(Of ParameterSyntax)()

                    If syntax IsNot Nothing AndAlso syntax.AsClause IsNot Nothing Then
                        location = syntax.AsClause.Type.GetLocation()
                    Else
                        location = param.Locations(0)
                    End If

                    ReportDiagnostics(diagnostics, location, infosBuffer)
                End If
            Next
        End Sub

        Private Sub GenerateVarianceDiagnosticsForConstraints(
            parameters As ImmutableArray(Of TypeParameterSymbol),
            <[In], Out> ByRef diagnostics As DiagnosticBag,
            <[In], Out> ByRef infosBuffer As ArrayBuilder(Of DiagnosticInfo)
        )
            Debug.Assert(Not HaveDiagnostics(infosBuffer))

            ' Each constraint on U1...Un is valid contravariantly
            ' "It is character-building to consider why this is required" [Eric Lippert, 2008]
            ' Interface IReadOnly(Of Out T) | Class Zoo(Of T)            |  Dim m As IReadOnly(Of Mammal) = new Zoo(Of Mammal)  ' OK through inheritance
            ' Sub Fun(Of U As T)()          | Implements IReadOnly(Of T) |  Dim a as IReadOnly(Of Animal) = m   ' OK through covariance
            ' End Interface                 | End Class                  |  a.Fun(Of Fish)() ' BAD: Fish is an Animal (satisfies "U as T"), but fun is expecting a mammal!

            For Each param As TypeParameterSymbol In parameters
                For Each constraint As TypeSymbol In param.ConstraintTypesNoUseSiteDiagnostics
                    GenerateVarianceDiagnosticsForType(constraint, VarianceKind.In, VarianceContext.Constraint, infosBuffer)
                    If HaveDiagnostics(infosBuffer) Then
                        Dim location As location = param.Locations(0)

                        For Each constraintInfo As TypeParameterConstraint In param.GetConstraints()
                            If constraintInfo.TypeConstraint IsNot Nothing AndAlso
                               constraintInfo.TypeConstraint.IsSameTypeIgnoringCustomModifiers(constraint) Then
                                location = constraintInfo.LocationOpt
                                Exit For
                            End If
                        Next

                        ReportDiagnostics(diagnostics, location, infosBuffer)
                    End If
                Next
            Next
        End Sub

        Private Sub GenerateVarianceDiagnosticsForProperty(
            [property] As PropertySymbol,
            <[In], Out> ByRef diagnostics As DiagnosticBag,
            <[In], Out> ByRef infosBuffer As ArrayBuilder(Of DiagnosticInfo)
        )
            Debug.Assert(Not HaveDiagnostics(infosBuffer))

            ' Gettable: requires covariance. Settable: requires contravariance. Gettable and settable: requires invariance.
            Dim requiredVariance As VarianceKind
            Dim context As VarianceContext

            If [property].IsReadOnly Then
                requiredVariance = VarianceKind.Out
                context = VarianceContext.ReadOnlyProperty
            ElseIf [property].IsWriteOnly Then
                requiredVariance = VarianceKind.In
                context = VarianceContext.WriteOnlyProperty
            Else
                requiredVariance = VarianceKind.None
                context = VarianceContext.Property
            End If

            GenerateVarianceDiagnosticsForType([property].Type, requiredVariance, context, infosBuffer)
            If HaveDiagnostics(infosBuffer) Then
                Dim location As location
                Dim syntax As PropertyStatementSyntax = [property].GetDeclaringSyntaxNode(Of PropertyStatementSyntax)()

                If syntax IsNot Nothing AndAlso syntax.AsClause IsNot Nothing Then
                    location = syntax.AsClause.Type.GetLocation()
                Else
                    location = [property].Locations(0)
                End If

                ReportDiagnostics(diagnostics, location, infosBuffer)
            End If

            ' A property might be declared with extra parameters, so we have to check that these are variance-valid.
            GenerateVarianceDiagnosticsForParameters([property].Parameters, diagnostics, infosBuffer)
        End Sub

        Private Sub GenerateVarianceDiagnosticsForEvent(
            [event] As EventSymbol,
            <[In], Out> ByRef diagnostics As DiagnosticBag,
            <[In], Out> ByRef infosBuffer As ArrayBuilder(Of DiagnosticInfo)
        )
            Debug.Assert(Not HaveDiagnostics(infosBuffer))

            Dim type As TypeSymbol = [event].Type

            If Not type.IsDelegateType() Then
                Return
            End If

            If type.ImplicitlyDefinedBy() Is [event] Then
                Return
            End If

            GenerateVarianceDiagnosticsForType(type, VarianceKind.In, VarianceContext.Complex, infosBuffer)

            If HaveDiagnostics(infosBuffer) Then
                Dim location As location
                Dim syntax As EventStatementSyntax = [event].GetDeclaringSyntaxNode(Of EventStatementSyntax)()

                If syntax IsNot Nothing AndAlso syntax.AsClause IsNot Nothing Then
                    location = syntax.AsClause.Type.GetLocation()
                Else
                    location = [event].Locations(0)
                End If

                ReportDiagnostics(diagnostics, location, infosBuffer)
            End If
        End Sub

        ''' <summary>
        ''' Ensure all attributes on all members in the named type are bound.
        ''' </summary>
        Private Sub BindAllMemberAttributes(cancellationToken As CancellationToken)
            ' Ensure all members are declared
            Dim lookup = Me.MemberAndInitializerLookup

            Dim haveExtensionMethods As Boolean = False

            ' Now bind all attributes on all members.  This must be done after the members are declared to avoid
            ' infinite recursion.
            For Each syms In lookup.Members.Values
                For Each sym In syms
                    sym.GetAttributes()

                    ' Make a note of extension methods
                    If Not haveExtensionMethods Then
                        haveExtensionMethods = (sym.Kind = SymbolKind.Method AndAlso DirectCast(sym, MethodSymbol).IsExtensionMethod)
                    End If

                    cancellationToken.ThrowIfCancellationRequested()
                Next
            Next

            If haveExtensionMethods Then
                Debug.Assert(Me.MightContainExtensionMethods)

                m_containingModule.RecordPresenceOfExtensionMethods()

                Debug.Assert(m_lazyContainsExtensionMethods <> ThreeState.False)
                m_lazyContainsExtensionMethods = ThreeState.True

                ' At this point we already processed all the attributes on the type.
                ' and should know whether there is an explicit Extension attribute on it.
                ' If there is an explicit attribute, or we passed through this code before,
                ' m_lazyEmitExtensionAttribute should have known value.
                If m_lazyEmitExtensionAttribute = ThreeState.Unknown Then

                    ' We need to emit an Extension attribute on the type. 
                    ' Can we locate it?
                    Dim useSiteError As DiagnosticInfo = Nothing
                    m_containingModule.ContainingSourceAssembly.DeclaringCompilation.GetExtensionAttributeConstructor(useSiteError:=useSiteError)

                    If useSiteError IsNot Nothing Then
                        ' Note, we are storing false because, even though we should emit the attribute,
                        ' we can't do that due to the use site error.
                        m_lazyEmitExtensionAttribute = ThreeState.False

                        ' also notify the containing assembly to not use the extension attribute
                        m_containingModule.ContainingSourceAssembly.AnErrorHasBeenReportedAboutExtensionAttribute()
                    Else
                        ' We have extension methods, we don't have explicit Extension attribute
                        ' on the type, which we were able to locate. Should emit it.
                        Debug.Assert(m_lazyEmitExtensionAttribute <> ThreeState.False)
                        m_lazyEmitExtensionAttribute = ThreeState.True
                    End If
                End If
            Else
                Debug.Assert(m_lazyContainsExtensionMethods <> ThreeState.True)
                m_lazyContainsExtensionMethods = ThreeState.False

                Debug.Assert(m_lazyEmitExtensionAttribute <> ThreeState.True)
                m_lazyEmitExtensionAttribute = ThreeState.False
            End If

            Debug.Assert(m_lazyEmitExtensionAttribute <> ThreeState.Unknown)
            Debug.Assert(m_lazyContainsExtensionMethods <> ThreeState.Unknown)
            Debug.Assert(m_lazyEmitExtensionAttribute = ThreeState.False OrElse m_lazyContainsExtensionMethods = ThreeState.True)
        End Sub
#End Region

#Region "Containers"

        Public Overrides ReadOnly Property ContainingSymbol As Symbol
            Get
                Return m_containingSymbol
            End Get
        End Property

        Public Overrides ReadOnly Property ContainingType As NamedTypeSymbol
            Get
                Return TryCast(m_containingSymbol, NamedTypeSymbol)
            End Get
        End Property

        Public Overrides ReadOnly Property ContainingModule As ModuleSymbol
            Get
                Return m_containingModule
            End Get
        End Property

        Public ReadOnly Property ContainingSourceModule As SourceModuleSymbol
            Get
                Return m_containingModule
            End Get
        End Property

#End Region

#Region "Flags Encoded Properties"

        Public Overrides ReadOnly Property DeclaredAccessibility As Accessibility
            Get
                Return CType((m_flags And SourceTypeFlags.AccessibilityMask), Accessibility)
            End Get
        End Property

        Public Overrides ReadOnly Property IsMustInherit As Boolean
            Get
                Return (m_flags And SourceTypeFlags.MustInherit) <> 0
            End Get
        End Property

        Public Overrides ReadOnly Property IsNotInheritable As Boolean
            Get
                Return (m_flags And SourceTypeFlags.NotInheritable) <> 0
            End Get
        End Property

        Friend Overrides ReadOnly Property ShadowsExplicitly As Boolean
            Get
                Return (m_flags And SourceTypeFlags.Shadows) <> 0
            End Get
        End Property

        Public Overrides ReadOnly Property TypeKind As TypeKind
            Get
1266
                Return CType((m_flags And SourceTypeFlags.TypeKindMask) >> CUInt(SourceTypeFlags.TypeKindShift), TypeKind)
P
Pilchie 已提交
1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285
            End Get
        End Property

        Friend Overrides ReadOnly Property IsInterface As Boolean
            Get
                Return Me.TypeKind = TypeKind.Interface
            End Get
        End Property

        Friend ReadOnly Property IsPartial As Boolean
            Get
                Return (m_flags And SourceTypeFlags.Partial) <> 0
            End Get
        End Property

#End Region

#Region "Syntax"

H
heejaechang 已提交
1286
        Friend ReadOnly Property TypeDeclaration As MergedTypeDeclaration
P
Pilchie 已提交
1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372
            Get
                Return m_declaration
            End Get
        End Property

        Public Overrides ReadOnly Property IsImplicitlyDeclared As Boolean
            Get
                Return False
            End Get
        End Property

        Public NotOverridable Overrides ReadOnly Property IsScriptClass As Boolean
            Get
                Dim kind = m_declaration.Declarations(0).Kind
                Return kind = DeclarationKind.Script OrElse kind = DeclarationKind.Submission
            End Get
        End Property

        Public NotOverridable Overrides ReadOnly Property IsSubmissionClass As Boolean
            Get
                Dim kind = m_declaration.Declarations(0).Kind
                Return kind = DeclarationKind.Submission
            End Get
        End Property

        Public NotOverridable Overrides ReadOnly Property IsImplicitClass As Boolean
            Get
                Return m_declaration.Declarations(0).Kind = DeclarationKind.ImplicitClass
            End Get
        End Property

        Public NotOverridable Overrides ReadOnly Property Arity As Integer
            Get
                Return m_declaration.Arity
            End Get
        End Property

        ' Get the declaration kind. Used to match up symbols with declarations in the BinderCache.
        ' Name, Arity, and DeclarationKind must match to exactly one source symbol, even in the presence 
        ' of errors.
        Friend ReadOnly Property DeclarationKind As DeclarationKind
            Get
                Return m_declaration.Kind
            End Get
        End Property

        Public NotOverridable Overrides ReadOnly Property Name As String
            Get
                Return m_name
            End Get
        End Property

        Friend NotOverridable Overrides ReadOnly Property MangleName As Boolean
            Get
                Return Arity > 0
            End Get
        End Property

        ''' <summary>
        ''' Should return full emitted namespace name for a top level type if the name 
        ''' might be different in case from containing namespace symbol full name, Nothing otherwise.
        ''' 
        ''' Although namespaces unify based on case-insensitive name, VB uses the casing the namespace
        ''' declaration surround the class definition for the name emitted to metadata. 
        ''' 
        ''' Namespace FOO
        '''    Class X
        '''    End Class
        ''' ENd Namespace
        ''' Namespace foo
        '''    Class Y
        '''    End Class
        ''' ENd Namespace
        ''' 
        ''' In metadata, these are classes "FOO.X" and "foo.Y" (and thus appear in different namespaces
        ''' when imported into C#.) This function determines the casing of the namespace part of a class, if needed
        ''' to override the namespace name.
        ''' </summary>
        Friend Overrides Function GetEmittedNamespaceName() As String
            Dim containingSourceNamespace = TryCast(m_containingSymbol, SourceNamespaceSymbol)
            If containingSourceNamespace IsNot Nothing AndAlso containingSourceNamespace.HasMultipleSpellings Then
                ' Find the namespace spelling surrounding the first declaration.
                Debug.Assert(Locations.Length > 0)
                Dim firstLocation = Me.DeclaringCompilation.FirstSourceLocation(Locations)
                Debug.Assert(firstLocation.IsInSource)

1373
                Return containingSourceNamespace.GetDeclarationSpelling(firstLocation.SourceTree, firstLocation.SourceSpan.Start)
P
Pilchie 已提交
1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511
            End If

            Return Nothing
        End Function

        Friend NotOverridable Overrides Function GetLexicalSortKey() As LexicalSortKey
            ' WARNING: this should not allocate memory!
            If Not m_lazyLexicalSortKey.IsInitialized Then
                m_lazyLexicalSortKey.SetFrom(m_declaration.GetLexicalSortKey(DeclaringCompilation))
            End If

            Return m_lazyLexicalSortKey
        End Function

        Public NotOverridable Overrides ReadOnly Property Locations As ImmutableArray(Of Location)
            Get
                Return m_declaration.NameLocations
            End Get
        End Property

        ''' <summary>
        ''' Syntax references of all parts of the type declaration. 
        ''' Submission and script classes are represented by their containing <see cref="CompilationUnitSyntax"/>,
        ''' implicit class can be represented by <see cref="CompilationUnitSyntax"/> or <see cref="NamespaceBlockSyntax"/>.
        ''' </summary>
        Public ReadOnly Property SyntaxReferences As ImmutableArray(Of SyntaxReference)
            Get
                Return m_declaration.SyntaxReferences
            End Get
        End Property

        Public NotOverridable Overrides ReadOnly Property DeclaringSyntaxReferences As ImmutableArray(Of SyntaxReference)
            Get
                Return GetDeclaringSyntaxReferenceHelper(SyntaxReferences)
            End Get
        End Property
#End Region

#Region "Member from Syntax"
        ' Types defined in source code go through the following states, described here. 
        ' The data that is computed by each state is stored in a contained immutable object, for storage efficiency
        ' and ease of lock-free implementation.
        ' 1) Created. Only the name, accessibility, arity, typekind, type parameters (but not their names),
        '    and references to the source code declarations are known.
        '    No access to the syntax tree is needed for this state, only the declaration.
        ' 2) Nested types created. No errors are diagnosed yet. Still no access to the syntax tree is
        '    needed for this state.
        ' 3) Type parameters names, variance, and constraints are known. Errors relating to type parameters
        '    are diagnosed. This needs to be done before resolving inheritance, because the base type can
        '    involve the type parameters.
        ' 4) Inheritance resolved. The base type, interfaces, type parameter names, variance, and
        '    constraints are known. Errors relating to partial types are reported.
        ' 5) Members declared. All members of the type are created, and errors with them reported. 
        '    Errors relating to nested type are reported here also.
        '
        ' Which phases have been completed is tracked by whether the state data for that
        ' that phase has been initialized. This allows us to update phases in a lock-free
        ' manner, which avoids many deadlock possibilities. It also makes sure that diagnostics are
        ' reported once and only once.
        '
        ' The states are not visible outside the class, the class moves to the given
        ' state on demand. Care must be given in implementing the transitions to avoid
        ' deadlock or infinite recursion.

        ' Given the syntax declaration, and a container, get the symbol relating to that syntax.
        ' This is done by looking up the name from the declaration in the container, handling duplicates, arity, and
        ' so forth correctly.
        Friend Shared Function FindSymbolFromSyntax(declarationSyntax As TypeStatementSyntax,
                                                    container As NamespaceOrTypeSymbol,
                                                    sourceModule As ModuleSymbol) As SourceNamedTypeSymbol
            Dim childName As String = declarationSyntax.Identifier.ValueText
            Dim childArity As Integer = DeclarationTreeBuilder.GetArity(declarationSyntax.TypeParameterList)
            Dim childDeclKind As DeclarationKind = DeclarationTreeBuilder.GetKind(declarationSyntax.Kind)

            Return FindSymbolInContainer(childName, childArity, childDeclKind, container, sourceModule)
        End Function

        ' Given the syntax declaration, and a container, get the symbol relating to that syntax.
        ' This is done by lookup up the name from the declaration in the container, handling duplicates, arity, and
        ' so forth correctly.
        Friend Shared Function FindSymbolFromSyntax(declarationSyntax As EnumStatementSyntax,
                                                    container As NamespaceOrTypeSymbol,
                                                    sourceModule As ModuleSymbol) As SourceNamedTypeSymbol
            Dim childName As String = declarationSyntax.Identifier.ValueText
            Dim childArity As Integer = 0
            Dim childDeclKind As DeclarationKind = DeclarationTreeBuilder.GetKind(declarationSyntax.Kind)

            Return FindSymbolInContainer(childName, childArity, childDeclKind, container, sourceModule)
        End Function

        ' Given the syntax declaration, and a container, get the symbol relating to that syntax.
        ' This is done by lookup up the name from the declaration in the container, handling duplicates, arity, and
        ' so forth correctly.
        Friend Shared Function FindSymbolFromSyntax(declarationSyntax As DelegateStatementSyntax,
                                                    container As NamespaceOrTypeSymbol,
                                                    sourceModule As ModuleSymbol) As SourceNamedTypeSymbol
            Dim childName As String = declarationSyntax.Identifier.ValueText
            Dim childArity As Integer = DeclarationTreeBuilder.GetArity(declarationSyntax.TypeParameterList)
            Dim childDeclKind As DeclarationKind = VisualBasic.Symbols.DeclarationKind.Delegate

            Return FindSymbolInContainer(childName, childArity, childDeclKind, container, sourceModule)
        End Function

        ' Helper for FindSymbolFromSyntax. Finds a child source type based on name, arity, DeclarationKind.
        Private Shared Function FindSymbolInContainer(childName As String,
                                                      childArity As Integer,
                                                      childDeclKind As DeclarationKind,
                                                      container As NamespaceOrTypeSymbol,
                                                      sourceModule As ModuleSymbol) As SourceNamedTypeSymbol
            ' We need to find the correct symbol, even in error cases. There must be only one
            ' symbol that is a source symbol, defined in our module, with the given name, arity,
            ' and declaration kind. The declaration table merges together symbols with the same
            ' arity, name, and declaration kind (regardless of the Partial modifier).

            For Each child In container.GetTypeMembers(childName, childArity)
                Dim sourceType = TryCast(child, SourceNamedTypeSymbol)
                If sourceType IsNot Nothing Then
                    If (sourceType.ContainingModule Is sourceModule AndAlso
                        sourceType.DeclarationKind = childDeclKind) Then
                        Return sourceType
                    End If
                End If
            Next

            Return Nothing
        End Function

#End Region

#Region "Members (phase 5)"

        ''' <summary>
        '''  Structure to wrap the different arrays of members.
        ''' </summary>
        Friend Class MembersAndInitializers
            Friend ReadOnly Members As Dictionary(Of String, ImmutableArray(Of Symbol))
            Friend ReadOnly StaticInitializers As ImmutableArray(Of ImmutableArray(Of FieldOrPropertyInitializer))
            Friend ReadOnly InstanceInitializers As ImmutableArray(Of ImmutableArray(Of FieldOrPropertyInitializer))
1512 1513
            Friend ReadOnly StaticInitializersSyntaxLength As Integer
            Friend ReadOnly InstanceInitializersSyntaxLength As Integer
P
Pilchie 已提交
1514 1515 1516 1517 1518 1519 1520 1521 1522 1523

            ''' <summary>
            ''' Initializes a new instance of the <see cref="MembersAndInitializers" /> class.
            ''' </summary>
            ''' <param name="members">The members.</param>
            ''' <param name="staticInitializers">The static initializers.</param>
            ''' <param name="instanceInitializers">The instance initializers.</param>
            Friend Sub New(
                members As Dictionary(Of String, ImmutableArray(Of Symbol)),
                staticInitializers As ImmutableArray(Of ImmutableArray(Of FieldOrPropertyInitializer)),
1524 1525 1526
                instanceInitializers As ImmutableArray(Of ImmutableArray(Of FieldOrPropertyInitializer)),
                staticInitializersSyntaxLength As Integer,
                instanceInitializersSyntaxLength As Integer)
P
Pilchie 已提交
1527 1528 1529 1530

                Me.Members = members
                Me.StaticInitializers = staticInitializers
                Me.InstanceInitializers = instanceInitializers
1531 1532 1533 1534 1535

                Debug.Assert(staticInitializersSyntaxLength = If(staticInitializers.IsDefaultOrEmpty, 0, staticInitializers.Sum(Function(s) s.Sum(Function(i) i.Syntax.Span.Length))))
                Debug.Assert(instanceInitializersSyntaxLength = If(instanceInitializers.IsDefaultOrEmpty, 0, instanceInitializers.Sum(Function(s) s.Sum(Function(i) i.Syntax.Span.Length))))
                Me.StaticInitializersSyntaxLength = staticInitializersSyntaxLength
                Me.InstanceInitializersSyntaxLength = instanceInitializersSyntaxLength
P
Pilchie 已提交
1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548
            End Sub
        End Class

        ''' <summary>
        '''  Accumulates different members kinds used while building the members.
        ''' </summary>
        Friend NotInheritable Class MembersAndInitializersBuilder
            Friend ReadOnly Members As Dictionary(Of String, ArrayBuilder(Of Symbol)) = New Dictionary(Of String, ArrayBuilder(Of Symbol))(IdentifierComparison.Comparer)
            Friend Property StaticInitializers As ArrayBuilder(Of ImmutableArray(Of FieldOrPropertyInitializer))
            Friend Property InstanceInitializers As ArrayBuilder(Of ImmutableArray(Of FieldOrPropertyInitializer))

            Friend ReadOnly DeferredMemberDiagnostic As ArrayBuilder(Of ValueTuple(Of Symbol, Binder)) = ArrayBuilder(Of ValueTuple(Of Symbol, Binder)).GetInstance()

1549 1550 1551
            Public StaticSyntaxLength As Integer = 0
            Public InstanceSyntaxLength As Integer = 0

P
Pilchie 已提交
1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562
            Friend Function ToReadOnlyAndFree() As MembersAndInitializers
                DeferredMemberDiagnostic.Free()

                Dim readonlyMembers = New Dictionary(Of String, ImmutableArray(Of Symbol))(IdentifierComparison.Comparer)
                For Each memberList In Members.Values
                    readonlyMembers.Add(memberList(0).Name, memberList.ToImmutableAndFree())
                Next

                Return New MembersAndInitializers(
                    readonlyMembers,
                    If(StaticInitializers IsNot Nothing, StaticInitializers.ToImmutableAndFree(), Nothing),
1563 1564 1565
                    If(InstanceInitializers IsNot Nothing, InstanceInitializers.ToImmutableAndFree(), Nothing),
                    StaticSyntaxLength,
                    InstanceSyntaxLength)
P
Pilchie 已提交
1566 1567 1568 1569 1570 1571 1572
            End Function
        End Class

        ''' <summary>
        ''' Adds a field initializer for the field to list of field initializers
        ''' </summary>
        ''' <param name="initializers">All initializers.</param>
1573
        ''' <param name="initializer">The field initializer to add to the list of initializers.</param>
1574
        Friend Shared Sub AddInitializer(ByRef initializers As ArrayBuilder(Of FieldOrPropertyInitializer), initializer As FieldOrPropertyInitializer, ByRef aggregateSyntaxLength As Integer)
P
Pilchie 已提交
1575 1576
            If initializers Is Nothing Then
                initializers = ArrayBuilder(Of FieldOrPropertyInitializer).GetInstance()
1577 1578 1579 1580
            Else
                ' initializers should be added in syntax order
                Debug.Assert(initializer.Syntax.SyntaxTree Is initializers.Last().Syntax.SyntaxTree)
                Debug.Assert(initializer.Syntax.Span.Start > initializers.Last().Syntax.Span.Start)
P
Pilchie 已提交
1581 1582
            End If

1583
            initializer.PrecedingInitializersLength = aggregateSyntaxLength
1584
            initializers.Add(initializer)
1585 1586 1587

            ' ignore leading and trailing trivia of the node
            aggregateSyntaxLength += initializer.Syntax.Span.Length
P
Pilchie 已提交
1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638
        End Sub

        ''' <summary>
        ''' Adds an array of initializers to the member collections structure
        ''' </summary>
        ''' <param name="allInitializers">All initializers.</param>
        ''' <param name="siblings">The siblings.</param>
        Friend Shared Sub AddInitializers(ByRef allInitializers As ArrayBuilder(Of ImmutableArray(Of FieldOrPropertyInitializer)), siblings As ArrayBuilder(Of FieldOrPropertyInitializer))
            If siblings IsNot Nothing Then
                If allInitializers Is Nothing Then
                    allInitializers = New ArrayBuilder(Of ImmutableArray(Of FieldOrPropertyInitializer))()
                End If

                allInitializers.Add(siblings.ToImmutableAndFree())
            End If
        End Sub

        Protected Function GetTypeMembersDictionary() As Dictionary(Of String, ImmutableArray(Of NamedTypeSymbol))
            If m_lazyTypeMembers Is Nothing Then
                Interlocked.CompareExchange(m_lazyTypeMembers, MakeTypeMembers(), Nothing)
                Debug.Assert(m_lazyTypeMembers IsNot Nothing)
            End If
            Return m_lazyTypeMembers
        End Function

        ' Create symbols for all the nested types, and put them in a lookup indexed by (case-insensitive) name.
        Private Function MakeTypeMembers() As Dictionary(Of String, ImmutableArray(Of NamedTypeSymbol))
            Dim children As ImmutableArray(Of MergedTypeDeclaration) = m_declaration.Children

            Debug.Assert(m_emptyTypeMembers.Count = 0)

            If children.IsEmpty Then
                Return m_emptyTypeMembers
            End If

            Return children.Select(Function(decl) CreateNestedType(decl)).ToDictionary(
                Function(decl) decl.Name,
                IdentifierComparison.Comparer)
        End Function

        Friend Overrides Function GetTypeMembersUnordered() As ImmutableArray(Of NamedTypeSymbol)
            Return GetTypeMembersDictionary().Flatten()
        End Function

        Public Overloads Overrides Function GetTypeMembers() As ImmutableArray(Of NamedTypeSymbol)
            Return GetTypeMembersDictionary().Flatten(LexicalOrderSymbolComparer.Instance)
        End Function

        Public Overloads Overrides Function GetTypeMembers(name As String) As ImmutableArray(Of NamedTypeSymbol)
            Dim members As ImmutableArray(Of NamedTypeSymbol) = Nothing
            If GetTypeMembersDictionary().TryGetValue(name, members) Then
1639
                Return members
P
Pilchie 已提交
1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694
            End If
            Return ImmutableArray(Of NamedTypeSymbol).Empty
        End Function

        Public Overrides Function GetTypeMembers(name As String, arity As Integer) As ImmutableArray(Of NamedTypeSymbol)
            Return GetTypeMembers(name).WhereAsArray(Function(t) t.Arity = arity)
        End Function

        Friend Overrides ReadOnly Property DefaultPropertyName As String
            Get
                If TypeKind <> TypeKind.Delegate Then
                    GetMembersAndInitializers() ' Ensure m_defaultPropertyName is set.
                Else
                    Debug.Assert(m_defaultPropertyName Is Nothing)
                End If

                Return m_defaultPropertyName
            End Get
        End Property

        Private ReadOnly Property MemberAndInitializerLookup As MembersAndInitializers
            Get
                Return GetMembersAndInitializers()
            End Get
        End Property

        Private Function GetMembersAndInitializers() As MembersAndInitializers
            If m_lazyMembersAndInitializers Is Nothing Then
                Dim diagBag = DiagnosticBag.GetInstance()
                Dim membersAndInitializers = BuildMembersAndInitializers(diagBag)
                m_containingModule.AtomicStoreReferenceAndDiagnostics(m_lazyMembersAndInitializers, membersAndInitializers, diagBag, CompilationStage.Declare)
                Debug.Assert(m_lazyMembersAndInitializers IsNot Nothing)
                diagBag.Free()

                Dim unused = Me.KnownCircularStruct

#If DEBUG Then
                VerifyMembers()
#End If
            End If

            Return m_lazyMembersAndInitializers
        End Function

#If DEBUG Then
        Protected Overridable Sub VerifyMembers()
        End Sub
#End If

        Friend ReadOnly Property MembersHaveBeenCreated As Boolean
            Get
                Return m_lazyMembersAndInitializers IsNot Nothing
            End Get
        End Property

1695 1696 1697 1698 1699 1700 1701 1702
#If DEBUG Then
        ' Thread id to catch cases where ComputeMembersAndInitializers
        ' is called recursively. This does not catch all recursive cases,
        ' only cases where the method is called recursively on the first
        ' thread that called ComputeMembersAndInitializers.
        Private m_computingMembersThreadId As Integer
#End If

P
Pilchie 已提交
1703
        Private Function BuildMembersAndInitializers(diagBag As DiagnosticBag) As MembersAndInitializers
1704
#If DEBUG Then
1705
            Dim threadId = Environment.CurrentManagedThreadId
1706 1707 1708

            ' Bug 1098580 tracks re-enabling this assert.
            'Debug.Assert(m_computingMembersThreadId <> threadId)
1709 1710
            Interlocked.CompareExchange(m_computingMembersThreadId, threadId, 0)
#End If
P
Pilchie 已提交
1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731
            ' Get type members
            Dim typeMembers = GetTypeMembersDictionary()

            ' Get non-type members
            Dim membersAndInitializers As MembersAndInitializers = BuildNonTypeMembers(diagBag)
            m_defaultPropertyName = DetermineDefaultPropertyName(membersAndInitializers.Members, diagBag)

            ' Find/process partial methods
            ProcessPartialMethodsIfAny(membersAndInitializers.Members, diagBag)

            ' Merge types with non-types
            For Each typeSymbols In typeMembers.Values
                Dim nontypeSymbols As ImmutableArray(Of Symbol) = Nothing
                Dim name = typeSymbols(0).Name
                If Not membersAndInitializers.Members.TryGetValue(name, nontypeSymbols) Then
                    membersAndInitializers.Members.Add(name, StaticCast(Of Symbol).From(typeSymbols))
                Else
                    membersAndInitializers.Members(name) = nontypeSymbols.Concat(StaticCast(Of Symbol).From(typeSymbols))
                End If
            Next

1732 1733 1734
#If DEBUG Then
            Interlocked.CompareExchange(m_computingMembersThreadId, 0, threadId)
#End If
P
Pilchie 已提交
1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914
            Return membersAndInitializers
        End Function

        ''' <summary> Examines the members collection and builds a set of partial methods if any, otherwise returns nothing </summary>
        Private Function FindPartialMethodDeclarations(diagnostics As DiagnosticBag, members As Dictionary(Of String, ImmutableArray(Of Symbol))) As HashSet(Of SourceMemberMethodSymbol)
            Dim partialMethods As HashSet(Of SourceMemberMethodSymbol) = Nothing
            For Each memberGroup In members
                For Each member In memberGroup.Value
                    Dim method = TryCast(member, SourceMemberMethodSymbol)
                    If method IsNot Nothing AndAlso method.IsPartial AndAlso method.MethodKind = MethodKind.Ordinary Then

                        If Not method.IsSub Then
                            Debug.Assert(method.Locations.Length = 1)
                            diagnostics.Add(ERRID.ERR_PartialMethodsMustBeSub1, method.NonMergedLocation, method.Name)
                        Else
                            If partialMethods Is Nothing Then
                                partialMethods = New HashSet(Of SourceMemberMethodSymbol)(ReferenceEqualityComparer.Instance)
                            End If
                            partialMethods.Add(method)
                        End If
                    End If
                Next
            Next
            Return partialMethods
        End Function

        Private Sub ProcessPartialMethodsIfAny(members As Dictionary(Of String, ImmutableArray(Of Symbol)), diagnostics As DiagnosticBag)
            '  Detect all partial method declarations
            Dim partialMethods As HashSet(Of SourceMemberMethodSymbol) = FindPartialMethodDeclarations(diagnostics, members)
            If partialMethods Is Nothing Then
                Return
            End If

            ' we have at least one partial method, note that the methods we 
            ' found may have the same names and/or signatures

            ' NOTE: we process partial methods one-by-one which is not optimal for the 
            '       case where we have a bunch of partial methods with the same name.
            ' TODO: revise

            While partialMethods.Count > 0

                Dim originalPartialMethod As SourceMemberMethodSymbol = partialMethods.First()
                partialMethods.Remove(originalPartialMethod)

                ' The best partial method
                Dim bestPartialMethod As SourceMemberMethodSymbol = originalPartialMethod
                Dim bestPartialMethodLocation As Location = bestPartialMethod.NonMergedLocation
                Debug.Assert(bestPartialMethodLocation IsNot Nothing)

                ' The best partial method implementation
                Dim bestImplMethod As SourceMemberMethodSymbol = Nothing
                Dim bestImplLocation As Location = Nothing

                '  Process the members group by name
                Dim memberGroup As ImmutableArray(Of Symbol) = members(originalPartialMethod.Name)
                For Each member In memberGroup
                    Dim candidate As SourceMemberMethodSymbol = TryCast(member, SourceMemberMethodSymbol)

                    If candidate IsNot Nothing AndAlso candidate IsNot originalPartialMethod AndAlso candidate.MethodKind = MethodKind.Ordinary Then
                        If ComparePartialMethodSignatures(originalPartialMethod, candidate) Then

                            '  candidate location
                            Dim candidateLocation As Location = candidate.NonMergedLocation
                            Debug.Assert(candidateLocation IsNot Nothing)

                            If partialMethods.Contains(candidate) Then
                                '  partial-partial conflict
                                partialMethods.Remove(candidate)

                                '  the 'best' partial method is the one with the 'smallest' 
                                ' location, we should report errors on the other
                                Dim candidateIsBigger As Boolean = Me.DeclaringCompilation.CompareSourceLocations(bestPartialMethodLocation, candidateLocation) < 0
                                Dim reportOnMethod As SourceMemberMethodSymbol = If(candidateIsBigger, candidate, bestPartialMethod)
                                Dim nameToReport As String = reportOnMethod.Name

                                diagnostics.Add(ERRID.ERR_OnlyOnePartialMethodAllowed2,
                                                If(candidateIsBigger, candidateLocation, bestPartialMethodLocation),
                                                nameToReport, nameToReport)
                                reportOnMethod.SuppressDuplicateProcDefDiagnostics = True

                                '  change the best partial method if needed
                                If Not candidateIsBigger Then
                                    bestPartialMethod = candidate
                                    bestPartialMethodLocation = candidateLocation
                                End If

                            ElseIf Not candidate.IsPartial Then

                                '  if there are more than one method
                                If bestImplMethod Is Nothing Then
                                    bestImplMethod = candidate
                                    bestImplLocation = candidateLocation

                                Else
                                    '  the 'best' implementation method is the one with the 'smallest' 
                                    ' location, we should report errors on the others
                                    Dim candidateIsBigger = Me.DeclaringCompilation.CompareSourceLocations(bestImplLocation, candidateLocation) < 0
                                    Dim reportOnMethod As SourceMemberMethodSymbol = If(candidateIsBigger, candidate, bestImplMethod)
                                    Dim reportedName As String = reportOnMethod.Name

                                    diagnostics.Add(ERRID.ERR_OnlyOneImplementingMethodAllowed3,
                                                    If(candidateIsBigger, candidateLocation, bestImplLocation),
                                                    reportedName, reportedName, reportedName)
                                    reportOnMethod.SuppressDuplicateProcDefDiagnostics = True

                                    '  change the best implementation if needed
                                    If Not candidateIsBigger Then
                                        bestImplMethod = candidate
                                        bestImplLocation = candidateLocation
                                    End If

                                End If

                            End If

                            ' NOTE: the rest of partial methods are already processed, those can be safely ignored

                        End If
                    End If
                Next

                '  Report ERR_PartialMethodMustBeEmpty
                If bestPartialMethod.BlockSyntax IsNot Nothing AndAlso bestPartialMethod.BlockSyntax.Statements.Count > 0 Then
                    diagnostics.Add(ERRID.ERR_PartialMethodMustBeEmpty, bestPartialMethodLocation)
                End If

                If bestImplMethod IsNot Nothing Then
                    ' We found the partial method implementation

                    ' Remove the best implementation from members
                    ' NOTE: conflicting partial method declarations and implementations are NOT removed 
                    Dim newMembers = ArrayBuilder(Of Symbol).GetInstance()
                    For i = 0 To memberGroup.Length - 1
                        Dim member As Symbol = memberGroup(i)
                        If bestImplMethod IsNot member Then
                            newMembers.Add(member)
                        End If
                    Next
                    members(originalPartialMethod.Name) = newMembers.ToImmutableAndFree()

                    '  Assign implementation to best partial method
                    SourceMemberMethodSymbol.InitializePartialMethodParts(bestPartialMethod, bestImplMethod)

                    ' Report errors on partial method implementation
                    ReportErrorsOnPartialMethodImplementation(bestPartialMethod, bestImplMethod, bestImplLocation, diagnostics)

                Else
                    '  There is no implementation
                    SourceMemberMethodSymbol.InitializePartialMethodParts(bestPartialMethod, Nothing)
                End If

            End While

        End Sub

        Private Sub ReportErrorsOnPartialMethodImplementation(partialMethod As SourceMethodSymbol,
                                                              implMethod As SourceMethodSymbol,
                                                              implMethodLocation As Location,
                                                              diagnostics As DiagnosticBag)

            ' Report 'Method '...' must be declared 'Private' in order to implement partial method '...'
            If implMethod.DeclaredAccessibility <> Accessibility.Private Then
                diagnostics.Add(ERRID.ERR_ImplementationMustBePrivate2,
                                implMethodLocation,
                                implMethod.Name, partialMethod.Name)
            End If

            ' Check method parameters' names
            If partialMethod.ParameterCount > 0 Then
                Debug.Assert(partialMethod.ParameterCount = implMethod.ParameterCount)

                Dim declMethodParams As ImmutableArray(Of ParameterSymbol) = partialMethod.Parameters
                Dim implMethodParams As ImmutableArray(Of ParameterSymbol) = implMethod.Parameters

                For index = 0 To declMethodParams.Length - 1
                    Dim declParameter As ParameterSymbol = declMethodParams(index)
                    Dim implParameter As ParameterSymbol = implMethodParams(index)

                    '  Check type parameter name
1915
                    If Not CaseInsensitiveComparison.Equals(declParameter.Name, implParameter.Name) Then
P
Pilchie 已提交
1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935

                        Debug.Assert(implParameter.Locations.Length = 1)
                        diagnostics.Add(ERRID.ERR_PartialMethodParamNamesMustMatch3,
                                        implParameter.Locations(0),
                                        implParameter.Name, declParameter.Name, implMethod.Name)
                    End If
                Next
            End If

            ' Generic type names/constraints 
            If implMethod.Arity > 0 Then
                Dim declTypeParams As ImmutableArray(Of TypeParameterSymbol) = partialMethod.TypeParameters
                Dim implTypeParams As ImmutableArray(Of TypeParameterSymbol) = implMethod.TypeParameters
                Debug.Assert(declTypeParams.Length = implTypeParams.Length)

                For index = 0 To declTypeParams.Length - 1
                    Dim declParameter As TypeParameterSymbol = declTypeParams(index)
                    Dim implParameter As TypeParameterSymbol = implTypeParams(index)

                    '  Check parameter name
1936
                    If Not CaseInsensitiveComparison.Equals(declParameter.Name, implParameter.Name) Then
P
Pilchie 已提交
1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146

                        Debug.Assert(implParameter.Locations.Length = 1)
                        diagnostics.Add(ERRID.ERR_PartialMethodTypeParamNameMismatch3,
                                        implParameter.Locations(0),
                                        implParameter.Name, declParameter.Name, implMethod.Name)
                    End If

                Next

                ' If type parameters constraints don't match at least on one of type 
                ' parameters, report an error on the implementation method
                Dim options = SymbolComparisonResults.ArityMismatch Or SymbolComparisonResults.ConstraintMismatch
                If MethodSignatureComparer.DetailedCompare(partialMethod, implMethod, options) <> Nothing Then
                    diagnostics.Add(ERRID.ERR_PartialMethodGenericConstraints2,
                                    implMethodLocation,
                                    implMethod.Name, partialMethod.Name)
                End If
            End If

        End Sub

        ''' <summary>
        ''' Compares two methods to check if the 'candidate' can be an implementation of the 'partialDeclaration'.
        ''' </summary>
        Private Function ComparePartialMethodSignatures(partialDeclaration As SourceMethodSymbol, candidate As SourceMethodSymbol) As Boolean
            ' Don't check values of optional parameters yet, this might cause an infinite cycle.
            ' Don't check ParamArray mismatch either, might cause us to bind attributes too early.
            Dim comparisons = SymbolComparisonResults.AllMismatches And
                                   Not (SymbolComparisonResults.CallingConventionMismatch Or
                                        SymbolComparisonResults.ConstraintMismatch Or
                                        SymbolComparisonResults.OptionalParameterValueMismatch Or
                                        SymbolComparisonResults.ParamArrayMismatch)

            Dim result As SymbolComparisonResults = MethodSignatureComparer.DetailedCompare(partialDeclaration, candidate, comparisons)

            If result <> Nothing Then
                Return False
            End If

            ' Dev10 also compares EQ_Flags { Shared|Overrides|MustOverride|Overloads }, but ignores 'Overloads'
            Return partialDeclaration.IsShared = candidate.IsShared AndAlso
                   partialDeclaration.IsOverrides = candidate.IsOverrides AndAlso
                   partialDeclaration.IsMustOverride = candidate.IsMustOverride
        End Function

        Friend Overrides ReadOnly Property KnownCircularStruct As Boolean
            Get
                If m_lazyStructureCycle = ThreeState.Unknown Then
                    If Not Me.IsStructureType Then
                        m_lazyStructureCycle = ThreeState.False
                    Else
                        Dim diagnostics = DiagnosticBag.GetInstance()
                        Dim hasCycle = Me.CheckStructureCircularity(diagnostics)

                        ' In either case we use AtomicStoreIntegerAndDiagnostics.
                        m_containingModule.AtomicStoreIntegerAndDiagnostics(m_lazyStructureCycle,
                                                                            If(hasCycle, ThreeState.True, ThreeState.False),
                                                                            ThreeState.Unknown,
                                                                            diagnostics,
                                                                            CompilationStage.Declare)
                        diagnostics.Free()
                    End If
                End If

                Return m_lazyStructureCycle = ThreeState.True
            End Get
        End Property

        ''' <summary> 
        ''' Poolable data set to be used in structure circularity detection.
        ''' </summary>
        Private Class StructureCircularityDetectionDataSet

            ''' <summary> 
            ''' Following C# implementation we keep up to 32 data sets so that we do not need to allocate 
            ''' them over and over. In this implementation though, circularity detection in one type can trigger
            ''' circularity detection in other types while it traverses the types tree. The traversal is being 
            ''' performed breadth-first, so the number of data sets used by one thread is not longer than the 
            ''' length of the longest structure-in-structure nesting chain.
            ''' </summary>
            Private Shared ReadOnly Pool As New ObjectPool(Of StructureCircularityDetectionDataSet)(
                                                    Function() New StructureCircularityDetectionDataSet(), 32)

            ''' <summary> Set of processed structure types </summary>
            Public ReadOnly ProcessedTypes As HashSet(Of NamedTypeSymbol)

            ''' <summary> Queue element structure </summary>
            Public Structure QueueElement
                Public Type As NamedTypeSymbol
                Public Path As ConsList(Of FieldSymbol)

                Public Sub New(type As NamedTypeSymbol, path As ConsList(Of FieldSymbol))
                    Debug.Assert(type IsNot Nothing)
                    Debug.Assert(path IsNot Nothing)
                    Me.Type = type
                    Me.Path = path
                End Sub
            End Structure

            ''' <summary> Queue of the types to be processed </summary>
            Public ReadOnly Queue As Queue(Of QueueElement)

            Private Sub New()
                ProcessedTypes = New HashSet(Of NamedTypeSymbol)()
                Queue = New Queue(Of QueueElement)
            End Sub

            Public Shared Function GetInstance() As StructureCircularityDetectionDataSet
                Return Pool.Allocate()
            End Function

            Public Sub Free()
                Me.Queue.Clear()
                Me.ProcessedTypes.Clear()
                Pool.Free(Me)
            End Sub

        End Class

        ''' <summary>
        ''' Analyzes structure type for circularities. Reports only errors relevant for 'structBeingAnalyzed' type.
        ''' </summary>
        ''' <remarks>
        ''' When VB Dev10 detects circularity it reports the error only once for each cycle. Thus, if the cycle 
        ''' is {S1 --> S2 --> S3 --> S1}, only one error will be reported, which one of S1/S2/S3 will have error
        ''' is non-deterministic (depends on the order of symbols in a hash table).
        ''' 
        ''' Moreover, Dev10 analyzes the type graph and reports only one error in case S1 --> S2 --> S1 even if 
        ''' there are two fields referensing S2 from S1.
        ''' 
        ''' Example:
        '''    Structure S2
        '''      Dim s1 As S1
        '''    End Structure
        ''' 
        '''    Structure S3
        '''      Dim s1 As S1
        '''    End Structure
        ''' 
        '''    Structure S1
        '''      Dim s2 As S2  ' ERROR
        '''      Dim s2_ As S2 ' NO ERROR 
        '''      Dim s3 As S3  ' ERROR
        '''    End Structure
        ''' 
        ''' Dev10 also reports only one error for all the cycles starting with the same field, which one is reported 
        ''' depends on the declaration order. Current implementation reports all of the cycles for consistency. 
        ''' See testcases MultiplyCyclesInStructure03 and MultiplyCyclesInStructure04 (report different errors in Dev10).
        ''' </remarks>
        Private Function CheckStructureCircularity(diagnostics As DiagnosticBag) As Boolean
            '  Must be a structure
            Debug.Assert(Me.IsValueType AndAlso Not Me.IsTypeParameter)

            '  Allocate data set
            Dim data = StructureCircularityDetectionDataSet.GetInstance()
            data.Queue.Enqueue(New StructureCircularityDetectionDataSet.QueueElement(Me, ConsList(Of FieldSymbol).Empty))

            Dim hasCycle = False

            Try
                While data.Queue.Count > 0

                    Dim current = data.Queue.Dequeue()
                    If Not data.ProcessedTypes.Add(current.Type) Then
                        ' In some cases the queue may contain two same types which are not processed yet
                        Continue While
                    End If

                    Dim cycleReportedForCurrentType As Boolean = False

                    '  iterate over non-static fields of structure data type
                    For Each member In current.Type.GetMembers()

                        Dim field = TryCast(member, FieldSymbol)
                        If field IsNot Nothing AndAlso Not field.IsShared Then

                            Dim fieldType = TryCast(field.Type, NamedTypeSymbol)
                            If fieldType IsNot Nothing AndAlso fieldType.IsValueType Then

                                '  if the type is constructed from a generic structure, we should 
                                '  process ONLY fields which types are instantiated from type arguments
                                If Not field.IsDefinition AndAlso field.Type.Equals(field.OriginalDefinition.Type) Then
                                    Continue For
                                End If

                                If fieldType.OriginalDefinition.Equals(Me) Then
                                    '  a cycle detected

                                    If Not cycleReportedForCurrentType Then

                                        '  the cycle includes 'current.Path' and ends with 'field'; the order is reversed in the list
                                        Dim cycleFields = New ConsList(Of FieldSymbol)(field, current.Path)

                                        '  generate a message info
                                        Dim diagnosticInfos = ArrayBuilder(Of DiagnosticInfo).GetInstance()
                                        Dim firstField As FieldSymbol = Nothing ' after the cycle is processed this will hold the last element in the list
                                        While Not cycleFields.IsEmpty
                                            firstField = cycleFields.Head

                                            '  generate next field description
                                            diagnosticInfos.Add(ErrorFactory.ErrorInfo(ERRID.ERR_RecordEmbeds2,
                                                                                       firstField.ContainingType,
                                                                                       firstField.Type,
                                                                                       firstField.Name))
                                            cycleFields = cycleFields.Tail
                                        End While
                                        diagnosticInfos.ReverseContents()
                                        Debug.Assert(firstField IsNot Nothing)

                                        '  Report an error
2147
                                        Dim symbolToReportErrorOn As Symbol = If(firstField.AssociatedSymbol, DirectCast(firstField, Symbol))
P
Pilchie 已提交
2148 2149 2150 2151 2152 2153
                                        Debug.Assert(symbolToReportErrorOn.Locations.Length > 0)
                                        diagnostics.Add(ERRID.ERR_RecordCycle2,
                                                        symbolToReportErrorOn.Locations(0),
                                                        firstField.ContainingType.Name,
                                                        New CompoundDiagnosticInfo(diagnosticInfos.ToArrayAndFree()))

2154
                                        '  Don't report errors for other fields of this type referencing 'structBeingAnalyzed'
P
Pilchie 已提交
2155
                                        cycleReportedForCurrentType = True
2156
                                        hasCycle = True
P
Pilchie 已提交
2157 2158 2159 2160 2161 2162
                                    End If

                                ElseIf Not data.ProcessedTypes.Contains(fieldType) Then
                                    ' Add to the queue if we don't know yet if it was processed

                                    If Not fieldType.IsDefinition Then
2163
                                        ' Types constructed from generic types are considered to be a separate types. We never report 
P
Pharring 已提交
2164
                                        ' errors on such types. We also process only fields actually changed compared to original generic type.
P
Pilchie 已提交
2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213
                                        data.Queue.Enqueue(New StructureCircularityDetectionDataSet.QueueElement(
                                                fieldType, New ConsList(Of FieldSymbol)(field, current.Path)))

                                        ' The original Generic type is added using regular rules (see next note).
                                        fieldType = fieldType.OriginalDefinition
                                    End If

                                    ' NOTE: we want to make sure we report the same error for the same types 
                                    '       consistently and don't depend on the call order; this solution uses
                                    '       the following approach: 
                                    '           (a) for each cycle we report the error only on the type which is
                                    '               'smaller' than the other types in this cycle; the criteria 
                                    '               used for detection of the 'smallest' type does not matter;
                                    '           (b) thus, this analysis only considers the cycles consisting of the 
                                    '               types which are 'bigger' than 'structBeingAnalyzed' because we will not 
                                    '               report the error regarding this cycle for this type anyway
                                    Dim stepIntoType As Boolean = DetectTypeCircularity_ShouldStepIntoType(fieldType)
                                    If stepIntoType Then
                                        '  enqueue to be processed
                                        data.Queue.Enqueue(New StructureCircularityDetectionDataSet.QueueElement(
                                                fieldType, New ConsList(Of FieldSymbol)(field, current.Path)))
                                    Else
                                        '  should not process 
                                        data.ProcessedTypes.Add(fieldType)
                                    End If
                                End If
                            End If
                        End If
                    Next
                End While
            Finally
                data.Free()
            End Try

            Return hasCycle
        End Function

        ''' <summary>
        ''' Simple check of whether or not we should step into the type 'typeToTest' during 
        ''' type graph traversal inside 'DetectStructureCircularity' or 'GetDependenceChain'.
        ''' 
        ''' The following rules are in place: 
        '''   (a) we order all symbols according their first source location 
        '''       comparison rules: first, source file names are compared, 
        '''       then SourceSpan.Start is used for symbols inside the same file;
        '''   (b) given this order we enter the loop if only 'typeToTest' is 'less' than 
        '''       'structBeingAnalyzed';
        '''   (c) we also always enter types from other modules
        ''' 
C
ChuckStoner 已提交
2214
        ''' !!! To be ONLY used in 'CheckStructureCircularity'.
P
Pilchie 已提交
2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235
        ''' </summary>
        ''' <returns>True if detect type circularity code should step into 'typeToTest' type </returns>
        Friend Function DetectTypeCircularity_ShouldStepIntoType(typeToTest As NamedTypeSymbol) As Boolean

            If typeToTest.ContainingModule Is Nothing OrElse Not typeToTest.ContainingModule.Equals(Me.ContainingModule) Then
                ' Types from other modules should never be considered target types
                Return True
            End If

            ' We use simple comparison based on source location 
            Debug.Assert(typeToTest.Locations.Length > 0)
            Dim typeToTestLocation = typeToTest.Locations(0)

            Debug.Assert(Me.Locations.Length > 0)
            Dim structBeingAnalyzedLocation = Me.Locations(0)

            Dim compilation = Me.DeclaringCompilation
            Dim fileCompResult = compilation.CompareSourceLocations(typeToTestLocation, structBeingAnalyzedLocation)

            ' NOTE: we use '>=' for locations comparison; this is a safeguard against the case where two different
            '       types are declared in the files with same file name (if possible) and have the same location;
C
ChuckStoner 已提交
2236
            '       if we used '>' we would not report the cycle, with '>=' we will report the cycle twice.
P
Pilchie 已提交
2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549
            Return (fileCompResult > 0) OrElse
                    ((fileCompResult = 0) AndAlso typeToTestLocation.SourceSpan.Start >= structBeingAnalyzedLocation.SourceSpan.Start)

        End Function

        Private Function DetermineDefaultPropertyName(membersByName As Dictionary(Of String, ImmutableArray(Of Symbol)), diagBag As DiagnosticBag) As String
            Dim defaultPropertyName As String = Nothing

            For Each pair In membersByName
                Dim name = pair.Key
                Dim members = pair.Value
                Dim defaultProperty As PropertySymbol = Nothing

                ' Check if any of the properties are marked default.
                For Each member In members
                    If member.Kind = SymbolKind.Property Then
                        Dim propertySymbol = DirectCast(member, PropertySymbol)

                        If propertySymbol.IsDefault Then
                            If defaultPropertyName Is Nothing Then
                                defaultProperty = propertySymbol
                                defaultPropertyName = name

                                If Not defaultProperty.ShadowsExplicitly Then
                                    CheckDefaultPropertyAgainstAllBases(Me, defaultPropertyName, propertySymbol.Locations(0), diagBag)
                                End If
                            Else
                                ' "'Default' can be applied to only one property name in a {0}."
                                diagBag.Add(ERRID.ERR_DuplicateDefaultProps1, propertySymbol.Locations(0), GetKindText())
                            End If
                            Exit For
                        End If
                    End If
                Next

                If defaultPropertyName IsNot Nothing AndAlso defaultPropertyName = name Then
                    Debug.Assert(defaultProperty IsNot Nothing)

                    ' Report an error for any property with this name not marked as default.
                    For Each member In members
                        If (member.Kind = SymbolKind.Property) Then
                            Dim propertySymbol = DirectCast(member, SourcePropertySymbol)

                            If Not propertySymbol.IsDefault Then
                                ' "'{0}' and '{1}' cannot overload each other because only one is declared 'Default'."
                                diagBag.Add(ERRID.ERR_DefaultMissingFromProperty2, propertySymbol.Locations(0), defaultProperty, propertySymbol)
                            End If
                        End If
                    Next
                End If
            Next

            Return defaultPropertyName
        End Function

        ' Check all bases of "namedType" and warn if they have a default property named "defaultPropertyName".
        Private Sub CheckDefaultPropertyAgainstAllBases(namedType As NamedTypeSymbol, defaultPropertyName As String, location As Location, diagBag As DiagnosticBag)
            If namedType.IsInterfaceType() Then
                For Each iface In namedType.InterfacesNoUseSiteDiagnostics
                    CheckDefaultPropertyAgainstBase(defaultPropertyName, iface, location, diagBag)
                Next
            Else
                CheckDefaultPropertyAgainstBase(defaultPropertyName, namedType.BaseTypeNoUseSiteDiagnostics, location, diagBag)
            End If
        End Sub

        ' Check and warn if "baseType" has a default property named "defaultProperty Name.
        ' If "baseType" doesn't have a default property, check its base types.
        Private Sub CheckDefaultPropertyAgainstBase(defaultPropertyName As String, baseType As NamedTypeSymbol, location As Location, diagBag As DiagnosticBag)
            If baseType IsNot Nothing Then
                Dim baseDefaultPropertyName = baseType.DefaultPropertyName
                If baseDefaultPropertyName IsNot Nothing Then
                    If Not CaseInsensitiveComparison.Equals(defaultPropertyName, baseDefaultPropertyName) Then
                        ' BC40007: Default property '{0}' conflicts with the default property '{1}' in the base {2} '{3}'. '{0}' will be the default property
                        diagBag.Add(ERRID.WRN_DefaultnessShadowed4, location,
                                    defaultPropertyName, baseDefaultPropertyName, baseType.GetKindText(), CustomSymbolDisplayFormatter.ShortErrorName(baseType))
                    End If
                Else
                    ' If this type didn't have a default property name, recursively check base(s) of this base.
                    ' If this type did have a default property name, don't go any further.
                    CheckDefaultPropertyAgainstAllBases(baseType, defaultPropertyName, location, diagBag)
                End If
            End If
        End Sub


        ''' <summary>
        ''' Returns true if at least one of the elements of this list needs to be injected into a 
        ''' constructor because it's not a const or it is a const and it's type is either decimal 
        ''' or date. Non const fields always require a constructor, so this function should be called to 
        ''' determine if a synthesized constructor is needed that is not listed in members list.
        ''' </summary>
        Friend Function AnyInitializerToBeInjectedIntoConstructor(
            initializerSet As IEnumerable(Of ImmutableArray(Of FieldOrPropertyInitializer)),
            includingNonMetadataConstants As Boolean
        ) As Boolean
            If initializerSet IsNot Nothing Then
                For Each initializers In initializerSet
                    For Each initializer In initializers
                        Dim fieldOrPropertyArray As ImmutableArray(Of Symbol) = initializer.FieldsOrProperty

                        If Not fieldOrPropertyArray.IsDefault Then
                            Debug.Assert(fieldOrPropertyArray.Length > 0)
                            Dim fieldOrProperty As Symbol = fieldOrPropertyArray.First

                            If fieldOrProperty.Kind = SymbolKind.Property Then
                                ' All properties require initializers to be injected
                                Return True

                            Else
                                Dim fieldSymbol = DirectCast(fieldOrProperty, FieldSymbol)
                                If Not fieldSymbol.IsConst OrElse includingNonMetadataConstants AndAlso fieldSymbol.IsConstButNotMetadataConstant Then
                                    Return True
                                End If
                            End If
                        End If
                    Next
                Next
            End If
            Return False
        End Function

        ''' <summary>
        ''' Performs a check for overloads/overrides/shadows conflicts, generates diagnostics.
        ''' </summary>
        ''' <param name="membersAndInitializers"></param>
        ''' <param name="diagBag"></param>
        ''' <remarks></remarks>
        Private Sub CheckForOverloadOverridesShadowsClashesInSameType(membersAndInitializers As MembersAndInitializers, diagBag As DiagnosticBag)
            For Each member In membersAndInitializers.Members
                '  list may contain both properties and methods
                Dim checkProperties As Boolean = True
                Dim checkMethods As Boolean = True
                '  result 
                Dim explicitlyShadows As Boolean = False
                Dim explicitlyOverloads As Boolean = False
                '  symbol flags
                Dim shadowsExplicitly As Boolean
                Dim overloadsExplicitly As Boolean
                Dim overridesExplicitly As Boolean

                For Each symbol In member.Value
                    '  only ordinary methods 
                    Select Case symbol.Kind
                        Case SymbolKind.Method
                            If Not checkMethods Then
                                Continue For
                            End If
                            '  skip properties from with this name if any
                            checkProperties = False
                        Case SymbolKind.Property
                            If Not checkProperties Then
                                Continue For
                            End If
                            '  skip methods from with this name if any
                            checkMethods = False
                        Case Else
                            '  other kind of member cancels the analysis
                            explicitlyShadows = False
                            explicitlyOverloads = False
                            Exit For
                    End Select

                    '  initialize symbol flags
                    If (GetExplicitSymbolFlags(symbol, shadowsExplicitly, overloadsExplicitly, overridesExplicitly)) Then
                        If shadowsExplicitly Then
                            '  if the method/property shadows explicitly the rest of the methods may be skipped
                            explicitlyShadows = True
                            Exit For
                        ElseIf overloadsExplicitly OrElse overridesExplicitly Then
                            explicitlyOverloads = True
                            '  continue search
                        End If
                    End If
                Next

                '  skip the whole name
                If explicitlyShadows OrElse explicitlyOverloads Then
                    '  all symbols are SourceMethodSymbol
                    For Each symbol In member.Value
                        If (symbol.Kind = SymbolKind.Method AndAlso checkMethods) OrElse (symbol.IsPropertyAndNotWithEvents AndAlso checkProperties) Then
                            '  initialize symbol flags
                            If (GetExplicitSymbolFlags(symbol, shadowsExplicitly, overloadsExplicitly, overridesExplicitly)) Then
                                If explicitlyShadows Then
                                    If Not shadowsExplicitly Then
                                        Debug.Assert(symbol.Locations.Length > 0)
                                        diagBag.Add(ERRID.ERR_MustShadow2, symbol.Locations(0), symbol.GetKindText(), symbol.Name)
                                    End If
                                ElseIf explicitlyOverloads Then
                                    If Not overridesExplicitly AndAlso Not overloadsExplicitly Then
                                        Debug.Assert(symbol.Locations.Length > 0)
                                        diagBag.Add(ERRID.ERR_MustBeOverloads2, symbol.Locations(0), symbol.GetKindText(), symbol.Name)
                                    End If
                                End If
                            End If
                        End If
                    Next
                End If
            Next
        End Sub

        Private Function GetExplicitSymbolFlags(symbol As Symbol, ByRef shadowsExplicitly As Boolean, ByRef overloadsExplicitly As Boolean, ByRef overridesExplicitly As Boolean) As Boolean
            Select Case symbol.Kind
                Case SymbolKind.Method
                    Dim sourceMethodSymbol As SourceMethodSymbol = TryCast(symbol, SourceMethodSymbol)
                    If (sourceMethodSymbol Is Nothing) Then
                        Return False
                    End If

                    shadowsExplicitly = sourceMethodSymbol.ShadowsExplicitly
                    overloadsExplicitly = sourceMethodSymbol.OverloadsExplicitly
                    overridesExplicitly = sourceMethodSymbol.OverridesExplicitly
                    Return sourceMethodSymbol.MethodKind = MethodKind.Ordinary OrElse sourceMethodSymbol.MethodKind = MethodKind.DeclareMethod

                Case SymbolKind.Property
                    Dim sourcePropertySymbol As SourcePropertySymbol = TryCast(symbol, SourcePropertySymbol)
                    If (sourcePropertySymbol Is Nothing) Then
                        Return False
                    End If
                    shadowsExplicitly = sourcePropertySymbol.ShadowsExplicitly
                    overloadsExplicitly = sourcePropertySymbol.OverloadsExplicitly
                    overridesExplicitly = sourcePropertySymbol.OverridesExplicitly
                    Return True

                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(symbol.Kind)
            End Select
        End Function

        ' Declare all the non-type members and put them in a list.
        Private Function BuildNonTypeMembers(diagnostics As DiagnosticBag) As MembersAndInitializers
            Dim membersBuilder As New MembersAndInitializersBuilder()

            AddDeclaredNonTypeMembers(membersBuilder, diagnostics)

            ' Add the default constructor, if needed.
            AddDefaultConstructorIfNeeded(membersBuilder, False, membersBuilder.InstanceInitializers, diagnostics)

            ' If there is a shared field, a shared constructor must be synthesized and added to the member list.
            ' Const fields of type Date or Decimal also require a synthesized shared constructor, but there was a decision
            ' to not add this to the member list in this case.
            AddDefaultConstructorIfNeeded(membersBuilder, True, membersBuilder.StaticInitializers, diagnostics)

            ' If there are any "Handles" methods, optimistically create methods/ctors that would be hosting
            ' hookup code.
            AddWithEventsHookupConstructorsIfNeeded(membersBuilder, diagnostics)

            ' Add "Group Class" members.
            AddGroupClassMembersIfNeeded(membersBuilder, diagnostics)

            ' Add synthetic Main method, if needed.
            AddEntryPointIfNeeded(membersBuilder)

            CheckMemberDiagnostics(membersBuilder, diagnostics)

            Dim membersAndInitializers = membersBuilder.ToReadOnlyAndFree()

            '  Check for overloads, overrides, shadows and implicit shadows clashes
            CheckForOverloadOverridesShadowsClashesInSameType(membersAndInitializers, diagnostics)

            Return membersAndInitializers
        End Function

        Protected Overridable Sub AddEntryPointIfNeeded(membersBuilder As MembersAndInitializersBuilder)
        End Sub

        Protected MustOverride Sub AddDeclaredNonTypeMembers(membersBuilder As MembersAndInitializersBuilder, diagnostics As DiagnosticBag)

        Protected Overridable Sub AddGroupClassMembersIfNeeded(membersBuilder As MembersAndInitializersBuilder, diagnostics As DiagnosticBag)
        End Sub

        ' Create symbol(s) for member syntax and add them to the member list
        Protected Sub AddMember(memberSyntax As StatementSyntax,
                                    binder As Binder,
                                    diagBag As DiagnosticBag,
                                    members As MembersAndInitializersBuilder,
                                    ByRef staticInitializers As ArrayBuilder(Of FieldOrPropertyInitializer),
                                    ByRef instanceInitializers As ArrayBuilder(Of FieldOrPropertyInitializer),
                                    reportAsInvalid As Boolean)

            ' Currently partial methods are not implemented. Here's my current thinking about the 
            ' right way to implement them:
            '  There's an accessor on a Method symbol that indicates its fully partial (no definition). After
            '  calling DeclareMethodMember, we check to see if the signature matches another already defined method.
            '  If a partial is declared and we already have a method with the same sig, the second partial is ignored and the first
            '  partial is updated to add the syntax ref from the second.
            '  If a non-partial is declared and we already have a partial with the same sig, the existing partial is removed
            '  and its syntax refs are added to the non-partial.
            '  This should probably be combined with the logic for detecting duplicate signatures in general.
            '
            ' Comparing of signature is a bit tricky when generic methods are taken into account. E.g.:
            '   f(Of T)(a as T)
            '   f(Of U)(b as U)
            ' have the same signature, even though a and b have different types.
            ' The MethodSignatureComparer class takes care of that, so be sure to use it!

            Select Case memberSyntax.Kind
                Case SyntaxKind.FieldDeclaration
                    Dim fieldDecl = DirectCast(memberSyntax, FieldDeclarationSyntax)

                    If reportAsInvalid Then
                        diagBag.Add(ERRID.ERR_InvalidInNamespace, fieldDecl.GetLocation())
                    End If

                    ' Declare all variables that a declared by this syntax, and add them to the list.
                    SourceMemberFieldSymbol.Create(Me, fieldDecl, binder, members, staticInitializers, instanceInitializers, diagBag)

                Case _
                    SyntaxKind.SubBlock,
                    SyntaxKind.FunctionBlock,
                    SyntaxKind.ConstructorBlock,
                    SyntaxKind.OperatorBlock

2550
                    Dim methodDecl = DirectCast(memberSyntax, MethodBlockBaseSyntax).BlockStatement
P
Pilchie 已提交
2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618

                    If reportAsInvalid Then
                        diagBag.Add(ERRID.ERR_InvalidInNamespace, methodDecl.GetLocation())
                    End If

                    Dim methodSymbol = CreateMethodMember(methodDecl, binder, diagBag)
                    If methodSymbol IsNot Nothing Then
                        AddMember(methodSymbol, binder, members, omitDiagnostics:=False)
                    End If

                Case _
                    SyntaxKind.SubStatement,
                    SyntaxKind.FunctionStatement,
                    SyntaxKind.SubNewStatement,
                    SyntaxKind.DeclareSubStatement,
                    SyntaxKind.DeclareFunctionStatement,
                    SyntaxKind.OperatorStatement

                    Dim methodDecl = DirectCast(memberSyntax, MethodBaseSyntax)

                    If reportAsInvalid Then
                        diagBag.Add(ERRID.ERR_InvalidInNamespace, methodDecl.GetLocation())
                    End If

                    Dim methodSymbol = CreateMethodMember(DirectCast(memberSyntax, MethodBaseSyntax), binder, diagBag)
                    If methodSymbol IsNot Nothing Then
                        AddMember(methodSymbol, binder, members, omitDiagnostics:=False)
                    End If

                Case SyntaxKind.PropertyBlock
                    Dim propertyDecl = DirectCast(memberSyntax, PropertyBlockSyntax)

                    If reportAsInvalid Then
                        diagBag.Add(ERRID.ERR_InvalidInNamespace, propertyDecl.PropertyStatement.GetLocation())
                    End If

                    CreateProperty(propertyDecl.PropertyStatement, propertyDecl, binder, diagBag, members, staticInitializers, instanceInitializers)

                Case SyntaxKind.PropertyStatement
                    Dim propertyDecl = DirectCast(memberSyntax, PropertyStatementSyntax)

                    If reportAsInvalid Then
                        diagBag.Add(ERRID.ERR_InvalidInNamespace, propertyDecl.GetLocation())
                    End If

                    CreateProperty(propertyDecl, Nothing, binder, diagBag, members, staticInitializers, instanceInitializers)

                Case SyntaxKind.LabelStatement
                    ' TODO (tomat): should be added to the initializers
                    Exit Select

                Case SyntaxKind.EventStatement
                    Dim eventDecl = DirectCast(memberSyntax, EventStatementSyntax)
                    CreateEvent(eventDecl, Nothing, binder, diagBag, members)

                Case SyntaxKind.EventBlock
                    Dim eventDecl = DirectCast(memberSyntax, EventBlockSyntax)
                    CreateEvent(eventDecl.EventStatement, eventDecl, binder, diagBag, members)

                Case Else
                    If binder.BindingTopLevelScriptCode Then
                        If memberSyntax.Kind = SyntaxKind.EmptyStatement OrElse TypeOf memberSyntax Is ExecutableStatementSyntax Then

                            If reportAsInvalid Then
                                diagBag.Add(ERRID.ERR_InvalidInNamespace, memberSyntax.GetLocation())
                            End If

                            Dim initializer = New FieldOrPropertyInitializer(binder.GetSyntaxReference(memberSyntax))
2619
                            SourceNamedTypeSymbol.AddInitializer(instanceInitializers, initializer, members.InstanceSyntaxLength)
P
Pilchie 已提交
2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639
                        End If
                    End If
            End Select
        End Sub

        Private Sub CreateProperty(syntax As PropertyStatementSyntax,
                                   blockSyntaxOpt As PropertyBlockSyntax,
                                   binder As Binder,
                                   diagBag As DiagnosticBag,
                                   members As MembersAndInitializersBuilder,
                                   ByRef staticInitializers As ArrayBuilder(Of FieldOrPropertyInitializer),
                                   ByRef instanceInitializers As ArrayBuilder(Of FieldOrPropertyInitializer))

            Dim propertySymbol = SourcePropertySymbol.Create(Me, binder, syntax, blockSyntaxOpt, diagBag)

            AddPropertyAndAccessors(propertySymbol, binder, members)

            ' initialization can happen because of a "= value" (InitializerOpt) or a "As New Type(...)" (AsClauseOpt)
            Dim initializerOpt = syntax.Initializer
            Dim asClauseOpt = syntax.AsClause
A
angocke 已提交
2640
            Dim equalsValueOrAsNewSyntax As VisualBasicSyntaxNode
P
Pilchie 已提交
2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651
            If asClauseOpt IsNot Nothing AndAlso asClauseOpt.Kind = SyntaxKind.AsNewClause Then
                equalsValueOrAsNewSyntax = asClauseOpt
            Else
                equalsValueOrAsNewSyntax = initializerOpt
            End If

            If equalsValueOrAsNewSyntax IsNot Nothing Then
                Dim initializerOptRef = binder.GetSyntaxReference(equalsValueOrAsNewSyntax)
                Dim initializer = New FieldOrPropertyInitializer(propertySymbol, initializerOptRef)

                If propertySymbol.IsShared Then
2652
                    AddInitializer(staticInitializers, initializer, members.StaticSyntaxLength)
P
Pilchie 已提交
2653 2654 2655 2656 2657 2658 2659 2660 2661
                Else
                    ' auto implemented properties inside of structures can only have an initialization value
                    ' if they are shared.
                    If propertySymbol.IsAutoProperty AndAlso
                        propertySymbol.ContainingType.TypeKind = TypeKind.Structure Then

                        Binder.ReportDiagnostic(diagBag, syntax.Identifier, ERRID.ERR_AutoPropertyInitializedInStructure)
                    End If

2662
                    AddInitializer(instanceInitializers, initializer, members.InstanceSyntaxLength)
P
Pilchie 已提交
2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716
                End If
            End If
        End Sub

        Private Sub CreateEvent(syntax As EventStatementSyntax,
                           blockSyntaxOpt As EventBlockSyntax,
                           binder As Binder,
                           diagBag As DiagnosticBag,
                           members As MembersAndInitializersBuilder)

            Dim propertySymbol = New SourceEventSymbol(Me, binder, syntax, blockSyntaxOpt, diagBag)

            AddEventAndAccessors(propertySymbol, binder, members)
        End Sub

        Private Function CreateMethodMember(methodBaseSyntax As MethodBaseSyntax,
                                             binder As Binder,
                                             diagBag As DiagnosticBag) As SourceMethodSymbol
            Select Case methodBaseSyntax.Kind
                Case SyntaxKind.SubStatement, SyntaxKind.FunctionStatement
                    Return SourceMethodSymbol.CreateRegularMethod(Me, DirectCast(methodBaseSyntax, MethodStatementSyntax), binder, diagBag)
                Case SyntaxKind.SubNewStatement
                    Return SourceMethodSymbol.CreateConstructor(Me, DirectCast(methodBaseSyntax, SubNewStatementSyntax), binder, diagBag)
                Case SyntaxKind.OperatorStatement
                    Return SourceMethodSymbol.CreateOperator(Me, DirectCast(methodBaseSyntax, OperatorStatementSyntax), binder, diagBag)
                Case SyntaxKind.DeclareSubStatement, SyntaxKind.DeclareFunctionStatement
                    Return SourceMethodSymbol.CreateDeclareMethod(Me, DirectCast(methodBaseSyntax, DeclareStatementSyntax), binder, diagBag)
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(methodBaseSyntax.Kind)
            End Select
        End Function

        ''' <summary>
        ''' Check to see if we need a default instance|shared constructor, and if so, create it.
        ''' 
        ''' NOTE: we only need a shared constructor if there are any initializers to be 
        ''' injected into it, we don't create a constructor otherwise. In this case we also 
        ''' ignore const fields which will still require to be injected, because in this case
        ''' we don't see the constructor to be visible in symbol table.
        ''' </summary>
        Private Sub AddDefaultConstructorIfNeeded(members As MembersAndInitializersBuilder,
                                                  isShared As Boolean,
                                                  initializers As ArrayBuilder(Of ImmutableArray(Of FieldOrPropertyInitializer)),
                                                  diagnostics As DiagnosticBag)

            If TypeKind = TypeKind.Submission Then

                ' Only add an constructor if it is not shared OR if there are shared initializers
                If Not isShared OrElse Me.AnyInitializerToBeInjectedIntoConstructor(initializers, False) Then

                    ' a submission can only have a single declaration:
                    Dim syntaxRef = SyntaxReferences.Single()

                    Dim binder As Binder = BinderBuilder.CreateBinderForType(m_containingModule, syntaxRef.SyntaxTree, Me)
2717
                    Dim constructor As New SynthesizedSubmissionConstructorSymbol(syntaxRef, Me, isShared, binder, diagnostics)
P
Pilchie 已提交
2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773
                    AddMember(constructor, binder, members, omitDiagnostics:=False)
                End If

            ElseIf TypeKind = TypeKind.Class OrElse
                   TypeKind = TypeKind.Structure OrElse
                   TypeKind = TypeKind.Enum OrElse
                   (TypeKind = TypeKind.Module AndAlso isShared) Then

                ' Do we need to create a constructor? We need a constructor if this is 
                ' an instance constructor, or if this is a shared constructor and 
                ' there is at least one non-constant initializers
                Dim anyInitializersToInject As Boolean =
                        Me.AnyInitializerToBeInjectedIntoConstructor(initializers, Not isShared)
                ' NOTE: for shared constructor we DO NOT check for non-metadata-const 
                '       initializers, those will be addressed later

                If isShared AndAlso Not anyInitializersToInject Then
                    Return
                End If

                Dim isDebuggable As Boolean = anyInitializersToInject

                EnsureCtor(members, isShared, isDebuggable, diagnostics)
            End If
        End Sub

        Private Sub EnsureCtor(members As MembersAndInitializersBuilder, isShared As Boolean, isDebuggable As Boolean, diagBag As DiagnosticBag)

            Dim constructorName = If(isShared, WellKnownMemberNames.StaticConstructorName, WellKnownMemberNames.InstanceConstructorName)

            ' Check to see if we have already declared an instance|shared constructor.
            Dim symbols As ArrayBuilder(Of Symbol) = Nothing
            If members.Members.TryGetValue(constructorName, symbols) Then
                Debug.Assert(symbols.Where(Function(sym) sym.Kind = SymbolKind.Method AndAlso
                                (DirectCast(sym, MethodSymbol).MethodKind = MethodKind.Constructor OrElse
                                 DirectCast(sym, MethodSymbol).MethodKind = MethodKind.SharedConstructor)
                             ).Any)

                For Each method As MethodSymbol In symbols
                    If method.MethodKind = MethodKind.Constructor AndAlso method.ParameterCount = 0 Then
                        Return ' definitely don't need to synthesize a constructor
                    End If
                Next
                ' have to synthesize a constructor if this is a non-shared struct

                If TypeKind <> TypeKind.Structure OrElse isShared Then
                    Return ' already have an instance|shared constructor. Don't add another one.
                End If
            End If


            ' Add a new instance|shared constructor.
            Dim syntaxRef = SyntaxReferences.First() ' use arbitrary part
            ' TODO: does it need to be deterministic?

            Dim binder As Binder = BinderBuilder.CreateBinderForType(m_containingModule, syntaxRef.SyntaxTree, Me)
2774
            Dim constructor As New SynthesizedConstructorSymbol(syntaxRef, Me, isShared, isDebuggable, binder, diagBag)
P
Pilchie 已提交
2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144
            AddMember(constructor, binder, members, omitDiagnostics:=False)
        End Sub

        Private Sub AddWithEventsHookupConstructorsIfNeeded(members As MembersAndInitializersBuilder, diagBag As DiagnosticBag)
            If TypeKind = TypeKind.Submission Then
                'TODO: anything to do here?

            ElseIf TypeKind = TypeKind.Class OrElse TypeKind = TypeKind.Module Then

                ' we need a separate list of methods since we may need to modify the members dictionary.
                Dim sourceMethodsWithHandles As ArrayBuilder(Of SourceMethodSymbol) = Nothing

                For Each membersOfSameName In members.Members.Values
                    For Each member In membersOfSameName
                        Dim sourceMethod = TryCast(member, SourceMethodSymbol)
                        If sourceMethod IsNot Nothing Then
                            If Not sourceMethod.HandlesEvents Then
                                Continue For
                            End If

                            If sourceMethodsWithHandles Is Nothing Then
                                sourceMethodsWithHandles = ArrayBuilder(Of SourceMethodSymbol).GetInstance
                            End If
                            sourceMethodsWithHandles.Add(sourceMethod)
                        End If
                    Next
                Next

                If sourceMethodsWithHandles Is Nothing Then
                    ' no source methods with Handles - we are done
                    Return
                End If

                ' binder used if we need to find something in the base. will be created when needed
                Dim baseBinder As Binder = Nothing

                For Each sourceMethod In sourceMethodsWithHandles
                    Dim methodStatement = DirectCast(sourceMethod.DeclarationSyntax, MethodStatementSyntax)

                    For Each handlesClause In methodStatement.HandlesClause.Events
                        If handlesClause.EventContainer.Kind = SyntaxKind.KeywordEventContainer Then

                            If Not sourceMethod.IsShared Then
                                ' if the method is not shared, we will be hooking up in the instance ctor
                                EnsureCtor(members, isShared:=False, isDebuggable:=False, diagBag:=diagBag)
                            Else
                                ' if both event and handler are shared, then hookup goes into shared ctor
                                ' otherwise into instance ctor

                                ' find our event
                                Dim eventName = handlesClause.EventMember.Identifier.ValueText
                                Dim eventSym As EventSymbol = Nothing

                                ' look in current members
                                If handlesClause.EventContainer.Kind <> SyntaxKind.MyBaseKeyword Then
                                    Dim candidates As ArrayBuilder(Of Symbol) = Nothing
                                    If members.Members.TryGetValue(eventName, candidates) Then
                                        If candidates.Count = 1 AndAlso candidates(0).Kind = SymbolKind.Event Then
                                            eventSym = DirectCast(candidates(0), EventSymbol)
                                        End If
                                    End If
                                End If

                                ' try find in base
                                If eventSym Is Nothing Then
                                    ' Set up a binder.
                                    baseBinder = If(baseBinder, BinderBuilder.CreateBinderForType(m_containingModule, methodStatement.SyntaxTree, Me))

                                    Dim useSiteDiagnostics As HashSet(Of DiagnosticInfo) = Nothing
                                    eventSym = SourceMemberMethodSymbol.FindEvent(Me.BaseTypeNoUseSiteDiagnostics, baseBinder, eventName, isThroughMyBase:=True, useSiteDiagnostics:=useSiteDiagnostics)
                                    diagBag.Add(handlesClause.EventMember, useSiteDiagnostics)
                                End If

                                ' still nothing?
                                If eventSym Is Nothing Then
                                    Continue For
                                End If

                                EnsureCtor(members, eventSym.IsShared, isDebuggable:=False, diagBag:=diagBag)
                            End If
                        End If
                    Next
                Next

                sourceMethodsWithHandles.Free()
            End If
        End Sub

        Private Sub AddPropertyAndAccessors(propertySymbol As SourcePropertySymbol,
                                           binder As Binder,
                                           members As MembersAndInitializersBuilder)

            AddMember(propertySymbol, binder, members, omitDiagnostics:=False)
            If propertySymbol.GetMethod IsNot Nothing Then
                AddMember(propertySymbol.GetMethod, binder, members, omitDiagnostics:=False)
            End If
            If propertySymbol.SetMethod IsNot Nothing Then
                AddMember(propertySymbol.SetMethod, binder, members, omitDiagnostics:=False)
            End If
            If propertySymbol.AssociatedField IsNot Nothing Then
                AddMember(propertySymbol.AssociatedField, binder, members, omitDiagnostics:=False)
            End If
        End Sub

        Private Sub AddEventAndAccessors(eventSymbol As SourceEventSymbol,
                                   binder As Binder,
                                   members As MembersAndInitializersBuilder)

            AddMember(eventSymbol, binder, members, omitDiagnostics:=False)

            If eventSymbol.AddMethod IsNot Nothing Then
                AddMember(eventSymbol.AddMethod, binder, members, omitDiagnostics:=False)
            End If

            If eventSymbol.RemoveMethod IsNot Nothing Then
                AddMember(eventSymbol.RemoveMethod, binder, members, omitDiagnostics:=False)
            End If

            If eventSymbol.RaiseMethod IsNot Nothing Then
                AddMember(eventSymbol.RaiseMethod, binder, members, omitDiagnostics:=False)
            End If

            If eventSymbol.AssociatedField IsNot Nothing Then
                AddMember(eventSymbol.AssociatedField, binder, members, omitDiagnostics:=False)
            End If
        End Sub


        Private Sub CheckMemberDiagnostics(
                             members As MembersAndInitializersBuilder,
                             diagBag As DiagnosticBag)

            If Me.Locations.Length > 1 AndAlso Not Me.IsPartial Then
                ' Suppress conflict member diagnostics when the enclosing type is an accidental duplicate
                Return
            End If

            For Each pair In members.DeferredMemberDiagnostic
                Dim sym As Symbol = pair.Item1
                Dim binder As Binder = pair.Item2

                ' Check name for duplicate type declarations
                ' First check if the member name conflicts with a type declaration in the container then
                ' Check if the member name conflicts with another member in the container.
                If Not CheckIfMemberNameConflictsWithTypeMember(sym, members, diagBag) Then
                    CheckIfMemberNameIsDuplicate(sym, diagBag, members)
                End If

                If sym.CanBeReferencedByName AndAlso
                    TypeParameters.MatchesAnyName(sym.Name) Then
                    If sym.IsImplicitlyDeclared Then
                        Dim symImplicitlyDefinedBy = sym.ImplicitlyDefinedBy(members.Members)
                        Debug.Assert(symImplicitlyDefinedBy IsNot Nothing)

                        ' "{0} '{1}' implicitly defines a member '{2}' which has the same name as a type parameter."
                        Binder.ReportDiagnostic(diagBag,
                                                symImplicitlyDefinedBy.Locations(0),
                                                ERRID.ERR_SyntMemberShadowsGenericParam3,
                                                symImplicitlyDefinedBy.GetKindText(),
                                                symImplicitlyDefinedBy.Name,
                                                sym.Name)
                    Else
                        ' "'{0}' has the same name as a type parameter."
                        Binder.ReportDiagnostic(diagBag, sym.Locations(0), ERRID.ERR_ShadowingGenericParamWithMember1, sym.Name)
                    End If
                End If
            Next
        End Sub

        Friend Sub AddMember(sym As Symbol,
                             binder As Binder,
                             members As MembersAndInitializersBuilder,
                             omitDiagnostics As Boolean)

            If Not omitDiagnostics Then
                members.DeferredMemberDiagnostic.Add(ValueTuple.Create(sym, binder))
            End If

            AddSymbolToMembers(sym, members.Members)
        End Sub

        Friend Sub AddSymbolToMembers(memberSymbol As Symbol,
                                      members As Dictionary(Of String, ArrayBuilder(Of Symbol)))

            Dim symbols As ArrayBuilder(Of Symbol) = Nothing
            If members.TryGetValue(memberSymbol.Name, symbols) Then
                symbols.Add(memberSymbol)
            Else
                symbols = New ArrayBuilder(Of Symbol)

                symbols.Add(memberSymbol)
                members(memberSymbol.Name) = symbols
            End If
        End Sub

        Private Function CheckIfMemberNameConflictsWithTypeMember(sym As Symbol,
                                                                  members As MembersAndInitializersBuilder,
                                                                  diagBag As DiagnosticBag) As Boolean
            ' Check name for conflicts with type members
            Dim definedTypes = Me.GetTypeMembers(sym.Name)

            If definedTypes.Length > 0 Then
                Dim type = definedTypes(0)
                If sym <> type Then
                    Return CheckIfMemberNameIsDuplicate(sym, type, members, diagBag, includeKind:=True)
                End If
            End If

            Return False
        End Function

        Private Function CheckIfMemberNameIsDuplicate(sym As Symbol,
                                                      diagBag As DiagnosticBag,
                                                      members As MembersAndInitializersBuilder) As Boolean
            ' Check name for duplicate declarations
            Dim definedSymbols As ArrayBuilder(Of Symbol) = Nothing

            If members.Members.TryGetValue(sym.Name, definedSymbols) Then
                Debug.Assert(definedSymbols.Count > 0)
                Dim other = definedSymbols(0)
                If (sym <> other) Then
                    Return CheckIfMemberNameIsDuplicate(sym, other, members, diagBag, includeKind:=False)
                End If
            End If

            Return False
        End Function

        Private Function CheckIfMemberNameIsDuplicate(firstSymbol As Symbol,
                                          secondSymbol As Symbol,
                                          members As MembersAndInitializersBuilder,
                                          diagBag As DiagnosticBag,
                                          includeKind As Boolean) As Boolean

            Dim firstAssociatedSymbol = secondSymbol.ImplicitlyDefinedBy(members.Members)
            If firstAssociatedSymbol Is Nothing AndAlso secondSymbol.IsUserDefinedOperator() Then
                ' For the purpose of this check, operator methods are treated as implicitly defined by themselves.
                firstAssociatedSymbol = secondSymbol
            End If

            Dim secondAssociatedSymbol = firstSymbol.ImplicitlyDefinedBy(members.Members)
            If secondAssociatedSymbol Is Nothing AndAlso firstSymbol.IsUserDefinedOperator() Then
                ' For the purpose of this check, operator methods are treated as implicitly defined by themselves.
                secondAssociatedSymbol = firstSymbol
            End If

            If firstAssociatedSymbol IsNot Nothing Then
                If secondAssociatedSymbol Is Nothing Then
                    Dim asType = TryCast(firstAssociatedSymbol, TypeSymbol)
                    If asType IsNot Nothing AndAlso asType.IsEnumType Then
                        ' enum members may conflict only with __Value and that produces a special diagnostics.
                        Return True
                    End If

                    ' "{0} '{1}' implicitly defines '{2}', which conflicts with a member of the same name in {3} '{4}'."
                    Binder.ReportDiagnostic(
                            diagBag,
                            firstAssociatedSymbol.Locations(0),
                            ERRID.ERR_SynthMemberClashesWithMember5,
                            firstAssociatedSymbol.GetKindText(),
                            OverrideHidingHelper.AssociatedSymbolName(firstAssociatedSymbol),
                            secondSymbol.Name,
                            Me.GetKindText(),
                            Me.Name)

                    Return True
                Else
                    ' If both symbols are implicitly defined (say an overloaded property P where each
                    ' overload implicitly defines get_P), no error is reported. 
                    ' If there are any errors in cases if definitng members have same names.
                    ' In such cases, the errors should be reported on the defining symbols.

                    If Not CaseInsensitiveComparison.Equals(firstAssociatedSymbol.Name,
                                                       secondAssociatedSymbol.Name) Then
                        '{0} '{1}' implicitly defines '{2}', which conflicts with a member implicitly declared for {3} '{4}' in {5} '{6}'.
                        Binder.ReportDiagnostic(
                                diagBag,
                                firstAssociatedSymbol.Locations(0),
                                ERRID.ERR_SynthMemberClashesWithSynth7,
                                firstAssociatedSymbol.GetKindText(),
                                OverrideHidingHelper.AssociatedSymbolName(firstAssociatedSymbol),
                                secondSymbol.Name,
                                secondAssociatedSymbol.GetKindText(),
                                OverrideHidingHelper.AssociatedSymbolName(secondAssociatedSymbol),
                                Me.GetKindText(),
                                Me.Name)
                    End If
                End If
            ElseIf secondAssociatedSymbol IsNot Nothing Then

                ' "{0} '{1}' conflicts with a member implicitly declared for {2} '{3}' in {4} '{5}'."
                Binder.ReportDiagnostic(
                        diagBag,
                        secondSymbol.Locations(0),
                        ERRID.ERR_MemberClashesWithSynth6,
                        secondSymbol.GetKindText(),
                        secondSymbol.Name,
                        secondAssociatedSymbol.GetKindText(),
                        OverrideHidingHelper.AssociatedSymbolName(secondAssociatedSymbol),
                        Me.GetKindText(),
                        Me.Name)

                Return True

            ElseIf ((firstSymbol.Kind <> SymbolKind.Method) AndAlso (Not firstSymbol.IsPropertyAndNotWithEvents)) OrElse
                    (firstSymbol.Kind <> secondSymbol.Kind) Then

                If Me.IsEnumType() Then

                    ' For Enum members, give more specific simpler errors.
                    ' "'{0}' is already declared in this {1}."
                    Binder.ReportDiagnostic(
                            diagBag,
                            firstSymbol.Locations(0),
                            ERRID.ERR_MultiplyDefinedEnumMember2,
                            firstSymbol.Name,
                            Me.GetKindText())

                Else
                    ' the formatting of this error message is quite special and needs special treatment
                    ' e.g. 'foo' is already declared as 'Class Foo' in this class.

                    ' "'{0}' is already declared as '{1}' in this {2}."
                    Binder.ReportDiagnostic(
                            diagBag,
                            firstSymbol.Locations(0),
                            ERRID.ERR_MultiplyDefinedType3,
                            firstSymbol.Name,
                            If(includeKind,
                               DirectCast(CustomSymbolDisplayFormatter.ErrorNameWithKind(secondSymbol), Object),
                               secondSymbol),
                            Me.GetKindText())
                End If

                Return True
            End If

            Return False
        End Function

        Public Overrides ReadOnly Property MemberNames As IEnumerable(Of String)
            Get
                Return m_declaration.MemberNames
            End Get
        End Property

        Friend Overrides Function GetMembersUnordered() As ImmutableArray(Of Symbol)
            If m_lazyMembersFlattened.IsDefault Then
                Dim lookup = Me.MemberAndInitializerLookup
                Dim result = lookup.Members.Flatten(Nothing)  ' Do Not sort right now.
                ImmutableInterlocked.InterlockedInitialize(Me.m_lazyMembersFlattened, result)
            End If

#If DEBUG Then
            ' In DEBUG, swap first And last elements so that use of Unordered in a place it isn't warranted is caught
            ' more obviously.
            Return m_lazyMembersFlattened.DeOrder()
#Else
            Return m_lazyMembersFlattened
#End If
        End Function

        Public Overloads Overrides Function GetMembers() As ImmutableArray(Of Symbol)
            If (m_lazyState And StateFlags.FlattenedMembersIsSortedMask) <> 0 Then
                Return m_lazyMembersFlattened

            Else
                Dim allMembers = Me.GetMembersUnordered()

                If allMembers.Length >= 2 Then
P
Pharring 已提交
3145 3146
                    allMembers = allMembers.Sort(LexicalOrderSymbolComparer.Instance)
                    ImmutableInterlocked.InterlockedExchange(m_lazyMembersFlattened, allMembers)
P
Pilchie 已提交
3147 3148 3149 3150
                End If

                ThreadSafeFlagOperations.Set(m_lazyState, StateFlags.FlattenedMembersIsSortedMask)

P
Pharring 已提交
3151
                Return allMembers
P
Pilchie 已提交
3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188
            End If
        End Function

        Public Overloads Overrides Function GetMembers(name As String) As ImmutableArray(Of Symbol)
            Dim lookup = Me.MemberAndInitializerLookup
            Dim members As ImmutableArray(Of Symbol) = Nothing

            If lookup.Members.TryGetValue(name, members) Then
                Return members
            End If

            Return ImmutableArray(Of Symbol).Empty
        End Function

        Friend Overrides Function GetSimpleNonTypeMembers(name As String) As ImmutableArray(Of Symbol)
            If m_lazyMembersAndInitializers IsNot Nothing OrElse MemberNames.Contains(name) Then
                Return GetMembers(name)
            End If

            Return ImmutableArray(Of Symbol).Empty
        End Function

        ''' <summary>
        ''' In case the passed initializers require a shared constructor, this method returns a new MethodSymbol instance for the 
        ''' shared constructor if there is not already an explicit shared constructor
        ''' </summary>
        Friend Function CreateSharedConstructorsForConstFieldsIfRequired(binder As Binder, diagnostics As DiagnosticBag) As MethodSymbol
            Dim lookup = Me.MemberAndInitializerLookup
            Dim staticInitializers = lookup.StaticInitializers

            If Not staticInitializers.IsDefaultOrEmpty Then
                Dim symbols As ImmutableArray(Of Symbol) = Nothing
                If Not MemberAndInitializerLookup.Members.TryGetValue(WellKnownMemberNames.StaticConstructorName, symbols) Then

                    ' call AnyInitializerToBeInjectedIntoConstructor if only there is no static constructor
                    If Me.AnyInitializerToBeInjectedIntoConstructor(staticInitializers, True) Then
                        Dim syntaxRef = SyntaxReferences.First() ' use arbitrary part
3189
                        Return New SynthesizedConstructorSymbol(syntaxRef, Me,
P
Pilchie 已提交
3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224
                                                                isShared:=True, isDebuggable:=True,
                                                                binder:=binder, diagnostics:=diagnostics)
                    End If
                End If
            End If

            Return Nothing
        End Function

        Friend Overrides Iterator Function GetFieldsToEmit() As IEnumerable(Of FieldSymbol)
            For Each member In GetMembersForCci()
                If member.Kind = SymbolKind.Field Then
                    Yield DirectCast(member, FieldSymbol)
                End If
            Next
        End Function

        ''' <summary>
        ''' Gets the static initializers.
        ''' </summary>
        Public ReadOnly Property StaticInitializers As ImmutableArray(Of ImmutableArray(Of FieldOrPropertyInitializer))
            Get
                Return Me.MemberAndInitializerLookup.StaticInitializers
            End Get
        End Property

        ''' <summary>
        ''' Gets the instance initializers.
        ''' </summary>
        Public ReadOnly Property InstanceInitializers As ImmutableArray(Of ImmutableArray(Of FieldOrPropertyInitializer))
            Get
                Return Me.MemberAndInitializerLookup.InstanceInitializers
            End Get
        End Property

3225
        Friend Function CalculateSyntaxOffsetInSynthesizedConstructor(position As Integer, tree As SyntaxTree, isShared As Boolean) As Integer
3226
            If IsScriptClass AndAlso Not isShared Then
3227 3228
                Dim aggregateLength As Integer = 0

3229 3230 3231
                For Each declaration In Me.m_declaration.Declarations
                    Dim syntaxRef = declaration.SyntaxReference

3232 3233
                    If tree Is syntaxRef.SyntaxTree Then
                        Return aggregateLength + position
3234 3235 3236 3237
                    End If

                    aggregateLength += syntaxRef.Span.Length
                Next
3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301

                Throw ExceptionUtilities.Unreachable
            End If

            Dim syntaxOffset As Integer
            If TryCalculateSyntaxOffsetOfPositionInInitializer(position, tree, isShared, syntaxOffset:=syntaxOffset) Then
                Return syntaxOffset
            End If

            ' An implicit constructor has no body and no initializer, so the variable has to be declared in a member initializer
            Throw ExceptionUtilities.Unreachable            
        End Function

        ' Calculates a syntax offset of a syntax position that is contained in a property or field initializer (if it is in fact contained in one).
        Friend Function TryCalculateSyntaxOffsetOfPositionInInitializer(position As Integer, tree As SyntaxTree, isShared As Boolean, ByRef syntaxOffset As Integer) As Boolean
            Dim membersAndInitializers = GetMembersAndInitializers()
            Dim allInitializers = If(isShared, membersAndInitializers.StaticInitializers, membersAndInitializers.InstanceInitializers)

            Dim siblingInitializers = GetInitializersInSourceTree(tree, allInitializers)
            Dim index = IndexOfInitializerContainingPosition(siblingInitializers, position)
            If index < 0 Then
                syntaxOffset = 0
                Return False
            End If

            '                                 |<-----------distanceFromCtorBody---------->|
            ' [      initializer 0    ][ initializer 1 ][ initializer 2 ][ initializer 3 ][ctor body]
            ' |<--preceding init len-->|      ^
            '                              position 
            Dim initializersLength = If(isShared, membersAndInitializers.StaticInitializersSyntaxLength, membersAndInitializers.InstanceInitializersSyntaxLength)
            Dim distanceFromInitializerStart = position - siblingInitializers(index).Syntax.Span.Start
            Dim distanceFromCtorBody = initializersLength - (siblingInitializers(index).PrecedingInitializersLength + distanceFromInitializerStart)

            Debug.Assert(distanceFromCtorBody > 0)

            ' syntax offset 0 is at the start of the ctor body:
            syntaxOffset = -distanceFromCtorBody
            Return True
        End Function

        Friend Shared Function GetInitializersInSourceTree(tree As SyntaxTree, initializers As ImmutableArray(Of ImmutableArray(Of FieldOrPropertyInitializer))) As ImmutableArray(Of FieldOrPropertyInitializer)
            For Each siblingInitializers In initializers
                If (siblingInitializers.First().Syntax.SyntaxTree Is tree) Then
                    Return siblingInitializers
                End If
            Next

            Return ImmutableArray(Of FieldOrPropertyInitializer).Empty
        End Function

        Friend Shared Function IndexOfInitializerContainingPosition(initializers As ImmutableArray(Of FieldOrPropertyInitializer), position As Integer) As Integer
            ' Search for the start of the span (the spans are non-overlapping and sorted)
            Dim index = initializers.BinarySearch(position, Function(initializer, pos) initializer.Syntax.Span.Start.CompareTo(pos))

            ' Binary search returns non-negative result if the position is exactly the start of some span.
            If index >= 0 Then
                Return index
            End If

            ' Otherwise, ~index is the closest span whose start is greater than the position.
            ' Make sure that this closest span contains the position.
            index = (Not index) - 1
            If index >= 0 AndAlso initializers(index).Syntax.Span.Contains(position) Then
                Return index
3302 3303
            End If

3304
            Return -1
3305 3306
        End Function

P
Pilchie 已提交
3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746
        Public Overrides ReadOnly Property MightContainExtensionMethods As Boolean
            Get
                ' Only Modules can declare extension methods.

                If m_lazyContainsExtensionMethods = ThreeState.Unknown Then
                    If Not (m_containingSymbol.Kind = SymbolKind.Namespace AndAlso Me.TypeKind = TypeKind.Module AndAlso Me.AnyMemberHasAttributes) Then
                        m_lazyContainsExtensionMethods = ThreeState.False
                    End If
                End If

                Return m_lazyContainsExtensionMethods <> ThreeState.False
            End Get
        End Property

        Friend Overrides Sub BuildExtensionMethodsMap(map As Dictionary(Of String, ArrayBuilder(Of MethodSymbol)),
                                                      appendThrough As NamespaceSymbol)
            If Me.MightContainExtensionMethods Then
                Dim lookup = Me.MemberAndInitializerLookup

                If Not appendThrough.BuildExtensionMethodsMap(map, lookup.Members) Then
                    ' Didn't find any extension methods, record the fact.
                    m_lazyContainsExtensionMethods = ThreeState.False
                End If
            End If
        End Sub

        Friend Overrides Sub AddExtensionMethodLookupSymbolsInfo(nameSet As LookupSymbolsInfo,
                                                                 options As LookupOptions,
                                                                 originalBinder As Binder,
                                                                 appendThrough As NamedTypeSymbol)
            If Me.MightContainExtensionMethods Then
                Dim lookup = Me.MemberAndInitializerLookup

                If Not appendThrough.AddExtensionMethodLookupSymbolsInfo(nameSet, options, originalBinder, lookup.Members) Then
                    ' Didn't find any extension methods, record the fact.
                    m_lazyContainsExtensionMethods = ThreeState.False
                End If
            End If
        End Sub

        ' Build the explicit interface map for this type. 
        '
        ' Also diagnoses the following errors and places diagnostics in the diagnostic bag:
        '   Same symbol implemented twice
        '   Interface symbol that should have been implemented wasn't.
        '   Generic interfaces might unify.
        Private Function MakeExplicitInterfaceImplementationMap(diagnostics As DiagnosticBag) As Dictionary(Of Symbol, Symbol)
            If Me.IsClassType() OrElse Me.IsStructureType() OrElse Me.IsInterfaceType() Then
                CheckInterfaceUnificationAndVariance(diagnostics)
            End If

            If Me.IsClassType() OrElse Me.IsStructureType() Then
                ' Go through all explicit interface implementations and record them.
                Dim map = New Dictionary(Of Symbol, Symbol)()
                For Each implementingMember In Me.GetMembers()
                    For Each interfaceMember In GetExplicitInterfaceImplementations(implementingMember)
                        If Not map.ContainsKey(interfaceMember) Then
                            map.Add(interfaceMember, implementingMember)
                        Else
                            'the same symbol was implemented twice.
                            If ShouldReportImplementationError(interfaceMember) Then
                                Dim diag = ErrorFactory.ErrorInfo(ERRID.ERR_MethodAlreadyImplemented2,
                                                                  CustomSymbolDisplayFormatter.ShortNameWithTypeArgs(interfaceMember.ContainingType),
                                                                  CustomSymbolDisplayFormatter.ShortErrorName(interfaceMember))
                                diagnostics.Add(New VBDiagnostic(diag, GetImplementingLocation(implementingMember, interfaceMember)))
                            End If
                        End If
                    Next
                Next

                ' Check to make sure all members of interfaces were implemented. Note that if our base class implemented
                ' an interface, we do not have to implemented those members again (although we can).
                For Each iface In InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics
                    ' Only check interfaces that our base type does NOT implement.
                    If Not Me.BaseTypeNoUseSiteDiagnostics.ImplementsInterface(iface, useSiteDiagnostics:=Nothing) Then
                        For Each ifaceMember In iface.GetMembers()
                            If ifaceMember.RequiresImplementation() AndAlso ShouldReportImplementationError(ifaceMember) Then
                                Dim implementingMember As Symbol = Nothing
                                Dim useSiteErrorInfo = ifaceMember.GetUseSiteErrorInfo()
                                If Not map.TryGetValue(ifaceMember, implementingMember) Then
                                    'member was not implemented.
                                    Dim diag = If(useSiteErrorInfo, ErrorFactory.ErrorInfo(ERRID.ERR_UnimplementedMember3,
                                                                        If(Me.IsStructureType(), "Structure", "Class"),
                                                                        CustomSymbolDisplayFormatter.ShortErrorName(Me),
                                                                        ifaceMember,
                                                                        CustomSymbolDisplayFormatter.ShortNameWithTypeArgs(iface)))
                                    diagnostics.Add(New VBDiagnostic(diag, GetImplementsLocation(iface)))
                                Else
                                    If useSiteErrorInfo IsNot Nothing Then
                                        diagnostics.Add(New VBDiagnostic(useSiteErrorInfo, implementingMember.Locations(0)))
                                    End If
                                End If
                            End If
                        Next
                    End If
                Next

                If map.Count > 0 Then
                    Return map
                Else
                    Return EmptyExplicitImplementationMap  ' Better to use singleton and garbage collection the empty dictionary we just created.
                End If
            Else
                Return EmptyExplicitImplementationMap
            End If
        End Function

        ' Should we report implementation errors for this member?
        ' We don't report errors on accessors, because we already report the errors on their containing property/event.
        Private Function ShouldReportImplementationError(interfaceMember As Symbol) As Boolean
            If interfaceMember.Kind = SymbolKind.Method AndAlso DirectCast(interfaceMember, MethodSymbol).MethodKind <> MethodKind.Ordinary Then
                Return False
            Else
                Return True
            End If
        End Function

        ' Get a dictionary with all the explicitly implemented interface symbols declared on this type. key = interface
        ' method/property/event, value = explicitly implementing method/property/event declared on this type
        '
        ' Getting this property also ensures that diagnostics relating to interface implementation, overriding, hiding and 
        ' overloading are created.
        Friend Overrides ReadOnly Property ExplicitInterfaceImplementationMap As Dictionary(Of Symbol, Symbol)
            Get
                If m_lazyExplicitInterfaceImplementationMap Is Nothing Then
                    Dim diagnostics As DiagnosticBag = DiagnosticBag.GetInstance()
                    Dim implementationMap = MakeExplicitInterfaceImplementationMap(diagnostics)
                    OverrideHidingHelper.CheckHidingAndOverridingForType(Me, diagnostics)

                    ' checking the overloads accesses the parameter types. The parameters are now lazily created to not happen at 
                    ' method/property symbol creation time. That's the reason why this check is delayed until here.
                    CheckForOverloadsErrors(diagnostics)

                    m_containingModule.AtomicStoreReferenceAndDiagnostics(m_lazyExplicitInterfaceImplementationMap, implementationMap, diagnostics, CompilationStage.Declare)
                    diagnostics.Free()
                End If

                Return m_lazyExplicitInterfaceImplementationMap
            End Get
        End Property

        ''' <summary>
        ''' Reports the overloads error for this type.
        ''' </summary>
        ''' <param name="diagnostics">The diagnostics.</param>
        Private Sub CheckForOverloadsErrors(diagnostics As DiagnosticBag)
            Debug.Assert(Me.IsDefinition) ' Don't do this on constructed types

            ' Enums and Delegates have nothing to do.
            Dim myTypeKind As TypeKind = Me.TypeKind
            Dim operatorsKnownToHavePair As HashSet(Of MethodSymbol) = Nothing

            If myTypeKind = TypeKind.Class OrElse myTypeKind = TypeKind.Interface OrElse myTypeKind = TypeKind.Structure OrElse myTypeKind = TypeKind.Module Then
                Dim lookup = MemberAndInitializerLookup

                Dim structEnumerator As Dictionary(Of String, ImmutableArray(Of Symbol)).Enumerator = lookup.Members.GetEnumerator
                Dim canDeclareOperators As Boolean = (myTypeKind <> TypeKind.Module AndAlso myTypeKind <> TypeKind.Interface)

                While structEnumerator.MoveNext()
                    Dim memberList As ImmutableArray(Of Symbol) = structEnumerator.Current.Value

                    ' only proceed if there are multiple declarations of methods with the same name
                    If memberList.Length < 2 Then
                        ' Validate operator overloading
                        If canDeclareOperators AndAlso CheckForOperatorOverloadingErrors(memberList, 0, structEnumerator, operatorsKnownToHavePair, diagnostics) Then
                            Continue While
                        End If

                        Continue While
                    End If

                    For Each memberKind In {SymbolKind.Method, SymbolKind.Property}

                        For memberIndex = 0 To memberList.Length - 2
                            Dim member As Symbol = memberList(memberIndex)
                            If member.Kind <> memberKind OrElse member.IsAccessor OrElse member.IsWithEventsProperty Then
                                Continue For
                            End If

                            ' Validate operator overloading
                            If canDeclareOperators AndAlso memberKind = SymbolKind.Method AndAlso
                               CheckForOperatorOverloadingErrors(memberList, memberIndex, structEnumerator, operatorsKnownToHavePair, diagnostics) Then
                                Continue For
                            End If

                            Dim sourceMethod = TryCast(member, SourceMemberMethodSymbol)
                            If sourceMethod IsNot Nothing Then
                                Debug.Assert(Not sourceMethod.IsUserDefinedOperator())
                                If sourceMethod.IsUserDefinedOperator() Then
                                    Continue For
                                End If

                                If sourceMethod.SuppressDuplicateProcDefDiagnostics Then
                                    Continue For
                                End If
                            End If

                            ' TODO: this is O(N^2), maybe this can be improved.
                            For nextMemberIndex = memberIndex + 1 To memberList.Length - 1
                                Dim nextMember = memberList(nextMemberIndex)
                                If nextMember.Kind <> memberKind OrElse nextMember.IsAccessor OrElse nextMember.IsWithEventsProperty Then
                                    Continue For
                                End If

                                sourceMethod = TryCast(nextMember, SourceMemberMethodSymbol)
                                If sourceMethod IsNot Nothing Then
                                    If sourceMethod.IsUserDefinedOperator() Then
                                        Continue For
                                    End If

                                    If sourceMethod.SuppressDuplicateProcDefDiagnostics Then
                                        Continue For
                                    End If
                                End If

                                ' only process non synthesized symbols
                                If Not member.IsImplicitlyDeclared AndAlso
                                   Not nextMember.IsImplicitlyDeclared Then

                                    ' Overload resolution (CollectOverloadedCandidates) does similar check for imported members
                                    ' from the same container. Both places should be in sync. CheckForOperatorOverloadingErrors too.
                                    Dim comparisonResults As SymbolComparisonResults = OverrideHidingHelper.DetailedSignatureCompare(
                                        member,
                                        nextMember,
                                        SymbolComparisonResults.AllMismatches And Not (SymbolComparisonResults.CallingConventionMismatch Or SymbolComparisonResults.ConstraintMismatch))

                                    ' only report diagnostics if the signature is considered equal following VB rules.
                                    If (comparisonResults And Not SymbolComparisonResults.MismatchesForConflictingMethods) = 0 Then
                                        ReportOverloadsErrors(comparisonResults, member, nextMember, member.Locations(0), diagnostics)
                                        Exit For
                                    End If
                                End If
                            Next
                        Next

                        ' Validate operator overloading for the last operator, it is not handled by the loop
                        If canDeclareOperators AndAlso memberKind = SymbolKind.Method AndAlso
                           CheckForOperatorOverloadingErrors(memberList, memberList.Length - 1, structEnumerator, operatorsKnownToHavePair, diagnostics) Then
                            Continue For
                        End If
                    Next
                End While
            End If
        End Sub

        ''' <summary>
        ''' Returns True if memberList(memberIndex) is an operator.
        ''' Also performs operator overloading validation and reports appropriate errors.
        ''' </summary>
        Private Function CheckForOperatorOverloadingErrors(
            memberList As ImmutableArray(Of Symbol),
            memberIndex As Integer,
            membersEnumerator As Dictionary(Of String, ImmutableArray(Of Symbol)).Enumerator,
            <[In](), Out()> ByRef operatorsKnownToHavePair As HashSet(Of MethodSymbol),
            diagnostics As DiagnosticBag
        ) As Boolean
            Dim member As Symbol = memberList(memberIndex)

            If member.Kind <> SymbolKind.Method Then
                Return False
            End If

            Dim method = DirectCast(member, MethodSymbol)
            Dim significantDiff As SymbolComparisonResults = Not SymbolComparisonResults.MismatchesForConflictingMethods
            Dim methodMethodKind As MethodKind = method.MethodKind

            Select Case methodMethodKind
                Case MethodKind.Conversion
                    significantDiff = significantDiff Or SymbolComparisonResults.ReturnTypeMismatch
                Case MethodKind.UserDefinedOperator
                Case Else
                    ' Not an operator.
                    Return False
            End Select

            Dim opInfo As OverloadResolution.OperatorInfo = OverloadResolution.GetOperatorInfo(method.Name)

            If Not OverloadResolution.ValidateOverloadedOperator(method, opInfo, diagnostics) Then
                ' Mulformed operator, but still an operator.
                Return True
            End If

            ' Check conflicting overloading with other operators.
            If IsConflictingOperatorOverloading(method, significantDiff, memberList, memberIndex + 1, diagnostics) Then
                Return True
            End If

            ' CType overloads across Widening and Narrowing, which use different metadata names.
            ' Need to handle this specially.
            If methodMethodKind = MethodKind.Conversion Then
                Dim otherName As String = If(IdentifierComparison.Equals(WellKnownMemberNames.ImplicitConversionName, method.Name),
                                             WellKnownMemberNames.ExplicitConversionName, WellKnownMemberNames.ImplicitConversionName)

                Dim otherMembers As ImmutableArray(Of Symbol) = Nothing
                If MemberAndInitializerLookup.Members.TryGetValue(otherName, otherMembers) Then
                    While membersEnumerator.MoveNext()
                        If membersEnumerator.Current.Value = otherMembers Then
                            If IsConflictingOperatorOverloading(method, significantDiff, otherMembers, 0, diagnostics) Then
                                Return True
                            End If

                            Exit While
                        End If
                    End While
                End If
            End If

            ' Check for operators that must be declared in pairs.
            Dim nameOfThePair As String = Nothing

            If opInfo.IsUnary Then
                Select Case opInfo.UnaryOperatorKind
                    Case UnaryOperatorKind.IsTrue
                        nameOfThePair = WellKnownMemberNames.FalseOperatorName
                    Case UnaryOperatorKind.IsFalse
                        nameOfThePair = WellKnownMemberNames.TrueOperatorName
                End Select
            Else
                Select Case opInfo.BinaryOperatorKind
                    Case BinaryOperatorKind.Equals
                        nameOfThePair = WellKnownMemberNames.InequalityOperatorName
                    Case BinaryOperatorKind.NotEquals
                        nameOfThePair = WellKnownMemberNames.EqualityOperatorName
                    Case BinaryOperatorKind.LessThan
                        nameOfThePair = WellKnownMemberNames.GreaterThanOperatorName
                    Case BinaryOperatorKind.GreaterThan
                        nameOfThePair = WellKnownMemberNames.LessThanOperatorName
                    Case BinaryOperatorKind.LessThanOrEqual
                        nameOfThePair = WellKnownMemberNames.GreaterThanOrEqualOperatorName
                    Case BinaryOperatorKind.GreaterThanOrEqual
                        nameOfThePair = WellKnownMemberNames.LessThanOrEqualOperatorName
                End Select
            End If

            If nameOfThePair IsNot Nothing AndAlso
               (operatorsKnownToHavePair Is Nothing OrElse Not operatorsKnownToHavePair.Contains(method)) Then

                Dim otherMembers As ImmutableArray(Of Symbol) = Nothing
                If MemberAndInitializerLookup.Members.TryGetValue(nameOfThePair, otherMembers) Then
                    For Each other As Symbol In otherMembers
                        If other.IsUserDefinedOperator() Then
                            Dim otherMethod = DirectCast(other, MethodSymbol)
                            Dim comparisonResults As SymbolComparisonResults = MethodSignatureComparer.DetailedCompare(
                                method,
                                otherMethod,
                                SymbolComparisonResults.AllMismatches And
                                Not (SymbolComparisonResults.CallingConventionMismatch Or
                                     SymbolComparisonResults.ConstraintMismatch Or
                                     SymbolComparisonResults.CustomModifierMismatch Or
                                     SymbolComparisonResults.NameMismatch))

                            If (comparisonResults And (Not SymbolComparisonResults.MismatchesForConflictingMethods Or SymbolComparisonResults.ReturnTypeMismatch)) = 0 Then
                                ' Found the pair
                                If operatorsKnownToHavePair Is Nothing Then
                                    operatorsKnownToHavePair = New HashSet(Of MethodSymbol)(ReferenceEqualityComparer.Instance)
                                End If

                                operatorsKnownToHavePair.Add(otherMethod)

                                Return True
                            End If
                        End If
                    Next
                End If

                diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_MatchingOperatorExpected2,
                                                       SyntaxFacts.GetText(OverloadResolution.GetOperatorTokenKind(nameOfThePair)),
                                                       method), method.Locations(0))
            End If

            Return True
        End Function

        ''' <summary>
        ''' See if any member in [memberList] starting with [memberIndex] conflict with [method],
        ''' report appropriate error and return true. 
        ''' </summary>
        Private Function IsConflictingOperatorOverloading(
            method As MethodSymbol,
            significantDiff As SymbolComparisonResults,
            memberList As ImmutableArray(Of Symbol),
            memberIndex As Integer,
            diagnostics As DiagnosticBag
        ) As Boolean
            For nextMemberIndex = memberIndex To memberList.Length - 1
                Dim nextMember = memberList(nextMemberIndex)
                If nextMember.Kind <> SymbolKind.Method Then
                    Continue For
                End If

                Dim nextMethod = DirectCast(nextMember, MethodSymbol)

                If nextMethod.MethodKind <> method.MethodKind Then
                    Continue For
                End If

                Dim comparisonResults As SymbolComparisonResults = MethodSignatureComparer.DetailedCompare(
                    method,
                    nextMethod,
                    SymbolComparisonResults.AllMismatches And
                    Not (SymbolComparisonResults.CallingConventionMismatch Or
                         SymbolComparisonResults.ConstraintMismatch Or
                         SymbolComparisonResults.CustomModifierMismatch Or
                         SymbolComparisonResults.NameMismatch))

                ' only report diagnostics if the signature is considered equal following VB rules.
                If (comparisonResults And significantDiff) = 0 Then
                    ReportOverloadsErrors(comparisonResults, method, nextMethod, method.Locations(0), diagnostics)
                    Return True
                End If
            Next

            Return False
        End Function

        ''' <summary>
        ''' Check for two different diagnostics on the set of implemented interfaces:
        '''   1) It is invalid for a type to directly (vs through a base class) implement two interfaces that
        '''   unify (i.e. are the same for some substitution of type parameters).
        ''' 
        '''   2) It is a warning to implement variant interfaces twice with type arguments that could cause
        '''   ambiguity during method dispatch.
        ''' </summary>
        Private Sub CheckInterfaceUnificationAndVariance(diagnostics As DiagnosticBag)
            Dim interfaces = Me.InterfacesAndTheirBaseInterfacesNoUseSiteDiagnostics

            If interfaces.Count < 2 Then
                Return ' can't have any conflicts
            End If

            ' We only need to check pairs of generic interfaces that have the same original definition. Put the interfaces
            ' into buckets by original definition.
            Dim originalDefinitionBuckets As New MultiDictionary(Of NamedTypeSymbol, NamedTypeSymbol)
            For Each iface In interfaces
                If iface.IsGenericType Then
                    originalDefinitionBuckets.Add(iface.OriginalDefinition, iface)
                End If
            Next

            ' Compare all pairs of interfaces in each bucket.
P
pgavlin 已提交
3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761
            For Each kvp In originalDefinitionBuckets
                If kvp.Value.Count >= 2 Then
                    Dim i1 As Integer = 0
                    For Each interface1 In kvp.Value
                        Dim i2 As Integer = 0
                        For Each interface2 In kvp.Value
                            If i2 > i1 Then
                                Debug.Assert(interface2.IsGenericType AndAlso interface1.OriginalDefinition = interface2.OriginalDefinition)

                                ' Check for interface unification, then variance ambiguity
                                If TypeUnification.CanUnify(Me, interface1, interface2) Then
                                    ReportInterfaceUnificationError(diagnostics, interface1, interface2)
                                ElseIf VarianceAmbiguity.HasVarianceAmbiguity(Me, interface1, interface2, Nothing) Then
                                    ReportVarianceAmbiguityWarning(diagnostics, interface1, interface2)
                                End If
P
Pilchie 已提交
3762
                            End If
P
pgavlin 已提交
3763 3764

                            i2 += 1
P
Pilchie 已提交
3765
                        Next
P
pgavlin 已提交
3766 3767

                        i1 += 1
P
Pilchie 已提交
3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916
                    Next
                End If
            Next

        End Sub

        Private Sub ReportOverloadsErrors(comparisonResults As SymbolComparisonResults, firstMember As Symbol, secondMember As Symbol, location As Location, diagnostics As DiagnosticBag)
            If (Me.Locations.Length > 1 AndAlso Not Me.IsPartial) Then
                ' if there was an error with the enclosing class, suppress these diagnostics
            ElseIf comparisonResults = 0 Then
                diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_DuplicateProcDef1, firstMember), location)
            Else
                ' TODO: maybe rewrite these diagnostics to if/elseifs to report just one diagnostic per
                ' symbol. This would reduce the error count, but may lead to a new diagnostics once the 
                ' previous one was fixed (byref + return type).

                If (comparisonResults And SymbolComparisonResults.ParameterByrefMismatch) <> 0 Then
                    diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_OverloadWithByref2, firstMember, secondMember), location)
                End If

                If (comparisonResults And SymbolComparisonResults.ReturnTypeMismatch) <> 0 Then
                    diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_OverloadWithReturnType2, firstMember, secondMember), location)
                End If

                If (comparisonResults And SymbolComparisonResults.ParamArrayMismatch) <> 0 Then
                    diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_OverloadWithArrayVsParamArray2, firstMember, secondMember), location)
                End If

                If (comparisonResults And SymbolComparisonResults.OptionalParameterMismatch) <> 0 AndAlso (comparisonResults And SymbolComparisonResults.TotalParameterCountMismatch) = 0 Then
                    ' We have Optional/Required parameter disparity AND the same number of parameters
                    diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_OverloadWithOptional2, firstMember, secondMember), location)
                End If

                '  With changes in overloading with optional parameters this should never happen
                Debug.Assert((comparisonResults And SymbolComparisonResults.OptionalParameterTypeMismatch) = 0)
                'If (comparisonResults And SymbolComparisonResults.OptionalParameterTypeMismatch) <> 0 Then
                '   diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_OverloadWithOptionalTypes2, firstMember, secondMember), location)
                ' ...

                ' Dev10 only checks the equality of the default values if the types match in 
                ' CompareParams, so we need to suppress the diagnostic here.
                If (comparisonResults And SymbolComparisonResults.OptionalParameterValueMismatch) <> 0 Then
                    diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_OverloadWithDefault2, firstMember, secondMember), location)
                End If

                If (comparisonResults And SymbolComparisonResults.PropertyAccessorMismatch) <> 0 Then
                    diagnostics.Add(ErrorFactory.ErrorInfo(ERRID.ERR_OverloadingPropertyKind2, firstMember, secondMember), location)
                End If
            End If
        End Sub

        ''' <summary>
        ''' Interface1 and Interface2 conflict for some type arguments. Report the correct error in the correct location.
        ''' </summary>
        Private Sub ReportInterfaceUnificationError(diagnostics As DiagnosticBag, interface1 As NamedTypeSymbol, interface2 As NamedTypeSymbol)
            If GetImplementsLocation(interface1).SourceSpan.Start > GetImplementsLocation(interface2).SourceSpan.Start Then
                ' Report error on second implement, for consistency.
                Dim temp = interface1
                interface1 = interface2
                interface2 = temp
            End If

            ' The direct base interfaces that interface1/2 were inherited through.
            Dim directInterface1 As NamedTypeSymbol = Nothing
            Dim directInterface2 As NamedTypeSymbol = Nothing
            Dim location1, location2 As Location
            location1 = GetImplementsLocation(interface1, directInterface1)
            location2 = GetImplementsLocation(interface2, directInterface2)
            Dim isInterface As Boolean = Me.IsInterfaceType()
            Dim diag As DiagnosticInfo

            If (directInterface1 = interface1 AndAlso directInterface2 = interface2) Then
                diag = ErrorFactory.ErrorInfo(If(isInterface, ERRID.ERR_InterfaceUnifiesWithInterface2, ERRID.ERR_InterfacePossiblyImplTwice2),
                                              CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface2),
                                              CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface1))
            ElseIf (directInterface1 <> interface1 AndAlso directInterface2 = interface2) Then
                diag = ErrorFactory.ErrorInfo(If(isInterface, ERRID.ERR_InterfaceUnifiesWithBase3, ERRID.ERR_ClassInheritsInterfaceUnifiesWithBase3),
                                              CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface2),
                                              CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface1),
                                              CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(directInterface1))
            ElseIf (directInterface1 = interface1 AndAlso directInterface2 <> interface2) Then
                diag = ErrorFactory.ErrorInfo(If(isInterface, ERRID.ERR_BaseUnifiesWithInterfaces3, ERRID.ERR_ClassInheritsBaseUnifiesWithInterfaces3),
                                              CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(directInterface2),
                                              CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface2),
                                              CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface1))
            Else
                Debug.Assert(directInterface1 <> interface1 AndAlso directInterface2 <> interface2)
                diag = ErrorFactory.ErrorInfo(If(isInterface, ERRID.ERR_InterfaceBaseUnifiesWithBase4, ERRID.ERR_ClassInheritsInterfaceBaseUnifiesWithBase4),
                                              CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(directInterface2),
                                              CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface2),
                                              CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(interface1),
                                              CustomSymbolDisplayFormatter.ShortNameWithTypeArgsAndContainingTypes(directInterface1))
            End If

            diagnostics.Add(New VBDiagnostic(diag, location2))
        End Sub

        ''' <summary>
        ''' Interface1 and Interface2 have variable ambiguity. Report the warning in the correct location.
        ''' </summary>
        Private Sub ReportVarianceAmbiguityWarning(diagnostics As DiagnosticBag, interface1 As NamedTypeSymbol, interface2 As NamedTypeSymbol)
            Dim useSiteDiagnostics As HashSet(Of DiagnosticInfo) = Nothing
            Dim hasVarianceAmbiguity As Boolean = VarianceAmbiguity.HasVarianceAmbiguity(Me, interface1, interface2, useSiteDiagnostics)

            If hasVarianceAmbiguity OrElse Not useSiteDiagnostics.IsNullOrEmpty Then
                If GetImplementsLocation(interface1).SourceSpan.Start > GetImplementsLocation(interface2).SourceSpan.Start Then
                    ' Report error on second implement, for consistency.
                    Dim temp = interface1
                    interface1 = interface2
                    interface2 = temp
                End If

                ' The direct base interfaces that interface1/2 were inherited through.
                Dim directInterface1 As NamedTypeSymbol = Nothing
                Dim directInterface2 As NamedTypeSymbol = Nothing
                Dim location1, location2 As Location
                location1 = GetImplementsLocation(interface1, directInterface1)
                location2 = GetImplementsLocation(interface2, directInterface2)

                If Not diagnostics.Add(location2, useSiteDiagnostics) AndAlso hasVarianceAmbiguity Then
                    Dim diag As DiagnosticInfo
                    diag = ErrorFactory.ErrorInfo(ERRID.WRN_VarianceDeclarationAmbiguous3,
                                                      CustomSymbolDisplayFormatter.QualifiedName(directInterface2),
                                                      CustomSymbolDisplayFormatter.QualifiedName(directInterface1),
                                                      CustomSymbolDisplayFormatter.ErrorNameWithKind(interface1.OriginalDefinition))
                    diagnostics.Add(New VBDiagnostic(diag, location2))
                End If
            End If
        End Sub

#End Region

#Region "Attributes"
        Protected Sub SuppressExtensionAttributeSynthesis()
            Debug.Assert(m_lazyEmitExtensionAttribute <> ThreeState.True)
            m_lazyEmitExtensionAttribute = ThreeState.False
        End Sub

        Private ReadOnly Property EmitExtensionAttribute As Boolean
            Get
                If m_lazyEmitExtensionAttribute = ThreeState.Unknown Then
                    BindAllMemberAttributes(cancellationToken:=Nothing)
                End If

                Debug.Assert(m_lazyEmitExtensionAttribute <> ThreeState.Unknown)
                Return m_lazyEmitExtensionAttribute = ThreeState.True
            End Get
        End Property

3917 3918
        Friend Overrides Sub AddSynthesizedAttributes(compilationState As ModuleCompilationState, ByRef attributes As ArrayBuilder(Of SynthesizedAttributeData))
            MyBase.AddSynthesizedAttributes(compilationState, attributes)
P
Pilchie 已提交
3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937

            If EmitExtensionAttribute Then
                AddSynthesizedAttribute(attributes, Me.DeclaringCompilation.SynthesizeExtensionAttribute())
            End If
        End Sub
#End Region

        Friend ReadOnly Property AnyMemberHasAttributes As Boolean
            Get
                If (Not Me.m_lazyAnyMemberHasAttributes.HasValue()) Then
                    Me.m_lazyAnyMemberHasAttributes = Me.m_declaration.AnyMemberHasAttributes.ToThreeState()
                End If

                Return Me.m_lazyAnyMemberHasAttributes.Value()
            End Get
        End Property
    End Class
End Namespace