public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindFirstAncestorOrSelf(root, context.Span, out ArgumentSyntax argument)) { return; } foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case DiagnosticIdentifiers.OptimizeStringBuilderAppendCall: { MemberInvocationExpressionInfo invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo((InvocationExpressionSyntax)argument.Parent.Parent); CodeAction codeAction = CodeAction.Create( $"Optimize '{invocationInfo.NameText}' call", cancellationToken => OptimizeStringBuilderAppendCallRefactoring.RefactorAsync(context.Document, argument, invocationInfo, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } } } }
public static void Analyze(SyntaxNodeAnalysisContext context, MemberInvocationExpressionInfo invocationInfo) { ExpressionSyntax expression = invocationInfo.InvocationExpression.WalkUpParentheses(); SyntaxNode parent = expression.Parent; SyntaxKind kind = parent.Kind(); if (kind == SyntaxKind.SimpleMemberAccessExpression) { MemberInvocationExpressionInfo invocationInfo2 = SyntaxInfo.MemberInvocationExpressionInfo(parent.Parent); if (!invocationInfo2.Success) { return; } Analyze(context, invocationInfo, invocationInfo2); } else if (kind == SyntaxKind.Argument) { Analyze(context, invocationInfo, (ArgumentSyntax)parent); } else if (kind == SyntaxKind.EqualsExpression || kind == SyntaxKind.NotEqualsExpression) { Analyze(context, invocationInfo, expression, (BinaryExpressionSyntax)parent); } }
public static void Analyze(SyntaxNodeAnalysisContext context, MemberInvocationExpressionInfo invocationInfo) { ExpressionSyntax expression = invocationInfo.Expression; if (expression.IsKind(SyntaxKind.InvocationExpression)) { MemberInvocationExpressionInfo invocationInfo2 = SyntaxInfo.MemberInvocationExpressionInfo((InvocationExpressionSyntax)expression); if (invocationInfo2.Success) { switch (invocationInfo2.NameText) { case "OrderBy": case "OrderByDescending": { if (IsLinqExtensionOfIEnumerableOfT(context, invocationInfo.InvocationExpression) && IsLinqExtensionOfIEnumerableOfT(context, invocationInfo2.InvocationExpression)) { context.ReportDiagnostic( DiagnosticDescriptors.CallThenByInsteadOfOrderBy, invocationInfo.Name, (invocationInfo.NameText == "OrderByDescending") ? "Descending" : null); } break; } } } } }
public static async Task <Document> RefactorAsync( Document document, InvocationExpressionSyntax invocationExpression, CancellationToken cancellationToken) { SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); MemberDeclarationSyntax memberDeclaration = invocationExpression.FirstAncestor <MemberDeclarationSyntax>(); Debug.Assert(memberDeclaration != null, ""); if (memberDeclaration != null) { BaseTypeDeclarationSyntax typeDeclaration = memberDeclaration.FirstAncestor <BaseTypeDeclarationSyntax>(); Debug.Assert(typeDeclaration != null, ""); if (typeDeclaration != null) { MemberInvocationExpressionInfo invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo(invocationExpression); string fieldName = NameGenerator.Default.EnsureUniqueLocalName("_regex", semanticModel, invocationExpression.SpanStart, cancellationToken: cancellationToken); MemberAccessExpressionSyntax newMemberAccess = invocationInfo.MemberAccessExpression.WithExpression(IdentifierName(Identifier(fieldName).WithRenameAnnotation())); ArgumentListPair pair = RewriteArgumentLists(invocationInfo.ArgumentList, semanticModel, cancellationToken); InvocationExpressionSyntax newInvocationExpression = invocationExpression .WithExpression(newMemberAccess) .WithArgumentList(pair.ArgumentList1); MemberDeclarationSyntax newTypeDeclaration = typeDeclaration.ReplaceNode(invocationExpression, newInvocationExpression); TypeSyntax regexType = semanticModel.GetTypeByMetadataName(MetadataNames.System_Text_RegularExpressions_Regex).ToMinimalTypeSyntax(semanticModel, typeDeclaration.SpanStart); FieldDeclarationSyntax fieldDeclaration = FieldDeclaration( (ShouldBeStatic(memberDeclaration, semanticModel, cancellationToken)) ? Modifiers.PrivateStaticReadOnly() : Modifiers.PrivateReadOnly(), regexType, Identifier(fieldName), EqualsValueClause( ObjectCreationExpression(regexType, pair.ArgumentList2))); SyntaxList <MemberDeclarationSyntax> newMembers = newTypeDeclaration .GetMembers() .InsertMember(fieldDeclaration, MemberDeclarationComparer.ByKind); newTypeDeclaration = newTypeDeclaration.WithMembers(newMembers); return(await document.ReplaceNodeAsync(typeDeclaration, newTypeDeclaration, cancellationToken).ConfigureAwait(false)); } } return(document); }
private static bool IsFixable(InvocationExpressionSyntax invocationExpression, SemanticModel semanticModel, CancellationToken cancellationToken) { MemberInvocationExpressionInfo invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo(invocationExpression); if (!invocationInfo.Success) { return(false); } if (!semanticModel.TryGetMethodInfo(invocationInfo.InvocationExpression, out MethodInfo methodInfo, cancellationToken)) { return(false); } if (!methodInfo.IsContainingType(SpecialType.System_String)) { return(false); } if (!methodInfo.IsReturnType(SpecialType.System_String)) { return(false); } switch (methodInfo.Name) { case "Substring": { if (methodInfo.HasParameters(SpecialType.System_Int32, SpecialType.System_Int32)) { return(true); } break; } case "Remove": { if (methodInfo.HasParameter(SpecialType.System_Int32)) { return(true); } break; } case "Format": { return(true); } } return(false); }
private string GetTextToAppend(ExpressionStatementSyntax expressionStatement) { MemberInvocationExpressionInfo invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo(GetInvocationExpression(expressionStatement)); MemberInvocationExpressionInfo firstMemberInvocation = WalkDownMethodChain(invocationInfo); InvocationExpressionSyntax invocationExpression = invocationInfo.InvocationExpression; return(invocationExpression .ToString() .Substring(firstMemberInvocation.OperatorToken.SpanStart - invocationExpression.SpanStart)); }
internal static void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context) { var invocationExpression = (InvocationExpressionSyntax)context.Node; if (!invocationExpression.ContainsDiagnostics && !invocationExpression.SpanContainsDirectives()) { MemberInvocationExpressionInfo invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo(invocationExpression); if (invocationInfo.Success && invocationInfo.NameText == "Any") { ArgumentSyntax argument1 = invocationInfo.Arguments.SingleOrDefault(shouldthrow: false); if (argument1 != null) { MemberInvocationExpressionInfo invocationInfo2 = SyntaxInfo.MemberInvocationExpressionInfo(invocationInfo.Expression); if (invocationInfo2.Success && invocationInfo2.NameText == "Where") { ArgumentSyntax argument2 = invocationInfo2.Arguments.SingleOrDefault(shouldthrow: false); if (argument2 != null) { SemanticModel semanticModel = context.SemanticModel; CancellationToken cancellationToken = context.CancellationToken; if (semanticModel.TryGetExtensionMethodInfo(invocationExpression, out MethodInfo methodInfo, ExtensionMethodKind.None, cancellationToken) && methodInfo.IsLinqExtensionOfIEnumerableOfTWithPredicate("Any") && semanticModel.TryGetExtensionMethodInfo(invocationInfo2.InvocationExpression, out MethodInfo methodInfo2, ExtensionMethodKind.None, cancellationToken) && methodInfo2.IsLinqWhere(allowImmutableArrayExtension: true)) { SingleParameterLambdaExpressionInfo lambda = SyntaxInfo.SingleParameterLambdaExpressionInfo(argument1.Expression); if (lambda.Success && lambda.Body is ExpressionSyntax) { SingleParameterLambdaExpressionInfo lambda2 = SyntaxInfo.SingleParameterLambdaExpressionInfo(argument2.Expression); if (lambda2.Success && lambda2.Body is ExpressionSyntax && lambda.ParameterName.Equals(lambda2.ParameterName, StringComparison.Ordinal)) { context.ReportDiagnostic( DiagnosticDescriptors.SimplifyLinqMethodChain, Location.Create(context.SyntaxTree(), TextSpan.FromBounds(invocationInfo2.Name.SpanStart, invocationExpression.Span.End))); } } } } } } } } }
protected static MemberInvocationExpressionInfo WalkDownMethodChain(MemberInvocationExpressionInfo invocationInfo) { while (true) { MemberInvocationExpressionInfo invocationInfo2 = SyntaxInfo.MemberInvocationExpressionInfo(invocationInfo.Expression); if (invocationInfo2.Success) { invocationInfo = invocationInfo2; } else { break; } } return(invocationInfo); }
private static bool TryCreateCaseChangingInvocation(ExpressionSyntax expression, out MemberInvocationExpressionInfo invocationInfo) { invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo(expression); if (invocationInfo.Success && !invocationInfo.Arguments.Any()) { string name = invocationInfo.NameText; return(name == "ToLower" || name == "ToLowerInvariant" || name == "ToUpper" || name == "ToUpperInvariant"); } invocationInfo = default(MemberInvocationExpressionInfo); return(false); }
private static void Analyze( SyntaxNodeAnalysisContext context, MemberInvocationExpressionInfo invocationInfo, ArgumentSyntax argument) { if (!(argument.Parent is ArgumentListSyntax argumentList)) { return; } SeparatedSyntaxList <ArgumentSyntax> arguments = argumentList.Arguments; if (arguments.Count != 2) { return; } MemberInvocationExpressionInfo equalsInvocation = SyntaxInfo.MemberInvocationExpressionInfo(argumentList.Parent); if (!equalsInvocation.Success) { return; } if (equalsInvocation.NameText != "Equals") { return; } if (!IsFixable(context, invocationInfo, argument, arguments)) { return; } if (!context.SemanticModel.TryGetMethodInfo(equalsInvocation.InvocationExpression, out MethodInfo info, context.CancellationToken) || !info.IsPublicStaticStringMethod("Equals") || !info.ReturnsBoolean || !info.HasParameters(SpecialType.System_String, SpecialType.System_String)) { return; } ReportDiagnostic(context, equalsInvocation); }
protected override bool IsFixableStatement( StatementSyntax statement, string name, ITypeSymbol typeSymbol, SemanticModel semanticModel, CancellationToken cancellationToken) { if (statement.SpanOrLeadingTriviaContainsDirectives()) { return(false); } if (!(statement is ExpressionStatementSyntax expressionStatement)) { return(false); } MemberInvocationExpressionInfo invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo(expressionStatement.Expression); if (!invocationInfo.Success) { return(false); } if (!(WalkDownMethodChain(invocationInfo).Expression is IdentifierNameSyntax identifierName)) { return(false); } if (name != identifierName.Identifier.ValueText) { return(false); } if (!semanticModel.TryGetMethodInfo(invocationInfo.InvocationExpression, out MethodInfo methodInfo, cancellationToken)) { return(false); } return(!methodInfo.IsStatic && methodInfo.ContainingType?.Equals(typeSymbol) == true && methodInfo.ReturnType.Equals(typeSymbol)); }
private static InvocationExpressionSyntax CreateInvocationExpression( InvocationExpressionSyntax innerInvocationExpression, InvocationExpressionSyntax outerInvocationExpression) { MemberInvocationExpressionInfo invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo(innerInvocationExpression); switch (invocationInfo.NameText) { case "Substring": { SeparatedSyntaxList <ArgumentSyntax> arguments = invocationInfo.Arguments; ArgumentListSyntax argumentList = ArgumentList( Argument(invocationInfo.Expression), arguments[0], arguments[1] ); return(CreateNewInvocationExpression(outerInvocationExpression, "Append", argumentList)); } case "Remove": { SeparatedSyntaxList <ArgumentSyntax> arguments = invocationInfo.Arguments; ArgumentListSyntax argumentList = ArgumentList( Argument(invocationInfo.Expression), Argument(NumericLiteralExpression(0)), arguments[0] ); return(CreateNewInvocationExpression(outerInvocationExpression, "Append", argumentList)); } case "Format": { return(CreateNewInvocationExpression(outerInvocationExpression, "AppendFormat", invocationInfo.ArgumentList)); } } Debug.Fail(innerInvocationExpression.ToString()); return(outerInvocationExpression); }
public static Task <Document> RefactorAsync( Document document, MemberInvocationExpressionInfo invocationInfo, CancellationToken cancellationToken) { MemberInvocationExpressionInfo invocationInfo2 = SyntaxInfo.MemberInvocationExpressionInfo(invocationInfo.Expression); SyntaxTriviaList trivia = invocationInfo2.InvocationExpression .GetTrailingTrivia() .EmptyIfWhitespace() .AddRange(invocationInfo.InvocationExpression.GetTrailingTrivia()); InvocationExpressionSyntax newNode = invocationInfo2 .WithName("AppendLine") .InvocationExpression .WithTrailingTrivia(trivia); return(document.ReplaceNodeAsync(invocationInfo.InvocationExpression, newNode, cancellationToken)); }
public static Task <Document> RefactorAsync( Document document, InvocationExpressionSyntax invocationExpression, CancellationToken cancellationToken) { MemberInvocationExpressionInfo invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo(invocationExpression); MemberInvocationExpressionInfo invocationInfo2 = SyntaxInfo.MemberInvocationExpressionInfo(invocationInfo.Expression); SingleParameterLambdaExpressionInfo lambda = SyntaxInfo.SingleParameterLambdaExpressionInfo((LambdaExpressionSyntax)invocationInfo.Arguments.First().Expression); SingleParameterLambdaExpressionInfo lambda2 = SyntaxInfo.SingleParameterLambdaExpressionInfo((LambdaExpressionSyntax)invocationInfo2.Arguments.First().Expression); BinaryExpressionSyntax logicalAnd = CSharpFactory.LogicalAndExpression( ((ExpressionSyntax)lambda2.Body).Parenthesize(), ((ExpressionSyntax)lambda.Body).Parenthesize()); InvocationExpressionSyntax newNode = invocationInfo2.InvocationExpression .ReplaceNode(invocationInfo2.Name, invocationInfo.Name.WithTriviaFrom(invocationInfo2.Name)) .WithArgumentList(invocationInfo2.ArgumentList.ReplaceNode((ExpressionSyntax)lambda2.Body, logicalAnd)); return(document.ReplaceNodeAsync(invocationExpression, newNode, cancellationToken)); }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindFirstAncestorOrSelf( root, context.Span, out SyntaxNode node, getInnermostNodeForTie: false, predicate: f => f.IsKind(SyntaxKind.Argument, SyntaxKind.InvocationExpression))) { return; } Diagnostic diagnostic = context.Diagnostics[0]; if (node is ArgumentSyntax argument) { MemberInvocationExpressionInfo invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo((InvocationExpressionSyntax)argument.Parent.Parent); CodeAction codeAction = CodeAction.Create( $"Optimize '{invocationInfo.NameText}' call", cancellationToken => OptimizeStringBuilderAppendCallRefactoring.RefactorAsync(context.Document, argument, invocationInfo, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } else if (node is InvocationExpressionSyntax invocationExpression) { MemberInvocationExpressionInfo invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo(invocationExpression); CodeAction codeAction = CodeAction.Create( $"Optimize '{invocationInfo.NameText}' call", cancellationToken => OptimizeStringBuilderAppendCallRefactoring.RefactorAsync(context.Document, invocationInfo, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } }
private static bool IsConfiguredTaskAwaitableOfT( ExpressionSyntax expression, INamedTypeSymbol expressionTypeSymbol, SemanticModel semanticModel) { MemberInvocationExpressionInfo invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo(expression); if (!invocationInfo.Success) { return(false); } if (invocationInfo.Arguments.Count != 1) { return(false); } if (invocationInfo.NameText != "ConfigureAwait") { return(false); } return(expressionTypeSymbol.ConstructedFrom.Equals(semanticModel.GetTypeByMetadataName(MetadataNames.System_Runtime_CompilerServices_ConfiguredTaskAwaitable_T))); }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindFirstAncestorOrSelf(root, context.Span, out InvocationExpressionSyntax invocation)) { return; } foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case DiagnosticIdentifiers.SimplifyLinqMethodChain: { var memberAccess = (MemberAccessExpressionSyntax)invocation.Expression; string name = memberAccess.Name.Identifier.ValueText; if (name == "Cast") { CodeAction codeAction = CodeAction.Create( "Simplify method chain", cancellationToken => CallOfTypeInsteadOfWhereAndCastRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } else if (name == "Any" && invocation.ArgumentList.Arguments.Count == 1) { CodeAction codeAction = CodeAction.Create( "Simplify method chain", cancellationToken => CombineEnumerableWhereAndAnyRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } else { CodeAction codeAction = CodeAction.Create( "Simplify method chain", cancellationToken => SimplifyLinqMethodChainRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } break; } case DiagnosticIdentifiers.CombineEnumerableWhereMethodChain: { CodeAction codeAction = CodeAction.Create( "Combine 'Where' method chain", cancellationToken => CombineEnumerableWhereMethodChainRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.UseCountOrLengthPropertyInsteadOfAnyMethod: { string propertyName = diagnostic.Properties["PropertyName"]; CodeAction codeAction = CodeAction.Create( $"Use '{propertyName}' property instead of calling 'Any'", cancellationToken => UseCountOrLengthPropertyInsteadOfAnyMethodRefactoring.RefactorAsync(context.Document, invocation, propertyName, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.UseCountOrLengthPropertyInsteadOfCountMethod: { string propertyName = diagnostic.Properties["PropertyName"]; CodeAction codeAction = CodeAction.Create( $"Use '{propertyName}' property instead of calling 'Count'", cancellationToken => UseCountOrLengthPropertyInsteadOfCountMethodRefactoring.RefactorAsync(context.Document, invocation, diagnostic.Properties["PropertyName"], cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.UseBitwiseOperationInsteadOfCallingHasFlag: { CodeAction codeAction = CodeAction.Create( UseBitwiseOperationInsteadOfCallingHasFlagRefactoring.Title, cancellationToken => UseBitwiseOperationInsteadOfCallingHasFlagRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.RemoveRedundantToStringCall: { CodeAction codeAction = CodeAction.Create( "Remove redundant 'ToString' call", cancellationToken => RemoveRedundantCallRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.CallCastInsteadOfSelect: { CodeAction codeAction = CodeAction.Create( "Call 'Cast' instead of 'Select'", cancellationToken => CallCastInsteadOfSelectRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.RemoveRedundantStringToCharArrayCall: { CodeAction codeAction = CodeAction.Create( "Remove redundant 'ToCharArray' call", cancellationToken => RemoveRedundantCallRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.CallFindInsteadOfFirstOrDefault: { CodeAction codeAction = CodeAction.Create( "Call 'Find' instead of 'FirstOrDefault'", cancellationToken => CallFindInsteadOfFirstOrDefaultRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.UseElementAccessInsteadOfElementAt: { CodeAction codeAction = CodeAction.Create( "Use [] instead of calling 'ElementAt'", cancellationToken => UseElementAccessInsteadOfElementAtRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.UseElementAccessInsteadOfFirst: { CodeAction codeAction = CodeAction.Create( "Use [] instead of calling 'First'", cancellationToken => UseElementAccessInsteadOfFirstRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.CallStringConcatInsteadOfStringJoin: { CodeAction codeAction = CodeAction.Create( "Call 'Concat' instead of 'Join'", cancellationToken => CallStringConcatInsteadOfStringJoinRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.CallDebugFailInsteadOfDebugAssert: { CodeAction codeAction = CodeAction.Create( "Call 'Fail' instead of 'Assert'", cancellationToken => CallDebugFailInsteadOfDebugAssertRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.CallExtensionMethodAsInstanceMethod: { CodeAction codeAction = CodeAction.Create( CallExtensionMethodAsInstanceMethodRefactoring.Title, cancellationToken => CallExtensionMethodAsInstanceMethodRefactoring.RefactorAsync(context.Document, invocation, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.CallThenByInsteadOfOrderBy: { MemberInvocationExpressionInfo invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo(invocation); string oldName = invocationInfo.NameText; string newName = (string.Equals(oldName, "OrderBy", StringComparison.Ordinal)) ? "ThenBy" : "ThenByDescending"; CodeAction codeAction = CodeAction.Create( $"Call '{newName}' instead of '{oldName}'", cancellationToken => CallThenByInsteadOfOrderByRefactoring.RefactorAsync(context.Document, invocation, newName, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } } } }
public async Task <Document> RefactorAsync( Document document, ExpressionStatementSyntax expressionStatement, CancellationToken cancellationToken) { SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); InvocationExpressionSyntax invocationExpression = GetInvocationExpression(expressionStatement); semanticModel.TryGetMethodInfo(invocationExpression, out MethodInfo methodInfo, cancellationToken); MemberInvocationExpressionInfo invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo(invocationExpression); ITypeSymbol typeSymbol = methodInfo.ReturnType; string name = ((IdentifierNameSyntax)WalkDownMethodChain(invocationInfo).Expression).Identifier.ValueText; StatementsInfo statementsInfo = SyntaxInfo.StatementsInfo(expressionStatement); SyntaxList <StatementSyntax> statements = statementsInfo.Statements; int index = statements.IndexOf(expressionStatement); string indentation = CSharpFormatter.GetIncreasedIndentation(expressionStatement, cancellationToken).ToString(); var sb = new StringBuilder(invocationExpression.ToString()); int j = index; while (j < statements.Count - 1) { StatementSyntax statement = statements[j + 1]; if (!IsFixableStatement(statement, name, typeSymbol, semanticModel, cancellationToken)) { break; } sb.AppendLine(); sb.Append(indentation); sb.Append(GetTextToAppend((ExpressionStatementSyntax)statement)); j++; } StatementSyntax lastStatement = statements[j]; SyntaxList <StatementSyntax> newStatements = statements; while (j > index) { newStatements = newStatements.RemoveAt(j); j--; } ExpressionSyntax newInvocationExpression = SyntaxFactory.ParseExpression(sb.ToString()); SyntaxTriviaList trailingTrivia = statementsInfo .Node .DescendantTrivia(TextSpan.FromBounds(invocationExpression.Span.End, lastStatement.Span.End)) .ToSyntaxTriviaList() .EmptyIfWhitespace() .AddRange(lastStatement.GetTrailingTrivia()); ExpressionStatementSyntax newExpressionStatement = expressionStatement .ReplaceNode(invocationExpression, newInvocationExpression) .WithLeadingTrivia(expressionStatement.GetLeadingTrivia()) .WithTrailingTrivia(trailingTrivia) .WithFormatterAndSimplifierAnnotations(); newStatements = newStatements.ReplaceAt(index, newExpressionStatement); return(await document.ReplaceStatementsAsync(statementsInfo, newStatements, cancellationToken).ConfigureAwait(false)); }
private void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context) { var invocation = (InvocationExpressionSyntax)context.Node; ExpressionSyntax expression = invocation.Expression; if (expression?.IsKind(SyntaxKind.SimpleMemberAccessExpression) == true) { var memberAccess = (MemberAccessExpressionSyntax)expression; ArgumentListSyntax argumentList = invocation.ArgumentList; if (argumentList?.IsMissing == false) { int argumentCount = argumentList.Arguments.Count; string methodName = memberAccess.Name?.Identifier.ValueText; if (argumentCount == 0) { switch (methodName) { case "Any": { SimplifyLinqMethodChainRefactoring.Analyze(context, invocation, memberAccess, methodName); break; } case "Cast": { CallOfTypeInsteadOfWhereAndCastRefactoring.Analyze(context, invocation); break; } case "Count": case "First": case "FirstOrDefault": case "Last": case "LastOrDefault": case "LongCount": case "Single": case "SingleOrDefault": { SimplifyLinqMethodChainRefactoring.Analyze(context, invocation, memberAccess, methodName); break; } } } else if (argumentCount == 1) { switch (methodName) { case "FirstOrDefault": { CallFindInsteadOfFirstOrDefaultRefactoring.Analyze(context, invocation, memberAccess); break; } case "Where": { CombineEnumerableWhereMethodChainRefactoring.Analyze(context, invocation, memberAccess); break; } } } } } if (UseBitwiseOperationInsteadOfCallingHasFlagRefactoring.CanRefactor(invocation, context.SemanticModel, context.CancellationToken) && !invocation.SpanContainsDirectives()) { context.ReportDiagnostic(DiagnosticDescriptors.UseBitwiseOperationInsteadOfCallingHasFlag, invocation); } RemoveRedundantStringToCharArrayCallRefactoring.Analyze(context, invocation); CombineEnumerableWhereAndAnyRefactoring.AnalyzeInvocationExpression(context); if (!invocation.ContainsDiagnostics) { if (!invocation.SpanContainsDirectives()) { CallExtensionMethodAsInstanceMethodAnalysis analysis = CallExtensionMethodAsInstanceMethodRefactoring.Analyze(invocation, context.SemanticModel, allowAnyExpression: false, cancellationToken: context.CancellationToken); if (analysis.Success && context.SemanticModel .GetEnclosingNamedType(analysis.InvocationExpression.SpanStart, context.CancellationToken)? .Equals(analysis.MethodSymbol.ContainingType) == false) { context.ReportDiagnostic(DiagnosticDescriptors.CallExtensionMethodAsInstanceMethod, invocation); } } MemberInvocationExpressionInfo invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo(invocation); if (invocationInfo.Success) { if (!invocation.SpanContainsDirectives()) { UseRegexInstanceInsteadOfStaticMethodRefactoring.Analyze(context, invocationInfo); } string methodName = invocationInfo.NameText; AvoidNullReferenceExceptionRefactoring.Analyze(context, invocationInfo); int argumentCount = invocationInfo.Arguments.Count; switch (argumentCount) { case 0: { switch (methodName) { case "Any": { UseCountOrLengthPropertyInsteadOfAnyMethodRefactoring.Analyze(context, invocationInfo); break; } case "Cast": { RemoveRedundantCastRefactoring.Analyze(context, invocationInfo); break; } case "Count": { UseInsteadOfCountMethodRefactoring.Analyze(context, invocationInfo); break; } case "First": { if (!invocationInfo.Expression.IsKind(SyntaxKind.InvocationExpression) && UseElementAccessInsteadOfFirstRefactoring.CanRefactor(invocationInfo, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(DiagnosticDescriptors.UseElementAccessInsteadOfFirst, invocationInfo.Name); } break; } case "ToString": { RemoveRedundantToStringCallRefactoring.Analyze(context, invocationInfo); UseNameOfOperatorRefactoring.Analyze(context, invocationInfo); break; } case "ToLower": case "ToLowerInvariant": case "ToUpper": case "ToUpperInvariant": { UseStringComparisonRefactoring.Analyze(context, invocationInfo); break; } } break; } case 1: { switch (methodName) { case "All": case "Any": { SimplifyLogicalNegationRefactoring.Analyze(context, invocationInfo); break; } case "ElementAt": { if (!invocationInfo.Expression.IsKind(SyntaxKind.InvocationExpression) && UseElementAccessInsteadOfElementAtRefactoring.CanRefactor(invocationInfo, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(DiagnosticDescriptors.UseElementAccessInsteadOfElementAt, invocationInfo.Name); } break; } } break; } } switch (methodName) { case "Append": case "AppendLine": case "AppendFormat": case "Insert": { OptimizeStringBuilderAppendCallRefactoring.Analyze(context, invocationInfo); break; } case "Select": { if (argumentCount == 1 || argumentCount == 2) { CallCastInsteadOfSelectRefactoring.Analyze(context, invocationInfo); } break; } case "OrderBy": case "OrderByDescending": { if (argumentCount == 1 || argumentCount == 2 || argumentCount == 3) { CallThenByInsteadOfOrderByRefactoring.Analyze(context, invocationInfo); } break; } } if (UseMethodChainingRefactoring.IsFixable(invocationInfo, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(DiagnosticDescriptors.UseMethodChaining, invocationInfo.InvocationExpression); } } } }
public static void Analyze(SyntaxNodeAnalysisContext context, MemberInvocationExpressionInfo invocationInfo) { INamedTypeSymbol stringBuilderSymbol = context.GetTypeByMetadataName(MetadataNames.System_Text_StringBuilder); if (stringBuilderSymbol == null) { return; } InvocationExpressionSyntax invocationExpression = invocationInfo.InvocationExpression; if (!context.SemanticModel.TryGetMethodInfo(invocationExpression, out MethodInfo methodInfo, context.CancellationToken)) { return; } if (methodInfo.IsExtensionMethod) { return; } if (methodInfo.ContainingType?.Equals(stringBuilderSymbol) != true) { return; } ImmutableArray <IParameterSymbol> parameters = methodInfo.Parameters; int parameterCount = parameters.Length; if (parameterCount == 0) { if (methodInfo.IsName("AppendLine")) { MemberInvocationExpressionInfo invocationInfo2 = SyntaxInfo.MemberInvocationExpressionInfo(invocationInfo.Expression); if (invocationInfo2.Success && invocationInfo2.NameText == "Append" && invocationInfo2.Arguments.Count == 1 && context.SemanticModel.TryGetMethodInfo(invocationInfo2.InvocationExpression, out MethodInfo methodInfo2, context.CancellationToken) && !methodInfo2.IsStatic && methodInfo2.ContainingType?.Equals(stringBuilderSymbol) == true && methodInfo2.HasParameter(SpecialType.System_String)) { context.ReportDiagnostic(DiagnosticDescriptors.OptimizeStringBuilderAppendCall, invocationInfo.Name, methodInfo.Name); } } } else if (parameterCount == 1) { if (methodInfo.IsName("Append", "AppendLine")) { ArgumentSyntax argument = invocationInfo.Arguments.SingleOrDefault(shouldThrow: false); if (argument != null) { ExpressionSyntax expression = argument.Expression; SyntaxKind expressionKind = expression.Kind(); switch (expressionKind) { case SyntaxKind.InterpolatedStringExpression: { context.ReportDiagnostic(DiagnosticDescriptors.OptimizeStringBuilderAppendCall, argument, methodInfo.Name); return; } case SyntaxKind.AddExpression: { StringConcatenationExpressionInfo concatenationInfo = SyntaxInfo.StringConcatenationExpressionInfo((BinaryExpressionSyntax)expression, context.SemanticModel, context.CancellationToken); if (concatenationInfo.Success) { context.ReportDiagnostic(DiagnosticDescriptors.OptimizeStringBuilderAppendCall, argument, methodInfo.Name); return; } break; } default: { if (expressionKind == SyntaxKind.InvocationExpression && IsFixable((InvocationExpressionSyntax)expression, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(DiagnosticDescriptors.OptimizeStringBuilderAppendCall, argument, methodInfo.Name); return; } if (methodInfo.IsName("Append") && parameterCount == 1 && parameters[0].Type.IsObject() && context.SemanticModel.GetTypeSymbol(argument.Expression, context.CancellationToken).IsValueType) { context.ReportDiagnostic(DiagnosticDescriptors.AvoidBoxingOfValueType, argument); return; } break; } } } } } else if (parameterCount == 2) { if (methodInfo.IsName("Insert") && parameters[0].Type.SpecialType == SpecialType.System_Int32 && parameters[1].Type.SpecialType == SpecialType.System_Object) { SeparatedSyntaxList <ArgumentSyntax> arguments = invocationInfo.Arguments; if (arguments.Count == 2 && context.SemanticModel .GetTypeSymbol(arguments[1].Expression, context.CancellationToken) .IsValueType) { context.ReportDiagnostic(DiagnosticDescriptors.AvoidBoxingOfValueType, arguments[1]); } } } }
public static async Task ComputeRefactoringsAsync(RefactoringContext context, InvocationExpressionSyntax invocation) { MemberInvocationExpressionInfo invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo(invocation); if (!invocationInfo.Success) { return; } switch (invocationInfo.NameText) { case "First": { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (invocationInfo.Arguments.Any()) { break; } if (!UseElementAccessInsteadOfFirstRefactoring.CanRefactor(invocationInfo, semanticModel, context.CancellationToken)) { break; } context.RegisterRefactoring( "Use [] instead of calling 'First'", cancellationToken => UseElementAccessInsteadOfFirstRefactoring.RefactorAsync(context.Document, invocation, cancellationToken)); break; } case "Last": { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (invocationInfo.Arguments.Any()) { break; } if (!UseElementAccessInsteadOfLastRefactoring.CanRefactor(invocationInfo, semanticModel, context.CancellationToken)) { break; } string propertyName = CSharpUtility.GetCountOrLengthPropertyName(invocationInfo.Expression, semanticModel, context.CancellationToken); if (propertyName == null) { break; } context.RegisterRefactoring( "Use [] instead of calling 'Last'", cancellationToken => UseElementAccessInsteadOfLastRefactoring.RefactorAsync(context.Document, invocation, propertyName, cancellationToken)); break; } case "ElementAt": { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (invocationInfo.Arguments.Count != 1) { break; } if (!UseElementAccessInsteadOfElementAtRefactoring.CanRefactor(invocationInfo, semanticModel, context.CancellationToken)) { break; } context.RegisterRefactoring( "Use [] instead of calling 'ElementAt'", cancellationToken => UseElementAccessInsteadOfElementAtRefactoring.RefactorAsync(context.Document, invocation, cancellationToken)); break; } } }
public static void AnalyzeMethodDeclaration(SyntaxNodeAnalysisContext context) { var methodDeclaration = (MethodDeclarationSyntax)context.Node; if (methodDeclaration.ContainsDirectives) { return; } if (methodDeclaration.ContainsDiagnostics) { return; } SyntaxTokenList modifiers = methodDeclaration.Modifiers; if (!modifiers.Contains(SyntaxKind.OverrideKeyword)) { return; } if (modifiers.ContainsAny(SyntaxKind.SealedKeyword, SyntaxKind.PartialKeyword)) { return; } if (methodDeclaration.AttributeLists.Any()) { return; } if (methodDeclaration.HasDocumentationComment()) { return; } if (!methodDeclaration.DescendantTrivia(methodDeclaration.Span).All(f => f.IsWhitespaceOrEndOfLineTrivia())) { return; } ExpressionSyntax expression = GetMethodExpression(methodDeclaration); MemberInvocationExpressionInfo invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo(expression); if (!invocationInfo.Success) { return; } if (!invocationInfo.Expression.IsKind(SyntaxKind.BaseExpression)) { return; } IMethodSymbol methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodDeclaration, context.CancellationToken); IMethodSymbol overriddenMethod = methodSymbol?.OverriddenMethod; if (overriddenMethod == null) { return; } ISymbol symbol = context.SemanticModel.GetSymbol(invocationInfo.Name, context.CancellationToken); if (!overriddenMethod.Equals(symbol)) { return; } if (!CheckParameters(methodDeclaration.ParameterList, invocationInfo.ArgumentList, context.SemanticModel, context.CancellationToken)) { return; } if (!CheckDefaultValues(methodSymbol.Parameters, overriddenMethod.Parameters)) { return; } context.ReportDiagnostic( DiagnosticDescriptors.RemoveRedundantOverridingMember, methodDeclaration, methodDeclaration.GetTitle()); }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindFirstAncestorOrSelf(root, context.Span, out SyntaxNode node, predicate: f => f.IsKind( SyntaxKind.EqualsExpression, SyntaxKind.NotEqualsExpression, SyntaxKind.InvocationExpression))) { return; } Diagnostic diagnostic = context.Diagnostics[0]; switch (node.Kind()) { case SyntaxKind.EqualsExpression: case SyntaxKind.NotEqualsExpression: { var binaryExpression = (BinaryExpressionSyntax)node; MemberInvocationExpressionInfo invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo(binaryExpression.Left.WalkDownParentheses()); if (!invocationInfo.Success) { invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo((InvocationExpressionSyntax)binaryExpression.Right.WalkDownParentheses()); } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); INamedTypeSymbol comparisonSymbol = semanticModel.GetTypeByMetadataName(MetadataNames.System_StringComparison); if (!invocationInfo.NameText.EndsWith("Invariant", StringComparison.Ordinal) || !RegisterCodeFix(context, diagnostic, binaryExpression, comparisonSymbol, "InvariantCultureIgnoreCase")) { RegisterCodeFix(context, diagnostic, binaryExpression, comparisonSymbol, "OrdinalIgnoreCase"); RegisterCodeFix(context, diagnostic, binaryExpression, comparisonSymbol, "CurrentCultureIgnoreCase"); } break; } case SyntaxKind.InvocationExpression: { var invocationExpression = (InvocationExpressionSyntax)node; MemberInvocationExpressionInfo invocationInfo = SyntaxInfo.MemberInvocationExpressionInfo(invocationExpression); SeparatedSyntaxList <ArgumentSyntax> arguments = invocationInfo.Arguments; InvocationExpressionSyntax invocationExpression2; if (arguments.Count == 1) { invocationExpression2 = (InvocationExpressionSyntax)invocationInfo.Expression; } else { invocationExpression2 = (arguments[0].Expression.WalkDownParentheses() as InvocationExpressionSyntax) ?? (InvocationExpressionSyntax)arguments[1].Expression.WalkDownParentheses(); } MemberInvocationExpressionInfo invocationInfo2 = SyntaxInfo.MemberInvocationExpressionInfo(invocationExpression2); SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); INamedTypeSymbol comparisonSymbol = semanticModel.GetTypeByMetadataName(MetadataNames.System_StringComparison); if (!invocationInfo2.NameText.EndsWith("Invariant", StringComparison.Ordinal) || !RegisterCodeFix(context, diagnostic, invocationInfo, comparisonSymbol, "InvariantCultureIgnoreCase")) { RegisterCodeFix(context, diagnostic, invocationInfo, comparisonSymbol, "OrdinalIgnoreCase"); RegisterCodeFix(context, diagnostic, invocationInfo, comparisonSymbol, "CurrentCultureIgnoreCase"); } break; } } }
private static void Analyze( SyntaxNodeAnalysisContext context, MemberInvocationExpressionInfo invocationInfo, MemberInvocationExpressionInfo invocationInfo2) { if (invocationInfo2.InvocationExpression.SpanContainsDirectives()) { return; } string name2 = invocationInfo2.NameText; if (name2 != "Equals" && name2 != "StartsWith" && name2 != "EndsWith" && name2 != "IndexOf" && name2 != "LastIndexOf" && name2 != "Contains") { return; } SeparatedSyntaxList <ArgumentSyntax> arguments = invocationInfo2.Arguments; if (arguments.Count != 1) { return; } ExpressionSyntax argumentExpression = arguments[0].Expression.WalkDownParentheses(); string name = invocationInfo.NameText; MemberInvocationExpressionInfo invocationInfo3; bool isStringLiteral = argumentExpression.IsKind(SyntaxKind.StringLiteralExpression); if (!isStringLiteral) { invocationInfo3 = SyntaxInfo.MemberInvocationExpressionInfo(argumentExpression); if (!invocationInfo3.Success) { return; } string name3 = invocationInfo3.NameText; if (name != name3) { return; } } SemanticModel semanticModel = context.SemanticModel; CancellationToken cancellationToken = context.CancellationToken; if (!CheckSymbol(invocationInfo, semanticModel, cancellationToken)) { return; } if (!(semanticModel.TryGetMethodInfo(invocationInfo2.InvocationExpression, out MethodInfo info, cancellationToken) && info.IsPublicInstanceStringMethod(name2) && info.ReturnType.SpecialType == ((name2.EndsWith("IndexOf", StringComparison.Ordinal)) ? SpecialType.System_Int32 : SpecialType.System_Boolean) && info.HasParameter(SpecialType.System_String))) { return; } if (!isStringLiteral && !CheckSymbol(invocationInfo3, semanticModel, cancellationToken)) { return; } ReportDiagnostic(context, invocationInfo2); }