コード例 #1
0
        private bool IsContractInvocation(
            InvocationExpressionSyntax invocationExpression,
            ContractMethodNames allowedMethodNames,
            INamedTypeSymbol contractTypeSymbol)
        {
            MemberAccessExpressionSyntax?memberAccess =
                invocationExpression
                .Expression.As(x => x as MemberAccessExpressionSyntax);

            if ((ParseContractMethodName(memberAccess?.Name.Identifier.Text) & allowedMethodNames) == ContractMethodNames.None)
            {
                return(false);
            }

            var memberSymbol =
                memberAccess
                ?.As(x => _semanticModel.GetSymbolInfo(x).Symbol as IMethodSymbol);

            // TODO: ToMetadataFullName() on every call is probably somewhat expensive
            if (memberSymbol == null || !memberSymbol.ContainingType.Equals(contractTypeSymbol))
            {
                // This is not Contract.
                return(false);
            }

            return(true);
        }
 private static string GetCheckMethod(ContractMethodNames contractMethod)
 {
     return(contractMethod switch
     {
         RequiresDebug => FluentContractNames.CheckDebugMethodName,
         AssertDebug => FluentContractNames.CheckDebugMethodName,
         _ => FluentContractNames.CheckMethodName,
     });
        private static string GetTargetMethod(ContractMethodNames contractMethod)
        {
            if (contractMethod.IsPrecondition())
            {
                return(FluentContractNames.Requires);
            }

            return(FluentContractNames.Assert);
        }
コード例 #4
0
        public bool GetContractInvocation(IMethodSymbol invokedMethod, out ContractMethodNames contract)
        {
            contract = default;
            if (!invokedMethod.ContainingType.Equals(_runtimeContractTypeSymbol))
            {
                return(false);
            }

            contract = ParseContractMethodName(invokedMethod.Name);
            return(true);
        }
コード例 #5
0
        private bool IsContractInvocation(
            IMethodSymbol?memberSymbol,
            ContractMethodNames allowedMethodNames,
            INamedTypeSymbol contractTypeSymbol)
        {
            if ((ParseContractMethodName(memberSymbol?.Name) & allowedMethodNames) == ContractMethodNames.None)
            {
                return(false);
            }

            // TODO: ToMetadataFullName() on every call is probably somewhat expensive
            if (memberSymbol == null || !memberSymbol.ContainingType.Equals(contractTypeSymbol))
            {
                // This is not Contract.
                return(false);
            }

            return(true);
        }
コード例 #6
0
 public bool IsStandardContractInvocation(
     InvocationExpressionSyntax invocationExpression,
     ContractMethodNames allowedMethodNames = ContractMethodNames.All)
 {
     return(IsContractInvocation(invocationExpression, allowedMethodNames, _standardContractTypeSymbol));
 }
コード例 #7
0
 public static bool IsForAll(this ContractMethodNames contract)
 => (contract & RequiresForAll) != None;
コード例 #8
0
 public static bool IsNotNullOrWhiteSpace(this ContractMethodNames contract)
 => (contract & (RequiresNotNullOrWhiteSpace | AssertNotNullOrWhiteSpace)) != None;
        private static (SyntaxNode source, SyntaxNode destination) GetFluentContractsReplacements(
            IInvocationOperation operation,
            ContractMethodNames contractMethod,
            SemanticModel semanticModel,
            CancellationToken token)
        {
            var invocationExpression = operation.Syntax;

            // Getting the original predicate.
            var predicateArgumentOperation = operation.Arguments[0];
            var predicateArgument          = (ArgumentSyntax)predicateArgumentOperation.Syntax;

            ArgumentSyntax?extraForAllArgument  = null;
            int            messageArgumentIndex = 1;

            // We need to mutate it for the cases like RequiresNotNull and RequiresNotNullOrEmpty

            if (contractMethod.IsNullCheck())
            {
                predicateArgument =
                    Argument(
                        // Changing NotNull(x) to x != null
                        BinaryExpression(
                            SyntaxKind.NotEqualsExpression,
                            predicateArgument.Expression,
                            LiteralExpression(SyntaxKind.NullLiteralExpression)))
                    .NormalizeWhitespace();
            }
            else if (contractMethod.IsNotNullOrEmpty() || contractMethod.IsNotNullOrWhiteSpace())
            {
                var stringMethodName = contractMethod.IsNotNullOrEmpty() ? nameof(string.IsNullOrEmpty) : nameof(string.IsNullOrWhiteSpace);

                // Targeting a full framework can cause an issue for null-ness analysis
                // because string.IsNullOrEmpty and string.IsNullOrWhiteSpace is not annotated with any attributes.
                // It means that the compiler can't recognize that the following code is correct and still emit the warning:
                // if (!string.IsNullOrEmpty(str)) return str.Length;
                predicateArgument =
                    Argument(
                        PrefixUnaryExpression(
                            SyntaxKind.LogicalNotExpression,
                            InvocationExpression(
                                MemberAccessExpression(
                                    SyntaxKind.SimpleMemberAccessExpression,
                                    PredefinedType(Token(SyntaxKind.StringKeyword)),
                                    IdentifierName(stringMethodName)))
                            .WithArgumentList(
                                ArgumentList(SingletonSeparatedList(predicateArgument))))
                        ).NormalizeWhitespace();
            }
            else if (contractMethod.IsForAll())
            {
                extraForAllArgument = (ArgumentSyntax)operation.Arguments[1].Syntax;
                messageArgumentIndex++;
            }

            // Detecting the following case:
            // if (predicate is false) {Contract.Assert(false, complicatedMessage);}
            var sourceNode = invocationExpression.Parent;

            if (predicateArgumentOperation.Value is ILiteralOperation lo &&
                lo.ConstantValue.HasValue && lo.ConstantValue.Value.Equals(false))
            {
                // this is Assert(false) case.
                if (operation.Parent?.Parent?.Parent is IConditionalOperation conditional &&
                    conditional.WhenFalse == null &&
                    conditional.WhenTrue.Children.Count() == 1)
                {
                    // The contract is inside the if block with a single statement.
                    sourceNode = conditional.Syntax;

                    var negatedCondition = (ExpressionSyntax?)SyntaxGeneratorExtensions.Negate(conditional.Condition.Syntax, semanticModel, token);
                    if (negatedCondition != null)
                    {
                        predicateArgument = Argument(negatedCondition);
                    }
                }
            }

            var           originalMessageArgument = operation.Arguments[messageArgumentIndex];
            Func <string> nonDefaultArgument      =
                () => contractMethod.IsForAll()
                    ? operation.Arguments[1].Syntax.ToFullString()
                    : predicateArgument.ToFullString();

            // Using an original message if provided.
            // Otherwise using a predicate as the new message.
            var messageArgument =
                originalMessageArgument.IsImplicit == false
                    ? (ArgumentSyntax)originalMessageArgument.Syntax
                    : Argument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(nonDefaultArgument())));

            var arguments =
                new SeparatedSyntaxList <ArgumentSyntax>()
                .Add(predicateArgument)
                .AddIfNotNull(extraForAllArgument);

            // Generating Contract.Check(predicate)?.Requires/Assert(message)

            var targetMethodName = GetTargetMethod(contractMethod);
            var checkMethodName  = GetCheckMethod(contractMethod);
            var contractCall     =
                ExpressionStatement(
                    ConditionalAccessExpression(
                        // Contract.Requires(
                        InvocationExpression(
                            MemberAccessExpression(
                                SyntaxKind.SimpleMemberAccessExpression,
                                IdentifierName(FluentContractNames.ContractClassName),
                                IdentifierName(checkMethodName)))
                        // (predicate)
                        .WithArgumentList(
                            ArgumentList(arguments)),
                        // ?.IsTrue(message)
                        InvocationExpression(
                            MemberBindingExpression(
                                IdentifierName(targetMethodName)))
                        .WithArgumentList(
                            ArgumentList(
                                SingletonSeparatedList(messageArgument)))
                        )
                    );

            var trivia    = sourceNode.GetLeadingTrivia();
            var finalNode = contractCall.WithLeadingTrivia(trivia);

            return(sourceNode, finalNode);
        }
