Перейти к содержимому
XBSL (1C:Element)
Русский
Esc
↑↓navigate↵open⌘Jpreview
На этой странице

Проверка исходников

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

Основной режим инструмента – проверка исходников. Полный перечень правил вынесен на отдельную страницу (Правила), а здесь написано, как управлять запуском.

Ключи CLI

--list-rules, --where (корень данных Элемента, источник и версии), --select/--enable/ --ignore (по id правила, по группе – это часть id до / – или по букве тира), --fix, --baseline/--write-baseline, --element-version, --data-dir, --lang, --format text|json|codeclimate, --summary/--compare.

--fix чинит механические находки прямо в файлах и отчитывается об оставшемся. Он убирает хвостовые пробелы, правит символы типографики (длинное тире на среднее, … на ..., кудрявые кавычки и ёлочки в комментариях на прямые, знак не с клавиатуры в комментарии на набираемый: → на ->) и приводит смешанные переводы строк к преобладающему стилю. Правки применяются только однозначные и только для правил текущего запуска, поэтому --fix --enable typography заодно гасит долг по тире и ёлочкам. В группе есть и typography/en-dash-comment, так что тот же запуск заменит среднее тире в комментарии дефисом; проект, который пишет там среднее тире, добавляет --ignore typography/en-dash-comment. Всё, что требует суждения, остаётся на месте.

Исправления кода тоже применяются: лишние приведения, импорты и имена локальных переменных, настойчивая операция, лишние проверки и литералы вместо конструкторов. Между проходами исходники проверяются заново, поэтому правки используют актуальные смещения. --fix --baseline <файл> защищает места, принятые в начале запуска, и не перезаписывает сам список. Новые похожие находки не получают эту защиту вместо прежних. Правки, пересекающие защищённое место, пропускаются; оставшиеся находки попадают в отчёт. Выполняется не более десяти проходов. --fix нельзя сочетать с --write-baseline и --prune-baseline.

Группа comment/ судит не знаки комментария, а его слог: comment/subjunctive (частица “бы” – замечание просит слово условия, потому что простое снятие частицы превращает предположение в утверждение о коде), comment/first-person (“мы”, “наш” и глаголы первого лица, а также английская строка комментария из словаря перевода), comment/emphasis-caps (слово прописными ради ударения: служебное, любое другое, которое файл пишет и строчными, однобуквенное посреди фразы, приставка отрицания и прописные английской строки комментария в словаре) и comment/dash-condition (условие через тире, как в “склад не задан - берется основной”; замечание предлагает формулировку со словом условия). Правила читают комментарии модулей, описаний элементов и файлов ресурсов. По умолчанию они выключены – на коде, где такого соглашения нет, срабатывают массово, – и проект, который его принял, включает группу в своём CI ключом --enable comment. --fix --enable comment/emphasis-caps возвращает регистр слову с ударением; у остальных правил исправления нет, перефразирует автор. Проект, который пишет в комментариях кода дефис, добавляет --enable typography/en-dash-comment.

Для редактора есть --stdin --filename ИМЯ: он проверяет один буфер из stdin и применяет только пофайловые правила. JSON ({diagnostics, summary}) при этом тот же, что отдаёт MCP-сервер.

Сводка этого JSON считает находки по правилам, файлам и уровням – by_rule, by_file и by_severity, причём последний называет все три уровня даже при нуле, – так что запуск можно взвесить, не читая список: какие правила сработали, в каких файлах и есть ли среди находок ошибка. Инструмент MCP lint_paths несёт те же ключи, а с compact отвечает сводкой, полными записями находок уровня error и – пока находок не больше десяти – самим списком, по строке на находку. Дальше вместо списка идёт их число и подсказка, как прочитать остальные. Сколько бы проверка ни нашла, краткий ответ укладывается в 12 000 знаков: сверх этого он обрезает с конца списки, которые растут с проверкой, в том числе записи ошибок, сохраняет все счетчики и называет каждый обрезанный список в truncated.

Пути нескольких корней проекта никогда не проверяются как один проект. Корень – это каталог с описанием проекта, поэтому два рабочих дерева одного проекта в одном запуске – это два корня, как и проекты внутри каталога, переданного целиком. Загруженные одним проектом, два дерева давали находку на каждое общее имя, а список принятых замечаний первого дерева судил файлы второго. Теперь каждый корень проверяется отдельно: со своим проектом вокруг файла, переданного по одному, со списком принятых, найденным над его файлами, и с --as-ci – с задачей конвейера своей рабочей копии. Находки всех корней идут одним списком. Текстовый отчет добавляет под итогом строку на каждый корень, а JSON – запись на корень в summary.projects: его счетчики, список принятых и задачу конвейера, которые тогда уходят из верхнего уровня сводки. xbsl baseline add и MCP baseline_prune меняют один файл, поэтому пути, чьи корни держат свои списки, они отклоняют и называют эти файлы.

--summary выводит вместо находок их счёт: по строке на правило с числом файлов и находок, а под таблицей итог запуска. --compare ФАЙЛ в первый раз выводит то же самое и сохраняет запуск в файл. Следующий запуск с этим файлом выводит только изменения. В таблице остаются правила, у которых изменились находки: их файлы и находки сейчас и число появившихся и исчезнувших находок. Под таблицей идут сами изменившиеся находки, по строке на каждую, пока их не больше десяти. Полный список изменений всегда лежит в файле, перед находками. Если ничего не изменилось, отчёт умещается в одну строку. Так сравнивают находки на наборе проектов до и после правки правил:

xbsl demo --compare запуски.json      # первый запуск сохраняет счёт
# ...правка...
xbsl demo --compare запуски.json
# правило              файлов  находок  появилось  исчезло
# typography/ellipsis       0        0          0        1
# whitespace/trailing       1        2          1        0
# - demo/Acme/Tasks/Основное/КарточкаЗадачи.xbsl:7:63: [typography/ellipsis] Символ многоточия U+2026 в комментарии – использовать три точки '...'.
# + demo/Acme/Tasks/Основное/КарточкаЗадачи.xbsl:8:50: [whitespace/trailing] Хвостовые пробелы в конце строки.
# По сравнению с запуски.json: появилось 1, исчезло 1. Находок: 4; правил с находками: 3; файлов с находками: 1 из 5

Находку опознают по пути из командной строки, файлу под ним, строке, столбцу, правилу и тексту. Пути двух запусков сопоставляются по каталогу, который они называют, поэтому demo и его абсолютный путь – один и тот же путь. Путь, каталог которого другой запуск не проверял, сопоставляется по написанию: xbsl demo из двух рабочих деревьев репозитория сравнивает две рабочие копии. Путь без пары в сравнение не входит. Не входит и правило, которое выбрал только один из запусков, если ключи --select, --ignore или --enable у них разные. О каждой такой части говорит отдельная строка с числом её находок. Запуск, сохранённый на другом языке вывода, не принимается: текст каждой находки был бы другим. Когда у проектов есть свой список принятых замечаний, ключ --no-baseline не даст ему спрятать изменение. Инструмент MCP lint_paths принимает тот же файл в параметре compare и отвечает тем же сравнением в виде данных.

xbsl --index ПУТЬ вместо проверки выводит в stdout JSON-индекс проекта. В индексе объекты вместе с табличными частями, локальными типами модулей и семействами членов для автодополнения после точки. Там же объявления методов – аннотации, список параметров как написан, тип возврата и комментарий-описание над объявлением – и именованные компоненты форм. Пути записаны в стиле POSIX относительно корня, строки нумеруются с единицы. Всё это нужно редакторам для перехода к определению и автодополнения.

--format codeclimate печатает отчёт GitLab Code Quality в формате issue Code Climate, с путями относительно текущего каталога. Запускайте его из корня репозитория и сохраняйте вывод как артефакт codequality.

Правила подробно

Полный перечень всех 280 правил базового набора – severity, включенность по умолчанию, область, ссылки на разделы документации платформы – лежит в RULES.ru.md. На месте его печатает xbsl --list-rules, и он учитывает ещё правила и переопределения уровней у установленных плагинов. Обзор по тирам есть в README, а ниже разобрано, что именно проверяют глубокие тиры.

Правило, которое судит по числу, это число называет. --list-rules сужается так же, как набор правил при проверке, поэтому спросить можно и про одно правило:

xbsl --list-rules --select code/duplicate-method-body
# D     code/duplicate-method-body     warning Тело метода повторяется в другом файле
#        параметр min-lines = 5 (умолчание 5, env XBSL_CODE_DUPLICATE_METHOD_BODY_MIN_LINES) - ...

Параметр объявлен там же, где правило им пользуется, поэтому список не может разойтись с величиной, по которой правило судит. Любой параметр переопределяется переменной среды. Имя её складывается из XBSL_, идентификатора правила и имени параметра, а всё, кроме букв и цифр, превращается в _. Список печатает и действующее значение, и умолчание. Значение, которое не удалось разобрать, оставляет умолчание, и об этом сказано прямо. Проверка со сдвинутыми параметрами называет их в своей сводке: ключ params в json и отдельная строка в тексте. Изменённый порог меняет находки, поэтому о нём и сообщают. Инструмент MCP list_rules(select=...) отдаёт те же записи в params.

Типовые правила тира D покрывают каждую типовую позицию в коде – новый, приведение как, аннотации, сигнатуры – и каждое значение Тип: в yaml, включая объединения А|Б|?, дженерики и nullable. Корень обязан быть известным типом: stdlib, объект проекта, локальный тип модуля или глобальный тип подключённой библиотеки (см. ниже). Цепочка с корнем-объектом проекта не выходит за семейство порождаемых им типов. В семейство входят производные из документации дистрибутива (Ссылка, Объект, СоздатьОбъект, автоформы…), табличные части и структуры модулей. Квалификация через пространство имён (Справочник.X.Ссылка) дополнительно сверяет вид объекта, а значения перечислений проекта проверяются и в коде, и в привязках yaml.

