
trailmark v0.5.0
Создавайте и запрашивайте графовое представление исходного кода.
Trailmark
Разбирает исходный код в графы функций, классов, вызовов и семантических аннотаций, пригодные для запросов, для целей анализа безопасности.
Trailmark использует tree-sitter для языконезависимого разбора AST и rustworkx для высокопроизводительного обхода графа. Долгосрочная цель — объединить этот граф с мутационным тестированием и фаззингом с покрытием для выявления разрывов между предположениями и тестовым покрытием, достижимых из пользовательского ввода.
Как это работает
Trailmark работает в три фазы: разбор, индексация и запрос.
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. Разбор
Парсер для конкретного языка обходит каталог, разбирает каждый файл в AST tree-sitter и извлекает:
- Узлы — функции, методы, классы, структуры, интерфейсы, трейты, перечисления, модули, пространства имён
- Рёбра — вызовы, наследование, реализация, вложенность, импорты
- Метаданные — аннотации типов, цикломатическая сложность, ветвления, docstring'и, типы исключений
Поддерживаемые языки
| Язык | Расширения | Ключевые конструкции |
|---|---|---|
| Python | .py | функции, классы, методы |
| JavaScript | .js, .jsx, .mjs, .cjs | функции, классы, стрелочные функции |
| TypeScript | .ts, .tsx | функции, классы, интерфейсы, перечисления |
| PHP | .php | функции, классы, интерфейсы, трейты |
| Ruby | .rb | методы, классы, модули |
| C | .c, .h | функции, структуры, перечисления |
| C++ | .cpp, .hpp, .cc, .hh, .cxx, .hxx | функции, классы, структуры, пространства имён |
| C# | .cs | методы, классы, интерфейсы, структуры, перечисления, пространства имён |
| Java | .java | методы, классы, интерфейсы, перечисления |
| Go | .go | функции, методы, структуры, интерфейсы |
| Rust | .rs | функции, структуры, трейты, перечисления, блоки impl, блоки verus! |
| Solidity | .sol | контракты, интерфейсы, библиотеки, функции, модификаторы, структуры, перечисления |
| Cairo | .cairo | функции, трейты, структуры, перечисления, блоки impl, контракты StarkNet |
| Circom | .circom | шаблоны, функции, сигналы, компоненты |
| Haskell | .hs | функции, типы данных, классы типов, экземпляры |
| Erlang | .erl | функции, записи, поведения, модули |
| Miden Assembly | .masm | процедуры, точки входа, константы, вызовы |
| Swift | .swift | функции, классы, структуры, перечисления, протоколы, расширения |
| Objective-C | .m, .mm, .h | функции C, классы, методы (именование на основе селекторов) |
| Kotlin | .kt, .kts | функции, классы, интерфейсы, классы данных, объекты, методы |
| Dart | .dart | функции, классы, абстрактные классы, методы, конструкторы |
| Move | .move | модули, функции, импорты, прямые вызовы |
| Tact | .tact | контракты, структуры, получатели, функции |
| Func | .fc, .func | функции, включения, прямые вызовы |
| Sway | .sw | интерфейсы ABI, структуры, методы impl, функции |
| Rego | .rego | пакеты, импорты, правила политик, вызовы правил |
| Proto | .proto | сервисы, RPC, сообщения, поля, перечисления |
| Thrift | .thrift | сервисы, функции, структуры, поля, перечисления |
| GraphQL | .graphql, .gql | объектные типы, корневые операции, поля, перечисления |
| SQL | .sql | схемы, таблицы, представления, функции, процедуры |
Rust-парсер извлекает обычные функции, функции spec и proof с телами
из блоков verus! { ... }. Грамматика Verus в настоящее время поддерживает только
блоки, ограниченные фигурными скобками; verus!(...) и verus![...] пропускаются с предупреждением.
Встроенные модули сохраняют квалифицированные идентификаторы (например, file.inner:function), а
методы сохраняют своего владельца impl или trait. Каждый блок разбирается независимо;
синтаксические ошибки порождают предупреждение с указанием файла и расположения блока.
Квалифицированные вызовы, такие как vstd::prelude::verus!, также поддерживаются.
Локальные вспомогательные функции сохраняют в своих идентификаторах объемлющую функцию (например,
file:outer.inner). Вызовы этих вспомогательных функций и относительные импорты, такие как
use super::*, разрешаются в объявления, присутствующие в файле.
Повторяющиеся локальные имена в разных блоках получают суффикс байтового смещения, чтобы сохранить
их узлы различимыми, а вызовы учитывают область видимости блока.
Локальные структуры, перечисления, трейты и встроенные модули также сохраняют объемлющую функцию:
например, file:outer.Guard.drop и file:outer.local.helper.
Их тела имеют отдельные вызовы и сложность. Явные вызовы известных локальных
типов и модулей разрешаются в пределах файла. Вызовы без квалификации ищут в областях видимости функций и модулей;
ассоциированные элементы используют пути вида Type::method. Вызовы через точку разрешаются
только когда получатель именует известную структуру или перечисление. Другие получатели-значения,
включая self.method(), остаются неразрешенными. Неявные вызовы деструкторов и вывод
типа получателя не моделируются. Эти правила применяются как к обычному Rust, так и к
блокам Verus. Редактирование кода перед объявлением с суффиксом смещения изменяет
его ID, поэтому trailmark diff может сообщить об этом объявлении как об удаленном и добавленном.
Trailmark не раскрывает произвольные макросы. Литеральные вызовы Verus внутри
непрозрачных макросов или тел функций пропускаются с предупреждением. Невызванные
определения macro_rules! не раскрываются. Сигнатуры функций без тел
и вызовы в клаузах requires/ensures не извлекаются.
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
Идентификаторы узлов следуют схеме module:function, module:Class или module:Class.method для однозначного поиска. При разборе каталога разрешаются вызовы между файлами без указания модуля, если существует единственное определение; неоднозначные межфайловые вызовы остаются на исходной приблизительной цели и помечаются как uncertain. Достоверность рёбер помечается как certain (прямые вызовы, self.method()), inferred (доступ к атрибутам объектов, не являющихся self) или uncertain (динамическая диспетчеризация или неоднозначное разрешение).
2. Индекс
GraphStore загружает CodeGraph в rustworkx PyDiGraph и строит двунаправленные сопоставления ID/индексов для быстрого обхода.
3. Запросы
QueryEngine предоставляет высокоуровневый API поверх индексированного графа: