@@ -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