Esempio n. 1
0
        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;
 }
Esempio n. 3
0
 private NullCheckExpressionInfo(
     ExpressionSyntax containingExpression,
     ExpressionSyntax expression,
     NullCheckStyles style)
 {
     NullCheckExpression = containingExpression;
     Expression          = expression;
     Style = style;
 }
Esempio n. 4
0
 /// <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));
        }
Esempio n. 8
0
 /// <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)));
        }
Esempio n. 10
0
        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));
        }
Esempio n. 11
0
        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);
            }
        }
Esempio n. 12
0
        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);
            }
        }