|  | 
|  | 1 | +package dotty.tools | 
|  | 2 | +package dotc | 
|  | 3 | +package reporting | 
|  | 4 | + | 
|  | 5 | +import dotty.tools.dotc.core.Contexts._ | 
|  | 6 | + | 
|  | 7 | +import java.util.regex.PatternSyntaxException | 
|  | 8 | +import scala.annotation.internal.sharable | 
|  | 9 | +import scala.collection.mutable.ListBuffer | 
|  | 10 | +import scala.util.matching.Regex | 
|  | 11 | + | 
|  | 12 | +enum MessageFilter: | 
|  | 13 | +  def matches(message: Diagnostic): Boolean = this match { | 
|  | 14 | +    case Any => true | 
|  | 15 | +    case Deprecated => message.isInstanceOf[Diagnostic.DeprecationWarning] | 
|  | 16 | +    case Feature => message.isInstanceOf[Diagnostic.FeatureWarning] | 
|  | 17 | +    case MessagePattern(pattern) => pattern.findFirstIn(message.msg.rawMessage).nonEmpty | 
|  | 18 | +  } | 
|  | 19 | +  case Any, Deprecated, Feature | 
|  | 20 | +  case MessagePattern(pattern: Regex) | 
|  | 21 | + | 
|  | 22 | +enum Action: | 
|  | 23 | +  case Error, Warning, Info, Silent | 
|  | 24 | + | 
|  | 25 | +final case class WConf(confs: List[(List[MessageFilter], Action)]): | 
|  | 26 | +  def action(message: Diagnostic): Action = confs.collectFirst { | 
|  | 27 | +    case (filters, action) if filters.forall(_.matches(message)) => action | 
|  | 28 | +  }.getOrElse(Action.Warning) | 
|  | 29 | + | 
|  | 30 | +@sharable object WConf: | 
|  | 31 | +  import Action._ | 
|  | 32 | +  import MessageFilter._ | 
|  | 33 | + | 
|  | 34 | +  private type Conf = (List[MessageFilter], Action) | 
|  | 35 | + | 
|  | 36 | +  def parseAction(s: String): Either[List[String], Action] = s match { | 
|  | 37 | +    case "error" | "e"            => Right(Error) | 
|  | 38 | +    case "warning" | "w"          => Right(Warning) | 
|  | 39 | +    case "info" | "i"             => Right(Info) | 
|  | 40 | +    case "silent" | "s"           => Right(Silent) | 
|  | 41 | +    case _                        => Left(List(s"unknown action: `$s`")) | 
|  | 42 | +  } | 
|  | 43 | + | 
|  | 44 | +  private def regex(s: String) = | 
|  | 45 | +    try Right(s.r) | 
|  | 46 | +    catch { case e: PatternSyntaxException => Left(s"invalid pattern `$s`: ${e.getMessage}") } | 
|  | 47 | + | 
|  | 48 | +  val splitter = raw"([^=]+)=(.+)".r | 
|  | 49 | + | 
|  | 50 | +  def parseFilter(s: String): Either[String, MessageFilter] = s match { | 
|  | 51 | +    case "any" => Right(Any) | 
|  | 52 | +    case splitter(filter, conf) => filter match { | 
|  | 53 | +      case "msg" => regex(conf).map(MessagePattern.apply) | 
|  | 54 | +      case "cat" => | 
|  | 55 | +        conf match { | 
|  | 56 | +          case "deprecation" => Right(Deprecated) | 
|  | 57 | +          case "feature"     => Right(Feature) | 
|  | 58 | +          case _             => Left(s"unknown category: $conf") | 
|  | 59 | +        } | 
|  | 60 | +      case _ => Left(s"unknown filter: $filter") | 
|  | 61 | +    } | 
|  | 62 | +    case _ => Left(s"unknown filter: $s") | 
|  | 63 | +  } | 
|  | 64 | + | 
|  | 65 | +  private var parsedCache: (List[String], WConf) = null | 
|  | 66 | +  def parsed(using Context): WConf = | 
|  | 67 | +    val setting = ctx.settings.Wconf.value | 
|  | 68 | +    if parsedCache == null || parsedCache._1 != setting then | 
|  | 69 | +      val conf = fromSettings(setting) | 
|  | 70 | +      parsedCache = (setting, conf.getOrElse(WConf(Nil))) | 
|  | 71 | +      conf.swap.foreach(msgs => | 
|  | 72 | +        val multiHelp = | 
|  | 73 | +          if (setting.sizeIs > 1) | 
|  | 74 | +            """ | 
|  | 75 | +              |Note: for multiple filters, use `-Wconf:filter1:action1,filter2:action2` | 
|  | 76 | +              |      or alternatively          `-Wconf:filter1:action1 -Wconf:filter2:action2`""".stripMargin | 
|  | 77 | +          else "" | 
|  | 78 | +        report.warning(s"Failed to parse `-Wconf` configuration: ${ctx.settings.Wconf.value.mkString(",")}\n${msgs.mkString("\n")}$multiHelp")) | 
|  | 79 | +    parsedCache._2 | 
|  | 80 | + | 
|  | 81 | +  def fromSettings(settings: List[String]): Either[List[String], WConf] = | 
|  | 82 | +    if (settings.isEmpty) Right(WConf(Nil)) | 
|  | 83 | +    else { | 
|  | 84 | +      val parsedConfs: List[Either[List[String], (List[MessageFilter], Action)]] = settings.map(conf => { | 
|  | 85 | +        val parts = conf.split("[&:]") // TODO: don't split on escaped \& | 
|  | 86 | +        val (ms, fs) = parts.view.init.map(parseFilter).toList.partitionMap(identity) | 
|  | 87 | +        if (ms.nonEmpty) Left(ms) | 
|  | 88 | +        else if (fs.isEmpty) Left(List("no filters or no action defined")) | 
|  | 89 | +        else parseAction(parts.last).map((fs, _)) | 
|  | 90 | +      }) | 
|  | 91 | +      val (ms, fs) = parsedConfs.partitionMap(identity) | 
|  | 92 | +      if (ms.nonEmpty) Left(ms.flatten) | 
|  | 93 | +      else Right(WConf(fs)) | 
|  | 94 | +    } | 
0 commit comments