.NET Source Generators: gerando código em tempo de escrita de código!
Tags: csharp, dotnet, compiler, roslyn
Published: 2023-12-06 15:17:51

Eu sei, é confuso...

E acredito que se você leu atentamente meu post anterior [.NET 8, JIT e AOT](https://dev.to/angelobelchior/net-8-jit-e-aot-25lk), percebeu que eu citei os _Sources Generators_ como uma estratégia importante para que seja possível ter um melhor suporte ao AOT. 

---

**Antes de continuar, #VemCodar com a gente!!**

Tá afim de criar APIs robustas com .NET?

Feito **de dev para dev**, direto das trincheiras, por quem coloca **software em produção há mais de 20 anos**, o curso ["APIs robustas com ASP.NET Core 8, Entity Framework, Docker e Redis"](https://www.sympla.com.br/evento-online/apis-robustas-com-asp-net-core-8-entity-framework-docker-e-redis/2315443?referrer=www.vemcodar.com.br) traz de maneira direta, sem enrolação, tudo que você precisa saber para construir suas APIs de tal forma que elas sejam seguras, performáticas, escaláveis e, acima de tudo, dentro de um ambiente de desenvolvimento totalmente configurado utilizando Docker e Docker Compose. 

Não fique de fora! Dê um Up na sua carreira!

**O treinamento acontecerá nos dias 27/02, 28/02 e 29/02, online, das 19.30hs às 22.30hs**

**Acesse: [https://vemcodar.com.br/](https://vemcodar.com.br/)**

---

Sendo assim, resolvi explorar um pouco mais a fundo esse conteúdo. Mas não se preocupe, eu vou explicar ponto a ponto e tenho certeza de que no final dessa leitura você vai curtir demais os _Sources Generators_. Aliás, é bem provável que você já os utilize e não percebeu.

Para começar, precisamos primeiro explicar...

## O que é o Roslyn?

![Logo do Roslyn](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lw6oyd3fbfo7xnby4y96.png)

### The .NET Compiler Platform! 

**A Plataforma de Compilação do .Net!** 

É assim que o time da Microsoft apresenta o [Roslyn](https://github.com/dotnet/roslyn).

Roslyn é a implementação dos compiladores do *csharp* e do *vb.net*, simples assim. É um **projeto de código fonte aberto com muito apoio da comunidade**. E isso é realmente incrível.

Além dos compiladores em si, ele traz um **conjunto de ferramentas para análise de código** e por ser totalmente extensível é possível consumir suas inúmeras APIs e criar ferramentas que possam inspecionar, modificar e efetuar refatorações no seu código. 

Essa abordagem o torna **uma plataforma de compilação como um serviço**. 

_Compiler as a Service_.  Que coisa chique!

> E o mais interessante disso tudo é que o Roslyn é escrito em csharp. 
Exatamente isso: **O compilador do csharp é escrito em csharp**. 
Não acredita? [Clica aqui então](https://github.com/dotnet/roslyn/blob/main/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs)!

Se você parar para analisar, vai perceber que tanto o [Visual Studio](https://visualstudio.microsoft.com/pt-br/) quanto o [Visual Studio Code](https://code.visualstudio.com/) com o [Dev Kit](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit) tem as mesmas ferramentas de análise de código e refatoração. Isso se deve ao fato de utilizarem o mesmo _core_ de compilação: Roslyn. **Isso demonstra o quão extensível e flexível ele é!** 

Se você quiser, pode criar sua própria IDE com um suporte extraordinário a csharp ~~e vb.net~~! Não é incrível?
 
> O [Rider](https://www.jetbrains.com/rider/) da Jetbrains utiliza outra abordagem, espero um dia fazer um post para falar um pouco mais sobre ele.

Deixo claro aqui que minha ideia não é ir muito a fundo nesse universo de compiladores. Pretendo um dia fazer um post falando especificamente sobre isso e, claro, utilizar o Roslyn como exemplo.

Porém, agora, eu quero apenas fazer uma sutil introdução de como algumas coisas funcionam por debaixo do capô para que fique mais simples entender como os _Source Generators_ atuam no seu código. **O Roslyn é parte fundamental disso**. 

Sendo assim, imagine que ele é um sistema que expõe inúmeras APIs organizadas em grupos:

- **APIs do Compilador**:  Essas APIs expõem modelos que correspondem ao processo de análise sintática e semântica. Cada linguagem vai ter sua API.
- **APIs de Diagnóstico**: Temos aqui APIs que são acionadas no momento da análise do código, onde o compilador pode produzir um conjunto de informações que abrange desde sintaxe inválida, problemas de semântica e alertas.  
- **APIs de Script**: É possível usar as APIs de script para executar snippets de código. O  [REPL](https://learn.microsoft.com/pt-br/archive/msdn-magazine/2016/january/essential-net-csharp-scripting) interativo usa essas APIs permitindo que você utilize o csharp como a linguagem de script, executando códigos de maneira interativa enquanto programa.
- **APIs de Workspace**: Esta camada de APIs abrange dados referentes aos projetos de uma solução, assim como informações utilizadas por ferramentas de análise de código, refatoração e  IDEs. Alguns exemplos englobam as APIs de **Localização de todas as Referências**, **Formatação** e **Geração de Código**..

---

![Fases de compilação é exposição de APIs do Roslyn](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8i3kdhpwi2iiww6dnyfa.png)

A imagem acima mostra as fazes de compilação e exposição das APIs do Roslyn. Cada caixinha dessas é um universo a parte, daria até para escrever um post específico para cada uma delas, de tanta informação que existe. Porém eu recomendo muito a leitura desse artigo: [Conceitos e modelo de objeto do SDK do .NET Compiler Platform](https://learn.microsoft.com/pt-br/dotnet/csharp/roslyn-sdk/compiler-api-model). Com certeza vai sanar suas dúvidas!

Essa é a minha introdução singela ao Roslyn. Mas não se engane, ele faz muito mais coisas do que eu citei aqui. Pra fins didáticos eu quis apenas trazer a ideia de que ele disponibiliza várias APIs que são utilizadas pelas IDEs e ferramentas externas ou internas. Fique com isso na sua cabeça!

Sendo assim, vamos começar...

---

## O que são Source Generators?

_Source Generators_ é uma _feature_ que veio junto com o csharp 9.
A ideia é permitir que códigos sejam gerados _automagicamente_ durante a compilação.
Lembrando que, a cada palavra escrita no seu código csharp, uma compilação direcionada é feita, isto é, todo código que depender daquele trecho onde foi feita uma alteração, será recompilado.

Esse processo de compilação gera eventos e as IDEs capturam essas informações e aplicam regras de formatação, efetuam análises de semântica e validações, dentre outras coisas. Um exemplo singelo disso é quando você escreve algo como `items.Lenght`. Nenhum ser humano sóbrio consegue escrever a palavra *Length* corretamente logo de primeira. A gente vai errar! O compilador vai analisar! A IDE vai notificar! E a gente vai corrigir. Esse é o ciclo.

A partir do momento em que o compilador emite notificações de alteração de código, isso abre oportunidades para que, além das IDEs, outras ferramentas possam interagir com o seu projeto. É ai que entram os _Source Generators_.

_Tá ok... acho que estou entendendo. Mas o que é de fato esse Source Generator?_

Ótima pergunta! 

O _Source Generator_ nada mais é do que uma _lib_ que você instala via _nuget_. Simples assim. 

Essa biblioteca precisa referenciar alguns pacotes específicos que vão dar suporte a criação dos códigos durante a escrita de código:

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" PrivateAssets="all" />
    <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
  </ItemGroup>
</Project>
```

Acima temos um exemplo de um *.csproj simples que suporta a criação de _Source Generators_. Para mais informações clique [aqui](https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview#get-started-with-source-generators)!

Ao instalar ou referenciar um _Source Generator_ ao seu projeto, ele entra num processo de execução que é orquestrado pelo Roslyn:


![Fluxo de geração de código em tempo de escrita de código](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ii0brn2kz21kxkeefdnx.png)

A imagem acima mostra exatamente como esse processo funciona:
- Você altera seu código;
- O compilador efetua uma análise e caso exista alguma referência para um _Source Generator_ ele o aciona;
- O _Source Generator_ inspeciona seu código e em seguida gera código adicional com base em determinadas regras ou modelos. Esse código fica num arquivo físico e é incorporado ao projeto.

Dessa forma é gerado código em tempo de escrita de código! 

Sacou?

Acho que ainda não, né? Mas com os exemplos a seguir vai ficar claro. Prometo!
Aliás, pega um café... porque a coisa vai ficar mais interessante a partir de agora....

---

### Vamos falar sobre Regex

> Calma, o post ainda é sobre _Source Generators_! Eu só vou usar o _Regex_ como exemplo para demonstrar todo o poder dessa _feature_.

Em resumo, o _Regex_, ou expressão regular, é uma sequência de caracteres que forma um padrão de pesquisa. Esse padrão de pesquisa é usado principalmente para correspondência de _strings_ em operações de busca, extração ou manipulação de texto. 

_Regex_ é algo muito utilizado. E depois que o **chatGPT** foi criado todas as dificuldades que a gente tinha para criar uma, foram resolvidas. 🤓 

Feita a devida apresentação, vamos colocar em prática o uso do _Regex_.
Vamos criar um sistema que recebe um texto e extrai todos os e-mails. Algo simples:

```csharp
using System.Text.RegularExpressions;

var regex = new Regex(@"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}", RegexOptions.Compiled);

var texto = 
"""
Este é um exemplo de texto com alguns e-mails, como contato@example.com e outro@email.com.br. 
Além disso, temos mais um: email@teste.com.";
""";

ExtrairEmails(regex, texto);
return;

static void ExtrairEmails(Regex regex, string texto)
{
    var matches = regex.Matches(texto);
    
    Console.WriteLine("E-mails encontrados:");
    foreach (Match match in matches)
    {
        Console.WriteLine(match.Value);
    }
}
```

Ao executar o resultado é esse:

```bash
/Volumes/SSD/Git/SourceGeneratorSample/SourceGeneratorSample/bin/Debug/net7.0/SourceGeneratorSample 
E-mails encontrados:
contato@example.com
outro@email.com.br
email@teste.com
```

> O código acima é para fins didáticos apenas e foi gerado via chatGPT sem a mínima responsabilidade.
> Minha intenção não é explicar a sintaxe do _regex_. Caso queira saber mais sobre, acesse esse post: [Regular expression - Wikipedia](https://en.wikipedia.org/wiki/Regular_expression)

Resumidamente o que temos é a definição do padrão de busca de e-mails.
Utilizamos a classe _Regex_ passando esse padrão.
Em seguida, procuramos dentro de um determinado texto, quais conjuntos de caracteres dão _match_ (combinam) com o padrão informado, e imprimimos o resultado. Simples, simples, simples!

**Agora vamos entender em como o csharp resolve isso internamente!**

Analisando esse processo, podemos notar que num determinado momento vai ser preciso fazer um _parser_ desse _regex_. Um parser nada mais é do que uma rotina responsável por analisar e interpretar padrões definidos por expressões regulares, no nosso caso esse texto: `@"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"`. O resultado gerado por esse _parser_ é conhecido com **árvore sintática**. Essa árvore representa a estrutura hierárquica do _regex_, mostrando como os diferentes elementos (caracteres que formam o padrão) estão relacionados entre si. A partir dessa árvore, o csharp pode interpretar ou compilar essas instruções. Mais abaixo eu mostro um trecho do código que faz essa compilação (geração de IL). 

Feito esse _parser_ é necessário criar um mecanismo que receba um determinado texto e execute os comandos gerados a partir dessa interpretação ou compilação. 

Com esse mecanismo criado basta invocá-lo para obter o resultado.

**Só que tem alguns "poréns"...** 

O processo de _Regex_ utiliza muito a geração dinâmica de código. Se a gente for analisar o [código fonte da classe](https://github.com/microsoft/referencesource/blob/master/System/regex/system/text/regularexpressions/RegexCompiler.cs#L75) `RegexCompiler` podemos notar logo de cara o gigantesco uso de IL e muito _reflection_:

```csharp
internal static MethodInfo     _chartolowerM; 
internal static MethodInfo     _getcharM; 
internal static MethodInfo     _crawlposM; 
internal static MethodInfo     _charInSetM;
internal static MethodInfo     _getCurrentCulture;
internal static MethodInfo     _getInvariantCulture;
internal static MethodInfo     _checkTimeoutM;
#if DBG
	internal static MethodInfo     _dumpstateM;
#endif

internal ILGenerator     _ilg;

//.....

/*
 * Pops an element off the tracking stack (leave it on the operand stack)
 */
internal void PopTrack() {
    _ilg.Emit(OpCodes.Ldloc_S, _trackV);
    _ilg.Emit(OpCodes.Ldloc_S, _trackposV);
    _ilg.Emit(OpCodes.Dup);
    _ilg.Emit(OpCodes.Ldc_I4_1);
    _ilg.Emit(OpCodes.Add);
    _ilg.Emit(OpCodes.Stloc_S, _trackposV);
    _ilg.Emit(OpCodes.Ldelem_I4);
}
```

O código acima é apenas uma pequena parte dessa classe, mas já notamos o uso do `MethodInfo` e do `ILGenerator`. É tanto `_ilg.Emit` espalhado pela classe, que eu desisti de tentar entender como isso está funcionando. Eu particularmente curto muito brincar com o IL. 

Mas o AOT não :(

No [post anterior](https://dev.to/angelobelchior/net-8-jit-e-aot-25lk) eu explico o motivo pelo qual AOT e código gerado dinamicamente não combinam.

**E é agora, o tão aguardado momento em que, de fato, vamos conhecer esse tal _Source Generators_.**

Tardô, mas num faiô!

Primeiro, vamos começar avaliando os analisadores de código disponíveis em nossa IDE. Eu uso o Rider, mas é possível ter o mesmo resultado em qualquer ferramenta.

Olha que interessante:

![Imagem do Rider sugerindo o uso do Source Generator do Regex](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fipvmjtmhk1l0ye3g602.png)

O Rider me sugeriu transformar esse _regex_ em um _GeneratedRegexAttribute_.

Como o próprio nome diz, a ideia é utilizar um _Source Generator_ que vai avaliar o padrão informado `@"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"` e criar um código csharp para a execução dessa busca por e-mails. 

Olha que coisa incrível! Ao invés de existir um _parser_ para o _regex_ e a criação em tempo de execução de um mecanismo de busca, compilado ou interpretado, nós teremos um código pronto, gerado única e exclusivamente para esse fim. 

E eu nem preciso dizer que esse processo acaba sendo mais performático do que o anterior, já que não vamos despender tempo para criação do _parser_, interpretadores, compilações e outras coisas. 

E outra, a partir do momento que o _Source Generator_ cria esse código, nós podemos debuga-lo! 
Isso mesmo! É possível colocar um _break point_ e entender o processo todo. 

Isso é um sonho realizado: Debugar Regex! Correu uma lágrima do meu olho agora...

E como ficou esse código gerado pelo _Source Generator_?

```csharp
using System.Text.RegularExpressions;

var regex = MeusRegex.BuscaPorEmails();

var texto = 
"""
Este é um exemplo de texto com alguns e-mails, como contato@example.com e outro@email.com.br. 
Além disso, temos mais um: email@teste.com.";
""";

ExtrairEmails(regex, texto);
return;

static void ExtrairEmails(Regex regex, string texto)
{
    var matches = regex.Matches(texto);
    
    Console.WriteLine("E-mails encontrados:");
    foreach (Match match in matches)
    {
        Console.WriteLine(match.Value);
    }
}

public partial class MeusRegex
{
    [GeneratedRegex("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}")]
    public static partial Regex BuscaPorEmails();
}
```

![Surpresaaaaa](https://media.tenor.com/J7pWSpZQUzEAAAAd/shocked-shock.gif)

**Não, esse não é o código gerado pelo _Source Generator_! É como o código ficou para utilizar o código gerado pelo _Source Generator_!**

Mas precisamos esclarecer um ponto antes de ir, de fato, ao código gerado _automagicamente_.

**_Partial Class_!?!** 

Se você não sabe o que é uma _partial class_ recomendo muito a leitura desse artigo. [Partial Classes and Methods - C# Programming Guide - C# | Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods) .

> Para entender como os _Sources Generators_ funcionam é muito importante entender como o _partial class_ funciona. 

Quando a gente cria um método _partial_ dentro de uma classe _partial_ e decora esse método com o atributo `GeneratedRegex` informando qual é a _regex_, o _Source Generator_ é acionado. Ele vai avaliar o _regex_ informado e transformá-lo em código csharp.

Ok, mas onde fica esse arquivo gerado pelo _Source Generator_?

![Arquivo gerado](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2tbvldxxe9mgkwd5fhui.png)

Basicamente, dentro da estrutura do projeto, vai existir uma pasta chamada _Source Generators_, ou algo do tipo, dependendo da sua IDE. E é aí que estarão todos os arquivos gerados por  _Source Generators_.
Podemos notar que o arquivo termina com *.g.cs. Esse "g" significa _generated_ e serve justamente para identificar que o conteúdo dele foi gerado por alguma ferramenta, externa ou não.

Note que dentro desse arquivo vamos ter uma classe _partial_ chamada `MeusRegex` com a implementação do método _partial_  BuscaPorEmails. 
Em outras palavras, no nosso arquivo _program.cs_ temos a criação dessa classe com algumas definições parciais, e no arquivo gerado _automagicamente_ temos as implementações concretas!

> E nem perca seu tempo tentando alterar esse arquivo. Ele vai ser sempre reescrito pelo _Source Generator_.

E agora só falta ver o código gerado. 

Respira fundo...

```csharp
// <auto-generated/>
#nullable enable
#pragma warning disable CS0162 // Unreachable code
#pragma warning disable CS0164 // Unreferenced label
#pragma warning disable CS0219 // Variable assigned but never used

partial class MeusRegex
{
    /// <remarks>
    /// Pattern explanation:<br/>
    /// <code>
    /// ○ Match a character in the set [%+-.0-9A-Z_a-z] atomically at least once.<br/>
    /// ○ Match '@'.<br/>
    /// ○ Match a character in the set [-.0-9A-Za-z] greedily at least once.<br/>
    /// ○ Match '.'.<br/>
    /// ○ Match a character in the set [A-Za-z] atomically at least twice.<br/>
    /// </code>
    /// </remarks>
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Text.RegularExpressions.Generator", "7.0.9.1910")]
    public static partial global::System.Text.RegularExpressions.Regex BuscaPorEmails() => global::System.Text.RegularExpressions.Generated.BuscaPorEmails_0.Instance;
}

namespace System.Text.RegularExpressions.Generated
{
    using System;
    using System.CodeDom.Compiler;
    using System.Collections;
    using System.ComponentModel;
    using System.Globalization;
    using System.Runtime.CompilerServices;
    using System.Text.RegularExpressions;
    using System.Threading;

    /// <summary>Custom <see cref="Regex"/>-derived type for the BuscaPorEmails method.</summary>
    [GeneratedCodeAttribute("System.Text.RegularExpressions.Generator", "7.0.9.1910")]
    file sealed class BuscaPorEmails_0 : Regex
    {
        /// <summary>Cached, thread-safe singleton instance.</summary>
        internal static readonly BuscaPorEmails_0 Instance = new();
    
        /// <summary>Initializes the instance.</summary>
        private BuscaPorEmails_0()
        {
            base.pattern = "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}";
            base.roptions = RegexOptions.None;
            ValidateMatchTimeout(Utilities.s_defaultTimeout);
            base.internalMatchTimeout = Utilities.s_defaultTimeout;
            base.factory = new RunnerFactory();
            base.capsize = 1;
        }
    
        /// <summary>Provides a factory for creating <see cref="RegexRunner"/> instances to be used by methods on <see cref="Regex"/>.</summary>
        private sealed class RunnerFactory : RegexRunnerFactory
        {
            /// <summary>Creates an instance of a <see cref="RegexRunner"/> used by methods on <see cref="Regex"/>.</summary>
            protected override RegexRunner CreateInstance() => new Runner();
        
            /// <summary>Provides the runner that contains the custom logic implementing the specified regular expression.</summary>
            private sealed class Runner : RegexRunner
            {
                /// <summary>Scan the <paramref name="inputSpan"/> starting from base.runtextstart for the next match.</summary>
                /// <param name="inputSpan">The text being scanned by the regular expression.</param>
                protected override void Scan(ReadOnlySpan<char> inputSpan)
                {
                    // Search until we can't find a valid starting position, we find a match, or we reach the end of the input.
                    while (TryFindNextPossibleStartingPosition(inputSpan) &&
                           !TryMatchAtCurrentPosition(inputSpan) &&
                           base.runtextpos != inputSpan.Length)
                    {
                        base.runtextpos++;
                        if (Utilities.s_hasTimeout)
                        {
                            base.CheckTimeout();
                        }
                    }
                }
        
                /// <summary>Search <paramref name="inputSpan"/> starting from base.runtextpos for the next location a match could possibly start.</summary>
                /// <param name="inputSpan">The text being scanned by the regular expression.</param>
                /// <returns>true if a possible match was found; false if no more matches are possible.</returns>
                private bool TryFindNextPossibleStartingPosition(ReadOnlySpan<char> inputSpan)
                {
                    int pos = base.runtextpos;
                    char ch;
                    
                    // Any possible match is at least 6 characters.
                    if (pos <= inputSpan.Length - 6)
                    {
                        // The pattern begins with an atomic loop for a character in the set [%+-.0-9A-Z_a-z], followed by the character '@'.
                        // Search for the literal, and then walk backwards to the beginning of the loop.
                        while (true)
                        {
                            ReadOnlySpan<char> slice = inputSpan.Slice(pos);
                            
                            int i = slice.IndexOf('@');
                            if (i < 0)
                            {
                                break;
                            }
                            
                            int prev = i - 1;
                            while ((uint)prev < (uint)slice.Length && ((ch = slice[prev]) < '{' && ("\0\0栠Ͽ\ufffe蟿\ufffe߿"[ch >> 4] & (1 << (ch & 0xF))) != 0))
                            {
                                prev--;
                            }
                            
                            if ((i - prev - 1) < 1)
                            {
                                pos += i + 1;
                                continue;
                            }
                            
                            base.runtextpos = pos + prev + 1;
                            base.runtrackpos = pos + i;
                            return true;
                        }
                    }
                    
                    // No match found.
                    base.runtextpos = inputSpan.Length;
                    return false;
                }
        
                /// <summary>Determine whether <paramref name="inputSpan"/> at base.runtextpos is a match for the regular expression.</summary>
                /// <param name="inputSpan">The text being scanned by the regular expression.</param>
                /// <returns>true if the regular expression matches at the current position; otherwise, false.</returns>
                private bool TryMatchAtCurrentPosition(ReadOnlySpan<char> inputSpan)
                {
                    int pos = base.runtextpos;
                    int matchStart = pos;
                    char ch;
                    int charloop_starting_pos = 0, charloop_ending_pos = 0;
                    ReadOnlySpan<char> slice = inputSpan.Slice(pos);
                    
                    // Skip loop already matched in TryFindNextPossibleStartingPosition.
                    pos = base.runtrackpos;
                    slice = inputSpan.Slice(pos);
                    
                    // Advance the next matching position.
                    if (base.runtextpos < pos)
                    {
                        base.runtextpos = pos;
                    }
                    
                    // Match '@'.
                    if (slice.IsEmpty || slice[0] != '@')
                    {
                        return false; // The input didn't match.
                    }
                    
                    // Match a character in the set [-.0-9A-Za-z] greedily at least once.
                    //{
                        pos++;
                        slice = inputSpan.Slice(pos);
                        charloop_starting_pos = pos;
                        
                        int iteration = 0;
                        while ((uint)iteration < (uint)slice.Length && ((ch = slice[iteration]) < '{' && ("\0\0怀Ͽ\ufffe߿\ufffe߿"[ch >> 4] & (1 << (ch & 0xF))) != 0))
                        {
                            iteration++;
                        }
                        
                        if (iteration == 0)
                        {
                            return false; // The input didn't match.
                        }
                        
                        slice = slice.Slice(iteration);
                        pos += iteration;
                        
                        charloop_ending_pos = pos;
                        charloop_starting_pos++;
                        goto CharLoopEnd;
                        
                        CharLoopBacktrack:
                        
                        if (Utilities.s_hasTimeout)
                        {
                            base.CheckTimeout();
                        }
                        
                        if (charloop_starting_pos >= charloop_ending_pos ||
                            (charloop_ending_pos = inputSpan.Slice(charloop_starting_pos, charloop_ending_pos - charloop_starting_pos).LastIndexOf('.')) < 0)
                        {
                            return false; // The input didn't match.
                        }
                        charloop_ending_pos += charloop_starting_pos;
                        pos = charloop_ending_pos;
                        slice = inputSpan.Slice(pos);
                        
                        CharLoopEnd:
                    //}
                    
                    // Match '.'.
                    if (slice.IsEmpty || slice[0] != '.')
                    {
                        goto CharLoopBacktrack;
                    }
                    
                    // Match a character in the set [A-Za-z] atomically at least twice.
                    {
                        pos++;
                        slice = inputSpan.Slice(pos);
                        int iteration1 = 0;
                        while ((uint)iteration1 < (uint)slice.Length && char.IsAsciiLetter(slice[iteration1]))
                        {
                            iteration1++;
                        }
                        
                        if (iteration1 < 2)
                        {
                            goto CharLoopBacktrack;
                        }
                        
                        slice = slice.Slice(iteration1);
                        pos += iteration1;
                    }
                    
                    // The input matched.
                    base.runtextpos = pos;
                    base.Capture(0, matchStart, pos);
                    return true;
                }
            }
        }

    }
    
    /// <summary>Helper methods used by generated <see cref="Regex"/>-derived implementations.</summary>
    [GeneratedCodeAttribute("System.Text.RegularExpressions.Generator", "7.0.9.1910")]
    file static class Utilities
    {
        /// <summary>Default timeout value set in <see cref="AppContext"/>, or <see cref="Regex.InfiniteMatchTimeout"/> if none was set.</summary>
        internal static readonly TimeSpan s_defaultTimeout = AppContext.GetData("REGEX_DEFAULT_MATCH_TIMEOUT") is TimeSpan timeout ? timeout : Regex.InfiniteMatchTimeout;
        
        /// <summary>Whether <see cref="s_defaultTimeout"/> is non-infinite.</summary>
        internal static readonly bool s_hasTimeout = s_defaultTimeout != Timeout.InfiniteTimeSpan;
    }
}
```

Não, eu não vou explicar o que esse código faz. Mesmo porque o objetivo dele é encontrar e-mails em um texto e retornar. E isso ele continua fazendo maravilhosamente bem. E de forma extremamente rápida.

Eu poderia inclusive fazer um _benchmark_ entre o código que não usa _Source Generator_ e esse ai de cima. Porém, já fizeram isso. E fizeram de uma forma espetacular: [Regular Expression Improvements in .NET 7 - .NET Blog (microsoft.com)](https://devblogs.microsoft.com/dotnet/regular-expression-improvements-in-dotnet-7/#source-generation).
Esse é o tipo de post que beira o estado da arte. O [**Stephen Toub**](https://devblogs.microsoft.com/dotnet/author/toub/) destrincha cada detalhe do uso de _Source Generators_ no _Regex_. Fora que tem outras questões que envolvem performance também e que ele detalha de maneira extraordinária. O cara é sensacional! 

---

Eu fiz questão de utilizar o exemplo do _Regex_ para reforçar a importância dos _Source Generators_ para o tipo de compilação AOT.

Fiz um exemplo bem simplório, mas acredito que deu pra entender o quão fantástico é a ideia do _Source Generator_.

Utilizando essa abordagem, o código gerado é um código csharp puro, focado em executar apenas aquilo que foi configurado. Sem IL, sem _Relflection_, sem código dinâmico e com muita performance. Mas muita mesmo! Se você leu o post do Stephen Toub, com certeza percebeu isso!

Agora sim o AOT curtiu \o/

---

Acredito que agora você conseguiu entender o quão genial é essa ideia de _Source Generators_ e o quanto isso vai facilitar as nossas vidas.

Inclusive, recomendo muito esse repositório: [https://github.com/amis92/csharp-source-generators](https://github.com/amis92/csharp-source-generators). Aqui são listados centenas de _Source Generators_ e é uma fonte maravilhosa de aprendizado. Mais recomendado do que pão de queijo.

E claro, não podemos deixar de citar a documentação da Microsoft sobre o assunto: 
[https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview](https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview)

Eu não mostrei como se cria um _Source Generator_ mas recomendo muito esse artigo aqui:
[https://devblogs.microsoft.com/dotnet/new-c-source-generator-samples/](https://devblogs.microsoft.com/dotnet/new-c-source-generator-samples/)

Era isso. Espero que tenham gostado!

Até a próxima e bebam água.