diff --git a/build.sbt b/build.sbt index 58a2096d7..d8424a675 100644 --- a/build.sbt +++ b/build.sbt @@ -84,7 +84,7 @@ lazy val core = projectMatrix } ) .defaultAxes(VirtualAxis.jvm) - .jvmPlatform(buildScalaVersions :+ scala3, Seq(), p => p) + .jvmPlatform(buildScalaVersions) .enablePlugins(BuildInfoPlugin) lazy val rules = projectMatrix @@ -105,7 +105,7 @@ lazy val rules = projectMatrix } ) .defaultAxes(VirtualAxis.jvm) - .jvmPlatform(buildScalaVersions :+ scala3, Seq(), p => p) + .jvmPlatform(buildScalaVersions) .dependsOn(core) .enablePlugins(BuildInfoPlugin) @@ -114,10 +114,18 @@ lazy val reflect = projectMatrix .settings( moduleName := "scalafix-reflect", isFullCrossVersion, - libraryDependencies ++= Seq( - "org.scala-lang" % "scala-compiler" % scalaVersion.value, - "org.scala-lang" % "scala-reflect" % scalaVersion.value - ) + libraryDependencies ++= { + if (!isScala3.value) + List( + "org.scala-lang" % "scala-compiler" % scalaVersion.value, + "org.scala-lang" % "scala-reflect" % scalaVersion.value + ) + else + List( + "org.scala-lang" %% "scala3-compiler" % scalaVersion.value, + "org.scala-lang" %% "scala3-library" % scalaVersion.value + ) + } ) .defaultAxes(VirtualAxis.jvm) .jvmPlatform(buildScalaVersions) @@ -129,13 +137,18 @@ lazy val cli = projectMatrix moduleName := "scalafix-cli", isFullCrossVersion, libraryDependencies ++= Seq( - java8Compat, nailgunServer, jgit, commonText ), libraryDependencies ++= { - if (isScala211.value) Seq() + if (!isScala3.value) + Seq(java8Compat) + else + Seq() + }, + libraryDependencies ++= { + if (isScala211.value || isScala3.value) Seq() else Seq( // metaconfig 0.10.0 shaded pprint but rules built with an old @@ -160,7 +173,7 @@ lazy val testsShared = projectMatrix coverageEnabled := false ) .defaultAxes(VirtualAxis.jvm) - .jvmPlatform(testTargetScalaVersions) + .jvmPlatform(buildScalaVersions) .disablePlugins(ScalafixPlugin) lazy val testsInput = projectMatrix @@ -176,7 +189,7 @@ lazy val testsInput = projectMatrix coverageEnabled := false ) .defaultAxes(VirtualAxis.jvm) - .jvmPlatform(testTargetScalaVersions) + .jvmPlatform(buildScalaVersions) .disablePlugins(ScalafixPlugin) lazy val testsOutput = projectMatrix @@ -188,7 +201,7 @@ lazy val testsOutput = projectMatrix coverageEnabled := false ) .defaultAxes(VirtualAxis.jvm) - .jvmPlatform(testTargetScalaVersions) + .jvmPlatform(buildScalaVersions) .disablePlugins(ScalafixPlugin) lazy val testkit = projectMatrix @@ -196,10 +209,8 @@ lazy val testkit = projectMatrix .settings( moduleName := "scalafix-testkit", isFullCrossVersion, - libraryDependencies ++= Seq( - googleDiff, - scalatest - ) + libraryDependencies += googleDiff, + libraryDependencies += scalatestDep.value ) .defaultAxes(VirtualAxis.jvm) .jvmPlatform(buildScalaVersions) @@ -211,18 +222,32 @@ lazy val unit = projectMatrix noPublishAndNoMima, // Change working directory to match when `fork := false`. Test / baseDirectory := (ThisBuild / baseDirectory).value, + // Prevent issues with scalatest serialization + Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat, javaOptions := Nil, - testFrameworks += new TestFramework("munit.Framework"), - buildInfoPackage := "scalafix.tests", - buildInfoObject := "BuildInfo", libraryDependencies ++= List( jgit, - coursier, - scalatest.withRevision( - "3.2.13" - ), // make sure testkit clients can use recent 3.x versions - scalametaTeskit + munit, + scalatest.withRevision(scalatestLatestV) ), + libraryDependencies ++= { + if (!isScala3.value) { + List( + coursier, + scalametaTeskit + ) + } else { + // exclude _2.13 artifacts that have their _3 counterpart in the classpath + List( + coursier + .exclude("org.scala-lang.modules", "scala-xml_2.13"), + scalametaTeskit + .exclude("com.lihaoyi", "sourcecode_2.13") + .exclude("org.scala-lang.modules", "scala-collection-compat_2.13") + .exclude("org.scalameta", "munit_2.13") + ) + } + }, Compile / compile / compileInputs := { (Compile / compile / compileInputs) .dependsOn( @@ -277,8 +302,11 @@ lazy val unit = projectMatrix IO.write(props, "Input data for scalafix testkit", out) List(out) }, + buildInfoPackage := "scalafix.tests", + buildInfoObject := "BuildInfo", buildInfoKeys := Seq[BuildInfoKey]( "scalametaVersion" -> scalametaV, + "scalaVersion" -> scalaVersion.value, "baseDirectory" -> (ThisBuild / baseDirectory).value, "unitResourceDirectory" -> (Compile / resourceDirectory).value, @@ -313,6 +341,11 @@ lazy val unit = projectMatrix } ) .defaultAxes(VirtualAxis.jvm) + .jvmPlatform( + scalaVersions = Seq(scala3), + axisValues = Seq(TargetAxis(scala3)), + settings = Seq() + ) .jvmPlatform( scalaVersions = Seq(scala212), axisValues = Seq(TargetAxis(scala3)), diff --git a/project/Dependencies.scala b/project/Dependencies.scala index f00011194..8cc0c5fc2 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -12,7 +12,7 @@ object Dependencies { val scala213 = "2.13.10" val scala3 = "3.1.3" - val buildScalaVersions = Seq(scala211, scala212, scala213) + val buildScalaVersions = Seq(scala211, scala212, scala213, scala3) val testTargetScalaVersions = Seq(scala211, scala212, scala213, scala3) // we support 3 last binary versions of scala212 and scala213 @@ -34,12 +34,15 @@ object Dependencies { val scalaXmlV = "2.1.0" val scalaXml211V = "1.3.0" // scala-xml stops publishing for scala 2.11 val scalametaV = "4.6.0" - val scalatestV = "3.0.8" // don't bump, to avoid forcing breaking changes on clients via eviction + val scalatestMinV = "3.0.8" // don't bump, to avoid forcing breaking changes on clients via eviction + val scalatestLatestV = "3.2.13" + val munitV = "0.7.29" val bijectionCore = "com.twitter" %% "bijection-core" % bijectionCoreV val collectionCompat = "org.scala-lang.modules" %% "scala-collection-compat" % collectionCompatV val commonText = "org.apache.commons" % "commons-text" % commontTextV - val coursier = "io.get-coursier" %% "coursier" % coursierV + val coursier = ("io.get-coursier" %% "coursier" % coursierV) + .cross(CrossVersion.for3Use2_13) val coursierInterfaces = "io.get-coursier" % "interface" % coursierInterfaceV val googleDiff = "com.googlecode.java-diff-utils" % "diffutils" % googleDiffV val java8Compat = "org.scala-lang.modules" %% "scala-java8-compat" % java8CompatV @@ -56,8 +59,10 @@ object Dependencies { // https://github.com/scalameta/scalameta/issues/2485 val scalameta = ("org.scalameta" %% "scalameta" % scalametaV) .cross(CrossVersion.for3Use2_13) - val scalametaTeskit = "org.scalameta" %% "testkit" % scalametaV - val scalatest = "org.scalatest" %% "scalatest" % scalatestV + val scalametaTeskit = ("org.scalameta" %% "testkit" % scalametaV) + .cross(CrossVersion.for3Use2_13) + val scalatest = "org.scalatest" %% "scalatest" % scalatestMinV + val munit = "org.scalameta" %% "munit" % munitV val semanticdbScalacCore = "org.scalameta" % "semanticdb-scalac-core" % scalametaV cross CrossVersion.full private def previousVersions(scalaVersion: String): List[String] = { diff --git a/project/ScalafixBuild.scala b/project/ScalafixBuild.scala index 893813291..2a384de45 100644 --- a/project/ScalafixBuild.scala +++ b/project/ScalafixBuild.scala @@ -121,6 +121,11 @@ object ScalafixBuild extends AutoPlugin with GhpagesKeys { lazy val buildInfoSettingsForRules: Seq[Def.Setting[_]] = Seq( buildInfoObject := "RulesBuildInfo" ) + + lazy val scalatestDep = Def.setting { + if (isScala3.value) scalatest.withRevision(scalatestLatestV) + else scalatest + } } import autoImport._ @@ -140,8 +145,7 @@ object ScalafixBuild extends AutoPlugin with GhpagesKeys { }, commands += Command.command("ci-3") { s => "unit2_12Target3/test" :: - "core3/compile" :: - "rules3/compile" :: + "unit3Target3/test" :: s }, commands += Command.command("ci-213") { s => @@ -218,8 +222,17 @@ object ScalafixBuild extends AutoPlugin with GhpagesKeys { Compile / doc / scalacOptions ++= scaladocOptions, Compile / unmanagedSourceDirectories ++= { val sourceDir = (Compile / sourceDirectory).value + val scala212PlusSourceDir = "scala-2.12+" + CrossVersion.partialVersion(scalaVersion.value) match { + case Some((2, n)) if n >= 12 => Seq(sourceDir / scala212PlusSourceDir) + case Some((3, _)) => Seq(sourceDir / scala212PlusSourceDir) + case _ => Seq() + } + }, + Compile / unmanagedResourceDirectories ++= { + val resourceParentDir = (Compile / resourceDirectory).value.getParentFile CrossVersion.partialVersion(scalaVersion.value) match { - case Some((2, n)) if n >= 12 => Seq(sourceDir / "scala-2.12+") + case Some((major, _)) => Seq(resourceParentDir / s"resources-${major}") case _ => Seq() } }, diff --git a/scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixFileEvaluationImpl.scala b/scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixFileEvaluationImpl.scala index 8e72226a9..62515ab13 100644 --- a/scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixFileEvaluationImpl.scala +++ b/scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixFileEvaluationImpl.scala @@ -143,7 +143,7 @@ object ScalafixFileEvaluationImpl { ctx: RuleCtx, index: Option[v0.SemanticdbIndex] ): ScalafixFileEvaluationImpl = { - val scalafixPatches = patches.map(ScalafixPatchImpl) + val scalafixPatches = patches.map(ScalafixPatchImpl.apply) ScalafixFileEvaluationImpl( originalPath = originalPath, fixedOpt = fixed, diff --git a/scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixInputImpl.scala b/scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixInputImpl.scala index 4840f7cbc..691d99bc2 100644 --- a/scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixInputImpl.scala +++ b/scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixInputImpl.scala @@ -7,6 +7,7 @@ import java.util.Optional import scala.{meta => m} import scala.meta.inputs.Input +import scala.meta.internal.inputs.XtensionInputSyntaxStructure import scalafix.interfaces.ScalafixInput diff --git a/scalafix-cli/src/main/scala/scalafix/internal/v1/MainOps.scala b/scalafix-cli/src/main/scala/scalafix/internal/v1/MainOps.scala index b322f862c..bac970b01 100644 --- a/scalafix-cli/src/main/scala/scalafix/internal/v1/MainOps.scala +++ b/scalafix-cli/src/main/scala/scalafix/internal/v1/MainOps.scala @@ -19,6 +19,7 @@ import scala.util.control.NoStackTrace import scala.util.control.NonFatal import scala.meta.inputs.Input +import scala.meta.internal.inputs.XtensionInputSyntaxStructure import scala.meta.internal.semanticdb.TextDocument import scala.meta.io.AbsolutePath import scala.meta.parsers.ParseException diff --git a/scalafix-cli/src/main/scala/scalafix/internal/v1/ValidatedArgs.scala b/scalafix-cli/src/main/scala/scalafix/internal/v1/ValidatedArgs.scala index bf7a3f43e..be0a0b784 100644 --- a/scalafix-cli/src/main/scala/scalafix/internal/v1/ValidatedArgs.scala +++ b/scalafix-cli/src/main/scala/scalafix/internal/v1/ValidatedArgs.scala @@ -3,6 +3,7 @@ package scalafix.internal.v1 import scala.meta.AbsolutePath import scala.meta.Input import scala.meta.Source +import scala.meta.internal.inputs.XtensionInputSyntaxStructure import scala.meta.internal.io.FileIO import scala.meta.internal.symtab.SymbolTable import scala.meta.io.RelativePath diff --git a/scalafix-reflect/src/main/scala/scalafix/internal/reflect/RuleCompiler.scala b/scalafix-reflect/src/main/scala-2/scalafix/internal/reflect/RuleCompiler.scala similarity index 58% rename from scalafix-reflect/src/main/scala/scalafix/internal/reflect/RuleCompiler.scala rename to scalafix-reflect/src/main/scala-2/scalafix/internal/reflect/RuleCompiler.scala index 428dc29dd..a414ea48d 100644 --- a/scalafix-reflect/src/main/scala/scalafix/internal/reflect/RuleCompiler.scala +++ b/scalafix-reflect/src/main/scala-2/scalafix/internal/reflect/RuleCompiler.scala @@ -1,17 +1,15 @@ package scalafix.internal.reflect import java.io.File -import java.nio.file.Paths import scala.reflect.internal.util.AbstractFileClassLoader import scala.reflect.internal.util.BatchSourceFile +import scala.reflect.io.Directory +import scala.reflect.io.PlainDirectory import scala.tools.nsc.Global import scala.tools.nsc.Settings -import scala.tools.nsc.io.AbstractFile import scala.tools.nsc.io.VirtualDirectory import scala.tools.nsc.reporters.StoreReporter -import scala.meta.io.AbsolutePath - import metaconfig.ConfError import metaconfig.Configured import metaconfig.Input @@ -19,17 +17,19 @@ import metaconfig.Position class RuleCompiler( classpath: String, - target: AbstractFile = new VirtualDirectory("(memory)", None) + targetDirectory: Option[File] = None ) { + private val output = targetDirectory match { + case Some(file) => new PlainDirectory(new Directory(file)) + case None => new VirtualDirectory("(memory)", None) + } private val settings = new Settings() settings.deprecation.value = true // enable detailed deprecation warnings settings.unchecked.value = true // enable detailed unchecked warnings - settings.outputDirs.setSingleOutput(target) + settings.outputDirs.setSingleOutput(output) settings.classpath.value = classpath lazy val reporter = new StoreReporter private val global = new Global(settings, reporter) - private val classLoader = - new AbstractFileClassLoader(target, this.getClass.getClassLoader) def compile(input: Input): Configured[ClassLoader] = { reporter.reset() @@ -55,33 +55,10 @@ class RuleCompiler( ConfError .fromResults(errors.toSeq) .map(_.notOk) - .getOrElse(Configured.Ok(classLoader)) - } -} -object RuleCompiler { - - def defaultClasspath: String = { - defaultClasspathPaths.mkString(File.pathSeparator) - } - - def defaultClasspathPaths: List[AbsolutePath] = { - val classLoader = ClasspathOps.thisClassLoader - val paths = classLoader.getURLs.iterator.map { u => - if (u.getProtocol.startsWith("bootstrap")) { - import java.nio.file._ - val stream = u.openStream - val tmp = Files.createTempFile("bootstrap-" + u.getPath, ".jar") - try { - Files.copy(stream, tmp, StandardCopyOption.REPLACE_EXISTING) - } finally { - stream.close() - } - AbsolutePath(tmp) - } else { - AbsolutePath(Paths.get(u.toURI)) + .getOrElse { + val classLoader: AbstractFileClassLoader = + new AbstractFileClassLoader(output, this.getClass.getClassLoader) + Configured.Ok(classLoader) } - } - paths.toList } - } diff --git a/scalafix-reflect/src/main/scala-3/scalafix/internal/reflect/RuleCompiler.scala b/scalafix-reflect/src/main/scala-3/scalafix/internal/reflect/RuleCompiler.scala new file mode 100644 index 000000000..e34552bf9 --- /dev/null +++ b/scalafix-reflect/src/main/scala-3/scalafix/internal/reflect/RuleCompiler.scala @@ -0,0 +1,72 @@ +package scalafix.internal.reflect + +import dotty.tools.dotc.Compiler +import dotty.tools.dotc.Run +import dotty.tools.dotc.core.Contexts.FreshContext +import dotty.tools.dotc.util.SourceFile +import dotty.tools.dotc.reporting.StoreReporter +import dotty.tools.dotc.interactive.InteractiveDriver +import dotty.tools.io.AbstractFile +import dotty.tools.io.Directory +import dotty.tools.io.PlainDirectory +import dotty.tools.io.VirtualFile +import dotty.tools.io.VirtualDirectory +import dotty.tools.repl.AbstractFileClassLoader + +import metaconfig.Configured +import metaconfig.Input +import metaconfig.ConfError +import metaconfig.Position + +import java.io.File +class RuleCompiler( + classpath: String, + targetDirectory: Option[File] = None +) { + private val output = targetDirectory match { + case Some(file) => new PlainDirectory(new Directory(file.toPath)) + case None => new VirtualDirectory("(memory)") + } + private val settings = + "-unchecked" :: "-deprecation" :: "-classpath" :: classpath :: Nil + private val driver = new InteractiveDriver(settings) + private val reporter: StoreReporter = new StoreReporter() + private var ctx: FreshContext = driver.currentCtx.fresh + ctx = ctx + .setReporter(reporter) + .setSetting(ctx.settings.outputDir, output) + .setSetting(ctx.settings.classpath, classpath) + + private val compiler: Compiler = new Compiler() + + def compile(input: Input): Configured[ClassLoader] = { + reporter.removeBufferedMessages(using ctx) + val run: Run = compiler.newRun(using ctx) + + val file: AbstractFile = input match { + case Input.File(path, _) => AbstractFile.getFile(input.path) + case Input.VirtualFile(path, _) => + VirtualFile(input.path, input.text.getBytes()) + case _ => throw RuntimeException("Invalid Input file") + } + + run.compileSources( + List(new SourceFile(file, input.chars)) + ) + + if (reporter.allErrors.isEmpty) { + val classLoader: AbstractFileClassLoader = + new AbstractFileClassLoader(output, this.getClass.getClassLoader) + Configured.Ok(classLoader) + } else { + val lastError = + "Error compiling rule(s) from source using Scala 3 compiler; " + + "to use the Scala 2.x compiler instead, use the corresponding " + + "scalafix-cli artifact or force scalafixScalaBinaryVersion " + + "to 2.x in your build tool" + val errors = (reporter.allErrors.map(_.getMessage) :+ lastError) + ConfError.apply(errors.map(ConfError.message)).map(_.notOk).get + } + + } +} diff --git a/scalafix-reflect/src/main/scala/scalafix/internal/reflect/RuleCompilerClasspath.scala b/scalafix-reflect/src/main/scala/scalafix/internal/reflect/RuleCompilerClasspath.scala new file mode 100644 index 000000000..7b0816765 --- /dev/null +++ b/scalafix-reflect/src/main/scala/scalafix/internal/reflect/RuleCompilerClasspath.scala @@ -0,0 +1,33 @@ +package scalafix.internal.reflect + +import java.io.File +import java.nio.file.Paths + +import scala.meta.io.AbsolutePath + +object RuleCompilerClasspath { + + def defaultClasspath: String = { + defaultClasspathPaths.mkString(File.pathSeparator) + } + + def defaultClasspathPaths: List[AbsolutePath] = { + val classLoader = ClasspathOps.thisClassLoader + val paths = classLoader.getURLs.iterator.map { u => + if (u.getProtocol.startsWith("bootstrap")) { + import java.nio.file._ + val stream = u.openStream + val tmp = Files.createTempFile("bootstrap-" + u.getPath, ".jar") + try { + Files.copy(stream, tmp, StandardCopyOption.REPLACE_EXISTING) + } finally { + stream.close() + } + AbsolutePath(tmp) + } else { + AbsolutePath(Paths.get(u.toURI)) + } + } + paths.toList + } +} diff --git a/scalafix-reflect/src/main/scala/scalafix/internal/reflect/RuleInstrumentation.scala b/scalafix-reflect/src/main/scala/scalafix/internal/reflect/RuleInstrumentation.scala index 0134fc8a4..2d5f420a6 100644 --- a/scalafix-reflect/src/main/scala/scalafix/internal/reflect/RuleInstrumentation.scala +++ b/scalafix-reflect/src/main/scala/scalafix/internal/reflect/RuleInstrumentation.scala @@ -15,29 +15,63 @@ object RuleInstrumentation { def unapply(templ: Template): Boolean = templ match { // v0 - case Template(_, init"Rewrite" :: _, _, _) => true - case Template(_, init"Rule($_)" :: _, _, _) => true - case Template(_, init"SemanticRewrite($_)" :: _, _, _) => true - case Template(_, init"SemanticRule($_, $_)" :: _, _, _) => true + case Template(_, Init(Type.Name("Rewrite"), _, Nil) :: _, _, _) => true + case Template( + _, + Init(Type.Name("Rule"), _, List(List(_))) :: _, + _, + _ + ) => + true + case Template( + _, + Init(Type.Name("SemanticRewrite"), _, List(List(_))) :: _, + _, + _ + ) => + true + case Template( + _, + Init(Type.Name("SemanticRule"), _, List(List(_, _))) :: _, + _, + _ + ) => + true // v1 - case Template(_, init"SemanticRule($_)" :: _, _, _) => true - case Template(_, init"v1.SemanticRule($_)" :: _, _, _) => true - case Template(_, init"SyntacticRule($_)" :: _, _, _) => true - case Template(_, init"v1.SyntacticRule($_)" :: _, _, _) => true + case Template( + _, + Init(Type.Name("SemanticRule"), _, List(List(_))) :: _, + _, + _ + ) => + true + case Template( + _, + Init(Type.Name("v1.SemanticRule"), _, List(List(_))) :: _, + _, + _ + ) => + true + case Template( + _, + Init(Type.Name("SyntacticRule"), _, List(List(_))) :: _, + _, + _ + ) => + true + case Template( + _, + Init(Type.Name("v1.SyntacticRule"), _, List(List(_))) :: _, + _, + _ + ) => + true case _ => false } } - object LambdaRule { - def unapply(arg: Term): Boolean = arg match { - case q"Rule.syntactic($_)" => true - case q"Rule.semantic($_)" => true - case q"Rule.syntactic($_)" => true - case q"Rule.semantic($_)" => true - case _ => false - } - } + (dialects.Scala213, code).parse[Source] match { case parsers.Parsed.Error(pos, msg, details) => ConfError.parseError(pos.toMetaconfig, msg).notOk diff --git a/scalafix-reflect/src/main/scala/scalafix/internal/reflect/ScalafixToolbox.scala b/scalafix-reflect/src/main/scala/scalafix/internal/reflect/ScalafixToolbox.scala index 923322c69..e5b97aa61 100644 --- a/scalafix-reflect/src/main/scala/scalafix/internal/reflect/ScalafixToolbox.scala +++ b/scalafix-reflect/src/main/scala/scalafix/internal/reflect/ScalafixToolbox.scala @@ -21,7 +21,7 @@ class ScalafixToolbox { new function.Function[String, RuleCompiler] { override def apply(classpath: String) = new RuleCompiler( - classpath + File.pathSeparator + RuleCompiler.defaultClasspath + classpath + File.pathSeparator + RuleCompilerClasspath.defaultClasspath ) } @@ -54,6 +54,6 @@ class ScalafixToolbox { ( compiler.compile(code) |@| RuleInstrumentation.getRuleFqn(code.toMeta) - ).map(CompiledRules.tupled.apply) + ).map((CompiledRules.apply _).tupled) } } diff --git a/scalafix-reflect/src/main/scala/scalafix/internal/v0/LegacyInMemorySemanticdbIndex.scala b/scalafix-reflect/src/main/scala/scalafix/internal/v0/LegacyInMemorySemanticdbIndex.scala index 19e9aac1a..598ae1556 100644 --- a/scalafix-reflect/src/main/scala/scalafix/internal/v0/LegacyInMemorySemanticdbIndex.scala +++ b/scalafix-reflect/src/main/scala/scalafix/internal/v0/LegacyInMemorySemanticdbIndex.scala @@ -3,6 +3,7 @@ package scalafix.internal.v0 import scala.{meta => m} import scala.meta._ +import scala.meta.internal.inputs.XtensionInputSyntaxStructure import scala.meta.internal.io._ import scala.meta.internal.symtab.SymbolTable import scala.meta.internal.{semanticdb => s} diff --git a/scalafix-reflect/src/main/scala/scalafix/v1/RuleDecoder.scala b/scalafix-reflect/src/main/scala/scalafix/v1/RuleDecoder.scala index 96053da11..6597dfd4f 100644 --- a/scalafix-reflect/src/main/scala/scalafix/v1/RuleDecoder.scala +++ b/scalafix-reflect/src/main/scala/scalafix/v1/RuleDecoder.scala @@ -61,7 +61,7 @@ object RuleDecoder { // Patch.replaceSymbols(from, to) case UriRuleString("replace", replace @ SlashSeparated(from, to)) => val constant = parseReplaceSymbol(from, to) - .map(Patch.internal.ReplaceSymbol.tupled) + .map((Patch.internal.ReplaceSymbol.apply _).tupled) .map(p => scalafix.v1.SemanticRule.constant(replace, p.atomic)) constant :: Nil // Classload rule from classloader diff --git a/scalafix-rules/src/main/resources/META-INF/services/scalafix.v1.Rule b/scalafix-rules/src/main/resources-2/META-INF/services/scalafix.v1.Rule similarity index 100% rename from scalafix-rules/src/main/resources/META-INF/services/scalafix.v1.Rule rename to scalafix-rules/src/main/resources-2/META-INF/services/scalafix.v1.Rule diff --git a/scalafix-rules/src/main/resources-3/META-INF/services/scalafix.v1.Rule b/scalafix-rules/src/main/resources-3/META-INF/services/scalafix.v1.Rule new file mode 100644 index 000000000..2578d8ae9 --- /dev/null +++ b/scalafix-rules/src/main/resources-3/META-INF/services/scalafix.v1.Rule @@ -0,0 +1,6 @@ +scalafix.internal.rule.DisableSyntax +scalafix.internal.rule.NoAutoTupling +scalafix.internal.rule.NoValInForComprehension +scalafix.internal.rule.RedundantSyntax +scalafix.internal.rule.RemoveUnused +scalafix.internal.rule.LeakingImplicitClassVal diff --git a/scalafix-rules/src/main/scala-3/scalafix/internal/rule/TPrintImplicits.scala b/scalafix-rules/src/main/scala-3/scalafix/internal/rule/TPrintImplicits.scala deleted file mode 100644 index 9e7723457..000000000 --- a/scalafix-rules/src/main/scala-3/scalafix/internal/rule/TPrintImplicits.scala +++ /dev/null @@ -1,16 +0,0 @@ -package scalafix.internal.rule - -import java.util.regex.Pattern - -import metaconfig.pprint._ -import scalafix.config.CustomMessage -import scalafix.config.Regex - -class TPrintImplicits { - implicit val tprintPattern - : TPrint[List[CustomMessage[Either[Regex, Pattern]]]] = - new TPrint[List[CustomMessage[Either[Regex, Pattern]]]] { - def render(implicit tpc: TPrintColors): fansi.Str = - fansi.Str("List[Regex]") - } -} diff --git a/scalafix-testkit/src/main/scala/scalafix/testkit/SemanticRuleSuite.scala b/scalafix-testkit/src/main/scala/scalafix/testkit/SemanticRuleSuite.scala index 03345d077..04644da4a 100644 --- a/scalafix-testkit/src/main/scala/scalafix/testkit/SemanticRuleSuite.scala +++ b/scalafix-testkit/src/main/scala/scalafix/testkit/SemanticRuleSuite.scala @@ -1,15 +1,16 @@ package scalafix.testkit import scala.meta._ +import scala.meta.internal.inputs.XtensionInputSyntaxStructure -import scalafix.internal.reflect.RuleCompiler +import scalafix.internal.reflect.RuleCompilerClasspath import scalafix.internal.testkit.EndOfLineAssertExtractor import scalafix.internal.testkit.MultiLineAssertExtractor object SemanticRuleSuite { def defaultClasspath(classDirectory: AbsolutePath): Classpath = Classpath( classDirectory :: - RuleCompiler.defaultClasspathPaths.filter(path => + RuleCompilerClasspath.defaultClasspathPaths.filter(path => path.toNIO.getFileName.toString.contains("scala-library") ) ) diff --git a/scalafix-tests/unit/src/main/resources/expect/Pretty.expect b/scalafix-tests/unit/src/main/resources-2/expect/Pretty.expect similarity index 100% rename from scalafix-tests/unit/src/main/resources/expect/Pretty.expect rename to scalafix-tests/unit/src/main/resources-2/expect/Pretty.expect diff --git a/scalafix-tests/unit/src/main/resources-3/expect/Pretty.expect b/scalafix-tests/unit/src/main/resources-3/expect/Pretty.expect new file mode 100644 index 000000000..038e1391a --- /dev/null +++ b/scalafix-tests/unit/src/main/resources-3/expect/Pretty.expect @@ -0,0 +1,113 @@ +[6:7..6:17]: test/PrettyTest# => class PrettyTest extends Object { self: PrettyTest => +6 decls } +[7:3..7:14]: *[Int] +[7:3..7:7]: *.apply[Int] +[9:10..9:11]: intWrapper(*) +[10:10..10:11]: intWrapper(*) +[14:10..14:16]: *.apply[Inclusive] +[14:10..14:26]: *(global) +[14:17..14:18]: intWrapper(*) +[15:10..15:16]: *.apply[Inclusive] +[15:10..15:32]: *(global) +[15:17..15:18]: intWrapper(*) +[20:9..20:14]: test/PrettyTest#Inner# => class Inner extends Object { self: Inner => +1 decls } +[21:7..21:8]: test/PrettyTest#a. => val method aPrettyTest +[22:7..22:8]: test/PrettyTest#b. => val method ba.Inner +[24:3..24:9]: *.apply[Object { + local4 => abstract method foo(a: Int): Int + local5 => abstract method bar(a: Int): Int + }] +[28:3..28:9]: *.apply[a.Inner] +[29:3..29:9]: *.apply[Int & String] +[30:3..30:9]: *.apply[PrettyTest] +[37:7..37:10]: test/ann# => class ann[T] extends Annotation with StaticAnnotation { self: ann[T] => +3 decls } +[37:11..37:12]: test/ann#[T] => typeparam T +[37:14..37:15]: test/ann#x. => private[this] val method xT +[38:7..38:11]: test/ann1# => class ann1 extends Annotation with StaticAnnotation { self: ann1 => +1 decls } +[39:7..39:11]: test/ann2# => class ann2 extends Annotation with StaticAnnotation { self: ann2 => +1 decls } +[41:7..41:8]: test/B# => class B extends Object { self: B => +1 decls } +[43:7..43:8]: test/C# => class C extends Object { self: C => +1 decls } +[45:7..45:8]: test/P# => class P extends Object { self: P => +8 decls } +[46:9..46:10]: test/P#C# => class C extends Object { self: C => +1 decls } +[47:9..47:10]: test/P#X# => class X extends Object { self: X => +1 decls } +[48:7..48:8]: test/P#x. => val method xX +[51:7..51:8]: test/T# => class T extends Object { self: T => +8 decls } +[52:9..52:10]: test/T#C# => class C extends Object { self: C => +1 decls } +[53:9..53:10]: test/T#X# => class X extends Object { self: X => +1 decls } +[54:7..54:8]: test/T#x. => val method xX +[57:8..57:12]: test/Test. => final object Test extends Object { self: Test.type => +10 decls } +[58:9..58:10]: test/Test.M# => class M extends Object { self: M => +2 decls } +[59:9..59:10]: test/Test.M#m(). => method m=> Int +[62:9..62:10]: test/Test.N# => trait N extends Object { self: N => +2 decls } +[63:9..63:10]: test/Test.N#n(). => method n=> Int +[66:9..66:10]: test/Test.C# => class C extends M { self: C => +42 decls } +[67:9..67:10]: test/Test.C#p. => val method pP +[68:9..68:10]: test/Test.C#x. => val method xp.X +[70:9..70:17]: test/Test.C#typeRef1. => val method typeRef1C +[71:9..71:17]: test/Test.C#typeRef2. => val method typeRef2p.C +[72:9..72:17]: test/Test.C#typeRef3. => val method typeRef3T#C +[73:9..73:17]: test/Test.C#typeRef4. => val method typeRef4List[Int] +[75:9..75:20]: test/Test.C#singleType1. => val method singleType1x.type +[76:9..76:20]: test/Test.C#singleType2. => val method singleType2p.x.type +[77:9..77:15]: test/Test.C#Either. => val method EitherEither.type +[79:9..79:18]: test/Test.C#thisType1. => val method thisType1C.this.type +[80:9..80:18]: test/Test.C#thisType2. => val method thisType2C.this.type +[82:9..82:19]: test/Test.C#superType1. => val method superType1Int +[83:9..83:19]: test/Test.C#superType2. => val method superType2Int +[84:9..84:19]: test/Test.C#superType3. => val method superType3Int +[86:9..86:22]: test/Test.C#compoundType1. => val method compoundType1Object { def k=> Int } +[87:9..87:22]: test/Test.C#compoundType2. => val method compoundType2M & N +[88:9..88:22]: test/Test.C#compoundType3. => val method compoundType3M & N { def k=> Int } +[89:9..89:22]: test/Test.C#compoundType4. => val method compoundType4Object +[90:9..90:22]: test/Test.C#compoundType5. => val method compoundType5M & N +[91:9..91:22]: test/Test.C#compoundType6. => val method compoundType6M & N +[93:9..93:17]: test/Test.C#annType1. => val method annType1T @ann[T] +[93:21..93:25]: [Int] +[94:9..94:17]: test/Test.C#annType2. => val method annType2T @ann1 @ann2 +[96:9..96:25]: test/Test.C#existentialType2. => val method existentialType2List[_] forSome { type _ } +[97:9..97:25]: test/Test.C#existentialType3. => val method existentialType3Class[_] forSome { type _ } +[98:9..98:25]: test/Test.C#existentialType4. => val method existentialType4Class[_] forSome { type _ } +[100:9..100:20]: test/Test.C#typeLambda1(). => method typeLambda1[M[type _]]: Nothing +[100:21..100:22]: test/Test.C#typeLambda1().[M] => typeparam M[type _] +[103:12..103:26]: test/Test.C#ClassInfoType1. => final object ClassInfoType1 extends Object { self: ClassInfoType1.type => +1 decls } +[104:11..104:25]: test/Test.C#ClassInfoType2# => class ClassInfoType2 extends B { self: ClassInfoType2 => +2 decls } +[104:42..104:43]: test/Test.C#ClassInfoType2#x(). => method x=> Int +[105:11..105:25]: test/Test.C#ClassInfoType3# => trait ClassInfoType3[T] extends Object { self: ClassInfoType3[T] => +2 decls } +[105:26..105:27]: test/Test.C#ClassInfoType3#[T] => typeparam T +[107:12..107:22]: test/Test.C#MethodType. => final object MethodType extends Object { self: MethodType.type => +7 decls } +[108:11..108:13]: test/Test.C#MethodType.x1(). => method x1=> Int +[109:11..109:13]: test/Test.C#MethodType.x2(). => method x2=> Int +[110:11..110:13]: test/Test.C#MethodType.m3(). => method m3=> Int +[111:11..111:13]: test/Test.C#MethodType.m4(). => method m4(): Int +[112:11..112:13]: test/Test.C#MethodType.m5(). => method m5(x: Int): Int +[112:14..112:15]: test/Test.C#MethodType.m5().(x) => param x: Int +[113:11..113:13]: test/Test.C#MethodType.m6(). => method m6[T](x: T): T +[113:14..113:15]: test/Test.C#MethodType.m6().[T] => typeparam T +[113:17..113:18]: test/Test.C#MethodType.m6().(x) => param x: T +[116:12..116:22]: test/Test.C#ByNameType. => final object ByNameType extends Object { self: ByNameType.type => +2 decls } +[117:11..117:13]: test/Test.C#ByNameType.m1(). => method m1(x: => Int): Int +[117:14..117:15]: test/Test.C#ByNameType.m1().(x) => param x: => Int +[120:16..120:28]: test/Test.C#RepeatedType# => case class RepeatedType extends Object with Product with Serializable { self: RepeatedType => +4 decls } +[120:29..120:30]: test/Test.C#RepeatedType#s. => val method sString* +[121:11..121:13]: test/Test.C#RepeatedType#m1(). => method m1(x: Int*): Int +[121:14..121:15]: test/Test.C#RepeatedType#m1().(x) => param x: Int* +[124:12..124:20]: test/Test.C#TypeType. => final object TypeType extends Object { self: TypeType.type => +6 decls } +[125:12..125:14]: test/Test.C#TypeType.T1# => type T1 +[126:11..126:13]: test/Test.C#TypeType.m2(). => method m2[T2 = C]: Nothing +[126:14..126:16]: test/Test.C#TypeType.m2().[T2] => typeparam T2 = C +[127:11..127:13]: test/Test.C#TypeType.m3(). => method m3[M3[type _]]: Nothing +[127:14..127:16]: test/Test.C#TypeType.m3().[M3] => typeparam M3[type _] +[128:12..128:14]: test/Test.C#TypeType.T4# => type T4 = C +[129:12..129:14]: test/Test.C#TypeType.T5# => type T5[U] = U +[129:15..129:16]: test/Test.C#TypeType.T5#[U] => typeparam U +[133:10..133:17]: test/Test.Literal. => final object Literal extends Object { self: Literal.type => +12 decls } +[134:15..134:18]: test/Test.Literal.int. => final val method int1 +[135:15..135:19]: test/Test.Literal.long. => final val method long1L +[136:15..136:20]: test/Test.Literal.float. => final val method float1.0f +[137:15..137:21]: test/Test.Literal.double. => final val method double2.0 +[138:15..138:18]: test/Test.Literal.nil. => final val method nilNull +[139:15..139:19]: test/Test.Literal.char. => final val method char'a' +[140:15..140:21]: test/Test.Literal.string. => final val method string"a" +[141:15..141:19]: test/Test.Literal.bool. => final val method booltrue +[142:15..142:19]: test/Test.Literal.unit. => final val method unitUnit +[143:15..143:23]: test/Test.Literal.javaEnum. => final val method javaEnumLinkOption +[144:15..144:22]: test/Test.Literal.clazzOf. => final val method clazzOfOption[Int] \ No newline at end of file diff --git a/scalafix-tests/unit/src/main/scala/scalafix/test/ExplicitSynthetic.scala b/scalafix-tests/unit/src/main/scala/scalafix/test/ExplicitSynthetic.scala index 75a4019d5..2a7ae616d 100644 --- a/scalafix-tests/unit/src/main/scala/scalafix/test/ExplicitSynthetic.scala +++ b/scalafix-tests/unit/src/main/scala/scalafix/test/ExplicitSynthetic.scala @@ -37,7 +37,7 @@ class ExplicitSynthetic() extends SemanticRule("ExplicitSynthetic") { Patch.addRight(t, ".apply") } } - patches.flatten.asPatch + Patch.replaceTree(q"a", "b") + patches.flatten.asPatch + Patch.replaceTree(Term.Name("a"), "b") } } diff --git a/scalafix-tests/unit/src/main/scala/scalafix/test/FqnRule.scala b/scalafix-tests/unit/src/main/scala/scalafix/test/FqnRule.scala index 9a2702f72..3262ca516 100644 --- a/scalafix-tests/unit/src/main/scala/scalafix/test/FqnRule.scala +++ b/scalafix-tests/unit/src/main/scala/scalafix/test/FqnRule.scala @@ -3,6 +3,8 @@ package banana.rule import scala.meta._ import scala.meta.contrib._ +import scalafix.XtensionOptionPatch +import scalafix.XtensionSeqPatch import scalafix.patch.Patch import scalafix.util.SymbolMatcher import scalafix.v0 @@ -11,7 +13,12 @@ import scalafix.v1 case class FqnRule(index: v0.SemanticdbIndex) extends v0.SemanticRule(index, "FqnRule") { override def fix(ctx: v0.RuleCtx): Patch = - ctx.addGlobalImport(importer"scala.collection.immutable") + { + ctx.addGlobalImport( + Importer( + Term.Select(Term.Name("scala"), Term.Name("collection")), + List(Importee.Name(Name("immutable"))) + ) + ) + { val fqnRule = SymbolMatcher.exact(v0.Symbol("test/FqnRule.")) ctx.tree.collect { case fqnRule(t: Term.Name) => ctx.addLeft(t, "/* matched */ ") diff --git a/scalafix-tests/unit/src/main/scala/scalafix/test/NoNull.scala b/scalafix-tests/unit/src/main/scala/scalafix/test/NoNull.scala index 79baa9e3b..b0135219a 100644 --- a/scalafix-tests/unit/src/main/scala/scalafix/test/NoNull.scala +++ b/scalafix-tests/unit/src/main/scala/scalafix/test/NoNull.scala @@ -1,13 +1,17 @@ package scalafix.test -import scala.meta._ +import scala.meta.Lit +import scala.meta.XtensionCollectionLikeUI -import scalafix.v0._ +import scalafix.lint.Diagnostic +import scalafix.v0.LintCategory +import scalafix.v0.Rule +import scalafix.v0.RuleCtx object NoNull extends Rule("NoNull") { val error: LintCategory = LintCategory.error("Nulls are not allowed.") override def check(ctx: RuleCtx): List[Diagnostic] = ctx.tree.collect { - case nil @ q"null" => error.at(nil.pos) + case nil @ Lit.Null() => error.at(nil.pos) } } diff --git a/scalafix-tests/unit/src/test/scala-2/scalafix/tests/util/compat/CompatSemanticdb.scala b/scalafix-tests/unit/src/test/scala-2/scalafix/tests/util/compat/CompatSemanticdb.scala new file mode 100644 index 000000000..5400ed5bc --- /dev/null +++ b/scalafix-tests/unit/src/test/scala-2/scalafix/tests/util/compat/CompatSemanticdb.scala @@ -0,0 +1,27 @@ +package scalafix.tests.util.compat + +import java.nio.file.Path + +import scala.tools.nsc.Main + +object CompatSemanticdb { + + def scalacOptions(src: Path, target: Path): Array[String] = { + Array[String]( + s"-P:semanticdb:targetroot:$target" + ) ++ scalacOptions(src) + } + + def scalacOptions(src: Path): Array[String] = { + Array[String]( + "-Yrangepos", + s"-Xplugin:${SemanticdbPlugin.semanticdbPluginPath()}", + "-Xplugin-require:semanticdb", + s"-P:semanticdb:sourceroot:$src" + ) + } + + def runScalac(scalacOptions: Seq[String]): Unit = { + Main.process(scalacOptions.toArray) + } +} diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/util/SemanticdbPlugin.scala b/scalafix-tests/unit/src/test/scala-2/scalafix/tests/util/compat/SemanticdbPlugin.scala similarity index 96% rename from scalafix-tests/unit/src/test/scala/scalafix/tests/util/SemanticdbPlugin.scala rename to scalafix-tests/unit/src/test/scala-2/scalafix/tests/util/compat/SemanticdbPlugin.scala index 1a52678cf..b5a665431 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/util/SemanticdbPlugin.scala +++ b/scalafix-tests/unit/src/test/scala-2/scalafix/tests/util/compat/SemanticdbPlugin.scala @@ -1,4 +1,4 @@ -package scalafix.tests.util +package scalafix.tests.util.compat import java.nio.file.Paths diff --git a/scalafix-tests/unit/src/test/scala-3/scalafix/tests/util/compat/CompatSemanticdb.scala b/scalafix-tests/unit/src/test/scala-3/scalafix/tests/util/compat/CompatSemanticdb.scala new file mode 100644 index 000000000..3b0897b3b --- /dev/null +++ b/scalafix-tests/unit/src/test/scala-3/scalafix/tests/util/compat/CompatSemanticdb.scala @@ -0,0 +1,23 @@ +package scalafix.tests.util.compat + +import java.nio.file.Path +import dotty.tools.dotc.Main + +object CompatSemanticdb { + + def scalacOptions(src: Path, target: Path): Array[String] = { + Array[String]( + s"-semanticdb-target:$target" + ) ++ scalacOptions(src) + } + + def scalacOptions(src: Path): Array[String] = { + Array[String]( + "-Xsemanticdb" + ) + } + + def runScalac(scalacOptions: Seq[String]): Unit = { + Main.process(scalacOptions.toArray) + } +} diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/cli/CliGitDiffSuite.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/cli/CliGitDiffSuite.scala index a9ca23e45..c4c2df10f 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/cli/CliGitDiffSuite.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/cli/CliGitDiffSuite.scala @@ -233,19 +233,19 @@ class CliGitDiffSuite extends AnyFunSuite with DiffAssertions { val bar1 = """|object B { - | def bar() {} + | def bar() { println(s"Foo") } |} |""".stripMargin val bar2 = """|object B { - | def bar(): Unit = {} + | def bar() { println("Foo") } |} |""".stripMargin fs.add(code, foo1) git.add(code) - fs.add(confFile, "rules = ProcedureSyntax") + fs.add(confFile, "rules = RedundantSyntax") git.add(confFile) git.commit() diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/cli/ScalafixImplSuite.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/cli/ScalafixImplSuite.scala index 3015e6d78..83ad638bb 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/cli/ScalafixImplSuite.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/cli/ScalafixImplSuite.scala @@ -24,17 +24,17 @@ import scalafix.interfaces.ScalafixException import scalafix.interfaces.ScalafixMainCallback import scalafix.interfaces.ScalafixMainMode import scalafix.internal.reflect.ClasspathOps -import scalafix.internal.reflect.RuleCompiler +import scalafix.internal.reflect.RuleCompilerClasspath import scalafix.test.StringFS import scalafix.testkit.DiffAssertions import scalafix.tests.util.ScalaVersions -import scalafix.tests.util.SemanticdbPlugin +import scalafix.tests.util.compat.CompatSemanticdb import scalafix.{interfaces => i} class ScalafixImplSuite extends AnyFunSuite with DiffAssertions { def scalaLibrary: AbsolutePath = - RuleCompiler.defaultClasspathPaths + RuleCompilerClasspath.defaultClasspathPaths .find(_.toNIO.getFileName.toString.contains("scala-library")) .getOrElse { throw new IllegalStateException("Unable to detect scala-library.jar") @@ -122,19 +122,22 @@ class ScalafixImplSuite extends AnyFunSuite with DiffAssertions { // if a non empty list of rules is provided, rules from config file are ignored val args2 = api .newArguments() - .withRules(List("ProcedureSyntax").asJava) + .withRules(List("RedundantSyntax").asJava) .withConfig(Optional.empty()) .withWorkingDirectory(cwd.toNIO) args2.validate() assert( args2.rulesThatWillRun().asScala.toList.map(_.name()) == List( - "ProcedureSyntax" + "RedundantSyntax" ) ) } test("runMain") { + // Todo(i1680): this is an integration test that uses many non supported rules in scala 3. + // Add a more simple test for scala 3. For now we ignore for Scala 3. + if (ScalaVersions.isScala3) cancel() // This is a full integration test that stresses the full breadth of the scalafix-interfaces API val api = i.Scalafix.classloadInstance(this.getClass.getClassLoader) // Assert that non-ascii characters read into "?" @@ -182,16 +185,13 @@ class ScalafixImplSuite extends AnyFunSuite with DiffAssertions { ) val scalacOptions = Array[String]( "-Yrangepos", - s"-Xplugin:${SemanticdbPlugin.semanticdbPluginPath()}", - "-Xplugin-require:semanticdb", "-classpath", scalaLibrary.toString, - s"-P:semanticdb:sourceroot:$src", "-d", d.toString, semicolon.toString, excluded.toString - ) + ) ++ CompatSemanticdb.scalacOptions(src) val compileSucceeded = scala.tools.nsc.Main.process(scalacOptions) val buf = List.newBuilder[ScalafixDiagnostic] val callback = new ScalafixMainCallback { diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/core/Classpaths.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/core/Classpaths.scala index 456c9d2f6..9e379fb11 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/core/Classpaths.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/core/Classpaths.scala @@ -3,11 +3,11 @@ package scalafix.tests.core import scala.meta.io.AbsolutePath import scala.meta.io.Classpath -import scalafix.internal.reflect.RuleCompiler +import scalafix.internal.reflect.RuleCompilerClasspath object Classpaths { def scalaLibrary: Classpath = Classpath( - RuleCompiler.defaultClasspathPaths.filter { path => + RuleCompilerClasspath.defaultClasspathPaths.filter { path => path.isFile || path.toNIO.getFileName.toString.contains("scala-library") } diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/core/DenotationOpsSuite.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/core/DenotationOpsSuite.scala index 769c89525..1fde2dd96 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/core/DenotationOpsSuite.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/core/DenotationOpsSuite.scala @@ -2,6 +2,7 @@ package scalafix.tests.core import scala.meta._ import scala.meta.contrib._ +import scala.meta.contrib.equality.Structurally.StructuralEq import scalafix.syntax._ @@ -13,19 +14,19 @@ class DenotationOpsSuite extends BaseSemanticSuite("DenotationOpsTest") { for { symbol <- t.symbol resultType <- symbol.resultType - } yield assert(resultType isEqual t"Boolean") + } yield assert(resultType isEqual Type.Name("Boolean")) case t @ Pat.Var(Name("y")) => for { symbol <- t.symbol resultType <- symbol.resultType - } yield assert(resultType isEqual t"List[String]") + } yield assert(resultType isEqual Type.Name("List[String]")) case t: Defn.Def if t.name.value == "m" => for { symbol <- t.symbol resultType <- symbol.resultType - } yield assert(resultType isEqual t"List[String]") + } yield assert(resultType isEqual Type.Name("List[String]")) } } diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/core/DialectSuite.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/core/DialectSuite.scala index 5d6d72e59..92d31f40f 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/core/DialectSuite.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/core/DialectSuite.scala @@ -3,6 +3,7 @@ package scalafix.tests.core import scala.meta._ import org.scalatest.funsuite.AnyFunSuiteLike +import scalafix.XtensionSeqPatch import scalafix.internal.tests.utils.SkipWindows import scalafix.patch.Patch import scalafix.testkit.AbstractSyntacticRuleSuite diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/core/EscapeHatchSuite.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/core/EscapeHatchSuite.scala index 77d03c938..1b3b8a071 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/core/EscapeHatchSuite.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/core/EscapeHatchSuite.scala @@ -4,6 +4,7 @@ import java.nio.file.Paths import scala.meta.Source import scala.meta.Tree +import scala.meta.XtensionParseInputLike import scala.meta.contrib.AssociatedComments import scala.meta.inputs.Input diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/core/PatchSuite.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/core/PatchSuite.scala index 61f17b556..5879339d5 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/core/PatchSuite.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/core/PatchSuite.scala @@ -9,6 +9,7 @@ import scala.meta.internal.prettyprinters.TreeSyntax import scala.meta.tokens.Token.Ident import org.scalatest.funsuite.AnyFunSuiteLike +import scalafix.XtensionSeqPatch import scalafix.internal.patch.PatchInternals import scalafix.internal.tests.utils.SkipWindows import scalafix.patch.Patch @@ -94,7 +95,12 @@ class PatchSuite extends AbstractSyntacticRuleSuite with AnyFunSuiteLike { case object AddGlobalImporter extends SyntacticRule("addGlobalImporter") { override def fix(implicit doc: SyntacticDocument): Patch = { - Patch.addGlobalImport(importer"scala.collection.{mutable => _}") + Patch.addGlobalImport( + Importer( + Term.Select(Term.Name("scala"), Term.Name("collection")), + List(Importee.Unimport(Name.Indeterminate("mutable"))) + ) + ) } } check( diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/interfaces/ScalafixArgumentsSuite.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/interfaces/ScalafixArgumentsSuite.scala index 063dccaef..9679e0e56 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/interfaces/ScalafixArgumentsSuite.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/interfaces/ScalafixArgumentsSuite.scala @@ -13,7 +13,6 @@ import scala.util.Try import scala.meta.internal.io.FileIO import scala.meta.io.AbsolutePath -import buildinfo.RulesBuildInfo import org.scalatest.funsuite.AnyFunSuite import scalafix.interfaces.ScalafixArguments import scalafix.interfaces.ScalafixDiagnostic @@ -26,16 +25,15 @@ import scalafix.internal.rule.RemoveUnusedConfig import scalafix.internal.tests.utils.SkipWindows import scalafix.test.StringFS import scalafix.testkit.DiffAssertions +import scalafix.tests.BuildInfo import scalafix.tests.core.Classpaths import scalafix.tests.util.ScalaVersions -import scalafix.tests.util.SemanticdbPlugin +import scalafix.tests.util.compat.CompatSemanticdb import scalafix.v1.SemanticRule class ScalafixArgumentsSuite extends AnyFunSuite with DiffAssertions { - val scalaBinaryVersion: String = - RulesBuildInfo.scalaVersion.split('.').take(2).mkString(".") - val scalaVersion = RulesBuildInfo.scalaVersion - val removeUnused: String = + private val scalaVersion = BuildInfo.scalaVersion + private val removeUnused: String = if (ScalaVersions.isScala213) "-Wunused:imports" else "-Ywarn-unused-import" @@ -66,22 +64,25 @@ class ScalafixArgumentsSuite extends AnyFunSuite with DiffAssertions { val main: Path = src.resolve("Main.scala") val relativePath: Path = cwd.relativize(main) + val specificScalacOption2: Seq[String] = + if (!ScalaVersions.isScala3) + Seq(removeUnused) + else Nil + val scalacOptions: Array[String] = Array[String]( - "-Yrangepos", - removeUnused, - s"-Xplugin:${SemanticdbPlugin.semanticdbPluginPath()}", - "-Xplugin-require:semanticdb", "-classpath", s"${scalaLibrary.mkString(":")}", - s"-P:semanticdb:sourceroot:$src", - s"-P:semanticdb:targetroot:$target", "-d", d.toString, main.toString - ) + ) ++ specificScalacOption2 ++ CompatSemanticdb.scalacOptions(src, target) test("ScalafixArguments.evaluate with a semantic rule", SkipWindows) { - val _ = scala.tools.nsc.Main.process(scalacOptions) + // Todo(i1680): this is an integration test that uses many non supported rules in scala 3. + // Add a more simple test for scala 3. For now we ignore for Scala 3. + if (ScalaVersions.isScala3) cancel() + + val _ = CompatSemanticdb.runScalac(scalacOptions) val result = api .withRules( List( @@ -158,8 +159,11 @@ class ScalafixArgumentsSuite extends AnyFunSuite with DiffAssertions { assertNoDiff(obtained3, expectedWithOnePatch) } + test("ScalafixArguments.evaluate getting StaleSemanticdb", SkipWindows) { - val _ = scala.tools.nsc.Main.process(scalacOptions) + // Todo(i1680): We need a semanticRule in scala 3. + if (ScalaVersions.isScala3) cancel() + val _ = CompatSemanticdb.runScalac(scalacOptions) val args = api .withRules( List( @@ -192,7 +196,9 @@ class ScalafixArgumentsSuite extends AnyFunSuite with DiffAssertions { "ScalafixArguments.evaluate doesn't take into account withMode and withMainCallback", SkipWindows ) { - val _ = scala.tools.nsc.Main.process(scalacOptions) + // Todo(i1680): We need a semanticRule in scala 3. + if (ScalaVersions.isScala3) cancel() + val _ = CompatSemanticdb.runScalac(scalacOptions) val contentBeforeEvaluation = FileIO.slurp(AbsolutePath(main), StandardCharsets.UTF_8) var maybeDiagnostic: Option[ScalafixDiagnostic] = None @@ -345,6 +351,8 @@ class ScalafixArgumentsSuite extends AnyFunSuite with DiffAssertions { } test("Scalafix-evaluation-error-messages: missing semanticdb", SkipWindows) { + // Todo(i1680): We need a semanticRule in scala 3. + if (ScalaVersions.isScala3) cancel() val eval = api .withPaths(Seq(main).asJava) .withRules(List("ExplicitResultTypes").asJava) @@ -437,7 +445,9 @@ class ScalafixArgumentsSuite extends AnyFunSuite with DiffAssertions { test("Scala 3 style wildcard import", SkipWindows) { // https://github.com/scalacenter/scalafix/issues/1663 - if (scala.util.Properties.versionNumberString.startsWith("2.11")) { + // Todo(i1680): Add another test for scala 3 that doesn't uses removeUnused or + // at least remove the if when removeUnused is supported in scala 3 + if (ScalaVersions.isScala211 || ScalaVersions.isScala3) { cancel() } @@ -461,20 +471,15 @@ class ScalafixArgumentsSuite extends AnyFunSuite with DiffAssertions { val scalacOptions = Array[String]( "-Xsource:3", - "-Yrangepos", removeUnused, - s"-Xplugin:${SemanticdbPlugin.semanticdbPluginPath()}", - "-Xplugin-require:semanticdb", "-classpath", s"${scalaLibrary.mkString(":")}", - s"-P:semanticdb:sourceroot:$src", - s"-P:semanticdb:targetroot:$target", "-d", d.toString, main.toString - ) + ) ++ CompatSemanticdb.scalacOptions(src, target) - val _ = scala.tools.nsc.Main.process(scalacOptions) + val _ = CompatSemanticdb.runScalac(scalacOptions) val result = api .withRules( Collections.singletonList(removeUnsuedRule().name.toString()) diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/reflect/RuleDecoderSuite.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/reflect/RuleDecoderSuite.scala index efbea5f55..6a04c59d1 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/reflect/RuleDecoderSuite.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/reflect/RuleDecoderSuite.scala @@ -1,5 +1,7 @@ package scalafix.tests.reflect +import java.nio.file.Files + import scala.meta.io.AbsolutePath import scala.meta.io.RelativePath @@ -26,13 +28,42 @@ class RuleDecoderSuite extends AnyFunSuite { RuleDecoder.Settings().withCwd(cwd) val decoder: ConfDecoder[Rules] = RuleDecoder.decoder(decoderSettings) val expectedName = "NoDummy" - val expectedDescription = "" + test("absolute path resolves as is", SkipWindows) { val rules = decoder.read(Conf.Str(s"file:$abspath")).get assert(expectedName == rules.name.value) } + test("relative resolves from custom working directory") { val rules = decoder.read(Conf.Str(s"file:$relpath")).get assert(expectedName == rules.name.value) } + + test("resolved classes can be reloaded", SkipWindows) { + val tmp = Files.createTempFile("scalafix", "CustomRule.scala") + + val customRuleV1 = + """package custom + |import scalafix.v1._ + |class CustomRule extends SyntacticRule("CustomRule") {} + """.stripMargin + Files.write(tmp, customRuleV1.getBytes) + val rules1 = + decoder.read(Conf.Str(s"file:${tmp.toFile.getAbsolutePath}")).get + val class1 = rules1.rules.head.getClass + + val customRuleV2 = + """package custom + |import scalafix.v1._ + |class CustomRule extends SyntacticRule("CustomRule") { + | def foo = 1 + |} + """.stripMargin + Files.write(tmp, customRuleV2.getBytes) + val rules2 = + decoder.read(Conf.Str(s"file:${tmp.toFile.getAbsolutePath}")).get + val class2 = rules2.rules.head.getClass + + assert(!class1.isAssignableFrom(class2)) + } } diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/reflect/ToolClasspathSuite.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/reflect/ToolClasspathSuite.scala index 4b9964c71..19cad653a 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/reflect/ToolClasspathSuite.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/reflect/ToolClasspathSuite.scala @@ -2,60 +2,69 @@ package scalafix.tests.reflect import java.nio.file.Files -import scala.reflect.io.Directory -import scala.reflect.io.PlainDirectory - import scala.meta.io.AbsolutePath import coursier._ import metaconfig.Conf import org.scalatest.BeforeAndAfterAll import org.scalatest.funsuite.AnyFunSuite +import scalafix.Versions import scalafix.internal.reflect.RuleCompiler +import scalafix.internal.reflect.RuleCompilerClasspath import scalafix.internal.tests.utils.SkipWindows -import scalafix.tests.util.ScalaVersions import scalafix.v1.RuleDecoder class ToolClasspathSuite extends AnyFunSuite with BeforeAndAfterAll { - var scalafmtClasspath: List[AbsolutePath] = _ + var scalaClasspath: List[AbsolutePath] = _ override def beforeAll(): Unit = { + var scalaVersionSuffix: String = "" + val versionSplit = Versions.scalaVersion.split("\\.") + if (versionSplit(0) == "3") { + scalaVersionSuffix = versionSplit(0) + } else { + scalaVersionSuffix = s"${versionSplit(0)}.${versionSplit(1)}" + } val jars = - if (ScalaVersions.isScala213) - Fetch() - .addDependencies(dep"org.scalameta:scalafmt-core_2.13:2.5.1") - .run() - .toList - else - Fetch() - .addDependencies(dep"com.geirsson:scalafmt-core_2.12:1.2.0") - .run() - .toList - scalafmtClasspath = jars.map(AbsolutePath(_)) + Fetch() + .addDependencies( + Dependency( + Module( + Organization("org.scalatest"), + ModuleName(s"scalatest_${scalaVersionSuffix}") + ), + "3.2.13" + ) + ) + .run() + .toList + + scalaClasspath = jars.map(AbsolutePath(_)) } test( "--tool-classpath is respected when compiling from source", SkipWindows ) { - val scalafmtRewrite = + val scalaRewrite = """ - |import org.scalafmt._ + |import org.scalatest.Assertions._ |import scalafix.v0._ | - |object FormatRule extends Rule("FormatRule") { - | override def description: String = "FormatRuleDescription" + |object FormatRule extends Rule("FormatRule") + | { + | override def description: String = "RuleDescription" | override def fix(ctx: RuleCtx): Patch = { - | val formatted = Scalafmt.format(ctx.tokens.mkString).get - | ctx.addLeft(ctx.tokens.last, formatted) + | assert("raz" == "raz") + | ctx.addLeft(ctx.tokens.last, "test") | } |} """.stripMargin - val tmp = Files.createTempFile("scalafix", "FormatRule.scala") - Files.write(tmp, scalafmtRewrite.getBytes) + val tmpFile = Files.createTempFile("scalafix", "FormatRule.scala") + Files.write(tmpFile, scalaRewrite.getBytes) val decoderSettings = - RuleDecoder.Settings().withToolClasspath(scalafmtClasspath) + RuleDecoder.Settings().withToolClasspath(scalaClasspath) val decoder = RuleDecoder.decoder(decoderSettings) - val obtained = decoder.read(Conf.Str(s"file:$tmp")).get + val obtained = decoder.read(Conf.Str(s"file:$tmpFile")).get val expectedName = "FormatRule" assert(obtained.name.value == expectedName) } @@ -68,8 +77,8 @@ class ToolClasspathSuite extends AnyFunSuite with BeforeAndAfterAll { """.stripMargin val tmp = Files.createTempDirectory("scalafix") val compiler = new RuleCompiler( - RuleCompiler.defaultClasspath, - new PlainDirectory(new Directory(tmp.toFile)) + RuleCompilerClasspath.defaultClasspath, + Some(tmp.toFile) ) compiler.compile(metaconfig.Input.VirtualFile("CustomRule.scala", rewrite)) val decoderSettings = diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/rule/MavenFuzzSuite.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/rule/MavenFuzzSuite.scala deleted file mode 100644 index 34614aff6..000000000 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/rule/MavenFuzzSuite.scala +++ /dev/null @@ -1,184 +0,0 @@ -package scalafix.tests.rule - -import java.nio.charset.StandardCharsets -import java.nio.file.FileSystems -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths - -import scala.collection.JavaConverters._ -import scala.sys.process._ -import scala.tools.nsc.reporters.StoreReporter - -import scala.meta.internal.io.FileIO -import scala.meta.internal.pc.ScalafixGlobal -import scala.meta.io.AbsolutePath - -import coursier._ -import coursier.util.Task -import org.scalatest.Ignore -import org.scalatest.funsuite.AnyFunSuite -import scalafix.interfaces.Scalafix -import scalafix.interfaces.ScalafixMainMode -import scalafix.testkit.DiffAssertions - -@Ignore // Ignored because this test is very slow. -class MavenFuzzSuite extends AnyFunSuite with DiffAssertions { - private def getCompilingSources( - g: ScalafixGlobal, - classfiles: Seq[Path], - sourceJars: Seq[Path], - tmp: Path - ): Seq[Path] = { - val result = Vector.newBuilder[Path] - val matcher = - FileSystems.getDefault().getPathMatcher("glob:*.scala") - sourceJars.foreach { jar => - FileIO.withJarFileSystem(AbsolutePath(jar), false, true) { root => - FileIO.listAllFilesRecursively(root).files.foreach { relpath => - val in = root.resolve(relpath) - val filename = relpath.toNIO.getFileName().toString() - if (matcher.matches(Paths.get(filename))) { - val text = FileIO.slurp(in, StandardCharsets.UTF_8) - val errors = compileErrors(g, text, relpath.toString()) - if (errors.isEmpty) { - val out = tmp.resolve(relpath.toString()) - Files.createDirectories(out.getParent()) - val stream = Files.newOutputStream(out) - try Files.copy(in.toNIO, stream) - finally stream.close() - result += out - } else { - println(errors) - } - } - } - } - } - result.result() - } - - def compileErrors( - g: ScalafixGlobal, - code: String, - filename: String - ): List[String] = { - val reporter = new StoreReporter() - val old = g.reporter - g.reporter = reporter - g.settings.stopAfter.value = List("typer") - val run = new g.Run() - val source = g.newSourceFile(code, filename) - run.compileSources(List(source)) - g.reporter = old - val errors = reporter.infos.filter(_.severity.id == 2) - errors.toList.map { d => - new StringBuilder() - .append(d.pos.source.path) - .append(":") - .append(d.pos.line) - .append(" ") - .append(d.msg) - .append("\n") - .append(d.pos.lineContent) - .append("\n") - .append(d.pos.lineCaret) - .toString() - } - } - - val metals: List[Dependency] = List( - Dependency( - Module( - Organization("org.scalameta"), - ModuleName("metals_2.12") - ), - "0.7.6" - ) - ) - // akka is a bad example since it has undeclared compile-time dependencies - // on "silencer" - val akka: Dependency = Dependency( - Module( - Organization("com.typesafe.akka"), - ModuleName("akka-actor_2.12") - ), - "2.5.25" - ) - val ammonite: List[Dependency] = List( - Dependency( - Module( - Organization("com.lihaoyi"), - ModuleName("ammonite-repl_2.12.10") - ), - "1.7.4-0-cdefbd9" - ), - Dependency( - Module( - Organization("com.lihaoyi"), - ModuleName("acyclic_2.12") - ), - "0.2.0" - ) - ) - val dependencies = ammonite // ammonite - val fetch: Fetch[Task] = Fetch() - - def check(rule: String): Unit = { - test(rule) { - val classfiles = fetch - .withDependencies(dependencies) - .run() - .map(_.toPath()) - val sources = fetch - .withClassifiers(Set(Classifier("sources"))) - .withDependencies(dependencies) - .run() - .map(_.toPath()) - .filter(_.toString().contains("lihaoyi")) - val scalafix = - Scalafix.classloadInstance(this.getClass().getClassLoader()) - val tmp = Files.createTempDirectory("scalafix") - def exec(cmd: String*): Unit = { - val gitinit = Process(cmd.toList, Some(tmp.toFile())).! - require(gitinit == 0, gitinit) - } - - tmp.toFile().deleteOnExit() - val g = ScalafixGlobal - .newCompiler( - classfiles.map(AbsolutePath(_)).toList, - Nil, - Map.empty - ) - .get - val paths = getCompilingSources(g, classfiles, sources, tmp) - exec("git", "init") - exec("git", "add", ".") - exec("git", "commit", "-m", "first-commit") - println(paths.length) - val args = scalafix - .newArguments() - .withSourceroot(tmp) - .withPaths(paths.asJava) - .withRules(List(rule).asJava) - .withClasspath(classfiles.asJava) - .withMode(ScalafixMainMode.CHECK) - val exit = args.run() - println(exit) - // exec("git", "diff") - FileIO.listAllFilesRecursively(AbsolutePath(tmp)).foreach { path => - if (path.toNIO.getFileName.toString.endsWith(".scala")) { - val text = FileIO.slurp(path, StandardCharsets.UTF_8) - val errors = compileErrors(g, text, path.toString()) - if (errors.nonEmpty) { - println(path) - println(errors) - } - } - } - println(tmp) - } - } - check("ExplicitResultTypes") -} diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/util/ExpectSuite.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/util/ExpectSuite.scala index d0e1ab8ae..b5620e678 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/util/ExpectSuite.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/util/ExpectSuite.scala @@ -5,6 +5,7 @@ import scala.meta.internal.io.FileIO import scala.meta.io.AbsolutePath import org.scalatest.funsuite.AnyFunSuite +import scalafix.Versions import scalafix.testkit.DiffAssertions import scalafix.tests.BuildInfo import scalafix.tests.core.BaseSemanticSuite @@ -14,10 +15,12 @@ trait ExpectSuite extends AnyFunSuite with DiffAssertions { def filename: String def obtained(): String - final def path: AbsolutePath = - AbsolutePath(BuildInfo.unitResourceDirectory) + final def path: AbsolutePath = { + val scalaMajorVersion = Versions.scalaVersion.split("\\.")(0) + AbsolutePath(s"${BuildInfo.unitResourceDirectory}-${scalaMajorVersion}") .resolve("expect") .resolve(filename.stripSuffix("Test.scala") + ".expect") + } final implicit lazy val sdoc: SemanticDocument = BaseSemanticSuite.loadDoc(filename) final def expected(): String = diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/util/SaveExpect.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/util/SaveExpect.scala index da2c6acb5..ad4b4d8b2 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/util/SaveExpect.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/util/SaveExpect.scala @@ -10,7 +10,7 @@ object SaveExpect { all.foreach { suite => Files.write( suite.path.toNIO, - suite.obtained.getBytes(StandardCharsets.UTF_8) + suite.obtained().getBytes(StandardCharsets.UTF_8) ) println(suite.path) } diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/util/ScalaVersions.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/util/ScalaVersions.scala index b9a742503..dd09f7b16 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/util/ScalaVersions.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/util/ScalaVersions.scala @@ -1,10 +1,14 @@ package scalafix.tests.util +import scalafix.tests.BuildInfo + object ScalaVersions { def isScala211: Boolean = - util.Properties.versionNumberString.startsWith("2.11") + BuildInfo.scalaVersion.startsWith("2.11") def isScala212: Boolean = - util.Properties.versionNumberString.startsWith("2.12") + BuildInfo.scalaVersion.startsWith("2.12") def isScala213: Boolean = - util.Properties.versionNumberString.startsWith("2.13") + BuildInfo.scalaVersion.startsWith("2.13") + def isScala3: Boolean = + BuildInfo.scalaVersion.startsWith("3") } diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/util/ScalametaStructureSuite.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/util/ScalametaStructureSuite.scala index 8dd020ef1..700bd6140 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/util/ScalametaStructureSuite.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/util/ScalametaStructureSuite.scala @@ -8,7 +8,13 @@ import scalafix.v1._ class ScalametaStructureSuite extends AnyFunSuite with DiffAssertions { test("pretty(t)") { - val obtained = q"a.b.c.d".structureWidth(1) + val obtained = Term + .Select( + Term + .Select(Term.Select(Term.Name("a"), Term.Name("b")), Term.Name("c")), + Term.Name("d") + ) + .structureWidth(1) val expected = """|Term.Select( | Term.Select( @@ -25,7 +31,13 @@ class ScalametaStructureSuite extends AnyFunSuite with DiffAssertions { } test("pretty(t, showFieldNames = true)") { - val obtained = q"a.b.c.d".structureLabeled(1) + val obtained = Term + .Select( + Term + .Select(Term.Select(Term.Name("a"), Term.Name("b")), Term.Name("c")), + Term.Name("d") + ) + .structureLabeled(1) val expected = """| |Term.Select( @@ -44,7 +56,17 @@ class ScalametaStructureSuite extends AnyFunSuite with DiffAssertions { test("option") { assertNoDiff( - q"def foo: A = ???".decltpe.structureWidth(1), + Defn + .Def( + List(), + Term.Name("foo"), + List(), + List(List()), + Some(Type.Name("A")), + Term.Name("???") + ) + .decltpe + .structureWidth(1), """| |Some(Type.Name("A")) |""".stripMargin @@ -54,7 +76,11 @@ class ScalametaStructureSuite extends AnyFunSuite with DiffAssertions { test("list") { assertNoDiff( // NOTE(olafur): need downcast because List is no longer a Product in 2.13. - q"foo(a)".args.asInstanceOf[Product].structureWidth(1), + Term + .Apply(Term.Name("foo"), List(Term.Name("a"))) + .args + .asInstanceOf[Product] + .structureWidth(1), """|List( | Term.Name("a") |)