Exemplo n.º 1
0
 private static void WriteOverloadedOperatorInvocation(HaxeWriter writer, BinaryExpressionSyntax expression, IMethodSymbol method)
 {
     writer.Write(method.ContainingType.ContainingNamespace.FullNameWithDot().ToLower());
     writer.Write(method.ContainingType.Name);
     writer.Write(".");
     writer.Write(OverloadResolver.MethodName(method));
     writer.Write("(");
     Core.Write(writer, expression.Left);
     writer.Write(", ");
     Core.Write(writer, expression.Right);
     writer.Write(")");
 }
Exemplo n.º 2
0
        private static void GoInternal(HaxeWriter writer, BaseMethodDeclarationSyntax method, TypeSyntax returnType, TypeParameterListSyntax typeParameterListOpt, SyntaxList <TypeParameterConstraintClauseSyntax>?constraintClassesOpt)
        {
            var methodSymbol = Program.GetModel(method).GetDeclaredSymbol(method);

            if (method.Modifiers.Any(SyntaxKind.PartialKeyword) && method.Body == null)
            {
                //We only want to render out one of the two partial methods.  If there's another, skip this one.
                if (TypeState.Instance.Partials.SelectMany(o => o.Syntax.As <ClassDeclarationSyntax>().Members)
                    .OfType <MethodDeclarationSyntax>()
                    .Except(method as MethodDeclarationSyntax)
                    .Where(o => o.Identifier.ValueText == methodSymbol.Name)
                    .Any())
                {
                    return;
                }
            }

            if (methodSymbol.Name == "System.Collections.IEnumerable.GetEnumerator")
            {
                return; //we don't support the non-generic enumerator
            }
            if (methodSymbol.Name == "GetEnumerator")
            {
                WriteGetEnumeratorFunction(writer, method, methodSymbol);
                return;
            }

            writer.WriteIndent();

            if (ShouldUseOverrideKeyword(method, methodSymbol))
            {
                writer.Write("override ");
            }
            if (method.Modifiers.Any(SyntaxKind.PublicKeyword) || method.Modifiers.Any(SyntaxKind.ProtectedKeyword) || method.Modifiers.Any(SyntaxKind.InternalKeyword))
            {
                writer.Write("public ");
            }
            if (method.Modifiers.Any(SyntaxKind.PrivateKeyword))
            {
                writer.Write("private ");
            }
            if (method.Modifiers.Any(SyntaxKind.StaticKeyword))
            {
                writer.Write("static ");
            }

            writer.Write("function ");
            var methodName = OverloadResolver.MethodName(methodSymbol);

            if (methodName == "ToString")
            {
                methodName = "toString";
            }

            writer.Write(methodName);

            if (typeParameterListOpt != null)
            {
                writer.Write("<");
                writer.Write(string.Join(", ", typeParameterListOpt.Parameters.Select(o => TypeParameter(o, constraintClassesOpt))));
                writer.Write(">");
            }

            writer.Write("(");

            Dictionary <string, ExpressionSyntax> deferredDefaults;

            WriteParameters(writer, method, methodSymbol, out deferredDefaults);

            writer.Write(")");
            writer.Write(TypeProcessor.ConvertTypeWithColon(returnType));

            if (method.Modifiers.Any(SyntaxKind.AbstractKeyword))
            {
                writer.WriteLine();
                writer.WriteOpenBrace();
                writer.WriteIndent();

                if (returnType.ToString() != "void")
                {
                    writer.Write("return ");                     //"return" the throw statement to work around haxe limitations
                }
                writer.Write("throw new Exception(\"Abstract item called\");\r\n");
                writer.WriteCloseBrace();
            }
            else if (method.Parent is InterfaceDeclarationSyntax)
            {
                writer.Write(";\r\n");
            }
            else
            {
                writer.WriteLine();
                writer.WriteOpenBrace();

                foreach (var defer in deferredDefaults)
                {
                    writer.WriteLine("if (" + defer.Key + " == null)");
                    writer.Indent++;
                    writer.WriteIndent();
                    writer.Write(defer.Key);
                    writer.Write(" = ");
                    Core.Write(writer, defer.Value);
                    writer.Write(";\r\n");
                    writer.Indent--;
                }

                if (method.Body != null)
                {
                    foreach (var statement in method.Body.Statements)
                    {
                        Core.Write(writer, statement);
                    }

                    TriviaProcessor.ProcessTrivias(writer, method.Body.DescendantTrivia());
                }

                writer.WriteCloseBrace();
            }
        }
