Skip to content

Commit ea0c2bd

Browse files
committed
Breaking: Reorganized diagnostic IDs by category (XPC1xxx=Info, XPC2xxx=Plugin structure, XPC3xxx=Style, XPC4xxx=Handler methods, XPC5xxx=Internal errors)
1 parent 086b9d3 commit ea0c2bd

21 files changed

Lines changed: 397 additions & 344 deletions

XrmPluginCore.SourceGenerator.Tests/DiagnosticTests/DiagnosticReportingTests.cs

Lines changed: 56 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,16 @@ public void Should_Not_Report_XPC1000_Success_Diagnostic_On_Successful_Generatio
2424
var result = GeneratorTestHelper.RunGenerator(
2525
CompilationHelper.CreateCompilation(source));
2626

27-
// Assert - XPC1000 is no longer reported to avoid spamming the user
27+
// Assert - XPC1001 is no longer reported to avoid spamming the user
2828
var successDiagnostics = result.GeneratorDiagnostics
29-
.Where(d => d.Id == "XPC1000")
29+
.Where(d => d.Id == "XPC1001")
3030
.ToArray();
3131

32-
successDiagnostics.Should().BeEmpty("XPC1000 success diagnostic should not be reported to avoid spam");
32+
successDiagnostics.Should().BeEmpty("XPC1001 success diagnostic should not be reported to avoid spam");
3333
}
3434

3535
[Fact]
36-
public async Task Should_Report_XPC4001_When_Plugin_Has_No_Parameterless_Constructor()
36+
public async Task Should_Report_XPC2001_When_Plugin_Has_No_Parameterless_Constructor()
3737
{
3838
// Arrange - plugin class with only a parameterized constructor (no parameterless)
3939
const string pluginSource = """
@@ -72,12 +72,12 @@ public class TestService : ITestService { public void Process(PreImage preImage)
7272
// Act - Run analyzer instead of generator
7373
var diagnostics = await GetAnalyzerDiagnosticsAsync(source, new NoParameterlessConstructorAnalyzer());
7474

75-
// Assert - should report XPC4001
75+
// Assert - should report XPC2001
7676
var errorDiagnostics = diagnostics
77-
.Where(d => d.Id == "XPC4001")
77+
.Where(d => d.Id == "XPC2001")
7878
.ToArray();
7979

80-
errorDiagnostics.Should().NotBeEmpty("XPC4001 should be reported when plugin class has no parameterless constructor");
80+
errorDiagnostics.Should().NotBeEmpty("XPC2001 should be reported when plugin class has no parameterless constructor");
8181
errorDiagnostics.Should().OnlyContain(d => d.Severity == DiagnosticSeverity.Warning);
8282
}
8383

@@ -107,7 +107,7 @@ public void Should_Handle_XPC5000_Generation_Error_Gracefully()
107107
}
108108

109109
[Fact]
110-
public async Task Should_Report_XPC4002_When_Handler_Method_Not_Found()
110+
public async Task Should_Report_XPC4001_When_Handler_Method_Not_Found()
111111
{
112112
// Arrange - method reference points to NonExistentMethod but service has Process
113113
const string pluginSource = """
@@ -153,15 +153,15 @@ public void Process() { }
153153

154154
// Assert
155155
var errorDiagnostics = diagnostics
156-
.Where(d => d.Id == "XPC4002")
156+
.Where(d => d.Id == "XPC4001")
157157
.ToArray();
158158

159-
errorDiagnostics.Should().NotBeEmpty("XPC4002 should be reported when handler method is not found");
159+
errorDiagnostics.Should().NotBeEmpty("XPC4001 should be reported when handler method is not found");
160160
errorDiagnostics.Should().OnlyContain(d => d.Severity == DiagnosticSeverity.Error);
161161
}
162162

