From 15c040b3f31a2b86f8ca99e33543c18d940a9684 Mon Sep 17 00:00:00 2001 From: Kieron Lanning Date: Thu, 3 Sep 2026 14:21:00 +0100 Subject: [PATCH 1/3] fix: missing nullability and xml comments --- global.json | 2 +- samples/SampleApp.Net48/Directory.Build.props | 6 +- samples/SampleApp/Directory.Build.props | 4 +- src/Directory.Build.targets | 14 ----- .../SourceGenerator.Refactorings.csproj | 5 +- .../Emitters/GeneratedTypesEmitter.cs | 59 ++++++++++++++----- .../SourceGenerator/SourceGenerator.csproj | 5 +- .../TelemetrySourceGeneratorTests.cs | 35 +++++++++++ 8 files changed, 89 insertions(+), 41 deletions(-) diff --git a/global.json b/global.json index 99327e7c..cf807fda 100644 --- a/global.json +++ b/global.json @@ -5,7 +5,7 @@ "allowPrerelease": false }, "msbuild-sdks": { - "Purview.DotNetProjectSdk": "1.0.0-prerelease.45", + "Purview.DotNetProjectSdk": "1.0.0-prerelease.46", "Aspire.AppHost.Sdk": "13.2.0" }, "test": { diff --git a/samples/SampleApp.Net48/Directory.Build.props b/samples/SampleApp.Net48/Directory.Build.props index abbf5ef2..49f0130f 100644 --- a/samples/SampleApp.Net48/Directory.Build.props +++ b/samples/SampleApp.Net48/Directory.Build.props @@ -18,11 +18,11 @@ CA1812: WeatherService is instantiated via DI, not directly. CA5394: Random is fine for simulation / demo purposes. CS1574: cref resolution differences in net48 XML doc comments (in generated code). - CS8618/CS8625: the generator's injected attribute templates initialize non-nullable - members with null when compiled under net8+/net10-style nullable settings. + CS8618: the sample's WeatherForecast DTO declares a non-nullable Summary that is + populated before use (sample-code relaxation; not from generated code). TSG3022: Activity? vs Activity — net48 disables nullable, so Activity is used here. --> - $(NoWarn);CA1031;CA1303;CA1515;CA1812;CA2007;CA2201;CA5394;CS1574;CS8618;CS8625;TSG3022; + $(NoWarn);CA1031;CA1303;CA1515;CA1812;CA2007;CA2201;CA5394;CS1574;CS8618;TSG3022; diff --git a/samples/SampleApp/Directory.Build.props b/samples/SampleApp/Directory.Build.props index 9b7237b2..4bd2c4a8 100644 --- a/samples/SampleApp/Directory.Build.props +++ b/samples/SampleApp/Directory.Build.props @@ -13,11 +13,9 @@ CA1724: sample type names intentionally shadow well-known namespaces. CA2007: fire-and-forget async calls are fine for the sample workflow. CA2201: simulated failures raise generic exception types. - CS8618/CS8625: the generator's injected attribute templates (with #nullable enable) - initialize non-nullable members with null under net8+/net10. TSG2008: the sample intentionally demonstrates a large ExpandEnumerable limit; source-generator diagnostics are not suppressible with #pragma, so it's NoWarn'd. --> - $(NoWarn);CA1031;CA1515;CA1724;CA2007;CA2201;CS8618;CS8625;TSG2008; + $(NoWarn);CA1031;CA1515;CA1724;CA2007;CA2201;TSG2008; diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 597b5004..a3bbd31a 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -1,17 +1,3 @@ - - - - - <_ReferenceCopyLocalPaths Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference')->WithMetadataValue('PrivateAssets', 'All'))" /> - - - - - - diff --git a/src/src/SourceGenerator.Refactorings/SourceGenerator.Refactorings.csproj b/src/src/SourceGenerator.Refactorings/SourceGenerator.Refactorings.csproj index 51f68849..fb9caf5d 100644 --- a/src/src/SourceGenerator.Refactorings/SourceGenerator.Refactorings.csproj +++ b/src/src/SourceGenerator.Refactorings/SourceGenerator.Refactorings.csproj @@ -1,6 +1,5 @@ - netstandard2.0 true false true @@ -8,8 +7,8 @@ Visual Studio code refactorings for the Purview Telemetry source generator. - - + + diff --git a/src/src/SourceGenerator/Emitters/GeneratedTypesEmitter.cs b/src/src/SourceGenerator/Emitters/GeneratedTypesEmitter.cs index f21a1308..00100b9e 100644 --- a/src/src/SourceGenerator/Emitters/GeneratedTypesEmitter.cs +++ b/src/src/SourceGenerator/Emitters/GeneratedTypesEmitter.cs @@ -72,7 +72,7 @@ public static void EmitAll(IncrementalGeneratorPostInitializationContext context foreach (var type in TypeLibrary.GetAllGeneratedTypes()) { CodeWriter writer = new(settings); - writer.WriteAutoGeneratedHeader(); + WriteMarkerFileHeader(writer); Emit(writer, type); @@ -80,6 +80,24 @@ public static void EmitAll(IncrementalGeneratorPostInitializationContext context } } + /// + /// Writes the header for marker-attribute files. Nullable annotations are only enabled on modern + /// targets — the injected templates declare plain string members for + /// NET48_OR_GREATER/PURVIEW_TELEMETRY_NON_NULLABLE consumers — and CS8625 is + /// suppressed because optional string parameters intentionally default to . + /// + static void WriteMarkerFileHeader(CodeWriter writer) + { + writer.WriteAutoGeneratedHeader(nullableDirective: NullableDirectiveMode.Disable); + writer + .WriteLine("#if !NET48_OR_GREATER && !PURVIEW_TELEMETRY_NON_NULLABLE") + .WriteLine("#nullable enable") + .WriteLine("#endif") + .NewLine() + .WriteLine("#pragma warning disable CS8625") + .NewLine(); + } + static void Emit(CodeWriter writer, TypeIdentity type) { if (!Emitters.TryGetValue(type, out var emit)) @@ -828,7 +846,7 @@ static void WriteLogAttribute(CodeWriter writer, TypeIdentity type) ); WriteNullableStringProperty(body, "MessageTemplate"); WritePublicProperty(body, "EventId", PurviewTypeLibrary.System.Int32.MakeNullable(writer)); - WritePublicProperty(body, "Name", PurviewTypeLibrary.System.String.MakeNullable(writer)); + WriteNullableStringProperty(body, "Name"); WritePublicProperty(body, "GenerationMode", TypeLibrary.Logging.LoggerGenerationMode.AsTypeReference()); }, wrapInExcludeLoggingGuard: true @@ -1220,10 +1238,10 @@ static void WriteTargetsEnum(CodeWriter writer, TypeIdentity type) { Attributes = [new AttributeDeclarationOptions(new TypeIdentity("FlagsAttribute", "System"))], }, - new("None", 0), - new("Activities", 1), - new("Logging", 2), - new("Metrics", 4), + new("None", 0, "No telemetry targets are excluded."), + new("Activities", 1, "Excludes activity (tracing) targets."), + new("Logging", 2, "Excludes logging targets."), + new("Metrics", 4, "Excludes metrics targets."), new("All", "Activities | Logging | Metrics") ); } @@ -1233,7 +1251,11 @@ static void WriteNamingConventionEnum(CodeWriter writer, TypeIdentity type) writer .WriteFileScopedNamespace(TypeLibrary.PurviewTelemetryNamespace) .XmlSummary("Determines the naming convention used for generated telemetry names.") - .WriteEnum(new(type.Name, TypeDeclarationAccessibility.Public), new("Legacy", 0), new("OpenTelemetry", 1)); + .WriteEnum( + new(type.Name, TypeDeclarationAccessibility.Public), + new("Legacy", 0, "Uses the legacy naming convention for generated telemetry names."), + new("OpenTelemetry", 1, "Uses the OpenTelemetry naming convention for generated telemetry names.") + ); } static void WriteLogPrefixTypeEnum(CodeWriter writer, TypeIdentity type) @@ -1244,11 +1266,11 @@ static void WriteLogPrefixTypeEnum(CodeWriter writer, TypeIdentity type) .XmlSummary("Determines the mode used to generate or override the prefix for the log entry.") .WriteEnum( new(type.Name, TypeDeclarationAccessibility.Public), - new("Default", 0), - new("Interface", 1), - new("Class", 2), - new("Custom", 3), - new("TrimmedClassName", 4) + new("Default", 0, "Uses the default log prefix."), + new("Interface", 1, "Uses the interface name as the log prefix."), + new("Class", 2, "Uses the class name as the log prefix."), + new("Custom", 3, "Uses a custom log prefix."), + new("TrimmedClassName", 4, "Uses the trimmed class name as the log prefix.") ); writer.WriteLine("#endif"); @@ -1261,7 +1283,12 @@ static void WriteLoggerGenerationModeEnum(CodeWriter writer, TypeIdentity type) writer .WriteFileScopedNamespace(TypeLibrary.PurviewTelemetryNamespace) .XmlSummary("Controls the generation mode used for log methods.") - .WriteEnum(new(type.Name, TypeDeclarationAccessibility.Public), new("Auto", 0), new("V1", 1), new("V2", 2)); + .WriteEnum( + new(type.Name, TypeDeclarationAccessibility.Public), + new("Auto", 0, "Automatically selects the log generation mode."), + new("V1", 1, "Uses the first-generation log implementation."), + new("V2", 2, "Uses the second-generation log implementation.") + ); writer.WriteLine("#endif"); } @@ -1271,6 +1298,10 @@ static void WriteMeterNameGenerationTypeEnum(CodeWriter writer, TypeIdentity typ writer .WriteFileScopedNamespace(TypeLibrary.PurviewTelemetryNamespace) .XmlSummary("Determines how meter names are generated when not explicitly specified.") - .WriteEnum(new(type.Name, TypeDeclarationAccessibility.Public), new("OpenTelemetry", 0), new("DotNet", 1)); + .WriteEnum( + new(type.Name, TypeDeclarationAccessibility.Public), + new("OpenTelemetry", 0, "Generates meter names using the OpenTelemetry convention."), + new("DotNet", 1, "Generates meter names using the .NET convention.") + ); } } diff --git a/src/src/SourceGenerator/SourceGenerator.csproj b/src/src/SourceGenerator/SourceGenerator.csproj index 1a622775..4236cae9 100644 --- a/src/src/SourceGenerator/SourceGenerator.csproj +++ b/src/src/SourceGenerator/SourceGenerator.csproj @@ -1,6 +1,5 @@  - netstandard2.0 true true 4.14.0 1.65.51 - 1.0.0-prerelease.29 + 1.0.0-prerelease.30 3.2.8 diff --git a/global.json b/global.json index cf807fda..b38e7fec 100644 --- a/global.json +++ b/global.json @@ -5,8 +5,7 @@ "allowPrerelease": false }, "msbuild-sdks": { - "Purview.DotNetProjectSdk": "1.0.0-prerelease.46", - "Aspire.AppHost.Sdk": "13.2.0" + "Purview.DotNetProjectSdk": "1.0.0-prerelease.46" }, "test": { "runner": "Microsoft.Testing.Platform" diff --git a/package.json b/package.json index dad549c2..8116fefc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "purview-telemetry-sourcegenerator", - "version": "5.0.0-prerelease.2", + "version": "5.0.0-prerelease.3", "description": "Generates [`ActivitySource`](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.activitysource), [`ILogger`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.ilogger), and [`Metrics`](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.metrics) based on interface methods.", "readme": "README.md", "repository": { diff --git a/src/src/SourceGenerator/Emitters/GeneratedTypesEmitter.cs b/src/src/SourceGenerator/Emitters/GeneratedTypesEmitter.cs index 00100b9e..3469e521 100644 --- a/src/src/SourceGenerator/Emitters/GeneratedTypesEmitter.cs +++ b/src/src/SourceGenerator/Emitters/GeneratedTypesEmitter.cs @@ -18,8 +18,8 @@ static class GeneratedTypesEmitter [TypeLibrary.TelemetryShared.ExcludeAttribute] = (writer, type) => WriteSimpleAttribute(writer, type, AttributeTargets.Method, includeSuppressMessage: false), [TypeLibrary.TelemetryShared.TelemetryGenerationAttribute] = WriteTelemetryGenerationAttribute, - [TypeLibrary.TelemetryShared.TargetsEnum] = WriteTargetsEnum, - [TypeLibrary.TelemetryShared.NamingConventionEnum] = WriteNamingConventionEnum, + [TypeLibrary.TelemetryShared.Targets] = WriteTargetsEnum, + [TypeLibrary.TelemetryShared.NamingConvention] = WriteNamingConventionEnum, [TypeLibrary.TelemetryShared.ExcludeTargetsAttribute] = WriteExcludeTargetsAttribute, // Activities [TypeLibrary.Activities.BaggageAttribute] = WriteTagLikeAttribute, @@ -322,7 +322,7 @@ static void WriteTagLikeAttribute(CodeWriter writer, TypeIdentity type) static void WriteTelemetryGenerationAttribute(CodeWriter writer, TypeIdentity type) { - var namingConvention = TypeLibrary.TelemetryShared.NamingConventionEnum; + var namingConvention = TypeLibrary.TelemetryShared.NamingConvention; EmitAttribute( writer, @@ -421,7 +421,7 @@ static void WriteTelemetryGenerationAttribute(CodeWriter writer, TypeIdentity ty static void WriteExcludeTargetsAttribute(CodeWriter writer, TypeIdentity type) { - var targets = TypeLibrary.TelemetryShared.TargetsEnum; + var targets = TypeLibrary.TelemetryShared.Targets; EmitAttribute( writer, @@ -1236,7 +1236,7 @@ static void WriteTargetsEnum(CodeWriter writer, TypeIdentity type) .WriteEnum( new(type.Name, TypeDeclarationAccessibility.Public) { - Attributes = [new AttributeDeclarationOptions(new TypeIdentity("FlagsAttribute", "System"))], + Attributes = [new(new TypeIdentity("FlagsAttribute", "System"))], }, new("None", 0, "No telemetry targets are excluded."), new("Activities", 1, "Excludes activity (tracing) targets."), diff --git a/src/src/SourceGenerator/Helpers/TypeLibrary.cs b/src/src/SourceGenerator/Helpers/TypeLibrary.cs index 29e33175..233be40b 100644 --- a/src/src/SourceGenerator/Helpers/TypeLibrary.cs +++ b/src/src/SourceGenerator/Helpers/TypeLibrary.cs @@ -321,11 +321,8 @@ public static class TelemetryShared nameof(TelemetryGenerationAttribute), PurviewTelemetryNamespace ); - public static readonly TypeIdentity TargetsEnum = new(nameof(TargetsEnum), PurviewTelemetryNamespace); - public static readonly TypeIdentity NamingConventionEnum = new( - nameof(NamingConventionEnum), - PurviewTelemetryNamespace - ); + public static readonly TypeIdentity Targets = new(nameof(Targets), PurviewTelemetryNamespace); + public static readonly TypeIdentity NamingConvention = new(nameof(NamingConvention), PurviewTelemetryNamespace); public static readonly TypeIdentity ExcludeTargetsAttribute = new( nameof(ExcludeTargetsAttribute), PurviewTelemetryNamespace @@ -336,8 +333,8 @@ public static ImmutableArray GetGeneratedTypes() => TagAttribute, ExcludeAttribute, TelemetryGenerationAttribute, - TargetsEnum, - NamingConventionEnum, + Targets, + NamingConvention, ExcludeTargetsAttribute, ]; } diff --git a/src/src/SourceGenerator/Sdk/assets/images/purview-logo.jpg b/src/src/SourceGenerator/Sdk/assets/images/purview-logo.jpg deleted file mode 120000 index 452118b3..00000000 --- a/src/src/SourceGenerator/Sdk/assets/images/purview-logo.jpg +++ /dev/null @@ -1 +0,0 @@ -../../../../../../assets/images/purview-logo.jpg \ No newline at end of file diff --git a/src/src/SourceGenerator/SourceGenerator.csproj b/src/src/SourceGenerator/SourceGenerator.csproj index 4236cae9..5578ba7d 100644 --- a/src/src/SourceGenerator/SourceGenerator.csproj +++ b/src/src/SourceGenerator/SourceGenerator.csproj @@ -2,10 +2,18 @@ true true - - + + true + false + true + snupkg + + false + $(NoWarn);IDE0005;EnableGenerateDocumentationFile; Purview Telemetry Source Generator .NET Source Generator for interface-based telemetry generating tracing, logs, and metrics. @@ -39,8 +47,8 @@ into analyzers/dotnet/cs; only the refactorings assembly is added here. --> - - + Excludes logging targets.") From bd5df3ddfaead9bda7b57411fc54a209525b8476 Mon Sep 17 00:00:00 2001 From: Kieron Lanning Date: Thu, 3 Sep 2026 16:08:39 +0100 Subject: [PATCH 3/3] chore: after removing incorrectly added skills - added gitignore for them --- .agents/skills/sdk-configuration-reference/.gitignore | 8 ++++++++ .../skills/sdk-project-behavior-and-detection/.gitignore | 8 ++++++++ 2 files changed, 16 insertions(+) create mode 100644 .agents/skills/sdk-configuration-reference/.gitignore create mode 100644 .agents/skills/sdk-project-behavior-and-detection/.gitignore diff --git a/.agents/skills/sdk-configuration-reference/.gitignore b/.agents/skills/sdk-configuration-reference/.gitignore new file mode 100644 index 00000000..27997545 --- /dev/null +++ b/.agents/skills/sdk-configuration-reference/.gitignore @@ -0,0 +1,8 @@ +# Ignore all files +* + +# Don't ignore directories, so Git can traverse them +!*/ + +# Keep this file +!.gitignore \ No newline at end of file diff --git a/.agents/skills/sdk-project-behavior-and-detection/.gitignore b/.agents/skills/sdk-project-behavior-and-detection/.gitignore new file mode 100644 index 00000000..27997545 --- /dev/null +++ b/.agents/skills/sdk-project-behavior-and-detection/.gitignore @@ -0,0 +1,8 @@ +# Ignore all files +* + +# Don't ignore directories, so Git can traverse them +!*/ + +# Keep this file +!.gitignore \ No newline at end of file