Exemplo n.º 3
0
        public static void Go(HaxeWriter writer, MethodDeclarationSyntax method)
        {
            if (method.Modifiers.Any(SyntaxKind.PartialKeyword) && method.Body == null)
            {
                //We only want to render out one of the two partial methods.  If there's another, skip this one.
                if (TypeState.Instance.Partials.SelectMany(o => o.Syntax.As <ClassDeclarationSyntax>().Members)
                    .OfType <MethodDeclarationSyntax>()
                    .Except(method)
                    .Where(o => o.Identifier.ValueText == method.Identifier.ValueText)
                    .Any())
                {
                    return;
                }
            }

            if (method.Identifier.ValueText == "GetEnumerator")
            {
                return;                 //skip GetEnumerator methods -- haxe can't enumerate on objects.  TODO: Render these out, but convert them to array-returning methods
            }
            var methodSymbol = Program.GetModel(method).GetDeclaredSymbol(method);

            writer.WriteIndent();

            if (ShouldUseOverrideKeyword(method, methodSymbol))
            {
                writer.Write("override ");
            }
            if (method.Modifiers.Any(SyntaxKind.PublicKeyword) || method.Modifiers.Any(SyntaxKind.ProtectedKeyword) || method.Modifiers.Any(SyntaxKind.InternalKeyword))
            {
                writer.Write("public ");
            }
            if (method.Modifiers.Any(SyntaxKind.PrivateKeyword))
            {
                writer.Write("private ");
            }
            if (method.Modifiers.Any(SyntaxKind.StaticKeyword))
            {
                writer.Write("static ");
            }

            writer.Write("function ");
            var methodName = OverloadResolver.MethodName(methodSymbol);

            if (methodName == "ToString")
            {
                methodName = "toString";
            }

            writer.Write(methodName);

            if (method.TypeParameterList != null)
            {
                writer.Write("<");
                writer.Write(string.Join(", ", method.TypeParameterList.Parameters.Select(o => TypeParameter(o, method.ConstraintClauses))));
                writer.Write(">");
            }

            writer.Write("(");
            var deferredDefaults = new Dictionary <string, ExpressionSyntax>();

            var firstParam = true;

            foreach (var parameter in method.ParameterList.Parameters)
            {
                bool isRef = parameter.Modifiers.Any(SyntaxKind.OutKeyword) || parameter.Modifiers.Any(SyntaxKind.RefKeyword);

                if (firstParam)
                {
                    firstParam = false;
                }
                else
                {
                    writer.Write(", ");
                }

                writer.Write(parameter.Identifier.ValueText);

                if (isRef)
                {
                    writer.Write(":CsRef<");
                    writer.Write(TypeProcessor.ConvertType(parameter.Type));
                    writer.Write(">");

                    Program.RefOutSymbols.TryAdd(Program.GetModel(method).GetDeclaredSymbol(parameter), null);
                }
                else
                {
                    writer.Write(TypeProcessor.ConvertTypeWithColon(parameter.Type));
                }

                if (parameter.Default != null)
                {
                    writer.Write(" = ");

                    if (TypeProcessor.ConvertType(parameter.Type).StartsWith("Nullable"))
                    {
                        writer.Write("null");
                        deferredDefaults.Add(parameter.Identifier.ValueText, parameter.Default.Value);
                    }
                    else
                    {
                        Core.Write(writer, parameter.Default.Value);
                    }
                }
            }

            writer.Write(")");
            writer.Write(TypeProcessor.ConvertTypeWithColon(method.ReturnType));

            if (method.Modifiers.Any(SyntaxKind.AbstractKeyword))
            {
                writer.WriteLine();
                writer.WriteOpenBrace();
                writer.WriteIndent();

                if (method.ReturnType.ToString() != "void")
                {
                    writer.Write("return ");                     //"return" the throw statement to work around haxe limitations
                }
                writer.Write("throw new Exception(\"Abstract item called\");\r\n");
                writer.WriteCloseBrace();
            }
            else if (method.Parent is InterfaceDeclarationSyntax)
            {
                writer.Write(";\r\n");
            }
            else
            {
                writer.WriteLine();
                writer.WriteOpenBrace();

                foreach (var defer in deferredDefaults)
                {
                    writer.WriteLine("if (" + defer.Key + " == null)");
                    writer.Indent++;
                    writer.WriteIndent();
                    writer.Write(defer.Key);
                    writer.Write(" = ");
                    Core.Write(writer, defer.Value);
                    writer.Write(";\r\n");
                    writer.Indent--;
                }

                if (method.Body != null)
                {
                    foreach (var statement in method.Body.Statements)
                    {
                        Core.Write(writer, statement);
                    }

                    TriviaProcessor.ProcessTrivias(writer, method.Body.DescendantTrivia());
                }

                writer.WriteCloseBrace();
            }
        }
