Skip to content

Initial refactoring of service source generator#4293

Merged
bernardnormier merged 19 commits intoicerpc:mainfrom
bernardnormier:merge-source-generators
Mar 11, 2026
Merged

Initial refactoring of service source generator#4293
bernardnormier merged 19 commits intoicerpc:mainfrom
bernardnormier:merge-source-generators

Conversation

@bernardnormier
Copy link
Member

@bernardnormier bernardnormier commented Mar 11, 2026

This PR combines the Slice service generator and the Ice service generator into a single service generator, IceRpc.ServiceGenerator.dll.

The controlling class attribute is now IceRpc.ServiceAttribute (in IceRpc.dll), and the source generator is packaged inside the main IceRpc NuGet package.

We want to merge the Protobuf service generator into this source generator in a follow-up PR.

Please note:

With this update, the IceRpc.Slice public API is primarily for use by the code generator / source generator, and not application code. So we have very few "using IceRpc.Slice;" left. We should consider further reducing this public API in a follow-up PR by moving ISliceFeature/SliceFeature to IceRpc.Features, and the ServiceProviderExtensions to IceRpc.Extensions.DependencyInjection.

The IceRpc.ServiceGenerator needs more refactoring to add Protobuf and also make Ice and Slice more separate.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR consolidates the separate Slice and Ice service source generators into a single generator (IceRpc.ServiceGenerator), standardizes the controlling attribute to [Service], and updates tests/examples/docs and packaging to use the unified generator.

Changes:

  • Replaced [SliceService] / [IceService] with [Service] across tests, examples, templates, and docfx snippets.
  • Added IceRpc.ServiceAttribute to IceRpc.dll and migrated generator internals into IceRpc.ServiceGenerator.
  • Updated project references/packaging to ship and consume the unified analyzer (IceRpc.ServiceGenerator.dll).

Reviewed changes

