Типы сообщений ошибок и предупреждений 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)


  1. ookami_kb
    16.02.2017 22:58
    +1

    Надо заметить, попахивает очень плохой практикой. Например, тот же "Data class inheritance from other classes is forbidden" запрещен не просто так, и это "чтобы они не мозолили глаза" может потом привести к неочевидным поведениям и с трудом отлавливаемым ошибкам.


    1. JouriM
      16.02.2017 23:25
      +1

      При чем тут практика вообще?
      Это таблица всех существующих сообщений.
      Нигде не написано что их все нужно или можно маскировать.


  1. fogone
    16.02.2017 23:04
    +1

    > Предупреждения и ошибки, которые генерирует компилятор Kotlin часто нужно замаскировать

    если вы действительно делаете это часто, то явно делаете что-то не то. Хотя сама по себе таблица вещь очень полезная, спасибо!


    1. 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")
      ...

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


      1. fogone
        17.02.2017 09:00

        да, это правда — unchecked cast это основной suppress и в моем коде, а вот NON_PUBLIC_CALL_FROM_PUBLIC_INLINE я использовать постеснялся, честно говоря, не уверен, что это хорошая идея. Может быть кто-то знает, почему компилятор это запрещает?


        1. JouriM
          17.02.2017 09:21
          +1

          Т.к. функция inline, то все ее тело подставляется в место использования и как бы получается что приватные поля и методы "используются" за пределами класса.
          Я голосую за обыкновенный баг.


          1. Morj
            17.02.2017 10:59
            +1

            Так они и на самом деле используются, просто для них вгенерятся синтетический геттер с названием access$getAllowlog$p. Если не предупредить об этом разработчика, то у приватного поля появится геттер (в виде статического метода), доступный из Java.


            1. JouriM
              19.02.2017 20:01

              У приватных пропертей нет гетеров
              Да и не в этом суть
              Это публичная функция, что и как она использует внутри своей реализации никого за ее пределами волновать не должно и от того что она inline ничего не меняется


              1. Morj
                20.02.2017 09:23

                У приватных полей нет геттеров, но появляются статические методы для их чтения, если вы делаете публичную инлайн-функцию с их использованием. От того, что функция инлайн меняется то, что напрямую через взятие поля это поле приватное поле не прочитать (без setAccessible), поэтому тело этой функции будет содержать вызов специального метода на стороне "вызова". Посмотрите сами в bytecode viewer.


                1. JouriM
                  20.02.2017 18:31

                  Да, извиняюсь, не сообразил с доступом.
                  Тем не менее (и даже более), выдавать сообщения основанные на внутренних собенностях реализации, о которых никто знать не должен и которые никак не влияют на выполнение программы, на мой взгляд, совершенно бессмысленно.


                  1. Morj
                    20.02.2017 18:35

                    Они влияют на API с точки зрения java, некоторым это может быть важно.


                    1. JouriM
                      20.02.2017 19:20

                      Хмм… не понимаю о чем речь.
                      Можете привести пример?


        1. abreslav
          20.02.2017 11:36

          Для этих целей в 1.1 появилась аннотация @PublishedApi, применимая к internal-декларациям


          1. JouriM
            20.02.2017 19:19
            -1

            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 )  //Мне НЕ надо чтобы был доступ
            }
            

            1. Если бы я захотел сделать поле доступным, то я бы это сделал просто указав public, зачем вообще эта аннотация нужна?
              Можете привести пример ее осмысленного использования?


            1. abreslav
              21.02.2017 09:37
              +1

              Слово internal означает "доступно внутри модуля". Ваш main в том же модуле.


              Мы не разрешаем ссылки и inline функций на приватные декларации, потому что это нарушает главный принцип работы с ними: изменения приватных деклараций не должно ломать клиентский бинарный код. Там выше подробно объяснили, почему код сломается, если разрешить ссылку на private.


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


  1. Sirikid
    16.02.2017 23:26

    Использовал своё awk-фу.


    Это таблица всех существующих сообщений.

    Почему есть много разных UNUSED_blah, но нету варианта UNUSED?


    1. JouriM
      16.02.2017 23:40

      Вообще вопроса не понял.


      А насчет создания списка для использования автодополнения (для чего ты, наверное, и сделал класс) я думал.
      С этого начал, но оказалось, что пользоваться этим ничуть не удобнее чем отдельной таблицей т.к. искать сообщение все равно можно только по тексту.
      Да и как-то жаба душит тащить кучу абсолютно ненужного кода в либу. По привычке.


      1. Sirikid
        17.02.2017 00:14

        Вообще вопроса не понял.

        Есть ещё (как минимум) один вариант такого сообщения, просто UNUSED — он работает и с функциями, и с классами, и с параметрами, в общем со всеми неиспользованными сущностями.


        Во первых автодополнение, а во вторых когда вручную пишешь все эти строки очень легко опечататся и никто ничего не скажет. Сам исправлял одну такую опечатку аж в аналогичном классе для джавовского @SuppressWarnings. В идеале этот параметр аннотации должен быть перечислением, но в Java разрешено использовать собственные предупреждения (компиляторам и даже средам) и видимо поэтому этот параметр строка.


        1. JouriM
          17.02.2017 00:54

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


          Иметь авто-дополнение для известных типов, конечно, хорошо, но только ради этого тащить кучу кода в любой проект, по моему, неадекватно и для эксклюзивов это никак не поможет.
          Типовой кейс: вылезает ошибка, которую надо исключить, копирую уникальную часть текста (с этим самые большие сложности т.к. в IntelliJ окно с сообщениями об ошибках — это боль), ищу его в таблице, копирую тип и втыкаю в исходник.
          В кнопках, помимо войны с текстом сообщения в IntelliJ, это все очень быстро: Alt-Tab, F4, F7, Ctrl-V, Enter пара шевелений стрелками, Ctrl-C и уже Alt-Tab обратно с типом.


  1. abreslav
    17.02.2017 10:52

    Хочу заметить, что это все — внутренние особенности компилятора, и мы не гарантируем, что они будут поддерживаться в будущем :)


    1. Sirikid
      17.02.2017 11:03
      +1

      А почему вы не делаете такие штуки перечислениями? (Неужели слишком много вариантов для перечисления?)


      1. yole
        17.02.2017 11:57

        Это не enum, потому что существуют диагностики, специфичные для конкретных бэкэндов, и если бы мы собрали их все в один enum, это создало бы очень неприятный coupling.


      1. abreslav
        17.02.2017 12:45

        Ну вы, наверное, заметили, что у этих штук разные типы :)


  1. yole
    17.02.2017 11:53
    +1

    Причина, почему мы не публикуем эту информацию — во-первых, то, что сказал Андрей (диагностики регулярно меняются, разделяются и объединяются), а во-вторых, не предполагается, что кто-то будет писать аннотации @Suppress руками, а не через квикфикс. Если вы знаете конкретные случаи, в которых квикфикс на сообщении от компилятора недоступен или не работает — пожалуйста, сообщите нам, и мы это исправим.


    1. Morj
      17.02.2017 13:23

      А если я в vim пишу на Котлине?


      1. HaruAtari
        17.02.2017 13:29
        -3

        В таком случае вы сам себе злой Буратино.


        1. zagayevskiy
          17.02.2017 14:02
          +1

          И почему же? Язык, на котором можно писать только в одной IDE? Только не разводите холивар на тему "vim — не IDE", речь не об этом.


          1. yole
            17.02.2017 14:48
            -1

            У нас есть официальные плагины для трёх IDE — IntelliJ IDEA, Eclipse и NetBeans. Если кто-то хочет программировать на Kotlin в другой среде — это их выбор, но мы не делаем ничего, чтобы как-то специально облегчить их жизнь.


            1. zagayevskiy
              17.02.2017 15:05

              А если хочется узнать, что означает конкретный Suppress, и это непонятно из названия?


              1. yole
                17.02.2017 15:11

                Сейчас нормального способа для этого нет (хотя я не помню, чтобы кто-то когда-то этого просил). В будущем мы планируем сделать для каждого диагностического сообщения страничку с документацией, в которой написано, в каких случаях оно возникает и как его правильно избегать, и тогда можно будет сделать навигацию с @Suppress на соответствующую страничку.


                1. zagayevskiy
                  17.02.2017 15:12

                  Спасибо. Я спрашиваю из праздного интереса, пока Котлином не пользуюсь. Ключевое слово — пока :)


    1. JouriM
      19.02.2017 20:10
      +1

      То что все меняется — это понятно и никто (надеюсь) не рассчитывает что этот список сообщений будет вечен
      А вто то что ни нигде не опубликованы — это плохо т.к. писать программу надо здесь и сейчас, на этой версии языка, а не на некоей идеальной, которая когда-то появится.
      Я это не к тому, что надо все бросать и делать немедленно этот список
      В языке пока есть много принципиальных проблем, которые меня лично волнуют значительно больше чем список сообщений, типа практически виснущего процесса компиляции на некоторых инлайновых функций или рразмножение одинаковых классов при использовнии ::StatFunction.
      Для меня лично отсутствие списка ошибок особой проблемы не составляет т.к. "на скорость не влияет"


      1. abreslav
        20.02.2017 11:42

        Пожалуйста, записывайте волнующие вас проблемы (особенно "практически виснущий" компилятор) в наш баг-трекер: https://youtrack.jetbrains.com/issues/KT


        1. JouriM
          20.02.2017 18:42

          1. abreslav
            21.02.2017 09:34

            Не вижу там ссылки на реквест в трекере