private static void ReportDiagnostics(
            SyntaxNodeAnalysisContext syntaxContext,
            StatementSyntax firstStatement,
            IfStatementSyntax ifStatement,
            ExpressionStatementSyntax expressionStatement,
            DiagnosticSeverity severity,
            List <Location> additionalLocations,
            string kind)
        {
            var tree = syntaxContext.Node.SyntaxTree;

            var properties = ImmutableDictionary <string, string> .Empty.Add(
                Constants.Kind, kind);

            var previousToken = expressionStatement.GetFirstToken().GetPreviousToken();
            var nextToken     = expressionStatement.GetLastToken().GetNextToken();

            // Fade out the code up to the expression statement.
            syntaxContext.ReportDiagnostic(Diagnostic.Create(s_unnecessaryDescriptor,
                                                             Location.Create(tree, TextSpan.FromBounds(firstStatement.SpanStart, previousToken.Span.End)),
                                                             additionalLocations, properties));

            // Put a diagnostic with the appropriate severity on the expression-statement itself.
            syntaxContext.ReportDiagnostic(Diagnostic.Create(CreateDescriptor(severity),
                                                             expressionStatement.GetLocation(),
                                                             additionalLocations, properties));

            // If the if-statement extends past the expression statement, then fade out the rest.
            if (nextToken.Span.Start < ifStatement.Span.End)
            {
                syntaxContext.ReportDiagnostic(Diagnostic.Create(s_unnecessaryDescriptor,
                                                                 Location.Create(tree, TextSpan.FromBounds(nextToken.Span.Start, ifStatement.Span.End)),
                                                                 additionalLocations, properties));
            }
        }
        private void ReportDiagnostics(
            SyntaxNodeAnalysisContext syntaxContext,
            StatementSyntax firstStatement,
            IfStatementSyntax ifStatement,
            ExpressionStatementSyntax expressionStatement,
            ReportDiagnostic severity,
            ImmutableArray <Location> additionalLocations,
            string kind)
        {
            var tree = syntaxContext.Node.SyntaxTree;

            var properties = ImmutableDictionary <string, string?> .Empty.Add(
                Constants.Kind, kind);

            var previousToken = expressionStatement.GetFirstToken().GetPreviousToken();
            var nextToken     = expressionStatement.GetLastToken().GetNextToken();

            // Fade out the code up to the expression statement.
            var fadeLocation = Location.Create(tree, TextSpan.FromBounds(firstStatement.SpanStart, previousToken.Span.End));

            syntaxContext.ReportDiagnostic(DiagnosticHelper.CreateWithLocationTags(
                                               Descriptor,
                                               fadeLocation,
                                               ReportDiagnostic.Default,
                                               additionalLocations,
                                               additionalUnnecessaryLocations: ImmutableArray.Create(fadeLocation),
                                               properties));

            // Put a diagnostic with the appropriate severity on the expression-statement itself.
            syntaxContext.ReportDiagnostic(DiagnosticHelper.Create(
                                               Descriptor,
                                               expressionStatement.GetLocation(),
                                               severity,
                                               additionalLocations, properties));

            // If the if-statement extends past the expression statement, then fade out the rest.
            if (nextToken.Span.Start < ifStatement.Span.End)
            {
                fadeLocation = Location.Create(tree, TextSpan.FromBounds(nextToken.Span.Start, ifStatement.Span.End));
                syntaxContext.ReportDiagnostic(DiagnosticHelper.CreateWithLocationTags(
                                                   Descriptor,
                                                   fadeLocation,
                                                   ReportDiagnostic.Default,
                                                   additionalLocations,
                                                   additionalUnnecessaryLocations: ImmutableArray.Create(fadeLocation),
                                                   properties));
            }
        }
