private void GenerateProxyMethodModifiers(CodeBuilder code)
        {
            // modifiers
            code.AddHeader("\t");
            if (_methodInfo.CodeGenProps.AccessLevel == AccessLevel.CopyExisting)
            {
                code.AddHeader(_methodInfo.MethodSyntax.Modifiers.ToFullString().Trim());
            }
            else
            {
                code.AddHeader(_methodInfo.CodeGenProps.GetAccessLevelModifierString() + " ");
                foreach (var modifier in _methodInfo.MethodSyntax.Modifiers)
                {
                    switch (modifier.Kind())
                    {
                    case SyntaxKind.PublicKeyword:
                    case SyntaxKind.PrivateKeyword:
                    case SyntaxKind.ProtectedKeyword:
                    case SyntaxKind.InternalKeyword:
                        // do not copy access modifiers
                        break;

                    default:
                        code.AddHeader(modifier + " ");
                        break;
                    }
                }
            }
        }
        /// <summary>
        /// Create cil source-code representation of a <see cref="EnumDefinition"/>.
        /// </summary>
        /// <exception cref="Exceptions.InvalidAssemblyNameException">
        /// Thrown when a invalid assembly name is given.
        /// </exception>
        /// <exception cref="Exceptions.InvalidNamespaceException">
        /// Thrown when a invalid namespace identifier is given.
        /// </exception>
        /// <exception cref="Exceptions.OutOfBoundsValueException">
        /// Thrown when enum value does not fit in given storage-type.
        /// </exception>
        /// <param name="enumDefinition">Enum to generate cil source-code for</param>
        /// <param name="assemblyName">Name of the assembly to generate</param>
        /// <param name="namespace">Optional namespace to add the enum to</param>
        /// <param name="headerMode">Mode to use when adding a header</param>
        /// <param name="indentMode">Mode to use for indenting</param>
        /// <param name="spaceIndentSize">When indenting with spaces this controls how many</param>
        /// <param name="newlineMode">Mode to use for ending lines</param>
        /// <param name="storageType">Underlying enum storage-type to use</param>
        /// <param name="curlyBracketMode">Mode to use when writing curly brackets</param>
        /// <returns>String containing the genenerated cil sourcecode</returns>
        public static string ExportCil(
            this EnumDefinition enumDefinition,
            string assemblyName,
            string @namespace                   = null,
            HeaderMode headerMode               = HeaderMode.Default,
            CodeBuilder.IndentMode indentMode   = CodeBuilder.IndentMode.Spaces,
            int spaceIndentSize                 = 4,
            CodeBuilder.NewlineMode newlineMode = CodeBuilder.NewlineMode.Unix,
            StorageType storageType             = StorageType.Implicit,
            CurlyBracketMode curlyBracketMode   = CurlyBracketMode.NewLine)
        {
            if (enumDefinition == null)
            {
                throw new ArgumentNullException(nameof(enumDefinition));
            }

            if (string.IsNullOrEmpty(assemblyName) || !IdentifierValidator.Validate(assemblyName))
            {
                throw new InvalidAssemblyNameException(assemblyName);
            }

            if (!string.IsNullOrEmpty(@namespace) && !IdentifierValidator.ValidateNamespace(@namespace))
            {
                throw new InvalidNamespaceException(@namespace);
            }

            foreach (var oobEntry in enumDefinition.Entries.Where(e => !storageType.Validate(e.Value)))
            {
                throw new OutOfBoundsValueException(storageType, oobEntry.Value);
            }

            var builder = new CodeBuilder(indentMode, spaceIndentSize, newlineMode);

            if (headerMode != HeaderMode.None)
            {
                builder.AddHeader();
                builder.WriteEndLine();
            }

            // Add reference to mscorlib.
            builder.WriteLine(".assembly extern mscorlib { }");
            builder.WriteEndLine();

            // Add assembly info.
            builder.Write($".assembly {assemblyName}");
            StartScope(builder, curlyBracketMode);
            builder.WriteLine(".ver 1:0:0:0");
            EndScope(builder);
            builder.WriteEndLine();

            // Add module info.
            builder.WriteLine($".module {assemblyName}.dll");
            builder.WriteEndLine();

            // Add enum class.
            builder.AddEnum(enumDefinition, storageType, curlyBracketMode, @namespace);

            return(builder.Build());
        }
        /// <summary>
        /// Create csharp source-code representation of a <see cref="EnumDefinition"/>.
        /// </summary>
        /// <exception cref="Exceptions.InvalidNamespaceException">
        /// Thrown when a invalid namespace identifier is given.
        /// </exception>
        /// <exception cref="Exceptions.OutOfBoundsValueException">
        /// Thrown when enum value does not fit in given storage-type.
        /// </exception>
        /// <param name="enumDefinition">Enum to generate csharp source-code for</param>
        /// <param name="namespace">Optional namespace to add the enum to</param>
        /// <param name="headerMode">Mode to use when adding a header</param>
        /// <param name="indentMode">Mode to use for indenting</param>
        /// <param name="spaceIndentSize">When indenting with spaces this controls how many</param>
        /// <param name="newlineMode">Mode to use for ending lines</param>
        /// <param name="storageType">Underlying enum storage-type to use</param>
        /// <param name="curlyBracketMode">Mode to use when writing curly brackets</param>
        /// <returns>String containing the genenerated csharp sourcecode</returns>
        public static string ExportCSharp(
            this EnumDefinition enumDefinition,
            string @namespace                   = null,
            HeaderMode headerMode               = HeaderMode.Default,
            CodeBuilder.IndentMode indentMode   = CodeBuilder.IndentMode.Spaces,
            int spaceIndentSize                 = 4,
            CodeBuilder.NewlineMode newlineMode = CodeBuilder.NewlineMode.Unix,
            StorageType storageType             = StorageType.Implicit,
            CurlyBracketMode curlyBracketMode   = CurlyBracketMode.NewLine)
        {
            if (enumDefinition == null)
            {
                throw new ArgumentNullException(nameof(enumDefinition));
            }

            if (!string.IsNullOrEmpty(@namespace) && !IdentifierValidator.ValidateNamespace(@namespace))
            {
                throw new InvalidNamespaceException(@namespace);
            }

            foreach (var oobEntry in enumDefinition.Entries.Where(e => !storageType.Validate(e.Value)))
            {
                throw new OutOfBoundsValueException(storageType, oobEntry.Value);
            }

            var builder = new CodeBuilder(indentMode, spaceIndentSize, newlineMode);

            if (headerMode != HeaderMode.None)
            {
                builder.AddHeader();
                builder.WriteEndLine();
            }

            builder.WriteLine("using System.CodeDom.Compiler;");
            builder.WriteEndLine();

            if (string.IsNullOrEmpty(@namespace))
            {
                builder.AddEnum(enumDefinition, storageType, curlyBracketMode);
            }
            else
            {
                builder.AddNamespace(
                    @namespace,
                    b => b.AddEnum(enumDefinition, storageType, curlyBracketMode),
                    curlyBracketMode);
            }

            return(builder.Build());
        }
        private void GenerateProxyMethodBody(CodeBuilder code)
        {
            // method body
            code.AddBlockHeader("\t");
            // create the initial CallParams struct
            code.AddHeader($"\t\tvar {ParamsVarName} = new {_methodInfo.MethodParamsStructName}{_methodInfo.GenericParams} (")
            .AddHeader(string.Join(", ",
                                   _methodInfo.MethodSyntax.ParameterList.Parameters.Select(parameterSyntax => _methodInfo.MethodModel.GetDeclaredSymbol(parameterSyntax).Name)))
            .AddHeaderLine(");");

            code.AddHeaderLine($"\t\tvar runner = new {_calculatorClassInfo.CalculatorClassName}{_calculatorClassInfo.GenericParams}();");
            code.AddHeaderLine($"\t\trunner.RunRecursion({ParamsVarName});");
            if (_methodInfo.ShouldGenerateReturnField)
            {
                code.AddHeaderLine($"\t\treturn runner.{_methodInfo.ReturnFieldName};");
            }
        }
        private void GenerateProxyMethodSignature(CodeBuilder code)
        {
            code.AddHeader(" ");
            code.AddHeader(_methodInfo.ReturnType);
            code.AddHeader(_methodInfo.MethodName);
            code.AddHeader(_methodInfo.GenericParams);

            code.AddHeader(GetProxyMethodParamsList().ToFullString());

            if (!string.IsNullOrEmpty(_methodInfo.ConstraintClauses))
            {
                code.AddHeader("\t\t");
                code.AddHeaderLine(_methodInfo.ConstraintClauses);
            }
        }
        internal string GenerateCalculatorMethodBody(TargetMethodsInfo targetMethods)
        {
            var code = new CodeBuilder();

            // unpack params
            code.AddHeaderLine("\t\t// unpack params");
            code.AddHeaderLine(string.Join("\n",
                                           _methodInfo.MethodSyntax.ParameterList.Parameters.Select(parameterSyntax =>
            {
                var paramSymbol = _methodInfo.MethodModel.GetDeclaredSymbol(parameterSyntax);
                return($"\t\tvar {paramSymbol.Name} = {ParamsVarName}.{paramSymbol.Name};");
            })));

            // find all recursive invocations
            var nodesToReplace = CollectInvocationsWalker.CollectInvocations(_context, _methodInfo, targetMethods);

            if (nodesToReplace.ContainsCriticalFailure)
            {
                _context.Log(_methodInfo.MethodSyntax.GetLocation(), $"An unsupported code was found for '{_methodInfo.MethodSymbol.Name}'. Abort processing.");
                return(null);
            }

            _context.Log(_methodInfo.MethodSyntax.GetLocation(), $"'{_methodInfo.MethodSymbol.Name}':" +
                         $" {nodesToReplace.VoidCalls.Count} void call(s)" +
                         $" {nodesToReplace.Assignments.Count} assignment(s)" +
                         $", {nodesToReplace.DeclarationAndAssignments.Count} var declaration(s)" +
                         $", {nodesToReplace.ReturnsRecursive.Count} recursive call(s) in return(s)" +
                         $", {nodesToReplace.Returns.Count} return(s)");


            // process all supported kinds of recursive invocations
            var allNodesToReplace = nodesToReplace.Returns.Cast <StatementSyntax>()
                                    .Concat(nodesToReplace.ReturnsRecursive)
                                    .Concat(nodesToReplace.VoidCalls.Select(t => t.containingStatement))
                                    .Concat(nodesToReplace.Assignments.Select((t) => t.containingStatement))
                                    .Concat(nodesToReplace.DeclarationAndAssignments.Select(t => t.containingStatement));
            var newRoot = _methodInfo.MethodSyntax.Body.ReplaceNodes(allNodesToReplace, (origNode, curNode) =>
            {
                // _context.Log("!! " + origNode.Kind() + " " + origNode.ToFullStringTrimmed());
                switch (origNode.Kind())
                {
                case SyntaxKind.ReturnStatement:
                    return(ReplaceReturn(targetMethods, (ReturnStatementSyntax)origNode, (ReturnStatementSyntax)curNode));

                case SyntaxKind.ExpressionStatement:
                    var origExpressionNode = (ExpressionStatementSyntax)origNode;
                    var expression         = origExpressionNode.Expression;
                    switch (expression.Kind())
                    {
                    case SyntaxKind.InvocationExpression:
                        return(ReplaceVoidCall(targetMethods, origExpressionNode, (ExpressionStatementSyntax)curNode));

                    case SyntaxKind.SimpleAssignmentExpression:
                        return(ReplaceAssignment(targetMethods, origExpressionNode, (ExpressionStatementSyntax)curNode));

                    default:
                        throw new AntiSOGenException($"Unexpected expression kind {expression.Kind()} to replace {origNode.ToFullStringTrimmed()}");
                    }


                case SyntaxKind.LocalDeclarationStatement:
                    return(ReplaceLocalVarDeclrAssignment(targetMethods, (LocalDeclarationStatementSyntax)origNode, (LocalDeclarationStatementSyntax)curNode));

                default:
                    throw new AntiSOGenException($"Unexpected statement kind {origNode.Kind()} to replace {origNode.ToFullStringTrimmed()}");
                }
            });

            code.AddHeaderLine("\t\t// method body");
            code.AddHeader(newRoot.ToFullString());
            return(code.BuildString());
        }
        /// <summary>
        /// Create fsharp source-code representation of a <see cref="EnumDefinition"/>.
        /// </summary>
        /// <exception cref="Exceptions.InvalidNamespaceException">
        /// Thrown when a invalid namespace identifier is given.
        /// </exception>
        /// <exception cref="Exceptions.OutOfBoundsValueException">
        /// Thrown when enum value does not fit in given storage-type.
        /// </exception>
        /// <param name="enumDefinition">Enum to generate fsharp source-code for</param>
        /// <param name="namespace">Namespace to add the enum to</param>
        /// <param name="headerMode">Mode to use when adding a header</param>
        /// <param name="indentSize">How many spaces should be used for indents</param>
        /// <param name="newlineMode">Mode to use for ending lines</param>
        /// <param name="storageType">Underlying enum storage-type to use</param>
        /// <returns>String containing the genenerated fsharp sourcecode</returns>
        public static string ExportFSharp(
            this EnumDefinition enumDefinition,
            string @namespace,
            HeaderMode headerMode = HeaderMode.Default,
            int indentSize        = 4,
            CodeBuilder.NewlineMode newlineMode = CodeBuilder.NewlineMode.Unix,
            StorageType storageType             = StorageType.Implicit)
        {
            if (enumDefinition == null)
            {
                throw new ArgumentNullException(nameof(enumDefinition));
            }

            if (!IdentifierValidator.ValidateNamespace(@namespace))
            {
                throw new InvalidNamespaceException(@namespace);
            }

            foreach (var oobEntry in enumDefinition.Entries.Where(e => !storageType.Validate(e.Value)))
            {
                throw new OutOfBoundsValueException(storageType, oobEntry.Value);
            }

            var builder = new CodeBuilder(CodeBuilder.IndentMode.Spaces, indentSize, newlineMode);

            if (headerMode != HeaderMode.None)
            {
                builder.AddHeader();
                builder.WriteEndLine();
            }

            // Add namespace.
            builder.WriteLine($"namespace {@namespace}");
            builder.WriteEndLine();

            // Open System.CodeDom.Compiler (for the 'GeneratedCode' attribute)
            builder.WriteLine("open System.CodeDom.Compiler");
            builder.WriteEndLine();

            // Add type comment.
            if (!string.IsNullOrEmpty(enumDefinition.Comment))
            {
                builder.AddSummary(enumDefinition.Comment);
            }

            // Add enum definition.
            var assemblyName = typeof(FSharpExporter).Assembly.GetName();

            builder.WriteLine($"[<GeneratedCode(\"{assemblyName.Name}\", \"{assemblyName.Version}\")>]");
            builder.WriteLine($"type {enumDefinition.Identifier} =");

            // Add entries.
            foreach (var entry in enumDefinition.Entries)
            {
                var literalSuffix = storageType == StorageType.Implicit ?
                                    string.Empty :
                                    storageType.GetFSharpLiteralSuffix();

                builder.WriteLine($"| {entry.Name} = {entry.Value}{literalSuffix}", additionalIndent: 1);
            }

            return(builder.Build());
        }