Created
August 15, 2018 16:10
-
-
Save MihaZupan/835c161c41abd4f2b2bb92a359e4753d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Immutable; | |
using System.Composition; | |
using System.Linq; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using Microsoft.CodeAnalysis; | |
using Microsoft.CodeAnalysis.CodeActions; | |
using Microsoft.CodeAnalysis.CodeFixes; | |
using Microsoft.CodeAnalysis.CSharp.Syntax; | |
using Microsoft.CodeAnalysis.Diagnostics; | |
using Microsoft.CodeAnalysis.Editing; | |
using Microsoft.CodeAnalysis.Text; | |
namespace Telegram.Bot.Analyzers.Analyzers | |
{ | |
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(MessageChatAndIdToMessage)), Shared] | |
public class MessageChatAndIdToMessage : CodeFixProvider, ISyntaxNodeRule | |
{ | |
public static MessageChatAndIdToMessage Instance { get; } = new MessageChatAndIdToMessage(); | |
public MessageChatAndIdToMessage() { } | |
public DiagnosticDescriptor Diagnostic { get; } = new DiagnosticDescriptor( | |
DiagnosticIDs.MessageChatAndIdToMessage, | |
"Method call parameters can be simplified", | |
"Use an overload for {0} that takes a Message parameter, instead of Chat and MessageId", | |
Constants.Category, | |
DiagnosticSeverity.Warning, | |
isEnabledByDefault: true); | |
public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(DiagnosticIDs.MessageChatAndIdToMessage); | |
private static readonly ImmutableHashSet<string> ValidMethods = | |
ImmutableHashSet.Create( | |
"ForwardMessageAsync", | |
"StopMessageLiveLocationAsync", | |
"EditMessageTextAsync", | |
"EditMessageCaptionAsync", | |
"EditMessageMediaAsync", | |
"EditMessageReplyMarkupAsync", | |
"EditMessageLiveLocationAsync", | |
"DeleteMessageAsync", | |
"PinChatMessageAsync"); | |
public void Analyze(SyntaxNodeAnalysisContext context) | |
{ | |
var invocation = context.Node as InvocationExpressionSyntax; | |
var methodAccess = invocation.Expression as MemberAccessExpressionSyntax; | |
string methodName = methodAccess.Name.Identifier.ValueText; | |
if (!ValidMethods.Contains(methodName)) return; | |
var argumentList = invocation.ArgumentList; | |
if (argumentList.IsMissing) return; | |
var arguments = argumentList.Arguments.ToArray(); | |
if (arguments.Length < 2) return; | |
for (int i = 0; i < arguments.Length - 1; i++) | |
{ | |
if (arguments[i].Expression is MemberAccessExpressionSyntax chatIdAccess && | |
arguments[i + 1].Expression is MemberAccessExpressionSyntax messageIdAccess && | |
chatIdAccess.AccessedMemberName() == "Chat" && | |
messageIdAccess.AccessedMemberName() == "MessageId" && | |
chatIdAccess.VariableName() == messageIdAccess.VariableName() && | |
chatIdAccess.CaleeParentTypeName(context) == "Message" && | |
messageIdAccess.CaleeParentTypeName(context) == "Message") | |
{ | |
int start = arguments[i].SpanStart; | |
int length = arguments[i + 1].Span.End - start; | |
Location location = Location.Create(invocation.SyntaxTree, new TextSpan(start, length)); | |
context.ReportDiagnostic(Microsoft.CodeAnalysis.Diagnostic.Create(Diagnostic, location, methodName)); | |
break; | |
} | |
} | |
} | |
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; | |
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously | |
public override async Task RegisterCodeFixesAsync(CodeFixContext context) | |
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously | |
{ | |
var diagnostic = context.Diagnostics.First(); | |
const string title = "Simplify method call"; | |
context.RegisterCodeFix( | |
CodeAction.Create( | |
title: title, | |
createChangedDocument: c => SimplifyMethodCall(context.Document, diagnostic, c), | |
equivalenceKey: DiagnosticIDs.MessageChatAndIdToMessage), | |
diagnostic); | |
} | |
private async Task<Document> SimplifyMethodCall(Document document, Diagnostic diagnostic, CancellationToken cancellationToken) | |
{ | |
var diagnosticSpan = diagnostic.Location.SourceSpan; | |
var root = await document.GetSyntaxRootAsync(cancellationToken); | |
var argumentList = root.FindNode(diagnosticSpan) as ArgumentListSyntax; | |
var arguments = argumentList.Arguments; | |
int messageChatArgumentIndex = arguments.IndexOf(a => a.SpanStart == diagnosticSpan.Start); | |
var memberAccess = arguments[messageChatArgumentIndex].Expression as MemberAccessExpressionSyntax; | |
var variableName = memberAccess.VariableName(); | |
var generator = SyntaxGenerator.GetGenerator(document); | |
var identifierName = generator.IdentifierName(variableName); | |
var newArgument = generator.Argument(identifierName) as ArgumentSyntax; | |
var newArguments = arguments | |
.RemoveAt(messageChatArgumentIndex) | |
.RemoveAt(messageChatArgumentIndex) | |
.Insert(messageChatArgumentIndex, newArgument); | |
var newArgumentList = argumentList.WithArguments(newArguments); | |
var newRoot = root.ReplaceNode(argumentList, newArgumentList); | |
return document.WithSyntaxRoot(newRoot); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment