Skip to content

Commit b12b64e

Browse files
authored
Add nullability to more MVC assemblies (#31338)
* Add nullability to more MVC assemblies * [x] MVC.Cors * [x] Mvc.DataAnnotations * [x] Mvc.Localization * [x] Mvc.Newtonsoft.Json * [ ] Mvc.ViewFeatures (partial) * Fixup * Update src/Mvc/Mvc.ViewFeatures/src/Rendering/ViewContext.cs * Apply suggestions from code review * Update RazorPagePropertyActivator.cs
1 parent 2afcac2 commit b12b64e

File tree

116 files changed

+1912
-615
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+1912
-615
lines changed

src/Mvc/Mvc.Abstractions/src/ModelBinding/Validation/ModelValidationResult.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class ModelValidationResult
1313
/// </summary>
1414
/// <param name="memberName">The name of the entry on which validation was performed.</param>
1515
/// <param name="message">The validation message.</param>
16-
public ModelValidationResult(string memberName, string message)
16+
public ModelValidationResult(string? memberName, string? message)
1717
{
1818
MemberName = memberName ?? string.Empty;
1919
Message = message ?? string.Empty;

src/Mvc/Mvc.Abstractions/src/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
*REMOVED*Microsoft.AspNetCore.Mvc.Routing.AttributeRouteInfo.Name.get -> string!
5151
*REMOVED*static Microsoft.AspNetCore.Mvc.Formatters.InputFormatterResult.Success(object! model) -> Microsoft.AspNetCore.Mvc.Formatters.InputFormatterResult!
5252
*REMOVED*static Microsoft.AspNetCore.Mvc.Formatters.InputFormatterResult.SuccessAsync(object! model) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Mvc.Formatters.InputFormatterResult!>!
53+
*REMOVED*Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ModelValidationResult.ModelValidationResult(string! memberName, string! message) -> void
5354
Microsoft.AspNetCore.Mvc.Abstractions.ActionDescriptor.RouteValues.get -> System.Collections.Generic.IDictionary<string!, string?>!
5455
Microsoft.AspNetCore.Mvc.Abstractions.ParameterDescriptor.BindingInfo.get -> Microsoft.AspNetCore.Mvc.ModelBinding.BindingInfo?
5556
Microsoft.AspNetCore.Mvc.ActionConstraints.ActionConstraintItem.Constraint.get -> Microsoft.AspNetCore.Mvc.ActionConstraints.IActionConstraint?
@@ -101,3 +102,4 @@ virtual Microsoft.AspNetCore.Mvc.Filters.ActionExecutedContext.Result.get -> Mic
101102
virtual Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.ActionArguments.get -> System.Collections.Generic.IDictionary<string!, object?>!
102103
virtual Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata.BoundConstructorInvoker.get -> System.Func<object?[]!, object!>?
103104
virtual Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata.ContainerMetadata.get -> Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata?
105+
Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ModelValidationResult.ModelValidationResult(string? memberName, string? message) -> void

src/Mvc/Mvc.Core/src/Formatters/ResponseContentTypeHelper.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
#nullable enable
5-
64
using System.Diagnostics;
75
using System.Text;
86
using Microsoft.AspNetCore.Http;

src/Mvc/Mvc.Cors/src/CorsAuthorizationFilter.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Threading.Tasks;
66
using Microsoft.AspNetCore.Cors.Infrastructure;
77
using Microsoft.AspNetCore.Http;
8+
using Microsoft.AspNetCore.Mvc.Filters;
89
using Microsoft.Extensions.Logging;
910
using Microsoft.Extensions.Logging.Abstractions;
1011
using Microsoft.Extensions.Primitives;
@@ -64,15 +65,15 @@ public CorsAuthorizationFilter(
6465
/// <summary>
6566
/// The policy name used to fetch a <see cref="CorsPolicy"/>.
6667
/// </summary>
67-
public string PolicyName { get; set; }
68+
public string? PolicyName { get; set; }
6869

6970
/// <inheritdoc />
7071
// Since clients' preflight requests would not have data to authenticate requests, this
7172
// filter must run before any other authorization filters.
7273
public int Order => int.MinValue + 100;
7374

7475
/// <inheritdoc />
75-
public async Task OnAuthorizationAsync(Filters.AuthorizationFilterContext context)
76+
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
7677
{
7778
if (context == null)
7879
{

src/Mvc/Mvc.Cors/src/CorsAuthorizationFilterFactory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ namespace Microsoft.AspNetCore.Mvc.Cors
1212
/// </summary>
1313
internal class CorsAuthorizationFilterFactory : IFilterFactory, IOrderedFilter
1414
{
15-
private readonly string _policyName;
15+
private readonly string? _policyName;
1616

1717
/// <summary>
1818
/// Creates a new instance of <see cref="CorsAuthorizationFilterFactory"/>.
1919
/// </summary>
2020
/// <param name="policyName">Name used to fetch a CORS policy.</param>
21-
public CorsAuthorizationFilterFactory(string policyName)
21+
public CorsAuthorizationFilterFactory(string? policyName)
2222
{
2323
_policyName = policyName;
2424
}

src/Mvc/Mvc.Cors/src/CorsLoggerExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) .NET Foundation. All rights reserved.
1+
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
@@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Mvc.Cors
88
{
99
internal static class CorsLoggerExtensions
1010
{
11-
private static readonly Action<ILogger, Type, Exception> _notMostEffectiveFilter;
11+
private static readonly Action<ILogger, Type, Exception?> _notMostEffectiveFilter;
1212

1313
static CorsLoggerExtensions()
1414
{

src/Mvc/Mvc.Cors/src/Microsoft.AspNetCore.Mvc.Cors.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<GenerateDocumentationFile>true</GenerateDocumentationFile>
88
<PackageTags>aspnetcore;aspnetcoremvc;cors</PackageTags>
99
<IsPackable>false</IsPackable>
10+
<Nullable>enable</Nullable>
1011
</PropertyGroup>
1112

1213
<ItemGroup>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,18 @@
11
#nullable enable
2+
*REMOVED*~Microsoft.AspNetCore.Mvc.Cors.CorsAuthorizationFilter.CorsAuthorizationFilter(Microsoft.AspNetCore.Cors.Infrastructure.ICorsService corsService, Microsoft.AspNetCore.Cors.Infrastructure.ICorsPolicyProvider policyProvider) -> void
3+
*REMOVED*~Microsoft.AspNetCore.Mvc.Cors.CorsAuthorizationFilter.CorsAuthorizationFilter(Microsoft.AspNetCore.Cors.Infrastructure.ICorsService corsService, Microsoft.AspNetCore.Cors.Infrastructure.ICorsPolicyProvider policyProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) -> void
4+
*REMOVED*~Microsoft.AspNetCore.Mvc.Cors.CorsAuthorizationFilter.OnAuthorizationAsync(Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext context) -> System.Threading.Tasks.Task
5+
*REMOVED*~Microsoft.AspNetCore.Mvc.Cors.CorsAuthorizationFilter.PolicyName.get -> string
6+
*REMOVED*~Microsoft.AspNetCore.Mvc.Cors.CorsAuthorizationFilter.PolicyName.set -> void
7+
*REMOVED*~static Microsoft.Extensions.DependencyInjection.MvcCorsMvcCoreBuilderExtensions.AddCors(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder builder) -> Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder
8+
*REMOVED*~static Microsoft.Extensions.DependencyInjection.MvcCorsMvcCoreBuilderExtensions.AddCors(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder builder, System.Action<Microsoft.AspNetCore.Cors.Infrastructure.CorsOptions> setupAction) -> Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder
9+
*REMOVED*~static Microsoft.Extensions.DependencyInjection.MvcCorsMvcCoreBuilderExtensions.ConfigureCors(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder builder, System.Action<Microsoft.AspNetCore.Cors.Infrastructure.CorsOptions> setupAction) -> Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder
10+
Microsoft.AspNetCore.Mvc.Cors.CorsAuthorizationFilter.CorsAuthorizationFilter(Microsoft.AspNetCore.Cors.Infrastructure.ICorsService! corsService, Microsoft.AspNetCore.Cors.Infrastructure.ICorsPolicyProvider! policyProvider) -> void
11+
Microsoft.AspNetCore.Mvc.Cors.CorsAuthorizationFilter.CorsAuthorizationFilter(Microsoft.AspNetCore.Cors.Infrastructure.ICorsService! corsService, Microsoft.AspNetCore.Cors.Infrastructure.ICorsPolicyProvider! policyProvider, Microsoft.Extensions.Logging.ILoggerFactory! loggerFactory) -> void
12+
Microsoft.AspNetCore.Mvc.Cors.CorsAuthorizationFilter.OnAuthorizationAsync(Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext! context) -> System.Threading.Tasks.Task!
13+
Microsoft.AspNetCore.Mvc.Cors.CorsAuthorizationFilter.PolicyName.get -> string?
14+
Microsoft.AspNetCore.Mvc.Cors.CorsAuthorizationFilter.PolicyName.set -> void
15+
static Microsoft.Extensions.DependencyInjection.MvcCorsMvcCoreBuilderExtensions.AddCors(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder! builder) -> Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder!
16+
static Microsoft.Extensions.DependencyInjection.MvcCorsMvcCoreBuilderExtensions.AddCors(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder! builder, System.Action<Microsoft.AspNetCore.Cors.Infrastructure.CorsOptions!>! setupAction) -> Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder!
17+
static Microsoft.Extensions.DependencyInjection.MvcCorsMvcCoreBuilderExtensions.ConfigureCors(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder! builder, System.Action<Microsoft.AspNetCore.Cors.Infrastructure.CorsOptions!>! setupAction) -> Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder!
18+

src/Mvc/Mvc.DataAnnotations/src/AttributeAdapterBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public abstract class AttributeAdapterBase<TAttribute> :
2222
/// </summary>
2323
/// <param name="attribute">The <see cref="ValidationAttribute"/> being wrapped.</param>
2424
/// <param name="stringLocalizer">The <see cref="IStringLocalizer"/> to be used in error generation.</param>
25-
public AttributeAdapterBase(TAttribute attribute, IStringLocalizer stringLocalizer)
25+
public AttributeAdapterBase(TAttribute attribute, IStringLocalizer? stringLocalizer)
2626
: base(attribute, stringLocalizer)
2727
{
2828
}

src/Mvc/Mvc.DataAnnotations/src/CompareAttributeAdapter.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ internal class CompareAttributeAdapter : AttributeAdapterBase<CompareAttribute>
1414
{
1515
private readonly string _otherProperty;
1616

17-
public CompareAttributeAdapter(CompareAttribute attribute, IStringLocalizer stringLocalizer)
17+
public CompareAttributeAdapter(CompareAttribute attribute, IStringLocalizer? stringLocalizer)
1818
: base(new CompareAttributeWrapper(attribute), stringLocalizer)
1919
{
2020
_otherProperty = "*." + attribute.OtherProperty;
@@ -54,7 +54,7 @@ public override string GetErrorMessage(ModelValidationContextBase validationCont
5454
// populate OtherPropertyDisplayName until you call FormatErrorMessage.
5555
private sealed class CompareAttributeWrapper : CompareAttribute
5656
{
57-
public ModelValidationContextBase ValidationContext { get; set; }
57+
public ModelValidationContextBase ValidationContext { get; set; } = default!;
5858

5959
public CompareAttributeWrapper(CompareAttribute attribute)
6060
: base(attribute.OtherProperty)

src/Mvc/Mvc.DataAnnotations/src/DataAnnotationsClientModelValidatorProvider.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
1919
internal class DataAnnotationsClientModelValidatorProvider : IClientModelValidatorProvider
2020
{
2121
private readonly IOptions<MvcDataAnnotationsLocalizationOptions> _options;
22-
private readonly IStringLocalizerFactory _stringLocalizerFactory;
22+
private readonly IStringLocalizerFactory? _stringLocalizerFactory;
2323
private readonly IValidationAttributeAdapterProvider _validationAttributeAdapterProvider;
2424

2525
/// <summary>
@@ -32,7 +32,7 @@ internal class DataAnnotationsClientModelValidatorProvider : IClientModelValidat
3232
public DataAnnotationsClientModelValidatorProvider(
3333
IValidationAttributeAdapterProvider validationAttributeAdapterProvider,
3434
IOptions<MvcDataAnnotationsLocalizationOptions> options,
35-
IStringLocalizerFactory stringLocalizerFactory)
35+
IStringLocalizerFactory? stringLocalizerFactory)
3636
{
3737
if (validationAttributeAdapterProvider == null)
3838
{
@@ -55,7 +55,7 @@ public void CreateValidators(ClientValidatorProviderContext context)
5555
{
5656
throw new ArgumentNullException(nameof(context));
5757
}
58-
IStringLocalizer stringLocalizer = null;
58+
IStringLocalizer? stringLocalizer = null;
5959
if (_options.Value.DataAnnotationLocalizerProvider != null && _stringLocalizerFactory != null)
6060
{
6161
// This will pass first non-null type (either containerType or modelType) to delegate.

src/Mvc/Mvc.DataAnnotations/src/DataAnnotationsLocalizationServices.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ internal static class DataAnnotationsLocalizationServices
1212
{
1313
public static void AddDataAnnotationsLocalizationServices(
1414
IServiceCollection services,
15-
Action<MvcDataAnnotationsLocalizationOptions> setupAction)
15+
Action<MvcDataAnnotationsLocalizationOptions>? setupAction)
1616
{
1717
services.AddLocalization();
1818

src/Mvc/Mvc.DataAnnotations/src/DataAnnotationsMetadataProvider.cs

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,11 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5-
using System.Collections;
65
using System.Collections.Generic;
76
using System.ComponentModel;
87
using System.ComponentModel.DataAnnotations;
9-
using System.Diagnostics.Contracts;
108
using System.Linq;
119
using System.Reflection;
12-
using System.Runtime.InteropServices;
13-
using System.Runtime.InteropServices.ComTypes;
1410
using Microsoft.AspNetCore.Mvc.ModelBinding;
1511
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
1612
using Microsoft.Extensions.Localization;
@@ -34,14 +30,14 @@ internal class DataAnnotationsMetadataProvider :
3430
private const string NullableContextAttributeFullName = "System.Runtime.CompilerServices.NullableContextAttribute";
3531
private const string NullableContextFlagsFieldName = "Flag";
3632

37-
private readonly IStringLocalizerFactory _stringLocalizerFactory;
33+
private readonly IStringLocalizerFactory? _stringLocalizerFactory;
3834
private readonly MvcOptions _options;
3935
private readonly MvcDataAnnotationsLocalizationOptions _localizationOptions;
4036

4137
public DataAnnotationsMetadataProvider(
4238
MvcOptions options,
4339
IOptions<MvcDataAnnotationsLocalizationOptions> localizationOptions,
44-
IStringLocalizerFactory stringLocalizerFactory)
40+
IStringLocalizerFactory? stringLocalizerFactory)
4541
{
4642
if (options == null)
4743
{
@@ -120,7 +116,7 @@ public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
120116
}
121117

122118
var containerType = context.Key.ContainerType ?? context.Key.ModelType;
123-
IStringLocalizer localizer = null;
119+
IStringLocalizer? localizer = null;
124120
if (_stringLocalizerFactory != null && _localizationOptions.DataAnnotationLocalizerProvider != null)
125121
{
126122
localizer = _localizationOptions.DataAnnotationLocalizerProvider(containerType, _stringLocalizerFactory);
@@ -201,20 +197,20 @@ public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
201197
var groupedDisplayNamesAndValues = new List<KeyValuePair<EnumGroupAndName, string>>();
202198
var namesAndValues = new Dictionary<string, string>();
203199

204-
IStringLocalizer enumLocalizer = null;
200+
IStringLocalizer? enumLocalizer = null;
205201
if (_stringLocalizerFactory != null && _localizationOptions.DataAnnotationLocalizerProvider != null)
206202
{
207203
enumLocalizer = _localizationOptions.DataAnnotationLocalizerProvider(underlyingType, _stringLocalizerFactory);
208204
}
209205

210206
var enumFields = Enum.GetNames(underlyingType)
211-
.Select(name => underlyingType.GetField(name))
207+
.Select(name => underlyingType.GetField(name)!)
212208
.OrderBy(field => field.GetCustomAttribute<DisplayAttribute>(inherit: false)?.GetOrder() ?? 1000);
213209

214210
foreach (var field in enumFields)
215211
{
216212
var groupName = GetDisplayGroup(field);
217-
var value = ((Enum)field.GetValue(obj: null)).ToString("d");
213+
var value = ((Enum)field.GetValue(obj: null)!).ToString("d");
218214

219215
groupedDisplayNamesAndValues.Add(new KeyValuePair<EnumGroupAndName, string>(
220216
new EnumGroupAndName(
@@ -271,9 +267,9 @@ public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
271267
}
272268

273269
// Order
274-
if (displayAttribute?.GetOrder() != null)
270+
if (displayAttribute?.GetOrder() is int order)
275271
{
276-
displayMetadata.Order = displayAttribute.GetOrder().Value;
272+
displayMetadata.Order = order;
277273
}
278274

279275
// Placeholder
@@ -374,25 +370,25 @@ public void CreateValidationMetadata(ValidationMetadataProviderContext context)
374370
// The only way we could arrive here is if the ModelMetadata was constructed using the non-default provider.
375371
// We'll cursorily examine the attributes on the property, but not the ContainerType to make a decision about it's nullability.
376372

377-
if (HasNullableAttribute(context.PropertyAttributes, out var propertyHasNullableAttribute))
373+
if (HasNullableAttribute(context.PropertyAttributes!, out var propertyHasNullableAttribute))
378374
{
379375
addInferredRequiredAttribute = propertyHasNullableAttribute;
380376
}
381377
}
382378
else
383379
{
384380
addInferredRequiredAttribute = IsNullableReferenceType(
385-
property.DeclaringType,
381+
property.DeclaringType!,
386382
member: null,
387-
context.PropertyAttributes);
383+
context.PropertyAttributes!);
388384
}
389385
}
390386
else if (context.Key.MetadataKind == ModelMetadataKind.Parameter)
391387
{
392388
addInferredRequiredAttribute = IsNullableReferenceType(
393-
context.Key.ParameterInfo?.Member.ReflectedType,
389+
context.Key.ParameterInfo!.Member.ReflectedType,
394390
context.Key.ParameterInfo.Member,
395-
context.ParameterAttributes);
391+
context.ParameterAttributes!);
396392
}
397393
else
398394
{
@@ -431,7 +427,7 @@ public void CreateValidationMetadata(ValidationMetadataProviderContext context)
431427
}
432428
}
433429

434-
private static string GetDisplayName(FieldInfo field, IStringLocalizer stringLocalizer)
430+
private static string GetDisplayName(FieldInfo field, IStringLocalizer? stringLocalizer)
435431
{
436432
var display = field.GetCustomAttribute<DisplayAttribute>(inherit: false);
437433
if (display != null)
@@ -466,7 +462,7 @@ private static string GetDisplayGroup(FieldInfo field)
466462
return string.Empty;
467463
}
468464

469-
internal static bool IsNullableReferenceType(Type containingType, MemberInfo member, IEnumerable<object> attributes)
465+
internal static bool IsNullableReferenceType(Type? containingType, MemberInfo? member, IEnumerable<object> attributes)
470466
{
471467
if (HasNullableAttribute(attributes, out var result))
472468
{
@@ -508,8 +504,13 @@ internal static bool HasNullableAttribute(IEnumerable<object> attributes, out bo
508504
return true; // [Nullable] found but type is not an NNRT
509505
}
510506

511-
internal static bool IsNullableBasedOnContext(Type containingType, MemberInfo member)
507+
internal static bool IsNullableBasedOnContext(Type? containingType, MemberInfo? member)
512508
{
509+
if (containingType is null)
510+
{
511+
return false;
512+
}
513+
513514
// For generic types, inspecting the nullability requirement additionally requires
514515
// inspecting the nullability constraint on generic type parameters. This is fairly non-triviial
515516
// so we'll just avoid calculating it. Users should still be able to apply an explicit [Required]

0 commit comments

Comments
 (0)