| File: ExtractMethod\Extensions.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.Runtime.CompilerServices Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.ExtractMethod Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod Friend Module Extensions <Extension()> Public Function GetUnparenthesizedExpression(node As SyntaxNode) As ExpressionSyntax Dim parenthesizedExpression = TryCast(node, ParenthesizedExpressionSyntax) If parenthesizedExpression Is Nothing Then Return DirectCast(node, ExpressionSyntax) End If Return GetUnparenthesizedExpression(parenthesizedExpression.Expression) End Function <Extension()> Public Function GetStatementContainer(node As SyntaxNode) As SyntaxNode Contract.ThrowIfNull(node) Dim statement = node.GetStatementUnderContainer() If statement Is Nothing Then Return Nothing End If Return statement.Parent End Function <Extension()> Public Function GetStatementUnderContainer(node As SyntaxNode) As ExecutableStatementSyntax Contract.ThrowIfNull(node) Do While node IsNot Nothing If node.Parent.IsStatementContainerNode() AndAlso TypeOf node Is ExecutableStatementSyntax AndAlso node.Parent.ContainStatement(DirectCast(node, ExecutableStatementSyntax)) Then Return TryCast(node, ExecutableStatementSyntax) End If node = node.Parent Loop Return Nothing End Function <Extension()> Public Function ContainStatement(node As SyntaxNode, statement As StatementSyntax) As Boolean Contract.ThrowIfNull(node) Contract.ThrowIfNull(statement) If Not node.IsStatementContainerNode() Then Return False End If Return node.GetStatements().IndexOf(statement) >= 0 End Function <Extension()> Public Function GetOutermostNodeWithSameSpan(initialNode As SyntaxNode, predicate As Func(Of SyntaxNode, Boolean)) As SyntaxNode If initialNode Is Nothing Then Return Nothing End If ' now try to find outmost node that has same span Dim firstContainingSpan = initialNode.Span Dim node = initialNode Dim lastNode = Nothing Do If predicate(node) Then lastNode = node End If node = node.Parent Loop While node IsNot Nothing AndAlso node.Span.Equals(firstContainingSpan) Return CType(If(lastNode, initialNode), SyntaxNode) End Function <Extension()> Public Function PartOfConstantInitializerExpression(node As SyntaxNode) As Boolean Return node.PartOfConstantInitializerExpression(Of FieldDeclarationSyntax)(Function(n) n.Modifiers) OrElse node.PartOfConstantInitializerExpression(Of LocalDeclarationStatementSyntax)(Function(n) n.Modifiers) End Function <Extension()> Private Function PartOfConstantInitializerExpression(Of T As SyntaxNode)(node As SyntaxNode, modifiersGetter As Func(Of T, SyntaxTokenList)) As Boolean Dim decl = node.GetAncestor(Of T)() If decl Is Nothing Then Return False End If If Not modifiersGetter(decl).Any(Function(m) m.Kind = SyntaxKind.ConstKeyword) Then Return False End If ' we are under decl with const modifier, check we are part of initializer expression Dim equal = node.GetAncestor(Of EqualsValueSyntax)() If equal Is Nothing Then Return False End If Return equal.Value IsNot Nothing AndAlso equal.Value.Span.Contains(node.Span) End Function <Extension()> Public Function IsArgumentForByRefParameter(node As SyntaxNode, model As SemanticModel, cancellationToken As CancellationToken) As Boolean Dim argument = node.FirstAncestorOrSelf(Of ArgumentSyntax)() ' make sure we are the argument If argument Is Nothing OrElse node.Span <> argument.Span Then Return False End If ' now find invocation node Dim invocation = argument.FirstAncestorOrSelf(Of InvocationExpressionSyntax)() ' argument for something we are not interested in If invocation Is Nothing Then Return False End If ' find argument index Dim argumentIndex = invocation.ArgumentList.Arguments.IndexOf(argument) If argumentIndex < 0 Then Return False End If ' get all method symbols Dim methodSymbols = model.GetSymbolInfo(invocation, cancellationToken).GetAllSymbols().Where(Function(s) s.Kind = SymbolKind.Method).Cast(Of IMethodSymbol)() For Each method In methodSymbols ' not a right method If method.Parameters.Length <= argumentIndex Then Continue For End If ' make sure there is no ref type Dim parameter = method.Parameters(argumentIndex) If parameter.RefKind <> RefKind.None Then Return True End If Next Return False End Function <Extension()> Public Function ContainArgumentlessThrowWithoutEnclosingCatch(ByVal tokens As IEnumerable(Of SyntaxToken), ByVal textSpan As TextSpan) As Boolean For Each token In tokens If token.Kind <> SyntaxKind.ThrowKeyword Then Continue For End If Dim throwStatement = TryCast(token.Parent, ThrowStatementSyntax) If throwStatement Is Nothing OrElse throwStatement.Expression IsNot Nothing Then Continue For End If Dim catchBlock = token.GetAncestor(Of CatchBlockSyntax)() If catchBlock Is Nothing OrElse Not textSpan.Contains(catchBlock.Span) Then Return True End If Next token Return False End Function <Extension()> Public Function ContainPreprocessorCrossOver(ByVal tokens As IEnumerable(Of SyntaxToken), ByVal textSpan As TextSpan) As Boolean Dim activeRegions As Integer = 0 Dim activeIfs As Integer = 0 For Each trivia In tokens.GetAllTrivia() If Not trivia.IsDirective Then Continue For End If Dim directive = DirectCast(trivia.GetStructure(), DirectiveTriviaSyntax) If Not textSpan.Contains(directive.Span) Then Continue For End If Select Case directive.Kind Case SyntaxKind.RegionDirectiveTrivia activeRegions += 1 Case SyntaxKind.EndRegionDirectiveTrivia If activeRegions <= 0 Then Return True activeRegions -= 1 Case SyntaxKind.IfDirectiveTrivia activeIfs += 1 Case SyntaxKind.EndIfDirectiveTrivia If activeIfs <= 0 Then Return True activeIfs -= 1 Case SyntaxKind.ElseDirectiveTrivia, SyntaxKind.ElseIfDirectiveTrivia If activeIfs <= 0 Then Return True End Select Next trivia Return activeIfs <> 0 OrElse activeRegions <> 0 End Function <Extension()> Public Function GetAllTrivia(ByVal tokens As IEnumerable(Of SyntaxToken)) As IEnumerable(Of SyntaxTrivia) Dim list = New List(Of SyntaxTrivia)() For Each token In tokens list.AddRange(token.LeadingTrivia) list.AddRange(token.TrailingTrivia) Next token Return list End Function <Extension()> Public Function ContainsFieldInitializer(node As SyntaxNode) As Boolean node = node.GetOutermostNodeWithSameSpan(Function(n) True) Return node.DescendantNodesAndSelf().Any(Function(n) TypeOf n Is FieldInitializerSyntax) End Function <Extension()> Public Function ContainsDotMemberAccess(node As SyntaxNode) As Boolean Dim predicate = Function(n As SyntaxNode) Dim member = TryCast(n, MemberAccessExpressionSyntax) If member Is Nothing Then Return False End If Return member.Expression Is Nothing AndAlso member.OperatorToken.Kind = SyntaxKind.DotToken End Function Return node.DescendantNodesAndSelf().Any(predicate) End Function <Extension()> Public Function UnderWithBlockContext(token As SyntaxToken) As Boolean Dim withBlock = token.GetAncestor(Of WithBlockSyntax)() If withBlock Is Nothing Then Return False End If Dim withBlockSpan = TextSpan.FromBounds(withBlock.WithStatement.Span.End, withBlock.EndWithStatement.SpanStart) Return withBlockSpan.Contains(token.Span) End Function <Extension()> Public Function UnderObjectMemberInitializerContext(token As SyntaxToken) As Boolean Dim initializer = token.GetAncestor(Of ObjectMemberInitializerSyntax)() If initializer Is Nothing Then Return False End If Dim initializerSpan = TextSpan.FromBounds(initializer.WithKeyword.Span.End, initializer.Span.End) Return initializerSpan.Contains(token.Span) End Function <Extension()> Public Function UnderValidContext(token As SyntaxToken) As Boolean Dim predicate As Func(Of SyntaxNode, Boolean) = Function(n) Dim range = TryCast(n, RangeArgumentSyntax) If range IsNot Nothing Then If range.UpperBound.Span.Contains(token.Span) AndAlso range.GetAncestor(Of FieldDeclarationSyntax)() IsNot Nothing Then Return True End If End If Dim [property] = TryCast(n, PropertyStatementSyntax) If [property] IsNot Nothing Then Dim asNewClause = TryCast([property].AsClause, AsNewClauseSyntax) If asNewClause IsNot Nothing AndAlso asNewClause.NewExpression IsNot Nothing Then Dim span = TextSpan.FromBounds(asNewClause.NewExpression.NewKeyword.Span.End, asNewClause.NewExpression.Span.End) Return span.Contains(token.Span) End If End If If n.CheckTopLevel(token.Span) Then Return True End If Return False End Function Return token.GetAncestors(Of SyntaxNode)().Any(predicate) End Function <Extension()> Public Function ContainedInValidType(node As SyntaxNode) As Boolean Contract.ThrowIfNull(node) For Each ancestor In node.AncestorsAndSelf If TryCast(ancestor, TypeBlockSyntax) IsNot Nothing Then Return True End If If TryCast(ancestor, NamespaceBlockSyntax) IsNot Nothing Then Return False End If Next Return True End Function <Extension()> Public Function ContainsInMethodBlockBody(block As MethodBlockBaseSyntax, textSpan As TextSpan) As Boolean If block Is Nothing Then Return False End If Dim blockSpan = TextSpan.FromBounds(block.BlockStatement.Span.End, block.EndBlockStatement.SpanStart) Return blockSpan.Contains(textSpan) End Function <Extension()> _ Public Function UnderValidContext(ByVal node As SyntaxNode) As Boolean Contract.ThrowIfNull(node) Dim predicate As Func(Of SyntaxNode, Boolean) = Function(n) If TypeOf n Is MethodBlockBaseSyntax OrElse TypeOf n Is MultiLineLambdaExpressionSyntax OrElse TypeOf n Is SingleLineLambdaExpressionSyntax Then Return True End If Return False End Function If Not node.GetAncestorsOrThis(Of SyntaxNode)().Any(predicate) Then Return False End If If node.FromScript() OrElse node.GetAncestor(Of TypeBlockSyntax)() IsNot Nothing Then Return True End If Return False End Function <Extension()> Public Function IsReturnableConstruct(node As SyntaxNode) As Boolean Return TypeOf node Is MethodBlockBaseSyntax OrElse TypeOf node Is SingleLineLambdaExpressionSyntax OrElse TypeOf node Is MultiLineLambdaExpressionSyntax End Function <Extension()> Public Function HasSyntaxAnnotation([set] As HashSet(Of SyntaxAnnotation), node As SyntaxNode) As Boolean Return [set].Any(Function(a) node.GetAnnotatedNodesAndTokens(a).Any()) End Function <Extension()> Public Function IsFunctionValue(symbol As ISymbol) As Boolean Dim local = TryCast(symbol, ILocalSymbol) Return local IsNot Nothing AndAlso local.IsFunctionValue End Function <Extension()> Public Function ToSeparatedList(Of T As SyntaxNode)(nodes As IEnumerable(Of Tuple(Of T, SyntaxToken))) As SeparatedSyntaxList(Of T) Dim list = New List(Of SyntaxNodeOrToken) For Each tuple In nodes Contract.ThrowIfNull(tuple.Item1) list.Add(tuple.Item1) If tuple.Item2.Kind = SyntaxKind.None Then Exit For End If list.Add(tuple.Item2) Next Return SyntaxFactory.SeparatedList(Of T)(list) End Function <Extension()> Public Function CreateAssignmentExpressionStatementWithValue(identifier As SyntaxToken, rvalue As ExpressionSyntax) As ExecutableStatementSyntax Return SyntaxFactory.SimpleAssignmentStatement(SyntaxFactory.IdentifierName(identifier), SyntaxFactory.Token(SyntaxKind.EqualsToken), rvalue).WithAppendedTrailingTrivia(SyntaxFactory.ElasticMarker) End Function <Extension()> Public Function ProcessLocalDeclarationStatement(variableToRemoveMap As HashSet(Of SyntaxAnnotation), declarationStatement As LocalDeclarationStatementSyntax, expressionStatements As List(Of StatementSyntax), variableDeclarators As List(Of VariableDeclaratorSyntax), triviaList As List(Of SyntaxTrivia)) As Boolean ' go through each var decls in decl statement, and create new assignment if ' variable is initialized at decl. Dim hasChange As Boolean = False Dim leadingTriviaApplied As Boolean = False For Each variableDeclarator In declarationStatement.Declarators Dim identifierList = New List(Of ModifiedIdentifierSyntax)() Dim nameCount = variableDeclarator.Names.Count For i = 0 To nameCount - 1 Step 1 Dim variable = variableDeclarator.Names(i) If variableToRemoveMap.HasSyntaxAnnotation(variable) Then If variableDeclarator.Initializer IsNot Nothing AndAlso i = nameCount - 1 Then ' move comments with the variable here Dim identifier As SyntaxToken = variable.Identifier ' The leading trivia from the declaration is applied to the first variable ' There is not much value in appending the trailing trivia of the modifier If i = 0 AndAlso Not leadingTriviaApplied AndAlso declarationStatement.HasLeadingTrivia Then identifier = identifier.WithLeadingTrivia(declarationStatement.GetLeadingTrivia.AddRange(identifier.LeadingTrivia)) leadingTriviaApplied = True End If expressionStatements.Add(identifier.CreateAssignmentExpressionStatementWithValue(variableDeclarator.Initializer.Value)) Continue For End If ' we don't remove trivia around tokens we remove triviaList.AddRange(variable.GetLeadingTrivia()) triviaList.AddRange(variable.GetTrailingTrivia()) Continue For End If If triviaList.Count > 0 Then identifierList.Add(variable.WithPrependedLeadingTrivia(triviaList)) triviaList.Clear() Continue For End If identifierList.Add(variable) Next If identifierList.Count = 0 Then ' attach left over trivia to last expression statement If triviaList.Count > 0 AndAlso expressionStatements.Count > 0 Then Dim lastStatement = expressionStatements(expressionStatements.Count - 1) lastStatement = lastStatement.WithPrependedLeadingTrivia(triviaList) expressionStatements(expressionStatements.Count - 1) = lastStatement triviaList.Clear() End If Continue For ElseIf identifierList.Count = variableDeclarator.Names.Count Then variableDeclarators.Add(variableDeclarator) ElseIf identifierList.Count > 0 Then variableDeclarators.Add( variableDeclarator.WithNames(SyntaxFactory.SeparatedList(identifierList)). WithPrependedLeadingTrivia(triviaList)) hasChange = True End If Next variableDeclarator Return hasChange OrElse declarationStatement.Declarators.Count <> variableDeclarators.Count End Function <Extension()> Public Function IsExpressionInCast(node As SyntaxNode) As Boolean Return TypeOf node Is ExpressionSyntax AndAlso TypeOf node.Parent Is CastExpressionSyntax End Function <Extension()> Public Function IsErrorType(type As ITypeSymbol) As Boolean Return type Is Nothing OrElse type.Kind = SymbolKind.ErrorType End Function <Extension()> Public Function IsObjectType(type As ITypeSymbol) As Boolean Return type Is Nothing OrElse type.SpecialType = SpecialType.System_Object End Function End Module End Namespace