Skip to content

Commit 22626c7

Browse files
Merge pull request #1409 from bjaglin/2136
support parsing scala 2.13.6 / 2.12.14 -Xsource:3 dialect
2 parents 67a68c4 + 72c61bd commit 22626c7

File tree

8 files changed

+65
-28
lines changed

8 files changed

+65
-28
lines changed

project/Mima.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ object Mima {
3434
ProblemFilters.exclude[Problem]("scalafix.testkit.SemanticRuleSuite.*"),
3535
ProblemFilters.exclude[MissingClassProblem]("scalafix.testkit.SyntacticRuleSuite$"),
3636
ProblemFilters.exclude[Problem]("scalafix.testkit.SyntacticRuleSuite"),
37-
ProblemFilters.exclude[ReversedMissingMethodProblem]("scalafix.interfaces.ScalafixArguments.withSemanticdbTargetroots")
37+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scalafix.interfaces.ScalafixArguments.withSemanticdbTargetroots"),
38+
ProblemFilters.exclude[IncompatibleMethTypeProblem]("scalafix.testkit.RuleTest.fromPath") // private[scalafix]
3839
)
3940
}
4041
}

scalafix-cli/src/main/scala/scalafix/internal/v1/Args.scala

+19-8
Original file line numberDiff line numberDiff line change
@@ -335,10 +335,18 @@ case class Args(
335335
}
336336
}
337337