コード例 #10
0
 public static bool IsNullCheck(this ContractMethodNames contract)
 => (contract & (RequiresNotNull | AssertNotNull)) != None;
コード例 #11
0
 public static bool IsPostcondition(this ContractMethodNames contract)
 => (contract & (Ensures | EnsuresOnThrow)) != None;
コード例 #12
0
 public static bool IsAssertFailure(this ContractMethodNames contract)
 => (contract & AssertFailure) != None;
コード例 #13
0
 public static bool IsPrecondition(this ContractMethodNames contract)
 => (contract & AllRequires) != None;
コード例 #14
0
 public bool IsFluentContractInvocation(
     InvocationExpressionSyntax invocationExpression,
     ContractMethodNames allowedMethodNames = ContractMethodNames.AllFluentContracts)
 {
     return(IsContractInvocation(invocationExpression, allowedMethodNames, _runtimeContractTypeSymbol));
 }
コード例 #15
0
 public static bool IsNotNullOrEmpty(this ContractMethodNames contract)
 => (contract & (RequiresNotNullOrEmpty | AssertNotNullOrEmpty)) != None;
コード例 #16
0
 public bool IsFluentContractInvocation(
     IMethodSymbol method,
     ContractMethodNames allowedMethodNames = ContractMethodNames.AllFluentContracts)
 {
     return(IsContractInvocation(method, allowedMethodNames, _runtimeContractTypeSymbol));
 }
