-Ywarn-unused-import срабатывает в файле маршрутов воспроизведения

Я хочу иметь возможность использовать -Xfatal-warnings и -Ywarn-unused-import, проблема в том, что компилятор вызывает ошибку в файле, содержащем маршруты воспроизведения для моего приложения:

[error] /path/to/app/conf/routes: Unused import
[error] /path/to/app/conf/routes: Unused import
[error] /path/to/app/conf/routes:1: Unused import
[error] GET        /document/:id        my.app.controllers.MyController.getById(id: Int)

то же самое и с другими маршрутами.

Возможно ли сказать scalac игнорировать файл?

Версия Scala 2.11.8.


person Ende Neu    schedule 24.05.2016    source источник
comment
Какая версия скалы? Некоторое время назад я внес исправление для предупреждения, связанного с неявным поиском. Я бы попробовал пример проекта, если вы можете это сделать. Нет механизма для игнорирования файла, за исключением того, что в версии 2.11 вы можете предоставить генератор отчетов, который делает все, что захочет.   -  person som-snytt    schedule 18.07.2016
comment
@som-snytt Я использую scala 2.11.8, здесь вы можете найти пример проекта, просто необходимо составить.   -  person Ende Neu    schedule 18.07.2016
comment
какие-нибудь обновления по теме?   -  person Louis F.    schedule 03.08.2016
comment
Шаблоны действительно вводят дополнительный импорт при компиляции маршрута. Можно было бы исключить классы маршрутов из задачи scalac, а затем использовать пользовательскую задачу для компиляции; или запустите средство форматирования, которое очищает импорт; или сделать компилятор маршрута умнее. Я еще не пробовал.   -  person som-snytt    schedule 31.08.2016
comment
@som-snytt Я действительно видел, как некоторые импортированные файлы генерируются в файле Routes.scala, но компилятор указывает непосредственно на файл routes, и я не уверен, почему и имеет ли это значение. Что касается форматера, scalariform не поддерживает оптимизацию импорта, возможно, ее можно установить в Intellij. Насчет scalac даже не знаю с чего начинать, гугление тоже не помогло.   -  person Ende Neu    schedule 31.08.2016
comment
У меня точно такая же проблема, Scala 2.11.8, Play 2.5.10. В итоге я закомментировал warn-unused-import. Вы когда-нибудь находили решение этой проблемы?   -  person Abhijit Sarkar    schedule 02.01.2017
comment
К сожалению, нет.   -  person Ende Neu    schedule 02.01.2017


Ответы (3)


Я только что столкнулся с той же проблемой со Scala 2.12 и Play 2.6 (которые вы, вероятно, сейчас используете).

Плагин компилятора Scala под названием Silencer решает эту проблему: https://github.com/ghik/silencer

Добавьте следующую зависимость в build.sbt:

val silencerVersion = "1.2.1"

libraryDependencies ++= Seq(
    compilerPlugin("com.github.ghik" %% "silencer-plugin" % silencerVersion),
    "com.github.ghik" %% "silencer-lib" % silencerVersion % Provided
)

Затем добавьте (также в build.sbt):

scalacOptions += "-P:silencer:globalFilters=Unused import"

Текст после globalFilters= представляет собой список совпадений регулярных выражений для предупреждений компилятора об отключении звука, может быть разделен запятыми. Возможно, вам придется настроить регулярное выражение для вашего собственного случая, но я обнаружил, что приведенный выше пример работает нормально.

Это означает, что он заглушает любые предупреждения «Неиспользуемый импорт», но если у вас есть привычка автоматически форматировать свой код (ctrl+alt+L в Intellij), включая очистку неиспользуемого импорта, то это не должно вызывать проблемы.

person Matt Stephens    schedule 09.10.2018
comment
Это выглядит многообещающе, я попробую это в ближайшие дни! - person Ende Neu; 11.10.2018
comment
Это просто подавляет все предупреждения для меня - person Pavel; 31.10.2018
comment
@Pavel Спасибо за отзыв, я обновил ответ, чтобы исправить регулярное выражение. - person Matt Stephens; 01.11.2018
comment
@MattStephens теперь не подавит все предупреждения о неиспользуемом импорте? Во всяком случае для меня плагин-глушитель с любым ключом globalFilters просто подавляет все предупреждения (пробовал разные регэкспы, разные ключи, разный код), наверняка в глушителе баг. - person Pavel; 01.11.2018
comment
@ Павел, да, это правильно, как я отметил в последнем абзаце. Насколько велика проблема, вероятно, зависит от личных предпочтений и от того, автоматически ли ваша команда очищает импорт. Не могу особо прокомментировать глушитель, так как здесь он работал нормально; изменив регулярное выражение, я мог бы переключить его с рабочего на неработающий и обратно. Возможно, разные версии Scala/JVM/sbt? - person Matt Stephens; 01.11.2018

Ужасным «решением» может быть удаление этих неиспользуемых импортов после создания маршрутов, но до запуска задачи компиляции. Вот набросок:

lazy val optimizeRoutesImports = taskKey[Unit]("Remove unused imports from generated routes sources.")

