|
| 1 | +// Ignore Spelling: analyzer |
| 2 | + |
| 3 | +using System.Collections.Immutable; |
| 4 | +using System.Composition; |
| 5 | +using Microsoft.CodeAnalysis; |
| 6 | +using Microsoft.CodeAnalysis.CodeActions; |
| 7 | +using Microsoft.CodeAnalysis.CodeFixes; |
| 8 | +using Microsoft.CodeAnalysis.CSharp; |
| 9 | +using Microsoft.CodeAnalysis.CSharp.Syntax; |
| 10 | +using Microsoft.CodeAnalysis.Text; |
| 11 | +using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; |
| 12 | + |
| 13 | +namespace CommunityToolkit.Maui.Analyzers; |
| 14 | + |
| 15 | +[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(MaximumRatingAnalyzerCodeFixProvider)), Shared] |
| 16 | +public class MaximumRatingAnalyzerCodeFixProvider : CodeFixProvider |
| 17 | +{ |
| 18 | + public sealed override ImmutableArray<string> FixableDiagnosticIds => [MaximumRatingRangeAnalyzer.DiagnosticId]; |
| 19 | + |
| 20 | + public sealed override FixAllProvider GetFixAllProvider() |
| 21 | + { |
| 22 | + return WellKnownFixAllProviders.BatchFixer; |
| 23 | + } |
| 24 | + |
| 25 | + public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) |
| 26 | + { |
| 27 | + SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); |
| 28 | + if (root is null) |
| 29 | + { |
| 30 | + return; |
| 31 | + } |
| 32 | + |
| 33 | + Diagnostic? diagnostic = context.Diagnostics.FirstOrDefault(); |
| 34 | + if (diagnostic is null) |
| 35 | + { |
| 36 | + return; |
| 37 | + } |
| 38 | + |
| 39 | + TextSpan diagnosticSpan = diagnostic.Location.SourceSpan; |
| 40 | + |
| 41 | + // Find the literal expression identified by the diagnostic. |
| 42 | + if (root.FindToken(diagnosticSpan.Start).Parent is not LiteralExpressionSyntax literalExpression) |
| 43 | + { |
| 44 | + return; |
| 45 | + } |
| 46 | + |
| 47 | + // Register a code action that will invoke the fix. |
| 48 | + context.RegisterCodeFix( |
| 49 | + CodeAction.Create( |
| 50 | + title: "Set MaximumRating to valid value, between 1 and 10", |
| 51 | + createChangedDocument: c => MaximumRatingToValidValueAsync(context.Document, literalExpression, c), |
| 52 | + equivalenceKey: nameof(MaximumRatingAnalyzerCodeFixProvider)), |
| 53 | + diagnostic); |
| 54 | + } |
| 55 | + |
| 56 | + static async Task<Document> MaximumRatingToValidValueAsync(Document document, LiteralExpressionSyntax literalExpression, CancellationToken cancellationToken) |
| 57 | + { |
| 58 | + // Get the original value from the literal expression. |
| 59 | + if (!int.TryParse(literalExpression.Token.ValueText, out int originalValue)) |
| 60 | + { |
| 61 | + originalValue = 1; // Fallback value if parsing fails. |
| 62 | + } |
| 63 | + |
| 64 | + int newValue = GetValidRatingValue(originalValue); |
| 65 | + // Create a new literal expression with the valid rating value. |
| 66 | + LiteralExpressionSyntax newLiteralExpression = LiteralExpression( |
| 67 | + SyntaxKind.NumericLiteralExpression, |
| 68 | + Literal(newValue)); |
| 69 | + |
| 70 | + SyntaxNode? root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); |
| 71 | + if (root is null) |
| 72 | + { |
| 73 | + return document; |
| 74 | + } |
| 75 | + |
| 76 | + // Replace the old literal with the new one. |
| 77 | + SyntaxNode newRoot = root.ReplaceNode(literalExpression, newLiteralExpression); |
| 78 | + |
| 79 | + return document.WithSyntaxRoot(newRoot); |
| 80 | + } |
| 81 | + |
| 82 | + static int GetValidRatingValue(int value) |
| 83 | + { |
| 84 | + return value switch |
| 85 | + { |
| 86 | + < 1 => 1, |
| 87 | + > 10 => 10, |
| 88 | + _ => value |
| 89 | + }; |
| 90 | + } |
| 91 | +} |
0 commit comments