Типы подключённых библиотек линтер берёт из их архивов. Проект.yaml объявляет только координаты – Поставщик, Имя и Версия, – поэтому имена ищутся в архиве {Поставщик}-{Имя}-{Версия}.xlib. Ищут его в каталоге описания проекта и выше, до четырёх уровней: там архив и лежит при поставке исходников. Известным становится элемент с ОбластьВидимости: Глобально, остальное – внутреннее дело библиотеки. Если архива рядом нет, типы библиотеки остаются неизвестными, как и до появления этой поддержки.

Межфайловые правила тира D ловят то, о чём компилятор сообщает поздно или молчит. Это обработчик из yaml без метода в парном модуле. Тип чужой подсистемы без импорта его пространства имён – подсистемы для элемента в её корне, Подсистема::Пакет для элемента пакета: импорт подсистемы её пакетов не даёт. Модуль проекта судится за элементы пакетов и корня подсистемы, а таблицы запроса тоже считаются ссылками: таблицы блока Запрос{...} – через импорты модуля, таблицы .xbql виртуальной таблицы – через секцию Импорт её yaml. Полное имя проекта, которое ведёт в пространство имён без своего элемента, сообщается с автоисправлением. Таблицы динамического списка тоже считаются ссылками его yaml, а частичное имя Подсистема::Имя сверяется с местом элемента так же, как полное. Присоединённые таблицы настроек ввода ссылки тоже считаются ссылками, а таблица должна быть публичной везде, где её называют список или запрос. Динамический список с типом автоформы, у которого в полях нет реквизита объекта. Вызов соседнего компонента Компоненты.X.Метод() без аннотации видимости. Несоответствия окружений: @НаСервере из клиентского обработчика без @ДоступноСКлиента, клиентский модуль в HttpСервисе. Зарезервированные имена: Тип или type у поля или параметра, свойство компонента со встроенным именем. Методы, на которые никто не ссылается; файл, который не разобрался, при этом считается упоминанием, а словарь перевода нет. И свойства верхнего уровня объектов по метамодели конфигурации. Группа query/ разбирает блоки Запрос{ ... } и сверяет таблицы ИЗ и СОЕДИНЕНИЕ с объектами проекта и их табличными частями. Блок с конструкциями вне поддержанного подмножества – временные таблицы, объединения, подзапросы – пропускается целиком, а не угадывается.

Читаются и комментарии. Слог комментария судит группа comment/, и одно её правило смотрит через файлы: comment/unknown-name собирает идентификаторы всех модулей, имена всех описаний элементов и каталог платформы и сообщает о слове комментария, которое похоже на идентификатор и которого не знает никто из них, – о методе, который переименовали, а упоминание осталось. Падежная форма известного имени, строка закомментированного кода и имя, рядом с которым названа другая система, не судятся. Систему, которую знает только этот проект, он объявляет ключом --other-system в той же строке CI. Название другого продукта в прозе без такого соседа выглядит так же, поэтому правило по умолчанию выключено и проект включает его ключом --enable comment/unknown-name.

Часть правил повторяет предупреждения IDE платформы, чтобы те же места были видны до того, как код откроют там, и каждое правило следует условию, которое применяет компилятор. code/unused-local и code/unused-loop-var разрешают имена по блочным областям видимости так же, как компилятор. Они сообщают о переменной знч, пер или исп, которую метод не читает, и о переменной цикла для Х из; у переменной, которой только присваивают, своё сообщение. Чтение внутри замыкания и внутри интерполяции строки или запроса засчитывается. Именованный аргумент Имя = значение, член .Имя и одноимённая переменная соседнего блока чтением не считаются. Как и в IDE, не проверяются счётчик для Х = А по Б, переменная поймать и параметры. Неиспользуемое имя исп исправляется снятием имени: исп Выражение держит ресурс до конца той же области видимости.

code/lambda-changes-outer-local идёт по тем же блочным областям видимости и находит ошибку, которую компилятор показывает только при применении сборки. Тело лямбды не может присваивать локальную переменную, объявленную вне лямбды: пер или параметр метода либо внешней лямбды. Член или элемент захваченного значения менять можно, как и переменную или параметр самой лямбды. Переменные знч, исп, переменные цикла и поймать доступны только для чтения везде, и на них у компилятора своя ошибка, поэтому правило о них не сообщает.

Пять правил тира C повторяют ошибки компиляции, на которые сервер отвечает откатом сборки: присваивание самому себе (code/self-assignment), левая часть, которая не может принять значение (code/assign-target), присваивание имени только для чтения (code/assign-readonly), код после инструкции, которая всегда завершает блок (code/unreachable-statement), и прервать/продолжить/возврат, которым некуда передать управление (code/misplaced-jump). Правила читают один файл и работают на каждое нажатие клавиши; где вердикт требует типа, которого файл не называет, – получателя в Объ.Поле = ..., типа значения в выбор, – они молчат и оставляют случай компилятору.

Ещё шесть файловых проверок находят пропущенные инициализаторы, значения по умолчанию обязательных полей, возврат закрываемого ресурса и повторы объявлений, значений выбора и типов исключений. Присваивание полю только для чтения проверяется через получателя, чей тип структуры или исключения известен из этого же файла. Неизвестные типы получателей и выражения констант, требующие вычисления, остаются за компилятором. code/statement-no-effect считает ошибкой любой оператор-выражение, кроме вызова или выброса исключения, даже если внутри есть вызов метода. Присвоенные значения и результаты коротких лямбд используются и остаются допустимыми.

code/unused-import проверяет, искал ли компилятор хоть раз тип в импортированном пространстве имён. Слово модуля само по себе ничего не удерживает: строка импорта, член после точки и локальная переменная с именем элемента употреблением не считаются. А вот значение может быть употреблением. Свойство парного yaml или результат метода другой подсистемы приносит свой тип, как и колонка запроса, и обращение к члену такого значения использует пространство этого типа. Если за компилятором не пройти, потому что модуль или парный yaml не разбирается или объявления элемента не читаются, правило молчит. Исправление снимает строку импорта.

style/constructor-literal сообщает о вызове вроде новый Дата("9999-12-31") или новый Длительность(1, 30, 0, 0): о конструкторе типа, у которого есть литерал, только с постоянными аргументами. Судится и НайтиТип с постоянным именем. Условие компилятора значение не проверяет, поэтому строка, которую литерал не вмещает, тоже находка. --fix ставит литерал там, где он хранит то же значение, например Дата{9999-12-31}, 1ч30м или Истина, а в остальных случаях вызов не трогает. НайтиТип не переписывается никогда: вызов возвращает Тип?, а литерал называет сам тип, и у переменной, объявленной из вызова, сменился бы тип.

Ещё четыре предупреждения IDE платформы обходятся без типа выражения. style/boolean-ternary находит тернарный оператор с ветвями Истина и Ложь в модуле, интерполяции строки или привязке yaml; исправление записывает условие или отрицание, и отрицание следует платформе: не покрывает сравнение, но не это, и и или, поэтому Х это Т превращается в Х это не Т. style/redundant-scope находит область, которая единственная инструкция своего блока, и снимает её. style/redundant-union-member убирает из объединения повтор, второе Неопределено и член, который уже покрывают Объект или базовый тип каталога; обобщённая база покрывает только с теми же аргументами, потому что каталог не говорит, какие параметры типа принимают более широкий аргумент. code/duplicate-import находит повторный импорт, считая короткое и полное имя одним пространством, а yaml/duplicate-import так же читает секцию Импорт элемента, хотя IDE её не проверяет.

Четыре правила тира D судят тип выражения, тогда как правила выше судят записанный тип. code/redundant-cast сообщает о приведении к типу, который у значения уже есть, а code/cast-to-non-null – о приведении, которое только отбрасывает Неопределено и которое можно заменить на !. code/redundant-undefined-guard сообщает о ??, ! или ?. над значением, в типе которого нет Неопределено, а code/redundant-type-check – о проверке Х это Тип, результат которой решили типы. Тип берётся из того, что написано в исходниках. Его называют объявление, приведение или конструктор. Компонент парной разметки получает тип из объявления, поэтому Компоненты.Поле.Значение у ПолеВвода<Число> – число. Член обобщённого типа берёт аргументы получателя: СобытиеПриИзменении<Строка>.НовоеЗначение – строка. Тип структуры и метода даёт модуль, который их объявляет. Строка Запрос{...} типизируется по списку выборки и yaml прочитанных таблиц; у поля через ссылку и у присоединённой стороны левого соединения есть Null, и ЗаменитьNull его снимает. Сравнение – как у компилятора, и условие, проверенное раньше, тип не сужает. Все четыре читают один вывод типов: по всему проекту – правила приведений и проверка типа, по модулю и разметке его компонента – защита от Неопределено, которая работает на каждое нажатие. Чего вывод не называет, то не судится: параметр лямбды без типа, объединение с неизвестной частью, метод, перегрузки которого для данных аргументов расходятся, колонка запроса, который компилятор отвергнет. Поэтому часть предупреждений IDE правила пропускают, а своих не добавляют.

code/row-field-null читает тот же вывод типов для строки запроса. Колонка, которая читает поле через ссылку или из присоединённой таблицы внешнего соединения, может быть Null. Правило находит, где такая колонка уходит в поле структуры, параметр метода проекта, переменную или результат метода с объявленным типом без Null; тип с ? тоже не принимает Null. Параметр лямбды Преобразовать, Фильтровать или ДляКаждого получает тип по сигнатуре метода платформы, так пока читает только это правило. Вычисляемую колонку правило не судит: ВЫБОР КОГДА Х ЕСТЬ NULL ТОГДА ... ИНАЧЕ Х КОНЕЦ компилятор сужает сам.