optimizeRoutesImports := {

  def removeUnusedImports(targetFiles: (File) => PathFinder, linesToRemove: Set[String], linesToReplace: Map[String, String]) = {
    val files = targetFiles(crossTarget.value).get
    files foreach { file =>
      val lines = sbt.IO.readLines(file)
      val updatedLines = lines map { line =>
        linesToReplace.getOrElse(line, line)
      } filterNot { line =>
        linesToRemove.contains(line.trim)
      }
      sbt.IO.writeLines(file, updatedLines, append = false)
    }
  }

  removeUnusedImports(
    _ / "routes" / "main" / "controllers" / "ReverseRoutes.scala",
    Set("import ReverseRouteContext.empty"),
    Map(
      "import play.api.mvc.{ QueryStringBindable, PathBindable, Call, JavascriptLiteral }" ->
        "import play.api.mvc.{ QueryStringBindable, PathBindable, Call }",
      "import play.core.routing.{ HandlerDef, ReverseRouteContext, queryString, dynamicString }" ->
        "import play.core.routing.{ ReverseRouteContext, queryString, dynamicString }"
    )
  )

  removeUnusedImports(
    _ / "routes" / "main" / "controllers" / "javascript" / "JavaScriptReverseRoutes.scala",
    Set(
      "import play.core.routing.{ HandlerDef, ReverseRouteContext, queryString, dynamicString }",
      "import ReverseRouteContext.empty"
    ),
    Map(
      "import play.api.mvc.{ QueryStringBindable, PathBindable, Call, JavascriptLiteral }" ->
        "import play.api.mvc.{ QueryStringBindable, PathBindable }"
    )
  )

  removeUnusedImports(
    _ / "routes" / "main" / "router" / "Routes.scala",
    Set("import play.core.j._"),
    Map())
}

Затем вы захотите разобраться в зависимостях задач:

// Our optimize routes imports task depends on the routes task.
optimizeRoutesImports := (optimizeRoutesImports dependsOn (play.sbt.routes.RoutesKeys.routes in Compile)).value

// And compilation depends on the unused routes having been removed.
compile := ((compile in Compile) dependsOn optimizeRoutesImports).value

Вам также, вероятно, потребуется установить TwirlKeys.templateImports в консервативный список, прежде чем включать -Ywarn-unused-import. Что-то вроде этого, в зависимости от того, какие типы используются в ваших представлениях:

TwirlKeys.templateImports := Seq("play.api.mvc._", "play.api.i18n.Messages", "controllers.routes")

Мне также пришлось выбить неиспользуемые TemplateMagic импорты из шаблонов Twirl (YMMV):

  removeUnusedImports(
    _ / "twirl" ** "*.template.scala",
    Set("import play.twirl.api.TemplateMagic._"),
    Map())

Если вы это сделаете, убедитесь, что зависимости задачи настроены соответствующим образом.

Это работает для меня. Scala 2.11.8, Play 2.5.10, -Xfatal-warnings и -Ywarn-unused-import включены. Это ужасно, но это работает.

person danielnixon    schedule 17.01.2017

Вот еще вариант (возможно, хуже, чем у danielnixon)

Я добавил следующее к build.sbt:

import CustomGenerator._

import play.sbt.routes.RoutesKeys
RoutesKeys.routesImport := Seq.empty
routesGenerator := ModifiedInjectedRoutesGenerator

Затем добавил это к project/CustomGenerator.scala (всегда верхний уровень project/):

object CustomGenerator {
  object ModifiedInjectedRoutesGenerator extends play.routes.compiler.RoutesGenerator {
    import play.routes.compiler._
    import play.routes.compiler.RoutesCompiler.RoutesCompilerTask

    def generate(task: RoutesCompilerTask, namespace: Option[String], rules: List[Rule]): Seq[(String, String)] = {
      play.routes.compiler.InjectedRoutesGenerator.generate(task, namespace, rules) map { case(key, value) =>
        var v = value
        if(key.endsWith("/ReverseRoutes.scala")) {
          v = v.replace("import ReverseRouteContext.empty", "implicit val empty = ReverseRouteContext(Map())")
          v = v.replace("import play.core.routing.{ HandlerDef, ReverseRouteContext, queryString, dynamicString }", "import play.core.routing.{ ReverseRouteContext, queryString }")
          v = v.replace("import play.api.mvc.{ QueryStringBindable, PathBindable, Call, JavascriptLiteral }", "import play.api.mvc.{ QueryStringBindable, Call }")
        }
        if(key.endsWith("migrations/ReverseRoutes.scala")) {
          v = v.replace("import play.api.mvc.{ QueryStringBindable, Call }", "import play.api.mvc.{ Call }")
          v = v.replace("import play.core.routing.{ ReverseRouteContext, queryString }", "import play.core.routing.{ ReverseRouteContext }")
        }
        if(key.endsWith("/JavaScriptReverseRoutes.scala")) {
          v = v.replace("import ReverseRouteContext.empty", "")
          v = v.replace("import play.api.mvc.{ QueryStringBindable, PathBindable, Call, JavascriptLiteral }", "import play.api.mvc.{ QueryStringBindable, JavascriptLiteral }")
          v = v.replace("import play.core.routing.{ HandlerDef, ReverseRouteContext, queryString, dynamicString }", "")
        }
        if(key.endsWith("migrations/javascript/JavaScriptReverseRoutes.scala")) {
          v = v.replace("import play.api.mvc.{ QueryStringBindable, JavascriptLiteral }", "")
        }
        if(key.endsWith("/Routes.scala")) {
          v = v.replace("import play.core.routing.HandlerInvokerFactory._", "")
          v = v.replace("import play.core.j._", "")
          v = v.replace("import ReverseRouteContext.empty", "implicit val empty = ReverseRouteContext(Map())")
        }
        (key, v)
      }
    }

    def id: String = "injected+"
  }
}

Плагин Play sbt генерирует код маршрутов (можно увидеть под target/scala-2.11/routes). Этот фрагмент кода удаляет или встраивает все неиспользуемые импорты. Возможно, вам придется адаптировать его для ваших маршрутов.

person Pavel    schedule 31.10.2018