2015-07-31 5 views
6

Ho scritto un CSharpSyntaxRewriter che sto usando per rimuovere gli attributi dai metodi, ma sto cercando di mantenere qualsiasi cosa prima dell'attributo (fino al metodo precedente) quando rimuovo tutti gli attributi da un metodo.Come rimuovere tutto l'attributo membro ma lasciare una riga vuota?

Ciò funziona perfettamente per i metodi con più di un attributo, ma non per uno solo.

Ecco un Repro minima:

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); 
    } 
} 

versione completa è qui sul mio gist (prima che qualcuno fa notare tutte le altre questioni).

risultato atteso è:

namespace P 
{ 
    class Program 
    { 
     public void NoAttributes() { } 
     //??? 

     public void OneAtt1() { } 

     public void OneAtt2() { } 

     [TestMethod] 
     public void TwoAtts() { } 
    } 
} 

uscita Attuale è:

namespace P 
{ 
    class Program 
    { 
    public void NoAttributes() { } 
    public void OneAtt1() { } 
    public void OneAtt2() { } 

    [TestMethod] 
    public void TwoAtts() { } 
    } 
} 

tutte le idee su quello che devo fare per mantenere gli spazi (o anche commenti !!)?

Ho combinato con ogni combinazione di Trivia a cui riesco a pensare. La modifica dei risultati SyntaxRemoveOptions in NullReferenceException s all'interno della base di codici di Roslyn e l'utilizzo dei metodi di estensione *Trivia comporta che gli attributi non siano stati rimossi più: solo spazi bianchi.

+0

Queste eccezioni sembrano bug per me. Li ho appena riprodotti localmente e non penso che la modifica di 'SyntaxRemoveOptions' dovrebbe causare eccezioni. Dovresti creare un problema su: https://github.com/dotnet/roslyn/issues – JoshVarty

risposta

1

Come ho detto nei commenti, mi sembra un bug in Roslyn. Puoi segnalarlo e utilizzare la seguente soluzione alternativa se lo desideri.

Ho appena provato a riscrivere a livello di metodo anziché a livello di attributo. (È possibile utilizzare un approccio simile sulle proprietà)

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's 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; 
    } 
} 

Ecco l'output che ottengo. È possibile filtrare ulteriormente leadTriv se si desidera rimuovere i commenti.

My output

Si noti che questo non copre certe situazioni ... perverse:

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

perderai la curiosità tra gli attributi. La curiosità è una delle cose più difficili da ottenere quando si costruiscono i rewriter.

+0

Grazie Josh, non sono preoccupato per l'oscura curiosità, ma penso che registrerò un problema su github per la squadra. – DaveShaw

+0

Ho appena notato che questo risolve un altro problema che ho trovato dopo aver postato la domanda per cui, quando qualcuno dichiara 2 attributi all'interno di '[]' 's e include separates, con un'interruzione di riga c'erano degli spazi bianchi iniziali. – DaveShaw