Skip to content

Commit 3c897e1

Browse files
crutkasCopilot
andcommitted
Fix SYSLIB1045: Convert Regex to GeneratedRegex source generators
Convert 115 Regex instances across 38 files to use [GeneratedRegex] source-generated regular expressions for compile-time code generation and improved performance. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent fbad0dc commit 3c897e1

File tree

39 files changed

+504
-277
lines changed

39 files changed

+504
-277
lines changed

src/dsc/PowerToys.Settings.DSC.Schema.Generator/Common.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@
1212

1313
namespace PowerToys.Settings.DSC.Schema;
1414

15-
internal sealed class Common
15+
internal sealed partial class Common
1616
{
1717
private static string[] TypeParts(string name)
1818
{
19-
return Regex.Split(name.ToLower(CultureInfo.CurrentCulture), @"(?<!^)(?=[A-Z])|\.");
19+
return CamelCaseSplitRegex().Split(name.ToLower(CultureInfo.CurrentCulture));
2020
}
2121

22+
[GeneratedRegex(@"(?<!^)(?=[A-Z])|\.")]
23+
private static partial Regex CamelCaseSplitRegex();
24+
2225
internal static bool InferIsBool(Type propertyInfo)
2326
{
2427
return TypeParts(propertyInfo.Name).Any(word => word.Equals("Bool", StringComparison.OrdinalIgnoreCase) || word.Equals("Boolean", StringComparison.OrdinalIgnoreCase));

src/modules/AdvancedPaste/AdvancedPaste/Helpers/JsonHelper.cs

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,34 @@
1616

1717
namespace AdvancedPaste.Helpers
1818
{
19-
internal static class JsonHelper
19+
internal static partial class JsonHelper
2020
{
21-
// Ini parts regex
22-
private static readonly Regex IniSectionNameRegex = new Regex(@"^\[(.+)\]");
23-
private static readonly Regex IniValueLineRegex = new Regex(@"(.+?)\s*=\s*(.*)");
24-
25-
// List of supported CSV delimiters and Regex to detect separator property
21+
// List of supported CSV delimiters
2622
private static readonly char[] CsvDelimArry = [',', ';', '\t'];
27-
private static readonly Regex CsvSepIdentifierRegex = new Regex(@"^sep=(.)$", RegexOptions.IgnoreCase);
2823

2924
// CSV: Split on every occurrence of the delimiter except if it is enclosed by " and ignore two " as escaped "
3025
private static readonly string CsvDelimSepRegexStr = @"(?=(?:[^""]*""[^""]*"")*(?![^""]*""))";
3126

27+
// Ini parts regex
28+
[GeneratedRegex(@"^\[(.+)\]")]
29+
private static partial Regex IniSectionNameRegex();
30+
31+
[GeneratedRegex(@"(.+?)\s*=\s*(.*)")]
32+
private static partial Regex IniValueLineRegex();
33+
34+
// Regex to detect separator property
35+
[GeneratedRegex(@"^sep=(.)$", RegexOptions.IgnoreCase)]
36+
private static partial Regex CsvSepIdentifierRegex();
37+
3238
// CSV: Regex to remove/replace quotation marks
33-
private static readonly Regex CsvRemoveSingleQuotationMarksRegex = new Regex(@"^""(?!"")|(?<!"")""$|^""""$");
34-
private static readonly Regex CsvRemoveStartAndEndQuotationMarksRegex = new Regex(@"^""(?=(""{2})+)|(?<=(""{2})+)""$");
35-
private static readonly Regex CsvReplaceDoubleQuotationMarksRegex = new Regex(@"""{2}");
39+
[GeneratedRegex(@"^""(?!"")|(?<!"")""$|^""""$")]
40+
private static partial Regex CsvRemoveSingleQuotationMarksRegex();
41+
42+
[GeneratedRegex(@"^""(?=(""{2})+)|(?<=(""{2})+)""$")]
43+
private static partial Regex CsvRemoveStartAndEndQuotationMarksRegex();
44+
45+
[GeneratedRegex(@"""{2}")]
46+
private static partial Regex CsvReplaceDoubleQuotationMarksRegex();
3647

3748
private static bool IsJson(string text)
3849
{
@@ -98,14 +109,14 @@ internal static async Task<string> ToJsonFromXmlOrCsvAsync(DataPackageView clipb
98109
// Validate content as ini
99110
// (First line is a section name and second line is a section name or a key-value-pair.
100111
// For the second line we check both, in case the first ini section is empty.)
101-
if (lines.Length >= 2 && IniSectionNameRegex.IsMatch(lines[0]) &&
102-
(IniSectionNameRegex.IsMatch(lines[1]) || IniValueLineRegex.IsMatch(lines[1])))
112+
if (lines.Length >= 2 && IniSectionNameRegex().IsMatch(lines[0]) &&
113+
(IniSectionNameRegex().IsMatch(lines[1]) || IniValueLineRegex().IsMatch(lines[1])))
103114
{
104115
// Parse and convert Ini
105116
foreach (string line in lines)
106117
{
107-
Match lineSectionNameCheck = IniSectionNameRegex.Match(line);
108-
Match lineKeyValuePairCheck = IniValueLineRegex.Match(line);
118+
Match lineSectionNameCheck = IniSectionNameRegex().Match(line);
119+
Match lineKeyValuePairCheck = IniValueLineRegex().Match(line);
109120

110121
if (lineSectionNameCheck.Success)
111122
{
@@ -163,7 +174,7 @@ internal static async Task<string> ToJsonFromXmlOrCsvAsync(DataPackageView clipb
163174
foreach (var line in lines)
164175
{
165176
// If line is separator property line, then skip it
166-
if (CsvSepIdentifierRegex.IsMatch(line))
177+
if (CsvSepIdentifierRegex().IsMatch(line))
167178
{
168179
continue;
169180
}
@@ -222,7 +233,7 @@ private static void GetCsvDelimiter(in string[] csvLines, out char delimiter, ou
222233
if (csvLines.Length > 1)
223234
{
224235
// Try to select the delimiter based on the separator property.
225-
Match matchChar = CsvSepIdentifierRegex.Match(csvLines[0]);
236+
Match matchChar = CsvSepIdentifierRegex().Match(csvLines[0]);
226237
if (matchChar.Success)
227238
{
228239
// We can do matchChar[0] as the match only returns one character.
@@ -273,15 +284,15 @@ private static void GetCsvDelimiter(in string[] csvLines, out char delimiter, ou
273284
private static string ReplaceQuotationMarksInCsvData(string str)
274285
{
275286
// Remove first and last single quotation mark (enclosing quotation marks) and remove quotation marks of an empty data set ("").
276-
str = CsvRemoveSingleQuotationMarksRegex.Replace(str, string.Empty);
287+
str = CsvRemoveSingleQuotationMarksRegex().Replace(str, string.Empty);
277288

278289
// Remove first quotation mark if followed by pairs of quotation marks
279290
// and remove last quotation mark if precede by pairs of quotation marks.
280291
// (Removes enclosing quotation marks around the cell data for data like /"""abc"""/.)
281-
str = CsvRemoveStartAndEndQuotationMarksRegex.Replace(str, string.Empty);
292+
str = CsvRemoveStartAndEndQuotationMarksRegex().Replace(str, string.Empty);
282293

283294
// Replace pairs of two quotation marks with a single quotation mark. (Escaped quotation marks.)
284-
str = CsvReplaceDoubleQuotationMarksRegex.Replace(str, "\"");
295+
str = CsvReplaceDoubleQuotationMarksRegex().Replace(str, "\"");
285296

286297
return str;
287298
}

src/modules/AdvancedPaste/AdvancedPaste/Helpers/MarkdownHelper.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,17 @@
1313

1414
namespace AdvancedPaste.Helpers
1515
{
16-
internal static class MarkdownHelper
16+
internal static partial class MarkdownHelper
1717
{
18+
[GeneratedRegex(@"<!--StartFragment-->|<!--EndFragment-->")]
19+
private static partial Regex FragmentCommentsRegex();
20+
21+
[GeneratedRegex(@"\s{2,}")]
22+
private static partial Regex ExcessiveWhitespaceRegex();
23+
24+
[GeneratedRegex(@"[\r\n]+")]
25+
private static partial Regex LineBreaksRegex();
26+
1827
public static async Task<string> ToMarkdownAsync(DataPackageView clipboardData)
1928
{
2029
Logger.LogTrace();
@@ -31,7 +40,7 @@ private static string CleanHtml(string html)
3140
Logger.LogTrace();
3241

3342
// Remove the "StartFragment" and "EndFragment" comments
34-
html = Regex.Replace(html, @"<!--StartFragment-->|<!--EndFragment-->", string.Empty);
43+
html = FragmentCommentsRegex().Replace(html, string.Empty);
3544

3645
HtmlDocument document = new HtmlDocument();
3746
document.LoadHtml(html);
@@ -95,8 +104,8 @@ private static void CleanUpWhitespace(HtmlNode node)
95104
// Clean up line breaks and excessive whitespace
96105
if (node.NodeType == HtmlNodeType.Text)
97106
{
98-
node.InnerHtml = Regex.Replace(node.InnerHtml, @"\s{2,}", " ");
99-
node.InnerHtml = Regex.Replace(node.InnerHtml, @"[\r\n]+", string.Empty);
107+
node.InnerHtml = ExcessiveWhitespaceRegex().Replace(node.InnerHtml, " ");
108+
node.InnerHtml = LineBreaksRegex().Replace(node.InnerHtml, string.Empty);
100109
}
101110
else
102111
{

src/modules/Hosts/HostsUILib/Helpers/ValidationHelper.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace HostsUILib.Helpers
99
{
10-
public static class ValidationHelper
10+
public static partial class ValidationHelper
1111
{
1212
/// <summary>
1313
/// Determines whether the address is a valid IPv4
@@ -19,8 +19,7 @@ public static bool ValidIPv4(string address)
1919
return false;
2020
}
2121

22-
var regex = new Regex("^(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
23-
return regex.IsMatch(address);
22+
return IPv4Regex().IsMatch(address);
2423
}
2524

2625
/// <summary>
@@ -33,8 +32,7 @@ public static bool ValidIPv6(string address)
3332
return false;
3433
}
3534

36-
var regex = new Regex("^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$");
37-
return regex.IsMatch(address);
35+
return IPv6Regex().IsMatch(address);
3836
}
3937

4038
/// <summary>
@@ -64,5 +62,11 @@ public static bool ValidHosts(string hosts, bool validateHostsLength)
6462

6563
return true;
6664
}
65+
66+
[GeneratedRegex("^(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$")]
67+
private static partial Regex IPv4Regex();
68+
69+
[GeneratedRegex("^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$")]
70+
private static partial Regex IPv6Regex();
6771
}
6872
}

src/modules/MeasureTool/Tests/ScreenRuler.UITests/TestHelper.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
namespace ScreenRuler.UITests
1414
{
15-
public static class TestHelper
15+
public static partial class TestHelper
1616
{
1717
private static readonly string[] ShortcutSeparators = { " + ", "+", " " };
1818

@@ -330,8 +330,8 @@ public static bool ValidateSpacingClipboardContent(string clipboardText, string
330330

331331
return spacingType switch
332332
{
333-
"Spacing" => Regex.IsMatch(clipboardText, @"\d+\s*[�x×]\s*\d+"),
334-
"Horizontal Spacing" or "Vertical Spacing" => Regex.IsMatch(clipboardText, @"^\d+$"),
333+
"Spacing" => SpacingPatternRegex().IsMatch(clipboardText),
334+
"Horizontal Spacing" or "Vertical Spacing" => DigitsOnlyRegex().IsMatch(clipboardText),
335335
_ => false,
336336
};
337337
}
@@ -462,5 +462,11 @@ private static void ValidateClipboardResults(string testName)
462462
containsValidPattern,
463463
$"{testName}: Clipboard should contain valid spacing measurement, but contained: '{clipboardText}'");
464464
}
465+
466+
[GeneratedRegex(@"\d+\s*[�x×]\s*\d+")]
467+
private static partial Regex SpacingPatternRegex();
468+
469+
[GeneratedRegex(@"^\d+$")]
470+
private static partial Regex DigitsOnlyRegex();
465471
}
466472
}

src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage2a.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ private void LinkButtonClick(object sender, System.EventArgs e)
8989
{
9090
if (GetSecureKey() != SecurityCodeField.Text)
9191
{
92-
Encryption.MyKey = Regex.Replace(SecurityCodeField.Text, @"\s+", string.Empty);
92+
Encryption.MyKey = WhitespaceRegex().Replace(SecurityCodeField.Text, string.Empty);
9393
SecurityCode = Encryption.MyKey;
9494
}
9595

@@ -113,5 +113,8 @@ private void CollapseHelpButtonClick(object sender, System.EventArgs e)
113113
HelpLabel.Hide();
114114
CollapseHelpButton.Hide();
115115
}
116+
117+
[GeneratedRegex(@"\s+")]
118+
private static partial Regex WhitespaceRegex();
116119
}
117120
}

src/modules/MouseWithoutBorders/App/Form/frmMatrix.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ private void ButtonOK_Click(object sender, EventArgs e)
7272
{
7373
buttonOK.Enabled = false;
7474

75-
if (!UpdateKey(Regex.Replace(textBoxEnc.Text, @"\s+", string.Empty)))
75+
if (!UpdateKey(WhitespaceRegex().Replace(textBoxEnc.Text, string.Empty)))
7676
{
7777
buttonOK.Enabled = true;
7878
return;
@@ -1233,5 +1233,8 @@ private void PaintMyLogo()
12331233
g2.Dispose();
12341234
}
12351235
#endif
1236+
1237+
[GeneratedRegex(@"\s+")]
1238+
private static partial Regex WhitespaceRegex();
12361239
}
12371240
}

src/modules/PowerOCR/PowerOCR/Helpers/OcrExtensions.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
namespace PowerOCR.Helpers
2222
{
23-
internal static class OcrExtensions
23+
internal static partial class OcrExtensions
2424
{
2525
public static void GetTextFromOcrLine(this OcrLine ocrLine, bool isSpaceJoiningOCRLang, StringBuilder text)
2626
{
@@ -40,13 +40,11 @@ public static void GetTextFromOcrLine(this OcrLine ocrLine, bool isSpaceJoiningO
4040
bool isFirstWord = true;
4141
bool isPrevWordSpaceJoining = false;
4242

43-
Regex regexSpaceJoiningWord = new(@"(^[\p{L}-[\p{Lo}]]|\p{Nd}$)|.{2,}");
44-
4543
foreach (OcrWord ocrWord in ocrLine.Words)
4644
{
4745
string wordString = ocrWord.Text;
4846

49-
bool isThisWordSpaceJoining = regexSpaceJoiningWord.IsMatch(wordString);
47+
bool isThisWordSpaceJoining = SpaceJoiningWordRegex().IsMatch(wordString);
5048

5149
if (isFirstWord || (!isThisWordSpaceJoining && !isPrevWordSpaceJoining))
5250
{
@@ -104,5 +102,8 @@ internal static async Task<OcrResult> GetOcrResultFromImageAsync(Bitmap bmp, Lan
104102
OcrEngine ocrEngine = OcrEngine.TryCreateFromLanguage(language);
105103
return await ocrEngine.RecognizeAsync(softwareBmp);
106104
}
105+
106+
[GeneratedRegex(@"(^[\p{L}-[\p{Lo}]]|\p{Nd}$)|.{2,}")]
107+
private static partial Regex SpaceJoiningWordRegex();
107108
}
108109
}

src/modules/PowerOCR/PowerOCR/Helpers/StringHelpers.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
namespace PowerOCR.Helpers;
1010

11-
internal static class StringHelpers
11+
internal static partial class StringHelpers
1212
{
1313
public static string MakeStringSingleLine(this string textToEdit)
1414
{
@@ -25,8 +25,7 @@ public static string MakeStringSingleLine(this string textToEdit)
2525
workingString.Replace('\n', ' ');
2626
workingString.Replace('\r', ' ');
2727

28-
Regex regex = new("[ ]{2,}");
29-
string temp = regex.Replace(workingString.ToString(), " ");
28+
string temp = MultipleSpacesRegex().Replace(workingString.ToString(), " ");
3029
workingString.Clear();
3130
workingString.Append(temp);
3231
if (workingString[0] == ' ')
@@ -41,4 +40,7 @@ public static string MakeStringSingleLine(this string textToEdit)
4140

4241
return workingString.ToString();
4342
}
43+
44+
[GeneratedRegex("[ ]{2,}")]
45+
private static partial Regex MultipleSpacesRegex();
4446
}

src/modules/Workspaces/WorkspacesCsharpLibrary/Models/BaseApplication.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,7 @@ public bool IsPackagedApp
192192
else
193193
{
194194
string appPath = AppPath.Replace("C:\\Program Files\\WindowsApps\\", string.Empty);
195-
Regex packagedAppPathRegex = new Regex(@"(?<APPID>[^_]*)_\d+.\d+.\d+.\d+_(:?x64|arm64)__(?<PublisherID>[^\\]*)", RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
196-
Match match = packagedAppPathRegex.Match(appPath);
195+
Match match = PackagedAppPathRegex().Match(appPath);
197196
_isPackagedApp = match.Success;
198197
if (match.Success)
199198
{
@@ -213,5 +212,8 @@ public void Dispose()
213212
{
214213
GC.SuppressFinalize(this);
215214
}
215+
216+
[GeneratedRegex(@"(?<APPID>[^_]*)_\d+.\d+.\d+.\d+_(:?x64|arm64)__(?<PublisherID>[^\\]*)", RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)]
217+
private static partial Regex PackagedAppPathRegex();
216218
}
217219
}

0 commit comments

Comments
 (0)