File: GenerateType\VisualBasicGenerateTypeService.vb
Web Access
Project: src\src\Features\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.Features.vbproj (Microsoft.CodeAnalysis.VisualBasic.Features)
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
 
Imports System.Collections.Immutable
Imports System.Composition
Imports System.Threading
Imports Microsoft.CodeAnalysis.AddImport
Imports Microsoft.CodeAnalysis.CodeGeneration
Imports Microsoft.CodeAnalysis.Editing
Imports Microsoft.CodeAnalysis.Formatting
Imports Microsoft.CodeAnalysis.GenerateType
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.LanguageService
Imports Microsoft.CodeAnalysis.Simplification
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.Utilities
Imports Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic.GenerateType
    <ExportLanguageService(GetType(IGenerateTypeService), LanguageNames.VisualBasic), [Shared]>
    Partial Friend NotInheritable Class VisualBasicGenerateTypeService
        Inherits AbstractGenerateTypeService(Of VisualBasicGenerateTypeService, SimpleNameSyntax, ObjectCreationExpressionSyntax, ExpressionSyntax, TypeBlockSyntax, ArgumentSyntax)
 
        <ImportingConstructor>
        <Obsolete(MefConstruction.ImportingConstructorMessage, True)>
        Public Sub New()
        End Sub
 
        Protected Overrides ReadOnly Property DefaultFileExtension As String = ".vb"
 
        Protected Overrides Function GenerateParameterNames(semanticModel As SemanticModel, arguments As IList(Of ArgumentSyntax), cancellationToken As CancellationToken) As IList(Of ParameterName)
            Return semanticModel.GenerateParameterNames(arguments, reservedNames:=Nothing, cancellationToken:=cancellationToken)
        End Function
 
        Protected Overrides Function GetLeftSideOfDot(simpleName As SimpleNameSyntax) As ExpressionSyntax
            Return simpleName.GetLeftSideOfDot()
        End Function
 
        Protected Overrides Function IsArrayElementType(expression As ExpressionSyntax) As Boolean
            Return expression.IsParentKind(SyntaxKind.ArrayCreationExpression)
        End Function
 
        Protected Overrides Function IsInCatchDeclaration(expression As ExpressionSyntax) As Boolean
            Return False
        End Function
 
        Protected Overrides Function IsInInterfaceList(expression As ExpressionSyntax) As Boolean
            If TypeOf expression Is TypeSyntax AndAlso
                      expression.IsParentKind(SyntaxKind.ImplementsStatement) Then
                Return True
            End If
 
            If TypeOf expression Is TypeSyntax AndAlso
               expression.IsParentKind(SyntaxKind.TypeConstraint) AndAlso
               expression.Parent.IsParentKind(SyntaxKind.TypeParameterMultipleConstraintClause) Then
                ' TODO: Code Coverage
                Dim typeConstraint = DirectCast(expression.Parent, TypeConstraintSyntax)
                Dim constraintClause = DirectCast(typeConstraint.Parent, TypeParameterMultipleConstraintClauseSyntax)
                Dim index = constraintClause.Constraints.IndexOf(typeConstraint)
                Return index > 0
            End If
 
            Return False
        End Function
 
        Protected Overrides Function IsInValueTypeConstraintContext(semanticModel As SemanticModel, expression As Microsoft.CodeAnalysis.VisualBasic.Syntax.ExpressionSyntax, cancellationToken As System.Threading.CancellationToken) As Boolean
            ' TODO(cyrusn) implement this
            Return False
        End Function
 
        Protected Overrides Function TryGetArgumentList(
                objectCreationExpression As ObjectCreationExpressionSyntax,
                ByRef argumentList As IList(Of ArgumentSyntax)) As Boolean
            If objectCreationExpression IsNot Nothing AndAlso
               objectCreationExpression.ArgumentList IsNot Nothing Then
                argumentList = objectCreationExpression.ArgumentList.Arguments.ToList()
                Return True
            End If
 
            Return False
        End Function
 
        Protected Overrides Function TryGetNameParts(expression As ExpressionSyntax,
                                                     ByRef nameParts As IList(Of String)) As Boolean
            Return expression.TryGetNameParts(nameParts)
        End Function
 
        Protected Overrides Function TryInitializeState(
                document As SemanticDocument, simpleName As SimpleNameSyntax, cancellationToken As CancellationToken, ByRef generateTypeServiceStateOptions As GenerateTypeServiceStateOptions) As Boolean
            generateTypeServiceStateOptions = New GenerateTypeServiceStateOptions()
 
            If simpleName.IsParentKind(SyntaxKind.DictionaryAccessExpression) Then
                Return False
            End If
 
            Dim nameOrMemberAccessExpression As ExpressionSyntax = Nothing
            If simpleName.IsRightSideOfDot() Then
                nameOrMemberAccessExpression = DirectCast(simpleName.Parent, ExpressionSyntax)
                If Not (TypeOf simpleName.GetLeftSideOfDot() Is NameSyntax) Then
                    Return False
                End If
            Else
                nameOrMemberAccessExpression = simpleName
            End If
 
            generateTypeServiceStateOptions.NameOrMemberAccessExpression = nameOrMemberAccessExpression
 
            If TypeOf nameOrMemberAccessExpression.Parent Is BinaryExpressionSyntax Then
                Return False
            End If
 
            Dim syntaxTree = document.SyntaxTree
            Dim semanticModel = document.SemanticModel
            If Not SyntaxFacts.IsInNamespaceOrTypeContext(nameOrMemberAccessExpression) Then
                generateTypeServiceStateOptions.IsDelegateAllowed = False
 
                Dim position = nameOrMemberAccessExpression.SpanStart
                Dim isExpressionContext = syntaxTree.IsExpressionContext(position, cancellationToken)
                Dim isStatementContext = syntaxTree.IsSingleLineStatementContext(position, cancellationToken)
                Dim isExpressionOrStatementContext = isExpressionContext OrElse isStatementContext
 
                If isExpressionOrStatementContext Then
                    If Not simpleName.IsLeftSideOfDot() Then
 
                        If nameOrMemberAccessExpression Is Nothing OrElse Not nameOrMemberAccessExpression.IsKind(SyntaxKind.SimpleMemberAccessExpression) Then
                            Return False
                        End If
 
                        Dim leftSymbol = semanticModel.GetSymbolInfo(DirectCast(nameOrMemberAccessExpression, MemberAccessExpressionSyntax).Expression).Symbol
                        Dim token = simpleName.GetLastToken().GetNextToken()
                        If leftSymbol Is Nothing OrElse
                            Not leftSymbol.IsKind(SymbolKind.Namespace) OrElse
                            Not token.IsKind(SyntaxKind.DotToken) Then
                            Return False
                        Else
                            generateTypeServiceStateOptions.IsMembersWithModule = True
                            generateTypeServiceStateOptions.IsTypeGeneratedIntoNamespaceFromMemberAccess = True
                        End If
                    End If
 
                    If Not generateTypeServiceStateOptions.IsTypeGeneratedIntoNamespaceFromMemberAccess AndAlso
                        Not SyntaxFacts.IsInNamespaceOrTypeContext(simpleName) Then
                        Dim token = simpleName.GetLastToken().GetNextToken()
                        If token.IsKind(SyntaxKind.DotToken) AndAlso
                            simpleName.Parent Is token.Parent Then
                            generateTypeServiceStateOptions.IsMembersWithModule = True
                            generateTypeServiceStateOptions.IsTypeGeneratedIntoNamespaceFromMemberAccess = True
                        End If
                    End If
                End If
            End If
 
            If nameOrMemberAccessExpression.Parent.IsKind(SyntaxKind.InvocationExpression) Then
                Return False
            End If
 
            ' Check if module could be an option
            Dim nextToken = simpleName.GetLastToken().GetNextToken()
            If simpleName.IsLeftSideOfDot() OrElse nextToken.IsKind(SyntaxKind.DotToken) Then
                If simpleName.IsRightSideOfDot() Then
                    Dim parent = TryCast(simpleName.Parent, QualifiedNameSyntax)
 
                    If parent IsNot Nothing Then
                        Dim leftSymbol = semanticModel.GetSymbolInfo(parent.Left).Symbol
 
                        If leftSymbol IsNot Nothing And leftSymbol.IsKind(SymbolKind.Namespace) Then
                            generateTypeServiceStateOptions.IsMembersWithModule = True
                        End If
                    End If
                End If
            End If
 
            If SyntaxFacts.IsInNamespaceOrTypeContext(nameOrMemberAccessExpression) Then
 
                ' In Namespace or Type Context we cannot have Interface, Enum, Delegate as part of the Left Expression of a QualifiedName
                If nextToken.IsKind(SyntaxKind.DotToken) Then
                    generateTypeServiceStateOptions.IsInterfaceOrEnumNotAllowedInTypeContext = True
                    generateTypeServiceStateOptions.IsDelegateAllowed = False
                    generateTypeServiceStateOptions.IsMembersWithModule = True
                End If
 
                ' Case : Class Goo(of T as MyType)
                If nameOrMemberAccessExpression.GetAncestors(Of TypeConstraintSyntax).Any() Then
                    generateTypeServiceStateOptions.IsClassInterfaceTypes = True
                    Return True
                End If
 
                ' Case : Custom Event E As Goo
                ' Case : Public Event F As Goo
                If nameOrMemberAccessExpression.GetAncestors(Of EventStatementSyntax)().Any() Then
                    ' Case : Goo
                    ' Only Delegate
                    If simpleName.Parent IsNot Nothing AndAlso TypeOf simpleName.Parent IsNot QualifiedNameSyntax Then
                        generateTypeServiceStateOptions.IsDelegateOnly = True
                        Return True
                    End If
 
                    ' Case : Something.Goo ...
                    If TypeOf nameOrMemberAccessExpression Is QualifiedNameSyntax Then
 
                        ' Case : NSOrSomething.GenType.Goo
                        If nextToken.IsKind(SyntaxKind.DotToken) Then
                            If nameOrMemberAccessExpression.Parent IsNot Nothing AndAlso TypeOf nameOrMemberAccessExpression.Parent Is QualifiedNameSyntax Then
                                Return True
                            Else
                                Throw ExceptionUtilities.Unreachable
                            End If
                        Else
                            ' Case : NSOrSomething.GenType
                            generateTypeServiceStateOptions.IsDelegateOnly = True
                            Return True
                        End If
                    End If
                End If
 
                ' Case : Public WithEvents G As Delegate1
                Dim fieldDecl = nameOrMemberAccessExpression.GetAncestor(Of FieldDeclarationSyntax)()
                If fieldDecl IsNot Nothing AndAlso fieldDecl.GetModifiers().Any(Function(n) n.Kind() = SyntaxKind.WithEventsKeyword) Then
                    generateTypeServiceStateOptions.IsClassInterfaceTypes = True
                    Return True
                End If
 
                ' No Enum Type Generation in AddHandler or RemoverHandler Statement
                If nameOrMemberAccessExpression.GetAncestors(Of AccessorStatementSyntax)().Any() Then
                    If Not nextToken.IsKind(SyntaxKind.DotToken) AndAlso
                    nameOrMemberAccessExpression.IsParentKind(SyntaxKind.SimpleAsClause) AndAlso
                    nameOrMemberAccessExpression.Parent.IsParentKind(SyntaxKind.Parameter) AndAlso
                    nameOrMemberAccessExpression.Parent.Parent.IsParentKind(SyntaxKind.ParameterList) AndAlso
                    (nameOrMemberAccessExpression.Parent.Parent.Parent.IsParentKind(SyntaxKind.AddHandlerAccessorStatement) OrElse
                    nameOrMemberAccessExpression.Parent.Parent.Parent.IsParentKind(SyntaxKind.RemoveHandlerAccessorStatement)) Then
                        generateTypeServiceStateOptions.IsDelegateOnly = True
                        Return True
                    End If
 
                    generateTypeServiceStateOptions.IsEnumNotAllowed = True
                End If
            Else
                ' MemberAccessExpression
                If nameOrMemberAccessExpression.GetAncestors(Of UnaryExpressionSyntax)().Any(Function(n) n.IsKind(SyntaxKind.AddressOfExpression)) Then
                    generateTypeServiceStateOptions.IsEnumNotAllowed = True
                End If
 
                ' Check to see if the expression is part of Invocation Expression
                If (nameOrMemberAccessExpression.IsKind(SyntaxKind.SimpleMemberAccessExpression) OrElse (nameOrMemberAccessExpression.Parent IsNot Nothing AndAlso nameOrMemberAccessExpression.IsParentKind(SyntaxKind.SimpleMemberAccessExpression))) _
                    AndAlso nameOrMemberAccessExpression.IsLeftSideOfDot() Then
                    Dim outerMostMemberAccessExpression As ExpressionSyntax = Nothing
                    If nameOrMemberAccessExpression.IsKind(SyntaxKind.SimpleMemberAccessExpression) Then
                        outerMostMemberAccessExpression = nameOrMemberAccessExpression
                    Else
                        Debug.Assert(nameOrMemberAccessExpression.IsParentKind(SyntaxKind.SimpleMemberAccessExpression))
                        outerMostMemberAccessExpression = DirectCast(nameOrMemberAccessExpression.Parent, ExpressionSyntax)
                    End If
 
                    outerMostMemberAccessExpression = outerMostMemberAccessExpression.GetAncestorsOrThis(Of ExpressionSyntax)().SkipWhile(Function(n) n IsNot Nothing AndAlso n.IsKind(SyntaxKind.SimpleMemberAccessExpression)).FirstOrDefault()
                    If outerMostMemberAccessExpression IsNot Nothing AndAlso TypeOf outerMostMemberAccessExpression Is InvocationExpressionSyntax Then
                        generateTypeServiceStateOptions.IsEnumNotAllowed = True
                    End If
                End If
            End If
 
            ' New MyDelegate(AddressOf goo)
            ' New NS.MyDelegate(Function(n) n)
            If TypeOf nameOrMemberAccessExpression.Parent Is ObjectCreationExpressionSyntax Then
                Dim objectCreationExpressionOpt = DirectCast(nameOrMemberAccessExpression.Parent, ObjectCreationExpressionSyntax)
                generateTypeServiceStateOptions.ObjectCreationExpressionOpt = objectCreationExpressionOpt
 
                ' Interface and Enum not allowed
                generateTypeServiceStateOptions.IsInterfaceOrEnumNotAllowedInTypeContext = True
 
                If objectCreationExpressionOpt.ArgumentList IsNot Nothing Then
                    If objectCreationExpressionOpt.ArgumentList.CloseParenToken.IsMissing Then
                        Return False
                    End If
 
                    ' Get the Method Symbol for Delegate to be created
                    ' Currently simple argument is the only argument that can be fed to the Object Creation for Delegate Creation
                    If generateTypeServiceStateOptions.IsDelegateAllowed AndAlso
                        objectCreationExpressionOpt.ArgumentList.Arguments.Count = 1 AndAlso
                        TypeOf objectCreationExpressionOpt.ArgumentList.Arguments(0) Is SimpleArgumentSyntax Then
                        Dim simpleArgumentExpression = DirectCast(objectCreationExpressionOpt.ArgumentList.Arguments(0), SimpleArgumentSyntax).Expression
 
                        If simpleArgumentExpression.IsKind(SyntaxKind.AddressOfExpression) Then
                            generateTypeServiceStateOptions.DelegateCreationMethodSymbol = GetMemberGroupIfPresent(semanticModel, DirectCast(simpleArgumentExpression, UnaryExpressionSyntax).Operand, cancellationToken)
                        ElseIf (simpleArgumentExpression.IsKind(SyntaxKind.MultiLineFunctionLambdaExpression) OrElse
                                simpleArgumentExpression.IsKind(SyntaxKind.SingleLineFunctionLambdaExpression) OrElse
                                simpleArgumentExpression.IsKind(SyntaxKind.MultiLineSubLambdaExpression) OrElse
                                simpleArgumentExpression.IsKind(SyntaxKind.SingleLineSubLambdaExpression)) Then
                            generateTypeServiceStateOptions.DelegateCreationMethodSymbol = TryCast(semanticModel.GetSymbolInfo(simpleArgumentExpression, cancellationToken).Symbol, IMethodSymbol)
                        End If
                    ElseIf objectCreationExpressionOpt.ArgumentList.Arguments.Count <> 1 Then
                        generateTypeServiceStateOptions.IsDelegateAllowed = False
                    End If
                End If
 
                Dim initializers = TryCast(objectCreationExpressionOpt.Initializer, ObjectMemberInitializerSyntax)
                If initializers IsNot Nothing Then
                    For Each initializer In initializers.Initializers.ToList()
                        Dim namedFieldInitializer = TryCast(initializer, NamedFieldInitializerSyntax)
                        If namedFieldInitializer IsNot Nothing Then
                            generateTypeServiceStateOptions.PropertiesToGenerate.Add(namedFieldInitializer.Name)
                        End If
                    Next
                End If
            End If
 
            Dim variableDeclarator As VariableDeclaratorSyntax = Nothing
            If generateTypeServiceStateOptions.IsDelegateAllowed Then
 
                ' Dim f As MyDel = ...
                ' Dim f as NS.MyDel = ...
                If nameOrMemberAccessExpression.IsParentKind(SyntaxKind.SimpleAsClause) AndAlso
                   nameOrMemberAccessExpression.Parent.IsParentKind(SyntaxKind.VariableDeclarator) Then
                    variableDeclarator = DirectCast(nameOrMemberAccessExpression.Parent.Parent, VariableDeclaratorSyntax)
 
                    If variableDeclarator.Initializer IsNot Nothing AndAlso variableDeclarator.Initializer.Value IsNot Nothing Then
                        Dim expression = variableDeclarator.Initializer.Value
                        If expression.IsKind(SyntaxKind.AddressOfExpression) Then
                            ' ... = AddressOf Goo
                            generateTypeServiceStateOptions.DelegateCreationMethodSymbol = GetMemberGroupIfPresent(semanticModel, DirectCast(expression, UnaryExpressionSyntax).Operand, cancellationToken)
                        Else
                            If TypeOf expression Is LambdaExpressionSyntax Then
                                '... = Lambda
                                Dim type = semanticModel.GetTypeInfo(expression, cancellationToken).Type
                                If type IsNot Nothing AndAlso type.IsDelegateType() Then
                                    generateTypeServiceStateOptions.DelegateCreationMethodSymbol = DirectCast(type, INamedTypeSymbol).DelegateInvokeMethod
                                End If
 
                                Dim symbol = semanticModel.GetSymbolInfo(expression, cancellationToken).Symbol
                                If symbol IsNot Nothing AndAlso symbol.IsKind(SymbolKind.Method) Then
                                    generateTypeServiceStateOptions.DelegateCreationMethodSymbol = DirectCast(symbol, IMethodSymbol)
                                End If
                            End If
                        End If
                    End If
                ElseIf TypeOf nameOrMemberAccessExpression.Parent Is CastExpressionSyntax Then
                    ' Case: Dim s1 = DirectCast(AddressOf goo, Myy)
                    '       Dim s2 = TryCast(AddressOf goo, Myy)
                    '       Dim s3 = CType(AddressOf goo, Myy)
                    Dim expressionToBeCasted = DirectCast(nameOrMemberAccessExpression.Parent, CastExpressionSyntax).Expression
                    If expressionToBeCasted.IsKind(SyntaxKind.AddressOfExpression) Then
                        ' ... = AddressOf Goo
                        generateTypeServiceStateOptions.DelegateCreationMethodSymbol = GetMemberGroupIfPresent(semanticModel, DirectCast(expressionToBeCasted, UnaryExpressionSyntax).Operand, cancellationToken)
                    End If
                End If
            End If
 
            Return True
        End Function
 
        Private Shared Function GetMemberGroupIfPresent(semanticModel As SemanticModel, expression As ExpressionSyntax, cancellationToken As CancellationToken) As IMethodSymbol
            If expression Is Nothing Then
                Return Nothing
            End If
 
            Dim memberGroup = semanticModel.GetMemberGroup(expression, cancellationToken)
            If memberGroup.Length <> 0 Then
                Return If(memberGroup.ElementAt(0).IsKind(SymbolKind.Method), DirectCast(memberGroup.ElementAt(0), IMethodSymbol), Nothing)
            End If
 
            Return Nothing
        End Function
 
        Public Overrides Function GetRootNamespace(options As CompilationOptions) As String
            Return DirectCast(options, VisualBasicCompilationOptions).RootNamespace
        End Function
 
        Protected Overloads Overrides Function GetTypeParameters(state As State,
                                                                 semanticModel As SemanticModel,
                                                                 cancellationToken As CancellationToken) As ImmutableArray(Of ITypeParameterSymbol)
            If TypeOf state.SimpleName Is GenericNameSyntax Then
                Dim genericName = DirectCast(state.SimpleName, GenericNameSyntax)
                Dim typeArguments = If(state.SimpleName.Arity = genericName.TypeArgumentList.Arguments.Count,
                    genericName.TypeArgumentList.Arguments.OfType(Of SyntaxNode)().ToList(),
                    Enumerable.Repeat(Of SyntaxNode)(Nothing, state.SimpleName.Arity))
                Return GetTypeParameters(state, semanticModel, typeArguments, cancellationToken)
            End If
 
            Return ImmutableArray(Of ITypeParameterSymbol).Empty
        End Function
 
        Protected Overrides Function IsInVariableTypeContext(expression As Microsoft.CodeAnalysis.VisualBasic.Syntax.ExpressionSyntax) As Boolean
            Return expression.IsParentKind(SyntaxKind.SimpleAsClause)
        End Function
 
        Protected Overrides Function DetermineTypeToGenerateIn(semanticModel As SemanticModel,
                                                               simpleName As SimpleNameSyntax,
                                                               cancellationToken As CancellationToken) As INamedTypeSymbol
            Dim typeBlock = simpleName.GetAncestorsOrThis(Of TypeBlockSyntax).
                                          Where(Function(t) t.Members.Count > 0).
                                          FirstOrDefault(Function(t) simpleName.SpanStart >= t.Members.First().SpanStart AndAlso
                                                             simpleName.Span.End <= t.Members.Last().Span.End)
            Return If(typeBlock Is Nothing, Nothing, TryCast(semanticModel.GetDeclaredSymbol(typeBlock.BlockStatement, cancellationToken), INamedTypeSymbol))
        End Function
 
        Protected Overrides Function GetAccessibility(state As State, semanticModel As SemanticModel, intoNamespace As Boolean, cancellationToken As CancellationToken) As Accessibility
            Dim accessibility = DetermineDefaultAccessibility(state, semanticModel, intoNamespace, cancellationToken)
            If Not state.IsTypeGeneratedIntoNamespaceFromMemberAccess Then
                Dim accessibilityConstraint = semanticModel.DetermineAccessibilityConstraint(
                    TryCast(state.NameOrMemberAccessExpression, TypeSyntax), cancellationToken)
 
                If accessibilityConstraint = Accessibility.Public OrElse
                   accessibilityConstraint = Accessibility.Internal Then
                    accessibility = accessibilityConstraint
                End If
            End If
 
            Return accessibility
        End Function
 
        Protected Overrides Function DetermineArgumentType(semanticModel As SemanticModel, argument As ArgumentSyntax, cancellationToken As CancellationToken) As ITypeSymbol
            Return argument.DetermineType(semanticModel, cancellationToken)
        End Function
 
        Protected Overrides Function IsConversionImplicit(compilation As Compilation, sourceType As ITypeSymbol, targetType As ITypeSymbol) As Boolean
            Return compilation.ClassifyConversion(sourceType, targetType).IsWidening
        End Function
 
        Public Overrides Async Function GetOrGenerateEnclosingNamespaceSymbolAsync(namedTypeSymbol As INamedTypeSymbol, containers() As String, selectedDocument As Document, selectedDocumentRoot As SyntaxNode, cancellationToken As CancellationToken) As Task(Of (INamespaceSymbol, INamespaceOrTypeSymbol, Location))
            Dim compilationUnit = DirectCast(selectedDocumentRoot, CompilationUnitSyntax)
            Dim semanticModel = Await selectedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(False)
 
            If containers.Length <> 0 Then
                ' Search the NS declaration in the root
                Dim containerList = New List(Of String)(containers)
                Dim enclosingNamespace = GetDeclaringNamespace(containerList, 0, compilationUnit)
                If enclosingNamespace IsNot Nothing Then
                    Dim enclosingNamespaceSymbol = semanticModel.GetSymbolInfo(enclosingNamespace.Name, cancellationToken)
                    If enclosingNamespaceSymbol.Symbol IsNot Nothing Then
                        Return (DirectCast(enclosingNamespaceSymbol.Symbol, INamespaceSymbol),
                                namedTypeSymbol,
                                DirectCast(enclosingNamespace.Parent, NamespaceBlockSyntax).EndNamespaceStatement.GetLocation())
                        Return Nothing
                    End If
                End If
            End If
 
            Dim globalNamespace = semanticModel.GetEnclosingNamespace(0, cancellationToken)
            Dim rootNamespaceOrType = namedTypeSymbol.GenerateRootNamespaceOrType(containers)
            Dim lastMember = compilationUnit.Members.LastOrDefault()
 
            ' Add at the end
            Dim afterThisLocation = If(lastMember Is Nothing,
                semanticModel.SyntaxTree.GetLocation(New TextSpan()),
                semanticModel.SyntaxTree.GetLocation(New TextSpan(lastMember.Span.End, 0)))
 
            Return (globalNamespace, rootNamespaceOrType, afterThisLocation)
        End Function
 
        Private Shared Function GetDeclaringNamespace(containers As List(Of String), indexDone As Integer, compilationUnit As CompilationUnitSyntax) As NamespaceStatementSyntax
            For Each member In compilationUnit.Members
                Dim namespaceDeclaration = GetDeclaringNamespace(containers, indexDone, member)
                If namespaceDeclaration IsNot Nothing Then
                    Return namespaceDeclaration
                End If
            Next
 
            Return Nothing
        End Function
 
        Private Shared Function GetDeclaringNamespace(containers As List(Of String), indexDone As Integer, localRoot As SyntaxNode) As NamespaceStatementSyntax
            Dim namespaceBlock = TryCast(localRoot, NamespaceBlockSyntax)
            If namespaceBlock IsNot Nothing Then
                Dim matchingNamesCount = MatchingNamesFromNamespaceName(containers, indexDone, namespaceBlock.NamespaceStatement)
 
                If matchingNamesCount = -1 Then
                    Return Nothing
                End If
 
                If containers.Count = indexDone + matchingNamesCount Then
                    Return namespaceBlock.NamespaceStatement
                Else
                    indexDone += matchingNamesCount
                End If
 
                For Each member In namespaceBlock.Members
                    Dim resultantNamespace = GetDeclaringNamespace(containers, indexDone, member)
                    If resultantNamespace IsNot Nothing Then
                        Return resultantNamespace
                    End If
                Next
 
                Return Nothing
            End If
 
            Return Nothing
 
        End Function
 
        Private Shared Function MatchingNamesFromNamespaceName(containers As List(Of String), indexDone As Integer, namespaceStatementSyntax As NamespaceStatementSyntax) As Integer
 
            If namespaceStatementSyntax Is Nothing Then
                Return -1
            End If
 
            Dim namespaceContainers = New List(Of String)()
            GetNamespaceContainers(namespaceStatementSyntax.Name, namespaceContainers)
 
            If namespaceContainers.Count + indexDone > containers.Count OrElse
                Not IdentifierMatches(indexDone, namespaceContainers, containers) Then
                Return -1
            End If
 
            Return namespaceContainers.Count
        End Function
 
        Private Shared Function IdentifierMatches(indexDone As Integer, namespaceContainers As List(Of String), containers As List(Of String)) As Boolean
            For index = 0 To namespaceContainers.Count - 1
                If Not namespaceContainers(index).Equals(containers(indexDone + index), StringComparison.OrdinalIgnoreCase) Then
                    Return False
                End If
            Next
 
            Return True
        End Function
 
        Private Shared Sub GetNamespaceContainers(name As NameSyntax, namespaceContainers As List(Of String))
            If TypeOf name Is QualifiedNameSyntax Then
                GetNamespaceContainers(DirectCast(name, QualifiedNameSyntax).Left, namespaceContainers)
                namespaceContainers.Add(DirectCast(name, QualifiedNameSyntax).Right.Identifier.ValueText)
            ElseIf TypeOf name Is SimpleNameSyntax Then
                namespaceContainers.Add(DirectCast(name, SimpleNameSyntax).Identifier.ValueText)
            Else
                Debug.Assert(TypeOf name Is GlobalNameSyntax)
                namespaceContainers.Add(DirectCast(name, GlobalNameSyntax).GlobalKeyword.ValueText)
            End If
        End Sub
 
        Friend Overrides Function TryGetBaseList(expression As ExpressionSyntax, ByRef typeKindValue As TypeKindOptions) As Boolean
            typeKindValue = TypeKindOptions.AllOptions
            If expression Is Nothing Then
                Return False
            End If
 
            If TypeOf expression.Parent Is InheritsStatementSyntax Then
                If expression.Parent.Parent IsNot Nothing AndAlso TypeOf expression.Parent.Parent Is InterfaceBlockSyntax Then
                    typeKindValue = TypeKindOptions.Interface
                    Return True
                End If
 
                typeKindValue = TypeKindOptions.Class
                Return True
            ElseIf TypeOf expression.Parent Is ImplementsStatementSyntax Then
                typeKindValue = TypeKindOptions.Interface
                Return True
            End If
 
            Return False
        End Function
 
        Friend Overrides Function IsPublicOnlyAccessibility(expression As ExpressionSyntax, project As Project) As Boolean
            If expression Is Nothing Then
                Return False
            End If
 
            Dim node As SyntaxNode = expression
            While node IsNot Nothing
                ' Types in BaseList, Type Constraint or Member Types cannot be of more restricted accessibility than the declaring type
                If TypeOf node Is InheritsOrImplementsStatementSyntax AndAlso
                    node.Parent IsNot Nothing AndAlso
                    TypeOf node.Parent Is TypeBlockSyntax Then
 
                    Return IsAllContainingTypeBlocksPublic(node.Parent)
                End If
 
                If TypeOf node Is TypeParameterListSyntax AndAlso
                        node.Parent IsNot Nothing AndAlso TypeOf node.Parent Is TypeStatementSyntax AndAlso
                        node.Parent.Parent IsNot Nothing AndAlso TypeOf node.Parent.Parent Is TypeBlockSyntax Then
                    Return IsAllContainingTypeBlocksPublic(node.Parent.Parent)
                End If
 
                If TypeOf node Is EventStatementSyntax AndAlso
                        node.Parent IsNot Nothing AndAlso TypeOf node.Parent Is TypeBlockSyntax Then
                    Return IsAllContainingTypeBlocksPublic(node)
                End If
 
                If TypeOf node Is FieldDeclarationSyntax AndAlso
                    node.Parent IsNot Nothing AndAlso TypeOf node.Parent Is TypeBlockSyntax Then
                    Return IsAllContainingTypeBlocksPublic(node)
                End If
 
                node = node.Parent
            End While
 
            Return False
        End Function
 
        Private Shared Function IsAllContainingTypeBlocksPublic(node As SyntaxNode) As Boolean
            ' Make sure all the Ancestoral Type Blocks are Declared with Public Access Modifiers
            Dim containingTypeBlocks = node.GetAncestorsOrThis(Of TypeBlockSyntax)()
            If containingTypeBlocks.Count() = 0 Then
                Return True
            Else
                Return containingTypeBlocks.All(Function(typeBlock) typeBlock.GetModifiers().Any(Function(n) n.Kind() = SyntaxKind.PublicKeyword))
            End If
        End Function
 
        Friend Overrides Function IsGenericName(expression As SimpleNameSyntax) As Boolean
            If expression Is Nothing Then
                Return False
            End If
 
            Dim node = TryCast(expression, GenericNameSyntax)
            Return node IsNot Nothing
        End Function
 
        Friend Overrides Function IsSimpleName(expression As ExpressionSyntax) As Boolean
            Return TypeOf expression Is SimpleNameSyntax
        End Function
 
        Friend Overrides Async Function TryAddUsingsOrImportToDocumentAsync(
                updatedSolution As Solution,
                modifiedRoot As SyntaxNode,
                document As Document,
                simpleName As SimpleNameSyntax,
                includeUsingsOrImports As String,
                cancellationToken As CancellationToken) As Task(Of Solution)
 
            ' Nothing to include
            If String.IsNullOrWhiteSpace(includeUsingsOrImports) Then
                Return updatedSolution
            End If
 
            Dim root As SyntaxNode = Nothing
            If modifiedRoot Is Nothing Then
                root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False)
            Else
                root = modifiedRoot
            End If
 
            If TypeOf root Is CompilationUnitSyntax Then
                Dim compilationRoot = DirectCast(root, CompilationUnitSyntax)
                Dim memberImportsClause = SyntaxFactory.SimpleImportsClause(
                    name:=SyntaxFactory.ParseName(includeUsingsOrImports))
                Dim lastToken = memberImportsClause.GetLastToken()
                Dim lastTokenWithEndOfLineTrivia = lastToken.WithTrailingTrivia(SyntaxFactory.ElasticCarriageReturnLineFeed)
 
                ' Replace the token the line carriage
                memberImportsClause = memberImportsClause.ReplaceToken(lastToken, lastTokenWithEndOfLineTrivia)
 
                Dim newImport = SyntaxFactory.ImportsStatement(
                    importsClauses:=SyntaxFactory.SingletonSeparatedList(Of ImportsClauseSyntax)(memberImportsClause))
 
                ' Check if the imports is already present
                Dim importsClauses = compilationRoot.Imports.Select(Function(n) n.ImportsClauses)
                For Each importClause In importsClauses
                    For Each import In importClause
                        If TypeOf import Is SimpleImportsClauseSyntax Then
                            Dim membersImport = DirectCast(import, SimpleImportsClauseSyntax)
                            If membersImport.Name IsNot Nothing AndAlso membersImport.Name.ToString().Equals(memberImportsClause.Name.ToString()) Then
                                Return updatedSolution
                            End If
                        End If
                    Next
                Next
 
                ' Check if the GFU is triggered from the namespace same as the imports namespace
                If Await IsWithinTheImportingNamespaceAsync(document, simpleName.SpanStart, includeUsingsOrImports, cancellationToken).ConfigureAwait(False) Then
                    Return updatedSolution
                End If
 
                Dim addImportOptions = Await document.GetAddImportPlacementOptionsAsync(cancellationToken).ConfigureAwait(False)
                Dim addedCompilationRoot = compilationRoot.AddImportsStatement(newImport, addImportOptions.PlaceSystemNamespaceFirst, Formatter.Annotation, Simplifier.Annotation)
                updatedSolution = updatedSolution.WithDocumentSyntaxRoot(document.Id, addedCompilationRoot, PreservationMode.PreserveIdentity)
            End If
 
            Return updatedSolution
        End Function
 
        Private Shared Function GetPropertyType(propIdentifierName As SimpleNameSyntax,
                                         semanticModel As SemanticModel,
                                         typeInference As ITypeInferenceService,
                                         cancellationToken As CancellationToken) As ITypeSymbol
 
            Dim fieldInitializer = TryCast(propIdentifierName.Parent, NamedFieldInitializerSyntax)
            If fieldInitializer IsNot Nothing Then
                Return typeInference.InferType(semanticModel, fieldInitializer.Name, True, cancellationToken)
            End If
 
            Return Nothing
        End Function
 
        Private Shared Function GenerateProperty(propertyName As SimpleNameSyntax, typeSymbol As ITypeSymbol) As IPropertySymbol
            Return CodeGenerationSymbolFactory.CreatePropertySymbol(
                attributes:=ImmutableArray(Of AttributeData).Empty,
                accessibility:=Accessibility.Public,
                modifiers:=New DeclarationModifiers(),
                explicitInterfaceImplementations:=Nothing,
                name:=propertyName.ToString,
                type:=typeSymbol,
                refKind:=RefKind.None,
                parameters:=Nothing,
                getMethod:=Nothing,
                setMethod:=Nothing,
                isIndexer:=False)
        End Function
 
        Friend Overrides Function TryGenerateProperty(propertyName As SimpleNameSyntax,
                                                      semanticModel As SemanticModel,
                                                      typeInferenceService As ITypeInferenceService,
                                                      cancellationToken As CancellationToken,
                                                      ByRef propertySymbol As IPropertySymbol) As Boolean
            Dim typeSymbol = GetPropertyType(propertyName, semanticModel, typeInferenceService, cancellationToken)
            If typeSymbol Is Nothing OrElse TypeOf typeSymbol Is IErrorTypeSymbol Then
                propertySymbol = GenerateProperty(propertyName, semanticModel.Compilation.ObjectType)
                Return propertySymbol IsNot Nothing
            End If
 
            propertySymbol = GenerateProperty(propertyName, typeSymbol)
            Return propertySymbol IsNot Nothing
        End Function
    End Class
End Namespace