File: Simplification\Reducers\VisualBasicVariableDeclaratorReducer.vb
Web Access
Project: src\src\Workspaces\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.Workspaces.vbproj (Microsoft.CodeAnalysis.VisualBasic.Workspaces)
' 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.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.CodeAnalysis.Formatting
Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Simplification
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
 
Namespace Microsoft.CodeAnalysis.VisualBasic.Simplification
    Partial Friend Class VisualBasicVariableDeclaratorReducer
        Inherits AbstractVisualBasicReducer
 
        Private Shared ReadOnly s_pool As ObjectPool(Of IReductionRewriter) =
            New ObjectPool(Of IReductionRewriter)(Function() New Rewriter(s_pool))
 
        Private Shared ReadOnly s_simplifyVariableDeclarator As Func(Of VariableDeclaratorSyntax, SemanticModel, VisualBasicSimplifierOptions, CancellationToken, SyntaxNode) = AddressOf SimplifyVariableDeclarator
 
        Public Sub New()
            MyBase.New(s_pool)
        End Sub
 
        Public Overrides Function IsApplicable(options As VisualBasicSimplifierOptions) As Boolean
            Return True
        End Function
 
        Private Overloads Shared Function SimplifyVariableDeclarator(
            node As VariableDeclaratorSyntax,
            semanticModel As SemanticModel,
            options As VisualBasicSimplifierOptions,
            cancellationToken As CancellationToken
        ) As SyntaxNode
            Dim replacementNode As SyntaxNode = Nothing
            Dim issueSpan As TextSpan
 
            If Not TryReduceVariableDeclaratorWithoutType(
                node, semanticModel, replacementNode, issueSpan) Then
                Return node
            End If
 
            replacementNode = node.CopyAnnotationsTo(replacementNode).WithAdditionalAnnotations(Formatter.Annotation)
            Return replacementNode.WithoutAnnotations(Simplifier.Annotation)
        End Function
 
        Private Shared Function TryReduceVariableDeclaratorWithoutType(
            variableDeclarator As VariableDeclaratorSyntax,
            semanticModel As SemanticModel,
            <Out> ByRef replacementNode As SyntaxNode,
            <Out> ByRef issueSpan As TextSpan) As Boolean
 
            replacementNode = Nothing
            issueSpan = Nothing
 
            ' Failfast Conditions
            If variableDeclarator.AsClause Is Nothing OrElse
               Not variableDeclarator.Parent.IsKind(
                    SyntaxKind.LocalDeclarationStatement,
                    SyntaxKind.UsingStatement,
                    SyntaxKind.ForStatement,
                    SyntaxKind.ForEachStatement) Then
                Return False
            End If
 
            If variableDeclarator.Names.Count <> 1 Then
                Return False
            End If
 
            Dim parent = variableDeclarator.Parent
            Dim modifiedIdentifier = variableDeclarator.Names.Single()
 
            Dim simpleAsClause = TryCast(variableDeclarator.AsClause, SimpleAsClauseSyntax)
            If simpleAsClause Is Nothing Then
                Return False
            End If
 
            If (parent.IsKind(SyntaxKind.LocalDeclarationStatement, SyntaxKind.UsingStatement) AndAlso
                variableDeclarator.Initializer IsNot Nothing) Then
 
                ' Type Check
 
                Dim declaredSymbolType As ITypeSymbol = Nothing
                If Not HasValidDeclaredTypeSymbol(modifiedIdentifier, semanticModel, declaredSymbolType) Then
                    Return False
                End If
 
                Dim initializerType As ITypeSymbol
 
                If declaredSymbolType.IsArrayType() AndAlso variableDeclarator.Initializer.Value.Kind() = SyntaxKind.CollectionInitializer Then
                    ' Get type of the array literal in context without the target type
                    initializerType = semanticModel.GetSpeculativeTypeInfo(variableDeclarator.Initializer.Value.SpanStart, variableDeclarator.Initializer.Value, SpeculativeBindingOption.BindAsExpression).ConvertedType
                Else
                    initializerType = semanticModel.GetTypeInfo(variableDeclarator.Initializer.Value).Type
                End If
 
                If Not declaredSymbolType.Equals(initializerType) Then
                    Return False
                End If
 
                Dim newModifiedIdentifier = SyntaxFactory.ModifiedIdentifier(modifiedIdentifier.Identifier) ' LeadingTrivia is copied here
                replacementNode = SyntaxFactory.VariableDeclarator(SyntaxFactory.SingletonSeparatedList(newModifiedIdentifier.WithTrailingTrivia(variableDeclarator.AsClause.GetTrailingTrivia())),
                                                                   asClause:=Nothing,
                                                                   initializer:=variableDeclarator.Initializer) 'TrailingTrivia is copied here
                issueSpan = variableDeclarator.Span
                Return True
            End If
 
            If (parent.IsKind(SyntaxKind.ForEachStatement, SyntaxKind.ForStatement)) Then
                ' Type Check for ForStatement
                If parent.IsKind(SyntaxKind.ForStatement) Then
                    Dim declaredSymbolType As ITypeSymbol = Nothing
                    If Not HasValidDeclaredTypeSymbol(modifiedIdentifier, semanticModel, declaredSymbolType) Then
                        Return False
                    End If
 
                    Dim valueType = semanticModel.GetTypeInfo(DirectCast(parent, ForStatementSyntax).ToValue).Type
 
                    If Not valueType.Equals(declaredSymbolType) Then
                        Return False
                    End If
                End If
 
                If parent.IsKind(SyntaxKind.ForEachStatement) Then
                    Dim forEachStatementInfo = semanticModel.GetForEachStatementInfo(DirectCast(parent, ForEachStatementSyntax))
                    If Not forEachStatementInfo.ElementConversion.IsIdentity Then
                        Return False
                    End If
                End If
 
                Dim newIdentifierName = SyntaxFactory.IdentifierName(modifiedIdentifier.Identifier) ' Leading Trivia is copied here
                replacementNode = newIdentifierName.WithTrailingTrivia(variableDeclarator.AsClause.GetTrailingTrivia()) ' Trailing Trivia is copied here
                issueSpan = variableDeclarator.Span
                Return True
            End If
 
            Return False
        End Function
 
        Private Shared Function HasValidDeclaredTypeSymbol(
            modifiedIdentifier As ModifiedIdentifierSyntax,
            semanticModel As SemanticModel,
            <Out> ByRef typeSymbol As ITypeSymbol) As Boolean
 
            Dim declaredSymbol = semanticModel.GetDeclaredSymbol(modifiedIdentifier)
            If declaredSymbol Is Nothing OrElse
               (Not TypeOf declaredSymbol Is ILocalSymbol AndAlso Not TypeOf declaredSymbol Is IFieldSymbol) Then
                Return False
            End If
 
            Dim localSymbol = TryCast(declaredSymbol, ILocalSymbol)
            If localSymbol IsNot Nothing AndAlso TypeOf localSymbol IsNot IErrorTypeSymbol AndAlso TypeOf localSymbol.Type IsNot IErrorTypeSymbol Then
                typeSymbol = localSymbol.Type
                Return True
            End If
 
            Return False
        End Function
 
    End Class
End Namespace