예제 #1
0
        public static bool Look(SyntaxNode node)
        {
            if (node == null)
            {
                throw new ArgumentNullException(nameof(node));
            }

            var finder = new ContractCSharpMethodCallFinder();

            finder.Visit(node);
            return(finder.found);
        }
        private bool VisitInvocationExpression(InvocationExpressionSyntax node, out StatementSyntax replacementSyntax)
        {
            replacementSyntax = null;

            var assessExpression         = node.Expression as MemberAccessExpressionSyntax;
            var contractTypeRef          = assessExpression?.Expression as IdentifierNameSyntax;
            var contractMethodRef        = assessExpression?.Name.Identifier.ValueText ?? "";
            var contractMethodTypeParams = (assessExpression?.Name as GenericNameSyntax)?.TypeArgumentList.Arguments.ToList();

            if (contractTypeRef?.Identifier.ValueText != "Contract" || ContractRemover.ContractMethods.Contains(contractMethodRef) == false)
            {
                return(false);
            }

            var methodParamNames = new List <string>();
            var methodDecl       = node.FirstAncestorOrSelf <BaseMethodDeclarationSyntax>();
            var propDecl         = node.FirstAncestorOrSelf <PropertyDeclarationSyntax>();
            var indexDecl        = node.FirstAncestorOrSelf <IndexerDeclarationSyntax>();
            var eventDecl        = node.FirstAncestorOrSelf <EventDeclarationSyntax>();

            if (methodDecl != null)
            {
                methodParamNames.AddRange(methodDecl.ParameterList.Parameters.Select(p => p.Identifier.ValueText).ToList());
            }
            else if (propDecl != null || eventDecl != null)
            {
                methodParamNames.Add("value");
            }
            else if (indexDecl != null)
            {
                methodParamNames.AddRange(indexDecl.ParameterList.Parameters.Select(p => p.Identifier.ValueText).ToList());
                methodParamNames.Add("value");
            }
            else
            {
                return(false);
            }


            if (this.mode == ContractReplacementMode.Convert && contractMethodRef == "Requires")
            {
                var nsPrefix        = this.HasNamespace("System", node.SyntaxTree) ? "" : "System.";
                var checkExpression = node.ArgumentList.Arguments[0].Expression;
                var exceptionType   = contractMethodTypeParams?.FirstOrDefault() ??
                                      (
                    IsArgumentNullCheck(checkExpression, methodParamNames) ? SyntaxFactory.ParseTypeName(nsPrefix + "ArgumentNullException") :
                    IsRangeCheck(checkExpression, methodParamNames) ? SyntaxFactory.ParseTypeName(nsPrefix + "ArgumentOutOfRangeException") :
                    SyntaxFactory.ParseTypeName(nsPrefix + "ArgumentException")
                                      );
                var paramRef = (
                    from n in checkExpression.DescendantNodes()
                    let idSyntax = n as IdentifierNameSyntax
                                   let id = idSyntax?.Identifier.ValueText
                                            where id != null && methodParamNames.Contains(id)
                                            select id
                    ).FirstOrDefault() ?? "";


                if (ContractCSharpMethodCallFinder.Look(checkExpression))
                {
                    return(true);                    // replace with null
                }
                replacementSyntax = SyntaxFactory.IfStatement(
                    condition: InverseExpression(checkExpression),
                    statement: SyntaxFactory.Block(
                        SyntaxFactory.ThrowStatement(
                            SyntaxFactory.ObjectCreationExpression(
                                type: exceptionType,
                                argumentList: SyntaxFactory.ArgumentList(
                                    new SeparatedSyntaxList <ArgumentSyntax>()
                                    .Add(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(paramRef))))
                                    .Add(SyntaxFactory.Argument(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(checkExpression.ToString()))))

                                    ),
                                initializer: null
                                )
                            )
                        )
                    ).NormalizeWhitespace(eol: " ", indentation: " ").WithTriviaFrom(node.Parent);
            }
            else if (this.mode == ContractReplacementMode.Convert && (contractMethodRef == "Assert" || contractMethodRef == "Assume"))
            {
                var nsPrefix = this.HasNamespace("System.Diagnostics", node.SyntaxTree) ? "" : "System.Diagnostics.";

                var checkExpression   = node.ArgumentList.Arguments[0].Expression;
                var messageExpression = node.ArgumentList.Arguments.ElementAtOrDefault(1)?.Expression;

                replacementSyntax = SyntaxFactory.ExpressionStatement(
                    SyntaxFactory.InvocationExpression(
                        expression: SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ParseTypeName(nsPrefix + "Debug"), SyntaxFactory.IdentifierName("Assert")),
                        argumentList: SyntaxFactory.ArgumentList(
                            new SeparatedSyntaxList <ArgumentSyntax>()
                            .Add(SyntaxFactory.Argument(checkExpression))
                            .Add(SyntaxFactory.Argument(messageExpression ?? SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(checkExpression.ToString()))))
                            )
                        )
                    ).NormalizeWhitespace(eol: string.Empty, indentation: " ").WithTriviaFrom(node.Parent);
            }

            return(true);
        }
        private bool VisitInvocationExpression(InvocationExpressionSyntax node, out StatementSyntax replacementSyntax)
        {
            replacementSyntax = null;

            var assessExpression         = node.Expression as MemberAccessExpressionSyntax;
            var contractTypeRef          = assessExpression?.Expression as IdentifierNameSyntax;
            var contractMethodRef        = assessExpression?.Name.Identifier.ValueText ?? "";
            var contractMethodTypeParams = (assessExpression?.Name as GenericNameSyntax)?.TypeArgumentList.Arguments.ToList();

            if (contractTypeRef?.Identifier.ValueText != "Contract" || ContractRemover.ContractMethods.Contains(contractMethodRef) == false)
            {
                return(false);
            }

            var methodParamNames = new List <string>();
            var methodDecl       = node.FirstAncestorOrSelf <BaseMethodDeclarationSyntax>();
            var propDecl         = node.FirstAncestorOrSelf <PropertyDeclarationSyntax>();
            var indexDecl        = node.FirstAncestorOrSelf <IndexerDeclarationSyntax>();
            var eventDecl        = node.FirstAncestorOrSelf <EventDeclarationSyntax>();

            if (methodDecl != null)
            {
                methodParamNames.AddRange(methodDecl.ParameterList.Parameters.Select(p => p.Identifier.ValueText).ToList());
            }
            else if (propDecl != null || eventDecl != null)
            {
                methodParamNames.Add("value");
            }
            else if (indexDecl != null)
            {
                methodParamNames.AddRange(indexDecl.ParameterList.Parameters.Select(p => p.Identifier.ValueText).ToList());
                methodParamNames.Add("value");
            }
            else
            {
                return(false);
            }


            if (this.mode == ContractReplacementMode.Convert && (contractMethodRef == "Assert" || contractMethodRef == "Assume" || contractMethodRef == "Requires"))
            {
                var nsPrefix               = this.HasNamespace("System", node.SyntaxTree) ? "" : "System.";
                var checkExpression        = node.ArgumentList.Arguments[0].Expression;
                var errorMessageExpression = node.ArgumentList.Arguments.Count > 1 ? node.ArgumentList.Arguments[1].Expression : null;
                var exceptionType          = contractMethodTypeParams?.FirstOrDefault() ??
                                             (
                    IsArgumentNullCheck(checkExpression, methodParamNames) ? ParseTypeName(nsPrefix + "ArgumentNullException") :
                    IsRangeCheck(checkExpression, methodParamNames) ? ParseTypeName(nsPrefix + "ArgumentOutOfRangeException") :
                    ParseTypeName(nsPrefix + "ArgumentException")
                                             );
                var paramRef = checkExpression.DescendantNodes().OfType <IdentifierNameSyntax>()
                               .Where(ids => methodParamNames.Contains(ids.Identifier.ValueText))
                               .FirstOrDefault();
                var paramNameRef = paramRef != null
                                        ? (ExpressionSyntax)InvocationExpression(IdentifierName("nameof"))
                                   .WithArgumentList(ArgumentList(SingletonSeparatedList(Argument(paramRef))))
                                        : LiteralExpression(SyntaxKind.StringLiteralExpression, Literal("value"));

                if (ContractCSharpMethodCallFinder.Look(checkExpression))
                {
                    return(true);                    // replace with null
                }
                var replacementParams = new ExpressionSyntax[] {
                    paramNameRef,
                    errorMessageExpression ?? LiteralExpression(SyntaxKind.StringLiteralExpression, Literal("Contract assertion not met: " + checkExpression.ToString()))
                };

                if (exceptionType.ToString().EndsWith(".ArgumentException", StringComparison.Ordinal) ||
                    exceptionType.ToString().Equals("ArgumentException", StringComparison.Ordinal))
                {
                    Array.Reverse(replacementParams);                     // ArgumentException has reverse params order
                }

                // this includes comments
                var firstbaseWhitespace = node.Parent.GetLeadingTrivia();
                // this is exclusively indention
                var baseWhitespace     = TriviaList(firstbaseWhitespace.Reverse().TakeWhile(stt => stt.IsKind(SyntaxKind.WhitespaceTrivia)).Reverse());
                var indentedWhitespace = baseWhitespace.Add(Whitespace("    "));
                var endOfLineTrivia    = node.Parent.GetTrailingTrivia();
                var spaceList          = TriviaList(Space);
                replacementSyntax = IfStatement(
                    condition: InverseExpression(checkExpression),
                    statement: Block(
                        ThrowStatement(
                            ObjectCreationExpression(
                                type: exceptionType,
                                argumentList: ArgumentList(
                                    new SeparatedSyntaxList <ArgumentSyntax>()
                                    .Add(Argument(replacementParams[0]))
                                    .Add(Argument(replacementParams[1]))

                                    ),
                                initializer: null
                                ).NormalizeWhitespace()
                            )
                        .WithThrowKeyword(Token(indentedWhitespace, SyntaxKind.ThrowKeyword, spaceList))
                        .WithSemicolonToken(Token(TriviaList(), SyntaxKind.SemicolonToken, endOfLineTrivia))
                        )
                    .WithOpenBraceToken(Token(baseWhitespace, SyntaxKind.OpenBraceToken, endOfLineTrivia))
                    .WithCloseBraceToken(Token(baseWhitespace, SyntaxKind.CloseBraceToken, endOfLineTrivia))
                    )
                                    .WithIfKeyword(Token(firstbaseWhitespace, SyntaxKind.IfKeyword, spaceList))
                                    .WithCloseParenToken(Token(TriviaList(), SyntaxKind.CloseParenToken, endOfLineTrivia));
            }
            //else if (this.mode == ContractReplacementMode.Convert && (contractMethodRef == "Assert" || contractMethodRef == "Assume"))
            //{
            //	var nsPrefix = this.HasNamespace("System.Diagnostics", node.SyntaxTree) ? "" : "System.Diagnostics.";

            //	var checkExpression = node.ArgumentList.Arguments[0].Expression;
            //	var messageExpression = node.ArgumentList.Arguments.ElementAtOrDefault(1)?.Expression;

            //	replacementSyntax = SyntaxFactory.ExpressionStatement(
            //		SyntaxFactory.InvocationExpression(
            //			expression: SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ParseTypeName(nsPrefix + "Debug"), SyntaxFactory.IdentifierName("Assert")),
            //			argumentList: SyntaxFactory.ArgumentList(
            //				new SeparatedSyntaxList<ArgumentSyntax>()
            //					.Add(SyntaxFactory.Argument(checkExpression))
            //					.Add(SyntaxFactory.Argument(messageExpression ?? SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(checkExpression.ToString()))))
            //			)
            //		)
            //	).NormalizeWhitespace(eol: string.Empty, indentation: " ").WithTriviaFrom(node.Parent);
            //}

            return(true);
        }