i582 / CodeQuery

(VK internal hackathon) Tool for searching and aggregating data for PHP

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

CodeQuery

(VK internal hackathon) Tool for searching and aggregating data for PHP

CodeQuery это инструмент который нацелен на поиск различных сложных связей между функциями и другими символами.

CodeQuery анализирует кодовую базу и создает слепок данных. Этот слепок содержит в себе всю необходимую для работы информацию и не зависит от кода. В данный момент он хранит все функции, глобальные переменные, а также граф вызовов.

CodeQuery использует подмножество языка SQL для запросов к данным.

Установка

Готовый бинарный файл

Перейдите на страницу релизов и скачайте нужную версию.

Из исходников

Для установки вам потребуется Golang версии не ниже 1.16.

Склонируйте репозиторий:

git clone https://github.com/i582/CodeQuery
cd CodeQuery

Выполните команду:

make build

Бинарный файл будет лежать в папке build

Создание слепка

Для того чтобы создать базу данных для вашего кода, перейдите в папку с проектом, а затем выполните следующую команду:

CodeQuery create --name testDatabase ./folder1 ./folder2 ...

Где после флага --name вам нужно указать список папок или файлов для анализа.

После выполнения этой команды у вас появится файл testDatabase.db с которым мы будет взаимодействовать дальше.

Возможности

Для входа в интерактивную оболочку выполните следующую команду:

CodeQuery open --name testDatabase

Для выполнения запросов в интерактивной оболочке используется команда run. Запрос указывается после команды в кавычках.

Например:

run "SELECT * FROM funcs"

Если вы хотите запустить запрос из файла, то используйте флаг -f:

run -f "test.sql"

Обратите внимание, передавать имя файла необходимо в кавычках!

Просмотр списка функций

Простой запрос:

SELECT * FROM funcs

Таблица состоит из 4 полей:

  • ID (id)
  • Имя (name)
  • Количество использований (uses)
  • Количество глобальных переменных (globals)

По каждому из полей можно сортировать таблицу используя ORDER BY:

SELECT * FROM funcs ORDER BY name

Поддерживается сортировка только по одному полю

По-умолчанию, выводятся первые 20 записей, вы можете изменить это количество с помощью LIMIT:

SELECT * FROM funcs LIMIT 100

Саму таблицу можно фильтровать с помощью WHERE:

SELECT * FROM funcs WHERE "<bool expression>"

Для выражений поддерживаются следующие операторы:

  • AND
  • OR
  • NOT
  • >
  • <
  • <=
  • >=
  • =
  • <>
  • ()

Поддерживаются целочисленные и числа с плавающей запятой в качестве литералов, а также строки в двойных и одинарных кавычках.

Рассмотрим простой запрос:

SELECT * FROM funcs WHERE func.countUse() > 0

Заметьте, когда вы работаете с таблицей funcs для доступа к каждому элементу вы должны использовать переменную func.

Переменная func имеет тип IFunc, который определяет следующие методы:

  • countUse — возвращает количество использований функции
  • globals — возвращает глобальные переменные используемые внутри функции
  • fullName — возвращает полное имя, включает в себя пространство имен и класс, если есть
  • name — возвращает имя функции или метода без пространства имен и класса
  • namespace — возвращает пространство имен
  • className — возвращает имя класса

Например, если вы хотите выбрать только функции:

SELECT * FROM funcs WHERE func.className() = ''

Методы fullName, name и прочие похожие возвращают строку для которой также определены некоторые методы:

  • contains — проверяет содержит ли в себе строка переданную строку

Например, если нужно найти функции только в определенном пространстве имен использующие глобальные переменные:

SELECT * FROM funcs 
WHERE func.className() = '' 
  AND func.namespace().contains('Some\\Namespace')
  AND func.globals().count() > 0

Здесь func.globals() возвращает значение типа IGlobals, который определяет следующие методы:

  • count — возвращает количество глобальных переменных
  • contains — проверяет наличие хотя бы одной функции из выборки в списке

Давайте рассмотрим метод contains, так как он не является обычным, потому что может принимать аргументом подзапрос.