163163
[Fact]
164-
public async Task Should_Report_XPC4003_When_Handler_Missing_PreImage_Parameter()
164+
public async Task Should_Report_XPC4002_When_Handler_Missing_PreImage_Parameter()
165165
{
166166
// Arrange - WithPreImage is registered but handler takes no parameters
167167
const string pluginSource = """
@@ -207,16 +207,16 @@ public void Process() { }
207207

208208
// Assert
209209
var errorDiagnostics = diagnostics
210-
.Where(d => d.Id == "XPC4003")
210+
.Where(d => d.Id == "XPC4002")
211211
.ToArray();
212212

213-
errorDiagnostics.Should().NotBeEmpty("XPC4003 should be reported when handler is missing PreImage parameter");
214-
// XPC4003 is Warning when generated types don't exist yet (allows initial build to succeed)
213+
errorDiagnostics.Should().NotBeEmpty("XPC4002 should be reported when handler is missing PreImage parameter");
214+
// XPC4002 is Warning when generated types don't exist yet (allows initial build to succeed)
215215
errorDiagnostics.Should().OnlyContain(d => d.Severity == DiagnosticSeverity.Warning);
216216
}
217217

218218
[Fact]
219-
public async Task Should_Report_XPC4003_When_Handler_Missing_PostImage_Parameter()
219+
public async Task Should_Report_XPC4002_When_Handler_Missing_PostImage_Parameter()
220220
{
221221
// Arrange - WithPostImage is registered but handler takes no parameters
222222
const string pluginSource = """
@@ -262,16 +262,16 @@ public void Process() { }
262262

263263
// Assert
264264
var errorDiagnostics = diagnostics
265-
.Where(d => d.Id == "XPC4003")
265+
.Where(d => d.Id == "XPC4002")
266266
.ToArray();
267267

268-
errorDiagnostics.Should().NotBeEmpty("XPC4003 should be reported when handler is missing PostImage parameter");
269-
// XPC4003 is Warning when generated types don't exist yet (allows initial build to succeed)
268+
errorDiagnostics.Should().NotBeEmpty("XPC4002 should be reported when handler is missing PostImage parameter");
269+
// XPC4002 is Warning when generated types don't exist yet (allows initial build to succeed)
270270
errorDiagnostics.Should().OnlyContain(d => d.Severity == DiagnosticSeverity.Warning);
271271
}
272272

273273
[Fact]
274-
public async Task Should_Report_XPC4003_When_Handler_Missing_Both_Image_Parameters()
274+
public async Task Should_Report_XPC4002_When_Handler_Missing_Both_Image_Parameters()
275275
{
276276
// Arrange - Both WithPreImage and WithPostImage but handler takes no parameters
277277
const string pluginSource = """
@@ -318,16 +318,16 @@ public void Process() { }
318318

319319
// Assert
320320
var errorDiagnostics = diagnostics
321-
.Where(d => d.Id == "XPC4003")
321+
.Where(d => d.Id == "XPC4002")
322322
.ToArray();
323323

324-
errorDiagnostics.Should().NotBeEmpty("XPC4003 should be reported when handler is missing both image parameters");
325-
// XPC4003 is Warning when generated types don't exist yet (allows initial build to succeed)
324+
errorDiagnostics.Should().NotBeEmpty("XPC4002 should be reported when handler is missing both image parameters");
325+
// XPC4002 is Warning when generated types don't exist yet (allows initial build to succeed)
326326
errorDiagnostics.Should().OnlyContain(d => d.Severity == DiagnosticSeverity.Warning);
327327
}
328328

329329
[Fact]
330-
public async Task Should_Report_XPC4003_When_Handler_Has_Wrong_Parameter_Order()
330+
public async Task Should_Report_XPC4002_When_Handler_Has_Wrong_Parameter_Order()
331331
{
332332
// Arrange - WithPreImage and WithPostImage but handler has parameters in wrong order
333333
const string pluginSource = """
@@ -375,16 +375,16 @@ public void Process(PostImage post, PreImage pre) { }
375375

376376
// Assert
377377
var errorDiagnostics = diagnostics
378-
.Where(d => d.Id == "XPC4003")
378+
.Where(d => d.Id == "XPC4002")
379379
.ToArray();
380380

381-
errorDiagnostics.Should().NotBeEmpty("XPC4003 should be reported when handler has wrong parameter order");
382-
// XPC4003 is Warning when generated types don't exist yet (allows initial build to succeed)
381+
errorDiagnostics.Should().NotBeEmpty("XPC4002 should be reported when handler has wrong parameter order");
382+
// XPC4002 is Warning when generated types don't exist yet (allows initial build to succeed)
383383
errorDiagnostics.Should().OnlyContain(d => d.Severity == DiagnosticSeverity.Warning);
384384
}
385385

386386
[Fact]
387-
public async Task Should_Report_XPC4004_When_WithPreImage_Used_With_Invocation_Syntax()
387+
public async Task Should_Report_XPC3003_When_WithPreImage_Used_With_Invocation_Syntax()
388388
{
389389
// Arrange - WithPreImage used with s => s.DoSomething() (invocation) instead of s => s.DoSomething (method reference)
390390
const string pluginSource = """
@@ -430,15 +430,15 @@ public void DoSomething() { }
430430

431431
// Assert
432432
var warningDiagnostics = diagnostics
433-
.Where(d => d.Id == "XPC4004")
433+
.Where(d => d.Id == "XPC3003")
434434
.ToArray();
435435

436-
warningDiagnostics.Should().NotBeEmpty("XPC4004 should be reported when WithPreImage is used with invocation syntax");
436+
warningDiagnostics.Should().NotBeEmpty("XPC3003 should be reported when WithPreImage is used with invocation syntax");
437437
warningDiagnostics.Should().OnlyContain(d => d.Severity == DiagnosticSeverity.Warning);
438438
}
439439

440440
[Fact]
441-
public async Task Should_Report_XPC4004_When_WithPostImage_Used_With_Invocation_Syntax()
441+
public async Task Should_Report_XPC3003_When_WithPostImage_Used_With_Invocation_Syntax()
442442
{
443443
// Arrange - WithPostImage used with s => s.DoSomething() (invocation) instead of s => s.DoSomething (method reference)
444444
const string pluginSource = """
@@ -484,15 +484,15 @@ public void DoSomething() { }
484484

485485
// Assert
486486
var warningDiagnostics = diagnostics
487-
.Where(d => d.Id == "XPC4004")
487+
.Where(d => d.Id == "XPC3003")
488488
.ToArray();
489489

490-
warningDiagnostics.Should().NotBeEmpty("XPC4004 should be reported when WithPostImage is used with invocation syntax");
490+
warningDiagnostics.Should().NotBeEmpty("XPC3003 should be reported when WithPostImage is used with invocation syntax");
491491
warningDiagnostics.Should().OnlyContain(d => d.Severity == DiagnosticSeverity.Warning);
492492
}
493493

494494
[Fact]
495-
public async Task Should_Not_Report_XPC4004_When_Using_Method_Reference_Syntax()
495+
public async Task Should_Not_Report_XPC3003_When_Using_Method_Reference_Syntax()
496496
{
497497
// Arrange - Method reference syntax (correct usage)
498498
const string pluginSource = """
@@ -539,14 +539,14 @@ public void HandleUpdate(PreImage preImage) { }
539539

540540
// Assert
541541
var warningDiagnostics = diagnostics
542-
.Where(d => d.Id == "XPC4004")
542+
.Where(d => d.Id == "XPC3003")
543543
.ToArray();
544544

545-
warningDiagnostics.Should().BeEmpty("XPC4004 should NOT be reported when using method reference syntax");
545+
warningDiagnostics.Should().BeEmpty("XPC3003 should NOT be reported when using method reference syntax");
546546
}
547547

548548
[Fact]
549-
public async Task Should_Not_Report_XPC4004_When_Old_Api_Used_Without_Images()
549+
public async Task Should_Not_Report_XPC3003_When_Old_Api_Used_Without_Images()
550550
{
551551
// Arrange - Invocation syntax but without WithPreImage/WithPostImage (no images registered)
552552
const string pluginSource = """
@@ -592,14 +592,14 @@ public void DoSomething() { }
592592

593593
// Assert
594594
var warningDiagnostics = diagnostics
595-
.Where(d => d.Id == "XPC4004")
595+
.Where(d => d.Id == "XPC3003")
596596
.ToArray();
597597

598-
warningDiagnostics.Should().BeEmpty("XPC4004 should NOT be reported when old API is used without images");
598+
warningDiagnostics.Should().BeEmpty("XPC3003 should NOT be reported when old API is used without images");
599599
}
600600

601601
[Fact]
602-
public async Task Should_Report_XPC4005_When_AddImage_Used_With_Invocation_Syntax()
602+
public async Task Should_Report_XPC3002_When_AddImage_Used_With_Invocation_Syntax()
603603
{
604604
// Arrange - AddImage (legacy API) used with s => s.DoSomething() (invocation)
605605
const string pluginSource = """
@@ -643,24 +643,24 @@ public void DoSomething() { }
643643
// Act - Run analyzer instead of generator
644644
var diagnostics = await GetAnalyzerDiagnosticsAsync(source, new ImageWithoutMethodReferenceAnalyzer());
645645

646-
// Assert - Should report XPC4005 (Info) NOT XPC4004 (Warning)
647-
var xpc4005Diagnostics = diagnostics
648-
.Where(d => d.Id == "XPC4005")
646+
// Assert - Should report XPC3002 (Info) NOT XPC3003 (Warning)
647+
var xpc3002Diagnostics = diagnostics
648+
.Where(d => d.Id == "XPC3002")
649649
.ToArray();
650650

651-
var xpc4004Diagnostics = diagnostics
652-
.Where(d => d.Id == "XPC4004")
651+
var xpc3003Diagnostics = diagnostics
652+
.Where(d => d.Id == "XPC3003")
653653
.ToArray();
654654

655-
xpc4005Diagnostics.Should().NotBeEmpty("XPC4005 should be reported when AddImage is used with invocation syntax");
656-
xpc4005Diagnostics.Should().OnlyContain(d => d.Severity == DiagnosticSeverity.Info);
657-
xpc4004Diagnostics.Should().BeEmpty("XPC4004 should NOT be reported for legacy AddImage API");
655+
xpc3002Diagnostics.Should().NotBeEmpty("XPC3002 should be reported when AddImage is used with invocation syntax");
656+
xpc3002Diagnostics.Should().OnlyContain(d => d.Severity == DiagnosticSeverity.Info);
657+
xpc3003Diagnostics.Should().BeEmpty("XPC3003 should NOT be reported for legacy AddImage API");
658658
}
659659

660660
[Fact]
661-
public async Task Should_Report_XPC4004_Not_XPC4005_When_WithPreImage_Used_Even_With_AddImage()
661+
public async Task Should_Report_XPC3003_Not_XPC3002_When_WithPreImage_Used_Even_With_AddImage()
662662
{
663-
// Arrange - Both WithPreImage (modern) and AddImage (legacy) used - should report XPC4004 since modern takes precedence
663+
// Arrange - Both WithPreImage (modern) and AddImage (legacy) used - should report XPC3003 since modern takes precedence
664664
const string pluginSource = """
665665
666666
using XrmPluginCore;
@@ -703,17 +703,17 @@ public void DoSomething() { }
703703
// Act - Run analyzer instead of generator
704704
var diagnostics = await GetAnalyzerDiagnosticsAsync(source, new ImageWithoutMethodReferenceAnalyzer());
705705

706-
// Assert - Should report XPC4004 (modern API takes precedence)
707-
var xpc4004Diagnostics = diagnostics
708-
.Where(d => d.Id == "XPC4004")
706+
// Assert - Should report XPC3003 (modern API takes precedence)
707+
var xpc3003Diagnostics = diagnostics
708+
.Where(d => d.Id == "XPC3003")
709709
.ToArray();
710710

711-
var xpc4005Diagnostics = diagnostics
712-
.Where(d => d.Id == "XPC4005")
711+
var xpc3002Diagnostics = diagnostics
712+
.Where(d => d.Id == "XPC3002")
713713
.ToArray();
714714

715-
xpc4004Diagnostics.Should().NotBeEmpty("XPC4004 should be reported when modern API (WithPreImage) is used");
716-
xpc4005Diagnostics.Should().BeEmpty("XPC4005 should NOT be reported when modern API is also present");
715+
xpc3003Diagnostics.Should().NotBeEmpty("XPC3003 should be reported when modern API (WithPreImage) is used");
716+
xpc3002Diagnostics.Should().BeEmpty("XPC3002 should NOT be reported when modern API is also present");
717717
}
718718

719719
private static async Task<ImmutableArray<Diagnostic>> GetAnalyzerDiagnosticsAsync(string source, DiagnosticAnalyzer analyzer)

XrmPluginCore.SourceGenerator/AnalyzerReleases.Shipped.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,32 @@ XPC4006 | XrmPluginCore.SourceGenerator | Error | XPC4006 Handler signature m
3232
### Changed Rules
3333

3434
Rule ID | New Category | New Severity | Old Category | Old Severity | Notes
35-
--------|--------------|--------------|--------------|--------------|-------|-------
35+
--------|--------------|--------------|--------------|--------------|-------
3636
XPC4003 | XrmPluginCore.SourceGenerator | Warning | XrmPluginCore.SourceGenerator | Error | Handler signature does not match registered images (generated types don't exist)
37+
38+
## Release 1.2.3
39+
40+
### New Rules
41+
42+
Rule ID | Category | Severity | Notes
43+
--------|----------|----------|-------
44+
XPC1001 | XrmPluginCore.SourceGenerator | Info | Generated type-safe wrapper classes (replaces XPC1000)
45+
XPC2001 | XrmPluginCore.SourceGenerator | Warning | No parameterless constructor found (renumbered from XPC4001)
46+
XPC3002 | XrmPluginCore.SourceGenerator | Info | Consider using modern image registration API (renumbered from XPC4005)
47+
XPC3003 | XrmPluginCore.SourceGenerator | Warning | Image registration without method reference (renumbered from XPC4004)
48+
XPC4001 | XrmPluginCore.SourceGenerator | Error | Handler method not found (renumbered from XPC4002)
49+
XPC4002 | XrmPluginCore.SourceGenerator | Warning | Handler signature mismatch - types don't exist (renumbered from XPC4003)
50+
XPC4003 | XrmPluginCore.SourceGenerator | Error | Handler signature mismatch - types exist (renumbered from XPC4006)
51+
XPC5001 | XrmPluginCore.SourceGenerator | Warning | Failed to resolve symbol (renumbered from XPC4000)
52+
XPC5002 | XrmPluginCore.SourceGenerator | Error | Failed to generate wrapper classes (renumbered from XPC5000)
53+
54+
### Removed Rules
55+
56+
Rule ID | Category | Severity | Notes
57+
--------|----------|----------|-------
58+
XPC1000 | XrmPluginCore.SourceGenerator | Info | Replaced by XPC1001
59+
XPC4000 | XrmPluginCore.SourceGenerator | Warning | Replaced by XPC5001
60+
XPC4004 | XrmPluginCore.SourceGenerator | Warning | Replaced by XPC3003
61+
XPC4005 | XrmPluginCore.SourceGenerator | Info | Replaced by XPC3002
62+
XPC4006 | XrmPluginCore.SourceGenerator | Error | Replaced by XPC4003
63+
XPC5000 | XrmPluginCore.SourceGenerator | Error | Replaced by XPC5002

XrmPluginCore.SourceGenerator/Analyzers/HandlerSignatureMismatchAnalyzer.cs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
1-
using Microsoft.CodeAnalysis;
2-
using Microsoft.CodeAnalysis.CSharp;
3-
using Microsoft.CodeAnalysis.CSharp.Syntax;
4-
using Microsoft.CodeAnalysis.Diagnostics;
5-
using System.Collections.Immutable;
6-
using System.Linq;
71
using XrmPluginCore.SourceGenerator.Helpers;
82

93
namespace XrmPluginCore.SourceGenerator.Analyzers;
104

115
/// <summary>
126
/// Analyzer that reports when a handler method signature does not match the registered images.
13-
/// Reports XPC4003 (Warning) when generated types don't exist yet, XPC4006 (Error) when they do.
7+
/// Reports XPC4002 (Warning) when generated types don't exist yet, XPC4003 (Error) when they do.
148
/// </summary>
159
[DiagnosticAnalyzer(LanguageNames.CSharp)]
1610
public class HandlerSignatureMismatchAnalyzer : DiagnosticAnalyzer
@@ -73,7 +67,7 @@ private void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
7367
var methods = TypeHelper.GetAllMethodsIncludingInherited(serviceType, methodName);
7468
if (!methods.Any())
7569
{
76-
return; // Method doesn't exist - XPC4002 handles this
70+
return; // Method doesn't exist - XPC4001 handles this
7771
}
7872

7973
// Check for registered images
@@ -103,7 +97,7 @@ private void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
10397
hasPreImage,
10498
hasPostImage);
10599

106-
// Choose diagnostic: XPC4006 (Error) if types exist, XPC4003 (Warning) if they don't
100+
// Choose diagnostic: XPC4003 (Error) if types exist, XPC4002 (Warning) if they don't
107101
var descriptor = generatedTypesExist
108102
? DiagnosticDescriptors.HandlerSignatureMismatchError
109103
: DiagnosticDescriptors.HandlerSignatureMismatch;

XrmPluginCore.SourceGenerator/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
### v1.2.3 - 28 November 2025
22
* Fix: Generate PreImage/PostImage types even when handler signature doesn't match (fixes chicken-and-egg problem where types couldn't be used until they existed)
3+
* Breaking: Reorganized diagnostic IDs by category (XPC1xxx=Info, XPC2xxx=Plugin structure, XPC3xxx=Style, XPC4xxx=Handler methods, XPC5xxx=Internal errors)
34

45
### v1.2.2 - 27 November 2025
56
* Fix: XPC4003 has been reduced to Warning to allow initial build to succeed

XrmPluginCore.SourceGenerator/CodeFixes/AddParameterlessConstructorCodeFixProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace XrmPluginCore.SourceGenerator.CodeFixes;
1818
public class AddParameterlessConstructorCodeFixProvider : CodeFixProvider
1919
{
2020
public sealed override ImmutableArray<string> FixableDiagnosticIds =>
21-
ImmutableArray.Create("XPC4001");
21+
ImmutableArray.Create(DiagnosticDescriptors.NoParameterlessConstructor.Id);
2222

2323
public sealed override FixAllProvider GetFixAllProvider() =>
2424
WellKnownFixAllProviders.BatchFixer;

XrmPluginCore.SourceGenerator/CodeFixes/CreateHandlerMethodCodeFixProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace XrmPluginCore.SourceGenerator.CodeFixes;
1919
public class CreateHandlerMethodCodeFixProvider : CodeFixProvider
2020
{
2121
public sealed override ImmutableArray<string> FixableDiagnosticIds =>
22-
ImmutableArray.Create("XPC4002");
22+
ImmutableArray.Create(DiagnosticDescriptors.HandlerMethodNotFound.Id);
2323

2424
public sealed override FixAllProvider GetFixAllProvider() =>
2525
WellKnownFixAllProviders.BatchFixer;

XrmPluginCore.SourceGenerator/CodeFixes/FixHandlerSignatureCodeFixProvider.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ namespace XrmPluginCore.SourceGenerator.CodeFixes;
1919
public class FixHandlerSignatureCodeFixProvider : CodeFixProvider
2020
{
2121
public sealed override ImmutableArray<string> FixableDiagnosticIds =>
22-
ImmutableArray.Create("XPC4003");
22+
ImmutableArray.Create(
23+
DiagnosticDescriptors.HandlerSignatureMismatch.Id,
24+
DiagnosticDescriptors.HandlerSignatureMismatchError.Id);
2325

2426
public sealed override FixAllProvider GetFixAllProvider() =>
2527
WellKnownFixAllProviders.BatchFixer;

0 commit comments

Comments
 (0)