Skip to content

Commit 832c9bb

Browse files
committed
Account for generic types when determining accessibility
1 parent 5cb4d0e commit 832c9bb

File tree

3 files changed

+67
-3
lines changed

3 files changed

+67
-3
lines changed

src/libraries/Common/src/SourceGenerators/TypeModelHelper.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
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;
54
using System.Text;
65
using Microsoft.CodeAnalysis;
76
using System.Collections.Generic;
@@ -74,7 +73,7 @@ public static string ToIdentifierCompatibleSubstring(this ITypeSymbol type, bool
7473
/// </summary>
7574
public static string ToMinimalDisplayString(this ITypeSymbol type) => type.ToDisplayString(s_minimalDisplayFormat);
7675

77-
private static List<ITypeSymbol>? GetAllTypeArgumentsInScope(this INamedTypeSymbol type)
76+
public static List<ITypeSymbol>? GetAllTypeArgumentsInScope(this INamedTypeSymbol type)
7877
{
7978
if (!type.IsGenericType)
8079
{

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/ConfigurationBindingGenerator.Parser.cs

+12
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Linq;
1010
using Microsoft.CodeAnalysis;
1111
using Microsoft.CodeAnalysis.Operations;
12+
using SourceGenerators;
1213

1314
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
1415
{
@@ -87,6 +88,17 @@ static bool IsAccessibleFromGenBinders(ITypeSymbol type)
8788
return IsAccessibleFromGenBinders(array.ElementType);
8889
}
8990

91+
if (type is INamedTypeSymbol namedType && namedType.GetAllTypeArgumentsInScope() is List<ITypeSymbol> typeArgsInScope)
92+
{
93+
foreach (ITypeSymbol genericArg in typeArgsInScope)
94+
{
95+
if (!IsAccessibleFromGenBinders(genericArg))
96+
{
97+
return false;
98+
}
99+
}
100+
}
101+
90102
return type.DeclaredAccessibility is Accessibility.Public or Accessibility.Internal or Accessibility.Friend;
91103
}
92104
}

src/libraries/Microsoft.Extensions.Configuration.Binder/tests/SourceGenerationTests/ConfigurationBinderTests.Generator.cs

+54-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
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;
5+
using System.Collections.Generic;
46
using Microsoft.Extensions.Configuration;
57
using Xunit;
68

@@ -336,17 +338,51 @@ public void MemberAccessibility_InaccessibleNestedTypeAsRootConfig()
336338
var c2 = configuration.Get<InaccessibleClass_2>();
337339
var c3 = configuration.Get<InaccessibleClass_3>();
338340

341+
// Generic collections.
342+
339343
configuration = TestHelpers
340344
.GetConfigurationFromJsonString(@"{""Array"": [{""Value"":1}]}")
341345
.GetSection("Array");
342346
var c4 = configuration.Get<InaccessibleClass_1[]>();
347+
var c5 = configuration.Get<List<InaccessibleClass_1>>();
348+
349+
// Generic types.
350+
351+
Action<BinderOptions>? configureOptions = options => options.BindNonPublicProperties = true;
352+
string GetNestedObjectPayload(string propName) => $$"""
353+
{
354+
"{{propName}}": {
355+
"Value": 1
356+
}
357+
}
358+
""";
359+
360+
configuration = TestHelpers.GetConfigurationFromJsonString(GetNestedObjectPayload("item1"));
361+
var c6 = configuration.Get<Dictionary<string, InaccessibleClass_1>>(configureOptions);
362+
363+
configuration = TestHelpers.GetConfigurationFromJsonString(GetNestedObjectPayload("protectedMember"));
364+
var c7 = configuration.Get<AccessibleGenericClass<InaccessibleClass_1>>(configureOptions);
365+
var c8 = configuration.Get<AccessibleGenericClass<InaccessibleClass_1>>(configureOptions);
366+
367+
configuration = TestHelpers.GetConfigurationFromJsonString(GetNestedObjectPayload("publicMember"));
368+
var c9 = configuration.Get<InaccessibleGenericClass<AccessibleClass>>(configureOptions);
369+
var c10 = configuration.Get<InaccessibleGenericClass<InaccessibleClass_1>>(configureOptions);
343370
#pragma warning disable SYSLIB1104
344371

345-
// Instead, there is a reflection fallback.
372+
// Reflection fallback.
373+
346374
Assert.Equal(1, c1.Value);
347375
Assert.Equal(1, c2.Value);
348376
Assert.Equal(1, c3.Value);
377+
349378
Assert.Equal(1, c4[0].Value);
379+
Assert.Equal(1, c5[0].Value);
380+
Assert.Equal(1, c6["item1"].Value);
381+
382+
Assert.Equal(1, c7.GetProtectedMember.Value);
383+
Assert.Equal(1, c8.GetProtectedMember.Value);
384+
Assert.Equal(1, c9.PublicMember.Value);
385+
Assert.Equal(1, c10.PublicMember.Value);
350386
}
351387

352388
private class InaccessibleClass_1()
@@ -362,5 +398,22 @@ protected internal class InaccessibleClass_3
362398

363399
public int Value { get; }
364400
}
401+
402+
internal class AccessibleGenericClass<T>
403+
{
404+
protected T ProtectedMember { get; set; }
405+
406+
public T GetProtectedMember => ProtectedMember;
407+
}
408+
409+
private class InaccessibleGenericClass<T>
410+
{
411+
public T PublicMember { get; set; }
412+
}
413+
414+
public class AccessibleClass()
415+
{
416+
public int Value { get; set; }
417+
}
365418
}
366419
}

0 commit comments

Comments
 (0)