/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Syntax/SyntaxReplacer.vb
409 строк
20 KB
Copilot
Improve documentation and error message for InsertTokensBefore/After and ReplaceToken methods (#80691)
01 ноя 2025, 07:07
Не верифицирован
01 ноя 2025, 07:07
8a96674
Код
Авторство
О чём код?
' 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 Imports System.Collections.Generic Imports System.Linq Imports Microsoft.CodeAnalysis.Text Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax Friend Class SyntaxReplacer Friend Shared Function Replace(Of TNode As SyntaxNode)( root As SyntaxNode, Optional nodes As IEnumerable(Of TNode) = Nothing, Optional computeReplacementNode As Func(Of TNode, TNode, SyntaxNode) = Nothing, Optional tokens As IEnumerable(Of SyntaxToken) = Nothing, Optional computeReplacementToken As Func(Of SyntaxToken, SyntaxToken, SyntaxToken) = Nothing, Optional trivia As IEnumerable(Of SyntaxTrivia) = Nothing, Optional computeReplacementTrivia As Func(Of SyntaxTrivia, SyntaxTrivia, SyntaxTrivia) = Nothing) As SyntaxNode Dim replacer = New Replacer(Of TNode)(nodes, computeReplacementNode, tokens, computeReplacementToken, trivia, computeReplacementTrivia) If replacer.HasWork Then Return replacer.Visit(root) Else Return root End If End Function Friend Shared Function Replace( root As SyntaxToken, Optional nodes As IEnumerable(Of SyntaxNode) = Nothing, Optional computeReplacementNode As Func(Of SyntaxNode, SyntaxNode, SyntaxNode) = Nothing, Optional tokens As IEnumerable(Of SyntaxToken) = Nothing, Optional computeReplacementToken As Func(Of SyntaxToken, SyntaxToken, SyntaxToken) = Nothing, Optional trivia As IEnumerable(Of SyntaxTrivia) = Nothing, Optional computeReplacementTrivia As Func(Of SyntaxTrivia, SyntaxTrivia, SyntaxTrivia) = Nothing) As SyntaxToken Dim replacer = New Replacer(Of SyntaxNode)(nodes, computeReplacementNode, tokens, computeReplacementToken, trivia, computeReplacementTrivia) If replacer.HasWork Then Return replacer.VisitToken(root) Else Return root End If End Function Private Class Replacer(Of TNode As SyntaxNode) Inherits VisualBasicSyntaxRewriter Private ReadOnly _computeReplacementNode As Func(Of TNode, TNode, SyntaxNode) Private ReadOnly _computeReplacementToken As Func(Of SyntaxToken, SyntaxToken, SyntaxToken) Private ReadOnly _computeReplacementTrivia As Func(Of SyntaxTrivia, SyntaxTrivia, SyntaxTrivia) Private ReadOnly _nodeSet As HashSet(Of SyntaxNode) Private ReadOnly _tokenSet As HashSet(Of SyntaxToken) Private ReadOnly _triviaSet As HashSet(Of SyntaxTrivia) Private ReadOnly _spanSet As HashSet(Of TextSpan) Private ReadOnly _totalSpan As TextSpan Private ReadOnly _visitStructuredTrivia As Boolean Private ReadOnly _shouldVisitTrivia As Boolean Public Sub New( nodes As IEnumerable(Of TNode), computeReplacementNode As Func(Of TNode, TNode, SyntaxNode), tokens As IEnumerable(Of SyntaxToken), computeReplacementToken As Func(Of SyntaxToken, SyntaxToken, SyntaxToken), trivia As IEnumerable(Of SyntaxTrivia), computeReplacementTrivia As Func(Of SyntaxTrivia, SyntaxTrivia, SyntaxTrivia)) Me._computeReplacementNode = computeReplacementNode Me._computeReplacementToken = computeReplacementToken Me._computeReplacementTrivia = computeReplacementTrivia Me._nodeSet = If(nodes IsNot Nothing, New HashSet(Of SyntaxNode)(nodes), s_noNodes) Me._tokenSet = If(tokens IsNot Nothing, New HashSet(Of SyntaxToken)(tokens), s_noTokens) Me._triviaSet = If(trivia IsNot Nothing, New HashSet(Of SyntaxTrivia)(trivia), s_noTrivia) Me._spanSet = New HashSet(Of TextSpan)(Me._nodeSet.Select(Function(n) n.FullSpan).Concat( Me._tokenSet.Select(Function(t) t.FullSpan)).Concat( Me._triviaSet.Select(Function(t) t.FullSpan))) Me._totalSpan = ComputeTotalSpan(Me._spanSet) Me._visitStructuredTrivia = Me._nodeSet.Any(Function(n) n.IsPartOfStructuredTrivia()) OrElse Me._tokenSet.Any(Function(t) t.IsPartOfStructuredTrivia()) OrElse Me._triviaSet.Any(Function(t) t.IsPartOfStructuredTrivia()) Me._shouldVisitTrivia = Me._triviaSet.Count > 0 OrElse Me._visitStructuredTrivia End Sub Private Shared ReadOnly s_noNodes As HashSet(Of SyntaxNode) = New HashSet(Of SyntaxNode)() Private Shared ReadOnly s_noTokens As HashSet(Of SyntaxToken) = New HashSet(Of SyntaxToken)() Private Shared ReadOnly s_noTrivia As HashSet(Of SyntaxTrivia) = New HashSet(Of SyntaxTrivia)() Public Overrides ReadOnly Property VisitIntoStructuredTrivia As Boolean Get Return Me._visitStructuredTrivia End Get End Property Public ReadOnly Property HasWork As Boolean Get Return Me._nodeSet.Count + Me._tokenSet.Count + Me._triviaSet.Count > 0 End Get End Property Private Shared Function ComputeTotalSpan(spans As IEnumerable(Of TextSpan)) As TextSpan Dim first = True Dim start As Integer = 0 Dim [end] As Integer = 0 For Each span In spans If first Then first = False start = span.Start [end] = span.End Else start = Math.Min(start, span.Start) [end] = Math.Max([end], span.End) End If Next Return New TextSpan(start, [end] - start) End Function Private Function ShouldVisit(span As TextSpan) As Boolean If Not span.IntersectsWith(Me._totalSpan) Then Return False End If For Each s In Me._spanSet If span.IntersectsWith(s) Then Return True End If Next Return False End Function Public Overrides Function Visit(node As SyntaxNode) As SyntaxNode Dim rewritten = node If node IsNot Nothing Then If Me.ShouldVisit(node.FullSpan) Then rewritten = MyBase.Visit(node) End If If Me._nodeSet.Contains(node) AndAlso Me._computeReplacementNode IsNot Nothing Then rewritten = Me._computeReplacementNode(DirectCast(node, TNode), DirectCast(rewritten, TNode)) End If End If Return rewritten End Function Public Overrides Function VisitToken(token As SyntaxToken) As SyntaxToken Dim rewritten = token If Me._shouldVisitTrivia AndAlso Me.ShouldVisit(token.FullSpan) Then rewritten = MyBase.VisitToken(token) End If If Me._tokenSet.Contains(token) AndAlso Me._computeReplacementToken IsNot Nothing Then rewritten = Me._computeReplacementToken(token, rewritten) End If Return rewritten End Function Public Overrides Function VisitListElement(trivia As SyntaxTrivia) As SyntaxTrivia Dim rewritten = trivia If Me.VisitIntoStructuredTrivia AndAlso trivia.HasStructure AndAlso Me.ShouldVisit(trivia.FullSpan) Then rewritten = Me.VisitTrivia(trivia) End If If Me._triviaSet.Contains(trivia) AndAlso Me._computeReplacementTrivia IsNot Nothing Then rewritten = Me._computeReplacementTrivia(trivia, rewritten) End If Return rewritten End Function End Class Public Shared Function ReplaceNodeInList(root As SyntaxNode, originalNode As SyntaxNode, newNodes As IEnumerable(Of SyntaxNode)) As SyntaxNode Return New NodeListEditor(originalNode, newNodes, ListEditKind.Replace).Visit(root) End Function Public Shared Function InsertNodeInList(root As SyntaxNode, nodeInList As SyntaxNode, nodesToInsert As IEnumerable(Of SyntaxNode), insertBefore As Boolean) As SyntaxNode Return New NodeListEditor(nodeInList, nodesToInsert, If(insertBefore, ListEditKind.InsertBefore, ListEditKind.InsertAfter)).Visit(root) End Function Public Shared Function ReplaceTokenInList(root As SyntaxNode, tokenInList As SyntaxToken, newTokens As IEnumerable(Of SyntaxToken)) As SyntaxNode Return New TokenListEditor(tokenInList, newTokens, ListEditKind.Replace).Visit(root) End Function Public Shared Function InsertTokenInList(root As SyntaxNode, tokenInList As SyntaxToken, newTokens As IEnumerable(Of SyntaxToken), insertBefore As Boolean) As SyntaxNode Return New TokenListEditor(tokenInList, newTokens, If(insertBefore, ListEditKind.InsertBefore, ListEditKind.InsertAfter)).Visit(root) End Function Public Shared Function ReplaceTriviaInList(root As SyntaxNode, triviaInList As SyntaxTrivia, newTrivia As IEnumerable(Of SyntaxTrivia)) As SyntaxNode Return New TriviaListEditor(triviaInList, newTrivia, ListEditKind.Replace).Visit(root) End Function Public Shared Function InsertTriviaInList(root As SyntaxNode, triviaInList As SyntaxTrivia, newTrivia As IEnumerable(Of SyntaxTrivia), insertBefore As Boolean) As SyntaxNode Return New TriviaListEditor(triviaInList, newTrivia, If(insertBefore, ListEditKind.InsertBefore, ListEditKind.InsertAfter)).Visit(root) End Function Public Shared Function ReplaceTriviaInList(root As SyntaxToken, triviaInList As SyntaxTrivia, newTrivia As IEnumerable(Of SyntaxTrivia)) As SyntaxToken Return New TriviaListEditor(triviaInList, newTrivia, ListEditKind.Replace).VisitToken(root) End Function Public Shared Function InsertTriviaInList(root As SyntaxToken, triviaInList As SyntaxTrivia, newTrivia As IEnumerable(Of SyntaxTrivia), insertBefore As Boolean) As SyntaxToken Return New TriviaListEditor(triviaInList, newTrivia, If(insertBefore, ListEditKind.InsertBefore, ListEditKind.InsertAfter)).VisitToken(root) End Function Private Enum ListEditKind InsertBefore InsertAfter Replace End Enum Private Shared Function GetItemNotListElementException() As InvalidOperationException Return New InvalidOperationException(CodeAnalysisResources.MissingListItem) End Function Private Shared Function GetTokenNotListElementException() As InvalidOperationException Return New InvalidOperationException(CodeAnalysisResources.MissingTokenListItem) End Function Private Class BaseListEditor Inherits VisualBasicSyntaxRewriter Private ReadOnly _elementSpan As TextSpan Protected ReadOnly _editKind As ListEditKind Private ReadOnly _visitTrivia As Boolean Private ReadOnly _visitIntoStructuredTrivia As Boolean Public Sub New( elementSpan As TextSpan, editKind As ListEditKind, visitTrivia As Boolean, visitIntoStructuredTrivia As Boolean) Me._elementSpan = elementSpan Me._editKind = editKind Me._visitTrivia = visitTrivia Or visitIntoStructuredTrivia Me._visitIntoStructuredTrivia = visitIntoStructuredTrivia End Sub Public Overrides ReadOnly Property VisitIntoStructuredTrivia As Boolean Get Return Me._visitIntoStructuredTrivia End Get End Property Private Function ShouldVisit(span As TextSpan) As Boolean Return span.IntersectsWith(Me._elementSpan) End Function Public Overrides Function Visit(node As SyntaxNode) As SyntaxNode Dim rewritten = node If node IsNot Nothing AndAlso ShouldVisit(node.FullSpan) Then rewritten = MyBase.Visit(node) End If Return rewritten End Function Public Overrides Function VisitToken(token As SyntaxToken) As SyntaxToken Dim rewritten = token If Me._visitTrivia AndAlso Me.ShouldVisit(token.FullSpan) Then rewritten = MyBase.VisitToken(token) End If Return rewritten End Function Public Overrides Function VisitListElement(element As SyntaxTrivia) As SyntaxTrivia Dim rewritten = element If Me._visitIntoStructuredTrivia AndAlso element.HasStructure AndAlso Me.ShouldVisit(element.FullSpan) Then rewritten = MyBase.VisitTrivia(element) End If Return rewritten End Function End Class Private Class NodeListEditor Inherits BaseListEditor Private ReadOnly _originalNode As SyntaxNode Private ReadOnly _replacementNodes As IEnumerable(Of SyntaxNode) Public Sub New(originalNode As SyntaxNode, replacementNodes As IEnumerable(Of SyntaxNode), editKind As ListEditKind) MyBase.New(originalNode.FullSpan, editKind, visitTrivia:=False, visitIntoStructuredTrivia:=originalNode.IsPartOfStructuredTrivia()) Me._originalNode = originalNode Me._replacementNodes = replacementNodes End Sub Public Overrides Function Visit(node As SyntaxNode) As SyntaxNode If node Is _originalNode Then Throw GetItemNotListElementException() End If Return MyBase.Visit(node) End Function Public Overrides Function VisitList(Of TNode As SyntaxNode)(list As SeparatedSyntaxList(Of TNode)) As SeparatedSyntaxList(Of TNode) If TypeOf Me._originalNode Is TNode Then Dim index = list.IndexOf(DirectCast(Me._originalNode, TNode)) If index >= 0 AndAlso index < list.Count Then Select Case Me._editKind Case ListEditKind.Replace Return list.ReplaceRange(DirectCast(Me._originalNode, TNode), Me._replacementNodes.Cast(Of TNode)) Case ListEditKind.InsertBefore Return list.InsertRange(index, Me._replacementNodes.Cast(Of TNode)) Case ListEditKind.InsertAfter Return list.InsertRange(index + 1, Me._replacementNodes.Cast(Of TNode)) End Select End If End If Return MyBase.VisitList(list) End Function Public Overrides Function VisitList(Of TNode As SyntaxNode)(list As SyntaxList(Of TNode)) As SyntaxList(Of TNode) If TypeOf Me._originalNode Is TNode Then Dim index = list.IndexOf(DirectCast(Me._originalNode, TNode)) If index >= 0 AndAlso index < list.Count Then Select Case Me._editKind Case ListEditKind.Replace Return list.ReplaceRange(DirectCast(Me._originalNode, TNode), Me._replacementNodes.Cast(Of TNode)) Case ListEditKind.InsertBefore Return list.InsertRange(index, Me._replacementNodes.Cast(Of TNode)) Case ListEditKind.InsertAfter Return list.InsertRange(index + 1, Me._replacementNodes.Cast(Of TNode)) End Select End If End If Return MyBase.VisitList(list) End Function End Class Private Class TokenListEditor Inherits BaseListEditor Private ReadOnly _originalToken As SyntaxToken Private ReadOnly _newTokens As IEnumerable(Of SyntaxToken) Public Sub New(originalToken As SyntaxToken, newTokens As IEnumerable(Of SyntaxToken), editKind As ListEditKind) MyBase.New(originalToken.FullSpan, editKind, visitTrivia:=False, visitIntoStructuredTrivia:=originalToken.IsPartOfStructuredTrivia()) Me._originalToken = originalToken Me._newTokens = newTokens End Sub Public Overrides Function VisitToken(token As SyntaxToken) As SyntaxToken If token = _originalToken Then Throw GetTokenNotListElementException() End If Return MyBase.VisitToken(token) End Function Public Overrides Function VisitList(list As SyntaxTokenList) As SyntaxTokenList Dim index = list.IndexOf(Me._originalToken) If index >= 0 AndAlso index < list.Count Then Select Case Me._editKind Case ListEditKind.Replace Return list.ReplaceRange(Me._originalToken, Me._newTokens) Case ListEditKind.InsertBefore Return list.InsertRange(index, Me._newTokens) Case ListEditKind.InsertAfter Return list.InsertRange(index + 1, Me._newTokens) End Select End If Return MyBase.VisitList(list) End Function End Class Private Class TriviaListEditor Inherits BaseListEditor Private ReadOnly _originalTrivia As SyntaxTrivia Private ReadOnly _newTrivia As IEnumerable(Of SyntaxTrivia) Public Sub New(originalTrivia As SyntaxTrivia, newTrivia As IEnumerable(Of SyntaxTrivia), editKind As ListEditKind) MyBase.New(originalTrivia.FullSpan, editKind, visitTrivia:=True, visitIntoStructuredTrivia:=originalTrivia.IsPartOfStructuredTrivia()) Me._originalTrivia = originalTrivia Me._newTrivia = newTrivia End Sub Public Overrides Function VisitList(list As SyntaxTriviaList) As SyntaxTriviaList Dim index = list.IndexOf(Me._originalTrivia) If index >= 0 AndAlso index < list.Count Then Select Case Me._editKind Case ListEditKind.Replace Return list.ReplaceRange(Me._originalTrivia, Me._newTrivia) Case ListEditKind.InsertBefore Return list.InsertRange(index, Me._newTrivia) Case ListEditKind.InsertAfter Return list.InsertRange(index + 1, Me._newTrivia) End Select End If Return MyBase.VisitList(list) End Function End Class End Class End Namespace