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