338-
def configureScalaVersion(
338+
def sourceScalaVersion: Option[ScalaVersion] =
339+
extractLastScalacOption("-Xsource:").flatMap(ScalaVersion.from(_).toOption)
340+
341+
def configureScalaVersions(
339342
conf: ScalafixConfig
340343
): Configured[ScalafixConfig] = {
341-
Configured.Ok(conf.copy(scalaVersion = scalaVersion))
344+
Configured.ok(
345+
conf.copy(
346+
scalaVersion = scalaVersion,
347+
sourceScalaVersion = sourceScalaVersion
348+
)
349+
)
342350
}
343351

344352
def configuredSourceroot: Configured[AbsolutePath] = {
@@ -369,16 +377,19 @@ case class Args(
369377
def classLoader: ClassLoader =
370378
ClasspathOps.toOrphanClassLoader(validatedClasspath)
371379

380+
private def extractLastScalacOption(flag: String) = {
381+
scalacOptions
382+
.filter(_.startsWith(flag))
383+
.lastOption
384+
.map(_.stripPrefix(flag))
385+
}
386+
372387
private def semanticdbOption(
373388
settingInScala2: String,
374389
settingInScala3Opt: Option[String]
375390
): Option[String] = {
376391
if (scalaVersion.isScala2) {
377-
val flag = s"-P:semanticdb:$settingInScala2:"
378-
scalacOptions
379-
.filter(_.startsWith(flag))
380-
.lastOption
381-
.map(_.stripPrefix(flag))
392+
extractLastScalacOption(s"-P:semanticdb:$settingInScala2:")
382393
} else {
383394
settingInScala3Opt.flatMap { settingInScala3 =>
384395
scalacOptions
@@ -409,7 +420,7 @@ case class Args(
409420
configuredRules(base, scalafixConfig) |@|
410421
resolvedPathReplace |@|
411422
configuredDiffDisable |@|
412-
configureScalaVersion(scalafixConfig)
423+
configureScalaVersions(scalafixConfig)
413424
).map {
414425
case (
415426
((((root, symtab), rulez), pathReplace), diffDisable),

scalafix-core/src/main/scala/scalafix/internal/config/ScalaVersion.scala

+8-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import scala.util.Success
55
import scala.util.Try
66

77
import scala.meta.Dialect
8+
import scala.meta.dialects
89

910
import scalafix.internal.config.ScalaVersion._
1011

@@ -16,10 +17,13 @@ sealed trait ScalaVersion {
1617
val major: MajorVersion
1718
val minor: Option[Int]
1819
val patch: Option[Int]
19-
val dialect: Dialect = major match {
20-
case ScalaVersion.Major.Scala2 => scala.meta.dialects.Scala213
21-
case ScalaVersion.Major.Scala3 => scala.meta.dialects.Scala3
22-
}
20+
21+
def dialect(sourceScalaVersion: Option[ScalaVersion]): Dialect =
22+
(major, sourceScalaVersion.map(_.major)) match {
23+
case (Major.Scala3, _) => dialects.Scala3
24+
case (Major.Scala2, Some(Major.Scala3)) => dialects.Scala213Source3
25+
case (Major.Scala2, _) => dialects.Scala213
26+
}
2327

2428
def isScala2: Boolean = major == Major.Scala2
2529
def isScala3: Boolean = major == Major.Scala3

scalafix-core/src/main/scala/scalafix/internal/config/ScalafixConfig.scala

+2-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ case class ScalafixConfig(
1515
reporter: ScalafixReporter = ScalafixReporter.default,
1616
patches: ConfigRulePatches = ConfigRulePatches.default,
1717
scalaVersion: ScalaVersion = ScalaVersion.scala2,
18+
sourceScalaVersion: Option[ScalaVersion] = None,
1819
lint: LintConfig = LintConfig.default
1920
) {
20-
val dialect = scalaVersion.dialect
21+
val dialect = scalaVersion.dialect(sourceScalaVersion)
2122

2223
def dialectForFile(path: String): Dialect =
2324
if (path.endsWith(".sbt")) DefaultSbtDialect
@@ -41,7 +42,5 @@ object ScalafixConfig {
4142
implicit lazy val ScalafixConfigDecoder: ConfDecoder[ScalafixConfig] =
4243
decoder(default)
4344

44-
val Scala2: Dialect = scala.meta.dialects.Scala213
45-
val Scala3: Dialect = scala.meta.dialects.Scala3
4645
val DefaultSbtDialect: Dialect = scala.meta.dialects.Sbt1
4746
}

scalafix-reflect/src/main/scala/scalafix/internal/reflect/RuleInstrumentation.scala

+1-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import scala.meta._
77
import metaconfig.ConfError
88
import metaconfig.Configured
99
import scalafix.internal.config.MetaconfigOps._
10-
import scalafix.internal.config.ScalafixConfig.Scala2
1110

1211
object RuleInstrumentation {
1312

@@ -39,7 +38,7 @@ object RuleInstrumentation {
3938
case _ => false
4039
}
4140
}
42-
(Scala2, code).parse[Source] match {
41+
(dialects.Scala213, code).parse[Source] match {
4342
case parsers.Parsed.Error(pos, msg, details) =>
4443
ConfError.parseError(pos.toMetaconfig, msg).notOk
4544
case parsers.Parsed.Success(ast) =>

scalafix-testkit/src/main/scala/scalafix/testkit/AbstractSemanticRuleSuite.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ abstract class AbstractSemanticRuleSuite(
109109
val classLoader = ClasspathOps.toClassLoader(args.validatedClasspath)
110110
val tests = TestkitPath.fromProperties(props)
111111
tests.map { test =>
112-
RuleTest.fromPath(props, test, classLoader, symtab)
112+
RuleTest.fromPath(args, test, classLoader, symtab)
113113
}
114114
}
115115
def runAllTests(): Unit = {

scalafix-testkit/src/main/scala/scalafix/testkit/RuleTest.scala

+6-9
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import metaconfig.internal.ConfGet
88
import metaconfig.typesafeconfig.typesafeConfigMetaconfigParser
99
import scalafix.internal.config.ScalafixConfig
1010
import scalafix.internal.diff.DiffDisable
11+
import scalafix.internal.v1.Args
1112
import scalafix.internal.v1.LazyValue
1213
import scalafix.internal.v1.Rules
1314
import scalafix.v1
@@ -21,14 +22,14 @@ final class RuleTest(
2122

2223
object RuleTest {
2324
private[scalafix] def fromPath(
24-
props: TestkitProperties,
25+
args: Args,
2526
test: TestkitPath,
2627
classLoader: ClassLoader,
2728
symtab: SymbolTable
2829
): RuleTest = {
2930
val run: () => (Rules, v1.SemanticDocument) = { () =>
3031
val input = test.toInput
31-
val dialect = getDialectFromScalaV(props.scalaVersion)
32+
val dialect = args.scalaVersion.dialect(args.sourceScalaVersion)
3233
val tree = dialect(input).parse[Source].get
3334
val comment = SemanticRuleSuite.findTestkitComment(tree.tokens)
3435
val syntax = comment.syntax.stripPrefix("/*").stripSuffix("*/")
@@ -55,17 +56,13 @@ object RuleTest {
5556
.getOrElse(Conf.Lst(Nil))
5657
val config = Configuration()
5758
.withConf(conf)
58-
.withScalaVersion(props.scalaVersion)
59-
.withScalacOptions(props.scalacOptions)
60-
.withScalacClasspath(props.inputClasspath.entries)
59+
.withScalaVersion(args.scalaVersion.value)
60+
.withScalacOptions(args.scalacOptions)
61+
.withScalacClasspath(args.classpath.entries)
6162
val rules = decoder.read(rulesConf).get.withConfiguration(config).get
6263
(rules, sdoc)
6364
}
6465

6566
new RuleTest(test, run)
6667
}
67-
68-
def getDialectFromScalaV(scalaV: String): Dialect =
69-
if (scalaV.startsWith("3")) ScalafixConfig.Scala3
70-
else ScalafixConfig.Scala2
7168
}

scalafix-tests/unit/src/test/scala/scalafix/tests/interfaces/ScalafixArgumentsSuite.scala

+26
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,32 @@ class ScalafixArgumentsSuite extends AnyFunSuite with DiffAssertions {
345345
assert(fileEvaluation.getErrorMessage.get.contains("SemanticDB not found"))
346346
}
347347

348+
test("Scala2 source with Scala3 syntax can only be parsed with -Xsource:3") {
349+
val cwd = StringFS
350+
.string2dir(
351+
s"""|/src/Scala2Source3.scala
352+
|open class Scala2Source3""".stripMargin,
353+
charset
354+
)
355+
.toNIO
356+
val src = cwd.resolve("src")
357+
val args = api
358+
.withScalaVersion("2.13.6")
359+
.withRules(List("DisableSyntax").asJava)
360+
.withSourceroot(src)
361+
362+
val withoutSource3 = args.evaluate().getFileEvaluations.head
363+
assert(!withoutSource3.isSuccessful)
364+
365+
val withSource3 =
366+
args
367+
.withScalacOptions(Collections.singletonList("-Xsource:3"))
368+
.evaluate()
369+
.getFileEvaluations
370+
.head
371+
assert(withSource3.isSuccessful)
372+
}
373+
348374
test("Source with Scala3 syntax can be parsed with dialect Scala3") {
349375
val content =
350376
"""|

0 commit comments

Comments
 (0)