Skip to content

Commit c75c551

Browse files
dependabot[bot]eiriktsarpalisCopilot
authored
deps: Bump the build-tools group with 3 updates (#418)
* deps: Bump the build-tools group with 3 updates Bumps Microsoft.CodeAnalysis.Analyzers from 3.11.0 to 5.3.0 Bumps Microsoft.CodeAnalysis.CSharp from 4.8.0 to 5.3.0 Bumps Microsoft.CodeAnalysis.CSharp.CodeFix.Testing from 1.1.2 to 1.1.3 --- updated-dependencies: - dependency-name: Microsoft.CodeAnalysis.Analyzers dependency-version: 5.3.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: build-tools - dependency-name: Microsoft.CodeAnalysis.CSharp dependency-version: 5.3.0 dependency-type: direct:production update-type: version-update:semver-major dependency-group: build-tools - dependency-name: Microsoft.CodeAnalysis.CSharp.CodeFix.Testing dependency-version: 1.1.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: build-tools ... Signed-off-by: dependabot[bot] <support@github.com> * Bump transitive BCL package versions for Roslyn 5.3.0 and ignore CS1701/CS1702 binding-redirect warnings on .NET Framework Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Bump JsonSchema.Net to 9.2.0 and update for new API Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove CS1701/CS1702 suppressions The underlying cause (mismatched BCL transitive package versions pulled in by Microsoft.CodeAnalysis.CSharp 5.3.0) is fully addressed by the central package bumps; the suppressions are no longer needed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address format-validation by declaring $schema dialect Replaces the format-error filter with the proper fix: prepend a draft 2020-12 $schema declaration so JsonSchema.Net 9.x treats the ormat keyword as an annotation rather than an assertion. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Emit $schema dialect from JsonSchemaGenerator Generated root schemas now declare the JSON Schema 2020-12 dialect, ensuring spec-compliant interpretation of keywords like `format` (annotation, not assertion) by validators such as JsonSchema.Net 9.x. Tests that compare root schemas to nested sub-schemas strip `\` before structural comparison. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Inline $schema dialect emission via depth tracking Replaces the post-processing AddSchemaDialect helper (which allocated a second JsonObject solely to insert $schema as the first keyword) with depth tracking inside GenerateSchema. Root schemas are preallocated with the $schema keyword in place; recursive calls bump depth so nested subschemas do not redundantly emit it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Construct $schema dialect at the point of root JsonObject creation Replaces the post-construction EnsureSchemaDialect helper with a NewSchema(depth) factory used by every switch-case construction. When depth == 0 the factory preallocates the JsonObject with $schema as the first keyword, so the dialect is set at construction time rather than reordered after the fact. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Remove `$schema` mutations from JsonSchema test assertions Replaces the `.Remove("$schema")` calls with a non-mutating `RootEqualsSubschema` helper used where a regenerated root schema is compared against a nested sub-schema (enumerable items / dictionary additionalProperties). For IOptionalTypeShape and ISurrogateTypeShape both sides are roots with identical `$schema` entries, so `JsonNode.DeepEquals` handles them directly. The default-case assertion now verifies `$schema` is the sole key rather than stripping it first. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Simplify $schema dialect emission via JsonObject.Insert Replace the NewSchema(depth) factory and preallocation sites with a single InsertDialectIfRoot helper invoked at root-level exit points. Uses JsonObject.Insert on net9+ and a clear-and-reassign polyfill on earlier TFMs to keep $schema as the first keyword. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Reference System.Text.Json on net8.0 to enable JsonObject.Insert Extends the System.Text.Json package reference (already at v10.0.5) to net8.0 so JsonObject.Insert is available everywhere PolyType.Examples is built. This removes the need for the polyfill in JsonSchemaGenerator.InsertDialectIfRoot. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Merge ApplyNullability and InsertDialectIfRoot into CompleteDocument Consolidates the post-construction nullability and root $schema insertion logic into a single CompleteDocument helper invoked at every exit point. The IOptionalTypeShape case now sets allowNull=true so the merged helper handles its nullability (eliminating the previous double-application). Also drops the "dialect" terminology in favor of MetaSchemaUri. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Drop CompleteDocument implementation comments Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Make MetaSchemaUri private Inline the literal in tests since the constant is no longer part of the public API. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Share a single Generator instance across IMethodShape sub-schemas Move the IMethodShape orchestration into Generator.GenerateMethodSchema so a single Generator instance (and its $ref location cache) spans the parameter and return sub-schemas. Push/Pop the JSON pointer path around each sub-schema so cached references stay valid within the function document. Reduces per-method allocations from N+1 Generator instances to 1. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Revert RoslynVersion to 4.8.0 to keep VS 2022 source generator support Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Scope JsonSchema.Net and BCL 10 deps to PolyType.Tests Keep root Directory.Packages.props at conservative versions matching main so library-shipped packages do not force higher BCL minima on consumers. JsonSchema.Net 9.2 (and the BCL 10 minima it pulls in transitively) only need to be visible to PolyType.Tests, so move those overrides into a scoped tests/PolyType.Tests/Directory.Packages.props. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Move tests Directory.Packages.props up to tests/ Shield PolyType.SourceGenerator.UnitTests with a pass-through Directory.Packages.props that imports the repo root directly, so its CompilationTests do not pick up the higher BCL minima from tests/Directory.Packages.props (which would cause CS1702 assembly-version mismatches in synthesized compilations). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Move test-only package versions out of the root central props Move xunit/TUnit/Microsoft.Testing.Extensions/BenchmarkDotNet/CodeFix.Testing and the JsonSchema.Net + BCL 10 overrides into tests/Directory.Packages.props. The benchmarks and source-generator unit tests projects keep their own scoped Directory.Packages.props (now imported into the slnx and noted in the root props) for project-specific overrides. Update Benchmarks props to import the tests/ scoped file so it picks up BenchmarkDotNet. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Bump System.* packages to .NET 10 baseline Bump System.Memory/Unsafe/Tasks.Extensions to 4.6.3/6.1.2/4.6.3 and System.Collections.Immutable/System.Formats.Cbor/System.Text.Json to 10.0.5/10.0.0/10.0.5. Removes the redundant BCL Update entries (including STJ) from tests/Directory.Packages.props and the no-longer-needed pass-through props in tests/PolyType.SourceGenerator.UnitTests. Microsoft.Extensions.Configuration is intentionally kept at 9.0.8 because 10.x changes object binding behavior in a way that triggers infinite recursion in the example ConfigurationBinder. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Document and trim Benchmarks scoped Directory.Packages.props Drop the now-redundant System.Collections.Immutable override (root is at 10.0.5 which is higher than the 9.0.0 BenchmarkDotNet pulls in transitively). Keep the Microsoft.CodeAnalysis.CSharp override and add a comment explaining why the project-scoped CPM file is necessary: BenchmarkDotNet 0.15.8 transitively requires Roslyn >= 4.14.0 while the solution-wide RoslynVersion is pinned at 4.8.0 to keep the source generator usable in VS 2022. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Pin Microsoft.Extensions.Configuration to 9.0.8 In Microsoft.Extensions.Configuration.Json 10.x, a JSON `null` literal and an empty object `{}` produce identical IConfigurationSection state (Value=null, no children) and are no longer distinguishable at the IConfiguration API surface. The PolyType example ConfigurationBinder relies on this distinction to bind null vs empty objects correctly. Since this cannot be worked around without changing the binder contract, and PolyType.Examples is a packable library where conservative dependency versions are preferred, keep MEC at 9.0.8. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Simplify JsonSchema test helper by stripping $schema before DeepEquals Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Restore original Push/Pop method positions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Replace project-scoped CPM with VersionOverride in Benchmarks csproj Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Restore conservative product dependency versions in root props BCL packages bumped by JsonSchema.Net 9.x transitive requirements are scoped to tests/Directory.Packages.props via Update overrides, keeping the packable PolyType.Examples and core projects on the minimum-supported BCL versions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Soften MEC pin comment (no documented breaking change) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Cite official .NET 10 breaking change in MEC pin comment Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Adopt Microsoft.Extensions.Configuration 10.x semantics MEC 10.x preserves JSON null literals as IConfigurationSection.Value=null instead of collapsing them to empty strings (https://learn.microsoft.com/dotnet/core/compatibility/extensions/10.0/configuration-null-values-preserved). Drop the empty-string-to-null workarounds from the example ConfigurationBinder and align IsNullConfiguration with the new semantics. Empty objects '{}' remain indistinguishable from nulls at the IConfigurationSection level; the test suite accommodates this by skipping equality assertions for nested empty objects. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Move Roslyn version override into tests/Directory.Packages.props Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Bump central System.* package versions, pin core PolyType conservatively Inverts the earlier policy: the root Directory.Packages.props now declares bleeding-edge versions for System.Memory, System.Runtime.CompilerServices.Unsafe, System.Threading.Tasks.Extensions, System.Collections.Immutable, System.Formats.Cbor, and System.Text.Json. The core PolyType library opts back down to conservative minima via VersionOverride in src/PolyType/PolyType.csproj. This cleans up redundant overrides scattered across PolyType.Examples.csproj and tests/Directory.Packages.props. Suppress CS1702 in PolyType.SourceGenerator.UnitTests, which surfaced on net472 once the test compilations started referencing the bumped System.Memory while PolyType itself remained pinned to the older assembly version. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Disable global transitive pinning for package compatibility Stop centrally pinning transitive dependencies solution-wide. This keeps analyzer packages like PolyType.SourceGenerator from advertising newer System.* transitive dependencies than their direct Roslyn references require, which is important for older Visual Studio compatibility. Keep the conservative runtime BCL floor pinned only in src/PolyType/PolyType.csproj via VersionOverride. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix netfx unit test restore Add a direct reference to Microsoft.CodeAnalysis.CSharp.Workspaces in the source generator unit test project so NuGet resolves the modern Roslyn workspace stack during net472 test runs, without reintroducing global transitive pinning. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix netfx tests without warning suppression Pin the full Roslyn test stack in tests/Directory.Packages.props and remove the temporary CS1702 suppression from CompilationHelpers. The net472 source-generator unit test leg now passes cleanly by resolving the intended package versions rather than hiding the warning. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix up * Remove unnecesary dependency --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Eirik Tsarpalis <eirik.tsarpalis@gmail.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 613cc04 commit c75c551

9 files changed

Lines changed: 150 additions & 110 deletions

File tree

Directory.Packages.props

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,52 @@
11
<Project>
22
<PropertyGroup>
33
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
4-
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
5-
<RoslynVersion>4.8.0</RoslynVersion>
4+
<RoslynVersion>5.3.0</RoslynVersion>
65
<RoslynVersionForAnalyzers>4.3.0</RoslynVersionForAnalyzers>
76
</PropertyGroup>
87
<ItemGroup>
98
<!-- Product dependencies -->
10-
<PackageVersion Include="System.Memory" Version="4.5.5" />
9+
<PackageVersion Include="System.Memory" Version="4.5.4" />
1110
<PackageVersion Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
1211
<PackageVersion Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
1312
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
1413
<!-- Source Generator dependencies -->
15-
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
14+
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="$(RoslynVersion)" />
1615
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="$(RoslynVersion)" />
17-
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing" Version="1.1.2" />
1816
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(RoslynVersion)" />
1917
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="$(RoslynVersion)" />
2018
<!-- Build Infra & Packaging -->
2119
<PackageVersion Include="PolySharp" Version="1.15.0" />
2220
<PackageVersion Include="Nerdbank.GitVersioning" Version="3.10.8-alpha" />
2321
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
24-
<!-- Testing dependencies -->
25-
<PackageVersion Include="xunit.v3.mtp-v2" Version="3.2.2" />
26-
<PackageVersion Include="TUnit" Version="1.12.65" />
27-
<PackageVersion Include="TUnit.Assertions" Version="1.12.65" />
28-
<PackageVersion Include="TUnit.Engine" Version="1.12.65" />
29-
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.8" />
30-
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="9.0.8" />
31-
<PackageVersion Include="Microsoft.Testing.Extensions.TrxReport" Version="2.0.2" />
32-
<PackageVersion Include="Microsoft.Testing.Extensions.CodeCoverage" Version="18.3.2" />
33-
<PackageVersion Include="Microsoft.Testing.Extensions.CrashDump" Version="2.0.2" />
34-
<PackageVersion Include="Microsoft.Testing.Extensions.HangDump" Version="2.0.2" />
35-
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
36-
<!-- Earliest version of the library supporting literals and frozen collections -->
37-
<PackageVersion Include="System.Collections.Immutable" Version="8.0.0" />
38-
<PackageVersion Include="System.Formats.Cbor" Version="9.0.8" />
39-
<PackageVersion Include="System.Text.Json" Version="9.0.8" />
22+
<!-- Used by PolyType.Examples and PolyType.TestCases -->
23+
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="10.0.5" />
24+
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="10.0.5" />
25+
<PackageVersion Include="System.Collections.Immutable" Version="10.0.5" />
26+
<PackageVersion Include="System.Formats.Cbor" Version="10.0.5" />
27+
<PackageVersion Include="System.Text.Json" Version="10.0.5" />
4028
<PackageVersion Include="YamlDotNet" Version="17.0.1" />
4129
<PackageVersion Include="Microsoft.Bcl.HashCode" Version="6.0.0" />
42-
<PackageVersion Include="JsonSchema.Net" Version="7.4.0" />
4330
<PackageVersion Include="FSharp.Core" Version="10.1.202" />
31+
<!-- Test infrastructure -->
32+
<PackageVersion Include="xunit.v3.mtp-v2" Version="3.2.2" />
33+
<PackageVersion Include="TUnit" Version="1.37.10" />
34+
<PackageVersion Include="TUnit.Assertions" Version="1.37.10" />
35+
<PackageVersion Include="TUnit.Engine" Version="1.37.10" />
36+
<PackageVersion Include="Microsoft.Testing.Extensions.TrxReport" Version="2.2.1" />
37+
<PackageVersion Include="Microsoft.Testing.Extensions.CodeCoverage" Version="18.6.2" />
38+
<PackageVersion Include="Microsoft.Testing.Extensions.CrashDump" Version="2.2.1" />
39+
<PackageVersion Include="Microsoft.Testing.Extensions.HangDump" Version="2.2.1" />
40+
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing" Version="1.1.2" />
41+
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8" />
42+
<PackageVersion Include="JsonSchema.Net" Version="9.2.0" />
4443
</ItemGroup>
4544
<ItemGroup Condition="'$(IsAnalyzerProject)'=='true'">
4645
<!-- Keep these versions in sync with what Unity documents as supported at
4746
https://docs.unity3d.com/6000.1/Documentation/Manual/create-source-generator.html (or a newer version of that doc). -->
47+
<PackageVersion Update="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
4848
<PackageVersion Update="Microsoft.CodeAnalysis.CSharp" Version="$(RoslynVersionForAnalyzers)" />
4949
<PackageVersion Update="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(RoslynVersionForAnalyzers)" />
5050
<PackageVersion Update="Microsoft.CodeAnalysis.Workspaces.Common" Version="$(RoslynVersionForAnalyzers)" />
51-
<PackageVersion Update="System.Collections.Immutable" Version="6.0.0" />
52-
<PackageVersion Update="System.Memory" Version="4.5.4" />
5351
</ItemGroup>
54-
</Project>
52+
</Project>

src/PolyType.Examples/ConfigurationBinder/ConfigurationBinder.Builder.cs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -313,15 +313,15 @@ private static IEnumerable<KeyValuePair<Type, object>> GetBuiltInParsers()
313313
yield return Create(text => float.Parse(text, NumberStyles.Float, CultureInfo.InvariantCulture));
314314
yield return Create(text => double.Parse(text, NumberStyles.Float, CultureInfo.InvariantCulture));
315315
yield return Create(text => decimal.Parse(text, NumberStyles.Float, CultureInfo.InvariantCulture));
316-
yield return Create(text => string.IsNullOrEmpty(text) ? null : text);
316+
yield return Create<string?>(text => text);
317317
yield return Create(char.Parse);
318318
yield return Create(Guid.Parse);
319319
yield return Create(text => TimeSpan.Parse(text, CultureInfo.InvariantCulture));
320320
yield return Create(text => DateTime.Parse(text, CultureInfo.InvariantCulture));
321321
yield return Create(text => DateTimeOffset.Parse(text, CultureInfo.InvariantCulture));
322-
yield return Create(text => text is "" ? null : new Uri(text, UriKind.RelativeOrAbsolute));
323-
yield return Create(text => text is "" ? null : Version.Parse(text));
324-
yield return Create(text => text is "" ? null : Convert.FromBase64String(text));
322+
yield return Create(text => new Uri(text, UriKind.RelativeOrAbsolute));
323+
yield return Create(Version.Parse);
324+
yield return Create(Convert.FromBase64String);
325325
#if NET
326326
yield return Create(text => UInt128.Parse(text, NumberStyles.Integer, CultureInfo.InvariantCulture));
327327
yield return Create(text => Int128.Parse(text, NumberStyles.Integer, CultureInfo.InvariantCulture));
@@ -332,8 +332,6 @@ private static IEnumerable<KeyValuePair<Type, object>> GetBuiltInParsers()
332332
#endif
333333

334334
yield return Create<object?>(text =>
335-
text is null ? new object() :
336-
text is "" ? null :
337335
bool.TryParse(text, out bool boolResult) ? boolResult :
338336
int.TryParse(text, out int intResult) ? intResult :
339337
double.TryParse(text, out double doubleResult) ? doubleResult :
@@ -367,6 +365,11 @@ private static Func<IConfiguration, T> CreateValueBinder<T>(Func<string, T> pars
367365
throw new InvalidOperationException();
368366
}
369367

368+
if (section.Value is null && default(T) is null)
369+
{
370+
return default!;
371+
}
372+
370373
try
371374
{
372375
return parser(section.Value!);
@@ -379,11 +382,10 @@ private static Func<IConfiguration, T> CreateValueBinder<T>(Func<string, T> pars
379382
}
380383

381384
private static Func<IConfiguration, T> CreateNotSupportedBinder<T>() =>
382-
config => default(T) is null && IsNullConfiguration(config) ? default! : throw new NotSupportedException($"Type '{typeof(T)}' is not supported.");
385+
config => IsNullConfiguration(config) ? default! : throw new NotSupportedException($"Type '{typeof(T)}' is not supported.");
383386

384387
private static bool IsNullConfiguration(IConfiguration configuration) =>
385-
// https://github.com/dotnet/runtime/issues/36510
386-
configuration is IConfigurationSection { Value: "" } &&
388+
configuration is IConfigurationSection { Value: null } &&
387389
!configuration.GetChildren().Any();
388390
}
389391

src/PolyType.Examples/JsonSchema/JsonSchemaGenerator.cs

Lines changed: 65 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public static class JsonSchemaGenerator
1818
public static JsonObject Generate<T>(ITypeShapeProvider typeShapeProvider)
1919
=> Generate(typeShapeProvider.GetTypeShapeOrThrow<T>());
2020

21+
private const string MetaSchemaUri = "https://json-schema.org/draft/2020-12/schema";
22+
2123
/// <summary>
2224
/// Generates a JSON schema using the specified shape.
2325
/// </summary>
@@ -28,42 +30,7 @@ public static JsonObject Generate(ITypeShape typeShape)
2830
/// Generates a JSON schema using the specified method shape.
2931
/// </summary>
3032
public static JsonObject Generate(IMethodShape methodShape)
31-
{
32-
JsonObject? parameterSchemas = null;
33-
JsonArray? requiredParams = null;
34-
foreach (var parameter in methodShape.Parameters)
35-
{
36-
if (parameter.ParameterType.Type == typeof(CancellationToken))
37-
{
38-
continue;
39-
}
40-
41-
(parameterSchemas ??= []).Add(parameter.Name, Generate(parameter.ParameterType));
42-
if (parameter.IsRequired)
43-
{
44-
(requiredParams ??= []).Add((JsonNode)parameter.Name);
45-
}
46-
}
47-
48-
JsonObject functionSchema = new JsonObject
49-
{
50-
["name"] = methodShape.Name,
51-
["type"] = "object",
52-
};
53-
54-
if (parameterSchemas is not null)
55-
{
56-
functionSchema["properties"] = parameterSchemas;
57-
}
58-
59-
if (requiredParams is not null)
60-
{
61-
functionSchema["required"] = requiredParams;
62-
}
63-
64-
functionSchema["output"] = Generate(methodShape.ReturnType);
65-
return functionSchema;
66-
}
33+
=> new Generator().GenerateMethodSchema(methodShape);
6734

6835
#if NET
6936
/// <summary>
@@ -87,13 +54,57 @@ private sealed class Generator
8754
private readonly Dictionary<(Type, bool AllowNull), string> _locations = new();
8855
private readonly List<string> _path = new();
8956

90-
public JsonObject GenerateSchema(ITypeShape typeShape, bool allowNull = true, bool cacheLocation = true)
57+
public JsonObject GenerateMethodSchema(IMethodShape methodShape)
58+
{
59+
JsonObject? parameterSchemas = null;
60+
JsonArray? requiredParams = null;
61+
foreach (var parameter in methodShape.Parameters)
62+
{
63+
if (parameter.ParameterType.Type == typeof(CancellationToken))
64+
{
65+
continue;
66+
}
67+
68+
Push("properties");
69+
Push(parameter.Name);
70+
(parameterSchemas ??= []).Add(parameter.Name, GenerateSchema(parameter.ParameterType, depth: 1));
71+
Pop();
72+
Pop();
73+
if (parameter.IsRequired)
74+
{
75+
(requiredParams ??= []).Add((JsonNode)parameter.Name);
76+
}
77+
}
78+
79+
JsonObject functionSchema = new JsonObject
80+
{
81+
["name"] = methodShape.Name,
82+
["type"] = "object",
83+
};
84+
85+
if (parameterSchemas is not null)
86+
{
87+
functionSchema["properties"] = parameterSchemas;
88+
}
89+
90+
if (requiredParams is not null)
91+
{
92+
functionSchema["required"] = requiredParams;
93+
}
94+
95+
Push("output");
96+
functionSchema["output"] = GenerateSchema(methodShape.ReturnType, depth: 1);
97+
Pop();
98+
return CompleteDocument(functionSchema, allowNull: false, depth: 0);
99+
}
100+
101+
public JsonObject GenerateSchema(ITypeShape typeShape, bool allowNull = true, bool cacheLocation = true, int depth = 0)
91102
{
92103
allowNull = allowNull && IsNullableType(typeShape.Type);
93104

94105
if (s_simpleTypeInfo.TryGetValue(typeShape.Type, out SimpleTypeJsonSchema simpleType))
95106
{
96-
return ApplyNullability(simpleType.ToSchemaDocument(), allowNull);
107+
return CompleteDocument(simpleType.ToSchemaDocument(), allowNull, depth);
97108
}
98109

99110
if (cacheLocation)
@@ -125,20 +136,20 @@ public JsonObject GenerateSchema(ITypeShape typeShape, bool allowNull = true, bo
125136
break;
126137

127138
case IOptionalTypeShape optionalShape:
128-
schema = GenerateSchema(optionalShape.ElementType, cacheLocation: false);
129-
ApplyNullability(schema, allowNull: true);
139+
schema = GenerateSchema(optionalShape.ElementType, cacheLocation: false, depth: depth + 1);
140+
allowNull = true;
130141
break;
131142

132143
case ISurrogateTypeShape surrogateShape:
133-
return GenerateSchema(surrogateShape.SurrogateType, cacheLocation: false);
144+
return CompleteDocument(GenerateSchema(surrogateShape.SurrogateType, cacheLocation: false, depth: depth + 1), allowNull: false, depth);
134145

135146
case IEnumerableTypeShape enumerableShape:
136147
for (int i = 0; i < enumerableShape.Rank; i++)
137148
{
138149
Push("items");
139150
}
140151

141-
schema = GenerateSchema(enumerableShape.ElementType);
152+
schema = GenerateSchema(enumerableShape.ElementType, depth: depth + 1);
142153

143154
for (int i = 0; i < enumerableShape.Rank; i++)
144155
{
@@ -155,7 +166,7 @@ public JsonObject GenerateSchema(ITypeShape typeShape, bool allowNull = true, bo
155166

156167
case IDictionaryTypeShape dictionaryShape:
157168
Push("additionalProperties");
158-
JsonObject additionalPropertiesSchema = GenerateSchema(dictionaryShape.ValueType);
169+
JsonObject additionalPropertiesSchema = GenerateSchema(dictionaryShape.ValueType, depth: depth + 1);
159170
Pop();
160171

161172
schema = new JsonObject
@@ -191,7 +202,7 @@ public JsonObject GenerateSchema(ITypeShape typeShape, bool allowNull = true, bo
191202
(associatedParameter is null || associatedParameter.IsNonNullable);
192203

193204
Push(prop.Name);
194-
JsonObject propSchema = GenerateSchema(prop.PropertyType, allowNull: !isNonNullable);
205+
JsonObject propSchema = GenerateSchema(prop.PropertyType, allowNull: !isNonNullable, depth: depth + 1);
195206
Pop();
196207

197208
properties.Add(prop.Name, propSchema);
@@ -220,7 +231,7 @@ public JsonObject GenerateSchema(ITypeShape typeShape, bool allowNull = true, bo
220231
foreach (IUnionCaseShape caseShape in unionShape.UnionCases)
221232
{
222233
Push($"{anyOf.Count}");
223-
JsonObject caseSchema = GenerateSchema(caseShape.UnionCaseType, cacheLocation: false);
234+
JsonObject caseSchema = GenerateSchema(caseShape.UnionCaseType, cacheLocation: false, depth: depth + 1);
224235
Pop();
225236

226237
if (caseShape.UnionCaseType is IObjectTypeShape or IDictionaryTypeShape)
@@ -274,7 +285,7 @@ public JsonObject GenerateSchema(ITypeShape typeShape, bool allowNull = true, bo
274285
if (!unionCasesContainBaseType)
275286
{
276287
Push($"{anyOf.Count}");
277-
JsonNode caseSchema = GenerateSchema(unionShape.BaseType, cacheLocation: false);
288+
JsonNode caseSchema = GenerateSchema(unionShape.BaseType, cacheLocation: false, depth: depth + 1);
278289
Pop();
279290

280291
anyOf.Add(caseSchema);
@@ -293,7 +304,7 @@ public JsonObject GenerateSchema(ITypeShape typeShape, bool allowNull = true, bo
293304
break;
294305
}
295306

296-
return ApplyNullability(schema, allowNull);
307+
return CompleteDocument(schema, allowNull, depth);
297308
}
298309

299310
private void Push(string name)
@@ -306,11 +317,11 @@ private void Pop()
306317
_path.RemoveAt(_path.Count - 1);
307318
}
308319

309-
private static JsonObject ApplyNullability(JsonObject schema, bool allowNull)
320+
private static JsonObject CompleteDocument(JsonObject schema, bool allowNull, int depth)
310321
{
311322
if (allowNull && schema.TryGetPropertyValue("type", out JsonNode? typeValue))
312323
{
313-
if (schema["type"] is JsonArray types)
324+
if (typeValue is JsonArray types)
314325
{
315326
types.Add((JsonNode)"null");
316327
}
@@ -320,6 +331,11 @@ private static JsonObject ApplyNullability(JsonObject schema, bool allowNull)
320331
}
321332
}
322333

334+
if (depth == 0)
335+
{
336+
schema.Insert(0, "$schema", MetaSchemaUri);
337+
}
338+
323339
return schema;
324340
}
325341

src/PolyType.Examples/PolyType.Examples.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,11 @@
2929
<PackageReference Include="YamlDotNet" />
3030
</ItemGroup>
3131

32-
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
32+
<ItemGroup Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)','net10.0'))">
3333
<PackageReference Include="System.Text.Json" />
34+
</ItemGroup>
35+
36+
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
3437
<PackageReference Include="Microsoft.Bcl.HashCode" />
3538
</ItemGroup>
3639

tests/PolyType.Benchmarks/Directory.Packages.props

Lines changed: 0 additions & 8 deletions
This file was deleted.

tests/PolyType.SourceGenerator.UnitTests/CompilationHelpers.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,18 @@ public static Compilation CreateCompilation(
104104
.. additionalReferences,
105105
];
106106

107+
var options = new CSharpCompilationOptions(outputKind, nullableContextOptions: nullableContextOptions, allowUnsafe: true);
108+
#if !NET
109+
// On .NET Framework the test process may load newer BCL assemblies (e.g. System.Memory)
110+
// than those PolyType was compiled against. CS1702 warns about the version mismatch,
111+
// which is benign in an in-memory compilation used for source-generator testing.
112+
options = options.WithSpecificDiagnosticOptions([new("CS1702", ReportDiagnostic.Suppress)]);
113+
#endif
107114
return CSharpCompilation.Create(
108115
assemblyName,
109116
syntaxTrees: syntaxTrees,
110117
references: references,
111-
options: new CSharpCompilationOptions(outputKind, nullableContextOptions: nullableContextOptions, allowUnsafe: true)
118+
options: options
112119
);
113120
}
114121

tests/PolyType.SourceGenerator.UnitTests/PolyType.SourceGenerator.UnitTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<ItemGroup>
2727
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing" />
2828
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
29+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" />
2930
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers">
3031
<PrivateAssets>all</PrivateAssets>
3132
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

0 commit comments

Comments
 (0)