code/deprecated-api проверяет вызовы устаревших методов платформы. Оно выбирает перегрузки по режиму совместимости проекта, числу, именам и выведенным типам аргументов и сообщает о вызове, только если все подходящие формы устарели. Неизвестные типы оставляют неоднозначный вызов без находки. Нужен каталог с извлечёнными отметками устаревания; собственные устаревшие объявления проекта не проверяются.

Подробные описания групп собраны в RULES.ru.md: query/ (составной тип в В с подзапросом), project/ (свойства проекта), naming/ (стандарт имён, набор [morph]) и style/ (соглашения по написанию кода и договорённость о том, что включено, а что нет).

Серверные вызовы из вычисляемых свойств

Включите code/computed-property-server-call, чтобы проверить свойства, выражения которых обращаются к серверному методу. Правило группирует находки по форме и методу, перечисляет строки свойств и полные цепочки вызовов. Это информационная проверка, выключенная по умолчанию: такое обращение может быть осознанным решением.

Правило называет только объявленные методы, доступные с клиента. Клиентский вариант имеет приоритет над серверным. Обработчики событий, отложенные лямбды, неоднозначные цели и методы с включённым или неизвестным КешироватьРезультат пропускаются. Правило не предсказывает частоту пересчёта свойства. Рассмотрите предварительную загрузку нужных данных; автоматически кешировать произвольные изменяемые данные нельзя.

Существующее code/image-binding-server-call продолжает проверять свойство Изображение компонентов платформы. Эти места исключены из общего правила. Собственное свойство компонента проекта с таким именем проверяется общим правилом. Для совместимости проверка изображений также принимает описание серверного модуля без исходника; общая проверка требует разрешить объявление метода.

Форма наследует свойства базового типа, например команду ЗаписатьИЗакрыть. Обе проверки читают ЗаписатьИЗакрыть.Выполнить() как вызов этого свойства, даже если в проекте есть общий модуль с таким же именем. Если базовый тип неизвестен, проверки пропускают в этой форме все вызовы вида Имя.Метод(), а вызовы собственных методов формы разбирают как обычно. Обе проверки также пропускают элемент, у которого в метаданных поле неверного типа, например дата в Имя.

Последовательные серверные вызовы

Включите code/sequential-server-calls, чтобы найти клиентский метод, который обращается к серверу несколько раз подряд на одном пути исполнения. Каждое обращение – отдельный круг до сервера, а один серверный метод с типизированным результатом обошелся бы одним. По умолчанию правило проверяет методы, которые может выполнить обработчик открытия: ПослеСоздания, ПослеЧтения и ПриОткрытииПоСсылке, через методы своего модуля, клиентские модули, Компоненты.X.M(...) и лямбды таймеров. Параметр scope со значением all (XBSL_CODE_SEQUENTIAL_SERVER_CALLS_SCOPE=all) проверяет все клиентские методы, а min-calls задает самую короткую серию. Проверка информационная и выключена по умолчанию: на зрелом проекте находок десятки, а исправление – новый составной серверный метод.

Вызов засчитывается, если доходит до доступного с клиента серверного метода без КешироватьРезультат = Истина: голый вызов метода своего модуля, Модуль.Метод(...) или клиентский метод другого модуля либо дочернего компонента, главный путь которого обращается к серверу до любого раннего выхода. Клиентские методы своего модуля разбираются на месте вызова. Ветви если/выбор, правый операнд и/или/?:/?? и цикл разрывают серию, а тело цикла не проверяется вовсе; лямбда и ссылка на метод исполняются в другом такте. Серия в блоке поймать и серия, вызовы которой стоят в разных попытка, пропускаются: такое разделение бывает намеренной обработкой ошибок.

Одна находка на серию стоит в строке первого вызова и перечисляет вызовы с их строками. Если аргументы вызова вычисляются из результата предыдущего, сообщение говорит об этом: это вычисление тоже переезжает на сервер. Объединение меняет границы транзакций и обработку ошибок, поэтому правило советует один серверный вызов, а не параллельное выполнение.

Чтение ресурса без кеша результата

code/resource-read-without-cache ищет доступный с клиента метод, тело которого состоит из одного возврат цепочки ПакетРесурсов.Текущий().Получить(...).ОткрытьПотокЧтения().ПрочитатьКакСтроку(). Такой метод исполняется на сервере, и без кеша каждый вызов с клиента обращается к серверу. Текст ресурса меняется только с новой сборкой. Правило предлагает включить КешироватьРезультат = Истина в аннотации @ДоступноСКлиента или передать текст клиенту через элемент ПараметрыРаботыКлиента. Что подойдёт, зависит от того, как долго должен жить результат, поэтому автоисправления нет.

