Skip to content

Commit e994314

Browse files
C#: Replace CSharpPattern.ToFindVisitor() with static CSharpPattern.Find() (#7136)
Mirrors CSharpTemplate.Rewrite() — both are now static factories returning visitors. Adds multi-pattern overloads that eliminate manual array + foreach in recipes.
1 parent 8802960 commit e994314

File tree

2 files changed

+138
-40
lines changed

2 files changed

+138
-40
lines changed

rewrite-csharp/csharp/OpenRewrite/CSharp/Template/CSharpPattern.cs

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ public T Find<T>(T tree, Cursor cursor, Func<T, Cursor, MatchResult, T> annotato
219219
return match != null ? annotator(tree, cursor, match) : tree;
220220
}
221221

222+
// ===============================================================
223+
// Find — declarative pattern→annotator visitor factory
224+
// ===============================================================
225+
222226
/// <summary>
223227
/// Create a <see cref="CSharpVisitor{ExecutionContext}"/> that visits every node and
224228
/// adds a <see cref="SearchResult"/> marker to matches. The pattern's fast-reject in
@@ -228,14 +232,14 @@ public T Find<T>(T tree, Cursor cursor, Func<T, Cursor, MatchResult, T> annotato
228232
/// <example>
229233
/// <code>
230234
/// public override JavaVisitor&lt;ExecutionContext&gt; GetVisitor() =>
231-
/// CSharpPattern.Expression("Console.WriteLine(\"hello\")")
232-
/// .ToFindVisitor("found it");
235+
/// CSharpPattern.Find(
236+
/// CSharpPattern.Expression("Console.WriteLine(\"hello\")"),
237+
/// "found it");
233238
/// </code>
234239
/// </example>
235-
public CSharpVisitor<Core.ExecutionContext> ToFindVisitor(string? description = null)
236-
{
237-
return ToFindVisitor((node, _, _) => SearchResult.Found(node, description));
238-
}
240+
public static CSharpVisitor<Core.ExecutionContext> Find(
241+
CSharpPattern pattern, string? description = null) =>
242+
new FindVisitor([pattern], (node, _, _) => SearchResult.Found(node, description));
239243

240244
/// <summary>
241245
/// Create a <see cref="CSharpVisitor{ExecutionContext}"/> that visits every node and
@@ -245,22 +249,54 @@ public T Find<T>(T tree, Cursor cursor, Func<T, Cursor, MatchResult, T> annotato
245249
/// <example>
246250
/// <code>
247251
/// public override JavaVisitor&lt;ExecutionContext&gt; GetVisitor() =>
248-
/// CSharpPattern.Expression($"new BinaryFormatter({args})")
249-
/// .ToFindVisitor((node, _, _) => Markup.CreateWarn(node, "BinaryFormatter is obsolete"));
252+
/// CSharpPattern.Find(
253+
/// CSharpPattern.Expression($"new BinaryFormatter({args})"),
254+
/// (node, _, _) => Markup.CreateWarn(node, "BinaryFormatter is obsolete"));
250255
/// </code>
251256
/// </example>
252-
public CSharpVisitor<Core.ExecutionContext> ToFindVisitor(Func<J, Cursor, MatchResult, J> annotator)
253-
{
254-
return new FindVisitor(this, annotator);
255-
}
257+
public static CSharpVisitor<Core.ExecutionContext> Find(
258+
CSharpPattern pattern, Func<J, Cursor, MatchResult, J> annotator) =>
259+
new FindVisitor([pattern], annotator);
260+
261+
/// <summary>
262+
/// Create a <see cref="CSharpVisitor{ExecutionContext}"/> that tries multiple patterns
263+
/// and adds a <see cref="SearchResult"/> marker on the first match.
264+
/// </summary>
265+
/// <example>
266+
/// <code>
267+
/// var x = Capture.Expression();
268+
/// return CSharpPattern.Find(
269+
/// [
270+
/// CSharpPattern.Expression($"{x} == double.NaN"),
271+
/// CSharpPattern.Expression($"{x} != double.NaN"),
272+
/// ],
273+
/// "Use IsNaN() instead");
274+
/// </code>
275+
/// </example>
276+
public static CSharpVisitor<Core.ExecutionContext> Find(
277+
CSharpPattern[] patterns, string? description = null) =>
278+
new FindVisitor(patterns, (node, _, _) => SearchResult.Found(node, description));
279+
280+
/// <summary>
281+
/// Create a <see cref="CSharpVisitor{ExecutionContext}"/> that tries multiple patterns
282+
/// and calls <paramref name="annotator"/> on the first match.
283+
/// </summary>
284+
public static CSharpVisitor<Core.ExecutionContext> Find(
285+
CSharpPattern[] patterns, Func<J, Cursor, MatchResult, J> annotator) =>
286+
new FindVisitor(patterns, annotator);
256287

257-
private sealed class FindVisitor(CSharpPattern pattern, Func<J, Cursor, MatchResult, J> annotator)
288+
private sealed class FindVisitor(CSharpPattern[] patterns, Func<J, Cursor, MatchResult, J> annotator)
258289
: CSharpVisitor<Core.ExecutionContext>
259290
{
260291
public override J? PostVisit(J tree, Core.ExecutionContext ctx)
261292
{
262-
var match = pattern.Match(tree, Cursor);
263-
return match != null ? annotator(tree, Cursor, match) : tree;
293+
foreach (var pattern in patterns)
294+
{
295+
var match = pattern.Match(tree, Cursor);
296+
if (match != null)
297+
return annotator(tree, Cursor, match);
298+
}
299+
return tree;
264300
}
265301
}
266302
}

rewrite-csharp/csharp/OpenRewrite/Tests/Template/AnnotateTests.cs

Lines changed: 87 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -109,16 +109,16 @@ public void FindDelegatesToAnnotate()
109109
}
110110

