Voltar às atualizações
New releaseJul 24, 2026

trailmark v0.5.0

Construir e consultar uma representação de banco de dados em grafo do código-fonte

Compartilhar

Trailmark

CI Mutation Testing

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

LinguagemExtensõesConstruções principais
Python.pyfunções, classes, métodos
JavaScript.js, .jsx, .mjs, .cjsfunções, classes, arrow functions
TypeScript.ts, .tsxfunções, classes, interfaces, enums
PHP.phpfunções, classes, interfaces, traits
Ruby.rbmétodos, classes, módulos
C.c, .hfunções, structs, enums
C++.cpp, .hpp, .cc, .hh, .cxx, .hxxfunções, classes, structs, namespaces
C#.csmétodos, classes, interfaces, structs, enums, namespaces
Java.javamétodos, classes, interfaces, enums
Go.gofunções, métodos, structs, interfaces
Rust.rsfunções, structs, traits, enums, blocos impl, blocos verus!
Solidity.solcontratos, interfaces, bibliotecas, funções, modificadores, structs, enums
Cairo.cairofunções, traits, structs, enums, blocos impl, contratos StarkNet
Circom.circomtemplates, funções, sinais, componentes
Haskell.hsfunções, tipos de dados, classes de tipo, instâncias
Erlang.erlfunções, records, behaviours, módulos
Miden Assembly.masmprocedimentos, entrypoints, constantes, invocações
Swift.swiftfunções, classes, structs, enums, protocolos, extensões
Objective-C.m, .mm, .hfunções C, classes, métodos (nomenclatura baseada em seletores)
Kotlin.kt, .ktsfunções, classes, interfaces, data classes, objetos, métodos
Dart.dartfunções, classes, classes abstratas, métodos, construtores
Move.movemódulos, funções, importações, chamadas diretas
Tact.tactcontratos, structs, receivers, funções
Func.fc, .funcfunções, includes, chamadas diretas
Sway.swinterfaces ABI, structs, métodos impl, funções
Rego.regopacotes, importações, regras de política, chamadas de regras
Proto.protoserviços, RPCs, mensagens, campos, enums
Thrift.thriftserviços, funções, structs, campos, enums
GraphQL.graphql, .gqltipos de objeto, operações raiz, campos, enums
SQL.sqlschemas, 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:

Categorias