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); }
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)); }