diff --git a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs
index 1f2a5d7de00a03..c2c9908398848e 100644
--- a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs
+++ b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Emitter.cs
@@ -37,7 +37,7 @@ public partial class RegexGenerator
};
/// Generates the code for one regular expression class.
- private static (string, ImmutableArray) EmitRegexType(RegexType regexClass, Compilation compilation)
+ private static (string, ImmutableArray) EmitRegexType(RegexType regexClass, bool allowUnsafe)
{
var sb = new StringBuilder(1024);
var writer = new IndentedTextWriter(new StringWriter(sb));
@@ -78,7 +78,7 @@ private static (string, ImmutableArray) EmitRegexType(RegexType rege
generatedName += ComputeStringHash(generatedName).ToString("X");
// Generate the regex type
- ImmutableArray diagnostics = EmitRegexMethod(writer, regexClass.Method, generatedName, compilation);
+ ImmutableArray diagnostics = EmitRegexMethod(writer, regexClass.Method, generatedName, allowUnsafe);
while (writer.Indent != 0)
{
@@ -145,7 +145,7 @@ static bool ExceedsMaxDepthForSimpleCodeGeneration(RegexNode node, int allowedDe
}
/// Generates the code for a regular expression method.
- private static ImmutableArray EmitRegexMethod(IndentedTextWriter writer, RegexMethod rm, string id, Compilation compilation)
+ private static ImmutableArray EmitRegexMethod(IndentedTextWriter writer, RegexMethod rm, string id, bool allowUnsafe)
{
string patternExpression = Literal(rm.Pattern);
string optionsExpression = Literal(rm.Options);
@@ -170,8 +170,6 @@ private static ImmutableArray EmitRegexMethod(IndentedTextWriter wri
return ImmutableArray.Create(Diagnostic.Create(DiagnosticDescriptors.LimitedSourceGeneration, rm.MethodSyntax.GetLocation()));
}
- bool allowUnsafe = compilation.Options is CSharpCompilationOptions { AllowUnsafe: true };
-
writer.WriteLine($"new {id}();");
writer.WriteLine();
writer.WriteLine($" private {id}()");
diff --git a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Parser.cs b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Parser.cs
index a7ba253ee2c929..5053b535ae64e6 100644
--- a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Parser.cs
+++ b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.Parser.cs
@@ -21,34 +21,42 @@ public partial class RegexGenerator
private const string RegexName = "System.Text.RegularExpressions.Regex";
private const string RegexGeneratorAttributeName = "System.Text.RegularExpressions.RegexGeneratorAttribute";
- private static bool IsSyntaxTargetForGeneration(SyntaxNode node) =>
+ private static bool IsSyntaxTargetForGeneration(SyntaxNode node, CancellationToken cancellationToken) =>
// We don't have a semantic model here, so the best we can do is say whether there are any attributes.
node is MethodDeclarationSyntax { AttributeLists: { Count: > 0 } };
- private static MethodDeclarationSyntax? GetSemanticTargetForGeneration(GeneratorSyntaxContext context)
+ private static bool IsSemanticTargetForGeneration(SemanticModel semanticModel, MethodDeclarationSyntax methodDeclarationSyntax, CancellationToken cancellationToken)
{
- var methodDeclarationSyntax = (MethodDeclarationSyntax)context.Node;
-
foreach (AttributeListSyntax attributeListSyntax in methodDeclarationSyntax.AttributeLists)
{
foreach (AttributeSyntax attributeSyntax in attributeListSyntax.Attributes)
{
- if (context.SemanticModel.GetSymbolInfo(attributeSyntax).Symbol is IMethodSymbol attributeSymbol &&
+ if (semanticModel.GetSymbolInfo(attributeSyntax, cancellationToken).Symbol is IMethodSymbol attributeSymbol &&
attributeSymbol.ContainingType.ToDisplayString() == RegexGeneratorAttributeName)
{
- return methodDeclarationSyntax;
+ return true;
}
}
}
- return null;
+ return false;
}
// Returns null if nothing to do, Diagnostic if there's an error to report, or RegexType if the type was analyzed successfully.
- private static object? GetRegexTypeToEmit(Compilation compilation, MethodDeclarationSyntax methodSyntax, CancellationToken cancellationToken)
+ private static object? GetSemanticTargetForGeneration(GeneratorSyntaxContext context, CancellationToken cancellationToken)
{
+ var methodSyntax = (MethodDeclarationSyntax)context.Node;
+ SemanticModel sm = context.SemanticModel;
+
+ if (!IsSemanticTargetForGeneration(sm, methodSyntax, cancellationToken))
+ {
+ return null;
+ }
+
+ Compilation compilation = sm.Compilation;
INamedTypeSymbol? regexSymbol = compilation.GetBestTypeByMetadataName(RegexName);
INamedTypeSymbol? regexGeneratorAttributeSymbol = compilation.GetBestTypeByMetadataName(RegexGeneratorAttributeName);
+
if (regexSymbol is null || regexGeneratorAttributeSymbol is null)
{
// Required types aren't available
@@ -61,8 +69,6 @@ private static bool IsSyntaxTargetForGeneration(SyntaxNode node) =>
return null;
}
- SemanticModel sm = compilation.GetSemanticModel(methodSyntax.SyntaxTree);
-
IMethodSymbol? regexMethodSymbol = sm.GetDeclaredSymbol(methodSyntax, cancellationToken) as IMethodSymbol;
if (regexMethodSymbol is null)
{
diff --git a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs
index 558c613eae3a8f..b840aff351e5a4 100644
--- a/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs
+++ b/src/libraries/System.Text.RegularExpressions/gen/RegexGenerator.cs
@@ -25,31 +25,31 @@ public partial class RegexGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
+ // To avoid invalidating the generator's output when anything from the compilation
+ // changes, we will extract from it the only thing we care about: whether unsafe
+ // code is allowed.
+ IncrementalValueProvider allowUnsafeProvider =
+ context.CompilationProvider
+ .Select((x, _) => x.Options is CSharpCompilationOptions { AllowUnsafe: true });
+
// Contains one entry per regex method, either the generated code for that regex method,
// a diagnostic to fail with, or null if no action should be taken for that regex.
IncrementalValueProvider> codeOrDiagnostics =
context.SyntaxProvider
- // Find all MethodDeclarationSyntax nodes attributed with RegexGenerator
- .CreateSyntaxProvider(static (s, _) => IsSyntaxTargetForGeneration(s), static (ctx, _) => GetSemanticTargetForGeneration(ctx))
+ // Find all MethodDeclarationSyntax nodes attributed with RegexGenerator and gather the required information
+ .CreateSyntaxProvider(IsSyntaxTargetForGeneration, GetSemanticTargetForGeneration)
.Where(static m => m is not null)
- // Pair each with the compilation
- .Combine(context.CompilationProvider)
-
- // Use a custom comparer that ignores the compilation. We want to avoid regenerating for regex methods
- // that haven't been changed, but any change to a regex method will change the Compilation, so we ignore
- // the Compilation for purposes of caching.
- .WithComparer(new LambdaComparer<(MethodDeclarationSyntax?, Compilation)>(
- static (left, right) => EqualityComparer.Default.Equals(left.Item1, right.Item1),
- static o => o.Item1?.GetHashCode() ?? 0))
+ // Pair each with whether unsafe code is allowed
+ .Combine(allowUnsafeProvider)
- // Get the resulting code string or error Diagnostic for each MethodDeclarationSyntax/Compilation pair
- .Select((state, cancellationToken) =>
+ // Get the resulting code string or error Diagnostic for
+ // each MethodDeclarationSyntax/allow-unsafe-blocks pair
+ .Select((state, _) =>
{
- Debug.Assert(state.Item1 is not null);
- object? result = GetRegexTypeToEmit(state.Item2, state.Item1, cancellationToken);
- return result is RegexType regexType ? EmitRegexType(regexType, state.Item2) : result;
+ Debug.Assert(state.Left is not null);
+ return state.Left is RegexType regexType ? EmitRegexType(regexType, state.Right) : state.Left;
})
.Collect();
@@ -83,21 +83,5 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
context.AddSource("RegexGenerator.g.cs", string.Join(Environment.NewLine, code));
});
}
-
- private sealed class LambdaComparer : IEqualityComparer
- {
- private readonly Func _equal;
- private readonly Func _getHashCode;
-
- public LambdaComparer(Func equal, Func getHashCode)
- {
- _equal = equal;
- _getHashCode = getHashCode;
- }
-
- public bool Equals(T? x, T? y) => _equal(x, y);
-
- public int GetHashCode(T obj) => _getHashCode(obj);
- }
}
}
diff --git a/src/libraries/System.Text.RegularExpressions/gen/Stubs.cs b/src/libraries/System.Text.RegularExpressions/gen/Stubs.cs
index 13626a4be5a3bc..2930b513e66740 100644
--- a/src/libraries/System.Text.RegularExpressions/gen/Stubs.cs
+++ b/src/libraries/System.Text.RegularExpressions/gen/Stubs.cs
@@ -84,8 +84,3 @@ public RegexReplacement(string rep, RegexNode concat, Hashtable caps) { }
public const int WholeString = -4;
}
}
-
-namespace System.Runtime.CompilerServices
-{
- internal static class IsExternalInit { }
-}
diff --git a/src/libraries/System.Text.RegularExpressions/gen/System.Text.RegularExpressions.Generator.csproj b/src/libraries/System.Text.RegularExpressions/gen/System.Text.RegularExpressions.Generator.csproj
index e6982a3b83ce0e..594725eb0aa69b 100644
--- a/src/libraries/System.Text.RegularExpressions/gen/System.Text.RegularExpressions.Generator.csproj
+++ b/src/libraries/System.Text.RegularExpressions/gen/System.Text.RegularExpressions.Generator.csproj
@@ -23,6 +23,7 @@
+