Repository navigation
RangeSet.From accepts a null element: the set holds null, IsEmpty() is false and Contains throws #356
Description
Problem
RangeSet<DateRange, DateOnly>.From([null!]) returns a set with Count == 1 whose element is null. IsEmpty() is false, Equals(RangeSet<DateRange, DateOnly>.Empty) is false, and Contains(day) throws NullReferenceException. That breaks the RangeSet rule (sorted, disjoint, non-adjacent, no empties) on a construction path.
You reach it without calling From yourself. On a null range, Except(Empty), Except((,)), Except(p) and Union(Empty) all return such a set: Except wraps the receiver in From([range]) when it doesn't overlap the operand, and Union is From([range, other]). Measured 2026-10-24 on main; RangeExtensions.cs is identical on release/10.x, so 10.1.0 does the same.
Change
From (both overloads, and RangeSet.Create through them) throws ArgumentException for a null element, so no path can build a set holding null. ArgumentException, not ArgumentNullException, as the value sets do: ValueSetCore throws ArgumentException("Value sets cannot contain null elements.", nameof(values)) for a null element and keeps ArgumentNullException for a bare null argument. Under #350's rule (c), Except and Union on a null range throw before they reach From on 11.0.0. On 10.x this check is what turns them into a loud failure.
That is also the value sets' rule. ValueSetNullContractTests holds that every way into a set refuses a null, "never by dropping", because a dropped null can't be told from a duplicate. Dropping a null range the way From drops empty ranges is ruled out on both counts: by that rule, and by (c), since it would make null.Except(x) an empty set, a value for a null range.
silent: the set is built and answers IsEmpty() and Equals wrongly before anything throws. On 10.x it ships with #350's predicate port in 10.2.0, under ### Changed answers. There, one row turns from a value into a throw: null.Union((,)) returns {(,)} today, because From collapses to the infinite set before it looks at the null.
Done when
A test calls every public way to build a RangeSet (From over a span and over an IEnumerable, and the extensions that call it) with a null element and expects ArgumentException. The test fails with the check removed. Done in #370: RangeSetNullContractTests, registered as an oracle with the seed rangeset-stores-a-null-range.patch.
Activity
fix: a range set refuses a null range instead of storing it (#356)
Merge pull request #373 from CaffeinatedCoder/fix/357-null-predicates…