Exemplo n.º 4
0
        public static void Go(HaxeWriter 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("InvocationExpression could not be identified.  Are you sure the C# is valid? " + Utility.Descriptor(invocationExpression));
            }
            var methodSymbol     = (IMethodSymbol)symbolInfo.Symbol;
            var origMethodSymbol = methodSymbol.OriginalDefinition.As <IMethodSymbol>().UnReduce();

            var translateOpt = MethodTranslation.Get(symbolInfo.Symbol.As <IMethodSymbol>());
            var memberReferenceExpressionOpt = invocationExpression.Expression as MemberAccessExpressionSyntax;
            //var returnTypeHaxe = TypeProcessor.ConvertType(methodSymbol.ReturnType);
            var firstParameter = true;

            if (methodSymbol.TypeArguments.Any(o => o.SpecialType == SpecialType.System_Char) && origMethodSymbol.ContainingType.Name != "Enumerable")
            {
                throw new Exception("Char cannot be passed as a type argument. methodName=" + origMethodSymbol.Name + " " + Utility.Descriptor(invocationExpression));                                 //the called function could call .ToString on it, which would give the int representation instead of the char.  We could fix this by wrapping chars in a class but that would hit performance, so instead just re-write the offending C#.  Skip this requirement for System.Linq.Enumerable because it is typically just mapping to a lambda, such as Select or TakeWhile, and our lambda can handle it correctly.  Ideally we'd detect if the type parameter is only used in a lambda
            }
            var              extensionNamespace = origMethodSymbol.IsExtensionMethod ? origMethodSymbol.ContainingNamespace.FullNameWithDot().ToLower() + origMethodSymbol.ContainingType.Name : null; //null means it's not an extension method, non-null means it is
            string           methodName;
            ExpressionSyntax subExpressionOpt;

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

                if (origMethodSymbol.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 (origMethodSymbol.Name)
                {
                case "Parse":
                    var t = TypeProcessor.ConvertType(origMethodSymbol.ReturnType);

                    if (t == "Bool")
                    {
                        methodName         = "ParseBool";
                        extensionNamespace = "Cs2Hx";
                    }
                    else if (t == "Int" || t == "Float")
                    {
                        methodName         = "parse" + t;
                        extensionNamespace = "Std";
                    }
                    else
                    {
                        throw new Exception("Parse method on " + t + " is not supported.  " + Utility.Descriptor(memberReferenceExpressionOpt));
                    }

                    break;

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

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

            if (translateOpt != null && translateOpt.HasComplexReplaceWith)
            {
                translateOpt.DoComplexReplaceWith(writer, memberReferenceExpressionOpt);                 //TODO: What if this is null?
                return;
            }

            if (translateOpt != null && translateOpt.SkipExtensionParameter)
            {
                subExpressionOpt = null;
            }
            else if (origMethodSymbol.MethodKind == MethodKind.DelegateInvoke)
            {
                subExpressionOpt = invocationExpression.Expression;
            }
            else if (memberReferenceExpressionOpt != null)
            {
                if (memberReferenceExpressionOpt.Expression is PredefinedTypeSyntax)
                {
                    subExpressionOpt = null;
                }
                else
                {
                    subExpressionOpt = memberReferenceExpressionOpt.Expression;
                }
            }
            else
            {
                subExpressionOpt = null;                 //TODO
            }
            //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() == origMethodSymbol.ContainingNamespace + "." + origMethodSymbol.ContainingType.Name)
            {
                extensionNamespace = null;
            }

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

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

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

                writer.Write("(");

                if (subExpressionOpt != null)
                {
                    firstParameter = false;

                    if (origMethodSymbol.IsExtensionMethod)
                    {
                        WriteForEachStatement.CheckWriteEnumerator(writer, subExpressionOpt, true);
                    }
                    else
                    {
                        Core.Write(writer, subExpressionOpt);
                    }
                }
            }
            else
            {
                if (memberReferenceExpressionOpt != null)
                {
                    var memberType     = model.GetTypeInfo(memberReferenceExpressionOpt.Expression).Type;
                    var memberTypeHaxe = TypeProcessor.ConvertType(memberType);

                    //sort calls without any parameters need to get the default sort parameter
                    if (methodName == "sort" && invocationExpression.ArgumentList.Arguments.Count == 0)
                    {
                        Core.Write(writer, memberReferenceExpressionOpt.Expression);
                        writer.Write(".sort(");

                        switch (memberTypeHaxe)
                        {
                        case "Array<Int>":
                            writer.Write("Cs2Hx.SortInts");
                            break;

                        case "Array<Float>":
                            writer.Write("Cs2Hx.SortFloats");
                            break;

                        case "Array<String>":
                            writer.Write("Cs2Hx.SortStrings");
                            break;

                        default:
                            throw new Exception("Unknown default sort type: " + memberTypeHaxe + ".  " + Utility.Descriptor(invocationExpression));
                        }

                        writer.Write(")");
                        return;
                    }

                    //Check against lowercase toString since it gets replaced with the haxe name before we get here
                    if (methodName == "toString")
                    {
                        if (memberType.TypeKind == TypeKind.Enum)
                        {
                            //calling ToString() on an enum forwards to our enum's special ToString method
                            writer.Write(memberType.ContainingNamespace.FullNameWithDot().ToLower());
                            writer.Write(WriteType.TypeName((INamedTypeSymbol)memberType));
                            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_Char)
                        {
                            writer.Write("Cs2Hx.CharToString(");
                            Core.Write(writer, memberReferenceExpressionOpt.Expression);
                            writer.Write(")");

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

                            return;
                        }

                        if (memberTypeHaxe == "Int" || memberTypeHaxe == "Float" || memberTypeHaxe == "Bool" || memberType.TypeKind == TypeKind.TypeParameter)
                        {
                            //ToString()'s on primitive types get replaced with Std.string
                            writer.Write("Std.string(");

                            if (memberReferenceExpressionOpt.Expression is ParenthesizedExpressionSyntax)
                            {
                                Core.Write(writer, memberReferenceExpressionOpt.Expression.As <ParenthesizedExpressionSyntax>().Expression);                                //eat a set of parenthesis, just to make the output code a bit nicer.
                            }
                            else
                            {
                                Core.Write(writer, memberReferenceExpressionOpt.Expression);
                            }

                            writer.Write(")");

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

                            return;                             //Skip parameters
                        }
                    }
                }

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

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

                writer.Write(methodName);
                writer.Write("(");
            }

            var prms = TranslateParameters(translateOpt, SortArguments(origMethodSymbol, invocationExpression.ArgumentList.Arguments, invocationExpression, extensionNamespace != null), invocationExpression).ToList();

            //If we invoke a method with type parameters that aren't used in the argument list, the haxe function won't have a way to see what args were used. To give it a way, add those as parameters at the end
            foreach (var typePrm in Utility.PassTypeArgsToMethod(origMethodSymbol))
            {
                var name = invocationExpression.Expression.ChildNodes().OfType <GenericNameSyntax>().ToList();
                if (name.Count == 0)
                {
                    name = invocationExpression.Expression.DescendantNodesAndSelf().OfType <GenericNameSyntax>().ToList(); //TODO: This will pick up false positives, we need a proper recursion function here
                }
                if (name.Count != 1)
                {
                    throw new Exception("Expected a single generic name, got " + string.Join(", ", name) + "  " + Utility.Descriptor(invocationExpression));
                }
                if (name.Count > 0 && name.Single().TypeArgumentList.Arguments.Count > 0)
                {
                    var typePrmIndex = origMethodSymbol.TypeParameters.IndexOf(origMethodSymbol.TypeParameters.Single(o => SymbolEqualityComparer.Default.Equals(o, typePrm)));
                    var genericVar   = name.Single().TypeArgumentList.Arguments.ElementAt(typePrmIndex);
                    if (genericVar.ToString() == typePrm.ToString())
                    {
                        writer.Write("t" + (typePrmIndex + 1));
                    }
                    else
                    {
                        prms.Add(new TransformedArgument(TypeProcessor.ConvertType(genericVar)));
                    }
                }
            }


            bool inParams = false;

            foreach (var arg in prms)
            {
                if (firstParameter)
                {
                    firstParameter = false;
                }
                else
                {
                    writer.Write(", ");
                }

                if (!inParams && IsParamsArgument(invocationExpression, arg.ArgumentOpt, origMethodSymbol) && TypeProcessor.ConvertType(model.GetTypeInfo(arg.ArgumentOpt.Expression).Type).StartsWith("Array<") == false)
                {
                    inParams = true;
                    writer.Write("[ ");
                }


                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 if (arg.ArgumentOpt != null)
                {
                    WriteForEachStatement.CheckWriteEnumerator(writer, arg.ArgumentOpt.Expression, false);
                }
                else
                {
                    arg.Write(writer);
                }
            }

            if (inParams)
            {
                writer.Write(" ]");
            }


            writer.Write(")");
        }
