using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace RobloxCS { internal sealed class MainTransformer : BaseTransformer { public MainTransformer(SyntaxTree tree, ConfigData config) : base(tree, config) { } public override SyntaxNode? VisitCompilationUnit(CompilationUnitSyntax node) { var usings = node.Usings; usings = usings.Add(SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName("Roblox"))); usings = usings.Add(SyntaxFactory.UsingDirective(SyntaxFactory.Token(SyntaxKind.StaticKeyword), null, SyntaxFactory.QualifiedName(SyntaxFactory.IdentifierName("Roblox"), SyntaxFactory.IdentifierName("Globals")))); return base.VisitCompilationUnit(node.WithUsings(usings)); } public override SyntaxNode? VisitFileScopedNamespaceDeclaration(FileScopedNamespaceDeclarationSyntax node) { return VisitNamespaceDeclaration(SyntaxFactory.NamespaceDeclaration(node.AttributeLists, node.Modifiers, node.Name, node.Externs, node.Usings, node.Members)); } public override SyntaxNode? VisitDoStatement(DoStatementSyntax node) { // invert condition return base.VisitDoStatement(node.WithCondition(SyntaxFactory.PrefixUnaryExpression(SyntaxKind.LogicalNotExpression, node.Condition))); } public override SyntaxNode? VisitBinaryExpression(BinaryExpressionSyntax node) { if (node.OperatorToken.Text == "is") { var pattern = SyntaxFactory.TypePattern(SyntaxFactory.ParseTypeName(((IdentifierNameSyntax)node.Right).Identifier.Text)); return SyntaxFactory.IsPatternExpression(node.Left, pattern); } return base.VisitBinaryExpression(node); } public override SyntaxNode? VisitMethodDeclaration(MethodDeclarationSyntax node) { var newNode = node.Identifier.ValueText switch { "ToString" => node.WithIdentifier(CreateIdentifierToken("__tostring")), "Equals" => node.WithIdentifier(CreateIdentifierToken("__eq")), _ => node }; if (node != newNode && HasSyntax(newNode.Modifiers, SyntaxKind.OverrideKeyword)) { var newModifiers = newNode.Modifiers.RemoveAt(newNode.Modifiers.Select(token => token.Kind()).ToList().IndexOf(SyntaxKind.OverrideKeyword)); newNode = newNode.WithModifiers(newModifiers); } return base.VisitMethodDeclaration(newNode); } public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node) { var identifierText = node.Identifier.Text; if (!identifierText.Contains("@") || identifierText == "var") { return base.VisitIdentifierName(node); } var fixedIdentifierText = identifierText.Replace("@", ""); var newToken = CreateIdentifierToken(fixedIdentifierText); return base.VisitIdentifierName(node.WithIdentifier(newToken)); } public override SyntaxNode? VisitArgument(ArgumentSyntax node) { if (node.Expression.IsKind(SyntaxKind.IdentifierName)) { var newExpression = VisitIdentifierName((IdentifierNameSyntax)node.Expression); return base.VisitArgument(node.WithExpression((ExpressionSyntax)newExpression!)); } return base.VisitArgument(node); } public override SyntaxNode? VisitConditionalAccessExpression(ConditionalAccessExpressionSyntax node) { var whenNotNull = ProcessWhenNotNull(node.Expression, node.WhenNotNull); if (whenNotNull != null) { return base.VisitConditionalAccessExpression(node.WithWhenNotNull(whenNotNull)); } return base.VisitConditionalAccessExpression(node); } private static bool HasSyntax(SyntaxTokenList tokens, SyntaxKind syntax) { return tokens.Any(token => token.IsKind(syntax)); } private ExpressionSyntax? ProcessWhenNotNull(ExpressionSyntax expression, ExpressionSyntax whenNotNull) { if (whenNotNull == null) { return null; } switch (whenNotNull) { case MemberBindingExpressionSyntax memberBinding: return SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, expression, memberBinding.Name); case InvocationExpressionSyntax invocation: return invocation.WithExpression((invocation.Expression switch { MemberAccessExpressionSyntax memberAccess => SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, expression, memberAccess.Name ), ConditionalAccessExpressionSyntax nestedConditional => ProcessWhenNotNull(nestedConditional.WhenNotNull, expression), MemberBindingExpressionSyntax memberBinding => SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, expression, memberBinding.Name ), _ => SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, expression, SyntaxFactory.IdentifierName(invocation.Expression.ToString()) ) })!); case ConditionalAccessExpressionSyntax conditionalAccess: return conditionalAccess .WithExpression(ProcessWhenNotNull(expression, conditionalAccess.Expression) ?? conditionalAccess.Expression) .WithWhenNotNull(ProcessWhenNotNull(expression, conditionalAccess.WhenNotNull) ?? conditionalAccess.WhenNotNull); default: return null; }; } } }