From dd40cc1e350860fc934c53973c6ecaf11a3aad1d Mon Sep 17 00:00:00 2001 From: Edward Trouw Date: Thu, 28 Jul 2022 08:52:34 +0200 Subject: [PATCH 1/3] Getting the full metadata name to use as the hint and check if its already been used (for e.g. a partial) --- InterfaceGenerator/AutoInterfaceGenerator.cs | 20 ++++++++- InterfaceGenerator/SymbolExtensions.cs | 44 ++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/InterfaceGenerator/AutoInterfaceGenerator.cs b/InterfaceGenerator/AutoInterfaceGenerator.cs index 026ed36..a027903 100644 --- a/InterfaceGenerator/AutoInterfaceGenerator.cs +++ b/InterfaceGenerator/AutoInterfaceGenerator.cs @@ -1,6 +1,7 @@ using System; using System.CodeDom.Compiler; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; @@ -21,6 +22,13 @@ namespace InterfaceGenerator public void Initialize(GeneratorInitializationContext context) { context.RegisterForSyntaxNotifications(() => new SyntaxReceiver()); + +#if DEBUG + if (!Debugger.IsAttached) + { + Debugger.Launch(); + } +#endif } public void Execute(GeneratorExecutionContext context) @@ -83,6 +91,8 @@ namespace InterfaceGenerator var classSymbols = GetImplTypeSymbols(compilation, receiver); + List classSymbolNames = new List(); + foreach (var implTypeSymbol in classSymbols) { if (!implTypeSymbol.TryGetAttribute(_generateAutoInterfaceAttribute, out var attributes)) @@ -90,9 +100,17 @@ namespace InterfaceGenerator continue; } + if(classSymbolNames.Contains(implTypeSymbol.GetFullMetadataName(useNameWhenNotFound: true))) + { + continue; // partial class, already added + } + + classSymbolNames.Add(implTypeSymbol.GetFullMetadataName(useNameWhenNotFound: true)); + var attribute = attributes.Single(); var source = SourceText.From(GenerateInterfaceCode(implTypeSymbol, attribute), Encoding.UTF8); - context.AddSource($"{implTypeSymbol.Name}_AutoInterface.cs", source); + + context.AddSource($"{implTypeSymbol.GetFullMetadataName(useNameWhenNotFound: true)}_AutoInterface.cs", source); } } diff --git a/InterfaceGenerator/SymbolExtensions.cs b/InterfaceGenerator/SymbolExtensions.cs index bb1fbce..ec84fdd 100644 --- a/InterfaceGenerator/SymbolExtensions.cs +++ b/InterfaceGenerator/SymbolExtensions.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Text; using Microsoft.CodeAnalysis; namespace InterfaceGenerator @@ -21,5 +22,48 @@ namespace InterfaceGenerator return symbol.GetAttributes() .Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attributeType)); } + + //Ref: https://stackoverflow.com/questions/27105909/get-fully-qualified-metadata-name-in-roslyn + public static string GetFullMetadataName(this ISymbol s, bool useNameWhenNotFound = false) + { + if (s == null || IsRootNamespace(s)) + { + if (useNameWhenNotFound) + { + return s.Name; + } + return string.Empty; + } + + var sb = new StringBuilder(s.MetadataName); + var last = s; + + s = s.ContainingSymbol; + + while (!IsRootNamespace(s)) + { + if (s is ITypeSymbol && last is ITypeSymbol) + { + sb.Insert(0, '+'); + } + else + { + sb.Insert(0, '.'); + } + + sb.Insert(0, s.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); + //sb.Insert(0, s.MetadataName); + s = s.ContainingSymbol; + } + + var retval = sb.ToString(); + return string.IsNullOrEmpty(retval) && useNameWhenNotFound ? s.Name : retval; + } + + private static bool IsRootNamespace(ISymbol symbol) + { + INamespaceSymbol s = null; + return ((s = symbol as INamespaceSymbol) != null) && s.IsGlobalNamespace; + } } } \ No newline at end of file From b0311c7977bc809b5f40476056d0bd875ad568e2 Mon Sep 17 00:00:00 2001 From: daver32 <38791383+daver32@users.noreply.github.com> Date: Thu, 28 Jul 2022 19:45:38 +0200 Subject: [PATCH 2/3] add test case for classes with identical names --- InterfaceGenerator.Tests/SameName/SameNameClass.1.cs | 12 ++++++++++++ InterfaceGenerator.Tests/SameName/SameNameClass.2.cs | 8 ++++++++ 2 files changed, 20 insertions(+) create mode 100644 InterfaceGenerator.Tests/SameName/SameNameClass.1.cs create mode 100644 InterfaceGenerator.Tests/SameName/SameNameClass.2.cs diff --git a/InterfaceGenerator.Tests/SameName/SameNameClass.1.cs b/InterfaceGenerator.Tests/SameName/SameNameClass.1.cs new file mode 100644 index 0000000..e3906f2 --- /dev/null +++ b/InterfaceGenerator.Tests/SameName/SameNameClass.1.cs @@ -0,0 +1,12 @@ +// ReSharper disable CheckNamespace +namespace InterfaceGenerator.Tests.SameName_1; + +/// +/// A class with the same name as . It exists to test if the generated source units have fully +/// qualified names. +/// +[GenerateAutoInterface] +internal class SameNameClass : ISameNameClass +{ + +} \ No newline at end of file diff --git a/InterfaceGenerator.Tests/SameName/SameNameClass.2.cs b/InterfaceGenerator.Tests/SameName/SameNameClass.2.cs new file mode 100644 index 0000000..9147b4f --- /dev/null +++ b/InterfaceGenerator.Tests/SameName/SameNameClass.2.cs @@ -0,0 +1,8 @@ +// ReSharper disable CheckNamespace +namespace InterfaceGenerator.Tests.SameName_2; + +[GenerateAutoInterface] +internal class SameNameClass : ISameNameClass +{ + +} \ No newline at end of file From 658526359af83dceb3d41a740bdf2f608e2779e8 Mon Sep 17 00:00:00 2001 From: daver32 <38791383+daver32@users.noreply.github.com> Date: Thu, 28 Jul 2022 20:06:45 +0200 Subject: [PATCH 3/3] minor quality adjustments --- InterfaceGenerator/AutoInterfaceGenerator.cs | 5 ++- InterfaceGenerator/SymbolExtensions.cs | 41 ++++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/InterfaceGenerator/AutoInterfaceGenerator.cs b/InterfaceGenerator/AutoInterfaceGenerator.cs index a027903..c7b2852 100644 --- a/InterfaceGenerator/AutoInterfaceGenerator.cs +++ b/InterfaceGenerator/AutoInterfaceGenerator.cs @@ -23,12 +23,13 @@ namespace InterfaceGenerator { context.RegisterForSyntaxNotifications(() => new SyntaxReceiver()); -#if DEBUG + #if DEBUG if (!Debugger.IsAttached) { + // sadly this is Windows only so as of now :( Debugger.Launch(); } -#endif + #endif } public void Execute(GeneratorExecutionContext context) diff --git a/InterfaceGenerator/SymbolExtensions.cs b/InterfaceGenerator/SymbolExtensions.cs index ec84fdd..5f309ae 100644 --- a/InterfaceGenerator/SymbolExtensions.cs +++ b/InterfaceGenerator/SymbolExtensions.cs @@ -24,46 +24,45 @@ namespace InterfaceGenerator } //Ref: https://stackoverflow.com/questions/27105909/get-fully-qualified-metadata-name-in-roslyn - public static string GetFullMetadataName(this ISymbol s, bool useNameWhenNotFound = false) + public static string GetFullMetadataName(this ISymbol symbol, bool useNameWhenNotFound = false) { - if (s == null || IsRootNamespace(s)) + if (IsRootNamespace(symbol)) { - if (useNameWhenNotFound) - { - return s.Name; - } - return string.Empty; + return useNameWhenNotFound ? symbol.Name : string.Empty; } - var sb = new StringBuilder(s.MetadataName); - var last = s; + var stringBuilder = new StringBuilder(symbol.MetadataName); + var last = symbol; - s = s.ContainingSymbol; + symbol = symbol.ContainingSymbol; - while (!IsRootNamespace(s)) + while (!IsRootNamespace(symbol)) { - if (s is ITypeSymbol && last is ITypeSymbol) + if (symbol is ITypeSymbol && last is ITypeSymbol) { - sb.Insert(0, '+'); + stringBuilder.Insert(0, '+'); } else { - sb.Insert(0, '.'); + stringBuilder.Insert(0, '.'); } - sb.Insert(0, s.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); - //sb.Insert(0, s.MetadataName); - s = s.ContainingSymbol; + stringBuilder.Insert(0, symbol.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); + symbol = symbol.ContainingSymbol; } - var retval = sb.ToString(); - return string.IsNullOrEmpty(retval) && useNameWhenNotFound ? s.Name : retval; + var retVal = stringBuilder.ToString(); + if (string.IsNullOrWhiteSpace(retVal) && useNameWhenNotFound) + { + return symbol.Name; + } + + return retVal; } private static bool IsRootNamespace(ISymbol symbol) { - INamespaceSymbol s = null; - return ((s = symbol as INamespaceSymbol) != null) && s.IsGlobalNamespace; + return symbol is INamespaceSymbol { IsGlobalNamespace: true }; } } } \ No newline at end of file