From 35c90edce533bd30d4be969aa29be2b99a96aa8e Mon Sep 17 00:00:00 2001 From: Korolev Dmitry Date: Thu, 22 May 2025 17:06:24 +0000 Subject: [PATCH 1/6] report warning if generating serializer for model from ref assembly --- src/Orleans.CodeGenerator/CodeGenerator.cs | 5 +++ ...anNotGenerateImplicitFieldIdsDiagnostic.cs | 2 +- .../Diagnostics/DiagnosticRuleId.cs | 16 +++++++ ...ttribute_NoDeclaringAssembly_Diagnostic.cs | 2 +- .../InaccessibleSerializableTypeDiagnostic.cs | 2 +- .../InaccessibleSetterDiagnostic.cs | 2 +- ...ctProxyBaseClassSpecificationDiagnostic.cs | 2 +- .../InvalidRpcMethodReturnTypeDiagnostic.cs | 2 +- ...leCancellationTokenParametersDiagnostic.cs | 2 +- ...ssemblyWithGenerateSerializerDiagnostic.cs | 24 +++++++++++ .../RpcInterfacePropertyDiagnostic.cs | 2 +- ...andledCodeGenerationExceptionDiagnostic.cs | 2 +- .../OrleansSourceGeneratorTests.cs | 43 ++++++++++++++++++- 13 files changed, 96 insertions(+), 10 deletions(-) create mode 100644 src/Orleans.CodeGenerator/Diagnostics/DiagnosticRuleId.cs create mode 100644 src/Orleans.CodeGenerator/Diagnostics/ReferenceAssemblyWithGenerateSerializerDiagnostic.cs diff --git a/src/Orleans.CodeGenerator/CodeGenerator.cs b/src/Orleans.CodeGenerator/CodeGenerator.cs index d499883eccc..27c7309e79a 100644 --- a/src/Orleans.CodeGenerator/CodeGenerator.cs +++ b/src/Orleans.CodeGenerator/CodeGenerator.cs @@ -126,6 +126,11 @@ public CompilationUnitSyntax GenerateCode(CancellationToken cancellationToken) } else if (ShouldGenerateSerializer(symbol)) { + // Emit a warning if the type is from a reference assembly + if (symbol.ContainingAssembly.IsReferenceAssembly) + { + throw new OrleansGeneratorDiagnosticAnalysisException(ReferenceAssemblyWithGenerateSerializerDiagnostic.CreateDiagnostic(symbol)); + } if (!Compilation.IsSymbolAccessibleWithin(symbol, Compilation.Assembly)) { throw new OrleansGeneratorDiagnosticAnalysisException(InaccessibleSerializableTypeDiagnostic.CreateDiagnostic(symbol)); diff --git a/src/Orleans.CodeGenerator/Diagnostics/CanNotGenerateImplicitFieldIdsDiagnostic.cs b/src/Orleans.CodeGenerator/Diagnostics/CanNotGenerateImplicitFieldIdsDiagnostic.cs index b85e89dab2b..377fdf8cd5a 100644 --- a/src/Orleans.CodeGenerator/Diagnostics/CanNotGenerateImplicitFieldIdsDiagnostic.cs +++ b/src/Orleans.CodeGenerator/Diagnostics/CanNotGenerateImplicitFieldIdsDiagnostic.cs @@ -5,7 +5,7 @@ namespace Orleans.CodeGenerator.Diagnostics; public static class CanNotGenerateImplicitFieldIdsDiagnostic { - public const string DiagnosticId = "ORLEANS0106"; + public const string DiagnosticId = DiagnosticRuleId.CanNotGenerateImplicitFieldIds; public const string Title = "Implicit field identifiers could not be generated"; public const string MessageFormat = "Could not generate implicit field identifiers for the type {0}: {reason}"; public const string Category = "Usage"; diff --git a/src/Orleans.CodeGenerator/Diagnostics/DiagnosticRuleId.cs b/src/Orleans.CodeGenerator/Diagnostics/DiagnosticRuleId.cs new file mode 100644 index 00000000000..d415880b7c9 --- /dev/null +++ b/src/Orleans.CodeGenerator/Diagnostics/DiagnosticRuleId.cs @@ -0,0 +1,16 @@ +// Centralized diagnostic rule IDs for Orleans.CodeGenerator +namespace Orleans.CodeGenerator.Diagnostics; + +internal static class DiagnosticRuleId +{ + public const string InaccessibleSetter = "ORLEANS0101"; + public const string InvalidRpcMethodReturnType = "ORLEANS0102"; + public const string UnhandledCodeGenerationException = "ORLEANS0103"; + public const string IncorrectProxyBaseClassSpecification = "ORLEANS0104"; + public const string RpcInterfaceProperty = "ORLEANS0105"; + public const string CanNotGenerateImplicitFieldIds = "ORLEANS0106"; + public const string InaccessibleSerializableType = "ORLEANS0107"; + public const string GenerateCodeForDeclaringAssemblyAttribute_NoDeclaringAssembly = "ORLEANS0108"; + public const string MultipleCancellationTokenParameters = "ORLEANS0109"; + public const string ReferenceAssemblyWithGenerateSerializer = "ORLEANS0110"; +} diff --git a/src/Orleans.CodeGenerator/Diagnostics/GenerateCodeForDeclaringAssemblyAttribute_NoDeclaringAssembly_Diagnostic.cs b/src/Orleans.CodeGenerator/Diagnostics/GenerateCodeForDeclaringAssemblyAttribute_NoDeclaringAssembly_Diagnostic.cs index a07cb96b5d1..b3a0c652e60 100644 --- a/src/Orleans.CodeGenerator/Diagnostics/GenerateCodeForDeclaringAssemblyAttribute_NoDeclaringAssembly_Diagnostic.cs +++ b/src/Orleans.CodeGenerator/Diagnostics/GenerateCodeForDeclaringAssemblyAttribute_NoDeclaringAssembly_Diagnostic.cs @@ -4,7 +4,7 @@ namespace Orleans.CodeGenerator.Diagnostics; public static class GenerateCodeForDeclaringAssemblyAttribute_NoDeclaringAssembly_Diagnostic { - public const string DiagnosticId = "ORLEANS0108"; + public const string DiagnosticId = DiagnosticRuleId.GenerateCodeForDeclaringAssemblyAttribute_NoDeclaringAssembly; public const string Title = "Types passed to GenerateCodeForDeclaringAssemblyAttribute must have a declaring assembly"; public const string MessageFormat = "The type {0} provided as an argument to {1} does not have a declaring assembly"; public const string Category = "Usage"; diff --git a/src/Orleans.CodeGenerator/Diagnostics/InaccessibleSerializableTypeDiagnostic.cs b/src/Orleans.CodeGenerator/Diagnostics/InaccessibleSerializableTypeDiagnostic.cs index 4f3226abb2e..97c77d9e35d 100644 --- a/src/Orleans.CodeGenerator/Diagnostics/InaccessibleSerializableTypeDiagnostic.cs +++ b/src/Orleans.CodeGenerator/Diagnostics/InaccessibleSerializableTypeDiagnostic.cs @@ -5,7 +5,7 @@ namespace Orleans.CodeGenerator.Diagnostics; public static class InaccessibleSerializableTypeDiagnostic { - public const string RuleId = "ORLEANS0107"; + public const string RuleId = DiagnosticRuleId.InaccessibleSerializableType; public const string Title = "Serializable type must be accessible from generated code"; public const string MessageFormat = "The type {0} is marked as being serializable but it is inaccessible from generated code"; public const string Descsription = "Source generation requires that all types marked as serializable are accessible from generated code. Either make the type public or make it internal and ensure that internals are visible to the generated code."; diff --git a/src/Orleans.CodeGenerator/Diagnostics/InaccessibleSetterDiagnostic.cs b/src/Orleans.CodeGenerator/Diagnostics/InaccessibleSetterDiagnostic.cs index 1c3b670cf83..214a40f2e04 100644 --- a/src/Orleans.CodeGenerator/Diagnostics/InaccessibleSetterDiagnostic.cs +++ b/src/Orleans.CodeGenerator/Diagnostics/InaccessibleSetterDiagnostic.cs @@ -4,7 +4,7 @@ namespace Orleans.CodeGenerator.Diagnostics; public static class InaccessibleSetterDiagnostic { - public const string RuleId = "ORLEANS0101"; + public const string RuleId = DiagnosticRuleId.InaccessibleSetter; private const string Category = "Usage"; private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.InaccessibleSetterTitle), Resources.ResourceManager, typeof(Resources)); private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.InaccessibleSetterMessageFormat), Resources.ResourceManager, typeof(Resources)); diff --git a/src/Orleans.CodeGenerator/Diagnostics/IncorrectProxyBaseClassSpecificationDiagnostic.cs b/src/Orleans.CodeGenerator/Diagnostics/IncorrectProxyBaseClassSpecificationDiagnostic.cs index bf3a3c6b49f..feaa47c94d5 100644 --- a/src/Orleans.CodeGenerator/Diagnostics/IncorrectProxyBaseClassSpecificationDiagnostic.cs +++ b/src/Orleans.CodeGenerator/Diagnostics/IncorrectProxyBaseClassSpecificationDiagnostic.cs @@ -4,7 +4,7 @@ namespace Orleans.CodeGenerator.Diagnostics; public static class IncorrectProxyBaseClassSpecificationDiagnostic { - public const string RuleId = "ORLEANS0104"; + public const string RuleId = DiagnosticRuleId.IncorrectProxyBaseClassSpecification; private const string Category = "Usage"; private static readonly LocalizableString Title = "The proxy base class specified is not a valid proxy base class"; private static readonly LocalizableString MessageFormat = "Proxy base class {0} does not conform to requirements: {1}"; diff --git a/src/Orleans.CodeGenerator/Diagnostics/InvalidRpcMethodReturnTypeDiagnostic.cs b/src/Orleans.CodeGenerator/Diagnostics/InvalidRpcMethodReturnTypeDiagnostic.cs index 6a752a2b4fd..61b693a26b1 100644 --- a/src/Orleans.CodeGenerator/Diagnostics/InvalidRpcMethodReturnTypeDiagnostic.cs +++ b/src/Orleans.CodeGenerator/Diagnostics/InvalidRpcMethodReturnTypeDiagnostic.cs @@ -5,7 +5,7 @@ namespace Orleans.CodeGenerator.Diagnostics; public static class InvalidRpcMethodReturnTypeDiagnostic { - public const string RuleId = "ORLEANS0102"; + public const string RuleId = DiagnosticRuleId.InvalidRpcMethodReturnType; private const string Category = "Usage"; private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.InvalidRpcMethodReturnTypeTitle), Resources.ResourceManager, typeof(Resources)); private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.InvalidRpcMethodReturnTypeMessageFormat), Resources.ResourceManager, typeof(Resources)); diff --git a/src/Orleans.CodeGenerator/Diagnostics/MultipleCancellationTokenParametersDiagnostic.cs b/src/Orleans.CodeGenerator/Diagnostics/MultipleCancellationTokenParametersDiagnostic.cs index 526c74fc4eb..0a18a7f18ce 100644 --- a/src/Orleans.CodeGenerator/Diagnostics/MultipleCancellationTokenParametersDiagnostic.cs +++ b/src/Orleans.CodeGenerator/Diagnostics/MultipleCancellationTokenParametersDiagnostic.cs @@ -5,7 +5,7 @@ namespace Orleans.CodeGenerator.Diagnostics; public static class MultipleCancellationTokenParametersDiagnostic { - public const string DiagnosticId = "ORLEANS0109"; + public const string DiagnosticId = DiagnosticRuleId.MultipleCancellationTokenParameters; public const string Title = "Grain method has multiple parameters of type CancellationToken"; public const string MessageFormat = "The type {0} contains method {1} which has multiple CancellationToken parameters. Only a single CancellationToken parameter is supported."; public const string Category = "Usage"; diff --git a/src/Orleans.CodeGenerator/Diagnostics/ReferenceAssemblyWithGenerateSerializerDiagnostic.cs b/src/Orleans.CodeGenerator/Diagnostics/ReferenceAssemblyWithGenerateSerializerDiagnostic.cs new file mode 100644 index 00000000000..0cb50dd84f1 --- /dev/null +++ b/src/Orleans.CodeGenerator/Diagnostics/ReferenceAssemblyWithGenerateSerializerDiagnostic.cs @@ -0,0 +1,24 @@ +using System.Linq; +using Microsoft.CodeAnalysis; + +namespace Orleans.CodeGenerator.Diagnostics; + +public static class ReferenceAssemblyWithGenerateSerializerDiagnostic +{ + public const string RuleId = DiagnosticRuleId.ReferenceAssemblyWithGenerateSerializer; + public const string Title = "[GenerateSerializer] used in a reference assembly"; + public const string MessageFormat = """ + The type {0} is marked with [GenerateSerializer] in a reference assembly. + Serialization is likely to fail. Options: + (1) Enable code generation on the target project directly; + (2) Disable reference assemblies using false in the codegen project; + (3) Use a different serializer or create surrogates. See https://aka.ms/orleans-serialization for details. + """; + public const string Description = "[GenerateSerializer] should not be used in reference assemblies. See Orleans documentation for supported patterns."; + public const string Category = "Usage"; + + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( + RuleId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description); + + internal static Diagnostic CreateDiagnostic(ISymbol symbol) => Diagnostic.Create(Rule, symbol.Locations.First(), symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); +} diff --git a/src/Orleans.CodeGenerator/Diagnostics/RpcInterfacePropertyDiagnostic.cs b/src/Orleans.CodeGenerator/Diagnostics/RpcInterfacePropertyDiagnostic.cs index 15a7e8efa2b..0cd35584d0e 100644 --- a/src/Orleans.CodeGenerator/Diagnostics/RpcInterfacePropertyDiagnostic.cs +++ b/src/Orleans.CodeGenerator/Diagnostics/RpcInterfacePropertyDiagnostic.cs @@ -5,7 +5,7 @@ namespace Orleans.CodeGenerator.Diagnostics; public static class RpcInterfacePropertyDiagnostic { - public const string DiagnosticId = "ORLEANS0105"; + public const string DiagnosticId = DiagnosticRuleId.RpcInterfaceProperty; public const string Title = "RPC interfaces must not contain properties"; public const string MessageFormat = "The interface {0} contains a property {1}. RPC interfaces must not contain properties."; public const string Category = "Usage"; diff --git a/src/Orleans.CodeGenerator/Diagnostics/UnhandledCodeGenerationExceptionDiagnostic.cs b/src/Orleans.CodeGenerator/Diagnostics/UnhandledCodeGenerationExceptionDiagnostic.cs index c5b3d9107f6..c8ea4fa0228 100644 --- a/src/Orleans.CodeGenerator/Diagnostics/UnhandledCodeGenerationExceptionDiagnostic.cs +++ b/src/Orleans.CodeGenerator/Diagnostics/UnhandledCodeGenerationExceptionDiagnostic.cs @@ -5,7 +5,7 @@ namespace Orleans.CodeGenerator.Diagnostics; public static class UnhandledCodeGenerationExceptionDiagnostic { - public const string RuleId = "ORLEANS0103"; + public const string RuleId = DiagnosticRuleId.UnhandledCodeGenerationException; private const string Category = "Usage"; private static readonly LocalizableString Title = "An unhandled source generation exception occurred"; private static readonly LocalizableString MessageFormat = "An unhandled exception occurred while generating source for your project: {0} {1}"; diff --git a/test/Orleans.CodeGenerator.Tests/OrleansSourceGeneratorTests.cs b/test/Orleans.CodeGenerator.Tests/OrleansSourceGeneratorTests.cs index 2b97fa15452..db674c69268 100644 --- a/test/Orleans.CodeGenerator.Tests/OrleansSourceGeneratorTests.cs +++ b/test/Orleans.CodeGenerator.Tests/OrleansSourceGeneratorTests.cs @@ -21,7 +21,7 @@ public class DemoData public string Value { get; set; } = string.Empty; }"); -[Fact] + [Fact] public Task TestBasicClassWithFields() => AssertSuccessfulSourceGeneration( @"using Orleans; @@ -503,6 +503,47 @@ public Task ProcessData(int inputInt, string inputString, ComplexDa } }"); + [Fact] + public async Task EmitsWarningForGenerateSerializerInReferenceAssembly() + { + var code = """ + using Orleans; + + namespace TestProject; + + [GenerateSerializer] + public class RefAsmType + { + [Id(0)] + public string Value { get; set; } = string.Empty; + } + """; + + // The ReferenceAssemblyAttribute marks the assembly as a reference assembly. + // This triggers the Orleans code generator's logic to emit a diagnostic if [GenerateSerializer] is used in such an assembly. + var compilation = await CreateCompilation(code, "TestProject"); + var referenceAssemblyAttribute = SyntaxFactory.Attribute(SyntaxFactory.ParseName("System.Runtime.CompilerServices.ReferenceAssemblyAttribute")); + var attrList = SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(referenceAssemblyAttribute)); + var assemblyAttr = SyntaxFactory.AttributeList( + SyntaxFactory.SingletonSeparatedList(referenceAssemblyAttribute)) + .WithTarget(SyntaxFactory.AttributeTargetSpecifier(SyntaxFactory.Token(SyntaxKind.AssemblyKeyword))); + var root = (CSharpSyntaxNode)compilation.SyntaxTrees[0].GetRoot(); + var newRoot = root.AddAttributeLists(assemblyAttr); + var newTree = compilation.SyntaxTrees[0].WithRootAndOptions(newRoot, compilation.SyntaxTrees[0].Options); + + // leave only syntaxTree with the ReferenceAssemblyAttribute + compilation = compilation.RemoveSyntaxTrees(compilation.SyntaxTrees[0]).AddSyntaxTrees(newTree); + + var generator = new OrleansSerializationSourceGenerator(); + GeneratorDriver driver = CSharpGeneratorDriver.Create( + generators: [generator], + driverOptions: new GeneratorDriverOptions(default)); + driver = driver.RunGenerators(compilation); + + var result = driver.GetRunResult().Results.Single(); + Assert.Contains(result.Diagnostics, d => d.Id == "ORLEANS0110"); + } + private static async Task AssertSuccessfulSourceGeneration(string code) { var projectName = "TestProject"; From 702420b7b7950799619a6bf1e04b17870ad3fb0a Mon Sep 17 00:00:00 2001 From: Dmitrii Korolev Date: Thu, 22 May 2025 20:00:20 +0200 Subject: [PATCH 2/6] fix build errors --- src/Orleans.CodeGenerator/CodeGenerator.cs | 7 +++++-- src/Orleans.CodeGenerator/Orleans.CodeGenerator.csproj | 5 ++++- .../OrleansSourceGeneratorTests.cs | 8 +++++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Orleans.CodeGenerator/CodeGenerator.cs b/src/Orleans.CodeGenerator/CodeGenerator.cs index 27c7309e79a..ce73530022e 100644 --- a/src/Orleans.CodeGenerator/CodeGenerator.cs +++ b/src/Orleans.CodeGenerator/CodeGenerator.cs @@ -99,6 +99,8 @@ public CompilationUnitSyntax GenerateCode(CancellationToken cancellationToken) { foreach (var symbol in asm.GetDeclaredTypes()) { + var containingAssemblyAttributes = symbol.ContainingAssembly.GetAttributes(); + if (GetWellKnownTypeId(symbol) is uint wellKnownTypeId) { MetadataModel.WellKnownTypeIds.Add((symbol.ToOpenTypeSyntax(), wellKnownTypeId)); @@ -126,11 +128,12 @@ public CompilationUnitSyntax GenerateCode(CancellationToken cancellationToken) } else if (ShouldGenerateSerializer(symbol)) { - // Emit a warning if the type is from a reference assembly - if (symbol.ContainingAssembly.IsReferenceAssembly) + if (containingAssemblyAttributes.Any(attr => attr.AttributeClass?.Name == "ReferenceAssemblyAttribute")) { + // not ALWAYS will be properly processed, therefore emit a warning throw new OrleansGeneratorDiagnosticAnalysisException(ReferenceAssemblyWithGenerateSerializerDiagnostic.CreateDiagnostic(symbol)); } + if (!Compilation.IsSymbolAccessibleWithin(symbol, Compilation.Assembly)) { throw new OrleansGeneratorDiagnosticAnalysisException(InaccessibleSerializableTypeDiagnostic.CreateDiagnostic(symbol)); diff --git a/src/Orleans.CodeGenerator/Orleans.CodeGenerator.csproj b/src/Orleans.CodeGenerator/Orleans.CodeGenerator.csproj index fea58530d0a..ef19c14209f 100644 --- a/src/Orleans.CodeGenerator/Orleans.CodeGenerator.csproj +++ b/src/Orleans.CodeGenerator/Orleans.CodeGenerator.csproj @@ -1,4 +1,4 @@ - + Microsoft.Orleans.CodeGenerator @@ -65,4 +65,7 @@ + + + diff --git a/test/Orleans.CodeGenerator.Tests/OrleansSourceGeneratorTests.cs b/test/Orleans.CodeGenerator.Tests/OrleansSourceGeneratorTests.cs index db674c69268..e49d21f94bc 100644 --- a/test/Orleans.CodeGenerator.Tests/OrleansSourceGeneratorTests.cs +++ b/test/Orleans.CodeGenerator.Tests/OrleansSourceGeneratorTests.cs @@ -1,7 +1,9 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Testing; using Microsoft.Extensions.DependencyInjection; +using Orleans.CodeGenerator.Diagnostics; using Orleans.Serialization; namespace Orleans.CodeGenerator.Tests; @@ -523,12 +525,12 @@ public class RefAsmType // This triggers the Orleans code generator's logic to emit a diagnostic if [GenerateSerializer] is used in such an assembly. var compilation = await CreateCompilation(code, "TestProject"); var referenceAssemblyAttribute = SyntaxFactory.Attribute(SyntaxFactory.ParseName("System.Runtime.CompilerServices.ReferenceAssemblyAttribute")); - var attrList = SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(referenceAssemblyAttribute)); + var attrList = SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(referenceAssemblyAttribute)); var assemblyAttr = SyntaxFactory.AttributeList( SyntaxFactory.SingletonSeparatedList(referenceAssemblyAttribute)) .WithTarget(SyntaxFactory.AttributeTargetSpecifier(SyntaxFactory.Token(SyntaxKind.AssemblyKeyword))); var root = (CSharpSyntaxNode)compilation.SyntaxTrees[0].GetRoot(); - var newRoot = root.AddAttributeLists(assemblyAttr); + var newRoot = ((CompilationUnitSyntax)root).AddAttributeLists(assemblyAttr); var newTree = compilation.SyntaxTrees[0].WithRootAndOptions(newRoot, compilation.SyntaxTrees[0].Options); // leave only syntaxTree with the ReferenceAssemblyAttribute @@ -541,7 +543,7 @@ public class RefAsmType driver = driver.RunGenerators(compilation); var result = driver.GetRunResult().Results.Single(); - Assert.Contains(result.Diagnostics, d => d.Id == "ORLEANS0110"); + Assert.Contains(result.Diagnostics, d => d.Id == DiagnosticRuleId.ReferenceAssemblyWithGenerateSerializer); } private static async Task AssertSuccessfulSourceGeneration(string code) From bfc7931502dd0b8065abfc49b2bbc6cda1359a47 Mon Sep 17 00:00:00 2001 From: Dmitrii Korolev Date: Thu, 22 May 2025 20:28:41 +0200 Subject: [PATCH 3/6] use proper attribute and parse correctly --- src/Orleans.CodeGenerator/CodeGenerator.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Orleans.CodeGenerator/CodeGenerator.cs b/src/Orleans.CodeGenerator/CodeGenerator.cs index ce73530022e..ecdd7153451 100644 --- a/src/Orleans.CodeGenerator/CodeGenerator.cs +++ b/src/Orleans.CodeGenerator/CodeGenerator.cs @@ -128,7 +128,23 @@ public CompilationUnitSyntax GenerateCode(CancellationToken cancellationToken) } else if (ShouldGenerateSerializer(symbol)) { - if (containingAssemblyAttributes.Any(attr => attr.AttributeClass?.Name == "ReferenceAssemblyAttribute")) + if (containingAssemblyAttributes.Any(attributeData => attributeData.AttributeClass is + { + Name: "ReferenceAssemblyAttribute", + ContainingNamespace: + { + Name: "CompilerServices", + ContainingNamespace: + { + Name: "Runtime", + ContainingNamespace: + { + Name: "System", + ContainingNamespace.IsGlobalNamespace: true + } + } + } + })) { // not ALWAYS will be properly processed, therefore emit a warning throw new OrleansGeneratorDiagnosticAnalysisException(ReferenceAssemblyWithGenerateSerializerDiagnostic.CreateDiagnostic(symbol)); From 4d3912c359bc9a910cbdf6ea7fe11bdf8602cabd Mon Sep 17 00:00:00 2001 From: Dmitrii Korolev Date: Thu, 22 May 2025 20:36:28 +0200 Subject: [PATCH 4/6] address PR comments --- .../AnalyzerReleases.Unshipped.md | 1 + src/Orleans.CodeGenerator/CodeGenerator.cs | 1 + ...nceAssemblyWithGenerateSerializerDiagnostic.cs | 15 ++++----------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/Orleans.CodeGenerator/AnalyzerReleases.Unshipped.md b/src/Orleans.CodeGenerator/AnalyzerReleases.Unshipped.md index d5a7efc0b74..ae418e73167 100644 --- a/src/Orleans.CodeGenerator/AnalyzerReleases.Unshipped.md +++ b/src/Orleans.CodeGenerator/AnalyzerReleases.Unshipped.md @@ -6,3 +6,4 @@ Rule ID | Category | Severity | Notes --------|----------|----------|-------------------- ORLEANS0109 | Usage | Error | Method has multiple CancellationToken parameters +ORLEANS0110 | Usage | Error | ReferenceAssemblyWithGenerateSerializerDiagnostic diff --git a/src/Orleans.CodeGenerator/CodeGenerator.cs b/src/Orleans.CodeGenerator/CodeGenerator.cs index ecdd7153451..ac2aed4af71 100644 --- a/src/Orleans.CodeGenerator/CodeGenerator.cs +++ b/src/Orleans.CodeGenerator/CodeGenerator.cs @@ -128,6 +128,7 @@ public CompilationUnitSyntax GenerateCode(CancellationToken cancellationToken) } else if (ShouldGenerateSerializer(symbol)) { + // https://learn.microsoft.com/dotnet/api/system.runtime.compilerservices.referenceassemblyattribute if (containingAssemblyAttributes.Any(attributeData => attributeData.AttributeClass is { Name: "ReferenceAssemblyAttribute", diff --git a/src/Orleans.CodeGenerator/Diagnostics/ReferenceAssemblyWithGenerateSerializerDiagnostic.cs b/src/Orleans.CodeGenerator/Diagnostics/ReferenceAssemblyWithGenerateSerializerDiagnostic.cs index 0cb50dd84f1..cc8a5d878ef 100644 --- a/src/Orleans.CodeGenerator/Diagnostics/ReferenceAssemblyWithGenerateSerializerDiagnostic.cs +++ b/src/Orleans.CodeGenerator/Diagnostics/ReferenceAssemblyWithGenerateSerializerDiagnostic.cs @@ -5,20 +5,13 @@ namespace Orleans.CodeGenerator.Diagnostics; public static class ReferenceAssemblyWithGenerateSerializerDiagnostic { - public const string RuleId = DiagnosticRuleId.ReferenceAssemblyWithGenerateSerializer; + public const string DiagnosticId = DiagnosticRuleId.ReferenceAssemblyWithGenerateSerializer; public const string Title = "[GenerateSerializer] used in a reference assembly"; - public const string MessageFormat = """ - The type {0} is marked with [GenerateSerializer] in a reference assembly. - Serialization is likely to fail. Options: - (1) Enable code generation on the target project directly; - (2) Disable reference assemblies using false in the codegen project; - (3) Use a different serializer or create surrogates. See https://aka.ms/orleans-serialization for details. - """; - public const string Description = "[GenerateSerializer] should not be used in reference assemblies. See Orleans documentation for supported patterns."; + public const string MessageFormat = "The type {0} is marked with [GenerateSerializer] in a reference assembly"; + public const string Description = "The type {0} is marked with [GenerateSerializer] in a reference assembly. Serialization is likely to fail. Options: (1) Enable code generation on the target project directly; (2) Disable reference assemblies using false in the codegen project; (3) Use a different serializer or create surrogates. See https://aka.ms/orleans-serialization for details."; public const string Category = "Usage"; - private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor( - RuleId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description); + private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description); internal static Diagnostic CreateDiagnostic(ISymbol symbol) => Diagnostic.Create(Rule, symbol.Locations.First(), symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); } From f5f8dcc096686fc31aaac842f874af973624eebb Mon Sep 17 00:00:00 2001 From: Dmitrii Korolev Date: Thu, 22 May 2025 20:53:04 +0200 Subject: [PATCH 5/6] dont warn for our type --- .../TestSerializerExternalModels.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Misc/TestSerializerExternalModels/TestSerializerExternalModels.csproj b/test/Misc/TestSerializerExternalModels/TestSerializerExternalModels.csproj index 5c84c6d5850..9934422289d 100644 --- a/test/Misc/TestSerializerExternalModels/TestSerializerExternalModels.csproj +++ b/test/Misc/TestSerializerExternalModels/TestSerializerExternalModels.csproj @@ -3,6 +3,7 @@ UnitTests.SerializerExternalModels SerializerExternalModels $(TestTargetFrameworks);netcoreapp3.1 + ORLEANS0110 true From 738666c59fd9a211b0d3a0a76d83fc5635d8c8e9 Mon Sep 17 00:00:00 2001 From: Dmitrii Korolev Date: Thu, 22 May 2025 20:58:43 +0200 Subject: [PATCH 6/6] dont produce ref assembly --- .../TestSerializerExternalModels.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Misc/TestSerializerExternalModels/TestSerializerExternalModels.csproj b/test/Misc/TestSerializerExternalModels/TestSerializerExternalModels.csproj index 9934422289d..f52cf9e6877 100644 --- a/test/Misc/TestSerializerExternalModels/TestSerializerExternalModels.csproj +++ b/test/Misc/TestSerializerExternalModels/TestSerializerExternalModels.csproj @@ -3,7 +3,7 @@ UnitTests.SerializerExternalModels SerializerExternalModels $(TestTargetFrameworks);netcoreapp3.1 - ORLEANS0110 + false true