👋Это демо-отчёт на синтетическом проекте — чтобы показать, что вы получите на выходе. Свой код грузить не нужно. Хотите проверить свой?
🔎1 POU на графических языках (CFC 1) — построчные проверки к ним НЕ применялись; имена покрыты частично (вхождения в блоках/выражениях). Это ограничение инструмента, а не признак чистоты этих POU.
0
Оценка качества: 0/100 Сохраните отчёт в профиль — соберётся подтверждённый трек качества. Код не сохраняется, только агрегат.
У каждого правила есть кнопка шум? — нажмите её, если это правило даёт нерелевантные срабатывания на вашем коде. Сигнал помогает нам подстроить проверки: понизить важность, сузить условие или отключить лишнее. Ваш код при этом не отправляется.
Критично 7 шт.
CFC: деление на литеральный ноль1 · cfc-div-zero
В графике (CFC/FBD) делитель блока DIV/MOD подключён к литеральной константе 0 — рантайм-исключение при исполнении блока. Проверяется по графу связей.
Варианты решения
Подайте на вход делителя переменную/уставку вместо константы 0 — обычно это забытая заглушка после отладки.
Если делитель может обнуляться в рантайме — поставьте перед DIV/MOD выбор безопасного значения (SEL по сравнению с 0).
CfcDiv
— делитель блока DIV подключён к литеральному 0 → гарантированное деление на ноль
Функция неявного контроля не защищает1 · broken-implicit-check
Функция неявного контроля (Check…) есть, но её защитная логика выхолощена: пустое тело или убрана проверка (делителя на 0 / границ). Рантайм вызывает её, но она ничего не ловит — ложное чувство безопасности, опаснее её отсутствия.
Варианты решения
Верните защитную логику: CheckDivReal должен проверять IF divisor = 0 THEN … := 1;
CheckBounds/CheckRange… должны зажимать значение в lower..upper. Сверьтесь с дефолтной реализацией CODESYS.
CheckBounds
— нет сравнения с границами lower/upper — значение не ограничивается — защита есть, но не работает (ложное чувство безопасности)
Деление на литеральный ноль2 · div-by-zero
Деление или MOD на литеральный ноль — гарантированное исключение исполнения (division by zero) при заходе в эту строку.
Варианты решения
Уберите деление на 0 — вероятно, опечатка в делителе.
Если делитель бывает нулём динамически — защитите: IF d <> 0 THEN r := x / d; END_IF
DivZeroVar:1
— деление на znever — переменная = 0 (инициализатор 0/пустой) и нигде не присваивается → гарантированное деление на ноль
qa := 5 / znever;
Main:2
— деление/MOD на литеральный ноль
d := d / 0;
Опечатка в имени глобала (gvl.*)1 · undeclared-global
Ссылка gvl.<имя> не находит объявления в GVL — почти всегда опечатка; переменная молча создаётся или код не компилируется.
Варианты решения
Сверьте написание с объявлением в GVL (регистр, подчёркивания, цифры).
Если переменная действительно нужна — добавьте её в GVL с типом и комментарием.
Main
— gvl.missing — нет в GVL (вероятная опечатка)
Индекс за границами массива1 · array-bounds
Константный индекс выходит за объявленные границы ARRAY[lo..hi] — заход в эту строку гарантированно читает/пишет чужую память. Без активных неявных проверок рантайм это не перехватит. Проверяются одномерные массивы с числовыми границами и литеральным индексом.
Варианты решения
Приведите индекс в диапазон объявления или расширьте границы массива.
Перед доступом проверяйте: IF idx >= LO AND idx <= HI THEN arr[idx] … END_IF
Main:1
— arr[5] вне границ [0..3]
arr[5] := 1;
Бесконечный цикл без выхода1 · infinite-loop
WHILE TRUE / REPEAT … UNTIL FALSE без EXIT и RETURN внутри — скан никогда не завершится: задача блокируется и срабатывает сторожевой таймер (а если он выключен — встаёт контроллер). В ПЛК длинную обработку разносят по сканам, а не крутят в цикле.
Варианты решения
Добавьте условие выхода и EXIT, либо ограничьте счётчиком итераций.
Тяжёлую обработку разбивайте по сканам — бесконечный цикл роняет watchdog задачи.
Main:11
— WHILE TRUE без EXIT/RETURN
Внимание 9 шт.
Деление на литеральный ноль1 · div-by-zero
Деление или MOD на литеральный ноль — гарантированное исключение исполнения (division by zero) при заходе в эту строку.
Варианты решения
Уберите деление на 0 — вероятно, опечатка в делителе.
Если делитель бывает нулём динамически — защитите: IF d <> 0 THEN r := x / d; END_IF
DivZeroVar:3
— деление на zcond — переменной присваивается 0 на одной из веток и нет защиты zcond <> 0 → возможное деление на ноль
qb := 5 / zcond;
Присваивание переменной самой себе1 · self-assignment
Строка вида `x := x;` — присваивание переменной самой себе: не делает ничего. Обычно опечатка в правой части (имелась в виду другая переменная) или остаток от удалённой логики.
Варианты решения
Вероятно, справа должна быть другая переменная (опечатка) — проверьте намерение.
Если строка лишняя — удалите её.
Main:6
— присваивание самой себе — нет эффекта
x := x;
Пустая ветвь IF/цикла1 · empty-branch
Ветвь IF/ELSIF/ELSE или тело цикла пусты (`THEN END_IF`, `DO END_FOR`) — логику либо не дописали, либо вырезали при правке и забыли убрать конструкцию.
Варианты решения
Заполните ветвь логикой или удалите пустой IF/цикл.
Если пусто намеренно — оставьте комментарий-пояснение.
Main:7
— пустой IF … THEN … END_IF
CASE без ветви ELSE (нет default)1 · case-without-else
CASE без ветви ELSE: если селектор примет значение, которого нет среди меток, не выполнится ничего — молчаливый пропуск (частый баг стейт-машин).
Варианты решения
Добавьте ветвь ELSE с обработкой по умолчанию (сигнал/лог/безопасное состояние).
Если все значения селектора точно покрыты — оставьте ELSE с комментарием-пояснением.
Main:8
— CASE без ELSE — неучтённые значения селектора молча игнорируются
Проверка присутствует, но не активна3 · implicit-check-disabled
Функция контроля есть в проекте, но не активна: либо не зарегистрирована (нет метки CheckFunction — рантайм её не вызывает), либо для целой категории (границы/деление/диапазон/указатели) нет ни одной активной проверки.
Варианты решения
Перегенерируйте POUs for Implicit Checks штатным диалогом CODESYS — так проверка получит регистрацию CheckFunction и будет вызвана рантаймом.
Добавьте недостающую категорию (границы/деление/диапазон/указатель) через Add Object → POUs for Implicit Checks.
проект
— рантайм не проверяет «деление на ноль» — нет активной функции контроля этой категории
проект
— рантайм не проверяет «выход за диапазон» — нет активной функции контроля этой категории
проект
— рантайм не проверяет «разыменование указателя» — нет активной функции контроля этой категории
Мелочи 7 шт.
Мёртвый код — POU нигде не вызывается3 · dead-pou
POU нигде не вызывается и не используется как тип — мёртвый код либо забытая правка имени (сверьте с ТЗ, прежде чем удалять).
Варианты решения
Не нужен — удалите POU.
Забытая версия — сверьте имя вызова с ТЗ, возможно опечатка в имени.
CfcDiv
— CfcDiv — не вызывается и не используется как тип
DivZeroVar
— DivZeroVar — не вызывается и не используется как тип
Main
— Main — не вызывается и не используется как тип
Криптовое имя переменной4 · naming
Односимвольные имена (кроме счётчиков i/j/k/n) нечитаемы при сопровождении. I/O-теги и пины FB не трогаем.
Варианты решения
Дайте осмысленное имя по назначению (напр. iRetryCount вместо n).
CheckBounds
— x — односимвольное имя, дайте осмысленное
Main
— d — односимвольное имя, дайте осмысленное
Main
— x — односимвольное имя, дайте осмысленное
Main
— c — односимвольное имя, дайте осмысленное
Чего линтер НЕ видит (нужен глаз и сверка с ТЗ): полярность сигналов, перепутанный гистерезис (< set+gist / > set−gist), copy-paste чужого контура. Отчёт — повод перепроверить, а не приговор.
Обработка идёт в памяти — загруженный код не сохраняется. Инструмент советует; правки по коду принимает инженер (см. что линтер не видит).
· Обратная связь
· GitHub