Правило сообщает только об этой форме. Аргументы Получить и ПрочитатьКакСтроку – строковые литералы без интерполяции или параметры метода. Обращение к члену, вызов и вычисляемое значение по умолчанию могут зависеть от пользователя, настроек или других данных – кеш сохранил бы ответ, верный лишь однажды, – и такой метод правило пропускает. Операция и интерполяция пропускаются по другой причине. "стили/" + ИмяФайла и "стили/%ИмяФайла" выглядят так, будто читают только аргументы, и для двух строк это правда, но значение другого типа присоединяется к строке через ВСтроку(), а форма интерполяции с $ – через Представление(), которому платформа разрешает зависеть от локали. Оба метода объявлены у типа Объект, и тип проекта волен определить их по-своему, так что доказать путь значило бы сначала доказать, что операнд – строка платформы. Сужение сделано намеренно. Корень цепочки должен обозначать тип платформы: если так называется параметр, объявление или импорт модуля, метод пропускается, и так же действует Имя в парном описании. Элемент проекта с таким именем выключает проверку во всём проекте, и то же делает глобальный элемент подключённой библиотеки – проект видит его по короткому имени. Окружение, доступность и кеш правило берёт из тех же фактов, что и code/computed-property-server-call. Пропускаются также клиентские варианты, обработчики, включённый или неизвестный КешироватьРезультат, повторяющиеся имена методов и модули без корректного описания.

Список принятых замечаний: старый долг и исключения с причиной

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

xbsl acme/app --enable style --write-baseline baseline.json   # заморозить долг один раз
xbsl acme/app --enable style --baseline baseline.json         # всплывает только НОВОЕ

Находку опознают по тройке (файл, правило, сообщение) с допустимым количеством. Перенос строки замороженную находку не будит, а действительно новое нарушение всплывает. Итог проверки показывает, сколько находок погашено и сколько записей устарело, то есть долг выплачен. Это сигнал перезаписать файл. Пути хранятся относительно самого файла со списком: коммитьте его в корне репозитория и запускайте линтер откуда угодно.

Тот же файл хранит и точечные исключения с причиной. Значение записи – либо голое количество, либо {"count": N, "reason": "..."}, где причина объясняет, почему код правильный намеренно. Причины записывает действие “Исключить проверку” из лампочки расширения VS Code, либо вы правите файл руками. При перезаписи --write-baseline сохраняет причины выживших записей. LSP-сервер принимает тот же ключ --baseline ФАЙЛ, поэтому исключённое гаснет и в редакторе. В опознание находки входит текст сообщения, поэтому записывайте и проверяйте файл под одним языком вывода. Ссылки на номера строк исходника в известных шаблонах сообщений не участвуют в опознании: сдвиг принятой находки не делает её новой. Имена и смысловые числа по-прежнему различают находки, а в выводе остаётся актуальный номер строки. Старые файлы списка сопоставляются так же, без перезаписи.

Новая версия может изменить текст сообщения правила. Запись, сохранённая с прежней формулировкой, всё равно удерживает свою находку, если её текст не подходит ни под одну нынешнюю формулировку правила, а значения в нём те же, в тех же кавычках и в том же порядке, и файл тот же. Сообщение без таких значений сопоставляется только по тексту. Такие записи проверка называет: строкой в текстовом отчёте, ключами baseline_reworded и summary.baseline_reworded_entries в json и в ответе MCP lint_paths. Перезапись --write-baseline обновляет их текст и сохраняет причины.

Устаревшими считаются только записи тех правил, которые в проверке участвовали. Правило, оставленное за набором – сужением --select, выключенное по умолчанию или неизвестное установленной надстройке, – находок не даёт по построению. Назвать его записи устаревшими значило бы объявить долг выплаченным, не посмотрев. Такие записи считаются отдельно: строка “Записей списка принятых не проверено” и ключ baseline_not_checked в json. --prune-baseline их не трогает.

Устаревшие записи называются поимённо, а не только считаются. --stale-baseline печатает путь, правило, количество, сообщение и отдельной строкой причину записи. --format json несёт те же записи в summary.baseline_stale_entries, и то же самое отдаёт ответ MCP lint_paths. --prune-baseline называет их и снимает из файла, сохраняя его порядок и формат. Файл лежит в git, а перезапись с пересортировкой даёт нечитаемое сравнение. Ещё команда говорит, сколько снятых записей несли причину: после коммита их текст остаётся только в истории. Со стороны MCP то же действие делает инструмент baseline_prune, а ключ dry_run позволяет сначала посмотреть. Побочным эффектом обычной проверки снятие не бывает никогда. Обычная проверка, насчитав устаревшие записи, называет оба ключа под итоговой строкой: раньше счёт без подсказки отправлял искать их вручную, переписывая файл и сравнивая версии.

xbsl baseline add <пути> --rule <правило> [--reason ...] замораживает находки по одной. Он запускает названное правило по указанным путям и дописывает только то, чего список ещё не покрывает. Новый файл встаёт на своё место в порядке, остальное не двигается, записанные причины остаются, а повторный вызов ничего не меняет. --format json отвечает {baseline, added, findings, written}. Список судится только в пределах запрошенных путей: записи чужих файлов считаются непроверенными, а не устаревшими, и baseline_not_checked разбит на _rules и _paths. Сводка называет, чем судили проверку: engine, plugins с версиями и rules {active, total, plugin} в json, строка “Набор проверки” в тексте. Так расхождение двух сред об одном дереве видно сразу.

