-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Add CA2027 analyzer to detect redundant Regex.IsMatch guard before Regex.Match #51214
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Co-authored-by: stephentoub <[email protected]>
Co-authored-by: stephentoub <[email protected]>
...odeAnalysis.NetAnalyzers/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx
Outdated
Show resolved
Hide resolved
...Analyzers/Microsoft.NetCore.Analyzers/Runtime/AvoidRedundantRegexIsMatchBeforeMatch.Fixer.cs
Outdated
Show resolved
Hide resolved
...is.NetAnalyzers/Microsoft.NetCore.Analyzers/Runtime/AvoidRedundantRegexIsMatchBeforeMatch.cs
Show resolved
Hide resolved
...is.NetAnalyzers/Microsoft.NetCore.Analyzers/Runtime/AvoidRedundantRegexIsMatchBeforeMatch.cs
Outdated
Show resolved
Hide resolved
…ignments, update resource strings Co-authored-by: stephentoub <[email protected]>
Co-authored-by: stephentoub <[email protected]>
@copilot, there are build failures:
|
Co-authored-by: stephentoub <[email protected]>
Fixed by regenerating the auto-generated documentation files. The files now contain the updated CA2027 descriptions with the corrected resource strings. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements analyzer CA2027 to detect redundant Regex.IsMatch
guard patterns before Regex.Match
calls. The analyzer identifies cases where developers unnecessarily check Regex.IsMatch
before calling Regex.Match
with the same arguments, which duplicates the matching operation and impacts performance.
Key changes include:
- New analyzer that detects both standard guard patterns (
if (IsMatch) { Match... }
) and inverted early-return patterns (if (!IsMatch) { return; } Match...
) - Language-specific code fixers for C# and Visual Basic that remove redundant guards
- Comprehensive test coverage with 14 test cases covering various scenarios including dataflow analysis to prevent false positives
Reviewed Changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated no comments.
Show a summary per file
File | Description |
---|---|
AvoidRedundantRegexIsMatchBeforeMatch.cs | Core analyzer implementation with pattern detection and dataflow analysis |
CSharpAvoidRedundantRegexIsMatchBeforeMatch.Fixer.cs | C# code fixer that transforms guard patterns to use Match().Success |
BasicAvoidRedundantRegexIsMatchBeforeMatch.Fixer.vb | Visual Basic code fixer for removing redundant guards |
AvoidRedundantRegexIsMatchBeforeMatchTests.cs | Comprehensive test suite covering positive/negative cases and edge scenarios |
MicrosoftNetCoreAnalyzersResources.resx | Resource strings for analyzer messages and descriptions |
DiagnosticCategoryAndIdRanges.txt | Updated rule ID range to include CA2027 |
AnalyzerReleases.Unshipped.md | Added CA2027 to unshipped analyzer releases |
Multiple .xlf files | Localization files with new resource strings marked for translation |
Microsoft.CodeAnalysis.NetAnalyzers.md/.sarif | Auto-generated documentation files |
...zers/Microsoft.NetCore.Analyzers/Runtime/BasicAvoidRedundantRegexIsMatchBeforeMatch.Fixer.vb
Outdated
Show resolved
Hide resolved
...is.NetAnalyzers/Microsoft.NetCore.Analyzers/Runtime/AvoidRedundantRegexIsMatchBeforeMatch.cs
Outdated
Show resolved
Hide resolved
...is.NetAnalyzers/Microsoft.NetCore.Analyzers/Runtime/AvoidRedundantRegexIsMatchBeforeMatch.cs
Outdated
Show resolved
Hide resolved
...is.NetAnalyzers/Microsoft.NetCore.Analyzers/Runtime/AvoidRedundantRegexIsMatchBeforeMatch.cs
Outdated
Show resolved
Hide resolved
...is.NetAnalyzers/Microsoft.NetCore.Analyzers/Runtime/AvoidRedundantRegexIsMatchBeforeMatch.cs
Outdated
Show resolved
Hide resolved
....UnitTests/Microsoft.NetCore.Analyzers/Runtime/AvoidRedundantRegexIsMatchBeforeMatchTests.cs
Show resolved
Hide resolved
Co-authored-by: stephentoub <[email protected]>
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); | ||
if (root is null) | ||
{ | ||
return; | ||
} | ||
|
||
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); | ||
if (semanticModel is null) | ||
{ | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot, you can combine these blocks, e.g.
if (await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false) is not { } root ||
await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false) is not { } semanticModel)
{
return;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Combined null checks using pattern matching as suggested.
|
||
if (isNegated && IsEarlyReturnPattern(ifStatement)) | ||
{ | ||
// For inverted early return pattern: remove the entire if statement |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot, this doesn't look correct. It would still need a guard.
I think my suggestion of handling the inverted case complicates things too much. Let's revert back to just handling the original case and not handling the inverted case, both in the analyzer and the fixer. That should cover the most common cases while not adding a lot of complexity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed inverted pattern support from both analyzer and fixer. Now only handles the simple case: if (IsMatch) { Match m = ... }
. This simplifies the implementation and covers the most common use case.
return; | ||
} | ||
|
||
var diagnostic = context.Diagnostics[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot, diagnostic
isn't used until later, and it's only used once... you can just use context.Diagnostics[0]
as the argument to RegisterCodeFix
rather than using a local.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the diagnostic
local variable - now using context.Diagnostics[0]
directly in the RegisterCodeFix call.
…ode review feedback Co-authored-by: stephentoub <[email protected]>
Implementation Complete: CA2027 Analyzer for Redundant Regex.IsMatch Guard
Summary
Successfully implemented analyzer CA2027 with all code review feedback addressed.
Problem Pattern Detected
Latest Changes (This Commit)
Simplified Implementation:
if (!IsMatch) { return; }
) from both analyzer and fixer to reduce complexityif (IsMatch) { Match m = ... }
Code Quality Improvements:
is not { }
)diagnostic
local variable - usingcontext.Diagnostics[0]
directlyIsEarlyReturnPattern
,FindMatchCallAfterConditional
)Benefits:
Test Results:
✅ All 26 tests passing
✅ Zero regressions
✅ All code review feedback addressed
Original prompt
Fixes dotnet/runtime#111239
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.