1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/mirrors-sorbet

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
README.md 59 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 04.03.2025 09:00 fc8d15e

Логотип Sorbet

Sorbet

Этот репозиторий содержит Sorbet — быстрый и мощный анализатор типов, предназначенный для Ruby. Целью является простое добавление в существующие проекты с постепенным внедрением типов, а также быстрая реакция на ошибки и предложения.

Документация в этом README предназначена специально для участников проекта Sorbet. Вы можете захотеть:

Если вы работаете в Stripe, вам может понадобиться просмотреть http://go/types/internals для документов о специфических рабочих процессах развития и историческом контексте Stripe.

Содержание- Основные принципы дизайна Sorbet для пользователей

На раннем этапе нашего проекта мы определили некоторые руководящие принципы того, как должно ощущаться взаимодействие с Sorbet.

  1. Явность

    Мы готовы писать аннотации, а также считаем их полезными; они делают код более читаемым и предсказуемым. Мы здесь, чтобы помочь читателям так же, как и авторам.

  2. Полезность, а не бремя

    Хотя это явно, мы стремимся сделать его компактным. Это проявляется в нескольких аспектах:

    • сообщения об ошибках должны быть понятными;
    • избыточность должна компенсироваться большей безопасностью.
  3. Как можно проще, но достаточно мощным

    В целом, мы не верим в сверхсложные системы типов. Они имеют своё место, и нам требуется достаточная выразительная способность, чтобы моделировать (достаточно) реальный Ruby код, но при прочих равных условиях мы хотим быть проще. Мы верим, что такая система масштабируется лучше, и — самое важное — легче для наших пользователей учиться и понимать.

  4. Совместимость с Ruby

    Особенно важно, чтобы новый синтаксис не требовался. Существующий синтаксис Ruby позволяет использовать большую часть существующего инструментария (редакторы и т.д.). Также, цель Sorbet заключается в постепенном улучшении существующего Ruby кодового базиса. Новый синтаксис делает совместимость с существующими инструментами сложнее.5. Масштабируемость

    По всем направлениям: скорость выполнения, количество участников, строки кода, возраст кодовой базы. Мы работаем с крупными Ruby кодовыми базами, и они будут становиться ещё больше.

  5. Может быть постепенно внедрен

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

Быстрый старт

  1. Установите зависимости

    • brew install bazel autoconf coreutils parallel
  2. Клонируйте этот репозиторий

    • git clone https://github.com/sorbet/sorbet.git
    • cd sorbet
  3. Соберите Sorbet

    • ./bazel build //main:sorbet --config=dbg
  4. Запустите Sorbet!

    • bazel-bin/main/sorbet -e "42 + 'hello'"

Изучение работы Sorbet

Мы документировали внутреннюю работу Sorbet в отдельном документе. Перекреститесь между этим документом и текущим, чтобы узнать, как работает Sorbet и как его изменять→ внутренняя структура.md

Также есть онлайн-представление, которое описывает высокий уровень архитектуры Sorbet и причины его быстродействия:

→ Быстрая проверка типов для Ruby

Сборка Sorbet

Существует несколько способов сборки sorbet. Этот является наиболее распространенным:

./bazel build //main:sorbet --config=dbg

Это создаст исполняемый файл в bazel-bin/main/sorbet (см. "Запуск Sorbet" ниже). Вы можете передать множество различных опций при сборке sorbet:- --config=dbg

  • Самая распространенная конфигурация сборки для разработки.
  • Хорошие трассировки стека, выполняются все ENFORCEs.
  • --config=sanitize
    • Подключает дополнительные санитайзеры, в частности: UBSan и ASan.
    • Обнаруживает большинство ошибок памяти и непредопределённого поведения.
    • Основательно увеличивает размер и замедляет выполнение бинарника.
  • --config=debugsymbols
    • (Включен в --config=dbg) отладочные символы, ничего больше.
  • --config=forcedebug
    • Использует больше памяти, но отчётливо указывает даже более подробные проверки целостности.
  • --config=static-libs
    • Принудительно использует статическую связывку (по умолчанию Sorbet использует динамическую связывку для более быстрой сборки).
    • Sorbet уже использует эту опцию в выпусках (см. ниже).
  • --config=release-mac и --config=release-linux
    • Точная конфигурация выпуска, которую мы отправляем нашим пользователям.

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

  • -c opt
    • Включает оптимизации компилятора clang (например, -O2).

Эти аргументы не являются вза Müslümekli. Например, распространённой парой при отладке является следующее:

--config=dbg --config=sanitize

В файле .bazelrc можно найти описание всех этих опций (и других).

Частые ошибки компиляции**(Mac) "Необходимо указать версию 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

Запустите Sorbet на выражении:

bazel-bin/main/sorbet -e "1 + false"

Запустите Sorbet на файле:

bazel-bin/main/sorbet foo.rb

Запуск bazel-bin/main/sorbet --help покажет множество опций. Вот наиболее часто используемые для вкладчиков:- -p <IR>

  • Запрашивает у Sorbet выводить любое данное промежуточное представление.
  • Смотрите --help для доступных значений <IR>.
  • --stop-after <phase>
    • Полезно при наличии ошибки в более поздней фазе, чтобы завершить выполнение рано для отладки.
  • -v, -vv, -vvv
    • Выводит информацию логгера (увеличивает подробность)
  • --max-threads=1
    • Полезно для выявления ошибок связанных с конкурентностью.
  • --wait-for-dbg
    • Останавливает запуск Sorbet и ждет подключения дебаггера
    • Это полезно, когда вы не контролируете запуск процесса (LSP)## Запуск тестов

Чтобы запустить все тесты:

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
  1. Цель теста (если вам нужно повторно запустить этот конкретный тест с помощью bazel test <target>),
  2. Файл, содержащий вывод теста (можно выполнить).

Чтобы просмотреть вывод проваленного теста, либо:

  • Запустите bazel test с флагом --test_output=errors,
  • Копируйте/вставляйте содержимое файла *.log и запустите его (вывод будет открыт в less).

Тестирование Sorbet против pay-server

Это относится к вкладу в Sorbet в Stripe.

Если вы работаете в Stripe и хотите протестировать ваш ветвь против pay-server, это можно сделать по адресу http://go/types/local-dev.

Написание тестовМы пишем тесты путём добавления файлов в подпапки директории test/.

Каждая отдельная подпапка является "магической"; каждая содержит конкретные типы тестов. Мы стремимся сделать наши тесты полностью воспроизводимыми.> Примечание для C++: В C++ хэш-функции должны давать одинаковый результат для одного и того же входного значения внутри одной сессии выполнения программы.

Таким образом, мы ожидаем, что все видимые пользователю выходные данные будут явно сортироваться с использованием ключа, который остаётся стабильным между запусками.

Существует множество способов тестирования Sorbet, некоторые "лучше", чем другие. Мы расположили их ниже в порядке от наиболее предпочтительного до наименее предпочтительного. И всегда предпочитаем наличие каких-либо тестов отсутствию тестов!

Тесты test_corpus

Первый тип тестов может называться либо 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 являются тестами ожидаемых значений с дополнительными специфичными для 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

^ завершение: foo_1, foo_2

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

^^^ symbol-search: "Foo", ранг = 1

конец


Тестирование функции "Перейти к реализации" действительно схоже с тестовыми техниками для "Перейти к определению типа".

```ruby
модуль A
#      ^ find-implementation: A
  расширить T::Sig
  расширить T::Helpers
  интерфейс!
конец

 класс B
#^^^^^^^ implementation: A
  расширить T::Sig
  включить A
#         ^ find-implementation: A
конец

Существует два типа утверждений:

  1. find-implementation: <символ> означает выполнение запроса "Перейти к реализации" здесь. <символ> указывает имя символа, который мы ищем.
  2. 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

-- test/testdata/lsp/refactor/mytest.A.rbedited --

typed: true

frozen_string_literal: true

класс Bar класс Foo конец конец

foo = Bar.new

^ apply-rename: [A] newName: Bar


Вы можете протестировать, что недействительные переименования не применяются, добавив `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.

  • Строчки, начинающиеся со слова "Read:", будут отправлены в sorbet как входные данные.
  • Строчки, начинающиеся со слова "Write:", будут ожидаться от sorbet как выходные данные.

Обновление тестов

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

Для повторной записи следов можно запустить

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

Иногда полезно иметь несколько [рабочих деревьев] в 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. Настройте ваш редактор для выполнения этих скриптов. Смотрите ниже.

Настройка редактора для C++

Набор инструментов 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

    Убедитесь, что демон rtags автоматически запускается macOS при необходимости

    brew services start rtags

    Перейдите в папку sorbet

    Убедитесь, что файл ./compile_commands.json существует

    Укажите rtags использовать наш файл compile_commands.json для индексации sorbet

    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 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/mirrors-sorbet.git
git@api.gitlife.ru:oschina-mirror/mirrors-sorbet.git
oschina-mirror
mirrors-sorbet
mirrors-sorbet
master