Использование в CI

xbsl возвращает ненулевой код только при находке уровня error, поэтому командой можно останавливать конвейер как есть: предупреждения и info сборку не валят. Единственное условие – данные о языке (см. Данные о языке). Сгенерируйте их прямо в задаче конвейера: экстракторы лежат в репозитории, значит, его надо выгрузить в рабочую копию. Либо поставьте пакет, который несёт данные через точку расширения xbsl.data (см. Расширение) – тогда достаточно pip install.

Локально – тем же набором, что и задача конвейера

Набор правил в задаче конвейера почти никогда не равен набору по умолчанию: проект включает свои правила ключами --enable прямо в файле конвейера. Локальный запуск о них не знает, и разницу узнаёшь из красной задачи, а это круг длиной в пуш.

--as-ci снимает вопрос. Ключи --select/--ignore/--enable и список принятых замечаний берутся из команды xbsl в файле конвейера рядом с проектом: это .gitlab-ci.yml или рабочий процесс GitHub. Файл ищется вверх от проверяемых путей, а можно назвать его явно – --as-ci путь/к/файлу. Совпадение получается по построению: список правил один и тот же, и второго списка, который надо держать в согласии, никто не заводит.

xbsl e1c --as-ci            # набор правил как в задаче конвейера

Первой строкой проверка печатает, что именно взяла:

Набор правил как в CI: /repo/.gitlab-ci.yml, задача xbsl-lint – --enable code/unused-method, ... --baseline .xbsllint-baseline

Ключи складываются: --as-ci --enable style/line-length – это набор задачи конвейера плюс правило, которое пробуют перед тем, как внести в конвейер. Путь к списку принятых разрешается относительно корня рабочей копии, поэтому запуск из подкаталога находит тот же файл, что и задача. Про то, как считать и что печатать, – --jobs, --format, пути – решает сам запуск: чаще всего проверяют одну папку, а не всё дерево.

Если файла конвейера нет или команды xbsl в нём нет, запуск отказывается с сообщением. Молча проверять более узким набором он не станет: именно такая тихая разница и стоила красной задачи.

После ключа называют файл конвейера, и необязательное значение тут – ловушка, о которой стоит знать. В xbsl --as-ci e1c ключ съедает дерево, которое собирались проверить, и линтер уходит проверять текущий каталог. Каталог, названный после ключа, отвергается с подсказкой рабочей формы – xbsl e1c --as-ci, ключ после путей. Отказом файловой системы “это каталог” дело не ограничивается.

