All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scalafix.internal.config.DisableConfig.scala Maven / Gradle / Ivy

package scalafix
package internal.config

import metaconfig._
import metaconfig.annotation.{Description, ExampleValue}
import metaconfig.generic.Surface
import org.langmeta.Symbol

import scalafix.internal.util.SymbolOps

case class DisabledSymbol(
    @ExampleValue("scala.sys.process.Process")
    @Description(
      "A single symbol to ban. Cannot be used together with the regex option.")
    symbol: Option[Symbol.Global],
    @Description("Custom message.")
    message: Option[String],
    @Description("Custom id for error messages.")
    id: Option[String],
    @Description(
      "Regex to ban symbols matching a given include/exclude patterns." +
        "Cannot be used together with the symbol option." +
        "Include and exclude filters can be either a list of regex strings or a single regex string.")
    @ExampleValue("""
                    |{
                    |  includes = [
                    |    "java.io.*"
                    |    "scala.io.*"
                    |  ]
                    |  excludes = "java.io.InputStream"
                    |}""".stripMargin)
    regex: Option[FilterMatcher]) {

  def matches(symbol: Symbol): Boolean = {
    this.symbol match {
      case Some(s) => SymbolOps.isSameNormalized(symbol, s)
      case None =>
        regex match {
          case Some(r) => r.matches(symbol.toString)
          case None => sys.error("impossible")
        }
    }
  }
}

object DisabledSymbol {
  implicit val surface: Surface[DisabledSymbol] =
    generic.deriveSurface[DisabledSymbol]

  private def normalizeMessage(msg: String): String =
    if (msg.isMultiline) {
      "\n" + msg.stripMargin
    } else {
      msg
    }
  implicit val reader: ConfDecoder[DisabledSymbol] =
    ConfDecoder.instance[DisabledSymbol] {
      case c: Conf.Obj =>
        (c.getOption[Symbol.Global]("symbol") |@|
          c.getOption[String]("message") |@|
          c.getOption[String]("id") |@|
          c.getOption[FilterMatcher]("regex"))
          .andThen {
            case (((Some(_), b), c), Some(_)) =>
              Configured.notOk(ConfError.message(
                "Cannot specify both symbol and regex, only one of them is allowed."))
            case (((a @ Some(_), b), c), None) =>
              Configured.ok(DisabledSymbol(a, b.map(normalizeMessage), c, None))
            case (((None, b), c), d @ Some(_)) =>
              Configured.ok(DisabledSymbol(None, b.map(normalizeMessage), c, d))
            case (((None, b), c), None) =>
              Configured.notOk(
                ConfError.message("Either symbol or regex must be specified."))
          }
      case s: Conf.Str =>
        symbolGlobalReader
          .read(s)
          .map(sym => DisabledSymbol(Some(sym), None, None, None))
    }
}

case class UnlessInsideBlock(
    @Description("The symbol that indicates a 'safe' block.")
    safeBlock: DisabledSymbol,
    @Description(
      "The unsafe symbols that are banned unless inside a 'safe' block")
    symbols: List[DisabledSymbol])

object UnlessInsideBlock {
  implicit val surface: Surface[UnlessInsideBlock] =
    generic.deriveSurface[UnlessInsideBlock]
  // NOTE(olafur): metaconfig.generic.deriveDecoder requires a default base values.
  // Here we require all fields to be provided by the user so we write the decoder manually.
  implicit val reader: ConfDecoder[UnlessInsideBlock] =
    ConfDecoder.instanceF[UnlessInsideBlock] {
      case c: Conf.Obj =>
        (c.get[DisabledSymbol]("safeBlock") |@|
          c.get[List[DisabledSymbol]]("symbols")).map {
          case (a, b) => UnlessInsideBlock(a, b)
        }
      case _ => Configured.NotOk(ConfError.message("Wrong config format"))
    }
}

case class DisableConfig(
    @Description("List of symbols to disable if written explicitly in source." +
      " Does not report an error for inferred symbols in macro expanded code " +
      "or implicits.")
    @ExampleValue("""
                    |[
                    |  {
                    |    symbol = "scala.Any.asInstanceOf"
                    |    message = "use pattern-matching instead"
                    |  }
                    |]""".stripMargin)
    symbols: List[DisabledSymbol] = Nil,
    @Description(
      "List of symbols to disable if inferred. Does not report an error for symbols written explicitly in source code.")
    @ExampleValue("""
                    |[
                    |  {
                    |    symbol = "scala.Predef.any2stringadd"
                    |    message = "use explicit toString be calling +"
                    |  }
                    |]""".stripMargin)
    ifSynthetic: List[DisabledSymbol] = Nil,
    @Description(
      "List of symbols to disable unless they are in the given block.")
    @ExampleValue("""
                    |[
                    |  {
                    |    safeBlock = "scala.util.Try"
                    |    symbols = [
                    |      {
                    |        symbol = "scala.Option.get"
                    |        message = "the function may throw an exception"
                    |      }
                    |    ]
                    |  }
                    |]
      """.stripMargin)
    unlessInside: List[UnlessInsideBlock] = Nil
) {
  lazy val allSafeBlocks: List[DisabledSymbol] =
    unlessInside.map(_.safeBlock)
  lazy val allDisabledSymbols: List[DisabledSymbol] =
    symbols ++ ifSynthetic ++ unlessInside.flatMap(_.symbols)
}

object DisableConfig {
  lazy val default = DisableConfig()
  implicit val surface: Surface[DisableConfig] =
    metaconfig.generic.deriveSurface[DisableConfig]
  implicit val decoder: ConfDecoder[DisableConfig] =
    generic.deriveDecoder[DisableConfig](default)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy