Search code examples
roslynroslyn-code-analysis

Roslyn Analyzer: How to handle a side effect of renaming classes


I'm writing a Roslyn analyzer that renames classes matching a certain pattern. The rename "fix" successfully renames the class, but in my unit test (which uses in-memory source which oddly enough results in a virtual file named '/0/Test0.cs'), whenever I specify "RenameFile: true" for the SymbolRenameOptions to rename the class "SomeTypeName" to "RenamedSomeTypeName", my test fails with the message:

Message:  Context: Iterative code fix application file name was expected to be '/0/Test0.cs' but was 'RenamedSomeTypeName.cs' Expected string length 11 but was 22. Strings differ at index 0. Expected: "/0/Test0.cs" But was: "RenamedSomeTypeName.cs"

I tried adding a new rule to my analyzer (although logically that seems like the wrong place to capture the problem since the issue occurs as a side effect of a fix which would typically happen after the analyze phase is complete).

I thought about trying to register a new code fix to my CodeFixProvider class, but that also seems futile since there would be no analyzer event to trigger a fix.

I've searched in vain for any hint as to how to handle this side effect failure message. Is there some sort of post-fix analyzer that can capture this issue so I can mark it as expected. That is, after all, what I really want to do.


Solution

  • I traced this behavior down to the NUnitVerifier.Equal(T expected, T actual, string? message = null) method. I fixed the issue by overriding this method in a derived class and then checking if the specific string pair matched the expected behavior of the unit test. If so, return with no action, else invoke base method. As a bonus, this solution allows me to fail negative cases.

    Note, I also had to override PushContext since the base class method looks like:

    public virtual IVerifier PushContext(string context)
    {
      Assert.AreEqual((object) typeof (NUnitVerifier), (object) this.GetType());
      return (IVerifier) new NUnitVerifier(this.Context.Push(context));
    }
    

    I simply switched out my type for NUnitVerifier in the assert and then returned a instance of my custom verifier class.