Spaces:
Running
Running
using Microsoft.CodeAnalysis; | |
using Microsoft.CodeAnalysis.CSharp; | |
using Microsoft.CodeAnalysis.CSharp.Syntax; | |
namespace RobloxCS | |
{ | |
public sealed class DebugTransformer : BaseTransformer | |
{ | |
public DebugTransformer(SyntaxTree tree, ConfigData config) | |
: base(tree, config) | |
{ | |
} | |
public override SyntaxNode? VisitInvocationExpression(InvocationExpressionSyntax node) | |
{ | |
if (node.Expression is IdentifierNameSyntax || node.Expression is MemberAccessExpressionSyntax) | |
{ | |
{ | |
if (node.Expression is MemberAccessExpressionSyntax memberAccess) | |
{ | |
if (Utility.GetNamesFromNode(memberAccess.Expression).LastOrDefault() != "Globals") | |
{ | |
goto DoNothing; | |
} | |
} | |
} | |
{ | |
var name = GetName(node.Expression is MemberAccessExpressionSyntax memberAccess ? memberAccess.Name : node.Expression); | |
HashSet<string> concatMethodNames = ["error"]; | |
HashSet<string> extraArgNames = ["print", "warn"]; | |
var newNode = node; | |
if (concatMethodNames.Contains(name)) | |
{ | |
newNode = ConcatenateFileInfoToMessageArgument(node); | |
} | |
else if (extraArgNames.Contains(name)) | |
{ | |
newNode = PrependFileInfoArgument(node); | |
} | |
return base.VisitInvocationExpression(node.Expression is MemberAccessExpressionSyntax _memberAccess ? newNode.WithExpression(_memberAccess.Name) : newNode); | |
} | |
} | |
DoNothing: | |
return base.VisitInvocationExpression(node); | |
} | |
private InvocationExpressionSyntax ConcatenateFileInfoToMessageArgument(InvocationExpressionSyntax node) | |
{ | |
var messageArgument = node.ArgumentList.Arguments.First().Expression; | |
var fileInfoLiteral = GetFileInfoLiteral(node, addSpace: true); | |
var emptyTrivia = SyntaxFactory.TriviaList(); | |
var plusToken = SyntaxFactory.Token(emptyTrivia, SyntaxKind.PlusToken, "+", "", emptyTrivia); | |
var binaryExpression = SyntaxFactory.BinaryExpression(SyntaxKind.AddExpression, fileInfoLiteral, plusToken, messageArgument); | |
var newArgument = SyntaxFactory.Argument(binaryExpression); | |
var newArguments = SeparatedSyntaxList.Create(new ReadOnlySpan<ArgumentSyntax>(ref newArgument)); | |
var newArgumentListNode = node.ArgumentList.WithArguments(newArguments); | |
return node.WithArgumentList(newArgumentListNode); | |
} | |
private InvocationExpressionSyntax PrependFileInfoArgument(InvocationExpressionSyntax node) | |
{ | |
var fileInfoLiteral = GetFileInfoLiteral(node, addSpace: false); | |
var argument = SyntaxFactory.Argument(fileInfoLiteral); | |
var newArguments = SeparatedSyntaxList.Create(new ReadOnlySpan<ArgumentSyntax>(new List<ArgumentSyntax> { argument }.Concat(node.ArgumentList.Arguments).ToArray())); // good lord why do they make that so convoluted | |
var newArgumentListNode = node.ArgumentList.WithArguments(newArguments); | |
return node.WithArgumentList(newArgumentListNode); | |
} | |
private LiteralExpressionSyntax GetFileInfoLiteral(SyntaxNode node, bool addSpace) | |
{ | |
var fileLocation = FormatLocation(node); | |
var infoText = $"[{fileLocation}]:" + (addSpace ? " " : ""); | |
var emptyTrivia = SyntaxFactory.TriviaList(); | |
var token = SyntaxFactory.Token(emptyTrivia, SyntaxKind.StringLiteralToken, infoText, infoText, emptyTrivia); // why is trivia required bruh | |
return SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, token); | |
} | |
private string FormatLocation(SyntaxNode node) | |
{ | |
var location = Utility.FormatLocation(node.GetLocation().GetLineSpan()) | |
.Replace(_config.SourceFolder + "/", "") | |
.Replace("..", "./") | |
.Replace("./", ""); | |
return location; | |
} | |
} | |
} |