Demeter and Roslyn–part 3–config and tests
Beyond the Squiggles: Flexible Reporting and Confidence Through Testing
So, we’ve built our Law of Demeter analyzer, RSCG_Demeter, using Roslyn to intelligently spot those overly-chatty code chains, even navigating the complexities of fluent interfaces. We could just have it throw warnings or errors directly in your IDE via Roslyn diagnostics.
But let’s be honest, sometimes a flood of new diagnostics can feel a bit… harsh, especially when introducing a new rule to a large codebase. Maybe you want a summary report instead, something you can review offline or integrate into a CI process?
Flexibility First: Sending Demeter Violations to a File
I wanted to offer more control. What if you could just get a list of potential violations written to a file? Good news – you can!
RSCG_Demeter allows you to configure file output directly in your .csproj. Just add these settings:
01 02 03 04 05 06 07 08 09 10 11 | <!-- In your .csproj file --> < PropertyGroup > <!-- Make the build property visible to the analyzer --> < CompilerVisibleProperty Include = "RSCG_Demeter_GenerateFile" /> </ PropertyGroup > < PropertyGroup > <!-- Define the output file path (relative to the csproj) --> <!-- Example: Output to 'DemeterReport.txt' in the project's parent directory --> < RSCG_Demeter_GenerateFile >../DemeterReport.txt</ RSCG_Demeter_GenerateFile > </ PropertyGroup > |
With this configuration, instead of diagnostics, the analyzer will neatly output all identified potential violations to the specified file.
How does it work under the hood? The analyzer uses Roslyn’s AnalyzerConfigOptionsProvider to read this custom MSBuild property. It even cleverly grabs the project directory (build_property.ProjectDir) to correctly resolve relative paths:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // Simplified snippet showing how the analyzer reads the setting var writeToFile = context.AnalyzerConfigOptionsProvider.Select((provider, ct) => { // Try reading the custom build property provider.GlobalOptions.TryGetValue( "build_property.RSCG_Demeter_GenerateFile" , out var filePath); if (! string .IsNullOrWhiteSpace(filePath)) { // Handle relative paths using the ProjectDir property #pragma warning disable RS1035 // Analyzer correctness rule if (!Path.IsPathRooted(filePath) && provider.GlobalOptions.TryGetValue( "build_property.ProjectDir" , out var csproj) && ! string .IsNullOrWhiteSpace(csproj)) { filePath = Path.GetFullPath(Path.Combine(csproj, filePath)); } #pragma warning restore RS1035 return filePath; // Return the absolute path } return string .Empty; // No file path configured }); // Later, the analyzer checks if 'writeToFile' has a value // and writes the report instead of creating diagnostics. |
This gives you the flexibility to choose how you want to consume the Demeter analysis results – immediate feedback via diagnostics or a consolidated report via file output.
Building Confidence: It’s Tested!
An analyzer is only useful if it’s reliable. How do we know RSCG_Demeter correctly identifies violations and doesn’t flag valid patterns like our fluent builders? Testing!
The repository includes a dedicated test project (RSCGDemeter_Tests) packed with unit tests covering various scenarios, including:
-
Simple violations
-
Fluent interface patterns (return this)
-
Complex LINQ chains
-
Edge cases
These tests ensure the logic we discussed – from the initial dot counting to the semantic analysis of return types – works as expected.
Ready to Give it a Go?
Whether you prefer direct IDE feedback or a generated report, RSCG_Demeter aims to provide a robust and flexible way to encourage adherence to the Law of Demeter in your C# projects.
Check out the code, explore the tests, and try it on your solution!
https://github.com/ignatandrei/RSCG_Demeter
Let’s write less coupled, more maintainable C# together!