/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Analyzers/VisualBasic/CodeFixes/AddExplicitCast/VisualBasicAddExplicitCastCodeFixProvider.vb
199 строк
10 KB
Cyrus Najmabadi
Simplify ancestor checks
22 июл 2025, 15:14
22 июл 2025, 15:14
23b7465
Код
Авторство
О чём код?
' 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.Composition Imports System.Diagnostics.CodeAnalysis Imports System.Threading Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.CodeFixes.AddExplicitCast Imports Microsoft.CodeAnalysis.LanguageService Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.AddExplicitCast <ExportCodeFixProvider(LanguageNames.VisualBasic, Name:=PredefinedCodeFixProviderNames.AddExplicitCast), [Shared]> Partial Friend NotInheritable Class VisualBasicAddExplicitCastCodeFixProvider Inherits AbstractAddExplicitCastCodeFixProvider(Of ExpressionSyntax) Friend Const BC30512 As String = "BC30512" ' Option Strict On disallows implicit conversions from '{0}' to '{1}'. Friend Const BC42016 As String = "BC42016" ' Implicit conversions from '{0}' to '{1}'. Friend Const BC30518 As String = "BC30518" ' Overload resolution failed because no accessible 'sub1' can be called with these arguments. Friend Const BC30519 As String = "BC30519" ' Overload resolution failed because no accessible 'sub1' can be called without a narrowing conversion. Private ReadOnly _fixer As ArgumentFixer <ImportingConstructor> <SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification:="Used in test code: https://github.com/dotnet/roslyn/issues/42814")> Public Sub New() _fixer = New ArgumentFixer() End Sub Public Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String) = ImmutableArray.Create(BC30512, BC42016, BC30518, BC30519) Protected Overrides Sub GetPartsOfCastOrConversionExpression( expression As ExpressionSyntax, ByRef type As SyntaxNode, ByRef castedExpression As ExpressionSyntax) Dim directCastExpression = TryCast(expression, DirectCastExpressionSyntax) If directCastExpression IsNot Nothing Then type = directCastExpression.Type castedExpression = directCastExpression.Expression Return End If Dim conversionExpression = DirectCast(expression, CTypeExpressionSyntax) type = conversionExpression.Type castedExpression = conversionExpression.Expression End Sub Protected Overrides Function Cast(expression As ExpressionSyntax, type As ITypeSymbol) As ExpressionSyntax Return expression.Cast(type, isResultPredefinedCast:=Nothing) End Function Protected Overrides Sub AddPotentialTargetTypes( document As Document, semanticModel As SemanticModel, root As SyntaxNode, diagnosticId As String, spanNode As ExpressionSyntax, candidates As ArrayBuilder(Of (node As ExpressionSyntax, type As ITypeSymbol)), cancellationToken As CancellationToken) Select Case diagnosticId Case BC30512, BC42016 Dim argument = spanNode.GetAncestor(Of ArgumentSyntax) If argument IsNot Nothing AndAlso argument.GetExpression.Equals(spanNode) Then ' spanNode is an argument expression Dim argumentList = DirectCast(argument.Parent, ArgumentListSyntax) Dim invocationNode = argumentList.Parent candidates.AddRange(_fixer.GetPotentialConversionTypes( document, semanticModel, root, argument, argumentList, invocationNode, cancellationToken)) Else ' spanNode is a right expression in assignment operation Dim inferenceService = document.GetRequiredLanguageService(Of ITypeInferenceService)() Dim conversionType = inferenceService.InferType(semanticModel, spanNode, objectAsDefault:=False, cancellationToken) candidates.Add((spanNode, conversionType)) End If Case BC30518, BC30519 Dim invocationExpressionNode = spanNode.GetAncestors(Of InvocationExpressionSyntax).FirstOrDefault( Function(node) node.ArgumentList IsNot Nothing) Dim attributeNode = spanNode.GetAncestors(Of AttributeSyntax).FirstOrDefault( Function(node) node.ArgumentList IsNot Nothing) ' Collect available cast pairs without target argument If invocationExpressionNode IsNot Nothing Then candidates.AddRange( GetPotentialConversionTypesWithInvocationNode(document, semanticModel, root, invocationExpressionNode, cancellationToken)) ElseIf attributeNode IsNot Nothing Then candidates.AddRange( GetPotentialConversionTypesWithInvocationNode(document, semanticModel, root, attributeNode, cancellationToken)) End If End Select End Sub ''' <summary> ''' Find the first argument that need to be cast ''' </summary> ''' <param name="parameters"> The parameters of method</param> ''' <param name="arguments"> The arguments of invocation node</param> ''' <returns> ''' Return the first argument that need to be cast, could be null if such argument doesn't exist ''' </returns> Private Shared Function GetTargetArgument( document As Document, semanticModel As SemanticModel, parameters As ImmutableArray(Of IParameterSymbol), arguments As SeparatedSyntaxList(Of ArgumentSyntax)) As ArgumentSyntax If parameters.Length = 0 Then Return Nothing End If Dim syntaxFacts = document.GetRequiredLanguageService(Of ISyntaxFactsService)() For i = 0 To arguments.Count - 1 ' Parameter index cannot out of its range, #arguments Is larger than #parameter only if ' the last parameter with keyword params Dim parameterIndex = Math.Min(i, parameters.Length - 1) ' If the argument has a name, get the corresponding parameter index Dim argumentName = syntaxFacts.GetNameForArgument(arguments(i)) If argumentName IsNot String.Empty AndAlso Not FindCorrespondingParameterByName(argumentName, parameters, parameterIndex) Then Return Nothing End If Dim argumentExpression = arguments(i).GetExpression If argumentExpression Is Nothing Then Continue For End If Dim parameterType = parameters(parameterIndex).Type If parameters(parameterIndex).IsParams Then Dim paramsType = TryCast(parameterType, IArrayTypeSymbol) If paramsType IsNot Nothing Then Dim conversion = semanticModel.ClassifyConversion(argumentExpression, paramsType.ElementType) If conversion.Exists AndAlso Not conversion.IsIdentity Then Return arguments(i) End If End If End If Dim argumentConversion = semanticModel.ClassifyConversion(argumentExpression, parameterType) If argumentConversion.Exists AndAlso Not argumentConversion.IsIdentity Then Return arguments(i) End If Next Return Nothing End Function ''' <summary> ''' Collect available cast pairs without target argument. ''' For each method, the first argument need to be cast is the target argument. ''' Return format is (argument expression, potential conversion type). ''' </summary> ''' <param name="invocationNode">The invocation node that contains some arguments need to be cast</param> ''' <returns> ''' Return all the available cast pairs, format is (argument expression, potential conversion type) ''' </returns> Private Function GetPotentialConversionTypesWithInvocationNode( document As Document, semanticModel As SemanticModel, root As SyntaxNode, invocationNode As SyntaxNode, cancellationToken As CancellationToken) As ImmutableArray(Of (ExpressionSyntax, ITypeSymbol)) Dim argumentList = invocationNode.ChildNodes.OfType(Of ArgumentListSyntax).FirstOrDefault() Dim symbolInfo = semanticModel.GetSymbolInfo(invocationNode, cancellationToken) Dim candidateSymbols = symbolInfo.CandidateSymbols Dim mutablePotentialConversionTypes = ArrayBuilder(Of (ExpressionSyntax, ITypeSymbol)).GetInstance() For Each candidateSymbol In candidateSymbols.OfType(Of IMethodSymbol)() Dim targetArgument = GetTargetArgument(document, semanticModel, candidateSymbol.Parameters, argumentList.Arguments) If targetArgument Is Nothing Then Continue For End If Dim conversionType As ITypeSymbol = Nothing If _fixer.CanArgumentTypesBeConvertedToParameterTypes( document, semanticModel, root, argumentList, candidateSymbol.Parameters, targetArgument, cancellationToken, conversionType) Then mutablePotentialConversionTypes.Add((targetArgument.GetExpression(), conversionType)) End If Next ' Sort the potential conversion types by inheritance distance, so that ' operations are in order And user can choose least specific types(more accurate) mutablePotentialConversionTypes.Sort(New InheritanceDistanceComparer(Of ExpressionSyntax)(semanticModel)) Return mutablePotentialConversionTypes.ToImmutable() End Function End Class End Namespace