Ho riscontrato una situazione difficile utilizzando SyntaxRewriter in Roslyn. Vorrei riscrivere certi tipi di dichiarazioni, incluse le dichiarazioni delle variabili locali. La soluzione mi impone di trasformare le dichiarazioni in questione in più istruzioni, come nel seguente esempio banale:SyntaxRewriter e più istruzioni
void method()
{
int i;
}
diventa
void method()
{
int i;
Console.WriteLine("I declared a variable.");
}
Ho visto altri esempi in cui vengono utilizzati i blocchi per raggiungere qualcosa di simile, ma ovviamente nel caso di una dichiarazione variabile, l'ambito della dichiarazione sarà interessato. Ho trovato la seguente soluzione, ma mi sto prendendo in giro. Sembra troppo complicato e richiede un'interruzione nel modello di visitatore:
class Rewriter: SyntaxRewriter
{
public override SyntaxList<TNode> VisitList<TNode>(SyntaxList<TNode> list)
{
if (typeof(TNode) == typeof(StatementSyntax))
return Syntax.List<TNode>(list.SelectMany(st => RewriteStatementInList(st as StatementSyntax).Cast<TNode>()));
else
return base.VisitList<TNode>(list);
}
private IEnumerable<SyntaxNode> RewriteStatementInList(StatementSyntax node)
{
if (node is LocalDeclarationStatementSyntax)
return PerformRewrite((LocalDeclarationStatementSyntax)node);
//else if other cases (non-visitor)
return Visit(node).AsSingleEnumerableOf();
}
private IEnumerable<SyntaxNode> PerformRewrite(LocalDeclarationStatementSyntax orig)
{
yield return orig;
yield return Syntax.ParseStatement("Console.WriteLine(\"I declared a variable.\");");
}
}
Cosa mi manca? Modificare le istruzioni e rimuoverle (tramite istruzioni vuote) sembra più semplice della riscrittura in multipli.
mio assumere la risposta:
class Rewriter : SyntaxRewriter
{
readonly ListVisitor _visitor = new ListVisitor();
public override SyntaxList<TNode> VisitList<TNode>(SyntaxList<TNode> list)
{
var result = Syntax.List(list.SelectMany(_visitor.Visit).Cast<TNode>());
return base.VisitList(result);
}
private class ListVisitor : SyntaxVisitor<IEnumerable<SyntaxNode>>
{
protected override IEnumerable<SyntaxNode> DefaultVisit(SyntaxNode node)
{
yield return node;
}
protected override IEnumerable<SyntaxNode> VisitLocalDeclarationStatement(
LocalDeclarationStatementSyntax node)
{
yield return node;
yield return Syntax.ParseStatement("Console.WriteLine(\"I declared a variable.\");");
}
}
}
Stavo pensando di farlo in questo modo. L'unico lato negativo sarebbe che IEnumerable avrà senso solo all'interno di una chiamata a VisitList. Penso che un costruttore/scrittore, sarebbe una risposta convincente, ma penso di essere ossessionato a riguardo :) Ti inviterò in futuro ragazzi, apparentemente non ho ancora abbastanza peso. –
Perché è un problema che 'IEnumerable' ha senso solo in una chiamata a' VisitList() '? Ogni volta che ha senso sostituire un nodo con una sequenza di nodi, credo che quel nodo dovrebbe essere una parte di qualche lista. – svick
In realtà, non ho notato che stavi chiamando direttamente "Visitor" e solo da "VisitList". Mi piace molto questa soluzione.Lo inserirò nella mia domanda, con 'yield return' per generare gli iteratori e annidare la classe visitor dell'elenco. –