Skip to content

Commit

Permalink
Look for 'latest' in toolkit CLI flags
Browse files Browse the repository at this point in the history
Test toolkit default and warnings for latest

Correctly treat both aliases for typelevel toolkit

Add an error about Scala 2.12 not supported for toolkits

Update tests for default of known toolkits

Test actionable diagnostics on deprecated directives for BSP

Update FixTests.scala
  • Loading branch information
MaciejG604 committed Dec 21, 2023
1 parent a8037d3 commit 6d50e41
Show file tree
Hide file tree
Showing 13 changed files with 396 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,12 @@ object WarningMessages {
"Script file named 'main.sc' detected, keep in mind that accessing it from other scripts is impossible due to a clash of `main` symbols"

def deprecatedWarning(old: String, `new`: String) =
s"Using `$old` is deprecated, use `${`new`}` instead"
s"Using '$old' is deprecated, use '${`new`}' instead"

def deprecatedToolkitLatest(updatedValue: String = "") =
if updatedValue.isEmpty then
"""Using 'latest' for toolkit is deprecated, use 'default' to get more stable behaviour"""
else
s"""Using 'latest' for toolkit is deprecated, use 'default' to get more stable behaviour:
| $updatedValue""".stripMargin
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package scala.build.preprocessing
import scala.build.Logger
import scala.build.errors.Diagnostic.TextEdit
import scala.build.internal.Constants
import scala.build.internal.util.WarningMessages.deprecatedWarning
import scala.build.internal.util.WarningMessages.{deprecatedToolkitLatest, deprecatedWarning}
import scala.build.preprocessing.directives.{
DirectiveHandler,
DirectiveUtil,
Expand Down Expand Up @@ -58,18 +58,18 @@ object DeprecatedDirectives {
DirectiveTemplate(
allKeysFrom(directives.Toolkit.handler),
Some(Seq("latest"))
) -> valueReplacement("default")(deprecatedWarning("latest", "default")),
) -> valueReplacement("default")(deprecatedToolkitLatest()),
DirectiveTemplate(
allKeysFrom(directives.Toolkit.handler),
Some(Seq(s"${Toolkit.typelevel}:latest"))
) -> valueReplacement(s"${Toolkit.typelevel}:default")(
deprecatedWarning("latest", "default")
deprecatedToolkitLatest()
),
DirectiveTemplate(
allKeysFrom(directives.Toolkit.handler),
Some(Seq(s"${Constants.typelevelOrganization}:latest"))
) -> valueReplacement(s"${Toolkit.typelevel}:default")(
deprecatedWarning("latest", "default")
deprecatedToolkitLatest()
)
)

Expand Down
24 changes: 24 additions & 0 deletions modules/build/src/test/scala/scala/build/tests/BuildTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -951,4 +951,28 @@ abstract class BuildTests(server: Boolean) extends TestUtil.ScalaCliBuildSuite {
expect(maybeBuild.exists(_.success))
}
}

for (dirValue <- Seq("default", "typelevel:default"))
test(s"error when toolkit $dirValue is used with Scala 2.12") {
val testInputs = TestInputs(
os.rel / "simple.sc" ->
s"""//> using toolkit $dirValue
|
|val n = 2
|println(s"n=$$n")
|""".stripMargin
)

val scala212Options = baseOptions.copy(
scalaOptions = baseOptions.scalaOptions.copy(
scalaVersion = Some(MaybeScalaVersion(Constants.defaultScala212Version)),
scalaBinaryVersion = None
),
scriptOptions = ScriptOptions(Some(true))
)

testInputs.withBuild(scala212Options, buildThreads, bloopConfigOpt) { (_, _, maybeBuild) =>
expect(maybeBuild.left.exists(_.message.startsWith("Toolkits do not support Scala 2.12")))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -393,13 +393,13 @@ final case class SharedOptions(
SharedOptions.parseDependencies(
dependencies.dependency.map(Positioned.none),
ignoreErrors
) ++ SharedOptions.resolveToolkitDependency(withToolkit)
) ++ SharedOptions.resolveToolkitDependency(withToolkit, logger)
),
extraCompileOnlyDependencies = ShadowingSeq.from(
SharedOptions.parseDependencies(
dependencies.compileOnlyDependency.map(Positioned.none),
ignoreErrors
) ++ SharedOptions.resolveToolkitDependency(withToolkit)
) ++ SharedOptions.resolveToolkitDependency(withToolkit, logger)
)
),
internal = bo.InternalOptions(
Expand Down Expand Up @@ -719,8 +719,21 @@ object SharedOptions {
}
}

