Skip to content

Commit a73a40d

Browse files
committed
Make arguments being lazy evaluated to avoid side effects
1 parent 70eeb2b commit a73a40d

File tree

1 file changed

+25
-15
lines changed

1 file changed

+25
-15
lines changed

compiler/src/dotty/tools/dotc/config/Settings.scala

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,23 @@ object Settings:
119119
def tryToSet(state: ArgsSummary): ArgsSummary =
120120
val ArgsSummary(sstate, arg :: args, errors, warnings) = state: @unchecked
121121

122-
def update(value: Any, args: List[String]): ArgsSummary =
122+
/**
123+
* Updates the value in state
124+
*
125+
* @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
127+
* @param args remaining arguments to process
128+
* @return new argumment state
129+
*/
130+
def update(getValue: => Any, argStringValue: String, args: List[String]): ArgsSummary =
123131
deprecation match
124132
case Some(Deprecation(msg, replacedBy)) =>
125133
val deprecatedMsg = s"Option $name is deprecated: $msg"
126-
if argValRest.isBlank() then state.deprecated(replacedBy, deprecatedMsg)
127-
else state.deprecated(s"$replacedBy:$argValRest", deprecatedMsg)
134+
if argStringValue.isEmpty then state.deprecated(replacedBy, deprecatedMsg)
135+
else state.deprecated(s"$replacedBy:$argStringValue", deprecatedMsg)
128136

129137
case None =>
138+
val value = getValue
130139
var dangers = warnings
131140
val valueNew =
132141
if sstate.wasChanged(idx) && isMultivalue then
@@ -154,16 +163,16 @@ object Settings:
154163
if ignoreInvalidArgs then state.warn(msg + ", the tag was ignored") else state.fail(msg)
155164

156165
def setBoolean(argValue: String, args: List[String]) =
157-
if argValue.equalsIgnoreCase("true") || argValue.isEmpty then update(true, args)
158-
else if argValue.equalsIgnoreCase("false") then update(false, args)
166+
if argValue.equalsIgnoreCase("true") || argValue.isEmpty then update(true, argValue, args)
167+
else if argValue.equalsIgnoreCase("false") then update(false, argValue, args)
159168
else state.fail(s"$argValue is not a valid choice for boolean setting $name")
160169

161170
def setString(argValue: String, args: List[String]) =
162171
choices match
163172
case Some(xs) if !xs.contains(argValue) =>
164173
state.fail(s"$argValue is not a valid choice for $name")
165174
case _ =>
166-
update(argValue, args)
175+
update(argValue, argValue, args)
167176

168177
def setInt(argValue: String, args: List[String]) =
169178
argValue.toIntOption.map: intValue =>
@@ -173,7 +182,7 @@ object Settings:
173182
case Some(xs) if !xs.contains(intValue) =>
174183
state.fail(s"$argValue is not a valid choice for $name")
175184
case _ =>
176-
update(intValue, args)
185+
update(intValue, argValue, args)
177186
.getOrElse:
178187
state.fail(s"$argValue is not an integer argument for $name")
179188

@@ -183,28 +192,29 @@ object Settings:
183192
if (!isJar && !path.isDirectory) then
184193
state.fail(s"'$argValue' does not exist or is not a directory or .jar file")
185194
else
186-
val output = if (isJar) JarArchive.create(path) else new PlainDirectory(path)
187-
update(output, args)
195+
/* Side effect, do not change this method to evaluate eagerly */
196+
def output = if (isJar) JarArchive.create(path) else new PlainDirectory(path)
197+
update(output, argValue, args)
188198

189199
def setVersion(argValue: String, args: List[String]) =
190200
ScalaVersion.parse(argValue) match
191-
case Success(v) => update(v, args)
201+
case Success(v) => update(v, argValue, args)
192202
case Failure(ex) => state.fail(ex.getMessage)
193203

194-
def appendList(strings: List[String], args: List[String]) =
204+
def appendList(strings: List[String], argValue: String, args: List[String]) =
195205
choices match
196206
case Some(valid) => strings.filterNot(valid.contains) match
197-
case Nil => update(strings, args)
207+
case Nil => update(strings, argValue, args)
198208
case invalid => invalidChoices(invalid)
199-
case _ => update(strings, args)
209+
case _ => update(strings, argValue, args)
200210

201211
def doSet(argRest: String) =
202212
((summon[ClassTag[T]], args): @unchecked) match
203213
case (BooleanTag, _) =>
204214
if sstate.wasChanged(idx) && preferPrevious then ignoreValue(args)
205215
else setBoolean(argRest, args)
206216
case (OptionTag, _) =>
207-
update(Some(propertyClass.get.getConstructor().newInstance()), args)
217+
update(Some(propertyClass.get.getConstructor().newInstance()), "", args)
208218
case (ct, args) =>
209219
val argInArgRest = !argRest.isEmpty || legacyArgs
210220
val argAfterParam = !argInArgRest && args.nonEmpty && (ct == IntTag || !args.head.startsWith("-"))
@@ -217,7 +227,7 @@ object Settings:
217227
def doSetArg(arg: String, argsLeft: List[String]) = summon[ClassTag[T]] match
218228
case ListTag =>
219229
val strings = arg.split(",").toList
220-
appendList(strings, argsLeft)
230+
appendList(strings, arg, argsLeft)
221231
case StringTag =>
222232
setString(arg, argsLeft)
223233
case OutputTag =>

0 commit comments

Comments
 (0)