Skip to content

Commit 1d60c69

Browse files
github-actions[bot]layomiacarlossanlop
authored
[release/8.0] Fix binder gen compile issues due to inaccessible members and identifier name clashes (#91967)
* Fix binder gen compile issues due to inaccessible members and identifier name clashes * Minimize net diff of binder-gen visibility/identifier-clash fix * Improve identifier formatting * Use built-in API for accessiblity check --------- Co-authored-by: Layomi Akinrinade <[email protected]> Co-authored-by: Carlos Sánchez López <[email protected]>
1 parent 5de5c46 commit 1d60c69

29 files changed

+421
-87
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.CodeAnalysis;
5+
using System.Collections.Generic;
6+
7+
namespace SourceGenerators
8+
{
9+
internal static class TypeModelHelper
10+
{
11+
public static List<ITypeSymbol>? GetAllTypeArgumentsInScope(this INamedTypeSymbol type)
12+
{
13+
if (!type.IsGenericType)
14+
{
15+
return null;
16+
}
17+
18+
List<ITypeSymbol>? args = null;
19+
TraverseContainingTypes(type);
20+
return args;
21+
22+
void TraverseContainingTypes(INamedTypeSymbol current)
23+
{
24+
if (current.ContainingType is INamedTypeSymbol parent)
25+
{
26+
TraverseContainingTypes(parent);
27+
}
28+
29+
if (!current.TypeArguments.IsEmpty)
30+
{
31+
(args ??= new()).AddRange(current.TypeArguments);
32+
}
33+
}
34+
}
35+
}
36+
}

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

Lines changed: 3 additions & 16 deletions
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
{
@@ -66,10 +67,11 @@ public Parser(SourceProductionContext context, KnownTypeSymbols typeSymbols, Imm
6667
return _sourceGenSpec;
6768
}
6869

69-
private static bool IsValidRootConfigType(ITypeSymbol? type)
70+
private bool IsValidRootConfigType(ITypeSymbol? type)
7071
{
7172
if (type is null ||
7273
type.SpecialType is SpecialType.System_Object or SpecialType.System_Void ||
74+
!_typeSymbols.Compilation.IsSymbolAccessibleWithin(type, _typeSymbols.Compilation.Assembly) ||
7375
type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
7476
type.IsRefLikeType ||
7577
ContainsGenericParameters(type))
@@ -914,19 +916,4 @@ private void RegisterTypeDiagnostic(ITypeSymbol causingType, InvocationDiagnosti
914916
}
915917
}
916918
}
917-
918-
public static class ParserExtensions
919-
{
920-
public static void RegisterCacheEntry<TKey, TValue, TEntry>(this Dictionary<TKey, TValue> cache, TKey key, TEntry entry)
921-
where TKey : notnull
922-
where TValue : ICollection<TEntry>, new()
923-
{
924-
if (!cache.TryGetValue(key, out TValue? entryCollection))
925-
{
926-
cache[key] = entryCollection = new TValue();
927-
}
928-
929-
entryCollection.Add(entry);
930-
}
931-
}
932919
}

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/ConfigurationBinder.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Collections.Generic;
5+
using SourceGenerators;
56

67
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
78
{
@@ -138,7 +139,7 @@ void EmitMethods(MethodsToGen_ConfigurationBinder method, string additionalParam
138139
EmitBlankLineIfRequired();
139140
_writer.WriteLine($"/// <summary>Attempts to bind the given object instance to configuration values by matching property names against configuration keys recursively.</summary>");
140141
EmitInterceptsLocationAnnotations(interceptorInfoList);
141-
EmitStartBlock($"public static void {Identifier.Bind}_{type.DisplayString.ToIdentifierSubstring()}(this {Identifier.IConfiguration} {Identifier.configuration}, {additionalParams})");
142+
EmitStartBlock($"public static void {Identifier.Bind}_{type.IdentifierCompatibleSubstring}(this {Identifier.IConfiguration} {Identifier.configuration}, {additionalParams})");
142143

143144
if (type.NeedsMemberBinding)
144145
{

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/CoreBindingHelpers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -923,7 +923,7 @@ private static string GetConditionKindExpr(ref bool isFirstType)
923923
}
924924

925925
private static string GetConfigKeyCacheFieldName(ObjectSpec type) =>
926-
$"s_configKeys_{type.DisplayString.ToIdentifierSubstring()}";
926+
$"s_configKeys_{type.IdentifierCompatibleSubstring}";
927927
}
928928
}
929929
}

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Helpers/Emitter/Helpers.cs

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -245,18 +245,7 @@ private bool EmitInitException(TypeSpec type)
245245
private string GetIncrementalIdentifier(string prefix) => $"{prefix}{_valueSuffixIndex++}";
246246

