λ1014: Throw expression in an eagerly evaluated fallback
A throw expression inside the fallback passed to GetOrElse, OrElse or Match throws before the method is even called.
Cause
An argument passed to the eager overload of GetOrElse, OrElse or the none parameter of Match on Option<T>,
Result<T> or Either<L, R> contains a throw expression, typically behind ?? or in a branch of ?:.
Reason for rule
Arguments are evaluated before the call. A fallback like fallback ?? throw new Exception() therefore throws whenever
fallback is null, no matter whether the option has a value and the fallback would even be needed. The code reads as
"throw if there is no value" but throws unconditionally.
Every fallback-taking method has a lazy overload that takes a function instead. The function is only invoked when the
fallback is needed, which is where the throw belongs.
How to fix violations
Pass a function instead of the value: prefix the argument with () => for Option<T>, or with _ => for Result<T>
and Either<L, R>, whose lazy overloads receive the exception or the left value. A code fix is available.
Examples
Disallowed
static string Example(Option<string> option, string? fallback)
=> option.GetOrElse(fallback ?? throw new InvalidOperationException("no fallback"));
static int Example(Result<int> result, bool condition)
=> result.GetOrElse(condition ? 42 : throw new InvalidOperationException());
Allowed
static string Example(Option<string> option, string? fallback)
=> option.GetOrElse(() => fallback ?? throw new InvalidOperationException("no fallback"));
static int Example(Result<int> result, bool condition)
=> result.GetOrElse(_ => condition ? 42 : throw new InvalidOperationException());
// The lazy overload re-throws the error of the result only when there is no value.
static int Example(Result<int> result)
=> result.GetOrElse(exception => throw exception);