| File: SimplifyTypeNames\AbstractSimplifyTypeNamesCodeFixProvider.cs | Web Access |
| Project: src\roslyn\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features) |
// 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. #nullable disable using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Simplification; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.SimplifyTypeNames; internal abstract partial class AbstractSimplifyTypeNamesCodeFixProvider<TSyntaxKind, TSimplifierOptions>( SimplifyTypeNamesDiagnosticAnalyzerBase<TSyntaxKind, TSimplifierOptions> analyzer) : SyntaxEditorBasedCodeFixProvider where TSyntaxKind : struct where TSimplifierOptions : SimplifierOptions { private readonly SimplifyTypeNamesDiagnosticAnalyzerBase<TSyntaxKind, TSimplifierOptions> _analyzer = analyzer; protected abstract string GetTitle(string diagnosticId, string nodeText); protected abstract SyntaxNode AddSimplificationAnnotationTo(SyntaxNode node); public sealed override ImmutableArray<string> FixableDiagnosticIds { get; } = [ IDEDiagnosticIds.SimplifyNamesDiagnosticId, IDEDiagnosticIds.SimplifyMemberAccessDiagnosticId, IDEDiagnosticIds.PreferBuiltInOrFrameworkTypeDiagnosticId, ]; private (SyntaxNode, string diagnosticId) GetNodeToSimplify( SyntaxNode root, SemanticModel model, TextSpan span, TSimplifierOptions options, CancellationToken cancellationToken) { var token = root.FindToken(span.Start, findInsideTrivia: true); if (!token.Span.IntersectsWith(span)) { return default; } SyntaxNode topmostSimplifiableNode = null; string topmostDiagnosticId = null; foreach (var node in token.GetAncestors<SyntaxNode>()) { if (node.Span.IntersectsWith(span) && CanSimplifyTypeNameExpression(model, node, options, span, out var diagnosticId, cancellationToken)) { // keep overwriting the best simplifiable node as long as we keep finding them. topmostSimplifiableNode = node; topmostDiagnosticId = diagnosticId; } else if (topmostSimplifiableNode != null) { // if we have found something simplifiable, but hit something that isn't, then // return the best thing we've found. break; } } return (topmostSimplifiableNode, topmostDiagnosticId); } public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { var document = context.Document; var span = context.Span; var cancellationToken = context.CancellationToken; var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var options = (TSimplifierOptions)await document.GetSimplifierOptionsAsync(cancellationToken).ConfigureAwait(false); var (node, diagnosticId) = GetNodeToSimplify( root, model, span, options, cancellationToken); if (node == null) return; var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>(); var title = GetTitle(diagnosticId, syntaxFacts.ConvertToSingleLine(node).ToString()); RegisterCodeFix(context, title, diagnosticId); } protected override async Task FixAllAsync( Document document, ImmutableArray<Diagnostic> diagnostics, SyntaxEditor editor, CancellationToken cancellationToken) { var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var simplifierOptions = (TSimplifierOptions)await document.GetSimplifierOptionsAsync(cancellationToken).ConfigureAwait(false); foreach (var diagnostic in diagnostics) { var (node, _) = GetNodeToSimplify( root, model, diagnostic.Location.SourceSpan, simplifierOptions, cancellationToken); if (node == null) return; editor.ReplaceNode( node, (current, _) => AddSimplificationAnnotationTo(current)); } } private bool CanSimplifyTypeNameExpression(SemanticModel model, SyntaxNode node, TSimplifierOptions options, TextSpan span, out string diagnosticId, CancellationToken cancellationToken) { diagnosticId = null; if (!_analyzer.IsCandidate(node) || !_analyzer.CanSimplifyTypeNameExpression( model, node, options, out var issueSpan, out diagnosticId, out _, cancellationToken)) { return false; } return issueSpan.Equals(span); } }