Add XPC2002: duplicate plugin step registration (error)#21
Merged
Conversation
Reports at compile time when a plugin registers the same entity/operation/stage combination more than once via RegisterStep or RegisterPluginStep. The platform rejects such duplicates at registration time, and they would make the source-generated image/wrapper type names (derived from that tuple) ambiguous — this establishes the uniqueness invariant the upcoming flat image-naming scheme relies on. Also renames the pending CHANGELOG entry to v1.5.0 (folding in the unreleased XPC3007 work), since the image-generation rework ships as part of this minor. Co-Authored-By: Claude <noreply@anthropic.com> via Conducktor <conducktor@contextand.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces a new Roslyn diagnostic (XPC2002) to enforce the invariant that a plugin must not register the same entity/operation/stage step tuple more than once, since duplicates are rejected by the Dataverse platform and would make generated wrapper/image type names ambiguous.
Changes:
- Add analyzer XPC2002 (error) to detect duplicate step registrations (including legacy
RegisterPluginStep). - Add rule documentation and announce the new rule in analyzer release notes + project changelog.
- Add unit tests covering core duplicate/non-duplicate scenarios.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| XrmPluginCore/CHANGELOG.md | Bumps unreleased heading to v1.5.0 and documents XPC2002. |
| XrmPluginCore.SourceGenerator/rules/XPC2002.md | Adds rule documentation for XPC2002 with examples and guidance. |
| XrmPluginCore.SourceGenerator/Helpers/RegisterStepHelper.cs | Adds helper to recognize both RegisterStep and RegisterPluginStep invocations. |
| XrmPluginCore.SourceGenerator/DiagnosticDescriptors.cs | Defines the XPC2002 DiagnosticDescriptor. |
| XrmPluginCore.SourceGenerator/Constants.cs | Adds RegisterPluginStepMethodName constant. |
| XrmPluginCore.SourceGenerator/Analyzers/DuplicateStepRegistrationAnalyzer.cs | Implements the XPC2002 analyzer logic. |
| XrmPluginCore.SourceGenerator/AnalyzerReleases.Unshipped.md | Records the new rule in analyzer release notes. |
| XrmPluginCore.SourceGenerator.Tests/DiagnosticTests/DuplicateStepRegistrationAnalyzerTests.cs | Adds tests for XPC2002 analyzer behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Only analyze types that derive from XrmPluginCore.Plugin, so unrelated classes with a generic RegisterStep/RegisterPluginStep method aren't flagged. - Aggregate registrations per plugin type across all syntax trees and evaluate at compilation end, so duplicates split across partial declarations are detected; also seed with inherited base-plugin registrations so a derived class that re-registers an inherited tuple is caught. (Uses the compilation-start pattern to stay within RS1030; descriptor tagged CompilationEnd.) - Resolve const/literal custom-message names via GetConstantValue instead of treating them as unresolved, fixing a false negative. - Reuse Constants.RegisterPluginStepMethodName in LocalPluginContextAsService code fix instead of a duplicate hard-coded string. - Add tests for legacy RegisterPluginStep, const message names, partial-split registrations, derived re-registration, and the non-plugin exclusion. Co-Authored-By: Claude <noreply@anthropic.com> via Conducktor <conducktor@contextand.com>
ResolveTupleValue now maps a const enum reference (e.g. const EventOperation Op = EventOperation.Update) from its underlying constant value back to the enum member name, so such registrations are keyed correctly instead of being skipped. Adds tests for duplicate and distinct const enum operation arguments. Co-Authored-By: Claude <noreply@anthropic.com> via Conducktor <conducktor@contextand.com>
ResolveTupleValue no longer short-circuits on member access. Constant evaluation now drives resolution uniformly, so a const string message member (Msgs.DoThing) resolves to its string value rather than the member identifier — two distinct members sharing a value are correctly treated as duplicates. Enum constants are still mapped back to the member name. Adds tests for shared and distinct const string message members. Co-Authored-By: Claude <noreply@anthropic.com> via Conducktor <conducktor@contextand.com>
ResolveTupleValue now falls back to the underlying integral's string form for enum constants with no matching member (e.g. (EventOperation)999), matching Enum.ToString(), so duplicates using undefined enum values are still detected instead of skipped. Adds duplicate and distinct undefined-enum-cast tests. Co-Authored-By: Claude <noreply@anthropic.com> via Conducktor <conducktor@contextand.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reports at compile time when a plugin registers the same entity/operation/stage combination more than once via RegisterStep or RegisterPluginStep. The platform rejects such duplicates at registration time, and they would make the source-generated image/wrapper type names (derived from that tuple) ambiguous — this establishes the uniqueness invariant the upcoming flat image-naming scheme relies on.
Also renames the pending CHANGELOG entry to v1.5.0 (folding in the unreleased XPC3007 work), since the image-generation rework ships as part of this minor.