public void ShouldIncludeAReturnKeywordForANewListInitStatement()
        {
            var exception       = Expression.Variable(typeof(Exception), "ex");
            var listConstructor = typeof(List <int>).GetConstructor(new[] { typeof(int) });
            var one             = Expression.Constant(1);
            // ReSharper disable once AssignNullToNotNullAttribute
            var newList               = Expression.New(listConstructor, one);
            var newListInit           = Expression.ListInit(newList, one);
            var rethrow               = Expression.Rethrow(newListInit.Type);
            var globalCatchAndRethrow = Expression.Catch(exception, rethrow);
            var tryCatch              = Expression.TryCatch(newListInit, globalCatchAndRethrow);

            var tryCatchBlock = Expression.Block(tryCatch);

            var translated = ToReadableString(tryCatchBlock);

            const string EXPECTED = @"
try
{
    return new List<int>(1) { 1 };
}
catch
{
    throw;
}";

            translated.ShouldBe(EXPECTED.TrimStart());
        }
        public void ShouldIncludeAReturnKeywordForANewArrayInitStatement()
        {
            var exception             = Expression.Variable(typeof(Exception), "ex");
            var zero                  = Expression.Constant(0, typeof(int));
            var newArray              = Expression.NewArrayInit(typeof(int), zero);
            var rethrow               = Expression.Rethrow(newArray.Type);
            var globalCatchAndRethrow = Expression.Catch(exception, rethrow);
            var tryCatch              = Expression.TryCatch(newArray, globalCatchAndRethrow);

            var tryCatchBlock = Expression.Block(tryCatch);

            var translated = ToReadableString(tryCatchBlock);

            const string EXPECTED = @"
try
{
    return new[] { 0 };
}
catch
{
    throw;
}";

            translated.ShouldBe(EXPECTED.TrimStart());
        }
        public void ShouldAssignTheResultOfATryCatch()
        {
            var intVariable = Expression.Variable(typeof(int), "i");

            var read = CreateLambda(() => Console.Read());

            var returnDefault = Expression.Catch(typeof(IOException), Expression.Default(typeof(int)));
            var readOrDefault = Expression.TryCatch(read.Body, returnDefault);

            var assignReadOrDefault = Expression.Assign(intVariable, readOrDefault);

            var translated = ToReadableString(assignReadOrDefault);

            const string EXPECTED = @"
i =
{
    try
    {
        return Console.Read();
    }
    catch (IOException)
    {
        return default(int);
    }
}";

            translated.ShouldBe(EXPECTED.TrimStart());
        }
        public void ShouldIncludeAReturnKeywordForAnObjectInitStatement()
        {
            var exception             = Expression.Variable(typeof(Exception), "ex");
            var newAddress            = Expression.New(typeof(Address).GetConstructors().First());
            var line1Property         = newAddress.Type.GetMember("Line1").First();
            var line1Value            = Expression.Constant("Over here");
            var line1Init             = Expression.Bind(line1Property, line1Value);
            var addressInit           = Expression.MemberInit(newAddress, line1Init);
            var rethrow               = Expression.Rethrow(newAddress.Type);
            var globalCatchAndRethrow = Expression.Catch(exception, rethrow);
            var tryCatch              = Expression.TryCatch(addressInit, globalCatchAndRethrow);

            var tryCatchBlock = Expression.Block(tryCatch);

            var translated = ToReadableString(tryCatchBlock);

            const string EXPECTED = @"
try
{
    return new WhenTranslatingBlocks.Address
    {
        Line1 = ""Over here""
    };
}
catch
{
    throw;
}";

            translated.ShouldBe(EXPECTED.TrimStart());
        }
        public void ShouldTranslateATryWithATopLevelCatchWithAWrappedExceptionThrow()
        {
            var exception = Expression.Variable(typeof(Exception), "ex");
            var writeBoom = CreateLambda(() => Console.Write("BOOM?"));

            var wrappedException = Expression.New(
                // ReSharper disable once AssignNullToNotNullAttribute
                typeof(InvalidOperationException).GetConstructor(new[] { typeof(string), typeof(Exception) }),
                Expression.Constant("Wrapped!"),
                exception);

            var throwWrapped = Expression.Throw(wrappedException);
            var globalCatch  = Expression.Catch(exception, throwWrapped);
            var tryCatch     = Expression.TryCatch(writeBoom.Body, globalCatch);

            var translated = ToReadableString(tryCatch);

            const string EXPECTED = @"
try
{
    Console.Write(""BOOM?"");
}
catch (Exception ex)
{
    throw new InvalidOperationException(""Wrapped!"", ex);
}";

            translated.ShouldBe(EXPECTED.TrimStart());
        }
        public void ShouldTranslateATryWithATopLevelCatchWithExceptionUseAndRethrow()
        {
            var writeException           = CreateLambda((Exception ex) => Console.Write(ex));
            var exception                = writeException.Parameters.First();
            var writeHello               = CreateLambda(() => Console.Write("Hello"));
            var rethrow                  = Expression.Throw(exception);
            var writeExceptionAndRethrow = Expression.Block(writeException.Body, rethrow);
            var globalCatch              = Expression.Catch(exception, writeExceptionAndRethrow);
            var tryCatch                 = Expression.TryCatch(writeHello.Body, globalCatch);

            var translated = ToReadableString(tryCatch);

            const string EXPECTED = @"
try
{
    Console.Write(""Hello"");
}
catch (Exception ex)
{
    Console.Write(ex);
    throw;
}";

            translated.ShouldBe(EXPECTED.TrimStart());
        }
        public void ShouldNotVarAssignAVariableAssignedInATryButUsedInACatch()
        {
            var exceptionFactory = CreateLambda((int number) => new Exception(number.ToString()));
            var intVariable      = exceptionFactory.Parameters.First();
            var newException     = exceptionFactory.Body;

            var assignment      = Expression.Assign(intVariable, Expression.Constant(10));
            var assignmentBlock = Expression.Block(assignment, Expression.Default(typeof(void)));

            var catchBlock    = Expression.Catch(typeof(Exception), Expression.Throw(newException));
            var tryCatch      = Expression.TryCatch(assignmentBlock, catchBlock);
            var tryCatchBlock = Expression.Block(new[] { intVariable }, tryCatch);

            var translated = ToReadableString(tryCatchBlock);

            const string EXPECTED = @"
int number;
try
{
    number = 10;
}
catch
{
    throw new Exception(number.ToString());
}";

            translated.ShouldBe(EXPECTED.TrimStart());
        }
        public void ShouldTranslateMultilineBlockSingleMethodArguments()
        {
            var intVariable          = Expression.Variable(typeof(int), "i");
            var variableInit         = Expression.Assign(intVariable, Expression.Constant(3));
            var variableMultiplyFive = Expression.Multiply(intVariable, Expression.Constant(5));
            var variableAdditionOne  = Expression.Assign(intVariable, variableMultiplyFive);
            var variableDivideThree  = Expression.Divide(intVariable, Expression.Constant(3));
            var variableAdditionTwo  = Expression.Assign(intVariable, variableDivideThree);

            var argumentBlock = Expression.Block(
                new[] { intVariable },
                variableInit,
                variableAdditionOne,
                variableAdditionTwo,
                intVariable);

            var catchBlock = Expression.Catch(
                typeof(Exception),
                Expression.Block(ReadableExpression.Comment("So what!"), Expression.Constant(0)));

            var tryCatch = Expression.TryCatch(argumentBlock, catchBlock);

            var collectionVariable = Expression.Variable(typeof(ICollection <int>), "ints");
            var addMethod          = collectionVariable.Type.GetPublicInstanceMethod("Add");
            var addMethodCall      = Expression.Call(collectionVariable, addMethod, tryCatch);

            const string EXPECTED = @"
ints.Add(
{
    try
    {
        var i = 3;
        i = i * 5;
        i = i / 3;

        return i;
    }
    catch
    {
        // So what!
        return 0;
    }
})";

            var translated = ToReadableString(addMethodCall);

            translated.ShouldBe(EXPECTED.TrimStart());
        }
        public void ShouldTranslateATryCatch()
        {
            var writeHello   = CreateLambda(() => Console.Write("Hello"));
            var exception    = Expression.Variable(typeof(TimeoutException), "timeoutEx");
            var timeoutCatch = Expression.Catch(exception, Expression.Empty());
            var tryCatch     = Expression.TryCatch(writeHello.Body, timeoutCatch);

            var translated = ToReadableString(tryCatch);

            const string EXPECTED = @"
try
{
    Console.Write(""Hello"");
}
catch (TimeoutException)
{
}";

            translated.ShouldBe(EXPECTED.TrimStart());
        }
        public void ShouldTranslateATryWithAFilteredCatch()
        {
            var writeHello   = CreateLambda(() => Console.Write("Hello"));
            var filter       = CreateLambda((TimeoutException timeoutEx) => timeoutEx.Data != null);
            var exception    = filter.Parameters.First();
            var timeoutCatch = Expression.Catch(exception, Expression.Empty(), filter.Body);
            var tryCatch     = Expression.TryCatch(writeHello.Body, timeoutCatch);

            var translated = ToReadableString(tryCatch);

            const string EXPECTED = @"
try
{
    Console.Write(""Hello"");
}
catch (TimeoutException timeoutEx) when (timeoutEx.Data != null)
{
}";

            translated.ShouldBe(EXPECTED.TrimStart());
        }
        public void ShouldTranslateMultilineConstructorParameters()
        {
            var consoleRead = CreateLambda(() => Console.Read());

            var catchAll   = Expression.Catch(typeof(Exception), Expression.Default(typeof(int)));
            var tryReadInt = Expression.TryCatch(consoleRead.Body, catchAll);

            var createStringBuilder = Expression.New(
                typeof(StringBuilder).GetPublicInstanceConstructor(typeof(int), typeof(int)),
                tryReadInt,
                tryReadInt);

            const string EXPECTED = @"
new StringBuilder(
    {
        try
        {
            return Console.Read();
        }
        catch
        {
            return default(int);
        }
    },
    {
        try
        {
            return Console.Read();
        }
        catch
        {
            return default(int);
        }
    })";

            var translated = ToReadableString(createStringBuilder);

            translated.ShouldBe(EXPECTED.TrimStart());
        }
        public void ShouldTranslateATryWithATopLevelCatchWithAnExplicitExceptionRethrow()
        {
            var exception             = Expression.Variable(typeof(Exception), "ex");
            var writeHello            = CreateLambda(() => Console.Write("Hello"));
            var rethrow               = Expression.Throw(exception);
            var globalCatchAndRethrow = Expression.Catch(exception, rethrow);
            var tryCatch              = Expression.TryCatch(writeHello.Body, globalCatchAndRethrow);

            var translated = ToReadableString(tryCatch);

            const string EXPECTED = @"
try
{
    Console.Write(""Hello"");
}
catch
{
    throw;
}";

            translated.ShouldBe(EXPECTED.TrimStart());
        }
        public void ShouldTranslateATryCatchFinally()
        {
            var writeHello             = CreateLambda(() => Console.Write("Hello"));
            var writeNotSupported      = CreateLambda((NotSupportedException ex) => Console.Write("NotSupported!"));
            var notSupportedCatchBlock = Expression.Catch(writeNotSupported.Parameters.First(), writeNotSupported.Body);
            var writeException         = CreateLambda((Exception ex) => Console.Write(ex));
            var topLevelCatchBlock     = Expression.Catch(writeException.Parameters.First(), writeException.Body);

            var writeFinished = CreateLambda(() => Console.WriteLine("Finished!"));
            var writeGoodbye  = CreateLambda(() => Console.Write("Goodbye"));
            var finallyBlock  = Expression.Block(writeFinished.Body, writeGoodbye.Body);

            var tryCatchFinally = Expression.TryCatchFinally(writeHello.Body, finallyBlock, notSupportedCatchBlock, topLevelCatchBlock);

            var translated = ToReadableString(tryCatchFinally);

            const string EXPECTED = @"
try
{
    Console.Write(""Hello"");
}
catch (NotSupportedException)
{
    Console.Write(""NotSupported!"");
}
catch (Exception ex)
{
    Console.Write(ex);
}
finally
{
    Console.WriteLine(""Finished!"");
    Console.Write(""Goodbye"");
}";

            translated.ShouldBe(EXPECTED.TrimStart());
        }
        public void ShouldIncludeAReturnKeywordForANewObjectStatement()
        {
            var exception             = Expression.Variable(typeof(Exception), "ex");
            var newList               = Expression.New(typeof(List <string>).GetConstructors().First());
            var rethrow               = Expression.Rethrow(newList.Type);
            var globalCatchAndRethrow = Expression.Catch(exception, rethrow);
            var tryCatch              = Expression.TryCatch(newList, globalCatchAndRethrow);

            var tryCatchBlock = Expression.Block(tryCatch);

            var translated = ToReadableString(tryCatchBlock);

            const string EXPECTED = @"
try
{
    return new List<string>();
}
catch
{
    throw;
}";

            translated.ShouldBe(EXPECTED.TrimStart());
        }
        public void ShouldVarAssignAVariableUsedInNestedConstructs()
        {
            var returnLabel    = Expression.Label(typeof(long), "Return");
            var streamVariable = Expression.Variable(typeof(Stream), "stream");

            var memoryStreamVariable   = Expression.Variable(typeof(MemoryStream), "memoryStream");
            var streamAsMemoryStream   = Expression.TypeAs(streamVariable, typeof(MemoryStream));
            var memoryStreamAssignment = Expression.Assign(memoryStreamVariable, streamAsMemoryStream);
            var nullMemoryStream       = Expression.Default(memoryStreamVariable.Type);
            var memoryStreamNotNull    = Expression.NotEqual(memoryStreamVariable, nullMemoryStream);
            var msLengthVariable       = Expression.Variable(typeof(long), "msLength");
            var memoryStreamLength     = Expression.Property(memoryStreamVariable, "Length");
            var msLengthAssignment     = Expression.Assign(msLengthVariable, memoryStreamLength);

            var msTryBlock = Expression.Block(new[] { msLengthVariable }, msLengthAssignment, msLengthVariable);
            var newNotSupportedException = Expression.New(typeof(NotSupportedException));
            var throwMsException         = Expression.Throw(newNotSupportedException, typeof(long));
            var msCatchBlock             = Expression.Catch(typeof(Exception), throwMsException);
            var memoryStreamTryCatch     = Expression.TryCatch(msTryBlock, msCatchBlock);
            var returnMemoryStreamResult = Expression.Return(returnLabel, memoryStreamTryCatch);
            var ifMemoryStreamTryCatch   = Expression.IfThen(memoryStreamNotNull, returnMemoryStreamResult);

            var fileStreamVariable   = Expression.Variable(typeof(FileStream), "fileStream");
            var streamAsFileStream   = Expression.TypeAs(streamVariable, typeof(FileStream));
            var fileStreamAssignment = Expression.Assign(fileStreamVariable, streamAsFileStream);
            var nullFileStream       = Expression.Default(fileStreamVariable.Type);
            var fileStreamNotNull    = Expression.NotEqual(fileStreamVariable, nullFileStream);
            var fsLengthVariable     = Expression.Variable(typeof(long), "fsLength");
            var fileStreamLength     = Expression.Property(fileStreamVariable, "Length");
            var fsLengthAssignment   = Expression.Assign(fsLengthVariable, fileStreamLength);

            var fsTryBlock             = Expression.Block(new[] { fsLengthVariable }, fsLengthAssignment, fsLengthVariable);
            var newIoException         = Expression.New(typeof(IOException));
            var throwIoException       = Expression.Throw(newIoException, typeof(long));
            var fsCatchBlock           = Expression.Catch(typeof(Exception), throwIoException);
            var fileStreamTryCatch     = Expression.TryCatch(fsTryBlock, fsCatchBlock);
            var returnFileStreamResult = Expression.Return(returnLabel, fileStreamTryCatch);
            var ifFileStreamTryCatch   = Expression.IfThen(fileStreamNotNull, returnFileStreamResult);

            var overallBlock = Expression.Block(
                new[] { memoryStreamVariable, fileStreamVariable },
                memoryStreamAssignment,
                ifMemoryStreamTryCatch,
                fileStreamAssignment,
                ifFileStreamTryCatch,
                Expression.Label(returnLabel, Expression.Constant(0L)));

            var overallCatchBlock = Expression.Catch(typeof(Exception), Expression.Constant(-1L));
            var overallTryCatch   = Expression.TryCatch(overallBlock, overallCatchBlock);

            const string EXPECTED = @"
try
{
    var memoryStream = stream as MemoryStream;

    if (memoryStream != null)
    {
        return 
        {
            try
            {
                var msLength = memoryStream.Length;

                return msLength;
            }
            catch
            {
                throw new NotSupportedException();
            }
        };
    }

    var fileStream = stream as FileStream;

    if (fileStream != null)
    {
        return 
        {
            try
            {
                var fsLength = fileStream.Length;

                return fsLength;
            }
            catch
            {
                throw new IOException();
            }
        };
    }

    return 0L;
}
catch
{
    return -1L;
}";

            var translated = ToReadableString(overallTryCatch);

            translated.ShouldBe(EXPECTED.TrimStart());
        }