From d35f2f118587bac9b0f2e0c92538d3aa177eef11 Mon Sep 17 00:00:00 2001 From: Piotr Siuszko Date: Wed, 11 Feb 2026 16:27:58 +0100 Subject: [PATCH] More precise diagnostics for methods attributes with wrong types --- .../Analyzers/ServicesAnalyzer.cs | 17 +++++++++++++---- .../MicroserviceSourceGen/Diagnostics.cs | 9 +++++++++ .../BeamableSourceGeneratorTests.Srv.cs | 9 +++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/templates/MicroserviceSourceGen/MicroserviceSourceGen/Analyzers/ServicesAnalyzer.cs b/templates/MicroserviceSourceGen/MicroserviceSourceGen/Analyzers/ServicesAnalyzer.cs index dd0520680f..d0acd5a38e 100644 --- a/templates/MicroserviceSourceGen/MicroserviceSourceGen/Analyzers/ServicesAnalyzer.cs +++ b/templates/MicroserviceSourceGen/MicroserviceSourceGen/Analyzers/ServicesAnalyzer.cs @@ -55,7 +55,7 @@ public class ServicesAnalyzer : DiagnosticAnalyzer Diagnostics.Srv.TypeInBeamGeneratedIsMissingBeamGeneratedAttribute, Diagnostics.Srv.DictionaryKeyMustBeStringOnSerializableTypes, Diagnostics.Srv.FieldOnSerializableTypeIsSubtypeFromDictionary, Diagnostics.Srv.FieldOnSerializableTypeIsSubtypeFromList, Diagnostics.Srv.CallableMethodDeclarationTypeIsInvalidDictionary, Diagnostics.Srv.CallableMethodDeclarationTypeIsSubtypeFromDictionary, - Diagnostics.Srv.CallableMethodDeclarationTypeIsSubtypeFromList, Diagnostics.Srv.InvalidGenericTypeOnMicroservice); + Diagnostics.Srv.CallableMethodDeclarationTypeIsSubtypeFromList, Diagnostics.Srv.InvalidGenericTypeOnMicroservice, Diagnostics.Srv.MissingSerializableAttributeForArgument); public override void Initialize(AnalysisContext context) { @@ -497,7 +497,7 @@ private static void ValidateParameters(SyntaxNodeAnalysisContext context, IMetho } ValidateNestedType(context.Compilation, context.ReportDiagnostic, parameterLocation, parameterSymbol.Type, methodSymbol.Name); - ValidateSerializableAttributeOnSymbol(context.Compilation, context.ReportDiagnostic, parameterSymbol.Type, parameterLocation); + ValidateSerializableAttributeOnSymbol(context.Compilation, context.ReportDiagnostic, parameterSymbol.Type, parameterLocation, parameterName: parameterSymbol.Name); ValidateMembersInSymbol(context.Compilation, context.ReportDiagnostic, parameterSymbol.Type, isBlueprintCompatible, fallbackLocation: parameterLocation); ValidateContentObjectType(context.ReportDiagnostic, parameterSymbol.Type, parameterLocation, $"parameter {parameterSymbol.Name}"); @@ -582,7 +582,7 @@ private static void ValidateIfTypeIsInMicroservice(SyntaxNodeAnalysisContext con } private static void ValidateSerializableAttributeOnSymbol(Compilation compilation, Action reportDiagnostic, - ITypeSymbol typeSymbol, Location fallbackLocation = null, HashSet processedTypes = null) + ITypeSymbol typeSymbol, Location fallbackLocation = null, HashSet processedTypes = null, string parameterName = null) { processedTypes ??= new HashSet(); @@ -637,9 +637,18 @@ private static void ValidateSerializableAttributeOnSymbol(Compilation compilatio return; } + Diagnostic diagnostic; + // Try to get type location, if not found because it is out of scope, try to get the fallback. // If none fallback is passed, use Location.None so we don't throw any Exception. - var diagnostic = Diagnostic.Create(Diagnostics.Srv.MissingSerializableAttributeOnType, location, typeSymbol.Name); + if (!string.IsNullOrWhiteSpace(parameterName)) + { + diagnostic = Diagnostic.Create(Diagnostics.Srv.MissingSerializableAttributeForArgument, location, typeSymbol.Name, parameterName); + } + else + { + diagnostic = Diagnostic.Create(Diagnostics.Srv.MissingSerializableAttributeOnType, location, typeSymbol.Name); + } reportDiagnostic.Invoke(diagnostic); } diff --git a/templates/MicroserviceSourceGen/MicroserviceSourceGen/Diagnostics.cs b/templates/MicroserviceSourceGen/MicroserviceSourceGen/Diagnostics.cs index f6907c4f03..a2ef672ef5 100644 --- a/templates/MicroserviceSourceGen/MicroserviceSourceGen/Diagnostics.cs +++ b/templates/MicroserviceSourceGen/MicroserviceSourceGen/Diagnostics.cs @@ -143,6 +143,15 @@ public static readonly DiagnosticDescriptor MissingSerializableAttributeOnType helpLinkUri: DocsPageHelper.GetCliDocsPageUrl($"{TROUBLESHOOTING_GUIDE_BASE_URL}#missing-serializable-attribute-on-type", Constants.CLI_CURRENT_DOCS_VERSION), isEnabledByDefault: true); + public static readonly DiagnosticDescriptor MissingSerializableAttributeForArgument + = new(MISSING_SERIALIZABLE_ATTRIBUTE_ON_TYPE_ID, + "Argument type is not serializable", + $"Type of argument '{{1}}' must be serializable or have [BeamGenerateSchema] attribute. Either add the [Serializable] attribute to type '{{0}}' or use different type.", + Category_Services, + DiagnosticSeverity.Error, + helpLinkUri: DocsPageHelper.GetCliDocsPageUrl($"{TROUBLESHOOTING_GUIDE_BASE_URL}#missing-serializable-attribute-on-type", Constants.CLI_CURRENT_DOCS_VERSION), + isEnabledByDefault: true); + public static readonly DiagnosticDescriptor PropertiesFoundInSerializableTypes = new(PROPERTIES_FOUND_IN_SERIALIZABLE_TYPES_ID, $"Properties in serializable types are ignored by the client code generator", diff --git a/templates/SourceGen/MicroserviceSourceGen.Tests/BeamableSourceGeneratorTests.Srv.cs b/templates/SourceGen/MicroserviceSourceGen.Tests/BeamableSourceGeneratorTests.Srv.cs index 8a64cf0d2d..72e466ed27 100644 --- a/templates/SourceGen/MicroserviceSourceGen.Tests/BeamableSourceGeneratorTests.Srv.cs +++ b/templates/SourceGen/MicroserviceSourceGen.Tests/BeamableSourceGeneratorTests.Srv.cs @@ -437,6 +437,11 @@ public NonSerializableEnum CallServiceEnum() { return NonSerializableEnum.None; } + + [ClientCallable] + public void CallWithWrongArgument(object {|#3:wrongArgument|}) + { + } } public struct {|#0:DTO_AsyncTest|} @@ -482,6 +487,10 @@ public enum OtherNonSerializableEnum { new DiagnosticResult(Diagnostics.Srv.MissingSerializableAttributeOnType) .WithLocation(2) .WithArguments("DTO_BeamGenSchemaAttribute")); + ctx.ExpectedDiagnostics.Add( + new DiagnosticResult(Diagnostics.Srv.MissingSerializableAttributeForArgument) + .WithLocation(3) + .WithArguments("Object", "wrongArgument")); PrepareForRun(ctx, UserCode);