111111
// ===============================================================
112-
// ToFindVisitor
112+
// CSharpPattern.Find (static factory)
113113
// ===============================================================
114114

115115
[Fact]
116-
public void ToFindVisitorDefaultSearchResult()
116+
public void FindDefaultSearchResult()
117117
{
118118
RewriteRun(
119-
spec => spec.SetRecipe(new ToFindVisitorRecipe(
120-
CSharpPattern.Expression("Console.WriteLine(\"hello\")")
121-
.ToFindVisitor())),
119+
spec => spec.SetRecipe(new FindVisitorRecipe(
120+
CSharpPattern.Find(
121+
CSharpPattern.Expression("Console.WriteLine(\"hello\")")))),
122122
CSharp(
123123
"class C { void M() { Console.WriteLine(\"hello\"); } }",
124124
"class C { void M() { /*~~>*/Console.WriteLine(\"hello\"); } }"
@@ -127,12 +127,13 @@ public void ToFindVisitorDefaultSearchResult()
127127
}
128128

129129
[Fact]
130-
public void ToFindVisitorWithDescription()
130+
public void FindWithDescription()
131131
{
132132
RewriteRun(
133-
spec => spec.SetRecipe(new ToFindVisitorRecipe(
134-
CSharpPattern.Expression("Console.WriteLine(\"hello\")")
135-
.ToFindVisitor("found it"))),
133+
spec => spec.SetRecipe(new FindVisitorRecipe(
134+
CSharpPattern.Find(
135+
CSharpPattern.Expression("Console.WriteLine(\"hello\")"),
136+
"found it"))),
136137
CSharp(
137138
"class C { void M() { Console.WriteLine(\"hello\"); } }",
138139
"class C { void M() { /*~~(found it)~~>*/Console.WriteLine(\"hello\"); } }"
@@ -141,12 +142,13 @@ public void ToFindVisitorWithDescription()
141142
}
142143

143144
[Fact]
144-
public void ToFindVisitorWithCustomAnnotator()
145+
public void FindWithCustomAnnotator()
145146
{
146147
RewriteRun(
147-
spec => spec.SetRecipe(new ToFindVisitorRecipe(
148-
CSharpPattern.Expression("Console.WriteLine(\"hello\")")
149-
.ToFindVisitor((node, _, _) => Markup.CreateWarn(node, "Avoid Console.WriteLine")))),
148+
spec => spec.SetRecipe(new FindVisitorRecipe(
149+
CSharpPattern.Find(
150+
CSharpPattern.Expression("Console.WriteLine(\"hello\")"),
151+
(node, _, _) => Markup.CreateWarn(node, "Avoid Console.WriteLine")))),
150152
CSharp(
151153
"class C { void M() { Console.WriteLine(\"hello\"); } }",
152154
"class C { void M() { /*~~(Avoid Console.WriteLine)~~>*/Console.WriteLine(\"hello\"); } }"
@@ -155,13 +157,14 @@ public void ToFindVisitorWithCustomAnnotator()
155157
}
156158

157159
[Fact]
158-
public void ToFindVisitorAnnotatorReceivesMatchResult()
160+
public void FindAnnotatorReceivesMatchResult()
159161
{
160162
var expr = Capture.Of<Expression>("expr");
161163
RewriteRun(
162-
spec => spec.SetRecipe(new ToFindVisitorRecipe(
163-
CSharpPattern.Expression($"Console.WriteLine({expr})")
164-
.ToFindVisitor((node, _, match) =>
164+
spec => spec.SetRecipe(new FindVisitorRecipe(
165+
CSharpPattern.Find(
166+
CSharpPattern.Expression($"Console.WriteLine({expr})"),
167+
(node, _, match) =>
165168
Markup.CreateWarn(node, $"Found arg: {match.Get(expr)!.GetType().Name}")))),
166169
CSharp(
167170
"class C { void M() { Console.WriteLine(42); } }",
@@ -171,12 +174,71 @@ public void ToFindVisitorAnnotatorReceivesMatchResult()
171174
}
172175

173176
[Fact]
174-
public void ToFindVisitorNoMatchLeavesUnchanged()
177+
public void FindNoMatchLeavesUnchanged()
175178
{
176179
RewriteRun(
177-
spec => spec.SetRecipe(new ToFindVisitorRecipe(
178-
CSharpPattern.Expression("Console.Write(\"hello\")")
179-
.ToFindVisitor((node, _, _) => Markup.CreateWarn(node, "should not appear")))),
180+
spec => spec.SetRecipe(new FindVisitorRecipe(
181+
CSharpPattern.Find(
182+
CSharpPattern.Expression("Console.Write(\"hello\")"),
183+
(node, _, _) => Markup.CreateWarn(node, "should not appear")))),
184+
CSharp(
185+
"class C { void M() { Console.WriteLine(\"hello\"); } }"
186+
)
187+
);
188+
}
189+
190+
// ===============================================================
191+
// CSharpPattern.Find with multiple patterns
192+
// ===============================================================
193+
194+
[Fact]
195+
public void FindMultiplePatternsMatchesFirst()
196+
{
197+
RewriteRun(
198+
spec => spec.SetRecipe(new FindVisitorRecipe(
199+
CSharpPattern.Find(
200+
[
201+
CSharpPattern.Expression("Console.WriteLine(\"hello\")"),
202+
CSharpPattern.Expression("Console.WriteLine(\"world\")"),
203+
],
204+
"found it"))),
205+
CSharp(
206+
"class C { void M() { Console.WriteLine(\"hello\"); Console.WriteLine(\"world\"); } }",
207+
"class C { void M() { /*~~(found it)~~>*/Console.WriteLine(\"hello\"); /*~~(found it)~~>*/Console.WriteLine(\"world\"); } }"
208+
)
209+
);
210+
}
211+
212+
[Fact]
213+
public void FindMultiplePatternsWithCustomAnnotator()
214+
{
215+
var x = Capture.Of<Expression>("x");
216+
RewriteRun(
217+
spec => spec.SetRecipe(new FindVisitorRecipe(
218+
CSharpPattern.Find(
219+
[
220+
CSharpPattern.Expression($"{x} == double.NaN"),
221+
CSharpPattern.Expression($"{x} != double.NaN"),
222+
],
223+
(node, _, _) => Markup.CreateWarn(node, "Use IsNaN() instead")))),
224+
CSharp(
225+
"class C { bool M(double d) { return d == double.NaN; } }",
226+
"class C { bool M(double d) { return /*~~(Use IsNaN() instead)~~>*/d == double.NaN; } }"
227+
)
228+
);
229+
}
230+
231+
[Fact]
232+
public void FindMultiplePatternsNoMatchLeavesUnchanged()
233+
{
234+
RewriteRun(
235+
spec => spec.SetRecipe(new FindVisitorRecipe(
236+
CSharpPattern.Find(
237+
[
238+
CSharpPattern.Expression("Console.Write(\"hello\")"),
239+
CSharpPattern.Expression("Console.Write(\"world\")"),
240+
],
241+
"should not appear"))),
180242
CSharp(
181243
"class C { void M() { Console.WriteLine(\"hello\"); } }"
182244
)
@@ -240,12 +302,12 @@ private class Visitor(CSharpPattern pat) : CSharpVisitor<ExecutionContext>
240302
}
241303

242304
/// <summary>
243-
/// Thin recipe wrapper around a pre-built visitor from <see cref="CSharpPattern.ToFindVisitor"/>.
305+
/// Thin recipe wrapper around a pre-built visitor from <see cref="CSharpPattern.Find(CSharpPattern, string?)"/>.
244306
/// </summary>
245-
file class ToFindVisitorRecipe(CSharpVisitor<ExecutionContext> visitor) : Core.Recipe
307+
file class FindVisitorRecipe(CSharpVisitor<ExecutionContext> visitor) : Core.Recipe
246308
{
247-
public override string DisplayName => "ToFindVisitor test";
248-
public override string Description => "Test recipe wrapping ToFindVisitor.";
309+
public override string DisplayName => "Find visitor test";
310+
public override string Description => "Test recipe wrapping CSharpPattern.Find.";
249311

250312
public override JavaVisitor<ExecutionContext> GetVisitor() => visitor;
251313
}

0 commit comments

Comments
 (0)