Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release/8.0] Fix binder gen compile issues due to inaccessible members and identifier name clashes #91967

Merged
merged 5 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/libraries/Common/src/SourceGenerators/TypeModelHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.CodeAnalysis;
using System.Collections.Generic;

namespace SourceGenerators
{
internal static class TypeModelHelper
{
public static List<ITypeSymbol>? GetAllTypeArgumentsInScope(this INamedTypeSymbol type)
{
if (!type.IsGenericType)
{
return null;
}

List<ITypeSymbol>? args = null;
TraverseContainingTypes(type);
return args;

void TraverseContainingTypes(INamedTypeSymbol current)
{
if (current.ContainingType is INamedTypeSymbol parent)
{
TraverseContainingTypes(parent);
}

if (!current.TypeArguments.IsEmpty)
{
(args ??= new()).AddRange(current.TypeArguments);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Operations;
using SourceGenerators;

namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
Expand Down Expand Up @@ -66,10 +67,11 @@ public Parser(SourceProductionContext context, KnownTypeSymbols typeSymbols, Imm
return _sourceGenSpec;
}

private static bool IsValidRootConfigType(ITypeSymbol? type)
private bool IsValidRootConfigType(ITypeSymbol? type)
{
if (type is null ||
type.SpecialType is SpecialType.System_Object or SpecialType.System_Void ||
!_typeSymbols.Compilation.IsSymbolAccessibleWithin(type, _typeSymbols.Compilation.Assembly) ||
type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
type.IsRefLikeType ||
ContainsGenericParameters(type))
Expand Down Expand Up @@ -914,19 +916,4 @@ private void RegisterTypeDiagnostic(ITypeSymbol causingType, InvocationDiagnosti
}
}
}

public static class ParserExtensions
{
public static void RegisterCacheEntry<TKey, TValue, TEntry>(this Dictionary<TKey, TValue> cache, TKey key, TEntry entry)
where TKey : notnull
where TValue : ICollection<TEntry>, new()
{
if (!cache.TryGetValue(key, out TValue? entryCollection))
{
cache[key] = entryCollection = new TValue();
}

entryCollection.Add(entry);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using SourceGenerators;

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

if (type.NeedsMemberBinding)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -923,7 +923,7 @@ private static string GetConditionKindExpr(ref bool isFirstType)
}

private static string GetConfigKeyCacheFieldName(ObjectSpec type) =>
$"s_configKeys_{type.DisplayString.ToIdentifierSubstring()}";
$"s_configKeys_{type.IdentifierCompatibleSubstring}";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -245,18 +245,7 @@ private bool EmitInitException(TypeSpec type)
private string GetIncrementalIdentifier(string prefix) => $"{prefix}{_valueSuffixIndex++}";

private static string GetInitalizeMethodDisplayString(ObjectSpec type) =>
$"{nameof(MethodsToGen_CoreBindingHelper.Initialize)}{type.DisplayString.ToIdentifierSubstring()}";
$"{nameof(MethodsToGen_CoreBindingHelper.Initialize)}{type.IdentifierCompatibleSubstring}";
}
}

internal static class EmitterExtensions
{
public static string ToIdentifierSubstring(this string typeDisplayName) =>
typeDisplayName
.Replace("[]", nameof(Array))
.Replace(", ", string.Empty)
.Replace(".", string.Empty)
.Replace("<", string.Empty)
.Replace(">", string.Empty);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Text;
using Microsoft.CodeAnalysis;
using SourceGenerators;

namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
internal static class ParserExtensions
{
private static readonly SymbolDisplayFormat s_identifierCompatibleFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
genericsOptions: SymbolDisplayGenericsOptions.None,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

public static void RegisterCacheEntry<TKey, TValue, TEntry>(this Dictionary<TKey, TValue> cache, TKey key, TEntry entry)
where TKey : notnull
where TValue : ICollection<TEntry>, new()
{
if (!cache.TryGetValue(key, out TValue? entryCollection))
{
cache[key] = entryCollection = new TValue();
}

entryCollection.Add(entry);
}

public static string ToIdentifierCompatibleSubstring(this ITypeSymbol type)
{
if (type is IArrayTypeSymbol arrayType)
{
int rank = arrayType.Rank;
string suffix = rank == 1 ? "Array" : $"Array{rank}D"; // Array, Array2D, Array3D, ...
return ToIdentifierCompatibleSubstring(arrayType.ElementType) + suffix;
}

string displayString = type.ContainingType is null
? type.Name
: type.ToDisplayString(s_identifierCompatibleFormat).Replace(".", string.Empty);

if (type is not INamedTypeSymbol { IsGenericType: true } namedType)
{
return displayString;
}

StringBuilder sb = new(displayString);

if (namedType.GetAllTypeArgumentsInScope() is List<ITypeSymbol> typeArgsInScope)
{
foreach (ITypeSymbol genericArg in typeArgsInScope)
{
sb.Append(ToIdentifierCompatibleSubstring(genericArg));
}
}

return sb.ToString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<Compile Include="$(CommonPath)\Roslyn\DiagnosticDescriptorHelper.cs" Link="Common\Roslyn\DiagnosticDescriptorHelper.cs" />
<Compile Include="$(CommonPath)\Roslyn\GetBestTypeByMetadataName.cs" Link="Common\Roslyn\GetBestTypeByMetadataName.cs" />
<Compile Include="$(CommonPath)\SourceGenerators\SourceWriter.cs" Link="Common\SourceGenerators\SourceWriter.cs" />
<Compile Include="$(CommonPath)\SourceGenerators\TypeModelHelper.cs" Link="Common\SourceGenerators\TypeModelHelper.cs" />
<Compile Include="ConfigurationBindingGenerator.cs" />
<Compile Include="ConfigurationBindingGenerator.Emitter.cs" />
<Compile Include="ConfigurationBindingGenerator.Parser.cs" />
Expand All @@ -40,6 +41,7 @@
<Compile Include="Helpers\Parser\BinderInvocation.cs" />
<Compile Include="Helpers\Parser\ConfigurationBinder.cs" />
<Compile Include="Helpers\Parser\Diagnostics.cs" />
<Compile Include="Helpers\Parser\Extensions.cs" />
<Compile Include="Helpers\Parser\OptionsBuilderConfigurationExtensions.cs" />
<Compile Include="Helpers\Parser\OptionsConfigurationServiceCollectionExtensions.cs" />
<Compile Include="Model\CollectionSpec.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
internal sealed record KnownTypeSymbols
{
public CSharpCompilation Compilation { get; }

public INamedTypeSymbol String { get; }
public INamedTypeSymbol? CultureInfo { get; }
public INamedTypeSymbol? DateOnly { get; }
Expand Down Expand Up @@ -57,6 +59,8 @@ internal sealed record KnownTypeSymbols

public KnownTypeSymbols(CSharpCompilation compilation)
{
Compilation = compilation;

// Primitives (needed because they are Microsoft.CodeAnalysis.SpecialType.None)
CultureInfo = compilation.GetBestTypeByMetadataName(typeof(CultureInfo));
DateOnly = compilation.GetBestTypeByMetadataName("System.DateOnly");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,19 @@ internal abstract record TypeSpec

public TypeSpec(ITypeSymbol type)
{
IsValueType = type.IsValueType;
Namespace = type.ContainingNamespace?.ToDisplayString();
DisplayString = type.ToDisplayString(s_minimalDisplayFormat);
Name = Namespace + "." + DisplayString.Replace(".", "+");
IsInterface = type.TypeKind is TypeKind.Interface;
Name = (Namespace is null ? string.Empty : Namespace + ".") + DisplayString.Replace(".", "+");
IdentifierCompatibleSubstring = type.ToIdentifierCompatibleSubstring();
IsValueType = type.IsValueType;
}

public string Name { get; }

public string DisplayString { get; }

public string IdentifierCompatibleSubstring { get; }

public string? Namespace { get; }

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

public virtual TypeSpec EffectiveType => this;

public bool IsInterface { get; }

protected bool CanInitComplexObject() => InitializationStrategy is not InitializationStrategy.None && InitExceptionMessage is null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@
<value>The collection type is not supported: '{0}'.</value>
</data>
<data name="CouldNotDetermineTypeInfoMessageFormat" xml:space="preserve">
<value>Binding logic was not generated for a binder call. Unsupported input patterns include generic calls and passing boxed objects.</value>
<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>
</data>
<data name="CouldNotDetermineTypeInfoTitle" xml:space="preserve">
<value>The target type for a binder call could not be determined</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
<note />
</trans-unit>
<trans-unit id="CouldNotDetermineTypeInfoMessageFormat">
<source>Binding logic was not generated for a binder call. Unsupported input patterns include generic calls and passing boxed objects.</source>
<target state="translated">Pro volání vazače se nevygenerovala logika vazby. Nepodporované vstupní vzory zahrnují obecná volání a předávání zabalených objektů.</target>
<source>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'.</source>
<target state="needs-review-translation">Pro volání vazače se nevygenerovala logika vazby. Nepodporované vstupní vzory zahrnují obecná volání a předávání zabalených objektů.</target>
<note />
</trans-unit>
<trans-unit id="CouldNotDetermineTypeInfoTitle">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
<note />
</trans-unit>
<trans-unit id="CouldNotDetermineTypeInfoMessageFormat">
<source>Binding logic was not generated for a binder call. Unsupported input patterns include generic calls and passing boxed objects.</source>
<target state="translated">Für einen Binderaufruf wurde keine Bindungslogik generiert. Nicht unterstützte Eingabemuster umfassen generische Aufrufe und übergeben geschachtelte Objekte.</target>
<source>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'.</source>
<target state="needs-review-translation">Für einen Binderaufruf wurde keine Bindungslogik generiert. Nicht unterstützte Eingabemuster umfassen generische Aufrufe und übergeben geschachtelte Objekte.</target>
<note />
</trans-unit>
<trans-unit id="CouldNotDetermineTypeInfoTitle">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
<note />
</trans-unit>
<trans-unit id="CouldNotDetermineTypeInfoMessageFormat">
<source>Binding logic was not generated for a binder call. Unsupported input patterns include generic calls and passing boxed objects.</source>
<target state="translated">No se generó la lógica de enlace para una llamada de enlazador. Los patrones de entrada no admitidos incluyen llamadas genéricas y pasar objetos en cuadros.</target>
<source>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'.</source>
<target state="needs-review-translation">No se generó la lógica de enlace para una llamada de enlazador. Los patrones de entrada no admitidos incluyen llamadas genéricas y pasar objetos en cuadros.</target>
<note />
</trans-unit>
<trans-unit id="CouldNotDetermineTypeInfoTitle">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
<note />
</trans-unit>
<trans-unit id="CouldNotDetermineTypeInfoMessageFormat">
<source>Binding logic was not generated for a binder call. Unsupported input patterns include generic calls and passing boxed objects.</source>
<target state="translated">La logique de liaison n’a pas été générée pour un appel de classeur. Les modèles d’entrée non pris en charge incluent les appels génériques et les objets boxed de passage.</target>
<source>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'.</source>
<target state="needs-review-translation">La logique de liaison n’a pas été générée pour un appel de classeur. Les modèles d’entrée non pris en charge incluent les appels génériques et les objets boxed de passage.</target>
<note />
</trans-unit>
<trans-unit id="CouldNotDetermineTypeInfoTitle">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
<note />
</trans-unit>
<trans-unit id="CouldNotDetermineTypeInfoMessageFormat">
<source>Binding logic was not generated for a binder call. Unsupported input patterns include generic calls and passing boxed objects.</source>
<target state="translated">La logica di binding non è stata generata per una chiamata binder. I modelli di input non supportati includono chiamate generiche e il passaggio di oggetti in caselle.</target>
<source>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'.</source>
<target state="needs-review-translation">La logica di binding non è stata generata per una chiamata binder. I modelli di input non supportati includono chiamate generiche e il passaggio di oggetti in caselle.</target>
<note />
</trans-unit>
<trans-unit id="CouldNotDetermineTypeInfoTitle">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
<note />
</trans-unit>
<trans-unit id="CouldNotDetermineTypeInfoMessageFormat">
<source>Binding logic was not generated for a binder call. Unsupported input patterns include generic calls and passing boxed objects.</source>
<target state="translated">バインダー呼び出しのバインド ロジックが生成されませんでした。サポートされていない入力パターンとしては、ジェネリック呼び出し、ボックス化されたオブジェクトの受け渡しなどがあります。</target>
<source>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'.</source>
<target state="needs-review-translation">バインダー呼び出しのバインド ロジックが生成されませんでした。サポートされていない入力パターンとしては、ジェネリック呼び出し、ボックス化されたオブジェクトの受け渡しなどがあります。</target>
<note />
</trans-unit>
<trans-unit id="CouldNotDetermineTypeInfoTitle">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
<note />
</trans-unit>
<trans-unit id="CouldNotDetermineTypeInfoMessageFormat">
<source>Binding logic was not generated for a binder call. Unsupported input patterns include generic calls and passing boxed objects.</source>
<target state="translated">바인더 호출에 대한 바인딩 논리가 생성되지 않았습니다. 지원되지 않는 입력 패턴에는 제네릭 호출 및 boxed 개체 전달이 포함됩니다.</target>
<source>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'.</source>
<target state="needs-review-translation">바인더 호출에 대한 바인딩 논리가 생성되지 않았습니다. 지원되지 않는 입력 패턴에는 제네릭 호출 및 boxed 개체 전달이 포함됩니다.</target>
<note />
</trans-unit>
<trans-unit id="CouldNotDetermineTypeInfoTitle">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
<note />
</trans-unit>
<trans-unit id="CouldNotDetermineTypeInfoMessageFormat">
<source>Binding logic was not generated for a binder call. Unsupported input patterns include generic calls and passing boxed objects.</source>
<target state="translated">Nie wygenerowano logiki powiązania dla wywołania integratora. Nieobsługiwane wzorce wejściowe obejmują wywołania ogólne i przekazywanie obiektów w ramce.</target>
<source>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'.</source>
<target state="needs-review-translation">Nie wygenerowano logiki powiązania dla wywołania integratora. Nieobsługiwane wzorce wejściowe obejmują wywołania ogólne i przekazywanie obiektów w ramce.</target>
<note />
</trans-unit>
<trans-unit id="CouldNotDetermineTypeInfoTitle">
Expand Down
Loading