tom-englert / RoslynAnalyzerTesting.CSharp.Extensions

MIT License
2 stars 1 forks source link

Analyzer on project that uses generated code. #3

Open grosch-intl opened 3 months ago

grosch-intl commented 3 months ago

First, thank you for this great library and the cookbook you wrote. It was incredibly helpful.

I created a code generator project and then added an analyzer to it. I'll eventually add a code fix as well.

The code generator provides an AamDbProvider attribute by creating Attribute.g.cs in the RegisterPostInitializationOutput delegate. That is working fine. Now I want my analyzer to do some extra checks on the code. The test isn't "running" the code generator, and so I get an error saying that the type of namespace name 'AamDbProvider<>' can't be found. How do I handle that?

This is what I'm doing in the test file:

    private static Task RunTestAsync(string source, params DiagnosticResult[] expected) {
        var test = new Test()
            .AddSources(source)
            .AddReferences(typeof(AAM).Assembly)
            .AddPackages(new PackageIdentity("MySqlConnector", "2.3.7"))
            .WithLanguageVersion(LanguageVersion.LatestMajor)
            .WithReferenceAssemblies(ReferenceAssemblies.Net.Net80)
            .AddExpectedDiagnostics(expected);

        return test.RunAsync(default);
    }

    [Fact]
    public Task JsonSerializer_Should_ExistAsync() {
        const string @ns = "Testing";

        var source = $$"""
            using System.Text.Json.Serialization;
            using System.Threading.Tasks;
            using MySqlConnector;

            namespace {{@ns}};

            [AamDbProvider<MySqlConnectionStringBuilder>("MySql")]
            internal partial class ConnectionDetails {
                public MySqlConnectionStringBuilder ToBuilder() => new();
            }

            [JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
            [JsonSerializable(typeof(ConnectionDetails)]
            internal partial class DbJsonSerializerContext : JsonSerializerContext;
            """;

        var expected = Verify
            .Diagnostic(Diagnostics.JsonSerializerContextMustExist)
            .WithLocation(4, 24)
            .WithArguments(CodeGenerator.SerializerContextTypeName, "Testing");

        return RunTestAsync(source, expected);
    }
tom-englert commented 3 months ago

Of course it does not run the code generator, as it's not part of this compilation - you did not add it to the analyzers.

Running additional analyzers would be more likely and integration test, for a unit test I would rather add a snapshot of the generated code to the source of the compilation.

grosch-intl commented 3 months ago

If I add a snapshot, then my tests aren't actually running against the generated attribute itself. If I update the attribute, and don't update the tests, then I won't catch things.

How would you do this as an integration test?

tom-englert commented 3 months ago

Add the code generator to your compilation like here:

https://github.com/tom-englert/RoslynAnalyzerTesting.CSharp.Extensions/blob/0d635a539e37b721d98df100501f8a6c53032526/source/AnalyzerTesting.CSharp.Extensions/CSharpDiagnosticSuppressorTest.cs#L28-L44