Например, мы хотим найти все функции, которые используют определенную глобальную переменную:

SELECT * FROM funcs 
WHERE func.className() = '' 
  AND func.globals().contains(
  	SELECT * FROM globals WHERE global.name() = 'someName'  
  )

Первым аргументом мы передаем подвыражение SELECT которое выбирает все глобальные переменные подходящие под условия, а потом проверяет, что в глобальных переменных функции есть хотя бы одна из подзапроса.

Просмотр списка глобальных переменных

Простой запрос:

SELECT * FROM globals

Таблица состоит из 4 полей:

  • ID (id)
  • Имя (name)
  • Количество использований (uses)

Таблицу также можно сортировать по столбцам, как в случае функций.

Вызовы функций

Запрос следующего вида:

SELECT * FROM calls 
WHERE call.func().name() = "someFunc"
  AND call.args() > 0
  AND call.arg(0).isString()

Найдет все вызовы функции someFunc где первый аргумент является строковым литералом.

Переменная call через которую можно обратиться к текущему вызову имеет тип IFuncCall и определяет следующие методы:

  • func — возвращает структуру функции которая вызывается
  • args — возвращает количество аргументов
  • arg — возвращает аргумент по индексу

Аргумент функции имеет тип IFuncArg и определяет следующие методы:

  • string — возвращает строковое представление аргумента
  • isInt — проверяет является ли аргумент целочисленным литералом
  • isFloat — проверяет является ли аргумент литералом с плавающей точкой
  • isBool — проверяет является ли аргумент булевым литералом
  • isString — проверяет является ли аргумент строковым литералом
  • isConstant — проверяет является ли аргумент константой
  • isVariable — проверяет является ли аргумент переменной
  • isExpression — проверяет является ли аргумент выражением, которое не подпадает под условия выше

Используя методы выше можно очень гибко искать вызовы каких-либо функций.

Зависимости функции

Рассмотрим следующий запрос

SELECT deps FROM (
   SELECT * FROM funcs WHERE func.name() = 'someFunc'
)

Этот запрос найдет все зависимости функции someFunc от других функций на глубине 3 (по-умолчанию), в итоге мы получим некоторый граф в котором корневым элементом будет функция someFunc, а от него будут идти узлы представляющие вызовы функций.

Когда мы запускаем запрос, мы можем указать программе создать не текстовое представление графа, а графическое, для этого нужно добавить флаг --graph:

run --graph "graph" "SELECT deps FROM (SELECT * FROM funcs WHERE func.name() = 'someFunc')"

И передать ему имя файла в который будет записан граф.

Обратите внимание, что название файла (или путь) нужно писать в кавычках

Граф создается в формате SVG и может быть открыт в браузере, для удобства в него встроен скрипт для удобного скроллинга и перетаскивания.

Значение максимальной глубины по-умолчанию не всегда достаточно, для того, чтобы установить свою максимальную глубину добавьте выражение WITH:

SELECT deps FROM (
   SELECT * FROM funcs WHERE func.name() = 'someFunc'
) WITH depth=10

При большой глубине или если функция вызывает множество других функций граф может становится очень громоздким и неудобным в использовании. Чтобы исправить это ситуацию вы можете фильтровать пути которые будут отображены на графе.

Для этого используйте переменную path в выражении WHERE.

Например, мы хотим оставить только те пути в которых есть функция someFunc2:

SELECT deps FROM (
   SELECT * FROM funcs WHERE func.name() = 'someFunc'
) WHERE path.contains(
          SELECT * FROM funcs
          WHERE func.className() = ''
          AND func.name() = 'someFunc2'
          LIMIT 1
 ) WITH depth=10

Переменная path имеет тип IFuncPath и определяет следующие методы:

  • begin — возвращает начальную функцию пути
  • end — возвращает конечную функцию пути
  • length — возвращает длину пути
  • at — возвращает функцию по индексу
  • contains — проверяет наличие переданных функций в пути

About

(VK internal hackathon) Tool for searching and aggregating data for PHP

License:MIT License


Languages

Language:Go 94.0%Language:Ragel 3.4%Language:Yacc 2.3%Language:Makefile 0.3%