Skip to content

CMTK0005 and CMTK0006 miss a type parameter constrained to a value object by new(), which CMTK0001 reports #207

Closed

Description

@CaffeinatedCoder

Found by a sweep of release/2.3.1 @ dbdd44e (lens: sibling-parity, analyzers) for the defect class of #191-#197; reproduced in a scratch analyzer test.

Package

CodoMetis.TypeKit.Analyzers

Version

2.3.0 (the same code in v2.3.0), and release/2.3.1 @ dbdd44e

How does it fail?

Silently. Two rules say nothing where their siblings report the same type

Problem

TypeKitSymbols.IsNoDefaultStruct (src/CodoMetis.TypeKit.Analyzers/TypeKitSymbols.cs:299-300), which CMTK0005 (DefaultFilledCollectionAnalyzer.cs:119) and CMTK0006 (UnassignedNoDefaultMemberAnalyzer.cs:163,171) call, admits a type parameter only with a struct constraint. DefaultRestriction, which CMTK0001 and CMTK0009 call, also admits ITypeParameterSymbol { HasConstructorConstraint: true, IsReferenceType: false }, since a generated class has no public parameterless constructor and such a T is a struct value object. The README defines CMTK0005 and CMTK0006 over "such a type", CMTK0001's list, which includes a type parameter whose constraints make it one.

Scenario

public interface IEntityId<TSelf> : IValue<int> where TSelf : IEntityId<TSelf> { }

public static T One<T>() where T : IEntityId<T>, new() => new T();     // CMTK0001
public static T[] Many<T>() where T : IEntityId<T>, new() => new T[3];  // nothing; with `struct, IEntityId<T>`: CMTK0005

public sealed class Holder<T> where T : IEntityId<T>, new()
{
    public T Id { get; set; }                                           // nothing; expected CMTK0006
}

Measured @ dbdd44e: the CMTK0005 and CMTK0006 rows report no diagnostic; the struct control and CMTK0001 on the same T report.

Change

IsNoDefaultStruct returns DefaultRestriction(type) is not null, which already admits only value types and new() type parameters that are not reference types. Both rules are Warnings, so reporting more needs no exception to decision 34.

Test

CMTK0005 and CMTK0006 rows for T : IEntityId<T>, new() and T : IValue<int>, new(), across TypeArgumentKinds (struct, new(), through a user interface, through another type parameter); reverted per verify-the-guard.

Environment

.NET SDK 10.0.4xx; any consumer.

Fixed in #216 (a3f72f5)

IsNoDefaultStruct is now DefaultRestriction(type) is not null, CMTK0001's predicate. NoDefaultKindTests holds CMTK0001, CMTK0005 and CMTK0006 to one oracle over all 80 kinds of TypeArgumentKinds (the reported new() shape through a derived interface added); the two new() kinds were the only disagreement, and no other rule called the narrow predicate.

Activity

  1. added this to the 2.3.1 milestone on Oct 30, 2026
  2. added
    bugSomething isn't working
    silentCompiles or answers and does less than it appears to
    severity/lowCosmetic, message or documentation-level difference
    package/analyzersAffects CodoMetis.TypeKit.Analyzers
    area/value-objectsValue-object contracts and what the aspects generate for them
    on Oct 30, 2026
  3. CaffeinatedCoder commented on Oct 31, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Fixed by #216, merged into release/2.3.1 (c855caa); ships in 2.3.1.

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 thembugSomething isn't workingpackage/analyzersAffects CodoMetis.TypeKit.Analyzersseverity/lowCosmetic, message or documentation-level differencesilentCompiles or answers and does less than it appears to

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions