<commit_before>﻿using System;
using System.Collections.Generic;
using System.Linq;
using CppSharp.AST;
using CppSharp.AST.Extensions;

namespace CppSharp.Generators.C
{
    public enum CppTypePrintFlavorKind
    {
        C,
        Cpp,
        ObjC
    }

    public class CppTypePrinter : TypePrinter
    {
        public CppTypePrintFlavorKind PrintFlavorKind { get; set; }
        public bool PrintLogicalNames { get; set; }
        public bool PrintTypeQualifiers { get; set; }
        public bool PrintTypeModifiers { get; set; }
        public bool PrintVariableArrayAsPointers { get; set; }

        public CppTypePrinter() : base(TypePrinterContextKind.Managed)
        {
            PrintFlavorKind = CppTypePrintFlavorKind.Cpp;
            ScopeKind = TypePrintScopeKind.GlobalQualified;
            PrintTypeQualifiers = true;
            PrintTypeModifiers = true;
        }

        public bool ResolveTypedefs { get; set; }

        public override TypePrinterResult VisitTagType(TagType tag, TypeQualifiers quals)
        {
            var qual = GetStringQuals(quals);
            return $"{qual}{tag.Declaration.Visit(this)}";
        }

        public override TypePrinterResult VisitArrayType(ArrayType array, TypeQualifiers quals)
        {
            var typeName = array.Type.Visit(this);

            switch (array.SizeType)
            {
            case ArrayType.ArraySize.Constant:
                return $"{typeName}[{array.Size}]";
            case ArrayType.ArraySize.Variable:
            case ArrayType.ArraySize.Dependent:
            case ArrayType.ArraySize.Incomplete:
                return $"{typeName}{(PrintVariableArrayAsPointers ? "*" : "[]")}";
            }

            throw new NotSupportedException();
        }

        private static string ConvertModifierToString(PointerType.TypeModifier modifier)
        {
            switch (modifier)
            {
                case PointerType.TypeModifier.Value: return "[]";
                case PointerType.TypeModifier.Pointer: return "*";
                case PointerType.TypeModifier.LVReference: return "&";
                case PointerType.TypeModifier.RVReference: return "&&";
            }

            return string.Empty;
        }

        public override TypePrinterResult VisitPointerType(PointerType pointer, TypeQualifiers quals)
        {
            var qual = GetStringQuals(quals, false);
            var pointeeType = pointer.Pointee.Visit(this, pointer.QualifiedPointee.Qualifiers);
            var mod = PrintTypeModifiers ? ConvertModifierToString(pointer.Modifier) : string.Empty;
            pointeeType.NameSuffix.Append(mod);
            if (!string.IsNullOrEmpty(qual))
                pointeeType.NameSuffix.Append(' ').Append(qual);
            return pointeeType;
        }

        public override TypePrinterResult VisitMemberPointerType(MemberPointerType member, TypeQualifiers quals)
        {
            return string.Empty;
        }

        public override TypePrinterResult VisitBuiltinType(BuiltinType builtin, TypeQualifiers quals)
        {
            var qual = GetStringQuals(quals);
            return $"{qual}{VisitPrimitiveType(builtin.Type)}";
        }

