
trailmark v0.5.0
Construir e consultar uma representação de banco de dados em grafo do código-fonte
Trailmark
Analisa o código-fonte em grafos consultáveis de funções, classes, chamadas e anotações semânticas para análise de segurança.
O Trailmark usa tree-sitter para análise de AST independente de linguagem e rustworkx para travessia de grafos de alto desempenho. A visão de longo prazo é combinar este grafo com testes de mutação e fuzzing guiado por cobertura para identificar lacunas entre suposições e cobertura de testes que sejam alcançáveis a partir da entrada do usuário.
Como Funciona
O Trailmark opera em três fases: análise, indexação e consulta.
flowchart TD
A["Source Files"] --> B["tree-sitter Parser"]
B --> C["CodeGraph (nodes + edges)"]
C --> D["rustworkx GraphStore"]
D --> E["QueryEngine"]
E --> F["JSON / Summary / Hotspots"]
classDef src fill:#007bff26,stroke:#007bff,color:#007bff
classDef parse fill:#28a74526,stroke:#28a745,color:#28a745
classDef data fill:#6f42c126,stroke:#6f42c1,color:#6f42c1
classDef query fill:#ffc10726,stroke:#e6a817,color:#e6a817
class A src
class B parse
class C,D data
class E,F query
1. Análise
Um analisador específico de linguagem percorre o diretório, analisa cada arquivo em uma AST do tree-sitter e extrai:
- Nós — funções, métodos, classes, structs, interfaces, traits, enums, módulos, namespaces
- Arestas — chamadas, herança, implementação, contenção, importações
- Metadados — anotações de tipo, complexidade ciclomática, ramificações, docstrings, tipos de exceção
Linguagens Suportadas
| Linguagem | Extensões | Construções principais |
|---|---|---|
| Python | .py | funções, classes, métodos |
| JavaScript | .js, .jsx, .mjs, .cjs | funções, classes, arrow functions |
| TypeScript | .ts, .tsx | funções, classes, interfaces, enums |
| PHP | .php | funções, classes, interfaces, traits |
| Ruby | .rb | métodos, classes, módulos |
| C | .c, .h | funções, structs, enums |
| C++ | .cpp, .hpp, .cc, .hh, .cxx, .hxx | funções, classes, structs, namespaces |
| C# | .cs | métodos, classes, interfaces, structs, enums, namespaces |
| Java | .java | métodos, classes, interfaces, enums |
| Go | .go | funções, métodos, structs, interfaces |
| Rust | .rs | funções, structs, traits, enums, blocos impl, blocos verus! |
| Solidity | .sol | contratos, interfaces, bibliotecas, funções, modificadores, structs, enums |
| Cairo | .cairo | funções, traits, structs, enums, blocos impl, contratos StarkNet |
| Circom | .circom | templates, funções, sinais, componentes |
| Haskell | .hs | funções, tipos de dados, classes de tipo, instâncias |
| Erlang | .erl | funções, records, behaviours, módulos |
| Miden Assembly | .masm | procedimentos, entrypoints, constantes, invocações |
| Swift | .swift | funções, classes, structs, enums, protocolos, extensões |
| Objective-C | .m, .mm, .h | funções C, classes, métodos (nomenclatura baseada em seletores) |
| Kotlin | .kt, .kts | funções, classes, interfaces, data classes, objetos, métodos |
| Dart | .dart | funções, classes, classes abstratas, métodos, construtores |
| Move | .move | módulos, funções, importações, chamadas diretas |
| Tact | .tact | contratos, structs, receivers, funções |
| Func | .fc, .func | funções, includes, chamadas diretas |
| Sway | .sw | interfaces ABI, structs, métodos impl, funções |
| Rego | .rego | pacotes, importações, regras de política, chamadas de regras |
| Proto | .proto | serviços, RPCs, mensagens, campos, enums |
| Thrift | .thrift | serviços, funções, structs, campos, enums |
| GraphQL | .graphql, .gql | tipos de objeto, operações raiz, campos, enums |
| SQL | .sql | schemas, tabelas, views, funções, procedimentos |
O parser Rust extrai funções comuns, spec e proof com corpos
de blocos verus! { ... }. A gramática Verus atualmente suporta apenas
blocos delimitados por chaves; verus!(...) e verus![...] são ignorados com um aviso.
Módulos inline mantêm IDs qualificados (por exemplo, file.inner:function), e
métodos mantêm seu impl ou trait proprietário. Cada bloco é analisado independentemente;
erros de sintaxe produzem um aviso com o arquivo e a localização do bloco.
Invocações qualificadas como vstd::prelude::verus! também são suportadas.
Funções auxiliares locais mantêm sua função envolvente em seus IDs (por exemplo,
file:outer.inner). Chamadas a essas funções auxiliares e importações relativas como
use super::* resolvem para declarações presentes no arquivo.
Nomes locais repetidos em blocos diferentes recebem um sufixo de deslocamento de bytes para manter
seus nós distintos, e as chamadas respeitam o escopo do bloco.
Structs, enums, traits e módulos inline locais também mantêm a função envolvente:
por exemplo, file:outer.Guard.drop e file:outer.local.helper.
Seus corpos têm chamadas e complexidade separadas. Chamadas explícitas a tipos e módulos
locais conhecidos resolvem dentro do arquivo. Chamadas simples buscam escopos de funções e módulos;
itens associados usam caminhos como Type::method. Chamadas com ponto resolvem
apenas quando o receptor nomeia um struct ou enum conhecido. Outros receptores de valor,
incluindo self.method(), permanecem não resolvidos. Chamadas implícitas de destrutor e inferência
de tipo de receptor não são modeladas. Essas regras se aplicam ao Rust comum, bem como a
blocos Verus. Editar código antes de uma declaração com sufixo de deslocamento altera
seu ID, então trailmark diff pode reportar essa declaração como removida e adicionada.
O Trailmark não expande macros arbitrárias. Invocações Verus literais dentro de
macros opacas ou corpos de funções são ignoradas com um aviso. Definições de
macro_rules! não invocadas não são expandidas. Assinaturas de funções sem corpos
e chamadas em cláusulas requires/ensures não são extraídas.
flowchart TD
subgraph "Per-File Parsing"
F["Source file"] --> TS["tree-sitter AST"]
TS --> EX["Extract nodes"]
TS --> EC["Extract call edges"]
TS --> EB["Count branches"]
TS --> ET["Resolve types"]
end
EX --> CG["CodeGraph"]
EC --> CG
EB --> CG
ET --> CG
classDef src fill:#007bff26,stroke:#007bff,color:#007bff
classDef parse fill:#28a74526,stroke:#28a745,color:#28a745
classDef extract fill:#ffc10726,stroke:#e6a817,color:#e6a817
classDef data fill:#6f42c126,stroke:#6f42c1,color:#6f42c1
class F src
class TS parse
class EX,EC,EB,ET extract
class CG data
Os IDs de nó seguem o esquema module:function, module:Class ou module:Class.method para uma consulta inequívoca. A análise de diretórios resolve chamadas entre arquivos sem qualificação quando existe uma definição única; chamadas entre arquivos ambíguas são mantidas em seu alvo original de melhor esforço e marcadas como uncertain. A confiança das arestas é marcada como certain (chamadas diretas, self.method()), inferred (acesso a atributos em objetos que não são self) ou uncertain (despacho dinâmico ou resolução ambígua).
2. Índice
O GraphStore carrega o CodeGraph em um PyDiGraph do rustworkx e constrói mapeamentos bidirecionais de ID/índice para travessia rápida.
3. Consulta
O QueryEngine fornece uma API de alto nível sobre o grafo indexado: