Browse Source

Non-empty stack traces for operator exceptions.

Bart De Smet 5 years ago
parent
commit
1bb1fcfd0e

+ 8 - 1
Rx.NET/Source/src/System.Reactive/Linq/Observable/Aggregate.cs

@@ -62,7 +62,14 @@ namespace System.Reactive.Linq.ObservableImpl
             {
                 if (!_hasAccumulation)
                 {
-                    ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
                 else
                 {

+ 40 - 5
Rx.NET/Source/src/System.Reactive/Linq/Observable/Average.cs

@@ -54,7 +54,14 @@ namespace System.Reactive.Linq.ObservableImpl
                 }
                 else
                 {
-                    ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
             }
         }
@@ -110,7 +117,14 @@ namespace System.Reactive.Linq.ObservableImpl
                 }
                 else
                 {
-                    ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
             }
         }
@@ -166,7 +180,14 @@ namespace System.Reactive.Linq.ObservableImpl
                 }
                 else
                 {
-                    ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
             }
         }
@@ -222,7 +243,14 @@ namespace System.Reactive.Linq.ObservableImpl
                 }
                 else
                 {
-                    ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
             }
         }
@@ -278,7 +306,14 @@ namespace System.Reactive.Linq.ObservableImpl
                 }
                 else
                 {
-                    ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
             }
         }

+ 8 - 1
Rx.NET/Source/src/System.Reactive/Linq/Observable/ElementAt.cs

@@ -44,7 +44,14 @@ namespace System.Reactive.Linq.ObservableImpl
             {
                 if (_i >= 0)
                 {
-                    ForwardOnError(new ArgumentOutOfRangeException("index"));
+                    try
+                    {
+                        throw new ArgumentOutOfRangeException("index");
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
             }
         }

+ 16 - 2
Rx.NET/Source/src/System.Reactive/Linq/Observable/FirstAsync.cs

@@ -39,7 +39,14 @@ namespace System.Reactive.Linq.ObservableImpl
                 {
                     if (!_found)
                     {
-                        ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                        try
+                        {
+                            throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                        }
+                        catch (Exception e)
+                        {
+                            ForwardOnError(e);
+                        }
                     }
                 }
             }
@@ -97,7 +104,14 @@ namespace System.Reactive.Linq.ObservableImpl
                 {
                     if (!_found)
                     {
-                        ForwardOnError(new InvalidOperationException(Strings_Linq.NO_MATCHING_ELEMENTS));
+                        try
+                        {
+                            throw new InvalidOperationException(Strings_Linq.NO_MATCHING_ELEMENTS);
+                        }
+                        catch (Exception e)
+                        {
+                            ForwardOnError(e);
+                        }
                     }
                 }
             }

+ 16 - 2
Rx.NET/Source/src/System.Reactive/Linq/Observable/LastAsync.cs

@@ -46,7 +46,14 @@ namespace System.Reactive.Linq.ObservableImpl
                 {
                     if (!_seenValue)
                     {
-                        ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                        try
+                        {
+                            throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                        }
+                        catch (Exception e)
+                        {
+                            ForwardOnError(e);
+                        }
                     }
                     else
                     {
@@ -118,7 +125,14 @@ namespace System.Reactive.Linq.ObservableImpl
                 {
                     if (!_seenValue)
                     {
-                        ForwardOnError(new InvalidOperationException(Strings_Linq.NO_MATCHING_ELEMENTS));
+                        try
+                        {
+                            throw new InvalidOperationException(Strings_Linq.NO_MATCHING_ELEMENTS);
+                        }
+                        catch (Exception e)
+                        {
+                            ForwardOnError(e);
+                        }
                     }
                     else
                     {

+ 48 - 6
Rx.NET/Source/src/System.Reactive/Linq/Observable/Max.cs

@@ -73,7 +73,14 @@ namespace System.Reactive.Linq.ObservableImpl
             {
                 if (!_hasValue)
                 {
-                    ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
                 else
                 {
@@ -177,7 +184,14 @@ namespace System.Reactive.Linq.ObservableImpl
             {
                 if (!_hasValue)
                 {
-                    ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
                 else
                 {
@@ -231,7 +245,14 @@ namespace System.Reactive.Linq.ObservableImpl
             {
                 if (!_hasValue)
                 {
-                    ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
                 else
                 {
@@ -285,7 +306,14 @@ namespace System.Reactive.Linq.ObservableImpl
             {
                 if (!_hasValue)
                 {
-                    ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
                 else
                 {
@@ -339,7 +367,14 @@ namespace System.Reactive.Linq.ObservableImpl
             {
                 if (!_hasValue)
                 {
-                    ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
                 else
                 {
@@ -393,7 +428,14 @@ namespace System.Reactive.Linq.ObservableImpl
             {
                 if (!_hasValue)
                 {
-                    ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
                 else
                 {

+ 48 - 6
Rx.NET/Source/src/System.Reactive/Linq/Observable/Min.cs

@@ -78,7 +78,14 @@ namespace System.Reactive.Linq.ObservableImpl
             {
                 if (!_hasValue)
                 {
-                    ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
                 else
                 {
@@ -177,7 +184,14 @@ namespace System.Reactive.Linq.ObservableImpl
             {
                 if (!_hasValue)
                 {
-                    ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
                 else
                 {
@@ -231,7 +245,14 @@ namespace System.Reactive.Linq.ObservableImpl
             {
                 if (!_hasValue)
                 {
-                    ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
                 else
                 {
@@ -285,7 +306,14 @@ namespace System.Reactive.Linq.ObservableImpl
             {
                 if (!_hasValue)
                 {
-                    ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
                 else
                 {
@@ -339,7 +367,14 @@ namespace System.Reactive.Linq.ObservableImpl
             {
                 if (!_hasValue)
                 {
-                    ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
                 else
                 {
@@ -393,7 +428,14 @@ namespace System.Reactive.Linq.ObservableImpl
             {
                 if (!_hasValue)
                 {
-                    ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        ForwardOnError(e);
+                    }
                 }
                 else
                 {

+ 32 - 4
Rx.NET/Source/src/System.Reactive/Linq/Observable/SingleAsync.cs

@@ -33,7 +33,14 @@ namespace System.Reactive.Linq.ObservableImpl
                 {
                     if (_seenValue)
                     {
-                        ForwardOnError(new InvalidOperationException(Strings_Linq.MORE_THAN_ONE_ELEMENT));
+                        try
+                        {
+                            throw new InvalidOperationException(Strings_Linq.MORE_THAN_ONE_ELEMENT);
+                        }
+                        catch (Exception e)
+                        {
+                            ForwardOnError(e);
+                        }
                         return;
                     }
 
@@ -45,7 +52,14 @@ namespace System.Reactive.Linq.ObservableImpl
                 {
                     if (!_seenValue)
                     {
-                        ForwardOnError(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                        try
+                        {
+                            throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                        }
+                        catch (Exception e)
+                        {
+                            ForwardOnError(e);
+                        }
                     }
                     else
                     {
@@ -101,7 +115,14 @@ namespace System.Reactive.Linq.ObservableImpl
                     {
                         if (_seenValue)
                         {
-                            ForwardOnError(new InvalidOperationException(Strings_Linq.MORE_THAN_ONE_MATCHING_ELEMENT));
+                            try
+                            {
+                                throw new InvalidOperationException(Strings_Linq.MORE_THAN_ONE_MATCHING_ELEMENT);
+                            }
+                            catch (Exception e)
+                            {
+                                ForwardOnError(e);
+                            }
                             return;
                         }
 
@@ -114,7 +135,14 @@ namespace System.Reactive.Linq.ObservableImpl
                 {
                     if (!_seenValue)
                     {
-                        ForwardOnError(new InvalidOperationException(Strings_Linq.NO_MATCHING_ELEMENTS));
+                        try
+                        {
+                            throw new InvalidOperationException(Strings_Linq.NO_MATCHING_ELEMENTS);
+                        }
+                        catch (Exception e)
+                        {
+                            ForwardOnError(e);
+                        }
                     }
                     else
                     {

+ 16 - 2
Rx.NET/Source/src/System.Reactive/Linq/Observable/SingleOrDefaultAsync.cs

@@ -33,7 +33,14 @@ namespace System.Reactive.Linq.ObservableImpl
                 {
                     if (_seenValue)
                     {
-                        ForwardOnError(new InvalidOperationException(Strings_Linq.MORE_THAN_ONE_ELEMENT));
+                        try
+                        {
+                            throw new InvalidOperationException(Strings_Linq.MORE_THAN_ONE_ELEMENT);
+                        }
+                        catch (Exception e)
+                        {
+                            ForwardOnError(e);
+                        }
                         return;
                     }
 
@@ -94,7 +101,14 @@ namespace System.Reactive.Linq.ObservableImpl
                     {
                         if (_seenValue)
                         {
-                            ForwardOnError(new InvalidOperationException(Strings_Linq.MORE_THAN_ONE_MATCHING_ELEMENT));
+                            try
+                            {
+                                throw new InvalidOperationException(Strings_Linq.MORE_THAN_ONE_MATCHING_ELEMENT);
+                            }
+                            catch (Exception e)
+                            {
+                                ForwardOnError(e);
+                            }
                             return;
                         }
 

+ 7 - 1
Rx.NET/Source/src/System.Reactive/Linq/QueryLanguage.Blocking.cs

@@ -235,6 +235,7 @@ namespace System.Reactive.Linq
         {
             var value = default(TSource);
             var seenValue = false;
+            var moreThanOneElement = false;
             var ex = default(Exception);
 
             using (var evt = new WaitAndSetOnce())
@@ -247,7 +248,7 @@ namespace System.Reactive.Linq
                     {
                         if (seenValue)
                         {
-                            ex = new InvalidOperationException(Strings_Linq.MORE_THAN_ONE_ELEMENT);
+                            moreThanOneElement = true;
                             evt.Set();
                         }
 
@@ -270,6 +271,11 @@ namespace System.Reactive.Linq
 
             ex.ThrowIfNotNull();
 
+            if (moreThanOneElement)
+            {
+                throw new InvalidOperationException(Strings_Linq.MORE_THAN_ONE_ELEMENT);
+            }
+
             if (throwOnEmpty && !seenValue)
             {
                 throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);

+ 8 - 1
Rx.NET/Source/src/System.Reactive/Threading/Tasks/TaskObservableExtensions.cs

@@ -470,7 +470,14 @@ namespace System.Reactive.Threading.Tasks
                 }
                 else
                 {
-                    _tcs.TrySetException(new InvalidOperationException(Strings_Linq.NO_ELEMENTS));
+                    try
+                    {
+                        throw new InvalidOperationException(Strings_Linq.NO_ELEMENTS);
+                    }
+                    catch (Exception e)
+                    {
+                        _tcs.TrySetException(e);
+                    }
                 }
 
                 _ctr.Dispose(); // no null-check needed (struct)

+ 57 - 0
Rx.NET/Source/tests/Tests.System.Reactive/Tests/Linq/Observable/SingleAsyncTest.cs

@@ -4,6 +4,9 @@
 
 using System;
 using System.Reactive.Linq;
+using System.Reactive.Threading.Tasks;
+using System.Threading.Tasks;
+
 using Microsoft.Reactive.Testing;
 using ReactiveTests.Dummies;
 using Xunit;
@@ -253,5 +256,59 @@ namespace ReactiveTests.Tests
             );
         }
 
+        [Fact] // https://github.com/dotnet/reactive/issues/1235
+        public void MeaningfulStackTrace()
+        {
+            static async Task Core()
+            {
+                static void AssertException(Exception e)
+                {
+                    Assert.IsType(typeof(InvalidOperationException), e);
+
+                    Assert.NotNull(e.StackTrace);
+                    Assert.NotEqual("", e.StackTrace);
+
+                    Assert.True(e.StackTrace.Contains("SingleAsync"));
+                }
+
+                var xs = Observable.Range(0, 2).SingleAsync();
+
+                try
+                {
+                    await xs;
+                }
+                catch (Exception e)
+                {
+                    AssertException(e);
+                }
+
+                try
+                {
+                    await xs.ToTask();
+                }
+                catch (Exception e)
+                {
+                    AssertException(e);
+                }
+
+                var tcs = new TaskCompletionSource<bool>();
+
+                xs.Subscribe(
+                    _ => { },
+                    e => tcs.SetException(e),
+                    () => tcs.SetResult(false));
+
+                try
+                {
+                    await tcs.Task;
+                }
+                catch (Exception e)
+                {
+                    AssertException(e);
+                }
+            }
+
+            Core().GetAwaiter().GetResult();
+        }
     }
 }