Skip to content

Commit 4d75ee9

Browse files
JamesNKeerhardt
andauthored
[AOT] Enable analysis and annotate Http.Results (#46082)
Co-authored-by: Eric Erhardt <[email protected]>
1 parent 1948389 commit 4d75ee9

21 files changed

+390
-62
lines changed

eng/TrimmableProjects.props

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
<TrimmableProject Include="Microsoft.AspNetCore.Http.Abstractions" />
2626
<TrimmableProject Include="Microsoft.AspNetCore.Http.Extensions" />
2727
<TrimmableProject Include="Microsoft.AspNetCore.Http.Features" />
28+
<TrimmableProject Include="Microsoft.AspNetCore.Http.Results" />
2829
<TrimmableProject Include="Microsoft.AspNetCore.Http" />
2930
<TrimmableProject Include="Microsoft.AspNetCore.Metadata" />
3031
<TrimmableProject Include="Microsoft.AspNetCore.Routing.Abstractions" />

src/Http/Http.Abstractions/test/RouteValueDictionaryTests.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,57 @@ public void CreateFromObject_MixedCaseThrows()
358358
Assert.Equal(message, exception.Message, ignoreCase: true);
359359
}
360360

361+
[Fact]
362+
public void CreateFromObject_Struct_ReadValues()
363+
{
364+
// Arrange
365+
var obj = new StructAddress() { City = "Singapore" };
366+
367+
// Act
368+
var dict = new RouteValueDictionary(obj);
369+
370+
// Assert
371+
Assert.NotNull(dict._propertyStorage);
372+
AssertEmptyArrayStorage(dict);
373+
Assert.Collection(
374+
dict.OrderBy(kvp => kvp.Key),
375+
kvp => { Assert.Equal("City", kvp.Key); Assert.Equal("Singapore", kvp.Value); },
376+
kvp => { Assert.Equal("State", kvp.Key); Assert.Null(kvp.Value); });
377+
}
378+
379+
[Fact]
380+
public void CreateFromObject_NullableStruct_ReadValues()
381+
{
382+
// Arrange
383+
StructAddress? obj = new StructAddress() { City = "Singapore" };
384+
385+
// Act
386+
var dict = new RouteValueDictionary(obj);
387+
388+
// Assert
389+
Assert.NotNull(dict._propertyStorage);
390+
AssertEmptyArrayStorage(dict);
391+
Assert.Collection(
392+
dict.OrderBy(kvp => kvp.Key),
393+
kvp => { Assert.Equal("City", kvp.Key); Assert.Equal("Singapore", kvp.Value); },
394+
kvp => { Assert.Equal("State", kvp.Key); Assert.Null(kvp.Value); });
395+
}
396+
397+
[Fact]
398+
public void CreateFromObject_NullStruct_ReadValues()
399+
{
400+
// Arrange
401+
StructAddress? obj = null;
402+
403+
// Act
404+
var dict = new RouteValueDictionary(obj);
405+
406+
// Assert
407+
Assert.Null(dict._propertyStorage);
408+
AssertEmptyArrayStorage(dict);
409+
Assert.Empty(dict);
410+
}
411+
361412
// Our comparer is hardcoded to be OrdinalIgnoreCase no matter what.
362413
[Fact]
363414
public void Comparer_IsOrdinalIgnoreCase()
@@ -2164,4 +2215,11 @@ private class Address
21642215

21652216
public string? State { get; set; }
21662217
}
2218+
2219+
private struct StructAddress
2220+
{
2221+
public string? City { get; set; }
2222+
2223+
public string? State { get; set; }
2224+
}
21672225
}

