From 8e3a5c2324ee09cbc2e5edcb67a818657e52c48b Mon Sep 17 00:00:00 2001 From: Glenn Watson <5834289+glennawatson@users.noreply.github.com> Date: Sat, 25 Jul 2026 16:41:41 +1000 Subject: [PATCH 1/8] chore: update roslyn common analyzers --- .editorconfig | 271 ++++++++++++++++++++++++----------- src/Directory.Packages.props | 2 +- 2 files changed, 192 insertions(+), 81 deletions(-) diff --git a/.editorconfig b/.editorconfig index 6428c8c..730fb50 100644 --- a/.editorconfig +++ b/.editorconfig @@ -723,122 +723,60 @@ dotnet_diagnostic.SYSLIB1104.severity = error # Configuration binding source gen # Bug catchers — always error. dotnet_diagnostic.IDE0035.severity = none # Remove unreachable code — covered by SST1453 dotnet_diagnostic.IDE0043.severity = none # Format string contains invalid placeholder — covered by SST1454 -dotnet_diagnostic.IDE0051.severity = none # Remove unused private member — covered by SST1440 dotnet_diagnostic.IDE0052.severity = none # Remove unread private member — covered by SST1441 -dotnet_diagnostic.IDE0076.severity = none # Remove invalid global SuppressMessage attribute — covered by SST1457 -dotnet_diagnostic.IDE0077.severity = none # Avoid legacy format target in global SuppressMessage attribute — covered by SST1458 -dotnet_diagnostic.IDE0080.severity = none # Remove unnecessary suppression operator — covered by SST2209 # Simplification / cleanup. -dotnet_diagnostic.IDE0001.severity = none # Simplify name — covered by SST1116 dotnet_diagnostic.IDE0002.severity = none # Simplify member access — covered by SST1117 dotnet_diagnostic.IDE0004.severity = none # Remove unnecessary cast — covered by SST1175 -dotnet_diagnostic.IDE0005.severity = none # Covered by SST1445 (canonical) dotnet_diagnostic.IDE0016.severity = none # Use throw expression — covered by SST2207 -dotnet_diagnostic.IDE0017.severity = none # Use object initializers — covered by SST1193 -dotnet_diagnostic.IDE0018.severity = none # Inline variable declaration — covered by SST2208 -dotnet_diagnostic.IDE0019.severity = none # Use pattern matching to avoid 'as' followed by a 'null' check — covered by SST2005 -dotnet_diagnostic.IDE0020.severity = none # Use pattern matching to avoid 'is' check followed by a cast — covered by SST2007 +dotnet_diagnostic.IDE0019.severity = none # Use pattern matching to avoid 'as' followed by a 'null' check — covered by SST2005/SST2274 dotnet_diagnostic.IDE0028.severity = none # Use collection initializers — covered by SST1194 -dotnet_diagnostic.IDE0029.severity = none # Use coalesce expression — covered by SST1195 -dotnet_diagnostic.IDE0030.severity = none # Use coalesce expression (nullable types) — covered by SST1195/SST2227 dotnet_diagnostic.IDE0031.severity = none # Use null propagation — covered by SST1196 -dotnet_diagnostic.IDE0032.severity = none # Use auto property — covered by SST1420 -dotnet_diagnostic.IDE0033.severity = none # Use explicitly provided tuple name — covered by SST1142 -dotnet_diagnostic.IDE0034.severity = none # Simplify default expression — covered by SST1188 -dotnet_diagnostic.IDE0037.severity = none # Use inferred member name — covered by SST1173/SST2216 dotnet_diagnostic.IDE0038.severity = none # Use pattern matching ('is' check without a cast) — covered by SST2007 -dotnet_diagnostic.IDE0041.severity = none # Use 'is null' check — covered by SST1149/SST2231 +dotnet_diagnostic.IDE0041.severity = none # Use 'is null' check — covered by SST1149/SST2231/SST2282 dotnet_diagnostic.IDE0042.severity = none # Deconstruct variable declaration — covered by SST2214 dotnet_diagnostic.IDE0044.severity = none # Add readonly modifier — covered by SST1424 -dotnet_diagnostic.IDE0046.severity = none # Use conditional expression for return — covered by SST1197 dotnet_diagnostic.IDE0047.severity = none # Remove unnecessary parentheses — covered by SST1459 dotnet_diagnostic.IDE0049.severity = none # Use language keywords instead of framework type names for type references — covered by SST1121 -dotnet_diagnostic.IDE0054.severity = none # Use compound assignment — covered by SST1185 -dotnet_diagnostic.IDE0056.severity = none # Use index operator — covered by SST2203 dotnet_diagnostic.IDE0057.severity = none # Use range operator — covered by SST2204 dotnet_diagnostic.IDE0059.severity = none # Remove unnecessary value assignment — covered by SST2222 -dotnet_diagnostic.IDE0062.severity = none # Make local function static — covered by SST2235 -dotnet_diagnostic.IDE0063.severity = none # Use simple 'using' statement — covered by SST2236 dotnet_diagnostic.IDE0064.severity = none # Make struct fields writable — flag readonly-but-mutable struct fields where intent diverges from declaration dotnet_diagnostic.IDE0066.severity = none # Use switch expression — covered by SST2201 -dotnet_diagnostic.IDE0070.severity = none # Use 'System.HashCode.Combine' — covered by SST2217 -dotnet_diagnostic.IDE0071.severity = none # Simplify interpolation — covered by SST2220 -dotnet_diagnostic.IDE0072.severity = none # Add missing cases to switch expression — covered by SST2206 -dotnet_diagnostic.IDE0074.severity = none # Use compound assignment — covered by SST1185 dotnet_diagnostic.IDE0075.severity = none # Simplify conditional expression — covered by SST1182 dotnet_diagnostic.IDE0078.severity = none # Use pattern matching — covered by SST2006/SST2231 -dotnet_diagnostic.IDE0082.severity = none # Convert typeof to nameof — covered by SST1199 -dotnet_diagnostic.IDE0083.severity = none # Use pattern matching ('not' operator) — covered by SST2006 dotnet_diagnostic.IDE0084.severity = none # Use pattern matching ('IsNot' operator) -dotnet_diagnostic.IDE0090.severity = none # Simplify 'new' expression — covered by SST2202 -dotnet_diagnostic.IDE0100.severity = none # Remove unnecessary equality operator — covered by SST1143 -dotnet_diagnostic.IDE0110.severity = none # Remove unnecessary discard — covered by SST2213 dotnet_diagnostic.IDE0120.severity = none # Simplify LINQ expression — covered by PSH1100/PSH1101 -dotnet_diagnostic.IDE0150.severity = none # Prefer 'null' check over type check — covered by SST2231 -dotnet_diagnostic.IDE0161.severity = none # Use file-scoped namespace declaration — covered by SST2237 -dotnet_diagnostic.IDE0170.severity = none # Simplify property pattern — covered by SST2238 -dotnet_diagnostic.IDE0180.severity = none # Use tuple to swap values — covered by SST2215 -dotnet_diagnostic.IDE0200.severity = none # Remove unnecessary lambda expression — covered by SST2239 -dotnet_diagnostic.IDE0220.severity = none # Add explicit cast in foreach loop — covered by SST2225 dotnet_diagnostic.IDE0221.severity = none # Add explicit cast — covered by SST2226 -dotnet_diagnostic.IDE0240.severity = none # Remove redundant nullable directive — covered by SST2210 -dotnet_diagnostic.IDE0241.severity = none # Remove unnecessary nullable directive — covered by SST2211 dotnet_diagnostic.IDE0250.severity = none # Make struct 'readonly' — covered by PSH1014 -dotnet_diagnostic.IDE0251.severity = none # Make member 'readonly' — covered by SST1460 dotnet_diagnostic.IDE0260.severity = none # Use pattern matching — covered by SST2006/SST2231 -dotnet_diagnostic.IDE0270.severity = none # Use coalesce expression — covered by SST2227 # Disabled — conflict with an existing SA/CA rule or project convention. -dotnet_diagnostic.IDE0003.severity = none # Remove 'this' qualifier — we don't follow the this. prefix style (SA1101 = none) dotnet_diagnostic.IDE0007.severity = none # Use var — we don't force var in either direction dotnet_diagnostic.IDE0008.severity = none # Use explicit type — we don't force var in either direction dotnet_diagnostic.IDE0009.severity = none # Member access should be qualified — SA1101 = none -dotnet_diagnostic.IDE0010.severity = none # Add missing cases to switch statement — too noisy for default/fallthrough patterns -dotnet_diagnostic.IDE0011.severity = none # Add braces — RCS1001 / RCS1007 already handle this -dotnet_diagnostic.IDE0021.severity = none # Use expression body for constructors — preference not enforced -dotnet_diagnostic.IDE0022.severity = none # Use expression body for methods — preference not enforced -dotnet_diagnostic.IDE0023.severity = none # Use expression body for conversion operators — preference not enforced -dotnet_diagnostic.IDE0024.severity = none # Use expression body for operators — preference not enforced -dotnet_diagnostic.IDE0025.severity = none # Use expression body for properties — preference not enforced -dotnet_diagnostic.IDE0026.severity = none # Use expression body for indexers — preference not enforced -dotnet_diagnostic.IDE0027.severity = none # Use expression body for accessors — preference not enforced -dotnet_diagnostic.IDE0036.severity = none # Order modifiers — SA1206 handles -dotnet_diagnostic.IDE0039.severity = none # Use local function — covered by SST2228 -dotnet_diagnostic.IDE0040.severity = none # Add accessibility modifiers — SA1400 handles -dotnet_diagnostic.IDE0045.severity = none # Use conditional expression for assignment — covered by SST1198 +dotnet_diagnostic.IDE0021.severity = none # Use expression body for constructors — covered by SST2276 (opt-in) +dotnet_diagnostic.IDE0022.severity = none # Use expression body for methods — covered by SST2275 +dotnet_diagnostic.IDE0023.severity = none # Use expression body for conversion operators — covered by SST2278 (opt-in) +dotnet_diagnostic.IDE0024.severity = none # Use expression body for operators — covered by SST2277 (opt-in) +dotnet_diagnostic.IDE0025.severity = none # Use expression body for properties — covered by SST2279 +dotnet_diagnostic.IDE0026.severity = none # Use expression body for indexers — covered by SST2280 dotnet_diagnostic.IDE0048.severity = none # Add parentheses for clarity — preference not enforced -dotnet_diagnostic.IDE0053.severity = none # Use expression body for lambdas — preference not enforced dotnet_diagnostic.IDE0055.severity = none # Formatting — StyleCop SA rules own formatting dotnet_diagnostic.IDE0058.severity = none # Remove unnecessary expression value — covered by SST2221 dotnet_diagnostic.IDE0060.severity = none # Remove unused parameter — CA1801 handles (with api_surface config) -dotnet_diagnostic.IDE0061.severity = none # Use expression body for local function — preference not enforced -dotnet_diagnostic.IDE0065.severity = none # 'using' directive placement — SA1200 = none (we put usings outside file-scoped namespaces) -dotnet_diagnostic.IDE0073.severity = none # Require file header — SA1633 handles +dotnet_diagnostic.IDE0061.severity = none # Use expression body for local function — covered by SST2281 dotnet_diagnostic.IDE0079.severity = none # Remove unnecessary suppression — can misfire on multi-TFM suppressions dotnet_diagnostic.IDE0130.severity = none # Namespace does not match folder structure — we don't enforce strict mirror dotnet_diagnostic.IDE0160.severity = none # Use block-scoped namespace — we use file-scoped (IDE0161) dotnet_diagnostic.IDE0210.severity = none # Convert to top-level statements — we use Main style dotnet_diagnostic.IDE0211.severity = none # Convert to 'Program.Main' style — we use Main style -dotnet_diagnostic.IDE0280.severity = none # Use nameof — CA1507 / RCS1015 handle this -dotnet_diagnostic.IDE0290.severity = none # Use primary constructor — subjective, not enforced dotnet_diagnostic.IDE0300.severity = none # Use collection expression for array — covered by SST2101 -dotnet_diagnostic.IDE0301.severity = none # Use collection expression for empty — covered by SST2100 -dotnet_diagnostic.IDE0302.severity = none # Use collection expression for stackalloc — covered by SST2102 -dotnet_diagnostic.IDE0303.severity = none # Use collection expression for Create — covered by SST2103 -dotnet_diagnostic.IDE0304.severity = none # Use collection expression for builder — covered by SST2104 -dotnet_diagnostic.IDE0305.severity = none # Use collection expression for fluent — covered by SST2105 dotnet_diagnostic.IDE0306.severity = none # Use collection expression for new — covered by SST2101 dotnet_diagnostic.IDE0320.severity = none # Make anonymous function static — covered by PSH1000 dotnet_diagnostic.IDE0050.severity = none # Convert anonymous type to tuple — covered by SST2224 -dotnet_diagnostic.IDE0230.severity = none # Use UTF-8 string literal — covered by SST2212 dotnet_diagnostic.IDE0310.severity = none # Convert lambda expression to method group — handled by RCS1207 -dotnet_diagnostic.IDE0340.severity = none # Use unbound generic type — covered by SST2232 -dotnet_diagnostic.IDE0350.severity = none # Use implicitly typed lambda — covered by SST2218 dotnet_diagnostic.IDE0360.severity = none # Simplify property accessor — covered by SST2219 dotnet_diagnostic.IDE0370.severity = none # Remove unnecessary suppression (null-forgiving operator) — disabled for the same reason as RCS1249: multi-TFM nullability annotations can differ per platform -dotnet_diagnostic.IDE0380.severity = none # Remove unnecessary unsafe modifier — covered by SST1455 -dotnet_diagnostic.IDE1005.severity = none # Use conditional delegate call — covered by SST2240 ################### # Microsoft .NET Style Rules (IDE1xxx / IDE3xxx) - Naming & Miscellaneous @@ -1907,6 +1845,7 @@ dotnet_diagnostic.SES1402.severity = error # Do not load an assembly from raw by dotnet_diagnostic.SES1403.severity = error # JSON deserialization depth limit must stay within a safe ceiling dotnet_diagnostic.SES1404.severity = error # A type is instantiated by name from a non-constant Activator typeName dotnet_diagnostic.SES1405.severity = error # MessagePack typeless deserialization reconstructs whatever type the payload names +dotnet_diagnostic.SES1406.severity = warning # Reflection must not reach non-public members via BindingFlags.NonPublic (opt-in; replaces S3011) # Web hardening dotnet_diagnostic.SES1501.severity = error # A CORS policy must not allow credentials together with any origin @@ -1933,6 +1872,178 @@ dotnet_diagnostic.SES1604.severity = error # Prompt-template input encoding must dotnet_diagnostic.SES1605.severity = error # AI instrumentation must not enable sensitive-data capture dotnet_diagnostic.SES1606.severity = error # Do not fetch model weights over cleartext HTTP +# RoslynCommonAnalyzers rules added to reach 3.37.0 (on-by-default enforced; opt-in left off) +dotnet_diagnostic.PSH1022.severity = error # A parameterless `new EventArgs()` allocates where the shared `EventArgs.Empty` singleton would serve. Code fix uses the singleton. +dotnet_diagnostic.PSH1114.severity = none # Freeze static lookup collections that are never mutated. Opt-in. +dotnet_diagnostic.PSH1226.severity = error # A string's `ToCharArray()` result is only iterated, allocating a throwaway `char[]`; iterate the string directly. Code fix drops the copy. +dotnet_diagnostic.PSH1227.severity = error # A cheaper equivalent exists — `string.CompareOrdinal` over `Compare(…, Ordinal)`, `Debug.Fail` over `Debug.Assert(false, …)`. Info. Code fix rewrites the call. +dotnet_diagnostic.PSH1306.severity = none # Guard one-time execution with an interlocked latch. Opt-in. +dotnet_diagnostic.PSH1309.severity = none # Register cancellation callbacks without flowing the execution context. Opt-in. +dotnet_diagnostic.PSH1410.severity = none # Mark trivial forwarders for aggressive inlining. Opt-in. +dotnet_diagnostic.PSH1420.severity = error # A shareable client held in an instance field of an Azure Functions worker class is rebuilt on every invocation, leaking sockets and connections; share a static/singleton client or inject `IHttpClientFactory`. +dotnet_diagnostic.PSH1506.severity = error # The HTTP request or response body is read or written synchronously (`ReadToEnd`, `Body.Read`, `Body.Write`), which blocks a thread on Kestrel and buffers the whole payload; use the async overload. Code fix awaits it when the method is already async. +dotnet_diagnostic.PSH1600.severity = error # A delegate captured per iteration inside a component render loop reallocates on every render (measured ~128 B per row per render) and churns the diff; hoist it to a cached delegate or a precomputed per-item model. +dotnet_diagnostic.PSH1601.severity = error # A JavaScript-interop call is issued once per loop iteration; on Interactive Server each is a separate SignalR round-trip. Batch into a single call over the collection. +dotnet_diagnostic.PSH1602.severity = error # `StateHasChanged` is called unconditionally in `OnAfterRender`/`OnAfterRenderAsync`, scheduling another render every time — a runaway loop. Guard it with `firstRender` or a state flag. +dotnet_diagnostic.PSH1603.severity = error # A non-delegate allocation is used as a component-parameter value inside a render loop, allocating per item and forcing the child to re-render each pass. Sibling of PSH1600. +dotnet_diagnostic.SES1701.severity = error # Raw HTML is rendered from a non-constant value (`MarkupString`/`AddMarkupContent`), bypassing automatic encoding — an XSS risk. Sanitizer allow-list via `securitysharp.SES1701.sanitizers`. +dotnet_diagnostic.SES1702.severity = error # A JavaScript-interop call targets a script-evaluation primitive (`eval`, `Function`, `document.write`), turning interop into a script-injection channel. +dotnet_diagnostic.SES1703.severity = error # `[Authorize]` on a non-routable component enforces nothing — authorization runs as a routing concern. Exempt types via `securitysharp.SES1703.exempt_types`. +dotnet_diagnostic.SES1704.severity = error # `IHttpContextAccessor` or a cascading `HttpContext` is used in an interactively-rendered component, where it is null or frozen at circuit start. +dotnet_diagnostic.SES1705.severity = error # `NavigationManager.NavigateTo` is called with a target that is not a verified relative URL — an open-redirect risk. Validator allow-list via `securitysharp.SES1705.validators`. +dotnet_diagnostic.SES1706.severity = error # An uploaded file is read with an unbounded or client-chosen size limit, letting an attacker fill server memory. Threshold via `securitysharp.SES1706.max_bytes`. +dotnet_diagnostic.SES1707.severity = error # A secret-shaped literal appears in code reachable as WebAssembly, which downloads to the browser in full — guaranteed disclosure. +dotnet_diagnostic.SES1708.severity = error # `CircuitOptions.DetailedErrors` is enabled, shipping server exception detail to every connected client. +dotnet_diagnostic.SES1709.severity = error # `SerializeAllClaims` serializes every claim into client-readable WebAssembly authentication state, exposing internal ids, tokens, and PII. +dotnet_diagnostic.SES1710.severity = error # Antiforgery validation is disabled on a form (`[RequireAntiforgeryToken(required: false)]`), removing CSRF protection. +dotnet_diagnostic.SST1101.severity = none # see docs/rules/SST1101.md +dotnet_diagnostic.SST1220.severity = error # An all-named argument list is in a different order than the parameters. Code fix reorders it to declaration order. Info. +dotnet_diagnostic.SST1221.severity = error # `where` constraint clauses are not ordered to match the type-parameter list. Code fix reorders them. Info. +dotnet_diagnostic.SST1321.severity = error # A method whose name ends in `Async` returns nothing awaitable — the inverse of SST1317. Code fix (rename) drops the suffix. +dotnet_diagnostic.SST1434.severity = error # see docs/rules/SST1434.md +dotnet_diagnostic.SST1525.severity = error # A multi-statement `switch` section has no braces; the braces-on policy extends to switch sections. Code fix wraps it. +dotnet_diagnostic.SST1526.severity = none # A wrapped binary expression places the operator inconsistently. Configurable (`before`/`after`, default before). Opt-in. +dotnet_diagnostic.SST1527.severity = none # The `=>` of an expression-bodied member wraps inconsistently. Configurable. Opt-in. +dotnet_diagnostic.SST1528.severity = none # The `=` of a wrapped initializer wraps inconsistently. Configurable. Opt-in. +dotnet_diagnostic.SST1529.severity = none # A wrapped `?.`/`.` call chain places the break inconsistently. Configurable. Opt-in. +dotnet_diagnostic.SST1530.severity = none # A newline sits between a type declaration and its base list. Code fix pulls the base list onto the declaration line. Opt-in. +dotnet_diagnostic.SST1531.severity = none # A short object initializer is split across lines. Code fix collapses it when it fits. Opt-in. +dotnet_diagnostic.SST1532.severity = none # A file mixes line endings. Configurable (`lf`/`crlf`, default lf). Opt-in. +dotnet_diagnostic.SST1533.severity = none # A source file contains no code. Opt-in. +dotnet_diagnostic.SST1660.severity = error # The `` tags are not in parameter order. Code fix reorders them. Info. +dotnet_diagnostic.SST1661.severity = error # A snippet uses ``/`` mismatched to single- vs multi-line content. Code fix swaps the tag. Info. +dotnet_diagnostic.SST1662.severity = none # A thrown exception type has no `` documentation. Code fix adds the skeleton. Opt-in. +dotnet_diagnostic.SST1663.severity = none # A `//` comment before a public member reads like a summary; use `///`. Code fix converts it. Opt-in. +dotnet_diagnostic.SST1664.severity = none # A summary separates paragraphs with blank lines instead of ``. Code fix wraps them. Opt-in. +dotnet_diagnostic.SST1708.severity = error # An extension method never uses its `this` receiver, so it need not be an extension. +dotnet_diagnostic.SST1709.severity = none # A method in a `*Extensions` class whose first parameter lacks `this`. Code fix converts it to an extension block. Opt-in. +dotnet_diagnostic.SST1804.severity = error # A positional record has an empty `{ }` body where `;` would do. Code fix rewrites it. Info. +dotnet_diagnostic.SST1900.severity = error # see docs/rules/SST1900.md +dotnet_diagnostic.SST2229.severity = error # see docs/rules/SST2229.md +dotnet_diagnostic.SST2230.severity = error # see docs/rules/SST2230.md +dotnet_diagnostic.SST2233.severity = none # see docs/rules/SST2233.md +dotnet_diagnostic.SST2254.severity = none # A target-typed `new()` is written where an explicit type reads more clearly; the code fix restores `new TypeName(...)`. Opt-in — the counterpart to SST2202's target-typed direction, so a team enables at most one. +dotnet_diagnostic.SST2255.severity = error # A hand-written null-or-empty string test. Code fix uses `string.IsNullOrEmpty`. +dotnet_diagnostic.SST2256.severity = error # An extension method called in static form. Code fix rewrites to instance form. Info. +dotnet_diagnostic.SST2257.severity = error # A lambda block body that is a single `return`. Code fix uses an expression body. Info. +dotnet_diagnostic.SST2258.severity = error # A redundant explicit delegate wrapper (`new EventHandler(M)`). Code fix drops it. Info. +dotnet_diagnostic.SST2259.severity = error # A stray `;` after a type declaration. Code fix removes it. Info. +dotnet_diagnostic.SST2260.severity = error # An `as` cast to a type the operand already has. Code fix removes it. Info. +dotnet_diagnostic.SST2261.severity = error # `(x && !y) +dotnet_diagnostic.SST2262.severity = error # A raw string literal whose content needs no raw syntax. Code fix demotes it. Info. +dotnet_diagnostic.SST2263.severity = error # An infinite loop whose body re-derives its stop condition. Code fix hoists the condition into the header. Info. +dotnet_diagnostic.SST2264.severity = error # A numeric literal cast to an enum. Code fix names the member. +dotnet_diagnostic.SST2265.severity = none # Consecutive fluent calls on one receiver can fold into a chain. Opt-in. +dotnet_diagnostic.SST2266.severity = none # A local read exactly once can be inlined into that use. Opt-in. +dotnet_diagnostic.SST2267.severity = none # Infinite loops written in mixed `while(true)`/`for(;;)` styles. Configurable. Opt-in. +dotnet_diagnostic.SST2268.severity = none # Inconsistent `()` on object creation with an initializer. Configurable. Opt-in. +dotnet_diagnostic.SST2269.severity = none # Inconsistent parentheses around a conditional's condition. Configurable. Opt-in. +dotnet_diagnostic.SST2270.severity = none # Inconsistent explicit-vs-implicit array-creation type. Configurable. Opt-in. +dotnet_diagnostic.SST2271.severity = none # `var`-vs-explicit local type per the configured preference. Configurable. Opt-in. +dotnet_diagnostic.SST2272.severity = none # `[Flags]` member values written as mixed decimals and shifts. Configurable. Opt-in. +dotnet_diagnostic.SST2273.severity = none # A function or loop body wraps its work in a trailing `if` that could be an early-exit guard clause. Code fix inverts it. Configurable threshold. Opt-in. +dotnet_diagnostic.SST2274.severity = error # A value assigned with `as` and then null-checked is an `is` declaration pattern in one step. Code fix rewrites it. +dotnet_diagnostic.SST2275.severity = error # A method whose block body is a single statement can use an expression body `=> expr`. Code fix rewrites it. +dotnet_diagnostic.SST2276.severity = none # A constructor whose block body is a single statement can use an expression body. Code fix rewrites it. Opt-in. +dotnet_diagnostic.SST2277.severity = none # An operator whose block body is a single `return` can use an expression body. Code fix rewrites it. Opt-in. +dotnet_diagnostic.SST2278.severity = none # A conversion operator whose block body is a single `return` can use an expression body. Code fix rewrites it. Opt-in. +dotnet_diagnostic.SST2279.severity = error # A get-only property whose getter is a single `return` can use a whole-member expression body. Code fix rewrites it. +dotnet_diagnostic.SST2280.severity = error # A get-only indexer whose getter is a single `return` can use a whole-member expression body. Code fix rewrites it. +dotnet_diagnostic.SST2281.severity = error # A local function whose block body is a single statement can use an expression body. Code fix rewrites it. +dotnet_diagnostic.SST2282.severity = error # A reference-type `ReferenceEquals` check against `null` reads as an `is null` / `is not null` pattern. Code fix rewrites it. +dotnet_diagnostic.SST2283.severity = error # A null guard that throws right before assigning the guarded value can fold into the assignment as `?? throw`. Code fix rewrites it. +dotnet_diagnostic.SST2329.severity = error # A `[Flags]` enum declares no zero-valued member. Code fix adds `None = 0`. +dotnet_diagnostic.SST2330.severity = error # A `[Flags]` member is a numeric literal equal to a combination of others (`All = 7`). Code fix writes `A +dotnet_diagnostic.SST2331.severity = none # An enum leaves member values implicit, so their numbers depend on declaration order. Opt-in. +dotnet_diagnostic.SST2332.severity = error # An auto-property's `private set` is only written during construction; make it get-only. +dotnet_diagnostic.SST2333.severity = none # A generic comparison/equality contract is implemented without its non-generic counterpart. Opt-in. +dotnet_diagnostic.SST2334.severity = none # A publicly visible type has no `[DebuggerDisplay]`. Opt-in. +dotnet_diagnostic.SST2335.severity = none # Parts of a partial type disagree on the `static` modifier. Opt-in. +dotnet_diagnostic.SST2491.severity = error # A non-`async` method returns an awaitable from inside `using`/`try-finally`/`lock`, so the resource is torn down before the task completes. Code fix makes it `async`. +dotnet_diagnostic.SST2492.severity = error # A null-guard throws on a parameter the signature declares may be null. +dotnet_diagnostic.SST2493.severity = error # `== null`/`!= null` on an unconstrained generic `T`. Code fix uses `is null`/`is not null`. +dotnet_diagnostic.SST2494.severity = error # A `??` whose left operand is a constant null, so the right is always taken. Code fix folds it. +dotnet_diagnostic.SST2495.severity = error # A `[Flags]` combination includes an operand whose bits another already covers. Code fix removes it. +dotnet_diagnostic.SST2496.severity = error # An explicit `Dispose`/`Close` on a resource an enclosing `using` already disposes. Code fix removes it. Info. +dotnet_diagnostic.SST2700.severity = error # An MVC route template contains a backslash; route segments are separated by `/`, so the route is unreachable. Code fix replaces `\` with `/`. +dotnet_diagnostic.SST2701.severity = error # A `[JSInvokable]` method is not public, so JavaScript interop cannot call it. Code fix makes it public. +dotnet_diagnostic.SST2702.severity = error # A `[SupplyParameterFromQuery]` property has a type the framework cannot bind from the query string, which throws at runtime. +dotnet_diagnostic.SST2703.severity = error # A routable component's route constraint (`{id:int}`) disagrees with the matching `[Parameter]` CLR type, so the route silently fails to match. +dotnet_diagnostic.SST2704.severity = error # A public action on an `[ApiController]` declares no HTTP-verb attribute, so it answers every verb and can make routing ambiguous. +dotnet_diagnostic.SST2705.severity = none # A bound model member is a non-nullable value type with no required marker, so a request that omits it binds the default with no error. Opt-in. +dotnet_diagnostic.SST2706.severity = error # A Windows Forms entry point carries neither `[STAThread]` nor `[MTAThread]`; without STA, clipboard, drag-and-drop, and common dialogs misbehave. Code fix adds `[STAThread]`. +dotnet_diagnostic.SST2707.severity = none # A fire-and-forget `Task.Run` in a controller captures the request's `HttpContext`, which is disposed when the request ends, so the background work throws `ObjectDisposedException`. Opt-in. +dotnet_diagnostic.SST2708.severity = error # A component subscribes to an event in a lifecycle method but never unsubscribes, so the event source keeps the component alive — a per-session leak on a Server circuit. +dotnet_diagnostic.SST2709.severity = error # `StateHasChanged` is called while the component is being disposed, which the renderer no longer supports and throws. +dotnet_diagnostic.SST2710.severity = error # `StateHasChanged` is called directly from a timer callback, off the renderer's dispatcher; marshal it with `InvokeAsync(StateHasChanged)`. +dotnet_diagnostic.SST2711.severity = error # A synchronous component lifecycle method is overridden as `async void`, which the framework never awaits; override the `…Async` twin returning `Task`. Code fix rewrites the signature. +dotnet_diagnostic.SST2712.severity = error # An `[Inject]`/`[CascadingParameter]` property has no setter, so the framework's reflection-based binding leaves it null. Code fix adds a setter. +dotnet_diagnostic.SST2713.severity = error # A `DotNetObjectReference.Create(this)` is passed inline and never stored, so nothing can dispose it and it leaks on the JavaScript side. + +# In-box IDE code-style rules disabled where a StyleSharp rule covers the same shape (avoids double-reporting under EnforceCodeStyleInBuild) +dotnet_diagnostic.IDE0001.severity = none # covered by SST1116 +dotnet_diagnostic.IDE0003.severity = none # covered by SST1117 +dotnet_diagnostic.IDE0005.severity = none # covered by SST1445 +dotnet_diagnostic.IDE0010.severity = none # covered by SST2205 +dotnet_diagnostic.IDE0011.severity = none # covered by SST1503 +dotnet_diagnostic.IDE0017.severity = none # covered by SST1193 +dotnet_diagnostic.IDE0018.severity = none # covered by SST2208 +dotnet_diagnostic.IDE0020.severity = none # covered by SST2007 +dotnet_diagnostic.IDE0027.severity = none # covered by SST2219 +dotnet_diagnostic.IDE0029.severity = none # covered by SST1195 +dotnet_diagnostic.IDE0030.severity = none # covered by SST1195 +dotnet_diagnostic.IDE0032.severity = none # covered by SST1420 +dotnet_diagnostic.IDE0033.severity = none # covered by SST1142 +dotnet_diagnostic.IDE0034.severity = none # covered by SST1188 +dotnet_diagnostic.IDE0036.severity = none # covered by SST1206 +dotnet_diagnostic.IDE0037.severity = none # covered by SST2216 +dotnet_diagnostic.IDE0039.severity = none # covered by SST2228 +dotnet_diagnostic.IDE0040.severity = none # covered by SST1400 +dotnet_diagnostic.IDE0045.severity = none # covered by SST1198 +dotnet_diagnostic.IDE0046.severity = none # covered by SST1197 +dotnet_diagnostic.IDE0051.severity = none # covered by SST1440 +dotnet_diagnostic.IDE0053.severity = none # covered by SST2257 +dotnet_diagnostic.IDE0054.severity = none # covered by SST1185 +dotnet_diagnostic.IDE0056.severity = none # covered by SST2203 +dotnet_diagnostic.IDE0062.severity = none # covered by SST2235 +dotnet_diagnostic.IDE0063.severity = none # covered by SST2236 +dotnet_diagnostic.IDE0065.severity = none # covered by SST1200 +dotnet_diagnostic.IDE0070.severity = none # covered by SST2217 +dotnet_diagnostic.IDE0071.severity = none # covered by SST2220 +dotnet_diagnostic.IDE0072.severity = none # covered by SST2206 +dotnet_diagnostic.IDE0073.severity = none # covered by SST1633 +dotnet_diagnostic.IDE0074.severity = none # covered by SST2223 +dotnet_diagnostic.IDE0076.severity = none # covered by SST1457 +dotnet_diagnostic.IDE0077.severity = none # covered by SST1458 +dotnet_diagnostic.IDE0080.severity = none # covered by SST2209 +dotnet_diagnostic.IDE0082.severity = none # covered by SST1199 +dotnet_diagnostic.IDE0083.severity = none # covered by SST2006 +dotnet_diagnostic.IDE0090.severity = none # covered by SST2202 +dotnet_diagnostic.IDE0100.severity = none # covered by SST1143 +dotnet_diagnostic.IDE0110.severity = none # covered by SST2213 +dotnet_diagnostic.IDE0150.severity = none # covered by SST2231 +dotnet_diagnostic.IDE0161.severity = none # covered by SST2237 +dotnet_diagnostic.IDE0170.severity = none # covered by SST2238 +dotnet_diagnostic.IDE0180.severity = none # covered by SST2215 +dotnet_diagnostic.IDE0200.severity = none # covered by SST2239 +dotnet_diagnostic.IDE0220.severity = none # covered by SST2225 +dotnet_diagnostic.IDE0230.severity = none # covered by SST2212 +dotnet_diagnostic.IDE0240.severity = none # covered by SST2210 +dotnet_diagnostic.IDE0241.severity = none # covered by SST2211 +dotnet_diagnostic.IDE0251.severity = none # covered by SST1460 +dotnet_diagnostic.IDE0270.severity = none # covered by SST1195 +dotnet_diagnostic.IDE0280.severity = none # covered by SST1463 +dotnet_diagnostic.IDE0290.severity = none # covered by SST2241 +dotnet_diagnostic.IDE0301.severity = none # covered by SST2100 +dotnet_diagnostic.IDE0302.severity = none # covered by SST2102 +dotnet_diagnostic.IDE0303.severity = none # covered by SST2103 +dotnet_diagnostic.IDE0304.severity = none # covered by SST2104 +dotnet_diagnostic.IDE0305.severity = none # covered by SST2105 +dotnet_diagnostic.IDE0340.severity = none # covered by SST2232 +dotnet_diagnostic.IDE0350.severity = none # covered by SST2218 +dotnet_diagnostic.IDE0380.severity = none # covered by SST1455 +dotnet_diagnostic.IDE1005.severity = none # covered by SST2240 + ################### # PublicApiAnalyzers (RSxxxx) - public API surface tracking ################### @@ -2140,15 +2251,15 @@ dotnet_diagnostic.S3927.severity = none # Serialization event handlers should be dotnet_diagnostic.S3981.severity = none # Collection sizes and array length comparisons should make sense — covered by SST1479 dotnet_diagnostic.S3984.severity = none # Exceptions should not be created without being thrown — covered by SST1480 dotnet_diagnostic.S4143.severity = none # Collection elements should not be replaced unconditionally — covered by SST1487 -dotnet_diagnostic.S4210.severity = none # Windows Forms entry points should be marked with STAThread -> off: WinForms entry point; not applicable to this cross-platform library +dotnet_diagnostic.S4210.severity = none # Windows Forms entry points should be marked with STAThread -> replaced by SST2706 dotnet_diagnostic.S4260.severity = none # "ConstructorArgument" parameters should exist in constructors -> replaced by SST2487 dotnet_diagnostic.S4428.severity = none # "PartCreationPolicyAttribute" should be used with "ExportAttribute" — covered by SST2474 dotnet_diagnostic.S6507.severity = none # Blocks should not be synchronized on local variables — covered by SST1903 dotnet_diagnostic.S6677.severity = none # Message template placeholders should be unique — covered by SST2442 -dotnet_diagnostic.S6797.severity = none # Blazor query parameter type should be supported -> off: Blazor-specific; no Blazor surface in this library -dotnet_diagnostic.S6798.severity = none # [JSInvokable] attribute should only be used on public methods -> off: Blazor-specific; no Blazor surface in this library -dotnet_diagnostic.S6800.severity = none # Component parameter type should match the route parameter type constraint -> off: Blazor-specific; no Blazor surface in this library -dotnet_diagnostic.S6930.severity = none # Backslash should be avoided in route templates -> off: ASP.NET MVC-specific; no controllers in this library +dotnet_diagnostic.S6797.severity = none # Blazor query parameter type should be supported -> replaced by SST2702 +dotnet_diagnostic.S6798.severity = none # [JSInvokable] attribute should only be used on public methods -> replaced by SST2701 +dotnet_diagnostic.S6800.severity = none # Component parameter type should match the route parameter type constraint -> replaced by SST2703 +dotnet_diagnostic.S6930.severity = none # Backslash should be avoided in route templates -> replaced by SST2700 ################### # SonarAnalyzer (Sxxxx) - Minor Bug @@ -2335,7 +2446,7 @@ dotnet_diagnostic.S2925.severity = none # "Thread.Sleep" should not be used in t dotnet_diagnostic.S2933.severity = none # Fields that are only assigned in the constructor should be "readonly" - DUPLICATE IDE0044 dotnet_diagnostic.S2971.severity = none # LINQ expressions should be simplified — covered by PSH1101/PSH1102 dotnet_diagnostic.S3010.severity = none # Static fields should not be updated in constructors — covered by SST2402 -dotnet_diagnostic.S3011.severity = none # Reflection should not be used to increase accessibility of classes, methods, or fields -> off: reflection-audit hotspot; not enforced here +dotnet_diagnostic.S3011.severity = none # Reflection should not be used to increase accessibility of classes, methods, or fields -> replaced by SES1406 dotnet_diagnostic.S3059.severity = none # Types should not have members with visibility set higher than the type's visibility dotnet_diagnostic.S3063.severity = none # "StringBuilder" data should be used — covered by SST2408 dotnet_diagnostic.S3169.severity = none # Multiple "OrderBy" calls should not be used — covered by PSH1108 @@ -2400,7 +2511,7 @@ dotnet_diagnostic.S6566.severity = none # Use "DateTimeOffset" instead of "DateT dotnet_diagnostic.S6575.severity = none # Use "TimeZoneInfo.FindSystemTimeZoneById" without converting the timezones with "TimezoneConverter" -> replaced by PSH1419 dotnet_diagnostic.S6580.severity = none # Use a format provider when parsing date and time - DUPLICATE CA1305 dotnet_diagnostic.S6673.severity = none # Log message template placeholders should be in the right order — covered by SST2440 -dotnet_diagnostic.S6802.severity = none # Using lambda expressions in loops should be avoided in Blazor markup section -> off: Blazor-specific; no Blazor surface in this library +dotnet_diagnostic.S6802.severity = none # Using lambda expressions in loops should be avoided in Blazor markup section -> replaced by PSH1600 dotnet_diagnostic.S6803.severity = none # Parameters with SupplyParameterFromQuery attribute should be used only in routable components -> off: Blazor-specific; no Blazor surface in this library dotnet_diagnostic.S6931.severity = none # ASP.NET controller actions should not have a route template starting with "/" -> off: ASP.NET MVC-specific; no controllers in this library dotnet_diagnostic.S6932.severity = none # Use model binding instead of reading raw request data -> off: ASP.NET MVC-specific; no controllers in this library @@ -2408,8 +2519,8 @@ dotnet_diagnostic.S6934.severity = none # A Route attribute should be added to t dotnet_diagnostic.S6960.severity = none # Controllers should not have mixed responsibilities -> off: ASP.NET MVC-specific; no controllers in this library dotnet_diagnostic.S6961.severity = none # API Controllers should derive from ControllerBase instead of Controller -> off: ASP.NET MVC-specific; no controllers in this library dotnet_diagnostic.S6962.severity = none # You should pool HTTP connections with HttpClientFactory — covered by PSH1418 -dotnet_diagnostic.S6964.severity = none # Value type property used as input in a controller action should be nullable, required or annotated with the JsonRequiredAttribute to avoid under-posting. -> off: ASP.NET MVC-specific; no controllers in this library -dotnet_diagnostic.S6965.severity = none # REST API actions should be annotated with an HTTP verb attribute -> off: ASP.NET MVC-specific; no controllers in this library +dotnet_diagnostic.S6964.severity = none # Value type property used as input in a controller action should be nullable, required or annotated with the JsonRequiredAttribute to avoid under-posting. -> replaced by SST2705 (opt-in) +dotnet_diagnostic.S6965.severity = none # REST API actions should be annotated with an HTTP verb attribute -> replaced by SST2704 dotnet_diagnostic.S6966.severity = none # Awaitable method should be used — covered by PSH1313 dotnet_diagnostic.S6968.severity = none # Actions that return a value should be annotated with ProducesResponseTypeAttribute containing the return type -> off: ASP.NET MVC-specific; no controllers in this library dotnet_diagnostic.S881.severity = none # Increment (++) and decrement (--) operators should not be used in a method call or mixed with other operators in an expression — covered by SST2015 diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 1d3b7ba..873efdb 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -5,7 +5,7 @@ true - 3.33.0 + 3.38.1 From 8621cef209310b57f22fd04228849c46a92f847b Mon Sep 17 00:00:00 2001 From: Glenn Watson <5834289+glennawatson@users.noreply.github.com> Date: Sat, 25 Jul 2026 17:19:42 +1000 Subject: [PATCH 2/8] build: resolve analyzer upgrade fallout - Update compatible analyzer, MAUI, and test dependencies. - Fix new diagnostics while preserving established public API names. --- .editorconfig | 3 ++- src/Directory.Packages.props | 6 +++--- .../CancellationTokenPolyfillExtensions.cs | 2 +- .../Advanced/StartSubscription.cs | 3 ++- src/Primitives.Async.Shared/AsyncContext.cs | 10 ++++++++-- .../Advanced/FromAsyncExternalCancellation{T}.cs | 5 ++++- .../Advanced/FromAsyncSubscription{T}.cs | 3 ++- .../SignalOperatorMixins.StatefulSignals.cs | 11 ++++------- src/Primitives.Shared/SignalOperatorMixins.cs | 2 +- .../SignalOperatorParityMixins.cs | 2 +- .../Signals/SignalExtensions{FromTask}.cs | 15 +++++++++++---- .../Signals/Signal{Factories}.cs | 2 +- .../Signals/Signal{FromTask}.cs | 6 ++++-- .../Signals/Signal{GetAwaiter}.cs | 3 ++- src/Primitives.Shared/Signals/TaskSignal{T}.cs | 6 +++++- .../Advanced/BackgroundJobSignal{T}.cs | 3 ++- .../Advanced/FlatMapWitness{TSource,TResult}.cs | 4 +++- .../Advanced/IntervalSubscription.cs | 8 +++++--- .../Advanced/PooledDelaySource.cs | 3 ++- .../Advanced/StartSubscription{TResult}.cs | 3 ++- ...testEnumerableCoordinator{TSource,TResult}.cs | 2 +- .../Advanced/TakeUntilLifecycle.cs | 2 +- .../Advanced/TaskResultCompletionSource.cs | 3 ++- .../ConnectableSignalAsyncHelper.cs | 2 +- .../Operators/Expire.cs | 3 ++- .../Signals/CommandSignal{TResult}.cs | 12 +++++++++--- .../ConcurrencyLimiter.cs | 6 +++++- .../Operators/SelectLatestAsyncObservable.cs | 10 +++++++++- .../Concurrency/WasmScheduler.cs | 14 +++++++++++--- .../SynchronizationContextSequencer.cs | 5 ++++- .../Concurrency/TaskPoolSequencer.cs | 3 ++- .../Concurrency/ThreadPoolSequencer.cs | 6 ++++-- .../Concurrency/WasmSequencer.cs | 3 ++- ...sComparisonBenchmarks.Library.Supplemental.cs | 5 +---- .../TerminalCancellationBenchmarks.cs | 12 ++++++++---- .../CombiningOperatorTests.Multicast.cs | 7 ++----- .../CombiningOperatorTests.cs | 2 +- .../TakeUntilOperatorTests.CompletionDelegate.cs | 2 +- .../TakeUntilOperatorTests.cs | 2 +- .../TaskSignalSubscriptionTests.cs | 3 ++- .../TransformationOperatorTests.cs | 3 ++- .../MauiDispatcherSequencerTests.cs | 2 +- .../MauiDispatcherSequencerTests.cs | 2 +- .../AsyncSignalTests.cs | 2 +- .../CommandExecutionTests.cs | 4 ++-- .../ConcurencyTests.cs | 2 +- .../ConnectableSignalTests.cs | 6 +++--- .../ObservableMixinsTests.cs | 2 +- .../PrioritySemaphoreSignalTests.cs | 6 +++--- .../SafeWitnessTests.cs | 4 ++-- .../ScheduledSignalTests.cs | 4 ++-- .../SignalFactoriesTests.DefaultSequencer.cs | 5 +---- .../SignalFactoriesTests.Scheduling.cs | 10 ++-------- .../SignalFactoriesTests.cs | 2 +- .../SignalFromTaskTest.cs | 16 ++++++++-------- .../SignalOperatorMixinsTests.cs | 2 +- .../SignalRecoverTests.cs | 2 +- .../ReactiveUI.Primitives.Tests/SignalTests.cs | 4 ++-- .../SinkObserverTests.cs | 6 +++--- .../StateSignalTests.cs | 2 +- .../SyncLatestTests.cs | 2 +- .../TaskSignalTests.cs | 2 +- .../UseSignalTests.cs | 2 +- 63 files changed, 176 insertions(+), 120 deletions(-) diff --git a/.editorconfig b/.editorconfig index 730fb50..dae5bee 100644 --- a/.editorconfig +++ b/.editorconfig @@ -556,6 +556,7 @@ dotnet_diagnostic.CA5367.severity = error # Do not serialize types with pointer dotnet_diagnostic.S1075.severity = none # Hardcoded URI — canonical SourceLink hosts are the point dotnet_diagnostic.S2436.severity = none # Too many generic parameters — needed for the projector overload dotnet_diagnostic.S4036.severity = none # PATH-relative process spawn — benchmark only, trusted env +dotnet_diagnostic.S8969.severity = none # Nullability inference is inconsistent across the repository's target frameworks ################### # Microsoft .NET Runtime Obsoletions (SYSLIB0xxx) @@ -1899,7 +1900,7 @@ dotnet_diagnostic.SES1710.severity = error # Antiforgery validation is disabled dotnet_diagnostic.SST1101.severity = none # see docs/rules/SST1101.md dotnet_diagnostic.SST1220.severity = error # An all-named argument list is in a different order than the parameters. Code fix reorders it to declaration order. Info. dotnet_diagnostic.SST1221.severity = error # `where` constraint clauses are not ordered to match the type-parameter list. Code fix reorders them. Info. -dotnet_diagnostic.SST1321.severity = error # A method whose name ends in `Async` returns nothing awaitable — the inverse of SST1317. Code fix (rename) drops the suffix. +dotnet_diagnostic.SST1321.severity = none # Public APIs intentionally use Async to describe asynchronous observable behavior without returning an awaitable. dotnet_diagnostic.SST1434.severity = error # see docs/rules/SST1434.md dotnet_diagnostic.SST1525.severity = error # A multi-statement `switch` section has no braces; the braces-on policy extends to switch sections. Code fix wraps it. dotnet_diagnostic.SST1526.severity = none # A wrapped binary expression places the operator inconsistently. Configurable (`before`/`after`, default before). Opt-in. diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 873efdb..df0c0d4 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -18,7 +18,7 @@ - + @@ -45,7 +45,7 @@ - + @@ -65,7 +65,7 @@ - + diff --git a/src/Polyfills/CancellationTokenPolyfillExtensions.cs b/src/Polyfills/CancellationTokenPolyfillExtensions.cs index a198a31..225c333 100644 --- a/src/Polyfills/CancellationTokenPolyfillExtensions.cs +++ b/src/Polyfills/CancellationTokenPolyfillExtensions.cs @@ -31,7 +31,7 @@ internal CancellationTokenRegistration static boxed => { var (inner, innerState, innerToken) = - ((Action Callback, object? State, CancellationToken Token))boxed!; + ((Action Callback, object? State, CancellationToken Token))boxed; inner(innerState, innerToken); }, (callback, state, token), diff --git a/src/Primitives.Async.Shared/Advanced/StartSubscription.cs b/src/Primitives.Async.Shared/Advanced/StartSubscription.cs index 3c4c160..1ab2712 100644 --- a/src/Primitives.Async.Shared/Advanced/StartSubscription.cs +++ b/src/Primitives.Async.Shared/Advanced/StartSubscription.cs @@ -49,7 +49,8 @@ await Task.Factory.StartNew( static s => { var (self, observer, cancellationToken) = - ((StartSubscription, IObserverAsync, CancellationToken))s!; + ((StartSubscription, IObserverAsync, CancellationToken))( + s ?? throw new InvalidOperationException("The start state is missing.")); return self.ExecuteActionAsync(observer, cancellationToken).AsTask(); }, (this, observer, cancellationToken), diff --git a/src/Primitives.Async.Shared/AsyncContext.cs b/src/Primitives.Async.Shared/AsyncContext.cs index 284e45d..795f80f 100644 --- a/src/Primitives.Async.Shared/AsyncContext.cs +++ b/src/Primitives.Async.Shared/AsyncContext.cs @@ -169,7 +169,10 @@ public void OnCompleted(Action continuation) var sc = AsyncContext.SynchronizationContext; if (sc is not null) { - sc.Post(static c => ((Action)c!).Invoke(), continuation); + sc.Post( + static state => ((Action)( + state ?? throw new InvalidOperationException("The continuation is missing."))).Invoke(), + continuation); return; } @@ -196,7 +199,10 @@ public void OnCompleted(Action continuation) // path Yield takes by default, so the saving lands on the operator's hot path. if (ts is null || ts == TaskScheduler.Default) { - _ = ThreadPool.UnsafeQueueUserWorkItem(static c => ((Action)c!).Invoke(), continuation); + _ = ThreadPool.UnsafeQueueUserWorkItem( + static state => ((Action)( + state ?? throw new InvalidOperationException("The continuation is missing."))).Invoke(), + continuation); } } diff --git a/src/Primitives.Shared/Advanced/FromAsyncExternalCancellation{T}.cs b/src/Primitives.Shared/Advanced/FromAsyncExternalCancellation{T}.cs index 1c9c8f6..205172a 100644 --- a/src/Primitives.Shared/Advanced/FromAsyncExternalCancellation{T}.cs +++ b/src/Primitives.Shared/Advanced/FromAsyncExternalCancellation{T}.cs @@ -64,7 +64,10 @@ internal bool Start() } Registration = - CancellationToken.Register(static state => ((FromAsyncExternalCancellation)state!).Cancel(), this); + CancellationToken.Register( + static state => ((FromAsyncExternalCancellation)( + state ?? throw new InvalidOperationException("The cancellation state is missing."))).Cancel(), + this); return !Lifetime.IsCompleted; } diff --git a/src/Primitives.Shared/Advanced/FromAsyncSubscription{T}.cs b/src/Primitives.Shared/Advanced/FromAsyncSubscription{T}.cs index 1655c02..73ebf79 100644 --- a/src/Primitives.Shared/Advanced/FromAsyncSubscription{T}.cs +++ b/src/Primitives.Shared/Advanced/FromAsyncSubscription{T}.cs @@ -101,7 +101,8 @@ internal IDisposable Start() FromAsyncTaskObservation observation = new(Observer, Lifetime, ExternalCancellation, linkedSource); _ = task.ContinueWith( - static (completedTask, state) => ((FromAsyncTaskObservation)state!).Observe(completedTask), + static (completedTask, state) => ((FromAsyncTaskObservation)( + state ?? throw new InvalidOperationException("The observation state is missing."))).Observe(completedTask), observation, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, diff --git a/src/Primitives.Shared/SignalOperatorMixins.StatefulSignals.cs b/src/Primitives.Shared/SignalOperatorMixins.StatefulSignals.cs index bfd2629..dc3e76f 100644 --- a/src/Primitives.Shared/SignalOperatorMixins.StatefulSignals.cs +++ b/src/Primitives.Shared/SignalOperatorMixins.StatefulSignals.cs @@ -317,20 +317,17 @@ public IDisposable Subscribe(IObserver observer) /// Creates the duplicate-tracking set, pre-sized when the source has a known element count. /// The set used to track already-observed values. - private HashSet CreateSeen() - { + private HashSet CreateSeen() => #if NET8_0_OR_GREATER - var capacity = _source is RangeSignal range ? range.Count : 0; - return capacity switch + (_source is RangeSignal range ? range.Count : 0) switch { - > 0 => new(capacity, _comparer), + var capacity when capacity > 0 => new(capacity, _comparer), _ when _comparer is null => [], _ => new(_comparer), }; #else - return new(_comparer); + new(_comparer); #endif - } } /// Dedicated signal for Unique (adjacent distinct). diff --git a/src/Primitives.Shared/SignalOperatorMixins.cs b/src/Primitives.Shared/SignalOperatorMixins.cs index 0d462f4..22b7414 100644 --- a/src/Primitives.Shared/SignalOperatorMixins.cs +++ b/src/Primitives.Shared/SignalOperatorMixins.cs @@ -687,7 +687,7 @@ public IObservable ToSignal() } return task.IsFaulted - ? new ImmediateThrowSignal(task.Exception!.InnerException ?? task.Exception) + ? new ImmediateThrowSignal(task.Exception.InnerException ?? task.Exception) : new TaskInstanceSignal(task); } } diff --git a/src/Primitives.Shared/SignalOperatorParityMixins.cs b/src/Primitives.Shared/SignalOperatorParityMixins.cs index 890a0aa..90c9a41 100644 --- a/src/Primitives.Shared/SignalOperatorParityMixins.cs +++ b/src/Primitives.Shared/SignalOperatorParityMixins.cs @@ -788,7 +788,7 @@ public IObservable ToObservable() } return task.IsFaulted - ? new ImmediateThrowSignal(task.Exception!.InnerException ?? task.Exception) + ? new ImmediateThrowSignal(task.Exception.InnerException ?? task.Exception) : new TaskInstanceSignal(task); } diff --git a/src/Primitives.Shared/Signals/SignalExtensions{FromTask}.cs b/src/Primitives.Shared/Signals/SignalExtensions{FromTask}.cs index db9a5ee..0b6c9f3 100644 --- a/src/Primitives.Shared/Signals/SignalExtensions{FromTask}.cs +++ b/src/Primitives.Shared/Signals/SignalExtensions{FromTask}.cs @@ -102,10 +102,17 @@ public async Task HandleCancellation(Action? action) internal async Task<(TResult Value, bool IsCanceled)> WhenCancelled(CancellationToken cancellationToken) { TaskCompletionSource tcs = new(TaskCreationOptions.RunContinuationsAsynchronously); - var registration = cancellationToken.Register( - static state => ((TaskCompletionSource)state!).TrySetCanceled(), - tcs, - false); + var registration = cancellationToken.UnsafeRegister( + static (state, token) => + { + if (state is not TaskCompletionSource source) + { + throw new InvalidOperationException("The cancellation state is not a completion source."); + } + + _ = source.TrySetCanceled(token); + }, + tcs); var cancellationTask = tcs.Task; try diff --git a/src/Primitives.Shared/Signals/Signal{Factories}.cs b/src/Primitives.Shared/Signals/Signal{Factories}.cs index c59bfcc..2e28016 100644 --- a/src/Primitives.Shared/Signals/Signal{Factories}.cs +++ b/src/Primitives.Shared/Signals/Signal{Factories}.cs @@ -235,7 +235,7 @@ public static IObservable FromTask(Task task) } return task.IsFaulted - ? new ImmediateThrowSignal(task.Exception!.InnerException ?? task.Exception) + ? new ImmediateThrowSignal(task.Exception.InnerException ?? task.Exception) : new TaskInstanceSignal(task); } diff --git a/src/Primitives.Shared/Signals/Signal{FromTask}.cs b/src/Primitives.Shared/Signals/Signal{FromTask}.cs index 5709405..fe39479 100644 --- a/src/Primitives.Shared/Signals/Signal{FromTask}.cs +++ b/src/Primitives.Shared/Signals/Signal{FromTask}.cs @@ -183,7 +183,7 @@ private static bool TryEmitSynchronously( return false; } - observer.OnError(task.Exception!.InnerException ?? task.Exception); + observer.OnError(task.Exception.InnerException ?? task.Exception); return true; } @@ -288,7 +288,9 @@ public void GetOperationCanceled(IObserver observer) ArgumentExceptionHelper.ThrowIfNull(observer); _ = SourceCore.Token.Register( - static state => ((IObserver)state!).OnNext(new OperationCanceledException()), + static state => ((IObserver)( + state ?? throw new InvalidOperationException("The cancellation observer is missing."))).OnNext( + new OperationCanceledException()), observer, false); } diff --git a/src/Primitives.Shared/Signals/Signal{GetAwaiter}.cs b/src/Primitives.Shared/Signals/Signal{GetAwaiter}.cs index 6aa4113..acf703c 100644 --- a/src/Primitives.Shared/Signals/Signal{GetAwaiter}.cs +++ b/src/Primitives.Shared/Signals/Signal{GetAwaiter}.cs @@ -121,7 +121,8 @@ internal static void RegisterCancelation( var ctr = token.Register( static state => { - var (subscription, subject, token) = ((IDisposable, IAwaitSignal, CancellationToken))state!; + var (subscription, subject, token) = ((IDisposable, IAwaitSignal, CancellationToken))( + state ?? throw new InvalidOperationException("The await cancellation state is missing.")); subscription.Dispose(); _ = Cancel(subject, token); }, diff --git a/src/Primitives.Shared/Signals/TaskSignal{T}.cs b/src/Primitives.Shared/Signals/TaskSignal{T}.cs index 88070d4..cf5538a 100644 --- a/src/Primitives.Shared/Signals/TaskSignal{T}.cs +++ b/src/Primitives.Shared/Signals/TaskSignal{T}.cs @@ -56,7 +56,11 @@ private TaskSignal(ISequencer? sequencer, CancellationTokenSource? cancellationT /// The observer. public void GetOperationCanceled(IObserver observer) => CancellationTokenSource?.Token - .Register(static o => ((IObserver)o!).OnNext(new OperationCanceledException()), observer) + .Register( + static state => ((IObserver)( + state ?? throw new InvalidOperationException("The cancellation observer is missing."))).OnNext( + new OperationCanceledException()), + observer) .DisposeWith(_cleanUp); /// Subscribes the specified observer. diff --git a/src/ReactiveUI.Primitives.Async.Core/Advanced/BackgroundJobSignal{T}.cs b/src/ReactiveUI.Primitives.Async.Core/Advanced/BackgroundJobSignal{T}.cs index 9f00898..3fedca7 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Advanced/BackgroundJobSignal{T}.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Advanced/BackgroundJobSignal{T}.cs @@ -76,7 +76,8 @@ await Task.Factory.StartNew( static s => { var (self, observer, cancellationToken) = - ((BackgroundJobSignal, IObserverAsync, CancellationToken))s!; + ((BackgroundJobSignal, IObserverAsync, CancellationToken))( + s ?? throw new InvalidOperationException("The background-job state is missing.")); return self.Job(observer, cancellationToken).AsTask(); }, (this, observer, cancellationToken), diff --git a/src/ReactiveUI.Primitives.Async.Core/Advanced/FlatMapWitness{TSource,TResult}.cs b/src/ReactiveUI.Primitives.Async.Core/Advanced/FlatMapWitness{TSource,TResult}.cs index 6f4fcbf..2969d91 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Advanced/FlatMapWitness{TSource,TResult}.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Advanced/FlatMapWitness{TSource,TResult}.cs @@ -37,7 +37,9 @@ protected override async ValueTask OnNextAsyncCore(TSource value, CancellationTo { var inner = SyncSelector is not null ? SyncSelector(value) - : await AsyncSelector!(value, cancellationToken).ConfigureAwait(false); + : await (AsyncSelector ?? throw new InvalidOperationException("The flat-map selector is missing."))( + value, + cancellationToken).ConfigureAwait(false); await Coordinator.SubscribeInnerAsync(inner).ConfigureAwait(false); } diff --git a/src/ReactiveUI.Primitives.Async.Core/Advanced/IntervalSubscription.cs b/src/ReactiveUI.Primitives.Async.Core/Advanced/IntervalSubscription.cs index e514439..312ed43 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Advanced/IntervalSubscription.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Advanced/IntervalSubscription.cs @@ -54,7 +54,8 @@ private async ValueTask DelayWithProviderAsync(CancellationToken cancellationTok { TaskCompletionSource tcs = new(TaskCreationOptions.RunContinuationsAsynchronously); await using var tp = TimeProvider!.CreateTimer( - static x => ((TaskCompletionSource)x!).TrySetResult(true), + static state => ((TaskCompletionSource)( + state ?? throw new InvalidOperationException("The interval state is missing."))).TrySetResult(true), tcs, Period, Timeout.InfiniteTimeSpan); @@ -64,7 +65,8 @@ private async ValueTask DelayWithProviderAsync(CancellationToken cancellationTok cancellationToken.Register( static x => { - var (tcs, ct) = ((TaskCompletionSource, CancellationToken))x!; + var (tcs, ct) = ((TaskCompletionSource, CancellationToken))( + x ?? throw new InvalidOperationException("The interval cancellation state is missing.")); _ = tcs.TrySetCanceled(ct); }, (tcs, cancellationToken)); @@ -73,7 +75,7 @@ private async ValueTask DelayWithProviderAsync(CancellationToken cancellationTok cancellationToken.Register( static x => { - var (tcs, ct) = ((TaskCompletionSource, CancellationToken))x!; + var (tcs, ct) = ((TaskCompletionSource, CancellationToken))x; _ = tcs.TrySetCanceled(ct); }, (tcs, cancellationToken)); diff --git a/src/ReactiveUI.Primitives.Async.Core/Advanced/PooledDelaySource.cs b/src/ReactiveUI.Primitives.Async.Core/Advanced/PooledDelaySource.cs index 9c2f2f3..93000ca 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Advanced/PooledDelaySource.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Advanced/PooledDelaySource.cs @@ -95,7 +95,8 @@ public ValueTask BeginAsync(TimeSpan delay, TimeProvider timeProvider, Cancellat // some test / benchmark providers); in that case _completed flips to Claimed before this // call returns. _timer = timeProvider.CreateTimer( - static state => ((PooledDelaySource)state!).OnTimerFired(), + static state => ((PooledDelaySource)( + state ?? throw new InvalidOperationException("The delay state is missing."))).OnTimerFired(), this, delay, Timeout.InfiniteTimeSpan); diff --git a/src/ReactiveUI.Primitives.Async.Core/Advanced/StartSubscription{TResult}.cs b/src/ReactiveUI.Primitives.Async.Core/Advanced/StartSubscription{TResult}.cs index c4b0729..96dd8b8 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Advanced/StartSubscription{TResult}.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Advanced/StartSubscription{TResult}.cs @@ -46,7 +46,8 @@ await Task.Factory.StartNew( static s => { var (self, observer, cancellationToken) = - ((StartSubscription, IObserverAsync, CancellationToken))s!; + ((StartSubscription, IObserverAsync, CancellationToken))( + s ?? throw new InvalidOperationException("The start state is missing.")); return self.ExecuteFunctionAsync(observer, cancellationToken).AsTask(); }, (this, observer, cancellationToken), diff --git a/src/ReactiveUI.Primitives.Async.Core/Advanced/SyncLatestEnumerableCoordinator{TSource,TResult}.cs b/src/ReactiveUI.Primitives.Async.Core/Advanced/SyncLatestEnumerableCoordinator{TSource,TResult}.cs index 93d3c98..249462c 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Advanced/SyncLatestEnumerableCoordinator{TSource,TResult}.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Advanced/SyncLatestEnumerableCoordinator{TSource,TResult}.cs @@ -121,7 +121,7 @@ internal async ValueTask OnNextAsync(int index, TSource indexValue, Cancellation for (var i = 0; i < Values.Length; i++) { - SnapshotBuffer[i] = Values[i].Value!; + SnapshotBuffer[i] = Values[i].Value; } TResult projected; diff --git a/src/ReactiveUI.Primitives.Async.Core/Advanced/TakeUntilLifecycle.cs b/src/ReactiveUI.Primitives.Async.Core/Advanced/TakeUntilLifecycle.cs index 4d5a119..eafe9cf 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Advanced/TakeUntilLifecycle.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Advanced/TakeUntilLifecycle.cs @@ -89,7 +89,7 @@ public async ValueTask RelayErrorAsync(Exception error) /// A ValueTask representing the asynchronous forward. public async ValueTask RelayCompletionAsync(Result result) { - using (await _gate.EnterAsync().ConfigureAwait(false)) + using (await _gate.EnterAsync(CancellationToken.None).ConfigureAwait(false)) { await _observer.OnCompletedAsync(result).ConfigureAwait(false); } diff --git a/src/ReactiveUI.Primitives.Async.Core/Advanced/TaskResultCompletionSource.cs b/src/ReactiveUI.Primitives.Async.Core/Advanced/TaskResultCompletionSource.cs index ba0cfa3..e716e16 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Advanced/TaskResultCompletionSource.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Advanced/TaskResultCompletionSource.cs @@ -76,7 +76,8 @@ private CancellationTokenRegistration RegisterCancellation() => _cancellationToken.Register( static state => { - var source = (TaskResultCompletionSource)state!; + var source = (TaskResultCompletionSource)( + state ?? throw new InvalidOperationException("The task result state is missing.")); _ = source._taskSource.TrySetException(new OperationCanceledException(source._cancellationToken)); }, this); diff --git a/src/ReactiveUI.Primitives.Async.Core/ConnectableSignalAsyncHelper.cs b/src/ReactiveUI.Primitives.Async.Core/ConnectableSignalAsyncHelper.cs index 0ec0f8b..5a38e6c 100644 --- a/src/ReactiveUI.Primitives.Async.Core/ConnectableSignalAsyncHelper.cs +++ b/src/ReactiveUI.Primitives.Async.Core/ConnectableSignalAsyncHelper.cs @@ -95,7 +95,7 @@ internal static void Dispose(ConnectableSignalAsyncState state) } state.DisposedCts.Cancel(); - state.Connection?.DisposeAsync().AsTask().Wait(); + state.Connection?.DisposeAsync().AsTask().Wait(CancellationToken.None); state.Dispose(); } diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/Expire.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/Expire.cs index 542823f..f7b6340 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/Expire.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/Expire.cs @@ -89,7 +89,8 @@ internal void StartTimer(CancellationToken cancellationToken) try { _timer = timeProvider.CreateTimer( - static state => ((TimeoutWitness)state!).OnTimerFired(), + static state => ((TimeoutWitness)( + state ?? throw new InvalidOperationException("The timeout state is missing."))).OnTimerFired(), this, dueTime, System.Threading.Timeout.InfiniteTimeSpan); diff --git a/src/ReactiveUI.Primitives.Core/Signals/CommandSignal{TResult}.cs b/src/ReactiveUI.Primitives.Core/Signals/CommandSignal{TResult}.cs index dd980e6..fb1c03b 100644 --- a/src/ReactiveUI.Primitives.Core/Signals/CommandSignal{TResult}.cs +++ b/src/ReactiveUI.Primitives.Core/Signals/CommandSignal{TResult}.cs @@ -157,7 +157,10 @@ public CommandExecution ExecuteAsync(CancellationToken cancellationToke return _executeSync is not null ? new(ExecuteSync(cancellationToken)) - : new CommandExecution(ExecuteAsyncCore(cancellationToken)); + : new CommandExecution( + ExecuteAsyncCore( + _executeAsync ?? throw new InvalidOperationException("The command has no execution delegate."), + cancellationToken)); } catch (Exception error) { @@ -269,14 +272,17 @@ private TResult ExecuteSync(CancellationToken cancellationToken) } /// Executes the asynchronous command path. + /// The constructor-selected delegate, guaranteed non-null for this path. /// Cancellation token for the operation. /// The command result. - private async Task ExecuteAsyncCore(CancellationToken cancellationToken) + private async Task ExecuteAsyncCore( + Func> executeAsync, + CancellationToken cancellationToken) { BeginExecution(); try { - var result = await _executeAsync!(cancellationToken).ConfigureAwait(false); + var result = await executeAsync(cancellationToken).ConfigureAwait(false); PublishResult(result); return result; } diff --git a/src/ReactiveUI.Primitives.Extensions.Core/ConcurrencyLimiter.cs b/src/ReactiveUI.Primitives.Extensions.Core/ConcurrencyLimiter.cs index dd12056..881f6ff 100644 --- a/src/ReactiveUI.Primitives.Extensions.Core/ConcurrencyLimiter.cs +++ b/src/ReactiveUI.Primitives.Extensions.Core/ConcurrencyLimiter.cs @@ -155,7 +155,11 @@ private void PullNextTask(Subscription subscription) _rator.Current?.ContinueWith( static (ant, state) => { - var sub = (Subscription)state!; + if (state is not Subscription sub) + { + throw new InvalidOperationException("The continuation state is not a subscription."); + } + sub.Limiter.ProcessTaskCompletion(sub, ant); }, subscription, diff --git a/src/ReactiveUI.Primitives.Extensions.Core/Operators/SelectLatestAsyncObservable.cs b/src/ReactiveUI.Primitives.Extensions.Core/Operators/SelectLatestAsyncObservable.cs index 7385798..6ddf11d 100644 --- a/src/ReactiveUI.Primitives.Extensions.Core/Operators/SelectLatestAsyncObservable.cs +++ b/src/ReactiveUI.Primitives.Extensions.Core/Operators/SelectLatestAsyncObservable.cs @@ -111,7 +111,15 @@ public void OnCompleted() } _ = toAwait.ContinueWith( - static (_, s) => ((SelectLatestAsyncSink)s!).SignalCompleted(), + static (_, state) => + { + if (state is not SelectLatestAsyncSink sink) + { + throw new InvalidOperationException("The continuation state is not a select-latest sink."); + } + + sink.SignalCompleted(); + }, this, TaskScheduler.Default); } diff --git a/src/ReactiveUI.Primitives.Reactive/Concurrency/WasmScheduler.cs b/src/ReactiveUI.Primitives.Reactive/Concurrency/WasmScheduler.cs index 6fa9d2d..6d63e83 100644 --- a/src/ReactiveUI.Primitives.Reactive/Concurrency/WasmScheduler.cs +++ b/src/ReactiveUI.Primitives.Reactive/Concurrency/WasmScheduler.cs @@ -63,7 +63,8 @@ public sealed class WasmScheduler : LocalScheduler, ISchedulerPeriodic, IDisposa "The drain timer is created disarmed, so nothing can call back into it until Schedule arms it after construction.")] internal WasmScheduler() => _drainTimer = new( - static state => ((WasmScheduler)state!).RunDrain(), + static state => ((WasmScheduler)( + state ?? throw new InvalidOperationException("The scheduler state is missing."))).RunDrain(), this, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); @@ -124,7 +125,13 @@ public override IDisposable Schedule( // The timer roots itself while armed through the callback's target (the work item), which stores the // timer; the item's Dispose cancels and releases it. - item.AttachTimer(new(static s => ((IReadyWorkItem)s!).Run(), item, dt, Timeout.InfiniteTimeSpan)); + item.AttachTimer( + new( + static state => ((IReadyWorkItem)( + state ?? throw new InvalidOperationException("The work-item state is missing."))).Run(), + item, + dt, + Timeout.InfiniteTimeSpan)); return item; } @@ -417,7 +424,8 @@ internal static PeriodicWorkItem Start(TState state, TimeSpan period, Fu { PeriodicWorkItem item = new(state, action); Timer timer = new( - static s => ((PeriodicWorkItem)s!).Tick(), + static state => ((PeriodicWorkItem)( + state ?? throw new InvalidOperationException("The periodic state is missing."))).Tick(), item, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); diff --git a/src/ReactiveUI.Primitives/Concurrency/SynchronizationContextSequencer.cs b/src/ReactiveUI.Primitives/Concurrency/SynchronizationContextSequencer.cs index 85da073..a4cc66e 100644 --- a/src/ReactiveUI.Primitives/Concurrency/SynchronizationContextSequencer.cs +++ b/src/ReactiveUI.Primitives/Concurrency/SynchronizationContextSequencer.cs @@ -40,7 +40,10 @@ public void Schedule(IWorkItem item) { ArgumentExceptionHelper.ThrowIfNull(item); - Context.Post(static state => ExecutePosted((IWorkItem)state!), item); + Context.Post( + static state => ExecutePosted((IWorkItem)( + state ?? throw new InvalidOperationException("The posted work item is missing."))), + item); } /// diff --git a/src/ReactiveUI.Primitives/Concurrency/TaskPoolSequencer.cs b/src/ReactiveUI.Primitives/Concurrency/TaskPoolSequencer.cs index 1d928ef..770bbed 100644 --- a/src/ReactiveUI.Primitives/Concurrency/TaskPoolSequencer.cs +++ b/src/ReactiveUI.Primitives/Concurrency/TaskPoolSequencer.cs @@ -50,7 +50,8 @@ public void Schedule(IWorkItem item) ArgumentExceptionHelper.ThrowIfNull(item); _ = _taskFactory.StartNew( - static state => ((DispatchState)state!).Run(), + static state => ((DispatchState)( + state ?? throw new InvalidOperationException("The dispatch state is missing."))).Run(), new DispatchState(this, item), _taskFactory.CancellationToken, _taskFactory.CreationOptions, diff --git a/src/ReactiveUI.Primitives/Concurrency/ThreadPoolSequencer.cs b/src/ReactiveUI.Primitives/Concurrency/ThreadPoolSequencer.cs index af37f14..d5c8b27 100644 --- a/src/ReactiveUI.Primitives/Concurrency/ThreadPoolSequencer.cs +++ b/src/ReactiveUI.Primitives/Concurrency/ThreadPoolSequencer.cs @@ -17,7 +17,8 @@ public sealed class ThreadPoolSequencer : ISequencer, IDisposable public static readonly ThreadPoolSequencer Instance = new(); /// Cached queue callback for immediate work. - private static readonly WaitCallback ImmediateCallback = static state => ExecuteQueued((IWorkItem)state!); + private static readonly WaitCallback ImmediateCallback = static state => ExecuteQueued( + (IWorkItem)(state ?? throw new InvalidOperationException("The queued work item is missing."))); /// Guards access to delayed work. private readonly Lock _gate = new(); @@ -47,7 +48,8 @@ public sealed class ThreadPoolSequencer : ISequencer, IDisposable "The timer is created disarmed, so nothing can call back into it until Schedule arms it after construction.")] internal ThreadPoolSequencer() => _timer = new( - static state => ((ThreadPoolSequencer)state!).RunDue(), + static state => ((ThreadPoolSequencer)( + state ?? throw new InvalidOperationException("The sequencer state is missing."))).RunDue(), this, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); diff --git a/src/ReactiveUI.Primitives/Concurrency/WasmSequencer.cs b/src/ReactiveUI.Primitives/Concurrency/WasmSequencer.cs index 9f12b2d..aab3f19 100644 --- a/src/ReactiveUI.Primitives/Concurrency/WasmSequencer.cs +++ b/src/ReactiveUI.Primitives/Concurrency/WasmSequencer.cs @@ -49,7 +49,8 @@ public sealed class WasmSequencer : ISequencer, IDisposable internal WasmSequencer() { _timer = new( - static state => ((WasmSequencer)state!).RunDrain(), + static state => ((WasmSequencer)( + state ?? throw new InvalidOperationException("The sequencer state is missing."))).RunDrain(), this, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); diff --git a/src/benchmarks/ReactiveUI.Primitives.Benchmarks/ReactiveExtensionsComparisonBenchmarks.Library.Supplemental.cs b/src/benchmarks/ReactiveUI.Primitives.Benchmarks/ReactiveExtensionsComparisonBenchmarks.Library.Supplemental.cs index 9a54e95..708bfb5 100644 --- a/src/benchmarks/ReactiveUI.Primitives.Benchmarks/ReactiveExtensionsComparisonBenchmarks.Library.Supplemental.cs +++ b/src/benchmarks/ReactiveUI.Primitives.Benchmarks/ReactiveExtensionsComparisonBenchmarks.Library.Supplemental.cs @@ -580,10 +580,7 @@ bool ShouldContinue() return hasRemaining; } - void RecordIteration() - { - total++; - } + void RecordIteration() => total++; return library == ExtensionsLibrary.Primitives ? DrainPrimitiveUnit(PrimitivesExtensions.While(ShouldContinue, RecordIteration)) + total diff --git a/src/benchmarks/ReactiveUI.Primitives.Benchmarks/TerminalCancellationBenchmarks.cs b/src/benchmarks/ReactiveUI.Primitives.Benchmarks/TerminalCancellationBenchmarks.cs index 5468ae8..5946746 100644 --- a/src/benchmarks/ReactiveUI.Primitives.Benchmarks/TerminalCancellationBenchmarks.cs +++ b/src/benchmarks/ReactiveUI.Primitives.Benchmarks/TerminalCancellationBenchmarks.cs @@ -61,14 +61,16 @@ public Task SystemReactiveFirstAsyncWithToken() => [Benchmark] public Task R3FirstAsyncWithToken() => R3.ObservableExtensions.FirstAsync( - R3.ObservableExtensions.Select(R3.Observable.Range(Start, Count), static x => x), + R3.ObservableExtensions.Select( + R3.Observable.Range(Start, Count, _liveSource.Token), + static x => x), _liveSource.Token); /// Benchmarks the task-shim ToTask overload that wraps a token around an existing task. /// The first value. [Benchmark] public Task PrimitivesTaskToTaskWithToken() => - Signal.Sequence(Start, Count).Map(static x => x).FirstAsync().ToTask(_liveSource.Token); + Signal.Sequence(Start, Count).Map(static x => x).FirstAsync(CancellationToken.None).ToTask(_liveSource.Token); /// Benchmarks awaiting the last-or-default value with a live token. /// The last value or default. @@ -87,7 +89,7 @@ public Task SystemReactiveLastOrDefaultAsyncWithToken() => [Benchmark] public Task R3LastOrDefaultAsyncWithToken() => R3.ObservableExtensions.LastOrDefaultAsync( - R3.Observable.Range(Start, Count), + R3.Observable.Range(Start, Count, _liveSource.Token), cancellationToken: _liveSource.Token); /// Benchmarks forwarding a completing range through TakeUntil with a live token. @@ -116,7 +118,9 @@ public int SystemReactiveTakeUntilToken() public int R3TakeUntilToken() { IntR3Witness observer = new(); - using var subscription = R3.ObservableExtensions.TakeUntil(R3.Observable.Range(Start, Count), _liveSource.Token) + using var subscription = R3.ObservableExtensions.TakeUntil( + R3.Observable.Range(Start, Count, _liveSource.Token), + _liveSource.Token) .Subscribe(observer); return observer.Total; } diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/CombiningOperatorTests.Multicast.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/CombiningOperatorTests.Multicast.cs index dc744fd..ce0515f 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/CombiningOperatorTests.Multicast.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/CombiningOperatorTests.Multicast.cs @@ -86,11 +86,8 @@ public async Task WhenMulticastConnectHandleDisposedTwice_ThenCanReconnectSucces await handle.DisposeAsync(); // After the double-dispose the connectable must accept a new connection. - await using var sub = await connectable.SubscribeAsync(static (_, _) => - { - // Signal is already completed from first connect, so no items arrive. - return ValueTask.CompletedTask; - }); + // Signal is already completed from first connect, so no items arrive. + await using var sub = await connectable.SubscribeAsync(static (_, _) => ValueTask.CompletedTask); // A new ConnectAsync succeeds, proving internal state was not corrupted. await using var newHandle = await connectable.ConnectAsync(CancellationToken.None); diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/CombiningOperatorTests.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/CombiningOperatorTests.cs index 29f7845..9b7f919 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/CombiningOperatorTests.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/CombiningOperatorTests.cs @@ -152,7 +152,7 @@ private sealed class ThrowingEnumerator : IEnumerator> public IObservableAsync Current => null!; /// - object System.Collections.IEnumerator.Current => Current!; + object System.Collections.IEnumerator.Current => Current; /// public bool MoveNext() => throw new InvalidOperationException("enumerator MoveNext boom"); diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/TakeUntilOperatorTests.CompletionDelegate.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/TakeUntilOperatorTests.CompletionDelegate.cs index 63e5d14..b5e243d 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/TakeUntilOperatorTests.CompletionDelegate.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/TakeUntilOperatorTests.CompletionDelegate.cs @@ -725,7 +725,7 @@ public async Task WhenTakeUntilCompletionDelegateNotifiesTwice_ThenSecondNotific notifyStop!(Result.Success); await firstCompletion.Task.WaitAsync(WaitTimeout); - notifyStop!(Result.Failure(new InvalidOperationException("second stop"))); + notifyStop(Result.Failure(new InvalidOperationException("second stop"))); var leaked = await AsyncTestHelpers.WaitForConditionAsync( () => errors.Count > 0 || completions.Count > 1, SecondNotificationSettleWindow); diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/TakeUntilOperatorTests.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/TakeUntilOperatorTests.cs index 6d9f3fa..00d63db 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/TakeUntilOperatorTests.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/TakeUntilOperatorTests.cs @@ -204,7 +204,7 @@ public async Task WhenTakeUntilObservableDisposed_ThenStopsEmissions() public void WhenTaskStopSignalNullSource_ThenThrowsArgumentNull() { const IObservableAsync Source = null!; - _ = Assert.Throws(static () => Source!.TakeUntil(Task.CompletedTask)); + _ = Assert.Throws(static () => Source.TakeUntil(Task.CompletedTask)); } /// Tests that task failure with SourceFailsWhenOtherFails=true completes with failure. diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/TaskSignalSubscriptionTests.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/TaskSignalSubscriptionTests.cs index 196814a..a4853a4 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/TaskSignalSubscriptionTests.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/TaskSignalSubscriptionTests.cs @@ -24,7 +24,8 @@ public async Task WhenDisposedReentrantlyAfterThreadHop_ThenDoesNotDeadlock() { await subscriptionReady.Task.ConfigureAwait(false); await Task.Yield(); - await subscription!.DisposeAsync().ConfigureAwait(false); + await (subscription ?? throw new InvalidOperationException("The subscription is not ready.")) + .DisposeAsync().ConfigureAwait(false); disposed.SetResult(); }); diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/TransformationOperatorTests.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/TransformationOperatorTests.cs index 2ba139d..ecf892b 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/TransformationOperatorTests.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/TransformationOperatorTests.cs @@ -553,7 +553,8 @@ public async Task WhenPrependCancelledBeforeAllValues_ThenEarlyReturn() received.Add(x); if (received.Count == 3) { - await subscription!.DisposeAsync(); + await (subscription ?? throw new InvalidOperationException("The subscription is not ready.")) + .DisposeAsync(); } }, null); diff --git a/src/tests/ReactiveUI.Primitives.Maui.Reactive.Tests/MauiDispatcherSequencerTests.cs b/src/tests/ReactiveUI.Primitives.Maui.Reactive.Tests/MauiDispatcherSequencerTests.cs index 8a2ae40..524f185 100644 --- a/src/tests/ReactiveUI.Primitives.Maui.Reactive.Tests/MauiDispatcherSequencerTests.cs +++ b/src/tests/ReactiveUI.Primitives.Maui.Reactive.Tests/MauiDispatcherSequencerTests.cs @@ -29,7 +29,7 @@ public async Task ConstructorRejectsNullDispatcher() => public async Task ToSequencerValidatesAndAdaptsDispatcher() { const IDispatcher nullDispatcher = null!; - await Assert.That(static () => nullDispatcher!.ToSequencer()).ThrowsExactly(); + await Assert.That(static () => nullDispatcher.ToSequencer()).ThrowsExactly(); FakeDispatcher dispatcher = new(); var scheduler = dispatcher.ToSequencer(); diff --git a/src/tests/ReactiveUI.Primitives.Maui.Tests/MauiDispatcherSequencerTests.cs b/src/tests/ReactiveUI.Primitives.Maui.Tests/MauiDispatcherSequencerTests.cs index b99a517..63bac59 100644 --- a/src/tests/ReactiveUI.Primitives.Maui.Tests/MauiDispatcherSequencerTests.cs +++ b/src/tests/ReactiveUI.Primitives.Maui.Tests/MauiDispatcherSequencerTests.cs @@ -29,7 +29,7 @@ public async Task ConstructorRejectsNullDispatcher() => public async Task ToSequencerValidatesAndAdaptsDispatcher() { const IDispatcher nullDispatcher = null!; - await Assert.That(static () => nullDispatcher!.ToSequencer()).ThrowsExactly(); + await Assert.That(static () => nullDispatcher.ToSequencer()).ThrowsExactly(); FakeDispatcher dispatcher = new(); var sequencer = dispatcher.ToSequencer(); diff --git a/src/tests/ReactiveUI.Primitives.Tests/AsyncSignalTests.cs b/src/tests/ReactiveUI.Primitives.Tests/AsyncSignalTests.cs index 5127451..78faedf 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/AsyncSignalTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/AsyncSignalTests.cs @@ -248,7 +248,7 @@ public async Task AsyncSignalSubscriberChurnLateTerminalsAndDisposalCoverBranche RecordingWitness asyncFirst = new(); RecordingWitness asyncSecond = new(); using var asyncSubscription = asyncSignal.Subscribe(asyncFirst); - using var asyncSecondSubscription = asyncSignal.Subscribe(asyncSecond); + var asyncSecondSubscription = asyncSignal.Subscribe(asyncSecond); asyncSecondSubscription.Dispose(); asyncSignal.OnNext(FirstEmittedValue); asyncSignal.OnCompleted(() => completionFaults++); diff --git a/src/tests/ReactiveUI.Primitives.Tests/CommandExecutionTests.cs b/src/tests/ReactiveUI.Primitives.Tests/CommandExecutionTests.cs index 5d99dca..0277449 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/CommandExecutionTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/CommandExecutionTests.cs @@ -46,7 +46,7 @@ public async Task AwaitingAFailedSynchronousCommandRethrowsTheOriginalException( await Assert.That(execution.GetAwaiter().IsCompleted).IsTrue(); var thrown = Assert.Throws(() => execution.GetAwaiter().GetResult()); - await Assert.That(thrown!).IsSameReferenceAs(fault); + await Assert.That(thrown).IsSameReferenceAs(fault); } /// @@ -72,7 +72,7 @@ public async Task ConfigureAwaitPreservesTheOutcomeOfEveryExecutionShape() using CommandSignal failing = new(() => throw fault); var configured = failing.ExecuteAsync().ConfigureAwait(false); var thrown = Assert.Throws(() => configured.GetAwaiter().GetResult()); - await Assert.That(thrown!).IsSameReferenceAs(fault); + await Assert.That(thrown).IsSameReferenceAs(fault); } /// diff --git a/src/tests/ReactiveUI.Primitives.Tests/ConcurencyTests.cs b/src/tests/ReactiveUI.Primitives.Tests/ConcurencyTests.cs index 3f8a9e5..5fe4a3d 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/ConcurencyTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/ConcurencyTests.cs @@ -91,7 +91,7 @@ public async Task TaskPoolScheduleActionCancel() { var nt = TaskPoolSequencer.Instance; var probe = new CancellationProbe(); - using var scheduled = nt.Schedule(probe, CancelDueTime, static p => p.RecordExecution()); + var scheduled = nt.Schedule(probe, CancelDueTime, static p => p.RecordExecution()); scheduled.Dispose(); var delay = Task.Delay(CancelObservationWindow); var observed = await Task.WhenAny(probe.Completed.Task, delay); diff --git a/src/tests/ReactiveUI.Primitives.Tests/ConnectableSignalTests.cs b/src/tests/ReactiveUI.Primitives.Tests/ConnectableSignalTests.cs index 745bb73..6be8a4b 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/ConnectableSignalTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/ConnectableSignalTests.cs @@ -56,8 +56,8 @@ public async Task ConnectableShareAndReplayLiveControlSourceSubscriptions() var shared = cold.ShareLatest(); List first = []; List second = []; - using var firstSubscription = shared.Subscribe(first.Add); - using var secondSubscription = shared.Subscribe(second.Add); + var firstSubscription = shared.Subscribe(first.Add); + var secondSubscription = shared.Subscribe(second.Add); source.OnNext(FirstSharedValue); firstSubscription.Dispose(); source.OnNext(SecondSharedValue); @@ -263,7 +263,7 @@ public async Task AutoShareDisposesConnectionWhenRefcountDropsDuringConnect() void OnNext(int _) { reentrantReleaseInvoked = true; - using AutoShareSubscription reentrantRelease = new(shared, Scope.Empty); + AutoShareSubscription reentrantRelease = new(shared, Scope.Empty); reentrantRelease.Dispose(); } diff --git a/src/tests/ReactiveUI.Primitives.Tests/ObservableMixinsTests.cs b/src/tests/ReactiveUI.Primitives.Tests/ObservableMixinsTests.cs index 47bcca8..d78a86c 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/ObservableMixinsTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/ObservableMixinsTests.cs @@ -141,7 +141,7 @@ public async Task WhenSubscriptionIsDisposed_ThenSourceAndOtherAreDisposed() var otherDisposable = new RecordingDisposable(); var source = new TrackingObservable(sourceDisposable); var other = new TrackingObservable(otherDisposable); - using var subscription = LinqExtensions.TakeUntil(source, other) + var subscription = LinqExtensions.TakeUntil(source, other) .Subscribe(static _ => { }, ThrowUnexpectedError, static () => { }); subscription.Dispose(); diff --git a/src/tests/ReactiveUI.Primitives.Tests/PrioritySemaphoreSignalTests.cs b/src/tests/ReactiveUI.Primitives.Tests/PrioritySemaphoreSignalTests.cs index 7c228e5..4f4fcb7 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/PrioritySemaphoreSignalTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/PrioritySemaphoreSignalTests.cs @@ -80,7 +80,7 @@ public async Task QueuedValuesDrainByPriorityWhenCapacityIsAvailable() { using var signal = new PrioritySemaphoreSignal(0); var observer = new RecordingObserver(); - using var subscription = signal.Subscribe(observer); + var subscription = signal.Subscribe(observer); await Assert.That(signal.HasObservers).IsTrue(); signal.OnNext(ThirdValue); @@ -132,7 +132,7 @@ public async Task ReleaseDoesNotAllowNegativeCount() { using var signal = new PrioritySemaphoreSignal(1); var observer = new RecordingObserver(); - using var subscription = signal.Subscribe(observer); + var subscription = signal.Subscribe(observer); signal.OnNext(FirstValue); @@ -209,7 +209,7 @@ public async Task ConcurrentOperationsDoNotNotifyObserverConcurrently() { using var signal = new PrioritySemaphoreSignal(InitialDrainCapacity); var observer = new ConcurrencyProbe(); - using var subscription = signal.Subscribe(observer); + var subscription = signal.Subscribe(observer); for (var i = 0; i < SeededValueCount; i++) { signal.OnNext(i); diff --git a/src/tests/ReactiveUI.Primitives.Tests/SafeWitnessTests.cs b/src/tests/ReactiveUI.Primitives.Tests/SafeWitnessTests.cs index 61e5557..7555363 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/SafeWitnessTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/SafeWitnessTests.cs @@ -92,7 +92,7 @@ public async Task DelegateWitnessMadeSafeStopsAfterItsValueCallbackThrows() // A second push must not reach the callback, so it must not throw either. safe.OnNext(LateValue); - await Assert.That(thrown!.Message).IsEqualTo("next-failed"); + await Assert.That(thrown.Message).IsEqualTo("next-failed"); await Assert.That(calls).IsEqualTo(1); } @@ -153,6 +153,6 @@ public async Task CancelFreeSafeWitnessStopsAfterTheObserverThrows() // The witness has latched stopped, so this must be a silent no-op rather than a second throw. safe.OnNext(LateValue); - await Assert.That(thrown!.Message).IsEqualTo("observer-next"); + await Assert.That(thrown.Message).IsEqualTo("observer-next"); } } diff --git a/src/tests/ReactiveUI.Primitives.Tests/ScheduledSignalTests.cs b/src/tests/ReactiveUI.Primitives.Tests/ScheduledSignalTests.cs index ae14971..3df19fd 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/ScheduledSignalTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/ScheduledSignalTests.cs @@ -90,8 +90,8 @@ public async Task DefaultObserverIsRestoredAfterLastSubscriberDisposes() var firstObserver = new RecordingObserver(); var secondObserver = new RecordingObserver(); - using var firstSubscription = signal.Subscribe(firstObserver); - using var secondSubscription = signal.Subscribe(secondObserver); + var firstSubscription = signal.Subscribe(firstObserver); + var secondSubscription = signal.Subscribe(secondObserver); signal.OnNext(SecondValue); sequencer.DrainAll(); diff --git a/src/tests/ReactiveUI.Primitives.Tests/SignalFactoriesTests.DefaultSequencer.cs b/src/tests/ReactiveUI.Primitives.Tests/SignalFactoriesTests.DefaultSequencer.cs index 8e6384d..1cd4443 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/SignalFactoriesTests.DefaultSequencer.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/SignalFactoriesTests.DefaultSequencer.cs @@ -78,10 +78,7 @@ public async Task StartWithoutASequencerRunsTheWorkOnTheDefaultSequencer() // A void method group is what selects Start(Action); a lambda over 'actionRuns++' is a // Func and would bind to the generic Start overload instead. - void RunAction() - { - actionRuns++; - } + void RunAction() => actionRuns++; using var actionSubscription = Signal.Start(RunAction).Subscribe(actionValues.Add); await TestPolling.SpinUntil(() => actionValues.Count == 1, DefaultSequencerTimeout); diff --git a/src/tests/ReactiveUI.Primitives.Tests/SignalFactoriesTests.Scheduling.cs b/src/tests/ReactiveUI.Primitives.Tests/SignalFactoriesTests.Scheduling.cs index a4af059..dc084c7 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/SignalFactoriesTests.Scheduling.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/SignalFactoriesTests.Scheduling.cs @@ -29,10 +29,7 @@ public async Task StartActionDefersToTheSequencerThenCompletesWithASingleUnit() VirtualClock clock = new(); RecordingWitness witness = new(); var runCount = 0; - void Run() - { - runCount++; - } + void Run() => runCount++; using var subscription = Signal.Start(Run, clock).Subscribe(witness); @@ -78,10 +75,7 @@ public async Task StartActionOnTheCurrentThreadSequencerRunsThroughTheTrampoline RecordingWitness witness = new(); var runCount = 0; - void Run() - { - runCount++; - } + void Run() => runCount++; using var subscription = Signal.Start(Run, Sequencer.CurrentThread).Subscribe(witness); diff --git a/src/tests/ReactiveUI.Primitives.Tests/SignalFactoriesTests.cs b/src/tests/ReactiveUI.Primitives.Tests/SignalFactoriesTests.cs index 5ab0f2b..e334489 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/SignalFactoriesTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/SignalFactoriesTests.cs @@ -142,7 +142,7 @@ public async Task FactoryAliasesScheduledRangesTasksAndTimersCoverRemainderBranc await TestPolling.SpinUntil( () => taskValues.Count == One && taskErrors.Count == Two, TimeSpan.FromSeconds(TimeoutSeconds)); - using var disposedTaskSubscription = Signal.FromTask(Task.FromResult(NinetyNine)) + var disposedTaskSubscription = Signal.FromTask(Task.FromResult(NinetyNine)) .Subscribe(_ => taskValues.Add(NinetyNine)); disposedTaskSubscription.Dispose(); _ = Signal.After(TimeSpan.FromTicks(Two), clock).Subscribe(afterValues.Add); diff --git a/src/tests/ReactiveUI.Primitives.Tests/SignalFromTaskTest.cs b/src/tests/ReactiveUI.Primitives.Tests/SignalFromTaskTest.cs index 4878c62..86be8b0 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/SignalFromTaskTest.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/SignalFromTaskTest.cs @@ -542,7 +542,7 @@ await Task.Delay(CommandDelayMilliseconds, cts.Token) return Signal.Fail(ex); }).OnCleanup(() => RecordStatus(statusTrail, ref position, ShouldAlwaysComeHere)); var result = false; - using var subscription = fixture.Subscribe(_ => result = true); + var subscription = fixture.Subscribe(_ => result = true); await Task.Delay(InitialDelayMilliseconds).ConfigureAwait(true); await Assert.That(StatusMessages(statusTrail)).Contains(StartedCommand); await Task.Delay(CommandDelayMilliseconds).ConfigureAwait(true); @@ -580,7 +580,7 @@ await Task.Delay(CommandDelayMilliseconds, cts.Token) return Signal.Fail(ex); }).OnCleanup(() => RecordStatus(statusTrail, ref position, ShouldAlwaysComeHere)); var result = false; - using var subscription = fixture.Subscribe(_ => result = true); + var subscription = fixture.Subscribe(_ => result = true); await Task.Delay(InitialDelayMilliseconds).ConfigureAwait(true); await Assert.That(StatusMessages(statusTrail)).Contains(StartedCommand); subscription.Dispose(); @@ -661,7 +661,7 @@ public async Task SignalFromTaskHandlesCancellationInBase() RecordStatus(statusTrail, ref position, ExceptionShouldBeHere); return Signal.Fail(ex); }).OnCleanup(() => RecordStatus(statusTrail, ref position, ShouldAlwaysComeHere)); - using var subscription = fixture.Subscribe(); + var subscription = fixture.Subscribe(); await Task.Delay(InitialDelayMilliseconds).ConfigureAwait(true); await Assert.That(StatusMessages(statusTrail)).Contains(StartedCommand); subscription.Dispose(); @@ -729,7 +729,7 @@ await Task.Delay(CommandDelayMilliseconds, cts.Token) return Signal.Fail(ex); }).OnCleanup(() => RecordStatus(statusTrail, ref position, ShouldAlwaysComeHere)); var result = false; - using var subscription = fixture.Subscribe(_ => result = true); + var subscription = fixture.Subscribe(_ => result = true); await Task.Delay(InitialDelayMilliseconds).ConfigureAwait(true); await Assert.That(StatusMessages(statusTrail)).Contains(StartedCommand); await Task.Delay(CommandDelayMilliseconds).ConfigureAwait(true); @@ -767,7 +767,7 @@ await Task.Delay(CommandDelayMilliseconds, cts.Token) return Signal.Fail(ex); }).OnCleanup(() => RecordStatus(statusTrail, ref position, ShouldAlwaysComeHere)); var result = false; - using var subscription = fixture.Subscribe(_ => result = true); + var subscription = fixture.Subscribe(_ => result = true); await Task.Delay(InitialDelayMilliseconds).ConfigureAwait(true); await Assert.That(StatusMessages(statusTrail)).Contains(StartedCommand); subscription.Dispose(); @@ -848,7 +848,7 @@ public async Task SignalFromTask_T_HandlesCancellationInBase() RecordStatus(statusTrail, ref position, ExceptionShouldBeHere); return Signal.Fail(ex); }).OnCleanup(() => RecordStatus(statusTrail, ref position, ShouldAlwaysComeHere)); - using var subscription = fixture.Subscribe(); + var subscription = fixture.Subscribe(); await Task.Delay(InitialDelayMilliseconds).ConfigureAwait(true); await Assert.That(StatusMessages(statusTrail)).Contains(StartedCommand); subscription.Dispose(); @@ -909,7 +909,7 @@ public async Task ImmediateTaskSignalErrorsWhenTheTokenIsCancelledBeforeTheResul ConcurrentQueue values = new(); ConcurrentQueue errors = new(); _ = taskSignal.Subscribe(values.Enqueue, errors.Enqueue, static () => { }); - await taskSignal.CancellationTokenSource!.CancelAsync(); + await taskSignal.CancellationTokenSource.CancelAsync(); completion.SetResult(SuccessValue); await TestPolling.SpinUntil(() => !errors.IsEmpty, PollTimeout).ConfigureAwait(false); await Assert.That(errors.Count).IsEqualTo(1); @@ -1067,7 +1067,7 @@ private static void RecordCancellationCleanup(StatusTrail statusTrail, ref int p /// A representing the asynchronous operation. private static async Task CancelAfterDelayAsync(CancellationTokenSource cts) { - await Task.Delay(TokenCancellationDelayMilliseconds).ConfigureAwait(false); + await Task.Delay(TokenCancellationDelayMilliseconds, cts.Token).ConfigureAwait(false); await cts.CancelAsync().ConfigureAwait(false); } diff --git a/src/tests/ReactiveUI.Primitives.Tests/SignalOperatorMixinsTests.cs b/src/tests/ReactiveUI.Primitives.Tests/SignalOperatorMixinsTests.cs index 08ad7d9..99a8b0d 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/SignalOperatorMixinsTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/SignalOperatorMixinsTests.cs @@ -566,8 +566,8 @@ public async Task TaskChainDirectSignalHandlesErrorsAndDisposal() TaskCompletionSource pending = new(TaskCreationOptions.RunContinuationsAsynchronously); Signal> disposableSource = new(); RecordingWitness disposed = new(); - using (var disposable = disposableSource.Chain().Subscribe(disposed)) { + var disposable = disposableSource.Chain().Subscribe(disposed); disposableSource.OnNext(pending.Task); disposable.Dispose(); pending.SetResult(Five); diff --git a/src/tests/ReactiveUI.Primitives.Tests/SignalRecoverTests.cs b/src/tests/ReactiveUI.Primitives.Tests/SignalRecoverTests.cs index 26eb997..9a63ed6 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/SignalRecoverTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/SignalRecoverTests.cs @@ -88,7 +88,7 @@ public async Task OnCleanupRunsTheActionWhenSubscribingToTheSourceThrows() var caught = Assert.Throws(() => failingSource.OnCleanup(() => cleanupRuns++).Subscribe(static _ => { })); - await Assert.That(caught!).IsSameReferenceAs(expected); + await Assert.That(caught).IsSameReferenceAs(expected); await Assert.That(cleanupRuns).IsEqualTo(1); } diff --git a/src/tests/ReactiveUI.Primitives.Tests/SignalTests.cs b/src/tests/ReactiveUI.Primitives.Tests/SignalTests.cs index f5431be..7c465db 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/SignalTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/SignalTests.cs @@ -477,9 +477,9 @@ public async Task SubjectsCoverMultipleSubscriberChurnLateTerminalsAndDisposalBr RecordingWitness third = new(); RecordingWitness fourth = new(); List actionValues = []; - using var action = subject.Subscribe(actionValues.Add); + var action = subject.Subscribe(actionValues.Add); using var firstSubscription = subject.Subscribe(first); - using var secondSubscription = subject.Subscribe(second); + var secondSubscription = subject.Subscribe(second); using var thirdSubscription = subject.Subscribe(third); using var fourthSubscription = subject.Subscribe(fourth); secondSubscription.Dispose(); diff --git a/src/tests/ReactiveUI.Primitives.Tests/SinkObserverTests.cs b/src/tests/ReactiveUI.Primitives.Tests/SinkObserverTests.cs index a97c94a..6db45b4 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/SinkObserverTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/SinkObserverTests.cs @@ -523,7 +523,7 @@ public async Task BufferSinkReleasesTheUpstreamWhenTheObserverThrowsOnACompleted var thrown = Assert.Throws(() => sink.OnNext(Two)); - await Assert.That(thrown!.Message).IsEqualTo(ObserverNextMessage); + await Assert.That(thrown.Message).IsEqualTo(ObserverNextMessage); await Assert.That(subscription.DisposeCount).IsEqualTo(1); } @@ -545,7 +545,7 @@ public async Task BufferSinkDropsValuesFromASourceThatKeepsPushingAfterTheObserv source.Next(One); var thrown = Assert.Throws(() => source.Next(Two)); - await Assert.That(thrown!.Message).IsEqualTo(ObserverNextMessage); + await Assert.That(thrown.Message).IsEqualTo(ObserverNextMessage); await Assert.That(observer.NextCount).IsEqualTo(One); // The sink disposed its upstream; this source ignores disposal and pushes on regardless. @@ -683,7 +683,7 @@ private static async Task AssertObserverFailureReleasesUpstream( { var thrown = Assert.Throws(() => sink.OnNext(1)); - await Assert.That(thrown!.Message).IsEqualTo("observer-next"); + await Assert.That(thrown.Message).IsEqualTo("observer-next"); await Assert.That(subscription.DisposeCount).IsEqualTo(1); } diff --git a/src/tests/ReactiveUI.Primitives.Tests/StateSignalTests.cs b/src/tests/ReactiveUI.Primitives.Tests/StateSignalTests.cs index 208186f..b6f2203 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/StateSignalTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/StateSignalTests.cs @@ -124,7 +124,7 @@ public async Task StatefulSignalsReportObserverValueErrorAndDisposalState() await Assert.That(current).IsEqualTo(InitialStateValue); Recorder observer = new(); - using var subscription = state.Subscribe(observer); + var subscription = state.Subscribe(observer); await Assert.That(state.HasObservers).IsTrue(); state.OnNext(UpdatedStateValue); await Assert.That(observer.Values.SequenceEqual([InitialStateValue, UpdatedStateValue])).IsTrue(); diff --git a/src/tests/ReactiveUI.Primitives.Tests/SyncLatestTests.cs b/src/tests/ReactiveUI.Primitives.Tests/SyncLatestTests.cs index 7bfb912..32b3afb 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/SyncLatestTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/SyncLatestTests.cs @@ -242,7 +242,7 @@ public async Task SyncLatestUnsubscribesEarlierSourcesWhenALaterSourceThrowsOnSu third, static (a, b, c) => a + b + c); var thrown = Assert.Throws(() => _ = combined.Subscribe(static _ => { })); - await Assert.That(thrown!).IsSameReferenceAs(expected); + await Assert.That(thrown).IsSameReferenceAs(expected); await Assert.That(first.HasObservers).IsFalse(); await Assert.That(third.HasObservers).IsFalse(); } diff --git a/src/tests/ReactiveUI.Primitives.Tests/TaskSignalTests.cs b/src/tests/ReactiveUI.Primitives.Tests/TaskSignalTests.cs index 26eb271..93dfe02 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/TaskSignalTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/TaskSignalTests.cs @@ -38,7 +38,7 @@ public async Task TaskSignalCoversCancellationAndDisposeBranches() [Test] public async Task TaskSignalCreateWithASchedulerBuildsASignalThatCancelsOnDisposal() { - using var taskSignal = TaskSignal.Create(static _ => Signal.Silent(), Sequencer.CurrentThread); + var taskSignal = TaskSignal.Create(static _ => Signal.Silent(), Sequencer.CurrentThread); await Assert.That(taskSignal.IsCancellationRequested).IsFalse(); await Assert.That(taskSignal.IsDisposed).IsFalse(); diff --git a/src/tests/ReactiveUI.Primitives.Tests/UseSignalTests.cs b/src/tests/ReactiveUI.Primitives.Tests/UseSignalTests.cs index a625cec..a6807bf 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/UseSignalTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/UseSignalTests.cs @@ -115,7 +115,7 @@ public async Task UseReleasesTheResourceWhenTheObserverThrows() // The sink has stopped, so this must be a silent no-op rather than a second trip into the observer. sink.OnNext(LateValue); - await Assert.That(thrown!.Message).IsEqualTo("observer-next"); + await Assert.That(thrown.Message).IsEqualTo("observer-next"); await Assert.That(resource.DisposeCount).IsEqualTo(1); await Assert.That(source.DisposeCount).IsEqualTo(1); } From 7738196e9098a6298df2833a143376bc32e61d21 Mon Sep 17 00:00:00 2001 From: Glenn Watson <5834289+glennawatson@users.noreply.github.com> Date: Sat, 25 Jul 2026 17:27:11 +1000 Subject: [PATCH 3/8] fix: restore null-forgiving expressions - Revert S8969-driven callback and test rewrites. - Keep established nullability assertions now that the rule is disabled. --- .../CancellationTokenPolyfillExtensions.cs | 2 +- .../Advanced/StartSubscription.cs | 3 +-- src/Primitives.Async.Shared/AsyncContext.cs | 10 ++-------- .../Advanced/FromAsyncExternalCancellation{T}.cs | 5 +---- .../Advanced/FromAsyncSubscription{T}.cs | 3 +-- src/Primitives.Shared/SignalOperatorMixins.cs | 2 +- .../SignalOperatorParityMixins.cs | 2 +- .../Signals/SignalExtensions{FromTask}.cs | 15 ++++----------- .../Signals/Signal{Factories}.cs | 2 +- src/Primitives.Shared/Signals/Signal{FromTask}.cs | 6 ++---- .../Signals/Signal{GetAwaiter}.cs | 3 +-- src/Primitives.Shared/Signals/TaskSignal{T}.cs | 6 +----- .../Advanced/BackgroundJobSignal{T}.cs | 3 +-- .../Advanced/FlatMapWitness{TSource,TResult}.cs | 4 +--- .../Advanced/IntervalSubscription.cs | 8 +++----- .../Advanced/PooledDelaySource.cs | 3 +-- .../Advanced/StartSubscription{TResult}.cs | 3 +-- ...atestEnumerableCoordinator{TSource,TResult}.cs | 2 +- .../Advanced/TaskResultCompletionSource.cs | 3 +-- .../Operators/Expire.cs | 3 +-- .../Signals/CommandSignal{TResult}.cs | 12 +++--------- .../ConcurrencyLimiter.cs | 6 +----- .../Operators/SelectLatestAsyncObservable.cs | 10 +--------- .../Concurrency/WasmScheduler.cs | 14 +++----------- .../SynchronizationContextSequencer.cs | 5 +---- .../Concurrency/TaskPoolSequencer.cs | 3 +-- .../Concurrency/ThreadPoolSequencer.cs | 6 ++---- .../Concurrency/WasmSequencer.cs | 3 +-- .../CombiningOperatorTests.cs | 2 +- .../TakeUntilOperatorTests.CompletionDelegate.cs | 2 +- .../TakeUntilOperatorTests.cs | 2 +- .../TaskSignalSubscriptionTests.cs | 3 +-- .../TransformationOperatorTests.cs | 3 +-- .../MauiDispatcherSequencerTests.cs | 2 +- .../MauiDispatcherSequencerTests.cs | 2 +- .../CommandExecutionTests.cs | 4 ++-- .../SafeWitnessTests.cs | 4 ++-- .../SignalFromTaskTest.cs | 2 +- .../SignalRecoverTests.cs | 2 +- .../SinkObserverTests.cs | 6 +++--- .../SyncLatestTests.cs | 2 +- .../ReactiveUI.Primitives.Tests/UseSignalTests.cs | 2 +- 42 files changed, 58 insertions(+), 127 deletions(-) diff --git a/src/Polyfills/CancellationTokenPolyfillExtensions.cs b/src/Polyfills/CancellationTokenPolyfillExtensions.cs index 225c333..a198a31 100644 --- a/src/Polyfills/CancellationTokenPolyfillExtensions.cs +++ b/src/Polyfills/CancellationTokenPolyfillExtensions.cs @@ -31,7 +31,7 @@ internal CancellationTokenRegistration static boxed => { var (inner, innerState, innerToken) = - ((Action Callback, object? State, CancellationToken Token))boxed; + ((Action Callback, object? State, CancellationToken Token))boxed!; inner(innerState, innerToken); }, (callback, state, token), diff --git a/src/Primitives.Async.Shared/Advanced/StartSubscription.cs b/src/Primitives.Async.Shared/Advanced/StartSubscription.cs index 1ab2712..3c4c160 100644 --- a/src/Primitives.Async.Shared/Advanced/StartSubscription.cs +++ b/src/Primitives.Async.Shared/Advanced/StartSubscription.cs @@ -49,8 +49,7 @@ await Task.Factory.StartNew( static s => { var (self, observer, cancellationToken) = - ((StartSubscription, IObserverAsync, CancellationToken))( - s ?? throw new InvalidOperationException("The start state is missing.")); + ((StartSubscription, IObserverAsync, CancellationToken))s!; return self.ExecuteActionAsync(observer, cancellationToken).AsTask(); }, (this, observer, cancellationToken), diff --git a/src/Primitives.Async.Shared/AsyncContext.cs b/src/Primitives.Async.Shared/AsyncContext.cs index 795f80f..284e45d 100644 --- a/src/Primitives.Async.Shared/AsyncContext.cs +++ b/src/Primitives.Async.Shared/AsyncContext.cs @@ -169,10 +169,7 @@ public void OnCompleted(Action continuation) var sc = AsyncContext.SynchronizationContext; if (sc is not null) { - sc.Post( - static state => ((Action)( - state ?? throw new InvalidOperationException("The continuation is missing."))).Invoke(), - continuation); + sc.Post(static c => ((Action)c!).Invoke(), continuation); return; } @@ -199,10 +196,7 @@ public void OnCompleted(Action continuation) // path Yield takes by default, so the saving lands on the operator's hot path. if (ts is null || ts == TaskScheduler.Default) { - _ = ThreadPool.UnsafeQueueUserWorkItem( - static state => ((Action)( - state ?? throw new InvalidOperationException("The continuation is missing."))).Invoke(), - continuation); + _ = ThreadPool.UnsafeQueueUserWorkItem(static c => ((Action)c!).Invoke(), continuation); } } diff --git a/src/Primitives.Shared/Advanced/FromAsyncExternalCancellation{T}.cs b/src/Primitives.Shared/Advanced/FromAsyncExternalCancellation{T}.cs index 205172a..1c9c8f6 100644 --- a/src/Primitives.Shared/Advanced/FromAsyncExternalCancellation{T}.cs +++ b/src/Primitives.Shared/Advanced/FromAsyncExternalCancellation{T}.cs @@ -64,10 +64,7 @@ internal bool Start() } Registration = - CancellationToken.Register( - static state => ((FromAsyncExternalCancellation)( - state ?? throw new InvalidOperationException("The cancellation state is missing."))).Cancel(), - this); + CancellationToken.Register(static state => ((FromAsyncExternalCancellation)state!).Cancel(), this); return !Lifetime.IsCompleted; } diff --git a/src/Primitives.Shared/Advanced/FromAsyncSubscription{T}.cs b/src/Primitives.Shared/Advanced/FromAsyncSubscription{T}.cs index 73ebf79..1655c02 100644 --- a/src/Primitives.Shared/Advanced/FromAsyncSubscription{T}.cs +++ b/src/Primitives.Shared/Advanced/FromAsyncSubscription{T}.cs @@ -101,8 +101,7 @@ internal IDisposable Start() FromAsyncTaskObservation observation = new(Observer, Lifetime, ExternalCancellation, linkedSource); _ = task.ContinueWith( - static (completedTask, state) => ((FromAsyncTaskObservation)( - state ?? throw new InvalidOperationException("The observation state is missing."))).Observe(completedTask), + static (completedTask, state) => ((FromAsyncTaskObservation)state!).Observe(completedTask), observation, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, diff --git a/src/Primitives.Shared/SignalOperatorMixins.cs b/src/Primitives.Shared/SignalOperatorMixins.cs index 22b7414..0d462f4 100644 --- a/src/Primitives.Shared/SignalOperatorMixins.cs +++ b/src/Primitives.Shared/SignalOperatorMixins.cs @@ -687,7 +687,7 @@ public IObservable ToSignal() } return task.IsFaulted - ? new ImmediateThrowSignal(task.Exception.InnerException ?? task.Exception) + ? new ImmediateThrowSignal(task.Exception!.InnerException ?? task.Exception) : new TaskInstanceSignal(task); } } diff --git a/src/Primitives.Shared/SignalOperatorParityMixins.cs b/src/Primitives.Shared/SignalOperatorParityMixins.cs index 90c9a41..890a0aa 100644 --- a/src/Primitives.Shared/SignalOperatorParityMixins.cs +++ b/src/Primitives.Shared/SignalOperatorParityMixins.cs @@ -788,7 +788,7 @@ public IObservable ToObservable() } return task.IsFaulted - ? new ImmediateThrowSignal(task.Exception.InnerException ?? task.Exception) + ? new ImmediateThrowSignal(task.Exception!.InnerException ?? task.Exception) : new TaskInstanceSignal(task); } diff --git a/src/Primitives.Shared/Signals/SignalExtensions{FromTask}.cs b/src/Primitives.Shared/Signals/SignalExtensions{FromTask}.cs index 0b6c9f3..db9a5ee 100644 --- a/src/Primitives.Shared/Signals/SignalExtensions{FromTask}.cs +++ b/src/Primitives.Shared/Signals/SignalExtensions{FromTask}.cs @@ -102,17 +102,10 @@ public async Task HandleCancellation(Action? action) internal async Task<(TResult Value, bool IsCanceled)> WhenCancelled(CancellationToken cancellationToken) { TaskCompletionSource tcs = new(TaskCreationOptions.RunContinuationsAsynchronously); - var registration = cancellationToken.UnsafeRegister( - static (state, token) => - { - if (state is not TaskCompletionSource source) - { - throw new InvalidOperationException("The cancellation state is not a completion source."); - } - - _ = source.TrySetCanceled(token); - }, - tcs); + var registration = cancellationToken.Register( + static state => ((TaskCompletionSource)state!).TrySetCanceled(), + tcs, + false); var cancellationTask = tcs.Task; try diff --git a/src/Primitives.Shared/Signals/Signal{Factories}.cs b/src/Primitives.Shared/Signals/Signal{Factories}.cs index 2e28016..c59bfcc 100644 --- a/src/Primitives.Shared/Signals/Signal{Factories}.cs +++ b/src/Primitives.Shared/Signals/Signal{Factories}.cs @@ -235,7 +235,7 @@ public static IObservable FromTask(Task task) } return task.IsFaulted - ? new ImmediateThrowSignal(task.Exception.InnerException ?? task.Exception) + ? new ImmediateThrowSignal(task.Exception!.InnerException ?? task.Exception) : new TaskInstanceSignal(task); } diff --git a/src/Primitives.Shared/Signals/Signal{FromTask}.cs b/src/Primitives.Shared/Signals/Signal{FromTask}.cs index fe39479..5709405 100644 --- a/src/Primitives.Shared/Signals/Signal{FromTask}.cs +++ b/src/Primitives.Shared/Signals/Signal{FromTask}.cs @@ -183,7 +183,7 @@ private static bool TryEmitSynchronously( return false; } - observer.OnError(task.Exception.InnerException ?? task.Exception); + observer.OnError(task.Exception!.InnerException ?? task.Exception); return true; } @@ -288,9 +288,7 @@ public void GetOperationCanceled(IObserver observer) ArgumentExceptionHelper.ThrowIfNull(observer); _ = SourceCore.Token.Register( - static state => ((IObserver)( - state ?? throw new InvalidOperationException("The cancellation observer is missing."))).OnNext( - new OperationCanceledException()), + static state => ((IObserver)state!).OnNext(new OperationCanceledException()), observer, false); } diff --git a/src/Primitives.Shared/Signals/Signal{GetAwaiter}.cs b/src/Primitives.Shared/Signals/Signal{GetAwaiter}.cs index acf703c..6aa4113 100644 --- a/src/Primitives.Shared/Signals/Signal{GetAwaiter}.cs +++ b/src/Primitives.Shared/Signals/Signal{GetAwaiter}.cs @@ -121,8 +121,7 @@ internal static void RegisterCancelation( var ctr = token.Register( static state => { - var (subscription, subject, token) = ((IDisposable, IAwaitSignal, CancellationToken))( - state ?? throw new InvalidOperationException("The await cancellation state is missing.")); + var (subscription, subject, token) = ((IDisposable, IAwaitSignal, CancellationToken))state!; subscription.Dispose(); _ = Cancel(subject, token); }, diff --git a/src/Primitives.Shared/Signals/TaskSignal{T}.cs b/src/Primitives.Shared/Signals/TaskSignal{T}.cs index cf5538a..88070d4 100644 --- a/src/Primitives.Shared/Signals/TaskSignal{T}.cs +++ b/src/Primitives.Shared/Signals/TaskSignal{T}.cs @@ -56,11 +56,7 @@ private TaskSignal(ISequencer? sequencer, CancellationTokenSource? cancellationT /// The observer. public void GetOperationCanceled(IObserver observer) => CancellationTokenSource?.Token - .Register( - static state => ((IObserver)( - state ?? throw new InvalidOperationException("The cancellation observer is missing."))).OnNext( - new OperationCanceledException()), - observer) + .Register(static o => ((IObserver)o!).OnNext(new OperationCanceledException()), observer) .DisposeWith(_cleanUp); /// Subscribes the specified observer. diff --git a/src/ReactiveUI.Primitives.Async.Core/Advanced/BackgroundJobSignal{T}.cs b/src/ReactiveUI.Primitives.Async.Core/Advanced/BackgroundJobSignal{T}.cs index 3fedca7..9f00898 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Advanced/BackgroundJobSignal{T}.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Advanced/BackgroundJobSignal{T}.cs @@ -76,8 +76,7 @@ await Task.Factory.StartNew( static s => { var (self, observer, cancellationToken) = - ((BackgroundJobSignal, IObserverAsync, CancellationToken))( - s ?? throw new InvalidOperationException("The background-job state is missing.")); + ((BackgroundJobSignal, IObserverAsync, CancellationToken))s!; return self.Job(observer, cancellationToken).AsTask(); }, (this, observer, cancellationToken), diff --git a/src/ReactiveUI.Primitives.Async.Core/Advanced/FlatMapWitness{TSource,TResult}.cs b/src/ReactiveUI.Primitives.Async.Core/Advanced/FlatMapWitness{TSource,TResult}.cs index 2969d91..6f4fcbf 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Advanced/FlatMapWitness{TSource,TResult}.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Advanced/FlatMapWitness{TSource,TResult}.cs @@ -37,9 +37,7 @@ protected override async ValueTask OnNextAsyncCore(TSource value, CancellationTo { var inner = SyncSelector is not null ? SyncSelector(value) - : await (AsyncSelector ?? throw new InvalidOperationException("The flat-map selector is missing."))( - value, - cancellationToken).ConfigureAwait(false); + : await AsyncSelector!(value, cancellationToken).ConfigureAwait(false); await Coordinator.SubscribeInnerAsync(inner).ConfigureAwait(false); } diff --git a/src/ReactiveUI.Primitives.Async.Core/Advanced/IntervalSubscription.cs b/src/ReactiveUI.Primitives.Async.Core/Advanced/IntervalSubscription.cs index 312ed43..e514439 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Advanced/IntervalSubscription.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Advanced/IntervalSubscription.cs @@ -54,8 +54,7 @@ private async ValueTask DelayWithProviderAsync(CancellationToken cancellationTok { TaskCompletionSource tcs = new(TaskCreationOptions.RunContinuationsAsynchronously); await using var tp = TimeProvider!.CreateTimer( - static state => ((TaskCompletionSource)( - state ?? throw new InvalidOperationException("The interval state is missing."))).TrySetResult(true), + static x => ((TaskCompletionSource)x!).TrySetResult(true), tcs, Period, Timeout.InfiniteTimeSpan); @@ -65,8 +64,7 @@ private async ValueTask DelayWithProviderAsync(CancellationToken cancellationTok cancellationToken.Register( static x => { - var (tcs, ct) = ((TaskCompletionSource, CancellationToken))( - x ?? throw new InvalidOperationException("The interval cancellation state is missing.")); + var (tcs, ct) = ((TaskCompletionSource, CancellationToken))x!; _ = tcs.TrySetCanceled(ct); }, (tcs, cancellationToken)); @@ -75,7 +73,7 @@ private async ValueTask DelayWithProviderAsync(CancellationToken cancellationTok cancellationToken.Register( static x => { - var (tcs, ct) = ((TaskCompletionSource, CancellationToken))x; + var (tcs, ct) = ((TaskCompletionSource, CancellationToken))x!; _ = tcs.TrySetCanceled(ct); }, (tcs, cancellationToken)); diff --git a/src/ReactiveUI.Primitives.Async.Core/Advanced/PooledDelaySource.cs b/src/ReactiveUI.Primitives.Async.Core/Advanced/PooledDelaySource.cs index 93000ca..9c2f2f3 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Advanced/PooledDelaySource.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Advanced/PooledDelaySource.cs @@ -95,8 +95,7 @@ public ValueTask BeginAsync(TimeSpan delay, TimeProvider timeProvider, Cancellat // some test / benchmark providers); in that case _completed flips to Claimed before this // call returns. _timer = timeProvider.CreateTimer( - static state => ((PooledDelaySource)( - state ?? throw new InvalidOperationException("The delay state is missing."))).OnTimerFired(), + static state => ((PooledDelaySource)state!).OnTimerFired(), this, delay, Timeout.InfiniteTimeSpan); diff --git a/src/ReactiveUI.Primitives.Async.Core/Advanced/StartSubscription{TResult}.cs b/src/ReactiveUI.Primitives.Async.Core/Advanced/StartSubscription{TResult}.cs index 96dd8b8..c4b0729 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Advanced/StartSubscription{TResult}.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Advanced/StartSubscription{TResult}.cs @@ -46,8 +46,7 @@ await Task.Factory.StartNew( static s => { var (self, observer, cancellationToken) = - ((StartSubscription, IObserverAsync, CancellationToken))( - s ?? throw new InvalidOperationException("The start state is missing.")); + ((StartSubscription, IObserverAsync, CancellationToken))s!; return self.ExecuteFunctionAsync(observer, cancellationToken).AsTask(); }, (this, observer, cancellationToken), diff --git a/src/ReactiveUI.Primitives.Async.Core/Advanced/SyncLatestEnumerableCoordinator{TSource,TResult}.cs b/src/ReactiveUI.Primitives.Async.Core/Advanced/SyncLatestEnumerableCoordinator{TSource,TResult}.cs index 249462c..93d3c98 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Advanced/SyncLatestEnumerableCoordinator{TSource,TResult}.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Advanced/SyncLatestEnumerableCoordinator{TSource,TResult}.cs @@ -121,7 +121,7 @@ internal async ValueTask OnNextAsync(int index, TSource indexValue, Cancellation for (var i = 0; i < Values.Length; i++) { - SnapshotBuffer[i] = Values[i].Value; + SnapshotBuffer[i] = Values[i].Value!; } TResult projected; diff --git a/src/ReactiveUI.Primitives.Async.Core/Advanced/TaskResultCompletionSource.cs b/src/ReactiveUI.Primitives.Async.Core/Advanced/TaskResultCompletionSource.cs index e716e16..ba0cfa3 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Advanced/TaskResultCompletionSource.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Advanced/TaskResultCompletionSource.cs @@ -76,8 +76,7 @@ private CancellationTokenRegistration RegisterCancellation() => _cancellationToken.Register( static state => { - var source = (TaskResultCompletionSource)( - state ?? throw new InvalidOperationException("The task result state is missing.")); + var source = (TaskResultCompletionSource)state!; _ = source._taskSource.TrySetException(new OperationCanceledException(source._cancellationToken)); }, this); diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/Expire.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/Expire.cs index f7b6340..542823f 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/Expire.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/Expire.cs @@ -89,8 +89,7 @@ internal void StartTimer(CancellationToken cancellationToken) try { _timer = timeProvider.CreateTimer( - static state => ((TimeoutWitness)( - state ?? throw new InvalidOperationException("The timeout state is missing."))).OnTimerFired(), + static state => ((TimeoutWitness)state!).OnTimerFired(), this, dueTime, System.Threading.Timeout.InfiniteTimeSpan); diff --git a/src/ReactiveUI.Primitives.Core/Signals/CommandSignal{TResult}.cs b/src/ReactiveUI.Primitives.Core/Signals/CommandSignal{TResult}.cs index fb1c03b..dd980e6 100644 --- a/src/ReactiveUI.Primitives.Core/Signals/CommandSignal{TResult}.cs +++ b/src/ReactiveUI.Primitives.Core/Signals/CommandSignal{TResult}.cs @@ -157,10 +157,7 @@ public CommandExecution ExecuteAsync(CancellationToken cancellationToke return _executeSync is not null ? new(ExecuteSync(cancellationToken)) - : new CommandExecution( - ExecuteAsyncCore( - _executeAsync ?? throw new InvalidOperationException("The command has no execution delegate."), - cancellationToken)); + : new CommandExecution(ExecuteAsyncCore(cancellationToken)); } catch (Exception error) { @@ -272,17 +269,14 @@ private TResult ExecuteSync(CancellationToken cancellationToken) } /// Executes the asynchronous command path. - /// The constructor-selected delegate, guaranteed non-null for this path. /// Cancellation token for the operation. /// The command result. - private async Task ExecuteAsyncCore( - Func> executeAsync, - CancellationToken cancellationToken) + private async Task ExecuteAsyncCore(CancellationToken cancellationToken) { BeginExecution(); try { - var result = await executeAsync(cancellationToken).ConfigureAwait(false); + var result = await _executeAsync!(cancellationToken).ConfigureAwait(false); PublishResult(result); return result; } diff --git a/src/ReactiveUI.Primitives.Extensions.Core/ConcurrencyLimiter.cs b/src/ReactiveUI.Primitives.Extensions.Core/ConcurrencyLimiter.cs index 881f6ff..dd12056 100644 --- a/src/ReactiveUI.Primitives.Extensions.Core/ConcurrencyLimiter.cs +++ b/src/ReactiveUI.Primitives.Extensions.Core/ConcurrencyLimiter.cs @@ -155,11 +155,7 @@ private void PullNextTask(Subscription subscription) _rator.Current?.ContinueWith( static (ant, state) => { - if (state is not Subscription sub) - { - throw new InvalidOperationException("The continuation state is not a subscription."); - } - + var sub = (Subscription)state!; sub.Limiter.ProcessTaskCompletion(sub, ant); }, subscription, diff --git a/src/ReactiveUI.Primitives.Extensions.Core/Operators/SelectLatestAsyncObservable.cs b/src/ReactiveUI.Primitives.Extensions.Core/Operators/SelectLatestAsyncObservable.cs index 6ddf11d..7385798 100644 --- a/src/ReactiveUI.Primitives.Extensions.Core/Operators/SelectLatestAsyncObservable.cs +++ b/src/ReactiveUI.Primitives.Extensions.Core/Operators/SelectLatestAsyncObservable.cs @@ -111,15 +111,7 @@ public void OnCompleted() } _ = toAwait.ContinueWith( - static (_, state) => - { - if (state is not SelectLatestAsyncSink sink) - { - throw new InvalidOperationException("The continuation state is not a select-latest sink."); - } - - sink.SignalCompleted(); - }, + static (_, s) => ((SelectLatestAsyncSink)s!).SignalCompleted(), this, TaskScheduler.Default); } diff --git a/src/ReactiveUI.Primitives.Reactive/Concurrency/WasmScheduler.cs b/src/ReactiveUI.Primitives.Reactive/Concurrency/WasmScheduler.cs index 6d63e83..6fa9d2d 100644 --- a/src/ReactiveUI.Primitives.Reactive/Concurrency/WasmScheduler.cs +++ b/src/ReactiveUI.Primitives.Reactive/Concurrency/WasmScheduler.cs @@ -63,8 +63,7 @@ public sealed class WasmScheduler : LocalScheduler, ISchedulerPeriodic, IDisposa "The drain timer is created disarmed, so nothing can call back into it until Schedule arms it after construction.")] internal WasmScheduler() => _drainTimer = new( - static state => ((WasmScheduler)( - state ?? throw new InvalidOperationException("The scheduler state is missing."))).RunDrain(), + static state => ((WasmScheduler)state!).RunDrain(), this, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); @@ -125,13 +124,7 @@ public override IDisposable Schedule( // The timer roots itself while armed through the callback's target (the work item), which stores the // timer; the item's Dispose cancels and releases it. - item.AttachTimer( - new( - static state => ((IReadyWorkItem)( - state ?? throw new InvalidOperationException("The work-item state is missing."))).Run(), - item, - dt, - Timeout.InfiniteTimeSpan)); + item.AttachTimer(new(static s => ((IReadyWorkItem)s!).Run(), item, dt, Timeout.InfiniteTimeSpan)); return item; } @@ -424,8 +417,7 @@ internal static PeriodicWorkItem Start(TState state, TimeSpan period, Fu { PeriodicWorkItem item = new(state, action); Timer timer = new( - static state => ((PeriodicWorkItem)( - state ?? throw new InvalidOperationException("The periodic state is missing."))).Tick(), + static s => ((PeriodicWorkItem)s!).Tick(), item, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); diff --git a/src/ReactiveUI.Primitives/Concurrency/SynchronizationContextSequencer.cs b/src/ReactiveUI.Primitives/Concurrency/SynchronizationContextSequencer.cs index a4cc66e..85da073 100644 --- a/src/ReactiveUI.Primitives/Concurrency/SynchronizationContextSequencer.cs +++ b/src/ReactiveUI.Primitives/Concurrency/SynchronizationContextSequencer.cs @@ -40,10 +40,7 @@ public void Schedule(IWorkItem item) { ArgumentExceptionHelper.ThrowIfNull(item); - Context.Post( - static state => ExecutePosted((IWorkItem)( - state ?? throw new InvalidOperationException("The posted work item is missing."))), - item); + Context.Post(static state => ExecutePosted((IWorkItem)state!), item); } /// diff --git a/src/ReactiveUI.Primitives/Concurrency/TaskPoolSequencer.cs b/src/ReactiveUI.Primitives/Concurrency/TaskPoolSequencer.cs index 770bbed..1d928ef 100644 --- a/src/ReactiveUI.Primitives/Concurrency/TaskPoolSequencer.cs +++ b/src/ReactiveUI.Primitives/Concurrency/TaskPoolSequencer.cs @@ -50,8 +50,7 @@ public void Schedule(IWorkItem item) ArgumentExceptionHelper.ThrowIfNull(item); _ = _taskFactory.StartNew( - static state => ((DispatchState)( - state ?? throw new InvalidOperationException("The dispatch state is missing."))).Run(), + static state => ((DispatchState)state!).Run(), new DispatchState(this, item), _taskFactory.CancellationToken, _taskFactory.CreationOptions, diff --git a/src/ReactiveUI.Primitives/Concurrency/ThreadPoolSequencer.cs b/src/ReactiveUI.Primitives/Concurrency/ThreadPoolSequencer.cs index d5c8b27..af37f14 100644 --- a/src/ReactiveUI.Primitives/Concurrency/ThreadPoolSequencer.cs +++ b/src/ReactiveUI.Primitives/Concurrency/ThreadPoolSequencer.cs @@ -17,8 +17,7 @@ public sealed class ThreadPoolSequencer : ISequencer, IDisposable public static readonly ThreadPoolSequencer Instance = new(); /// Cached queue callback for immediate work. - private static readonly WaitCallback ImmediateCallback = static state => ExecuteQueued( - (IWorkItem)(state ?? throw new InvalidOperationException("The queued work item is missing."))); + private static readonly WaitCallback ImmediateCallback = static state => ExecuteQueued((IWorkItem)state!); /// Guards access to delayed work. private readonly Lock _gate = new(); @@ -48,8 +47,7 @@ public sealed class ThreadPoolSequencer : ISequencer, IDisposable "The timer is created disarmed, so nothing can call back into it until Schedule arms it after construction.")] internal ThreadPoolSequencer() => _timer = new( - static state => ((ThreadPoolSequencer)( - state ?? throw new InvalidOperationException("The sequencer state is missing."))).RunDue(), + static state => ((ThreadPoolSequencer)state!).RunDue(), this, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); diff --git a/src/ReactiveUI.Primitives/Concurrency/WasmSequencer.cs b/src/ReactiveUI.Primitives/Concurrency/WasmSequencer.cs index aab3f19..9f12b2d 100644 --- a/src/ReactiveUI.Primitives/Concurrency/WasmSequencer.cs +++ b/src/ReactiveUI.Primitives/Concurrency/WasmSequencer.cs @@ -49,8 +49,7 @@ public sealed class WasmSequencer : ISequencer, IDisposable internal WasmSequencer() { _timer = new( - static state => ((WasmSequencer)( - state ?? throw new InvalidOperationException("The sequencer state is missing."))).RunDrain(), + static state => ((WasmSequencer)state!).RunDrain(), this, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/CombiningOperatorTests.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/CombiningOperatorTests.cs index 9b7f919..29f7845 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/CombiningOperatorTests.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/CombiningOperatorTests.cs @@ -152,7 +152,7 @@ private sealed class ThrowingEnumerator : IEnumerator> public IObservableAsync Current => null!; /// - object System.Collections.IEnumerator.Current => Current; + object System.Collections.IEnumerator.Current => Current!; /// public bool MoveNext() => throw new InvalidOperationException("enumerator MoveNext boom"); diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/TakeUntilOperatorTests.CompletionDelegate.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/TakeUntilOperatorTests.CompletionDelegate.cs index b5e243d..63e5d14 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/TakeUntilOperatorTests.CompletionDelegate.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/TakeUntilOperatorTests.CompletionDelegate.cs @@ -725,7 +725,7 @@ public async Task WhenTakeUntilCompletionDelegateNotifiesTwice_ThenSecondNotific notifyStop!(Result.Success); await firstCompletion.Task.WaitAsync(WaitTimeout); - notifyStop(Result.Failure(new InvalidOperationException("second stop"))); + notifyStop!(Result.Failure(new InvalidOperationException("second stop"))); var leaked = await AsyncTestHelpers.WaitForConditionAsync( () => errors.Count > 0 || completions.Count > 1, SecondNotificationSettleWindow); diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/TakeUntilOperatorTests.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/TakeUntilOperatorTests.cs index 00d63db..6d9f3fa 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/TakeUntilOperatorTests.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/TakeUntilOperatorTests.cs @@ -204,7 +204,7 @@ public async Task WhenTakeUntilObservableDisposed_ThenStopsEmissions() public void WhenTaskStopSignalNullSource_ThenThrowsArgumentNull() { const IObservableAsync Source = null!; - _ = Assert.Throws(static () => Source.TakeUntil(Task.CompletedTask)); + _ = Assert.Throws(static () => Source!.TakeUntil(Task.CompletedTask)); } /// Tests that task failure with SourceFailsWhenOtherFails=true completes with failure. diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/TaskSignalSubscriptionTests.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/TaskSignalSubscriptionTests.cs index a4853a4..196814a 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/TaskSignalSubscriptionTests.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/TaskSignalSubscriptionTests.cs @@ -24,8 +24,7 @@ public async Task WhenDisposedReentrantlyAfterThreadHop_ThenDoesNotDeadlock() { await subscriptionReady.Task.ConfigureAwait(false); await Task.Yield(); - await (subscription ?? throw new InvalidOperationException("The subscription is not ready.")) - .DisposeAsync().ConfigureAwait(false); + await subscription!.DisposeAsync().ConfigureAwait(false); disposed.SetResult(); }); diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/TransformationOperatorTests.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/TransformationOperatorTests.cs index ecf892b..2ba139d 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/TransformationOperatorTests.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/TransformationOperatorTests.cs @@ -553,8 +553,7 @@ public async Task WhenPrependCancelledBeforeAllValues_ThenEarlyReturn() received.Add(x); if (received.Count == 3) { - await (subscription ?? throw new InvalidOperationException("The subscription is not ready.")) - .DisposeAsync(); + await subscription!.DisposeAsync(); } }, null); diff --git a/src/tests/ReactiveUI.Primitives.Maui.Reactive.Tests/MauiDispatcherSequencerTests.cs b/src/tests/ReactiveUI.Primitives.Maui.Reactive.Tests/MauiDispatcherSequencerTests.cs index 524f185..8a2ae40 100644 --- a/src/tests/ReactiveUI.Primitives.Maui.Reactive.Tests/MauiDispatcherSequencerTests.cs +++ b/src/tests/ReactiveUI.Primitives.Maui.Reactive.Tests/MauiDispatcherSequencerTests.cs @@ -29,7 +29,7 @@ public async Task ConstructorRejectsNullDispatcher() => public async Task ToSequencerValidatesAndAdaptsDispatcher() { const IDispatcher nullDispatcher = null!; - await Assert.That(static () => nullDispatcher.ToSequencer()).ThrowsExactly(); + await Assert.That(static () => nullDispatcher!.ToSequencer()).ThrowsExactly(); FakeDispatcher dispatcher = new(); var scheduler = dispatcher.ToSequencer(); diff --git a/src/tests/ReactiveUI.Primitives.Maui.Tests/MauiDispatcherSequencerTests.cs b/src/tests/ReactiveUI.Primitives.Maui.Tests/MauiDispatcherSequencerTests.cs index 63bac59..b99a517 100644 --- a/src/tests/ReactiveUI.Primitives.Maui.Tests/MauiDispatcherSequencerTests.cs +++ b/src/tests/ReactiveUI.Primitives.Maui.Tests/MauiDispatcherSequencerTests.cs @@ -29,7 +29,7 @@ public async Task ConstructorRejectsNullDispatcher() => public async Task ToSequencerValidatesAndAdaptsDispatcher() { const IDispatcher nullDispatcher = null!; - await Assert.That(static () => nullDispatcher.ToSequencer()).ThrowsExactly(); + await Assert.That(static () => nullDispatcher!.ToSequencer()).ThrowsExactly(); FakeDispatcher dispatcher = new(); var sequencer = dispatcher.ToSequencer(); diff --git a/src/tests/ReactiveUI.Primitives.Tests/CommandExecutionTests.cs b/src/tests/ReactiveUI.Primitives.Tests/CommandExecutionTests.cs index 0277449..5d99dca 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/CommandExecutionTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/CommandExecutionTests.cs @@ -46,7 +46,7 @@ public async Task AwaitingAFailedSynchronousCommandRethrowsTheOriginalException( await Assert.That(execution.GetAwaiter().IsCompleted).IsTrue(); var thrown = Assert.Throws(() => execution.GetAwaiter().GetResult()); - await Assert.That(thrown).IsSameReferenceAs(fault); + await Assert.That(thrown!).IsSameReferenceAs(fault); } /// @@ -72,7 +72,7 @@ public async Task ConfigureAwaitPreservesTheOutcomeOfEveryExecutionShape() using CommandSignal failing = new(() => throw fault); var configured = failing.ExecuteAsync().ConfigureAwait(false); var thrown = Assert.Throws(() => configured.GetAwaiter().GetResult()); - await Assert.That(thrown).IsSameReferenceAs(fault); + await Assert.That(thrown!).IsSameReferenceAs(fault); } /// diff --git a/src/tests/ReactiveUI.Primitives.Tests/SafeWitnessTests.cs b/src/tests/ReactiveUI.Primitives.Tests/SafeWitnessTests.cs index 7555363..61e5557 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/SafeWitnessTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/SafeWitnessTests.cs @@ -92,7 +92,7 @@ public async Task DelegateWitnessMadeSafeStopsAfterItsValueCallbackThrows() // A second push must not reach the callback, so it must not throw either. safe.OnNext(LateValue); - await Assert.That(thrown.Message).IsEqualTo("next-failed"); + await Assert.That(thrown!.Message).IsEqualTo("next-failed"); await Assert.That(calls).IsEqualTo(1); } @@ -153,6 +153,6 @@ public async Task CancelFreeSafeWitnessStopsAfterTheObserverThrows() // The witness has latched stopped, so this must be a silent no-op rather than a second throw. safe.OnNext(LateValue); - await Assert.That(thrown.Message).IsEqualTo("observer-next"); + await Assert.That(thrown!.Message).IsEqualTo("observer-next"); } } diff --git a/src/tests/ReactiveUI.Primitives.Tests/SignalFromTaskTest.cs b/src/tests/ReactiveUI.Primitives.Tests/SignalFromTaskTest.cs index 86be8b0..2c873ab 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/SignalFromTaskTest.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/SignalFromTaskTest.cs @@ -909,7 +909,7 @@ public async Task ImmediateTaskSignalErrorsWhenTheTokenIsCancelledBeforeTheResul ConcurrentQueue values = new(); ConcurrentQueue errors = new(); _ = taskSignal.Subscribe(values.Enqueue, errors.Enqueue, static () => { }); - await taskSignal.CancellationTokenSource.CancelAsync(); + await taskSignal.CancellationTokenSource!.CancelAsync(); completion.SetResult(SuccessValue); await TestPolling.SpinUntil(() => !errors.IsEmpty, PollTimeout).ConfigureAwait(false); await Assert.That(errors.Count).IsEqualTo(1); diff --git a/src/tests/ReactiveUI.Primitives.Tests/SignalRecoverTests.cs b/src/tests/ReactiveUI.Primitives.Tests/SignalRecoverTests.cs index 9a63ed6..26eb997 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/SignalRecoverTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/SignalRecoverTests.cs @@ -88,7 +88,7 @@ public async Task OnCleanupRunsTheActionWhenSubscribingToTheSourceThrows() var caught = Assert.Throws(() => failingSource.OnCleanup(() => cleanupRuns++).Subscribe(static _ => { })); - await Assert.That(caught).IsSameReferenceAs(expected); + await Assert.That(caught!).IsSameReferenceAs(expected); await Assert.That(cleanupRuns).IsEqualTo(1); } diff --git a/src/tests/ReactiveUI.Primitives.Tests/SinkObserverTests.cs b/src/tests/ReactiveUI.Primitives.Tests/SinkObserverTests.cs index 6db45b4..a97c94a 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/SinkObserverTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/SinkObserverTests.cs @@ -523,7 +523,7 @@ public async Task BufferSinkReleasesTheUpstreamWhenTheObserverThrowsOnACompleted var thrown = Assert.Throws(() => sink.OnNext(Two)); - await Assert.That(thrown.Message).IsEqualTo(ObserverNextMessage); + await Assert.That(thrown!.Message).IsEqualTo(ObserverNextMessage); await Assert.That(subscription.DisposeCount).IsEqualTo(1); } @@ -545,7 +545,7 @@ public async Task BufferSinkDropsValuesFromASourceThatKeepsPushingAfterTheObserv source.Next(One); var thrown = Assert.Throws(() => source.Next(Two)); - await Assert.That(thrown.Message).IsEqualTo(ObserverNextMessage); + await Assert.That(thrown!.Message).IsEqualTo(ObserverNextMessage); await Assert.That(observer.NextCount).IsEqualTo(One); // The sink disposed its upstream; this source ignores disposal and pushes on regardless. @@ -683,7 +683,7 @@ private static async Task AssertObserverFailureReleasesUpstream( { var thrown = Assert.Throws(() => sink.OnNext(1)); - await Assert.That(thrown.Message).IsEqualTo("observer-next"); + await Assert.That(thrown!.Message).IsEqualTo("observer-next"); await Assert.That(subscription.DisposeCount).IsEqualTo(1); } diff --git a/src/tests/ReactiveUI.Primitives.Tests/SyncLatestTests.cs b/src/tests/ReactiveUI.Primitives.Tests/SyncLatestTests.cs index 32b3afb..7bfb912 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/SyncLatestTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/SyncLatestTests.cs @@ -242,7 +242,7 @@ public async Task SyncLatestUnsubscribesEarlierSourcesWhenALaterSourceThrowsOnSu third, static (a, b, c) => a + b + c); var thrown = Assert.Throws(() => _ = combined.Subscribe(static _ => { })); - await Assert.That(thrown).IsSameReferenceAs(expected); + await Assert.That(thrown!).IsSameReferenceAs(expected); await Assert.That(first.HasObservers).IsFalse(); await Assert.That(third.HasObservers).IsFalse(); } diff --git a/src/tests/ReactiveUI.Primitives.Tests/UseSignalTests.cs b/src/tests/ReactiveUI.Primitives.Tests/UseSignalTests.cs index a6807bf..a625cec 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/UseSignalTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/UseSignalTests.cs @@ -115,7 +115,7 @@ public async Task UseReleasesTheResourceWhenTheObserverThrows() // The sink has stopped, so this must be a silent no-op rather than a second trip into the observer. sink.OnNext(LateValue); - await Assert.That(thrown.Message).IsEqualTo("observer-next"); + await Assert.That(thrown!.Message).IsEqualTo("observer-next"); await Assert.That(resource.DisposeCount).IsEqualTo(1); await Assert.That(source.DisposeCount).IsEqualTo(1); } From 136182a077b6445232c042de874dbd9e1bac51e8 Mon Sep 17 00:00:00 2001 From: Glenn Watson <5834289+glennawatson@users.noreply.github.com> Date: Sat, 25 Jul 2026 17:41:11 +1000 Subject: [PATCH 4/8] build: organize analyzer configuration - Group diagnostics by analyzer source and category with official IDE rule names. - Preserve the null-forgiving callback cast while forwarding cancellation tokens. --- .editorconfig | 513 ++++++++---------- .../Signals/SignalExtensions{FromTask}.cs | 7 +- 2 files changed, 229 insertions(+), 291 deletions(-) diff --git a/.editorconfig b/.editorconfig index dae5bee..2057a87 100644 --- a/.editorconfig +++ b/.editorconfig @@ -183,8 +183,9 @@ csharp_space_between_square_brackets = false dotnet_diagnostic.AvoidAsyncVoid.severity = suggestion ################### -# Microsoft .NET Analyzers (CA) - Design Rules +# Microsoft.CodeAnalysis.NetAnalyzers (CA) ################### +# Design dotnet_diagnostic.CA1000.severity = none # Do not declare static members on generic types — common factory pattern dotnet_diagnostic.CA1001.severity = error # Types that own disposable fields should be disposable dotnet_diagnostic.CA1002.severity = none # Do not expose generic lists — we deliberately expose List; interface-based collections are an older convention we don't follow @@ -237,22 +238,16 @@ dotnet_diagnostic.CA1068.severity = error # CancellationToken parameters must co dotnet_diagnostic.CA1069.severity = error # Enums should not have duplicate values dotnet_diagnostic.CA1070.severity = error # Do not declare event fields as virtual -################### -# Microsoft .NET Analyzers (CA) - Globalization Rules -################### +# Globalization dotnet_diagnostic.CA1303.severity = none # Do not pass literals as localized parameters — we don't ship localized resources dotnet_diagnostic.CA1307.severity = none # Covered by PSH1207 (canonical) dotnet_diagnostic.CA1308.severity = none # Normalize strings to uppercase — ToLowerInvariant is correct for filesystem path / cache key normalization dotnet_diagnostic.CA1310.severity = none # Covered by PSH1207 (canonical) -################### -# Microsoft .NET Analyzers (CA) - Interoperability Rules -################### +# Interoperability dotnet_diagnostic.CA1401.severity = error # P/Invokes should not be visible -################### -# Microsoft .NET Analyzers (CA) - Maintainability Rules -################### +# Maintainability dotnet_diagnostic.CA1500.severity = none # Variable names should not match field names — covered by SST1484 dotnet_diagnostic.CA1501.severity = none # Covered by SST1446 (canonical) dotnet_diagnostic.CA1502.severity = none # Covered by SST1442 (canonical) @@ -269,15 +264,11 @@ dotnet_diagnostic.CA1514.severity = none # Avoid redundant length argument — c dotnet_diagnostic.CA1515.severity = none # Consider making public types internal — interferes with tests and reflection-discovered types (BenchmarkDotNet, TUnit, etc.) dotnet_diagnostic.CA1516.severity = error # Use cross-platform intrinsics -################### -# Microsoft .NET Analyzers (CA) - Naming Rules -################### +# Naming dotnet_diagnostic.CA1710.severity = suggestion # Identifiers should have correct suffix dotnet_diagnostic.CA1724.severity = none # Type Names Should Not Match Namespaces — namespace/type name overlap is intentional API surface -################### -# Microsoft .NET Analyzers (CA) - Performance Rules -################### +# Performance dotnet_diagnostic.CA1802.severity = none # Use literals where appropriate — covered by PSH1402 dotnet_diagnostic.CA1805.severity = none # Do not initialize unnecessarily — covered by PSH1403 dotnet_diagnostic.CA1806.severity = error # Do not ignore method results @@ -346,9 +337,7 @@ dotnet_diagnostic.CA1874.severity = none # Use 'Regex.IsMatch' — covered by PS dotnet_diagnostic.CA1875.severity = none # Use 'Regex.Count' — covered by PSH1406 dotnet_diagnostic.CA1877.severity = none # Use 'Encoding.GetString' instead of 'Encoding.GetChars' — covered by PSH1225 -################### -# Microsoft .NET Analyzers (CA) - Reliability Rules -################### +# Reliability dotnet_diagnostic.CA2000.severity = suggestion # Dispose objects before losing scope dotnet_diagnostic.CA2002.severity = error # Do not lock on objects with weak identity dotnet_diagnostic.CA2007.severity = none # Do not directly await a Task — Rx and library callers drive synchronization context themselves @@ -371,9 +360,7 @@ dotnet_diagnostic.CA2024.severity = error # Do not use 'StreamReader.EndOfStream dotnet_diagnostic.CA2025.severity = error # Do not pass 'IDisposable' instances into unawaited tasks dotnet_diagnostic.CA2026.severity = error # Do not use methods or types annotated with [RequiresDynamicCode] in code that uses [RequiresDynamicCode] -################### -# Microsoft .NET Analyzers (CA) - Usage Rules -################### +# Usage dotnet_diagnostic.CA1801.severity = error # Review unused parameters dotnet_code_quality.CA1801.api_surface = private, internal # only flag non-public APIs so we don't break public signatures dotnet_diagnostic.CA1816.severity = error # Call GC.SuppressFinalize correctly @@ -426,9 +413,7 @@ dotnet_diagnostic.CA2268.severity = error # Use 'string.Equals(string, string, S # Skipped (deprecated ISerializable formatter): CA2229 Implement serialization constructors, # CA2235 Mark all non-serializable fields, CA2237 Mark ISerializable types with SerializableAttribute. -################### -# Microsoft .NET Analyzers (CA) - Security Rules -################### +# Security # SQL Injection & Command Injection dotnet_diagnostic.CA2100.severity = error # Review SQL queries for security vulnerabilities @@ -551,16 +536,9 @@ dotnet_diagnostic.CA2153.severity = error # Do not catch corrupted state excepti dotnet_diagnostic.CA5367.severity = error # Do not serialize types with pointer fields ################### -# SonarAnalyzer (Sxxxx) — global suppressions -################### -dotnet_diagnostic.S1075.severity = none # Hardcoded URI — canonical SourceLink hosts are the point -dotnet_diagnostic.S2436.severity = none # Too many generic parameters — needed for the projector overload -dotnet_diagnostic.S4036.severity = none # PATH-relative process spawn — benchmark only, trusted env -dotnet_diagnostic.S8969.severity = none # Nullability inference is inconsistent across the repository's target frameworks - -################### -# Microsoft .NET Runtime Obsoletions (SYSLIB0xxx) +# Microsoft .NET SDK Diagnostics (SYSLIB) ################### +# Runtime obsoletions dotnet_diagnostic.SYSLIB0001.severity = error # The UTF-7 encoding is insecure and should not be used dotnet_diagnostic.SYSLIB0002.severity = error # PrincipalPermissionAttribute is not honored by the runtime and must not be used dotnet_diagnostic.SYSLIB0003.severity = error # Code Access Security (CAS) is not supported or honored by the runtime @@ -623,9 +601,7 @@ dotnet_diagnostic.SYSLIB0059.severity = error # SystemEvents.EventsThreadShutdow dotnet_diagnostic.SYSLIB0060.severity = error # Constructors of DirectoryServices.ActiveDirectory.ConfigurationContext are obsolete dotnet_diagnostic.SYSLIB0061.severity = error # CryptographyConfig.AddOID and AddAlgorithm methods are obsolete -################### -# Microsoft .NET Source Generator Diagnostics (SYSLIB1xxx) -################### +# Source generators # LoggerMessage source generator dotnet_diagnostic.SYSLIB1001.severity = error # Logging method names cannot start with _ dotnet_diagnostic.SYSLIB1002.severity = error # Don't include log level parameters as templates in the logging message @@ -713,8 +689,9 @@ dotnet_diagnostic.SYSLIB1103.severity = error # Configuration binding source gen dotnet_diagnostic.SYSLIB1104.severity = error # Configuration binding source generator: language version is too low ################### -# Microsoft .NET Style Rules (IDExxxx) - Language Rules +# Microsoft .NET Code Style Analyzers (IDE) ################### +# Language rules # EnforceCodeStyleInBuild=true (set in Directory.Build.props) promotes these # IDE rules into `dotnet build` so they fire at compile time, not just in the # IDE. We enable the ones that are unambiguous wins and skip the rules that @@ -779,9 +756,71 @@ dotnet_diagnostic.IDE0310.severity = none # Convert lambda expression to method dotnet_diagnostic.IDE0360.severity = none # Simplify property accessor — covered by SST2219 dotnet_diagnostic.IDE0370.severity = none # Remove unnecessary suppression (null-forgiving operator) — disabled for the same reason as RCS1249: multi-TFM nullability annotations can differ per platform -################### -# Microsoft .NET Style Rules (IDE1xxx / IDE3xxx) - Naming & Miscellaneous -################### +# Language and unnecessary code rules. +dotnet_diagnostic.IDE0001.severity = none # Simplify name +dotnet_diagnostic.IDE0003.severity = none # Name can be simplified +dotnet_diagnostic.IDE0005.severity = none # Remove unnecessary import +dotnet_diagnostic.IDE0010.severity = none # Add missing cases to switch statement +dotnet_diagnostic.IDE0011.severity = none # Add braces +dotnet_diagnostic.IDE0017.severity = none # Use object initializers +dotnet_diagnostic.IDE0018.severity = none # Inline variable declaration +dotnet_diagnostic.IDE0020.severity = none # Use pattern matching to avoid is check followed by a cast (with variable) +dotnet_diagnostic.IDE0027.severity = none # Use expression body for accessors +dotnet_diagnostic.IDE0029.severity = none # Null check can be simplified +dotnet_diagnostic.IDE0030.severity = none # Null check can be simplified +dotnet_diagnostic.IDE0032.severity = none # Use auto property +dotnet_diagnostic.IDE0033.severity = none # Use explicitly provided tuple name +dotnet_diagnostic.IDE0034.severity = none # Simplify default expression +dotnet_diagnostic.IDE0036.severity = none # Order modifiers +dotnet_diagnostic.IDE0037.severity = none # Use inferred member name +dotnet_diagnostic.IDE0039.severity = none # Use local function instead of lambda +dotnet_diagnostic.IDE0040.severity = none # Add accessibility modifiers +dotnet_diagnostic.IDE0045.severity = none # Use conditional expression for assignment +dotnet_diagnostic.IDE0046.severity = none # Use conditional expression for return +dotnet_diagnostic.IDE0051.severity = none # Remove unused private member +dotnet_diagnostic.IDE0053.severity = none # Use expression body for lambdas +dotnet_diagnostic.IDE0054.severity = none # Use compound assignment +dotnet_diagnostic.IDE0056.severity = none # Use index operator +dotnet_diagnostic.IDE0062.severity = none # Make local function static +dotnet_diagnostic.IDE0063.severity = none # Use simple using statement +dotnet_diagnostic.IDE0065.severity = none # Using directive placement +dotnet_diagnostic.IDE0070.severity = none # Use System.HashCode.Combine +dotnet_diagnostic.IDE0071.severity = none # Simplify interpolation +dotnet_diagnostic.IDE0072.severity = none # Add missing cases to switch expression +dotnet_diagnostic.IDE0073.severity = none # Use file header +dotnet_diagnostic.IDE0074.severity = none # Use coalesce compound assignment +dotnet_diagnostic.IDE0076.severity = none # Remove invalid global SuppressMessageAttribute +dotnet_diagnostic.IDE0077.severity = none # Avoid legacy format target in global SuppressMessageAttribute +dotnet_diagnostic.IDE0080.severity = none # Remove unnecessary suppression operator +dotnet_diagnostic.IDE0082.severity = none # Convert typeof to nameof +dotnet_diagnostic.IDE0083.severity = none # Use pattern matching (not operator) +dotnet_diagnostic.IDE0090.severity = none # Simplify new expression +dotnet_diagnostic.IDE0100.severity = none # Remove unnecessary equality operator +dotnet_diagnostic.IDE0110.severity = none # Remove unnecessary discard +dotnet_diagnostic.IDE0150.severity = none # Prefer null check over type check +dotnet_diagnostic.IDE0161.severity = none # Use file-scoped namespace +dotnet_diagnostic.IDE0170.severity = none # Simplify property pattern +dotnet_diagnostic.IDE0180.severity = none # Use tuple to swap values +dotnet_diagnostic.IDE0200.severity = none # Remove unnecessary lambda expression +dotnet_diagnostic.IDE0220.severity = none # Add explicit cast in foreach loop +dotnet_diagnostic.IDE0230.severity = none # Use UTF-8 string literal +dotnet_diagnostic.IDE0240.severity = none # Nullable directive is redundant +dotnet_diagnostic.IDE0241.severity = none # Nullable directive is unnecessary +dotnet_diagnostic.IDE0251.severity = none # Member can be made readonly +dotnet_diagnostic.IDE0270.severity = none # Null check can be simplified +dotnet_diagnostic.IDE0280.severity = none # Use nameof +dotnet_diagnostic.IDE0290.severity = none # Use primary constructor +dotnet_diagnostic.IDE0301.severity = none # Use collection expression for empty +dotnet_diagnostic.IDE0302.severity = none # Use collection expression for stackalloc +dotnet_diagnostic.IDE0303.severity = none # Use collection expression for Create() +dotnet_diagnostic.IDE0304.severity = none # Use collection expression for builder +dotnet_diagnostic.IDE0305.severity = none # Use collection expression for fluent +dotnet_diagnostic.IDE0340.severity = none # Use unbound generic type +dotnet_diagnostic.IDE0350.severity = none # Use implicitly typed lambda +dotnet_diagnostic.IDE0380.severity = none # Remove unnecessary unsafe modifier +dotnet_diagnostic.IDE1005.severity = none # Use conditional delegate call + +# Naming and miscellaneous # Naming conventions — SA1300 family already enforces PascalCase / interface # prefix / field casing (with our _underscore convention on SA1306/1309/1311 # deliberately disabled). Leaving IDE1006 off because configuring @@ -791,8 +830,9 @@ dotnet_diagnostic.IDE1006.severity = none # Naming rule violation — SA1300 fam dotnet_diagnostic.IDE3000.severity = none # Disabled per project convention — not enforced ################### -# Roslynator Analyzers (RCS1xxx) - Code Simplification +# Roslynator.CSharp.Analyzers (RCS) ################### +# Code simplification dotnet_diagnostic.RCS1001.severity = none # Add braces (when expression spans over multiple lines) — covered by SST1519 dotnet_diagnostic.RCS1003.severity = none # Add braces to if-else (when expression spans over multiple lines) — covered by SST1519 dotnet_diagnostic.RCS1005.severity = error # Simplify nested using statement @@ -802,6 +842,7 @@ dotnet_diagnostic.RCS1031.severity = none # Remove unnecessary braces in switch dotnet_diagnostic.RCS1032.severity = error # Remove redundant parentheses dotnet_diagnostic.RCS1033.severity = none # Remove redundant boolean literal — covered by SST1143 dotnet_diagnostic.RCS1039.severity = none # Remove argument list from attribute — covered by SST1411 +dotnet_diagnostic.RCS1040.severity = none # Remove empty statement — covered by SST1180 dotnet_diagnostic.RCS1042.severity = none # Remove enum default underlying type — covered by SST1177 dotnet_diagnostic.RCS1043.severity = error # Remove 'partial' modifier from type with a single part dotnet_diagnostic.RCS1049.severity = none # Simplify boolean comparison — covered by SST1143 @@ -863,9 +904,7 @@ dotnet_diagnostic.RCS1262.severity = error # Unnecessary raw string literal dotnet_diagnostic.RCS1265.severity = none # Remove redundant catch block — covered by SST1470 dotnet_diagnostic.RCS1268.severity = error # Simplify numeric comparison -################### -# Roslynator Analyzers (RCS1xxx) - Code Quality -################### +# Code quality dotnet_diagnostic.RCS1013.severity = none # Use predefined type — covered by SST1121 dotnet_diagnostic.RCS1014.severity = error # Use explicitly/implicitly typed array dotnet_diagnostic.RCS1015.severity = error # Use nameof operator @@ -950,9 +989,7 @@ dotnet_diagnostic.RCS1264.severity = error # Use 'var' or explicit type (replace dotnet_diagnostic.RCS1266.severity = none # Use raw string literal — covered by SST2243 dotnet_diagnostic.RCS1267.severity = error # Use string interpolation instead of 'string.Concat' -################### -# Roslynator Analyzers (RCS1xxx) - Performance -################### +# Performance dotnet_diagnostic.RCS1077.severity = none # Covered by the PSH1101-PSH1111 family (canonical) dotnet_diagnostic.RCS1080.severity = none # Covered by PSH1106 (canonical) dotnet_diagnostic.RCS1112.severity = none # Combine 'Enumerable.Where' method chain — covered by PSH1109 @@ -966,9 +1003,7 @@ dotnet_diagnostic.RCS1235.severity = error # Optimize method call dotnet_diagnostic.RCS1236.severity = none # Use exception filter — covered by SST2009 dotnet_diagnostic.RCS1246.severity = none # Use element access — covered by PSH1106 -################### -# Roslynator Analyzers (RCS1xxx) - Maintainability -################### +# Maintainability dotnet_diagnostic.RCS1158.severity = none # Static member in generic type should use a type parameter — common factory pattern — covered by SST1431 dotnet_diagnostic.RCS1163.severity = none # Unused parameter — interface implementations and Rx selectors often have unused parameters dotnet_diagnostic.RCS1164.severity = none # Unused type parameter - DUPLICATE IDE0060 (UnusedParameter analyzer 210ms; IDE0060 bundled at lower cost) @@ -978,9 +1013,7 @@ dotnet_diagnostic.RCS1213.severity = none # Remove unused member declaration - D dotnet_diagnostic.RCS1241.severity = error # Implement non-generic counterpart dotnet_diagnostic.RCS1256.severity = none # Invalid argument null check — conflicts with our ArgumentExceptionHelper helper pattern -################### -# Roslynator Analyzers (RCS1xxx) - Documentation -################### +# Documentation dotnet_diagnostic.RCS1181.severity = error # Convert comment to documentation comment dotnet_diagnostic.RCS1189.severity = error # Add or remove region name dotnet_diagnostic.RCS1226.severity = none # Add paragraph to documentation comment — <para> wrapping is subjective and adds noise @@ -989,9 +1022,7 @@ dotnet_diagnostic.RCS1232.severity = error # Order elements in documentation com dotnet_diagnostic.RCS1253.severity = error # Format documentation comment summary dotnet_diagnostic.RCS1263.severity = none # Invalid reference in a documentation comment -################### -# Roslynator Analyzers (RCS1xxx) - Disabled (covered by CA/SA equivalent) -################### +# Disabled dotnet_diagnostic.RCS1018.severity = none # Add/remove accessibility modifiers — covered by SA1400 dotnet_diagnostic.RCS1019.severity = none # Order modifiers — covered by SA1206 / SA1208 dotnet_diagnostic.RCS1037.severity = none # Remove trailing white-space — covered by SA1028 @@ -1009,9 +1040,7 @@ dotnet_diagnostic.RCS1175.severity = none # Unused 'this' parameter — covered dotnet_diagnostic.RCS1194.severity = none # Implement exception constructors — covered by CA1032 dotnet_diagnostic.RCS1203.severity = none # Use AttributeUsageAttribute — covered by CA1018 -################### -# Roslynator Formatting Analyzers (RCS0xxx) - covered by StyleCop SA equivalents -################### +# Formatting dotnet_diagnostic.RCS0001.severity = none # Add blank line after embedded statement — covered by StyleCop layout rules dotnet_diagnostic.RCS0002.severity = none # Add blank line after #region — covered by StyleCop SA1517 family dotnet_diagnostic.RCS0003.severity = none # Add blank line after using directive list — covered by SA1516 @@ -1095,10 +1124,6 @@ stylesharp.max_file_lines = 1000 # SST1522 (code lines # stylesharp.include_internal = true # SST1499 (set false to report only fields visible outside the assembly) # stylesharp.require_parameterless = true # SST1488 (set false where every exception must carry a message) # stylesharp.include_non_public_types = true # SST1488 (set false to check only externally visible exceptions) -# performancesharp.empty_string_style = pattern # PSH1204 (pattern | length | is_null_or_empty; the last two are only offered where the string is provably not null) -# performancesharp.include_public = false # PSH1411 (set true in an app to seal public types too; a break in a library) -# performancesharp.excluded_properties = Items, Keys # PSH1017 (comma-separated; properties allowed to copy on read) - # Spacing dotnet_diagnostic.SST1000.severity = error # A control-flow keyword is not followed by a space dotnet_diagnostic.SST1001.severity = error # A comma is spaced incorrectly @@ -1132,6 +1157,7 @@ dotnet_diagnostic.SST1028.severity = error # A line ends with trailing whitespac # Readability and maintainability dotnet_diagnostic.SST1100.severity = error # A base. prefix is used where the type does not override the member +dotnet_diagnostic.SST1101.severity = none # see docs/rules/SST1101.md dotnet_diagnostic.SST1102.severity = error # A query clause is separated from the previous clause by a blank line dotnet_diagnostic.SST1103.severity = error # Query clauses mix single-line and multi-line layout dotnet_diagnostic.SST1104.severity = error # A query clause shares the last line of a multi-line previous clause @@ -1249,6 +1275,8 @@ dotnet_diagnostic.SST1216.severity = error # Using static directives should be p dotnet_diagnostic.SST1217.severity = error # Using static directives should be ordered alphabetically dotnet_diagnostic.SST1218.severity = error # Other members separate a method's overloads dotnet_diagnostic.SST1219.severity = error # A switch default clause is not placed last +dotnet_diagnostic.SST1220.severity = error # An all-named argument list is in a different order than the parameters. Code fix reorders it to declaration order. Info. +dotnet_diagnostic.SST1221.severity = error # `where` constraint clauses are not ordered to match the type-parameter list. Code fix reorders them. Info. # Naming dotnet_diagnostic.SST1300.severity = none # Types and members should be PascalCase — naming duplicates existing analyzers @@ -1271,6 +1299,7 @@ dotnet_diagnostic.SST1317.severity = none # Asynchronous method names should end dotnet_diagnostic.SST1318.severity = error # Overriding parameter names should match the base declaration dotnet_diagnostic.SST1319.severity = error # An enumeration's type name is not PascalCase dotnet_diagnostic.SST1320.severity = error # A parameter name matches its method's name +dotnet_diagnostic.SST1321.severity = none # Public APIs intentionally use Async to describe asynchronous observable behavior without returning an awaitable. # Maintainability dotnet_diagnostic.SST1400.severity = error # An element does not declare an access modifier @@ -1306,6 +1335,7 @@ dotnet_diagnostic.SST1430.severity = error # Rethrow with 'throw;' to preserve t dotnet_diagnostic.SST1431.severity = error # Static members of a generic type should use a type parameter dotnet_diagnostic.SST1432.severity = error # Classes with only static members should be static dotnet_diagnostic.SST1433.severity = error # Redundant constructors should be removed +dotnet_diagnostic.SST1434.severity = error # see docs/rules/SST1434.md dotnet_diagnostic.SST1435.severity = error # Empty namespace declarations should be removed dotnet_diagnostic.SST1436.severity = error # Empty types should not be declared dotnet_diagnostic.SST1437.severity = error # Empty interfaces should not be declared @@ -1398,6 +1428,15 @@ dotnet_diagnostic.SST1521.severity = error # A line is longer than the configure dotnet_diagnostic.SST1522.severity = error # A file has more code lines than the configured maximum (default 500) dotnet_diagnostic.SST1523.severity = error # A member has more code lines than the configured maximum (default 60) dotnet_diagnostic.SST1524.severity = error # A switch section has more code lines than the configured maximum (default 20) +dotnet_diagnostic.SST1525.severity = error # A multi-statement `switch` section has no braces; the braces-on policy extends to switch sections. Code fix wraps it. +dotnet_diagnostic.SST1526.severity = none # A wrapped binary expression places the operator inconsistently. Configurable (`before`/`after`, default before). Opt-in. +dotnet_diagnostic.SST1527.severity = none # The `=>` of an expression-bodied member wraps inconsistently. Configurable. Opt-in. +dotnet_diagnostic.SST1528.severity = none # The `=` of a wrapped initializer wraps inconsistently. Configurable. Opt-in. +dotnet_diagnostic.SST1529.severity = none # A wrapped `?.`/`.` call chain places the break inconsistently. Configurable. Opt-in. +dotnet_diagnostic.SST1530.severity = none # A newline sits between a type declaration and its base list. Code fix pulls the base list onto the declaration line. Opt-in. +dotnet_diagnostic.SST1531.severity = none # A short object initializer is split across lines. Code fix collapses it when it fits. Opt-in. +dotnet_diagnostic.SST1532.severity = none # A file mixes line endings. Configurable (`lf`/`crlf`, default lf). Opt-in. +dotnet_diagnostic.SST1533.severity = none # A source file contains no code. Opt-in. # Documentation dotnet_diagnostic.SST1600.severity = error # Externally visible members should be documented @@ -1447,8 +1486,14 @@ dotnet_diagnostic.SST1656.severity = error # Extension block type parameters sho dotnet_diagnostic.SST1657.severity = error # Extension block documentation should reference a real parameter or type parameter dotnet_diagnostic.SST1658.severity = error # Documentation text repeats a word dotnet_diagnostic.SST1659.severity = error # A comment has no text at all +dotnet_diagnostic.SST1660.severity = error # The `` tags are not in parameter order. Code fix reorders them. Info. +dotnet_diagnostic.SST1661.severity = error # A snippet uses ``/`` mismatched to single- vs multi-line content. Code fix swaps the tag. Info. +dotnet_diagnostic.SST1662.severity = none # A thrown exception type has no `` documentation. Code fix adds the skeleton. Opt-in. +dotnet_diagnostic.SST1663.severity = none # A `//` comment before a public member reads like a summary; use `///`. Code fix converts it. Opt-in. +dotnet_diagnostic.SST1664.severity = none # A summary separates paragraphs with blank lines instead of ``. Code fix wraps them. Opt-in. # Concurrency and modernization +dotnet_diagnostic.SST1900.severity = error # see docs/rules/SST1900.md dotnet_diagnostic.SST1901.severity = error # A lock targets a field or property reachable from outside the declaring type dotnet_diagnostic.SST1902.severity = error # Do not lock on 'this', a Type, or a string dotnet_diagnostic.SST1903.severity = error # Do not lock on a newly-created object @@ -1483,10 +1528,13 @@ dotnet_diagnostic.SST1704.severity = error # A class declaring extension blocks dotnet_diagnostic.SST1705.severity = error # A class mixes classic extension methods with extension blocks dotnet_diagnostic.SST1706.severity = error # An extension block targets a broad receiver type such as object or dynamic dotnet_diagnostic.SST1707.severity = error # Extension blocks should be ordered by receiver type +dotnet_diagnostic.SST1708.severity = error # An extension method never uses its `this` receiver, so it need not be an extension. +dotnet_diagnostic.SST1709.severity = none # A method in a `*Extensions` class whose first parameter lacks `this`. Code fix converts it to an extension block. Opt-in. dotnet_diagnostic.SST1800.severity = error # Record classes should be sealed dotnet_diagnostic.SST1801.severity = error # A positional record parameter does not match the configured casing dotnet_diagnostic.SST1802.severity = error # A record declares a settable rather than init-only instance property dotnet_diagnostic.SST1803.severity = error # A record struct is not declared readonly +dotnet_diagnostic.SST1804.severity = error # A positional record has an empty `{ }` body where `;` would do. Code fix rewrites it. Info. dotnet_diagnostic.SST2100.severity = error # An empty collection creation can use [] dotnet_diagnostic.SST2101.severity = error # An explicit collection creation can use [...] dotnet_diagnostic.SST2102.severity = error # A span-targeted stackalloc initializer can use a collection expression @@ -1522,8 +1570,11 @@ dotnet_diagnostic.SST2225.severity = error # A foreach loop hides an explicit el dotnet_diagnostic.SST2226.severity = error # A cast hides an inner explicit conversion dotnet_diagnostic.SST2227.severity = error # A post-assignment null fallback can be folded into the assigned expression dotnet_diagnostic.SST2228.severity = error # A delegate local used only as a call target can be a local function +dotnet_diagnostic.SST2229.severity = error # see docs/rules/SST2229.md +dotnet_diagnostic.SST2230.severity = error # see docs/rules/SST2230.md dotnet_diagnostic.SST2231.severity = error # A broad object pattern can use a direct null pattern dotnet_diagnostic.SST2232.severity = error # nameof does not need concrete generic type arguments +dotnet_diagnostic.SST2233.severity = none # see docs/rules/SST2233.md dotnet_diagnostic.SST2234.severity = error # Nullable should use the T? shorthand dotnet_diagnostic.SST2235.severity = error # Capture-free local functions should be static dotnet_diagnostic.SST2236.severity = error # Tail-position using blocks can use using declarations @@ -1543,8 +1594,36 @@ dotnet_diagnostic.SST2249.severity = error # A literal-format string.Format or l dotnet_diagnostic.SST2250.severity = error # A bare local assigned once by the next statement can be an initialized declaration dotnet_diagnostic.SST2251.severity = error # A method call names type arguments that inference would supply dotnet_diagnostic.SST2252.severity = error # A switch statement is nested inside another switch statement -dotnet_diagnostic.RCS1040.severity = none # covered by SST1180 - +dotnet_diagnostic.SST2254.severity = none # A target-typed `new()` is written where an explicit type reads more clearly; the code fix restores `new TypeName(...)`. Opt-in — the counterpart to SST2202's target-typed direction, so a team enables at most one. +dotnet_diagnostic.SST2255.severity = error # A hand-written null-or-empty string test. Code fix uses `string.IsNullOrEmpty`. +dotnet_diagnostic.SST2256.severity = error # An extension method called in static form. Code fix rewrites to instance form. Info. +dotnet_diagnostic.SST2257.severity = error # A lambda block body that is a single `return`. Code fix uses an expression body. Info. +dotnet_diagnostic.SST2258.severity = error # A redundant explicit delegate wrapper (`new EventHandler(M)`). Code fix drops it. Info. +dotnet_diagnostic.SST2259.severity = error # A stray `;` after a type declaration. Code fix removes it. Info. +dotnet_diagnostic.SST2260.severity = error # An `as` cast to a type the operand already has. Code fix removes it. Info. +dotnet_diagnostic.SST2261.severity = error # `(x && !y) +dotnet_diagnostic.SST2262.severity = error # A raw string literal whose content needs no raw syntax. Code fix demotes it. Info. +dotnet_diagnostic.SST2263.severity = error # An infinite loop whose body re-derives its stop condition. Code fix hoists the condition into the header. Info. +dotnet_diagnostic.SST2264.severity = error # A numeric literal cast to an enum. Code fix names the member. +dotnet_diagnostic.SST2265.severity = none # Consecutive fluent calls on one receiver can fold into a chain. Opt-in. +dotnet_diagnostic.SST2266.severity = none # A local read exactly once can be inlined into that use. Opt-in. +dotnet_diagnostic.SST2267.severity = none # Infinite loops written in mixed `while(true)`/`for(;;)` styles. Configurable. Opt-in. +dotnet_diagnostic.SST2268.severity = none # Inconsistent `()` on object creation with an initializer. Configurable. Opt-in. +dotnet_diagnostic.SST2269.severity = none # Inconsistent parentheses around a conditional's condition. Configurable. Opt-in. +dotnet_diagnostic.SST2270.severity = none # Inconsistent explicit-vs-implicit array-creation type. Configurable. Opt-in. +dotnet_diagnostic.SST2271.severity = none # `var`-vs-explicit local type per the configured preference. Configurable. Opt-in. +dotnet_diagnostic.SST2272.severity = none # `[Flags]` member values written as mixed decimals and shifts. Configurable. Opt-in. +dotnet_diagnostic.SST2273.severity = none # A function or loop body wraps its work in a trailing `if` that could be an early-exit guard clause. Code fix inverts it. Configurable threshold. Opt-in. +dotnet_diagnostic.SST2274.severity = error # A value assigned with `as` and then null-checked is an `is` declaration pattern in one step. Code fix rewrites it. +dotnet_diagnostic.SST2275.severity = error # A method whose block body is a single statement can use an expression body `=> expr`. Code fix rewrites it. +dotnet_diagnostic.SST2276.severity = none # A constructor whose block body is a single statement can use an expression body. Code fix rewrites it. Opt-in. +dotnet_diagnostic.SST2277.severity = none # An operator whose block body is a single `return` can use an expression body. Code fix rewrites it. Opt-in. +dotnet_diagnostic.SST2278.severity = none # A conversion operator whose block body is a single `return` can use an expression body. Code fix rewrites it. Opt-in. +dotnet_diagnostic.SST2279.severity = error # A get-only property whose getter is a single `return` can use a whole-member expression body. Code fix rewrites it. +dotnet_diagnostic.SST2280.severity = error # A get-only indexer whose getter is a single `return` can use a whole-member expression body. Code fix rewrites it. +dotnet_diagnostic.SST2281.severity = error # A local function whose block body is a single statement can use an expression body. Code fix rewrites it. +dotnet_diagnostic.SST2282.severity = error # A reference-type `ReferenceEquals` check against `null` reads as an `is null` / `is not null` pattern. Code fix rewrites it. +dotnet_diagnostic.SST2283.severity = error # A null guard that throws right before assigning the guarded value can fold into the assignment as `?? throw`. Code fix rewrites it. # Design dotnet_diagnostic.SST2300.severity = error # A class implements IDisposable but builds only half of the disposal pattern dotnet_diagnostic.SST2301.severity = error # A class implementing IEquatable for itself can still be derived from @@ -1575,6 +1654,13 @@ dotnet_diagnostic.SST2325.severity = error # An async method checks an argument dotnet_diagnostic.SST2326.severity = none # An interface-typed value is narrowed to a concrete implementation — this is a high-performance core library, not strictly SOLID code, and narrowing to a known runtime type is a normal fast-path technique here: foreach over IEnumerable boxes List's struct enumerator (40 bytes per call, measured) where the indexed loop behind the type test allocates nothing dotnet_diagnostic.SST2327.severity = error # A type tests its own runtime type against a class instead of dispatching through a virtual member dotnet_diagnostic.SST2328.severity = error # A raw native pointer handle is exposed instead of a SafeHandle +dotnet_diagnostic.SST2329.severity = error # A `[Flags]` enum declares no zero-valued member. Code fix adds `None = 0`. +dotnet_diagnostic.SST2330.severity = error # A `[Flags]` member is a numeric literal equal to a combination of others (`All = 7`). Code fix writes `A +dotnet_diagnostic.SST2331.severity = none # An enum leaves member values implicit, so their numbers depend on declaration order. Opt-in. +dotnet_diagnostic.SST2332.severity = error # An auto-property's `private set` is only written during construction; make it get-only. +dotnet_diagnostic.SST2333.severity = none # A generic comparison/equality contract is implemented without its non-generic counterpart. Opt-in. +dotnet_diagnostic.SST2334.severity = none # A publicly visible type has no `[DebuggerDisplay]`. Opt-in. +dotnet_diagnostic.SST2335.severity = none # Parts of a partial type disagree on the `static` modifier. Opt-in. # Correctness dotnet_diagnostic.SST2400.severity = error # Two arguments name each other's parameters and have been transposed @@ -1654,6 +1740,12 @@ dotnet_diagnostic.SST2487.severity = error # A [ConstructorArgument] does not na dotnet_diagnostic.SST2488.severity = error # An exception is logged and rethrown, duplicating the record dotnet_diagnostic.SST2489.severity = error # A relational comparison is decided by the operand's type rather than its value dotnet_diagnostic.SST2490.severity = error # Adjacent try statements with identical handling should be merged +dotnet_diagnostic.SST2491.severity = error # A non-`async` method returns an awaitable from inside `using`/`try-finally`/`lock`, so the resource is torn down before the task completes. Code fix makes it `async`. +dotnet_diagnostic.SST2492.severity = error # A null-guard throws on a parameter the signature declares may be null. +dotnet_diagnostic.SST2493.severity = error # `== null`/`!= null` on an unconstrained generic `T`. Code fix uses `is null`/`is not null`. +dotnet_diagnostic.SST2494.severity = error # A `??` whose left operand is a constant null, so the right is always taken. Code fix folds it. +dotnet_diagnostic.SST2495.severity = error # A `[Flags]` combination includes an operand whose bits another already covers. Code fix removes it. +dotnet_diagnostic.SST2496.severity = error # An explicit `Dispose`/`Close` on a resource an enclosing `using` already disposes. Code fix removes it. Info. # Testing dotnet_diagnostic.SST2500.severity = error # A test method contains no assertion and no expected-exception check @@ -1671,11 +1763,29 @@ dotnet_diagnostic.SST2509.severity = error # A test method has a shape the runne dotnet_diagnostic.SST2600.severity = error # Application output is written through legacy Trace instead of a structured logger dotnet_diagnostic.SST2601.severity = error # A logger field or property does not follow the logger naming convention +# Frameworks +dotnet_diagnostic.SST2700.severity = error # An MVC route template contains a backslash; route segments are separated by `/`, so the route is unreachable. Code fix replaces `\` with `/`. +dotnet_diagnostic.SST2701.severity = error # A `[JSInvokable]` method is not public, so JavaScript interop cannot call it. Code fix makes it public. +dotnet_diagnostic.SST2702.severity = error # A `[SupplyParameterFromQuery]` property has a type the framework cannot bind from the query string, which throws at runtime. +dotnet_diagnostic.SST2703.severity = error # A routable component's route constraint (`{id:int}`) disagrees with the matching `[Parameter]` CLR type, so the route silently fails to match. +dotnet_diagnostic.SST2704.severity = error # A public action on an `[ApiController]` declares no HTTP-verb attribute, so it answers every verb and can make routing ambiguous. +dotnet_diagnostic.SST2705.severity = none # A bound model member is a non-nullable value type with no required marker, so a request that omits it binds the default with no error. Opt-in. +dotnet_diagnostic.SST2706.severity = error # A Windows Forms entry point carries neither `[STAThread]` nor `[MTAThread]`; without STA, clipboard, drag-and-drop, and common dialogs misbehave. Code fix adds `[STAThread]`. +dotnet_diagnostic.SST2707.severity = none # A fire-and-forget `Task.Run` in a controller captures the request's `HttpContext`, which is disposed when the request ends, so the background work throws `ObjectDisposedException`. Opt-in. +dotnet_diagnostic.SST2708.severity = error # A component subscribes to an event in a lifecycle method but never unsubscribes, so the event source keeps the component alive — a per-session leak on a Server circuit. +dotnet_diagnostic.SST2709.severity = error # `StateHasChanged` is called while the component is being disposed, which the renderer no longer supports and throws. +dotnet_diagnostic.SST2710.severity = error # `StateHasChanged` is called directly from a timer callback, off the renderer's dispatcher; marshal it with `InvokeAsync(StateHasChanged)`. +dotnet_diagnostic.SST2711.severity = error # A synchronous component lifecycle method is overridden as `async void`, which the framework never awaits; override the `…Async` twin returning `Task`. Code fix rewrites the signature. +dotnet_diagnostic.SST2712.severity = error # An `[Inject]`/`[CascadingParameter]` property has no setter, so the framework's reflection-based binding leaves it null. Code fix adds a setter. +dotnet_diagnostic.SST2713.severity = error # A `DotNetObjectReference.Create(this)` is passed inline and never stored, so nothing can dispose it and it leaks on the JavaScript side. + ################### -# PerformanceSharp Analyzers (PSH) - severities staged ahead of package adoption; -# inert until the PerformanceSharp.Analyzers package reference is enabled. +# PerformanceSharp Analyzers (PSH) ################### performancesharp.avoid_linq_on_hot_path = true +# performancesharp.empty_string_style = pattern # PSH1204 (pattern | length | is_null_or_empty; the last two are only offered where the string is provably not null) +# performancesharp.excluded_properties = Items, Keys # PSH1017 (comma-separated; properties allowed to copy on read) +# performancesharp.include_public = false # PSH1411 (set true in an app to seal public types too; a break in a library) dotnet_diagnostic.PSH1000.severity = error # Anonymous functions without captures should be static dotnet_diagnostic.PSH1001.severity = error # Avoid allocating zero-length arrays (fix prefers [] on C# 12+, else Array.Empty()) dotnet_diagnostic.PSH1002.severity = error # Empty finalizers should be removed @@ -1698,6 +1808,7 @@ dotnet_diagnostic.PSH1018.severity = error # A hand-written array is passed to a dotnet_diagnostic.PSH1019.severity = error # The range indexer on an array allocates a copy; slice with AsSpan/AsMemory dotnet_diagnostic.PSH1020.severity = error # Prefer a jagged array over a multidimensional one dotnet_diagnostic.PSH1021.severity = error # An explicit GC.Collect or GC.WaitForPendingFinalizers forces collection the runtime tunes itself +dotnet_diagnostic.PSH1022.severity = error # A parameterless `new EventArgs()` allocates where the shared `EventArgs.Empty` singleton would serve. Code fix uses the singleton. dotnet_diagnostic.PSH1100.severity = error # Hot-path code should avoid System.Linq.Enumerable calls dotnet_diagnostic.PSH1101.severity = none # LINQ terminal predicate simplification is reserved for test code; production code should avoid LINQ on hot paths dotnet_diagnostic.PSH1102.severity = none # LINQ type-filter simplification is reserved for test code; production code should avoid LINQ on hot paths @@ -1712,7 +1823,7 @@ dotnet_diagnostic.PSH1110.severity = error # Use the collection's own predicate dotnet_diagnostic.PSH1111.severity = error # Use Contains for membership tests dotnet_diagnostic.PSH1112.severity = error # Seed the collection through its constructor (fix honors performancesharp.prefer_collection_expressions) dotnet_diagnostic.PSH1113.severity = error # Sort naturally instead of ordering by the element itself -# dotnet_diagnostic.PSH1114.severity = error # Freeze static lookup collections (opt-in; freezing trades slower construction for faster reads) +dotnet_diagnostic.PSH1114.severity = none # Freeze static lookup collections that are never mutated. Opt-in. dotnet_diagnostic.PSH1115.severity = error # Insert-if-absent should probe the dictionary once dotnet_diagnostic.PSH1116.severity = error # Probe string-keyed collections with a span through GetAlternateLookup dotnet_diagnostic.PSH1117.severity = error # Ask the collection whether it is empty @@ -1750,16 +1861,18 @@ dotnet_diagnostic.PSH1222.severity = error # Concatenate slices without material dotnet_diagnostic.PSH1223.severity = error # A reused composite format string is re-parsed on every call dotnet_diagnostic.PSH1224.severity = error # Convert bytes to hex in one call, not by building the string twice dotnet_diagnostic.PSH1225.severity = error # Decode bytes to a string in one call, without a throwaway char[] +dotnet_diagnostic.PSH1226.severity = error # A string's `ToCharArray()` result is only iterated, allocating a throwaway `char[]`; iterate the string directly. Code fix drops the copy. +dotnet_diagnostic.PSH1227.severity = error # A cheaper equivalent exists — `string.CompareOrdinal` over `Compare(…, Ordinal)`, `Debug.Fail` over `Debug.Assert(false, …)`. Info. Code fix rewrites the call. dotnet_diagnostic.PSH1300.severity = error # Use System.Threading.Lock for a dedicated lock object dotnet_diagnostic.PSH1301.severity = error # Do not wrap a single task in WhenAll or WaitAll dotnet_diagnostic.PSH1302.severity = error # TaskCompletionSource should run continuations asynchronously dotnet_diagnostic.PSH1303.severity = error # Do not block an async method with Thread.Sleep dotnet_diagnostic.PSH1304.severity = error # Use PeriodicTimer instead of pacing a loop with Task.Delay dotnet_diagnostic.PSH1305.severity = error # Enumerate a ConcurrentDictionary directly, not its Keys/Values snapshots -# dotnet_diagnostic.PSH1306.severity = error # Guard one-time execution with an interlocked latch (opt-in; thread-safety needs are contextual) +dotnet_diagnostic.PSH1306.severity = none # Guard one-time execution with an interlocked latch. Opt-in. dotnet_diagnostic.PSH1307.severity = error # Access interlocked fields with Volatile dotnet_diagnostic.PSH1308.severity = error # Return the completed task instead of Task.FromResult -# dotnet_diagnostic.PSH1309.severity = error # Register cancellation callbacks without flowing the execution context (opt-in; changes AsyncLocal visibility) +dotnet_diagnostic.PSH1309.severity = none # Register cancellation callbacks without flowing the execution context. Opt-in. dotnet_diagnostic.PSH1310.severity = error # Dispose IAsyncDisposable resources with await using in async code dotnet_diagnostic.PSH1311.severity = error # Remove a pass-through async state machine and return the task directly dotnet_diagnostic.PSH1312.severity = error # Return a completed task instead of null @@ -1777,7 +1890,7 @@ dotnet_diagnostic.PSH1406.severity = error # Ask Regex for the answer directly dotnet_diagnostic.PSH1407.severity = error # Query the dictionary, not its Keys view dotnet_diagnostic.PSH1408.severity = error # Measure elapsed time with Stopwatch timestamps dotnet_diagnostic.PSH1409.severity = error # Use the built-in throw helpers for argument guards -# dotnet_diagnostic.PSH1410.severity = error # Mark trivial forwarders for aggressive inlining (opt-in; opinionated convention) +dotnet_diagnostic.PSH1410.severity = none # Mark trivial forwarders for aggressive inlining. Opt-in. dotnet_diagnostic.PSH1411.severity = error # Seal non-public types nothing derives from so the JIT can devirtualize dotnet_diagnostic.PSH1412.severity = error # Use Random.Shared instead of allocating a Random dotnet_diagnostic.PSH1413.severity = error # Read the Unix epoch from the framework, not a hand-built DateTime @@ -1787,6 +1900,7 @@ dotnet_diagnostic.PSH1416.severity = error # Cache the serializer options instea dotnet_diagnostic.PSH1417.severity = error # Do not compute an expensive argument for an assertion dotnet_diagnostic.PSH1418.severity = error # An HttpClient is constructed on every call dotnet_diagnostic.PSH1419.severity = error # A time-zone is resolved with a platform-specific id instead of the cross-platform API +dotnet_diagnostic.PSH1420.severity = error # A shareable client held in an instance field of an Azure Functions worker class is rebuilt on every invocation, leaking sockets and connections; share a static/singleton client or inject `IHttpClientFactory`. # ASP.NET Core - inert in this repo (no route handlers or middleware), enabled so the set stays complete dotnet_diagnostic.PSH1500.severity = error # A minimal API handler returns Results instead of TypedResults, boxing the result @@ -1794,6 +1908,13 @@ dotnet_diagnostic.PSH1501.severity = error # Middleware uses the legacy nested-d dotnet_diagnostic.PSH1502.severity = error # A route handler returns a deferred sequence the serializer enumerates on the request thread dotnet_diagnostic.PSH1503.severity = error # Response caching is used where server-side output caching applies dotnet_diagnostic.PSH1505.severity = error # Exceptions are handled in an MVC exception filter instead of an IExceptionHandler +dotnet_diagnostic.PSH1506.severity = error # The HTTP request or response body is read or written synchronously (`ReadToEnd`, `Body.Read`, `Body.Write`), which blocks a thread on Kestrel and buffers the whole payload; use the async overload. Code fix awaits it when the method is already async. + +# Blazor +dotnet_diagnostic.PSH1600.severity = error # A delegate captured per iteration inside a component render loop reallocates on every render (measured ~128 B per row per render) and churns the diff; hoist it to a cached delegate or a precomputed per-item model. +dotnet_diagnostic.PSH1601.severity = error # A JavaScript-interop call is issued once per loop iteration; on Interactive Server each is a separate SignalR round-trip. Batch into a single call over the collection. +dotnet_diagnostic.PSH1602.severity = error # `StateHasChanged` is called unconditionally in `OnAfterRender`/`OnAfterRenderAsync`, scheduling another render every time — a runaway loop. Guard it with `firstRender` or a state flag. +dotnet_diagnostic.PSH1603.severity = error # A non-delegate allocation is used as a component-parameter value inside a render loop, allocating per item and forcing the child to re-render each pass. Sibling of PSH1600. ################### # SecuritySharp Analyzers (SES) @@ -1835,10 +1956,10 @@ dotnet_diagnostic.SES1303.severity = error # Regular-expression pattern must not dotnet_diagnostic.SES1304.severity = error # An archive entry name must not build a write path without a containment check dotnet_diagnostic.SES1305.severity = error # Do not build a storage path from an uploaded file name dotnet_diagnostic.SES1306.severity = error # Do not compile or execute non-constant C# via the scripting API -dotnet_diagnostic.SES1310.severity = error # A directory bind is performed without authenticating dotnet_diagnostic.SES1307.severity = error # Path.GetTempFileName creates a predictable, world-readable temporary file dotnet_diagnostic.SES1308.severity = error # A file or directory is created group- or world-writable dotnet_diagnostic.SES1309.severity = error # An XSLT stylesheet is loaded with embedded script enabled +dotnet_diagnostic.SES1310.severity = error # A directory bind is performed without authenticating # Serialization dotnet_diagnostic.SES1401.severity = error # A type resolved from non-constant data must not be instantiated or deserialized @@ -1873,20 +1994,7 @@ dotnet_diagnostic.SES1604.severity = error # Prompt-template input encoding must dotnet_diagnostic.SES1605.severity = error # AI instrumentation must not enable sensitive-data capture dotnet_diagnostic.SES1606.severity = error # Do not fetch model weights over cleartext HTTP -# RoslynCommonAnalyzers rules added to reach 3.37.0 (on-by-default enforced; opt-in left off) -dotnet_diagnostic.PSH1022.severity = error # A parameterless `new EventArgs()` allocates where the shared `EventArgs.Empty` singleton would serve. Code fix uses the singleton. -dotnet_diagnostic.PSH1114.severity = none # Freeze static lookup collections that are never mutated. Opt-in. -dotnet_diagnostic.PSH1226.severity = error # A string's `ToCharArray()` result is only iterated, allocating a throwaway `char[]`; iterate the string directly. Code fix drops the copy. -dotnet_diagnostic.PSH1227.severity = error # A cheaper equivalent exists — `string.CompareOrdinal` over `Compare(…, Ordinal)`, `Debug.Fail` over `Debug.Assert(false, …)`. Info. Code fix rewrites the call. -dotnet_diagnostic.PSH1306.severity = none # Guard one-time execution with an interlocked latch. Opt-in. -dotnet_diagnostic.PSH1309.severity = none # Register cancellation callbacks without flowing the execution context. Opt-in. -dotnet_diagnostic.PSH1410.severity = none # Mark trivial forwarders for aggressive inlining. Opt-in. -dotnet_diagnostic.PSH1420.severity = error # A shareable client held in an instance field of an Azure Functions worker class is rebuilt on every invocation, leaking sockets and connections; share a static/singleton client or inject `IHttpClientFactory`. -dotnet_diagnostic.PSH1506.severity = error # The HTTP request or response body is read or written synchronously (`ReadToEnd`, `Body.Read`, `Body.Write`), which blocks a thread on Kestrel and buffers the whole payload; use the async overload. Code fix awaits it when the method is already async. -dotnet_diagnostic.PSH1600.severity = error # A delegate captured per iteration inside a component render loop reallocates on every render (measured ~128 B per row per render) and churns the diff; hoist it to a cached delegate or a precomputed per-item model. -dotnet_diagnostic.PSH1601.severity = error # A JavaScript-interop call is issued once per loop iteration; on Interactive Server each is a separate SignalR round-trip. Batch into a single call over the collection. -dotnet_diagnostic.PSH1602.severity = error # `StateHasChanged` is called unconditionally in `OnAfterRender`/`OnAfterRenderAsync`, scheduling another render every time — a runaway loop. Guard it with `firstRender` or a state flag. -dotnet_diagnostic.PSH1603.severity = error # A non-delegate allocation is used as a component-parameter value inside a render loop, allocating per item and forcing the child to re-render each pass. Sibling of PSH1600. +# Web UI trust boundaries dotnet_diagnostic.SES1701.severity = error # Raw HTML is rendered from a non-constant value (`MarkupString`/`AddMarkupContent`), bypassing automatic encoding — an XSS risk. Sanitizer allow-list via `securitysharp.SES1701.sanitizers`. dotnet_diagnostic.SES1702.severity = error # A JavaScript-interop call targets a script-evaluation primitive (`eval`, `Function`, `document.write`), turning interop into a script-injection channel. dotnet_diagnostic.SES1703.severity = error # `[Authorize]` on a non-routable component enforces nothing — authorization runs as a routing concern. Exempt types via `securitysharp.SES1703.exempt_types`. @@ -1897,164 +2005,20 @@ dotnet_diagnostic.SES1707.severity = error # A secret-shaped literal appears in dotnet_diagnostic.SES1708.severity = error # `CircuitOptions.DetailedErrors` is enabled, shipping server exception detail to every connected client. dotnet_diagnostic.SES1709.severity = error # `SerializeAllClaims` serializes every claim into client-readable WebAssembly authentication state, exposing internal ids, tokens, and PII. dotnet_diagnostic.SES1710.severity = error # Antiforgery validation is disabled on a form (`[RequireAntiforgeryToken(required: false)]`), removing CSRF protection. -dotnet_diagnostic.SST1101.severity = none # see docs/rules/SST1101.md -dotnet_diagnostic.SST1220.severity = error # An all-named argument list is in a different order than the parameters. Code fix reorders it to declaration order. Info. -dotnet_diagnostic.SST1221.severity = error # `where` constraint clauses are not ordered to match the type-parameter list. Code fix reorders them. Info. -dotnet_diagnostic.SST1321.severity = none # Public APIs intentionally use Async to describe asynchronous observable behavior without returning an awaitable. -dotnet_diagnostic.SST1434.severity = error # see docs/rules/SST1434.md -dotnet_diagnostic.SST1525.severity = error # A multi-statement `switch` section has no braces; the braces-on policy extends to switch sections. Code fix wraps it. -dotnet_diagnostic.SST1526.severity = none # A wrapped binary expression places the operator inconsistently. Configurable (`before`/`after`, default before). Opt-in. -dotnet_diagnostic.SST1527.severity = none # The `=>` of an expression-bodied member wraps inconsistently. Configurable. Opt-in. -dotnet_diagnostic.SST1528.severity = none # The `=` of a wrapped initializer wraps inconsistently. Configurable. Opt-in. -dotnet_diagnostic.SST1529.severity = none # A wrapped `?.`/`.` call chain places the break inconsistently. Configurable. Opt-in. -dotnet_diagnostic.SST1530.severity = none # A newline sits between a type declaration and its base list. Code fix pulls the base list onto the declaration line. Opt-in. -dotnet_diagnostic.SST1531.severity = none # A short object initializer is split across lines. Code fix collapses it when it fits. Opt-in. -dotnet_diagnostic.SST1532.severity = none # A file mixes line endings. Configurable (`lf`/`crlf`, default lf). Opt-in. -dotnet_diagnostic.SST1533.severity = none # A source file contains no code. Opt-in. -dotnet_diagnostic.SST1660.severity = error # The `` tags are not in parameter order. Code fix reorders them. Info. -dotnet_diagnostic.SST1661.severity = error # A snippet uses ``/`` mismatched to single- vs multi-line content. Code fix swaps the tag. Info. -dotnet_diagnostic.SST1662.severity = none # A thrown exception type has no `` documentation. Code fix adds the skeleton. Opt-in. -dotnet_diagnostic.SST1663.severity = none # A `//` comment before a public member reads like a summary; use `///`. Code fix converts it. Opt-in. -dotnet_diagnostic.SST1664.severity = none # A summary separates paragraphs with blank lines instead of ``. Code fix wraps them. Opt-in. -dotnet_diagnostic.SST1708.severity = error # An extension method never uses its `this` receiver, so it need not be an extension. -dotnet_diagnostic.SST1709.severity = none # A method in a `*Extensions` class whose first parameter lacks `this`. Code fix converts it to an extension block. Opt-in. -dotnet_diagnostic.SST1804.severity = error # A positional record has an empty `{ }` body where `;` would do. Code fix rewrites it. Info. -dotnet_diagnostic.SST1900.severity = error # see docs/rules/SST1900.md -dotnet_diagnostic.SST2229.severity = error # see docs/rules/SST2229.md -dotnet_diagnostic.SST2230.severity = error # see docs/rules/SST2230.md -dotnet_diagnostic.SST2233.severity = none # see docs/rules/SST2233.md -dotnet_diagnostic.SST2254.severity = none # A target-typed `new()` is written where an explicit type reads more clearly; the code fix restores `new TypeName(...)`. Opt-in — the counterpart to SST2202's target-typed direction, so a team enables at most one. -dotnet_diagnostic.SST2255.severity = error # A hand-written null-or-empty string test. Code fix uses `string.IsNullOrEmpty`. -dotnet_diagnostic.SST2256.severity = error # An extension method called in static form. Code fix rewrites to instance form. Info. -dotnet_diagnostic.SST2257.severity = error # A lambda block body that is a single `return`. Code fix uses an expression body. Info. -dotnet_diagnostic.SST2258.severity = error # A redundant explicit delegate wrapper (`new EventHandler(M)`). Code fix drops it. Info. -dotnet_diagnostic.SST2259.severity = error # A stray `;` after a type declaration. Code fix removes it. Info. -dotnet_diagnostic.SST2260.severity = error # An `as` cast to a type the operand already has. Code fix removes it. Info. -dotnet_diagnostic.SST2261.severity = error # `(x && !y) -dotnet_diagnostic.SST2262.severity = error # A raw string literal whose content needs no raw syntax. Code fix demotes it. Info. -dotnet_diagnostic.SST2263.severity = error # An infinite loop whose body re-derives its stop condition. Code fix hoists the condition into the header. Info. -dotnet_diagnostic.SST2264.severity = error # A numeric literal cast to an enum. Code fix names the member. -dotnet_diagnostic.SST2265.severity = none # Consecutive fluent calls on one receiver can fold into a chain. Opt-in. -dotnet_diagnostic.SST2266.severity = none # A local read exactly once can be inlined into that use. Opt-in. -dotnet_diagnostic.SST2267.severity = none # Infinite loops written in mixed `while(true)`/`for(;;)` styles. Configurable. Opt-in. -dotnet_diagnostic.SST2268.severity = none # Inconsistent `()` on object creation with an initializer. Configurable. Opt-in. -dotnet_diagnostic.SST2269.severity = none # Inconsistent parentheses around a conditional's condition. Configurable. Opt-in. -dotnet_diagnostic.SST2270.severity = none # Inconsistent explicit-vs-implicit array-creation type. Configurable. Opt-in. -dotnet_diagnostic.SST2271.severity = none # `var`-vs-explicit local type per the configured preference. Configurable. Opt-in. -dotnet_diagnostic.SST2272.severity = none # `[Flags]` member values written as mixed decimals and shifts. Configurable. Opt-in. -dotnet_diagnostic.SST2273.severity = none # A function or loop body wraps its work in a trailing `if` that could be an early-exit guard clause. Code fix inverts it. Configurable threshold. Opt-in. -dotnet_diagnostic.SST2274.severity = error # A value assigned with `as` and then null-checked is an `is` declaration pattern in one step. Code fix rewrites it. -dotnet_diagnostic.SST2275.severity = error # A method whose block body is a single statement can use an expression body `=> expr`. Code fix rewrites it. -dotnet_diagnostic.SST2276.severity = none # A constructor whose block body is a single statement can use an expression body. Code fix rewrites it. Opt-in. -dotnet_diagnostic.SST2277.severity = none # An operator whose block body is a single `return` can use an expression body. Code fix rewrites it. Opt-in. -dotnet_diagnostic.SST2278.severity = none # A conversion operator whose block body is a single `return` can use an expression body. Code fix rewrites it. Opt-in. -dotnet_diagnostic.SST2279.severity = error # A get-only property whose getter is a single `return` can use a whole-member expression body. Code fix rewrites it. -dotnet_diagnostic.SST2280.severity = error # A get-only indexer whose getter is a single `return` can use a whole-member expression body. Code fix rewrites it. -dotnet_diagnostic.SST2281.severity = error # A local function whose block body is a single statement can use an expression body. Code fix rewrites it. -dotnet_diagnostic.SST2282.severity = error # A reference-type `ReferenceEquals` check against `null` reads as an `is null` / `is not null` pattern. Code fix rewrites it. -dotnet_diagnostic.SST2283.severity = error # A null guard that throws right before assigning the guarded value can fold into the assignment as `?? throw`. Code fix rewrites it. -dotnet_diagnostic.SST2329.severity = error # A `[Flags]` enum declares no zero-valued member. Code fix adds `None = 0`. -dotnet_diagnostic.SST2330.severity = error # A `[Flags]` member is a numeric literal equal to a combination of others (`All = 7`). Code fix writes `A -dotnet_diagnostic.SST2331.severity = none # An enum leaves member values implicit, so their numbers depend on declaration order. Opt-in. -dotnet_diagnostic.SST2332.severity = error # An auto-property's `private set` is only written during construction; make it get-only. -dotnet_diagnostic.SST2333.severity = none # A generic comparison/equality contract is implemented without its non-generic counterpart. Opt-in. -dotnet_diagnostic.SST2334.severity = none # A publicly visible type has no `[DebuggerDisplay]`. Opt-in. -dotnet_diagnostic.SST2335.severity = none # Parts of a partial type disagree on the `static` modifier. Opt-in. -dotnet_diagnostic.SST2491.severity = error # A non-`async` method returns an awaitable from inside `using`/`try-finally`/`lock`, so the resource is torn down before the task completes. Code fix makes it `async`. -dotnet_diagnostic.SST2492.severity = error # A null-guard throws on a parameter the signature declares may be null. -dotnet_diagnostic.SST2493.severity = error # `== null`/`!= null` on an unconstrained generic `T`. Code fix uses `is null`/`is not null`. -dotnet_diagnostic.SST2494.severity = error # A `??` whose left operand is a constant null, so the right is always taken. Code fix folds it. -dotnet_diagnostic.SST2495.severity = error # A `[Flags]` combination includes an operand whose bits another already covers. Code fix removes it. -dotnet_diagnostic.SST2496.severity = error # An explicit `Dispose`/`Close` on a resource an enclosing `using` already disposes. Code fix removes it. Info. -dotnet_diagnostic.SST2700.severity = error # An MVC route template contains a backslash; route segments are separated by `/`, so the route is unreachable. Code fix replaces `\` with `/`. -dotnet_diagnostic.SST2701.severity = error # A `[JSInvokable]` method is not public, so JavaScript interop cannot call it. Code fix makes it public. -dotnet_diagnostic.SST2702.severity = error # A `[SupplyParameterFromQuery]` property has a type the framework cannot bind from the query string, which throws at runtime. -dotnet_diagnostic.SST2703.severity = error # A routable component's route constraint (`{id:int}`) disagrees with the matching `[Parameter]` CLR type, so the route silently fails to match. -dotnet_diagnostic.SST2704.severity = error # A public action on an `[ApiController]` declares no HTTP-verb attribute, so it answers every verb and can make routing ambiguous. -dotnet_diagnostic.SST2705.severity = none # A bound model member is a non-nullable value type with no required marker, so a request that omits it binds the default with no error. Opt-in. -dotnet_diagnostic.SST2706.severity = error # A Windows Forms entry point carries neither `[STAThread]` nor `[MTAThread]`; without STA, clipboard, drag-and-drop, and common dialogs misbehave. Code fix adds `[STAThread]`. -dotnet_diagnostic.SST2707.severity = none # A fire-and-forget `Task.Run` in a controller captures the request's `HttpContext`, which is disposed when the request ends, so the background work throws `ObjectDisposedException`. Opt-in. -dotnet_diagnostic.SST2708.severity = error # A component subscribes to an event in a lifecycle method but never unsubscribes, so the event source keeps the component alive — a per-session leak on a Server circuit. -dotnet_diagnostic.SST2709.severity = error # `StateHasChanged` is called while the component is being disposed, which the renderer no longer supports and throws. -dotnet_diagnostic.SST2710.severity = error # `StateHasChanged` is called directly from a timer callback, off the renderer's dispatcher; marshal it with `InvokeAsync(StateHasChanged)`. -dotnet_diagnostic.SST2711.severity = error # A synchronous component lifecycle method is overridden as `async void`, which the framework never awaits; override the `…Async` twin returning `Task`. Code fix rewrites the signature. -dotnet_diagnostic.SST2712.severity = error # An `[Inject]`/`[CascadingParameter]` property has no setter, so the framework's reflection-based binding leaves it null. Code fix adds a setter. -dotnet_diagnostic.SST2713.severity = error # A `DotNetObjectReference.Create(this)` is passed inline and never stored, so nothing can dispose it and it leaks on the JavaScript side. -# In-box IDE code-style rules disabled where a StyleSharp rule covers the same shape (avoids double-reporting under EnforceCodeStyleInBuild) -dotnet_diagnostic.IDE0001.severity = none # covered by SST1116 -dotnet_diagnostic.IDE0003.severity = none # covered by SST1117 -dotnet_diagnostic.IDE0005.severity = none # covered by SST1445 -dotnet_diagnostic.IDE0010.severity = none # covered by SST2205 -dotnet_diagnostic.IDE0011.severity = none # covered by SST1503 -dotnet_diagnostic.IDE0017.severity = none # covered by SST1193 -dotnet_diagnostic.IDE0018.severity = none # covered by SST2208 -dotnet_diagnostic.IDE0020.severity = none # covered by SST2007 -dotnet_diagnostic.IDE0027.severity = none # covered by SST2219 -dotnet_diagnostic.IDE0029.severity = none # covered by SST1195 -dotnet_diagnostic.IDE0030.severity = none # covered by SST1195 -dotnet_diagnostic.IDE0032.severity = none # covered by SST1420 -dotnet_diagnostic.IDE0033.severity = none # covered by SST1142 -dotnet_diagnostic.IDE0034.severity = none # covered by SST1188 -dotnet_diagnostic.IDE0036.severity = none # covered by SST1206 -dotnet_diagnostic.IDE0037.severity = none # covered by SST2216 -dotnet_diagnostic.IDE0039.severity = none # covered by SST2228 -dotnet_diagnostic.IDE0040.severity = none # covered by SST1400 -dotnet_diagnostic.IDE0045.severity = none # covered by SST1198 -dotnet_diagnostic.IDE0046.severity = none # covered by SST1197 -dotnet_diagnostic.IDE0051.severity = none # covered by SST1440 -dotnet_diagnostic.IDE0053.severity = none # covered by SST2257 -dotnet_diagnostic.IDE0054.severity = none # covered by SST1185 -dotnet_diagnostic.IDE0056.severity = none # covered by SST2203 -dotnet_diagnostic.IDE0062.severity = none # covered by SST2235 -dotnet_diagnostic.IDE0063.severity = none # covered by SST2236 -dotnet_diagnostic.IDE0065.severity = none # covered by SST1200 -dotnet_diagnostic.IDE0070.severity = none # covered by SST2217 -dotnet_diagnostic.IDE0071.severity = none # covered by SST2220 -dotnet_diagnostic.IDE0072.severity = none # covered by SST2206 -dotnet_diagnostic.IDE0073.severity = none # covered by SST1633 -dotnet_diagnostic.IDE0074.severity = none # covered by SST2223 -dotnet_diagnostic.IDE0076.severity = none # covered by SST1457 -dotnet_diagnostic.IDE0077.severity = none # covered by SST1458 -dotnet_diagnostic.IDE0080.severity = none # covered by SST2209 -dotnet_diagnostic.IDE0082.severity = none # covered by SST1199 -dotnet_diagnostic.IDE0083.severity = none # covered by SST2006 -dotnet_diagnostic.IDE0090.severity = none # covered by SST2202 -dotnet_diagnostic.IDE0100.severity = none # covered by SST1143 -dotnet_diagnostic.IDE0110.severity = none # covered by SST2213 -dotnet_diagnostic.IDE0150.severity = none # covered by SST2231 -dotnet_diagnostic.IDE0161.severity = none # covered by SST2237 -dotnet_diagnostic.IDE0170.severity = none # covered by SST2238 -dotnet_diagnostic.IDE0180.severity = none # covered by SST2215 -dotnet_diagnostic.IDE0200.severity = none # covered by SST2239 -dotnet_diagnostic.IDE0220.severity = none # covered by SST2225 -dotnet_diagnostic.IDE0230.severity = none # covered by SST2212 -dotnet_diagnostic.IDE0240.severity = none # covered by SST2210 -dotnet_diagnostic.IDE0241.severity = none # covered by SST2211 -dotnet_diagnostic.IDE0251.severity = none # covered by SST1460 -dotnet_diagnostic.IDE0270.severity = none # covered by SST1195 -dotnet_diagnostic.IDE0280.severity = none # covered by SST1463 -dotnet_diagnostic.IDE0290.severity = none # covered by SST2241 -dotnet_diagnostic.IDE0301.severity = none # covered by SST2100 -dotnet_diagnostic.IDE0302.severity = none # covered by SST2102 -dotnet_diagnostic.IDE0303.severity = none # covered by SST2103 -dotnet_diagnostic.IDE0304.severity = none # covered by SST2104 -dotnet_diagnostic.IDE0305.severity = none # covered by SST2105 -dotnet_diagnostic.IDE0340.severity = none # covered by SST2232 -dotnet_diagnostic.IDE0350.severity = none # covered by SST2218 -dotnet_diagnostic.IDE0380.severity = none # covered by SST1455 -dotnet_diagnostic.IDE1005.severity = none # covered by SST2240 - -################### -# PublicApiAnalyzers (RSxxxx) - public API surface tracking + ################### +# Microsoft.CodeAnalysis.PublicApiAnalyzers (RS) +################### +# Public API surface tracking dotnet_diagnostic.RS0016.severity = error # public symbol missing from the PublicAPI baseline dotnet_diagnostic.RS0017.severity = error # PublicAPI baseline entry no longer in source ################### -# Trimming Analyzer Warnings (IL2001 - IL2123) -# See: https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trim-warnings/ +# Microsoft.NET.ILLink.Analyzers (IL) ################### +# Trimming +# See: https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trim-warnings/ dotnet_diagnostic.IL2001.severity = error # Type in UnreferencedCode attribute doesn't have matching RequiresUnreferencedCode dotnet_diagnostic.IL2002.severity = error # Method with RequiresUnreferencedCode called from code without that attribute dotnet_diagnostic.IL2003.severity = error # RequiresUnreferencedCode attribute is only supported on methods @@ -2170,10 +2134,8 @@ dotnet_diagnostic.IL2117.severity = error # Methods with DynamicallyAccessedMemb dotnet_diagnostic.IL2122.severity = error # Reflection call to method with UnreferencedCode attribute cannot be statically analyzed dotnet_diagnostic.IL2123.severity = error # DynamicallyAccessedMembers on method or parameter doesn't match overridden member -################### -# AOT Analyzer Warnings (IL3xxx) +# Native AOT # See: https://learn.microsoft.com/en-us/dotnet/core/deploying/native-aot/warnings/ -################### dotnet_diagnostic.IL3050.severity = error # Using member annotated with RequiresDynamicCode dotnet_diagnostic.IL3051.severity = error # RequiresDynamicCode attribute is only supported on methods and constructors dotnet_diagnostic.IL3052.severity = error # RequiresDynamicCode attribute on type is not supported @@ -2184,8 +2146,15 @@ dotnet_diagnostic.IL3056.severity = error # MakeGenericMethod on non-supported m dotnet_diagnostic.IL3057.severity = error # Reflection access to generic parameter requires dynamic code ################### -# SonarAnalyzer (Sxxxx) - Blocker Bug +# SonarAnalyzer.CSharp (S) ################### +# Repository suppressions +dotnet_diagnostic.S1075.severity = none # Hardcoded URI — canonical SourceLink hosts are the point +dotnet_diagnostic.S2436.severity = none # Too many generic parameters — needed for the projector overload +dotnet_diagnostic.S4036.severity = none # PATH-relative process spawn — benchmark only, trusted env +dotnet_diagnostic.S8969.severity = none # Nullability inference is inconsistent across the repository's target frameworks + +# Blocker bugs dotnet_diagnostic.S1048.severity = none # Finalizers should not throw exceptions — covered by SST1485 dotnet_diagnostic.S2190.severity = none # Loops and recursions should not be infinite dotnet_diagnostic.S2275.severity = none # Composite format strings should not lead to unexpected behavior at runtime - DUPLICATE CA2241 @@ -2197,9 +2166,7 @@ dotnet_diagnostic.S3869.severity = none # "SafeHandle.DangerousGetHandle" should dotnet_diagnostic.S3889.severity = none # "Thread.Resume" and "Thread.Suspend" should not be used -> replaced by obsolete or compiler dotnet_diagnostic.S4159.severity = none # Classes should implement their "ExportAttribute" interfaces — covered by SST2472 -################### -# SonarAnalyzer (Sxxxx) - Critical Bug -################### +# Critical bugs dotnet_diagnostic.S2551.severity = none # Shared resources should not be used for locking — covered by SST1902 dotnet_diagnostic.S2952.severity = none # Classes should "Dispose" of members from the classes' own "Dispose" methods -> replaced by SST2315 dotnet_diagnostic.S3449.severity = none # Right operands of shift operators should be integers -> replaced by SST1478 @@ -2210,9 +2177,7 @@ dotnet_diagnostic.S4586.severity = none # Non-async "Task/Task" methods shoul dotnet_diagnostic.S5856.severity = none # Regular expressions should be syntactically valid — covered by SST2444 dotnet_diagnostic.S6674.severity = none # Log message template should be syntactically correct — covered by SST2441 -################### -# SonarAnalyzer (Sxxxx) - Major Bug -################### +# Major bugs dotnet_diagnostic.S1244.severity = none # Floating point numbers should not be tested for equality — covered by SST1473 dotnet_diagnostic.S1656.severity = none # Variables should not be self-assigned — covered by SST1189 dotnet_diagnostic.S1751.severity = none # Loops with at most one iteration should be refactored - covered by SST1444 @@ -2262,9 +2227,7 @@ dotnet_diagnostic.S6798.severity = none # [JSInvokable] attribute should only be dotnet_diagnostic.S6800.severity = none # Component parameter type should match the route parameter type constraint -> replaced by SST2703 dotnet_diagnostic.S6930.severity = none # Backslash should be avoided in route templates -> replaced by SST2700 -################### -# SonarAnalyzer (Sxxxx) - Minor Bug -################### +# Minor bugs dotnet_diagnostic.S1206.severity = none # "Equals(Object)" and "GetHashCode()" should be overridden in pairs - DUPLICATE CA2218 dotnet_diagnostic.S1226.severity = none # Method parameters, caught exceptions and foreach variables' initial values should not be ignored dotnet_diagnostic.S2183.severity = none # Integral numbers should not be shifted by zero or more than their number of bits-1 — covered by SST1478 @@ -2279,17 +2242,13 @@ dotnet_diagnostic.S3397.severity = none # "base.Equals" should not be used to ch dotnet_diagnostic.S3456.severity = none # "string.ToCharArray()" and "ReadOnlySpan.ToArray()" should not be called redundantly — covered by PSH1217 dotnet_diagnostic.S3887.severity = none # Mutable, non-private fields should not be "readonly" — covered by SST2322 -################### -# SonarAnalyzer (Sxxxx) - Blocker Vulnerability -################### +# Blocker vulnerabilities dotnet_diagnostic.S2115.severity = none # A secure password should be used when connecting to a database -> replaced by SES1203 dotnet_diagnostic.S2755.severity = none # XML parsers should not be vulnerable to XXE attacks -> replaced by CA3075 dotnet_diagnostic.S3884.severity = none # "CoSetProxyBlanket" and "CoInitializeSecurity" should not be used -> replaced by obsolete (COM interop security) dotnet_diagnostic.S6418.severity = none # Secrets should not be hard-coded — covered by SES1201 -################### -# SonarAnalyzer (Sxxxx) - Critical Vulnerability -################### +# Critical vulnerabilities dotnet_diagnostic.S4423.severity = none # Weak SSL/TLS protocols should not be used -> replaced by CA5397/CA5398 dotnet_diagnostic.S4426.severity = none # Cryptographic keys should be robust -> replaced by CA5385 (RSA) / CA5384 (DSA) dotnet_diagnostic.S4433.severity = none # LDAP connections should be authenticated -> replaced by SES1310 @@ -2300,9 +2259,7 @@ dotnet_diagnostic.S5542.severity = none # Encryption algorithms should be used w dotnet_diagnostic.S5547.severity = none # Cipher algorithms should be robust -> replaced by CA5351 dotnet_diagnostic.S5659.severity = none # JWT should be signed and verified with strong cipher algorithms -> replaced by SES1503 -################### -# SonarAnalyzer (Sxxxx) - Major Vulnerability -################### +# Major vulnerabilities dotnet_diagnostic.S2068.severity = none # Credentials should not be hard-coded -> replaced by SES1201 dotnet_diagnostic.S2612.severity = none # File permissions should not be set to world-accessible values -> replaced by SES1308 dotnet_diagnostic.S4211.severity = none # Members should not have conflicting transparency annotations -> replaced by obsolete (Code Access Security) @@ -2310,9 +2267,7 @@ dotnet_diagnostic.S4212.severity = none # Serialization constructors should be s dotnet_diagnostic.S6377.severity = none # XML signatures should be validated securely -> replaced by SES1008 dotnet_diagnostic.S7039.severity = none # Content Security Policies should be restrictive -> replaced by SES1515 -################### -# SonarAnalyzer (Sxxxx) - Blocker Code Smell -################### +# Blocker code smells dotnet_diagnostic.S1147.severity = none # Exit methods should not be called — covered by SST2321 dotnet_diagnostic.S1451.severity = none # Track lack of copyright and license headers dotnet_diagnostic.S2178.severity = none # Short-circuit logic should be used in boolean contexts — covered by SST2415 @@ -2335,9 +2290,7 @@ dotnet_diagnostic.S4462.severity = none # Calls to "async" methods should not be dotnet_diagnostic.S6422.severity = none # Calls to "async" methods should not be blocking in Azure Functions — covered by PSH1315 dotnet_diagnostic.S6424.severity = none # Interfaces for durable entities should satisfy the restrictions -> off: Durable Entity-specific; not used in this library -################### -# SonarAnalyzer (Sxxxx) - Critical Code Smell -################### +# Critical code smells dotnet_diagnostic.S1006.severity = none # Method overrides should not change parameter defaults — covered by SST2424 dotnet_diagnostic.S1067.severity = none # Expressions should not be too complex dotnet_diagnostic.S1163.severity = none # Exceptions should not be thrown in finally blocks -> replaced by CA2219 @@ -2400,9 +2353,7 @@ dotnet_diagnostic.S8380.severity = none # Return types named "partial" should be dotnet_diagnostic.S8381.severity = none # "scoped" should be escaped when used as an identifier or type name in parenthesized lambda parameter lists -> replaced by compiler dotnet_diagnostic.S927.severity = none # Parameter names should match base declaration and other partial definitions - DUPLICATE CA1725 -################### -# SonarAnalyzer (Sxxxx) - Major Code Smell -################### +# Major code smells dotnet_diagnostic.S103.severity = none # Lines should not be too long — covered by SST1521 dotnet_diagnostic.S104.severity = none # Files should not have too many lines of code — covered by SST1522 dotnet_diagnostic.S106.severity = none # Covered by SST1449 (canonical) @@ -2527,9 +2478,7 @@ dotnet_diagnostic.S6968.severity = none # Actions that return a value should be dotnet_diagnostic.S881.severity = none # Increment (++) and decrement (--) operators should not be used in a method call or mixed with other operators in an expression — covered by SST2015 dotnet_diagnostic.S907.severity = none # "goto" statement should not be used — covered by SST2014 -################### -# SonarAnalyzer (Sxxxx) - Minor Code Smell -################### +# Minor code smells dotnet_diagnostic.S100.severity = none # Methods and properties should be named in PascalCase — covered by SST1300 dotnet_diagnostic.S101.severity = none # Types should be named in PascalCase — covered by SST1300 dotnet_diagnostic.S105.severity = none # Tabulation characters should not be used — covered by SST1027 @@ -2657,16 +2606,12 @@ dotnet_diagnostic.S6675.severity = none # "Trace.WriteLineIf" should not be used dotnet_diagnostic.S6678.severity = none # Use PascalCase for named placeholders -> replaced by CA1727 dotnet_diagnostic.S818.severity = none # Literal suffixes should be upper case — covered by SST2244 -################### -# SonarAnalyzer (Sxxxx) - Info Code Smell -################### +# Informational code smells dotnet_diagnostic.S1133.severity = none # Deprecated code should be removed — covered by SST2310 dotnet_diagnostic.S1135.severity = none # Track uses of "TODO" tags -> off: FIXME comment tracker; not enforced here dotnet_diagnostic.S1309.severity = none # Track uses of in-source issue suppressions -################### -# SonarAnalyzer (Sxxxx) - Uncategorized -################### +# Uncategorized dotnet_diagnostic.S9999-cpd.severity = error # Copy-paste token calculator dotnet_diagnostic.S9999-log.severity = error # Log generator dotnet_diagnostic.S9999-metadata.severity = error # File metadata generator @@ -2677,9 +2622,7 @@ dotnet_diagnostic.S9999-testMethodDeclaration.severity = error # Test method dec dotnet_diagnostic.S9999-token-type.severity = error # Token type calculator dotnet_diagnostic.S9999-warning.severity = error # Analysis Warning generator -################### -# SonarAnalyzer (Sxxxx) - Critical Security Hotspot -################### +# Critical security hotspots dotnet_diagnostic.S2245.severity = none # Using pseudorandom number generators (PRNGs) is security-sensitive - DUPLICATE CA5394 dotnet_diagnostic.S2257.severity = none # Using non-standard cryptographic algorithms is security-sensitive -> replaced by SES1007 dotnet_diagnostic.S4502.severity = none # Disabling CSRF protections is security-sensitive -> replaced by in-box ASP.NET antiforgery analyzer @@ -2689,9 +2632,7 @@ dotnet_diagnostic.S5042.severity = none # Expanding archive files without contro dotnet_diagnostic.S5332.severity = none # Using clear-text protocols is security-sensitive -> replaced by SES1106 dotnet_diagnostic.S5443.severity = none # Using publicly writable directories is security-sensitive -> replaced by SES1308 -################### -# SonarAnalyzer (Sxxxx) - Major Security Hotspot -################### +# Major security hotspots dotnet_diagnostic.S1313.severity = none # Using hardcoded IP addresses is security-sensitive -> off: hardcoded-IP heuristic; too noisy to enforce dotnet_diagnostic.S2077.severity = none # Formatting SQL queries is security-sensitive -> replaced by CA2100 dotnet_diagnostic.S5693.severity = none # Allowing requests with excessive content length is security-sensitive -> replaced by SES1505 @@ -2700,9 +2641,7 @@ dotnet_diagnostic.S5766.severity = none # Creating Serializable objects without dotnet_diagnostic.S6444.severity = none # Not specifying a timeout for regular expressions is security-sensitive -> replaced by SES1509 dotnet_diagnostic.S6640.severity = none # Using unsafe code blocks is security-sensitive -> off: unsafe-code audit; not enforced here -################### -# SonarAnalyzer (Sxxxx) - Minor Security Hotspot -################### +# Minor security hotspots dotnet_diagnostic.S2092.severity = none # Creating cookies without the "secure" flag is security-sensitive -> replaced by CA5382 dotnet_diagnostic.S3330.severity = none # Creating cookies without the "HttpOnly" flag is security-sensitive -> replaced by CA5383 dotnet_diagnostic.S4507.severity = none # Delivering code in production with debug features activated is security-sensitive -> off: debug features in production; partly covered by SES1506, rest not enforced diff --git a/src/Primitives.Shared/Signals/SignalExtensions{FromTask}.cs b/src/Primitives.Shared/Signals/SignalExtensions{FromTask}.cs index db9a5ee..816e809 100644 --- a/src/Primitives.Shared/Signals/SignalExtensions{FromTask}.cs +++ b/src/Primitives.Shared/Signals/SignalExtensions{FromTask}.cs @@ -102,10 +102,9 @@ public async Task HandleCancellation(Action? action) internal async Task<(TResult Value, bool IsCanceled)> WhenCancelled(CancellationToken cancellationToken) { TaskCompletionSource tcs = new(TaskCreationOptions.RunContinuationsAsynchronously); - var registration = cancellationToken.Register( - static state => ((TaskCompletionSource)state!).TrySetCanceled(), - tcs, - false); + var registration = cancellationToken.UnsafeRegister( + static (state, token) => ((TaskCompletionSource)state!).TrySetCanceled(token), + tcs); var cancellationTask = tcs.Task; try From a43d8c56e08130ebbe484b0c9eeed1b87f63469e Mon Sep 17 00:00:00 2001 From: Glenn Watson <5834289+glennawatson@users.noreply.github.com> Date: Sat, 25 Jul 2026 18:04:01 +1000 Subject: [PATCH 5/8] style: enable remaining layout analyzers - Enforce SST1526 through SST1533 and update existing code. - Exclude incompatible source files from .NET Framework builds. --- .editorconfig | 16 +-- src/Polyfills/RequiredMemberAttribute.cs | 8 +- src/Primitives.Async.Shared/AsyncContext.cs | 6 +- .../Mixins/AsyncContextExtensions.cs | 4 +- .../ParityHelpers.FilterFusions.Reactive.cs | 3 +- .../Operators/RetryWithBackoffObservable.cs | 4 +- .../ReactiveExtensions.Retry.cs | 4 +- .../Advanced/ExpireSignal{T}.cs | 4 +- .../Advanced/FromAsyncTaskObservation{T}.cs | 4 +- .../Advanced/OnErrorResumeNextSignal{T}.cs | 4 +- ...SignalOperatorMixins.ChooseSwitchSelect.cs | 6 +- .../SignalOperatorMixins.SchedulerSignals.cs | 4 +- src/Primitives.Shared/SignalOperatorMixins.cs | 24 ++-- .../SignalOperatorParityMixins.RxNames.cs | 12 +- .../SignalOperatorParityMixins.cs | 12 +- .../Signals/Signal{Factories}.cs | 12 +- .../Signals/Signal{RxAliases}.cs | 4 +- ...LatestEnumerableSignal{TSource,TResult}.cs | 4 +- .../ConcurrentWitnessCallsException.cs | 4 +- .../Disposables/DisposableAsync.cs | 3 +- .../Observables/Defer.cs | 3 +- .../Observables/Return.cs | 3 +- .../Operators/AnyAllAsync.cs | 10 +- .../Operators/Blend.cs | 21 ++- .../Operators/ChainSignalSourcesSignal{T}.cs | 12 +- .../Operators/ContainsAsync.cs | 20 +-- .../Operators/CountAsync.cs | 15 +- .../Operators/Distinct.cs | 3 +- .../Operators/Expire.cs | 6 +- .../Operators/FirstAsync.cs | 11 +- .../Operators/ForEachAsync.cs | 11 +- .../Operators/GroupBy.cs | 3 +- .../Operators/LastAsync.cs | 11 +- .../Operators/LongCountAsync.cs | 15 +- .../Operators/OnDispose.cs | 9 +- .../ParityHelpers.OperatorFusions.cs | 3 +- .../Operators/ParityHelpers.Partition.cs | 3 +- .../Operators/RefCount.cs | 3 +- .../Operators/Shift.cs | 6 +- .../Operators/SingleAsync.cs | 16 +-- .../Operators/SkipWhile.cs | 3 +- .../Operators/SwitchToSignal.cs | 20 +-- .../Operators/TakeUntil.PredicateSignals.cs | 6 +- .../Operators/TakeUntil.cs | 6 +- .../Operators/TakeWhile.cs | 3 +- .../Operators/Throttle.cs | 6 +- .../Operators/ToAsyncEnumerable.cs | 4 +- .../Operators/ToDictionaryAsync.cs | 11 +- .../Operators/ToListAsync.cs | 11 +- .../Operators/Unique.cs | 3 +- .../Operators/WaitCompletionAsync.cs | 3 +- .../ConcurrentReplayLatestSignalAsync.cs | 3 +- ...currentStatelessReplayLatestSignalAsync.cs | 3 +- .../Options/BehaviorSignalCreationOptions.cs | 6 +- .../ReplayLatestSignalCreationOptions.cs | 6 +- .../Signals/Options/SignalCreationOptions.cs | 6 +- .../Signals/SerialReplayLatestSignalAsync.cs | 3 +- .../SerialStatelessReplayLatestSignalAsync.cs | 3 +- .../WitnessAsync.cs | 3 +- .../Advanced/AggregateWitness.cs | 3 +- .../Advanced/AllPredicateWitness.cs | 3 +- .../Advanced/AnyPredicateWitness.cs | 3 +- .../Advanced/AppendDelegateWitness.cs | 3 +- .../Advanced/ContainsWitness.cs | 3 +- .../Advanced/UseSignal{TResource,T}.cs | 4 +- .../Advanced/Witness.cs | 3 +- .../Core/EventPattern{TEventArgs}.cs | 4 +- .../ReactiveUI.Primitives.Core.csproj | 6 + .../Signals/KeepWithSignal{T,TState}.cs | 6 +- .../Signals/MapSignal{TSource,TResult}.cs | 3 +- ...ProjectedReadOnlyState{TSource,TResult}.cs | 3 +- .../Signals/Signal{T}.cs | 4 +- .../Signals/TapWithSignal{T,TState}.cs | 3 +- .../FirstAsTaskHelper.cs | 3 +- .../Operators/BinaryMinMaxObservable.cs | 3 +- .../Operators/SynchronizeAsyncObservable.cs | 3 +- .../R3AsyncBridgeGenerator.cs | 8 +- .../R3BridgeGenerator.cs | 10 +- .../ReactiveUI.Primitives.Reactive.csproj | 5 + .../SynchronizationContextSequencer.cs | 4 +- .../ReactiveUI.Primitives.csproj | 5 + .../SmokeParityValidator.cs | 18 +-- .../CombineLatestArityTests.Arity10.cs | 4 +- .../CombineLatestArityTests.Arity11.cs | 6 +- .../CombineLatestArityTests.Arity12.cs | 6 +- .../CombineLatestArityTests.Arity13.cs | 6 +- .../CombineLatestArityTests.Arity14.cs | 6 +- .../CombineLatestArityTests.Arity15.cs | 8 +- .../CombineLatestArityTests.Arity16.cs | 28 ++-- .../CombineLatestArityTests.Arity9.cs | 4 +- .../CombiningOperatorTests.Multicast.cs | 72 ++-------- .../CombineLatestIndexedObserverTests.cs | 3 +- .../SyncLatestCoordinatorBaseTests.cs | 3 +- .../SignalTests.BehaviorAndReplay.cs | 132 +++--------------- .../SignalTests.CreationOptions.cs | 6 +- .../TimeBasedOperatorTests.cs | 4 +- .../UnhandledExceptionCapture.cs | 4 +- .../ReactiveComponentBaseTests.cs | 5 +- .../AotSafeAssertionExtensions.cs | 4 +- .../DispatchWorkItemBaseTests.cs | 3 +- .../ConcurencyTests.cs | 4 +- .../DispatchSequencerBaseTests.cs | 3 +- .../EverySignalTests.cs | 5 +- .../FatalExceptionHelperTests.cs | 4 +- .../OnErrorResumeNextSignalTests.cs | 3 +- .../RxNamesTests.Helpers.cs | 52 +++---- .../RxNamesTests.cs | 16 +-- .../SwitchWitnessTests.cs | 5 +- 108 files changed, 366 insertions(+), 575 deletions(-) diff --git a/.editorconfig b/.editorconfig index 2057a87..01cccff 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1429,14 +1429,14 @@ dotnet_diagnostic.SST1522.severity = error # A file has more code lines than the dotnet_diagnostic.SST1523.severity = error # A member has more code lines than the configured maximum (default 60) dotnet_diagnostic.SST1524.severity = error # A switch section has more code lines than the configured maximum (default 20) dotnet_diagnostic.SST1525.severity = error # A multi-statement `switch` section has no braces; the braces-on policy extends to switch sections. Code fix wraps it. -dotnet_diagnostic.SST1526.severity = none # A wrapped binary expression places the operator inconsistently. Configurable (`before`/`after`, default before). Opt-in. -dotnet_diagnostic.SST1527.severity = none # The `=>` of an expression-bodied member wraps inconsistently. Configurable. Opt-in. -dotnet_diagnostic.SST1528.severity = none # The `=` of a wrapped initializer wraps inconsistently. Configurable. Opt-in. -dotnet_diagnostic.SST1529.severity = none # A wrapped `?.`/`.` call chain places the break inconsistently. Configurable. Opt-in. -dotnet_diagnostic.SST1530.severity = none # A newline sits between a type declaration and its base list. Code fix pulls the base list onto the declaration line. Opt-in. -dotnet_diagnostic.SST1531.severity = none # A short object initializer is split across lines. Code fix collapses it when it fits. Opt-in. -dotnet_diagnostic.SST1532.severity = none # A file mixes line endings. Configurable (`lf`/`crlf`, default lf). Opt-in. -dotnet_diagnostic.SST1533.severity = none # A source file contains no code. Opt-in. +dotnet_diagnostic.SST1526.severity = error # A wrapped binary expression places the operator inconsistently. Configurable (`before`/`after`, default before). Opt-in. +dotnet_diagnostic.SST1527.severity = error # The `=>` of an expression-bodied member wraps inconsistently. Configurable. Opt-in. +dotnet_diagnostic.SST1528.severity = error # The `=` of a wrapped initializer wraps inconsistently. Configurable. Opt-in. +dotnet_diagnostic.SST1529.severity = error # A wrapped `?.`/`.` call chain places the break inconsistently. Configurable. Opt-in. +dotnet_diagnostic.SST1530.severity = error # A newline sits between a type declaration and its base list. Code fix pulls the base list onto the declaration line. Opt-in. +dotnet_diagnostic.SST1531.severity = error # A short object initializer is split across lines. Code fix collapses it when it fits. Opt-in. +dotnet_diagnostic.SST1532.severity = error # A file mixes line endings. Configurable (`lf`/`crlf`, default lf). Opt-in. +dotnet_diagnostic.SST1533.severity = error # A source file contains no code. Opt-in. # Documentation dotnet_diagnostic.SST1600.severity = error # Externally visible members should be documented diff --git a/src/Polyfills/RequiredMemberAttribute.cs b/src/Polyfills/RequiredMemberAttribute.cs index f0182fa..7f4cf26 100644 --- a/src/Polyfills/RequiredMemberAttribute.cs +++ b/src/Polyfills/RequiredMemberAttribute.cs @@ -14,10 +14,10 @@ namespace System.Runtime.CompilerServices; [ExcludeFromCodeCoverage] [DebuggerNonUserCode] [AttributeUsage( - AttributeTargets.Class | - AttributeTargets.Struct | - AttributeTargets.Field | - AttributeTargets.Property, + AttributeTargets.Class + | AttributeTargets.Struct + | AttributeTargets.Field + | AttributeTargets.Property, AllowMultiple = false, Inherited = false)] internal sealed class RequiredMemberAttribute : Attribute; diff --git a/src/Primitives.Async.Shared/AsyncContext.cs b/src/Primitives.Async.Shared/AsyncContext.cs index 284e45d..d66f2f0 100644 --- a/src/Primitives.Async.Shared/AsyncContext.cs +++ b/src/Primitives.Async.Shared/AsyncContext.cs @@ -49,9 +49,9 @@ private AsyncContext() public ISequencer? Sequencer { get; init; } /// Gets a value indicating whether the current context uses the default task scheduler and no synchronization context. - internal bool UsesDefaultSequencer => SynchronizationContext is null && - Sequencer is null && - (TaskScheduler is null || TaskScheduler == TaskScheduler.Default); + internal bool UsesDefaultSequencer => SynchronizationContext is null + && Sequencer is null + && (TaskScheduler is null || TaskScheduler == TaskScheduler.Default); /// Creates a new AsyncContext that uses the specified SynchronizationContext for asynchronous operations. /// The returned AsyncContext will have its TaskScheduler property set to null. Use this method diff --git a/src/Primitives.Async.Shared/Mixins/AsyncContextExtensions.cs b/src/Primitives.Async.Shared/Mixins/AsyncContextExtensions.cs index 3cde902..6dac57e 100644 --- a/src/Primitives.Async.Shared/Mixins/AsyncContextExtensions.cs +++ b/src/Primitives.Async.Shared/Mixins/AsyncContextExtensions.cs @@ -32,8 +32,8 @@ public bool IsSameAsCurrentAsyncContext() if (@this.Sequencer is not null) { - return TaskScheduler.Current is AsyncContext.SequencerTaskScheduler adapter && - ReferenceEquals(adapter.Sequencer, @this.Sequencer); + return TaskScheduler.Current is AsyncContext.SequencerTaskScheduler adapter + && ReferenceEquals(adapter.Sequencer, @this.Sequencer); } return @this.TaskScheduler is not null diff --git a/src/Primitives.Async.Shared/Operators/ParityHelpers.FilterFusions.Reactive.cs b/src/Primitives.Async.Shared/Operators/ParityHelpers.FilterFusions.Reactive.cs index 2c2f022..7727027 100644 --- a/src/Primitives.Async.Shared/Operators/ParityHelpers.FilterFusions.Reactive.cs +++ b/src/Primitives.Async.Shared/Operators/ParityHelpers.FilterFusions.Reactive.cs @@ -37,8 +37,7 @@ async ValueTask IObservableAsync.SubscribeAsync( /// Forwards for every upstream emission. /// The downstream observer. /// The subscribe-time cancellation token. - internal sealed class AsSignalWitness(IObserverAsync downstream, CancellationToken subscribeToken) - : WitnessAsync(subscribeToken) + internal sealed class AsSignalWitness(IObserverAsync downstream, CancellationToken subscribeToken) : WitnessAsync(subscribeToken) { /// protected override ValueTask OnNextAsyncCore(T value, CancellationToken cancellationToken) => diff --git a/src/Primitives.Extensions.Shared/Operators/RetryWithBackoffObservable.cs b/src/Primitives.Extensions.Shared/Operators/RetryWithBackoffObservable.cs index 38ed8a3..930e654 100644 --- a/src/Primitives.Extensions.Shared/Operators/RetryWithBackoffObservable.cs +++ b/src/Primitives.Extensions.Shared/Operators/RetryWithBackoffObservable.cs @@ -71,8 +71,8 @@ public void OnError(Exception error) if (_retries < policy.MaxRetries) { - var delay = TimeSpan.FromTicks((long)(policy.InitialDelay.Ticks * - Math.Pow(policy.BackoffFactor, _retries))); + var delay = TimeSpan.FromTicks((long)(policy.InitialDelay.Ticks + * Math.Pow(policy.BackoffFactor, _retries))); if (policy.MaxDelay.HasValue && delay > policy.MaxDelay.Value) { delay = policy.MaxDelay.Value; diff --git a/src/Primitives.Extensions.Shared/ReactiveExtensions.Retry.cs b/src/Primitives.Extensions.Shared/ReactiveExtensions.Retry.cs index a43b782..42f777e 100644 --- a/src/Primitives.Extensions.Shared/ReactiveExtensions.Retry.cs +++ b/src/Primitives.Extensions.Shared/ReactiveExtensions.Retry.cs @@ -222,8 +222,8 @@ public IObservable RetryForeverWithDelay(TimeSpan delay) => /// The retry count. /// The delay. /// An IObservable of T. - public IObservable RetryWithFixedDelay(int retryCount, TimeSpan delay) - => new RetryWithBackoffObservable( + public IObservable RetryWithFixedDelay(int retryCount, TimeSpan delay) => + new RetryWithBackoffObservable( source, new( retryCount, diff --git a/src/Primitives.Shared/Advanced/ExpireSignal{T}.cs b/src/Primitives.Shared/Advanced/ExpireSignal{T}.cs index daf8462..c3856d6 100644 --- a/src/Primitives.Shared/Advanced/ExpireSignal{T}.cs +++ b/src/Primitives.Shared/Advanced/ExpireSignal{T}.cs @@ -38,8 +38,8 @@ public ExpireSignal(IObservable source, TimeSpan dueTime, ISequencer sequence /// public bool IsRequiredSubscribeOnCurrentThread() => - _sequencer == Sequencer.CurrentThread || - (_source is IRequireCurrentThread currentThread && currentThread.IsRequiredSubscribeOnCurrentThread()); + _sequencer == Sequencer.CurrentThread + || (_source is IRequireCurrentThread currentThread && currentThread.IsRequiredSubscribeOnCurrentThread()); /// public IDisposable Subscribe(IObserver observer) diff --git a/src/Primitives.Shared/Advanced/FromAsyncTaskObservation{T}.cs b/src/Primitives.Shared/Advanced/FromAsyncTaskObservation{T}.cs index 5f1313f..2c8e464 100644 --- a/src/Primitives.Shared/Advanced/FromAsyncTaskObservation{T}.cs +++ b/src/Primitives.Shared/Advanced/FromAsyncTaskObservation{T}.cs @@ -121,8 +121,8 @@ private void OnSuccess(T value) /// The observed task error. private void OnError(Exception error) { - if (Lifetime.IsCancellationRequested || ExternalCancellation.TryForwardCancellation() || - !Lifetime.TryComplete()) + if (Lifetime.IsCancellationRequested || ExternalCancellation.TryForwardCancellation() + || !Lifetime.TryComplete()) { return; } diff --git a/src/Primitives.Shared/Advanced/OnErrorResumeNextSignal{T}.cs b/src/Primitives.Shared/Advanced/OnErrorResumeNextSignal{T}.cs index 5ed2cb1..afc5a5a 100644 --- a/src/Primitives.Shared/Advanced/OnErrorResumeNextSignal{T}.cs +++ b/src/Primitives.Shared/Advanced/OnErrorResumeNextSignal{T}.cs @@ -152,8 +152,8 @@ private void ReadNextSource(out IObservable? source, out Exception? error, ou return; } - source = enumerator.Current ?? - throw new InvalidOperationException("OnErrorResumeNext source contained null."); + source = enumerator.Current + ?? throw new InvalidOperationException("OnErrorResumeNext source contained null."); } catch (Exception exception) { diff --git a/src/Primitives.Shared/SignalOperatorMixins.ChooseSwitchSelect.cs b/src/Primitives.Shared/SignalOperatorMixins.ChooseSwitchSelect.cs index 9a4c109..4b77947 100644 --- a/src/Primitives.Shared/SignalOperatorMixins.ChooseSwitchSelect.cs +++ b/src/Primitives.Shared/SignalOperatorMixins.ChooseSwitchSelect.cs @@ -79,8 +79,7 @@ public IDisposable Subscribe(IObserver observer) /// Applies the chooser to each value and forwards only the chosen ones. /// The downstream observer that receives the chosen values. /// Maps a source value to (HasValue, Value); the value is skipped when HasValue is . - private sealed class Sink(IObserver downstream, Func chooser) - : IObserver + private sealed class Sink(IObserver downstream, Func chooser) : IObserver { /// public void OnNext(TIn value) @@ -134,8 +133,7 @@ public IDisposable Subscribe(IObserver observer) /// Subscribes to the source, switching the active inner subscription on each non-null value. /// Projects each non-null source value to an inner observable. /// The downstream observer that receives values from the latest inner observable. - private sealed class Sink(Func> selector, IObserver downstream) - : IObserver, IDisposable + private sealed class Sink(Func> selector, IObserver downstream) : IObserver, IDisposable { /// Guards the switching state so outer and inner notifications stay consistent. private readonly Lock _gate = new(); diff --git a/src/Primitives.Shared/SignalOperatorMixins.SchedulerSignals.cs b/src/Primitives.Shared/SignalOperatorMixins.SchedulerSignals.cs index 57882d7..1e42b3d 100644 --- a/src/Primitives.Shared/SignalOperatorMixins.SchedulerSignals.cs +++ b/src/Primitives.Shared/SignalOperatorMixins.SchedulerSignals.cs @@ -526,8 +526,8 @@ private sealed class AbsoluteExpireSignal(IObservable source, DateTimeOffs /// public bool IsRequiredSubscribeOnCurrentThread() => - _scheduler == Sequencer.CurrentThread || - (_source is IRequireCurrentThread currentThread && currentThread.IsRequiredSubscribeOnCurrentThread()); + _scheduler == Sequencer.CurrentThread + || (_source is IRequireCurrentThread currentThread && currentThread.IsRequiredSubscribeOnCurrentThread()); /// public IDisposable Subscribe(IObserver observer) diff --git a/src/Primitives.Shared/SignalOperatorMixins.cs b/src/Primitives.Shared/SignalOperatorMixins.cs index 0d462f4..3c92260 100644 --- a/src/Primitives.Shared/SignalOperatorMixins.cs +++ b/src/Primitives.Shared/SignalOperatorMixins.cs @@ -389,10 +389,10 @@ public IObservable Pair(IObservable right, Fun ArgumentExceptionHelper.ThrowIfNull(selector); - return typeof(T) == typeof(int) && - typeof(TRight) == typeof(int) && - source is RangeSignal leftRange && - right is RangeSignal rightRange + return typeof(T) == typeof(int) + && typeof(TRight) == typeof(int) + && source is RangeSignal leftRange + && right is RangeSignal rightRange ? new RangeZipSignal( leftRange, rightRange, @@ -417,10 +417,10 @@ public IObservable SyncLatest( ArgumentExceptionHelper.ThrowIfNull(selector); - return typeof(T) == typeof(int) && - typeof(TRight) == typeof(int) && - source is RangeSignal leftRange && - right is RangeSignal rightRange + return typeof(T) == typeof(int) + && typeof(TRight) == typeof(int) + && source is RangeSignal leftRange + && right is RangeSignal rightRange ? new RangeSyncLatestSignal( leftRange, rightRange, @@ -444,10 +444,10 @@ public IObservable Latch(IObservable right, Fu ArgumentExceptionHelper.ThrowIfNull(selector); - return typeof(T) == typeof(int) && - typeof(TRight) == typeof(int) && - source is RangeSignal leftRange && - right is RangeSignal rightRange + return typeof(T) == typeof(int) + && typeof(TRight) == typeof(int) + && source is RangeSignal leftRange + && right is RangeSignal rightRange ? new RangeWithLatestSignal( leftRange, rightRange, diff --git a/src/Primitives.Shared/SignalOperatorParityMixins.RxNames.cs b/src/Primitives.Shared/SignalOperatorParityMixins.RxNames.cs index ef5a2ff..aba340d 100644 --- a/src/Primitives.Shared/SignalOperatorParityMixins.RxNames.cs +++ b/src/Primitives.Shared/SignalOperatorParityMixins.RxNames.cs @@ -638,8 +638,8 @@ public IObservable Zip( ArgumentExceptionHelper.ThrowIfNull(selector); - return typeof(TLeft) == typeof(int) && typeof(TRight) == typeof(int) && left is RangeSignal leftRange && - right is RangeSignal rightRange + return typeof(TLeft) == typeof(int) && typeof(TRight) == typeof(int) && left is RangeSignal leftRange + && right is RangeSignal rightRange ? new RangeZipSignal(leftRange, rightRange, (Func)(object)selector) : new ZipSignal(left, right, selector); } @@ -661,8 +661,8 @@ public IObservable CombineLatest( ArgumentExceptionHelper.ThrowIfNull(selector); - return typeof(TLeft) == typeof(int) && typeof(TRight) == typeof(int) && left is RangeSignal leftRange && - right is RangeSignal rightRange + return typeof(TLeft) == typeof(int) && typeof(TRight) == typeof(int) && left is RangeSignal leftRange + && right is RangeSignal rightRange ? new RangeCombineLatestSignal( leftRange, rightRange, @@ -687,8 +687,8 @@ public IObservable WithLatestFrom( ArgumentExceptionHelper.ThrowIfNull(selector); - return typeof(TLeft) == typeof(int) && typeof(TRight) == typeof(int) && left is RangeSignal leftRange && - right is RangeSignal rightRange + return typeof(TLeft) == typeof(int) && typeof(TRight) == typeof(int) && left is RangeSignal leftRange + && right is RangeSignal rightRange ? new RangeWithLatestSignal(leftRange, rightRange, (Func)(object)selector) : new LatchSignal(left, right, selector); } diff --git a/src/Primitives.Shared/SignalOperatorParityMixins.cs b/src/Primitives.Shared/SignalOperatorParityMixins.cs index 890a0aa..80f4296 100644 --- a/src/Primitives.Shared/SignalOperatorParityMixins.cs +++ b/src/Primitives.Shared/SignalOperatorParityMixins.cs @@ -708,8 +708,8 @@ public IObservable PairLatest( ArgumentExceptionHelper.ThrowIfNull(selector); - return typeof(T) == typeof(int) && typeof(TRight) == typeof(int) && source is RangeSignal leftRange && - right is RangeSignal rightRange + return typeof(T) == typeof(int) && typeof(TRight) == typeof(int) && source is RangeSignal leftRange + && right is RangeSignal rightRange ? new RangeSyncLatestSignal(leftRange, rightRange, (Func)(object)selector) : new CombineLatestSignal(source, right, selector); } @@ -731,8 +731,8 @@ public IObservable FuseLatest( ArgumentExceptionHelper.ThrowIfNull(source); - return typeof(T) == typeof(int) && typeof(TRight) == typeof(int) && source is RangeSignal leftRange && - right is RangeSignal rightRange + return typeof(T) == typeof(int) && typeof(TRight) == typeof(int) && source is RangeSignal leftRange + && right is RangeSignal rightRange ? new RangeSyncLatestSignal(leftRange, rightRange, (Func)(object)selector) : new CombineLatestSignal(source, right, selector); } @@ -754,8 +754,8 @@ public IObservable ForkJoin( ArgumentExceptionHelper.ThrowIfNull(selector); - return typeof(T) == typeof(int) && typeof(TRight) == typeof(int) && source is RangeSignal leftRange && - right is RangeSignal rightRange + return typeof(T) == typeof(int) && typeof(TRight) == typeof(int) && source is RangeSignal leftRange + && right is RangeSignal rightRange ? new RangeForkJoinSignal(leftRange, rightRange, (Func)(object)selector) : new ForkJoinSignal(source, right, selector); } diff --git a/src/Primitives.Shared/Signals/Signal{Factories}.cs b/src/Primitives.Shared/Signals/Signal{Factories}.cs index c59bfcc..8be9942 100644 --- a/src/Primitives.Shared/Signals/Signal{Factories}.cs +++ b/src/Primitives.Shared/Signals/Signal{Factories}.cs @@ -498,8 +498,8 @@ public static IObservable Pair( ArgumentExceptionHelper.ThrowIfNull(selector); - return typeof(TLeft) == typeof(int) && typeof(TRight) == typeof(int) && left is RangeSignal leftRange && - right is RangeSignal rightRange + return typeof(TLeft) == typeof(int) && typeof(TRight) == typeof(int) && left is RangeSignal leftRange + && right is RangeSignal rightRange ? new RangeZipSignal(leftRange, rightRange, (Func)(object)selector) : new PairSignal(left, right, selector); } @@ -523,8 +523,8 @@ public static IObservable SyncLatest( ArgumentExceptionHelper.ThrowIfNull(selector); - return typeof(TLeft) == typeof(int) && typeof(TRight) == typeof(int) && left is RangeSignal leftRange && - right is RangeSignal rightRange + return typeof(TLeft) == typeof(int) && typeof(TRight) == typeof(int) && left is RangeSignal leftRange + && right is RangeSignal rightRange ? new RangeSyncLatestSignal(leftRange, rightRange, (Func)(object)selector) : new SyncLatestSignal(left, right, selector); } @@ -548,8 +548,8 @@ public static IObservable ForkJoin( ArgumentExceptionHelper.ThrowIfNull(selector); - return typeof(TLeft) == typeof(int) && typeof(TRight) == typeof(int) && left is RangeSignal leftRange && - right is RangeSignal rightRange + return typeof(TLeft) == typeof(int) && typeof(TRight) == typeof(int) && left is RangeSignal leftRange + && right is RangeSignal rightRange ? new RangeForkJoinSignal(leftRange, rightRange, (Func)(object)selector) : new ForkJoinSignal(left, right, selector); } diff --git a/src/Primitives.Shared/Signals/Signal{RxAliases}.cs b/src/Primitives.Shared/Signals/Signal{RxAliases}.cs index 4c387d9..cb4971e 100644 --- a/src/Primitives.Shared/Signals/Signal{RxAliases}.cs +++ b/src/Primitives.Shared/Signals/Signal{RxAliases}.cs @@ -476,8 +476,8 @@ public static IObservable PairLatest( ArgumentExceptionHelper.ThrowIfNull(selector); - return typeof(TLeft) == typeof(int) && typeof(TRight) == typeof(int) && left is RangeSignal leftRange && - right is RangeSignal rightRange + return typeof(TLeft) == typeof(int) && typeof(TRight) == typeof(int) && left is RangeSignal leftRange + && right is RangeSignal rightRange ? new RangeSyncLatestSignal(leftRange, rightRange, (Func)(object)selector) : new SyncLatestSignal(left, right, selector); } diff --git a/src/ReactiveUI.Primitives.Async.Core/Advanced/SyncLatestEnumerableSignal{TSource,TResult}.cs b/src/ReactiveUI.Primitives.Async.Core/Advanced/SyncLatestEnumerableSignal{TSource,TResult}.cs index 547b9e6..baad0c0 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Advanced/SyncLatestEnumerableSignal{TSource,TResult}.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Advanced/SyncLatestEnumerableSignal{TSource,TResult}.cs @@ -20,8 +20,8 @@ public SyncLatestEnumerableSignal( { ArgumentExceptionHelper.ThrowIfNull(resultSelector); - Sources = (sources as IObservableAsync[]) ?? - [.. sources ?? throw new ArgumentNullException(nameof(sources))]; + Sources = (sources as IObservableAsync[]) + ?? [.. sources ?? throw new ArgumentNullException(nameof(sources))]; ResultSelector = resultSelector; } diff --git a/src/ReactiveUI.Primitives.Async.Core/ConcurrentWitnessCallsException.cs b/src/ReactiveUI.Primitives.Async.Core/ConcurrentWitnessCallsException.cs index 2b21b0d..abf674b 100644 --- a/src/ReactiveUI.Primitives.Async.Core/ConcurrentWitnessCallsException.cs +++ b/src/ReactiveUI.Primitives.Async.Core/ConcurrentWitnessCallsException.cs @@ -20,8 +20,8 @@ public class ConcurrentWitnessCallsException : Exception { /// The default error message describing the concurrent observer call violation. private const string DefaultMessage = - $"Concurrent calls of {nameof(WitnessAsync<>)}.OnNextAsync, {nameof(WitnessAsync<>)}.OnErrorResumeAsync," + - $" {nameof(WitnessAsync<>)}.OnCompletedAsync are not allowed. There is already a call pending"; + $"Concurrent calls of {nameof(WitnessAsync<>)}.OnNextAsync, {nameof(WitnessAsync<>)}.OnErrorResumeAsync," + + $" {nameof(WitnessAsync<>)}.OnCompletedAsync are not allowed. There is already a call pending"; /// Initializes a new instance of the class. public ConcurrentWitnessCallsException() diff --git a/src/ReactiveUI.Primitives.Async.Core/Disposables/DisposableAsync.cs b/src/ReactiveUI.Primitives.Async.Core/Disposables/DisposableAsync.cs index d375a0a..af85725 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Disposables/DisposableAsync.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Disposables/DisposableAsync.cs @@ -62,8 +62,7 @@ internal sealed class DelegateAsyncDisposable(Func disposeAsync) : IA /// The type of the state passed to the dispose delegate. /// The state forwarded to the dispose delegate at dispose time. /// The delegate invoked to perform asynchronous disposal. - internal sealed class DelegateAsyncDisposable(TState state, Func disposeAsync) - : IAsyncDisposable + internal sealed class DelegateAsyncDisposable(TState state, Func disposeAsync) : IAsyncDisposable { /// A flag indicating whether has already been called (0 = not disposed, 1 = disposed). private int _disposed; diff --git a/src/ReactiveUI.Primitives.Async.Core/Observables/Defer.cs b/src/ReactiveUI.Primitives.Async.Core/Observables/Defer.cs index c1fde40..5e2acda 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Observables/Defer.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Observables/Defer.cs @@ -61,8 +61,7 @@ ValueTask IObservableAsync.SubscribeAsync( /// state-machine box per call to host the factory's await. /// The element type. /// The deferred factory invoked once per subscribe. - internal sealed class DeferAsyncSignalAsync(Func>> factory) - : IObservableAsync + internal sealed class DeferAsyncSignalAsync(Func>> factory) : IObservableAsync { /// async ValueTask IObservableAsync.SubscribeAsync( diff --git a/src/ReactiveUI.Primitives.Async.Core/Observables/Return.cs b/src/ReactiveUI.Primitives.Async.Core/Observables/Return.cs index cabd2f5..c8f9fd8 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Observables/Return.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Observables/Return.cs @@ -53,8 +53,7 @@ ValueTask IObservableAsync.SubscribeAsync( /// Per-subscription task body that emits the captured value and signals completion. /// The downstream observer. /// The captured value. - private sealed class ReturnSubscription(IObserverAsync observer, T value) - : TaskSignalSubscription(observer) + private sealed class ReturnSubscription(IObserverAsync observer, T value) : TaskSignalSubscription(observer) { /// protected override async ValueTask ExecuteAsyncCore( diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/AnyAllAsync.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/AnyAllAsync.cs index 8263875..42e7533 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/AnyAllAsync.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/AnyAllAsync.cs @@ -40,8 +40,8 @@ public async ValueTask AnyAsync(Func? predicate, CancellationToke /// A cancellation token that can be used to cancel the asynchronous operation. /// A task that represents the asynchronous operation. The task result contains if the /// source contains any elements; otherwise, . - public ValueTask AnyAsync(CancellationToken cancellationToken) - => @this.AnyAsync(null, cancellationToken); + public ValueTask AnyAsync(CancellationToken cancellationToken) => + @this.AnyAsync(null, cancellationToken); /// Asynchronously determines whether all elements in the sequence satisfy the specified predicate. /// A function to test each element for a condition. The method evaluates this predicate for each element in the @@ -76,8 +76,7 @@ public async ValueTask AllAsync(Func predicate, CancellationToken /// The type of elements in the sequence. /// An optional predicate to test each element. If null, the sequence is checked for any elements. /// A cancellation token for the operation. - internal sealed class AnyTaskWitness(Func? predicate, CancellationToken cancellationToken) - : TaskResultWitnessAsyncBase(cancellationToken) + internal sealed class AnyTaskWitness(Func? predicate, CancellationToken cancellationToken) : TaskResultWitnessAsyncBase(cancellationToken) { /// protected override ValueTask OnNextAsyncCore(T value, CancellationToken cancellationToken) @@ -99,8 +98,7 @@ protected override ValueTask OnCompletedAsyncCore(Result result) => /// The type of elements in the sequence. /// The predicate to test each element against. /// A cancellation token for the operation. - internal sealed class AllTaskWitness(Func predicate, CancellationToken cancellationToken) - : TaskResultWitnessAsyncBase(cancellationToken) + internal sealed class AllTaskWitness(Func predicate, CancellationToken cancellationToken) : TaskResultWitnessAsyncBase(cancellationToken) { /// The predicate function used to test each element in the sequence. private readonly Func _predicate = predicate; diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/Blend.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/Blend.cs index 6a7c12d..9f80f86 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/Blend.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/Blend.cs @@ -100,8 +100,7 @@ public IObservableAsync Merge(IObservableAsync other) => /// Async observable that merges items from an observable of observables into a single stream. /// The type of the elements emitted by the inner observable sequences. /// The source observable whose inner observable sequences will be merged. - internal sealed class BlendSignalSourcesSignal(IObservableAsync> sources) - : IObservableAsync + internal sealed class BlendSignalSourcesSignal(IObservableAsync> sources) : IObservableAsync { /// ValueTask IObservableAsync.SubscribeAsync( @@ -396,8 +395,7 @@ protected override async ValueTask DisposeAsyncCore() /// The type of the elements in the merged sequence. /// The downstream observer to forward merged items to. /// The maximum number of inner observable sequences to subscribe to concurrently. - internal sealed class BoundedBlendCoordinator(IObserverAsync observer, int maxConcurrent) - : BlendCoordinator(observer) + internal sealed class BoundedBlendCoordinator(IObserverAsync observer, int maxConcurrent) : BlendCoordinator(observer) { /// Limits the number of concurrently subscribed inner observables. private readonly SemaphoreSlim _semaphore = new(maxConcurrent, maxConcurrent); @@ -436,8 +434,7 @@ internal override BlendBranchWitness CreateBranchObserver() => /// Inner witness that releases a semaphore slot on disposal. /// The parent bounded merge coordinator whose semaphore slot is released on disposal. - internal sealed class BlendBranchWitnessWithPermit(BoundedBlendCoordinator parent) - : BlendBranchWitness(parent) + internal sealed class BlendBranchWitnessWithPermit(BoundedBlendCoordinator parent) : BlendBranchWitness(parent) { /// Tracks whether the semaphore slot has already been released for this witness. /// @@ -731,18 +728,18 @@ internal async ValueTask FinishAsync(Result? result) internal sealed class BlendBranchWitness(BlendSequenceCoordinator parent) : WitnessAsync { /// - protected override ValueTask OnNextAsyncCore(T value, CancellationToken cancellationToken) - => parent.RelayNextAsync(value, cancellationToken); + protected override ValueTask OnNextAsyncCore(T value, CancellationToken cancellationToken) => + parent.RelayNextAsync(value, cancellationToken); /// protected override ValueTask OnErrorResumeAsyncCore( Exception error, - CancellationToken cancellationToken) - => parent.RelayErrorAsync(error, cancellationToken); + CancellationToken cancellationToken) => + parent.RelayErrorAsync(error, cancellationToken); /// - protected override ValueTask OnCompletedAsyncCore(Result result) - => parent.AcceptBranchCompletionAsync(result); + protected override ValueTask OnCompletedAsyncCore(Result result) => + parent.AcceptBranchCompletionAsync(result); /// protected override async ValueTask DisposeAsyncCore() diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/ChainSignalSourcesSignal{T}.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/ChainSignalSourcesSignal{T}.cs index 0fe6f18..f4b742d 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/ChainSignalSourcesSignal{T}.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/ChainSignalSourcesSignal{T}.cs @@ -224,8 +224,8 @@ internal sealed class ChainOuterWitness(ChainCoordinator subscription) : Witness /// The new inner observable. /// A token to cancel the operation. /// A task representing the asynchronous operation. - protected override ValueTask OnNextAsyncCore(IObservableAsync value, CancellationToken cancellationToken) - => subscription.AcceptOuterValueAsync(value); + protected override ValueTask OnNextAsyncCore(IObservableAsync value, CancellationToken cancellationToken) => + subscription.AcceptOuterValueAsync(value); /// Forwards a non-fatal error from the outer sequence to the downstream observer. /// The error to forward. @@ -250,8 +250,8 @@ protected override async ValueTask OnErrorResumeAsyncCore( /// Handles the outer sequence completing. /// The completion result. /// A task representing the asynchronous operation. - protected override ValueTask OnCompletedAsyncCore(Result result) - => subscription.AcceptOuterCompletionAsync(result); + protected override ValueTask OnCompletedAsyncCore(Result result) => + subscription.AcceptOuterCompletionAsync(result); } /// A witness for the currently active inner observable sequence that delegates to the parent . @@ -291,8 +291,8 @@ protected override async ValueTask OnErrorResumeAsyncCore( /// Handles the inner sequence completing, triggering subscription to the next buffered sequence. /// The completion result. /// A task representing the asynchronous operation. - protected override ValueTask OnCompletedAsyncCore(Result result) - => subscription.AcceptInnerCompletionAsync(result); + protected override ValueTask OnCompletedAsyncCore(Result result) => + subscription.AcceptInnerCompletionAsync(result); } } } diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/ContainsAsync.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/ContainsAsync.cs index a58b928..d7e4e96 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/ContainsAsync.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/ContainsAsync.cs @@ -21,8 +21,8 @@ public static partial class SignalAsyncExtensions /// type. /// A task that represents the asynchronous operation. The task result contains if the /// value is found in the sequence; otherwise, . - public ValueTask ContainsAsync(T value, IEqualityComparer? comparer) - => @this.ContainsAsync(value, comparer, CancellationToken.None); + public ValueTask ContainsAsync(T value, IEqualityComparer? comparer) => + @this.ContainsAsync(value, comparer, CancellationToken.None); /// Asynchronously determines whether the sequence contains a specified value using the given equality comparer. /// The value to locate in the sequence. @@ -48,16 +48,16 @@ public async ValueTask ContainsAsync( /// The value to locate in the collection. /// A task that represents the asynchronous operation. The task result contains if the /// value is found in the collection; otherwise, . - public ValueTask ContainsAsync(T value) - => @this.ContainsAsync(value, null, CancellationToken.None); + public ValueTask ContainsAsync(T value) => + @this.ContainsAsync(value, null, CancellationToken.None); /// Asynchronously determines whether the collection contains a specified value. /// The value to locate in the collection. /// A cancellation token that can be used to cancel the asynchronous operation. /// A task that represents the asynchronous operation. The task result contains if the /// value is found in the collection; otherwise, . - public ValueTask ContainsAsync(T value, CancellationToken cancellationToken) - => @this.ContainsAsync(value, null, cancellationToken); + public ValueTask ContainsAsync(T value, CancellationToken cancellationToken) => + @this.ContainsAsync(value, null, cancellationToken); } /// A witness that determines whether a sequence contains a specified value. @@ -78,11 +78,11 @@ protected override ValueTask OnNextAsyncCore(T value, CancellationToken cancella } /// - protected override ValueTask OnErrorResumeAsyncCore(Exception error, CancellationToken cancellationToken) - => SetExceptionAndDisposeAsync(error); + protected override ValueTask OnErrorResumeAsyncCore(Exception error, CancellationToken cancellationToken) => + SetExceptionAndDisposeAsync(error); /// - protected override ValueTask OnCompletedAsyncCore(Result result) - => result.IsSuccess ? SetResultAndDisposeAsync(false) : SetExceptionAndDisposeAsync(result.Exception); + protected override ValueTask OnCompletedAsyncCore(Result result) => + result.IsSuccess ? SetResultAndDisposeAsync(false) : SetExceptionAndDisposeAsync(result.Exception); } } diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/CountAsync.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/CountAsync.cs index baf7cba..2625dee 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/CountAsync.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/CountAsync.cs @@ -19,8 +19,8 @@ public static partial class SignalAsyncExtensions /// A function to test each element for a condition. If null, all elements are counted. /// A task that represents the asynchronous count operation. The task result contains the number of elements /// that match the predicate. - public ValueTask CountAsync(Func? predicate) - => @this.CountAsync(predicate, CancellationToken.None); + public ValueTask CountAsync(Func? predicate) => + @this.CountAsync(predicate, CancellationToken.None); /// Asynchronously counts the number of elements that satisfy a specified condition. /// A function to test each element for a condition. If null, all elements are counted. @@ -39,23 +39,22 @@ public async ValueTask CountAsync(Func? predicate, CancellationTok /// Asynchronously returns the total number of elements in the data source. /// A task that represents the asynchronous operation. The task result contains the number of elements in the /// data source. - public ValueTask CountAsync() - => @this.CountAsync(null, CancellationToken.None); + public ValueTask CountAsync() => + @this.CountAsync(null, CancellationToken.None); /// Asynchronously returns the total number of elements in the data source. /// A cancellation token that can be used to cancel the asynchronous count operation. /// A task that represents the asynchronous operation. The task result contains the number of elements in the /// data source. - public ValueTask CountAsync(CancellationToken cancellationToken) - => @this.CountAsync(null, cancellationToken); + public ValueTask CountAsync(CancellationToken cancellationToken) => + @this.CountAsync(null, cancellationToken); } /// A witness that counts elements in a sequence, optionally filtered by a predicate. /// The type of elements in the source sequence. /// An optional predicate to filter elements. If null, all elements are counted. /// A cancellation token for the operation. - internal sealed class CountTaskWitness(Func? predicate, CancellationToken cancellationToken) - : TaskResultWitnessAsyncBase(cancellationToken) + internal sealed class CountTaskWitness(Func? predicate, CancellationToken cancellationToken) : TaskResultWitnessAsyncBase(cancellationToken) { /// The running count of elements that satisfy the predicate. private int _count; diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/Distinct.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/Distinct.cs index 0e56949..9781cc7 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/Distinct.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/Distinct.cs @@ -95,8 +95,7 @@ public IObservableAsync DistinctBy( /// The element type. /// The upstream observable. /// The equality comparer used to detect duplicates. - internal sealed class DistinctSignal(IObservableAsync source, IEqualityComparer comparer) - : IObservableAsync + internal sealed class DistinctSignal(IObservableAsync source, IEqualityComparer comparer) : IObservableAsync { /// async ValueTask IObservableAsync.SubscribeAsync( diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/Expire.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/Expire.cs index 542823f..dc60b73 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/Expire.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/Expire.cs @@ -39,8 +39,7 @@ public IObservableAsync Expire(TimeSpan dueTime) /// The source observable sequence. /// The maximum allowed inter-element interval. /// The time provider used for scheduling the dueTime. - internal sealed class TimeoutSignal(IObservableAsync source, TimeSpan dueTime, TimeProvider timeProvider) - : IObservableAsync + internal sealed class TimeoutSignal(IObservableAsync source, TimeSpan dueTime, TimeProvider timeProvider) : IObservableAsync { /// Subscribes the specified observer and starts the dueTime timer. /// The observer to receive elements from the source. @@ -63,8 +62,7 @@ async ValueTask IObservableAsync.SubscribeAsync( /// The downstream observer to forward elements to. /// The maximum allowed inter-element interval. /// The time provider used for scheduling the dueTime. - internal sealed class TimeoutWitness(IObserverAsync observer, TimeSpan dueTime, TimeProvider timeProvider) - : WitnessAsync + internal sealed class TimeoutWitness(IObserverAsync observer, TimeSpan dueTime, TimeProvider timeProvider) : WitnessAsync { /// Synchronization gate protecting timer state. private readonly Lock _gate = new(); diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/FirstAsync.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/FirstAsync.cs index 2b21e52..9f53387 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/FirstAsync.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/FirstAsync.cs @@ -20,8 +20,8 @@ public static partial class SignalAsyncExtensions /// predicate returns . /// A task that represents the asynchronous operation. The task result contains the first element that matches /// the predicate. - public ValueTask FirstAsync(Func predicate) - => @this.FirstAsync(predicate, CancellationToken.None); + public ValueTask FirstAsync(Func predicate) => + @this.FirstAsync(predicate, CancellationToken.None); /// Asynchronously returns the first element in the sequence that satisfies the specified predicate. /// A function to test each element for a condition. The method returns the first element for which this @@ -43,8 +43,8 @@ public async ValueTask FirstAsync(Func predicate, CancellationToken /// in an exception being thrown. /// A task that represents the asynchronous operation. The task result contains the first element of the /// sequence. - public ValueTask FirstAsync() - => @this.FirstAsync(CancellationToken.None); + public ValueTask FirstAsync() => + @this.FirstAsync(CancellationToken.None); /// Asynchronously returns the first element of the sequence. /// If the sequence is empty, the behavior depends on the implementation and may result @@ -66,8 +66,7 @@ public async ValueTask FirstAsync(CancellationToken cancellationToken) /// The type of elements in the source sequence. /// An optional predicate to filter elements. /// A cancellation token for the operation. - internal sealed class FirstTaskWitness(Func? predicate, CancellationToken cancellationToken) - : TaskResultWitnessAsyncBase(cancellationToken) + internal sealed class FirstTaskWitness(Func? predicate, CancellationToken cancellationToken) : TaskResultWitnessAsyncBase(cancellationToken) { /// protected override ValueTask OnNextAsyncCore(T value, CancellationToken cancellationToken) diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/ForEachAsync.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/ForEachAsync.cs index 36860df..9af561d 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/ForEachAsync.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/ForEachAsync.cs @@ -23,8 +23,8 @@ public static partial class SignalAsyncExtensions /// token, and returns a ValueTask that completes when processing is finished. /// A ValueTask that represents the asynchronous operation. The task completes when all elements have been /// processed or the operation is canceled. - public ValueTask ForEachAsync(Func onNextAsync) - => @this.ForEachAsync(onNextAsync, CancellationToken.None); + public ValueTask ForEachAsync(Func onNextAsync) => + @this.ForEachAsync(onNextAsync, CancellationToken.None); /// Asynchronously invokes the specified action for each element in the sequence as elements are received. /// If the sequence completes or is canceled, the method returns when all in-flight @@ -53,8 +53,8 @@ public async ValueTask ForEachAsync( /// A task that represents the asynchronous iteration operation. The task completes when the sequence has been /// fully processed or the operation is canceled. /// Thrown if is null. - public ValueTask ForEachAsync(Action onNext) - => @this.ForEachAsync(onNext, CancellationToken.None); + public ValueTask ForEachAsync(Action onNext) => + @this.ForEachAsync(onNext, CancellationToken.None); /// Asynchronously invokes the specified action for each element in the sequence as elements are received. /// The action to invoke for each element in the sequence. Cannot be null. @@ -98,8 +98,7 @@ protected override ValueTask OnCompletedAsyncCore(Result result) => /// The type of elements in the sequence. /// The synchronous callback to invoke for each element. /// A cancellation token for the operation. - internal sealed class ForEachSyncTaskWitness(Action onNext, CancellationToken cancellationToken) - : TaskResultWitnessAsyncBase(cancellationToken) + internal sealed class ForEachSyncTaskWitness(Action onNext, CancellationToken cancellationToken) : TaskResultWitnessAsyncBase(cancellationToken) { /// The synchronous callback invoked for each element in the sequence. private readonly Action _onNext = onNext; diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/GroupBy.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/GroupBy.cs index b8b5c91..5ecea4f 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/GroupBy.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/GroupBy.cs @@ -75,8 +75,7 @@ public IObservableAsync> GroupBy( internal sealed class GroupByAsyncSignal( IObservableAsync source, Func keySelector, - Func> groupSignalSelector) - : IObservableAsync> + Func> groupSignalSelector) : IObservableAsync> where TKey : notnull { /// The source observable sequence whose elements are grouped by key. diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/LastAsync.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/LastAsync.cs index 5c3ab33..1221d5a 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/LastAsync.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/LastAsync.cs @@ -20,8 +20,8 @@ public static partial class SignalAsyncExtensions /// predicate returns . /// A task that represents the asynchronous operation. The task result contains the last element that matches /// the predicate. - public ValueTask LastAsync(Func predicate) - => @this.LastAsync(predicate, CancellationToken.None); + public ValueTask LastAsync(Func predicate) => + @this.LastAsync(predicate, CancellationToken.None); /// Asynchronously returns the last element in the sequence that satisfies the specified predicate. /// A function to test each element for a condition. The method returns the last element for which this @@ -44,8 +44,8 @@ public async ValueTask LastAsync(Func predicate, CancellationToken c /// immediately. /// A task that represents the asynchronous operation. The task result contains the last element of the /// sequence. - public ValueTask LastAsync() - => @this.LastAsync(CancellationToken.None); + public ValueTask LastAsync() => + @this.LastAsync(CancellationToken.None); /// Asynchronously returns the last element of the sequence. /// If the sequence is empty, the behavior depends on the implementation and may result @@ -68,8 +68,7 @@ public async ValueTask LastAsync(CancellationToken cancellationToken) /// The type of elements in the source sequence. /// An optional predicate to filter elements. /// A cancellation token for the operation. - internal sealed class LastTaskWitness(Func? predicate, CancellationToken cancellationToken) - : TaskResultWitnessAsyncBase(cancellationToken) + internal sealed class LastTaskWitness(Func? predicate, CancellationToken cancellationToken) : TaskResultWitnessAsyncBase(cancellationToken) { /// A value indicating whether any matching element has been observed. private bool _hasValue; diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/LongCountAsync.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/LongCountAsync.cs index 2e6fa3f..0c939c5 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/LongCountAsync.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/LongCountAsync.cs @@ -19,8 +19,8 @@ public static partial class SignalAsyncExtensions /// A function to test each element for a condition. If null, all elements are counted. /// A task that represents the asynchronous operation. The task result contains the number of elements that /// satisfy the predicate, or the total number of elements if the predicate is null. - public ValueTask LongCountAsync(Func? predicate) - => @this.LongCountAsync(predicate, CancellationToken.None); + public ValueTask LongCountAsync(Func? predicate) => + @this.LongCountAsync(predicate, CancellationToken.None); /// Asynchronously returns the number of elements in the sequence that satisfy an optional predicate. /// A function to test each element for a condition. If null, all elements are counted. @@ -41,23 +41,22 @@ public async ValueTask LongCountAsync( /// Asynchronously returns the total number of elements in the sequence as a 64-bit integer. /// A value task representing the asynchronous operation. The result contains the number of elements in the /// sequence as a 64-bit integer. - public ValueTask LongCountAsync() - => @this.LongCountAsync(null, CancellationToken.None); + public ValueTask LongCountAsync() => + @this.LongCountAsync(null, CancellationToken.None); /// Asynchronously returns the total number of elements in the sequence as a 64-bit integer. /// A cancellation token that can be used to cancel the asynchronous operation. /// A value task representing the asynchronous operation. The result contains the number of elements in the /// sequence as a 64-bit integer. - public ValueTask LongCountAsync(CancellationToken cancellationToken) - => @this.LongCountAsync(null, cancellationToken); + public ValueTask LongCountAsync(CancellationToken cancellationToken) => + @this.LongCountAsync(null, cancellationToken); } /// Witness that counts elements in a sequence as a 64-bit integer, optionally filtered by a predicate. /// The type of elements in the source sequence. /// An optional predicate to filter elements. If null, all elements are counted. /// A cancellation token for the operation. - internal sealed class LongCountTaskWitness(Func? predicate, CancellationToken cancellationToken) - : TaskResultWitnessAsyncBase(cancellationToken) + internal sealed class LongCountTaskWitness(Func? predicate, CancellationToken cancellationToken) : TaskResultWitnessAsyncBase(cancellationToken) { /// The running count of elements that satisfy the predicate. private long _count; diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/OnDispose.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/OnDispose.cs index 30a7d8f..74f388b 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/OnDispose.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/OnDispose.cs @@ -50,8 +50,7 @@ public IObservableAsync OnDispose(Action disposeAction) /// The element type. /// The upstream observable. /// The async dispose action. - internal sealed class OnDisposeSignal(IObservableAsync source, Func disposeAction) - : IObservableAsync + internal sealed class OnDisposeSignal(IObservableAsync source, Func disposeAction) : IObservableAsync { /// ValueTask IObservableAsync.SubscribeAsync( @@ -83,8 +82,7 @@ ValueTask IObservableAsync.SubscribeAsync( /// The type of elements in the sequence. /// The downstream observer to forward notifications to. /// The synchronous action to invoke when disposed. - internal sealed class OnDisposeWitnessSync(IObserverAsync observer, Action finallySync) - : ForwardingWitnessAsync(observer) + internal sealed class OnDisposeWitnessSync(IObserverAsync observer, Action finallySync) : ForwardingWitnessAsync(observer) { /// protected override async ValueTask DisposeAsyncCore() @@ -104,8 +102,7 @@ protected override async ValueTask DisposeAsyncCore() /// The type of elements in the sequence. /// The downstream observer to forward notifications to. /// The asynchronous callback to invoke when disposed. - internal sealed class OnDisposeWitness(IObserverAsync observer, Func finallyAsync) - : ForwardingWitnessAsync(observer) + internal sealed class OnDisposeWitness(IObserverAsync observer, Func finallyAsync) : ForwardingWitnessAsync(observer) { /// protected override async ValueTask DisposeAsyncCore() diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/ParityHelpers.OperatorFusions.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/ParityHelpers.OperatorFusions.cs index 59ba94b..900f304 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/ParityHelpers.OperatorFusions.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/ParityHelpers.OperatorFusions.cs @@ -81,8 +81,7 @@ protected override ValueTask OnCompletedAsyncCore(Result result) => internal sealed class ScanWithInitialAsyncSignal( IObservableAsync source, TAccumulate initial, - Func> accumulator) - : IObservableAsync + Func> accumulator) : IObservableAsync { /// async ValueTask IObservableAsync.SubscribeAsync( diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/ParityHelpers.Partition.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/ParityHelpers.Partition.cs index 9e908a8..6641f4d 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/ParityHelpers.Partition.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/ParityHelpers.Partition.cs @@ -265,8 +265,7 @@ ValueTask IObservableAsync.SubscribeAsync( /// Branch subscription handle returned to the subscriber. /// The owning coordinator. /// for the truthy branch. - internal sealed class BranchSubscription(PartitionCoordinator coordinator, bool isTrueBranch) - : IAsyncDisposable + internal sealed class BranchSubscription(PartitionCoordinator coordinator, bool isTrueBranch) : IAsyncDisposable { /// Latches to 1 on the first call so release is idempotent. private int _disposed; diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/RefCount.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/RefCount.cs index 4d69ca7..4a7149b 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/RefCount.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/RefCount.cs @@ -117,8 +117,7 @@ await connection /// /// The parent ref-count observable. /// The downstream witness to forward notifications to. - internal sealed class RefCountWitness(RefCountSignal parent, IObserverAsync observer) - : WitnessAsync + internal sealed class RefCountWitness(RefCountSignal parent, IObserverAsync observer) : WitnessAsync { /// Forwards an element to the downstream witness. /// The element to forward. diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/Shift.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/Shift.cs index 7497332..9363010 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/Shift.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/Shift.cs @@ -37,8 +37,7 @@ public IObservableAsync Shift(TimeSpan delayInterval) /// The source observable sequence. /// The time span by which to delay each element notification. /// The time provider used to control timing. - internal sealed class DelaySignal(IObservableAsync source, TimeSpan delayInterval, TimeProvider timeProvider) - : IObservableAsync + internal sealed class DelaySignal(IObservableAsync source, TimeSpan delayInterval, TimeProvider timeProvider) : IObservableAsync { /// ValueTask IObservableAsync.SubscribeAsync( @@ -58,8 +57,7 @@ internal sealed class DelayWitness( IObserverAsync observer, TimeSpan delayInterval, TimeProvider timeProvider, - CancellationToken subscribeToken) - : WitnessAsync(subscribeToken) + CancellationToken subscribeToken) : WitnessAsync(subscribeToken) { /// protected override async ValueTask OnNextAsyncCore(T value, CancellationToken cancellationToken) diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/SingleAsync.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/SingleAsync.cs index fbd0cb3..9d28c4a 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/SingleAsync.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/SingleAsync.cs @@ -30,8 +30,8 @@ public static partial class SignalAsyncExtensions /// returns . /// A task that represents the asynchronous operation. The task result contains the single element that matches /// the predicate. - public ValueTask SingleAsync(Func predicate) - => @this.SingleAsync(predicate, CancellationToken.None); + public ValueTask SingleAsync(Func predicate) => + @this.SingleAsync(predicate, CancellationToken.None); /// /// Asynchronously returns the single element of a sequence that satisfies a specified condition, or throws an @@ -45,8 +45,8 @@ public ValueTask SingleAsync(Func predicate) /// A cancellation token that can be used to cancel the asynchronous operation. /// A task that represents the asynchronous operation. The task result contains the single element that matches /// the predicate. - public ValueTask SingleAsync(Func predicate, CancellationToken cancellationToken) - => SingleCoreAsync(@this, predicate, cancellationToken); + public ValueTask SingleAsync(Func predicate, CancellationToken cancellationToken) => + SingleCoreAsync(@this, predicate, cancellationToken); /// /// Asynchronously returns the single element of the sequence, and throws an exception if the sequence does not @@ -56,8 +56,8 @@ public ValueTask SingleAsync(Func predicate, CancellationToken cance /// sequence is empty or contains more than one element, an exception is thrown. /// A task that represents the asynchronous operation. The task result contains the single element of the /// sequence. - public ValueTask SingleAsync() - => @this.SingleAsync(CancellationToken.None); + public ValueTask SingleAsync() => + @this.SingleAsync(CancellationToken.None); /// /// Asynchronously returns the single element of the sequence, and throws an exception if the sequence does not @@ -68,8 +68,8 @@ public ValueTask SingleAsync() /// A cancellation token that can be used to cancel the asynchronous operation. /// A task that represents the asynchronous operation. The task result contains the single element of the /// sequence. - public ValueTask SingleAsync(CancellationToken cancellationToken) - => SingleCoreAsync(@this, null, cancellationToken); + public ValueTask SingleAsync(CancellationToken cancellationToken) => + SingleCoreAsync(@this, null, cancellationToken); } /// Shared body for the SingleAsync overloads; subscribes the shared observer and unwraps the result. diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/SkipWhile.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/SkipWhile.cs index 1c2abda..e25839c 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/SkipWhile.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/SkipWhile.cs @@ -56,8 +56,7 @@ public IObservableAsync SkipWhile(Func predicate) /// The element type. /// The upstream observable. /// The skip-while predicate. - internal sealed class SkipWhileSyncSignal(IObservableAsync source, Func predicate) - : IObservableAsync + internal sealed class SkipWhileSyncSignal(IObservableAsync source, Func predicate) : IObservableAsync { /// async ValueTask IObservableAsync.SubscribeAsync( diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/SwitchToSignal.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/SwitchToSignal.cs index 1f65682..eddb113 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/SwitchToSignal.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/SwitchToSignal.cs @@ -306,8 +306,8 @@ internal sealed class SwitchToOuterWitness(SwitchToCoordinator subscription) : W /// The new inner observable. /// A token to cancel the operation. /// A task representing the asynchronous operation. - protected override ValueTask OnNextAsyncCore(IObservableAsync value, CancellationToken cancellationToken) - => subscription.AcceptOuterValueAsync(value); + protected override ValueTask OnNextAsyncCore(IObservableAsync value, CancellationToken cancellationToken) => + subscription.AcceptOuterValueAsync(value); /// Forwards a non-fatal error from the outer sequence to the downstream observer. /// The error to forward. @@ -329,8 +329,8 @@ await subscription._observer.OnErrorResumeAsync(error, subscription._disposeCanc /// Handles the outer sequence completing. /// The completion result. /// A task representing the asynchronous operation. - protected override ValueTask OnCompletedAsyncCore(Result result) - => subscription.AcceptOuterCompletionAsync(result); + protected override ValueTask OnCompletedAsyncCore(Result result) => + subscription.AcceptOuterCompletionAsync(result); } /// Witness for the currently active inner observable sequence that delegates to the parent . @@ -341,21 +341,21 @@ internal sealed class SwitchToInnerWitness(SwitchToCoordinator subscription) : W /// The element to forward. /// A token to cancel the operation. /// A task representing the asynchronous operation. - protected override ValueTask OnNextAsyncCore(T value, CancellationToken cancellationToken) - => subscription.AcceptInnerValueAsync(value, cancellationToken); + protected override ValueTask OnNextAsyncCore(T value, CancellationToken cancellationToken) => + subscription.AcceptInnerValueAsync(value, cancellationToken); /// Forwards a non-fatal error from the inner sequence to the downstream observer. /// The error to forward. /// A token to cancel the operation. /// A task representing the asynchronous operation. - protected override ValueTask OnErrorResumeAsyncCore(Exception error, CancellationToken cancellationToken) - => subscription.AcceptInnerErrorAsync(error, cancellationToken); + protected override ValueTask OnErrorResumeAsyncCore(Exception error, CancellationToken cancellationToken) => + subscription.AcceptInnerErrorAsync(error, cancellationToken); /// Handles the inner sequence completing. /// The completion result. /// A task representing the asynchronous operation. - protected override ValueTask OnCompletedAsyncCore(Result result) - => subscription.AcceptInnerCompletionAsync(result); + protected override ValueTask OnCompletedAsyncCore(Result result) => + subscription.AcceptInnerCompletionAsync(result); } } } diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/TakeUntil.PredicateSignals.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/TakeUntil.PredicateSignals.cs index 242fc13..966a1ce 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/TakeUntil.PredicateSignals.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/TakeUntil.PredicateSignals.cs @@ -15,8 +15,7 @@ public static partial class SignalAsyncExtensions /// The type of the elements in the source sequence. /// The source observable sequence. /// The predicate that signals when to stop emitting items. - internal sealed class PredicateStopSignal(IObservableAsync source, Func predicate) - : IObservableAsync + internal sealed class PredicateStopSignal(IObservableAsync source, Func predicate) : IObservableAsync { /// The predicate that signals when to stop emitting items. private readonly Func _predicate = predicate; @@ -38,8 +37,7 @@ ValueTask IObservableAsync.SubscribeAsync( /// Observer that forwards items from the source until the predicate returns true. /// The parent observable that owns this subscription. /// The downstream observer to forward items to. - internal sealed class PredicateStopCoordinator(PredicateStopSignal parent, IObserverAsync observer) - : WitnessAsync + internal sealed class PredicateStopCoordinator(PredicateStopSignal parent, IObserverAsync observer) : WitnessAsync { /// The inner subscription handle. private IAsyncDisposable? _subscription; diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/TakeUntil.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/TakeUntil.cs index 69d0346..b7ca996 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/TakeUntil.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/TakeUntil.cs @@ -315,8 +315,7 @@ public IObservableAsync TakeUntil( /// The type of the elements in the source sequence. /// The source observable sequence. /// The cancellation token that triggers completion. - internal sealed class CancellationStopSignal(IObservableAsync source, CancellationToken cancellationToken) - : IObservableAsync + internal sealed class CancellationStopSignal(IObservableAsync source, CancellationToken cancellationToken) : IObservableAsync { /// The source observable sequence. private readonly IObservableAsync _source = source; @@ -584,8 +583,7 @@ private async ValueTask DisposeStopRegistrationAsync() /// The source observable sequence. /// The task whose completion triggers the end of the sequence. /// Options controlling the take-until behavior. - internal sealed class TaskStopSignal(IObservableAsync source, Task task, TakeUntilOptions options) - : IObservableAsync + internal sealed class TaskStopSignal(IObservableAsync source, Task task, TakeUntilOptions options) : IObservableAsync { /// The source observable sequence. private readonly IObservableAsync _source = source; diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/TakeWhile.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/TakeWhile.cs index b20e45a..15e64ac 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/TakeWhile.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/TakeWhile.cs @@ -49,8 +49,7 @@ public IObservableAsync TakeWhile(Func predicate) /// The element type. /// The upstream observable. /// The take-while predicate. - internal sealed class TakeWhileSyncSignal(IObservableAsync source, Func predicate) - : IObservableAsync + internal sealed class TakeWhileSyncSignal(IObservableAsync source, Func predicate) : IObservableAsync { /// async ValueTask IObservableAsync.SubscribeAsync( diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/Throttle.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/Throttle.cs index d515df7..cc936a5 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/Throttle.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/Throttle.cs @@ -79,8 +79,7 @@ internal static ValueTask DelayAsync( /// The source observable sequence to throttle. /// The quiet period that must elapse before an element is forwarded. /// The time provider used for scheduling the debounce timer. - internal sealed class ThrottleSignal(IObservableAsync source, TimeSpan dueTime, TimeProvider timeProvider) - : IObservableAsync + internal sealed class ThrottleSignal(IObservableAsync source, TimeSpan dueTime, TimeProvider timeProvider) : IObservableAsync { /// Subscribes the specified observer with throttle behavior applied. /// The observer to receive throttled elements. @@ -101,8 +100,7 @@ ValueTask IObservableAsync.SubscribeAsync( /// The downstream observer to forward debounced elements to. /// The quiet period that must elapse before an element is forwarded. /// The time provider used for scheduling the debounce timer. - internal sealed class ThrottleWitness(IObserverAsync observer, TimeSpan dueTime, TimeProvider timeProvider) - : WitnessAsync + internal sealed class ThrottleWitness(IObserverAsync observer, TimeSpan dueTime, TimeProvider timeProvider) : WitnessAsync { /// The synchronization gate protecting shared throttle state. private readonly Lock _gate = new(); diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/ToAsyncEnumerable.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/ToAsyncEnumerable.cs index f217614..6e4281a 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/ToAsyncEnumerable.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/ToAsyncEnumerable.cs @@ -32,8 +32,8 @@ public static partial class SignalAsyncExtensions /// Thrown when or /// is null. public IAsyncEnumerable ToAsyncEnumerable( - Func> channelFactory) - => @this.ToAsyncEnumerable(channelFactory, null); + Func> channelFactory) => + @this.ToAsyncEnumerable(channelFactory, null); /// /// Converts the specified observable sequence to an asynchronous enumerable sequence, enabling consumption using diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/ToDictionaryAsync.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/ToDictionaryAsync.cs index d9ebcb6..d5416ff 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/ToDictionaryAsync.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/ToDictionaryAsync.cs @@ -138,8 +138,7 @@ private sealed class ToDictionaryTaskWitness( Func keySelector, Func elementSelector, IEqualityComparer? comparer, - CancellationToken cancellationToken) - : TaskResultWitnessAsyncBase>(cancellationToken) + CancellationToken cancellationToken) : TaskResultWitnessAsyncBase>(cancellationToken) where TKey : notnull { /// The dictionary that accumulates key-value pairs from the source sequence. @@ -154,11 +153,11 @@ protected override ValueTask OnNextAsyncCore(TSource value, CancellationToken ca } /// - protected override ValueTask OnErrorResumeAsyncCore(Exception error, CancellationToken cancellationToken) - => SetExceptionAndDisposeAsync(error); + protected override ValueTask OnErrorResumeAsyncCore(Exception error, CancellationToken cancellationToken) => + SetExceptionAndDisposeAsync(error); /// - protected override ValueTask OnCompletedAsyncCore(Result result) - => !result.IsSuccess ? SetExceptionAndDisposeAsync(result.Exception) : SetResultAndDisposeAsync(_map); + protected override ValueTask OnCompletedAsyncCore(Result result) => + !result.IsSuccess ? SetExceptionAndDisposeAsync(result.Exception) : SetResultAndDisposeAsync(_map); } } diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/ToListAsync.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/ToListAsync.cs index ac5137e..707e15a 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/ToListAsync.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/ToListAsync.cs @@ -18,14 +18,14 @@ public static partial class SignalAsyncExtensions /// Asynchronously collects all elements from the source sequence into a list. /// A task that represents the asynchronous operation. The task result contains a list of all elements in the /// source sequence, in the order they were received. - public ValueTask> CollectListAsync() - => @this.ToListAsync(CancellationToken.None); + public ValueTask> CollectListAsync() => + @this.ToListAsync(CancellationToken.None); /// Asynchronously collects all elements from the source sequence into a list. /// A task that represents the asynchronous operation. The task result contains a list of all elements in the /// source sequence, in the order they were received. - public ValueTask> ToListAsync() - => @this.CollectListAsync(); + public ValueTask> ToListAsync() => + @this.CollectListAsync(); /// Asynchronously collects all elements from the source sequence into a list. /// A cancellation token that can be used to cancel the asynchronous operation. @@ -52,8 +52,7 @@ public async ValueTask CollectArrayAsync() /// Witness that collects all elements from a sequence into a list. /// The type of elements in the source sequence. /// A cancellation token for the operation. - internal sealed class ToListTaskWitness(CancellationToken cancellationToken) - : TaskResultWitnessAsyncBase>(cancellationToken) + internal sealed class ToListTaskWitness(CancellationToken cancellationToken) : TaskResultWitnessAsyncBase>(cancellationToken) { /// The list that accumulates all elements received from the source sequence. private readonly List _items = []; diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/Unique.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/Unique.cs index 8d71747..9c730a6 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/Unique.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/Unique.cs @@ -104,8 +104,7 @@ public IObservableAsync UniqueBy( /// The element type. /// The upstream observable. /// The equality comparer used to detect duplicates. - internal sealed class UniqueSignal(IObservableAsync source, IEqualityComparer comparer) - : IObservableAsync + internal sealed class UniqueSignal(IObservableAsync source, IEqualityComparer comparer) : IObservableAsync { /// async ValueTask IObservableAsync.SubscribeAsync( diff --git a/src/ReactiveUI.Primitives.Async.Core/Operators/WaitCompletionAsync.cs b/src/ReactiveUI.Primitives.Async.Core/Operators/WaitCompletionAsync.cs index df54d46..182372f 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Operators/WaitCompletionAsync.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Operators/WaitCompletionAsync.cs @@ -40,8 +40,7 @@ public async ValueTask WaitCompletionAsync( /// Observer that waits for a sequence to complete, ignoring all emitted values. /// The type of elements in the source sequence. /// A cancellation token for the operation. - internal sealed class CompletionTaskWitness(CancellationToken cancellationToken) - : TaskResultWitnessAsyncBase(cancellationToken) + internal sealed class CompletionTaskWitness(CancellationToken cancellationToken) : TaskResultWitnessAsyncBase(cancellationToken) { /// protected override ValueTask OnNextAsyncCore(T value, CancellationToken cancellationToken) => default; diff --git a/src/ReactiveUI.Primitives.Async.Core/Signals/ConcurrentReplayLatestSignalAsync.cs b/src/ReactiveUI.Primitives.Async.Core/Signals/ConcurrentReplayLatestSignalAsync.cs index c29e015..4862317 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Signals/ConcurrentReplayLatestSignalAsync.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Signals/ConcurrentReplayLatestSignalAsync.cs @@ -13,8 +13,7 @@ namespace ReactiveUI.Primitives.Async.Signals; /// and suitable for use in asynchronous and concurrent environments. /// The type of the elements processed by the Signal. /// An optional initial value to be emitted to observers upon subscription if no other value has been published. -public sealed class ConcurrentReplayLatestSignalAsync(Optional startValue) - : ISignalAsync +public sealed class ConcurrentReplayLatestSignalAsync(Optional startValue) : ISignalAsync { /// IObservableAsync ISignalAsync.Values => this; diff --git a/src/ReactiveUI.Primitives.Async.Core/Signals/ConcurrentStatelessReplayLatestSignalAsync.cs b/src/ReactiveUI.Primitives.Async.Core/Signals/ConcurrentStatelessReplayLatestSignalAsync.cs index d27d266..48a87b3 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Signals/ConcurrentStatelessReplayLatestSignalAsync.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Signals/ConcurrentStatelessReplayLatestSignalAsync.cs @@ -15,8 +15,7 @@ namespace ReactiveUI.Primitives.Async.Signals; /// The type of the elements processed by the Signal. /// An optional initial value to be replayed to new observers. If not specified, no value is replayed until the first /// value is published. -public sealed class ConcurrentStatelessReplayLatestSignalAsync(Optional startValue) - : ISignalAsync +public sealed class ConcurrentStatelessReplayLatestSignalAsync(Optional startValue) : ISignalAsync { /// IObservableAsync ISignalAsync.Values => this; diff --git a/src/ReactiveUI.Primitives.Async.Core/Signals/Options/BehaviorSignalCreationOptions.cs b/src/ReactiveUI.Primitives.Async.Core/Signals/Options/BehaviorSignalCreationOptions.cs index 5ec96b1..b20e068 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Signals/Options/BehaviorSignalCreationOptions.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Signals/Options/BehaviorSignalCreationOptions.cs @@ -11,11 +11,7 @@ public sealed record BehaviorSignalCreationOptions /// Gets the default configuration options for creating a new BehaviorSignal instance. /// Use this property to obtain a standard set of options when creating a BehaviorSignal, unless /// custom behavior is required. - public static BehaviorSignalCreationOptions Default { get; } = new() - { - PublishingOption = PublishingOption.Serial, - IsStateless = false - }; + public static BehaviorSignalCreationOptions Default { get; } = new() { PublishingOption = PublishingOption.Serial, IsStateless = false }; /// Gets the publishing option to use when processing the item. public required PublishingOption PublishingOption { get; init; } diff --git a/src/ReactiveUI.Primitives.Async.Core/Signals/Options/ReplayLatestSignalCreationOptions.cs b/src/ReactiveUI.Primitives.Async.Core/Signals/Options/ReplayLatestSignalCreationOptions.cs index 59ec682..c805032 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Signals/Options/ReplayLatestSignalCreationOptions.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Signals/Options/ReplayLatestSignalCreationOptions.cs @@ -18,11 +18,7 @@ public sealed record ReplayLatestSignalCreationOptions /// Gets the default configuration options for creating a ReplayLatestSignal instance. /// The default options specify serial publishing and stateful behavior. Use this property to /// obtain a baseline configuration that can be modified as needed. - public static ReplayLatestSignalCreationOptions Default { get; } = new() - { - PublishingOption = PublishingOption.Serial, - IsStateless = false - }; + public static ReplayLatestSignalCreationOptions Default { get; } = new() { PublishingOption = PublishingOption.Serial, IsStateless = false }; /// Gets the publishing option to use when processing this item. public required PublishingOption PublishingOption { get; init; } diff --git a/src/ReactiveUI.Primitives.Async.Core/Signals/Options/SignalCreationOptions.cs b/src/ReactiveUI.Primitives.Async.Core/Signals/Options/SignalCreationOptions.cs index 5daa95d..dd8f269 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Signals/Options/SignalCreationOptions.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Signals/Options/SignalCreationOptions.cs @@ -17,11 +17,7 @@ public sealed record SignalCreationOptions /// Gets the default configuration for Signal creation options. /// The default options use serial publishing and are not stateless. Use this property as a /// baseline when configuring new Signals if standard behavior is desired. - public static SignalCreationOptions Default { get; } = new() - { - PublishingOption = PublishingOption.Serial, - IsStateless = false - }; + public static SignalCreationOptions Default { get; } = new() { PublishingOption = PublishingOption.Serial, IsStateless = false }; /// Gets the publishing option to use when processing the item. public required PublishingOption PublishingOption { get; init; } diff --git a/src/ReactiveUI.Primitives.Async.Core/Signals/SerialReplayLatestSignalAsync.cs b/src/ReactiveUI.Primitives.Async.Core/Signals/SerialReplayLatestSignalAsync.cs index 0c551c5..a92ce99 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Signals/SerialReplayLatestSignalAsync.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Signals/SerialReplayLatestSignalAsync.cs @@ -15,8 +15,7 @@ namespace ReactiveUI.Primitives.Async.Signals; /// or state broadcasts. /// The type of the elements processed by the Signal. /// An optional initial value to be emitted to new subscribers before any other values are published. -public sealed class SerialReplayLatestSignalAsync(Optional startValue) - : ISignalAsync +public sealed class SerialReplayLatestSignalAsync(Optional startValue) : ISignalAsync { /// IObservableAsync ISignalAsync.Values => this; diff --git a/src/ReactiveUI.Primitives.Async.Core/Signals/SerialStatelessReplayLatestSignalAsync.cs b/src/ReactiveUI.Primitives.Async.Core/Signals/SerialStatelessReplayLatestSignalAsync.cs index 8b6a0ad..8c4a284 100644 --- a/src/ReactiveUI.Primitives.Async.Core/Signals/SerialStatelessReplayLatestSignalAsync.cs +++ b/src/ReactiveUI.Primitives.Async.Core/Signals/SerialStatelessReplayLatestSignalAsync.cs @@ -15,8 +15,7 @@ namespace ReactiveUI.Primitives.Async.Signals; /// The type of the elements processed by the Signal. /// An optional initial value to be replayed to new observers before any values are published. If not specified, no /// value is replayed until the first value is received. -public sealed class SerialStatelessReplayLatestSignalAsync(Optional startValue) - : ISignalAsync +public sealed class SerialStatelessReplayLatestSignalAsync(Optional startValue) : ISignalAsync { /// IObservableAsync ISignalAsync.Values => this; diff --git a/src/ReactiveUI.Primitives.Async.Core/WitnessAsync.cs b/src/ReactiveUI.Primitives.Async.Core/WitnessAsync.cs index 889010d..361af65 100644 --- a/src/ReactiveUI.Primitives.Async.Core/WitnessAsync.cs +++ b/src/ReactiveUI.Primitives.Async.Core/WitnessAsync.cs @@ -642,8 +642,7 @@ private CancellationTokenSource MaterializeDisposeCts() /// /// The linked CTS to dispose, or if no allocation was needed. /// The effective cancellation token for the notification call. - internal readonly record struct LinkedTokenScope(CancellationTokenSource? Cts, CancellationToken Token) - : IDisposable + internal readonly record struct LinkedTokenScope(CancellationTokenSource? Cts, CancellationToken Token) : IDisposable { /// public void Dispose() => Cts?.Dispose(); diff --git a/src/ReactiveUI.Primitives.Core/Advanced/AggregateWitness.cs b/src/ReactiveUI.Primitives.Core/Advanced/AggregateWitness.cs index ee53ff1..c4c03d1 100644 --- a/src/ReactiveUI.Primitives.Core/Advanced/AggregateWitness.cs +++ b/src/ReactiveUI.Primitives.Core/Advanced/AggregateWitness.cs @@ -15,8 +15,7 @@ namespace ReactiveUI.Primitives.Advanced; /// The value-type accumulator that folds values and yields the result. /// The downstream observer. /// The seed accumulator. -public sealed class AggregateWitness(IObserver observer, TAggregator aggregator) - : IObserver, IDisposable +public sealed class AggregateWitness(IObserver observer, TAggregator aggregator) : IObserver, IDisposable where TAggregator : struct, IAggregator { /// The downstream observer. diff --git a/src/ReactiveUI.Primitives.Core/Advanced/AllPredicateWitness.cs b/src/ReactiveUI.Primitives.Core/Advanced/AllPredicateWitness.cs index c569480..bd5019e 100644 --- a/src/ReactiveUI.Primitives.Core/Advanced/AllPredicateWitness.cs +++ b/src/ReactiveUI.Primitives.Core/Advanced/AllPredicateWitness.cs @@ -8,8 +8,7 @@ namespace ReactiveUI.Primitives.Advanced; /// The source value type. /// The downstream observer. /// The predicate. -public sealed class AllPredicateWitness(IObserver observer, Func predicate) - : IObserver, IDisposable +public sealed class AllPredicateWitness(IObserver observer, Func predicate) : IObserver, IDisposable { /// The predicate. private readonly Func _predicate = predicate; diff --git a/src/ReactiveUI.Primitives.Core/Advanced/AnyPredicateWitness.cs b/src/ReactiveUI.Primitives.Core/Advanced/AnyPredicateWitness.cs index a8d961b..debba23 100644 --- a/src/ReactiveUI.Primitives.Core/Advanced/AnyPredicateWitness.cs +++ b/src/ReactiveUI.Primitives.Core/Advanced/AnyPredicateWitness.cs @@ -8,8 +8,7 @@ namespace ReactiveUI.Primitives.Advanced; /// The source value type. /// The downstream observer. /// The predicate. -public sealed class AnyPredicateWitness(IObserver observer, Func predicate) - : IObserver, IDisposable +public sealed class AnyPredicateWitness(IObserver observer, Func predicate) : IObserver, IDisposable { /// The downstream observer. private readonly IObserver _observer = observer; diff --git a/src/ReactiveUI.Primitives.Core/Advanced/AppendDelegateWitness.cs b/src/ReactiveUI.Primitives.Core/Advanced/AppendDelegateWitness.cs index d64cbdc..4a39293 100644 --- a/src/ReactiveUI.Primitives.Core/Advanced/AppendDelegateWitness.cs +++ b/src/ReactiveUI.Primitives.Core/Advanced/AppendDelegateWitness.cs @@ -10,8 +10,7 @@ namespace ReactiveUI.Primitives.Advanced; /// The error callback. /// The completion callback. /// The appended value. -public sealed class AppendDelegateWitness(Action onNext, Action onError, Action onCompleted, T value) - : IObserver, IDisposable +public sealed class AppendDelegateWitness(Action onNext, Action onError, Action onCompleted, T value) : IObserver, IDisposable { /// The next callback. private readonly Action _onNext = onNext; diff --git a/src/ReactiveUI.Primitives.Core/Advanced/ContainsWitness.cs b/src/ReactiveUI.Primitives.Core/Advanced/ContainsWitness.cs index 936a049..502dee6 100644 --- a/src/ReactiveUI.Primitives.Core/Advanced/ContainsWitness.cs +++ b/src/ReactiveUI.Primitives.Core/Advanced/ContainsWitness.cs @@ -9,8 +9,7 @@ namespace ReactiveUI.Primitives.Advanced; /// The downstream observer. /// The value to locate. /// The comparer used for equality checks. -public sealed class ContainsWitness(IObserver observer, T value, IEqualityComparer comparer) - : IObserver, IDisposable +public sealed class ContainsWitness(IObserver observer, T value, IEqualityComparer comparer) : IObserver, IDisposable { /// The value to locate. private readonly T _value = value; diff --git a/src/ReactiveUI.Primitives.Core/Advanced/UseSignal{TResource,T}.cs b/src/ReactiveUI.Primitives.Core/Advanced/UseSignal{TResource,T}.cs index 33ee056..f052b61 100644 --- a/src/ReactiveUI.Primitives.Core/Advanced/UseSignal{TResource,T}.cs +++ b/src/ReactiveUI.Primitives.Core/Advanced/UseSignal{TResource,T}.cs @@ -32,8 +32,8 @@ public IDisposable Subscribe(IObserver observer) try { resource = _resourceFactory(); - source = _signalFactory(resource) ?? - throw new InvalidOperationException("The signal factory returned null."); + source = _signalFactory(resource) + ?? throw new InvalidOperationException("The signal factory returned null."); } catch (Exception error) { diff --git a/src/ReactiveUI.Primitives.Core/Advanced/Witness.cs b/src/ReactiveUI.Primitives.Core/Advanced/Witness.cs index 37fff35..3f6301b 100644 --- a/src/ReactiveUI.Primitives.Core/Advanced/Witness.cs +++ b/src/ReactiveUI.Primitives.Core/Advanced/Witness.cs @@ -172,8 +172,7 @@ public void OnNext(T value) /// Callback invoked for each value. /// Callback invoked for an error. /// Callback invoked for completion. - private sealed class DelegateWitness(Action onNext, Action onError, Action onCompleted) - : IObserver + private sealed class DelegateWitness(Action onNext, Action onError, Action onCompleted) : IObserver { /// Callback invoked for each value. private readonly Action _onNext = onNext; diff --git a/src/ReactiveUI.Primitives.Core/Core/EventPattern{TEventArgs}.cs b/src/ReactiveUI.Primitives.Core/Core/EventPattern{TEventArgs}.cs index 418b4dd..5c91373 100644 --- a/src/ReactiveUI.Primitives.Core/Core/EventPattern{TEventArgs}.cs +++ b/src/ReactiveUI.Primitives.Core/Core/EventPattern{TEventArgs}.cs @@ -45,8 +45,8 @@ public EventPattern(object? sender, TEventArgs eventArgs) /// public bool Equals(EventPattern other) => - ReferenceEquals(Sender, other.Sender) && - EqualityComparer.Default.Equals(EventArgs, other.EventArgs); + ReferenceEquals(Sender, other.Sender) + && EqualityComparer.Default.Equals(EventArgs, other.EventArgs); /// public override bool Equals(object? obj) => obj is EventPattern other && Equals(other); diff --git a/src/ReactiveUI.Primitives.Core/ReactiveUI.Primitives.Core.csproj b/src/ReactiveUI.Primitives.Core/ReactiveUI.Primitives.Core.csproj index f220f2e..6245b43 100644 --- a/src/ReactiveUI.Primitives.Core/ReactiveUI.Primitives.Core.csproj +++ b/src/ReactiveUI.Primitives.Core/ReactiveUI.Primitives.Core.csproj @@ -32,6 +32,12 @@ + + + + + + diff --git a/src/ReactiveUI.Primitives.Core/Signals/KeepWithSignal{T,TState}.cs b/src/ReactiveUI.Primitives.Core/Signals/KeepWithSignal{T,TState}.cs index bbbafdd..b34445a 100644 --- a/src/ReactiveUI.Primitives.Core/Signals/KeepWithSignal{T,TState}.cs +++ b/src/ReactiveUI.Primitives.Core/Signals/KeepWithSignal{T,TState}.cs @@ -15,8 +15,7 @@ namespace ReactiveUI.Primitives.Signals; /// The source sequence. /// The state passed to the predicate. /// The predicate applied to each source value and the state. -public sealed class KeepWithSignal(IObservable source, TState state, Func predicate) - : IRequireCurrentThread +public sealed class KeepWithSignal(IObservable source, TState state, Func predicate) : IRequireCurrentThread { /// The source sequence. private readonly IObservable _source = source; @@ -46,8 +45,7 @@ public IDisposable Subscribe(IObserver observer) /// The downstream observer. /// The state passed to the predicate. /// The predicate applied to each source value and the state. - private sealed class KeepWithWitness(IObserver observer, TState state, Func predicate) - : IObserver + private sealed class KeepWithWitness(IObserver observer, TState state, Func predicate) : IObserver { /// The downstream observer. private readonly IObserver _observer = observer; diff --git a/src/ReactiveUI.Primitives.Core/Signals/MapSignal{TSource,TResult}.cs b/src/ReactiveUI.Primitives.Core/Signals/MapSignal{TSource,TResult}.cs index e155753..62b7fd5 100644 --- a/src/ReactiveUI.Primitives.Core/Signals/MapSignal{TSource,TResult}.cs +++ b/src/ReactiveUI.Primitives.Core/Signals/MapSignal{TSource,TResult}.cs @@ -11,8 +11,7 @@ namespace ReactiveUI.Primitives.Signals; /// The TResult type. /// The source value. /// The selector value. -public sealed class MapSignal(IObservable source, Func selector) - : IRequireCurrentThread +public sealed class MapSignal(IObservable source, Func selector) : IRequireCurrentThread { /// Stores state for the signal implementation. private readonly IObservable _source = source; diff --git a/src/ReactiveUI.Primitives.Core/Signals/ProjectedReadOnlyState{TSource,TResult}.cs b/src/ReactiveUI.Primitives.Core/Signals/ProjectedReadOnlyState{TSource,TResult}.cs index 1997048..2d59f1b 100644 --- a/src/ReactiveUI.Primitives.Core/Signals/ProjectedReadOnlyState{TSource,TResult}.cs +++ b/src/ReactiveUI.Primitives.Core/Signals/ProjectedReadOnlyState{TSource,TResult}.cs @@ -230,8 +230,7 @@ private void ThrowIfDisposed() /// Projection subscription. /// Parent projection. /// Observer to remove. - private sealed class Subscription(ProjectedReadOnlyState parent, IObserver observer) - : IDisposable + private sealed class Subscription(ProjectedReadOnlyState parent, IObserver observer) : IDisposable { /// Parent projection. [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2213:Disposable fields should be disposed", Justification = "Back-reference to the parent projection; unsubscribed, not owned.")] diff --git a/src/ReactiveUI.Primitives.Core/Signals/Signal{T}.cs b/src/ReactiveUI.Primitives.Core/Signals/Signal{T}.cs index fdaca75..6fda5f4 100644 --- a/src/ReactiveUI.Primitives.Core/Signals/Signal{T}.cs +++ b/src/ReactiveUI.Primitives.Core/Signals/Signal{T}.cs @@ -52,8 +52,8 @@ public class Signal : ISignal /// Gets a value indicating whether indicates whether the subject has observers subscribed to it. public virtual bool HasObservers => - (_singleActionSubscription is not null || _singleObserverSubscription is not null || _subscriptionCount != 0) && - !_isStopped; + (_singleActionSubscription is not null || _singleObserverSubscription is not null || _subscriptionCount != 0) + && !_isStopped; /// Gets a value indicating whether indicates whether the subject has been disposed. public virtual bool IsDisposed => _isDisposed; diff --git a/src/ReactiveUI.Primitives.Core/Signals/TapWithSignal{T,TState}.cs b/src/ReactiveUI.Primitives.Core/Signals/TapWithSignal{T,TState}.cs index e8e4fa3..353d281 100644 --- a/src/ReactiveUI.Primitives.Core/Signals/TapWithSignal{T,TState}.cs +++ b/src/ReactiveUI.Primitives.Core/Signals/TapWithSignal{T,TState}.cs @@ -15,8 +15,7 @@ namespace ReactiveUI.Primitives.Signals; /// The source sequence. /// The state passed to the action. /// The action invoked for each value and the state. -public sealed class TapWithSignal(IObservable source, TState state, Action onNext) - : IRequireCurrentThread +public sealed class TapWithSignal(IObservable source, TState state, Action onNext) : IRequireCurrentThread { /// The source sequence. private readonly IObservable _source = source; diff --git a/src/ReactiveUI.Primitives.Extensions.Core/FirstAsTaskHelper.cs b/src/ReactiveUI.Primitives.Extensions.Core/FirstAsTaskHelper.cs index 07d3828..17cd70b 100644 --- a/src/ReactiveUI.Primitives.Extensions.Core/FirstAsTaskHelper.cs +++ b/src/ReactiveUI.Primitives.Extensions.Core/FirstAsTaskHelper.cs @@ -25,8 +25,7 @@ public static Task FirstAsTask(IObservable source) /// Combined TaskCompletionSource + IObserver — one heap allocation per call instead of two. /// The element type. - private sealed class FirstWitness() - : TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously), IObserver + private sealed class FirstWitness() : TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously), IObserver { /// Latches to 1 once the task has been settled so subsequent callbacks are no-ops. private int _settled; diff --git a/src/ReactiveUI.Primitives.Extensions.Core/Operators/BinaryMinMaxObservable.cs b/src/ReactiveUI.Primitives.Extensions.Core/Operators/BinaryMinMaxObservable.cs index e2eeac7..bbfaf0b 100644 --- a/src/ReactiveUI.Primitives.Extensions.Core/Operators/BinaryMinMaxObservable.cs +++ b/src/ReactiveUI.Primitives.Extensions.Core/Operators/BinaryMinMaxObservable.cs @@ -11,8 +11,7 @@ namespace ReactiveUI.Primitives.Extensions.Operators; /// The first source. /// The second source. /// true to emit the maximum; false to emit the minimum. -public sealed class BinaryMinMaxObservable(IObservable left, IObservable right, bool emitMaximum) - : IObservable +public sealed class BinaryMinMaxObservable(IObservable left, IObservable right, bool emitMaximum) : IObservable where T : struct, IComparable { /// The first source. diff --git a/src/ReactiveUI.Primitives.Extensions.Core/Operators/SynchronizeAsyncObservable.cs b/src/ReactiveUI.Primitives.Extensions.Core/Operators/SynchronizeAsyncObservable.cs index 378d7c8..80a3cab 100644 --- a/src/ReactiveUI.Primitives.Extensions.Core/Operators/SynchronizeAsyncObservable.cs +++ b/src/ReactiveUI.Primitives.Extensions.Core/Operators/SynchronizeAsyncObservable.cs @@ -24,8 +24,7 @@ public IDisposable Subscribe(IObserver<(T Value, IDisposable Sync)> observer) /// The sink for the . /// The downstream observer. - private sealed class SynchronizeAsyncSink(IObserver<(T Value, IDisposable Sync)> downstream) - : IObserver, IDisposable + private sealed class SynchronizeAsyncSink(IObserver<(T Value, IDisposable Sync)> downstream) : IObserver, IDisposable { /// The gate for state access. private readonly Lock _gate = new(); diff --git a/src/ReactiveUI.Primitives.R3Bridge.Generator/R3AsyncBridgeGenerator.cs b/src/ReactiveUI.Primitives.R3Bridge.Generator/R3AsyncBridgeGenerator.cs index 3cd0fd7..d21c7b8 100644 --- a/src/ReactiveUI.Primitives.R3Bridge.Generator/R3AsyncBridgeGenerator.cs +++ b/src/ReactiveUI.Primitives.R3Bridge.Generator/R3AsyncBridgeGenerator.cs @@ -138,10 +138,10 @@ private sealed class PrimitivesToR3AsyncObserver(global::R3Async.AsyncObserve public void Initialize(IncrementalGeneratorInitializationContext context) => context.RegisterSourceOutput(context.CompilationProvider, static (output, compilation) => { - if (compilation.GetTypeByMetadataName("R3Async.AsyncObservable`1") is null || - compilation.GetTypeByMetadataName("R3Async.AsyncObserver`1") is null || - compilation.GetTypeByMetadataName("R3Async.Result") is null || - compilation.GetTypeByMetadataName("ReactiveUI.Primitives.Async.IObservableAsync`1") is null) + if (compilation.GetTypeByMetadataName("R3Async.AsyncObservable`1") is null + || compilation.GetTypeByMetadataName("R3Async.AsyncObserver`1") is null + || compilation.GetTypeByMetadataName("R3Async.Result") is null + || compilation.GetTypeByMetadataName("ReactiveUI.Primitives.Async.IObservableAsync`1") is null) { return; } diff --git a/src/ReactiveUI.Primitives.R3Bridge.Generator/R3BridgeGenerator.cs b/src/ReactiveUI.Primitives.R3Bridge.Generator/R3BridgeGenerator.cs index 6d826e0..1d69aff 100644 --- a/src/ReactiveUI.Primitives.R3Bridge.Generator/R3BridgeGenerator.cs +++ b/src/ReactiveUI.Primitives.R3Bridge.Generator/R3BridgeGenerator.cs @@ -280,8 +280,8 @@ public void Initialize(IncrementalGeneratorInitializationContext context) context.RegisterSourceOutput(context.CompilationProvider, static (output, compilation) => { - if (!HasR3ObservableShapes(compilation) || - compilation.GetTypeByMetadataName("ReactiveUI.Primitives.Async.IObservableAsync`1") is null) + if (!HasR3ObservableShapes(compilation) + || compilation.GetTypeByMetadataName("ReactiveUI.Primitives.Async.IObservableAsync`1") is null) { return; } @@ -294,7 +294,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) /// Consumer compilation inspected by the generator. /// when the required R3 observable, observer, and result shapes exist. private static bool HasR3ObservableShapes(Compilation compilation) => - compilation.GetTypeByMetadataName("R3.Observable`1") is not null && - compilation.GetTypeByMetadataName("R3.Observer`1") is not null && - compilation.GetTypeByMetadataName("R3.Result") is not null; + compilation.GetTypeByMetadataName("R3.Observable`1") is not null + && compilation.GetTypeByMetadataName("R3.Observer`1") is not null + && compilation.GetTypeByMetadataName("R3.Result") is not null; } diff --git a/src/ReactiveUI.Primitives.Reactive/ReactiveUI.Primitives.Reactive.csproj b/src/ReactiveUI.Primitives.Reactive/ReactiveUI.Primitives.Reactive.csproj index 6cc3dec..c17b5f9 100644 --- a/src/ReactiveUI.Primitives.Reactive/ReactiveUI.Primitives.Reactive.csproj +++ b/src/ReactiveUI.Primitives.Reactive/ReactiveUI.Primitives.Reactive.csproj @@ -50,6 +50,11 @@ + + + + + diff --git a/src/ReactiveUI.Primitives/Concurrency/SynchronizationContextSequencer.cs b/src/ReactiveUI.Primitives/Concurrency/SynchronizationContextSequencer.cs index 85da073..640cf5e 100644 --- a/src/ReactiveUI.Primitives/Concurrency/SynchronizationContextSequencer.cs +++ b/src/ReactiveUI.Primitives/Concurrency/SynchronizationContextSequencer.cs @@ -18,8 +18,8 @@ public SynchronizationContextSequencer(SynchronizationContext context) => /// Gets a sequencer for the current synchronization context. /// There is no current synchronization context. public static SynchronizationContextSequencer Current => - new(SynchronizationContext.Current ?? - throw new InvalidOperationException("There is no current synchronization context.")); + new(SynchronizationContext.Current + ?? throw new InvalidOperationException("There is no current synchronization context.")); /// Gets the synchronization context used to schedule work. public SynchronizationContext Context { get; } diff --git a/src/ReactiveUI.Primitives/ReactiveUI.Primitives.csproj b/src/ReactiveUI.Primitives/ReactiveUI.Primitives.csproj index 8e9668d..932b3ed 100644 --- a/src/ReactiveUI.Primitives/ReactiveUI.Primitives.csproj +++ b/src/ReactiveUI.Primitives/ReactiveUI.Primitives.csproj @@ -29,6 +29,11 @@ + + + + + diff --git a/src/benchmarks/ReactiveUI.Primitives.Benchmarks/SmokeParityValidator.cs b/src/benchmarks/ReactiveUI.Primitives.Benchmarks/SmokeParityValidator.cs index 0399c19..338ef1d 100644 --- a/src/benchmarks/ReactiveUI.Primitives.Benchmarks/SmokeParityValidator.cs +++ b/src/benchmarks/ReactiveUI.Primitives.Benchmarks/SmokeParityValidator.cs @@ -137,9 +137,9 @@ internal static void Validate(string output) /// The benchmark result name. /// when the name has a known library prefix. private static bool HasKnownLibraryPrefix(string name) => - name.StartsWith(PrimitivesPrefix, StringComparison.Ordinal) || - name.StartsWith(SystemReactivePrefix, StringComparison.Ordinal) || - name.StartsWith(R3Prefix, StringComparison.Ordinal); + name.StartsWith(PrimitivesPrefix, StringComparison.Ordinal) + || name.StartsWith(SystemReactivePrefix, StringComparison.Ordinal) + || name.StartsWith(R3Prefix, StringComparison.Ordinal); /// Validates that every library row in the group matches the Primitives value. /// The Primitives result name. @@ -247,13 +247,13 @@ private static (string? PrimitivesName, int PrimitivesValue, int SystemReactiveV return null; } - return primitivesValue == expected.Primitives && - systemReactiveValue == expected.SystemReactive && - r3Value == expected.R3 + return primitivesValue == expected.Primitives + && systemReactiveValue == expected.SystemReactive + && r3Value == expected.R3 ? null - : $"{primitivesName}: documented scheduling difference changed; expected " + - $"Primitives={expected.Primitives}, System.Reactive={expected.SystemReactive}, R3={expected.R3}, " + - $"but got Primitives={primitivesValue}, System.Reactive={systemReactiveValue}, R3={r3Value}."; + : $"{primitivesName}: documented scheduling difference changed; expected " + + $"Primitives={expected.Primitives}, System.Reactive={expected.SystemReactive}, R3={expected.R3}, " + + $"but got Primitives={primitivesValue}, System.Reactive={systemReactiveValue}, R3={r3Value}."; } /// Parses a single key=value smoke output row into its name and integer value. diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity10.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity10.cs index 5e2fb88..b41c86c 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity10.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity10.cs @@ -158,8 +158,8 @@ public async Task WhenCombineLatest10AllSourcesEmit_ThenSelectorResultEmitted() null); await EmitSeedAndPlaceValuesAsync(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10); await emitted.Task.WaitAsync(TimeSpan.FromSeconds(EmissionTimeoutSeconds)); - await Assert.That(results[0]).IsEqualTo(1 + PlaceValue1 + PlaceValue2 + PlaceValue3 + PlaceValue4 + - PlaceValue5 + PlaceValue6 + PlaceValue7 + PlaceValue8 + PlaceValue9); + await Assert.That(results[0]).IsEqualTo(1 + PlaceValue1 + PlaceValue2 + PlaceValue3 + PlaceValue4 + + PlaceValue5 + PlaceValue6 + PlaceValue7 + PlaceValue8 + PlaceValue9); await CompleteAllAsync(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10); } diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity11.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity11.cs index 621607e..31b38fa 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity11.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity11.cs @@ -154,9 +154,9 @@ public async Task WhenCombineLatest11AllSourcesEmit_ThenSelectorResultEmitted() v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11).SubscribeAsync(RecordAndSignalValues(results, emitted), null); await EmitSeedAndPlaceValuesAsync(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11); await emitted.Task.WaitAsync(TimeSpan.FromSeconds(EmissionTimeoutSeconds)); - await Assert.That(results[0]).IsEqualTo(1 + PlaceValue1 + PlaceValue2 + PlaceValue3 + PlaceValue4 + - PlaceValue5 + PlaceValue6 + PlaceValue7 + PlaceValue8 + PlaceValue9 + - PlaceValue10); + await Assert.That(results[0]).IsEqualTo(1 + PlaceValue1 + PlaceValue2 + PlaceValue3 + PlaceValue4 + + PlaceValue5 + PlaceValue6 + PlaceValue7 + PlaceValue8 + PlaceValue9 + + PlaceValue10); await CompleteAllAsync(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11); } diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity12.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity12.cs index 9ebbc48..a8ae6b2 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity12.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity12.cs @@ -165,9 +165,9 @@ public async Task WhenCombineLatest12AllSourcesEmit_ThenSelectorResultEmitted() v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11 + v12).SubscribeAsync(RecordAndSignalValues(results, emitted), null); await EmitSeedAndPlaceValuesAsync(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12); await emitted.Task.WaitAsync(TimeSpan.FromSeconds(EmissionTimeoutSeconds)); - await Assert.That(results[0]).IsEqualTo(1 + PlaceValue1 + PlaceValue2 + PlaceValue3 + PlaceValue4 + - PlaceValue5 + PlaceValue6 + PlaceValue7 + PlaceValue8 + PlaceValue9 + - PlaceValue10 + PlaceValue11); + await Assert.That(results[0]).IsEqualTo(1 + PlaceValue1 + PlaceValue2 + PlaceValue3 + PlaceValue4 + + PlaceValue5 + PlaceValue6 + PlaceValue7 + PlaceValue8 + PlaceValue9 + + PlaceValue10 + PlaceValue11); await CompleteAllAsync(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12); } diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity13.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity13.cs index 81f6699..eb3b10d 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity13.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity13.cs @@ -172,9 +172,9 @@ public async Task WhenCombineLatest13AllSourcesEmit_ThenSelectorResultEmitted() v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11 + v12 + v13).SubscribeAsync(RecordAndSignalValues(results, emitted), null); await EmitSeedAndPlaceValuesAsync(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13); await emitted.Task.WaitAsync(TimeSpan.FromSeconds(EmissionTimeoutSeconds)); - await Assert.That(results[0]).IsEqualTo(1 + PlaceValue1 + PlaceValue2 + PlaceValue3 + PlaceValue4 + - PlaceValue5 + PlaceValue6 + PlaceValue7 + PlaceValue8 + PlaceValue9 + - PlaceValue10 + PlaceValue11 + PlaceValue12); + await Assert.That(results[0]).IsEqualTo(1 + PlaceValue1 + PlaceValue2 + PlaceValue3 + PlaceValue4 + + PlaceValue5 + PlaceValue6 + PlaceValue7 + PlaceValue8 + PlaceValue9 + + PlaceValue10 + PlaceValue11 + PlaceValue12); await CompleteAllAsync(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13); } diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity14.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity14.cs index bd1e8b2..fdc3594 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity14.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity14.cs @@ -180,9 +180,9 @@ public async Task WhenCombineLatest14AllSourcesEmit_ThenSelectorResultEmitted() v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14).SubscribeAsync(RecordAndSignalValues(results, emitted), null); await EmitSeedAndPlaceValuesAsync(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14); await emitted.Task.WaitAsync(TimeSpan.FromSeconds(EmissionTimeoutSeconds)); - await Assert.That(results[0]).IsEqualTo(1 + PlaceValue1 + PlaceValue2 + PlaceValue3 + PlaceValue4 + - PlaceValue5 + PlaceValue6 + PlaceValue7 + PlaceValue8 + PlaceValue9 + - PlaceValue10 + PlaceValue11 + PlaceValue12 + PlaceValue13); + await Assert.That(results[0]).IsEqualTo(1 + PlaceValue1 + PlaceValue2 + PlaceValue3 + PlaceValue4 + + PlaceValue5 + PlaceValue6 + PlaceValue7 + PlaceValue8 + PlaceValue9 + + PlaceValue10 + PlaceValue11 + PlaceValue12 + PlaceValue13); await CompleteAllAsync(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14); } diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity15.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity15.cs index a26167d..2b8bf46 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity15.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity15.cs @@ -188,10 +188,10 @@ public async Task WhenCombineLatest15AllSourcesEmit_ThenSelectorResultEmitted() v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14 + v15).SubscribeAsync(RecordAndSignalValues(results, emitted), null); await EmitSeedAndPlaceValuesAsync(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15); await emitted.Task.WaitAsync(TimeSpan.FromSeconds(EmissionTimeoutSeconds)); - await Assert.That(results[0]).IsEqualTo(1 + PlaceValue1 + PlaceValue2 + PlaceValue3 + PlaceValue4 + - PlaceValue5 + PlaceValue6 + PlaceValue7 + PlaceValue8 + PlaceValue9 + - PlaceValue10 + PlaceValue11 + PlaceValue12 + PlaceValue13 + - PlaceValue14); + await Assert.That(results[0]).IsEqualTo(1 + PlaceValue1 + PlaceValue2 + PlaceValue3 + PlaceValue4 + + PlaceValue5 + PlaceValue6 + PlaceValue7 + PlaceValue8 + PlaceValue9 + + PlaceValue10 + PlaceValue11 + PlaceValue12 + PlaceValue13 + + PlaceValue14); await CompleteAllAsync(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15); } diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity16.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity16.cs index 56c32f2..6423367 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity16.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity16.cs @@ -48,8 +48,8 @@ await Assert.That(async () => await s1.Values.CombineLatest( s14.Values, s15.Values, throwingSrc, - static (v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) => v1 + v2 + v3 + v4 + v5 + v6 + - v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14 + v15 + v16).SubscribeAsync(static (_, _) => default, null)) + static (v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) => v1 + v2 + v3 + v4 + v5 + v6 + + v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14 + v15 + v16).SubscribeAsync(static (_, _) => default, null)) .ThrowsExactly(); } @@ -91,8 +91,8 @@ public async Task WhenCombineLatest16DisposedBeforeCombine_ThenOnNextCombinedIsG s14.Values, s15.Values, s16.Values, - static (v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) => v1 + v2 + v3 + v4 + v5 + v6 + - v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14 + v15 + v16).SubscribeAsync(RecordValues(results), null); + static (v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) => v1 + v2 + v3 + v4 + v5 + v6 + + v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14 + v15 + v16).SubscribeAsync(RecordValues(results), null); await EmitSeedAndPlaceValuesAsync(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16); await sub.DisposeAsync(); await s1.OnNextAsync(PostDisposeValue, CancellationToken.None); @@ -138,8 +138,8 @@ public async Task WhenCombineLatest16OneSourceErrors_ThenCombinedErrorForwarded( s14.Values, s15.Values, s16.Values, - static (v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) => v1 + v2 + v3 + v4 + v5 + v6 + - v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14 + v15 + v16).SubscribeAsync(static (_, _) => default, (ex, _) => + static (v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) => v1 + v2 + v3 + v4 + v5 + v6 + + v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14 + v15 + v16).SubscribeAsync(static (_, _) => default, (ex, _) => { receivedError = ex; IgnoredResult.Of(errorReceived.TrySetResult()); @@ -190,14 +190,14 @@ public async Task WhenCombineLatest16AllSourcesEmit_ThenSelectorResultEmitted() s14.Values, s15.Values, s16.Values, - static (v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) => v1 + v2 + v3 + v4 + v5 + v6 + - v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14 + v15 + v16).SubscribeAsync(RecordAndSignalValues(results, emitted), null); + static (v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) => v1 + v2 + v3 + v4 + v5 + v6 + + v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14 + v15 + v16).SubscribeAsync(RecordAndSignalValues(results, emitted), null); await EmitSeedAndPlaceValuesAsync(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16); await emitted.Task.WaitAsync(TimeSpan.FromSeconds(EmissionTimeoutSeconds)); - await Assert.That(results[0]).IsEqualTo(1 + PlaceValue1 + PlaceValue2 + PlaceValue3 + PlaceValue4 + - PlaceValue5 + PlaceValue6 + PlaceValue7 + PlaceValue8 + PlaceValue9 + - PlaceValue10 + PlaceValue11 + PlaceValue12 + PlaceValue13 + - PlaceValue14 + PlaceValue15); + await Assert.That(results[0]).IsEqualTo(1 + PlaceValue1 + PlaceValue2 + PlaceValue3 + PlaceValue4 + + PlaceValue5 + PlaceValue6 + PlaceValue7 + PlaceValue8 + PlaceValue9 + + PlaceValue10 + PlaceValue11 + PlaceValue12 + PlaceValue13 + + PlaceValue14 + PlaceValue15); await CompleteAllAsync(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16); } @@ -239,8 +239,8 @@ public async Task WhenCombineLatest16AllSourcesComplete_ThenCombinedCompletes() s14.Values, s15.Values, s16.Values, - static (v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) => v1 + v2 + v3 + v4 + v5 + v6 + - v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14 + v15 + v16).SubscribeAsync(static (_, _) => default, null, r => + static (v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) => v1 + v2 + v3 + v4 + v5 + v6 + + v7 + v8 + v9 + v10 + v11 + v12 + v13 + v14 + v15 + v16).SubscribeAsync(static (_, _) => default, null, r => { _ = completed.TrySetResult(r); return default; diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity9.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity9.cs index db8c79e..1b33aba 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity9.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/CombineLatestArityTests.Arity9.cs @@ -148,8 +148,8 @@ public async Task WhenCombineLatest9AllSourcesEmit_ThenSelectorResultEmitted() static (v1, v2, v3, v4, v5, v6, v7, v8, v9) => v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9).SubscribeAsync(RecordAndSignalValues(results, emitted), null); await EmitSeedAndPlaceValuesAsync(s1, s2, s3, s4, s5, s6, s7, s8, s9); await emitted.Task.WaitAsync(TimeSpan.FromSeconds(EmissionTimeoutSeconds)); - await Assert.That(results[0]).IsEqualTo(1 + PlaceValue1 + PlaceValue2 + PlaceValue3 + PlaceValue4 + - PlaceValue5 + PlaceValue6 + PlaceValue7 + PlaceValue8); + await Assert.That(results[0]).IsEqualTo(1 + PlaceValue1 + PlaceValue2 + PlaceValue3 + PlaceValue4 + + PlaceValue5 + PlaceValue6 + PlaceValue7 + PlaceValue8); await CompleteAllAsync(s1, s2, s3, s4, s5, s6, s7, s8, s9); } diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/CombiningOperatorTests.Multicast.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/CombiningOperatorTests.Multicast.cs index ce0515f..a2f9e7e 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/CombiningOperatorTests.Multicast.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/CombiningOperatorTests.Multicast.cs @@ -445,11 +445,7 @@ public async Task WhenMulticastConnectionDisposedTwice_ThenSecondIsNoOp() [Test] public async Task WhenPublishWithConcurrentStatefulOptions_ThenEmitsToSubscribers() { - SignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Concurrent, - IsStateless = false - }; + SignalCreationOptions options = new() { PublishingOption = PublishingOption.Concurrent, IsStateless = false }; var items = await ConnectAndCollectAsync(SignalAsync.Range(1, PublishedItemCount).Publish(options)); @@ -461,11 +457,7 @@ public async Task WhenPublishWithConcurrentStatefulOptions_ThenEmitsToSubscriber [Test] public async Task WhenPublishWithSerialStatelessOptions_ThenEmitsToSubscribers() { - SignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Serial, - IsStateless = true - }; + SignalCreationOptions options = new() { PublishingOption = PublishingOption.Serial, IsStateless = true }; var items = await ConnectAndCollectAsync(SignalAsync.Range(1, PublishedItemCount).Publish(options)); @@ -477,11 +469,7 @@ public async Task WhenPublishWithSerialStatelessOptions_ThenEmitsToSubscribers() [Test] public async Task WhenPublishWithConcurrentStatelessOptions_ThenEmitsToSubscribers() { - SignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Concurrent, - IsStateless = true - }; + SignalCreationOptions options = new() { PublishingOption = PublishingOption.Concurrent, IsStateless = true }; var items = await ConnectAndCollectAsync(SignalAsync.Range(1, PublishedItemCount).Publish(options)); @@ -492,11 +480,7 @@ public async Task WhenPublishWithConcurrentStatelessOptions_ThenEmitsToSubscribe [Test] public void WhenPublishWithUnsupportedOptions_ThenThrowsArgumentOutOfRange() { - SignalCreationOptions options = new() - { - PublishingOption = UnsupportedPublishingOption, - IsStateless = false - }; + SignalCreationOptions options = new() { PublishingOption = UnsupportedPublishingOption, IsStateless = false }; _ = Assert.Throws( () => SignalAsync.Range(1, PublishedItemCount).Publish(options)); @@ -507,11 +491,7 @@ public void WhenPublishWithUnsupportedOptions_ThenThrowsArgumentOutOfRange() [Test] public async Task WhenPublishWithInitialValueAndConcurrentStatefulOptions_ThenReplaysInitialValue() { - BehaviorSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Concurrent, - IsStateless = false - }; + BehaviorSignalCreationOptions options = new() { PublishingOption = PublishingOption.Concurrent, IsStateless = false }; var items = await ConnectAndCollectAsync( SignalAsync.Range(1, PublishedItemCount).Publish(PublishedInitialValue, options)); @@ -525,11 +505,7 @@ public async Task WhenPublishWithInitialValueAndConcurrentStatefulOptions_ThenRe [Test] public async Task WhenPublishWithInitialValueAndSerialStatelessOptions_ThenReplaysInitialValue() { - BehaviorSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Serial, - IsStateless = true - }; + BehaviorSignalCreationOptions options = new() { PublishingOption = PublishingOption.Serial, IsStateless = true }; var items = await ConnectAndCollectAsync( SignalAsync.Range(1, PublishedItemCount).Publish(PublishedInitialValue, options)); @@ -543,11 +519,7 @@ public async Task WhenPublishWithInitialValueAndSerialStatelessOptions_ThenRepla [Test] public async Task WhenPublishWithInitialValueAndConcurrentStatelessOptions_ThenReplaysInitialValue() { - BehaviorSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Concurrent, - IsStateless = true - }; + BehaviorSignalCreationOptions options = new() { PublishingOption = PublishingOption.Concurrent, IsStateless = true }; var items = await ConnectAndCollectAsync( SignalAsync.Range(1, PublishedItemCount).Publish(PublishedInitialValue, options)); @@ -560,11 +532,7 @@ public async Task WhenPublishWithInitialValueAndConcurrentStatelessOptions_ThenR [Test] public void WhenPublishWithInitialValueAndUnsupportedOptions_ThenThrowsArgumentOutOfRange() { - BehaviorSignalCreationOptions options = new() - { - PublishingOption = UnsupportedPublishingOption, - IsStateless = false - }; + BehaviorSignalCreationOptions options = new() { PublishingOption = UnsupportedPublishingOption, IsStateless = false }; _ = Assert.Throws( () => SignalAsync.Range(1, PublishedItemCount).Publish(PublishedInitialValue, options)); @@ -575,11 +543,7 @@ public void WhenPublishWithInitialValueAndUnsupportedOptions_ThenThrowsArgumentO [Test] public async Task WhenReplayLatestPublishWithConcurrentStatefulOptions_ThenEmitsToSubscribers() { - ReplayLatestSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Concurrent, - IsStateless = false - }; + ReplayLatestSignalCreationOptions options = new() { PublishingOption = PublishingOption.Concurrent, IsStateless = false }; var items = await ConnectAndCollectAsync( SignalAsync.Range(1, PublishedItemCount).ReplayLatestPublish(options)); @@ -592,11 +556,7 @@ public async Task WhenReplayLatestPublishWithConcurrentStatefulOptions_ThenEmits [Test] public async Task WhenReplayLatestPublishWithSerialStatelessOptions_ThenEmitsToSubscribers() { - ReplayLatestSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Serial, - IsStateless = true - }; + ReplayLatestSignalCreationOptions options = new() { PublishingOption = PublishingOption.Serial, IsStateless = true }; var items = await ConnectAndCollectAsync( SignalAsync.Range(1, PublishedItemCount).ReplayLatestPublish(options)); @@ -609,11 +569,7 @@ public async Task WhenReplayLatestPublishWithSerialStatelessOptions_ThenEmitsToS [Test] public async Task WhenReplayLatestPublishWithConcurrentStatelessOptions_ThenEmitsToSubscribers() { - ReplayLatestSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Concurrent, - IsStateless = true - }; + ReplayLatestSignalCreationOptions options = new() { PublishingOption = PublishingOption.Concurrent, IsStateless = true }; var items = await ConnectAndCollectAsync( SignalAsync.Range(1, PublishedItemCount).ReplayLatestPublish(options)); @@ -625,11 +581,7 @@ public async Task WhenReplayLatestPublishWithConcurrentStatelessOptions_ThenEmit [Test] public void WhenReplayLatestPublishWithUnsupportedOptions_ThenThrowsArgumentOutOfRange() { - ReplayLatestSignalCreationOptions options = new() - { - PublishingOption = UnsupportedPublishingOption, - IsStateless = true - }; + ReplayLatestSignalCreationOptions options = new() { PublishingOption = UnsupportedPublishingOption, IsStateless = true }; _ = Assert.Throws( () => SignalAsync.Range(1, PublishedItemCount).ReplayLatestPublish(options)); diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/Internals/CombineLatestIndexedObserverTests.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/Internals/CombineLatestIndexedObserverTests.cs index df5666f..c71ae92 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/Internals/CombineLatestIndexedObserverTests.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/Internals/CombineLatestIndexedObserverTests.cs @@ -72,8 +72,7 @@ public async Task WhenOnCompletedAsync_ThenLifecycleForwardsCompletion() /// Minimal concrete subclass exposing the base's EmitLatestAsync invocation count. /// The downstream observer. - private sealed class TestSubscription(IObserverAsync observer) - : SyncLatestCoordinatorBase(observer, 1) + private sealed class TestSubscription(IObserverAsync observer) : SyncLatestCoordinatorBase(observer, 1) { /// Gets the number of times has been invoked. public int EmitLatestCount { get; private set; } diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/Internals/SyncLatestCoordinatorBaseTests.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/Internals/SyncLatestCoordinatorBaseTests.cs index 53149b7..cb1761e 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/Internals/SyncLatestCoordinatorBaseTests.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/Internals/SyncLatestCoordinatorBaseTests.cs @@ -145,8 +145,7 @@ public async Task WhenLockOnValuesLock_ThenNoThrow() /// Minimal concrete subclass exposing the base's protected surface for testing. /// The downstream observer. /// The number of upstream sources. - private sealed class TestSubscription(IObserverAsync observer, int sourceCount) - : SyncLatestCoordinatorBase(observer, sourceCount) + private sealed class TestSubscription(IObserverAsync observer, int sourceCount) : SyncLatestCoordinatorBase(observer, sourceCount) { /// Gets the indices passed to in order. public List SubscribedIndices { get; } = []; diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/SignalTests.BehaviorAndReplay.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/SignalTests.BehaviorAndReplay.cs index 6243651..8908dd0 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/SignalTests.BehaviorAndReplay.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/SignalTests.BehaviorAndReplay.cs @@ -39,11 +39,7 @@ public async Task WhenBehaviorSignalWithStartValue_ThenNewSubscriberReceivesLate [Test] public async Task WhenBehaviorSignalConcurrent_ThenNewSubscriberReceivesLatest() { - BehaviorSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Concurrent, - IsStateless = false - }; + BehaviorSignalCreationOptions options = new() { PublishingOption = PublishingOption.Concurrent, IsStateless = false }; const int StartValue = 100; var signal = Signal.CreateBehavior(StartValue, options); List items = []; @@ -102,11 +98,7 @@ public async Task WhenReplayLatestSignal_ThenLateSubscriberGetsLatestValue() [Test] public async Task WhenReplayLatestSignalConcurrent_ThenLateSubscriberGetsLatest() { - ReplayLatestSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Concurrent, - IsStateless = false - }; + ReplayLatestSignalCreationOptions options = new() { PublishingOption = PublishingOption.Concurrent, IsStateless = false }; var signal = Signal.CreateReplayLatest(options); const int PushedValue = 5; @@ -138,11 +130,7 @@ public async Task WhenReplayLatestSignalConcurrent_ThenLateSubscriberGetsLatest( [Test] public async Task WhenBehaviorSignalStateless_ThenEmitsStartValueToNewSubscriber() { - BehaviorSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Serial, - IsStateless = true - }; + BehaviorSignalCreationOptions options = new() { PublishingOption = PublishingOption.Serial, IsStateless = true }; var signal = Signal.CreateBehavior("initial", options); List items = []; TaskCompletionSource firstReceived = new(TaskCreationOptions.RunContinuationsAsynchronously); @@ -167,11 +155,7 @@ public async Task WhenBehaviorSignalStateless_ThenEmitsStartValueToNewSubscriber [Test] public async Task WhenReplayLatestStateless_ThenEmitsLatestToNewSubscriber() { - ReplayLatestSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Serial, - IsStateless = true - }; + ReplayLatestSignalCreationOptions options = new() { PublishingOption = PublishingOption.Serial, IsStateless = true }; var signal = Signal.CreateReplayLatest(options); const int PushedValue = 7; @@ -199,11 +183,7 @@ public async Task WhenReplayLatestStateless_ThenEmitsLatestToNewSubscriber() [Test] public async Task WhenConcurrentStatelessReplayLatest_ThenEmitsLatest() { - ReplayLatestSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Concurrent, - IsStateless = true - }; + ReplayLatestSignalCreationOptions options = new() { PublishingOption = PublishingOption.Concurrent, IsStateless = true }; var signal = Signal.CreateReplayLatest(options); const int PushedValue = 77; @@ -235,11 +215,7 @@ public async Task WhenConcurrentStatelessReplayLatest_ThenEmitsLatest() [Test] public async Task WhenStatelessReplayLastOnNext_ThenLateSubscriberReceivesReplayedValue() { - ReplayLatestSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Serial, - IsStateless = true - }; + ReplayLatestSignalCreationOptions options = new() { PublishingOption = PublishingOption.Serial, IsStateless = true }; var signal = Signal.CreateReplayLatest(options); const int FirstValue = 42; @@ -266,11 +242,7 @@ public async Task WhenStatelessReplayLastOnNext_ThenLateSubscriberReceivesReplay [Test] public async Task WhenStatelessReplayLastOnErrorResume_ThenObserverReceivesError() { - ReplayLatestSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Serial, - IsStateless = true - }; + ReplayLatestSignalCreationOptions options = new() { PublishingOption = PublishingOption.Serial, IsStateless = true }; var signal = Signal.CreateReplayLatest(options); TaskCompletionSource errorTcs = new(TaskCreationOptions.RunContinuationsAsynchronously); @@ -294,11 +266,7 @@ public async Task WhenStatelessReplayLastOnErrorResume_ThenObserverReceivesError [Test] public async Task WhenConcurrentStatelessReplayLastOnErrorResume_ThenObserverReceivesError() { - ReplayLatestSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Concurrent, - IsStateless = true - }; + ReplayLatestSignalCreationOptions options = new() { PublishingOption = PublishingOption.Concurrent, IsStateless = true }; var signal = Signal.CreateReplayLatest(options); TaskCompletionSource errorTcs = new(TaskCreationOptions.RunContinuationsAsynchronously); @@ -322,11 +290,7 @@ public async Task WhenConcurrentStatelessReplayLastOnErrorResume_ThenObserverRec [Test] public async Task WhenStatelessReplayLastOnCompleted_ThenObserverReceivesCompletionAndStateResets() { - ReplayLatestSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Serial, - IsStateless = true - }; + ReplayLatestSignalCreationOptions options = new() { PublishingOption = PublishingOption.Serial, IsStateless = true }; var signal = Signal.CreateReplayLatest(options); TaskCompletionSource resultTcs = new(TaskCreationOptions.RunContinuationsAsynchronously); @@ -364,11 +328,7 @@ public async Task WhenStatelessReplayLastOnCompleted_ThenObserverReceivesComplet [Test] public async Task WhenConcurrentStatelessReplayLastOnCompleted_ThenObserverReceivesCompletion() { - ReplayLatestSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Concurrent, - IsStateless = true - }; + ReplayLatestSignalCreationOptions options = new() { PublishingOption = PublishingOption.Concurrent, IsStateless = true }; var signal = Signal.CreateReplayLatest(options); TaskCompletionSource resultTcs = new(TaskCreationOptions.RunContinuationsAsynchronously); @@ -392,11 +352,7 @@ public async Task WhenConcurrentStatelessReplayLastOnCompleted_ThenObserverRecei [Test] public async Task WhenStatelessReplayLastDispose_ThenCompletesSuccessfully() { - ReplayLatestSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Serial, - IsStateless = true - }; + ReplayLatestSignalCreationOptions options = new() { PublishingOption = PublishingOption.Serial, IsStateless = true }; var signal = Signal.CreateReplayLatest(options); await signal.OnNextAsync(1, CancellationToken.None); @@ -408,11 +364,7 @@ public async Task WhenStatelessReplayLastDispose_ThenCompletesSuccessfully() [Test] public async Task WhenConcurrentStatelessReplayLastDispose_ThenCompletesSuccessfully() { - ReplayLatestSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Concurrent, - IsStateless = true - }; + ReplayLatestSignalCreationOptions options = new() { PublishingOption = PublishingOption.Concurrent, IsStateless = true }; var signal = Signal.CreateReplayLatest(options); await signal.OnNextAsync(1, CancellationToken.None); @@ -424,11 +376,7 @@ public async Task WhenConcurrentStatelessReplayLastDispose_ThenCompletesSuccessf [Test] public async Task WhenConcurrentStatelessBehavior_ThenEmitsStartValue() { - BehaviorSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Concurrent, - IsStateless = true - }; + BehaviorSignalCreationOptions options = new() { PublishingOption = PublishingOption.Concurrent, IsStateless = true }; const int StartValue = 55; var signal = Signal.CreateBehavior(StartValue, options); List items = []; @@ -606,11 +554,7 @@ public async Task WhenReplayLatestDisposeAsync_ThenCompletesSuccessfully() [Test] public async Task WhenConcurrentReplayLatestOnErrorResume_ThenObserverReceivesError() { - ReplayLatestSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Concurrent, - IsStateless = false - }; + ReplayLatestSignalCreationOptions options = new() { PublishingOption = PublishingOption.Concurrent, IsStateless = false }; var signal = Signal.CreateReplayLatest(options); TaskCompletionSource errorTcs = new(TaskCreationOptions.RunContinuationsAsynchronously); @@ -634,11 +578,7 @@ public async Task WhenConcurrentReplayLatestOnErrorResume_ThenObserverReceivesEr [Test] public async Task WhenConcurrentReplayLatestOnCompleted_ThenObserverReceivesCompletion() { - ReplayLatestSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Concurrent, - IsStateless = false - }; + ReplayLatestSignalCreationOptions options = new() { PublishingOption = PublishingOption.Concurrent, IsStateless = false }; var signal = Signal.CreateReplayLatest(options); TaskCompletionSource resultTcs = new(TaskCreationOptions.RunContinuationsAsynchronously); @@ -704,11 +644,7 @@ public async Task WhenSubscribeToCompletedReplayLatest_ThenObserverReceivesImmed [Test] public async Task WhenReplayLatestOnNextWithCustomToken_ThenForwardsValue() { - var signal = Signal.CreateReplayLatest(new() - { - PublishingOption = PublishingOption.Concurrent, - IsStateless = false - }); + var signal = Signal.CreateReplayLatest(new() { PublishingOption = PublishingOption.Concurrent, IsStateless = false }); TaskCompletionSource tcs = new(TaskCreationOptions.RunContinuationsAsynchronously); await using var sub = await signal.Values.SubscribeAsync((v, _) => @@ -731,11 +667,7 @@ public async Task WhenReplayLatestOnNextWithCustomToken_ThenForwardsValue() [Test] public async Task WhenReplayLatestOnErrorResumeWithCustomToken_ThenForwardsError() { - var signal = Signal.CreateReplayLatest(new() - { - PublishingOption = PublishingOption.Concurrent, - IsStateless = false - }); + var signal = Signal.CreateReplayLatest(new() { PublishingOption = PublishingOption.Concurrent, IsStateless = false }); TaskCompletionSource tcs = new(TaskCreationOptions.RunContinuationsAsynchronously); await using var sub = await signal.Values.SubscribeAsync( @@ -760,11 +692,7 @@ public async Task WhenReplayLatestOnErrorResumeWithCustomToken_ThenForwardsError [Test] public async Task WhenStatelessReplayLatestOnNextWithCustomToken_ThenForwardsValue() { - var signal = Signal.CreateReplayLatest(new() - { - PublishingOption = PublishingOption.Serial, - IsStateless = true - }); + var signal = Signal.CreateReplayLatest(new() { PublishingOption = PublishingOption.Serial, IsStateless = true }); TaskCompletionSource tcs = new(TaskCreationOptions.RunContinuationsAsynchronously); await using var sub = await signal.Values.SubscribeAsync((value, _) => @@ -787,11 +715,7 @@ public async Task WhenStatelessReplayLatestOnNextWithCustomToken_ThenForwardsVal [Test] public async Task WhenStatelessReplayLatestOnErrorResumeWithCustomToken_ThenForwardsError() { - var signal = Signal.CreateReplayLatest(new() - { - PublishingOption = PublishingOption.Serial, - IsStateless = true - }); + var signal = Signal.CreateReplayLatest(new() { PublishingOption = PublishingOption.Serial, IsStateless = true }); TaskCompletionSource tcs = new(TaskCreationOptions.RunContinuationsAsynchronously); await using var sub = await signal.Values.SubscribeAsync( @@ -831,11 +755,7 @@ public async Task WhenReplayLatestSubscribeWithCustomToken_ThenSubscriptionCompl [Test] public async Task WhenStatelessReplayLatestSubscribeWithCustomToken_ThenSubscriptionCompletes() { - var signal = Signal.CreateReplayLatest(new() - { - PublishingOption = PublishingOption.Serial, - IsStateless = true - }); + var signal = Signal.CreateReplayLatest(new() { PublishingOption = PublishingOption.Serial, IsStateless = true }); using CancellationTokenSource cts = new(); var sub = await signal.Values.SubscribeAsync(static (_, _) => default, cts.Token); @@ -866,11 +786,7 @@ public async Task WhenReplayLatestSubscriptionDisposedAfterSignalDisposed_ThenDi [Test] public async Task WhenStatelessReplayLatestSubscriptionDisposedAfterSignalDisposed_ThenDisposeIsIdempotent() { - var signal = Signal.CreateReplayLatest(new() - { - PublishingOption = PublishingOption.Serial, - IsStateless = true - }); + var signal = Signal.CreateReplayLatest(new() { PublishingOption = PublishingOption.Serial, IsStateless = true }); var sub = await signal.Values.SubscribeAsync(static (_, _) => default); await signal.DisposeAsync(); @@ -898,11 +814,7 @@ public async Task WhenReplayLatestSignalDisposedTwice_ThenIdempotent() [Test] public async Task WhenStatelessReplayLastSignalDisposedTwice_ThenIdempotent() { - var signal = Signal.CreateReplayLatest(new() - { - PublishingOption = PublishingOption.Serial, - IsStateless = true - }); + var signal = Signal.CreateReplayLatest(new() { PublishingOption = PublishingOption.Serial, IsStateless = true }); await signal.DisposeAsync(); await signal.DisposeAsync(); diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/SignalTests.CreationOptions.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/SignalTests.CreationOptions.cs index 4fcc428..3b7ae94 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/SignalTests.CreationOptions.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/SignalTests.CreationOptions.cs @@ -86,11 +86,7 @@ public async Task WhenCreateReplayLatestWithSerialStatefulOptions_ThenReplaysLat [Test] public async Task WhenConcurrentReplayLatestSignalDisposed_ThenLaterSubscribeIsCancelled() { - BehaviorSignalCreationOptions options = new() - { - PublishingOption = PublishingOption.Concurrent, - IsStateless = false - }; + BehaviorSignalCreationOptions options = new() { PublishingOption = PublishingOption.Concurrent, IsStateless = false }; var signal = Signal.CreateBehavior(OptionsStartValue, options); await signal.DisposeAsync(); diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/TimeBasedOperatorTests.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/TimeBasedOperatorTests.cs index ccd0602..9af054d 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/TimeBasedOperatorTests.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/TimeBasedOperatorTests.cs @@ -935,8 +935,8 @@ public ValueTask DisposeAsync() /// Invokes the callback once if the timer has not been disposed. internal void Fire() { - if (Volatile.Read(ref _disposed) != 0 || - Interlocked.Exchange(ref _fired, 1) != 0) + if (Volatile.Read(ref _disposed) != 0 + || Interlocked.Exchange(ref _fired, 1) != 0) { return; } diff --git a/src/tests/ReactiveUI.Primitives.Async.Tests/UnhandledExceptionCapture.cs b/src/tests/ReactiveUI.Primitives.Async.Tests/UnhandledExceptionCapture.cs index 78bf6aa..3721b7b 100644 --- a/src/tests/ReactiveUI.Primitives.Async.Tests/UnhandledExceptionCapture.cs +++ b/src/tests/ReactiveUI.Primitives.Async.Tests/UnhandledExceptionCapture.cs @@ -61,8 +61,8 @@ public UnhandledExceptionCapture() using PeriodicTimer poll = new(PollInterval); - while (TimeProvider.System.GetUtcNow() < deadline && - await poll.WaitForNextTickAsync(CancellationToken.None)) + while (TimeProvider.System.GetUtcNow() < deadline + && await poll.WaitForNextTickAsync(CancellationToken.None)) { match = Find(predicate); diff --git a/src/tests/ReactiveUI.Primitives.Blazor.Tests/ReactiveComponentBaseTests.cs b/src/tests/ReactiveUI.Primitives.Blazor.Tests/ReactiveComponentBaseTests.cs index 2c402e6..5479448 100644 --- a/src/tests/ReactiveUI.Primitives.Blazor.Tests/ReactiveComponentBaseTests.cs +++ b/src/tests/ReactiveUI.Primitives.Blazor.Tests/ReactiveComponentBaseTests.cs @@ -234,10 +234,7 @@ private sealed class ComponentHarness : IAsyncDisposable public async Task AttachAsync() { HarnessComponent? captured = null; - var parameters = ParameterView.FromDictionary(new Dictionary - { - ["Captured"] = (Action)(component => captured = component), - }); + var parameters = ParameterView.FromDictionary(new Dictionary { ["Captured"] = (Action)(component => captured = component), }); await Dispatcher.InvokeAsync(() => _renderer.BeginRenderingComponent(parameters)); diff --git a/src/tests/ReactiveUI.Primitives.Extensions.Tests/AotSafeAssertionExtensions.cs b/src/tests/ReactiveUI.Primitives.Extensions.Tests/AotSafeAssertionExtensions.cs index 39ae467..332060d 100644 --- a/src/tests/ReactiveUI.Primitives.Extensions.Tests/AotSafeAssertionExtensions.cs +++ b/src/tests/ReactiveUI.Primitives.Extensions.Tests/AotSafeAssertionExtensions.cs @@ -27,7 +27,7 @@ internal static class AotSafeAssertionExtensions /// The expected element sequence. /// The chained collection-equivalency assertion. internal IsEquivalentToAssertion IsCollectionEqualTo( - IEnumerable expected) - => source.IsEquivalentTo(expected, EqualityComparer.Default); + IEnumerable expected) => + source.IsEquivalentTo(expected, EqualityComparer.Default); } } diff --git a/src/tests/ReactiveUI.Primitives.Reactive.Tests/DispatchWorkItemBaseTests.cs b/src/tests/ReactiveUI.Primitives.Reactive.Tests/DispatchWorkItemBaseTests.cs index aafa78c..0318998 100644 --- a/src/tests/ReactiveUI.Primitives.Reactive.Tests/DispatchWorkItemBaseTests.cs +++ b/src/tests/ReactiveUI.Primitives.Reactive.Tests/DispatchWorkItemBaseTests.cs @@ -43,8 +43,7 @@ public async Task RunDisposesTheStartedWorkWhenCancellationClaimsTheItemDuringIt /// The scheduler passed back to the scheduled action. /// The scheduled state. /// The scheduled action. - private sealed class ProbeWorkItem(IScheduler scheduler, int state, Func action) - : DispatchWorkItemBase(scheduler, state, action) + private sealed class ProbeWorkItem(IScheduler scheduler, int state, Func action) : DispatchWorkItemBase(scheduler, state, action) { /// Claims the single cancellation transition, as a concurrent dispose would. /// for the first caller. diff --git a/src/tests/ReactiveUI.Primitives.Tests/ConcurencyTests.cs b/src/tests/ReactiveUI.Primitives.Tests/ConcurencyTests.cs index 5fe4a3d..cbb0be0 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/ConcurencyTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/ConcurencyTests.cs @@ -126,8 +126,8 @@ private sealed class InlineExecutionProbe /// Completes with whether this callback ran inline on the scheduling thread. public void RecordExecution() => _ = Completed.TrySetResult( - Environment.CurrentManagedThreadId == _schedulingThreadId && - Volatile.Read(ref _scheduling) != 0); + Environment.CurrentManagedThreadId == _schedulingThreadId + && Volatile.Read(ref _scheduling) != 0); } /// Records whether a scheduled callback ran at all, so a cancellation can be shown to have suppressed it. diff --git a/src/tests/ReactiveUI.Primitives.Tests/DispatchSequencerBaseTests.cs b/src/tests/ReactiveUI.Primitives.Tests/DispatchSequencerBaseTests.cs index 46d1207..3bbb343 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/DispatchSequencerBaseTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/DispatchSequencerBaseTests.cs @@ -432,8 +432,7 @@ private bool Post(Action drain) /// Sequencer under test. /// Recorded values. /// Value to record. - private sealed class DrainingWorkItem(ConfigurableDispatchSequencer sequencer, List values, int value) - : IWorkItem + private sealed class DrainingWorkItem(ConfigurableDispatchSequencer sequencer, List values, int value) : IWorkItem { /// Sequencer under test. private readonly ConfigurableDispatchSequencer _sequencer = sequencer; diff --git a/src/tests/ReactiveUI.Primitives.Tests/EverySignalTests.cs b/src/tests/ReactiveUI.Primitives.Tests/EverySignalTests.cs index 23b81ef..1929b75 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/EverySignalTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/EverySignalTests.cs @@ -297,10 +297,7 @@ private sealed class SubscribingThread private SubscribingThread(Action body) { _body = body; - _thread = new(Run) - { - IsBackground = true, - }; + _thread = new(Run) { IsBackground = true, }; } /// Starts a subscription body on a background thread of its own. diff --git a/src/tests/ReactiveUI.Primitives.Tests/FatalExceptionHelperTests.cs b/src/tests/ReactiveUI.Primitives.Tests/FatalExceptionHelperTests.cs index 564f5e6..545fb14 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/FatalExceptionHelperTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/FatalExceptionHelperTests.cs @@ -45,8 +45,8 @@ public async Task IsFatalReturnsFalseForRecoverableExceptions() /// The exception type. /// An exception instance. private static Exception CreateException( - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | - DynamicallyAccessedMemberTypes.NonPublicConstructors)] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors + | DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type exceptionType) => (Exception)System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject(exceptionType); } diff --git a/src/tests/ReactiveUI.Primitives.Tests/OnErrorResumeNextSignalTests.cs b/src/tests/ReactiveUI.Primitives.Tests/OnErrorResumeNextSignalTests.cs index 3536a61..901ee71 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/OnErrorResumeNextSignalTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/OnErrorResumeNextSignalTests.cs @@ -196,8 +196,7 @@ public IEnumerator> GetEnumerator() => /// The first source to return. /// The error to throw on the second move. /// The source value type. - private sealed class ThrowingAfterFirstEnumerable(IObservable first, Exception error) - : IEnumerable> + private sealed class ThrowingAfterFirstEnumerable(IObservable first, Exception error) : IEnumerable> { /// public IEnumerator> GetEnumerator() diff --git a/src/tests/ReactiveUI.Primitives.Tests/RxNamesTests.Helpers.cs b/src/tests/ReactiveUI.Primitives.Tests/RxNamesTests.Helpers.cs index 922faf9..31301e7 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/RxNamesTests.Helpers.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/RxNamesTests.Helpers.cs @@ -282,8 +282,8 @@ private static IObservable CombineLatestOfEleven(CombineLatestSources sourc sources.Tenth, sources.Eleventh, static (value1, value2, value3, value4, value5, value6, value7, value8, value9, value10, value11) => - value1 + value2 + value3 + value4 + value5 + value6 + value7 + value8 + value9 + value10 + - value11); + value1 + value2 + value3 + value4 + value5 + value6 + value7 + value8 + value9 + value10 + + value11); /// Builds the twelve-source CombineLatest overload. /// The named source signals. @@ -314,8 +314,8 @@ private static IObservable CombineLatestOfTwelve(CombineLatestSources sourc value10, value11, value12) => - value1 + value2 + value3 + value4 + value5 + value6 + value7 + value8 + value9 + value10 + - value11 + value12); + value1 + value2 + value3 + value4 + value5 + value6 + value7 + value8 + value9 + value10 + + value11 + value12); /// Builds the thirteen-source CombineLatest overload. /// The named source signals. @@ -348,8 +348,8 @@ private static IObservable CombineLatestOfThirteen(CombineLatestSources sou value11, value12, value13) => - value1 + value2 + value3 + value4 + value5 + value6 + value7 + value8 + value9 + value10 + - value11 + value12 + value13); + value1 + value2 + value3 + value4 + value5 + value6 + value7 + value8 + value9 + value10 + + value11 + value12 + value13); /// Builds the fourteen-source CombineLatest overload. /// The named source signals. @@ -384,8 +384,8 @@ private static IObservable CombineLatestOfFourteen(CombineLatestSources sou value12, value13, value14) => - value1 + value2 + value3 + value4 + value5 + value6 + value7 + value8 + value9 + value10 + - value11 + value12 + value13 + value14); + value1 + value2 + value3 + value4 + value5 + value6 + value7 + value8 + value9 + value10 + + value11 + value12 + value13 + value14); /// Builds the fifteen-source CombineLatest overload. /// The named source signals. @@ -422,8 +422,8 @@ private static IObservable CombineLatestOfFifteen(CombineLatestSources sour value13, value14, value15) => - value1 + value2 + value3 + value4 + value5 + value6 + value7 + value8 + value9 + value10 + - value11 + value12 + value13 + value14 + value15); + value1 + value2 + value3 + value4 + value5 + value6 + value7 + value8 + value9 + value10 + + value11 + value12 + value13 + value14 + value15); /// Sums sixteen values for the widest CombineLatest overload. /// Value 1. @@ -464,22 +464,22 @@ private static int SumSixteen( int value14, int value15, int value16) => - value1 + - value2 + - value3 + - value4 + - value5 + - value6 + - value7 + - value8 + - value9 + - value10 + - value11 + - value12 + - value13 + - value14 + - value15 + - value16; + value1 + + value2 + + value3 + + value4 + + value5 + + value6 + + value7 + + value8 + + value9 + + value10 + + value11 + + value12 + + value13 + + value14 + + value15 + + value16; /// Subscribes to a source and collects its forwarded values. /// The source sequence. diff --git a/src/tests/ReactiveUI.Primitives.Tests/RxNamesTests.cs b/src/tests/ReactiveUI.Primitives.Tests/RxNamesTests.cs index f72e07b..dbeffb2 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/RxNamesTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/RxNamesTests.cs @@ -476,14 +476,14 @@ public async Task AbsoluteTimeOperatorsUseDefaultScheduler() await TestPolling.SpinUntil( () => - delayedScalar.Count == One && - delayedRange.Count == Two && - delayedExplicitRange.Count == Two && - delayedSubscriptionScalar.Count == One && - delayedSubscriptionRange.Count == Two && - delayedSubscriptionExplicitRange.Count == Two && - timeout is not null && - explicitTimeout is not null, + delayedScalar.Count == One + && delayedRange.Count == Two + && delayedExplicitRange.Count == Two + && delayedSubscriptionScalar.Count == One + && delayedSubscriptionRange.Count == Two + && delayedSubscriptionExplicitRange.Count == Two + && timeout is not null + && explicitTimeout is not null, TimeSpan.FromSeconds(PollTimeoutSeconds)); await Assert.That(delayedScalar.SequenceEqual([One])).IsTrue(); diff --git a/src/tests/ReactiveUI.Primitives.Tests/SwitchWitnessTests.cs b/src/tests/ReactiveUI.Primitives.Tests/SwitchWitnessTests.cs index da5bf86..b000674 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/SwitchWitnessTests.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/SwitchWitnessTests.cs @@ -426,10 +426,7 @@ private sealed class DeliveryThread private DeliveryThread(Action body) { _body = body; - _thread = new(Run) - { - IsBackground = true, - }; + _thread = new(Run) { IsBackground = true, }; } /// Starts a delivery body on a background thread of its own. From 7357f571825664e1aba038bbdb29284af372010d Mon Sep 17 00:00:00 2001 From: Glenn Watson <5834289+glennawatson@users.noreply.github.com> Date: Sat, 25 Jul 2026 21:18:52 +1000 Subject: [PATCH 6/8] style(apple): use target-typed dispatch times --- .../Platforms/apple/NSRunloopSequencer.cs | 2 +- src/ReactiveUI.Primitives/Platforms/apple/NSRunloopSequencer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ReactiveUI.Primitives.Reactive/Platforms/apple/NSRunloopSequencer.cs b/src/ReactiveUI.Primitives.Reactive/Platforms/apple/NSRunloopSequencer.cs index 8174093..9ba8aed 100644 --- a/src/ReactiveUI.Primitives.Reactive/Platforms/apple/NSRunloopSequencer.cs +++ b/src/ReactiveUI.Primitives.Reactive/Platforms/apple/NSRunloopSequencer.cs @@ -55,7 +55,7 @@ protected override IDisposable ScheduleOnDispatcher(Action work, TimeSpan dueTim { var block = new DispatchBlock(work); var nanoseconds = (long)dueTime.TotalMilliseconds * NanosecondsPerMillisecond; - DispatchQueue.MainQueue.DispatchAfter(new DispatchTime(DispatchTime.Now, nanoseconds), block); + DispatchQueue.MainQueue.DispatchAfter(new(DispatchTime.Now, nanoseconds), block); return Disposable.Create(block, static b => b.Cancel()); } } diff --git a/src/ReactiveUI.Primitives/Platforms/apple/NSRunloopSequencer.cs b/src/ReactiveUI.Primitives/Platforms/apple/NSRunloopSequencer.cs index be0285e..65b13f4 100644 --- a/src/ReactiveUI.Primitives/Platforms/apple/NSRunloopSequencer.cs +++ b/src/ReactiveUI.Primitives/Platforms/apple/NSRunloopSequencer.cs @@ -59,7 +59,7 @@ public sealed class NSRunloopSequencer : ISequencer private static void ScheduleDelayed(IWorkItem item, long dueTimestamp) { var nanoseconds = (long)DispatchSequencerState.DelayUntil(dueTimestamp).TotalMilliseconds * NanosecondsPerMillisecond; - DispatchQueue.MainQueue.DispatchAfter(new DispatchTime(DispatchTime.Now, nanoseconds), () => DispatchSequencerState.RunIfActive(item)); + DispatchQueue.MainQueue.DispatchAfter(new(DispatchTime.Now, nanoseconds), () => DispatchSequencerState.RunIfActive(item)); } /// Marshals the cached drain callback onto the main dispatch queue. From 856bd05e22b979b0793135780b1d7d27f3a52d8c Mon Sep 17 00:00:00 2001 From: Glenn Watson <5834289+glennawatson@users.noreply.github.com> Date: Sat, 25 Jul 2026 21:24:36 +1000 Subject: [PATCH 7/8] style(windows): collapse thread initializers --- .../ControlSequencerTests.cs | 6 +----- .../ControlSequencerTests.cs | 6 +----- .../DispatcherSequencerTests.cs | 6 +----- .../DispatcherSequencerTests.cs | 6 +----- 4 files changed, 4 insertions(+), 20 deletions(-) diff --git a/src/tests/ReactiveUI.Primitives.WinForms.Reactive.Tests/ControlSequencerTests.cs b/src/tests/ReactiveUI.Primitives.WinForms.Reactive.Tests/ControlSequencerTests.cs index 49e6db8..c5eecc9 100644 --- a/src/tests/ReactiveUI.Primitives.WinForms.Reactive.Tests/ControlSequencerTests.cs +++ b/src/tests/ReactiveUI.Primitives.WinForms.Reactive.Tests/ControlSequencerTests.cs @@ -55,11 +55,7 @@ public ControlHarness() ThreadId = Environment.CurrentManagedThreadId; ready.Set(); Application.Run(); - }) - { - IsBackground = true, - Name = "WinFormsControlHarness", - }; + }) { IsBackground = true, Name = "WinFormsControlHarness" }; _thread.SetApartmentState(ApartmentState.STA); _thread.Start(); diff --git a/src/tests/ReactiveUI.Primitives.WinForms.Tests/ControlSequencerTests.cs b/src/tests/ReactiveUI.Primitives.WinForms.Tests/ControlSequencerTests.cs index 1ed6691..50cd4ac 100644 --- a/src/tests/ReactiveUI.Primitives.WinForms.Tests/ControlSequencerTests.cs +++ b/src/tests/ReactiveUI.Primitives.WinForms.Tests/ControlSequencerTests.cs @@ -68,11 +68,7 @@ public ControlHarness() ThreadId = Environment.CurrentManagedThreadId; ready.Set(); Application.Run(); - }) - { - IsBackground = true, - Name = "WinFormsControlHarness", - }; + }) { IsBackground = true, Name = "WinFormsControlHarness" }; _thread.SetApartmentState(ApartmentState.STA); _thread.Start(); diff --git a/src/tests/ReactiveUI.Primitives.Wpf.Reactive.Tests/DispatcherSequencerTests.cs b/src/tests/ReactiveUI.Primitives.Wpf.Reactive.Tests/DispatcherSequencerTests.cs index 34d5d22..97da0bb 100644 --- a/src/tests/ReactiveUI.Primitives.Wpf.Reactive.Tests/DispatcherSequencerTests.cs +++ b/src/tests/ReactiveUI.Primitives.Wpf.Reactive.Tests/DispatcherSequencerTests.cs @@ -73,11 +73,7 @@ public DispatcherHarness() ThreadId = Environment.CurrentManagedThreadId; ready.Set(); Dispatcher.Run(); - }) - { - IsBackground = true, - Name = "WpfDispatcherHarness", - }; + }) { IsBackground = true, Name = "WpfDispatcherHarness" }; _thread.SetApartmentState(ApartmentState.STA); _thread.Start(); diff --git a/src/tests/ReactiveUI.Primitives.Wpf.Tests/DispatcherSequencerTests.cs b/src/tests/ReactiveUI.Primitives.Wpf.Tests/DispatcherSequencerTests.cs index a6170b4..4befe7b 100644 --- a/src/tests/ReactiveUI.Primitives.Wpf.Tests/DispatcherSequencerTests.cs +++ b/src/tests/ReactiveUI.Primitives.Wpf.Tests/DispatcherSequencerTests.cs @@ -88,11 +88,7 @@ public DispatcherHarness() ThreadId = Environment.CurrentManagedThreadId; ready.Set(); Dispatcher.Run(); - }) - { - IsBackground = true, - Name = "WpfDispatcherHarness", - }; + }) { IsBackground = true, Name = "WpfDispatcherHarness" }; _thread.SetApartmentState(ApartmentState.STA); _thread.Start(); From 46f5827d7b4825ba5201ea50e9a5d9243dc701bf Mon Sep 17 00:00:00 2001 From: Glenn Watson <5834289+glennawatson@users.noreply.github.com> Date: Sat, 25 Jul 2026 21:59:49 +1000 Subject: [PATCH 8/8] test: harden task cancellation test - Coordinate cancellation after the handler is ready.\n- Remove timer and thread-pool scheduling sensitivity. --- .../ReactiveUI.Primitives.Tests/SignalFromTaskTest.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/tests/ReactiveUI.Primitives.Tests/SignalFromTaskTest.cs b/src/tests/ReactiveUI.Primitives.Tests/SignalFromTaskTest.cs index 2c873ab..3f7cdf6 100644 --- a/src/tests/ReactiveUI.Primitives.Tests/SignalFromTaskTest.cs +++ b/src/tests/ReactiveUI.Primitives.Tests/SignalFromTaskTest.cs @@ -598,20 +598,20 @@ await Task.Delay(CommandDelayMilliseconds, cts.Token) public async Task SignalFromTaskHandlesTokenCancellation() { StatusTrail statusTrail = new(); + TaskCompletionSource cancellationReady = + new(TaskCreationOptions.RunContinuationsAsynchronously); TaskCompletionSource cleanupCompleted = new(TaskCreationOptions.RunContinuationsAsynchronously); TaskCompletionSource finallyCompleted = new(TaskCreationOptions.RunContinuationsAsynchronously); var position = 0; var fixture = Signal.FromTask(async cts => { RecordStatus(statusTrail, ref position, StartedCommand); - await Task.Delay(TokenCancellationDelayMilliseconds, cts.Token).HandleCancellation().ConfigureAwait(true); - var cancellationTask = CancelAfterDelayAsync(cts); - await Task.Delay(CleanupDelayMilliseconds, cts.Token).HandleCancellation(() => + _ = cancellationReady.TrySetResult(cts); + await Task.Delay(Timeout.InfiniteTimeSpan, cts.Token).HandleCancellation(() => { RecordCancellationCleanup(statusTrail, ref position); _ = cleanupCompleted.TrySetResult(); }).ConfigureAwait(true); - await cancellationTask.ConfigureAwait(false); if (!cts.IsCancellationRequested) { RecordStatus(statusTrail, ref position, FinishedCommandNormally); @@ -629,8 +629,9 @@ await Task.Delay(CleanupDelayMilliseconds, cts.Token).HandleCancellation(() => }); var result = false; using var subscription = fixture.Subscribe(_ => result = true); - await Task.Delay(InitialDelayMilliseconds).ConfigureAwait(true); + var cancellationSource = await cancellationReady.Task.ConfigureAwait(false); await Assert.That(StatusMessages(statusTrail)).Contains(StartedCommand); + await cancellationSource.CancelAsync().ConfigureAwait(false); await WaitForCancellationCallbacks(cleanupCompleted.Task, finallyCompleted.Task).ConfigureAwait(false); await Assert.That(StatusMessages(statusTrail)).Contains(StartingCancellingCommand); await Assert.That(StatusMessages(statusTrail)).Contains(ShouldAlwaysComeHere);