Exemple #3
0
        public override SyntaxNode?VisitExpressionStatement(ExpressionStatementSyntax node)
        {
            if (node.GetFirstToken().Text == "Logger")
            {
                var isConstructorDeclaration = node.AncestorsAndSelf().Any(x => x.Kind() == SyntaxKind.ConstructorDeclaration);
                if (isConstructorDeclaration)
                {
                    _logger.LogInformation("removing old logger assignment expression in constructor");
                    _removedOldLogger = true;
                    return(null);
                }

                var isMethodOrPropertyDeclaration = node.AncestorsAndSelf().Any(x =>
                {
                    var kind = x.Kind();
                    return(kind == SyntaxKind.MethodDeclaration || kind == SyntaxKind.PropertyDeclaration);
                });
                if (isMethodOrPropertyDeclaration)
                {
                    if (node.Expression.Kind() == SyntaxKind.InvocationExpression)
                    {
                        _logger.LogInformation("┌────────────{counter}────────────┐", ++_counter);
                        _logger.LogInformation("refactoring: {expression}", node.ToString());


                        // method
                        var invocationExpressionSyntax = (InvocationExpressionSyntax)node.Expression;
                        var methodName = invocationExpressionSyntax.Expression.GetLastToken().ToString();
                        methodName = GetLoggerFunctionName(methodName);

                        // args
                        var argumentList   = invocationExpressionSyntax.ArgumentList;
                        var newArgList     = ArgumentList(SeparatedList <ArgumentSyntax>());
                        var argumentsToAdd = new List <ArgumentSyntax>();

                        // find exception parameter
                        var exceptionArgumentSyntax = argumentList.Arguments.FirstOrDefault(x => IsException(SemanticModel.GetTypeInfo(x.Expression).Type));
                        if (exceptionArgumentSyntax != null)
                        {
                            _logger.LogInformation("found exception param: {name}", exceptionArgumentSyntax.ToString());
                            newArgList = newArgList.AddArguments(exceptionArgumentSyntax);
                        }

                        // find message parameter
                        var messageArgumentSyntax = argumentList.Arguments
                                                    .First(x =>
                        {
                            var typeName = SemanticModel.GetTypeInfo(x.Expression).Type.Name;
                            var kind     = x.Expression.Kind();
                            return(typeName == nameof(String) &&
                                   (kind == SyntaxKind.StringLiteralExpression ||
                                    kind == SyntaxKind.InterpolatedStringExpression ||
                                    kind == SyntaxKind.AddExpression ||
                                    kind == SyntaxKind.IdentifierName ||
                                    kind == SyntaxKind.SimpleMemberAccessExpression ||
                                    kind == SyntaxKind.InvocationExpression));
                        });
                        var messageArgumentExpressionKind = messageArgumentSyntax.Expression.Kind();

                        if (messageArgumentExpressionKind == SyntaxKind.StringLiteralExpression)
                        {
                            var messageArgumentIndex = argumentList.Arguments.IndexOf(messageArgumentSyntax);
                            var str   = messageArgumentSyntax.Expression.GetFirstToken().ValueText;
                            var index = 0;

                            var indexStr   = $"{{{index++}}}";
                            var foundIndex = str.IndexOf(indexStr);

                            while (foundIndex != -1)
                            {
                                // find argument to replace index
                                var argIndex = messageArgumentIndex + index;
                                if (argIndex < argumentList.Arguments.Count)
                                {
                                    var arg = argumentList.Arguments[argIndex];
                                    argumentsToAdd.Add(arg);
                                    var propertyStr = "{" + Regex.Replace(arg.Expression.ToString(), "[^A-Za-z0-9]", "") + "}";

                                    _logger.LogDebug("replacing {indexStr} to {propertyStr}", indexStr, propertyStr);
                                    str = str.Replace(indexStr, propertyStr);
                                }

                                // next
                                indexStr   = $"{{{index++}}}";
                                foundIndex = str.IndexOf(indexStr);
                            }

                            _logger.LogInformation("adding message param: {message}", str);
                            var messageArgument = LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(str));
                            newArgList = newArgList.AddArguments(Argument(messageArgument));

                            foreach (var argumentSyntax in argumentsToAdd)
                            {
                                _logger.LogInformation("adding other param: {param}", argumentSyntax.Expression.ToString());
                                newArgList = newArgList.AddArguments(argumentSyntax);
                            }
                        }

                        if (messageArgumentExpressionKind == SyntaxKind.InterpolatedStringExpression)
                        {
                            var syntax = (InterpolatedStringExpressionSyntax)messageArgumentSyntax.Expression;
                            var str    = string.Concat(syntax.Contents.Select(x => x.ToString()));

                            // fetch param from interpolation string
                            var interpolationSyntaxes = syntax.Contents.OfType <InterpolationSyntax>();
                            foreach (var interpolationSyntax in interpolationSyntaxes)
                            {
                                var name = interpolationSyntax.Expression.ToString();
                                var arg  = Argument(IdentifierName(name));

                                var propertyStr = Regex.Replace(name, "[^A-Za-z0-9]", "");
                                if (name != propertyStr)
                                {
                                    name        = $"{{{name}}}";
                                    propertyStr = $"{{{propertyStr}}}";
                                    _logger.LogDebug("replacing {interpolationStr} to {propertyStr}", name, propertyStr);
                                    str = str.Replace(name, propertyStr);
                                }

                                argumentsToAdd.Add(arg);
                            }

                            _logger.LogInformation("adding message param: {message}", str);
                            newArgList = newArgList.AddArguments(Argument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(str))));

                            foreach (var argumentSyntax in argumentsToAdd)
                            {
                                _logger.LogInformation("adding other param: {param}", argumentSyntax.Expression.ToString());
                                newArgList = newArgList.AddArguments(argumentSyntax);
                            }
                        }

                        if (messageArgumentExpressionKind == SyntaxKind.AddExpression)
                        {
                            var str = messageArgumentSyntax.Expression.ToString();
                            _logger.LogWarning("adding message param with string concat: {message}", str);
                            newArgList = newArgList.AddArguments(messageArgumentSyntax);
                        }

                        if (messageArgumentExpressionKind == SyntaxKind.IdentifierName ||
                            messageArgumentExpressionKind == SyntaxKind.SimpleMemberAccessExpression ||
                            messageArgumentExpressionKind == SyntaxKind.InvocationExpression)
                        {
                            var str = messageArgumentSyntax.Expression.ToString();
                            _logger.LogInformation("adding message param: {message}", str);
                            newArgList = newArgList.AddArguments(messageArgumentSyntax);
                        }

                        var expression = InvocationExpression(
                            MemberAccessExpression(
                                SyntaxKind.SimpleMemberAccessExpression,
                                IdentifierName("_logger"),
                                IdentifierName(methodName)
                                )
                            )
                                         .WithArgumentList(
                            newArgList.NormalizeWhitespace()
                            )
                                         .WithLeadingTrivia(node.Expression.GetLeadingTrivia())
                                         .WithTrailingTrivia(node.Expression.GetTrailingTrivia())
                        ;

                        // output skipped arguments
                        var skippedArguments = argumentList.Arguments
                                               .Except(new[] { exceptionArgumentSyntax, messageArgumentSyntax })
                                               .Except(argumentsToAdd)
                                               .ToList();
                        foreach (var skippedArgument in skippedArguments)
                        {
                            _logger.LogWarning("skipped argument: {argument}", skippedArgument.ToString());
                        }

                        _logger.LogInformation("refactored: {statement}", expression.ToString());
                        _logger.LogInformation("└────────────{counterText}────────────┘", new string('─', (int)Math.Floor(Math.Log10(_counter) + 1)));

                        return(node.WithExpression(expression));
                    }
                }
            }

            return(base.VisitExpressionStatement(node));
        }