Skip to content

C#: Fix template substitution for method names and variadic args#6940

Merged
knutwannheden merged 1 commit intomainfrom
csharp/template-substitution-bug
Mar 11, 2026
Merged

C#: Fix template substitution for method names and variadic args#6940
knutwannheden merged 1 commit intomainfrom
csharp/template-substitution-bug

Conversation

@knutwannheden
Copy link
Contributor

@knutwannheden knutwannheden commented Mar 11, 2026

Problem

CSharpTemplate.Apply() outputs placeholder names instead of captured values when captures appear in method-name or variadic-argument positions:

var method = Capture.Of<Identifier>("method");
var args = Capture.Variadic<Expression>("args");

var pat = CSharpPattern.Create($"new Random().{method}({args})");
var tmpl = CSharpTemplate.Create($"Random.Shared.{method}({args})");

When matching new Random().Next(10), the template produces:

Random.Shared.__plh_method__(__plh_args__)

instead of:

Random.Shared.Next(10)

Summary

  • Fix SubstitutionVisitor to substitute placeholders in method-name positions (MethodInvocation.Name) and field-name positions (FieldAccess.Name), which the base visitor never visits
  • Fix variadic argument expansion: detect variadic capture placeholders in argument lists and replace them with the captured expression list
  • Fix MatchResult.GetList<T> to handle IReadOnlyList<object> (as stored by the pattern matcher) via Cast<T>() instead of failing the type check silently
  • Handle empty variadic captures by removing the placeholder argument rather than leaving it in the output
  • Use Cast<T>() instead of OfType<T>() to fail fast on type mismatches

Test plan

  • SubstitutesMethodNameCapture: pattern captures method name, template substitutes it into a different target
  • SubstitutesVariadicArgs: variadic capture of multiple args, template substitutes into different method
  • SubstitutesMethodNameAndVariadicArgs: combined method name + variadic args (new Random().Next(10)Random.Shared.Next(10))
  • SubstitutesFieldNameCapture: isolated field-name substitution (DateTime.NowDateTimeOffset.Now)
  • All 95 existing template tests continue to pass

…adic args

SubstitutionVisitor.VisitIdentifier was the only substitution path, but
base visitors for MethodInvocation and FieldAccess never visit their Name
children, leaving placeholders unresolved in those positions. Variadic
captures stored as IReadOnlyList<object> also failed the IReadOnlyList<T>
type check in MatchResult.GetList, returning empty and leaving placeholders.

- Override VisitMethodInvocation/VisitFieldAccess to substitute name placeholders
- Add ExpandVariadicArgs to replace variadic placeholder with captured arg list
- Fix GetList<T> to handle IReadOnlyList<object> via Cast<T>() (fails-fast on
  type mismatch instead of silently dropping with OfType)
- Handle empty variadic captures by removing placeholder rather than keeping it
@github-project-automation github-project-automation bot moved this to In Progress in OpenRewrite Mar 11, 2026
@knutwannheden knutwannheden changed the title Fix CSharp template substitution for method names and variadic args C#: Fix template substitution for method names and variadic args Mar 11, 2026
@knutwannheden knutwannheden added c# bug Something isn't working labels Mar 11, 2026
@knutwannheden knutwannheden merged commit c87dc1e into main Mar 11, 2026
2 checks passed
@knutwannheden knutwannheden deleted the csharp/template-substitution-bug branch March 11, 2026 10:33
@github-project-automation github-project-automation bot moved this from In Progress to Done in OpenRewrite Mar 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working c#

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant