Background
I use StackExchange.Precompilation to implement aspect-oriented programming in C #. Check out my repository on GitHub.
The main idea is that the client code will be able to place custom attributes for members, and the precompiler will perform syntactic conversions for any members with these attributes. A simple example is the NonNullAttribute
that I created. When NonNullAttribute
is placed in the p
parameter, the precompiler will insert
if (Object.Equals(p, null)) throw new ArgumentNullException(nameof(p));
at the beginning of the method body.
The diagnosis is amazing ...
I would like to make it difficult to use these attributes incorrectly. The best way I've found (other than intuitive design) is to create Diagnostic
time for compilation for invalid or illogical attribute uses.
For example, NonNullAttribute
does not make sense to use for the entered element values. (Even for null type values, because if you want to ensure that they are not null, you should use a non-null type instead.) Creating a Diagnostic
is a great way to inform the user about this error without crashing the assembly as an exception.
... but how to check them?
Diagnostics is a great way to isolate errors, but I also want to make sure my diagnostic code has no errors. I would like to be able to set up a unit test, which can precompile a sample code like this
public class TestClass { public void ShouldCreateDiagnostic([NonNull] int n) { } }
and confirm that the correct diagnosis has been created (or in some cases when no diagnosis has been created).
Can anyone familiar with StackExchange.Precompilation give me some tips on this?
Decision:
The answer given by @ m0sa was incredibly helpful. There are many details to implement, so here is what unit test looks like (using NUnit 3). Pay attention to using static
for SyntaxFactory
, this eliminates a lot of noise in the construction of the syntax tree.
using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using NUnit.Framework; using StackExchange.Precompilation; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; namespace MyPrecompiler.Tests { [TestFixture] public class NonNull_CompilationDiagnosticsTest { [Test] public void NonNullAttribute_CreatesDiagnosticIfAppliedToValueTypeParameter() { var context = new BeforeCompileContext { Compilation = TestCompilation_NonNullOnValueTypeParameter(), Diagnostics = new List<Diagnostic>() }; ICompileModule module = new MyPrecompiler.MyModule(); module.BeforeCompile(context); var diagnostic = context.Diagnostics.SingleOrDefault(); Assert.NotNull(diagnostic); Assert.AreEqual("MyPrecompiler: Invalid attribute usage", diagnostic.Descriptor.Title.ToString());