コード例 #17
0
        private static (SyntaxNode source, SyntaxNode destination) GetFluentContractsReplacements(
            IInvocationOperation operation,
            ContractMethodNames contractMethod,
            SemanticModel semanticModel,
            CancellationToken token)
        {
            var invocationExpression = operation.Syntax;

            // Getting the original predicate.
            var predicateArgumentOperation = operation.Arguments[0];
            var predicateArgument          = (ArgumentSyntax)predicateArgumentOperation.Syntax;

            ArgumentSyntax?extraForAllArgument  = null;
            int            messageArgumentIndex = 1;

            // We need to mutate it for the cases like RequiresNotNull and RequiresNotNullOrEmpty

            if (contractMethod.IsNullCheck())
            {
                predicateArgument =
                    Argument(
                        // Changing NotNull(x) to x != null
                        BinaryExpression(
                            SyntaxKind.NotEqualsExpression,
                            predicateArgument.Expression,
                            LiteralExpression(SyntaxKind.NullLiteralExpression)))
                    .NormalizeWhitespace();
            }
            else if (contractMethod.IsNotNullOrEmpty() || contractMethod.IsNotNullOrWhiteSpace())
            {
                var stringMethodName = contractMethod.IsNotNullOrEmpty() ? nameof(string.IsNullOrEmpty) : nameof(string.IsNullOrWhiteSpace);

                // Targeting a full framework can cause an issue for null-ness analysis
                // because string.IsNullOrEmpty and string.IsNullOrWhiteSpace is not annotated with any attributes.
                // It means that the compiler can't recognize that the following code is correct and still emit the warning:
                // if (!string.IsNullOrEmpty(str)) return str.Length;
                predicateArgument =
                    Argument(
                        PrefixUnaryExpression(
                            SyntaxKind.LogicalNotExpression,
                            InvocationExpression(
                                MemberAccessExpression(
                                    SyntaxKind.SimpleMemberAccessExpression,
                                    PredefinedType(Token(SyntaxKind.StringKeyword)),
                                    IdentifierName(stringMethodName)))
                            .WithArgumentList(
                                ArgumentList(SingletonSeparatedList(predicateArgument))))
                        ).NormalizeWhitespace();
            }

            // Detecting the following case:
            // if (predicate is false) {Contract.Assert(false, complicatedMessage);}
            var sourceNode = invocationExpression.Parent;

            var originalMessageArgument = operation.Arguments[messageArgumentIndex];

            // Using an original message if provided.
            // Otherwise using a predicate as the new message.
            var messageArgument =
                originalMessageArgument.IsImplicit == false
                    ? (ArgumentSyntax)originalMessageArgument.Syntax
                    : null;

            var arguments =
                new SeparatedSyntaxList <ArgumentSyntax>()
                .Add(predicateArgument)
                .AddIfNotNull(extraForAllArgument)
                .AddIfNotNull(messageArgument);

            // Generating Contract.Check(predicate)?.Requires/Assert(message)

            var targetMethodName = GetTargetMethod(contractMethod);
            var contractCall     =
                //ExpressionStatement(
                // Contract.Requires(
                InvocationExpression(
                    MemberAccessExpression(
                        SyntaxKind.SimpleMemberAccessExpression,
                        IdentifierName(FluentContractNames.ContractClassName),
                        IdentifierName(targetMethodName)))
                // (predicate, message)
                .WithArgumentList(ArgumentList(arguments)
                                  //)
                                  );

            var trivia    = sourceNode.GetLeadingTrivia();
            var finalNode = contractCall.WithLeadingTrivia(trivia);

            return(invocationExpression, finalNode);
        }