Skip to content

Named object component aliases emit duplicate provided type #477

Description

@lukaszkrzywizna

Minimal reproduction

With SwaggerProvider 4.0.0 or 4.1.0 and .NET SDK 10.0.301:

open SwaggerProvider

type ReproApi =
    OpenApiClientProvider<"repro.json", SsrfProtection=false>

let client = ReproApi.Client()
{
  "openapi": "3.0.0",
  "info": { "title": "Named object alias reproduction", "version": "1.0.0" },
  "paths": {
    "/parent": {
      "get": {
        "operationId": "getParent",
        "responses": {
          "200": {
            "description": "Parent response",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Parent" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Parent": {
        "oneOf": [
          { "$ref": "#/components/schemas/Parent_Child" }
        ]
      },
      "Parent_Child": {
        "type": "object",
        "required": ["childValue"],
        "properties": {
          "childValue": { "type": "string" }
        }
      }
    }
  }
}

The equivalent single-reference anyOf and allOf component aliases fail in the same way.

Version matrix

SwaggerProvider Result
4.0.0 Reproduced
4.1.0 Reproduced

Actual behavior

dotnet build fails while emitting the provided assembly:

FS3033: The type provider 'SwaggerProvider.OpenApiClientTypeProvider' reported an error: duplicate entry 'Parent_Child' in type index table

Expected behavior

  • Parent resolves to the generated Parent_Child object type.
  • The Parent component path is cached as an alias to that type.
  • Parent_Child is added to the generated namespace exactly once.
  • An operation returning Parent exposes the properties generated for Parent_Child.

Root-cause analysis

DefinitionCompiler.compileSingleRefOrNewObject recognizes that Parent is a single-reference alias, releases the Parent name reservation, and resolves Parent_Child.

At the end of compileBySchema, the unconditional registerNew(tyName, tyType) call for fromByPathCompiler both caches the component path and registers the returned type in the namespace. The returned ProvidedTypeDefinition is the already-generated Parent_Child, so it is registered under both component entries. NamespaceAbstraction.GetProvidedTypes() consequently returns the same provided type more than once, and assembly emission fails with the duplicate type-index entry.

The fix should separate component-path caching from namespace registration for aliases, rather than suppressing, renaming, dropping, or weakening the generated type.

PR #375 did not cover this case. Its regression tests place a single-reference wrapper on an object property that points to a primitive alias. They do not compile a named top-level component that aliases another named object component, so no duplicate ProvidedTypeDefinition is emitted.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions