| File: ExtractMethod\VisualBasicMethodExtractor.VisualBasicCodeGenerator.ExpressionCodeGenerator.vb | Web Access |
| Project: src\roslyn\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.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.ExtractMethod Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod Partial Friend NotInheritable Class VisualBasicExtractMethodService Partial Friend Class VisualBasicMethodExtractor Partial Private Class VisualBasicCodeGenerator Private Class ExpressionCodeGenerator Inherits VisualBasicCodeGenerator Public Sub New( selectionResult As SelectionResult, analyzerResult As AnalyzerResult, options As ExtractMethodGenerationOptions) MyBase.New(selectionResult, analyzerResult, options) End Sub Protected Overrides Function CreateMethodName() As SyntaxToken Dim methodName = "NewMethod" Dim containingScope = Me.SelectionResult.GetContainingScope() methodName = GetMethodNameBasedOnExpression(methodName, containingScope) Dim semanticModel = SemanticDocument.SemanticModel Dim nameGenerator = New UniqueNameGenerator(semanticModel) Return SyntaxFactory.Identifier( nameGenerator.CreateUniqueMethodName(containingScope, methodName)) End Function Private Shared Function GetMethodNameBasedOnExpression(methodName As String, expression As SyntaxNode) As String If expression.IsParentKind(SyntaxKind.EqualsValue) AndAlso expression.Parent.IsParentKind(SyntaxKind.VariableDeclarator) Then Dim varDecl = DirectCast(expression.Parent.Parent, VariableDeclaratorSyntax) If varDecl.Names.Count <> 1 Then Return methodName End If Dim identifierNode = varDecl.Names(0) If identifierNode Is Nothing Then Return methodName End If Dim name = identifierNode.Identifier.ValueText Return If(name IsNot Nothing AndAlso name.Length > 0, MakeMethodName("Get", name, camelCase:=False), methodName) End If If TypeOf expression Is MemberAccessExpressionSyntax Then expression = CType(expression, MemberAccessExpressionSyntax).Name End If If TypeOf expression Is NameSyntax Then Dim lastDottedName = CType(expression, NameSyntax).GetLastDottedName() Dim plainName = CType(lastDottedName, SimpleNameSyntax).Identifier.ValueText Return If(plainName IsNot Nothing AndAlso plainName.Length > 0, MakeMethodName("Get", plainName, camelCase:=False), methodName) End If Return methodName End Function Protected Overrides Function GetInitialStatementsForMethodDefinitions() As ImmutableArray(Of StatementSyntax) Contract.ThrowIfFalse(Me.SelectionResult.IsExtractMethodOnExpression) Dim expression = DirectCast(Me.SelectionResult.GetContainingScope(), ExpressionSyntax) If Me.AnalyzerResult.CoreReturnType.SpecialType <> SpecialType.System_Void Then Return ImmutableArray.Create(Of StatementSyntax)(SyntaxFactory.ReturnStatement(expression)) Else ' we have expression for void method (Sub). make the expression as call ' statement if possible we can create call statement only from invocation ' and member access expression. otherwise, it is not a valid expression. ' return error code If expression.Kind <> SyntaxKind.InvocationExpression AndAlso expression.Kind <> SyntaxKind.SimpleMemberAccessExpression Then Return ImmutableArray(Of StatementSyntax).Empty End If Return ImmutableArray.Create(Of StatementSyntax)(SyntaxFactory.ExpressionStatement(expression)) End If End Function Protected Overrides Function GetFirstStatementOrInitializerSelectedAtCallSite() As StatementSyntax Return Me.SelectionResult.GetContainingScopeOf(Of StatementSyntax)() End Function Protected Overrides Function GetLastStatementOrInitializerSelectedAtCallSite() As StatementSyntax Return GetFirstStatementOrInitializerSelectedAtCallSite() End Function Protected Overrides Async Function GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(cancellationToken As CancellationToken) As Task(Of StatementSyntax) Dim enclosingStatement = GetFirstStatementOrInitializerSelectedAtCallSite() Dim callSignature = CreateCallSignature().WithAdditionalAnnotations(CallSiteAnnotation) Dim sourceNode = Me.SelectionResult.GetContainingScope() Contract.ThrowIfTrue( sourceNode.IsParentKind(SyntaxKind.SimpleMemberAccessExpression) AndAlso DirectCast(sourceNode.Parent, MemberAccessExpressionSyntax).Name Is sourceNode, "invalid scope. scope is not an expression") ' To lower the chances that replacing sourceNode with callSignature will break the user's ' code, we make the enclosing statement semantically explicit. This ends up being a little ' bit more work because we need to annotate the sourceNode so that we can get back to it ' after rewriting the enclosing statement. Dim sourceNodeAnnotation = New SyntaxAnnotation() Dim enclosingStatementAnnotation = New SyntaxAnnotation() Dim newEnclosingStatement = enclosingStatement. ReplaceNode(sourceNode, sourceNode.WithAdditionalAnnotations(sourceNodeAnnotation)). WithAdditionalAnnotations(enclosingStatementAnnotation) Dim updatedDocument = Await Me.SemanticDocument.Document.ReplaceNodeAsync(enclosingStatement, newEnclosingStatement, cancellationToken).ConfigureAwait(False) Dim updatedRoot = Await updatedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False) newEnclosingStatement = DirectCast(updatedRoot.GetAnnotatedNodesAndTokens(enclosingStatementAnnotation).Single().AsNode(), StatementSyntax) ' because of the complexification we cannot guarantee that there is only one annotation. ' however complexification of names is prepended, so the last annotation should be the original one. sourceNode = updatedRoot.GetAnnotatedNodesAndTokens(sourceNodeAnnotation).Last().AsNode() Return newEnclosingStatement.ReplaceNode(sourceNode, callSignature) End Function End Class End Class End Class End Class End Namespace