dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
19.07k stars 4.04k forks source link

`AttributeListSyntax` trivia appears to be lost after applying a code fix #72088

Open Rekkonnect opened 9 months ago

Rekkonnect commented 9 months ago

Note that this issue might be related to https://github.com/dotnet/roslyn/issues/29611, which has been standing for very long.

I have developed some code fixes, one of them applies a new attribute and either adjusts or removes a previously existing attribute.

The code fix is here.

This issue is tracked by https://github.com/Rekkonnect/GenericsAnalyzer/issues/47

Debugging

When debugging the code, I place a breakpoint right after the return statement on the code fix (here), which assigns the resulting document to the document variable. Upon inspecting the variable, calling document.GetTextAsync(cancellationToken).Result.ToString() gives me the resulting document, which includes all trivia as expected, which aligns with the expected source in the test case.

I followed the rest of the process of asserting the code fix test case, and I found that the attribute after the newly placed attribute lacks leading syntax trivia, and thus appears at the start of the line, as it is eventually asserted.

At this point, when the assertion of the document's text begins, the trivia of the attribute node that was not removed seems to have lost its leading trivia.

I have no further information about how and where this issue is caused. I believe that there is some dark magic happening with the attribute's trivia during the node replacement.

Here are the tests that test the behavior of the code fix that I have written. The first test yields:

Expected:

class C
<
    [PermittedTypes(typeof(long))]
    [PermittedBaseTypes(typeof(IEnumerable<>))]
    [OnlyPermitSpecifiedTypes]
    T
>
{
}

Actual:

class C
<
    [PermittedTypes(typeof(long))]
[PermittedBaseTypes(typeof(IEnumerable<>))]
    [OnlyPermitSpecifiedTypes]
    T
>
{
}
Rekkonnect commented 9 months ago

With further inspection on the issue, the takeaway is that the Formatter will always remove the trivia from the attributes, because this style appears to not be supported (or I'm missing some available configuration values). When the document is fully formatted, all the whitespace from the attributes and the type parameters is removed. In this particular instance it only appears to change the next line because it's near the replaced node, and the rest remain intact.

It's also noteworthy that "fix formatting" code fixes are available within the generic type parameter list, which removes the whitespace when triggered.

With all that in mind, it might not be linked to #29611.