        public TypePrinterResult VisitPrimitiveType(PrimitiveType primitive)
        {
            switch (primitive)
            {
                case PrimitiveType.Bool: return "bool";
                case PrimitiveType.Void: return "void";
                case PrimitiveType.Char16: return "char16_t";
                case PrimitiveType.Char32: return "char32_t";
                case PrimitiveType.WideChar: return "wchar_t";
                case PrimitiveType.Char: return "char";
                case PrimitiveType.SChar: return "signed char";
                case PrimitiveType.UChar: return "unsigned char";
                case PrimitiveType.Short: return "short";
                case PrimitiveType.UShort: return "unsigned short";
                case PrimitiveType.Int: return "int";
                case PrimitiveType.UInt: return "unsigned int";
                case PrimitiveType.Long: return "long";
                case PrimitiveType.ULong: return "unsigned long";
                case PrimitiveType.LongLong: return "long long";
                case PrimitiveType.ULongLong: return "unsigned long long";
                case PrimitiveType.Int128: return "__int128_t";
                case PrimitiveType.UInt128: return "__uint128_t";
                case PrimitiveType.Half: return "__fp16";
                case PrimitiveType.Float: return "float";
                case PrimitiveType.Double: return "double";
                case PrimitiveType.LongDouble: return "long double";
                case PrimitiveType.Float128: return "__float128";
                case PrimitiveType.IntPtr: return "void*";
                case PrimitiveType.UIntPtr: return "uintptr_t";
                case PrimitiveType.Null: return PrintFlavorKind == CppTypePrintFlavorKind.Cpp ? "std::nullptr_t" : "NULL";
                case PrimitiveType.String:
                {
                    switch (PrintFlavorKind)
                    {
                    case CppTypePrintFlavorKind.C:
                        return "const char*";
                    case CppTypePrintFlavorKind.Cpp:
                        return "std::string";
                    case CppTypePrintFlavorKind.ObjC:
                        return "NSString";
                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }
                case PrimitiveType.Decimal:
                {
                    switch (PrintFlavorKind)
                    {
                    case CppTypePrintFlavorKind.C:
                    case CppTypePrintFlavorKind.Cpp:
                        return "_Decimal32";
                    case CppTypePrintFlavorKind.ObjC:
                        return "NSDecimalNumber";
                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }
            }

            throw new NotSupportedException();
        }

        public override TypePrinterResult VisitPrimitiveType(PrimitiveType primitive, TypeQualifiers quals)
        {
            var qual = GetStringQuals(quals);
            return $"{qual}{VisitPrimitiveType(primitive)}";
        }

        public override TypePrinterResult VisitTypedefType(TypedefType typedef, TypeQualifiers quals)
        {
            FunctionType func;
            var qual = GetStringQuals(quals);
            if (ResolveTypedefs && !typedef.Declaration.Type.IsPointerTo(out func))
            {
                TypePrinterResult type = typedef.Declaration.QualifiedType.Visit(this);
                return new TypePrinterResult { Type = $"{qual}{type.Type}", NameSuffix = type.NameSuffix };
            }
            return $"{qual}{typedef.Declaration.Visit(this)}";
        }

        public override TypePrinterResult VisitAttributedType(AttributedType attributed, TypeQualifiers quals)
        {
            return attributed.Modified.Visit(this);
        }

        public override TypePrinterResult VisitDecayedType(DecayedType decayed, TypeQualifiers quals)
        {
            return decayed.Decayed.Visit(this);
        }

        public override TypePrinterResult VisitTemplateSpecializationType(TemplateSpecializationType template, TypeQualifiers quals)
        {
            var specialization = template.GetClassTemplateSpecialization();
            if (specialization == null)
                return string.Empty;

            var qual = GetStringQuals(quals);
            return $"{qual}{VisitClassTemplateSpecializationDecl(specialization)}";
        }

        public override TypePrinterResult VisitDependentTemplateSpecializationType(
            DependentTemplateSpecializationType template, TypeQualifiers quals)
        {
            if (template.Desugared.Type != null)
                return template.Desugared.Visit(this);
            return string.Empty;
        }

        public override TypePrinterResult VisitTemplateParameterType(TemplateParameterType param, TypeQualifiers quals)
        {
            if (param.Parameter == null || param.Parameter.Name == null)
                return string.Empty;

            return param.Parameter.Name;
        }

        public override TypePrinterResult VisitTemplateParameterSubstitutionType(
            TemplateParameterSubstitutionType param, TypeQualifiers quals)
        {
            return param.Replacement.Type.Visit(this, quals);
        }

        public override TypePrinterResult VisitInjectedClassNameType(InjectedClassNameType injected, TypeQualifiers quals)
        {
            return injected.Class.Visit(this);
        }

        public override TypePrinterResult VisitDependentNameType(DependentNameType dependent, TypeQualifiers quals)
        {
            return dependent.Qualifier.Type != null ? dependent.Qualifier.Visit(this).Type : string.Empty;
        }

        public override TypePrinterResult VisitPackExpansionType(PackExpansionType packExpansionType, TypeQualifiers quals)
        {
            return string.Empty;
        }

        public override TypePrinterResult VisitUnaryTransformType(UnaryTransformType unaryTransformType, TypeQualifiers quals)
        {
            if (unaryTransformType.Desugared.Type != null)
                return unaryTransformType.Desugared.Visit(this);
            return unaryTransformType.BaseType.Visit(this);
        }

        public override TypePrinterResult VisitVectorType(VectorType vectorType, TypeQualifiers quals)
        {
            // an incomplete implementation but we'd hardly need anything better
            return "__attribute__()";
        }

        public override TypePrinterResult VisitCILType(CILType type, TypeQualifiers quals)
        {
            if (type.Type == typeof(string))
                return quals.IsConst ? "const char*" : "char*";

            switch (System.Type.GetTypeCode(type.Type))
            {
                case TypeCode.Boolean:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.Bool), quals);
                case TypeCode.Char:
                case TypeCode.SByte:
                case TypeCode.Byte:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.Char), quals);
                case TypeCode.Int16:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.Short), quals);
                case TypeCode.UInt16:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.UShort), quals);
                case TypeCode.Int32:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.Int), quals);
                case TypeCode.UInt32:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.UInt), quals);
                case TypeCode.Int64:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.Long), quals);
                case TypeCode.UInt64:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.ULong), quals);
                case TypeCode.Single:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.Float), quals);
                case TypeCode.Double:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.Double), quals);
                case TypeCode.String:
                    return quals.IsConst ? "const char*" : "char*";
            }

            return "void*";
        }

        public override TypePrinterResult VisitUnsupportedType(UnsupportedType type, TypeQualifiers quals)
        {
            return string.Empty;
        }

        public override TypePrinterResult VisitDeclaration(Declaration decl, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public override TypePrinterResult VisitFunctionType(FunctionType function, TypeQualifiers quals)
        {
            var arguments = function.Parameters;
            var returnType = function.ReturnType;
            var args = string.Empty;

            if (arguments.Count > 0)
                args = VisitParameters(function.Parameters, hasNames: false);

            var callingConvention = string.Empty;
            if (function.CallingConvention != CallingConvention.Default &&
                function.CallingConvention != CallingConvention.C)
            {
                string conventionString = function.CallingConvention.ToString();
                callingConvention = $"__{conventionString.ToLowerInvariant()} ";
            }
            return $"{returnType.Visit(this)} ({callingConvention}{{0}})({args})";
        }

        public override TypePrinterResult VisitParameters(IEnumerable<Parameter> @params,
            bool hasNames = true)
        {
            var args = new List<string>();

            foreach (var param in @params)
                args.Add(VisitParameter(param, hasNames));

            if (PrintFlavorKind == CppTypePrintFlavorKind.ObjC)
                return string.Join(" ", args);

            return string.Join(", ", args);
        }

        public override TypePrinterResult VisitParameter(Parameter arg, bool hasName = true)
        {
            string type = arg.Type.Visit(this, arg.QualifiedType.Qualifiers);
            string name = arg.Name;
            bool printName = hasName && !string.IsNullOrEmpty(name);

            if (PrintFlavorKind == CppTypePrintFlavorKind.ObjC)
                return printName ? string.Format(":({0}){1}", type, name)
                    : string.Format(":({0})", type);

            CppSharp.AST.Type desugared = arg.Type.Desugar();
            desugared = (desugared.GetFinalPointee() ?? desugared).Desugar();
            return printName ?
                ((!(arg.Type is TypedefType) || ResolveTypedefs) &&
                  desugared is FunctionType ?
                  type.Replace("(*)", $"(*{name})") : $"{type} {name}") : type;
        }

        public override TypePrinterResult VisitDelegate(FunctionType function)
        {
            throw new NotImplementedException();
        }

        public TypePrinterResult GetDeclName(Declaration declaration, TypePrintScopeKind scope)
        {
            switch (scope)
            {
            case TypePrintScopeKind.Local:
                return PrintLogicalNames ? declaration.LogicalOriginalName
                    : declaration.OriginalName;
            case TypePrintScopeKind.Qualified:
                if (declaration.Namespace is Class)
                    return $"{declaration.Namespace.Visit(this)}::{declaration.OriginalName}";
                return PrintLogicalNames ? declaration.QualifiedLogicalOriginalName
                    : declaration.QualifiedOriginalName;
            case TypePrintScopeKind.GlobalQualified:
                if (declaration.Namespace is Class)
                    return $"{declaration.Namespace.Visit(this)}::{declaration.OriginalName}";
                var qualifier = PrintFlavorKind == CppTypePrintFlavorKind.Cpp ? "::" : string.Empty;
                return qualifier + GetDeclName(declaration, TypePrintScopeKind.Qualified);
            }

            throw new NotSupportedException();
        }

        public override TypePrinterResult VisitDeclaration(Declaration decl)
        {
            return GetDeclName(decl, ScopeKind);
        }

        public override TypePrinterResult VisitTranslationUnit(TranslationUnit unit)
        {
            return VisitDeclaration(unit);
        }

        public override TypePrinterResult VisitClassDecl(Class @class)<commit_msg>Minor refactorings in CppTypePrinter.cs.<commit_after>﻿using System;
using System.Collections.Generic;
using System.Linq;
using CppSharp.AST;
using CppSharp.AST.Extensions;

namespace CppSharp.Generators.C
{
    public enum CppTypePrintFlavorKind
    {
        C,
        Cpp,
        ObjC
    }

    public class CppTypePrinter : TypePrinter
    {
        public CppTypePrintFlavorKind PrintFlavorKind { get; set; }
        public bool PrintLogicalNames { get; set; }
        public bool PrintTypeQualifiers { get; set; }
        public bool PrintTypeModifiers { get; set; }
        public bool PrintVariableArrayAsPointers { get; set; }

        public CppTypePrinter() : base(TypePrinterContextKind.Managed)
        {
            PrintFlavorKind = CppTypePrintFlavorKind.Cpp;
            ScopeKind = TypePrintScopeKind.GlobalQualified;
            PrintTypeQualifiers = true;
            PrintTypeModifiers = true;
        }

        public bool ResolveTypedefs { get; set; }

        public override TypePrinterResult VisitTagType(TagType tag, TypeQualifiers quals)
        {
            var qual = GetStringQuals(quals);
            return $"{qual}{tag.Declaration.Visit(this)}";
        }

        public override TypePrinterResult VisitArrayType(ArrayType array, TypeQualifiers quals)
        {
            var typeName = array.Type.Visit(this);

            switch (array.SizeType)
            {
            case ArrayType.ArraySize.Constant:
                return $"{typeName}[{array.Size}]";
            case ArrayType.ArraySize.Variable:
            case ArrayType.ArraySize.Dependent:
            case ArrayType.ArraySize.Incomplete:
                return $"{typeName}{(PrintVariableArrayAsPointers ? "*" : "[]")}";
            }

            throw new NotSupportedException();
        }

        private static string ConvertModifierToString(PointerType.TypeModifier modifier)
        {
            switch (modifier)
            {
                case PointerType.TypeModifier.Value: return "[]";
                case PointerType.TypeModifier.Pointer: return "*";
                case PointerType.TypeModifier.LVReference: return "&";
                case PointerType.TypeModifier.RVReference: return "&&";
            }

            return string.Empty;
        }

        public override TypePrinterResult VisitPointerType(PointerType pointer, TypeQualifiers quals)
        {
            var pointeeType = pointer.Pointee.Visit(this, pointer.QualifiedPointee.Qualifiers);
            var mod = PrintTypeModifiers ? ConvertModifierToString(pointer.Modifier) : string.Empty;
            pointeeType.NameSuffix.Append(mod);

            var qual = GetStringQuals(quals, false);
            if (!string.IsNullOrEmpty(qual))
                pointeeType.NameSuffix.Append(' ').Append(qual);

            return pointeeType;
        }

        public override TypePrinterResult VisitMemberPointerType(MemberPointerType member, TypeQualifiers quals)
        {
            return string.Empty;
        }

        public override TypePrinterResult VisitBuiltinType(BuiltinType builtin, TypeQualifiers quals)
        {
            var qual = GetStringQuals(quals);
            return $"{qual}{VisitPrimitiveType(builtin.Type)}";
        }

        public TypePrinterResult VisitPrimitiveType(PrimitiveType primitive)
        {
            switch (primitive)
            {
                case PrimitiveType.Bool: return "bool";
                case PrimitiveType.Void: return "void";
                case PrimitiveType.Char16: return "char16_t";
                case PrimitiveType.Char32: return "char32_t";
                case PrimitiveType.WideChar: return "wchar_t";
                case PrimitiveType.Char: return "char";
                case PrimitiveType.SChar: return "signed char";
                case PrimitiveType.UChar: return "unsigned char";
                case PrimitiveType.Short: return "short";
                case PrimitiveType.UShort: return "unsigned short";
                case PrimitiveType.Int: return "int";
                case PrimitiveType.UInt: return "unsigned int";
                case PrimitiveType.Long: return "long";
                case PrimitiveType.ULong: return "unsigned long";
                case PrimitiveType.LongLong: return "long long";
                case PrimitiveType.ULongLong: return "unsigned long long";
                case PrimitiveType.Int128: return "__int128_t";
                case PrimitiveType.UInt128: return "__uint128_t";
                case PrimitiveType.Half: return "__fp16";
                case PrimitiveType.Float: return "float";
                case PrimitiveType.Double: return "double";
                case PrimitiveType.LongDouble: return "long double";
                case PrimitiveType.Float128: return "__float128";
                case PrimitiveType.IntPtr: return "void*";
                case PrimitiveType.UIntPtr: return "uintptr_t";
                case PrimitiveType.Null: return PrintFlavorKind == CppTypePrintFlavorKind.Cpp ? "std::nullptr_t" : "NULL";
                case PrimitiveType.String:
                {
                    switch (PrintFlavorKind)
                    {
                    case CppTypePrintFlavorKind.C:
                        return "const char*";
                    case CppTypePrintFlavorKind.Cpp:
                        return "std::string";
                    case CppTypePrintFlavorKind.ObjC:
                        return "NSString";
                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }
                case PrimitiveType.Decimal:
                {
                    switch (PrintFlavorKind)
                    {
                    case CppTypePrintFlavorKind.C:
                    case CppTypePrintFlavorKind.Cpp:
                        return "_Decimal32";
                    case CppTypePrintFlavorKind.ObjC:
                        return "NSDecimalNumber";
                    default:
                        throw new ArgumentOutOfRangeException();
                    }
                }
            }

            throw new NotSupportedException();
        }

        public override TypePrinterResult VisitPrimitiveType(PrimitiveType primitive, TypeQualifiers quals)
        {
            var qual = GetStringQuals(quals);
            return $"{qual}{VisitPrimitiveType(primitive)}";
        }

        public override TypePrinterResult VisitTypedefType(TypedefType typedef, TypeQualifiers quals)
        {
            FunctionType func;
            var qual = GetStringQuals(quals);
            if (ResolveTypedefs && !typedef.Declaration.Type.IsPointerTo(out func))
            {
                TypePrinterResult type = typedef.Declaration.QualifiedType.Visit(this);
                return new TypePrinterResult { Type = $"{qual}{type.Type}", NameSuffix = type.NameSuffix };
            }
            return $"{qual}{typedef.Declaration.Visit(this)}";
        }

        public override TypePrinterResult VisitAttributedType(AttributedType attributed, TypeQualifiers quals)
        {
            return attributed.Modified.Visit(this);
        }

        public override TypePrinterResult VisitDecayedType(DecayedType decayed, TypeQualifiers quals)
        {
            return decayed.Decayed.Visit(this);
        }

        public override TypePrinterResult VisitTemplateSpecializationType(TemplateSpecializationType template, TypeQualifiers quals)
        {
            var specialization = template.GetClassTemplateSpecialization();
            if (specialization == null)
                return string.Empty;

            var qual = GetStringQuals(quals);
            return $"{qual}{VisitClassTemplateSpecializationDecl(specialization)}";
        }

        public override TypePrinterResult VisitDependentTemplateSpecializationType(
            DependentTemplateSpecializationType template, TypeQualifiers quals)
        {
            if (template.Desugared.Type != null)
                return template.Desugared.Visit(this);
            return string.Empty;
        }

        public override TypePrinterResult VisitTemplateParameterType(TemplateParameterType param, TypeQualifiers quals)
        {
            if (param.Parameter == null || param.Parameter.Name == null)
                return string.Empty;

            return param.Parameter.Name;
        }

        public override TypePrinterResult VisitTemplateParameterSubstitutionType(
            TemplateParameterSubstitutionType param, TypeQualifiers quals)
        {
            return param.Replacement.Type.Visit(this, quals);
        }

        public override TypePrinterResult VisitInjectedClassNameType(InjectedClassNameType injected, TypeQualifiers quals)
        {
            return injected.Class.Visit(this);
        }

        public override TypePrinterResult VisitDependentNameType(DependentNameType dependent, TypeQualifiers quals)
        {
            return dependent.Qualifier.Type != null ? dependent.Qualifier.Visit(this).Type : string.Empty;
        }

        public override TypePrinterResult VisitPackExpansionType(PackExpansionType packExpansionType, TypeQualifiers quals)
        {
            return string.Empty;
        }

        public override TypePrinterResult VisitUnaryTransformType(UnaryTransformType unaryTransformType, TypeQualifiers quals)
        {
            if (unaryTransformType.Desugared.Type != null)
                return unaryTransformType.Desugared.Visit(this);
            return unaryTransformType.BaseType.Visit(this);
        }

        public override TypePrinterResult VisitVectorType(VectorType vectorType, TypeQualifiers quals)
        {
            // an incomplete implementation but we'd hardly need anything better
            return "__attribute__()";
        }

        public override TypePrinterResult VisitCILType(CILType type, TypeQualifiers quals)
        {
            if (type.Type == typeof(string))
                return quals.IsConst ? "const char*" : "char*";

            switch (System.Type.GetTypeCode(type.Type))
            {
                case TypeCode.Boolean:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.Bool), quals);
                case TypeCode.Char:
                case TypeCode.SByte:
                case TypeCode.Byte:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.Char), quals);
                case TypeCode.Int16:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.Short), quals);
                case TypeCode.UInt16:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.UShort), quals);
                case TypeCode.Int32:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.Int), quals);
                case TypeCode.UInt32:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.UInt), quals);
                case TypeCode.Int64:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.Long), quals);
                case TypeCode.UInt64:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.ULong), quals);
                case TypeCode.Single:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.Float), quals);
                case TypeCode.Double:
                    return VisitBuiltinType(new BuiltinType(PrimitiveType.Double), quals);
                case TypeCode.String:
                    return quals.IsConst ? "const char*" : "char*";
            }

            return "void*";
        }

        public override TypePrinterResult VisitUnsupportedType(UnsupportedType type, TypeQualifiers quals)
        {
            return string.Empty;
        }

        public override TypePrinterResult VisitDeclaration(Declaration decl, TypeQualifiers quals)
        {
            throw new NotImplementedException();
        }

        public override TypePrinterResult VisitFunctionType(FunctionType function, TypeQualifiers quals)
        {
            var arguments = function.Parameters;
            var returnType = function.ReturnType;
            var args = string.Empty;

            if (arguments.Count > 0)
                args = VisitParameters(function.Parameters, hasNames: false);

            var callingConvention = string.Empty;
            if (function.CallingConvention != CallingConvention.Default &&
                function.CallingConvention != CallingConvention.C)
            {
                string conventionString = function.CallingConvention.ToString();
                callingConvention = $"__{conventionString.ToLowerInvariant()} ";
            }
            return $"{returnType.Visit(this)} ({callingConvention}{{0}})({args})";
        }

        public override TypePrinterResult VisitParameters(IEnumerable<Parameter> @params,
            bool hasNames = true)
        {
            var args = new List<string>();

            foreach (var param in @params)
                args.Add(VisitParameter(param, hasNames));

            if (PrintFlavorKind == CppTypePrintFlavorKind.ObjC)
                return string.Join(" ", args);

            return string.Join(", ", args);
        }

        public override TypePrinterResult VisitParameter(Parameter arg, bool hasName = true)
        {
            string type = arg.Type.Visit(this, arg.QualifiedType.Qualifiers);
            string name = arg.Name;
            bool printName = hasName && !string.IsNullOrEmpty(name);

            if (PrintFlavorKind == CppTypePrintFlavorKind.ObjC)
                return printName ? $":({type}){name}" : $":({type})";

            CppSharp.AST.Type desugared = arg.Type.Desugar();
            desugared = (desugared.GetFinalPointee() ?? desugared).Desugar();
            return printName ?
                ((!(arg.Type is TypedefType) || ResolveTypedefs) &&
                  desugared is FunctionType ?
                  type.Replace("(*)", $"(*{name})") : $"{type} {name}") : type;
        }

        public override TypePrinterResult VisitDelegate(FunctionType function)
        {
            throw new NotImplementedException();
        }

        public TypePrinterResult GetDeclName(Declaration declaration, TypePrintScopeKind scope)
        {
            switch (scope)
            {
            case TypePrintScopeKind.Local:
            {
                return PrintLogicalNames ? declaration.LogicalOriginalName
                    : declaration.OriginalName;
            }
            case TypePrintScopeKind.Qualified:
            {
                if (declaration.Namespace is Class)
                    return $"{declaration.Namespace.Visit(this)}::{declaration.OriginalName}";

                return PrintLogicalNames ? declaration.QualifiedLogicalOriginalName
                    : declaration.QualifiedOriginalName;
            }
            case TypePrintScopeKind.GlobalQualified:
            {
                if (declaration.Namespace is Class)
                    return $"{declaration.Namespace.Visit(this)}::{declaration.OriginalName}";

                var qualifier = PrintFlavorKind == CppTypePrintFlavorKind.Cpp ? "::" : string.Empty;
                return qualifier + GetDeclName(declaration, TypePrintScopeKind.Qualified);
            }
            }

            throw new NotSupportedException();
        }

        public override TypePrinterResult VisitDeclaration(Declaration decl)
        {
            return GetDeclName(decl, ScopeKind);
        }

        public override TypePrinterResult VisitTranslationUnit(TranslationUnit unit)
        {
            return VisitDeclaration(unit);
        }

        public override TypePrinterResult VisitClassDecl(Class @class)
{'fraction_non_alphanumeric': 0.07769797889919919, 'fraction_numerical': 0.002891826617516207, 'mean_word_length': 1.970174610665408, 'pattern_counts': {'":': 6, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4730125', 'n_tokens_mistral': 7950, 'n_tokens_neox': 7563, 'n_words': 1631}