Skip to content

[TCGC] @@hierarchyBuilding drops properties that were inherited from the original base but not present in the new base #4505

@ArcturusZhang

Description

@ArcturusZhang

Description

When using @@hierarchyBuilding to change a model's base type to a different model that doesn't contain all the same properties as the original base, properties inherited from the original base are silently dropped from the model instead of being promoted to the model's own properties.

This causes generated SDK code to lose properties that were previously available via inheritance, breaking the API surface.

Repro

Consider a spec like (simplified from Azure Compute):

model UpdateResourceDefinition {
  @visibility(Lifecycle.Read) id?: string;
  @visibility(Lifecycle.Read) name?: string;
  @visibility(Lifecycle.Read) type?: string;
  tags?: Record<string>;
}

model GalleryUpdate extends UpdateResourceDefinition {
  properties?: GalleryProperties;
  identity?: GalleryIdentity;
}

In client.tsp:

#suppress "@azure-tools/typespec-azure-core/no-legacy-usage" "Preserve previous base type"
@@Azure.ClientGenerator.Core.Legacy.hierarchyBuilding(GalleryUpdate,
  Azure.ResourceManager.Foundations.ProxyResource,
  "csharp"
);

Expected behavior

TCGC should output GalleryUpdate with:

  • baseModel: ProxyResource
  • properties: [properties, identity, tags] — the tags property should be promoted from the dropped UpdateResourceDefinition base because ProxyResource does not define it.

(id, name, type are correctly omitted because ProxyResource provides them.)

Actual behavior

tspCodeModel.json shows:

{
  "name": "GalleryUpdate",
  "baseModel": { "$ref": "<ProxyResource>" },
  "properties": [
    { "name": "properties", ... },
    { "name": "identity", ... }
  ]
}

The tags property is missing entirely — it was on the original base (UpdateResourceDefinition) but is not on the new base (ProxyResource), and TCGC drops it instead of promoting it to GalleryUpdate.

Impact

In Azure SDK for .NET Compute migration (Azure/azure-sdk-for-net#58139):

  • Pre-existing baseline: public class GalleryPatch : ResourceData with public IDictionary<string, string> Tags { get; } (inherited from base).
  • After applying @@hierarchyBuilding(GalleryUpdate, ProxyResource, "csharp") to fix the base type, Tags is missing from generated code, breaking ApiCompat with MembersMustExist: Member 'Tags.get()' does not exist.

This affects 9 Gallery patch models in Compute: GalleryPatch, GalleryImagePatch, GalleryImageVersionPatch, GalleryApplicationPatch, GalleryApplicationVersionPatch, GalleryScriptPatch, GalleryScriptVersionPatch, GalleryInVmAccessControlProfilePatch, GalleryInVmAccessControlProfileVersionPatch.

Workaround

Add custom partial class code to re-introduce the missing properties — but this is fragile and duplicates the spec.

Environment

  • TCGC version: latest (as of @azure-typespec/http-client-csharp-mgmt emitter package shipped with azure-sdk-for-net main)
  • Reproducible with the Compute spec at azure-rest-api-specs commit bf19852c9f6 on branch compute/add-csharp-client-customizations (ArcturusZhang fork).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinglib:tcgcIssues for @azure-tools/typespec-client-generator-core library

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions