Beispiel #1
0
        /// <summary>
        /// calls to Enum.Parse get re-written as calls to our special Parse methods on each enum.  We assume the first parameter to Enum.Parse is a a typeof()
        /// </summary>
        private static void WriteEnumParse(ScalaWriter writer, InvocationExpressionSyntax invocationExpression)
        {
            var args = invocationExpression.ArgumentList.Arguments;

            if (args.Count < 2 || args.Count > 3)
            {
                throw new Exception("Expected 2-3 args to Enum.Parse");
            }

            if (args.Count == 3 && (!(args[2].Expression is LiteralExpressionSyntax) || args[2].Expression.As <LiteralExpressionSyntax>().ToString() != "false"))
            {
                throw new NotImplementedException("Case-insensitive Enum.Parse is not supported " + Utility.Descriptor(invocationExpression));
            }

            if (!(args[0].Expression is TypeOfExpressionSyntax))
            {
                throw new Exception("Expected a typeof() expression as the first parameter of Enum.Parse " + Utility.Descriptor(invocationExpression));
            }

            var type = Program.GetModel(invocationExpression).GetTypeInfo(args[0].Expression.As <TypeOfExpressionSyntax>().Type).Type;

            writer.Write(type.ContainingNamespace.FullNameWithDot());
            writer.Write(WriteType.TypeName((INamedTypeSymbol)type));
            writer.Write(".Parse(");
            Core.Write(writer, args[1].Expression);
            writer.Write(")");
        }
Beispiel #2
0
        private static void Generate()
        {
            Console.WriteLine("Parsing...");
            var sw = Stopwatch.StartNew();

            if (!Directory.Exists(OutDir))
            {
                Directory.CreateDirectory(OutDir);
            }

            var allTypes = Compilation.SyntaxTrees
                           .SelectMany(o => o.GetRoot().DescendantNodes().OfType <BaseTypeDeclarationSyntax>())
                           .Select(o => new { Syntax = o, Symbol = GetModel(o).GetDeclaredSymbol(o), TypeName = WriteType.TypeName(GetModel(o).GetDeclaredSymbol(o)) })
                           .GroupBy(o => o.Symbol.ContainingNamespace.FullNameWithDot() + o.TypeName)
                           .ToList();

            Utility.Parallel(Compilation.SyntaxTrees.ToList(), tree =>
            {
                foreach (var n in TriviaProcessor.DoNotWrite(tree))
                {
                    DoNotWrite.TryAdd(n, null);
                }

                //Init ClassTags
                foreach (var method in tree.GetRoot().DescendantNodes().OfType <MethodDeclarationSyntax>().Where(o => o.TypeParameterList != null))
                {
                    ClassTags.InitMethod(GetModel(method).GetDeclaredSymbol(method), method);
                }
            });

            Console.WriteLine("Parsed in " + sw.Elapsed + ". Writing out scala...");
            sw.Restart();

            Compilation.SyntaxTrees.SelectMany(o => o.GetRoot().DescendantNodes().OfType <AnonymousObjectCreationExpressionSyntax>())
            .Select(o => new { Syntax = o, Name = WriteAnonymousObjectCreationExpression.TypeName(o) })
            .GroupBy(o => o.Name)
            .Parallel(o => WriteAnonymousObjectCreationExpression.WriteAnonymousType(o.First().Syntax));


            allTypes.Parallel(type =>
            {
                TypeState.Instance          = new TypeState();
                TypeState.Instance.TypeName = type.First().TypeName;
                TypeState.Instance.Partials = type.Select(o => new TypeState.SyntaxAndSymbol {
                    Symbol = o.Symbol, Syntax = o.Syntax
                })
                                              .Where(o => !DoNotWrite.ContainsKey(o.Syntax))
                                              .ToList();

                if (TypeState.Instance.Partials.Count > 0)
                {
                    WriteType.Go();
                }
            });

            WriteConstructorBody.WriteConstructorsHelper(allTypes.SelectMany(o => o).Where(o => !DoNotWrite.ContainsKey(o.Syntax)).Select(o => o.Symbol));

            Console.WriteLine("Scala written out in " + sw.Elapsed);
        }
Beispiel #3
0
        private static void WriteEnumGetValues(ScalaWriter writer, InvocationExpressionSyntax invocationExpression)
        {
            if (!(invocationExpression.ArgumentList.Arguments[0].Expression is TypeOfExpressionSyntax))
            {
                throw new Exception("Expected a typeof() expression as the first parameter of Enum.GetValues " + Utility.Descriptor(invocationExpression));
            }

            var type = Program.GetModel(invocationExpression).GetTypeInfo(invocationExpression.ArgumentList.Arguments[0].Expression.As <TypeOfExpressionSyntax>().Type).Type;

            writer.Write(type.ContainingNamespace.FullNameWithDot());
            writer.Write(WriteType.TypeName((INamedTypeSymbol)type));
            writer.Write(".Values");
        }
Beispiel #4
0
        /// <summary>
        /// calls to Enum.Parse get re-written as calls to CsScala.EnumTryParse, where we pass in our special Parse methods on each enum.
        /// </summary>
        private static void WriteEnumTryParse(ScalaWriter writer, InvocationExpressionSyntax invocationExpression)
        {
            var args = invocationExpression.ArgumentList.Arguments;

            if (args.Count != 2)
            {
                throw new Exception("Expected 2 args to Enum.TryParse.  Other overloads are not supported");
            }

            writer.Write("System.CsScala.EnumTryParse(");
            Core.Write(writer, args[0].Expression);
            writer.Write(", ");
            writer.Write(args[1].Expression.As <IdentifierNameSyntax>().Identifier.ToString());
            writer.Write(", ");

            var type = Program.GetModel(invocationExpression).GetTypeInfo(args[1].Expression).Type;

            writer.Write(type.ContainingNamespace.FullNameWithDot());
            writer.Write(WriteType.TypeName((INamedTypeSymbol)type));
            writer.Write(".Parse)");
        }
Beispiel #5
0
        public static void WriteMember(ScalaWriter writer, ExpressionSyntax expression)
        {
            var symbol = Program.GetModel(expression).GetSymbolInfo(expression).Symbol;

            if (symbol is INamedTypeSymbol)
            {
                var translateOpt = TypeTranslation.Get(symbol.ContainingNamespace.FullNameWithDot() + symbol.Name, symbol.As <INamedTypeSymbol>());

                if (translateOpt != null)
                {
                    writer.Write(translateOpt.ReplaceWith);
                }
                else
                {
                    writer.Write(symbol.ContainingNamespace.FullNameWithDot() + WriteType.TypeName(symbol.As <INamedTypeSymbol>()));
                }
            }
            else
            {
                Core.Write(writer, expression);
            }
        }
        public static void WriteConstructorsHelper(IEnumerable <INamedTypeSymbol> allTypes)
        {
            foreach (var t in allTypes.Select(o => o.ContainingNamespace.FullNameWithDot() + WriteType.TypeName(o)))
            {
                AllTypes.Add(t);
            }

            if (StaticConstructors.Count == 0)
            {
                return; //no need for it.
            }
            using (var writer = new ScalaWriter("CsRoot", "Constructors"))
            {
                writer.WriteLine(@"package CsRoot;
/*
This file lists all the static constructors.  Scala doesn't have the same concept of static constructors as C#, so CsScala generated cctor() methods.  You must call these manually.  If you call Constructors.init(), all static constructors will be called at once and you won't have to worry about calling each one manually.
*/");

                //foreach (var type in AllTypes.OrderBy(o => o))
                //	writer.WriteLine("import " + type + ";");

                writer.WriteLine("object Constructors");
                writer.WriteOpenBrace();

                writer.WriteLine("def init()");
                writer.WriteOpenBrace();
                foreach (var cctor in StaticConstructors.OrderBy(o => o))
                {
                    writer.WriteLine(cctor + ".cctor();");
                }
                writer.WriteCloseBrace();
                writer.WriteCloseBrace();
            }
        }
Beispiel #7
0
        private static string ConvertTypeUncached(ITypeSymbol typeSymbol)
        {
            if (typeSymbol.IsAnonymousType)
            {
                return(WriteAnonymousObjectCreationExpression.TypeName(typeSymbol.As <INamedTypeSymbol>()));
            }

            var array = typeSymbol as IArrayTypeSymbol;

            if (array != null)
            {
                return("Array[" + TryConvertType(array.ElementType) + "]");
            }

            var typeInfoStr = typeSymbol.ToString();

            var named = typeSymbol as INamedTypeSymbol;

            if (typeSymbol.TypeKind == TypeKind.TypeParameter)
            {
                return(typeSymbol.Name);
            }

            if (typeSymbol.TypeKind == TypeKind.Delegate)
            {
                var dlg = named.DelegateInvokeMethod.As <IMethodSymbol>();
                if (dlg.Parameters.Length == 0)
                {
                    return("() => " + TryConvertType(dlg.ReturnType));
                }
                else
                {
                    return("(" + string.Join(", ", dlg.Parameters.ToList().Select(o => TryConvertType(o.Type))) + ") => " + TryConvertType(dlg.ReturnType));
                }
            }

            if (typeSymbol.TypeKind == TypeKind.Enum)
            {
                return("Int"); //enums are always ints
            }
            if (named != null && named.Name == "Nullable" && named.ContainingNamespace.ToString() == "System")
            {
                //Nullable types, if value types, get replaced with the java.lang alternatives.  If reference types, just use them as-is
                var convertedType = TryConvertType(named.TypeArguments.Single());

                switch (convertedType)
                {
                case "Int":
                    return("java.lang.Integer");

                case "Boolean":
                    return("java.lang.Boolean");

                case "Byte":
                    return("java.lang.Byte");

                case "Short":
                    return("java.lang.Short");

                case "Float":
                    return("java.lang.Float");

                case "Double":
                    return("java.lang.Double");

                case "Char":
                    return("java.lang.Char");

                case "Long":
                    return("java.lang.Long");

                default:
                    return(convertedType);
                }
            }

            var typeStr = GenericTypeName(typeSymbol);

            var trans = TypeTranslation.Get(typeStr, named);

            if (named.IsGenericType() && !named.IsUnboundGenericType && (trans == null || trans.SkipGenericTypes == false))
            {
                return(TryConvertType(named.ConstructUnboundGenericType()) + "[" + string.Join(", ", TypeArguments(named).Select(o => TryConvertType(o))) + "]");
            }


            switch (typeStr)
            {
            case "System.Void":
                return("Unit");

            case "System.Boolean":
                return("Boolean");

            case "System.Object":
                return("Any");

            case "System.UInt64":
            case "System.Double":
                return("Double");

            case "System.Single":
                return("Float");

            case "System.String":
                return("String");

            case "System.Int32":
            case "System.UInt16":
                return("Int");


            case "System.Int64":
            case "System.UInt32":
                return("Long");

            case "System.Byte":
                return("Byte");

            case "System.Int16":
                return("Short");

            case "System.Char":
                return("Char");

            case "System.Array":
                return(null);    //in scala, unlike C#, array must always have type arguments.  To work around this, just avoid printing the type anytime we see a bare Array type in C#. scala will infer it.

            default:
                if (trans != null)
                {
                    return(trans.As <TypeTranslation>().Replace(named));
                }

                if (named != null)
                {
                    return(typeSymbol.ContainingNamespace.FullNameWithDot() + WriteType.TypeName(named));
                }

                //This type does not get translated and gets used as-is
                return(typeSymbol.ContainingNamespace.FullNameWithDot() + typeSymbol.Name);
            }
        }
        private static void Go(ScalaWriter writer, ExpressionSyntax left, SyntaxToken operatorToken, ExpressionSyntax right)
        {
            if (operatorToken.Kind() == SyntaxKind.AsKeyword)
            {
                writer.Write("CsScala.As[");
                writer.Write(TypeProcessor.ConvertType(right));
                writer.Write("](");
                Core.Write(writer, left);
                writer.Write(")");
            }
            else if (operatorToken.Kind() == SyntaxKind.IsKeyword)
            {
                Core.Write(writer, left);
                writer.Write(".isInstanceOf[");
                writer.Write(TypeProcessor.ConvertType(right));
                writer.Write("]");
            }
            else if (operatorToken.Kind() == SyntaxKind.QuestionQuestionToken)
            {
                writer.Write("CsScala.Coalesce(");
                Core.Write(writer, left);
                writer.Write(", ");
                Core.Write(writer, right);
                writer.Write(")");
            }
            else
            {
                if (left is ElementAccessExpressionSyntax && IsAssignmentToken(operatorToken.Kind()))
                {
                    var subExpr = left.As <ElementAccessExpressionSyntax>();
                    var typeStr = TypeProcessor.GenericTypeName(Program.GetModel(left).GetTypeInfo(subExpr.Expression).Type);
                    var trans   = ElementAccessTranslation.Get(typeStr);

                    if (trans != null)
                    {
                        Core.Write(writer, subExpr.Expression);
                        writer.Write(".");

                        if (operatorToken.Kind() == SyntaxKind.EqualsToken)
                        {
                            writer.Write(trans.ReplaceAssign);
                        }
                        else
                        {
                            throw new Exception(operatorToken.Kind() + " is not supported on " + typeStr + " " + Utility.Descriptor(left.Parent));
                        }

                        writer.Write("(");
                        foreach (var arg in subExpr.ArgumentList.Arguments)
                        {
                            Core.Write(writer, arg.Expression);
                            writer.Write(", ");
                        }

                        Core.Write(writer, right);
                        writer.Write(")");

                        return;
                    }
                }

                Action <ExpressionSyntax> write = e =>
                {
                    var model = Program.GetModel(left);
                    var type  = model.GetTypeInfo(e);

                    //Check for enums being converted to strings by string concatenation
                    if (operatorToken.Kind() == SyntaxKind.PlusToken && type.Type.TypeKind == TypeKind.Enum)
                    {
                        writer.Write(type.Type.ContainingNamespace.FullNameWithDot());
                        writer.Write(WriteType.TypeName(type.Type.As <INamedTypeSymbol>()));
                        writer.Write(".ToString(");
                        Core.Write(writer, e);
                        writer.Write(")");
                    }
                    else if (operatorToken.Kind() == SyntaxKind.PlusToken && (type.Type.Name == "Nullable" && type.Type.ContainingNamespace.FullName() == "System" && type.Type.As <INamedTypeSymbol>().TypeArguments.Single().TypeKind == TypeKind.Enum))
                    {
                        var enumType = type.Type.As <INamedTypeSymbol>().TypeArguments.Single();
                        writer.Write(enumType.ContainingNamespace.FullNameWithDot());
                        writer.Write(WriteType.TypeName(enumType.As <INamedTypeSymbol>()));
                        writer.Write(".ToString(");
                        Core.Write(writer, e);
                        writer.Write(")");
                    }
                    else if (operatorToken.Kind() == SyntaxKind.PlusToken && IsException(type.Type))     //Check for exceptions being converted to strings by string concatenation
                    {
                        writer.Write("System.CsScala.ExceptionToString(");
                        Core.Write(writer, e);
                        writer.Write(")");
                    }
                    else if (operatorToken.Kind() == SyntaxKind.PlusToken && type.Type.SpecialType == SpecialType.System_Byte && !Utility.IsNumeric(type.ConvertedType))
                    {
                        //bytes are signed in the JVM, so we need to take care when converting them to strings.  Exclude numeric types, since Core.Writer will convert these to ints
                        writer.Write("System.CsScala.ByteToString(");
                        Core.Write(writer, e);
                        writer.Write(")");
                    }
                    else if (operatorToken.Kind() == SyntaxKind.PlusToken && !(e is BinaryExpressionSyntax) && type.Type.SpecialType == SpecialType.System_String && CouldBeNullString(model, e))
                    {
                        //In .net, concatenating a null string does not alter the output. However, in the JVM, it produces the "null" string. To counter this, we must check non-const strings.
                        writer.Write("System.CsScala.NullCheck(");
                        Core.Write(writer, e);
                        writer.Write(")");
                    }
                    else if (operatorToken.Kind() == SyntaxKind.PlusToken && !(e is BinaryExpressionSyntax) && type.Type is INamedTypeSymbol && type.Type.As <INamedTypeSymbol>().ConstructedFrom.SpecialType == SpecialType.System_Nullable_T)
                    {
                        //Concatening a nullable type in .net just produces an empty string if it's null.  In scala it produces "null" or a null reference exception -- we want neither.
                        writer.Write("System.CsScala.NullCheck(");
                        Core.Write(writer, e);
                        writer.Write(")");
                    }
                    else if (operatorToken.Kind() == SyntaxKind.PlusToken && !(e is BinaryExpressionSyntax) && type.Type.SpecialType == SpecialType.System_Boolean)
                    {
                        writer.Write("System.CsScala.BooleanToString(");
                        Core.Write(writer, e);
                        writer.Write(")");
                    }
                    else
                    {
                        Core.Write(writer, e);
                    }
                };

                write(left);
                writer.Write(" ");
                writer.Write(operatorToken.ToString());
                writer.Write(" ");
                write(right);
            }
        }
Beispiel #9
0
        public static void Go(ScalaWriter writer, InvocationExpressionSyntax invocationExpression)
        {
            var model = Program.GetModel(invocationExpression);

            var symbolInfo       = model.GetSymbolInfo(invocationExpression);
            var expressionSymbol = model.GetSymbolInfo(invocationExpression.Expression);

            if (symbolInfo.Symbol == null)
            {
                throw new Exception("symbolInfo.Symbol null at " + Utility.Descriptor(invocationExpression));
            }
            if (symbolInfo.Symbol.OriginalDefinition == null)
            {
                throw new Exception("symbolInfo.Symbol.OriginalDefinition null at " + Utility.Descriptor(invocationExpression));
            }
            var methodSymbol = symbolInfo.Symbol.OriginalDefinition.As <IMethodSymbol>().UnReduce();

            var translateOpt = MethodTranslation.Get(symbolInfo.Symbol.As <IMethodSymbol>());
            var memberReferenceExpressionOpt = invocationExpression.Expression as MemberAccessExpressionSyntax;
            var firstParameter = true;

            var              extensionNamespace = methodSymbol.IsExtensionMethod ? methodSymbol.ContainingNamespace.FullNameWithDot() + methodSymbol.ContainingType.Name : null; //null means it's not an extension method, non-null means it is
            string           methodName;
            string           typeParameters = null;
            ExpressionSyntax subExpressionOpt;

            if (methodSymbol.ContainingType.Name == "Enum")
            {
                if (methodSymbol.Name == "Parse")
                {
                    WriteEnumParse(writer, invocationExpression);
                    return;
                }

                if (methodSymbol.Name == "TryParse")
                {
                    WriteEnumTryParse(writer, invocationExpression);
                    return;
                }

                if (methodSymbol.Name == "GetValues")
                {
                    WriteEnumGetValues(writer, invocationExpression);
                    return;
                }
            }

            if (expressionSymbol.Symbol is IEventSymbol)
            {
                methodName = "Invoke"; //Would need to append the number of arguments to this to support events.  However, events are not currently supported
            }
            else if (memberReferenceExpressionOpt != null && memberReferenceExpressionOpt.Expression is PredefinedTypeSyntax)
            {
                switch (methodSymbol.Name)
                {
                case "Parse":
                    if (invocationExpression.ArgumentList.Arguments.Count > 1)
                    {
                        throw new Exception("Multiple arguments to .Parse methods are not supported: " + Utility.Descriptor(invocationExpression));
                    }
                    Core.Write(writer, invocationExpression.ArgumentList.Arguments.Single().Expression);

                    writer.Write(".trim().to");     //we must trim strings before parsing them.  In C#, a string like "4 " parses just fine, but if we don't trim this same string would false to parse in java
                    writer.Write(TypeProcessor.ConvertType(methodSymbol.ReturnType));

                    return;

                case "TryParse":
                    methodName         = "TryParse" + TypeProcessor.ConvertType(methodSymbol.Parameters[1].Type);
                    extensionNamespace = "CsScala";
                    break;

                default:
                    methodName         = methodSymbol.Name;
                    extensionNamespace = "CsScala";
                    break;
                }
            }
            else if (translateOpt != null && translateOpt.ReplaceWith != null)
            {
                methodName = translateOpt.ReplaceWith;
            }
            else if (methodSymbol.MethodKind == MethodKind.DelegateInvoke)
            {
                methodName = null;
            }
            else
            {
                methodName = OverloadResolver.MethodName(methodSymbol);
            }

            if (translateOpt != null && translateOpt.HasComplexReplaceWith)
            {
                translateOpt.DoComplexReplaceWith(writer, memberReferenceExpressionOpt);
                return;
            }

            if (translateOpt != null && translateOpt.SkipExtensionParameter)
            {
                subExpressionOpt = null;
            }
            else if (methodSymbol.MethodKind == MethodKind.DelegateInvoke)
            {
                subExpressionOpt = invocationExpression.Expression;
            }
            else if (memberReferenceExpressionOpt != null)
            {
                if (memberReferenceExpressionOpt.Expression is PredefinedTypeSyntax)
                {
                    subExpressionOpt = null;
                }
                else
                {
                    subExpressionOpt = memberReferenceExpressionOpt.Expression;
                }
            }
            else
            {
                subExpressionOpt = null;
            }



            //When the code specifically names generic arguments, include them in the method name
            var genNameExpression = invocationExpression.Expression as GenericNameSyntax;

            if (genNameExpression == null && memberReferenceExpressionOpt != null)
            {
                genNameExpression = memberReferenceExpressionOpt.Name as GenericNameSyntax;
            }
            if (genNameExpression != null && genNameExpression.TypeArgumentList.Arguments.Count > 0)
            {
                typeParameters = "[" + string.Join(", ", genNameExpression.TypeArgumentList.Arguments.Select(TypeProcessor.ConvertType)) + "]";
            }


            //Determine if it's an extension method called in a non-extension way.  In this case, just pretend it's not an extension method
            if (extensionNamespace != null && subExpressionOpt != null && model.GetTypeInfo(subExpressionOpt).ConvertedType.ToString() == methodSymbol.ContainingNamespace + "." + methodSymbol.ContainingType.Name)
            {
                extensionNamespace = null;
            }

            if (translateOpt != null && !string.IsNullOrEmpty(translateOpt.ExtensionNamespace))
            {
                extensionNamespace = translateOpt.ExtensionNamespace;
            }
            else if (translateOpt != null && translateOpt.ExtensionNamespace == "")
            {
                extensionNamespace = null;
            }

            var memberType     = memberReferenceExpressionOpt == null ? null : model.GetTypeInfo(memberReferenceExpressionOpt.Expression).Type;
            var isNullableEnum = memberType != null && (memberType.Name == "Nullable" && memberType.ContainingNamespace.FullName() == "System") && memberType.As <INamedTypeSymbol>().TypeArguments.Single().TypeKind == TypeKind.Enum;

            if (isNullableEnum && methodSymbol.Name == "ToString")
            {
                extensionNamespace = null; //override Translations.xml for nullable enums. We want them to convert to the enum's ToString method
                methodName         = "toString";
            }


            if (extensionNamespace != null)
            {
                writer.Write(extensionNamespace);

                if (methodName != null)
                {
                    writer.Write(".");
                    writer.Write(methodName);
                }

                WriteTypeParameters(writer, translateOpt, typeParameters, invocationExpression);


                writer.Write("(");

                if (subExpressionOpt != null)
                {
                    firstParameter = false;
                    Core.Write(writer, subExpressionOpt);
                }
            }
            else
            {
                if (memberReferenceExpressionOpt != null)
                {
                    //Check against lowercase toString since it gets replaced with the lowered version before we get here
                    if (methodName == "toString")
                    {
                        if (memberType.TypeKind == TypeKind.Enum || isNullableEnum)
                        {
                            var enumType = memberType.TypeKind == TypeKind.Enum ? memberType : memberType.As <INamedTypeSymbol>().TypeArguments.Single();

                            //calling ToString() on an enum forwards to our enum's special ToString method
                            writer.Write(enumType.ContainingNamespace.FullNameWithDot());
                            writer.Write(WriteType.TypeName((INamedTypeSymbol)enumType));
                            writer.Write(".ToString(");
                            Core.Write(writer, memberReferenceExpressionOpt.Expression);
                            writer.Write(")");

                            if (invocationExpression.ArgumentList.Arguments.Count > 0)
                            {
                                throw new Exception("Enum's ToString detected with parameters.  These are not supported " + Utility.Descriptor(invocationExpression));
                            }

                            return;
                        }

                        if (memberType.SpecialType == SpecialType.System_Byte)
                        {
                            //Calling ToString on a byte needs to take special care since it's signed in the JVM
                            writer.Write("System.CsScala.ByteToString(");
                            Core.Write(writer, memberReferenceExpressionOpt.Expression);
                            writer.Write(")");

                            if (invocationExpression.ArgumentList.Arguments.Count > 0)
                            {
                                throw new Exception("Byte's ToString detected with parameters.  These are not supported " + Utility.Descriptor(invocationExpression));
                            }

                            return;
                        }
                    }
                }

                if (subExpressionOpt != null)
                {
                    WriteMemberAccessExpression.WriteMember(writer, subExpressionOpt);

                    if (methodName != null)
                    {
                        writer.Write(".");
                    }
                }
                else if (methodSymbol.IsStatic && extensionNamespace == null)
                {
                    writer.Write(methodSymbol.ContainingNamespace.FullNameWithDot());
                    writer.Write(WriteType.TypeName(methodSymbol.ContainingType));
                    writer.Write(".");
                }


                writer.Write(methodName);
                WriteTypeParameters(writer, translateOpt, typeParameters, invocationExpression);
                writer.Write("(");
            }

            bool inParams         = false;
            bool foundParamsArray = false;

            foreach (var arg in TranslateParameters(translateOpt, invocationExpression.ArgumentList.Arguments, invocationExpression))
            {
                if (firstParameter)
                {
                    firstParameter = false;
                }
                else
                {
                    writer.Write(", ");
                }

                if (!inParams && IsParamsArgument(invocationExpression, arg.ArgumentOpt, methodSymbol))
                {
                    foundParamsArray = true;

                    if (!TypeProcessor.ConvertType(model.GetTypeInfo(arg.ArgumentOpt.Expression).Type).StartsWith("Array["))
                    {
                        inParams = true;
                        writer.Write("Array(");
                    }
                }


                if (arg.ArgumentOpt != null &&
                    arg.ArgumentOpt.RefOrOutKeyword.Kind() != SyntaxKind.None &&
                    model.GetSymbolInfo(arg.ArgumentOpt.Expression).Symbol is IFieldSymbol)
                {
                    throw new Exception("ref/out cannot reference fields, only local variables.  Consider using ref/out on a local variable and then assigning it into the field. " + Utility.Descriptor(invocationExpression));
                }


                //When passing an argument by ref or out, leave off the .Value suffix
                if (arg.ArgumentOpt != null && arg.ArgumentOpt.RefOrOutKeyword.Kind() != SyntaxKind.None)
                {
                    WriteIdentifierName.Go(writer, arg.ArgumentOpt.Expression.As <IdentifierNameSyntax>(), true);
                }
                else
                {
                    arg.Write(writer);
                }
            }

            if (inParams)
            {
                writer.Write(")");
            }
            else if (!foundParamsArray && methodSymbol.Parameters.Any() && methodSymbol.Parameters.Last().IsParams)
            {
                writer.Write(", Array()"); //params method called without any params argument.  Send an empty array.
            }
            writer.Write(")");
        }