private static NullCheckExpressionInfo Create( BinaryExpressionSyntax binaryExpression, SyntaxKind binaryExpressionKind, ExpressionSyntax expression1, ExpressionSyntax expression2, NullCheckStyles allowedStyles, bool allowMissing, SemanticModel semanticModel, CancellationToken cancellationToken) { switch (expression1.Kind()) { case SyntaxKind.NullLiteralExpression: { NullCheckStyles style = (binaryExpressionKind == SyntaxKind.EqualsExpression) ? NullCheckStyles.EqualsToNull : NullCheckStyles.NotEqualsToNull; if ((allowedStyles & style) == 0) { break; } return(new NullCheckExpressionInfo( binaryExpression, expression2, style)); } case SyntaxKind.TrueLiteralExpression: { NullCheckStyles style = (binaryExpressionKind == SyntaxKind.EqualsExpression) ? NullCheckStyles.HasValue : NullCheckStyles.NotHasValue; return(Create( binaryExpression, expression2, style, allowedStyles, allowMissing, semanticModel, cancellationToken)); } case SyntaxKind.FalseLiteralExpression: { NullCheckStyles style = (binaryExpressionKind == SyntaxKind.EqualsExpression) ? NullCheckStyles.NotHasValue : NullCheckStyles.HasValue; return(Create( binaryExpression, expression2, style, allowedStyles, allowMissing, semanticModel, cancellationToken)); } } return(Default); }
private NullCheckExpressionInfo( ExpressionSyntax nullCheckExpression, ExpressionSyntax expression, NullCheckStyles style) { NullCheckExpression = nullCheckExpression; Expression = expression; Style = style; }
private NullCheckExpressionInfo( ExpressionSyntax containingExpression, ExpressionSyntax expression, NullCheckStyles style) { NullCheckExpression = containingExpression; Expression = expression; Style = style; }
/// <summary> /// Creates a new <see cref="Syntax.NullCheckExpressionInfo"/> from the specified node. /// </summary> /// <param name="node"></param> /// <param name="allowedStyles"></param> /// <param name="walkDownParentheses"></param> /// <param name="allowMissing"></param> /// /// <returns></returns> public static NullCheckExpressionInfo NullCheckExpressionInfo( SyntaxNode node, NullCheckStyles allowedStyles = NullCheckStyles.ComparisonToNull | NullCheckStyles.IsPattern, bool walkDownParentheses = true, bool allowMissing = false) { return(Syntax.NullCheckExpressionInfo.Create( node, allowedStyles, walkDownParentheses, allowMissing)); }
private static NullCheckExpressionInfo CreateImpl( SyntaxNode node, SemanticModel semanticModel, NullCheckStyles allowedStyles, bool walkDownParentheses, bool allowMissing, CancellationToken cancellationToken) { ExpressionSyntax expression = WalkAndCheck(node, walkDownParentheses, allowMissing); if (expression == null) { return(default);
private static bool IsFixable( ExpressionSyntax left, ExpressionSyntax right, SyntaxKind binaryExpressionKind, SemanticModel semanticModel, CancellationToken cancellationToken) { NullCheckStyles allowedStyles = (binaryExpressionKind == SyntaxKind.LogicalAndExpression) ? NullCheckStyles.NotEqualsToNull : NullCheckStyles.EqualsToNull; NullCheckExpressionInfo nullCheck = SyntaxInfo.NullCheckExpressionInfo(left, allowedStyles: allowedStyles); ExpressionSyntax expression = nullCheck.Expression; if (expression == null) { return(false); } ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(expression, cancellationToken); if (typeSymbol == null) { return(false); } if (!typeSymbol.IsReferenceTypeOrNullableType()) { return(false); } if (right == null) { return(false); } if (!ValidateRightExpression(right, binaryExpressionKind, semanticModel, cancellationToken)) { return(false); } if (RefactoringUtility.ContainsOutArgumentWithLocal(right, semanticModel, cancellationToken)) { return(false); } ExpressionSyntax expression2 = FindExpressionThatCanBeConditionallyAccessed(expression, right, isNullable: !typeSymbol.IsReferenceType); return(expression2?.SpanContainsDirectives() == false); }
internal static NullCheckExpressionInfo Create( SyntaxNode node, SemanticModel semanticModel, NullCheckStyles allowedStyles = NullCheckStyles.All, bool walkDownParentheses = true, bool allowMissing = false, CancellationToken cancellationToken = default(CancellationToken)) { if (semanticModel == null) { throw new ArgumentNullException(nameof(semanticModel)); } return(CreateImpl(node, semanticModel, allowedStyles, walkDownParentheses, allowMissing, cancellationToken)); }
/// <summary> /// Creates a new <see cref="Syntax.NullCheckExpressionInfo"/> from the specified node. /// </summary> /// <param name="node"></param> /// <param name="semanticModel"></param> /// <param name="allowedStyles"></param> /// <param name="walkDownParentheses"></param> /// <param name="allowMissing"></param> /// <param name="cancellationToken"></param> /// <returns></returns> public static NullCheckExpressionInfo NullCheckExpressionInfo( SyntaxNode node, SemanticModel semanticModel, NullCheckStyles allowedStyles = NullCheckStyles.All, bool walkDownParentheses = true, bool allowMissing = false, CancellationToken cancellationToken = default(CancellationToken)) { return(Syntax.NullCheckExpressionInfo.Create( node, semanticModel, allowedStyles, walkDownParentheses, allowMissing, cancellationToken)); }
internal static NullCheckExpressionInfo Create( SyntaxNode node, NullCheckStyles allowedStyles = NullCheckStyles.ComparisonToNull | NullCheckStyles.IsPattern, bool walkDownParentheses = true, bool allowMissing = false) { if ((allowedStyles & NullCheckStyles.HasValue) != 0) { throw new ArgumentException($"'{nameof(NullCheckStyles.HasValue)}' style requires a SemanticModel to be provided.", nameof(allowedStyles)); } if ((allowedStyles & NullCheckStyles.NotHasValue) != 0) { throw new ArgumentException($"'{nameof(NullCheckStyles.NotHasValue)}' style requires a SemanticModel to be provided.", nameof(allowedStyles)); } return(CreateImpl(node, default(SemanticModel), allowedStyles, walkDownParentheses, allowMissing, default(CancellationToken))); }
private static NullCheckExpressionInfo Create( BinaryExpressionSyntax binaryExpression, ExpressionSyntax expression, NullCheckStyles style, NullCheckStyles allowedStyles, bool allowMissing, SemanticModel semanticModel, CancellationToken cancellationToken) { if ((allowedStyles & (NullCheckStyles.HasValueProperty)) == 0) { return(Default); } if (!(expression is MemberAccessExpressionSyntax memberAccessExpression)) { return(Default); } if (memberAccessExpression.Kind() != SyntaxKind.SimpleMemberAccessExpression) { return(Default); } if (!IsPropertyOfNullableOfT(memberAccessExpression.Name, "HasValue", semanticModel, cancellationToken)) { return(Default); } if ((allowedStyles & style) == 0) { return(Default); } ExpressionSyntax expression2 = memberAccessExpression.Expression; if (!Check(expression2, allowMissing)) { return(Default); } return(new NullCheckExpressionInfo(binaryExpression, expression2, style)); }
private static void Analyze(SyntaxNodeAnalysisContext context, BinaryExpressionSyntax binaryExpression, NullCheckStyles allowedStyles) { NullCheckExpressionInfo nullCheck = SyntaxInfo.NullCheckExpressionInfo(binaryExpression, allowedStyles: allowedStyles); if (nullCheck.Success && IsUnconstrainedTypeParameter(context.SemanticModel.GetTypeSymbol(nullCheck.Expression, context.CancellationToken)) && !binaryExpression.SpanContainsDirectives()) { DiagnosticHelpers.ReportDiagnostic(context, DiagnosticDescriptors.UnconstrainedTypeParameterCheckedForNull, binaryExpression); } }
private static NullCheckExpressionInfo CreateImpl( SyntaxNode node, SemanticModel semanticModel, NullCheckStyles allowedStyles, bool walkDownParentheses, bool allowMissing, CancellationToken cancellationToken) { ExpressionSyntax expression = WalkAndCheck(node, walkDownParentheses, allowMissing); if (expression == null) { return(Default); } SyntaxKind kind = expression.Kind(); switch (kind) { case SyntaxKind.EqualsExpression: case SyntaxKind.NotEqualsExpression: { var binaryExpression = (BinaryExpressionSyntax)expression; ExpressionSyntax left = WalkAndCheck(binaryExpression.Left, walkDownParentheses, allowMissing); if (left == null) { break; } ExpressionSyntax right = WalkAndCheck(binaryExpression.Right, walkDownParentheses, allowMissing); if (right == null) { break; } NullCheckExpressionInfo info = Create(binaryExpression, kind, left, right, allowedStyles, allowMissing, semanticModel, cancellationToken); if (info.Success) { return(info); } else { return(Create(binaryExpression, kind, right, left, allowedStyles, allowMissing, semanticModel, cancellationToken)); } } case SyntaxKind.SimpleMemberAccessExpression: { if ((allowedStyles & NullCheckStyles.HasValue) == 0) { break; } var memberAccessExpression = (MemberAccessExpressionSyntax)expression; if (!IsPropertyOfNullableOfT(memberAccessExpression.Name, "HasValue", semanticModel, cancellationToken)) { break; } return(new NullCheckExpressionInfo(expression, memberAccessExpression.Expression, NullCheckStyles.HasValue)); } case SyntaxKind.IsPatternExpression: { var isPatternExpression = (IsPatternExpressionSyntax)expression; if (!(isPatternExpression.Pattern is ConstantPatternSyntax constantPattern)) { break; } if (constantPattern.Expression?.IsKind(SyntaxKind.NullLiteralExpression) != true) { break; } ExpressionSyntax e = WalkAndCheck(isPatternExpression.Expression, walkDownParentheses, allowMissing); if (e == null) { break; } return(new NullCheckExpressionInfo(expression, e, NullCheckStyles.IsNull)); } case SyntaxKind.LogicalNotExpression: { if ((allowedStyles & (NullCheckStyles.NotHasValue | NullCheckStyles.NotIsNull)) == 0) { break; } var logicalNotExpression = (PrefixUnaryExpressionSyntax)expression; ExpressionSyntax operand = WalkAndCheck(logicalNotExpression.Operand, walkDownParentheses, allowMissing); if (operand == null) { break; } switch (operand.Kind()) { case SyntaxKind.SimpleMemberAccessExpression: { var memberAccessExpression = (MemberAccessExpressionSyntax)operand; if (!IsPropertyOfNullableOfT(memberAccessExpression.Name, "HasValue", semanticModel, cancellationToken)) { break; } return(new NullCheckExpressionInfo(expression, memberAccessExpression.Expression, NullCheckStyles.NotHasValue)); } case SyntaxKind.IsPatternExpression: { var isPatternExpression = (IsPatternExpressionSyntax)operand; if (!(isPatternExpression.Pattern is ConstantPatternSyntax constantPattern)) { break; } if (constantPattern.Expression?.IsKind(SyntaxKind.NullLiteralExpression) != true) { break; } ExpressionSyntax e = WalkAndCheck(isPatternExpression.Expression, walkDownParentheses, allowMissing); if (e == null) { break; } return(new NullCheckExpressionInfo(expression, e, NullCheckStyles.NotIsNull)); } } break; } } return(Default); }
public static async Task <Document> RefactorAsync( Document document, BinaryExpressionSyntax binaryExpression, CancellationToken cancellationToken) { SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); SyntaxKind kind = binaryExpression.Kind(); (ExpressionSyntax left, ExpressionSyntax right) = UseConditionalAccessAnalyzer.GetFixableExpressions(binaryExpression, kind, semanticModel, cancellationToken); NullCheckStyles allowedStyles = (kind == SyntaxKind.LogicalAndExpression) ? NullCheckStyles.NotEqualsToNull : NullCheckStyles.EqualsToNull; NullCheckExpressionInfo nullCheck = SyntaxInfo.NullCheckExpressionInfo(left, allowedStyles: allowedStyles); ExpressionSyntax expression = nullCheck.Expression; bool isNullable = semanticModel.GetTypeSymbol(expression, cancellationToken).IsNullableType(); ExpressionSyntax expression2 = UseConditionalAccessAnalyzer.FindExpressionThatCanBeConditionallyAccessed( expression, right, isNullable: isNullable, semanticModel, cancellationToken); var builder = new SyntaxNodeTextBuilder(binaryExpression, StringBuilderCache.GetInstance(binaryExpression.FullSpan.Length)); builder.Append(TextSpan.FromBounds(binaryExpression.FullSpan.Start, left.SpanStart)); int parenDiff = GetParenTokenDiff(); if (parenDiff > 0) { builder.Append('(', parenDiff); } builder.AppendSpan(expression); builder.Append("?"); builder.Append(TextSpan.FromBounds(expression2.Span.End, right.Span.End)); switch (right.Kind()) { case SyntaxKind.LogicalOrExpression: case SyntaxKind.LogicalAndExpression: case SyntaxKind.BitwiseOrExpression: case SyntaxKind.BitwiseAndExpression: case SyntaxKind.ExclusiveOrExpression: case SyntaxKind.EqualsExpression: case SyntaxKind.NotEqualsExpression: case SyntaxKind.LessThanExpression: case SyntaxKind.LessThanOrEqualExpression: case SyntaxKind.GreaterThanExpression: case SyntaxKind.GreaterThanOrEqualExpression: case SyntaxKind.IsExpression: case SyntaxKind.AsExpression: case SyntaxKind.IsPatternExpression: { break; } case SyntaxKind.LogicalNotExpression: { builder.Append((kind == SyntaxKind.LogicalAndExpression) ? " == false" : " != true"); break; } default: { builder.Append((kind == SyntaxKind.LogicalAndExpression) ? " == true" : " != false"); break; } } if (parenDiff < 0) { builder.Append(')', -parenDiff); } builder.Append(TextSpan.FromBounds(right.Span.End, binaryExpression.FullSpan.End)); string text = StringBuilderCache.GetStringAndFree(builder.StringBuilder); ParenthesizedExpressionSyntax newNode = SyntaxFactory.ParseExpression(text) .WithFormatterAnnotation() .Parenthesize(); return(await document.ReplaceNodeAsync(binaryExpression, newNode, cancellationToken).ConfigureAwait(false)); int GetParenTokenDiff() { int count = 0; foreach (SyntaxToken token in binaryExpression.DescendantTokens(TextSpan.FromBounds(left.SpanStart, expression2.Span.End))) { SyntaxKind tokenKind = token.Kind(); if (tokenKind == SyntaxKind.OpenParenToken) { if (token.IsParentKind(SyntaxKind.ParenthesizedExpression)) { count++; } } else if (tokenKind == SyntaxKind.CloseParenToken) { if (token.IsParentKind(SyntaxKind.ParenthesizedExpression)) { count--; } } } return(count); } }