using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using Mono.Cecil; using Mono.Collections.Generic; using Mono.Documentation.Util; namespace Mono.Documentation.Updater { public class FSharpFormatter : MemberFormatter { private static readonly Dictionary operators = new Dictionary() { { "op_Nil" ,"[]" }, { "op_Cons" ,"::" }, { "op_Addition" ,"+" }, { "op_Subtraction" ,"-" }, { "op_Multiply" ,"*" }, { "op_Division" ,"/" }, { "op_Append" ,"@" }, { "op_Concatenate" ,"^" }, { "op_Modulus" ,"%" }, { "op_BitwiseAnd" ,"&&&" }, { "op_BitwiseOr" ,"|||" }, { "op_ExclusiveOr" ,"^^^" }, { "op_LeftShift" ,"<<<" }, { "op_LogicalNot" ,"~~~" }, { "op_RightShift" ,">>>" }, { "op_UnaryPlus" ,"~+" }, { "op_UnaryNegation" ,"~-" }, { "op_Equality" ,"=" }, { "op_LessThanOrEqual" ,"<=" }, { "op_GreaterThanOrEqual" ,">=" }, { "op_LessThan" ,"<" }, { "op_GreaterThan" ,">" }, { "op_Dynamic" ,"?" }, { "op_DynamicAssignment" ,"?<-" }, { "op_PipeRight" ,"|>" }, { "op_PipeRight2" ,"||>" }, { "op_PipeRight3" ,"|||>" }, { "op_PipeLeft" ,"<|" }, { "op_PipeLeft2" ,"<||" }, { "op_PipeLeft3" ,"<|||" }, { "op_Dereference" ,"!" }, { "op_ComposeRight" ,">>" }, { "op_ComposeLeft" ,"<<" }, { "op_Quotation" ,"<@ @>" }, { "op_QuotationUntyped" ,"<@@ @@>" }, { "op_AdditionAssignment" ,"+=" }, { "op_SubtractionAssignment" ,"-=" }, { "op_MultiplyAssignment" ,"*=" }, { "op_DivisionAssignment" ,"/=" }, { "op_Range" ,".." }, { "op_RangeStep" ,".. .." }, }; // Other combinations of operator characters that are not listed here can be used as operators and have names // that are made up by concatenating names for the individual characters from the following table. // For example, +! becomes op_PlusBang private static readonly Dictionary combinatedOperators = new Dictionary() { {"Greater" , ">"}, {"Less" , "<"}, {"Plus" , "+"}, {"Minus" , "-"}, {"Multiply", "*"}, {"Equals" , "="}, {"Twiddle" , "~"}, {"Percent" , "%"}, {"Dot" , "."}, {"Amp" , "&"}, {"Bar" , "|"}, {"At" , "@"}, {"Hash" , "#"}, {"Hat" , "^"}, {"Bang" , "!"}, {"Qmark" , "?"}, {"Divide" , "/"}, {"Colon" , ":"}, {"LParen" , "("}, {"Comma" , ","}, {"RParen" , ")"}, {"LBrack" , "["}, {"RBrack" , "]"}, }; private static readonly Dictionary typeAbbreviations = new Dictionary() { {"System.Boolean", "bool"}, {"System.Byte", "byte"}, {"System.SByte", "sbyte"}, {"System.Int16", "int16"}, {"System.UInt16", "uint16"}, {"System.Int32", "int"}, {"System.UInt32", "uint32"}, {"System.Int64", "int64"}, {"System.UInt64", "uint64"}, {"System.IntPtr", "nativeint"}, {"System.UIntPtr", "unativeint"}, {"System.Char", "char"}, {"System.String", "string"}, {"System.Decimal", "decimal"}, {"System.Void", "unit"}, {"System.Single", "single"},// Can be float32 {"System.Double", "double"},// Can be float {"System.Object", "obj"}, {"Microsoft.FSharp.Core.Unit", "unit"}, {"Microsoft.FSharp.Core.FSharpOption`1", "option"}, {"System.Collections.Generic.IEnumerable`1", "seq"}, {"Microsoft.FSharp.Core.FSharpRef`1", "ref"}, }; private static readonly HashSet fSharpPrefixes = new HashSet() { "Microsoft.FSharp.Collections.FSharp", "Microsoft.FSharp.Core.FSharp", "Microsoft.FSharp.Control.FSharp", "Microsoft.FSharp.Data.FSharp", "Microsoft.FSharp.Linq.FSharp", "Microsoft.FSharp.NativeInterop.FSharp", "Microsoft.FSharp.Quotations.FSharp", "Microsoft.FSharp.Reflection.FSharp", }; private static readonly HashSet ignoredValueTypeInterfaces = new HashSet() { "System.IEquatable`1", "System.Collections.IStructuralEquatable", "System.IComparable`1", "System.IComparable", "System.Collections.IStructuralComparable", }; private GenericParameterState genericParameterState = GenericParameterState.None; protected string GetFSharpType(TypeReference type) { string typeToCompare = type.FullName; var fullName = type.IsGenericInstance ? type.GetElementType().FullName : type.FullName; if (typeAbbreviations.ContainsKey(fullName)) { typeToCompare = typeAbbreviations[fullName]; } else { var prefixToRemove = fSharpPrefixes.FirstOrDefault(i => fullName.StartsWith(i)); if (prefixToRemove != null) { typeToCompare = base.AppendTypeName(new StringBuilder(), type.FullName.Replace(prefixToRemove, "")).ToString(); } if (type.IsGenericParameter) { typeToCompare = GetGenericName(type.Name); } } return typeToCompare == type.FullName ? null : typeToCompare; } protected override StringBuilder AppendTypeName(StringBuilder buf, TypeReference type, DynamicParserContext context) { string fSharpType = GetFSharpType(type); if (fSharpType != null) { return buf.Append(fSharpType); } if (type.IsGenericParameter) { return buf.Append(GetGenericName(type.Name)); } return base.AppendTypeName(buf, type, context); } protected override string GetTypeDeclaration(TypeDefinition type) { string visibility = GetTypeVisibility(type.Attributes); if (visibility == null) return null; StringBuilder buf = new StringBuilder(); if (IsModule(type)) { AppendModuleDeclaraion(buf, type); return buf.ToString(); } if (IsDiscriminatedUnionCase(type)) { AppendDiscriminatedUnionCase(buf, type); return buf.ToString(); } buf.Append("type "); buf.Append(visibility); buf.Append(GetTypeName(type)); buf.Append(" = "); if (IsRecord(type)) { buf.Append("{}"); return buf.ToString(); } if (IsDiscriminatedUnion(type)) { return buf.ToString(); } if (type.IsEnum) { return buf.ToString(); } buf.Append($"{GetTypeKind(type)}"); if (DocUtils.IsDelegate(type)) { buf.Append(" "); MethodDefinition invoke = type.GetMethod("Invoke"); AppendFunctionSignature(buf, invoke); return buf.ToString(); } AppendBaseType(buf, type); foreach (var interfaceImplementation in type.Interfaces) { var resolvedInterface = interfaceImplementation.InterfaceType.Resolve (); if (type.IsValueType && ignoredValueTypeInterfaces.Any(i => interfaceImplementation.InterfaceType.FullName.StartsWith(i)) || (resolvedInterface != null && resolvedInterface.IsNotPublic)) continue; buf.Append($"{GetLineEnding()}{Consts.Tab}interface "); AppendTypeName(buf, GetTypeName(interfaceImplementation.InterfaceType)); } return buf.ToString(); } private void AppendDiscriminatedUnionCase(StringBuilder buf, TypeDefinition type) { buf.Append(GetName(type)); buf.Append(" : "); var constructor = type.Methods.First(i => i.Name == ".ctor"); AppendParameters(buf, constructor, constructor.Parameters); buf.Append(" -> "); buf.Append(GetName(type.DeclaringType)); } private void AppendBaseType(StringBuilder buf, TypeDefinition type) { TypeReference basetype = type.BaseType; if (basetype != null && basetype.FullName == "System.Object" || type.IsValueType) basetype = null; if (basetype != null) { buf.Append($"{GetLineEnding()}{Consts.Tab}inherit "); buf.Append(GetName(basetype)); } } private void AppendFunctionSignature(StringBuilder buf, MethodDefinition method) { bool isTuple = method.Parameters.Count == 1 && IsTuple(method.Parameters[0].ParameterType); if (isTuple) buf.Append("("); AppendParameters(buf, method, method.Parameters); if (isTuple) buf.Append(")"); buf.Append(" -> "); if (method.IsConstructor) buf.Append(GetTypeName(method.DeclaringType)); else { if (IsFSharpFunction(method.ReturnType)) buf.Append("("); buf.Append(GetTypeName(method.ReturnType)); if (IsFSharpFunction(method.ReturnType)) buf.Append(")"); } } private string GetTypeKind(TypeDefinition type) { if (type.IsInterface) { return "interface"; } if (type.IsValueType) { return "struct"; } if (DocUtils.IsDelegate(type)) { return "delegate of"; } return "class"; } protected override StringBuilder AppendGenericType(StringBuilder buf, TypeReference type, DynamicParserContext context, bool appendGeneric = true) { List decls = DocUtils.GetDeclaringTypes( type is GenericInstanceType ? type.GetElementType() : type); List genArgs = GetGenericArguments(type); int argIdx = 0; int displayedInParentArguments = 0; bool insertNested = false; foreach (var decl in decls) { TypeReference declDef = decl.Resolve() ?? decl; if (insertNested) { buf.Append(NestedTypeSeparator); } insertNested = true; bool isTuple = IsTuple(type) // If this is a declaration of `Tuple` type // treat it as an ordinary type && !(type is TypeDefinition); bool isFSharpFunction = IsFSharpFunction(type); if (!isTuple && !isFSharpFunction) AppendTypeName(buf, declDef, context); int argumentCount = DocUtils.GetGenericArgumentCount(declDef); int notYetDisplayedArguments = argumentCount - displayedInParentArguments; displayedInParentArguments = argumentCount;// nested TypeReferences have parents' generic arguments, but we shouldn't display them if (notYetDisplayedArguments > 0) { if (!isTuple && !isFSharpFunction) buf.Append(GenericTypeContainer[0]); if (isFSharpFunction && genericParameterState == GenericParameterState.WithinTuple) buf.Append("("); var origState = MemberFormatterState; var genericParameterStateOrigState = genericParameterState; MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters; genericParameterState = isTuple ? GenericParameterState.WithinTuple : GenericParameterState.None; for (int i = 0; i < notYetDisplayedArguments; ++i) { if (i > 0) buf.Append(isTuple ? " * " : isFSharpFunction ? " -> " : ", "); var genArg = genArgs[argIdx++]; var genericParameter = genArg as GenericParameter; if (genericParameter != null && IsFlexibleType(genericParameter)) { buf.Append("#");// replace genericParameter which is a flexible type with its constraint type #if NEW_CECIL _AppendTypeName(buf, genericParameter.Constraints[0].ConstraintType, context); #else _AppendTypeName(buf, genericParameter.Constraints[0], context); #endif } else { _AppendTypeName(buf, genArg, context); } } MemberFormatterState = origState; genericParameterState = genericParameterStateOrigState; if (MemberFormatterState == MemberFormatterState.None) { AppendConstraints(buf, genArgs.GetRange(0, notYetDisplayedArguments) .SafeCast() .ToList()); } if (!isTuple && !isFSharpFunction) buf.Append(GenericTypeContainer[1]); if (isFSharpFunction && genericParameterState == GenericParameterState.WithinTuple) buf.Append(")"); } } return buf; } private void AppendModuleDeclaraion(StringBuilder buf, TypeDefinition type) { buf.Append("module "); buf.Append(GetModuleName(type)); } private string GetModuleName(TypeDefinition type) { var moduleName = GetTypeName(type); const string moduleKeyWord = "Module"; if (moduleName.EndsWith(moduleKeyWord)) { moduleName = moduleName.Substring(0, moduleName.Length - moduleKeyWord.Length); } return moduleName; } protected override StringBuilder AppendGenericTypeConstraints(StringBuilder buf, TypeReference type) { return buf; } private void AppendConstraints(StringBuilder buf, IList genArgs) { var origMemberFormatterState = MemberFormatterState; MemberFormatterState = MemberFormatterState.WithinGenericTypeParameters; List constraintStrings = new List(); foreach (GenericParameter genArg in genArgs.Where(i => !IsFlexibleType(i))) { GenericParameterAttributes attrs = genArg.Attributes; #if NEW_CECIL Mono.Collections.Generic.Collection constraints = genArg.Constraints; #else IList constraints = genArg.Constraints; #endif if (attrs == GenericParameterAttributes.NonVariant && constraints.Count == 0) continue; bool isref = (attrs & GenericParameterAttributes.ReferenceTypeConstraint) != 0; bool isvt = (attrs & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0; bool isnew = (attrs & GenericParameterAttributes.DefaultConstructorConstraint) != 0; if (!isref && !isvt && !isnew && constraints.Count == 0) continue; var genericName = GetGenericName(genArg.Name); if (isref) { // May be "not struct". We don't know it here constraintStrings.Add($"{genericName} : null"); } else if (isvt) { constraintStrings.Add($"{genericName} : struct"); } if (constraints.Count > 0 && !isvt) { foreach (var typeReference in constraints) { #if NEW_CECIL constraintStrings.Add($"{genericName} :> {GetTypeName(typeReference.ConstraintType)}"); #else constraintStrings.Add($"{genericName} :> {GetTypeName(typeReference)}"); #endif } } if (isnew && !isvt) { constraintStrings.Add($"{genericName} : (new : unit -> {GetTypeName(genArg)})"); } } if (constraintStrings.Count > 0) { buf.Append($" (requires {string.Join(" and ", constraintStrings)})"); } MemberFormatterState = origMemberFormatterState; } protected override string GetConstructorDeclaration(MethodDefinition constructor) { StringBuilder buf = new StringBuilder(); if (constructor.Parameters.Count == 0) return null; if (AppendVisibility(buf, constructor) == null) return null; buf.Append("new "); buf.Append(GetTypeName(constructor.DeclaringType)); buf.Append(" : "); AppendFunctionSignature(buf, constructor); return buf.ToString(); } protected override string GetMethodDeclaration(MethodDefinition method) { var visibilityBuf = new StringBuilder(); if (AppendVisibility(visibilityBuf, method) == null) return null; string visibility = visibilityBuf.ToString(); StringBuilder buf = new StringBuilder(); var kind = GetMethodKind(method); switch (kind) { case FSharpMethodKind.InModule: AppendModuleMethod(buf, method); break; case FSharpMethodKind.Static: AppendStaticMethod(buf, method, visibility); break; case FSharpMethodKind.Override: AppendOverrideMethod(buf, method, visibility); break; case FSharpMethodKind.Abstract: AppendAbstractMethod(buf, method, visibility); break; case FSharpMethodKind.Virtual: AppendVirtualMethod(buf, method, visibility); break; case FSharpMethodKind.Common: AppendCommonMethod(buf, method, visibility); break; default: throw new ArgumentOutOfRangeException(); } return buf.ToString(); } private FSharpMethodKind GetMethodKind(MethodDefinition method) { if (IsModule(method.DeclaringType)) return FSharpMethodKind.InModule; if (method.IsStatic) return FSharpMethodKind.Static; if (IsOverride(method)) return FSharpMethodKind.Override; if (method.IsAbstract) return FSharpMethodKind.Abstract; if (method.IsVirtual) return FSharpMethodKind.Virtual; return FSharpMethodKind.Common; } private void AppendModuleMethod(StringBuilder buf, MethodDefinition method) { if (!IsOperator(method)) { buf.Append($"{GetModuleName(method.DeclaringType)}."); } AppendMethodDeclarationEnding(buf, method); } private void AppendVirtualMethod(StringBuilder buf, MethodDefinition method, string visibility) { AppendAbstractMethod(buf, method, visibility); buf.Append(GetLineEnding()); AppendOverrideMethod(buf, method, visibility); } private void AppendStaticMethod(StringBuilder buf, MethodDefinition method, string visibility) { buf.Append("static member "); buf.Append(visibility); AppendMethodDeclarationEnding(buf, method); } private void AppendAbstractMethod(StringBuilder buf, MethodDefinition method, string visibility) { buf.Append("abstract member "); buf.Append(visibility); AppendMethodDeclarationEnding(buf, method); } private void AppendOverrideMethod(StringBuilder buf, MethodDefinition method, string visibility) { buf.Append("override "); buf.Append(visibility); buf.Append("this."); AppendMethodDeclarationEnding(buf, method); } private void AppendCommonMethod(StringBuilder buf, MethodDefinition method, string visibility) { buf.Append("member "); buf.Append(visibility); buf.Append("this."); AppendMethodDeclarationEnding(buf, method); } private void AppendMethodDeclarationEnding(StringBuilder buf, MethodDefinition method) { AppendMethodName(buf, method); buf.Append(" : "); AppendFunctionSignature(buf, method); AppendConstraints(buf, method.GenericParameters); } protected override StringBuilder AppendMethodName(StringBuilder buf, MethodDefinition method) { if (IsOperator(method)) { // this is an operator if (TryAppendOperatorName(buf, method)) return buf; return base.AppendMethodName(buf, method); } var compilationSourceNameAttribute = method.CustomAttributes.FirstOrDefault (i => i.AttributeType.FullName == "Microsoft.FSharp.Core.CompilationSourceNameAttribute"); if (compilationSourceNameAttribute != null) return buf.Append(compilationSourceNameAttribute.ConstructorArguments.First().Value); return buf.Append(method.Name); } protected override StringBuilder AppendGenericMethodConstraints(StringBuilder buf, MethodDefinition method) { return buf; } protected override StringBuilder AppendRefTypeName(StringBuilder buf, TypeReference type, DynamicParserContext context) { return buf; } protected override StringBuilder AppendModifiers(StringBuilder buf, MethodDefinition method) { return buf; } private bool IsOverride(MethodDefinition method) { return IsOverride(method.DeclaringType, method); } private bool IsOverride(TypeDefinition type, MethodDefinition method) { if (type == null || type.BaseType == null) return false; var baseType = type.BaseType.Resolve(); if (baseType != null && baseType.Methods.Any(i => i.Name == method.Name)) return true; return IsOverride(type.BaseType.Resolve(), method); } protected override StringBuilder AppendGenericMethod(StringBuilder buf, MethodDefinition method) { return buf; } protected void AppendRecordParameter(StringBuilder buf, PropertyDefinition property) { bool hasParameterName = !string.IsNullOrEmpty(property.Name); if (property.SetMethod != null && property.SetMethod.IsPublic) buf.Append("mutable "); if (hasParameterName) { buf.Append(property.Name.TrimEnd('@')); } if (property.PropertyType.FullName != "System.Object") { var typeName = GetTypeName(property.PropertyType, new DynamicParserContext(property)); if (hasParameterName) buf.Append(" : "); buf.Append(typeName); } } protected override StringBuilder AppendParameters(StringBuilder buf, MethodDefinition method, IList parameters) { var curryBorders = GetCurryBorders(method); if (parameters.Count > 0) { bool isExtensionMethod = IsExtensionMethod(method); if (!isExtensionMethod) {// If it's an extension method, first parameter is ignored AppendParameter(buf, parameters[0]); } for (int i = 1; i < parameters.Count; ++i) { if (!(isExtensionMethod && i == 1)) { if (curryBorders.Contains(i)) buf.Append(" -> "); else buf.Append(" * "); } AppendParameter(buf, parameters[i]); } } else { buf.Append("unit"); } return buf; } private void AppendParameter(StringBuilder buf, ParameterDefinition parameter) { bool isFSharpFunction = IsFSharpFunction(parameter.ParameterType); if (isFSharpFunction) buf.Append("("); var typeName = GetTypeName(parameter.ParameterType, new DynamicParserContext(parameter)); buf.Append(typeName); if (isFSharpFunction) buf.Append(")"); } protected override string GetPropertyDeclaration(PropertyDefinition property) { string getVisible = null; if (DocUtils.IsAvailablePropertyMethod(property.GetMethod)) getVisible = AppendVisibility(new StringBuilder(), property.GetMethod)?.ToString(); string setVisible = null; if (DocUtils.IsAvailablePropertyMethod(property.SetMethod)) setVisible = AppendVisibility(new StringBuilder(), property.SetMethod)?.ToString(); if (setVisible == null && getVisible == null) return null; bool isField = GetFSharpFlags(property.CustomAttributes).Any(i => i == SourceConstructFlags.Field); StringBuilder buf = new StringBuilder(); if (IsRecord(property.DeclaringType)) { AppendRecordParameter(buf, property); return buf.ToString(); } if (IsModule(property.DeclaringType)) { buf.Append($"{GetName(property.DeclaringType)}."); } else { if (isField) buf.Append("val "); else buf.Append("member this."); } buf.Append(DocUtils.GetPropertyName(property, NestedTypeSeparator)); if (property.Parameters.Count != 0) { buf.Append("("); AppendParameters(buf, property.GetMethod ?? property.SetMethod, property.Parameters); buf.Append(")"); } buf.Append(" : "); buf.Append(GetTypeName(property.PropertyType)); if (getVisible != null && setVisible != null) { buf.Append(" with get, set"); } return buf.ToString(); } protected override string GetFieldDeclaration(FieldDefinition field) { TypeDefinition declType = (TypeDefinition)field.DeclaringType; if (declType.IsEnum && field.Name == "value__") return null; // This member of enums aren't documented. var visibility = GetFieldVisibility(field); if (visibility == null) return null; var buf = new StringBuilder(); if (declType.IsEnum) { buf.Append(field.Name); if (field.IsLiteral) { buf.Append($" = {field.Constant}"); } return buf.ToString(); } if (field.IsStatic && !field.IsLiteral) buf.Append(" static"); buf.Append("val mutable"); if (!string.IsNullOrEmpty(visibility)) buf.Append(" "); buf.Append(visibility); buf.Append(" "); buf.Append(field.Name); buf.Append(" : "); buf.Append(GetTypeName(field.FieldType, new DynamicParserContext(field))); return buf.ToString(); } protected override string GetEventDeclaration(EventDefinition e) { StringBuilder buf = new StringBuilder(); StringBuilder visibilityBuf = new StringBuilder(); if (AppendVisibility(visibilityBuf, e.AddMethod) == null) { return null; } buf.Append("member this."); if (visibilityBuf.Length > 0) buf.Append(visibilityBuf).Append(' '); buf.Append(e.Name).Append(" : "); buf.Append(GetTypeName(e.EventType, new DynamicParserContext(e.AddMethod.Parameters[0]))).Append(' '); return buf.ToString(); } private static IEnumerable GetFSharpFlags(Collection customAttributes) { foreach (var attribute in customAttributes) { if (attribute.AttributeType.Name.Contains("CompilationMapping")) { var sourceConstructFlags = attribute.ConstructorArguments.Where( i => i.Type.FullName == "Microsoft.FSharp.Core.SourceConstructFlags"); foreach (var customAttributeArgument in sourceConstructFlags) { var constructFlags = (SourceConstructFlags)customAttributeArgument.Value; yield return constructFlags; } } } } private static string GetGenericName(string name) { var trimmedName = name.TrimStart('T'); if (trimmedName.Length == 0 || !Regex.IsMatch(trimmedName, @"^[a-zA-Z]+$")) trimmedName = name; return $"\'{trimmedName}"; } /// /// Get sequencing of curried arguments /// /// Positions between arguments which are curried protected HashSet GetCurryBorders(MethodDefinition method) { var compilationArgumentCounts = GetCustomAttribute(method.CustomAttributes, "CompilationArgumentCountsAttribute"); int[] curryCounts = { }; if (compilationArgumentCounts != null) { var customAttributeArguments = (CustomAttributeArgument[])compilationArgumentCounts.ConstructorArguments[0].Value; curryCounts = customAttributeArguments.Select(i => (int)i.Value).ToArray(); } var curryBorders = new HashSet(); int sum = 0; foreach (var curryCount in curryCounts) { sum += curryCount; curryBorders.Add(sum); } return curryBorders; } private CustomAttribute GetCustomAttribute(Collection customAttributes, string name) { return customAttributes.SingleOrDefault(i => i.AttributeType.Name == name); } protected bool TryAppendOperatorName(StringBuilder buf, MethodDefinition method) { if (!IsOperator(method)) return false; if (operators.ContainsKey(method.Name)) { buf.Append($"( {operators[method.Name]} )"); return true; } if (TryAppendCombinatedOperandName(buf, method)) return true; return false; } private static bool TryAppendCombinatedOperandName(StringBuilder buf, MethodDefinition method) { var oldName = method.Name.Remove(0, 3); var newName = new StringBuilder(); bool found; do { found = false; foreach (var op in combinatedOperators) { if (oldName.StartsWith(op.Key)) { oldName = oldName.Remove(0, op.Key.Length); newName.Append(op.Value); found = true; } } } while (found); if (oldName.Length == 0) { buf.Append($"( {newName} )"); return true; } return false; } protected override StringBuilder AppendPointerTypeName(StringBuilder buf, TypeReference type, DynamicParserContext context) { TypeSpecification spec = type as TypeSpecification; buf.Append("nativeptr<"); _AppendTypeName(buf, spec != null ? spec.ElementType : type.GetElementType(), context); buf.Append(">"); return buf; } #region "Is" methods private static bool IsOperator(MethodDefinition method) { return method.Name.StartsWith("op_", StringComparison.Ordinal); } private static bool IsFSharpFunction(TypeReference type) { return type.FullName.StartsWith("Microsoft.FSharp.Core.FSharpFunc`"); } private static bool IsTuple(TypeReference type) { return type.FullName.StartsWith("System.Tuple"); } private bool IsFlexibleType(GenericParameter genericParameter) { #if NEW_CECIL return genericParameter.Constraints.Count == 1 && GetFSharpType(genericParameter.Constraints[0].ConstraintType.GetElementType()) != null; #else return genericParameter.Constraints.Count == 1 && GetFSharpType(genericParameter.Constraints[0].GetElementType()) != null; #endif } private static bool IsModule(TypeDefinition type) { var fSharpFlags = GetFSharpFlags(type.CustomAttributes); return fSharpFlags.Any(i => i == SourceConstructFlags.Module); } private static bool IsDiscriminatedUnion(TypeDefinition type) { var fSharpFlags = GetFSharpFlags(type.CustomAttributes); return fSharpFlags.Any(i => i == SourceConstructFlags.SumType); } private static bool IsDiscriminatedUnionCase(TypeDefinition type) { return type.DeclaringType != null && IsDiscriminatedUnion(type.DeclaringType); } private static bool IsRecord(TypeDefinition type) { var fSharpFlags = GetFSharpFlags(type.CustomAttributes); return fSharpFlags.Any(i => i == SourceConstructFlags.RecordType); } protected static bool IsExtensionMethod(MethodDefinition method) { var firstParameter = method.Parameters.FirstOrDefault(); return firstParameter != null && firstParameter.Name == "this"; } #endregion #region Visibility protected override StringBuilder AppendVisibility(StringBuilder buf, MethodDefinition method) { if (method.IsPublic || method.IsFamily || method.IsFamilyOrAssembly) return buf.Append(""); return null; } private static string GetTypeVisibility(TypeAttributes ta) { switch (ta & TypeAttributes.VisibilityMask) { case TypeAttributes.Public: case TypeAttributes.NestedPublic: return ""; } return null; } private static string GetFieldVisibility(FieldDefinition field) { if (field.IsPublic || field.IsFamily || field.IsFamilyOrAssembly) { return ""; } return null; } #endregion #region Supported public override bool IsSupported(TypeReference tref) { if (tref.DeclaringType != null && IsDiscriminatedUnion(tref.DeclaringType.Resolve()) && tref.Name == "Tags") { return false; } return true; } public override bool IsSupported(MemberReference mref) { if (mref.DeclaringType != null && IsDiscriminatedUnion(mref.DeclaringType.Resolve())) { var property = mref as PropertyDefinition; if (property?.GetMethod != null) { var fSharpFlags = GetFSharpFlags(property.GetMethod.CustomAttributes); if (fSharpFlags.Any(i => i == SourceConstructFlags.UnionCase))// For descriminated unions show only properties with UnionCase attribute return true; } return false; } switch (mref) { case MethodDefinition method: return !(method.HasCustomAttributes && method.CustomAttributes.Any( ca => ca.GetDeclaringType() == "System.Diagnostics.Contracts.ContractInvariantMethodAttribute" || ca.GetDeclaringType() == Consts.CompilerGeneratedAttribute)) && AppendVisibility(new StringBuilder(), method) != null; } return true; } #endregion #region Private types // Copied from F# Core private enum SourceConstructFlags { /// Indicates that the compiled entity has no relationship to an element in F# source code. None, /// Indicates that the compiled entity is part of the representation of an F# union type declaration. SumType, /// Indicates that the compiled entity is part of the representation of an F# record type declaration. RecordType, /// Indicates that the compiled entity is part of the representation of an F# class or other object type declaration. ObjectType, /// Indicates that the compiled entity is part of the representation of an F# record or union case field declaration. Field, /// Indicates that the compiled entity is part of the representation of an F# exception declaration. Exception, /// Indicates that the compiled entity is part of the representation of an F# closure. Closure, /// Indicates that the compiled entity is part of the representation of an F# module declaration. Module, /// Indicates that the compiled entity is part of the representation of an F# union case declaration. UnionCase, /// Indicates that the compiled entity is part of the representation of an F# value declaration. Value, /// The mask of values related to the kind of the compiled entity. KindMask = 31, /// Indicates that the compiled entity had private or internal representation in F# source code. NonPublicRepresentation } private enum GenericParameterState { None, WithinTuple } private enum FSharpMethodKind { InModule, Static, Override, Abstract, Virtual, Common, } #endregion } }