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
35 changes: 21 additions & 14 deletions Rdmp.Core.Generators/TypeRegistryGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@
name.StartsWith("NUnit") ||
name.StartsWith("coverlet") ||
name.StartsWith("NSubstitute") ||
name == "CommandLine")
name == "CommandLine" ||
name == "Spectre.Console.Ansi")
return;

CollectTypesFromNamespace(assembly.GlobalNamespace, types, includeInternal);
Expand Down Expand Up @@ -146,6 +147,13 @@

private static string GenerateTypeRegistry(List<TypeInfo> types)
{
// Deduplicate types by qualified name (can happen when multiple assemblies expose the same type).
// Normalize by stripping global:: so dedup matches the dictionary key space.
var deduped = types
.GroupBy(t => t.FullName.Replace("global::", ""))
.Select(g => g.First())
.ToList();

Comment thread
jas88 marked this conversation as resolved.
var sb = new StringBuilder();

sb.AppendLine("// <auto-generated/>");
Expand All @@ -162,7 +170,7 @@
sb.AppendLine();
sb.AppendLine("/// <summary>");
sb.AppendLine("/// Compile-time generated type registry containing all types from referenced assemblies.");
sb.AppendLine($"/// Generated at compile-time with {types.Count} types.");
sb.AppendLine($"/// Generated at compile-time with {deduped.Count} types.");
sb.AppendLine("/// </summary>");
sb.AppendLine("public static partial class CompiledTypeRegistry");
sb.AppendLine("{");
Expand All @@ -173,30 +181,30 @@
sb.AppendLine(" var dict = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);");
sb.AppendLine();

// Group types by short name to handle duplicates
var grouped = types.GroupBy(t => t.ShortName);

foreach (var group in grouped)
// Group types by short name to handle duplicates.
// Materialize each group into a list to avoid repeated enumeration.
foreach (var group in deduped.GroupBy(t => t.ShortName))
{
if (group.Count() == 1)
var members = group.ToList();
if (members.Count == 1)
{
var type = group.First();
var type = members[0];
sb.AppendLine($" // {type.FullName}");
// Add all lookup variants
AddTypeLookup(sb, type.ShortName, type.FullName);
AddTypeLookup(sb, type.FullName, type.FullName);
}
else
{
// Multiple types with same short name - only store full names
foreach (var type in group)
foreach (var type in members)
{
sb.AppendLine($" // {type.FullName} (short name conflict)");
AddTypeLookup(sb, type.FullName, type.FullName);
}
}
sb.AppendLine();
}

Check notice

Code scanning / CodeQL

Missed opportunity to use Select Note

This foreach loop immediately
maps its iteration variable to another variable
- consider mapping the sequence explicitly using '.Select(...)'.

sb.AppendLine(" _typesByName = dict.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);");
sb.AppendLine(" }");
Expand All @@ -219,11 +227,10 @@

private static void AddTypeLookup(StringBuilder sb, string key, string typeFullName)
{
// Escape quotes in key
var escapedKey = key.Replace("\"", "\\\"");
// Remove global:: prefix for the key lookup, but keep it for typeof()
var lookupKey = key.Replace("global::", "");
sb.AppendLine($" dict.TryAdd(\"{escapedKey}\", typeof({typeFullName}));");
// Remove global:: prefix for the dictionary key so Type.FullName lookups hit the fast path,
// but keep global:: in the typeof() expression to avoid namespace conflicts
var lookupKey = key.Replace("global::", "").Replace("\"", "\\\"");
sb.AppendLine($" dict.TryAdd(\"{lookupKey}\", typeof({typeFullName}));");
}

private static string GenerateInterfaceIndices(List<TypeInfo> types)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public void Init()
public static IEnumerable<Type> GetAllCommandTypes()
{
return MEF.GetTypes<IAtomicCommand>()
.Where(t => !t.IsGenericTypeDefinition)
.Where(t => !KnownIncompatibleCommands.Contains(t))
.OrderBy(t => t.FullName);
}
Expand Down
Loading