Copilot reviewed 68 out of 69 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/IntegrationTests/ProtocolBridgingTests.cs Updates test service attribute usage to unified [Service].
tests/IntegrationTests/IntegrationTests.csproj Switches analyzer reference to IceRpc.ServiceGenerator.
tests/IntegrationTests/IceObjectTests.cs Updates test service attribute usage to unified [Service].
tests/IntegrationTests/CustomTransportTests.cs Updates test service attribute usage to unified [Service].
tests/IceRpc.Slice.CodeGen.Tests/TypeNameQualificationTests.cs Updates test service attribute usage to unified [Service].
tests/IceRpc.Slice.CodeGen.Tests/ServiceTests.cs Updates attributes/comments to unified [Service] terminology.
tests/IceRpc.Slice.CodeGen.Tests/ProxyTests.cs Updates test service attribute usage to unified [Service].
tests/IceRpc.Slice.CodeGen.Tests/OperationTests.cs Updates test service attribute usage to unified [Service].
tests/IceRpc.Slice.CodeGen.Tests/NamespaceAttributeTests.cs Updates test service attribute usage to unified [Service].
tests/IceRpc.Slice.CodeGen.Tests/IdentifierAttributeTests.cs Updates test service attribute usage to unified [Service].
tests/IceRpc.Slice.CodeGen.Tests/IceRpc.Slice.CodeGen.Tests.csproj Switches analyzer reference to IceRpc.ServiceGenerator.
tests/IceRpc.Ice.CodeGen.Tests/ProxyTests.cs Updates test service attribute usage to unified [Service].
tests/IceRpc.Ice.CodeGen.Tests/IceRpc.Ice.CodeGen.Tests.csproj Switches analyzer reference to IceRpc.ServiceGenerator.
tests/IceRpc.Ice.CodeGen.Tests/ExceptionTests.cs Updates test service attribute usage to unified [Service].
tests/IceRpc.Compressor.Tests/OperationCompressorTests.cs Updates test service attribute usage to unified [Service].
tests/IceRpc.Compressor.Tests/IceRpc.Compressor.Tests.csproj Switches analyzer reference to IceRpc.ServiceGenerator.
src/IceRpc/ServiceAttribute.cs Introduces the new unified [Service] attribute in IceRpc.dll.
src/IceRpc.Templates/Templates/IceRpc-Slice-Server/Chatbot.cs Updates template service attribute usage to unified [Service].
src/IceRpc.Templates/Templates/IceRpc-Slice-DI-Server/Chatbot.cs Updates template service attribute usage to unified [Service].
src/IceRpc.Slice/SliceServiceAttribute.cs Removes the old Slice-specific service attribute type.
src/IceRpc.Slice/README.md Updates docs to reference [Service] and the unified generator assembly.
src/IceRpc.Slice/IceRpc.Slice.csproj Removes packaging of the Slice generator analyzer from IceRpc.Slice.
src/IceRpc.Slice.Generators/ServiceGenerator.cs Removes the old Slice generator implementation.
src/IceRpc.Slice.Generators/Internal/ServiceClass.cs Removes old Slice generator internal model.
src/IceRpc.Slice.Generators/Internal/ContainerDefinition.cs Removes old Slice generator internal model.
src/IceRpc.Slice.Generators/IceRpc.Slice.Generators.csproj Removes old Slice generator project.
src/IceRpc.ServiceGenerator/ServiceGenerator.cs Rehomes and retargets the unified generator to [Service].
src/IceRpc.ServiceGenerator/Internal/StringExtensions.cs Moves internal helpers under unified generator namespace.
src/IceRpc.ServiceGenerator/Internal/ServiceMethod.cs Adds IDL classification to support multiple IDLs in one generator.
src/IceRpc.ServiceGenerator/Internal/ServiceClass.cs Updates internal model namespace/docs to unified generator context.
src/IceRpc.ServiceGenerator/Internal/Parser.cs Updates parsing to recognize Ice + Slice operation attributes under [Service].
src/IceRpc.ServiceGenerator/Internal/Idl.cs Adds IDL enum used by unified generator emission.
src/IceRpc.ServiceGenerator/Internal/Emitter.cs Emits correct using IceRpc.{Idl}; based on the operations encountered.
src/IceRpc.ServiceGenerator/Internal/DiagnosticDescriptors.cs Updates diagnostics messaging/category for unified generator.
src/IceRpc.ServiceGenerator/Internal/ContainerDefinition.cs Moves internal helpers under unified generator namespace.
src/IceRpc.Ice/README.md Updates docs to reference [Service] for Ice integration.
src/IceRpc.Ice/IceServiceAttribute.cs Removes the old Ice-specific service attribute type.
src/IceRpc.Ice/IceRpc.Ice.csproj Packages unified generator analyzer instead of Ice-specific generator DLL.
src/IceRpc.Ice.Generators/Internal/StringExtensions.cs Removes old Ice generator utility.
src/IceRpc.Ice.Generators/Internal/ServiceMethod.cs Removes old Ice generator internal model.
src/IceRpc.Ice.Generators/Internal/Parser.cs Removes old Ice generator parser implementation.
src/IceRpc.Ice.Generators/Internal/Emitter.cs Removes old Ice generator emitter implementation.
src/IceRpc.Ice.Generators/Internal/DiagnosticDescriptors.cs Removes old Ice generator diagnostics.
examples/slice/Upload/Server/EarthImageStore.cs Updates example to use unified [Service] attribute.
examples/slice/Thermostat/Server/ThermoFacade.cs Updates example to use unified [Service] attribute.
examples/slice/Thermostat/Server/ThermoBridge.cs Updates example to use unified [Service] attribute.
examples/slice/Thermostat/Device/ThermoBot.cs Updates example to use unified [Service] attribute.
examples/slice/Telemetry/Server/Chatbot.cs Updates example to use unified [Service] attribute.
examples/slice/TcpFallback/Server/Chatbot.cs Updates example to use unified [Service] attribute.
examples/slice/Tcp/Server/Chatbot.cs Updates example to use unified [Service] attribute.
examples/slice/Stream/Server/RandomGenerator.cs Updates example to use unified [Service] attribute.
examples/slice/Retry/Server/Chatbot.cs Updates example to use unified [Service] attribute.
examples/slice/RequestContext/Server/Chatbot.cs Updates example to use unified [Service] attribute.
examples/slice/MultipleInterfaces/Server/Chatbot.cs Updates example to use unified [Service] attribute.
examples/slice/Metrics/Server/Chatbot.cs Updates example to use unified [Service] attribute.
examples/slice/Logger/Server/Chatbot.cs Updates example to use unified [Service] attribute.
examples/slice/InteropGreeter/Server/Chatbot.cs Updates example to use unified [Service] attribute.
examples/slice/Greeter/Server/Chatbot.cs Updates example to use unified [Service] attribute.
examples/slice/GenericHost/Server/Chatbot.cs Updates example to use unified [Service] attribute.
examples/slice/Download/Server/EarthImageServer.cs Updates example to use unified [Service] attribute.
examples/slice/DiscriminatedUnion/Server/MathWizard.cs Updates example to use unified [Service] attribute.
examples/slice/Deadline/Server/SlowChatbot.cs Updates example to use unified [Service] attribute.
examples/slice/CustomError/Server/Chatbot.cs Updates example to use unified [Service] attribute.
examples/slice/Compress/Server/Chatbot.cs Updates example to use unified [Service] attribute.
docfx/examples/IceRpc.RequestContext.Examples/Chatbot.cs Updates docfx example to use unified [Service] attribute.
docfx/examples/Chatbot.cs Updates docfx example to use unified [Service] attribute.
docfx/docfx.json Updates docfx exclusion list for new generator project path.
IceRpc.slnx Replaces old generator projects with IceRpc.ServiceGenerator.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +179 to +193
Idl idl = default;
AttributeData? attribute = null;

