/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Analyzers/CSharp/CodeFixes/UseCompoundAssignment/CSharpUseCompoundCoalesceAssignmentCodeFixProvider.cs
119 строк
6 KB
Cyrus Najmabadi
Reapply "Update methods to be `async`." (#81808)
31 дек 2025, 02:04
31 дек 2025, 02:04
ba729ad
Код
Авторство
О чём код?
// 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.Composition; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseCompoundAssignment; using static CSharpSyntaxTokens; using static SyntaxFactory; [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseCompoundCoalesceAssignment), Shared] [method: ImportingConstructor] [method: SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] internal sealed class CSharpUseCompoundCoalesceAssignmentCodeFixProvider() : SyntaxEditorBasedCodeFixProvider { public override ImmutableArray<string> FixableDiagnosticIds { get; } = [IDEDiagnosticIds.UseCoalesceCompoundAssignmentDiagnosticId]; public override async Task RegisterCodeFixesAsync(CodeFixContext context) { RegisterCodeFix(context, AnalyzersResources.Use_compound_assignment, nameof(AnalyzersResources.Use_compound_assignment)); } protected override async Task FixAllAsync( Document document, ImmutableArray<Diagnostic> diagnostics, SyntaxEditor editor, CancellationToken cancellationToken) { var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var syntaxFacts = document.GetRequiredLanguageService<ISyntaxFactsService>(); var syntaxKinds = syntaxFacts.SyntaxKinds; foreach (var diagnostic in diagnostics) { var coalesceOrIfStatement = diagnostic.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken); if (coalesceOrIfStatement is IfStatementSyntax ifStatement) { Contract.ThrowIfFalse(CSharpUseCompoundCoalesceAssignmentDiagnosticAnalyzer.GetWhenTrueAssignment( ifStatement, out var whenTrueStatement, out var assignment)); // we have `if (x is null) x = y;`. Update `x = y` to be `x ??= y`, then replace the entire // if-statement with that assignment statement. var newAssignment = AssignmentExpression( SyntaxKind.CoalesceAssignmentExpression, assignment.Left, QuestionQuestionEqualsToken.WithTriviaFrom(assignment.OperatorToken), assignment.Right).WithTriviaFrom(assignment); var newWhenTrueStatement = whenTrueStatement.ReplaceNode(assignment, newAssignment); // If there's leading trivia on the original inner statement, then combine that with the leading // trivia on the if-statement. We'll need to add a formatting annotation so that the leading comments // are put in the right location. if (newWhenTrueStatement.GetLeadingTrivia().Any(t => t.IsSingleOrMultiLineComment())) { newWhenTrueStatement = newWhenTrueStatement .WithPrependedLeadingTrivia(ifStatement.GetLeadingTrivia()) .WithAdditionalAnnotations(Formatter.Annotation); } else { newWhenTrueStatement = newWhenTrueStatement.WithLeadingTrivia(ifStatement.GetLeadingTrivia()); } // If there's trailing comments on the original inner statement, then preserve that. Otherwise, // replace it with the trailing trivia of hte original if-statement. if (!newWhenTrueStatement.GetTrailingTrivia().Any(t => t.IsSingleOrMultiLineComment())) newWhenTrueStatement = newWhenTrueStatement.WithTrailingTrivia(ifStatement.GetTrailingTrivia()); editor.ReplaceNode(ifStatement, newWhenTrueStatement); } else { var coalesce = coalesceOrIfStatement; // changing from `x ?? (x = y)` to `x ??= y` can change the type. Specifically, // with nullable value types (`int?`) it could change from `int?` to `int`. // // Add an explicit cast to the original type to ensure semantics are preserved. // Simplification engine can then remove it if it's not necessary. var type = semanticModel.GetTypeInfo(coalesce, cancellationToken).Type; editor.ReplaceNode(coalesce, (currentCoalesceNode, generator) => { var currentCoalesce = (BinaryExpressionSyntax)currentCoalesceNode; var coalesceRight = (ParenthesizedExpressionSyntax)currentCoalesce.Right; var assignment = (AssignmentExpressionSyntax)coalesceRight.Expression; var compoundOperator = QuestionQuestionEqualsToken; var finalAssignment = AssignmentExpression( SyntaxKind.CoalesceAssignmentExpression, assignment.Left, compoundOperator.WithTriviaFrom(assignment.OperatorToken), assignment.Right); return type == null || type.IsErrorType() ? finalAssignment : generator.CastExpression(type, finalAssignment); }); } } } }