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 diff --git a/InterfaceGenerator/AutoInterfaceGenerator.cs b/InterfaceGenerator/AutoInterfaceGenerator.cs index 026ed36..c7b2852 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,14 @@ namespace InterfaceGenerator public void Initialize(GeneratorInitializationContext context) { context.RegisterForSyntaxNotifications(() => new SyntaxReceiver()); + + #if DEBUG + if (!Debugger.IsAttached) + { + // sadly this is Windows only so as of now :( + Debugger.Launch(); + } + #endif } public void Execute(GeneratorExecutionContext context) @@ -83,6 +92,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 +101,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..5f309ae 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,47 @@ 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 symbol, bool useNameWhenNotFound = false) + { + if (IsRootNamespace(symbol)) + { + return useNameWhenNotFound ? symbol.Name : string.Empty; + } + + var stringBuilder = new StringBuilder(symbol.MetadataName); + var last = symbol; + + symbol = symbol.ContainingSymbol; + + while (!IsRootNamespace(symbol)) + { + if (symbol is ITypeSymbol && last is ITypeSymbol) + { + stringBuilder.Insert(0, '+'); + } + else + { + stringBuilder.Insert(0, '.'); + } + + stringBuilder.Insert(0, symbol.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat)); + symbol = symbol.ContainingSymbol; + } + + var retVal = stringBuilder.ToString(); + if (string.IsNullOrWhiteSpace(retVal) && useNameWhenNotFound) + { + return symbol.Name; + } + + return retVal; + } + + private static bool IsRootNamespace(ISymbol symbol) + { + return symbol is INamespaceSymbol { IsGlobalNamespace: true }; + } } } \ No newline at end of file