if (_sliceOperationAttribute is not null)
{
attribute = GetAttribute(method, _sliceOperationAttribute);
idl = Idl.Slice;
}
if (attribute is null && _iceOperationAttribute is not null)
{
attribute = GetAttribute(method, _iceOperationAttribute);
idl = Idl.Ice;
}

if (attribute is null)
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a method ends up with both SliceOperationAttribute and IceOperationAttribute, the current logic will silently prefer Slice. To make this deterministic and debuggable, consider explicitly detecting the "both attributes present" case and reporting a diagnostic (or otherwise making the precedence an intentional, documented rule).

Copilot uses AI. Check for mistakes.
Copy link
Member Author

@bernardnormier bernardnormier Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are generated methods, with a single attribute per method. We could emit a diagnostic if we encounter a method with more than one of these attributes.

Comment on lines +15 to +19
foreach (Idl idl in serviceClass.ServiceMethods.Select(serviceMethod => serviceMethod.Idl).Distinct())
{
codeBlock.WriteLine($"using IceRpc.{idl};");
}
codeBlock.WriteLine("using ZeroC.Slice.Codec;");
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generating using IceRpc.{idl}; relies on Idl.ToString() matching the target namespace exactly. Since Idl is expected to grow (TODO: Protobuf), this is brittle (enum renames or different namespace conventions would break generation). Consider using an explicit mapping (e.g., switch expression from Idl to namespace string) so future IDLs/namespaces can be added safely.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 82 out of 82 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/IceRpc.ServiceGenerator/Internal/Emitter.cs:18

  • The generated file conditionally adds using IceRpc.Slice; and/or using IceRpc.Ice;. If a service class ends up with both Slice and Ice operations, bringing both namespaces into scope can make calls like request.DispatchOperationAsync(...) ambiguous (notably for void-return operations where the overload sets differ only by optional parameters), causing a compile-time error or binding to the wrong extension method. Consider emitting fully-qualified calls to the correct IncomingRequestExtensions.DispatchOperationAsync based on serviceMethod.Idl (or emitting a diagnostic that mixed IDLs in one service class are not supported).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

</ItemGroup>
<ItemGroup>
<ProjectReference Include="../IceRpc/IceRpc.csproj" ExactVersion="true" />
<ProjectReference Include="../IceRpc.ServiceGenerator/IceRpc.ServiceGenerator.csproj" ExactVersion="true" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we no longer using ReferenceOutputAssembly="false", shouldn't we also use OutputItemType="Analyzer" like the tests?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fixed it as you suggested. And the current setup still pulls IceRpc.ServiceGenerator in projects with a reference to the IceRpc.Slice package.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we can't use ReferenceOutputAssembly="false" as it prevents this dependency from flowing transitively to consumers of IceRpc.Slice/IceRpc.Ice. See updated project file.

Copy link
Member

@externl externl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 82 out of 82 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

src/IceRpc.ServiceGenerator/Internal/Emitter.cs:19

  • Emit adds using IceRpc.{idl}; for each IDL used by the service methods. If a service class ends up implementing both Slice and Ice service interfaces, the generated file will import both IceRpc.Slice and IceRpc.Ice, and calls like request.CheckNonIdempotent() / request.DispatchOperationAsync(...) become ambiguous because both namespaces define IncomingRequestExtensions with these methods. Consider generating fully-qualified static calls based on serviceMethod.Idl (or adding a diagnostic that forbids mixing IDLs on the same [Service] class) to avoid ambiguous extension method resolution.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@bernardnormier bernardnormier merged commit b77dd15 into icerpc:main Mar 11, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants