diff --git a/build.sbt b/build.sbt index 054868f1e..6e9d04ae8 100644 --- a/build.sbt +++ b/build.sbt @@ -212,8 +212,7 @@ lazy val unit = project } put( "inputClasspath", - (testsInput / Compile / fullClasspath).value.map(_.data) :+ - (testsInput / Compile / semanticdbTargetRoot).value + (testsInput / Compile / fullClasspath).value.map(_.data) ) put( "inputSourceDirectories", @@ -246,7 +245,10 @@ lazy val unit = project "testsInputResources" -> (testsInput / Compile / sourceDirectory).value / "resources", "semanticClasspath" -> - (testsShared / Compile / semanticdbTargetRoot).value, + Seq( + (testsInput / Compile / semanticdbTargetRoot).value, + (testsShared / Compile / semanticdbTargetRoot).value + ), "sharedSourceroot" -> (ThisBuild / baseDirectory).value / "scalafix-tests" / "shared" / "src" / "main", diff --git a/project/Mima.scala b/project/Mima.scala index 7ec2cbfe9..e79cef64e 100644 --- a/project/Mima.scala +++ b/project/Mima.scala @@ -34,7 +34,7 @@ object Mima { ProblemFilters.exclude[Problem]("scalafix.testkit.SemanticRuleSuite.*"), ProblemFilters.exclude[MissingClassProblem]("scalafix.testkit.SyntacticRuleSuite$"), ProblemFilters.exclude[Problem]("scalafix.testkit.SyntacticRuleSuite"), - ProblemFilters.exclude[ReversedMissingMethodProblem]("scalafix.interfaces.ScalafixArguments.withSemanticdbTargetroot") + ProblemFilters.exclude[ReversedMissingMethodProblem]("scalafix.interfaces.ScalafixArguments.withSemanticdbTargetroots") ) } } diff --git a/scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixArgumentsImpl.scala b/scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixArgumentsImpl.scala index daf20375d..82ae1ed8f 100644 --- a/scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixArgumentsImpl.scala +++ b/scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixArgumentsImpl.scala @@ -160,9 +160,13 @@ final case class ScalafixArgumentsImpl(args: Args = Args.default) copy(args = args.copy(sourceroot = Some(AbsolutePath(path)(args.cwd)))) } - override def withSemanticdbTargetroot(path: Path): ScalafixArguments = { + override def withSemanticdbTargetroots( + paths: util.List[Path] + ): ScalafixArguments = { copy(args = - args.copy(semanticdbTargetroot = Some(AbsolutePath(path)(args.cwd))) + args.copy(semanticdbTargetroots = + paths.asScala.toList.map(AbsolutePath(_)(args.cwd)) + ) ) } diff --git a/scalafix-cli/src/main/scala/scalafix/internal/v1/Args.scala b/scalafix-cli/src/main/scala/scalafix/internal/v1/Args.scala index fdc1b7041..9e6be8188 100644 --- a/scalafix-cli/src/main/scala/scalafix/internal/v1/Args.scala +++ b/scalafix-cli/src/main/scala/scalafix/internal/v1/Args.scala @@ -110,10 +110,10 @@ case class Args( ) sourceroot: Option[AbsolutePath] = None, @Description( - "Absolute path passed to semanticdb with -P:semanticdb:targetroot:. " + + "Absolute paths passed to semanticdb with -P:semanticdb:targetroot:. " + "Used to locate semanticdb files. By default, Scalafix will try to locate semanticdb files in the classpath" ) - semanticdbTargetroot: Option[AbsolutePath] = None, + semanticdbTargetroots: List[AbsolutePath] = Nil, @Description( "If set, automatically infer the --classpath flag by scanning for directories with META-INF/semanticdb" ) @@ -348,11 +348,12 @@ case class Args( } def validatedClasspath: Classpath = { - val targetrootClasspath = semanticdbTargetroot - .map(_.toString()) - .orElse(semanticdbOption("targetroot", Some("-semanticdb-target"))) - .map(option => Classpath(option)) - .getOrElse(Classpath(Nil)) + val targetrootClasspath = semanticdbTargetroots match { + case Nil => + semanticdbOption("targetroot", Some("-semanticdb-target")).toList + .map(AbsolutePath.apply) + case _ => semanticdbTargetroots + } val baseClasspath = if (autoClasspath && classpath.entries.isEmpty) { val roots = @@ -362,7 +363,7 @@ case class Args( } else { classpath } - targetrootClasspath ++ baseClasspath + Classpath(targetrootClasspath) ++ baseClasspath } def classLoader: ClassLoader = diff --git a/scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixArguments.java b/scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixArguments.java index a3a86c824..5715130bd 100644 --- a/scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixArguments.java +++ b/scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixArguments.java @@ -140,10 +140,10 @@ ScalafixArguments withToolClasspath( ScalafixArguments withSourceroot(Path path); /** - * @param path The SemanticDB targetroot path passed via --targetroot. Must match path - * in -Xplugin:semanticdb:targetroot:{path} if used. + * @param path The SemanticDB targetroot paths passed via --semanticdb-targetroots. Must match + * path in -Xplugin:semanticdb:targetroot:{path} if used. */ - ScalafixArguments withSemanticdbTargetroot(Path path); + ScalafixArguments withSemanticdbTargetroots(List path); /** * @param callback Handler for reported linter messages. If not provided, defaults to printing diff --git a/scalafix-reflect/src/main/scala/scalafix/internal/reflect/ClasspathOps.scala b/scalafix-reflect/src/main/scala/scalafix/internal/reflect/ClasspathOps.scala index ab2d42644..9af8ee72d 100644 --- a/scalafix-reflect/src/main/scala/scalafix/internal/reflect/ClasspathOps.scala +++ b/scalafix-reflect/src/main/scala/scalafix/internal/reflect/ClasspathOps.scala @@ -43,6 +43,11 @@ object ClasspathOps { new URLClassLoader(url +: getURLs(classLoader), classLoader) } + def thisClassLoaderWith(urls: Seq[URL]): URLClassLoader = { + val classLoader = this.getClass.getClassLoader + new URLClassLoader(urls.toArray ++ getURLs(classLoader), classLoader) + } + def thisClasspath: Classpath = { Classpath( getURLs(this.getClass().getClassLoader()) diff --git a/scalafix-testkit/src/main/scala/scalafix/testkit/AbstractSemanticRuleSuite.scala b/scalafix-testkit/src/main/scala/scalafix/testkit/AbstractSemanticRuleSuite.scala index 411ea3c60..47dad840f 100644 --- a/scalafix-testkit/src/main/scala/scalafix/testkit/AbstractSemanticRuleSuite.scala +++ b/scalafix-testkit/src/main/scala/scalafix/testkit/AbstractSemanticRuleSuite.scala @@ -10,10 +10,12 @@ import org.scalatest.BeforeAndAfterAll import org.scalatest.Suite import org.scalatest.TestRegistration import org.scalatest.exceptions.TestFailedException +import scalafix.internal.config.ScalaVersion import scalafix.internal.patch.PatchInternals import scalafix.internal.reflect.ClasspathOps import scalafix.internal.testkit.AssertDiff import scalafix.internal.testkit.CommentAssertion +import scalafix.internal.v1.Args /** * Construct a test suite for running semantic Scalafix rules. @@ -98,8 +100,13 @@ abstract class AbstractSemanticRuleSuite( } lazy val testsToRun: List[RuleTest] = { + val args = Args.default.copy( + scalaVersion = ScalaVersion.from(props.scalaVersion).get, + scalacOptions = props.scalacOptions, + classpath = props.inputClasspath + ) val symtab = ClasspathOps.newSymbolTable(props.inputClasspath) - val classLoader = ClasspathOps.toClassLoader(props.inputClasspath) + val classLoader = ClasspathOps.toClassLoader(args.validatedClasspath) val tests = TestkitPath.fromProperties(props) tests.map { test => RuleTest.fromPath(props, test, classLoader, symtab) diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/cli/BaseCliSuite.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/cli/BaseCliSuite.scala index e963a8b02..60f76dacb 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/cli/BaseCliSuite.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/cli/BaseCliSuite.scala @@ -10,8 +10,6 @@ import java.nio.file.SimpleFileVisitor import java.nio.file.StandardCopyOption import java.nio.file.attribute.BasicFileAttributes -import scala.collection.immutable.Seq - import scala.meta.internal.io.FileIO import scala.meta.io.AbsolutePath import scala.meta.io.RelativePath @@ -23,6 +21,7 @@ import scalafix.test.StringFS import scalafix.testkit.DiffAssertions import scalafix.testkit.SemanticRuleSuite import scalafix.testkit.TestkitProperties +import scalafix.tests.BuildInfo import scalafix.tests.util.ScalaVersions import scalafix.v1.Main @@ -179,6 +178,8 @@ trait BaseCliSuite extends AnyFunSuite with DiffAssertions { def checkSemantic( name: String, args: Array[String], + targetroots: Seq[String] = + BuildInfo.semanticClasspath.map(_.getAbsolutePath), expectedExit: ExitStatus, preprocess: AbsolutePath => Unit = _ => (), outputAssert: String => Unit = _ => (), @@ -207,11 +208,13 @@ trait BaseCliSuite extends AnyFunSuite with DiffAssertions { val sourceroot = if (args.contains("--sourceroot")) Array[String]() else Array("--sourceroot", cwd.toString) + val targetroots0 = + targetroots.flatMap(Seq("--semanticdb-targetroots", _)) val scalaOption = if (ScalaVersions.isScala213) "-Wunused:imports" else "-Ywarn-unused-import" - val allArguments = args ++ sourceroot ++ Seq( + val allArguments = args ++ sourceroot ++ targetroots0 ++ Seq( "--scalac-options", scalaOption, "-r", diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/cli/CliSemanticSuite.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/cli/CliSemanticSuite.scala index 280e1acda..c0b97a689 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/cli/CliSemanticSuite.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/cli/CliSemanticSuite.scala @@ -9,6 +9,7 @@ import scala.meta.io.Classpath import scala.meta.testkit.StringFS import scalafix.cli._ +import scalafix.tests.BuildInfo import scalafix.tests.core.Classpaths class CliSemanticSuite extends BaseCliSuite { @@ -50,6 +51,7 @@ class CliSemanticSuite extends BaseCliSuite { checkSemantic( name = "missing --classpath", args = Array(), + targetroots = Seq.empty, expectedExit = ExitStatus.MissingSemanticdbError ) @@ -170,13 +172,13 @@ class CliSemanticSuite extends BaseCliSuite { checkSemantic( name = "-P:semanticdb:targetroot", args = { - val (_ :: targetroot :: Nil, jars) = - props.inputClasspath.entries.partition(_.isDirectory) + val jars = props.inputClasspath.entries.filter(_.isDirectory) + val targetroot = BuildInfo.semanticClasspath.last.getAbsolutePath Array( s"--scalacOptions", s"-P:semanticdb:targetroot:shouldBeIgnored", s"--scalacOptions", - s"-P:semanticdb:targetroot:${targetroot.toString()}", + s"-P:semanticdb:targetroot:$targetroot", "--classpath", Classpath(jars).syntax ) diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/core/BaseSemanticSuite.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/core/BaseSemanticSuite.scala index 5cb0dfed8..0237c8808 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/core/BaseSemanticSuite.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/core/BaseSemanticSuite.scala @@ -31,7 +31,9 @@ object BaseSemanticSuite { SemanticDocument.fromPath( doc, relpath, - ClasspathOps.thisClassLoaderWith(BuildInfo.semanticClasspath.toURI.toURL), + ClasspathOps.thisClassLoaderWith( + BuildInfo.semanticClasspath.map(_.toURI.toURL) + ), symtab ) } @@ -50,9 +52,10 @@ abstract class BaseSemanticSuite(filename: String) } override def beforeAll(): Unit = { - val dir = AbsolutePath(scalafix.tests.BuildInfo.semanticClasspath) + val dirs = + scalafix.tests.BuildInfo.semanticClasspath.map(AbsolutePath.apply) _db = LegacyInMemorySemanticdbIndex.load( - Classpaths.withDirectory(dir), + Classpaths.withDirectories(dirs.toList), PathIO.workingDirectory ) _input = _db.inputs diff --git a/scalafix-tests/unit/src/test/scala/scalafix/tests/core/PrettyTypeSuite.scala b/scalafix-tests/unit/src/test/scala/scalafix/tests/core/PrettyTypeSuite.scala index 3b02ec4dc..a518f2e49 100644 --- a/scalafix-tests/unit/src/test/scala/scalafix/tests/core/PrettyTypeSuite.scala +++ b/scalafix-tests/unit/src/test/scala/scalafix/tests/core/PrettyTypeSuite.scala @@ -24,11 +24,11 @@ class BasePrettyTypeSuite extends BaseSemanticSuite("TypeToTreeInput") { super.beforeAll() val classDir: m.AbsolutePath = m.AbsolutePath(scalafix.tests.BuildInfo.sharedClasspath) - val semanticdbTargetRoot: AbsolutePath = - m.AbsolutePath(scalafix.tests.BuildInfo.semanticClasspath) + val semanticdbTargetRoots: List[AbsolutePath] = + scalafix.tests.BuildInfo.semanticClasspath.map(m.AbsolutePath.apply).toList val classpath: Classpath = - Classpaths.withDirectories(List(semanticdbTargetRoot, classDir)) + Classpaths.withDirectories(semanticdbTargetRoots :+ classDir) val table: GlobalSymbolTable = GlobalSymbolTable(classpath, includeJdk = true) }