Skip to content

Commit 57a533c

Browse files
Smaug123claude
andcommitted
Reserve spec-defined names before generating anonymous type names
Anonymous inline schemas were named Type1, Type2, ... without checking the names already taken by the spec's definitions and responses, so a valid spec defining a type called "Type1" alongside an inline object schema generated two `type Type1` declarations and failed to compile with FS0037. The anonymous-name counter now starts past any sanitised definition/response name of the form Type{N}. Verified at the level the repo's generators are tested at: the new swagger-anonymous-types.json in ConsumePlugin defines "Type1" plus an inline object response, so a regression here fails the build. (An in-process generate test isn't currently possible: WoofWare.Expect forces Fantomas.FCS 7.0.3 in the test project, which is binary- incompatible with the Fantomas.FCS 6.1.1 that WoofWare.Whippet.Fantomas 0.7.3 was compiled against, so calling the generator's AST construction from the test host throws MissingMethodException.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 140b508 commit 57a533c

4 files changed

Lines changed: 127 additions & 0 deletions

File tree

ConsumePlugin/ConsumePlugin.fsproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,16 @@
118118
<Compile Include="Generated2SwaggerGitea.fs">
119119
<MyriadFile>GeneratedSwaggerGitea.fs</MyriadFile>
120120
</Compile>
121+
<!-- Regression test: this spec defines a type named "Type1", which must not
122+
collide with the names invented for anonymous inline schemas. If they
123+
collide, this project fails to compile with a duplicate-definition error. -->
124+
<None Include="swagger-anonymous-types.json" />
125+
<Compile Include="GeneratedSwaggerAnonymousTypes.fs">
126+
<MyriadFile>swagger-anonymous-types.json</MyriadFile>
127+
<MyriadParams>
128+
<ClassName>AnonymousTypes</ClassName>
129+
</MyriadParams>
130+
</Compile>
121131
</ItemGroup>
122132

123133
<ItemGroup>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//------------------------------------------------------------------------------
2+
// This code was generated by myriad.
3+
// Changes to this file will be lost when the code is regenerated.
4+
//------------------------------------------------------------------------------
5+
6+
7+
8+
9+
10+
11+
12+
13+
namespace AnonymousTypes
14+
15+
open WoofWare.Myriad.Plugins
16+
17+
[<JsonParse true ; JsonSerialize true>]
18+
type Type1 =
19+
{
20+
[<System.Text.Json.Serialization.JsonExtensionData>]
21+
AdditionalProperties : System.Collections.Generic.Dictionary<string, System.Text.Json.Nodes.JsonNode>
22+
[<System.Text.Json.Serialization.JsonPropertyName "name">]
23+
Name : string option
24+
}
25+
26+
[<JsonParse true ; JsonSerialize true>]
27+
type Type2 =
28+
{
29+
[<System.Text.Json.Serialization.JsonExtensionData>]
30+
AdditionalProperties : System.Collections.Generic.Dictionary<string, System.Text.Json.Nodes.JsonNode>
31+
[<System.Text.Json.Serialization.JsonPropertyName "data">]
32+
Data : string option
33+
}
34+
35+
/// An API whose spec explicitly defines a type called "Type1", the first name WoofWare.Myriad would otherwise invent for the anonymous inline response schema below.
36+
[<HttpClient false ; RestEase.BasePath "/v1">]
37+
type IAnonymousTypes =
38+
/// Gets a foo
39+
[<RestEase.Get "foo">]
40+
[<RestEase.Header("Content-Type", "json")>]
41+
abstract GetFoo : ?ct : System.Threading.CancellationToken -> Type2 System.Threading.Tasks.Task
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
{
2+
"swagger": "2.0",
3+
"info": {
4+
"description": "An API whose spec explicitly defines a type called \"Type1\", the first name WoofWare.Myriad would otherwise invent for the anonymous inline response schema below.",
5+
"title": "Anonymous type collision test",
6+
"license": {
7+
"name": "MIT"
8+
},
9+
"version": "1.0.0"
10+
},
11+
"consumes": [
12+
"application/json"
13+
],
14+
"produces": [
15+
"application/json"
16+
],
17+
"schemes": [
18+
"https"
19+
],
20+
"basePath": "/v1",
21+
"paths": {
22+
"/foo": {
23+
"get": {
24+
"tags": [
25+
"foo"
26+
],
27+
"summary": "Gets a foo",
28+
"operationId": "getFoo",
29+
"responses": {
30+
"200": {
31+
"description": "ok",
32+
"schema": {
33+
"type": "object",
34+
"properties": {
35+
"data": {
36+
"type": "string"
37+
}
38+
}
39+
}
40+
}
41+
}
42+
}
43+
}
44+
},
45+
"definitions": {
46+
"Type1": {
47+
"type": "object",
48+
"properties": {
49+
"name": {
50+
"type": "string"
51+
}
52+
}
53+
}
54+
},
55+
"responses": {}
56+
}

WoofWare.Myriad.Plugins/SwaggerClientGenerator.fs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,26 @@ module internal SwaggerV2Generator =
542542
let byHandle = Dictionary ()
543543
let anonymousTypeCount = ref 0
544544

545+
// A spec is free to define types whose (sanitised) names look like the
546+
// "Type{N}" names we invent for anonymous inline schemas; start counting
547+
// past any such name so we never collide with them.
548+
let namedTypes =
549+
seq {
550+
for KeyValue (k, _) in contents.Definitions do
551+
yield k
552+
553+
for KeyValue (k, _) in contents.Responses do
554+
yield k
555+
}
556+
557+
for name in namedTypes do
558+
let name = (Ident.createSanitisedTypeName name).idText
559+
560+
if name.StartsWith ("Type", System.StringComparison.Ordinal) then
561+
match System.Int32.TryParse (name.Substring "Type".Length) with
562+
| true, n -> anonymousTypeCount.Value <- max anonymousTypeCount.Value n
563+
| false, _ -> ()
564+
545565
let rec go (contents : ((string option * SwaggerV2.Definition) * string) list) =
546566
let lastRound = countAll ()
547567

0 commit comments

Comments
 (0)