// x < 0 >>> false private static void AnalyzeLessThanExpression(SyntaxNodeAnalysisContext context) { var lessThanExpression = (BinaryExpressionSyntax)context.Node; BinaryExpressionInfo info = SyntaxInfo.BinaryExpressionInfo(lessThanExpression); if (!info.Success) { return; } if (!IsAlwaysEqualToTrueOrFalse(lessThanExpression, info.Left, info.Right, context.SemanticModel, context.CancellationToken)) { return; } ReportExpressionAlwaysEqualToTrueOrFalse(context, "false"); }
// x > 0 (x > 0) true/false // 0 > x (x < 0) false public static void GreaterThanExpression(SyntaxNodeAnalysisContext context) { var greaterThanExpression = (BinaryExpressionSyntax)context.Node; BinaryExpressionInfo info = SyntaxInfo.BinaryExpressionInfo(greaterThanExpression); if (!info.Success) { return; } if (!IsFixable(greaterThanExpression, info.Right, info.Left, context.SemanticModel, context.CancellationToken)) { return; } ReportDiagnostic(context, "false"); }
// x <= 0 (x <= 0) true/false // 0 <= x (x >= 0) true public static void AnalyzeLessThanOrEqualExpression(SyntaxNodeAnalysisContext context) { var lessThanOrEqualExpression = (BinaryExpressionSyntax)context.Node; BinaryExpressionInfo info = SyntaxInfo.BinaryExpressionInfo(lessThanOrEqualExpression); if (!info.Success) { return; } if (!IsFixable(lessThanOrEqualExpression, info.Right, info.Left, context.SemanticModel, context.CancellationToken)) { return; } ReportDiagnostic(context, "true"); }
private static void AnalyzeSimpleAssignment(SyntaxNodeAnalysisContext context) { var assignmentExpression = (AssignmentExpressionSyntax)context.Node; SimpleAssignmentExpressionInfo assignmentInfo = SyntaxInfo.SimpleAssignmentExpressionInfo(assignmentExpression); if (!assignmentInfo.Success) { return; } if (assignmentExpression.IsParentKind(SyntaxKind.ObjectInitializerExpression)) { return; } ExpressionSyntax right = assignmentInfo.Right; if (!CanBeReplacedWithCompoundAssignment(right.Kind())) { return; } BinaryExpressionInfo binaryInfo = SyntaxInfo.BinaryExpressionInfo((BinaryExpressionSyntax)right); if (!binaryInfo.Success) { return; } if (!CSharpFactory.AreEquivalent(assignmentInfo.Left, binaryInfo.Left)) { return; } var binaryExpression = (BinaryExpressionSyntax)right; context.ReportDiagnostic(DiagnosticDescriptors.UseCompoundAssignment, assignmentExpression, GetCompoundAssignmentOperatorText(binaryExpression)); context.ReportNode(DiagnosticDescriptors.UseCompoundAssignmentFadeOut, binaryExpression.Left); }
// x: // byte // ushort // uint // ulong // Array.Length // string.Length // ICollection<T>.Count // IReadOnlyCollection<T>.Count // x >= 0 >>> true // 0 >= x >>> 0 == x private static void AnalyzeGreaterThanOrEqualExpression(SyntaxNodeAnalysisContext context) { var greaterThanOrEqualExpression = (BinaryExpressionSyntax)context.Node; BinaryExpressionInfo info = SyntaxInfo.BinaryExpressionInfo(greaterThanOrEqualExpression); if (!info.Success) { return; } if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.ExpressionIsAlwaysEqualToTrueOrFalse) && IsAlwaysEqualToTrueOrFalse(greaterThanOrEqualExpression, info.Left, info.Right, context.SemanticModel, context.CancellationToken)) { ReportExpressionAlwaysEqualToTrueOrFalse(context, "true"); } else if (!context.IsAnalyzerSuppressed(DiagnosticDescriptors.UnnecessaryOperator) && IsUnnecessaryRelationalOperator(info.Right, info.Left, context.SemanticModel, context.CancellationToken)) { ReportUnnecessaryRelationalOperator(context, info.OperatorToken); } }
// 0 <= x >>> true // x <= 0 >>> x == 0 private static void AnalyzeLessThanOrEqualExpression(SyntaxNodeAnalysisContext context) { var lessThanOrEqualExpression = (BinaryExpressionSyntax)context.Node; BinaryExpressionInfo info = SyntaxInfo.BinaryExpressionInfo(lessThanOrEqualExpression); if (!info.Success) { return; } if (DiagnosticRules.ExpressionIsAlwaysEqualToTrueOrFalse.IsEffective(context) && IsAlwaysEqualToTrueOrFalse(lessThanOrEqualExpression, info.Right, info.Left, context.SemanticModel, context.CancellationToken)) { ReportExpressionAlwaysEqualToTrueOrFalse(context, "true"); } else if (DiagnosticRules.UnnecessaryOperator.IsEffective(context) && IsUnnecessaryRelationalOperator(info.Left, info.Right, context.SemanticModel, context.CancellationToken)) { ReportUnnecessaryRelationalOperator(context, info.OperatorToken); } }
private static void AnalyzeEqualsExpression(SyntaxNodeAnalysisContext context) { var equalsExpression = (BinaryExpressionSyntax)context.Node; if (equalsExpression.ContainsDirectives) { return; } BinaryExpressionInfo equalsExpressionInfo = SyntaxInfo.BinaryExpressionInfo(equalsExpression); if (!equalsExpressionInfo.Success) { return; } ExpressionSyntax left = equalsExpressionInfo.Left; ExpressionSyntax right = equalsExpressionInfo.Right; SemanticModel semanticModel = context.SemanticModel; CancellationToken cancellationToken = context.CancellationToken; if (CSharpUtility.IsEmptyStringExpression(left, semanticModel, cancellationToken)) { if (CSharpUtility.IsStringExpression(right, semanticModel, cancellationToken) && !equalsExpression.IsInExpressionTree(semanticModel, cancellationToken)) { DiagnosticHelpers.ReportDiagnostic(context, DiagnosticRules.UseStringLengthInsteadOfComparisonWithEmptyString, equalsExpression); } } else if (CSharpUtility.IsEmptyStringExpression(right, semanticModel, cancellationToken) && CSharpUtility.IsStringExpression(left, semanticModel, cancellationToken) && !equalsExpression.IsInExpressionTree(semanticModel, cancellationToken)) { DiagnosticHelpers.ReportDiagnostic(context, DiagnosticRules.UseStringLengthInsteadOfComparisonWithEmptyString, equalsExpression); } }
public static void AnalyzeLogicalOrExpression(SyntaxNodeAnalysisContext context) { SyntaxNode node = context.Node; if (node.ContainsDiagnostics) { return; } if (node.SpanContainsDirectives()) { return; } BinaryExpressionInfo info = SyntaxInfo.BinaryExpressionInfo((BinaryExpressionSyntax)context.Node); if (!info.Success) { return; } if (!info.Left.IsKind(SyntaxKind.LogicalAndExpression)) { return; } if (!info.Right.IsKind(SyntaxKind.LogicalAndExpression)) { return; } ExpressionPair expressions = GetExpressionPair((BinaryExpressionSyntax)info.Left); if (!expressions.IsValid) { return; } ExpressionPair expressions2 = GetExpressionPair((BinaryExpressionSyntax)info.Right); if (!expressions2.IsValid) { return; } if (expressions.Expression.Kind() != expressions2.NegatedExpression.Kind()) { return; } if (expressions.NegatedExpression.Kind() != expressions2.Expression.Kind()) { return; } if (!AreEquivalent(expressions.Expression, expressions2.NegatedExpression)) { return; } if (!AreEquivalent(expressions.NegatedExpression, expressions2.Expression)) { return; } context.ReportDiagnostic(DiagnosticDescriptors.UseExclusiveOrOperator, context.Node); }
private static void AnalyzeBinaryExpression(SyntaxNodeAnalysisContext context) { var binaryExpression = (BinaryExpressionSyntax)context.Node; BinaryExpressionInfo binaryExpressionInfo = SyntaxInfo.BinaryExpressionInfo(binaryExpression); if (!binaryExpressionInfo.Success) { return; } if (binaryExpressionInfo.Right.Kind() != SyntaxKind.TrueLiteralExpression) { return; } ExpressionSyntax left = binaryExpressionInfo.Left; if (left.Kind() != SyntaxKind.ConditionalAccessExpression) { return; } var conditionalAccess = (ConditionalAccessExpressionSyntax)left; ExpressionSyntax whenNotNull = conditionalAccess.WhenNotNull; if (whenNotNull.Kind() != SyntaxKind.InvocationExpression) { return; } var invocationExpression = (InvocationExpressionSyntax)whenNotNull; if (invocationExpression.ArgumentList.Arguments.Count != 1) { return; } ExpressionSyntax expression = invocationExpression.Expression; if (expression.Kind() != SyntaxKind.MemberBindingExpression) { return; } var memberBindingExpression = (MemberBindingExpressionSyntax)expression; SimpleNameSyntax name = memberBindingExpression.Name; if (name.Kind() != SyntaxKind.IdentifierName) { return; } var identifierName = (IdentifierNameSyntax)name; if (!string.Equals(identifierName.Identifier.ValueText, "IsKind", StringComparison.Ordinal)) { return; } ISymbol symbol = context.SemanticModel.GetSymbol(invocationExpression, context.CancellationToken); if (symbol?.Kind != SymbolKind.Method) { return; } var methodSymbol = (IMethodSymbol)symbol; if (methodSymbol.MethodKind != MethodKind.ReducedExtension) { return; } if (methodSymbol.ReturnType.SpecialType != SpecialType.System_Boolean) { return; } if (methodSymbol.ContainingType?.HasMetadataName(RoslynMetadataNames.Microsoft_CodeAnalysis_CSharpExtensions) != true) { return; } ImmutableArray <IParameterSymbol> parameters = methodSymbol .ReducedFrom .Parameters; if (parameters.Length != 2) { return; } if (!parameters[0].Type.HasMetadataName(RoslynMetadataNames.Microsoft_CodeAnalysis_SyntaxNode)) { return; } if (!parameters[1].Type.HasMetadataName(CSharpMetadataNames.Microsoft_CodeAnalysis_CSharp_SyntaxKind)) { return; } DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.UnnecessaryConditionalAccess, conditionalAccess.OperatorToken); DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.UnnecessaryConditionalAccessFadeOut, binaryExpression.Right); }
private static void AnalyzeLogicalAndExpression(SyntaxNodeAnalysisContext context) { var logicalAnd = (BinaryExpressionSyntax)context.Node; if (logicalAnd.SpanContainsDirectives()) { return; } BinaryExpressionInfo logicalAndInfo = SyntaxInfo.BinaryExpressionInfo(logicalAnd); if (!logicalAndInfo.Success) { return; } NullCheckExpressionInfo nullCheck = SyntaxInfo.NullCheckExpressionInfo( logicalAndInfo.Left, context.SemanticModel, NullCheckStyles.NotEqualsToNull | NullCheckStyles.HasValue, cancellationToken: context.CancellationToken); if (!nullCheck.Success) { return; } ExpressionSyntax right = logicalAndInfo.Right; switch (right.Kind()) { case SyntaxKind.LogicalNotExpression: { var logicalNot = (PrefixUnaryExpressionSyntax)right; Analyze(nullCheck.Expression, logicalNot.Operand?.WalkDownParentheses(), null); break; } case SyntaxKind.EqualsExpression: case SyntaxKind.LessThanExpression: case SyntaxKind.LessThanOrEqualExpression: case SyntaxKind.GreaterThanExpression: case SyntaxKind.GreaterThanOrEqualExpression: { BinaryExpressionInfo binaryExpressionInfo = SyntaxInfo.BinaryExpressionInfo((BinaryExpressionSyntax)right); if (!binaryExpressionInfo.Success) { break; } ExpressionSyntax left = binaryExpressionInfo.Left; Analyze(nullCheck.Expression, left, binaryExpressionInfo.Right); break; } case SyntaxKind.SimpleMemberAccessExpression: { AnalyzeSimpleMemberAccessExpression(nullCheck.Expression, (MemberAccessExpressionSyntax)right, null); break; } } void Analyze(ExpressionSyntax expression1, ExpressionSyntax expression2, ExpressionSyntax expression3) { if (expression2.IsKind(SyntaxKind.SimpleMemberAccessExpression)) { AnalyzeSimpleMemberAccessExpression(expression1, (MemberAccessExpressionSyntax)expression2, expression3); } } void AnalyzeSimpleMemberAccessExpression(ExpressionSyntax expression, MemberAccessExpressionSyntax memberAccessExpression, ExpressionSyntax expression3) { if (memberAccessExpression.Name is not IdentifierNameSyntax identifierName || !string.Equals(identifierName.Identifier.ValueText, "Value", StringComparison.Ordinal)) { return; } if (!SyntaxUtility.IsPropertyOfNullableOfT(memberAccessExpression, "Value", context.SemanticModel, context.CancellationToken)) { return; } if (!AreEquivalent(expression, memberAccessExpression.Expression)) { return; } if (expression3 != null) { switch (expression3.Kind()) { case SyntaxKind.NumericLiteralExpression: case SyntaxKind.StringLiteralExpression: case SyntaxKind.CharacterLiteralExpression: case SyntaxKind.TrueLiteralExpression: case SyntaxKind.FalseLiteralExpression: { break; } case SyntaxKind.NullLiteralExpression: case SyntaxKind.DefaultLiteralExpression: { return; } default: { if (context.SemanticModel.GetTypeSymbol(expression3, context.CancellationToken).IsNullableType()) { return; } break; } } } DiagnosticHelpers.ReportDiagnostic(context, DiagnosticRules.UnnecessaryNullCheck, logicalAnd); } }
private static void AnalyzeElementAccessExpression(SyntaxNodeAnalysisContext context) { var elementAccessExpression = (ElementAccessExpressionSyntax)context.Node; ExpressionSyntax expression = elementAccessExpression .ArgumentList .Arguments .SingleOrDefault(shouldThrow: false)? .Expression .WalkDownParentheses(); if (expression == null) { return; } if (!expression.IsKind(SyntaxKind.SubtractExpression)) { return; } BinaryExpressionInfo subtractExpressionInfo = SyntaxInfo.BinaryExpressionInfo((BinaryExpressionSyntax)expression); if (!subtractExpressionInfo.Right.IsNumericLiteralExpression("1")) { return; } if (!subtractExpressionInfo.Left.IsKind(SyntaxKind.SimpleMemberAccessExpression)) { return; } var memberAccessExpression = (MemberAccessExpressionSyntax)subtractExpressionInfo.Left; if (!(memberAccessExpression.Name is IdentifierNameSyntax identifierName)) { return; } if (identifierName.Identifier.ValueText != "Count") { return; } if (!CSharpFactory.AreEquivalent(elementAccessExpression.Expression, memberAccessExpression.Expression)) { return; } ISymbol symbol = context.SemanticModel.GetSymbol(elementAccessExpression, context.CancellationToken); if (symbol?.Kind != SymbolKind.Property || symbol.IsStatic || symbol.DeclaredAccessibility != Accessibility.Public || !RoslynSymbolUtility.IsList(symbol.ContainingType.OriginalDefinition)) { return; } context.ReportDiagnostic(DiagnosticDescriptors.CallLastInsteadOfUsingElementAccess, elementAccessExpression.ArgumentList); }
public override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindNode(root, context.Span, out ExpressionSyntax expression)) { return; } foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case CompilerDiagnosticIdentifiers.CannotImplicitlyConvertTypeExplicitConversionExists: { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); TypeInfo typeInfo = semanticModel.GetTypeInfo(expression, context.CancellationToken); ITypeSymbol type = typeInfo.Type; ITypeSymbol convertedType = typeInfo.ConvertedType; if ((type is INamedTypeSymbol namedType) && namedType.IsNullableType()) { if (convertedType?.SpecialType == SpecialType.System_Boolean || AddComparisonWithBooleanLiteralRefactoring.IsCondition(expression)) { if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddComparisonWithBooleanLiteral)) { CodeAction codeAction = CodeAction.Create( AddComparisonWithBooleanLiteralRefactoring.GetTitle(expression), cancellationToken => AddComparisonWithBooleanLiteralRefactoring.RefactorAsync(context.Document, expression, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.AddComparisonWithBooleanLiteral)); context.RegisterCodeFix(codeAction, diagnostic); } } else if (SymbolEqualityComparer.Default.Equals(namedType.TypeArguments[0], convertedType)) { if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.UseCoalesceExpression)) { CodeAction codeAction = CodeAction.Create( "Use coalesce expression", cancellationToken => { ExpressionSyntax defaultValue = convertedType.GetDefaultValueSyntax(context.Document.GetDefaultSyntaxOptions()); ExpressionSyntax newNode = CoalesceExpression(expression.WithoutTrivia(), defaultValue) .WithTriviaFrom(expression) .Parenthesize() .WithFormatterAnnotation(); return(context.Document.ReplaceNodeAsync(expression, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic, CodeFixIdentifiers.UseCoalesceExpression)); context.RegisterCodeFix(codeAction, diagnostic); } } } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression) && expression.IsParentKind(SyntaxKind.ReturnStatement, SyntaxKind.YieldReturnStatement)) { ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddCastExpression)) { CodeFixRegistrator.AddCastExpression(context, diagnostic, expression, convertedType, semanticModel); } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeTypeAccordingToInitializer)) { ChangeTypeAccordingToInitializerRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.CreateSingletonArray) && type?.IsErrorType() == false && !SymbolEqualityComparer.Default.Equals(type, convertedType) && (convertedType is IArrayTypeSymbol arrayType) && semanticModel.IsImplicitConversion(expression, arrayType.ElementType)) { CodeAction codeAction = CodeAction.Create( "Create singleton array", cancellationToken => CreateSingletonArrayRefactoring.RefactorAsync(context.Document, expression, arrayType.ElementType, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.CreateSingletonArray)); context.RegisterCodeFix(codeAction, diagnostic); } break; } case CompilerDiagnosticIdentifiers.ConstantValueCannotBeConverted: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.UseUncheckedExpression)) { break; } CodeAction codeAction = CodeAction.Create( "Use 'unchecked'", cancellationToken => { CheckedExpressionSyntax newNode = CSharpFactory.UncheckedExpression(expression.WithoutTrivia()); newNode = newNode.WithTriviaFrom(expression); return(context.Document.ReplaceNodeAsync(expression, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.ExpressionBeingAssignedMustBeConstant: { SyntaxNode parent = expression.Parent; if (parent?.IsKind(SyntaxKind.EqualsValueClause) != true) { break; } parent = parent.Parent; if (parent?.IsKind(SyntaxKind.VariableDeclarator) != true) { break; } parent = parent.Parent; if (!(parent is VariableDeclarationSyntax variableDeclaration)) { break; } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveConstModifier) && variableDeclaration.Parent is LocalDeclarationStatementSyntax localDeclarationStatement) { SyntaxTokenList modifiers = localDeclarationStatement.Modifiers; if (!modifiers.Contains(SyntaxKind.ConstKeyword)) { break; } ModifiersCodeFixRegistrator.RemoveModifier(context, diagnostic, localDeclarationStatement, SyntaxKind.ConstKeyword); } else if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceConstantWithField) && variableDeclaration.Variables.Count == 1 && (variableDeclaration.Parent is FieldDeclarationSyntax fieldDeclaration) && fieldDeclaration.Modifiers.Contains(SyntaxKind.ConstKeyword)) { CodeAction codeAction = CodeAction.Create( ReplaceConstantWithFieldRefactoring.Title, cancellationToken => ReplaceConstantWithFieldRefactoring.RefactorAsync(context.Document, fieldDeclaration, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } break; } case CompilerDiagnosticIdentifiers.CannotConvertNullToTypeBecauseItIsNonNullableValueType: case CompilerDiagnosticIdentifiers.CannotConvertNullToTypeParameterBecauseItCouldBeNonNullableValueType: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue)) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); CodeFixRegistrator.ReplaceNullWithDefaultValue(context, diagnostic, expression, semanticModel); break; } case CompilerDiagnosticIdentifiers.ResultOfExpressionIsAlwaysConstantSinceValueIsNeverEqualToNull: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveConditionThatIsAlwaysEqualToTrueOrFalse)) { break; } NullCheckExpressionInfo nullCheck = SyntaxInfo.NullCheckExpressionInfo(expression, allowedStyles: NullCheckStyles.ComparisonToNull); if (!nullCheck.Success) { break; } CodeAction codeAction = CodeAction.Create( "Remove condition", cancellationToken => { cancellationToken.ThrowIfCancellationRequested(); SyntaxNode newRoot = RemoveCondition(root, expression, nullCheck.Style == NullCheckStyles.NotEqualsToNull); cancellationToken.ThrowIfCancellationRequested(); return(Task.FromResult(context.Document.WithSyntaxRoot(newRoot))); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.OnlyAssignmentCallIncrementDecrementAndNewObjectExpressionsCanBeUsedAsStatement: { if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveParentheses) && expression is ParenthesizedExpressionSyntax parenthesizedExpression && parenthesizedExpression?.IsMissing == false) { CodeAction codeAction = CodeActionFactory.RemoveParentheses(context.Document, parenthesizedExpression, equivalenceKey: GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (expression.Parent is ArrowExpressionClauseSyntax arrowExpresssionClause) { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression)) { break; } ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); } else if (expression.Parent is ExpressionStatementSyntax expressionStatement) { if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddArgumentList) && expression.IsKind( SyntaxKind.IdentifierName, SyntaxKind.SimpleMemberAccessExpression)) { SyntaxNode invocationExpression = InvocationExpression(expression); if (semanticModel.GetSpeculativeMethodSymbol(expression.SpanStart, invocationExpression) != null) { CodeAction codeAction = CodeAction.Create( "Add argument list", cancellationToken => context.Document.ReplaceNodeAsync(expression, invocationExpression, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.AddArgumentList)); context.RegisterCodeFix(codeAction, diagnostic); } } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceComparisonWithAssignment) && expression.IsKind(SyntaxKind.EqualsExpression)) { BinaryExpressionInfo info = SyntaxInfo.BinaryExpressionInfo(expression); if (!info.Success) { break; } ITypeSymbol leftTypeSymbol = semanticModel.GetTypeSymbol(info.Left, context.CancellationToken); if (leftTypeSymbol?.IsErrorType() != false) { break; } if (!semanticModel.IsImplicitConversion(info.Right, leftTypeSymbol)) { break; } CodeAction codeAction = CodeAction.Create( "Replace comparison with assignment", cancellationToken => { AssignmentExpressionSyntax simpleAssignment = SimpleAssignmentExpression(info.Left, info.Right).WithTriviaFrom(expression); return(context.Document.ReplaceNodeAsync(expression, simpleAssignment, cancellationToken)); }, GetEquivalenceKey(diagnostic, CodeFixIdentifiers.ReplaceComparisonWithAssignment)); context.RegisterCodeFix(codeAction, diagnostic); } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceConditionalExpressionWithIfElse) && (expression is ConditionalExpressionSyntax conditionalExpression) && conditionalExpression.Condition != null) { ExpressionSyntax whenTrue = conditionalExpression.WhenTrue; ExpressionSyntax whenFalse = conditionalExpression.WhenFalse; if (whenTrue != null && whenFalse != null && semanticModel.GetTypeSymbol(whenTrue, context.CancellationToken)?.SpecialType == SpecialType.System_Void && semanticModel.GetTypeSymbol(whenFalse, context.CancellationToken)?.SpecialType == SpecialType.System_Void) { CodeAction codeAction = CodeAction.Create( "Replace ?: with if-else", cancellationToken => { IfStatementSyntax newNode = IfStatement( conditionalExpression.Condition.WalkDownParentheses(), Block(ExpressionStatement(whenTrue)), ElseClause(Block(ExpressionStatement(whenFalse)))); newNode = newNode .WithTriviaFrom(expressionStatement) .WithFormatterAnnotation(); return(context.Document.ReplaceNodeAsync(expressionStatement, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic, CodeFixIdentifiers.ReplaceConditionalExpressionWithIfElse)); context.RegisterCodeFix(codeAction, diagnostic); } } if (semanticModel.GetSymbol(expression, context.CancellationToken)?.IsErrorType() != false) { break; } ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(expression, context.CancellationToken); if (typeSymbol?.IsErrorType() != false) { break; } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.IntroduceLocalVariable) && !expressionStatement.IsEmbedded()) { bool addAwait = typeSymbol.OriginalDefinition.EqualsOrInheritsFromTaskOfT() && semanticModel.GetEnclosingSymbol(expressionStatement.SpanStart, context.CancellationToken).IsAsyncMethod(); CodeAction codeAction = CodeAction.Create( IntroduceLocalVariableRefactoring.GetTitle(expression), cancellationToken => IntroduceLocalVariableRefactoring.RefactorAsync(context.Document, expressionStatement, typeSymbol, addAwait, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.IntroduceLocalVariable)); context.RegisterCodeFix(codeAction, diagnostic); } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.IntroduceField)) { CodeAction codeAction = CodeAction.Create( $"Introduce field for '{expression}'", cancellationToken => IntroduceFieldRefactoring.RefactorAsync(context.Document, expressionStatement, typeSymbol, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.IntroduceField)); context.RegisterCodeFix(codeAction, diagnostic); } } break; } case CompilerDiagnosticIdentifiers.CannotImplicitlyConvertType: { if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceYieldReturnWithForEach) && expression.IsParentKind(SyntaxKind.YieldReturnStatement)) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ReplaceYieldReturnWithForEachRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); break; } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression) && expression.IsParentKind(SyntaxKind.ReturnStatement, SyntaxKind.YieldReturnStatement, SyntaxKind.ArrowExpressionClause)) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); break; } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeTypeAccordingToInitializer)) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); CodeFixRegistrationResult result = ChangeTypeAccordingToInitializerRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); if (!result.Success) { RemoveAssignmentOfVoidExpression(context, diagnostic, expression, semanticModel); } break; } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceStringLiteralWithCharacterLiteral) && expression?.Kind() == SyntaxKind.StringLiteralExpression) { var literalExpression = (LiteralExpressionSyntax)expression; if (literalExpression.Token.ValueText.Length == 1) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (semanticModel.GetTypeInfo(expression, context.CancellationToken).ConvertedType?.SpecialType == SpecialType.System_Char) { CodeAction codeAction = CodeAction.Create( "Replace string literal with character literal", cancellationToken => ReplaceStringLiteralWithCharacterLiteralRefactoring.RefactorAsync(context.Document, literalExpression, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.ReplaceStringLiteralWithCharacterLiteral)); context.RegisterCodeFix(codeAction, diagnostic); } } } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.UseYieldReturnInsteadOfReturn) && expression.IsParentKind(SyntaxKind.ReturnStatement)) { var returnStatement = (ReturnStatementSyntax)expression.Parent; SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ISymbol containingSymbol = semanticModel.GetEnclosingSymbol(returnStatement.SpanStart, context.CancellationToken); if (containingSymbol?.Kind == SymbolKind.Method && ((IMethodSymbol)containingSymbol).ReturnType.OriginalDefinition.IsIEnumerableOrIEnumerableOfT()) { CodeAction codeAction = CodeAction.Create( "Use yield return instead of return", cancellationToken => UseYieldReturnInsteadOfReturnRefactoring.RefactorAsync(context.Document, returnStatement, SyntaxKind.YieldReturnStatement, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.UseYieldReturnInsteadOfReturn)); context.RegisterCodeFix(codeAction, diagnostic); } } break; } case CompilerDiagnosticIdentifiers.LeftHandSideOfAssignmentMustBeVariablePropertyOrIndexer: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveConstModifier)) { return; } if (!expression.IsKind(SyntaxKind.IdentifierName)) { return; } if (!expression.IsParentKind(SyntaxKind.SimpleAssignmentExpression)) { return; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); SymbolInfo symbolInfo = semanticModel.GetSymbolInfo(expression, context.CancellationToken); if (symbolInfo.CandidateReason != CandidateReason.NotAVariable) { return; } if (!(symbolInfo.CandidateSymbols.SingleOrDefault(shouldThrow: false) is ILocalSymbol localSymbol)) { return; } if (!localSymbol.IsConst) { return; } SyntaxNode node = localSymbol.GetSyntaxOrDefault(context.CancellationToken); if (!node.IsKind(SyntaxKind.VariableDeclarator)) { return; } node = node.Parent; if (!node.IsKind(SyntaxKind.VariableDeclaration)) { return; } node = node.Parent; if (!(node is LocalDeclarationStatementSyntax localDeclaration)) { return; } SyntaxToken constModifier = localDeclaration.Modifiers.Find(SyntaxKind.ConstKeyword); if (!constModifier.IsKind(SyntaxKind.ConstKeyword)) { return; } ModifiersCodeFixRegistrator.RemoveModifier(context, diagnostic, localDeclaration, constModifier); break; } case CompilerDiagnosticIdentifiers.ReadOnlyFieldCannotBeAssignedTo: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.MakeFieldWritable)) { break; } SimpleAssignmentExpressionInfo simpleAssignment = SyntaxInfo.SimpleAssignmentExpressionInfo(expression.Parent); if (!simpleAssignment.Success) { return; } if (simpleAssignment.Left != expression) { return; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); SymbolInfo symbolInfo = semanticModel.GetSymbolInfo(expression, context.CancellationToken); if (symbolInfo.CandidateReason != CandidateReason.NotAVariable) { return; } if (!(symbolInfo.CandidateSymbols.SingleOrDefault(shouldThrow: false) is IFieldSymbol fieldSymbol)) { return; } if (fieldSymbol.DeclaredAccessibility != Accessibility.Private) { return; } if (!(fieldSymbol.GetSyntax().Parent.Parent is FieldDeclarationSyntax fieldDeclaration)) { return; } TypeDeclarationSyntax containingTypeDeclaration = fieldDeclaration.FirstAncestor <TypeDeclarationSyntax>(); if (!expression.Ancestors().Any(f => f == containingTypeDeclaration)) { return; } ModifiersCodeFixRegistrator.RemoveModifier( context, diagnostic, fieldDeclaration, SyntaxKind.ReadOnlyKeyword, title: $"Make '{fieldSymbol.Name}' writable"); break; } } } }
public static void Analyze(SyntaxNodeAnalysisContext context, SimpleMemberInvocationExpressionInfo invocationInfo) { INamedTypeSymbol stringBuilderSymbol = context.GetTypeByMetadataName(MetadataNames.System_Text_StringBuilder); if (stringBuilderSymbol == null) { return; } InvocationExpressionSyntax invocationExpression = invocationInfo.InvocationExpression; IMethodSymbol methodSymbol = context.SemanticModel.GetMethodSymbol(invocationExpression, context.CancellationToken); if (methodSymbol == null) { return; } if (methodSymbol.IsExtensionMethod) { return; } if (methodSymbol.ContainingType?.Equals(stringBuilderSymbol) != true) { return; } ImmutableArray <IParameterSymbol> parameters = methodSymbol.Parameters; int parameterCount = parameters.Length; if (parameterCount == 0) { if (methodSymbol.IsName("AppendLine")) { SimpleMemberInvocationExpressionInfo invocationInfo2 = SyntaxInfo.SimpleMemberInvocationExpressionInfo(invocationInfo.Expression); if (invocationInfo2.Success && invocationInfo2.NameText == "Append" && invocationInfo2.Arguments.Count == 1) { IMethodSymbol methodInfo2 = context.SemanticModel.GetMethodSymbol(invocationInfo2.InvocationExpression, context.CancellationToken); if (methodInfo2?.IsStatic == false && methodInfo2.ContainingType?.Equals(stringBuilderSymbol) == true && methodInfo2.HasSingleParameter(SpecialType.System_String)) { context.ReportDiagnostic(DiagnosticDescriptors.OptimizeStringBuilderAppendCall, invocationInfo.Name, methodSymbol.Name); } } } } else if (parameterCount == 1) { if (methodSymbol.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, methodSymbol.Name); return; } case SyntaxKind.AddExpression: { BinaryExpressionInfo binaryExpressionInfo = SyntaxInfo.BinaryExpressionInfo((BinaryExpressionSyntax)expression); if (binaryExpressionInfo.Success && binaryExpressionInfo.IsStringConcatenation(context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(DiagnosticDescriptors.OptimizeStringBuilderAppendCall, argument, methodSymbol.Name); return; } break; } default: { if (expressionKind == SyntaxKind.InvocationExpression && IsFixable((InvocationExpressionSyntax)expression, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(DiagnosticDescriptors.OptimizeStringBuilderAppendCall, argument, methodSymbol.Name); return; } if (methodSymbol.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 (methodSymbol.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]); } } } }
private static void AnalyzeSimpleAssignment(SyntaxNodeAnalysisContext context) { var assignmentExpression = (AssignmentExpressionSyntax)context.Node; SimpleAssignmentExpressionInfo assignmentInfo = SyntaxInfo.SimpleAssignmentExpressionInfo(assignmentExpression); if (!assignmentInfo.Success) { return; } if (assignmentExpression.IsParentKind(SyntaxKind.ObjectInitializerExpression)) { return; } ExpressionSyntax right = assignmentInfo.Right; if (!CanBeReplacedWithCompoundAssignment(right.Kind())) { return; } BinaryExpressionInfo binaryInfo = SyntaxInfo.BinaryExpressionInfo((BinaryExpressionSyntax)right); if (!binaryInfo.Success) { return; } if (!CSharpFactory.AreEquivalent(assignmentInfo.Left, binaryInfo.Left)) { return; } var binaryExpression = (BinaryExpressionSyntax)right; DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.UseCompoundAssignment, assignmentExpression, GetCompoundAssignmentOperatorText(binaryExpression)); DiagnosticHelpers.ReportNode(context, DiagnosticDescriptors.UseCompoundAssignmentFadeOut, binaryExpression.Left); bool CanBeReplacedWithCompoundAssignment(SyntaxKind kind) { switch (kind) { case SyntaxKind.AddExpression: case SyntaxKind.SubtractExpression: case SyntaxKind.MultiplyExpression: case SyntaxKind.DivideExpression: case SyntaxKind.ModuloExpression: case SyntaxKind.BitwiseAndExpression: case SyntaxKind.ExclusiveOrExpression: case SyntaxKind.BitwiseOrExpression: case SyntaxKind.LeftShiftExpression: case SyntaxKind.RightShiftExpression: return(true); case SyntaxKind.CoalesceExpression: return(((CSharpCompilation)context.Compilation).LanguageVersion >= LanguageVersion.CSharp8); default: return(false); } } }
private static void AnalyzeSimpleMemberAccessExpression(SyntaxNodeAnalysisContext context) { var memberAccessExpression = (MemberAccessExpressionSyntax)context.Node; SimpleNameSyntax name = memberAccessExpression.Name; switch (name) { case IdentifierNameSyntax identifierName: { switch (identifierName.Identifier.ValueText) { case "Start": { ExpressionSyntax expression = memberAccessExpression.Expression; if (!expression.IsKind(SyntaxKind.SimpleMemberAccessExpression)) { break; } ISymbol symbol = context.SemanticModel.GetSymbol(memberAccessExpression, context.CancellationToken); if (symbol == null) { break; } if (!symbol.ContainingType.HasMetadataName(RoslynMetadataNames.Microsoft_CodeAnalysis_Text_TextSpan)) { break; } var memberAccess2 = (MemberAccessExpressionSyntax)expression; SimpleNameSyntax name2 = memberAccess2.Name; if (!(name2 is IdentifierNameSyntax identifierName2)) { break; } if (!string.Equals(identifierName2.Identifier.ValueText, "Span", StringComparison.Ordinal)) { break; } ISymbol symbol2 = context.SemanticModel.GetSymbol(expression, context.CancellationToken); if (symbol2 == null) { break; } if (!symbol2.ContainingType.HasMetadataName(RoslynMetadataNames.Microsoft_CodeAnalysis_SyntaxNode)) { break; } DiagnosticHelpers.ReportDiagnostic(context, DiagnosticRules.UsePropertySyntaxNodeSpanStart, memberAccessExpression); break; } case "Count": { CallAnyInsteadOfUsingCount(); break; } } break; } } void CallAnyInsteadOfUsingCount() { SyntaxNode expression = memberAccessExpression.WalkUpParentheses(); SyntaxNode parent = expression.Parent; if (!parent.IsKind(SyntaxKind.EqualsExpression, SyntaxKind.NotEqualsExpression, SyntaxKind.GreaterThanExpression)) { return; } BinaryExpressionInfo binaryExpressionInfo = SyntaxInfo.BinaryExpressionInfo((BinaryExpressionSyntax)parent); if (!binaryExpressionInfo.Success) { return; } ExpressionSyntax otherExpression = (expression == binaryExpressionInfo.Left) ? binaryExpressionInfo.Right : binaryExpressionInfo.Left; if (!otherExpression.IsKind(SyntaxKind.NumericLiteralExpression)) { return; } var numericLiteralExpression = (LiteralExpressionSyntax)otherExpression; if (numericLiteralExpression.Token.ValueText != "0") { return; } ISymbol symbol = context.SemanticModel.GetSymbol(memberAccessExpression, context.CancellationToken); if (symbol?.Kind != SymbolKind.Property || symbol.IsStatic || symbol.DeclaredAccessibility != Accessibility.Public || !RoslynSymbolUtility.IsList(symbol.ContainingType.OriginalDefinition)) { return; } TextSpan span = (memberAccessExpression == binaryExpressionInfo.Left) ? TextSpan.FromBounds(name.SpanStart, numericLiteralExpression.Span.End) : TextSpan.FromBounds(numericLiteralExpression.SpanStart, name.Span.End); DiagnosticHelpers.ReportDiagnostic(context, DiagnosticRules.CallAnyInsteadOfAccessingCount, Location.Create(memberAccessExpression.SyntaxTree, span)); } }
public static void ComputeRefactoring(RefactoringContext context, BinaryExpressionSyntax binaryExpression) { BinaryExpressionInfo info = SyntaxInfo.BinaryExpressionInfo(binaryExpression); if (!info.Success) { return; } if (CanRefactor()) { context.RegisterRefactoring( "Swap operands", ct => { BinaryExpressionSyntax newBinaryExpression = SyntaxRefactorings.SwapBinaryOperands(binaryExpression); newBinaryExpression = newBinaryExpression.WithOperatorToken(newBinaryExpression.OperatorToken.WithNavigationAnnotation()); return(context.Document.ReplaceNodeAsync(binaryExpression, newBinaryExpression, ct)); }, RefactoringDescriptors.SwapBinaryOperands); } bool CanRefactor() { SyntaxKind kind = binaryExpression.Kind(); switch (kind) { case SyntaxKind.LogicalAndExpression: case SyntaxKind.LogicalOrExpression: case SyntaxKind.BitwiseAndExpression: case SyntaxKind.BitwiseOrExpression: case SyntaxKind.ExclusiveOrExpression: case SyntaxKind.AddExpression: case SyntaxKind.MultiplyExpression: { return(!info.Left.IsKind(kind)); } case SyntaxKind.EqualsExpression: case SyntaxKind.NotEqualsExpression: { return(!info.Right.IsKind( SyntaxKind.NullLiteralExpression, SyntaxKind.TrueLiteralExpression, SyntaxKind.FalseLiteralExpression)); } case SyntaxKind.GreaterThanExpression: case SyntaxKind.GreaterThanOrEqualExpression: case SyntaxKind.LessThanExpression: case SyntaxKind.LessThanOrEqualExpression: { return(true); } } return(false); } }