public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
        {
            SyntaxNode root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);

            Diagnostic diagnostic     = context.Diagnostics.First();
            TextSpan   diagnosticSpan = diagnostic.Location.SourceSpan;

            ExpressionStatementSyntax registration = root
                                                     .FindToken(diagnosticSpan.Start)
                                                     .Parent
                                                     .AncestorsAndSelf()
                                                     .OfType <ExpressionStatementSyntax>()
                                                     .First();

            TypeDeclarationSyntax declaration = registration
                                                .AncestorsAndSelf()
                                                .OfType <TypeDeclarationSyntax>()
                                                .First();

            context.RegisterCodeFix(
                CodeAction.Create(
                    title: title,
                    createChangedDocument: c => MoveRegistrationToStaticCtorAsync(context.Document, declaration, registration, c),
                    equivalenceKey: title),
                diagnostic);
        }
示例#2
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));
        }