Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SyntaxEditor.SetModifiers may not properly format the result when multiple modifiers are added at once #27835

Closed
BhaaLseN opened this issue Jun 14, 2018 · 3 comments
Labels
Area-IDE Concept-API This issue involves adding, removing, clarification, or modification of an API. help wanted The issue is "up for grabs" - add a comment if you are interested in working on it IDE-Formatter Code formatter and/or smart indent
Milestone

Comments

@BhaaLseN
Copy link

(I apologize in advance for not using the issue template; but selecting the code line and using "Open Issue" didn't suggest it. Also, I'm not sure if it is actually tied to any particular Visual Studio version since it happens in my own Analyzer using either Microsoft.CodeAnalysis.CSharp.Workspaces 2.4.0 or 2.8.2. In case it does matter, this is on Visual Studio Enterprise 2017 15.7.0)

I tried writing a CodeFix to make certain fields static and readonly; so I borrowed the following snippet:

editor.SetModifiers(fieldDeclarators.Key, editor.Generator.GetModifiers(fieldDeclarators.Key) | DeclarationModifiers.ReadOnly);

Whenever this code snippet is run (at least in my CodeFix), the resulting code is incorrectly formatted. I don't know if and when this happens during the built-in "Make read only" CodeFix, but when I copied that snippet into one of my own analyzers, the space being between readonly and the type name is missing (only in Visual Studio though when trying it on my target project; it works correctly in the Unit Test which is even more interresting).

Hence I'm assuming that the built-in CodeFix would fail in that case, just like my own does.

My CodeFix doesn't have those fancy base classes that do the work for me, so the method looks a little more involved (but should basically do just the same):

private Task<Document> MakeStaticReadonly(Document document, SyntaxNode root, SyntaxNode fieldDecl, CancellationToken cancellationToken)
{
    var syntaxEditor = new SyntaxEditor(root, document.Project.Solution.Workspace);
    syntaxEditor.SetModifiers(fieldDecl, syntaxEditor.Generator.GetModifiers(fieldDecl).WithIsReadOnly(true).WithIsStatic(true));
    var newRoot = syntaxEditor.GetChangedRoot();
    return Task.FromResult(document.WithSyntaxRoot(newRoot));
}    

When invoked on a static field

class Test
{
    private static object _field = default;
}

instead of the expected

class Test
{
    private static readonly object _field = default;
}

the result is the less compily (is that a word?) with a missing space

class Test
{
    private static readonlyobject _field = default;
}

If I use the else block (modified for single result) in my own CodeFix, the space is applied correctly:

private async Task<Document> MakeStaticReadonlyAsync(Document document, SyntaxNode root, VariableDeclaratorSyntax declarator, CancellationToken cancellationToken)
{
    var editor = new SyntaxEditor(root, document.Project.Solution.Workspace);
    var model = await document.GetSemanticModelAsync().ConfigureAwait(false);
    var generator = editor.Generator;

    var symbol = (IFieldSymbol)model.GetDeclaredSymbol(declarator);
    var fieldDeclaration = declarator.FirstAncestorOrSelf<FieldDeclarationSyntax>();
    var modifiers = generator.GetModifiers(fieldDeclaration).WithIsReadOnly(true).WithIsStatic(true);

    var newDeclaration = generator.FieldDeclaration(
            symbol.Name,
            generator.TypeExpression(symbol.Type),
            Accessibility.Private,
            modifiers,
            declarator.Initializer?.Value)
        .WithAdditionalAnnotations(Formatter.Annotation);

    editor.InsertAfter(fieldDeclaration, newDeclaration);
    editor.RemoveNode(fieldDeclaration);

    var newRoot = editor.GetChangedRoot();
    return document.WithSyntaxRoot(newRoot);
}

The result is always correct if I run the Unit Test that is created with the "Analyzer with CodeFix" template, but it isn't when run inside Visual Studio on "real" code.

I'm not sure what the proper fix for this would be (or if this is actually a real bug in roslyn code vs. me missing something while copying the code over to my CodeFix); the main apparent difference is that the loop applies .WithAdditionalAnnotations(Formatter.Annotation) to the newly generated nodes (other than the fact the whole expression is rebuilt from scratch instead of updated).

My CodeFix works just fine when the target field has no modifiers at all and both are added at the same time. Adding static also works (probably because my Code Style settings/.editorconfig specify that static should come before readonly), but adding just readonly does not.

Please let me know if theres any additional infomation I (sh|c)ould provide (other than a sample solution; which will probably have to wait until the weekend or so).

@jmarolf
Copy link
Contributor

jmarolf commented Jun 14, 2018

@BhaaLseN a quick glance looks like editor.SetModifiers is not correctly setting the formatter annotation when you add multiple modifiers at once.

@BhaaLseN
Copy link
Author

BhaaLseN commented Jun 14, 2018

Thats interresting. I'll see if I can find that piece of code myself and create a pull request later. Gave up after an hour, ended up in generated code all the time so no idea where to tackle this one.
It is still interresting that this behavior is not caught by a Unit Test; they simply run just fine.

@BhaaLseN BhaaLseN changed the title AbstractMakeFieldReadonlyCodeFixProvider may not properly format the result in all cases SyntaxEditor.SetModifiers may not properly format the result when multiple modifiers are added at once Jun 14, 2018
@jinujoseph jinujoseph added the Concept-API This issue involves adding, removing, clarification, or modification of an API. label Sep 6, 2018
@jinujoseph jinujoseph added this to the Unknown milestone Sep 6, 2018
@jinujoseph jinujoseph added the help wanted The issue is "up for grabs" - add a comment if you are interested in working on it label Sep 6, 2018
@CyrusNajmabadi CyrusNajmabadi added the IDE-Formatter Code formatter and/or smart indent label Feb 10, 2023
@github-project-automation github-project-automation bot moved this to InQueue in Small Fixes Oct 22, 2024
@CyrusNajmabadi
Copy link
Member

Fixed with: #76054

@github-project-automation github-project-automation bot moved this from InQueue to Completed in Small Fixes Nov 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-IDE Concept-API This issue involves adding, removing, clarification, or modification of an API. help wanted The issue is "up for grabs" - add a comment if you are interested in working on it IDE-Formatter Code formatter and/or smart indent
Projects
Status: Completed
Development

No branches or pull requests

5 participants