From 498b5e6165ad48984591d3e6a58e2b8892034419 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Jul 2022 10:26:54 -0700 Subject: [PATCH 1/6] Polyfill the incremental generator ForAttributeWithMetadataName from roslyn (for LibraryImportGenerator). --- .../LibraryImportGenerator.cs | 34 +++++-------------- .../LibraryImportGenerator.csproj | 15 ++++++++ .../Generic/ValueListBuilder.Pop.cs | 20 +++++++++++ 3 files changed, 43 insertions(+), 26 deletions(-) create mode 100644 src/libraries/System.Runtime.InteropServices/src/System/Collections/Generic/ValueListBuilder.Pop.cs diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.cs index c262c2e0017ec..ac1ce59a47aaa 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.DotnetRuntime.Extensions; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; [assembly: System.Resources.NeutralResourcesLanguage("en-US")] @@ -61,19 +62,13 @@ public static class StepNames public void Initialize(IncrementalGeneratorInitializationContext context) { var attributedMethods = context.SyntaxProvider - .CreateSyntaxProvider( - static (node, ct) => ShouldVisitNode(node), - static (context, ct) => - { - MethodDeclarationSyntax syntax = (MethodDeclarationSyntax)context.Node; - if (context.SemanticModel.GetDeclaredSymbol(syntax, ct) is IMethodSymbol methodSymbol - && methodSymbol.GetAttributes().Any(static attribute => attribute.AttributeClass?.ToDisplayString() == TypeNames.LibraryImportAttribute)) - { - return new { Syntax = syntax, Symbol = methodSymbol }; - } - - return null; - }) + .ForAttributeWithMetadataName( + context, + TypeNames.LibraryImportAttribute, + static (node, ct) => node is MethodDeclarationSyntax, + static (context, ct) => context.TargetSymbol is IMethodSymbol methodSymbol + ? new { Syntax = (MethodDeclarationSyntax)context.TargetNode, Symbol = methodSymbol } + : null) .Where( static modelData => modelData is not null); @@ -584,19 +579,6 @@ static ExpressionSyntax CreateEnumExpressionSyntax(T value) where T : Enum } } - private static bool ShouldVisitNode(SyntaxNode syntaxNode) - { - // We only support C# method declarations. - if (syntaxNode.Language != LanguageNames.CSharp - || !syntaxNode.IsKind(SyntaxKind.MethodDeclaration)) - { - return false; - } - - // Filter out methods with no attributes early. - return ((MethodDeclarationSyntax)syntaxNode).AttributeLists.Count > 0; - } - private static Diagnostic? GetDiagnosticIfInvalidMethodForGeneration(MethodDeclarationSyntax methodSyntax, IMethodSymbol method) { // Verify the method has no generic types or defined implementation diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.csproj b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.csproj index 5806bf43f581c..0d6c923602b13 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.csproj +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.csproj @@ -35,4 +35,19 @@ + + + + + + + + + + + + + + + diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Collections/Generic/ValueListBuilder.Pop.cs b/src/libraries/System.Runtime.InteropServices/src/System/Collections/Generic/ValueListBuilder.Pop.cs new file mode 100644 index 0000000000000..c5aa6bbff3a6d --- /dev/null +++ b/src/libraries/System.Runtime.InteropServices/src/System/Collections/Generic/ValueListBuilder.Pop.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +namespace System.Collections.Generic +{ + /// + /// These public methods are required by RegexWriter. + /// + internal ref partial struct ValueListBuilder + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Pop() + { + _pos--; + return _span[_pos]; + } + } +} From 6cade39596c0f7ddccb0ab8b3fb4b678c8d03125 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Jul 2022 11:06:10 -0700 Subject: [PATCH 2/6] Move common code to shared location --- .../Generic/ValueListBuilder.Pop.cs | 0 .../LibraryImportGenerator.csproj | 2 +- ...m.Text.RegularExpressions.Generator.csproj | 2 +- .../Generic/ValueListBuilder.Pop.cs | 20 ------------------- 4 files changed, 2 insertions(+), 22 deletions(-) rename src/libraries/{System.Runtime.InteropServices => System.Private.CoreLib}/src/System/Collections/Generic/ValueListBuilder.Pop.cs (100%) delete mode 100644 src/libraries/System.Text.RegularExpressions/src/System/Collections/Generic/ValueListBuilder.Pop.cs diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Collections/Generic/ValueListBuilder.Pop.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ValueListBuilder.Pop.cs similarity index 100% rename from src/libraries/System.Runtime.InteropServices/src/System/Collections/Generic/ValueListBuilder.Pop.cs rename to src/libraries/System.Private.CoreLib/src/System/Collections/Generic/ValueListBuilder.Pop.cs diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.csproj b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.csproj index 0d6c923602b13..ab8172b697907 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.csproj +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.csproj @@ -47,7 +47,7 @@ - + diff --git a/src/libraries/System.Text.RegularExpressions/gen/System.Text.RegularExpressions.Generator.csproj b/src/libraries/System.Text.RegularExpressions/gen/System.Text.RegularExpressions.Generator.csproj index ff5a56cee8569..bf14a855c9014 100644 --- a/src/libraries/System.Text.RegularExpressions/gen/System.Text.RegularExpressions.Generator.csproj +++ b/src/libraries/System.Text.RegularExpressions/gen/System.Text.RegularExpressions.Generator.csproj @@ -34,7 +34,7 @@ - + diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Collections/Generic/ValueListBuilder.Pop.cs b/src/libraries/System.Text.RegularExpressions/src/System/Collections/Generic/ValueListBuilder.Pop.cs deleted file mode 100644 index c5aa6bbff3a6d..0000000000000 --- a/src/libraries/System.Text.RegularExpressions/src/System/Collections/Generic/ValueListBuilder.Pop.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -namespace System.Collections.Generic -{ - /// - /// These public methods are required by RegexWriter. - /// - internal ref partial struct ValueListBuilder - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T Pop() - { - _pos--; - return _span[_pos]; - } - } -} From f8b212b0dc02d4b834bbc8e985f08b6a9c3700f7 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 5 Jul 2022 11:40:21 -0700 Subject: [PATCH 3/6] Polyfill the incremental generator ForAttributeWithMetadataName from roslyn (for EventSourcewGenerator). --- .../gen/EventSourceGenerator.Parser.cs | 124 +++++++----------- .../gen/EventSourceGenerator.cs | 10 +- .../System.Private.CoreLib.Generators.csproj | 15 +++ 3 files changed, 72 insertions(+), 77 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Parser.cs b/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Parser.cs index b13526f6a5eec..1dcd01ee56df8 100644 --- a/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Parser.cs +++ b/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Parser.cs @@ -4,116 +4,90 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.DotnetRuntime.Extensions; namespace Generators { public partial class EventSourceGenerator { - private static bool IsSyntaxTargetForGeneration(SyntaxNode node, CancellationToken cancellationToken) => - node is ClassDeclarationSyntax { AttributeLists.Count: > 0 }; - - private static EventSourceClass? GetSemanticTargetForGeneration(GeneratorSyntaxContext context, CancellationToken cancellationToken) + private static EventSourceClass? GetSemanticTargetForGeneration(GeneratorAttributeSyntaxContext context, CancellationToken cancellationToken) { - const string EventSourceAutoGenerateAttribute = "System.Diagnostics.Tracing.EventSourceAutoGenerateAttribute"; const string EventSourceAttribute = "System.Diagnostics.Tracing.EventSourceAttribute"; - var classDef = (ClassDeclarationSyntax)context.Node; - SemanticModel sm = context.SemanticModel; + var classDef = (ClassDeclarationSyntax)context.TargetNode; + EventSourceClass? eventSourceClass = null; - bool autoGenerate = false; - foreach (AttributeListSyntax cal in classDef.AttributeLists) + foreach (AttributeData attribute in context.TargetSymbol.GetAttributes()) { - foreach (AttributeSyntax ca in cal.Attributes) - { - if (sm.GetSymbolInfo(ca, cancellationToken).Symbol is not IMethodSymbol caSymbol) - { - // badly formed attribute definition, or not the right attribute - continue; - } - - string attributeFullName = caSymbol.ContainingType.ToDisplayString(); + if (attribute.AttributeClass is null) + continue; - if (attributeFullName.Equals(EventSourceAutoGenerateAttribute, StringComparison.Ordinal)) + NamespaceDeclarationSyntax? ns = classDef.Parent as NamespaceDeclarationSyntax; + if (ns is null) + { + if (classDef.Parent is not CompilationUnitSyntax) { - autoGenerate = true; + // since this generator doesn't know how to generate a nested type... continue; } + } - if (!attributeFullName.Equals(EventSourceAttribute, StringComparison.Ordinal)) - { - continue; - } + if (attribute.AttributeClass.Name != "EventSourceAttribute" && + attribute.AttributeClass.ToDisplayString() != EventSourceAttribute) + { + continue; + } - string nspace = string.Empty; - NamespaceDeclarationSyntax? ns = classDef.Parent as NamespaceDeclarationSyntax; - if (ns is null) + string nspace = string.Empty; + if (ns is not null) + { + nspace = ns.Name.ToString(); + while (true) { - if (classDef.Parent is not CompilationUnitSyntax) + ns = ns.Parent as NamespaceDeclarationSyntax; + if (ns == null) { - // since this generator doesn't know how to generate a nested type... - continue; + break; } - } - else - { - nspace = ns.Name.ToString(); - while (true) - { - ns = ns.Parent as NamespaceDeclarationSyntax; - if (ns == null) - { - break; - } - nspace = $"{ns.Name}.{nspace}"; - } + nspace = $"{ns.Name}.{nspace}"; } + } - string className = classDef.Identifier.ToString(); - string name = className; - string guid = ""; + string className = classDef.Identifier.ValueText; + string name = className; + string guid = ""; - SeparatedSyntaxList? args = ca.ArgumentList?.Arguments; - if (args is not null) - { - foreach (AttributeArgumentSyntax arg in args) - { - string argName = arg.NameEquals!.Name.Identifier.ToString(); - string value = sm.GetConstantValue(arg.Expression, cancellationToken).ToString(); - - switch (argName) - { - case "Guid": - guid = value; - break; - case "Name": - name = value; - break; - } - } - } + ImmutableArray> args = attribute.NamedArguments; + foreach (KeyValuePair arg in args) + { + string argName = arg.Key; + string value = arg.Value.Value?.ToString(); - if (!Guid.TryParse(guid, out Guid result)) + switch (argName) { - result = GenerateGuidFromName(name.ToUpperInvariant()); + case "Guid": + guid = value; + break; + case "Name": + name = value; + break; } + } - eventSourceClass = new EventSourceClass(nspace, className, name, result); - continue; + if (!Guid.TryParse(guid, out Guid result)) + { + result = GenerateGuidFromName(name.ToUpperInvariant()); } - } - if (!autoGenerate) - { - return null; + eventSourceClass = new EventSourceClass(nspace, className, name, result); + continue; } return eventSourceClass; diff --git a/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.cs b/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.cs index cebe3a5d32c50..e57f11d6f4ea5 100644 --- a/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.cs +++ b/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.cs @@ -7,6 +7,7 @@ using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.DotnetRuntime.Extensions; namespace Generators { @@ -35,9 +36,14 @@ public partial class EventSourceGenerator : IIncrementalGenerator public void Initialize(IncrementalGeneratorInitializationContext context) { + const string EventSourceAutoGenerateAttribute = "System.Diagnostics.Tracing.EventSourceAutoGenerateAttribute"; + IncrementalValuesProvider eventSourceClasses = - context.SyntaxProvider - .CreateSyntaxProvider(IsSyntaxTargetForGeneration, GetSemanticTargetForGeneration) + context.SyntaxProvider.ForAttributeWithMetadataName( + context, + EventSourceAutoGenerateAttribute, + (node, _) => node is ClassDeclarationSyntax, + GetSemanticTargetForGeneration) .Where(x => x is not null); context.RegisterSourceOutput(eventSourceClasses, EmitSourceFile); diff --git a/src/libraries/System.Private.CoreLib/gen/System.Private.CoreLib.Generators.csproj b/src/libraries/System.Private.CoreLib/gen/System.Private.CoreLib.Generators.csproj index 41709a4998251..90d6ae99bb27c 100644 --- a/src/libraries/System.Private.CoreLib/gen/System.Private.CoreLib.Generators.csproj +++ b/src/libraries/System.Private.CoreLib/gen/System.Private.CoreLib.Generators.csproj @@ -12,6 +12,21 @@ + + + + + + + + + + + + + + + From 2ed84a28a920f6b50da66f9f5e5869b488666b0b Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 7 Jul 2022 13:38:04 -0700 Subject: [PATCH 4/6] Update src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Parser.cs --- .../System.Private.CoreLib/gen/EventSourceGenerator.Parser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Parser.cs b/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Parser.cs index 1dcd01ee56df8..231266dba3d6b 100644 --- a/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Parser.cs +++ b/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Parser.cs @@ -38,7 +38,7 @@ public partial class EventSourceGenerator } } - if (attribute.AttributeClass.Name != "EventSourceAttribute" && + if (attribute.AttributeClass.Name != "EventSourceAttribute" || attribute.AttributeClass.ToDisplayString() != EventSourceAttribute) { continue; From b2847be1d00608681065649d958386c52826d07c Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Jul 2022 09:05:14 -0700 Subject: [PATCH 5/6] Revert --- .../LibraryImportGenerator.cs | 38 ++++++++++++++----- .../LibraryImportGenerator.csproj | 15 -------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.cs index ac1ce59a47aaa..e32ccc145f659 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.cs @@ -12,7 +12,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.DotnetRuntime.Extensions; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; [assembly: System.Resources.NeutralResourcesLanguage("en-US")] @@ -62,13 +61,19 @@ public static class StepNames public void Initialize(IncrementalGeneratorInitializationContext context) { var attributedMethods = context.SyntaxProvider - .ForAttributeWithMetadataName( - context, - TypeNames.LibraryImportAttribute, - static (node, ct) => node is MethodDeclarationSyntax, - static (context, ct) => context.TargetSymbol is IMethodSymbol methodSymbol - ? new { Syntax = (MethodDeclarationSyntax)context.TargetNode, Symbol = methodSymbol } - : null) + .CreateSyntaxProvider( + static (node, ct) => ShouldVisitNode(node), + static (context, ct) => + { + MethodDeclarationSyntax syntax = (MethodDeclarationSyntax)context.Node; + if (context.SemanticModel.GetDeclaredSymbol(syntax, ct) is IMethodSymbol methodSymbol + && methodSymbol.GetAttributes().Any(static attribute => attribute.AttributeClass?.ToDisplayString() == TypeNames.LibraryImportAttribute)) + { + return new { Syntax = syntax, Symbol = methodSymbol }; + } + + return null; + }) .Where( static modelData => modelData is not null); @@ -353,13 +358,13 @@ private static IncrementalStubGenerationContext CalculateStubInformation( // Since the char type in an array will not be part of the P/Invoke signature, we can // use the regular blittable marshaller in all cases. new CharMarshallingGeneratorFactory(generatorFactory, useBlittableMarshallerForUtf16: true), - new AttributedMarshallingModelOptions(runtimeMarshallingDisabled, Scenario.ElementIn, Scenario.ElementRef, Scenario.ElementOut)); + new AttributedMarshallingModelOptions(runtimeMarshallingDisabled, MarshalMode.ElementIn, MarshalMode.ElementRef, MarshalMode.ElementOut)); // We don't need to include the later generator factories for collection elements // as the later generator factories only apply to parameters. generatorFactory = new AttributedMarshallingModelGeneratorFactory( generatorFactory, elementFactory, - new AttributedMarshallingModelOptions(runtimeMarshallingDisabled, Scenario.ManagedToUnmanagedIn, Scenario.ManagedToUnmanagedRef, Scenario.ManagedToUnmanagedOut)); + new AttributedMarshallingModelOptions(runtimeMarshallingDisabled, MarshalMode.ManagedToUnmanagedIn, MarshalMode.ManagedToUnmanagedRef, MarshalMode.ManagedToUnmanagedOut)); generatorFactory = new ByValueContentsMarshalKindValidator(generatorFactory); } @@ -579,6 +584,19 @@ static ExpressionSyntax CreateEnumExpressionSyntax(T value) where T : Enum } } + private static bool ShouldVisitNode(SyntaxNode syntaxNode) + { + // We only support C# method declarations. + if (syntaxNode.Language != LanguageNames.CSharp + || !syntaxNode.IsKind(SyntaxKind.MethodDeclaration)) + { + return false; + } + + // Filter out methods with no attributes early. + return ((MethodDeclarationSyntax)syntaxNode).AttributeLists.Count > 0; + } + private static Diagnostic? GetDiagnosticIfInvalidMethodForGeneration(MethodDeclarationSyntax methodSyntax, IMethodSymbol method) { // Verify the method has no generic types or defined implementation diff --git a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.csproj b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.csproj index ab8172b697907..5806bf43f581c 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.csproj +++ b/src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.csproj @@ -35,19 +35,4 @@ - - - - - - - - - - - - - - - From 4ce9f7627509e73b37cfd06826e9c71fee9e6a32 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Tue, 12 Jul 2022 09:12:32 -0700 Subject: [PATCH 6/6] Simplify --- .../gen/EventSourceGenerator.Parser.cs | 61 ++++++++++--------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Parser.cs b/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Parser.cs index 231266dba3d6b..43a498b589af8 100644 --- a/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Parser.cs +++ b/src/libraries/System.Private.CoreLib/gen/EventSourceGenerator.Parser.cs @@ -20,45 +20,28 @@ public partial class EventSourceGenerator const string EventSourceAttribute = "System.Diagnostics.Tracing.EventSourceAttribute"; var classDef = (ClassDeclarationSyntax)context.TargetNode; + NamespaceDeclarationSyntax? ns = classDef.Parent as NamespaceDeclarationSyntax; + if (ns is null) + { + if (classDef.Parent is not CompilationUnitSyntax) + { + // since this generator doesn't know how to generate a nested type... + return null; + } + } EventSourceClass? eventSourceClass = null; + string? nspace = null; foreach (AttributeData attribute in context.TargetSymbol.GetAttributes()) { - if (attribute.AttributeClass is null) - continue; - - NamespaceDeclarationSyntax? ns = classDef.Parent as NamespaceDeclarationSyntax; - if (ns is null) - { - if (classDef.Parent is not CompilationUnitSyntax) - { - // since this generator doesn't know how to generate a nested type... - continue; - } - } - - if (attribute.AttributeClass.Name != "EventSourceAttribute" || + if (attribute.AttributeClass?.Name != "EventSourceAttribute" || attribute.AttributeClass.ToDisplayString() != EventSourceAttribute) { continue; } - string nspace = string.Empty; - if (ns is not null) - { - nspace = ns.Name.ToString(); - while (true) - { - ns = ns.Parent as NamespaceDeclarationSyntax; - if (ns == null) - { - break; - } - - nspace = $"{ns.Name}.{nspace}"; - } - } + nspace ??= ConstructNamespace(ns); string className = classDef.Identifier.ValueText; string name = className; @@ -93,6 +76,26 @@ public partial class EventSourceGenerator return eventSourceClass; } + private static string? ConstructNamespace(NamespaceDeclarationSyntax? ns) + { + if (ns is null) + return string.Empty; + + string nspace = ns.Name.ToString(); + while (true) + { + ns = ns.Parent as NamespaceDeclarationSyntax; + if (ns == null) + { + break; + } + + nspace = $"{ns.Name}.{nspace}"; + } + + return nspace; + } + // From System.Private.CoreLib private static Guid GenerateGuidFromName(string name) {