Этот репозиторий содержит Sorbet — быстрый и мощный анализатор типов, предназначенный для Ruby. Целью является простое добавление в существующие проекты с постепенным внедрением типов, а также быстрая реакция на ошибки и предложения.
Документация в этом README предназначена специально для участников проекта Sorbet. Вы можете захотеть:
Если вы работаете в Stripe, вам может понадобиться просмотреть http://go/types/internals для документов о специфических рабочих процессах развития и историческом контексте Stripe.
На раннем этапе нашего проекта мы определили некоторые руководящие принципы того, как должно ощущаться взаимодействие с Sorbet.
Явность
Мы готовы писать аннотации, а также считаем их полезными; они делают код более читаемым и предсказуемым. Мы здесь, чтобы помочь читателям так же, как и авторам.
Полезность, а не бремя
Хотя это явно, мы стремимся сделать его компактным. Это проявляется в нескольких аспектах:
Как можно проще, но достаточно мощным
В целом, мы не верим в сверхсложные системы типов. Они имеют своё место, и нам требуется достаточная выразительная способность, чтобы моделировать (достаточно) реальный Ruby код, но при прочих равных условиях мы хотим быть проще. Мы верим, что такая система масштабируется лучше, и — самое важное — легче для наших пользователей учиться и понимать.
Совместимость с Ruby
Особенно важно, чтобы новый синтаксис не требовался. Существующий синтаксис Ruby позволяет использовать большую часть существующего инструментария (редакторы и т.д.). Также, цель Sorbet заключается в постепенном улучшении существующего Ruby кодового базиса. Новый синтаксис делает совместимость с существующими инструментами сложнее.5. Масштабируемость
По всем направлениям: скорость выполнения, количество участников, строки кода, возраст кодовой базы. Мы работаем с крупными Ruby кодовыми базами, и они будут становиться ещё больше.
Может быть постепенно внедрен
Чтобы сделать внедрение возможным на большой шкале, мы не можем требовать от каждой команды или проекта одновременного перехода на Sorbet. Sorbet должен поддерживать команды, внедряющие его по различному темпу.
Установите зависимости
brew install bazel autoconf coreutils parallel
Клонируйте этот репозиторий
git clone https://github.com/sorbet/sorbet.git
cd sorbet
Соберите Sorbet
./bazel build //main:sorbet --config=dbg
Запустите Sorbet!
bazel-bin/main/sorbet -e "42 + 'hello'"
Мы документировали внутреннюю работу Sorbet в отдельном документе.
Перекреститесь между этим документом и текущим, чтобы узнать, как работает Sorbet и как его изменять
Также есть онлайн-представление, которое описывает высокий уровень архитектуры Sorbet и причины его быстродействия:
→ Быстрая проверка типов для Ruby
Существует несколько способов сборки sorbet
. Этот является наиболее распространенным:
./bazel build //main:sorbet --config=dbg
Это создаст исполняемый файл в bazel-bin/main/sorbet
(см. "Запуск Sorbet" ниже). Вы можете передать множество различных опций при сборке sorbet
:- --config=dbg
--config=sanitize
--config=debugsymbols
--config=dbg
) отладочные символы, ничего больше.--config=forcedebug
--config=static-libs
--config=release-mac
и --config=release-linux
Независимо от использования или игнорирования вышеупомянутых флагов, вы можете активировать оптимизации для любой сборки:
-c opt
clang
(например, -O2
).Эти аргументы не являются вза Müslümekli. Например, распространённой парой при отладке является следующее:
--config=dbg --config=sanitize
В файле .bazelrc
можно найти описание всех этих опций (и других).
"Необходимо указать версию Xcode для использования Apple CROSS-TOOL"
**Эта ошибка обычно возникает после обновления Xcode.
Должны быть установлены средства разработки, принята лицензия Xcode, и ваша текущая директория командной строки Xcode должна указывать на установленную версию Xcode.
Следующие команды должны помочь:
# Установите командные инструменты
xcode-select --install
# Убедитесь, что система находит командные инструменты в текущей директории Xcode
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
# Принять лицензию Xcode.
sudo xcodebuild -license
# Очистить кэш Bazel, который может содержать файлы, сгенерированные из предыдущих версий командных инструментов Xcode.
bazel clean --expunge
(Mac) "Критическая ошибка: файл 'math.h' не найден"
(или какой-то другой системный заголовочный файл)
Эта ошибка может возникнуть на Mac, если отсутствует папка /usr/include
. Решение — установить заголовочные файлы macOS с помощью следующего пакета:
macOS Mojave:
open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg
macOS Catalina:
sudo ln -s /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/* /usr/local/include/
Запустите Sorbet на выражении:
bazel-bin/main/sorbet -e "1 + false"
Запустите Sorbet на файле:
bazel-bin/main/sorbet foo.rb
Запуск bazel-bin/main/sorbet --help
покажет множество опций. Вот наиболее часто используемые для вкладчиков:- -p <IR>
--help
для доступных значений <IR>
.--stop-after <phase>
-v
, -vv
, -vvv
--max-threads=1
--wait-for-dbg
Чтобы запустить все тесты:
bazel test //... --config=dbg
(Литеральное значение //...
означает "все цели".)
Чтобы запустить подмножество тестов, отобранных для быстрого прототипирования и повышения скорости разработки, выполните:
bazel test test --config=dbg
Обратите внимание, что во втором случае test
является псевдонимом для //test:test
, так что мы немного шутим здесь.
По умолчанию, весь вывод тестов записывается в файлы. Чтобы также выводить его на экран:
bazel test //... --config=dbg --test_output=errors
Если хотя бы один тест провалился, вы увидите две части информации:
1. //test:test_testdata/resolver/optional_constant
2. /private/var/tmp/.../test/test_testdata/resolver/optional_constant/test.log
bazel test <target>
),Чтобы просмотреть вывод проваленного теста, либо:
bazel test
с флагом --test_output=errors
,*.log
и запустите его (вывод будет открыт в less
).Это относится к вкладу в Sorbet в Stripe.
Если вы работаете в Stripe и хотите протестировать ваш ветвь против pay-server, это можно сделать по адресу http://go/types/local-dev.
test/
.Каждая отдельная подпапка является "магической"; каждая содержит конкретные типы тестов. Мы стремимся сделать наши тесты полностью воспроизводимыми.> Примечание для C++: В C++ хэш-функции должны давать одинаковый результат для одного и того же входного значения внутри одной сессии выполнения программы.
Таким образом, мы ожидаем, что все видимые пользователю выходные данные будут явно сортироваться с использованием ключа, который остаётся стабильным между запусками.
Существует множество способов тестирования Sorbet, некоторые "лучше", чем другие. Мы расположили их ниже в порядке от наиболее предпочтительного до наименее предпочтительного. И всегда предпочитаем наличие каких-либо тестов отсутствию тестов!
Первый тип тестов может называться либо test_corpus тестами, либо testdata тестами, в зависимости от названия тестового механизма или папки, содержащей эти тесты соответственно.
Чтобы создать тест test_corpus, добавьте любой файл <name>.rb
в test/testdata
, на любом уровне вложенности. Файл должен либо:
Чтобы указать, что строка должна содержать ошибки, припишите # error: <сообщение>
(сообщение должно совпадать с сообщением выброшенной ошибки). В случае нескольких ошибок на этой строке, добавьте # error: <сообщение>
на своей собственной строке сразу после.Проверки ошибок могут указывать на диапазон символов вместо строки:
1 + '' # ошибка: `строка` не соответствует `целое число`
rescue Foo, Bar => baz
# ^^^ ошибка: невозможно разрешить константу `Foo`
# ^^^ ошибка: невозможно разрешить константу `Bar`
Вы можете запустить этот тест следующей командой:
bazel test //test:test_PosTests/testdata/path/to/<name>
Каждый тест test_corpus может быть преобразован в ожидаемый тест путём создания любого количества файлов <name>.rb.<phase>.exp
(где <name>
соответствует имени файла Ruby для этого теста). Эти файлы содержат красиво отформатированные представления внутренних структур данных, согласно тому, что было бы напечатано с помощью -p <phase>
. Снимок должен точно соответствовать выводу, сгенерированному выполнением sorbet -p <phase> <name>.rb
для прохождения теста. Вы можете запустить этот тест с помощью команды:
bazel test //test:test_PosTests/testdata/path/to/<name>
Файлы, начинающиеся с префикса и __
, будут выполняться вместе. Например, файлы foo__1.rb
и foo__2.rb
будут выполнены как один тест foo
. Если такие наборы файлов имеют связанные ими файлы *.exp
, то эти файлы должны следовать за шаблоном <name>.<phase>.exp
, где <name>
не включает суффикс __*.rb
. Так что файлы foo__1.rb
и foo__2.rb
будут иметь файл exp
в виде foo.<pass>.exp
.
Ещё одно исключение: для тестов package-tree
файла exp
всегда будет называться pass.package-tree.exp
, независимо от имени теста.### Тесты командной строки (CLI)
Любой каталог <name>
, добавленный в папку test/cli/
, становится тестом. В этом каталоге должен находиться исполняемый файл test.sh
. При запуске его вывод будет сравниваться с содержимым файла test.out
в этой же папке.
Наши настройки Bazel создают два целевых объекта:
bazel run //test/cli:test_<name>
выполнит файл .sh
.bazel test //test/cli:test_<name>
выполнит файл .sh
и проверит его содержимое против того, что указано в файле .out
.Скрипты выполняются внутри Bazel, поэтому они будут запущены из вершины рабочего пространства и смогут получить доступ к исходным файлам и построенным целям через пути от корневой директории. В частности, скомпилированный бинарник Sorbet доступен по пути main/sorbet
.
Большинство тестов LSP являются тестами ожидаемых значений с дополнительными специфичными для LSP аннотациями. Они главным образом расположены в папке test/testdata/lsp
, но все файлы в папке test/testdata
также проходят тестирование в режиме LSP. Вы можете запустить тест test/testdata/lsp/<name>.rb
следующей командой:
bazel test //test:test_LSPTests/testdata/lsp/<name>
Тесты LSP имеют доступ к аннотациям def
и usage
, которые можно использовать для указания мест определения и использования переменной:
a = 10
# ^ def: a
b = a + 10
# ^ usage: a
```С этими аннотациями тест проверяет, что "Найти определение" из оператора сложения приведёт к `a = 10`, а "Найти все ссылки" из любого места вернёт как определение, так и использование.
Если переменная переопределена, она может быть аннотирована номером версии:
```ruby
a = 10
# ^ def: a 1
a = 20
# ^ def: a 2
b = a + 10
# ^ usage: a 2
Аннотации usage
могут принимать несколько номеров версий, разделённых запятой. Это полезно, если у вас есть переменные, которые переопределяются несколькими путями:
if some_condition
a = 10
# ^ def: a 1
else
a = 'hello'
# ^ def: a 2
end
p a
# ^ usage: a 1,2
Если местоположение не должно отображать ни одну определённую или используемую сущность, то используйте волшебную метку (ничего)
:
a = 10
# ^ def: (ничего)
Если местоположение должно отображать несколько определений (например, класс или модуль, открытый в нескольких файлах), то можно добавить второе def
с тем же именем:
class Foo
# ^^^ def: foo
end
class Foo
# ^^^ def: foo
end
При маркировании определений, соответствующих аргументам методов со значениями по умолчанию, потребуется отметить несколько определений: одно для самого определения аргумента и одно для его значения по умолчанию. Значение по умолчанию должно иметь другую версию и также быть помечено как default-arg-value
:
def foo(a: 1)
# ^ def: a 1
# ^ def: a 2 default-arg-value
p a
# ^ usage: a 1,2
end
```Это связано с переводом значений по умолчанию в CFG: существует условие, которое выбирает либо инициализацию переменной из переданного аргумента при вызове, либо установку значения по умолчанию, когда значение отсутствует.
Поиск всех ссылок работает по-разному в файлах спецификаций пакетов (`__package.rb`). Рассмотрим следующий пример:
```ruby
class Foo < PackageSpec
import Bar
Вызов "поиск всех ссылок" на Bar
в этом файле вернет только ссылки на Bar
в пакете Foo
. Тесты LSP имеют доступ к утверждениям import
и importusage
, которые вы можете использовать для тестирования этой функциональности.
class Foo < PackageSpec
import Bar
# ^^^ import: bar
class Foo::Baz
Bar.new
# ^^^ importusage: bar
end
С этими аннотациями тест LSP проверяет, что "поиск всех ссылок" на Bar
в операторе import Bar
возвращает использование Bar.new
.
Обратите внимание, что утверждение import
отличается от утверждения def
тем, что это фактический подкласс утверждения usage
. В данном случае def
, соответствующий import
, — это объявление импортированного пакета в PackageSpec. Вызов "поиск всех ссылок" на объявлении PackageSpec вернет все импорты этого пакета.
class Bar < PackageSpec
# ^^^ def: bar
import Bar
class Foo < PackageSpec
import Bar
# ^^^ import: bar
class Baz < PackageSpec
import Bar
# ^^^ импорт: бар
С этими аннотациями тест LSP проверяет, что "найти все ссылки" на Bar
из объявления class Bar < PackageSpec
возвращает само это объявление вместе с импортом.#### Тестирование "Перейти к определению типа"
Это немного похоже на "Найти определение" выше, но также имеет некоторые различия, потому что нет аналога "Найти все определения типа".
class A; end
# ^ тип-определение: some-label
aaa = A.new
# ^ тип: some-label
Ассерт тип: some-label
говорит "пожалуйста, имитируйте переход к определению типа
здесь, названному some-label
", а ассерт тип-определение: some-label
говорит "утверждает,
что результаты для some-label
точно такие местоположения".
Это значит, если определение типа может вернуть несколько локаций, то ассерты будут должны покрывать все результаты:
class A; end
# ^ тип-определение: AorB
class B; end
# ^ тип-определение: AorB
aaa = T.let(A.new, T.any(A, B))
# ^ тип: AorB
Если локация должна не отображать никакое определение или использование, то используйте волшебную метку (ничего)
:
# typed: false
class A; end
aaa = A.new
# ^ def: (ничего)
Тесты LSP также могут утверждать содержание ответов подсказок с помощью ассертов hover
:
a = 10
# ^ hover: Integer(10)
Если локация должна отображать пустую строку, используйте специальную метку (ничего)
:
a = 10
# ^ hover: (ничего)
Утвердите содержание конкретной строки ответа подсказки с помощью ассертов hover-line
:
a = 10
# ^ hover-line: 1 Integer(10)
Тесты LSP также могут утверждать содержание ответов завершения с помощью ассертов completion
.```ruby
class A
def self.foo_1; end
def self.foo_2; end
foo
end
Знак `^` соответствует положению курсора. Таким образом, в приведённом выше примере, курсор расположен так: `foo│`. Если бы знак `^` был под последним `о`, он был бы расположен так: `fo|o`. Используется только первый `^`. Если вы используете `^^^` в ассертах, тестовый хук отправит ассерт завершения в положении первого курсора.
Вы также можете написать тест для частичного префикса результатов завершения:
```ruby
class A
def self.foo_ Yö; end
def self.foo_2; end
``````diff
foo
# ^ завершение: foo_1, ...
конец
Добавьте суффикс , ...
в конец частичного списка результатов завершения, и тестовый управляющий механизм обеспечит соответствие указанным идентификаторам префиксу результатов завершения. Этот префикс всё ещё должен быть указан в порядке.
Если местоположение должно сообщить о нулевых результатах завершения, используйте специальное сообщение (ничего)
:
class A
def self.foo_1; end
def self.foo_2; end
zzz
# ^ завершение: (ничего)
конец
Чтобы написать тест для фрагмента, который будет вставлен в документ при выборе конкретного результата завершения, вы можете создать два файла:
# -- test/testdata/lsp/завершение/mytest.rb --
class A
def self.foo_1; end
end
A.foo_
# ^ применить-завершение: [A] элемент: 0
Ассертация apply-completion
говорит: "убедитесь, что файл mytest.A.edited
содержит результат вставки фрагмента завершения для 0-го элемента завершения в файл."
class A
def self.foo_1; end
def self.foo_2; end
foo
# ^ завершение: foo_1, foo_2
end
Знак ^
соответствует положению курсора. Таким образом, в приведённом выше примере, курсор расположен так: foo│
. Если бы знак ^
был под последним о
, он был бы расположен так: fo|o
. Используется только первый ^
. Если вы используете ^^^
в ассертах, тестовый хук отправит ассерт завершения в положении первого курсора.
Вы также можете написать тест для частичного префикса результатов завершения:
class A
def self.foo_1; end
def self.foo_2; end
``````diff
foo
# ^ завершение: foo_1, ...
конец
Добавьте суффикс , ...
в конец частичного списка результатов завершения, и тестовый управляющий механизм обеспечит соответствие указанным идентификаторам префиксу результатов завершения. Этот префикс всё ещё должен быть указан в порядке.
Если местоположение должно сообщить о нулевых результатах завершения, используйте специальное сообщение (ничего)
:
class A
def self.foo_1; end
def self.foo_2; end
zzz
# ^ завершение: (ничего)
конец
Чтобы написать тест для фрагмента, который будет вставлен в документ при выборе конкретного результата завершения, вы можете создать два файла:
# -- test/testdata/lsp/completion/mytest.rb --
class A
def self.foo_1; end
end
A.foo_
# ^ apply-completion: [A] element: 0
Ассертация apply-completion
говорит: "убедитесь, что файл mytest.A.edited
содержит результат вставки фрагмента завершения для 0-го элемента завершения в файл."
# -- test/testdata/lsp/завершение/mytest.A.rbedited --
class A
def self.foo_1; end
end
A.foo_1${0}
# ^ применить завершение: [A] элемент: 0
Как можно заметить, сложные ${...}
(место заполнения табулятора) выводятся буквально в результате, если они были отправлены в ответе завершения.
В настоящее время невозможно протестировать следующие части ответа завершения:
Для этих случаев лучшим решением является ручной тест в VS Code / вашем предпочитаемом редакторе и проверка ваших изменений. Для документации в частности почти все пути кода совпадают с подсказками, поэтому вы также можете написать тест подсказки.
Тесты LSP могут утверждать, что определенный элемент присутствует в поиске символов (запрос textDocument/workspaceSymbols
) с помощью ассерта symbol-search
:
class Project::Foo
# ^^^ symbol-search: "Foo"
конец
Ассерт symbol-search
может опционально указывать, как этот элемент должен отображаться в результатах поиска:
class Project::Foo
# ^^^ symbol-search: "Foo", имя = "Foo", контейнер = "Project"
конец
В этом случае контейнер
также может быть специальной строкой "(ничего)"
, чтобы указать, что элемент не имеет контейнера.
Ассерт symbol-search
также может указывать относительный ранг элемента в упорядоченных результатах поиска:```ruby
class Project::Foo
конец
Тестирование функции "Перейти к реализации" действительно схоже с тестовыми техниками для "Перейти к определению типа".
```ruby
модуль A
# ^ find-implementation: A
расширить T::Sig
расширить T::Helpers
интерфейс!
конец
класс B
#^^^^^^^ implementation: A
расширить T::Sig
включить A
# ^ find-implementation: A
конец
Существует два типа утверждений:
find-implementation: <символ>
означает выполнение запроса "Перейти к реализации" здесь. <символ>
указывает имя символа, который мы ищем.implementation: <символ>
отмечает местоположение, которое должно быть возвращено при вызове "Перейти к реализации" для данного <символ>
.Если запрос возвращает несколько местоположений, вы должны отметить все их с помощью implementation: <символ>
Чтобы написать тест для переименования констант, вам потребуется создать как минимум два файла:
# -- test/testdata/lsp/refactor/mytest.rb --
# typed: true
# frozen_string_literal: true
класс Foo
класс Foo
конец
конец
foo = Foo.new
# ^ apply-rename: [A] newName: Bar
Утверждение apply-rename
здесь говорит "симулировать действия пользователя, начавших переименование из позиции этого курсора". Вам потребуется добавить файл .rbedited
, который будет отражать результат изменения. В данном случае файл будет выглядеть следующим образом:```ruby
класс Bar класс Foo конец конец
foo = Bar.new
Вы можете протестировать, что недействительные переименования не применяются, добавив `invalid: true` к вашему тесту, как показано ниже:
```ruby
# -- test/testdata/lsp/refactor/mytest.rb --
# typed: true
# frozen_string_literal: true
класс Foo
класс Foo
конец
конец
foo = Foo.new
# ^ apply-rename: [A] newName: foo invalid: true
Для проверки конкретного сообщения об ошибке добавьте аргумент expectedErrorMessage
к тесту:
# typed: true
# frozen_string_literal: true
require_relative './constant__class_definition.rb'
sig { params(foo: Foo::Foo).returns(Foo::Foo) }
def foo(foo)
конец
класс Baz
# ^ apply-rename: [D] newName: Bar invalid: true expectedErrorMessage: Переименование констант, определённых в файлах .rbi, не поддерживается; символ Baz определён в test/testdata/lsp/rename/constant__rbi_class_reference.rbi
## Проверка пошагового типового контроля
В режиме LSP Sorbet выполняет обновление файлов по быстрому пути или медленному пути. Он проверяет структуру файла до и после обновления, чтобы определить, находится ли изменение в рамках быстрого пути. Если это так, он выполняет дальнейшие операции для определения набора файлов, которые требуют типового контроля.
Тесты LSP могут определять обновления файлов в файлах <name>.<version>.rbupdate
, которые содержат содержимое <name>.rb
после выполнения обновления. Например, файл foo.1.rbupdate
содержит обновленное содержимое foo.rb
.
Если тест содержит несколько файлов с использованием префикса со суффиксом __
, то все rbupdates с одинаковой версией будут применены одновременно. Например, foo__bar.1.rbupdate
и foo__baz.1.rbupdate
будут применены одновременно для обновления foo__bar.rb
и foo__baz.rb
.
Внутри файлов *.rbupdate
можно указать, что был выбран медленный путь, добавив строку с # assert-slow-path: true
. Можно указать, что был выбран быстрый путь для foo__bar.rb
и foo__baz.rb
с помощью # assert-fast-path: foo__bar.rb,foo__baz.rb
.Обратите внимание, что по умолчанию при тестировании множества файлов (например, *__1.1.rbupdate
+ *__2.1.rbupdate
) включаются все файлы в создаваемое и отправляемое на сервер LSP обновление файла. При тестировании изменений, которые утверждают, какие именно файлы были проверены на быстром пути с помощью assert-fast-path
, вам также, скорее всего, потребуется объявить, какие файлы не должны быть включены в обновление файла, оставив Sorbet решать, какой подмножество файлов должно быть проверено. Но независимо от того, был ли файл включен в множество обновлений, вам, скорее всего, потребуется утверждать, что ошибки возникают в определённых местах внутри файла. Для этого вы можете использовать # exclude-from-file-update: true
внутри файла rbupdate
. Обратите внимание, что при использовании этого подхода действие добавления утверждения exclude-from-file-update
в файл rbupdate
будет иметь эффект сдвига всех утверждений об ошибках на одну строку относительно места, где сервер LSP сообщит об этих ошибках. Чтобы обойти это, следует оставить пробелную строку в предыдущем файле, чтобы утверждение exclude-from-file-update
заменило пробелную строку вместо того, чтобы быть вставленной как совершенно новая строка. Поиск за spacer
в некоторых тестах fast_path
позволит увидеть пример.Чтобы создать обновление для файла RBI, используйте .rbiupdate
вместо .rbupdate
,
если вы не хотите имитировать эффект конвертации файла RBI в файл RB.### Логические тесты LSP
Возможно, записывать сессию LSP и использовать её как тест. Мы стараемся отказаться от такого типа тестирования, поскольку такие тесты сложно обновлять и понимать. В случае возможности попробуйте добавить ваш случай тестирования как обычный LSP тест.
Любой каталог <name>
, добавленный в test/lsp/
, станет тестом.
Этот каталог должен содержать файл с названием <folderName>.rec
, который содержит записанную сессию LSP.
Часто, когда тест проваливается, это происходит потому что что-то неважное изменилось в захваченном выводе, а не потому что есть баг в вашем коде.
Для повторной записи следов можно запустить
tools/scripts/update_exp_files.sh
Вероятно, вам потребуется просмотреть изменения и выполнить git checkout
для любых файлов с изменениями,
которые вы считаете реальными ошибками в вашем коде и исправить свой код.
update_exp_files.sh
обновляет каждый тип файла-шаблона, известный Sorbet. Это может занять некоторое время,
в зависимости от того, что требуется перекомпилировать и обновить. Некоторые более быстрые команды:
# Только обновляем файлы `*.exp` в `test/testdata`
tools/scripts/update_testdata_exp.sh
```# Обновляем только файлы `*.exp` в `test/testdata/cfg`
tools/scripts/update_testdata_exp.sh test/testdata/cfg
# Обновляем только один тестовый файл `.exp`
tools/scripts/update_testdata_exp.sh test/testdata/cfg/next.rb
# Обновляем только файлы `*.out` в `test/cli`
bazel test //test/cli:update
## Отладка
Общие рекомендации:
- Чтобы отладить обычное сборочное дерево Sorbet?
- `lldb bazel-bin/main/sorbet -- <args> ...`
- (Рассмотрите использование `--config=static-libs` для лучшей отладочной информации)
- Если вы видите странные ошибки Python на macOS, попробуйте `PATH=/usr/bin lldb`.
- Чтобы отладить уже запущенный процесс Sorbet (например, LSP)?
- Запустите Sorbet с флагом `--wait-for-dbg`
- `lldb -p <pid>`
- Установите точки останова и затем продолжайте выполнение программы
Кроме того, полезно привыкнуть к тому, чтобы исправлять ошибки, начиная с добавления `ENFORCE` (утверждения),
который бы захватил эту ошибку до фактического исправления. Это намного проще исправлять ошибки, когда есть хорошее сообщение об ошибке, которое указывает на нарушенное ограничение. `ENFORCES` бесплатны в выпусках.
## Создание документов
Исходники веб-сайта с документацией Sorbet находятся в папке
[`website/`](website/). Конкретно, документация находится в папке
[`website/docs/`](website/docs/), все документы создаются с использованием
Markdown и собираются с помощью [Docusaurus](https://docusaurus.io/).
[→ website/README.md](website/README.md)
^ Узнайте здесь, как работать с локальной версией сайта документации.## Редактор и среда
### Bazel
Bazel поддерживает наличие постоянного кеша предыдущих результатов сборки, чтобы повторные сборки для одних и тех же входных файлов были быстрее. Чтобы включить эту возможность, запустите этот скрипт для создания файла `./.bazelrc.local` и кеша:
```shell
tools/create_local_bazelrc.sh
Иногда полезно иметь несколько [рабочих деревьев] в Git. Это позволяет вам иметь несколько активных проверочных выгрузок Sorbet, используя одну и ту же папку .git/
. Для установки нового рабочего дерева с Sorbet:
tools/scripts/make_worktree.sh <worktree_name>
Многие команды сборки очень длинные. Вы можете рассмотреть вариант сокращения часто используемых команд с помощью shell-алиасов:
# мнемоника: 's' для sorbet
alias sb="bazel build //main:sorbet --config=dbg"
alias st="bazel test //... --config=dbg --test_output=errors"
Мы гарантируем, что файлы на C++ отформатированы с помощью clang-format
, а файлы Bazel BUILD — с помощью buildifier
. Чтобы избежать несоответствий между различными версиями этих инструментов, мы имеем скрипты, которые загружают и выполняют эти инструменты через bazel
:
tools/scripts/format_cxx.sh
tools/scripts/format_build_files.sh
CI завершится ошибкой, если будут найдены незаконченные файлы, поэтому вы могли бы захотеть автоматически форматировать свои файлы одним из следующих способов:1. Настройте привязку до коммита / до отправки, которая будет выполнять эти скрипты. 2. Настройте ваш редактор для выполнения этих скриптов. Смотрите ниже.
Набор инструментов clang
имеет отличную историю использования в редакторах: вы можете создать файл compile_commands.json
с использованием формата Compilation Database. Clang.
Множество инструментов на основе Clang используют этот файл для предоставления функциональностей, осознающих язык, например, в интеграциях редактора.
Чтобы создать файл compile_commands.json
для Sorbet с помощью bazel:
tools/scripts/build_compilation_db.sh
Этот скрипт создает файл ./compile_commands.json
(который игнорируется в .gitignore
). Этот файл закодирует некоторые пути в песочницу Bazel. Эти файлы могут устареть, особенно если они были сгенерированы с помощью Bazel genrule
. В частности, файл ./compile_commands.json
ссылается на файлы в конфигурации Bazel opt
(например, то, что было последним построено с ключами -c opt
/ --compilation_mode=opt
). Если вы видите устаревшие ошибки, рассмотрите возможность выполнения команды типа ./bazel build //main:sorbet -c opt
.
Вы можете экспериментировать со различными инструментами на основе clang, использующими базу данных compile_commands.json
. Некоторые предложения:
rtags — Инструмент на основе clang для перехода к определению / поиска ссылок / и т.д. ```shell brew install rtags
brew services start rtags
rc -J .
Для редакторов есть плагины rtags.
clangd — Реализация сервера языков на основе Clang
clangd
поддерживает больше функций, чем rtags
(в частности, отчет о диагностических сообщениях), но может быть немного медленнее из-за того, что он не предварительно индексирует весь ваш код, как это делает rtags.
После успешной компиляции Sorbet направьте свой редактор использовать исполняемый файл clangd
, расположенный в bazel-sorbet/external/llvm_toolchain_bkup/llvm-toolchain-15.0.7/bin/clangd
.
clang-format — Инструмент на основе Clang для форматирования исходного кода
Мы собираем clang-format
с помощью Bazel, чтобы гарантировать, что все используют одну и ту же версию. Вот как можно получить clang-format
из Bazel для использования его в своем редакторе:
# Соберите clang-format с помощью bazel
./bazel build //tools:clang-format
# Когда bazel снова запустится, эта символьная ссылка на clang-format исчезнет.
# Нам нужно скопировать её из Bazel, чтобы редактор мог использовать её:
mkdir -p "$HOME/bin"
cp bazel-bin/tools/clang-format $HOME/bin
# (Убедитесь, что $HOME/bin находится в вашем PATH, или используйте другой путь)
``` Со `clang-format` в вашем PATH вы должны иметь возможность найти плагин редактора, который будет использовать его для форматирования вашего кода при сохранении.
Примечание: наш скрипт форматирования передает несколько дополнительных опций в `clang-format`. Настройте ваш редактор для передачи этих опций вместе с `clang-format`:
```shell
-style=file -assume-filename=<CURRENT_FILE>
CLion — JetBrains C/C++ IDE
CLion может узнать о базе данных compile_commands.json
.
Заменяет ваш полный процесс редактирования текста (полноценный IDE).
[vscode-clangd] — Расширение Clangd для VS Code
Это расширение интегрирует clangd (см. выше) с VS Code. Оно также будет запускать clang-format
каждый раз при сохранении. Примечание: Расширение C/C++ от Microsoft не работает корректно с compile_commands.json
Sorbet.
Настройки этого репозитория автоматически конфигурируют vscode-clangd для запуска исполняемого файла clangd в директории bazel-sorbet
. Обратите внимание, что вам потребуется скомпилировать Sorbet хотя бы один раз перед тем, как он начнёт работать.
clangd работает с compile_commands.json
, поэтому убедитесь, что вы запускаете скрипт ./tools/scripts/build_compilation_db.sh
.
[vscode-clangd]: https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangdВот несколько примеров конфигураций:
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )