NRefactory Pattern Matching

The pattern matching API for the C# AST is similar to how regular expressions work in .NET, except that it's working with AstNodes instead of characters.
First you declare a pattern, for example: "X a = new X(...);".
var pattern = new VariableDeclarationStatement {
Type = new AnyNode("type"),
Variables = {
new VariableInitializer {
Name = Pattern.AnyString,
Initializer = new ObjectCreateExpression {
Type = new Backreference("type"),
Arguments = { new Repeat(new AnyNode()) }
Initializer = new OptionalNode(new AnyNode())
}
}
}};
This is a pattern AST - it is a C# AST using the normal C# AST classes, but also contains special pattern nodes (AnyNode, Repeat, OptionalNode, Backreference, NamedNode, Choice, ...).
If you call
Match m = pattern.Match(someNode);
then m.Success will be true if someNode is a VariableDeclarationStatement that declares a single variable, which is initialized to a new object of the variable type.
Basically, Match performs a comparison of the pattern AST with someNode. If the pattern AST does not contain any pattern nodes, the match will be successful if the ASTs are syntactically identical (whitespace/comments are not compared).
AnyNode will match any non-null node, and optionally store the node it was matched against in a named capture group.
Backreference will match any node that is syntactically identical to the node that was previously stored in the named capture group.
Repeat can be used only in AstNodeCollections and will try to match its child pattern against any number of nodes - this is equivalent to * (Kleene star) in regular expressions.
OptionalNode will match null nodes, or will try to match its child pattern against the node. OptionalNode can also be used in AstNodeCollections, where it is equivalent to a Repeat with MinCount=0 and MaxCount=1.
The resulting match object can also be used to retrieve the nodes stored in the capture groups:
if (m.Success) {
m.Get<AstType>("type").Single().ReplaceWith(new SimpleType("var"));
}
This feature was originally written for the decompiler in ILSpy, but it is also very useful for refactorings. You can also implement custom pattern nodes (derive from the Pattern base class) with custom match logic - for example if you want to check the semantics of a node.