Here is a script that
- Identifies the type information for a class using its semantic model
- Renames the class by replacing its Identifier token
- Identifies the type information for the renamed class using its semantic model
- Runs a SyntaxRewriter on the renamed class - the rewriter replaces all ocurrences of the old type info with the new type info.
(I hope) as minimally as possible:
static void Main(string[] args)
{
// This class needs to be renamed. We want to use Roslyn and symbols,
// because string-based replacement would change things that it's not supposed to.
var someScript = "public class FooBar { public NotFooBar a; FooBar string b; public OtherNamespace.FooBar c; }";
// Minimal Roslyn boilerplate
var compilation = CSharpCompilation.Create("CodeGeneration")
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddSyntaxTrees(CSharpSyntaxTree.ParseText(someScript));
var tree = compilation.SyntaxTrees[0];
var classDeclaration = tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>().Single();
var originalType = compilation.GetSemanticModel(classDeclaration.SyntaxTree).GetDeclaredSymbol(classDeclaration);
var renamedClass = classDeclaration.ReplaceToken(
classDeclaration.Identifier,
SyntaxFactory.Identifier(classDeclaration.Identifier.Text.Replace("Bar", "Baz"))
);
var semanticModel = compilation.GetSemanticModel(renamedClass.SyntaxTree);
var renamedType = semanticModel.GetDeclaredSymbol(renamedClass);
var rewritten = new TestRewriter(semanticModel, originalType, renamedType).Visit(renamedClass);
Console.Write(rewritten.GetText());
}
class TestRewriter : CSharpSyntaxRewriter
{
private readonly SemanticModel _model;
private readonly ITypeSymbol _replace;
private readonly ITypeSymbol _replacement;
public TestRewriter(SemanticModel model, ITypeSymbol replace, ITypeSymbol replacement)
{
_model = model;
_replace = replace;
_replacement = replacement;
}
public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax node)
{
return base.VisitIdentifierName(
_model.GetTypeInfo(node).Type.Equals(_replace)
? node.ReplaceToken(node.Identifier, SyntaxFactory.Identifier(_replacement.ToDisplayString()))
: node
);
}
}
When running this program, compilation.GetSemanticModel(renamedClass.SyntaxTree) causes the following exception
System.ArgumentException:SyntaxTreepublic class FooBaz{ public NotFooBar a; FooBar string b; public OtherNamespace.FooBar c; }not found to remove
If, instead of getting a semantic model from the renamed class' tree, the semantic model from the original class' tree is used, then GetTypeInfo(node) will (as expected) throw an exception because node is not a part of the model's tree.
What step am I missing that would prevent the "not found to remove" exception?