private def resolveToolkitDependency(toolkitVersion: Option[String])
: Seq[Positioned[AnyDependency]] =
private def resolveToolkitDependency(
toolkitVersion: Option[String],
logger: Logger
): Seq[Positioned[AnyDependency]] = {
if (
toolkitVersion.contains("latest")
|| toolkitVersion.contains(Toolkit.typelevel + ":latest")
|| toolkitVersion.contains(Constants.typelevelOrganization + ":latest")
) logger.message(
WarningMessages.deprecatedToolkitLatest(
s"--toolkit ${toolkitVersion.map(_.replace("latest", "default")).getOrElse("default")}"
)
)

toolkitVersion.toList.map(Positioned.commandLine)
.flatMap(Toolkit.resolveDependenciesWithRequirements(_).map(_.value))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package scala.build.errors

import scala.build.Position

final class ToolkitVersionError(msg: String, positions: Seq[Position])
extends BuildException(msg, positions)
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ final case class Toolkit(
object Toolkit {
val typelevel = "typelevel"

object TypelevelToolkit {
def unapply(s: Option[String]): Boolean =
s.contains(typelevel) || s.contains(Constants.typelevelOrganization)
}

/** @param toolkitCoords
* the toolkit coordinates
* @return
Expand All @@ -56,7 +61,7 @@ object Toolkit {
val notDefaultVersion = if rawVersion == "latest" then "latest.release" else rawVersion
val flavor = tokens.dropRight(1).headOption
val (org, v) = flavor match {
case Some(Toolkit.typelevel) => Constants.typelevelOrganization -> {
case TypelevelToolkit() => Constants.typelevelOrganization -> {
if isDefault then Constants.typelevelToolkitDefaultVersion
else notDefaultVersion
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1908,6 +1908,136 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String])
}
}

if (!actualScalaVersion.startsWith("2.12"))
test("actionable diagnostics on deprecated using directives") {
val inputs = TestInputs(
os.rel / "test.sc" ->
"""//> using toolkit latest
|//> using test.toolkit "typelevel:latest"
|
|//> using lib org.typelevel::cats-core:2.6.1
|
|object Test extends App {
| println("Hello")
|}
|""".stripMargin
)

withBsp(inputs, Seq(".", "--actions=false")) { (root, localClient, remoteServer) =>
async {
val buildTargetsResp = await(remoteServer.workspaceBuildTargets().asScala)
val target = {
val targets = buildTargetsResp.getTargets.asScala.map(_.getId).toSeq
expect(targets.length == 2)
extractMainTargets(targets)
}

val targetUri = TestUtil.normalizeUri(target.getUri)
checkTargetUri(root, targetUri)

val targets = List(target).asJava

val compileResp = await {
remoteServer
.buildTargetCompile(new b.CompileParams(targets))
.asScala
}
expect(compileResp.getStatusCode == b.StatusCode.OK)

val diagnosticsParams = {
val diagnostics = localClient.diagnostics()
.filter(_.getReset == false)
expect(diagnostics.size == 3)
val params = diagnostics.head
expect(params.getBuildTarget.getUri == targetUri)
expect(
TestUtil.normalizeUri(params.getTextDocument.getUri) ==
TestUtil.normalizeUri((root / "test.sc").toNIO.toUri.toASCIIString)
)
diagnostics
}

val diagnostics = diagnosticsParams.flatMap(_.getDiagnostics.asScala)
.sortBy(_.getRange().getEnd().getCharacter())

{
checkDiagnostic(
diagnostic = diagnostics.apply(0),
expectedMessage =
"Using 'latest' for toolkit is deprecated, use 'default' to get more stable behaviour",
expectedSeverity = b.DiagnosticSeverity.WARNING,
expectedStartLine = 0,
expectedStartCharacter = 10,
expectedEndLine = 0,
expectedEndCharacter = 24
)

checkScalaAction(
diagnostic = diagnostics.apply(0),
expectedActionsSize = 1,
expectedTitle = "Change to: toolkit default",
expectedChanges = 1,
expectedStartLine = 0,
expectedStartCharacter = 10,
expectedEndLine = 0,
expectedEndCharacter = 24,
expectedNewText = "toolkit default"
)
}

{
checkDiagnostic(
diagnostic = diagnostics.apply(1),
expectedMessage =
"Using 'latest' for toolkit is deprecated, use 'default' to get more stable behaviour",
expectedSeverity = b.DiagnosticSeverity.WARNING,
expectedStartLine = 1,
expectedStartCharacter = 10,
expectedEndLine = 1,
expectedEndCharacter = 41
)

checkScalaAction(
diagnostic = diagnostics.apply(1),
expectedActionsSize = 1,
expectedTitle = "Change to: test.toolkit typelevel:default",
expectedChanges = 1,
expectedStartLine = 1,
expectedStartCharacter = 10,
expectedEndLine = 1,
expectedEndCharacter = 41,
expectedNewText = "test.toolkit typelevel:default"
)
}

{
checkDiagnostic(
diagnostic = diagnostics.apply(2),
expectedMessage =
"Using 'lib' is deprecated, use 'dep' instead",
expectedSeverity = b.DiagnosticSeverity.WARNING,
expectedStartLine = 3,
expectedStartCharacter = 10,
expectedEndLine = 3,
expectedEndCharacter = 44
)

checkScalaAction(
diagnostic = diagnostics.apply(2),
expectedActionsSize = 1,
expectedTitle = "Change to: dep org.typelevel::cats-core:2.6.1",
expectedChanges = 1,
expectedStartLine = 3,
expectedStartCharacter = 10,
expectedEndLine = 3,
expectedEndCharacter = 44,
expectedNewText = "dep org.typelevel::cats-core:2.6.1"
)
}
}
}
}

private def checkIfBloopProjectIsInitialised(
root: os.Path,
buildTargetsResp: b.WorkspaceBuildTargetsResult
Expand Down Expand Up @@ -1969,6 +2099,45 @@ abstract class BspTestDefinitions(val scalaVersionOpt: Option[String])
expect(diagnostic.getSource == es)
}

private def checkScalaAction(
diagnostic: b.Diagnostic,
expectedActionsSize: Int,
expectedTitle: String,
expectedChanges: Int,
expectedStartLine: Int,
expectedStartCharacter: Int,
expectedEndLine: Int,
expectedEndCharacter: Int,
expectedNewText: String
) = {
expect(diagnostic.getDataKind == "scala")

val gson = new com.google.gson.Gson()

val scalaDiagnostic: b.ScalaDiagnostic = gson.fromJson(
diagnostic.getData.toString,
classOf[b.ScalaDiagnostic]
)

val actions = scalaDiagnostic.getActions.asScala

expect(actions.size == expectedActionsSize)

val action = actions.head
expect(action.getTitle == expectedTitle)

val edit = action.getEdit
expect(edit.getChanges.asScala.size == expectedChanges)
val change = edit.getChanges.asScala.head

val expectedRange = new b.Range(
new b.Position(expectedStartLine, expectedStartCharacter),
new b.Position(expectedEndLine, expectedEndCharacter)
)
expect(change.getRange == expectedRange)
expect(change.getNewText == expectedNewText)
}

private def extractWorkspaceReloadResponse(workspaceReloadResult: AnyRef): Option[ResponseError] =
workspaceReloadResult match {
case gsonMap: LinkedTreeMap[?, ?] if !gsonMap.isEmpty =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class FixTests extends ScalaCliSuite {
|}
|""".stripMargin,
os.rel / projectFileName ->
s"""//> using lib "com.lihaoyi::pprint:0.6.6"
s"""//> using deps "com.lihaoyi::pprint:0.6.6"
|""".stripMargin
)

Expand Down Expand Up @@ -85,7 +85,7 @@ class FixTests extends ScalaCliSuite {
|println(os.pwd)
|""".stripMargin,
os.rel / projectFileName ->
s"""//> using lib "com.lihaoyi::pprint:0.6.6"
s"""//> using deps "com.lihaoyi::pprint:0.6.6"
|""".stripMargin
)

Expand Down Expand Up @@ -162,7 +162,7 @@ class FixTests extends ScalaCliSuite {
|}
|""".stripMargin,
os.rel / projectFileName ->
s"""//> using lib com.lihaoyi::pprint:0.6.6
s"""//> using deps com.lihaoyi::pprint:0.6.6
|""".stripMargin
)

Expand Down Expand Up @@ -289,7 +289,7 @@ class FixTests extends ScalaCliSuite {
|}
|""".stripMargin,
os.rel / projectFileName ->
s"""//> using lib com.lihaoyi::pprint:0.6.6
s"""//> using deps com.lihaoyi::pprint:0.6.6
|
|//> using publish.ci.password env:PUBLISH_PASSWORD
|//> using publish.ci.secretKey env:PUBLISH_SECRET_KEY
Expand All @@ -305,7 +305,7 @@ class FixTests extends ScalaCliSuite {
"fix",
".",
"--script-snippet",
"//> using toolkit latest",
"//> using toolkit default",
"-v",
"-v",
extraOptions
Expand Down Expand Up @@ -341,7 +341,7 @@ class FixTests extends ScalaCliSuite {
|//> using options "-Werror"
|//> using files "$includePath"
|//> using objectWrapper
|//> using toolkit "latest"
|//> using toolkit "default"
|//> using dependency "com.lihaoyi::os-lib:0.9.1" "com.lihaoyi::pprint:0.6.6"
|
|//> using publish.ci.password "env:PUBLISH_PASSWORD"
Expand Down
Loading

0 comments on commit 6d50e41

Please sign in to comment.