Skip to content

CMTK0006 reports an explicit interface property that every constructor assigns through ((IHasId)this).Id = id #231

Closed

Description

@CaffeinatedCoder

Found by a pre-release sweep of release/2.3.1 @ dbdd44e plus #216, #217 and #218 as landed (lens: long-method, analyzers); reproduced in a scratch analyzer test.

Package

CodoMetis.TypeKit.Analyzers

Version

2.3.0 (measured on v2.3.0), and 2.3.1 as above

How does it fail?

A false positive: CMTK0006 is reported on code that does what its advice says. It is a Warning, so a warnings-as-errors build fails.

Problem

UnassignedNoDefaultMemberAnalyzer.Assignments.Collect (src/CodoMetis.TypeKit.Analyzers/UnassignedNoDefaultMemberAnalyzer.cs:329-345) records a property assignment only as IPropertyReferenceOperation { Instance: IInstanceReferenceOperation }, and adds property.Property. A class can reach its own explicit interface implementation only through a conversion, ((IHasId)this).Id = id. That assignment's target is the interface's property, and its Instance is an IConversionOperation over this. So the pattern doesn't match. Even if it did, the symbol would be IHasId.Id, not the class's implementation that is the candidate, and Members.Contains would miss it.

The member is a candidate on purpose: UnassignedNoDefaultMemberAnalyzerTests.cs:141 (ExplicitImplementation) holds that one that nothing sets is reported. The README's CMTK0006 advice and the message both say "assign it in every constructor".

Scenario

public interface IHasId { Plain Id { get; set; } }

public sealed class Order : IHasId
{
    public Order(Plain id) => ((IHasId)this).Id = id;
    Plain IHasId.Id { get; set; }
}

Measured on v2.3.0 and on 2.3.1: warning CMTK0006: 'IHasId.Id' starts as a default 'Plain', which passed no factory: it has no initializer, is not 'required', and the constructor 'Order(Plain)' does not assign it. Initialize it, or assign it in every constructor. Expected: no diagnostic. A local first, IHasId self = this; self.Id = id;, reports the same. That form isn't an assignment to the instance, so whether it should count is the fix's decision.

Change

In Collect, strip conversions from Instance down to an IInstanceReferenceOperation. When the property is an interface member, map it to the analysed type's implementation (type.FindImplementationForInterfaceMember) before adding it. The same goes for a ref/out argument and a tuple element, which go through Collect as well.

Test

Input class: the forms that assign a member in a constructor (plain, this., a cast of this to each interface the member implements, deconstruction, out/ref). Rows in UnassignedNoDefaultMemberAnalyzerTests: an explicit implementation assigned through the cast is silent. Two constructors where only one assigns through the cast report CMTK0006 naming the other, so the mapping doesn't turn into "assigns everything". Reverted per verify-the-guard.

Environment

.NET SDK 10.0.4xx; any consumer.

Activity

  1. added this to the 2.3.1 milestone on Oct 31, 2026
  2. added
    bugSomething isn't working
    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 31, 2026
  3. CaffeinatedCoder commented on Oct 31, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Fixed in #243. Decision on the local alias: IHasId self = this; self.Id = id; stays reported, since the rule doesn't follow locals; the README's CMTK0006 section now says so. The cast fix covers more than the explicit case: an implicit implementation reached through the cast, the second of two interfaces, a generic interface, a cast to the class itself (a field) and out through it were all reported and are silent now. A sibling found running the input class (Found-By: VA, its own commit): an object initializer or with of another instance inside a constructor counted as assigning this one, so CMTK0006 was silent there; only the containing instance counts now.

  4. CaffeinatedCoder commented on Oct 31, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Fixed by #243, merged into release/2.3.1 (88ae0fe); 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 difference

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions