/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Utilities/SpeculationAnalyzer.vb
620 строк
38 KB
Cyrus Najmabadi
Fix crash in 'use collection initializer'
25 сен 2024, 22:48
25 сен 2024, 22:48
196aa84
Код
Авторство
О чём код?
' 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.VisualBasic.LanguageService Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.Utilities ''' <summary> ''' Helper class to analyze the semantic effects of a speculated syntax node replacement on the parenting nodes. ''' Given an expression node from a syntax tree and a new expression from a different syntax tree, ''' it replaces the expression with the new expression to create a speculated syntax tree. ''' It uses the original tree's semantic model to create a speculative semantic model and verifies that ''' the syntax replacement doesn't break the semantics of any parenting nodes of the original expression. ''' </summary> Friend Class SpeculationAnalyzer Inherits AbstractSpeculationAnalyzer(Of ExpressionSyntax, TypeSyntax, AttributeSyntax, ArgumentSyntax, ForEachStatementSyntax, ThrowStatementSyntax, InvocationExpressionSyntax, Conversion) ''' <summary> ''' Creates a semantic analyzer for speculative syntax replacement. ''' </summary> ''' <param name="expression">Original expression to be replaced.</param> ''' <param name="newExpression">New expression to replace the original expression.</param> ''' <param name="semanticModel">Semantic model of <paramref name="expression"/> node's syntax tree.</param> ''' <param name="cancellationToken">Cancellation token.</param> ''' <param name="skipVerificationForReplacedNode"> ''' True if semantic analysis should be skipped for the replaced node and performed starting from parent of the original and replaced nodes. ''' This could be the case when custom verifications are required to be done by the caller or ''' semantics of the replaced expression are different from the original expression. ''' </param> ''' <param name="failOnOverloadResolutionFailuresInOriginalCode"> ''' True if semantic analysis should fail when any of the invocation expression ancestors of <paramref name="expression"/> in original code has overload resolution failures. ''' </param> Public Sub New(expression As ExpressionSyntax, newExpression As ExpressionSyntax, semanticModel As SemanticModel, cancellationToken As CancellationToken, Optional skipVerificationForReplacedNode As Boolean = False, Optional failOnOverloadResolutionFailuresInOriginalCode As Boolean = False) MyBase.New(expression, newExpression, semanticModel, cancellationToken, skipVerificationForReplacedNode, failOnOverloadResolutionFailuresInOriginalCode) End Sub Protected Overrides ReadOnly Property SyntaxFactsService As CodeAnalysis.LanguageService.ISyntaxFacts = VisualBasicSyntaxFacts.Instance Protected Overrides Function CanAccessInstanceMemberThrough(expression As ExpressionSyntax) As Boolean ' vb can reference an instance member by just writing `.X` (when in a 'with' block), or by writing Me.X, ' MyBase.X and MyClass.X (the latter is not just for accessing static members). Return expression Is Nothing OrElse expression.IsKind(SyntaxKind.MeExpression, SyntaxKind.MyBaseExpression, SyntaxKind.MyClassExpression) End Function Protected Overrides Function GetSemanticRootForSpeculation(expression As ExpressionSyntax) As SyntaxNode Debug.Assert(expression IsNot Nothing) Dim parentNodeToSpeculate = expression _ .AncestorsAndSelf(ascendOutOfTrivia:=False) _ .Where(Function(node) CanSpeculateOnNode(node)) _ .LastOrDefault() If parentNodeToSpeculate Is Nothing Then parentNodeToSpeculate = expression End If Return parentNodeToSpeculate End Function Public Shared Function CanSpeculateOnNode(node As SyntaxNode) As Boolean Return TypeOf node Is ExecutableStatementSyntax OrElse TypeOf node Is TypeSyntax OrElse node.Kind = SyntaxKind.Attribute OrElse node.Kind = SyntaxKind.EqualsValue OrElse node.Kind = SyntaxKind.AsNewClause OrElse node.Kind = SyntaxKind.RangeArgument End Function Protected Overrides Function GetSemanticRootOfReplacedExpression(semanticRootOfOriginalExpr As SyntaxNode, annotatedReplacedExpression As ExpressionSyntax) As SyntaxNode Dim originalExpression = Me.OriginalExpression ' Speculation is not supported for AsNewClauseSyntax nodes. ' Generate an EqualsValueSyntax node with the inner NewExpression of the AsNewClauseSyntax node for speculation. If semanticRootOfOriginalExpr.Kind = SyntaxKind.AsNewClause Then ' Because the original expression will change identity in the newly generated EqualsValueSyntax node, ' we annotate it here to allow us to get back to it after replace. Dim originalExprAnnotation = New SyntaxAnnotation() Dim annotatedOriginalExpression = originalExpression.WithAdditionalAnnotations(originalExprAnnotation) semanticRootOfOriginalExpr = semanticRootOfOriginalExpr.ReplaceNode(originalExpression, annotatedOriginalExpression) Dim asNewClauseNode = DirectCast(semanticRootOfOriginalExpr, AsNewClauseSyntax) semanticRootOfOriginalExpr = SyntaxFactory.EqualsValue(asNewClauseNode.NewExpression) semanticRootOfOriginalExpr = asNewClauseNode.CopyAnnotationsTo(semanticRootOfOriginalExpr) originalExpression = DirectCast(semanticRootOfOriginalExpr.GetAnnotatedNodesAndTokens(originalExprAnnotation).Single().AsNode(), ExpressionSyntax) End If Return semanticRootOfOriginalExpr.ReplaceNode(originalExpression, annotatedReplacedExpression) End Function Protected Overrides Sub ValidateSpeculativeSemanticModel(speculativeSemanticModel As SemanticModel, nodeToSpeculate As SyntaxNode) Debug.Assert(speculativeSemanticModel IsNot Nothing OrElse TypeOf nodeToSpeculate Is ExpressionSyntax OrElse Me.SemanticRootOfOriginalExpression.GetAncestors().Any(Function(node) node.IsKind(SyntaxKind.IncompleteMember)), "SemanticModel.TryGetSpeculativeSemanticModel() API returned false.") End Sub Protected Overrides Function CreateSpeculativeSemanticModel(originalNode As SyntaxNode, nodeToSpeculate As SyntaxNode, semanticModel As SemanticModel) As SemanticModel Return CreateSpeculativeSemanticModelForNode(originalNode, nodeToSpeculate, semanticModel) End Function Public Shared Function CreateSpeculativeSemanticModelForNode(originalNode As SyntaxNode, nodeToSpeculate As SyntaxNode, semanticModel As SemanticModel) As SemanticModel Dim position = originalNode.SpanStart Dim isInNamespaceOrTypeContext = SyntaxFacts.IsInNamespaceOrTypeContext(TryCast(originalNode, ExpressionSyntax)) Return CreateSpeculativeSemanticModelForNode(nodeToSpeculate, semanticModel, position, isInNamespaceOrTypeContext) End Function Public Shared Function CreateSpeculativeSemanticModelForNode(nodeToSpeculate As SyntaxNode, semanticModel As SemanticModel, position As Integer, isInNamespaceOrTypeContext As Boolean) As SemanticModel If semanticModel.IsSpeculativeSemanticModel Then ' Chaining speculative model Not supported, speculate off the original model. Debug.Assert(semanticModel.ParentModel IsNot Nothing) Debug.Assert(Not semanticModel.ParentModel.IsSpeculativeSemanticModel) position = semanticModel.OriginalPositionForSpeculation semanticModel = semanticModel.ParentModel End If Dim speculativeModel As SemanticModel = Nothing Dim statementNode = TryCast(nodeToSpeculate, ExecutableStatementSyntax) If statementNode IsNot Nothing Then semanticModel.TryGetSpeculativeSemanticModel(position, statementNode, speculativeModel) Return speculativeModel End If Dim type = TryCast(nodeToSpeculate, TypeSyntax) If type IsNot Nothing Then Dim bindingOption = If(isInNamespaceOrTypeContext, SpeculativeBindingOption.BindAsTypeOrNamespace, SpeculativeBindingOption.BindAsExpression) semanticModel.TryGetSpeculativeSemanticModel(position, type, speculativeModel, bindingOption) Return speculativeModel End If Select Case nodeToSpeculate.Kind Case SyntaxKind.Attribute semanticModel.TryGetSpeculativeSemanticModel(position, DirectCast(nodeToSpeculate, AttributeSyntax), speculativeModel) Return speculativeModel Case SyntaxKind.EqualsValue semanticModel.TryGetSpeculativeSemanticModel(position, DirectCast(nodeToSpeculate, EqualsValueSyntax), speculativeModel) Return speculativeModel Case SyntaxKind.RangeArgument semanticModel.TryGetSpeculativeSemanticModel(position, DirectCast(nodeToSpeculate, RangeArgumentSyntax), speculativeModel) Return speculativeModel Case SyntaxKind.AsNewClause ' Speculation is not supported for AsNewClauseSyntax nodes. ' Generate an EqualsValueSyntax node with the inner NewExpression of the AsNewClauseSyntax node for speculation. Dim asNewClauseNode = DirectCast(nodeToSpeculate, AsNewClauseSyntax) nodeToSpeculate = SyntaxFactory.EqualsValue(asNewClauseNode.NewExpression) nodeToSpeculate = asNewClauseNode.CopyAnnotationsTo(nodeToSpeculate) semanticModel.TryGetSpeculativeSemanticModel(position, DirectCast(nodeToSpeculate, EqualsValueSyntax), speculativeModel) Return speculativeModel End Select ' CONSIDER: Do we care about this case? Debug.Assert(TypeOf nodeToSpeculate Is ExpressionSyntax) Return Nothing End Function #Region "Semantic comparison helpers" Private Function QuerySymbolsAreCompatible(originalNode As CollectionRangeVariableSyntax, newNode As CollectionRangeVariableSyntax) As Boolean Debug.Assert(originalNode IsNot Nothing) Debug.Assert(Me.SemanticRootOfOriginalExpression.DescendantNodesAndSelf().Contains(originalNode)) Debug.Assert(newNode IsNot Nothing) Debug.Assert(Me.SemanticRootOfReplacedExpression.DescendantNodesAndSelf().Contains(newNode)) Dim originalSymbolInfo = Me.OriginalSemanticModel.GetCollectionRangeVariableSymbolInfo(originalNode) Dim newSymbolInfo = Me.SpeculativeSemanticModel.GetCollectionRangeVariableSymbolInfo(newNode) Return SymbolInfosAreCompatible(originalSymbolInfo, newSymbolInfo) End Function Private Function QuerySymbolsAreCompatible(originalNode As AggregateClauseSyntax, newNode As AggregateClauseSyntax) As Boolean Debug.Assert(originalNode IsNot Nothing) Debug.Assert(Me.SemanticRootOfOriginalExpression.DescendantNodesAndSelf().Contains(originalNode)) Debug.Assert(newNode IsNot Nothing) Debug.Assert(Me.SemanticRootOfReplacedExpression.DescendantNodesAndSelf().Contains(newNode)) Dim originalSymbolInfo = Me.OriginalSemanticModel.GetAggregateClauseSymbolInfo(originalNode) Dim newSymbolInfo = Me.SpeculativeSemanticModel.GetAggregateClauseSymbolInfo(newNode) Return SymbolInfosAreCompatible(originalSymbolInfo, newSymbolInfo) End Function Private Function QuerySymbolsAreCompatible(originalNode As ExpressionRangeVariableSyntax, newNode As ExpressionRangeVariableSyntax) As Boolean Debug.Assert(originalNode IsNot Nothing) Debug.Assert(Me.SemanticRootOfOriginalExpression.DescendantNodesAndSelf().Contains(originalNode)) Debug.Assert(newNode IsNot Nothing) Debug.Assert(Me.SemanticRootOfReplacedExpression.DescendantNodesAndSelf().Contains(newNode)) Dim originalSymbolInfo = Me.OriginalSemanticModel.GetSymbolInfo(originalNode) Dim newSymbolInfo = Me.SpeculativeSemanticModel.GetSymbolInfo(newNode) Return SymbolInfosAreCompatible(originalSymbolInfo, newSymbolInfo) End Function Private Function QuerySymbolsAreCompatible(originalNode As OrderingSyntax, newNode As OrderingSyntax) As Boolean Debug.Assert(originalNode IsNot Nothing) Debug.Assert(Me.SemanticRootOfOriginalExpression.DescendantNodesAndSelf().Contains(originalNode)) Debug.Assert(newNode IsNot Nothing) Debug.Assert(Me.SemanticRootOfReplacedExpression.DescendantNodesAndSelf().Contains(newNode)) Dim originalSymbolInfo = Me.OriginalSemanticModel.GetSymbolInfo(originalNode) Dim newSymbolInfo = Me.SpeculativeSemanticModel.GetSymbolInfo(newNode) Return SymbolInfosAreCompatible(originalSymbolInfo, newSymbolInfo) End Function Private Function QuerySymbolsAreCompatible(originalNode As QueryClauseSyntax, newNode As QueryClauseSyntax) As Boolean Debug.Assert(originalNode IsNot Nothing) Debug.Assert(Me.SemanticRootOfOriginalExpression.DescendantNodesAndSelf().Contains(originalNode)) Debug.Assert(newNode IsNot Nothing) Debug.Assert(Me.SemanticRootOfReplacedExpression.DescendantNodesAndSelf().Contains(newNode)) Dim originalSymbolInfo = Me.OriginalSemanticModel.GetSymbolInfo(originalNode) Dim newSymbolInfo = Me.SpeculativeSemanticModel.GetSymbolInfo(newNode) Return SymbolInfosAreCompatible(originalSymbolInfo, newSymbolInfo) End Function Private Overloads Function SymbolInfosAreCompatible(originalSymbolInfo As CollectionRangeVariableSymbolInfo, newSymbolInfo As CollectionRangeVariableSymbolInfo) As Boolean Return SymbolInfosAreCompatible(originalSymbolInfo.ToQueryableCollectionConversion, newSymbolInfo.ToQueryableCollectionConversion) AndAlso SymbolInfosAreCompatible(originalSymbolInfo.AsClauseConversion, newSymbolInfo.AsClauseConversion) AndAlso SymbolInfosAreCompatible(originalSymbolInfo.SelectMany, newSymbolInfo.SelectMany) End Function Private Overloads Function SymbolInfosAreCompatible(originalSymbolInfo As AggregateClauseSymbolInfo, newSymbolInfo As AggregateClauseSymbolInfo) As Boolean Return SymbolInfosAreCompatible(originalSymbolInfo.Select1, newSymbolInfo.Select1) AndAlso SymbolInfosAreCompatible(originalSymbolInfo.Select2, newSymbolInfo.Select2) End Function #End Region ''' <summary> ''' Determines whether performing the syntax replacement in one of the sibling nodes of the given lambda expressions will change the lambda binding semantics. ''' This is done by first determining the lambda parameters whose type differs in the replaced lambda node. ''' For each of these parameters, we find the descendant identifier name nodes in the lambda body and check if semantics of any of the parenting nodes of these ''' identifier nodes have changed in the replaced lambda. ''' </summary> Public Function ReplacementChangesSemanticsOfUnchangedLambda(originalLambda As ExpressionSyntax, replacedLambda As ExpressionSyntax) As Boolean originalLambda = originalLambda.WalkDownParentheses() replacedLambda = replacedLambda.WalkDownParentheses() Dim originalLambdaBody As SyntaxNode, replacedLambdaBody As SyntaxNode Dim originalParams As SeparatedSyntaxList(Of ParameterSyntax), replacedParams As SeparatedSyntaxList(Of ParameterSyntax) Select Case originalLambda.Kind Case SyntaxKind.SingleLineFunctionLambdaExpression, SyntaxKind.SingleLineSubLambdaExpression Dim originalSingleLineLambda = DirectCast(originalLambda, SingleLineLambdaExpressionSyntax) Dim replacedSingleLineLambda = DirectCast(replacedLambda, SingleLineLambdaExpressionSyntax) originalParams = originalSingleLineLambda.SubOrFunctionHeader.ParameterList.Parameters replacedParams = replacedSingleLineLambda.SubOrFunctionHeader.ParameterList.Parameters originalLambdaBody = originalSingleLineLambda.Body replacedLambdaBody = replacedSingleLineLambda.Body Case SyntaxKind.MultiLineFunctionLambdaExpression, SyntaxKind.MultiLineSubLambdaExpression Dim originalMultiLineLambda = DirectCast(originalLambda, MultiLineLambdaExpressionSyntax) Dim replacedMultiLineLambda = DirectCast(replacedLambda, MultiLineLambdaExpressionSyntax) originalParams = originalMultiLineLambda.SubOrFunctionHeader.ParameterList.Parameters replacedParams = replacedMultiLineLambda.SubOrFunctionHeader.ParameterList.Parameters originalLambdaBody = originalMultiLineLambda replacedLambdaBody = replacedMultiLineLambda Case Else Throw ExceptionUtilities.UnexpectedValue(originalLambda.Kind) End Select Debug.Assert(originalParams.Count = replacedParams.Count) If Not originalParams.Any() Then Return False End If Dim paramNames = New List(Of String)() For i As Integer = 0 To originalParams.Count - 1 Dim originalParam = originalParams(i) Dim replacedParam = replacedParams(i) If Not HaveSameParameterType(originalParam, replacedParam) Then paramNames.Add(originalParam.Identifier.Identifier.ValueText) End If Next If Not paramNames.Any() Then Return False End If Dim originalIdentifierNodes = originalLambdaBody _ .DescendantNodes() _ .OfType(Of IdentifierNameSyntax)() _ .Where(Function(node) paramNames.Contains(node.Identifier.ValueText)) If Not originalIdentifierNodes.Any() Then Return False End If Dim replacedIdentifierNodes = replacedLambdaBody _ .DescendantNodes() _ .OfType(Of IdentifierNameSyntax)() _ .Where(Function(node) paramNames.Contains(node.Identifier.ValueText)) Return ReplacementChangesSemanticsForNodes(originalIdentifierNodes, replacedIdentifierNodes, originalLambdaBody) End Function Private Function HaveSameParameterType(originalParam As ParameterSyntax, replacedParam As ParameterSyntax) As Boolean Dim originalParamType = Me.OriginalSemanticModel.GetDeclaredSymbol(originalParam).Type Dim replacedParamType = Me.SpeculativeSemanticModel.GetDeclaredSymbol(replacedParam).Type Return Equals(originalParamType, replacedParamType) End Function Private Function ReplacementChangesSemanticsForNodes( originalIdentifierNodes As IEnumerable(Of IdentifierNameSyntax), replacedIdentifierNodes As IEnumerable(Of IdentifierNameSyntax), originalRoot As SyntaxNode) As Boolean Debug.Assert(originalIdentifierNodes.Any()) Debug.Assert(originalIdentifierNodes.Count() = replacedIdentifierNodes.Count()) Dim originalChildNodeEnum = originalIdentifierNodes.GetEnumerator() Dim replacedChildNodeEnum = replacedIdentifierNodes.GetEnumerator() While originalChildNodeEnum.MoveNext() replacedChildNodeEnum.MoveNext() If ReplacementChangesSemantics(originalChildNodeEnum.Current, replacedChildNodeEnum.Current, originalRoot, skipVerificationForCurrentNode:=True) Then Return True End If End While Return False End Function Protected Overrides Function ReplacementChangesSemanticsForNodeLanguageSpecific(currentOriginalNode As SyntaxNode, currentReplacedNode As SyntaxNode, previousOriginalNode As SyntaxNode, previousReplacedNode As SyntaxNode) As Boolean Debug.Assert(previousOriginalNode Is Nothing OrElse previousOriginalNode.Parent Is currentOriginalNode) Debug.Assert(previousReplacedNode Is Nothing OrElse previousReplacedNode.Parent Is currentReplacedNode) If TypeOf currentOriginalNode Is BinaryExpressionSyntax Then ' If replacing the node will result in a broken binary expression, we won't remove it. Dim originalExpression = DirectCast(currentOriginalNode, BinaryExpressionSyntax) Dim newExpression = DirectCast(currentReplacedNode, BinaryExpressionSyntax) If ReplacementBreaksBinaryExpression(originalExpression, newExpression) Then Return True End If Return Not ImplicitConversionsAreCompatible(originalExpression, newExpression) ElseIf TypeOf currentOriginalNode Is AssignmentStatementSyntax Then Dim originalAssignmentStatement = DirectCast(currentOriginalNode, AssignmentStatementSyntax) If SyntaxFacts.IsAssignmentStatementOperatorToken(originalAssignmentStatement.OperatorToken.Kind()) Then Dim newAssignmentStatement = DirectCast(currentReplacedNode, AssignmentStatementSyntax) If ReplacementBreaksCompoundAssignment(originalAssignmentStatement.Left, originalAssignmentStatement.Right, newAssignmentStatement.Left, newAssignmentStatement.Right) Then Return True End If End If ElseIf currentOriginalNode.Kind = SyntaxKind.ConditionalAccessExpression Then Dim originalExpression = DirectCast(currentOriginalNode, ConditionalAccessExpressionSyntax) Dim newExpression = DirectCast(currentReplacedNode, ConditionalAccessExpressionSyntax) Return ReplacementBreaksConditionalAccessExpression(originalExpression, newExpression) ElseIf currentOriginalNode.Kind = SyntaxKind.VariableDeclarator Then ' Heuristic: If replacing the node will result in changing the type of a local variable ' that is type-inferred, we won't remove it. It's possible to do this analysis, but it's ' very expensive and the benefit to the user is small. Dim originalDeclarator = DirectCast(currentOriginalNode, VariableDeclaratorSyntax) Dim newDeclarator = DirectCast(currentReplacedNode, VariableDeclaratorSyntax) If originalDeclarator.IsTypeInferred(Me.OriginalSemanticModel) AndAlso Not ConvertedTypesAreCompatible(originalDeclarator.Initializer.Value, newDeclarator.Initializer.Value) Then Return True End If Return False ElseIf currentOriginalNode.Kind = SyntaxKind.CollectionInitializer Then Return _ previousOriginalNode IsNot Nothing AndAlso ReplacementBreaksCollectionInitializerAddMethod(DirectCast(previousOriginalNode, ExpressionSyntax), DirectCast(previousReplacedNode, ExpressionSyntax)) ElseIf currentOriginalNode.Kind = SyntaxKind.Interpolation Then Dim orignalInterpolation = DirectCast(currentOriginalNode, InterpolationSyntax) Dim newInterpolation = DirectCast(currentReplacedNode, InterpolationSyntax) Return ReplacementBreaksInterpolation(orignalInterpolation, newInterpolation) ElseIf currentOriginalNode.Kind = SyntaxKind.WithStatement Then Dim originalWithStatement = DirectCast(currentOriginalNode, WithStatementSyntax) Dim newWithStatement = DirectCast(currentReplacedNode, WithStatementSyntax) Return ReplacementBreaksWithStatement(originalWithStatement, newWithStatement) Else Dim originalCollectionRangeVariableSyntax = TryCast(currentOriginalNode, CollectionRangeVariableSyntax) If originalCollectionRangeVariableSyntax IsNot Nothing Then Dim newCollectionRangeVariableSyntax = DirectCast(currentReplacedNode, CollectionRangeVariableSyntax) Return Not QuerySymbolsAreCompatible(originalCollectionRangeVariableSyntax, newCollectionRangeVariableSyntax) End If Dim originalAggregateSyntax = TryCast(currentOriginalNode, AggregateClauseSyntax) If originalAggregateSyntax IsNot Nothing Then Dim newAggregateSyntax = DirectCast(currentReplacedNode, AggregateClauseSyntax) Return Not QuerySymbolsAreCompatible(originalAggregateSyntax, newAggregateSyntax) End If Dim originalExprRangeVariableSyntax = TryCast(currentOriginalNode, ExpressionRangeVariableSyntax) If originalExprRangeVariableSyntax IsNot Nothing Then Dim newExprRangeVariableSyntax = DirectCast(currentReplacedNode, ExpressionRangeVariableSyntax) Return Not QuerySymbolsAreCompatible(originalExprRangeVariableSyntax, newExprRangeVariableSyntax) End If Dim originalFunctionAggregationSyntax = TryCast(currentOriginalNode, FunctionAggregationSyntax) If originalFunctionAggregationSyntax IsNot Nothing Then Dim newFunctionAggregationSyntax = DirectCast(currentReplacedNode, FunctionAggregationSyntax) Return Not SymbolsAreCompatible(originalFunctionAggregationSyntax, newFunctionAggregationSyntax) End If Dim originalOrderingSyntax = TryCast(currentOriginalNode, OrderingSyntax) If originalOrderingSyntax IsNot Nothing Then Dim newOrderingSyntax = DirectCast(currentReplacedNode, OrderingSyntax) Return Not QuerySymbolsAreCompatible(originalOrderingSyntax, newOrderingSyntax) End If Dim originalQueryClauseSyntax = TryCast(currentOriginalNode, QueryClauseSyntax) If originalQueryClauseSyntax IsNot Nothing Then Dim newQueryClauseSyntax = DirectCast(currentReplacedNode, QueryClauseSyntax) Return Not QuerySymbolsAreCompatible(originalQueryClauseSyntax, newQueryClauseSyntax) End If End If Return False End Function Private Function ReplacementBreaksWithStatement(originalWithStatement As WithStatementSyntax, replacedWithStatement As WithStatementSyntax) As Boolean Dim originalTypeInfo = Me.OriginalSemanticModel.GetTypeInfo(originalWithStatement.Expression) Dim replacedTypeInfo = Me.SpeculativeSemanticModel.GetTypeInfo(replacedWithStatement.Expression) Return Not originalTypeInfo.Equals(replacedTypeInfo) End Function Private Function ReplacementBreaksCollectionInitializerAddMethod(originalInitializer As ExpressionSyntax, newInitializer As ExpressionSyntax) As Boolean Dim originalSymbol = Me.OriginalSemanticModel.GetCollectionInitializerSymbolInfo(originalInitializer, CancellationToken).Symbol Dim newSymbol = Me.SpeculativeSemanticModel.GetCollectionInitializerSymbolInfo(newInitializer, CancellationToken).Symbol Return Not SymbolsAreCompatible(originalSymbol, newSymbol) End Function Protected Overrides Function IsForEachTypeInferred(forEachStatement As ForEachStatementSyntax, semanticModel As SemanticModel) As Boolean Dim forEachControlVariable = TryCast(forEachStatement.ControlVariable, VariableDeclaratorSyntax) Return forEachControlVariable IsNot Nothing AndAlso forEachControlVariable.IsTypeInferred(Me.OriginalSemanticModel) End Function Protected Overrides Function ExpressionMightReferenceMember(node As SyntaxNode) As Boolean Return node.IsKind(SyntaxKind.InvocationExpression) OrElse node.IsKind(SyntaxKind.SimpleMemberAccessExpression) End Function Protected Overrides Function GetReceiver(expression As ExpressionSyntax) As ExpressionSyntax expression = expression.WalkDownParentheses() Select Case expression.Kind Case SyntaxKind.SimpleMemberAccessExpression Return DirectCast(expression, MemberAccessExpressionSyntax).Expression.WalkDownParentheses() Case SyntaxKind.InvocationExpression Dim result = DirectCast(expression, InvocationExpressionSyntax).Expression.WalkDownParentheses() If result.IsKind(SyntaxKind.SimpleMemberAccessExpression) Then Return GetReceiver(result) End If Return result Case Else Return Nothing End Select End Function Protected Overrides Function IsNamedArgument(argument As ArgumentSyntax) As Boolean Return argument.IsNamed End Function Protected Overrides Function GetNamedArgumentIdentifierValueText(argument As ArgumentSyntax) As String Return DirectCast(argument, SimpleArgumentSyntax).NameColonEquals.Name.Identifier.ValueText End Function Protected Overrides Function GetArguments(expression As ExpressionSyntax) As ImmutableArray(Of ArgumentSyntax) Dim argumentList = GetArgumentList(expression) Return If(argumentList IsNot Nothing, argumentList.Arguments.AsImmutable(), Nothing) End Function Private Shared Function GetArgumentList(expression As ExpressionSyntax) As ArgumentListSyntax expression = expression.WalkDownParentheses() Select Case expression.Kind Case SyntaxKind.InvocationExpression Return DirectCast(expression, InvocationExpressionSyntax).ArgumentList Case SyntaxKind.ObjectCreationExpression Return DirectCast(expression, ObjectCreationExpressionSyntax).ArgumentList Case Else Return Nothing End Select End Function Private Function ReplacementBreaksBinaryExpression(binaryExpression As BinaryExpressionSyntax, newBinaryExpression As BinaryExpressionSyntax) As Boolean Dim operatorTokenKind = binaryExpression.OperatorToken.Kind If SyntaxFacts.IsAssignmentStatementOperatorToken(operatorTokenKind) AndAlso operatorTokenKind <> SyntaxKind.LessThanLessThanEqualsToken AndAlso operatorTokenKind <> SyntaxKind.GreaterThanGreaterThanEqualsToken AndAlso ReplacementBreaksCompoundAssignment(binaryExpression.Left, binaryExpression.Right, newBinaryExpression.Left, newBinaryExpression.Right) Then Return True End If Return Not SymbolsAreCompatible(binaryExpression, newBinaryExpression) OrElse Not TypesAreCompatible(binaryExpression, newBinaryExpression) End Function Private Function ReplacementBreaksConditionalAccessExpression(conditionalAccessExpression As ConditionalAccessExpressionSyntax, newConditionalAccessExpression As ConditionalAccessExpressionSyntax) As Boolean Return _ Not SymbolsAreCompatible(conditionalAccessExpression, newConditionalAccessExpression) OrElse Not TypesAreCompatible(conditionalAccessExpression, newConditionalAccessExpression) OrElse Not SymbolsAreCompatible(conditionalAccessExpression.WhenNotNull, newConditionalAccessExpression.WhenNotNull) OrElse Not TypesAreCompatible(conditionalAccessExpression.WhenNotNull, newConditionalAccessExpression.WhenNotNull) End Function Private Function ReplacementBreaksInterpolation(interpolation As InterpolationSyntax, newInterpolation As InterpolationSyntax) As Boolean Return Not TypesAreCompatible(interpolation.Expression, newInterpolation.Expression) End Function Protected Overrides Function GetForEachStatementExpression(forEachStatement As ForEachStatementSyntax) As ExpressionSyntax Return forEachStatement.Expression End Function Protected Overrides Function GetThrowStatementExpression(throwStatement As ThrowStatementSyntax) As ExpressionSyntax Return throwStatement.Expression End Function Protected Overrides Function IsInNamespaceOrTypeContext(node As ExpressionSyntax) As Boolean Return SyntaxFacts.IsInNamespaceOrTypeContext(node) End Function Protected Overrides Function IsParenthesizedExpression(node As SyntaxNode) As Boolean Return node.IsKind(SyntaxKind.ParenthesizedExpression) End Function Protected Overrides Function ConversionsAreCompatible(originalModel As SemanticModel, originalExpression As ExpressionSyntax, newModel As SemanticModel, newExpression As ExpressionSyntax) As Boolean Return ConversionsAreCompatible(originalModel.GetConversion(originalExpression), newModel.GetConversion(newExpression)) End Function Protected Overrides Function ConversionsAreCompatible(originalExpression As ExpressionSyntax, originalTargetType As ITypeSymbol, newExpression As ExpressionSyntax, newTargetType As ITypeSymbol) As Boolean Dim originalConversion As Conversion? Dim newConversion As Conversion? Me.GetConversions(originalExpression, originalTargetType, newExpression, newTargetType, originalConversion, newConversion) If originalConversion Is Nothing OrElse newConversion Is Nothing Then Return False End If ' When Option Strict is not Off and the new expression has a constant value, it's possible that ' there Is a hidden narrowing conversion that will be missed. In that case, use the ' conversion between the type of the new expression and the new target type. If Me.OriginalSemanticModel.OptionStrict() <> OptionStrict.Off AndAlso Me.SpeculativeSemanticModel.GetConstantValue(newExpression).HasValue Then Dim newExpressionType = Me.SpeculativeSemanticModel.GetTypeInfo(newExpression).ConvertedType newConversion = Me.OriginalSemanticModel.Compilation.ClassifyConversion(newExpressionType, newTargetType) End If Return ConversionsAreCompatible(originalConversion.Value, newConversion.Value) End Function Private Overloads Function ConversionsAreCompatible(originalConversion As Conversion, newConversion As Conversion) As Boolean If originalConversion.Exists <> newConversion.Exists OrElse ((Not originalConversion.IsNarrowing) AndAlso newConversion.IsNarrowing) Then Return False End If Dim originalIsUserDefined = originalConversion.IsUserDefined Dim newIsUserDefined = newConversion.IsUserDefined If (originalIsUserDefined <> newIsUserDefined) Then Return False End If If (originalIsUserDefined OrElse originalConversion.MethodSymbol IsNot Nothing OrElse newConversion.MethodSymbol IsNot Nothing) Then Return SymbolsAreCompatible(originalConversion.MethodSymbol, newConversion.MethodSymbol) End If Return True End Function Protected Overrides Function ForEachConversionsAreCompatible(originalModel As SemanticModel, originalForEach As ForEachStatementSyntax, newModel As SemanticModel, newForEach As ForEachStatementSyntax) As Boolean Dim originalInfo = originalModel.GetForEachStatementInfo(originalForEach) Dim newInfo = newModel.GetForEachStatementInfo(newForEach) Return ConversionsAreCompatible(originalInfo.CurrentConversion, newInfo.CurrentConversion) AndAlso ConversionsAreCompatible(originalInfo.ElementConversion, newInfo.ElementConversion) End Function Protected Overrides Sub GetForEachSymbols( model As SemanticModel, forEach As ForEachStatementSyntax, ByRef getEnumeratorMethod As IMethodSymbol, ByRef elementType As ITypeSymbol, ByRef localVariables As ImmutableArray(Of ILocalSymbol)) Dim info = model.GetForEachStatementInfo(forEach) getEnumeratorMethod = info.GetEnumeratorMethod elementType = info.ElementType localVariables = ImmutableArray.Create(DirectCast(model.GetDeclaredSymbol(forEach), ILocalSymbol)) End Sub Protected Overrides Function IsReferenceConversion(compilation As Compilation, sourceType As ITypeSymbol, targetType As ITypeSymbol) As Boolean Return compilation.ClassifyConversion(sourceType, targetType).IsReference End Function Protected Overrides Function ClassifyConversion(model As SemanticModel, expression As ExpressionSyntax, targetType As ITypeSymbol) As Conversion Return model.ClassifyConversion(expression, targetType) End Function Protected Overrides Function ClassifyConversion(model As SemanticModel, originalType As ITypeSymbol, targetType As ITypeSymbol) As Conversion Return model.Compilation.ClassifyConversion(originalType, targetType) End Function End Class End Namespace