@@ -17,6 +17,8 @@ import dotty.tools.dotc.config.Settings.Setting.ChoiceWithHelp
1717
1818object Settings:
1919
20+ private inline def classTag[T](using ctag: ClassTag[T]): ClassTag[T] = ctag
21+
2022 val BooleanTag: ClassTag[Boolean] = ClassTag.Boolean
2123 val IntTag: ClassTag[Int] = ClassTag.Int
2224 val StringTag: ClassTag[String] = ClassTag(classOf[String])
@@ -93,21 +95,25 @@ object Settings:
9395 assert(legacyArgs || !choices.exists(_.contains("")), s"Empty string is not supported as a choice for setting $name")
9496 // Without the following assertion, it would be easy to mistakenly try to pass a file to a setting that ignores invalid args.
9597 // Example: -opt Main.scala would be interpreted as -opt:Main.scala, and the source file would be ignored.
96- assert(!(summon[ClassTag[T] ] == ListTag && ignoreInvalidArgs), s"Ignoring invalid args is not supported for multivalue settings: $name")
98+ assert(!(classTag[T ] == ListTag && ignoreInvalidArgs), s"Ignoring invalid args is not supported for multivalue settings: $name")
9799
98100 val allFullNames: List[String] = s"$name" :: s"-$name" :: aliases
99101
102+ def isPresentIn(state: SettingsState): Boolean = state.wasChanged(idx)
103+
100104 def valueIn(state: SettingsState): T = state.value(idx).asInstanceOf[T]
101105
106+ def userValueIn(state: SettingsState): Option[T] = if isPresentIn(state) then Some(valueIn(state)) else None
107+
102108 def updateIn(state: SettingsState, x: Any): SettingsState = x match
103109 case _: T => state.update(idx, x)
104- case _ => throw IllegalArgumentException(s"found: $x of type ${x.getClass.getName}, required: ${summon[ClassTag[T] ]}")
110+ case _ => throw IllegalArgumentException(s"found: $x of type ${x.getClass.getName}, required: ${classTag[T ]}")
105111
106112 def isDefaultIn(state: SettingsState): Boolean = valueIn(state) == default
107113
108- def isMultivalue: Boolean = summon[ClassTag[T] ] == ListTag
114+ def isMultivalue: Boolean = classTag[T ] == ListTag
109115
110- def acceptsNoArg: Boolean = summon[ClassTag[T]] == BooleanTag || summon[ClassTag[T] ] == OptionTag || choices.exists(_.contains(""))
116+ def acceptsNoArg: Boolean = classTag[T] == BooleanTag || classTag[T ] == OptionTag || choices.exists(_.contains(""))
111117
112118 def legalChoices: String =
113119 choices match
@@ -123,7 +129,7 @@ object Settings:
123129 * Updates the value in state
124130 *
125131 * @param getValue it is crucial that this argument is passed by name, as [setOutput] have side effects.
126- * @param argStringValue string value of currently proccessed argument that will be used to set deprecation replacement
132+ * @param argStringValue string value of currently processed argument that will be used to set deprecation replacement
127133 * @param args remaining arguments to process
128134 * @return new argumment state
129135 */
@@ -159,11 +165,17 @@ object Settings:
159165
160166 def missingArg =
161167 val msg = s"missing argument for option $name"
162- if ignoreInvalidArgs then state.warn(msg + " , the tag was ignored") else state.fail(msg)
168+ if ignoreInvalidArgs then state.warn(s"$msg , the tag was ignored") else state.fail(msg)
163169
164170 def invalidChoices(invalid: List[String]) =
165171 val msg = s"invalid choice(s) for $name: ${invalid.mkString(",")}"
166- if ignoreInvalidArgs then state.warn(msg + ", the tag was ignored") else state.fail(msg)
172+ if ignoreInvalidArgs then state.warn(s"$msg, the tag was ignored") else state.fail(msg)
173+
174+ def isEmptyDefault = default == null.asInstanceOf[T] || classTag[T].match
175+ case ListTag => default.asInstanceOf[List[?]].isEmpty
176+ case StringTag => default.asInstanceOf[String].isEmpty
177+ case OptionTag => default.asInstanceOf[Option[?]].isEmpty
178+ case _ => false
167179
168180 def setBoolean(argValue: String, args: List[String]) =
169181 if argValue.equalsIgnoreCase("true") || argValue.isEmpty then update(true, argValue, args)
@@ -192,11 +204,11 @@ object Settings:
192204 def setOutput(argValue: String, args: List[String]) =
193205 val path = Directory(argValue)
194206 val isJar = path.ext.isJar
195- if ( !isJar && !path.isDirectory) then
207+ if !isJar && !path.isDirectory then
196208 state.fail(s"'$argValue' does not exist or is not a directory or .jar file")
197209 else
198210 /* Side effect, do not change this method to evaluate eagerly */
199- def output = if ( isJar) JarArchive.create(path) else new PlainDirectory(path)
211+ def output = if isJar then JarArchive.create(path) else new PlainDirectory(path)
200212 update(output, argValue, args)
201213
202214 def setVersion(argValue: String, args: List[String]) =
@@ -212,22 +224,28 @@ object Settings:
212224 case _ => update(strings, argValue, args)
213225
214226 def doSet(argRest: String) =
215- ((summon[ClassTag[T]], args): @unchecked) match
216- case ( BooleanTag, _) =>
227+ classTag[T] match
228+ case BooleanTag =>
217229 if sstate.wasChanged(idx) && preferPrevious then ignoreValue(args)
218230 else setBoolean(argRest, args)
219- case ( OptionTag, _) =>
231+ case OptionTag =>
220232 update(Some(propertyClass.get.getConstructor().newInstance()), "", args)
221- case (ct, args) =>
233+ case ct =>
222234 val argInArgRest = !argRest.isEmpty || legacyArgs
223- val argAfterParam = !argInArgRest && args.nonEmpty && (ct == IntTag || !args.head.startsWith("-"))
235+ inline def argAfterParam = !argInArgRest && args.nonEmpty && (ct == IntTag || !args.head.startsWith("-"))
224236 if argInArgRest then
225237 doSetArg(argRest, args)
226238 else if argAfterParam then
227239 doSetArg(args.head, args.tail)
228- else missingArg
240+ else if isEmptyDefault then
241+ missingArg
242+ else
243+ doSetArg(arg = null, args)
229244
230- def doSetArg(arg: String, argsLeft: List[String]) = summon[ClassTag[T]] match
245+ def doSetArg(arg: String, argsLeft: List[String]) =
246+ classTag[T] match
247+ case ListTag if arg == null =>
248+ update(List(default), arg, argsLeft)
231249 case ListTag =>
232250 val strings = arg.split(",").toList
233251 appendList(strings, arg, argsLeft)
@@ -283,6 +301,7 @@ object Settings:
283301 object Setting:
284302 extension [T](setting: Setting[T])
285303 def value(using Context): T = setting.valueIn(ctx.settingsState)
304+ def userValue(using Context): Option[T] = setting.userValueIn(ctx.settingsState)
286305 def update(x: T)(using Context): SettingsState = setting.updateIn(ctx.settingsState, x)
287306 def isDefault(using Context): Boolean = setting.isDefaultIn(ctx.settingsState)
288307
@@ -412,7 +431,7 @@ object Settings:
412431 publish(Setting(category, prependName(name), descr, default, legacyArgs = legacyArgs, deprecation = deprecation))
413432
414433 def OptionSetting[T: ClassTag](category: SettingCategory, name: String, descr: String, aliases: List[String] = Nil, deprecation: Option[Deprecation] = None): Setting[Option[T]] =
415- publish(Setting(category, prependName(name), descr, None, propertyClass = Some(summon[ClassTag[T] ].runtimeClass), aliases = aliases, deprecation = deprecation))
434+ publish(Setting(category, prependName(name), descr, None, propertyClass = Some(classTag[T ].runtimeClass), aliases = aliases, deprecation = deprecation))
416435
417436 end SettingGroup
418437end Settings
0 commit comments