Repository navigation
Description
Problem
LeftJoin and RightJoin (.NET 10) give default for the side with no match. Over a value object, Option, Result or [RequireCustomInitialization] struct, that is a value no constructor ran, and nothing reports it.
.NET 11 adds FullJoin, which gives default on either side, on Enumerable, Queryable and AsyncEnumerable (What's new in .NET 11 libraries, dotnet/runtime#127236). EF Core 11 translates it to FULL JOIN.
CMTK0009 reports the calls that return a no-default struct's default when they find nothing (FirstOrDefault, DefaultIfEmpty and the rest), and CMTK0008 already resolves these joins for its key check. The joins aren't in CMTK0009's list, so this widens the rule. That makes it 2.4.0 work, as with #229.
Change
CMTK0009 reports, resolved by symbol, every overload of:
LeftJoinwhose inner element is a no-default struct;RightJoinwhose outer element is one;FullJoinwhere either element is one.
That covers Enumerable, Queryable and AsyncEnumerable. FullJoin exists only for a net11.0 consumer; the analyzer (netstandard2.0) needs no target split.
The message names the way out:
- In memory: wrap each element in an
Optionbefore the join (inner.Select(Option.Some)). AdefaultOptionisNone, so absence is explicit and a realdefault-valued element stays a value. - In a query: select a nullable first, as CMTK0009 already says for
Queryable.DefaultIfEmpty. Check that EF Core 11 translates a nullable projection underFULL JOIN; if it doesn't, the row says so.
A Warning, with no code fix, since the rewrite is the author's (as for #129). An Option-returning join helper is out of scope; decide on it after this ships.
Test
- A row per method, host and side, plus controls: a class element, a nullable element,
Join(which has no unmatched side), and the side that is notdefault. Run acrossTypeArgumentKinds. - The
FullJoinrows compile against .NET 11 reference assemblies, or a stub with the same signatures if the analyzer tests have none yet. - If Completeness tests: every Queryable operator and every BCL array allocator covered or waived #240 lands first, its
Queryablecompleteness test listsFullJoin.
Done when
- The rows pass and are shown caught by reverting the rule (
verify-the-guard). - The Analyzers README's CMTK0009 table,
design/analyzers.mdand the changelog name the joins. - A claim in
test/oracles/claims.jsonholds it.
Settled in the work
- An
Optionelement of a join is not reported. AdefaultOptionisNone, and wrapping each element in anOptionis the way out the message names; reporting anOptionelement would flag that rewrite in turn. Value objects,Resultand[RequireCustomInitialization]structs are reported. - .NET 11 also adds tuple-returning
LeftJoin,RightJoinandFullJoin<TOuter, TInner, TKey>on all three hosts (the rows compile against the 11.0.0-rc.3.26522.148 reference assemblies). "Every overload" covers them. - EF Core 11 translates the nullable projection under
FULL JOIN(EF Core 11.0.0-rc.3.26522.148 on SQLite, run on the 11.0.0-rc.1 runtime, the only one installed:10 | null,20 | 20,null | 30); without it EF throws "Nullable object must have a value." Recorded as KB-059. - A join that does not bind (a lambda reading a same-project
.Valuebefore weaving) is read from its candidates: the elements from the sequences passed, and the candidate whose outer sequence the receiver is, so a query gets the query's advice.
Activity