src/Http/Http.Results/src/AcceptedAtRoute.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics.CodeAnalysis;
45
using System.Reflection;
56
using Microsoft.AspNetCore.Builder;
67
using Microsoft.AspNetCore.Http.Metadata;
8+
using Microsoft.AspNetCore.Internal;
79
using Microsoft.AspNetCore.Routing;
810
using Microsoft.Extensions.DependencyInjection;
911
using Microsoft.Extensions.Logging;
@@ -22,11 +24,24 @@ public sealed class AcceptedAtRoute : IResult, IEndpointMetadataProvider, IStatu
2224
/// provided.
2325
/// </summary>
2426
/// <param name="routeValues">The route data to use for generating the URL.</param>
27+
[RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
2528
internal AcceptedAtRoute(object? routeValues)
2629
: this(routeName: null, routeValues: routeValues)
2730
{
2831
}
2932

33+
/// <summary>
34+
/// Initializes a new instance of the <see cref="AcceptedAtRoute"/> class with the values
35+
/// provided.
36+
/// </summary>
37+
/// <param name="routeName">The name of the route to use for generating the URL.</param>
38+
/// <param name="routeValues">The route data to use for generating the URL.</param>
39+
[RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
40+
internal AcceptedAtRoute(string? routeName, object? routeValues)
41+
: this(routeName, new RouteValueDictionary(routeValues))
42+
{
43+
}
44+
3045
/// <summary>
3146
/// Initializes a new instance of the <see cref="AcceptedAtRoute"/> class with the values
3247
/// provided.
@@ -35,10 +50,10 @@ internal AcceptedAtRoute(object? routeValues)
3550
/// <param name="routeValues">The route data to use for generating the URL.</param>
3651
internal AcceptedAtRoute(
3752
string? routeName,
38-
object? routeValues)
53+
RouteValueDictionary routeValues)
3954
{
4055
RouteName = routeName;
41-
RouteValues = new RouteValueDictionary(routeValues);
56+
RouteValues = routeValues;
4257
}
4358

4459
/// <summary>

src/Http/Http.Results/src/AcceptedAtRouteOfT.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics.CodeAnalysis;
45
using System.Reflection;
56
using Microsoft.AspNetCore.Builder;
67
using Microsoft.AspNetCore.Http.Metadata;
8+
using Microsoft.AspNetCore.Internal;
79
using Microsoft.AspNetCore.Routing;
810
using Microsoft.Extensions.DependencyInjection;
911
using Microsoft.Extensions.Logging;
@@ -24,11 +26,25 @@ public sealed class AcceptedAtRoute<TValue> : IResult, IEndpointMetadataProvider
2426
/// </summary>
2527
/// <param name="routeValues">The route data to use for generating the URL.</param>
2628
/// <param name="value">The value to format in the entity body.</param>
29+
[RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
2730
internal AcceptedAtRoute(object? routeValues, TValue? value)
2831
: this(routeName: null, routeValues: routeValues, value: value)
2932
{
3033
}
3134

35+
/// <summary>
36+
/// Initializes a new instance of the <see cref="AcceptedAtRoute"/> class with the values
37+
/// provided.
38+
/// </summary>
39+
/// <param name="routeName">The name of the route to use for generating the URL.</param>
40+
/// <param name="routeValues">The route data to use for generating the URL.</param>
41+
/// <param name="value">The value to format in the entity body.</param>
42+
[RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
43+
internal AcceptedAtRoute(string? routeName, object? routeValues, TValue? value)
44+
: this(routeName, new RouteValueDictionary(routeValues), value)
45+
{
46+
}
47+
3248
/// <summary>
3349
/// Initializes a new instance of the <see cref="AcceptedAtRoute"/> class with the values
3450
/// provided.
@@ -38,12 +54,12 @@ internal AcceptedAtRoute(object? routeValues, TValue? value)
3854
/// <param name="value">The value to format in the entity body.</param>
3955
internal AcceptedAtRoute(
4056
string? routeName,
41-
object? routeValues,
57+
RouteValueDictionary routeValues,
4258
TValue? value)
4359
{
4460
Value = value;
4561
RouteName = routeName;
46-
RouteValues = new RouteValueDictionary(routeValues);
62+
RouteValues = routeValues;
4763
HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode);
4864
}
4965

src/Http/Http.Results/src/CreatedAtRoute.cs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics.CodeAnalysis;
45
using System.Reflection;
56
using Microsoft.AspNetCore.Builder;
67
using Microsoft.AspNetCore.Http.Metadata;
8+
using Microsoft.AspNetCore.Internal;
79
using Microsoft.AspNetCore.Routing;
810
using Microsoft.Extensions.DependencyInjection;
911
using Microsoft.Extensions.Logging;
@@ -22,11 +24,24 @@ public sealed class CreatedAtRoute : IResult, IEndpointMetadataProvider, IStatus
2224
/// provided.
2325
/// </summary>
2426
/// <param name="routeValues">The route data to use for generating the URL.</param>
27+
[RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
2528
internal CreatedAtRoute(object? routeValues)
2629
: this(routeName: null, routeValues: routeValues)
2730
{
2831
}
2932

33+
/// <summary>
34+
/// Initializes a new instance of the <see cref="CreatedAtRoute"/> class with the values
35+
/// provided.
36+
/// </summary>
37+
/// <param name="routeName">The name of the route to use for generating the URL.</param>
38+
/// <param name="routeValues">The route data to use for generating the URL.</param>
39+
[RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
40+
internal CreatedAtRoute(string? routeName, object? routeValues)
41+
: this(routeName, new RouteValueDictionary(routeValues))
42+
{
43+
}
44+
3045
/// <summary>
3146
/// Initializes a new instance of the <see cref="CreatedAtRoute"/> class with the values
3247
/// provided.
@@ -35,10 +50,10 @@ internal CreatedAtRoute(object? routeValues)
3550
/// <param name="routeValues">The route data to use for generating the URL.</param>
3651
internal CreatedAtRoute(
3752
string? routeName,
38-
object? routeValues)
53+
RouteValueDictionary routeValues)
3954
{
4055
RouteName = routeName;
41-
RouteValues = new RouteValueDictionary(routeValues);
56+
RouteValues = routeValues;
4257
}
4358

4459
/// <summary>

src/Http/Http.Results/src/CreatedAtRouteOfT.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics.CodeAnalysis;
45
using System.Reflection;
56
using Microsoft.AspNetCore.Builder;
67
using Microsoft.AspNetCore.Http.Metadata;
8+
using Microsoft.AspNetCore.Internal;
79
using Microsoft.AspNetCore.Routing;
810
using Microsoft.Extensions.DependencyInjection;
911
using Microsoft.Extensions.Logging;
@@ -24,11 +26,25 @@ public sealed class CreatedAtRoute<TValue> : IResult, IEndpointMetadataProvider,
2426
/// </summary>
2527
/// <param name="routeValues">The route data to use for generating the URL.</param>
2628
/// <param name="value">The value to format in the entity body.</param>
29+
[RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
2730
internal CreatedAtRoute(object? routeValues, TValue? value)
2831
: this(routeName: null, routeValues: routeValues, value: value)
2932
{
3033
}
3134

35+
/// <summary>
36+
/// Initializes a new instance of the <see cref="CreatedAtRoute"/> class with the values
37+
/// provided.
38+
/// </summary>
39+
/// <param name="routeName">The name of the route to use for generating the URL.</param>
40+
/// <param name="routeValues">The route data to use for generating the URL.</param>
41+
/// <param name="value">The value to format in the entity body.</param>
42+
[RequiresUnreferencedCode(RouteValueDictionaryTrimmerWarning.Warning)]
43+
internal CreatedAtRoute(string? routeName, object? routeValues, TValue? value)
44+
: this(routeName, new RouteValueDictionary(routeValues), value)
45+
{
46+
}
47+
3248
/// <summary>
3349
/// Initializes a new instance of the <see cref="CreatedAtRoute"/> class with the values
3450
/// provided.
@@ -38,12 +54,12 @@ internal CreatedAtRoute(object? routeValues, TValue? value)
3854
/// <param name="value">The value to format in the entity body.</param>
3955
internal CreatedAtRoute(
4056
string? routeName,
41-
object? routeValues,
57+
RouteValueDictionary routeValues,
4258
TValue? value)
4359
{
4460
Value = value;
4561
RouteName = routeName;
46-
RouteValues = new RouteValueDictionary(routeValues);
62+
RouteValues = routeValues;
4763
HttpResultsHelper.ApplyProblemDetailsDefaultsIfNeeded(Value, StatusCode);
4864
}
4965

src/Http/Http.Results/src/HttpResultsHelper.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics.CodeAnalysis;
45
using System.Text;
56
using System.Text.Json;
67
using Microsoft.AspNetCore.Internal;
@@ -15,6 +16,9 @@ internal static partial class HttpResultsHelper
1516
internal const string DefaultContentType = "text/plain; charset=utf-8";
1617
private static readonly Encoding DefaultEncoding = Encoding.UTF8;
1718

19+
// Remove once https://github.com/dotnet/aspnetcore/pull/46008 is done.
20+
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
21+
[UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "<Pending>")]
1822
public static Task WriteResultAsJsonAsync<T>(
1923
HttpContext httpContext,
2024
ILogger logger,

src/Http/Http.Results/src/Microsoft.AspNetCore.Http.Results.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<GenerateDocumentationFile>true</GenerateDocumentationFile>
88
<PackageTags>aspnetcore</PackageTags>
99
<IsPackable>false</IsPackable>
10-
<Nullable>enable</Nullable>
10+
<IsTrimmable>true</IsTrimmable>
1111
<RootNamespace>Microsoft.AspNetCore.Http.Result</RootNamespace>
1212
</PropertyGroup>
1313

@@ -19,6 +19,7 @@
1919
<Compile Include="$(SharedSourceRoot)ProblemDetails\ProblemDetailsDefaults.cs" LinkBase="Shared" />
2020
<Compile Include="$(SharedSourceRoot)ApiExplorerTypes\*.cs" LinkBase="Shared" />
2121
<Compile Include="$(SharedSourceRoot)RoutingMetadata\AcceptsMetadata.cs" LinkBase="Shared" />
22+
<Compile Include="$(SharedSourceRoot)RouteValueDictionaryTrimmerWarning.cs" LinkBase="Shared" />
2223
</ItemGroup>
2324

2425
<ItemGroup>

0 commit comments

Comments
 (0)