Skip to content
Open
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
5 changes: 4 additions & 1 deletion src/CodeGenHelpers/ClassBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ internal override void Write(in CodeWriter writer)
IsSealed ? "sealed" : null,
IsAbstract ? "abstract" : null,
_isPartial ? "partial" : null,
Kind.ToString().ToLowerInvariant(),
KindKeyword(Kind),
$"{Name}{_generics}",
extra
};
Expand Down Expand Up @@ -393,6 +393,9 @@ internal override void Write(in CodeWriter writer)
}
}

private static string KindKeyword(TypeKind kind) =>
kind == TypeKind.Struct ? "struct" : kind.ToString().ToLowerInvariant();

private static void WriteClassAttributes(IEnumerable<string> classAttributes, in CodeWriter writer)
{
foreach (var attr in classAttributes.Distinct().OrderBy(x => x))
Expand Down
97 changes: 94 additions & 3 deletions src/CodeGenHelpers/CodeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ public sealed class CodeBuilder

Changes to this file may cause incorrect behavior and will be lost if
the code is regenerated.";
private static readonly SymbolDisplayFormat ConstraintDisplayFormat = new SymbolDisplayFormat(
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes);
private string? _autoGeneratedMessage;
private readonly List<string> _namespaceImports = new List<string>();
private readonly List<string> _assemblyAttributes = new List<string>();
Expand Down Expand Up @@ -69,10 +73,97 @@ public static CodeBuilder Create(INamespaceSymbol namespaceSymbol, IndentStyle i

public static ClassBuilder Create(ITypeSymbol typeSymbol, IndentStyle indentStyle = IndentStyle.Spaces)
{
var containingTypes = new List<ITypeSymbol>();

if (typeSymbol.ContainingType != null)
{
var currentType = typeSymbol;

while (currentType.ContainingType is { } containingType)
{
containingTypes.Add(containingType);
currentType = containingType;
}

containingTypes.Reverse();
}

var builder = Create(typeSymbol.ContainingNamespace, indentStyle);
return builder.AddClass(typeSymbol.Name)
.WithAccessModifier(typeSymbol.DeclaredAccessibility)
.OfType(typeSymbol.TypeKind);
ClassBuilder? classBuilder = null;
// Top-level symbols keep the historical name, accessibility, and kind.
// Nested symbols also repeat generics and modifiers on each partial.
var copyDeclarationDetails = containingTypes.Count > 0;

foreach (var containingType in containingTypes)
{
classBuilder = AddClass(containingType);
}

return AddClass(typeSymbol);

ClassBuilder AddClass(ITypeSymbol symbol)
{
if (classBuilder != null)
{
classBuilder = classBuilder.AddNestedClass(symbol.Name, true);
}
else
{
classBuilder = builder.AddClass(symbol.Name);
}

classBuilder
.WithAccessModifier(symbol.DeclaredAccessibility)
.OfType(symbol.TypeKind);

if (copyDeclarationDetails)
CopyDeclarationDetails(classBuilder, symbol);

return classBuilder;
}
}

private static void CopyDeclarationDetails(ClassBuilder classBuilder, ITypeSymbol symbol)
{
if (symbol.IsStatic)
{
classBuilder.MakeStaticClass();
}
else if (symbol.TypeKind == TypeKind.Class)
{
// Structs report IsSealed, but `sealed struct` does not compile.
// Static classes are handled above and must not also be abstract or sealed.
if (symbol.IsAbstract)
classBuilder.Abstract();

if (symbol.IsSealed)
classBuilder.Sealed();
}

if (symbol is not INamedTypeSymbol namedType)
return;

foreach (var typeParameter in namedType.TypeParameters)
{
classBuilder.AddGeneric(typeParameter.Name, generic =>
{
if (typeParameter.HasReferenceTypeConstraint)
generic.Class();
else if (typeParameter.HasUnmanagedTypeConstraint)
generic.AddConstraint("unmanaged");
else if (typeParameter.HasValueTypeConstraint)
generic.AddConstraint("struct");

if (typeParameter.HasNotNullConstraint)
generic.AddConstraint("notnull");

foreach (var constraintType in typeParameter.ConstraintTypes)
generic.AddConstraint(constraintType.ToDisplayString(ConstraintDisplayFormat));

if (typeParameter.HasConstructorConstraint)
generic.New();
});
}
}

public CodeBuilder AddNamespaceImport(string importedNamespace)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace Demo.Containers
{
internal partial struct OuterStruct
{
public partial class NestedValue
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace Demo.Containers
{
internal static partial class Outer<T>
where T : class
{
protected internal abstract partial class Middle<TItem>
where TItem : struct
{
private protected sealed partial class Inner<TResult>
where TResult : class, System.IComparable<TResult>
{
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace Demo.Containers
{
internal partial class Outer
{
protected internal partial class Middle
{
private protected partial class Inner
{
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace Demo
{
public partial class Widget
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace Demo.Containers
{
public partial class Outer
{
internal partial class Inner
{
}
}
}
Loading