Перейти к содержимому

solo_lints

solo_lints — плагин анализатора для кода на solo и flutter_solo. Он сообщает об ошибках, которые компилируются, работают и часто проходят первый тест: ожидание в уборке, которое так и не случается; ресурс, потерянный при отмене; catch, который сбрасывает исправное устройство; задача, которая ждёт саму себя; один ключ на все запросы; ожидание, которое отмена не прерывает. За большинством правил стоит раздел «Частых ошибок» — страницы solo, где каждая ошибка показана рядом с правильным способом.

Правила ядра, async_job, приходят из async_job_lints, и этот плагин их включает: коду на solo нужен только solo_lints. Называйте один из двух плагинов, а не оба: сервер анализа разрешает версии всех плагинов вместе, и если они не сходятся, не работает ни один.

Назовите плагин в analysis_options.yaml в корне пакета или рабочего пространства:

plugins:
solo_lints: ^0.1.5

Плагин сервер анализа скачивает сам, поэтому в pubspec.yaml он не зависимость. Плагинам нужен Dart 3.10 или новее. Его диагностики показывают dart analyze и редактор. Запускайте dart analyze для корня пакета или для отдельных файлов: для каталога ниже корня, например dart analyze lib, он не показывает ни одной. flutter analyze показывает их на Flutter 3.38, но начиная с Flutter 3.44 не показывает ни одной; во Flutter-пакете плагин проверяют через dart analyze. Правку раздела plugins редактор видит после перезапуска своего сервера анализа.

Предупреждения включаются, как только плагин назван. Четыре линта, catch_without_check, key_per_request, ignore_failure_hides_bugs и bare_await, включаются по имени:

plugins:
solo_lints:
version: ^0.1.5
diagnostics:
catch_without_check: true
key_per_request: true
ignore_failure_hides_bugs: true
bare_await: true

Вместо true правилу можно дать уровень: error, warning или info. key_per_request: warning включает линт как предупреждение, и тогда dart analyze на нём падает, как на любом предупреждении. false выключает предупреждение.

Диагностику глушат именем плагина перед именем правила: // ignore: solo_lints/ctx_in_cleanup строкой выше или // ignore_for_file: solo_lints/ctx_in_cleanup на весь файл.

Быстрые исправления предлагает редактор. dart fix их не применяет: он применяет исправления самого анализатора, а не плагинов.

На Dart 3.12 dart analyze в пакете, где назван плагин, печатает каждую диагностику самого анализатора дважды. Dart 3.13 печатает её один раз.

Правило Вид Сообщает
ctx_in_cleanup предупреждение член контекста, который ждёт, вызван в уборке
release_on_the_call предупреждение освобождение зарегистрировано строкой ниже вызова, который приносит ресурс
catch_without_check линт широкий catch в теле, которое действует, не спросив задачу
sync_fake_async предупреждение async-колбэк fakeAsync
await_own_queue предупреждение задача ждёт задачу из очереди своего же контроллера
key_per_request линт один ключ у задачи Policy.droppable, чьё тело читает аргумент
ignore_failure_hides_bugs линт ignoreFailure() у задачи, которая называет свои ожидаемые ошибки
bare_await линт await в теле задачи, который идёт мимо контекста

Пять правил ядра описаны в README async_job_lints: их имена в таблице ведут к их разделам в нём. Там // ignore: ставит перед правилом async_job_lints/; когда назван этот плагин, — solo_lints/, как и для трёх правил ниже.

Job<void> syncAndRecord(int item) =>
enqueue<Ready, void>(key: _Op.syncAndRecord, (ctx) async {
final path = await sync(item).value; // await_own_queue
await recordPath(path).value; // await_own_queue
});

sync(item) ставит задачу в очередь за той, что её ждёт, и ничего не движется, пока close() не отменит обе. Выполняйте задачу через ctx.run(_sync(item)). Правило берёт метод того же контроллера, который приносит SoloJob — по объявленному типу или, если метод объявлен в том же файле, по тому, что он возвращает, — а также close() и cancelAll(). Оно смотрит в тело задачи с SoloContext, поставленной в очередь этого контроллера, включая то, что тело возвращает, в действия, которых это тело ждёт через join, abandonable и uncancellable, и в его уборку: её очередь тоже ждёт. Контроллер — и mixin с on Solo. bare_await, который сообщает об остальных простых ожиданиях тела, здесь молчит. Подробнее в разделе «Шаги детьми».

Job<Profile> load(String id) => enqueue<Ready, Profile>(
key: _Op.load, // key_per_request
policy: Policy.droppable,
(ctx) => ctx.abandonable(() => api.load(id)),
);

Политика сравнивает ключи, а не аргументы. При одном ключе на все load(id) Policy.droppable принимает load('grace'), вызванный, пока идёт load('ada'), за дубликат и отдаёт его вызывающему профиль Ады. Положите аргумент в ключ: key: (_Op.load, id). Правило берёт enqueue класса Solo с Policy.droppable и enqueueJob с job и той же политикой, когда ключ не читает ни одного параметра объемлющего метода или функции, а тело читает. Ключ, который читает локальную переменную или параметр замыкания, правило не трогает. Это линт, а не предупреждение: один ключ иногда задуман, когда результат от аргумента не зависит. Подробнее в разделе «Ключ на каждый запрос».

Быстрое исправление заменяет ключ K записью (K, a, b) из параметров, которые читает тело. Исправление предлагается, когда каждый из них сравнивается по значению: String, число, bool, перечисление или один из них с null.

SoloJob<Profile> load() => enqueue<Ready, Profile>(
(ctx) => api.load(),
isExpectedError: (e) => e is ApiException,
);
profiles.load().ignoreFailure(); // ignore_failure_hides_bugs

isExpectedError уже держит названные в нём ошибки тихими и пропускает баги в зону, а ignoreFailure() прячет и их. Уберите ignoreFailure(): ожидаемые ошибки останутся тихими, а баг по-прежнему будет замечен. Правило берёт ignoreFailure() у задачи, которая пришла из job или enqueue с isExpectedError или из add поля с collect или accumulate с ним, в том числе через метод или функцию той же библиотеки, чьё тело — => или один return, когда задача дальше никому не нужна: сам вызов или каскад, значение которого отброшено, как у onPressed кнопки. У страницы частых ошибок раздела для этого правила нет. Это линт, а не предупреждение: задача, чей исход никому не нужен, а отчёт оставлен хукам, может вызывать ignoreFailure() намеренно, см. «Ответ за каждую ошибку».

Плагин работает на Dart 3.10 и новее, с analyzer от 12.1 до 14. SDK Dart, которому нужен analyzer 15, эту версию не загружает, и её правила молчат, пока выпуск не поднимет границу.

Типы solo правила узнают по именам и библиотеке, откуда те пришли, и от пакета не зависят. На версии solo, где имя сменилось, правило молчит, а не сообщает о правильном коде. Правила ядра — это правила async_job_lints, и их изменения перечисляет его список изменений.