// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; using System.IO; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace SDL.SourceGeneration { public class UnfriendlyMethodFinder : ISyntaxReceiver { public readonly Dictionary> Methods = new Dictionary>(); private static readonly string[] sdlPrefixes = ["SDL_", "TTF_", "IMG_", "MIX_"]; /// /// Checks whether the method is from any SDL library. /// It identifies those by checking the SDL prefix in the method name. /// private static bool IsMethodFromSDL(MethodDeclarationSyntax methodNode) { foreach (string prefix in sdlPrefixes) { if (methodNode.Identifier.ValueText.StartsWith(prefix, StringComparison.Ordinal)) return true; } return false; } public void OnVisitSyntaxNode(SyntaxNode syntaxNode) { if (syntaxNode is MethodDeclarationSyntax method) { string name = method.Identifier.ValueText; bool isUnsafe = name.StartsWith(Helper.UnsafePrefix, StringComparison.Ordinal); if (!IsMethodFromSDL(method) && !isUnsafe) return; if (method.ParameterList.Parameters.Any(p => p.Identifier.IsKind(SyntaxKind.ArgListKeyword))) return; var changes = Changes.None; // if the method is not marked unsafe, the `byte*` is not a string. if (method.ReturnType.IsBytePtr() && isUnsafe) { changes |= Changes.ChangeReturnTypeToString | Changes.TrimUnsafeFromName; if (!method.IsReturnTypeConstCharPtr()) changes |= Changes.FreeReturnedPointer; } foreach (var parameter in method.ParameterList.Parameters) { if (parameter.IsTypeConstCharPtr()) changes |= Changes.ChangeParamsToUtf8String; } if (changes != Changes.None) { string fileName = Path.GetFileName(method.SyntaxTree.FilePath); if (!Methods.TryGetValue(fileName, out var list)) Methods[fileName] = list = []; list.Add(new GeneratedMethod(method, changes)); } } } } }