247247
private static string GetInitalizeMethodDisplayString(ObjectSpec type) =>
248-
$"{nameof(MethodsToGen_CoreBindingHelper.Initialize)}{type.DisplayString.ToIdentifierSubstring()}";
248+
$"{nameof(MethodsToGen_CoreBindingHelper.Initialize)}{type.IdentifierCompatibleSubstring}";
249249
}
250250
}
251-
252-
internal static class EmitterExtensions
253-
{
254-
public static string ToIdentifierSubstring(this string typeDisplayName) =>
255-
typeDisplayName
256-
.Replace("[]", nameof(Array))
257-
.Replace(", ", string.Empty)
258-
.Replace(".", string.Empty)
259-
.Replace("<", string.Empty)
260-
.Replace(">", string.Empty);
261-
}
262251
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Generic;
5+
using System.Text;
6+
using Microsoft.CodeAnalysis;
7+
using SourceGenerators;
8+
9+
namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
10+
{
11+
internal static class ParserExtensions
12+
{
13+
private static readonly SymbolDisplayFormat s_identifierCompatibleFormat = new SymbolDisplayFormat(
14+
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
15+
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
16+
genericsOptions: SymbolDisplayGenericsOptions.None,
17+
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);
18+
19+
public static void RegisterCacheEntry<TKey, TValue, TEntry>(this Dictionary<TKey, TValue> cache, TKey key, TEntry entry)
20+
where TKey : notnull
21+
where TValue : ICollection<TEntry>, new()
22+
{
23+
if (!cache.TryGetValue(key, out TValue? entryCollection))
24+
{
25+
cache[key] = entryCollection = new TValue();
26+
}
27+
28+
entryCollection.Add(entry);
29+
}
30+
31+
public static string ToIdentifierCompatibleSubstring(this ITypeSymbol type)
32+
{
33+
if (type is IArrayTypeSymbol arrayType)
34+
{
35+
int rank = arrayType.Rank;
36+
string suffix = rank == 1 ? "Array" : $"Array{rank}D"; // Array, Array2D, Array3D, ...
37+
return ToIdentifierCompatibleSubstring(arrayType.ElementType) + suffix;
38+
}
39+
40+
string displayString = type.ContainingType is null
41+
? type.Name
42+
: type.ToDisplayString(s_identifierCompatibleFormat).Replace(".", string.Empty);
43+
44+
if (type is not INamedTypeSymbol { IsGenericType: true } namedType)
45+
{
46+
return displayString;
47+
}
48+
49+
StringBuilder sb = new(displayString);
50+
51+
if (namedType.GetAllTypeArgumentsInScope() is List<ITypeSymbol> typeArgsInScope)
52+
{
53+
foreach (ITypeSymbol genericArg in typeArgsInScope)
54+
{
55+
sb.Append(ToIdentifierCompatibleSubstring(genericArg));
56+
}
57+
}
58+
59+
return sb.ToString();
60+
}
61+
}
62+
}

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Microsoft.Extensions.Configuration.Binder.SourceGeneration.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
<Compile Include="$(CommonPath)\Roslyn\DiagnosticDescriptorHelper.cs" Link="Common\Roslyn\DiagnosticDescriptorHelper.cs" />
2626
<Compile Include="$(CommonPath)\Roslyn\GetBestTypeByMetadataName.cs" Link="Common\Roslyn\GetBestTypeByMetadataName.cs" />
2727
<Compile Include="$(CommonPath)\SourceGenerators\SourceWriter.cs" Link="Common\SourceGenerators\SourceWriter.cs" />
28+
<Compile Include="$(CommonPath)\SourceGenerators\TypeModelHelper.cs" Link="Common\SourceGenerators\TypeModelHelper.cs" />
2829
<Compile Include="ConfigurationBindingGenerator.cs" />
2930
<Compile Include="ConfigurationBindingGenerator.Emitter.cs" />
3031
<Compile Include="ConfigurationBindingGenerator.Parser.cs" />
@@ -40,6 +41,7 @@
4041
<Compile Include="Helpers\Parser\BinderInvocation.cs" />
4142
<Compile Include="Helpers\Parser\ConfigurationBinder.cs" />
4243
<Compile Include="Helpers\Parser\Diagnostics.cs" />
44+
<Compile Include="Helpers\Parser\Extensions.cs" />
4345
<Compile Include="Helpers\Parser\OptionsBuilderConfigurationExtensions.cs" />
4446
<Compile Include="Helpers\Parser\OptionsConfigurationServiceCollectionExtensions.cs" />
4547
<Compile Include="Model\CollectionSpec.cs" />

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/KnownTypeSymbols.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
1313
{
1414
internal sealed record KnownTypeSymbols
1515
{
16+
public CSharpCompilation Compilation { get; }
17+
1618
public INamedTypeSymbol String { get; }
1719
public INamedTypeSymbol? CultureInfo { get; }
1820
public INamedTypeSymbol? DateOnly { get; }
@@ -57,6 +59,8 @@ internal sealed record KnownTypeSymbols
5759

5860
public KnownTypeSymbols(CSharpCompilation compilation)
5961
{
62+
Compilation = compilation;
63+
6064
// Primitives (needed because they are Microsoft.CodeAnalysis.SpecialType.None)
6165
CultureInfo = compilation.GetBestTypeByMetadataName(typeof(CultureInfo));
6266
DateOnly = compilation.GetBestTypeByMetadataName("System.DateOnly");

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Model/TypeSpec.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,19 @@ internal abstract record TypeSpec
1515

1616
public TypeSpec(ITypeSymbol type)
1717
{
18-
IsValueType = type.IsValueType;
1918
Namespace = type.ContainingNamespace?.ToDisplayString();
2019
DisplayString = type.ToDisplayString(s_minimalDisplayFormat);
21-
Name = Namespace + "." + DisplayString.Replace(".", "+");
22-
IsInterface = type.TypeKind is TypeKind.Interface;
20+
Name = (Namespace is null ? string.Empty : Namespace + ".") + DisplayString.Replace(".", "+");
21+
IdentifierCompatibleSubstring = type.ToIdentifierCompatibleSubstring();
22+
IsValueType = type.IsValueType;
2323
}
2424

2525
public string Name { get; }
2626

2727
public string DisplayString { get; }
2828

29+
public string IdentifierCompatibleSubstring { get; }
30+
2931
public string? Namespace { get; }
3032

3133
public bool IsValueType { get; }
@@ -42,8 +44,6 @@ public TypeSpec(ITypeSymbol type)
4244

4345
public virtual TypeSpec EffectiveType => this;
4446

45-
public bool IsInterface { get; }
46-
4747
protected bool CanInitComplexObject() => InitializationStrategy is not InitializationStrategy.None && InitExceptionMessage is null;
4848
}
4949

src/libraries/Microsoft.Extensions.Configuration.Binder/gen/Resources/Strings.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@
121121
<value>The collection type is not supported: '{0}'.</value>
122122
</data>
123123
<data name="CouldNotDetermineTypeInfoMessageFormat" xml:space="preserve">
124-
<value>Binding logic was not generated for a binder call. Unsupported input patterns include generic calls and passing boxed objects.</value>
124+
<value>Binding logic was not generated for a binder call. Unsupported input patterns include generic calls, passing boxed objects, and passing types that are not 'public' or 'internal'.</value>
125125
</data>
126126
<data name="CouldNotDetermineTypeInfoTitle" xml:space="preserve">
127127
<value>The target type for a binder call could not be determined</value>

0 commit comments

Comments
 (0)