How to remove all attributes of an element, but leave a blank line?

I wrote a CSharpSyntaxRewriter that I use to remove attributes from methods, but I try my best to save anything from the attribute (to the previous method) when I remove all attributes from the method.

This works great for methods with multiple attributes, but not for one.

Here's the minimum playback:

 void Main() { var code = @"namespace P { class Program { public void NoAttributes() { } //??? [TestCategory(""Atomic"")] public void OneAtt1() { } [TestCategory(""Atomic"")] public void OneAtt2() { } [TestMethod, TestCategory(""Atomic"")] public void TwoAtts() { } } }"; var tree = CSharpSyntaxTree.ParseText(code); var rewriter = new AttributeRemoverRewriter(); var rewrittenRoot = rewriter.Visit(tree.GetRoot()); Console.WriteLine(rewrittenRoot.GetText().ToString()); } public class AttributeRemoverRewriter : CSharpSyntaxRewriter { public override SyntaxNode VisitAttributeList(AttributeListSyntax attributeList) { var nodesToRemove = attributeList .Attributes .Where(att => (att.Name as IdentifierNameSyntax).Identifier.Text.StartsWith("TestCategory")) .ToArray(); if (nodesToRemove.Length == attributeList.Attributes.Count) { //Remove the entire attribute return attributeList .RemoveNode(attributeList, SyntaxRemoveOptions.KeepNoTrivia); } else { //Remove just the matching ones recursively foreach (var node in nodesToRemove) return VisitAttributeList(attributeList.RemoveNode(node, SyntaxRemoveOptions.KeepNoTrivia)); } return base.VisitAttributeList(attributeList); } } 

The full version is here on my gist (before anyone notes any other issues).

Expected Result:

 namespace P { class Program { public void NoAttributes() { } //??? public void OneAtt1() { } public void OneAtt2() { } [TestMethod] public void TwoAtts() { } } } 

Actual conclusion:

 namespace P { class Program { public void NoAttributes() { } public void OneAtt1() { } public void OneAtt2() { } [TestMethod] public void TwoAtts() { } } } 

Any ideas on what I need to do to keep spaces (or even comments !!)?

I messed up all the Trivia combinations that I can think of. Changing SyntaxRemoveOptions results in a NullReferenceException inside the Roslyn code base and using the *Trivia extension methods means that attributes that were no longer removed are just spaces.

+6
source share
1 answer

As I said in the comments, this seems like a bug in Roslyn for me. You can report this and use the following workaround if you want.

I just tried rewriting at the method level, not at the attribute level. (You can use a similar property approach)

 public class AttributeRemoverRewriter : CSharpSyntaxRewriter { public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node) { var newAttributes = new SyntaxList<AttributeListSyntax>(); foreach (var attributeList in node.AttributeLists) { var nodesToRemove = attributeList .Attributes .Where(att => (att.Name as IdentifierNameSyntax).Identifier.Text.StartsWith("TestCategory")) .ToArray(); if (nodesToRemove.Length == attributeList.Attributes.Count) { //Do not add the attribute to the list. It being removed completely. } else { //We want to remove only some of the attributes var newAttribute = (AttributeListSyntax)VisitAttributeList(attributeList.RemoveNodes(nodesToRemove, SyntaxRemoveOptions.KeepNoTrivia)); newAttributes = newAttributes.Add(newAttribute); } } //Get the leading trivia (the newlines and comments) var leadTriv = node.GetLeadingTrivia(); node = node.WithAttributeLists(newAttributes); //Append the leading trivia to the method node = node.WithLeadingTrivia(leadTriv); return node; } } 

Here I get the conclusion. You can also filter leadTriv if you want to remove comments.

My conclusion

Please note that this does not cover some ... vicious situations:

 [TestCategory(""Atomic"")] #if Debug #endif /*Trivia in unfortunate places*/ [TestCategory("test")] public void OneAtt2() { } 

You will lose the little things between attributes. Trivia is one of the most difficult things you can get when creating authors.

+1
source

All Articles