
# Pesquisa AST

O modo AST de Greph pesquisa código-fonte PHP por estrutura, não por texto. Os padrões são escritos como código PHP comum, com metavariáveis `$VARIABLE` e `$$$VARIADIC`. O padrão é analisado por [nikic/php-parser](https://github.com/nikic/PHP-Parser) na mesma forma AST do código-fonte pesquisado e, em seguida, comparado nó por nó.

Este é o mesmo modelo de [ast-grep](https://ast-grep.github.io/), e o wrapper de compatibilidade `sg` expõe a superfície de ast-grep CLI usando o mesmo mecanismo.

## Exemplos rápidos

```bash
# Find every constructor call
./vendor/bin/greph -p 'new $CLASS()' src

# Find every method call with any number of arguments
./vendor/bin/greph -p '$obj->$method($$$ARGS)' src

# Find old-style array() calls
./vendor/bin/greph -p 'array($$$ITEMS)' src

# Find void-returning functions
./vendor/bin/greph -p 'function $name($$$PARAMS): void {}' src

# Find isset ternaries that should be ??
./vendor/bin/greph -p 'isset($x) ? $x : $default' src
```

## Sintaxe dos padrões

Um padrão é código-fonte PHP válido. Em qualquer lugar onde uma expressão ou instrução real poderia aparecer, você pode substituí-la por uma metavariável para capturar esse nó.

| Sintaxe | Corresponde a | Exemplo |
|---|---|---|
| `$VAR` | Qualquer nó AST único, capturado como `VAR` | `$x + $y` corresponde a `foo() + bar` |
| `$_` | Qualquer nó AST único, sem captura | `$_->method()` corresponde a qualquer receptor |
| `$$$ARGS` | Zero ou mais nós (variádicos), capturados como `ARGS` | `func($$$ARGS)` corresponde a `func()`, `func(1)`, `func(1, 2, 3)` |
| `$VAR` (repetido) | O mesmo `$VAR` deve corresponder à mesma estrutura nas duas ocorrências | `$x == $x` corresponde a `a == a`, mas não a `a == b` |

O nome da metavariável costuma ser em maiúsculas por convenção, mas não precisa ser. `$_` é um curinga sem captura.

`$$$VARIADIC` funciona sempre que PHP permite uma lista de nós: argumentos de função, itens de array, listas de parâmetros e corpos de instruções. A captura armazena toda a sequência correspondente (inclusive se não houver itens).

## Metavariáveis de identificador

Nomes de classes, funções, interfaces, traits e enums são identificadores, não expressões. Por isso, normalmente PHP não aceitaria `$Name` após `class`. Greph reescreve esses padrões antes da análise para que a metavariável continue funcionando:

```bash
./vendor/bin/greph -p 'class $NAME extends BaseController {}' src
./vendor/bin/greph -p 'function $NAME(): void {}' src
./vendor/bin/greph -p 'interface $NAME {}' src
```

A mesma reescrita ocorre para `class`, `interface`, `trait`, `enum` e `function`.

## Metavariáveis repetidas

Reutilizar uma metavariável no mesmo padrão afirma que as duas ocorrências devem corresponder estruturalmente ao mesmo nó. Isso é útil para identificar código redundante:

```bash
# Tautological comparisons
./vendor/bin/greph -p '$X == $X' src

# Redundant ternaries
./vendor/bin/greph -p '$X ? $X : null' src
```

A mesma regra se aplica a `$$$VARIADIC`: duas ocorrências de `$$$ARGS` devem corresponder à mesma sequência.

## Filtragem de arquivos

O modo AST percorre a árvore de arquivos com o mesmo percorridor do modo de texto e usa por padrão o filtro de tipos de arquivo `php`. Substitua-o com `--type`, `--type-not`, `--glob`, `--no-ignore` e `--hidden`. O percorridor ainda respeita `.gitignore` e `.grephignore`, além de ignorar arquivos binários.

```bash
./vendor/bin/greph -p 'new $CLASS()' --glob 'src/**/*.php' .
./vendor/bin/greph -p 'new $CLASS()' --type-not phpt src
```

## Saída

A saída padrão usa o mesmo formato `file:line:content` do modo de texto, com o código correspondente recolhido em uma única linha:

```text
src/Greph.php:38:return (new FileWalker())->walk($paths, $options);
src/Greph.php:49:$searcher = new TextSearcher();
```

Passe `--json` para emitir correspondências estruturadas:

```json
[
  {
    "file": "src/Greph.php",
    "start_line": 38,
    "end_line": 38,
    "start_file_pos": 1234,
    "end_file_pos": 1252,
    "code": "new FileWalker()"
  }
]
```

## Erros de análise

Por padrão, Greph ignora silenciosamente arquivos que não podem ser analisados. Esse é o comportamento adequado ao percorrer uma base de código grande que contenha PHP experimentais ou gerados. Passe `--strict-parse` (em `greph-index ast-index search` e `greph-index ast-cache search`) para gerar o erro de análise, ou defina `skipParseErrors: false` em `AstSearchOptions` ao usar a fachada diretamente.

## Uso programático

```php
use Greph\Greph;
use Greph\Ast\AstSearchOptions;

$matches = Greph::searchAst(
    '$obj->$method($$$ARGS)',
    'src',
    new AstSearchOptions(jobs: 4),
);

foreach ($matches as $match) {
    echo "{$match->file}:{$match->startLine}\n";
    echo $match->code . "\n";

    foreach ($match->captures as $name => $node) {
        echo "  {$name} = " . get_debug_type($node) . "\n";
    }
}
```

`$match->node` é o `PhpParser\Node` capturado (para que você possa percorrê-lo com PHP-Parser `NodeVisitor`), e `$match->captures` associa cada nome de metavariável ao nó ou à lista de nós capturados.

## Como funciona

O pipeline é:

1. **Análise do padrão**: `Greph\Ast\PatternParser` reescreve as metavariáveis de identificador e, em seguida, analisa o padrão com PHP-Parser.
2. **Análise do código-fonte**: cada arquivo candidato é analisado uma vez. No modo `ast-cache`, esta etapa é substituída pela desserialização do armazenamento em cache.
3. **Filtragem de candidatos**: `Greph\Ast\AstPatternPrefilter` extrai do padrão uma assinatura aproximada (nome de chamada, nome de classe, tipo de instrução) e a usa para ignorar arquivos que claramente não podem conter uma correspondência. No modo `ast-index`, esta etapa consulta um armazenamento de fatos pré-computado.
4. **Comparação**: `Greph\Ast\PatternMatcher` percorre a AST do código-fonte e a compara com a AST do padrão, associando metavariáveis e garantindo a consistência das variáveis repetidas.
5. **Emissão de resultados**: os nós correspondentes são convertidos em objetos `AstMatch` com posições de linha/byte e o trecho original do código-fonte.

Em cargas de trabalho repetidas, a [pesquisa AST indexada](https://greph.inline0.dev/pt/docs/indexed/ast) e a [pesquisa AST em cache](https://greph.inline0.dev/pt/docs/indexed/cache) ignoram a maior parte das etapas 2 e 3.