Конвейер редко состоит из одного файла. Ключ GitLab include: приносит задачи со стороны, и у проекта на общем шаблоне задача линта лежит ровно там. Раньше линтер читал только корневой файл и отвечал “команды xbsl нет” про конвейер, который её запускает. Теперь читаются локальные файлы репозитория: include: ci/lint.yml, include: {local: /ci/lint.yml}, списки того и другого и шаблоны путей, которые разворачивает сам GitLab (ci/*.yml). Пути включений отсчитываются от корня рабочей копии – так их отсчитывает GitLab, и вложенное включение ведёт себя так же. Корень – это ближайший каталог над файлом конвейера, в котором лежит .git. Поэтому конвейер, лежащий в ci/sub/lint.yml, находит include: /ci/base.yml наверху репозитория. Рабочее дерево git worktree – такая же рабочая копия: git worktree add пишет .git файлом, и вид записи не проверяется. Если .git над файлом нет – например, конвейер скопировали в отдельную папку, – за корень идёт эта папка. Задача, объявленная и в корневом файле, и во включении, берётся из корневого: приоритет тот же, что у самого конвейера. В строке о взятом наборе называется файл, в котором команда на самом деле стоит:

Набор правил как в CI: /repo/.gitlab-ci.yml (включение /repo/ci/lint.yml), задача xbsl-lint – --enable code/unused-method

Всё, что лежит вне рабочей копии, линтер не загружает: удалённый адрес, шаблон GitLab, файл чужого проекта, компонент. Для этого нужна сеть, а чаще и токен. Линтер, который втихую качает адрес из конфигурационного файла, – это неожиданность, а не удобство. Такие включения он просто называет, и локальное включение, которого в рабочей копии нет, называется так же. Рядом с невидимой задачей печатается причина, а отказ выше несёт тот же перечень:

Включения не читались (нужна сеть или чужой репозиторий): template: Jobs/SAST.gitlab-ci.yml

Какую задачу брать, когда конвейер запускает линтер дважды. Проект, который собирает второе дерево, проверяет его второй задачей: исходники одной, результат translate другой. Наборы у них разные – у переведённого дерева нет своего списка принятых и выключено правило. Без имени берётся первая команда, и линтер вслух говорит, что выбор был:

Линтер в этом файле гоняет ещё: English to S3 – выбрать: --as-ci-job <имя>

--as-ci-job берёт названную задачу и сам включает --as-ci, отдельно писать его не нужно. Имя можно назвать частью, пока подходит одна задача: имя с пробелами утомительно закавычивать:

xbsl build/en --as-ci-job english      # набор задачи "English to S3"

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

В машинном отчёте. С --format json тот же ответ лежит в summary.as_ci: взят ли набор задачи (adopted), какая это задача и из какого файла (job, file, а при включении ещё source), корень рабочей копии, сам набор данными (select, ignore, enable, baseline, no_baseline) и строкой, которую печатает терминал (flags), невзятые задачи (jobs) и непрочитанные включения. Отказ по-прежнему возвращает код 2, а причина лежит там же, под тем же ключом. Замечаний в таком ответе нет совсем: запуск не состоялся, а пустой список находок читался бы как чистое дерево. Инструмент MCP lint_paths принимает as_ci и as_ci_job и отвечает той же записью. Редактор спрашивает её запросом xbsl/ciStatus.

И в редакторе. Строка состояния говорит, каким набором судит панель. Пока действует набор задачи, там стоит CI: <задача>. Если набор просили, а взять его не вышло, там стоит предупреждение. Сервер не отказывается работать из-за отсутствующего файла конвейера – это стоило бы всей сессии редактирования, – он продолжает судить набором настроек. Раньше единственным следом этого была одна строка в канале вывода. Отвечает на вопрос сам сервер, запросом xbsl/ciStatus, а не настройки: настройки говорят, что просили, и только сервер знает, что из этого вышло. По нажатию открывается файл конвейера, в котором стоит задача.

xbsl.linter.asCi, а для выбора задачи xbsl.linter.asCiJob, заставляет панель “Проблемы” судить тем же набором. Расширение передаёт ключ, а сервер LSP читает тот же файл конвейера. Набор правил в настройки не копируется: копия и есть тот второй список, ради отсутствия которого всё это затевалось. Собственные xbsl.rules редактора остаются поверх набора задачи, так что пробуемое правило не теряется. Одно отличие от терминала: если файла конвейера нет, сервер не отказывается. Отказ стоил бы терминалу одного запуска, а редактору всей сессии, поэтому сервер пишет причину в канал вывода XBSL и оставляет набор настроек.

GitHub Actions

lint:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-python@v5
      with: { python-version: "3.12" }
    - run: pip install xbsl
    # генерация данных из вашего дистрибутива 1С:Элемент (или установите пакет с данными):
    - run: xbsl extract --dist "$ELEMENT_DIST"
    - run: xbsl acme/          # задача падает при любой находке уровня error

GitLab CI (виджет Code Quality)

--format codeclimate пишет отчёт Code Climate, который GitLab показывает прямо в merge request. Запускайте его из корня репозитория и сохраняйте вывод как отчёт codequality. Код возврата при находках уровня error остаётся ненулевым, поэтому artifacts.when: always сохраняет отчёт даже тогда, когда задача останавливает конвейер. Если нужен только виджет, добавьте || true:

lint:
  script:
    - pip install xbsl
    - xbsl --format codeclimate acme/ > gl-code-quality-report.json
  artifacts:
    when: always
    reports:
      codequality: gl-code-quality-report.json

Установленные сигнатуры и типы коллекций

code/call-arity и code/call-arity-cross связывают именованные аргументы с известной сигнатурой, в том числе с обязательными параметрами после необязательных. Перегрузки, порожденные методы менеджера и неустановленные приёмники не угадываются. code/collection-field-needs-req учитывает и необобщенные платформенные типы без значения по умолчанию, скалярные значения и локальные объявления типов. Простые имена проектных типов из других файлов это правило не разрешает.

code/redundant-skip-undefined предупреждает, когда вывод типов в пределах файла доказывает отсутствие Неопределено в типе элемента коллекции. Исправление для перебираемой коллекции вызывает ВМассив(), сохраняя создание массива. Вызов на последовательности автоматически не переписывается.

Неполный разбор и пропущенные ошибки сборки

Проверки инициализаторов и повторных объявлений/ветвей сохраняют находки в здоровых методах, если другой метод файла не разобрался. Поврежденный метод пропускается целиком; структура с поврежденным заголовком тоже пропускается. Фильтрация не меняет сохраненное дерево разбора и смещения исходного текста, используемые автоисправлениями.

code/missing-return требует сигнатур проекта и значений перечислений, поэтому работает при проверке проекта. code/captured-local-write и code/unused-return-value используют факты файла. Последнему нужна таблица checked_return_methods в извлеченном каталоге стандартной библиотеки. Старые каталоги поддерживаются, но это правило с ними молчит. Чтобы включить проверку, заново извлеките стандартную библиотеку из соответствующего дистрибутива платформы.

code/ambiguous-type и yaml/ambiguous-type проверяют явно записанные типы по модели размещения проекта. Правила не угадывают типы в произвольных строках и именах значений и не объединяют объявления из разных корней проектов.

Последнее обновление 7 октября 2026 г.

Эта страница была полезной?