Skip to content

Match the MA0110 regex arguments by parameter instead of position - #1439

Merged
meziantou merged 1 commit into
mainfrom
feature/regex-conversion-argument-matching-2d263b
Sep 8, 2026
Merged

meziantou merged 1 commit into
mainfrom
feature/regex-conversion-argument-matching-2d263b

Conversation

@meziantou

Copy link
Copy Markdown
Owner

Problem

Roslyn lists the arguments of an operation in evaluation order, so explicitly reordered named arguments do not line up with the parameters. UseRegexSourceGeneratorAnalyzer nevertheless recorded fixed indices for the pattern, the options and the match timeout, and UseRegexSourceGeneratorFixer removed and lifted the arguments at those indices.

For this valid input:

Regex.IsMatch(pattern: "a", input: "b")

the fix produced:

[GeneratedRegex("b")]
private static partial Regex MyRegex { get; }
// ...
MyRegex.IsMatch(pattern: "a")

The pattern was taken from the wrong argument, and the generated call does not compile (CS1501) even after the real Regex source generator runs. The constructor and every supported static Regex method shared the same positional assumption. The analyzer's constant check was positional too, so it validated input instead of pattern.

What changed

UseRegexSourceGeneratorAnalyzer

  • The pattern, the options and the match timeout arguments are located from IArgumentOperation.Parameter (pattern, options, matchTimeout) instead of a hardcoded position, and the resolved indices are what the diagnostic properties carry.
  • The constant check applies to exactly those three arguments, replacing the positional skip index 0 (and index 2 for Replace) loops.
  • An overload with a parameter that is neither lifted nor a known runtime parameter (input, replacement, evaluator) is no longer reported: the code fixer would otherwise silently drop that argument.
  • The IsMatch/Match/Matches/Split and Replace branches collapse into a single path, as parameter identity makes the per-method shapes irrelevant.

UseRegexSourceGeneratorFixer

  • Argument removal moves to a shared GetRemainingArgumentSyntaxes helper used by both the type-declaration and the top-level-statement paths. It keeps the surviving arguments in their source (evaluation) order with their name: colons intact, so the evaluation order of the runtime arguments is preserved.
  • RegisterCodeFixesAsync validates the semantic model, the Regex / GeneratedRegexAttribute / RegexOptions symbols, the operation kind and the reported indices before registering either code action, following the fixer convention in AGENTS.md. Index lookups are bounds checked.

No name translation is needed when switching from the static to the instance overload: the two sets of parameters use identical names (input, replacement, evaluator). The tests compile the fixed code with the real source generator, so any mismatch would surface as CS1501.

Tests

Six tests added to UseRegexSourceGeneratorAnalyzerTests:

  • RegexIsMatch_NamedArgumentsInReverseOrder (the reported case, partial method)
  • RegexIsMatch_NamedArgumentsInReverseOrder_PartialProperty (the reported case, partial property)
  • RegexIsMatch_Options_Timeout_NamedArgumentsInReverseOrder (all four arguments reversed)
  • RegexReplace_NamedArgumentsInReverseOrder
  • NewRegex_Options_Timeout_NamedArgumentsInReverseOrder (constructor)
  • RegexIsMatch_NonConstantPatternPassedAsNamedArgument (no diagnostic; previously the positional check looked at input)

Verification

  • dotnet build — succeeded, 0 warnings.
  • dotnet test tests/Meziantou.Analyzer.Test/Meziantou.Analyzer.Test.roslyn5.9.csproj — 4186/4186 passing.
  • MA0110 tests on every Roslyn version — 41/41 on roslyn4.8 and roslyn4.14, 59/59 on roslyn5.0, roslyn5.6 and roslyn5.9.
  • dotnet run --project src/DocumentationGenerator — exit code 0, no markdown changes (no rule metadata changed).

Roslyn lists the arguments of an operation in evaluation order, so
reordered named arguments do not line up with the parameters. The
analyzer recorded fixed indices for the pattern, the options and the
match timeout, and the code fixer removed and lifted the arguments at
those indices, so `Regex.IsMatch(pattern: "a", input: "b")` was
converted to `MyRegex.IsMatch(pattern: "a")` with a `[GeneratedRegex("b")]`
attribute: the pattern was wrong and the generated call did not compile.

The analyzer now locates the pattern, the options and the match timeout
from `IArgumentOperation.Parameter`, and reports the resolved indices.
The constant check applies to those three arguments instead of relying
on their position, and an overload with an unknown parameter is no
longer reported, as the code fixer would silently drop the argument.

The code fixer keeps the surviving arguments in their source order with
their name colons, and validates the symbols, the operation and the
reported indices before registering the code actions.
@meziantou
meziantou merged commit bd01863 into main Sep 8, 2026
13 checks passed
@meziantou
meziantou deleted the feature/regex-conversion-argument-matching-2d263b branch September 8, 2026 16:08
This was referenced Sep 8, 2026
This was referenced Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant