private static void AnalyzeUsingStatement(SyntaxNodeAnalysisContext context) { var usingStatement = (UsingStatementSyntax)context.Node; VariableDeclaratorSyntax declarator = usingStatement.Declaration?.Variables.SingleOrDefault(shouldThrow: false); if (declarator == null) { return; } if (!(usingStatement.Statement?.SingleNonBlockStatementOrDefault() is WhileStatementSyntax whileStatement)) { return; } SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(whileStatement.Condition); if (!invocationInfo.Success) { return; } if (invocationInfo.Arguments.Any()) { return; } if (!string.Equals(invocationInfo.NameText, WellKnownMemberNames.MoveNextMethodName, StringComparison.Ordinal)) { return; } if (!string.Equals((invocationInfo.Expression as IdentifierNameSyntax)?.Identifier.ValueText, declarator.Identifier.ValueText, StringComparison.Ordinal)) { return; } SimpleMemberInvocationExpressionInfo invocationInfo2 = SyntaxInfo.SimpleMemberInvocationExpressionInfo(declarator.Initializer.Value); if (!invocationInfo2.Success) { return; } if (invocationInfo2.Arguments.Any()) { return; } if (!string.Equals(invocationInfo2.NameText, WellKnownMemberNames.GetEnumeratorMethodName, StringComparison.Ordinal)) { return; } bool?isFixable; UnnecessaryUsageOfEnumeratorWalker walker = null; try { walker = UnnecessaryUsageOfEnumeratorWalker.GetInstance(); walker.SetValues(declarator, context.SemanticModel, context.CancellationToken); walker.Visit(whileStatement.Statement); isFixable = walker.IsFixable; } finally { if (walker != null) { UnnecessaryUsageOfEnumeratorWalker.Free(walker); } } if (isFixable == true) { DiagnosticHelpers.ReportDiagnostic(context, DiagnosticRules.UnnecessaryExplicitUseOfEnumerator, usingStatement.UsingKeyword); } }
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.InvocationExpression, SyntaxKind.EqualsExpression, SyntaxKind.NotEqualsExpression, SyntaxKind.IsPatternExpression, SyntaxKind.ConditionalExpression))) { return; } Diagnostic diagnostic = context.Diagnostics[0]; Document document = context.Document; CancellationToken cancellationToken = context.CancellationToken; SyntaxKind kind = node.Kind(); if (kind == SyntaxKind.InvocationExpression) { var invocation = (InvocationExpressionSyntax)node; SimpleMemberInvocationExpressionInfo invocationInfo = SimpleMemberInvocationExpressionInfo(invocation); if (diagnostic.Properties.TryGetValue("Name", out string name) && name == "SimplifyLinqMethodChain") { SimpleMemberInvocationExpressionInfo invocationInfo2 = SimpleMemberInvocationExpressionInfo(invocationInfo.Expression); CodeAction codeAction = CodeAction.Create( $"Combine '{invocationInfo2.NameText}' and '{invocationInfo.NameText}'", ct => SimplifyLinqMethodChainAsync(document, invocationInfo, ct), GetEquivalenceKey(diagnostic, "SimplifyLinqMethodChain")); context.RegisterCodeFix(codeAction, diagnostic); return; } switch (invocationInfo.NameText) { case "Cast": { CodeAction codeAction = CodeAction.Create( "Call 'OfType' instead of 'Where' and 'Cast'", ct => CallOfTypeInsteadOfWhereAndCastAsync(document, invocationInfo, ct), GetEquivalenceKey(diagnostic, "CallOfTypeInsteadOfWhereAndCast")); context.RegisterCodeFix(codeAction, diagnostic); return; } case "Any": { CodeAction codeAction = CodeAction.Create( "Combine 'Where' and 'Any'", ct => CombineWhereAndAnyAsync(document, invocationInfo, ct), GetEquivalenceKey(diagnostic, "CombineWhereAndAny")); context.RegisterCodeFix(codeAction, diagnostic); return; } case "ToList": case "ToImmutableArray": { CodeAction codeAction = CodeAction.Create( "Call 'ConvertAll'", ct => CallConvertAllInsteadOfSelectAsync(document, invocationInfo, ct), GetEquivalenceKey(diagnostic, "CallConvertAllInsteadOfSelect")); context.RegisterCodeFix(codeAction, diagnostic); return; } case "OfType": { TypeSyntax typeArgument = ((GenericNameSyntax)invocationInfo.Name).TypeArgumentList.Arguments.Single(); SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (semanticModel.GetTypeSymbol(typeArgument, cancellationToken).IsValueType) { CodeAction codeAction = CodeAction.Create( "Remove redundant 'OfType' call", ct => { ExpressionSyntax newNode = invocationInfo.Expression.WithTrailingTrivia(invocation.GetTrailingTrivia()); return(document.ReplaceNodeAsync(invocation, newNode, ct)); }, GetEquivalenceKey(diagnostic, "RemoveRedundantOfTypeCall")); context.RegisterCodeFix(codeAction, diagnostic); } else { CodeAction codeAction = CodeAction.Create( "Call 'Where' instead of 'OfType'", ct => CallWhereInsteadOfOfTypeAsync(document, invocationInfo, ct), GetEquivalenceKey(diagnostic, "CallWhereInsteadOfOfType")); context.RegisterCodeFix(codeAction, diagnostic); } return; } case "Reverse": { CodeAction codeAction = CodeAction.Create( "Call 'OrderByDescending'", ct => CallOrderByDescendingInsteadOfOrderByAndReverseAsync(document, invocationInfo, ct), GetEquivalenceKey(diagnostic, "CallOrderByDescendingInsteadOfOrderByAndReverse")); context.RegisterCodeFix(codeAction, diagnostic); return; } case "Where": { SimpleMemberInvocationExpressionInfo invocationInfo2 = SimpleMemberInvocationExpressionInfo( invocationInfo.Expression); CodeAction codeAction = CodeAction.Create( $"Call '{invocationInfo2.NameText}' and 'Where' in reverse order", ct => CallOrderByAndWhereInReverseOrderAsync(document, invocationInfo, invocationInfo2, ct), GetEquivalenceKey(diagnostic, "CallOrderByAndWhereInReverseOrder")); context.RegisterCodeFix(codeAction, diagnostic); return; } case "Select": { CodeAction codeAction = CodeAction.Create( "Call 'Cast' instead of 'Select'", ct => CallCastInsteadOfSelectAsync(document, invocation, ct), GetEquivalenceKey(diagnostic, "CallCastInsteadOfSelect")); context.RegisterCodeFix(codeAction, diagnostic); return; } case "FirstOrDefault": { SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); CodeAction codeAction = CodeAction.Create( "Call 'Find' instead of 'FirstOrDefault'", ct => CallFindInsteadOfFirstOrDefaultAsync(document, invocationInfo, semanticModel, ct), GetEquivalenceKey(diagnostic, "CallFindInsteadOfFirstOrDefault")); context.RegisterCodeFix(codeAction, diagnostic); return; } case "First": { if (diagnostic.Properties.TryGetValue("MethodName", out string methodName) && methodName == "Peek") { CodeAction codeAction = CodeAction.Create( "Call 'Peek' instead of 'First'", ct => document.ReplaceNodeAsync(invocation, ChangeInvokedMethodName(invocation, "Peek"), ct), GetEquivalenceKey(diagnostic, "CallPeekInsteadOfFirst")); context.RegisterCodeFix(codeAction, diagnostic); } else { CodeAction codeAction = CodeAction.Create( "Use [] instead of calling 'First'", ct => UseElementAccessInsteadOfEnumerableMethodRefactoring.UseElementAccessInsteadOfFirstAsync(context.Document, invocation, ct), GetEquivalenceKey(diagnostic, "UseElementAccessInsteadOfFirst")); context.RegisterCodeFix(codeAction, diagnostic); } return; } case "Count": { if (diagnostic.Properties.TryGetValue("PropertyName", out string propertyName)) { if (diagnostic.Properties.TryGetValue("MethodName", out string methodName) && methodName == "Sum") { CodeAction codeAction = CodeAction.Create( "Call 'Sum'", ct => CallSumInsteadOfSelectManyAndCountAsync(document, invocationInfo, propertyName, ct), GetEquivalenceKey(diagnostic, "CallSumInsteadOfSelectManyAndCount")); context.RegisterCodeFix(codeAction, diagnostic); } else { CodeAction codeAction = CodeAction.Create( $"Use '{propertyName}' property instead of calling 'Count'", ct => UseCountOrLengthPropertyInsteadOfCountMethodAsync(document, invocation, diagnostic.Properties["PropertyName"], ct), GetEquivalenceKey(diagnostic, "UseCountOrLengthPropertyInsteadOfCountMethod")); context.RegisterCodeFix(codeAction, diagnostic); } } else if (invocation.Parent is BinaryExpressionSyntax binaryExpression) { CodeAction codeAction = CodeAction.Create( "Call 'Any' instead of 'Count'", ct => CallAnyInsteadOfCountAsync(document, invocation, binaryExpression, ct), GetEquivalenceKey(diagnostic, "CallAnyInsteadOfCount")); context.RegisterCodeFix(codeAction, diagnostic); } return; } case "ElementAt": { CodeAction codeAction = CodeAction.Create( "Use [] instead of calling 'ElementAt'", ct => UseElementAccessInsteadOfEnumerableMethodRefactoring.UseElementAccessInsteadOfElementAtAsync(document, invocation, ct), GetEquivalenceKey(diagnostic, "UseElementAccessInsteadOfElementAt")); context.RegisterCodeFix(codeAction, diagnostic); return; } } } else if (kind == SyntaxKind.ConditionalExpression) { CodeAction codeAction = CodeAction.Create( "Call 'FirstOrDefault' instead of ?:", ct => CallFirstOrDeafultInsteadOfConditionalExpressionAsync(document, (ConditionalExpressionSyntax)node, ct), GetEquivalenceKey(diagnostic, "CallFirstOrDefaultInsteadOfConditionalExpression")); context.RegisterCodeFix(codeAction, diagnostic); } else if (kind.Is( SyntaxKind.EqualsExpression, SyntaxKind.NotEqualsExpression, SyntaxKind.IsPatternExpression)) { CodeAction codeAction = CodeAction.Create( "Call 'Any' instead of 'FirstOrDefault'", ct => CallAnyInsteadOfFirstOrDefaultAsync(document, node, ct), GetEquivalenceKey(diagnostic, "CallAnyInsteadOfFirstOrDefault")); context.RegisterCodeFix(codeAction, diagnostic); } }
private static void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context) { var invocation = (InvocationExpressionSyntax)context.Node; if (invocation.ContainsDiagnostics) { return; } SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(invocation); if (!invocationInfo.Success) { return; } string methodName = invocationInfo.NameText; int argumentCount = invocationInfo.Arguments.Count; switch (argumentCount) { case 0: { switch (methodName) { case "Any": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.UseCountOrLengthPropertyInsteadOfAnyMethod)) { UseCountOrLengthPropertyInsteadOfAnyMethodAnalysis.Analyze(context, invocationInfo); } if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.OptimizeLinqMethodCall)) { OptimizeLinqMethodCallAnalysis.AnalyzeWhere(context, invocationInfo); OptimizeLinqMethodCallAnalysis.AnalyzeAny(context, invocationInfo); } break; } case "Cast": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.OptimizeLinqMethodCall)) { OptimizeLinqMethodCallAnalysis.AnalyzeWhereAndCast(context, invocationInfo); } if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.RemoveRedundantCast)) { RemoveRedundantCastAnalyzer.Analyze(context, invocationInfo); } break; } case "Count": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.OptimizeLinqMethodCall)) { OptimizeLinqMethodCallAnalysis.AnalyzeCount(context, invocationInfo); OptimizeLinqMethodCallAnalysis.AnalyzeWhere(context, invocationInfo); } break; } case "First": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.OptimizeLinqMethodCall)) { if (!invocationInfo.Expression.IsKind(SyntaxKind.InvocationExpression) && UseElementAccessAnalysis.IsFixableFirst(invocationInfo, context.SemanticModel, context.CancellationToken)) { DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.OptimizeLinqMethodCall, Location.Create(invocation.SyntaxTree, TextSpan.FromBounds(invocationInfo.Name.SpanStart, invocationInfo.ArgumentList.Span.End))); } OptimizeLinqMethodCallAnalysis.AnalyzeWhere(context, invocationInfo); OptimizeLinqMethodCallAnalysis.AnalyzeFirst(context, invocationInfo); } break; } case "Max": case "Min": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.OptimizeLinqMethodCall)) { OptimizeLinqMethodCallAnalysis.AnalyzeSelectAndMinOrMax(context, invocationInfo); } break; } case "ToString": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.RemoveRedundantToStringCall)) { RemoveRedundantToStringCallAnalysis.Analyze(context, invocationInfo); } if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.UseNameOfOperator) && ((CSharpCompilation)context.Compilation).LanguageVersion >= LanguageVersion.CSharp6) { UseNameOfOperatorAnalyzer.Analyze(context, invocationInfo); } break; } case "ToLower": case "ToLowerInvariant": case "ToUpper": case "ToUpperInvariant": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.UseStringComparison)) { UseStringComparisonAnalysis.Analyze(context, invocationInfo); } break; } case "FirstOrDefault": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.OptimizeLinqMethodCall)) { OptimizeLinqMethodCallAnalysis.AnalyzeWhere(context, invocationInfo); OptimizeLinqMethodCallAnalysis.AnalyzeFirstOrDefault(context, invocationInfo); } break; } case "Last": case "LastOrDefault": case "LongCount": case "Single": case "SingleOrDefault": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.OptimizeLinqMethodCall)) { OptimizeLinqMethodCallAnalysis.AnalyzeWhere(context, invocationInfo); } break; } case "OfType": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.OptimizeLinqMethodCall) && !invocation.SpanContainsDirectives()) { OptimizeLinqMethodCallAnalysis.AnalyzeOfType(context, invocationInfo); } break; } case "ToCharArray": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.RemoveRedundantStringToCharArrayCall)) { RemoveRedundantStringToCharArrayCallAnalysis.Analyze(context, invocationInfo); } break; } } break; } case 1: { switch (methodName) { case "All": case "Any": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.SimplifyLogicalNegation)) { SimplifyLogicalNegationAnalyzer.Analyze(context, invocationInfo); } if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.OptimizeLinqMethodCall) && !invocation.SpanContainsDirectives()) { OptimizeLinqMethodCallAnalysis.AnalyzeWhereAndAny(context, invocationInfo); } break; } case "ContainsKey": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.OptimizeMethodCall)) { OptimizeMethodCallAnalysis.OptimizeDictionaryContainsKey(context, invocationInfo); } break; } case "ElementAt": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.OptimizeLinqMethodCall) && !invocationInfo.Expression.IsKind(SyntaxKind.InvocationExpression) && UseElementAccessAnalysis.IsFixableElementAt(invocationInfo, context.SemanticModel, context.CancellationToken)) { DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.OptimizeLinqMethodCall, Location.Create(invocation.SyntaxTree, TextSpan.FromBounds(invocationInfo.Name.SpanStart, invocationInfo.ArgumentList.Span.End))); } break; } case "FirstOrDefault": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.OptimizeLinqMethodCall)) { OptimizeLinqMethodCallAnalysis.AnalyzeFirstOrDefault(context, invocationInfo); } break; } case "GetValueOrDefault": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.UseCoalesceExpression) && invocationInfo.Name.IsKind(SyntaxKind.IdentifierName) && !invocation.IsParentKind(SyntaxKind.InvocationExpression, SyntaxKind.SimpleMemberAccessExpression, SyntaxKind.ElementAccessExpression) && context.SemanticModel .GetMethodSymbol(invocationInfo.InvocationExpression, context.CancellationToken)? .ContainingType .OriginalDefinition .SpecialType == SpecialType.System_Nullable_T) { DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.UseCoalesceExpression, invocationInfo.Name); } break; } case "Where": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.CombineEnumerableWhereMethodChain)) { CombineEnumerableWhereMethodChainAnalysis.Analyze(context, invocationInfo); } break; } case "HasFlag": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.UseBitwiseOperationInsteadOfCallingHasFlag) && !invocation.SpanContainsDirectives() && UseBitwiseOperationInsteadOfCallingHasFlagAnalysis.IsFixable(invocationInfo, context.SemanticModel, context.CancellationToken)) { DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.UseBitwiseOperationInsteadOfCallingHasFlag, invocation); } break; } case "Select": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.OptimizeLinqMethodCall)) { CallCastInsteadOfSelectAnalysis.Analyze(context, invocationInfo); } break; } case "OrderBy": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.CallThenByInsteadOfOrderBy)) { CallThenByInsteadOfOrderByAnalysis.Analyze(context, invocationInfo); } break; } } break; } case 2: { switch (invocationInfo.NameText) { case "IsMatch": case "Match": case "Matches": case "Split": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.UseRegexInstanceInsteadOfStaticMethod) && !invocation.SpanContainsDirectives()) { UseRegexInstanceInsteadOfStaticMethodAnalysis.Analyze(context, invocationInfo); } break; } case "Select": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.OptimizeLinqMethodCall)) { CallCastInsteadOfSelectAnalysis.Analyze(context, invocationInfo); } break; } case "OrderBy": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.CallThenByInsteadOfOrderBy)) { CallThenByInsteadOfOrderByAnalysis.Analyze(context, invocationInfo); } break; } } break; } case 3: { switch (invocationInfo.NameText) { case "IsMatch": case "Match": case "Matches": case "Split": case "Replace": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.UseRegexInstanceInsteadOfStaticMethod) && !invocation.SpanContainsDirectives()) { UseRegexInstanceInsteadOfStaticMethodAnalysis.Analyze(context, invocationInfo); } break; } case "OrderBy": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.CallThenByInsteadOfOrderBy)) { CallThenByInsteadOfOrderByAnalysis.Analyze(context, invocationInfo); } break; } case "Compare": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.OptimizeMethodCall)) { OptimizeMethodCallAnalysis.OptimizeStringCompare(context, invocationInfo); } break; } } break; } case 4: { switch (invocationInfo.NameText) { case "Replace": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.UseRegexInstanceInsteadOfStaticMethod) && !invocation.SpanContainsDirectives()) { UseRegexInstanceInsteadOfStaticMethodAnalysis.Analyze(context, invocationInfo); } break; } } break; } } switch (methodName) { case "ElementAtOrDefault": case "FirstOrDefault": case "LastOrDefault": case "SingleOrDefault": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.AvoidNullReferenceException)) { if (argumentCount == 0 || argumentCount == 1 || argumentCount == 2) { AvoidNullReferenceExceptionAnalyzer.Analyze(context, invocationInfo); } } break; } case "Append": case "AppendLine": case "AppendFormat": case "Insert": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.OptimizeStringBuilderAppendCall)) { OptimizeStringBuilderAppendCallAnalysis.Analyze(context, invocationInfo); } if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.AvoidBoxingOfValueType)) { AvoidBoxingOfValueTypeAnalysis.Analyze(context, invocationInfo); } break; } case "Assert": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.OptimizeMethodCall) && (argumentCount >= 1 && argumentCount <= 3)) { OptimizeMethodCallAnalysis.OptimizeDebugAssert(context, invocationInfo); } break; } case "Join": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.OptimizeMethodCall) && argumentCount >= 2) { OptimizeMethodCallAnalysis.OptimizeStringJoin(context, invocationInfo); } break; } } if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.UseMethodChaining) && UseMethodChainingAnalysis.IsFixable(invocationInfo, context.SemanticModel, context.CancellationToken)) { DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.UseMethodChaining, invocationInfo.InvocationExpression); } }
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; SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(binaryExpression.Left.WalkDownParentheses()); if (!invocationInfo.Success) { invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo((InvocationExpressionSyntax)binaryExpression.Right.WalkDownParentheses()); } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); INamedTypeSymbol comparisonSymbol = semanticModel.GetTypeByMetadataName("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; SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(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(); } SimpleMemberInvocationExpressionInfo invocationInfo2 = SyntaxInfo.SimpleMemberInvocationExpressionInfo(invocationExpression2); SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); INamedTypeSymbol comparisonSymbol = semanticModel.GetTypeByMetadataName("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; } } }
public static async Task ComputeRefactoringsAsync(RefactoringContext context, InvocationExpressionSyntax invocation) { SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(invocation); if (!invocationInfo.Success) { return; } switch (invocationInfo.NameText) { case "First": { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (invocationInfo.Arguments.Any()) { break; } if (!UseElementAccessInsteadOfFirstAnalysis.IsFixable(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 (!UseElementAccessInsteadOfLastAnalysis.IsFixable(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 (!UseElementAccessInsteadOfElementAtAnalysis.IsFixable(invocationInfo, semanticModel, context.CancellationToken)) { break; } context.RegisterRefactoring( "Use [] instead of calling 'ElementAt'", cancellationToken => UseElementAccessInsteadOfElementAtRefactoring.RefactorAsync(context.Document, invocation, cancellationToken)); break; } } }
public static async Task <Document> RefactorAsync( Document document, ArgumentSyntax argument, SimpleMemberInvocationExpressionInfo invocationInfo, CancellationToken cancellationToken) { SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); InvocationExpressionSyntax invocation = invocationInfo.InvocationExpression; InvocationExpressionSyntax newInvocation = null; bool isAppendLine = string.Equals(invocationInfo.NameText, "AppendLine", StringComparison.Ordinal); ExpressionSyntax expression = argument.Expression; switch (expression.Kind()) { case SyntaxKind.InterpolatedStringExpression: { newInvocation = ConvertInterpolatedStringExpressionToInvocationExpression((InterpolatedStringExpressionSyntax)argument.Expression, invocationInfo, semanticModel); break; } case SyntaxKind.AddExpression: { ImmutableArray <ExpressionSyntax> expressions = SyntaxInfo.BinaryExpressionInfo((BinaryExpressionSyntax)expression) .Expressions(leftToRight: true) .ToImmutableArray(); newInvocation = invocation .ReplaceNode(invocationInfo.Name, IdentifierName("Append").WithTriviaFrom(invocationInfo.Name)) .WithArgumentList(invocation.ArgumentList.WithArguments(SingletonSeparatedList(Argument(expressions[0]))).WithoutTrailingTrivia()); for (int i = 1; i < expressions.Length; i++) { ExpressionSyntax argumentExpression = expressions[i]; string methodName; if (i == expressions.Length - 1 && isAppendLine && semanticModel .GetTypeInfo(argumentExpression, cancellationToken) .ConvertedType? .SpecialType == SpecialType.System_String) { methodName = "AppendLine"; } else { methodName = "Append"; } newInvocation = SimpleMemberInvocationExpression( newInvocation, IdentifierName(methodName), ArgumentList(Argument(argumentExpression))); if (i == expressions.Length - 1 && isAppendLine && !string.Equals(methodName, "AppendLine", StringComparison.Ordinal)) { newInvocation = SimpleMemberInvocationExpression( newInvocation, IdentifierName("AppendLine"), ArgumentList()); } } break; } default: { newInvocation = CreateInvocationExpression( (InvocationExpressionSyntax)expression, invocation); if (isAppendLine) { newInvocation = SimpleMemberInvocationExpression(newInvocation, IdentifierName("AppendLine"), ArgumentList()); } break; } } newInvocation = newInvocation .WithTriviaFrom(invocation) .WithFormatterAnnotation(); return(await document.ReplaceNodeAsync(invocation, newInvocation, cancellationToken).ConfigureAwait(false)); }
public static void Analyze( SyntaxNodeAnalysisContext context, SimpleMemberInvocationExpressionInfo invocationInfo) { InvocationExpressionSyntax invocationExpression = invocationInfo.InvocationExpression; if (invocationInfo.Name.SpanOrTrailingTriviaContainsDirectives()) { return; } if (invocationInfo.ArgumentList.SpanOrLeadingTriviaContainsDirectives()) { return; } SemanticModel semanticModel = context.SemanticModel; CancellationToken cancellationToken = context.CancellationToken; ExtensionMethodSymbolInfo extensionInfo = semanticModel.GetExtensionMethodInfo(invocationExpression, cancellationToken); if (extensionInfo.Symbol == null) { return; } if (!SymbolUtility.IsLinqSelect(extensionInfo.Symbol, semanticModel, allowImmutableArrayExtension: true)) { return; } ITypeSymbol typeArgument = extensionInfo.ReducedSymbolOrSymbol.TypeArguments[0]; if (!typeArgument.IsReferenceType) { return; } if (typeArgument.SpecialType == SpecialType.System_Object) { return; } ExpressionSyntax expression = invocationExpression.ArgumentList?.Arguments.Last().Expression; SingleParameterLambdaExpressionInfo lambdaInfo = SyntaxInfo.SingleParameterLambdaExpressionInfo(expression); if (!lambdaInfo.Success) { return; } CastExpressionSyntax castExpression = GetCastExpression(lambdaInfo.Body); if (castExpression == null) { return; } if (!(castExpression.Expression is IdentifierNameSyntax identifierName)) { return; } if (!string.Equals(lambdaInfo.Parameter.Identifier.ValueText, identifierName.Identifier.ValueText, StringComparison.Ordinal)) { return; } var castSymbol = semanticModel.GetSymbol(castExpression, cancellationToken) as IMethodSymbol; if (castSymbol?.MethodKind == MethodKind.Conversion) { return; } context.ReportDiagnostic( DiagnosticDescriptors.CallCastInsteadOfSelect, Location.Create(invocationExpression.SyntaxTree, TextSpan.FromBounds(invocationInfo.Name.SpanStart, invocationExpression.Span.End))); }
public static bool IsConfigureAwait(ExpressionSyntax expression) { SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(expression); return(IsConfigureAwait(invocationInfo)); }
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.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.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 => context.Document.ReplaceNodeAsync(invocation, RemoveInvocation(invocation).WithFormatterAnnotation(), cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.RemoveRedundantStringToCharArrayCall: { CodeAction codeAction = CodeAction.Create( "Remove redundant 'ToCharArray' call", cancellationToken => context.Document.ReplaceNodeAsync(invocation, RemoveInvocation(invocation).WithFormatterAnnotation(), 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: { SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(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 static void Analyze(SyntaxNodeAnalysisContext context, SimpleMemberInvocationExpressionInfo invocationInfo) { InvocationExpressionSyntax invocation = invocationInfo.InvocationExpression; ArgumentSyntax firstArgument = invocationInfo.Arguments.FirstOrDefault(); if (firstArgument == null) { return; } if (invocationInfo.NameText != "Join") { return; } if (invocationInfo.MemberAccessExpression.SpanOrTrailingTriviaContainsDirectives() || invocationInfo.ArgumentList.OpenParenToken.ContainsDirectives || firstArgument.ContainsDirectives) { return; } SemanticModel semanticModel = context.SemanticModel; CancellationToken cancellationToken = context.CancellationToken; IMethodSymbol methodSymbol = semanticModel.GetMethodSymbol(invocation, cancellationToken); if (!SymbolUtility.IsPublicStaticNonGeneric(methodSymbol, "Join")) { return; } if (methodSymbol.ContainingType?.SpecialType != SpecialType.System_String) { return; } if (!methodSymbol.IsReturnType(SpecialType.System_String)) { return; } ImmutableArray <IParameterSymbol> parameters = methodSymbol.Parameters; if (parameters.Length != 2) { return; } if (parameters[0].Type.SpecialType != SpecialType.System_String) { return; } if (!parameters[1].IsParameterArrayOf(SpecialType.System_String, SpecialType.System_Object) && !parameters[1].Type.OriginalDefinition.IsIEnumerableOfT()) { return; } if (firstArgument.Expression == null) { return; } if (!CSharpUtility.IsEmptyStringExpression(firstArgument.Expression, semanticModel, cancellationToken)) { return; } context.ReportDiagnostic(DiagnosticDescriptors.CallStringConcatInsteadOfStringJoin, invocationInfo.Name); }
public static async Task <Document> RefactorAsync( Document document, UseMethodChainingAnalysis analysis, ExpressionStatementSyntax expressionStatement, CancellationToken cancellationToken) { SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); InvocationExpressionSyntax invocationExpression = GetInvocationExpression(expressionStatement); SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(invocationExpression); ITypeSymbol returnType = semanticModel.GetMethodSymbol(invocationExpression, cancellationToken).ReturnType; string name = ((IdentifierNameSyntax)UseMethodChainingAnalysis.WalkDownMethodChain(invocationInfo).Expression).Identifier.ValueText; StatementListInfo statementsInfo = SyntaxInfo.StatementListInfo(expressionStatement); SyntaxList <StatementSyntax> statements = statementsInfo.Statements; int index = statements.IndexOf(expressionStatement); string indentation = expressionStatement.GetIncreasedIndentation(cancellationToken).ToString(); var sb = new StringBuilder(invocationExpression.ToString()); int j = index; while (j < statements.Count - 1) { StatementSyntax statement = statements[j + 1]; if (!analysis.IsFixableStatement(statement, name, returnType, 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 .Parent .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) .WithFormatterAndSimplifierAnnotation(); newStatements = newStatements.ReplaceAt(index, newExpressionStatement); return(await document.ReplaceStatementsAsync(statementsInfo, newStatements, cancellationToken).ConfigureAwait(false)); }
public static async Task ComputeRefactoringsAsync(RefactoringContext context, InvocationExpressionSyntax invocation) { if (invocation.IsParentKind(SyntaxKind.ExpressionStatement)) { return; } SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(invocation); if (!invocationInfo.Success) { return; } switch (invocationInfo.NameText) { case "First": { if (invocationInfo.Arguments.Any()) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (!UseElementAccessAnalysis.IsFixableFirst(invocationInfo, semanticModel, context.CancellationToken)) { break; } context.RegisterRefactoring( "Use [] instead of calling 'First'", cancellationToken => UseElementAccessInsteadOfEnumerableMethodRefactoring.UseElementAccessInsteadOfFirstAsync(context.Document, invocation, cancellationToken), RefactoringIdentifiers.UseElementAccessInsteadOfEnumerableMethod); break; } case "Last": { if (invocationInfo.Arguments.Any()) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (!UseElementAccessAnalysis.IsFixableLast(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 => UseElementAccessInsteadOfEnumerableMethodRefactoring.UseElementAccessInsteadOfLastAsync(context.Document, invocation, propertyName, cancellationToken), RefactoringIdentifiers.UseElementAccessInsteadOfEnumerableMethod); break; } case "ElementAt": { if (invocationInfo.Arguments.SingleOrDefault(shouldThrow: false)?.Expression?.IsMissing != false) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (!UseElementAccessAnalysis.IsFixableElementAt(invocationInfo, semanticModel, context.CancellationToken)) { break; } context.RegisterRefactoring( "Use [] instead of calling 'ElementAt'", cancellationToken => UseElementAccessInsteadOfEnumerableMethodRefactoring.UseElementAccessInsteadOfElementAtAsync(context.Document, invocation, cancellationToken), RefactoringIdentifiers.UseElementAccessInsteadOfEnumerableMethod); break; } } }
private static Task <Document> UsePatternMatchingAsync( Document document, SwitchStatementSyntax switchStatement, CancellationToken cancellationToken) { SyntaxList <SwitchSectionSyntax> newSections = switchStatement.Sections.Select(section => { if (!(section.Labels.Single() is CaseSwitchLabelSyntax label)) { return(section); } SyntaxList <StatementSyntax> statements = section.Statements; StatementSyntax statement = statements[0]; if (statement is BlockSyntax block) { statement = block.Statements.FirstOrDefault(); } SingleLocalDeclarationStatementInfo localInfo = SyntaxInfo.SingleLocalDeclarationStatementInfo((LocalDeclarationStatementSyntax)statement); var castExpression = (CastExpressionSyntax)localInfo.Value; CasePatternSwitchLabelSyntax newLabel = CasePatternSwitchLabel( DeclarationPattern( castExpression.Type, SingleVariableDesignation(localInfo.Identifier)), label.ColonToken); SwitchSectionSyntax newSection = section.RemoveStatement(localInfo.Statement); newSection = newSection.WithLabels(newSection.Labels.ReplaceAt(0, newLabel)); return(newSection.WithFormatterAnnotation()); }) .ToSyntaxList(); ExpressionSyntax expression = switchStatement.Expression; ExpressionSyntax newExpression = expression; LocalDeclarationStatementSyntax localDeclaration = null; if (expression.IsKind(SyntaxKind.InvocationExpression)) { SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(expression); newExpression = invocationInfo.Expression; } else { localDeclaration = (LocalDeclarationStatementSyntax)switchStatement.PreviousStatement(); SingleLocalDeclarationStatementInfo localInfo = SyntaxInfo.SingleLocalDeclarationStatementInfo(localDeclaration); SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(localInfo.Value); newExpression = invocationInfo.Expression; } SwitchStatementSyntax newSwitchStatement = switchStatement .WithExpression(newExpression.WithTriviaFrom(expression)) .WithSections(newSections); if (localDeclaration != null) { StatementListInfo statementsInfo = SyntaxInfo.StatementListInfo(switchStatement); newSwitchStatement = newSwitchStatement.WithLeadingTrivia(localDeclaration.GetLeadingTrivia()); SyntaxList <StatementSyntax> newStatements = statementsInfo.Statements .Replace(switchStatement, newSwitchStatement) .RemoveAt(statementsInfo.IndexOf(localDeclaration)); return(document.ReplaceStatementsAsync(statementsInfo, newStatements, cancellationToken)); } else { return(document.ReplaceNodeAsync(switchStatement, newSwitchStatement, cancellationToken)); } }
public 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.InvocationExpression, SyntaxKind.EqualsExpression, SyntaxKind.NotEqualsExpression, SyntaxKind.IfStatement))) { return; } Document document = context.Document; Diagnostic diagnostic = context.Diagnostics[0]; switch (node.Kind()) { case SyntaxKind.InvocationExpression: { var invocationExpression = (InvocationExpressionSyntax)node; SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(invocationExpression); switch (invocationInfo.NameText) { case "Add": { CodeAction codeAction = CodeAction.Create( "Call 'AddRange' instead of 'Add'", ct => CallAddRangeInsteadOfAddAsync(document, invocationExpression, ct), GetEquivalenceKey(diagnostic, "CallAddRangeInsteadOfAdd")); context.RegisterCodeFix(codeAction, diagnostic); break; } case "Compare": { CodeAction codeAction = CodeAction.Create( "Call 'CompareOrdinal' instead of 'Compare'", ct => CallCompareOrdinalInsteadOfCompareAsync(document, invocationInfo, ct), GetEquivalenceKey(diagnostic, "CallCompareOrdinalInsteadOfCompare")); context.RegisterCodeFix(codeAction, diagnostic); break; } case "Join": { CodeAction codeAction = CodeAction.Create( "Call 'Concat' instead of 'Join'", ct => CallStringConcatInsteadOfStringJoinAsync(document, invocationExpression, ct), GetEquivalenceKey(diagnostic, "CallConcatInsteadOfJoin")); context.RegisterCodeFix(codeAction, diagnostic); break; } case "Assert": { CodeAction codeAction = CodeAction.Create( "Call 'Fail' instead of 'Assert'", ct => CallDebugFailInsteadOfDebugAssertAsync(context.Document, invocationExpression, ct), GetEquivalenceKey(diagnostic, "CallFailInsteadOfAssert")); context.RegisterCodeFix(codeAction, diagnostic); break; } } break; } case SyntaxKind.EqualsExpression: case SyntaxKind.NotEqualsExpression: { var equalityExpression = (BinaryExpressionSyntax)node; CodeAction codeAction = CodeAction.Create( "Call 'Equals' instead of 'Compare'", ct => CallEqualsInsteadOfCompareAsync(document, equalityExpression, ct), GetEquivalenceKey(diagnostic, "CallEqualsInsteadOfCompare")); context.RegisterCodeFix(codeAction, diagnostic); break; } case SyntaxKind.IfStatement: { var ifStatement = (IfStatementSyntax)node; CodeAction codeAction = CodeAction.Create( "Use [] instead of 'ContainsKey'", ct => UseElementAccessInsteadOfContainsKeyAsync(document, ifStatement, ct), GetEquivalenceKey(diagnostic, "UseElementAccessInsteadOfContainsKey")); context.RegisterCodeFix(codeAction, diagnostic); break; } } }
public static void Analyze(SyntaxNodeAnalysisContext context, SimpleMemberInvocationExpressionInfo invocationInfo) { SimpleMemberInvocationExpressionInfo invocationInfo2 = SyntaxInfo.SimpleMemberInvocationExpressionInfo(invocationInfo.Expression); if (!invocationInfo2.Success) { return; } ArgumentSyntax argument = invocationInfo2.Arguments.SingleOrDefault(shouldThrow: false); if (argument == null) { return; } if (!string.Equals(invocationInfo2.NameText, "Where", StringComparison.Ordinal)) { return; } SemanticModel semanticModel = context.SemanticModel; CancellationToken cancellationToken = context.CancellationToken; IMethodSymbol methodSymbol = semanticModel.GetReducedExtensionMethodInfo(invocationInfo.InvocationExpression, cancellationToken).Symbol; if (methodSymbol == null) { return; } if (!SymbolUtility.IsLinqCast(methodSymbol, semanticModel)) { return; } IMethodSymbol methodSymbol2 = semanticModel.GetReducedExtensionMethodInfo(invocationInfo2.InvocationExpression, cancellationToken).Symbol; if (methodSymbol2 == null) { return; } if (!SymbolUtility.IsLinqWhere(methodSymbol2, semanticModel)) { return; } IsExpressionInfo isExpressionInfo = SyntaxInfo.IsExpressionInfo(GetLambdaExpression(argument.Expression)); if (!isExpressionInfo.Success) { return; } TypeSyntax type2 = (invocationInfo.Name as GenericNameSyntax)?.TypeArgumentList?.Arguments.SingleOrDefault(shouldThrow: false); if (type2 == null) { return; } ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(isExpressionInfo.Type, cancellationToken); if (typeSymbol == null) { return; } ITypeSymbol typeSymbol2 = semanticModel.GetTypeSymbol(type2, cancellationToken); if (!typeSymbol.Equals(typeSymbol2)) { return; } TextSpan span = TextSpan.FromBounds(invocationInfo2.Name.SpanStart, invocationInfo.InvocationExpression.Span.End); if (invocationInfo.InvocationExpression.ContainsDirectives(span)) { return; } context.ReportDiagnostic( DiagnosticDescriptors.SimplifyLinqMethodChain, Location.Create(invocationInfo.InvocationExpression.SyntaxTree, span)); }
private static void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context) { var invocationExpression = (InvocationExpressionSyntax)context.Node; if (invocationExpression.ContainsDiagnostics) { return; } SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(invocationExpression); if (!invocationInfo.Success) { return; } if (invocationInfo.Arguments.Count != 0) { return; } if (invocationInfo.NameText != "Count") { return; } SemanticModel semanticModel = context.SemanticModel; CancellationToken cancellationToken = context.CancellationToken; IMethodSymbol methodSymbol = semanticModel.GetReducedExtensionMethodInfo(invocationExpression, cancellationToken).Symbol; if (methodSymbol == null) { return; } if (!SymbolUtility.IsLinqExtensionOfIEnumerableOfTWithoutParameters(methodSymbol, "Count")) { return; } SyntaxNode parent = invocationExpression.Parent; switch (parent.Kind()) { case SyntaxKind.GreaterThanExpression: case SyntaxKind.LessThanOrEqualExpression: { var binaryExpression = (BinaryExpressionSyntax)parent; if (binaryExpression.Left == invocationExpression) { if (!binaryExpression.Right.IsNumericLiteralExpression("0")) { ReportDiagnostic(parent); } } else if (!binaryExpression.Left.IsNumericLiteralExpression("1")) { ReportDiagnostic(parent); } break; } case SyntaxKind.GreaterThanOrEqualExpression: case SyntaxKind.LessThanExpression: { var binaryExpression = (BinaryExpressionSyntax)parent; if (binaryExpression.Left == invocationExpression) { if (!binaryExpression.Right.IsNumericLiteralExpression("1")) { ReportDiagnostic(parent); } } else if (!binaryExpression.Left.IsNumericLiteralExpression("0")) { ReportDiagnostic(parent); } break; } } void ReportDiagnostic(SyntaxNode node) { DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.CallSkipAndAnyInsteadOfCount, node); } }
public override bool IsFixableStatement( StatementSyntax statement, string name, ITypeSymbol typeSymbol, SemanticModel semanticModel, CancellationToken cancellationToken) { if (statement.SpanOrLeadingTriviaContainsDirectives()) { return(false); } if (statement is not ExpressionStatementSyntax expressionStatement) { return(false); } SimpleAssignmentExpressionInfo assignmentInfo = SyntaxInfo.SimpleAssignmentExpressionInfo(expressionStatement.Expression); if (!assignmentInfo.Success) { return(false); } if (name != (assignmentInfo.Left as IdentifierNameSyntax)?.Identifier.ValueText) { return(false); } SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(assignmentInfo.Right); if (!invocationInfo.Success) { return(false); } if (WalkDownMethodChain(invocationInfo).Expression is not IdentifierNameSyntax identifierName) { return(false); } if (name != identifierName.Identifier.ValueText) { return(false); } IMethodSymbol methodSymbol = semanticModel.GetMethodSymbol(invocationInfo.InvocationExpression, cancellationToken); if (methodSymbol == null) { return(false); } if (!SymbolEqualityComparer.Default.Equals(methodSymbol.ReturnType, typeSymbol)) { return(false); } if (IsReferenced(invocationInfo.InvocationExpression, identifierName, name, semanticModel, cancellationToken)) { return(false); } return(true); }
private static void AnalyzeMethodDeclaration(SyntaxNodeAnalysisContext context) { var methodDeclaration = (MethodDeclarationSyntax)context.Node; if (methodDeclaration.ContainsDirectives) { return; } if (methodDeclaration.ContainsDiagnostics) { return; } if (methodDeclaration.AttributeLists.Any()) { return; } if (!CheckModifiers(methodDeclaration.Modifiers)) { return; } if (methodDeclaration.HasDocumentationComment()) { return; } if (!methodDeclaration.DescendantTrivia(methodDeclaration.Span).All(f => f.IsWhitespaceOrEndOfLineTrivia())) { return; } ExpressionSyntax expression = GetMethodExpression(methodDeclaration); SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(expression); if (!invocationInfo.Success) { return; } if (invocationInfo.Expression.Kind() != SyntaxKind.BaseExpression) { return; } SemanticModel semanticModel = context.SemanticModel; CancellationToken cancellationToken = context.CancellationToken; IMethodSymbol methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration, cancellationToken); if (methodSymbol == null) { return; } IMethodSymbol overriddenMethod = methodSymbol.OverriddenMethod; if (overriddenMethod == null) { return; } ISymbol symbol = semanticModel.GetSymbol(invocationInfo.Name, cancellationToken); if (!SymbolEqualityComparer.Default.Equals(overriddenMethod, symbol)) { return; } if (!CheckParameters(methodDeclaration.ParameterList, invocationInfo.ArgumentList, semanticModel, cancellationToken)) { return; } if (!CheckDefaultValues(methodSymbol.Parameters, overriddenMethod.Parameters)) { return; } DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.RemoveRedundantOverridingMember, methodDeclaration, CSharpFacts.GetTitle(methodDeclaration)); }
private static void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context) { var invocationExpression = (InvocationExpressionSyntax)context.Node; if (invocationExpression.ContainsDiagnostics) { return; } SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(invocationExpression); if (!invocationInfo.Success) { return; } ISymbol symbol = null; string methodName = invocationInfo.NameText; switch (invocationInfo.Arguments.Count) { case 0: { switch (methodName) { case "First": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.UseElementAccess)) { UseElementAccessInsteadOfCallingFirst(); } break; } } break; } case 1: { switch (methodName) { case "ElementAt": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.UseElementAccess)) { UseElementAccessInsteadOfCallingElementAt(); } break; } case "IsKind": { if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.UnnecessaryNullCheck)) { AnalyzeUnnecessaryNullCheck(); } break; } } break; } } if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.UseReturnValue) && invocationExpression.IsParentKind(SyntaxKind.ExpressionStatement)) { UseReturnValue(); } void AnalyzeUnnecessaryNullCheck() { ExpressionSyntax expression = invocationInfo.InvocationExpression.WalkUpParentheses(); SyntaxNode parent = expression.Parent; if (!parent.IsKind(SyntaxKind.LogicalAndExpression)) { return; } var binaryExpression = (BinaryExpressionSyntax)parent; if (expression != binaryExpression.Right) { return; } if (binaryExpression.Left.ContainsDirectives) { return; } if (binaryExpression.OperatorToken.ContainsDirectives) { return; } NullCheckExpressionInfo nullCheckInfo = SyntaxInfo.NullCheckExpressionInfo(binaryExpression.Left, NullCheckStyles.CheckingNotNull & ~NullCheckStyles.HasValue); if (!nullCheckInfo.Success) { return; } if (!CSharpFactory.AreEquivalent(invocationInfo.Expression, nullCheckInfo.Expression)) { return; } if (!CSharpSymbolUtility.IsIsKindExtensionMethod(invocationExpression, context.SemanticModel, context.CancellationToken)) { return; } TextSpan span = TextSpan.FromBounds(binaryExpression.Left.SpanStart, binaryExpression.OperatorToken.Span.End); context.ReportDiagnostic( DiagnosticDescriptors.UnnecessaryNullCheck, Location.Create(invocationInfo.InvocationExpression.SyntaxTree, span)); } void UseElementAccessInsteadOfCallingFirst() { if (!invocationInfo.Expression.GetTrailingTrivia().IsEmptyOrWhitespace()) { return; } symbol = context.SemanticModel.GetSymbol(invocationExpression, context.CancellationToken); if (symbol?.Kind != SymbolKind.Method || symbol.IsStatic || symbol.DeclaredAccessibility != Accessibility.Public || !RoslynSymbolUtility.IsList(symbol.ContainingType.OriginalDefinition)) { return; } TextSpan span = TextSpan.FromBounds(invocationInfo.Name.SpanStart, invocationExpression.Span.End); context.ReportDiagnostic( DiagnosticDescriptors.UseElementAccess, Location.Create(invocationExpression.SyntaxTree, span)); } void UseElementAccessInsteadOfCallingElementAt() { if (!invocationInfo.Expression.GetTrailingTrivia().IsEmptyOrWhitespace()) { return; } symbol = context.SemanticModel.GetSymbol(invocationExpression, context.CancellationToken); if (symbol?.Kind != SymbolKind.Method || symbol.IsStatic || symbol.DeclaredAccessibility != Accessibility.Public || !symbol.ContainingType.OriginalDefinition.HasMetadataName(RoslynMetadataNames.Microsoft_CodeAnalysis_SyntaxTriviaList)) { return; } TextSpan span = TextSpan.FromBounds(invocationInfo.Name.SpanStart, invocationExpression.Span.End); context.ReportDiagnostic( DiagnosticDescriptors.UseElementAccess, Location.Create(invocationExpression.SyntaxTree, span)); } void UseReturnValue() { if (symbol == null) { symbol = context.SemanticModel.GetSymbol(invocationExpression, context.CancellationToken); } if (symbol?.Kind != SymbolKind.Method) { return; } if (!RoslynSymbolUtility.IsRoslynType(symbol.ContainingType)) { return; } var methodSymbol = (IMethodSymbol)symbol; if (!RoslynSymbolUtility.IsRoslynType(methodSymbol.ReturnType)) { return; } context.ReportDiagnostic(DiagnosticDescriptors.UseReturnValue, invocationExpression); } }
private static void AnalyzeInvocationExpression(SyntaxNodeAnalysisContext context) { var invocation = (InvocationExpressionSyntax)context.Node; if (invocation.ContainsDiagnostics) { return; } SimpleMemberInvocationExpressionInfo invocationInfo = SyntaxInfo.SimpleMemberInvocationExpressionInfo(invocation); if (!invocationInfo.Success) { return; } string methodName = invocationInfo.NameText; int argumentCount = invocationInfo.Arguments.Count; switch (argumentCount) { case 0: { switch (methodName) { case "Any": { UseCountOrLengthPropertyInsteadOfAnyMethodAnalysis.Analyze(context, invocationInfo); OptimizeLinqMethodCallAnalysis.AnalyzeWhere(context, invocationInfo); break; } case "Cast": { OptimizeLinqMethodCallAnalysis.AnalyzeWhereAndCast(context, invocationInfo); RemoveRedundantCastAnalyzer.Analyze(context, invocationInfo); break; } case "Count": { OptimizeLinqMethodCallAnalysis.AnalyzeCount(context, invocationInfo); OptimizeLinqMethodCallAnalysis.AnalyzeWhere(context, invocationInfo); break; } case "First": { if (!invocationInfo.Expression.IsKind(SyntaxKind.InvocationExpression) && UseElementAccessAnalysis.IsFixableFirst(invocationInfo, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(DiagnosticDescriptors.OptimizeLinqMethodCall, Location.Create(invocation.SyntaxTree, TextSpan.FromBounds(invocationInfo.Name.SpanStart, invocationInfo.ArgumentList.Span.End))); } OptimizeLinqMethodCallAnalysis.AnalyzeWhere(context, invocationInfo); OptimizeLinqMethodCallAnalysis.AnalyzeFirst(context, invocationInfo); break; } case "ToString": { RemoveRedundantToStringCallAnalysis.Analyze(context, invocationInfo); UseNameOfOperatorAnalyzer.Analyze(context, invocationInfo); break; } case "ToLower": case "ToLowerInvariant": case "ToUpper": case "ToUpperInvariant": { UseStringComparisonAnalysis.Analyze(context, invocationInfo); break; } case "FirstOrDefault": { OptimizeLinqMethodCallAnalysis.AnalyzeWhere(context, invocationInfo); OptimizeLinqMethodCallAnalysis.AnalyzeFirstOrDefault(context, invocationInfo); break; } case "Last": case "LastOrDefault": case "LongCount": case "Single": case "SingleOrDefault": { OptimizeLinqMethodCallAnalysis.AnalyzeWhere(context, invocationInfo); break; } case "OfType": { if (!invocation.SpanContainsDirectives()) { OptimizeLinqMethodCallAnalysis.AnalyzeOfType(context, invocationInfo); } break; } case "ToCharArray": { RemoveRedundantStringToCharArrayCallAnalysis.Analyze(context, invocationInfo); break; } } break; } case 1: { switch (methodName) { case "All": case "Any": { SimplifyLogicalNegationAnalyzer.Analyze(context, invocationInfo); if (!invocation.SpanContainsDirectives()) { OptimizeLinqMethodCallAnalysis.AnalyzeWhereAndAny(context, invocationInfo); } break; } case "ElementAt": { if (!invocationInfo.Expression.IsKind(SyntaxKind.InvocationExpression) && UseElementAccessAnalysis.IsFixableElementAt(invocationInfo, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(DiagnosticDescriptors.OptimizeLinqMethodCall, Location.Create(invocation.SyntaxTree, TextSpan.FromBounds(invocationInfo.Name.SpanStart, invocationInfo.ArgumentList.Span.End))); } break; } case "FirstOrDefault": { OptimizeLinqMethodCallAnalysis.AnalyzeFirstOrDefault(context, invocationInfo); break; } case "Where": { CombineEnumerableWhereMethodChainAnalysis.Analyze(context, invocationInfo); break; } case "HasFlag": { if (!invocation.SpanContainsDirectives() && UseBitwiseOperationInsteadOfCallingHasFlagAnalysis.IsFixable(invocationInfo, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(DiagnosticDescriptors.UseBitwiseOperationInsteadOfCallingHasFlag, invocation); } break; } case "Select": { CallCastInsteadOfSelectAnalysis.Analyze(context, invocationInfo); break; } case "OrderBy": { CallThenByInsteadOfOrderByAnalysis.Analyze(context, invocationInfo); break; } } break; } case 2: { switch (invocationInfo.NameText) { case "IsMatch": case "Match": case "Matches": case "Split": { if (!invocation.SpanContainsDirectives()) { UseRegexInstanceInsteadOfStaticMethodAnalysis.Analyze(context, invocationInfo); } break; } case "Select": { CallCastInsteadOfSelectAnalysis.Analyze(context, invocationInfo); break; } case "OrderBy": { CallThenByInsteadOfOrderByAnalysis.Analyze(context, invocationInfo); break; } } break; } case 3: { switch (invocationInfo.NameText) { case "IsMatch": case "Match": case "Matches": case "Split": case "Replace": { if (!invocation.SpanContainsDirectives()) { UseRegexInstanceInsteadOfStaticMethodAnalysis.Analyze(context, invocationInfo); } break; } case "OrderBy": { CallThenByInsteadOfOrderByAnalysis.Analyze(context, invocationInfo); break; } } break; } case 4: { switch (invocationInfo.NameText) { case "Replace": { if (!invocation.SpanContainsDirectives()) { UseRegexInstanceInsteadOfStaticMethodAnalysis.Analyze(context, invocationInfo); } break; } } break; } } switch (methodName) { case "ElementAtOrDefault": case "FirstOrDefault": case "LastOrDefault": case "SingleOrDefault": { if (argumentCount == 0 || argumentCount == 1 || argumentCount == 2) { AvoidNullReferenceExceptionAnalyzer.Analyze(context, invocationInfo); } break; } case "Append": case "AppendLine": case "AppendFormat": case "Insert": { OptimizeStringBuilderAppendCallAnalysis.Analyze(context, invocationInfo); break; } case "Join": { if (argumentCount >= 2) { CallStringConcatInsteadOfStringJoinAnalysis.Analyze(context, invocationInfo); } break; } } if (UseMethodChainingAnalysis.IsFixable(invocationInfo, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(DiagnosticDescriptors.UseMethodChaining, invocationInfo.InvocationExpression); } }