Типы сообщений ошибок и предупреждений Kotlin
Предупреждения и ошибки, которые генерирует компилятор Kotlin
часто нужно замаскировать, либо для того, чтобы они не мозолили глаза, либо просто потому, что логика программы нуждается именно в таком коде, который приводит к сообщению об ошибке или предупреждению.
Маскировка сообщений компилятора как в Java
так и Kotlin
происходит одинаково:
@Suppress("MESSAGE")
где "MESSAGE"
— это тип сообщения.
Проблема в том, что узнать каким-то простым способом какой тип сообщения соответствует конкретному тексту часто оказывается невозможно. Подсказки Lint
работают почему-то сильно не всегда, автодополнения нет, а разработчиками Kotlin
эта информация почему-то нигде не опубликована.
Для облегчения поиска нужных типов сообщений я свел их вместе с текстом в одну таблицу. В случае возникновения необходимости замаскировать какое-то сообщение его можно легко найти в этой таблице и узнать какой тип нужно указать для ее подавления.
Пример таблицы
Тип | Сообщение |
---|---|
Data class inheritance from other classes is forbidden | DATA_CLASS_CANNOT_HAVE_CLASS_SUPERTYPES |
Data class must have at least one primary constructor parameter | DATA_CLASS_WITHOUT_PARAMETERS |
В один поста на хабре таблицу сообщений засунуть невозможно, а размазывать ее по нескольким сообщением не имеет смысла т.к. ею будет неудобно пользоваться, поэтому я выложил ее на GitHub.
> Ссылка на полный вариант таблицы
Возможно, кому-то эта информация пригодится.
Комментарии (35)
fogone
16.02.2017 23:04+1> Предупреждения и ошибки, которые генерирует компилятор Kotlin часто нужно замаскировать
если вы действительно делаете это часто, то явно делаете что-то не то. Хотя сама по себе таблица вещь очень полезная, спасибо!JouriM
16.02.2017 23:35Все зависит от того чем заниматься.
Если писать приложение, то воевать с ошибками приходится чрезвычайно редко.
Если же писать либу, то часто.
У меня безусловный победитель по количеству использований — это UNCHECKED_CAST, 90% применений которого приходится на бездарные шаблоны (женерики), которые неспособны приводить типы.
Но бывают и эксклюзивы, типа:
class PLog(private val allowlog : Boolean = true) { ... @Suppress("NON_PUBLIC_CALL_FROM_PUBLIC_INLINE") inline fun log( cb:()->String ) = if (allowlog) note(indent.indent() + cb() + "\n") ...
Открывать поле я не хочу, а избавиться от ошибки синтаксическими способами я не смог.
fogone
17.02.2017 09:00да, это правда — unchecked cast это основной suppress и в моем коде, а вот NON_PUBLIC_CALL_FROM_PUBLIC_INLINE я использовать постеснялся, честно говоря, не уверен, что это хорошая идея. Может быть кто-то знает, почему компилятор это запрещает?
JouriM
17.02.2017 09:21+1Т.к. функция inline, то все ее тело подставляется в место использования и как бы получается что приватные поля и методы "используются" за пределами класса.
Я голосую за обыкновенный баг.Morj
17.02.2017 10:59+1Так они и на самом деле используются, просто для них вгенерятся синтетический геттер с названием
access$getAllowlog$p
. Если не предупредить об этом разработчика, то у приватного поля появится геттер (в виде статического метода), доступный из Java.JouriM
19.02.2017 20:01У приватных пропертей нет гетеров
Да и не в этом суть
Это публичная функция, что и как она использует внутри своей реализации никого за ее пределами волновать не должно и от того что она inline ничего не меняетсяMorj
20.02.2017 09:23У приватных полей нет геттеров, но появляются статические методы для их чтения, если вы делаете публичную инлайн-функцию с их использованием. От того, что функция инлайн меняется то, что напрямую через взятие поля это поле приватное поле не прочитать (без setAccessible), поэтому тело этой функции будет содержать вызов специального метода на стороне "вызова". Посмотрите сами в bytecode viewer.
JouriM
20.02.2017 18:31Да, извиняюсь, не сообразил с доступом.
Тем не менее (и даже более), выдавать сообщения основанные на внутренних собенностях реализации, о которых никто знать не должен и которые никак не влияют на выполнение программы, на мой взгляд, совершенно бессмысленно.
abreslav
20.02.2017 11:36Для этих целей в 1.1 появилась аннотация @PublishedApi, применимая к internal-декларациям
JouriM
20.02.2017 19:19-1- Мне совершенно не нужно, чтобы это поле было доступно конечному юзеру.
class A { @PublishedApi internal val allow = false inline fun F(cb : () -> String) { if (allow) println(cb()) } } fun main(a : Array<String>) { val a = A() println( a.allow ) //Мне НЕ надо чтобы был доступ }
- Если бы я захотел сделать поле доступным, то я бы это сделал просто указав public, зачем вообще эта аннотация нужна?
Можете привести пример ее осмысленного использования?
abreslav
21.02.2017 09:37+1Слово internal означает "доступно внутри модуля". Ваш main в том же модуле.
Мы не разрешаем ссылки и inline функций на приватные декларации, потому что это нарушает главный принцип работы с ними: изменения приватных деклараций не должно ломать клиентский бинарный код. Там выше подробно объяснили, почему код сломается, если разрешить ссылку на private.
Мы разрешаем ссылки на internal-декларации со специальной аннотацией, чтобы была возможность что-то не показывать клиентам из другого модуля. Лучше сделать не можем, к сожалению.
Sirikid
16.02.2017 23:26
Это таблица всех существующих сообщений.
Почему есть много разных
UNUSED_blah
, но нету вариантаUNUSED
?JouriM
16.02.2017 23:40Вообще вопроса не понял.
А насчет создания списка для использования автодополнения (для чего ты, наверное, и сделал класс) я думал.
С этого начал, но оказалось, что пользоваться этим ничуть не удобнее чем отдельной таблицей т.к. искать сообщение все равно можно только по тексту.
Да и как-то жаба душит тащить кучу абсолютно ненужного кода в либу. По привычке.Sirikid
17.02.2017 00:14Вообще вопроса не понял.
Есть ещё (как минимум) один вариант такого сообщения, просто
UNUSED
— он работает и с функциями, и с классами, и с параметрами, в общем со всеми неиспользованными сущностями.
Во первых автодополнение, а во вторых когда вручную пишешь все эти строки очень легко опечататся и никто ничего не скажет. Сам исправлял одну такую опечатку аж в аналогичном классе для джавовского
@SuppressWarnings
. В идеале этот параметр аннотации должен быть перечислением, но в Java разрешено использовать собственные предупреждения (компиляторам и даже средам) и видимо поэтому этот параметр строка.JouriM
17.02.2017 00:54У меня в статье выдержка только сообщений котлина, взятая из его исходников, так что ничего универсального в ней и быть не может.
Иметь авто-дополнение для известных типов, конечно, хорошо, но только ради этого тащить кучу кода в любой проект, по моему, неадекватно и для эксклюзивов это никак не поможет.
Типовой кейс: вылезает ошибка, которую надо исключить, копирую уникальную часть текста (с этим самые большие сложности т.к. в IntelliJ окно с сообщениями об ошибках — это боль), ищу его в таблице, копирую тип и втыкаю в исходник.
В кнопках, помимо войны с текстом сообщения в IntelliJ, это все очень быстро: Alt-Tab, F4, F7, Ctrl-V, Enter пара шевелений стрелками, Ctrl-C и уже Alt-Tab обратно с типом.
abreslav
17.02.2017 10:52Хочу заметить, что это все — внутренние особенности компилятора, и мы не гарантируем, что они будут поддерживаться в будущем :)
Sirikid
17.02.2017 11:03+1А почему вы не делаете такие штуки перечислениями? (Неужели слишком много вариантов для перечисления?)
yole
17.02.2017 11:57Это не enum, потому что существуют диагностики, специфичные для конкретных бэкэндов, и если бы мы собрали их все в один enum, это создало бы очень неприятный coupling.
yole
17.02.2017 11:53+1Причина, почему мы не публикуем эту информацию — во-первых, то, что сказал Андрей (диагностики регулярно меняются, разделяются и объединяются), а во-вторых, не предполагается, что кто-то будет писать аннотации @Suppress руками, а не через квикфикс. Если вы знаете конкретные случаи, в которых квикфикс на сообщении от компилятора недоступен или не работает — пожалуйста, сообщите нам, и мы это исправим.
Morj
17.02.2017 13:23А если я в vim пишу на Котлине?
HaruAtari
17.02.2017 13:29-3В таком случае вы сам себе злой Буратино.
zagayevskiy
17.02.2017 14:02+1И почему же? Язык, на котором можно писать только в одной IDE? Только не разводите холивар на тему "vim — не IDE", речь не об этом.
yole
17.02.2017 14:48-1У нас есть официальные плагины для трёх IDE — IntelliJ IDEA, Eclipse и NetBeans. Если кто-то хочет программировать на Kotlin в другой среде — это их выбор, но мы не делаем ничего, чтобы как-то специально облегчить их жизнь.
zagayevskiy
17.02.2017 15:05А если хочется узнать, что означает конкретный Suppress, и это непонятно из названия?
yole
17.02.2017 15:11Сейчас нормального способа для этого нет (хотя я не помню, чтобы кто-то когда-то этого просил). В будущем мы планируем сделать для каждого диагностического сообщения страничку с документацией, в которой написано, в каких случаях оно возникает и как его правильно избегать, и тогда можно будет сделать навигацию с @Suppress на соответствующую страничку.
zagayevskiy
17.02.2017 15:12Спасибо. Я спрашиваю из праздного интереса, пока Котлином не пользуюсь. Ключевое слово — пока :)
JouriM
19.02.2017 20:10+1То что все меняется — это понятно и никто (надеюсь) не рассчитывает что этот список сообщений будет вечен
А вто то что ни нигде не опубликованы — это плохо т.к. писать программу надо здесь и сейчас, на этой версии языка, а не на некоей идеальной, которая когда-то появится.
Я это не к тому, что надо все бросать и делать немедленно этот список
В языке пока есть много принципиальных проблем, которые меня лично волнуют значительно больше чем список сообщений, типа практически виснущего процесса компиляции на некоторых инлайновых функций или рразмножение одинаковых классов при использовнии::StatFunction
.
Для меня лично отсутствие списка ошибок особой проблемы не составляет т.к. "на скорость не влияет"abreslav
20.02.2017 11:42Пожалуйста, записывайте волнующие вас проблемы (особенно "практически виснущий" компилятор) в наш баг-трекер: https://youtrack.jetbrains.com/issues/KT
JouriM
20.02.2017 18:42Довольно давно есть:
https://discuss.kotlinlang.org/t/compiller-works-very-slow-for-big-amout-of-inlined-code/2234/2
ookami_kb
Надо заметить, попахивает очень плохой практикой. Например, тот же "Data class inheritance from other classes is forbidden" запрещен не просто так, и это "чтобы они не мозолили глаза" может потом привести к неочевидным поведениям и с трудом отлавливаемым ошибкам.
JouriM
При чем тут практика вообще?
Это таблица всех существующих сообщений.
Нигде не написано что их все нужно или можно маскировать.