Skip to content

CMTK0009: report LeftJoin, RightJoin and FullJoin whose unmatched side is a no-default struct #261

Open

Description

@CaffeinatedCoder

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:

  • LeftJoin whose inner element is a no-default struct;
  • RightJoin whose outer element is one;
  • FullJoin where 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 Option before the join (inner.Select(Option.Some)). A default Option is None, so absence is explicit and a real default-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 under FULL 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

Done when

  • The rows pass and are shown caught by reverting the rule (verify-the-guard).
  • The Analyzers README's CMTK0009 table, design/analyzers.md and the changelog name the joins.
  • A claim in test/oracles/claims.json holds it.

Settled in the work

  • An Option element of a join is not reported. A default Option is None, and wrapping each element in an Option is the way out the message names; reporting an Option element would flag that rewrite in turn. Value objects, Result and [RequireCustomInitialization] structs are reported.
  • .NET 11 also adds tuple-returning LeftJoin, RightJoin and FullJoin<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 .Value before 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/value-objectsValue-object contracts and what the aspects generate for themenhancementNew feature or requestpackage/analyzersAffects CodoMetis.TypeKit.Analyzers

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions