/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Analyzers/CSharp/CodeFixes/AssignOutParameters/AbstractAssignOutParametersCodeFixProvider.cs
166 строк
6 KB
Cyrus Najmabadi
Use WhereAsArray
05 авг 2025, 13:55
05 авг 2025, 13:55
ed8dfdd
Код
Авторство
О чём код?
// 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. using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.CodeGeneration; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.AssignOutParameters; internal abstract class AbstractAssignOutParametersCodeFixProvider : SyntaxEditorBasedCodeFixProvider { private const string CS0177 = nameof(CS0177); // The out parameter 'x' must be assigned to before control leaves the current method public sealed override ImmutableArray<string> FixableDiagnosticIds { get; } = [CS0177]; public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var cancellationToken = context.CancellationToken; var document = context.Document; var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var (container, location) = GetContainer(root, context.Span); if (container != null) { var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var dataFlow = semanticModel.AnalyzeDataFlow(location); if (dataFlow.Succeeded) { TryRegisterFix(context, document, container, location); } } } protected abstract void TryRegisterFix(CodeFixContext context, Document document, SyntaxNode container, SyntaxNode location); private static (SyntaxNode container, SyntaxNode exprOrStatement) GetContainer(SyntaxNode root, TextSpan span) { var location = root.FindNode(span); if (IsValidLocation(location)) { var container = GetContainer(location); if (container != null) { return (container, location); } } return default; } private static bool IsValidLocation(SyntaxNode location) { if (location is StatementSyntax) { return location.Parent is BlockSyntax || location.Parent is SwitchSectionSyntax || location.Parent.IsEmbeddedStatementOwner(); } if (location is ExpressionSyntax) { return location.Parent is ArrowExpressionClauseSyntax or LambdaExpressionSyntax; } return false; } private static SyntaxNode? GetContainer(SyntaxNode node) { for (var current = node; current != null; current = current.Parent) { var parameterList = current.GetParameterList(); if (parameterList != null) { return current; } } return null; } private static async Task<MultiDictionary<SyntaxNode, (SyntaxNode exprOrStatement, ImmutableArray<IParameterSymbol>)>> GetUnassignedParametersAsync( Document document, ImmutableArray<Diagnostic> diagnostics, CancellationToken cancellationToken) { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var containersAndLocations = diagnostics.SelectAsArray(d => GetContainer(root, d.Location.SourceSpan)) .WhereAsArray(t => t.container != null); var result = new MultiDictionary<SyntaxNode, (SyntaxNode exprOrStatement, ImmutableArray<IParameterSymbol>)>(); foreach (var group in containersAndLocations.GroupBy(t => t.container)) { var container = group.Key; var parameterList = container.GetParameterList(); Contract.ThrowIfNull(parameterList); var outParameters = parameterList.Parameters .Select(p => semanticModel.GetRequiredDeclaredSymbol(p, cancellationToken)) .WhereAsArray(p => p.RefKind == RefKind.Out); var distinctExprsOrStatements = group.Select(t => t.exprOrStatement).Distinct(); foreach (var exprOrStatement in distinctExprsOrStatements) { var dataFlow = semanticModel.AnalyzeDataFlow(exprOrStatement); var unassignedParameters = outParameters.WhereAsArray( p => !dataFlow.DefinitelyAssignedOnExit.Contains(p)); if (unassignedParameters.Length > 0) { result.Add(container, (exprOrStatement, unassignedParameters)); } } } return result; } protected sealed override async Task FixAllAsync( Document document, ImmutableArray<Diagnostic> diagnostics, SyntaxEditor editor, CancellationToken cancellationToken) { var unassignedParameters = await GetUnassignedParametersAsync( document, diagnostics, cancellationToken).ConfigureAwait(false); foreach (var container in unassignedParameters.Keys.OrderByDescending(n => n.Span.Start)) { AssignOutParameters( editor, container, unassignedParameters[container], cancellationToken); } } protected abstract void AssignOutParameters( SyntaxEditor editor, SyntaxNode container, MultiDictionary<SyntaxNode, (SyntaxNode exprOrStatement, ImmutableArray<IParameterSymbol> unassignedParameters)>.ValueSet values, CancellationToken cancellationToken); protected static ImmutableArray<SyntaxNode> GenerateAssignmentStatements( SyntaxGenerator generator, ImmutableArray<IParameterSymbol> unassignedParameters) { var result = ArrayBuilder<SyntaxNode>.GetInstance(); foreach (var parameter in unassignedParameters) { result.Add(generator.ExpressionStatement(generator.AssignmentStatement( generator.IdentifierName(parameter.Name), ExpressionGenerator.GenerateExpression(parameter.Type, value: null, canUseFieldReference: false)))); } return result.ToImmutableAndFree(); } }