Exemplo n.º 5
0
        public static void Go(HaxeWriter writer, InvocationExpressionSyntax invocationExpression)
        {
            var model = Program.GetModel(invocationExpression);

            var symbolInfo       = model.GetSymbolInfo(invocationExpression);
            var expressionSymbol = model.GetSymbolInfo(invocationExpression.Expression);
            var methodSymbol     = symbolInfo.Symbol.OriginalDefinition.As <IMethodSymbol>().UnReduce();

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

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

            if (methodSymbol.ContainingType.Name == "Enum")
            {
                if (methodSymbol.Name == "Parse")
                {
                    WriteEnumParse(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":
                    var t = TypeProcessor.ConvertType(methodSymbol.ReturnType);

                    if (t == "Bool")
                    {
                        methodName         = "ParseBool";
                        extensionNamespace = "Cs2Hx";
                    }
                    else if (t == "Int" || t == "Float")
                    {
                        methodName         = "parse" + t;
                        extensionNamespace = "Std";
                    }
                    else
                    {
                        throw new Exception("Parse method on " + t + " is not supported.  " + Utility.Descriptor(memberReferenceExpressionOpt));
                    }

                    break;

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

                default:
                    methodName         = methodSymbol.Name;
                    extensionNamespace = "Cs2Hx";
                    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);                 //TODO: What if this is null?
                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;                 //TODO
            }
            //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;
            }

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

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

                writer.Write("(");

                if (subExpressionOpt != null)
                {
                    firstParameter = false;

                    if (methodSymbol.IsExtensionMethod)
                    {
                        WriteForEachStatement.CheckWriteEnumerator(writer, subExpressionOpt, true);
                    }
                    else
                    {
                        Core.Write(writer, subExpressionOpt);
                    }
                }
            }
            else
            {
                if (memberReferenceExpressionOpt != null)
                {
                    var memberType     = model.GetTypeInfo(memberReferenceExpressionOpt.Expression).Type;
                    var memberTypeHaxe = TypeProcessor.ConvertType(memberType);

                    //sort calls without any parameters need to get the default sort parameter
                    if (methodName == "sort" && invocationExpression.ArgumentList.Arguments.Count == 0)
                    {
                        Core.Write(writer, memberReferenceExpressionOpt.Expression);
                        writer.Write(".sort(");

                        switch (memberTypeHaxe)
                        {
                        case "Array<Int>":
                            writer.Write("Cs2Hx.SortInts");
                            break;

                        case "Array<Float>":
                            writer.Write("Cs2Hx.SortFloats");
                            break;

                        case "Array<String>":
                            writer.Write("Cs2Hx.SortStrings");
                            break;

                        default:
                            throw new Exception("Unknown default sort type: " + memberTypeHaxe + ".  " + Utility.Descriptor(invocationExpression));
                        }

                        writer.Write(")");
                        return;
                    }

                    //Check against lowercase toString since it gets replaced with the haxe name before we get here
                    if (methodName == "toString")
                    {
                        if (memberType.TypeKind == TypeKind.Enum)
                        {
                            //calling ToString() on an enum forwards to our enum's special ToString method
                            writer.Write(memberType.ContainingNamespace.FullNameWithDot().ToLower());
                            writer.Write(WriteType.TypeName((INamedTypeSymbol)memberType));
                            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_Char)
                        {
                            writer.Write("Cs2Hx.CharToString(");
                            Core.Write(writer, memberReferenceExpressionOpt.Expression);
                            writer.Write(")");

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

                            return;
                        }

                        if (memberTypeHaxe == "Int" || memberTypeHaxe == "Float" || memberTypeHaxe == "Bool" || memberType.TypeKind == TypeKind.TypeParameter)
                        {
                            //ToString()'s on primitive types get replaced with Std.string
                            writer.Write("Std.string(");

                            if (memberReferenceExpressionOpt.Expression is ParenthesizedExpressionSyntax)
                            {
                                Core.Write(writer, memberReferenceExpressionOpt.Expression.As <ParenthesizedExpressionSyntax>().Expression);                                //eat a set of parenthesis, just to make the output code a bit nicer.
                            }
                            else
                            {
                                Core.Write(writer, memberReferenceExpressionOpt.Expression);
                            }

                            writer.Write(")");

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

                            return;                             //Skip parameters
                        }
                    }
                }

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

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

                writer.Write(methodName);
                writer.Write("(");
            }

            bool inParams = false;

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

                if (!inParams && IsParamsArgument(invocationExpression, arg.ArgumentOpt, methodSymbol) && TypeProcessor.ConvertType(model.GetTypeInfo(arg.ArgumentOpt.Expression).Type).StartsWith("Array<") == false)
                {
                    inParams = true;
                    writer.Write("[ ");
                }


                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 if (arg.ArgumentOpt != null)
                {
                    WriteForEachStatement.CheckWriteEnumerator(writer, arg.ArgumentOpt.Expression, false);
                }
                else
                {
                    arg.Write(writer);
                }
            }

            if (inParams)
            {
                writer.Write(" ]");
            }


            writer.Write(")");
        }