1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
5 using System.Collections.Generic;
6 using System.Diagnostics;
7 using System.Globalization;
10 using System.Text.RegularExpressions.Generator;
11 using System.Threading;
12 using System.Threading.Tasks;
13 using Microsoft.CodeAnalysis;
14 using Microsoft.CodeAnalysis.CSharp;
15 using Microsoft.CodeAnalysis.Testing;
17 using VerifyCS = System.Text.RegularExpressions.Tests.CSharpCodeFixVerifier<
18 System.Text.RegularExpressions.Generator.UpgradeToGeneratedRegexAnalyzer,
19 System.Text.RegularExpressions.Generator.UpgradeToGeneratedRegexCodeFixer>;
21 namespace System.Text.RegularExpressions.Tests
23 [ActiveIssue("https://github.com/dotnet/runtime/issues/69823", TestRuntimes.Mono)]
24 public class UpgradeToGeneratedRegexAnalyzerTests
26 private const string UseRegexSourceGeneratorDiagnosticId = @"SYSLIB1045";
29 public async Task NoDiagnosticsForEmpty()
30 => await VerifyCS.VerifyAnalyzerAsync(source: string.Empty);
32 public static IEnumerable<object[]> ConstructorWithTimeoutTestData()
34 yield return new object[] { @"using System;
35 using System.Text.RegularExpressions;
39 public static void Main(string[] args)
41 var regex = new Regex("""", RegexOptions.None, TimeSpan.FromSeconds(10));
45 yield return new object[] { @"using System;
46 using System.Text.RegularExpressions;
50 public static void Main(string[] args)
52 var regex = new Regex("""", matchTimeout: TimeSpan.FromSeconds(10), options: RegexOptions.None);
56 yield return new object[] { @"using System;
57 using System.Text.RegularExpressions;
61 public static void Main(string[] args)
63 var regex = new Regex(matchTimeout: TimeSpan.FromSeconds(10), pattern: """", options: RegexOptions.None);
67 yield return new object[] { @"using System;
68 using System.Text.RegularExpressions;
72 public static void Main(string[] args)
74 var regex = new Regex(matchTimeout: TimeSpan.FromSeconds(10), options: RegexOptions.None, pattern: """");
80 [MemberData(nameof(ConstructorWithTimeoutTestData))]
81 public async Task NoDiagnosticForConstructorWithTimeout(string test)
83 await VerifyCS.VerifyAnalyzerAsync(test);
87 [MemberData(nameof(InvocationTypes))]
88 public async Task TopLevelStatements(InvocationType invocationType)
90 string isMatchInvocation = invocationType == InvocationType.Constructor ? @".IsMatch("""")" : string.Empty;
91 string test = @"using System.Text.RegularExpressions;
92 var isMatch = [|" + ConstructRegexInvocation(invocationType, pattern: "\"\"") + @"|]" + isMatchInvocation + ";";
93 string fixedCode = @"using System.Text.RegularExpressions;
94 var isMatch = MyRegex().IsMatch("""");
98 [GeneratedRegex("""")]
99 private static partial Regex MyRegex();
101 await new VerifyCS.Test
104 FixedCode = fixedCode,
105 TestState = { OutputKind = OutputKind.ConsoleApplication },
109 public static IEnumerable<object[]> StaticInvocationWithTimeoutTestData()
111 foreach (string method in new[] { "Count", "EnumerateMatches", "IsMatch", "Match", "Matches", "Split" })
113 yield return new object[] { @"using System;
114 using System.Text.RegularExpressions;
118 public static void Main(string[] args)
120 Regex." + method + @"(""input"", ""a|b"", RegexOptions.None, TimeSpan.FromSeconds(10));
125 // Replace is special since it takes an extra argument
126 yield return new object[] { @"using System;
127 using System.Text.RegularExpressions;
131 public static void Main(string[] args)
133 Regex.Replace(""input"", ""a|b"", ""replacement"" ,RegexOptions.None, TimeSpan.FromSeconds(10));
139 [MemberData(nameof(StaticInvocationWithTimeoutTestData))]
140 public async Task NoDiagnosticForStaticInvocationWithTimeout(string test)
141 => await VerifyCS.VerifyAnalyzerAsync(test);
144 [MemberData(nameof(InvocationTypes))]
145 public async Task NoDiagnosticsForNet60(InvocationType invocationType)
147 string isMatchInvocation = invocationType == InvocationType.Constructor ? @".IsMatch("""")" : string.Empty;
148 string test = @"using System.Text;
149 using System.Text.RegularExpressions;
153 public static void Main(string[] args)
155 var isMatch = " + ConstructRegexInvocation(invocationType, pattern: "\"\"") + isMatchInvocation + @";
159 await VerifyCS.VerifyAnalyzerAsync(test, ReferenceAssemblies.Net.Net60);
163 [MemberData(nameof(InvocationTypes))]
164 public async Task NoDiagnosticsForLowerLanguageVersion(InvocationType invocationType)
166 string isMatchInvocation = invocationType == InvocationType.Constructor ? @".IsMatch("""")" : string.Empty;
167 string test = @"using System.Text;
168 using System.Text.RegularExpressions;
172 public static void Main(string[] args)
174 var isMatch = " + ConstructRegexInvocation(invocationType, "\"\"") + isMatchInvocation + @";
177 await new VerifyCS.Test
181 LanguageVersion = LanguageVersion.CSharp10,
186 public async Task CodeFixSupportsInvalidPatternFromWhichOptionsCanBeParsed()
188 string pattern = ".{99999999999}"; // throws during real parse
189 string test = $@"using System.Text;
190 using System.Text.RegularExpressions;
194 public static void Main(string[] args)
196 var isMatch = [|Regex.IsMatch("""", @""{pattern}"")|];
200 string fixedSource = @$"using System.Text;
201 using System.Text.RegularExpressions;
203 public partial class Program
205 public static void Main(string[] args)
207 var isMatch = MyRegex().IsMatch("""");
210 [GeneratedRegex(@""{pattern}"")]
211 private static partial Regex MyRegex();
213 // We successfully offer the diagnostic and make the fix despite the pattern
214 // being invalid, because it was valid enough to parse out any options in the pattern.
215 await VerifyCS.VerifyCodeFixAsync(test, fixedSource);
219 public async Task CodeFixIsNotOfferedForInvalidPatternFromWhichOptionsCannotBeParsed()
221 string pattern = "\\g"; // throws during pre-parse for options
222 string test = $@"using System.Text;
223 using System.Text.RegularExpressions;
227 public static void Main(string[] args)
229 var isMatch = Regex.IsMatch("""", @""{pattern}""); // fixer not offered
232 // We need to be able to parse the pattern sufficiently that we can extract
233 // any inline options; in this case we can't, so we don't offer the fix.
234 await VerifyCS.VerifyCodeFixAsync(test, test);
237 public static IEnumerable<object[]> ConstantPatternTestData()
239 foreach (InvocationType invocationType in new[] { InvocationType.Constructor, InvocationType.StaticMethods })
241 string isMatchInvocation = invocationType == InvocationType.Constructor ? @".IsMatch("""")" : string.Empty;
242 // Test constructor with a passed in literal pattern.
243 yield return new object[] { @"using System.Text;
244 using System.Text.RegularExpressions;
248 public static void Main(string[] args)
250 var isMatch = [|" + ConstructRegexInvocation(invocationType, "\"\"") + @"|]" + isMatchInvocation + @";
252 }", @"using System.Text;
253 using System.Text.RegularExpressions;
255 public partial class Program
257 public static void Main(string[] args)
259 var isMatch = MyRegex().IsMatch("""");
262 [GeneratedRegex("""")]
263 private static partial Regex MyRegex();
266 // Test constructor with a local constant pattern.
267 yield return new object[] { @"using System.Text;
268 using System.Text.RegularExpressions;
272 public static void Main(string[] args)
274 const string pattern = @"""";
275 var isMatch = [|" + ConstructRegexInvocation(invocationType, "\"\"") + @"|]" + isMatchInvocation + @";
277 }", @"using System.Text;
278 using System.Text.RegularExpressions;
280 public partial class Program
282 public static void Main(string[] args)
284 const string pattern = @"""";
285 var isMatch = MyRegex().IsMatch("""");
288 [GeneratedRegex("""")]
289 private static partial Regex MyRegex();
292 // Test constructor with a constant field pattern.
293 yield return new object[] { @"using System.Text;
294 using System.Text.RegularExpressions;
298 private const string pattern = @"""";
300 public static void Main(string[] args)
302 var isMatch = [|" + ConstructRegexInvocation(invocationType, "\"\"") + @"|]" + isMatchInvocation + @";
304 }", @"using System.Text;
305 using System.Text.RegularExpressions;
307 public partial class Program
309 private const string pattern = @"""";
311 public static void Main(string[] args)
313 var isMatch = MyRegex().IsMatch("""");
316 [GeneratedRegex("""")]
317 private static partial Regex MyRegex();
323 [MemberData(nameof(ConstantPatternTestData))]
324 public async Task DiagnosticEmittedForConstantPattern(string test, string fixedSource)
326 await VerifyCS.VerifyCodeFixAsync(test, fixedSource);
329 public static IEnumerable<object[]> VariablePatternTestData()
331 foreach (InvocationType invocationType in new[] { InvocationType.Constructor, InvocationType.StaticMethods })
333 string isMatchInvocation = invocationType == InvocationType.Constructor ? @".IsMatch("""")" : string.Empty;
334 // Test constructor with passed in parameter
335 yield return new object[] { @"using System.Text;
336 using System.Text.RegularExpressions;
340 public static void Main(string[] args)
342 var isMatch = " + ConstructRegexInvocation(invocationType, "args[0]") + isMatchInvocation + @";
346 // Test constructor with passed in variable
347 yield return new object[] { @"using System.Text;
348 using System.Text.RegularExpressions;
352 public static void Main(string[] args)
354 string somePattern = """";
355 var isMatch = " + ConstructRegexInvocation(invocationType, "somePattern") + isMatchInvocation + @";
359 // Test constructor with readonly property
360 yield return new object[] { @"using System.Text.RegularExpressions;
364 public string Pattern { get; }
368 var isMatch = " + ConstructRegexInvocation(invocationType, "Pattern") + isMatchInvocation + @";
372 // Test constructor with field
373 yield return new object[] { @"using System.Text.RegularExpressions;
377 public readonly string Pattern;
381 var isMatch = " + ConstructRegexInvocation(invocationType, "Pattern") + isMatchInvocation + @";
385 // Test constructor with return method
386 yield return new object[] { @"using System.Text.RegularExpressions;
390 public string GetMyPattern() => """";
394 var isMatch = " + ConstructRegexInvocation(invocationType, "GetMyPattern()") + isMatchInvocation + @";
401 [MemberData(nameof(VariablePatternTestData))]
402 public async Task DiagnosticNotEmittedForVariablePattern(string test)
403 => await VerifyCS.VerifyAnalyzerAsync(test);
405 public static IEnumerable<object[]> ConstantOptionsTestData()
407 foreach (InvocationType invocationType in new[] { InvocationType.Constructor, InvocationType.StaticMethods })
409 string isMatchInvocation = invocationType == InvocationType.Constructor ? @".IsMatch("""")" : string.Empty;
410 // Test options as passed in literal
411 yield return new object[] { @"using System.Text.RegularExpressions;
415 public static void Main(string[] args)
417 var isMatch = [|" + ConstructRegexInvocation(invocationType, "\"\"", "RegexOptions.None") + @"|]" + isMatchInvocation + @";
419 }", @"using System.Text.RegularExpressions;
421 public partial class Program
423 public static void Main(string[] args)
425 var isMatch = MyRegex().IsMatch("""");
428 [GeneratedRegex("""", RegexOptions.None)]
429 private static partial Regex MyRegex();
432 // Test options as local constant
433 yield return new object[] { @"using System.Text.RegularExpressions;
437 public static void Main(string[] args)
439 const RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
440 var isMatch = [|" + ConstructRegexInvocation(invocationType, "\"\"", "options") + @"|]" + isMatchInvocation + @";
442 }", @"using System.Text.RegularExpressions;
444 public partial class Program
446 public static void Main(string[] args)
448 const RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
449 var isMatch = MyRegex().IsMatch("""");
452 [GeneratedRegex("""", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
453 private static partial Regex MyRegex();
456 // Test options as constant field
457 yield return new object[] { @"using System.Text.RegularExpressions;
461 const RegexOptions Options = RegexOptions.None;
463 public static void Main(string[] args)
465 var isMatch = [|" + ConstructRegexInvocation(invocationType, "\"\"", "Options") + @"|]" + isMatchInvocation + @";
467 }", @"using System.Text.RegularExpressions;
469 public partial class Program
471 const RegexOptions Options = RegexOptions.None;
473 public static void Main(string[] args)
475 var isMatch = MyRegex().IsMatch("""");
478 [GeneratedRegex("""", RegexOptions.None)]
479 private static partial Regex MyRegex();
485 [MemberData(nameof(ConstantOptionsTestData))]
486 public async Task DiagnosticEmittedForConstantOptions(string test, string fixedSource)
488 await VerifyCS.VerifyCodeFixAsync(test, fixedSource);
491 public static IEnumerable<object[]> VariableOptionsTestData()
493 foreach (InvocationType invocationType in new[] { InvocationType.Constructor, InvocationType.StaticMethods })
495 string isMatchInvocation = invocationType == InvocationType.Constructor ? @".IsMatch("""")" : string.Empty;
496 // Test options as passed in parameter
497 yield return new object[] { @"using System.Text;
498 using System.Text.RegularExpressions;
502 public static void Main(RegexOptions options)
504 var isMatch = " + ConstructRegexInvocation(invocationType, "\"\"", "options") + isMatchInvocation + @";
508 // Test options as passed in variable
509 yield return new object[] { @"using System.Text;
510 using System.Text.RegularExpressions;
514 public static void Main(string[] args)
516 RegexOptions options = RegexOptions.None;
517 var isMatch = " + ConstructRegexInvocation(invocationType, "\"\"", "options") + isMatchInvocation + @";
521 // Test options as readonly property
522 yield return new object[] { @"using System.Text.RegularExpressions;
526 public RegexOptions Options { get; }
530 var isMatch = " + ConstructRegexInvocation(invocationType, "\"\"", "Options") + isMatchInvocation + @";
534 // Test options as readonly field
535 yield return new object[] { @"using System.Text.RegularExpressions;
539 public readonly RegexOptions Options;
543 var isMatch = " + ConstructRegexInvocation(invocationType, "\"\"", "Options") + isMatchInvocation + @";
547 // Test options as return method.
548 yield return new object[] { @"using System.Text.RegularExpressions;
552 public RegexOptions GetMyOptions() => RegexOptions.None;
556 var isMatch = " + ConstructRegexInvocation(invocationType, "\"\"", "GetMyOptions()") + isMatchInvocation + @";
563 [MemberData(nameof(VariableOptionsTestData))]
564 public async Task DiagnosticNotEmittedForVariableOptions(string test)
565 => await VerifyCS.VerifyAnalyzerAsync(test);
567 public static IEnumerable<object[]> StaticInvocationsAndFixedSourceTestData()
569 const string testTemplateWithOptions = @"using System.Text.RegularExpressions;
573 public static void Main(string[] args)
575 [|Regex.@@Method@@(""input"", ""a|b"", RegexOptions.None)|];
578 const string fixedSourceWithOptions = @"using System.Text.RegularExpressions;
580 public partial class Program
582 public static void Main(string[] args)
584 MyRegex().@@Method@@(""input"");
587 [GeneratedRegex(""a|b"", RegexOptions.None)]
588 private static partial Regex MyRegex();
591 const string testTemplateWithoutOptions = @"using System.Text.RegularExpressions;
595 public static void Main(string[] args)
597 [|Regex.@@Method@@(""input"", ""a|b"")|];
600 const string fixedSourceWithoutOptions = @"using System.Text.RegularExpressions;
602 public partial class Program
604 public static void Main(string[] args)
606 MyRegex().@@Method@@(""input"");
609 [GeneratedRegex(""a|b"")]
610 private static partial Regex MyRegex();
613 foreach (bool includeRegexOptions in new[] { true, false })
615 foreach (string methodName in new[] { "Count", "EnumerateMatches", "IsMatch", "Match", "Matches", "Split" })
617 if (includeRegexOptions)
619 yield return new object[] { testTemplateWithOptions.Replace("@@Method@@", methodName), fixedSourceWithOptions.Replace("@@Method@@", methodName) };
623 yield return new object[] { testTemplateWithoutOptions.Replace("@@Method@@", methodName), fixedSourceWithoutOptions.Replace("@@Method@@", methodName) };
629 // Replace has one additional parameter so we treat that case separately.
631 yield return new object[] { @"using System.Text.RegularExpressions;
635 public static void Main(string[] args)
637 [|Regex.Replace(""input"", ""a[b|c]*"", ""replacement"", RegexOptions.CultureInvariant)|];
640 ", @"using System.Text.RegularExpressions;
642 public partial class Program
644 public static void Main(string[] args)
646 MyRegex().Replace(""input"", ""replacement"");
649 [GeneratedRegex(""a[b|c]*"", RegexOptions.CultureInvariant)]
650 private static partial Regex MyRegex();
654 yield return new object[] { @"using System.Text.RegularExpressions;
658 public static void Main(string[] args)
660 [|Regex.Replace(""input"", ""a[b|c]*"", ""replacement"")|];
663 ", @"using System.Text.RegularExpressions;
665 public partial class Program
667 public static void Main(string[] args)
669 MyRegex().Replace(""input"", ""replacement"");
672 [GeneratedRegex(""a[b|c]*"")]
673 private static partial Regex MyRegex();
679 [MemberData(nameof(StaticInvocationsAndFixedSourceTestData))]
680 public async Task DiagnosticAndCodeFixForAllStaticMethods(string test, string fixedSource)
681 => await VerifyCS.VerifyCodeFixAsync(test, fixedSource);
684 public async Task CodeFixSupportsNesting()
686 string test = @"using System.Text.RegularExpressions;
690 public partial class B
694 public partial class D
698 Regex regex = [|new Regex(""pattern"", RegexOptions.IgnorePatternWhitespace)|];
705 string fixedSource = @"using System.Text.RegularExpressions;
707 public partial class A
709 public partial class B
711 public partial class C
713 public partial class D
717 Regex regex = MyRegex();
720 [GeneratedRegex(""pattern"", RegexOptions.IgnorePatternWhitespace)]
721 private static partial Regex MyRegex();
728 await VerifyCS.VerifyCodeFixAsync(test, fixedSource);
732 [MemberData(nameof(InvocationTypes))]
733 public async Task NoDiagnosticForRegexOptionsNonBacktracking(InvocationType invocationType)
735 string isMatchInvocation = invocationType == InvocationType.Constructor ? @".IsMatch("""")" : string.Empty;
736 string test = @"using System.Text.RegularExpressions;
740 public static void Main(string[] args)
742 var isMatch = " + ConstructRegexInvocation(invocationType, "\"\"", "RegexOptions.IgnoreCase | RegexOptions.NonBacktracking") + isMatchInvocation + @";
746 await VerifyCS.VerifyAnalyzerAsync(test);
750 public async Task AnayzerSupportsMultipleDiagnostics()
752 string test = @"using System.Text.RegularExpressions;
756 public static void Main()
758 Regex regex1 = [|new Regex(""a|b"")|];
759 Regex regex2 = [|new Regex(""c|d"", RegexOptions.CultureInvariant)|];
763 string fixedSource = @"using System.Text.RegularExpressions;
765 public partial class Program
767 public static void Main()
769 Regex regex1 = MyRegex();
770 Regex regex2 = MyRegex1();
773 [GeneratedRegex(""a|b"")]
774 private static partial Regex MyRegex();
775 [GeneratedRegex(""c|d"", RegexOptions.CultureInvariant)]
776 private static partial Regex MyRegex1();
779 await new VerifyCS.Test
782 FixedCode = fixedSource,
783 NumberOfFixAllIterations = 2,
788 public async Task CodeFixerSupportsNamedParameters()
790 string test = @"using System.Text.RegularExpressions;
794 static void Main(string[] args)
796 Regex r = [|new Regex(options: RegexOptions.None, pattern: ""a|b"")|];
800 string fixedSource = @"using System.Text.RegularExpressions;
802 partial class Program
804 static void Main(string[] args)
809 [GeneratedRegex(""a|b"", RegexOptions.None)]
810 private static partial Regex MyRegex();
813 await VerifyCS.VerifyCodeFixAsync(test, fixedSource);
817 public async Task CodeFixerDoesNotSimplifyStyle()
819 string test = @"using System.Text.RegularExpressions;
825 int i = (4 - 4); // this shouldn't be changed by fixer
826 Regex r = [|new Regex(options: RegexOptions.None, pattern: ""a|b"")|];
830 string fixedSource = @"using System.Text.RegularExpressions;
832 partial class Program
836 int i = (4 - 4); // this shouldn't be changed by fixer
840 [GeneratedRegex(""a|b"", RegexOptions.None)]
841 private static partial Regex MyRegex();
844 await VerifyCS.VerifyCodeFixAsync(test, fixedSource);
848 public async Task TopLevelStatements_MultipleSourceFiles()
850 await new VerifyCS.Test
854 Sources = { "public class C { }", @"var r = [|new System.Text.RegularExpressions.Regex("""")|];" },
855 OutputKind = OutputKind.ConsoleApplication,
859 Sources = { "public class C { }", @"var r = MyRegex();
861 partial class Program
863 [System.Text.RegularExpressions.GeneratedRegex("""")]
864 private static partial System.Text.RegularExpressions.Regex MyRegex();
871 public async Task LeadingAndTrailingTriviaIsPreservedByFixer()
873 string test = @"using System.Text.RegularExpressions;
877 public static string CollapseWhitespace(this string text) =>
878 [|Regex.Replace(text, "" \\s+"" , "" "")|];
881 string expectedFixedCode = @"using System.Text.RegularExpressions;
883 static partial class Class
885 public static string CollapseWhitespace(this string text) =>
886 MyRegex().Replace(text, "" "");
887 [GeneratedRegex("" \\s+"")]
888 private static partial Regex MyRegex();
891 await VerifyCS.VerifyCodeFixAsync(test, expectedFixedCode);
895 public async Task VerbatimStringLiteralSyntaxPreservedByFixer()
897 string test = @"using System.Text.RegularExpressions;
901 public static string CollapseWhitespace(this string text) =>
902 [|Regex.Replace(text, @"" \s+"" , @"" "")|];
905 string expectedFixedCode = @"using System.Text.RegularExpressions;
907 static partial class Class
909 public static string CollapseWhitespace(this string text) =>
910 MyRegex().Replace(text, @"" "");
911 [GeneratedRegex(@"" \s+"")]
912 private static partial Regex MyRegex();
915 await VerifyCS.VerifyCodeFixAsync(test, expectedFixedCode);
919 public async Task RawStringLiteralSyntaxPreservedByFixer()
921 string test = @"using System.Text.RegularExpressions;
925 public static string CollapseWhitespace(this string text) =>
926 [|Regex.Replace(text, """"""
929 """""""" hello """""" world """""""")|];
932 string expectedFixedCode = @"using System.Text.RegularExpressions;
934 static partial class Class
936 public static string CollapseWhitespace(this string text) =>
937 MyRegex().Replace(text, """""""" hello """""" world """""""");
938 [GeneratedRegex(""""""
941 private static partial Regex MyRegex();
944 await VerifyCS.VerifyCodeFixAsync(test, expectedFixedCode);
948 public async Task InterpolatedStringLiteralSyntaxPreservedByFixer()
950 string test = @"using System.Text.RegularExpressions;
952 partial class Program
954 static void Main(string[] args)
956 const string pattern = @""a|b\s\n"";
957 const string pattern2 = $""{pattern}2"";
959 Regex regex = [|new Regex(pattern2)|];
963 string expectedFixedCode = @"using System.Text.RegularExpressions;
965 partial class Program
967 static void Main(string[] args)
969 const string pattern = @""a|b\s\n"";
970 const string pattern2 = $""{pattern}2"";
972 Regex regex = MyRegex();
975 [GeneratedRegex(""a|b\\s\\n2"")]
976 private static partial Regex MyRegex();
979 await VerifyCS.VerifyCodeFixAsync(test, expectedFixedCode);
983 public async Task TestAsArgument()
985 string test = @"using System.Text.RegularExpressions;
988 void M1(Regex r) => _ = r;
989 void M2() => M1([|new Regex("""")|]);
993 string fixedCode = @"using System.Text.RegularExpressions;
994 public partial class C
996 void M1(Regex r) => _ = r;
997 void M2() => M1(MyRegex());
998 [GeneratedRegex("""")]
999 private static partial Regex MyRegex();
1003 await VerifyCS.VerifyCodeFixAsync(test, fixedCode);
1007 public async Task InvalidRegexOptions()
1009 string test = @"using System.Text.RegularExpressions;
1015 Regex regex = [|new Regex(""pattern"", (RegexOptions)0x0800)|];
1019 string fixedSource = @"using System.Text.RegularExpressions;
1021 public partial class A
1025 Regex regex = MyRegex();
1028 [GeneratedRegex(""pattern"", (RegexOptions)(2048))]
1029 private static partial Regex MyRegex();
1033 await VerifyCS.VerifyCodeFixAsync(test, fixedSource);
1037 public async Task InvalidRegexOptions_LocalConstant()
1039 string test = @"using System.Text.RegularExpressions;
1045 const RegexOptions MyOptions = (RegexOptions)0x0800;
1046 Regex regex = [|new Regex(""pattern"", MyOptions)|];
1050 string fixedSource = @"using System.Text.RegularExpressions;
1052 public partial class A
1056 const RegexOptions MyOptions = (RegexOptions)0x0800;
1057 Regex regex = MyRegex();
1060 [GeneratedRegex(""pattern"", (RegexOptions)(2048))]
1061 private static partial Regex MyRegex();
1065 await VerifyCS.VerifyCodeFixAsync(test, fixedSource);
1069 public async Task InvalidRegexOptions_Negative()
1071 string test = @"using System.Text.RegularExpressions;
1077 Regex regex = [|new Regex(""pattern"", (RegexOptions)(-10000))|];
1081 string fixedSource = @"using System.Text.RegularExpressions;
1083 public partial class A
1087 Regex regex = MyRegex();
1090 [GeneratedRegex(""pattern"", (RegexOptions)(-10000))]
1091 private static partial Regex MyRegex();
1094 await VerifyCS.VerifyCodeFixAsync(test, fixedSource);
1097 public static IEnumerable<object[]> DetectsCurrentCultureTestData()
1099 foreach (InvocationType invocationType in new[] { InvocationType.Constructor, InvocationType.StaticMethods })
1101 string isMatchInvocation = invocationType == InvocationType.Constructor ? @".IsMatch("""")" : string.Empty;
1103 foreach (bool useInlineIgnoreCase in new[] { true, false })
1105 string pattern = useInlineIgnoreCase ? "\"(?:(?>abc)(?:(?s)d|e)(?:(?:(?xi)ki)*))\"" : "\"abc\"";
1106 string options = useInlineIgnoreCase ? "RegexOptions.None" : "RegexOptions.IgnoreCase";
1108 // Test using current culture
1109 yield return new object[] { @"using System.Text.RegularExpressions;
1111 public class Program
1113 public static void Main(string[] args)
1115 var isMatch = [|" + ConstructRegexInvocation(invocationType, pattern, options) + @"|]" + isMatchInvocation + @";
1117 }", @"using System.Text.RegularExpressions;
1119 public partial class Program
1121 public static void Main(string[] args)
1123 var isMatch = MyRegex().IsMatch("""");
1126 [GeneratedRegex(" + $"{pattern}, {options}, \"{CultureInfo.CurrentCulture.Name}" + @""")]
1127 private static partial Regex MyRegex();
1130 // Test using CultureInvariant which should default to the 2 parameter constructor
1131 options = useInlineIgnoreCase ? "RegexOptions.CultureInvariant" : "RegexOptions.IgnoreCase | RegexOptions.CultureInvariant";
1132 yield return new object[] { @"using System.Text.RegularExpressions;
1134 public class Program
1136 public static void Main(string[] args)
1138 var isMatch = [|" + ConstructRegexInvocation(invocationType, pattern, options) + @"|]" + isMatchInvocation + @";
1140 }", @"using System.Text.RegularExpressions;
1142 public partial class Program
1144 public static void Main(string[] args)
1146 var isMatch = MyRegex().IsMatch("""");
1149 [GeneratedRegex(" + $"{pattern}, {options}" + @")]
1150 private static partial Regex MyRegex();
1156 public static IEnumerable<object[]> NoOptionsCultureTestData()
1158 foreach (InvocationType invocationType in new[] { InvocationType.Constructor, InvocationType.StaticMethods })
1160 string isMatchInvocation = invocationType == InvocationType.Constructor ? @".IsMatch("""")" : string.Empty;
1162 // Test no options passed in
1163 yield return new object[] { @"using System.Text.RegularExpressions;
1165 public class Program
1167 public static void Main(string[] args)
1169 var isMatch = [|" + ConstructRegexInvocation(invocationType, "\"(?i)abc\"") + @"|]" + isMatchInvocation + @";
1171 }", @"using System.Text.RegularExpressions;
1173 public partial class Program
1175 public static void Main(string[] args)
1177 var isMatch = MyRegex().IsMatch("""");
1180 [GeneratedRegex(" + $"\"(?i)abc\", RegexOptions.None, \"{CultureInfo.CurrentCulture.Name}" + @""")]
1181 private static partial Regex MyRegex();
1187 [MemberData(nameof(DetectsCurrentCultureTestData))]
1188 [MemberData(nameof(NoOptionsCultureTestData))]
1189 public async Task DetectsCurrentCulture(string test, string fixedSource)
1190 => await VerifyCS.VerifyCodeFixAsync(test, fixedSource);
1192 #region Test helpers
1194 private static string ConstructRegexInvocation(InvocationType invocationType, string pattern, string? options = null)
1195 => invocationType switch
1197 InvocationType.StaticMethods => (pattern is null, options is null) switch
1199 (false, true) => $"Regex.IsMatch(\"\", {pattern})",
1200 (false, false) => $"Regex.IsMatch(\"\", {pattern}, {options})",
1201 _ => throw new InvalidOperationException()
1203 InvocationType.Constructor => (pattern is null, options is null) switch
1205 (false, true) => $"new Regex({pattern})",
1206 (false, false) => $"new Regex({pattern}, {options})",
1207 _ => throw new InvalidOperationException()
1209 _ => throw new ArgumentOutOfRangeException(nameof(invocationType))
1212 public static IEnumerable<object[]> InvocationTypes
1215 new object[] { InvocationType.StaticMethods },
1216 new object[] { InvocationType.Constructor }
1219 public enum InvocationType
1225 #endregion Test helpers