Skip to content
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
120 changes: 120 additions & 0 deletions src/libraries/Common/src/SourceGenerators/TypeModelHelper.cs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

namespace SourceGenerators
{
internal static class TypeModelHelper
{
private static readonly SymbolDisplayFormat s_minimalDisplayFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

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

StringBuilder? sb = null;
string? symbolName = null;

if (useUniqueName)
{
string uniqueDisplayString = type.ToMinimalDisplayString();
PopulateIdentifierCompatibleSubstring(sb = new(), uniqueDisplayString);
}
else
{
symbolName = type.Name;
}

#if DEBUG
bool usingUniqueName = (symbolName is null || sb is not null) && useUniqueName;
bool usingSymbolName = (symbolName is not null && sb is null) && !useUniqueName;
bool configuredNameCorrectly = (usingUniqueName && !usingSymbolName) || (!usingUniqueName && usingSymbolName);
Debug.Assert(configuredNameCorrectly);
#endif

if (type is not INamedTypeSymbol namedType || !namedType.IsGenericType)
{
return symbolName ?? sb!.ToString();
}

if (sb is null)
{
(sb = new()).Append(symbolName!);
}

Debug.Assert(sb.Length > 0);

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

return sb.ToString();
}

/// <summary>
/// Type name, prefixed with containing type names if it is nested (e.g. ContainingType.NestedType).
/// </summary>
public static string ToMinimalDisplayString(this ITypeSymbol type) => type.ToDisplayString(s_minimalDisplayFormat);

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);
}
}
}

private static void PopulateIdentifierCompatibleSubstring(StringBuilder sb, string input)
{
foreach (char c in input)
{
if (c is '[')
{
sb.Append("Array");
}
else if (c is ',')
{
sb.Append("Comma");
}
else if (char.IsLetterOrDigit(c))
{
sb.Append(c);
}
}
}
}
}
Original file line numberDiff line numberDiff 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 DownExpand Up@@ -70,6 +71,7 @@ private static bool IsValidRootConfigType(ITypeSymbol? type)
{
if (type is null ||
type.SpecialType is SpecialType.System_Object or SpecialType.System_Void ||
!IsAccessibleFromGenBinders(type) ||
Comment thread
layomia marked this conversation as resolved.
type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
type.IsRefLikeType ||
ContainsGenericParameters(type))
Expand All@@ -78,6 +80,27 @@ type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
}

return true;

static bool IsAccessibleFromGenBinders(ITypeSymbol type)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a "within" symbol. The symbol would be the generated binder extension class which doesn't exist yet.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't you use another type symbol in the same assembly as a proxy for the one that will be generated?

@eiriktsarpaliseiriktsarpalisSep 7, 2023

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possible to use the current IAssemblySymbol as the 'within' parameter although I haven't tested that myself.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I considered the proxy approach. It didn't feel deterministic on first thought, but yes it would be a correct/better check. I'll also try using the assembly symbol.

@layomialayomiaSep 12, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a proxy would work in the common case, but we'd need to account for the assembly possibly having just one (e.g. a singular, simple target-config POCO with primitive fields). Any handwritten fallback would have a vastly different impl.

Comparing with assembly doesn't work - nested privates aren't visible.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing with assembly doesn't work - nested privates aren't visible.

Are any of the types being generated nested within user-defined types? If not, I would expect its visibility to be equivalent to that of the assembly?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing with assembly doesn't work - nested privates aren't visible.

I tried it again; the assembly check works. Must have not used the ! operator when I tried. Thanks.

{
if (type is IArrayTypeSymbol array)
{
return IsAccessibleFromGenBinders(array.ElementType);
}

if (type is INamedTypeSymbol namedType && namedType.GetAllTypeArgumentsInScope() is List<ITypeSymbol> typeArgsInScope)
{
foreach (ITypeSymbol genericArg in typeArgsInScope)
{
if (!IsAccessibleFromGenBinders(genericArg))
{
return false;
}
}
}

return type.DeclaredAccessibility is Accessibility.Public or Accessibility.Internal or Accessibility.Friend;
Comment thread
layomia marked this conversation as resolved.
}
}

private TypeSpec? GetTargetTypeForRootInvocation(ITypeSymbol? type, Location? invocationLocation)
Expand Down
Original file line numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,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 Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,22 +2,18 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.CodeAnalysis;
using SourceGenerators;

namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
internal abstract record TypeSpec
{
private static readonly SymbolDisplayFormat s_minimalDisplayFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

public TypeSpec(ITypeSymbol type)
{
IsValueType = type.IsValueType;
Namespace = type.ContainingNamespace?.ToDisplayString();
DisplayString = type.ToDisplayString(s_minimalDisplayFormat);
DisplayString = type.ToMinimalDisplayString();
IdentifierCompatibleSubstring = type.ToIdentifierCompatibleSubstring(useUniqueName: true);
Name = Namespace + "." + DisplayString.Replace(".", "+");
IsInterface = type.TypeKind is TypeKind.Interface;
}
Expand All@@ -26,6 +22,8 @@ public TypeSpec(ITypeSymbol type)

public string DisplayString { get; }

public string IdentifierCompatibleSubstring { get; }

public string? Namespace { get; }

public bool IsValueType { get; }
Expand Down
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Fix binder gen compile issues due to inaccessible members and identifier name clashes by layomia · Pull Request #91657 · dotnet/runtime · GitHub
Skip to content
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
120 changes: 120 additions & 0 deletions src/libraries/Common/src/SourceGenerators/TypeModelHelper.cs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

namespace SourceGenerators
{
internal static class TypeModelHelper
{
private static readonly SymbolDisplayFormat s_minimalDisplayFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

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

StringBuilder? sb = null;
string? symbolName = null;

if (useUniqueName)
{
string uniqueDisplayString = type.ToMinimalDisplayString();
PopulateIdentifierCompatibleSubstring(sb = new(), uniqueDisplayString);
}
else
{
symbolName = type.Name;
}

#if DEBUG
bool usingUniqueName = (symbolName is null || sb is not null) && useUniqueName;
bool usingSymbolName = (symbolName is not null && sb is null) && !useUniqueName;
bool configuredNameCorrectly = (usingUniqueName && !usingSymbolName) || (!usingUniqueName && usingSymbolName);
Debug.Assert(configuredNameCorrectly);
#endif

if (type is not INamedTypeSymbol namedType || !namedType.IsGenericType)
{
return symbolName ?? sb!.ToString();
}

if (sb is null)
{
(sb = new()).Append(symbolName!);
}

Debug.Assert(sb.Length > 0);

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

return sb.ToString();
}

/// <summary>
/// Type name, prefixed with containing type names if it is nested (e.g. ContainingType.NestedType).
/// </summary>
public static string ToMinimalDisplayString(this ITypeSymbol type) => type.ToDisplayString(s_minimalDisplayFormat);

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);
}
}
}

private static void PopulateIdentifierCompatibleSubstring(StringBuilder sb, string input)
{
foreach (char c in input)
{
if (c is '[')
{
sb.Append("Array");
}
else if (c is ',')
{
sb.Append("Comma");
}
else if (char.IsLetterOrDigit(c))
{
sb.Append(c);
}
}
}
}
}
Original file line numberDiff line numberDiff 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 DownExpand Up@@ -70,6 +71,7 @@ private static bool IsValidRootConfigType(ITypeSymbol? type)
{
if (type is null ||
type.SpecialType is SpecialType.System_Object or SpecialType.System_Void ||
!IsAccessibleFromGenBinders(type) ||
Comment thread
layomia marked this conversation as resolved.
type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
type.IsRefLikeType ||
ContainsGenericParameters(type))
Expand All@@ -78,6 +80,27 @@ type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
}

return true;

static bool IsAccessibleFromGenBinders(ITypeSymbol type)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a "within" symbol. The symbol would be the generated binder extension class which doesn't exist yet.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't you use another type symbol in the same assembly as a proxy for the one that will be generated?

@eiriktsarpaliseiriktsarpalisSep 7, 2023

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possible to use the current IAssemblySymbol as the 'within' parameter although I haven't tested that myself.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I considered the proxy approach. It didn't feel deterministic on first thought, but yes it would be a correct/better check. I'll also try using the assembly symbol.

@layomialayomiaSep 12, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a proxy would work in the common case, but we'd need to account for the assembly possibly having just one (e.g. a singular, simple target-config POCO with primitive fields). Any handwritten fallback would have a vastly different impl.

Comparing with assembly doesn't work - nested privates aren't visible.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing with assembly doesn't work - nested privates aren't visible.

Are any of the types being generated nested within user-defined types? If not, I would expect its visibility to be equivalent to that of the assembly?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing with assembly doesn't work - nested privates aren't visible.

I tried it again; the assembly check works. Must have not used the ! operator when I tried. Thanks.

{
if (type is IArrayTypeSymbol array)
{
return IsAccessibleFromGenBinders(array.ElementType);
}

if (type is INamedTypeSymbol namedType && namedType.GetAllTypeArgumentsInScope() is List<ITypeSymbol> typeArgsInScope)
{
foreach (ITypeSymbol genericArg in typeArgsInScope)
{
if (!IsAccessibleFromGenBinders(genericArg))
{
return false;
}
}
}

return type.DeclaredAccessibility is Accessibility.Public or Accessibility.Internal or Accessibility.Friend;
Comment thread
layomia marked this conversation as resolved.
}
}

private TypeSpec? GetTargetTypeForRootInvocation(ITypeSymbol? type, Location? invocationLocation)
Expand Down
Original file line numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,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 Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,22 +2,18 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.CodeAnalysis;
using SourceGenerators;

namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
internal abstract record TypeSpec
{
private static readonly SymbolDisplayFormat s_minimalDisplayFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

public TypeSpec(ITypeSymbol type)
{
IsValueType = type.IsValueType;
Namespace = type.ContainingNamespace?.ToDisplayString();
DisplayString = type.ToDisplayString(s_minimalDisplayFormat);
DisplayString = type.ToMinimalDisplayString();
IdentifierCompatibleSubstring = type.ToIdentifierCompatibleSubstring(useUniqueName: true);
Name = Namespace + "." + DisplayString.Replace(".", "+");
IsInterface = type.TypeKind is TypeKind.Interface;
}
Expand All@@ -26,6 +22,8 @@ public TypeSpec(ITypeSymbol type)

public string DisplayString { get; }

public string IdentifierCompatibleSubstring { get; }

public string? Namespace { get; }

public bool IsValueType { get; }
Expand Down
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Fix binder gen compile issues due to inaccessible members and identifier name clashes by layomia · Pull Request #91657 · dotnet/runtime · GitHub
Skip to content
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
120 changes: 120 additions & 0 deletions src/libraries/Common/src/SourceGenerators/TypeModelHelper.cs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

namespace SourceGenerators
{
internal static class TypeModelHelper
{
private static readonly SymbolDisplayFormat s_minimalDisplayFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

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

StringBuilder? sb = null;
string? symbolName = null;

if (useUniqueName)
{
string uniqueDisplayString = type.ToMinimalDisplayString();
PopulateIdentifierCompatibleSubstring(sb = new(), uniqueDisplayString);
}
else
{
symbolName = type.Name;
}

#if DEBUG
bool usingUniqueName = (symbolName is null || sb is not null) && useUniqueName;
bool usingSymbolName = (symbolName is not null && sb is null) && !useUniqueName;
bool configuredNameCorrectly = (usingUniqueName && !usingSymbolName) || (!usingUniqueName && usingSymbolName);
Debug.Assert(configuredNameCorrectly);
#endif

if (type is not INamedTypeSymbol namedType || !namedType.IsGenericType)
{
return symbolName ?? sb!.ToString();
}

if (sb is null)
{
(sb = new()).Append(symbolName!);
}

Debug.Assert(sb.Length > 0);

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

return sb.ToString();
}

/// <summary>
/// Type name, prefixed with containing type names if it is nested (e.g. ContainingType.NestedType).
/// </summary>
public static string ToMinimalDisplayString(this ITypeSymbol type) => type.ToDisplayString(s_minimalDisplayFormat);

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);
}
}
}

private static void PopulateIdentifierCompatibleSubstring(StringBuilder sb, string input)
{
foreach (char c in input)
{
if (c is '[')
{
sb.Append("Array");
}
else if (c is ',')
{
sb.Append("Comma");
}
else if (char.IsLetterOrDigit(c))
{
sb.Append(c);
}
}
}
}
}
Original file line numberDiff line numberDiff 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 DownExpand Up@@ -70,6 +71,7 @@ private static bool IsValidRootConfigType(ITypeSymbol? type)
{
if (type is null ||
type.SpecialType is SpecialType.System_Object or SpecialType.System_Void ||
!IsAccessibleFromGenBinders(type) ||
Comment thread
layomia marked this conversation as resolved.
type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
type.IsRefLikeType ||
ContainsGenericParameters(type))
Expand All@@ -78,6 +80,27 @@ type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
}

return true;

static bool IsAccessibleFromGenBinders(ITypeSymbol type)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a "within" symbol. The symbol would be the generated binder extension class which doesn't exist yet.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't you use another type symbol in the same assembly as a proxy for the one that will be generated?

@eiriktsarpaliseiriktsarpalisSep 7, 2023

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possible to use the current IAssemblySymbol as the 'within' parameter although I haven't tested that myself.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I considered the proxy approach. It didn't feel deterministic on first thought, but yes it would be a correct/better check. I'll also try using the assembly symbol.

@layomialayomiaSep 12, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a proxy would work in the common case, but we'd need to account for the assembly possibly having just one (e.g. a singular, simple target-config POCO with primitive fields). Any handwritten fallback would have a vastly different impl.

Comparing with assembly doesn't work - nested privates aren't visible.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing with assembly doesn't work - nested privates aren't visible.

Are any of the types being generated nested within user-defined types? If not, I would expect its visibility to be equivalent to that of the assembly?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing with assembly doesn't work - nested privates aren't visible.

I tried it again; the assembly check works. Must have not used the ! operator when I tried. Thanks.

{
if (type is IArrayTypeSymbol array)
{
return IsAccessibleFromGenBinders(array.ElementType);
}

if (type is INamedTypeSymbol namedType && namedType.GetAllTypeArgumentsInScope() is List<ITypeSymbol> typeArgsInScope)
{
foreach (ITypeSymbol genericArg in typeArgsInScope)
{
if (!IsAccessibleFromGenBinders(genericArg))
{
return false;
}
}
}

return type.DeclaredAccessibility is Accessibility.Public or Accessibility.Internal or Accessibility.Friend;
Comment thread
layomia marked this conversation as resolved.
}
}

private TypeSpec? GetTargetTypeForRootInvocation(ITypeSymbol? type, Location? invocationLocation)
Expand Down
Original file line numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,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 Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,22 +2,18 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.CodeAnalysis;
using SourceGenerators;

namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
internal abstract record TypeSpec
{
private static readonly SymbolDisplayFormat s_minimalDisplayFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

public TypeSpec(ITypeSymbol type)
{
IsValueType = type.IsValueType;
Namespace = type.ContainingNamespace?.ToDisplayString();
DisplayString = type.ToDisplayString(s_minimalDisplayFormat);
DisplayString = type.ToMinimalDisplayString();
IdentifierCompatibleSubstring = type.ToIdentifierCompatibleSubstring(useUniqueName: true);
Name = Namespace + "." + DisplayString.Replace(".", "+");
IsInterface = type.TypeKind is TypeKind.Interface;
}
Expand All@@ -26,6 +22,8 @@ public TypeSpec(ITypeSymbol type)

public string DisplayString { get; }

public string IdentifierCompatibleSubstring { get; }

public string? Namespace { get; }

public bool IsValueType { get; }
Expand Down
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Fix binder gen compile issues due to inaccessible members and identifier name clashes by layomia · Pull Request #91657 · dotnet/runtime · GitHub
Skip to content
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
120 changes: 120 additions & 0 deletions src/libraries/Common/src/SourceGenerators/TypeModelHelper.cs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

namespace SourceGenerators
{
internal static class TypeModelHelper
{
private static readonly SymbolDisplayFormat s_minimalDisplayFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

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

StringBuilder? sb = null;
string? symbolName = null;

if (useUniqueName)
{
string uniqueDisplayString = type.ToMinimalDisplayString();
PopulateIdentifierCompatibleSubstring(sb = new(), uniqueDisplayString);
}
else
{
symbolName = type.Name;
}

#if DEBUG
bool usingUniqueName = (symbolName is null || sb is not null) && useUniqueName;
bool usingSymbolName = (symbolName is not null && sb is null) && !useUniqueName;
bool configuredNameCorrectly = (usingUniqueName && !usingSymbolName) || (!usingUniqueName && usingSymbolName);
Debug.Assert(configuredNameCorrectly);
#endif

if (type is not INamedTypeSymbol namedType || !namedType.IsGenericType)
{
return symbolName ?? sb!.ToString();
}

if (sb is null)
{
(sb = new()).Append(symbolName!);
}

Debug.Assert(sb.Length > 0);

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

return sb.ToString();
}

/// <summary>
/// Type name, prefixed with containing type names if it is nested (e.g. ContainingType.NestedType).
/// </summary>
public static string ToMinimalDisplayString(this ITypeSymbol type) => type.ToDisplayString(s_minimalDisplayFormat);

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);
}
}
}

private static void PopulateIdentifierCompatibleSubstring(StringBuilder sb, string input)
{
foreach (char c in input)
{
if (c is '[')
{
sb.Append("Array");
}
else if (c is ',')
{
sb.Append("Comma");
}
else if (char.IsLetterOrDigit(c))
{
sb.Append(c);
}
}
}
}
}
Original file line numberDiff line numberDiff 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 DownExpand Up@@ -70,6 +71,7 @@ private static bool IsValidRootConfigType(ITypeSymbol? type)
{
if (type is null ||
type.SpecialType is SpecialType.System_Object or SpecialType.System_Void ||
!IsAccessibleFromGenBinders(type) ||
Comment thread
layomia marked this conversation as resolved.
type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
type.IsRefLikeType ||
ContainsGenericParameters(type))
Expand All@@ -78,6 +80,27 @@ type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
}

return true;

static bool IsAccessibleFromGenBinders(ITypeSymbol type)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a "within" symbol. The symbol would be the generated binder extension class which doesn't exist yet.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't you use another type symbol in the same assembly as a proxy for the one that will be generated?

@eiriktsarpaliseiriktsarpalisSep 7, 2023

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possible to use the current IAssemblySymbol as the 'within' parameter although I haven't tested that myself.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I considered the proxy approach. It didn't feel deterministic on first thought, but yes it would be a correct/better check. I'll also try using the assembly symbol.

@layomialayomiaSep 12, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a proxy would work in the common case, but we'd need to account for the assembly possibly having just one (e.g. a singular, simple target-config POCO with primitive fields). Any handwritten fallback would have a vastly different impl.

Comparing with assembly doesn't work - nested privates aren't visible.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing with assembly doesn't work - nested privates aren't visible.

Are any of the types being generated nested within user-defined types? If not, I would expect its visibility to be equivalent to that of the assembly?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing with assembly doesn't work - nested privates aren't visible.

I tried it again; the assembly check works. Must have not used the ! operator when I tried. Thanks.

{
if (type is IArrayTypeSymbol array)
{
return IsAccessibleFromGenBinders(array.ElementType);
}

if (type is INamedTypeSymbol namedType && namedType.GetAllTypeArgumentsInScope() is List<ITypeSymbol> typeArgsInScope)
{
foreach (ITypeSymbol genericArg in typeArgsInScope)
{
if (!IsAccessibleFromGenBinders(genericArg))
{
return false;
}
}
}

return type.DeclaredAccessibility is Accessibility.Public or Accessibility.Internal or Accessibility.Friend;
Comment thread
layomia marked this conversation as resolved.
}
}

private TypeSpec? GetTargetTypeForRootInvocation(ITypeSymbol? type, Location? invocationLocation)
Expand Down
Original file line numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,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 Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,22 +2,18 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.CodeAnalysis;
using SourceGenerators;

namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
internal abstract record TypeSpec
{
private static readonly SymbolDisplayFormat s_minimalDisplayFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

public TypeSpec(ITypeSymbol type)
{
IsValueType = type.IsValueType;
Namespace = type.ContainingNamespace?.ToDisplayString();
DisplayString = type.ToDisplayString(s_minimalDisplayFormat);
DisplayString = type.ToMinimalDisplayString();
IdentifierCompatibleSubstring = type.ToIdentifierCompatibleSubstring(useUniqueName: true);
Name = Namespace + "." + DisplayString.Replace(".", "+");
IsInterface = type.TypeKind is TypeKind.Interface;
}
Expand All@@ -26,6 +22,8 @@ public TypeSpec(ITypeSymbol type)

public string DisplayString { get; }

public string IdentifierCompatibleSubstring { get; }

public string? Namespace { get; }

public bool IsValueType { get; }
Expand Down
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Fix binder gen compile issues due to inaccessible members and identifier name clashes by layomia · Pull Request #91657 · dotnet/runtime · GitHub
Skip to content
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
120 changes: 120 additions & 0 deletions src/libraries/Common/src/SourceGenerators/TypeModelHelper.cs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

namespace SourceGenerators
{
internal static class TypeModelHelper
{
private static readonly SymbolDisplayFormat s_minimalDisplayFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

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

StringBuilder? sb = null;
string? symbolName = null;

if (useUniqueName)
{
string uniqueDisplayString = type.ToMinimalDisplayString();
PopulateIdentifierCompatibleSubstring(sb = new(), uniqueDisplayString);
}
else
{
symbolName = type.Name;
}

#if DEBUG
bool usingUniqueName = (symbolName is null || sb is not null) && useUniqueName;
bool usingSymbolName = (symbolName is not null && sb is null) && !useUniqueName;
bool configuredNameCorrectly = (usingUniqueName && !usingSymbolName) || (!usingUniqueName && usingSymbolName);
Debug.Assert(configuredNameCorrectly);
#endif

if (type is not INamedTypeSymbol namedType || !namedType.IsGenericType)
{
return symbolName ?? sb!.ToString();
}

if (sb is null)
{
(sb = new()).Append(symbolName!);
}

Debug.Assert(sb.Length > 0);

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

return sb.ToString();
}

/// <summary>
/// Type name, prefixed with containing type names if it is nested (e.g. ContainingType.NestedType).
/// </summary>
public static string ToMinimalDisplayString(this ITypeSymbol type) => type.ToDisplayString(s_minimalDisplayFormat);

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);
}
}
}

private static void PopulateIdentifierCompatibleSubstring(StringBuilder sb, string input)
{
foreach (char c in input)
{
if (c is '[')
{
sb.Append("Array");
}
else if (c is ',')
{
sb.Append("Comma");
}
else if (char.IsLetterOrDigit(c))
{
sb.Append(c);
}
}
}
}
}
Original file line numberDiff line numberDiff 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 DownExpand Up@@ -70,6 +71,7 @@ private static bool IsValidRootConfigType(ITypeSymbol? type)
{
if (type is null ||
type.SpecialType is SpecialType.System_Object or SpecialType.System_Void ||
!IsAccessibleFromGenBinders(type) ||
Comment thread
layomia marked this conversation as resolved.
type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
type.IsRefLikeType ||
ContainsGenericParameters(type))
Expand All@@ -78,6 +80,27 @@ type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
}

return true;

static bool IsAccessibleFromGenBinders(ITypeSymbol type)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a "within" symbol. The symbol would be the generated binder extension class which doesn't exist yet.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't you use another type symbol in the same assembly as a proxy for the one that will be generated?

@eiriktsarpaliseiriktsarpalisSep 7, 2023

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possible to use the current IAssemblySymbol as the 'within' parameter although I haven't tested that myself.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I considered the proxy approach. It didn't feel deterministic on first thought, but yes it would be a correct/better check. I'll also try using the assembly symbol.

@layomialayomiaSep 12, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a proxy would work in the common case, but we'd need to account for the assembly possibly having just one (e.g. a singular, simple target-config POCO with primitive fields). Any handwritten fallback would have a vastly different impl.

Comparing with assembly doesn't work - nested privates aren't visible.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing with assembly doesn't work - nested privates aren't visible.

Are any of the types being generated nested within user-defined types? If not, I would expect its visibility to be equivalent to that of the assembly?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing with assembly doesn't work - nested privates aren't visible.

I tried it again; the assembly check works. Must have not used the ! operator when I tried. Thanks.

{
if (type is IArrayTypeSymbol array)
{
return IsAccessibleFromGenBinders(array.ElementType);
}

if (type is INamedTypeSymbol namedType && namedType.GetAllTypeArgumentsInScope() is List<ITypeSymbol> typeArgsInScope)
{
foreach (ITypeSymbol genericArg in typeArgsInScope)
{
if (!IsAccessibleFromGenBinders(genericArg))
{
return false;
}
}
}

return type.DeclaredAccessibility is Accessibility.Public or Accessibility.Internal or Accessibility.Friend;
Comment thread
layomia marked this conversation as resolved.
}
}

private TypeSpec? GetTargetTypeForRootInvocation(ITypeSymbol? type, Location? invocationLocation)
Expand Down
Original file line numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,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 Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,22 +2,18 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.CodeAnalysis;
using SourceGenerators;

namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
internal abstract record TypeSpec
{
private static readonly SymbolDisplayFormat s_minimalDisplayFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

public TypeSpec(ITypeSymbol type)
{
IsValueType = type.IsValueType;
Namespace = type.ContainingNamespace?.ToDisplayString();
DisplayString = type.ToDisplayString(s_minimalDisplayFormat);
DisplayString = type.ToMinimalDisplayString();
IdentifierCompatibleSubstring = type.ToIdentifierCompatibleSubstring(useUniqueName: true);
Name = Namespace + "." + DisplayString.Replace(".", "+");
IsInterface = type.TypeKind is TypeKind.Interface;
}
Expand All@@ -26,6 +22,8 @@ public TypeSpec(ITypeSymbol type)

public string DisplayString { get; }

public string IdentifierCompatibleSubstring { get; }

public string? Namespace { get; }

public bool IsValueType { get; }
Expand Down
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Fix binder gen compile issues due to inaccessible members and identifier name clashes by layomia · Pull Request #91657 · dotnet/runtime · GitHub
Skip to content
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
120 changes: 120 additions & 0 deletions src/libraries/Common/src/SourceGenerators/TypeModelHelper.cs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

namespace SourceGenerators
{
internal static class TypeModelHelper
{
private static readonly SymbolDisplayFormat s_minimalDisplayFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

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

StringBuilder? sb = null;
string? symbolName = null;

if (useUniqueName)
{
string uniqueDisplayString = type.ToMinimalDisplayString();
PopulateIdentifierCompatibleSubstring(sb = new(), uniqueDisplayString);
}
else
{
symbolName = type.Name;
}

#if DEBUG
bool usingUniqueName = (symbolName is null || sb is not null) && useUniqueName;
bool usingSymbolName = (symbolName is not null && sb is null) && !useUniqueName;
bool configuredNameCorrectly = (usingUniqueName && !usingSymbolName) || (!usingUniqueName && usingSymbolName);
Debug.Assert(configuredNameCorrectly);
#endif

if (type is not INamedTypeSymbol namedType || !namedType.IsGenericType)
{
return symbolName ?? sb!.ToString();
}

if (sb is null)
{
(sb = new()).Append(symbolName!);
}

Debug.Assert(sb.Length > 0);

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

return sb.ToString();
}

/// <summary>
/// Type name, prefixed with containing type names if it is nested (e.g. ContainingType.NestedType).
/// </summary>
public static string ToMinimalDisplayString(this ITypeSymbol type) => type.ToDisplayString(s_minimalDisplayFormat);

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);
}
}
}

private static void PopulateIdentifierCompatibleSubstring(StringBuilder sb, string input)
{
foreach (char c in input)
{
if (c is '[')
{
sb.Append("Array");
}
else if (c is ',')
{
sb.Append("Comma");
}
else if (char.IsLetterOrDigit(c))
{
sb.Append(c);
}
}
}
}
}
Original file line numberDiff line numberDiff 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 DownExpand Up@@ -70,6 +71,7 @@ private static bool IsValidRootConfigType(ITypeSymbol? type)
{
if (type is null ||
type.SpecialType is SpecialType.System_Object or SpecialType.System_Void ||
!IsAccessibleFromGenBinders(type) ||
Comment thread
layomia marked this conversation as resolved.
type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
type.IsRefLikeType ||
ContainsGenericParameters(type))
Expand All@@ -78,6 +80,27 @@ type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
}

return true;

static bool IsAccessibleFromGenBinders(ITypeSymbol type)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a "within" symbol. The symbol would be the generated binder extension class which doesn't exist yet.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't you use another type symbol in the same assembly as a proxy for the one that will be generated?

@eiriktsarpaliseiriktsarpalisSep 7, 2023

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possible to use the current IAssemblySymbol as the 'within' parameter although I haven't tested that myself.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I considered the proxy approach. It didn't feel deterministic on first thought, but yes it would be a correct/better check. I'll also try using the assembly symbol.

@layomialayomiaSep 12, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a proxy would work in the common case, but we'd need to account for the assembly possibly having just one (e.g. a singular, simple target-config POCO with primitive fields). Any handwritten fallback would have a vastly different impl.

Comparing with assembly doesn't work - nested privates aren't visible.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing with assembly doesn't work - nested privates aren't visible.

Are any of the types being generated nested within user-defined types? If not, I would expect its visibility to be equivalent to that of the assembly?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing with assembly doesn't work - nested privates aren't visible.

I tried it again; the assembly check works. Must have not used the ! operator when I tried. Thanks.

{
if (type is IArrayTypeSymbol array)
{
return IsAccessibleFromGenBinders(array.ElementType);
}

if (type is INamedTypeSymbol namedType && namedType.GetAllTypeArgumentsInScope() is List<ITypeSymbol> typeArgsInScope)
{
foreach (ITypeSymbol genericArg in typeArgsInScope)
{
if (!IsAccessibleFromGenBinders(genericArg))
{
return false;
}
}
}

return type.DeclaredAccessibility is Accessibility.Public or Accessibility.Internal or Accessibility.Friend;
Comment thread
layomia marked this conversation as resolved.
}
}

private TypeSpec? GetTargetTypeForRootInvocation(ITypeSymbol? type, Location? invocationLocation)
Expand Down
Original file line numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,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 Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,22 +2,18 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.CodeAnalysis;
using SourceGenerators;

namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
internal abstract record TypeSpec
{
private static readonly SymbolDisplayFormat s_minimalDisplayFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

public TypeSpec(ITypeSymbol type)
{
IsValueType = type.IsValueType;
Namespace = type.ContainingNamespace?.ToDisplayString();
DisplayString = type.ToDisplayString(s_minimalDisplayFormat);
DisplayString = type.ToMinimalDisplayString();
IdentifierCompatibleSubstring = type.ToIdentifierCompatibleSubstring(useUniqueName: true);
Name = Namespace + "." + DisplayString.Replace(".", "+");
IsInterface = type.TypeKind is TypeKind.Interface;
}
Expand All@@ -26,6 +22,8 @@ public TypeSpec(ITypeSymbol type)

public string DisplayString { get; }

public string IdentifierCompatibleSubstring { get; }

public string? Namespace { get; }

public bool IsValueType { get; }
Expand Down
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Fix binder gen compile issues due to inaccessible members and identifier name clashes by layomia · Pull Request #91657 · dotnet/runtime · GitHub
Skip to content
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
120 changes: 120 additions & 0 deletions src/libraries/Common/src/SourceGenerators/TypeModelHelper.cs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

namespace SourceGenerators
{
internal static class TypeModelHelper
{
private static readonly SymbolDisplayFormat s_minimalDisplayFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

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

StringBuilder? sb = null;
string? symbolName = null;

if (useUniqueName)
{
string uniqueDisplayString = type.ToMinimalDisplayString();
PopulateIdentifierCompatibleSubstring(sb = new(), uniqueDisplayString);
}
else
{
symbolName = type.Name;
}

#if DEBUG
bool usingUniqueName = (symbolName is null || sb is not null) && useUniqueName;
bool usingSymbolName = (symbolName is not null && sb is null) && !useUniqueName;
bool configuredNameCorrectly = (usingUniqueName && !usingSymbolName) || (!usingUniqueName && usingSymbolName);
Debug.Assert(configuredNameCorrectly);
#endif

if (type is not INamedTypeSymbol namedType || !namedType.IsGenericType)
{
return symbolName ?? sb!.ToString();
}

if (sb is null)
{
(sb = new()).Append(symbolName!);
}

Debug.Assert(sb.Length > 0);

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

return sb.ToString();
}

/// <summary>
/// Type name, prefixed with containing type names if it is nested (e.g. ContainingType.NestedType).
/// </summary>
public static string ToMinimalDisplayString(this ITypeSymbol type) => type.ToDisplayString(s_minimalDisplayFormat);

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);
}
}
}

private static void PopulateIdentifierCompatibleSubstring(StringBuilder sb, string input)
{
foreach (char c in input)
{
if (c is '[')
{
sb.Append("Array");
}
else if (c is ',')
{
sb.Append("Comma");
}
else if (char.IsLetterOrDigit(c))
{
sb.Append(c);
}
}
}
}
}
Original file line numberDiff line numberDiff 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 DownExpand Up@@ -70,6 +71,7 @@ private static bool IsValidRootConfigType(ITypeSymbol? type)
{
if (type is null ||
type.SpecialType is SpecialType.System_Object or SpecialType.System_Void ||
!IsAccessibleFromGenBinders(type) ||
Comment thread
layomia marked this conversation as resolved.
type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
type.IsRefLikeType ||
ContainsGenericParameters(type))
Expand All@@ -78,6 +80,27 @@ type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
}

return true;

static bool IsAccessibleFromGenBinders(ITypeSymbol type)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a "within" symbol. The symbol would be the generated binder extension class which doesn't exist yet.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't you use another type symbol in the same assembly as a proxy for the one that will be generated?

@eiriktsarpaliseiriktsarpalisSep 7, 2023

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possible to use the current IAssemblySymbol as the 'within' parameter although I haven't tested that myself.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I considered the proxy approach. It didn't feel deterministic on first thought, but yes it would be a correct/better check. I'll also try using the assembly symbol.

@layomialayomiaSep 12, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a proxy would work in the common case, but we'd need to account for the assembly possibly having just one (e.g. a singular, simple target-config POCO with primitive fields). Any handwritten fallback would have a vastly different impl.

Comparing with assembly doesn't work - nested privates aren't visible.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing with assembly doesn't work - nested privates aren't visible.

Are any of the types being generated nested within user-defined types? If not, I would expect its visibility to be equivalent to that of the assembly?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing with assembly doesn't work - nested privates aren't visible.

I tried it again; the assembly check works. Must have not used the ! operator when I tried. Thanks.

{
if (type is IArrayTypeSymbol array)
{
return IsAccessibleFromGenBinders(array.ElementType);
}

if (type is INamedTypeSymbol namedType && namedType.GetAllTypeArgumentsInScope() is List<ITypeSymbol> typeArgsInScope)
{
foreach (ITypeSymbol genericArg in typeArgsInScope)
{
if (!IsAccessibleFromGenBinders(genericArg))
{
return false;
}
}
}

return type.DeclaredAccessibility is Accessibility.Public or Accessibility.Internal or Accessibility.Friend;
Comment thread
layomia marked this conversation as resolved.
}
}

private TypeSpec? GetTargetTypeForRootInvocation(ITypeSymbol? type, Location? invocationLocation)
Expand Down
Original file line numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,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 Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,22 +2,18 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.CodeAnalysis;
using SourceGenerators;

namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
internal abstract record TypeSpec
{
private static readonly SymbolDisplayFormat s_minimalDisplayFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

public TypeSpec(ITypeSymbol type)
{
IsValueType = type.IsValueType;
Namespace = type.ContainingNamespace?.ToDisplayString();
DisplayString = type.ToDisplayString(s_minimalDisplayFormat);
DisplayString = type.ToMinimalDisplayString();
IdentifierCompatibleSubstring = type.ToIdentifierCompatibleSubstring(useUniqueName: true);
Name = Namespace + "." + DisplayString.Replace(".", "+");
IsInterface = type.TypeKind is TypeKind.Interface;
}
Expand All@@ -26,6 +22,8 @@ public TypeSpec(ITypeSymbol type)

public string DisplayString { get; }

public string IdentifierCompatibleSubstring { get; }

public string? Namespace { get; }

public bool IsValueType { get; }
Expand Down
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Fix binder gen compile issues due to inaccessible members and identifier name clashes by layomia · Pull Request #91657 · dotnet/runtime · GitHub
Skip to content
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
120 changes: 120 additions & 0 deletions src/libraries/Common/src/SourceGenerators/TypeModelHelper.cs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

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

namespace SourceGenerators
{
internal static class TypeModelHelper
{
private static readonly SymbolDisplayFormat s_minimalDisplayFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

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

StringBuilder? sb = null;
string? symbolName = null;

if (useUniqueName)
{
string uniqueDisplayString = type.ToMinimalDisplayString();
PopulateIdentifierCompatibleSubstring(sb = new(), uniqueDisplayString);
}
else
{
symbolName = type.Name;
}

#if DEBUG
bool usingUniqueName = (symbolName is null || sb is not null) && useUniqueName;
bool usingSymbolName = (symbolName is not null && sb is null) && !useUniqueName;
bool configuredNameCorrectly = (usingUniqueName && !usingSymbolName) || (!usingUniqueName && usingSymbolName);
Debug.Assert(configuredNameCorrectly);
#endif

if (type is not INamedTypeSymbol namedType || !namedType.IsGenericType)
{
return symbolName ?? sb!.ToString();
}

if (sb is null)
{
(sb = new()).Append(symbolName!);
}

Debug.Assert(sb.Length > 0);

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

return sb.ToString();
}

/// <summary>
/// Type name, prefixed with containing type names if it is nested (e.g. ContainingType.NestedType).
/// </summary>
public static string ToMinimalDisplayString(this ITypeSymbol type) => type.ToDisplayString(s_minimalDisplayFormat);

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);
}
}
}

private static void PopulateIdentifierCompatibleSubstring(StringBuilder sb, string input)
{
foreach (char c in input)
{
if (c is '[')
{
sb.Append("Array");
}
else if (c is ',')
{
sb.Append("Comma");
}
else if (char.IsLetterOrDigit(c))
{
sb.Append(c);
}
}
}
}
}
Original file line numberDiff line numberDiff 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 DownExpand Up@@ -70,6 +71,7 @@ private static bool IsValidRootConfigType(ITypeSymbol? type)
{
if (type is null ||
type.SpecialType is SpecialType.System_Object or SpecialType.System_Void ||
!IsAccessibleFromGenBinders(type) ||
Comment thread
layomia marked this conversation as resolved.
type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
type.IsRefLikeType ||
ContainsGenericParameters(type))
Expand All@@ -78,6 +80,27 @@ type.TypeKind is TypeKind.TypeParameter or TypeKind.Pointer or TypeKind.Error ||
}

return true;

static bool IsAccessibleFromGenBinders(ITypeSymbol type)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a "within" symbol. The symbol would be the generated binder extension class which doesn't exist yet.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't you use another type symbol in the same assembly as a proxy for the one that will be generated?

@eiriktsarpaliseiriktsarpalisSep 7, 2023

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possible to use the current IAssemblySymbol as the 'within' parameter although I haven't tested that myself.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I considered the proxy approach. It didn't feel deterministic on first thought, but yes it would be a correct/better check. I'll also try using the assembly symbol.

@layomialayomiaSep 12, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a proxy would work in the common case, but we'd need to account for the assembly possibly having just one (e.g. a singular, simple target-config POCO with primitive fields). Any handwritten fallback would have a vastly different impl.

Comparing with assembly doesn't work - nested privates aren't visible.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing with assembly doesn't work - nested privates aren't visible.

Are any of the types being generated nested within user-defined types? If not, I would expect its visibility to be equivalent to that of the assembly?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comparing with assembly doesn't work - nested privates aren't visible.

I tried it again; the assembly check works. Must have not used the ! operator when I tried. Thanks.

{
if (type is IArrayTypeSymbol array)
{
return IsAccessibleFromGenBinders(array.ElementType);
}

if (type is INamedTypeSymbol namedType && namedType.GetAllTypeArgumentsInScope() is List<ITypeSymbol> typeArgsInScope)
{
foreach (ITypeSymbol genericArg in typeArgsInScope)
{
if (!IsAccessibleFromGenBinders(genericArg))
{
return false;
}
}
}

return type.DeclaredAccessibility is Accessibility.Public or Accessibility.Internal or Accessibility.Friend;
Comment thread
layomia marked this conversation as resolved.
}
}

private TypeSpec? GetTargetTypeForRootInvocation(ITypeSymbol? type, Location? invocationLocation)
Expand Down
Original file line numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff line change
Expand Up@@ -23,6 +23,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 Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,22 +2,18 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.CodeAnalysis;
using SourceGenerators;

namespace Microsoft.Extensions.Configuration.Binder.SourceGeneration
{
internal abstract record TypeSpec
{
private static readonly SymbolDisplayFormat s_minimalDisplayFormat = new SymbolDisplayFormat(
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);

public TypeSpec(ITypeSymbol type)
{
IsValueType = type.IsValueType;
Namespace = type.ContainingNamespace?.ToDisplayString();
DisplayString = type.ToDisplayString(s_minimalDisplayFormat);
DisplayString = type.ToMinimalDisplayString();
IdentifierCompatibleSubstring = type.ToIdentifierCompatibleSubstring(useUniqueName: true);
Name = Namespace + "." + DisplayString.Replace(".", "+");
IsInterface = type.TypeKind is TypeKind.Interface;
}
Expand All@@ -26,6 +22,8 @@ public TypeSpec(ITypeSymbol type)

public string DisplayString { get; }

public string IdentifierCompatibleSubstring { get; }

public string? Namespace { get; }

public bool IsValueType { get; }
Expand Down
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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