diff --git a/.drone.yml b/.drone.yml index 6062e1e77999..07ee3a45f974 100644 --- a/.drone.yml +++ b/.drone.yml @@ -23,7 +23,7 @@ pipeline: # We run tests in parallel. Tests run in a copy of the working directory to avoid conflict test: group: test - image: lampepfl/dotty:2018-01-17 + image: lampepfl/dotty:2018-04-10 commands: - cp -R . /tmp/1/ && cd /tmp/1/ - ./project/scripts/sbt ";compile ;test" @@ -31,31 +31,31 @@ pipeline: test_bootstrapped: group: test - image: lampepfl/dotty:2018-01-17 + image: lampepfl/dotty:2018-04-10 commands: - cp -R . /tmp/2/ && cd /tmp/2/ - ./project/scripts/sbt ";dotty-bootstrapped/compile ;dotty-bootstrapped/test" test_optimised: group: test - image: lampepfl/dotty:2018-01-17 + image: lampepfl/dotty:2018-04-10 commands: - cp -R . /tmp/3/ && cd /tmp/3/ - ./project/scripts/sbt dotty-optimised/test test_sbt: group: test - image: lampepfl/dotty:2018-01-17 + image: lampepfl/dotty:2018-04-10 commands: - cp -R . /tmp/4/ && cd /tmp/4/ - ./project/scripts/sbt sbt-dotty/scripted when: - # sbt scripted tests are slow and don't run on PRs + # sbt scripted tests are slow and only run on nightly or deployment event: [ tag, deployment ] # DOCUMENTATION: documentation: - image: lampepfl/dotty:2018-01-17 + image: lampepfl/dotty:2018-04-10 commands: - ./project/scripts/genDocs secrets: [ bot_pass ] @@ -67,7 +67,7 @@ pipeline: # PUBLISHING: # Publishing expect NIGHTLYBUILD or RELEASEBUILD to be set. See dottyVersion in Build.scala publish_nightly: - image: lampepfl/dotty:2018-01-17 + image: lampepfl/dotty:2018-04-10 environment: - NIGHTLYBUILD=yes commands: @@ -78,7 +78,7 @@ pipeline: environment: nightly publish_release: - image: lampepfl/dotty:2018-01-17 + image: lampepfl/dotty:2018-04-10 environment: - RELEASEBUILD=yes commands: @@ -102,7 +102,7 @@ pipeline: event: tag publish_sbt_release: - image: lampepfl/dotty:2018-01-17 + image: lampepfl/dotty:2018-04-10 environment: - RELEASEBUILD=yes commands: diff --git a/.gitignore b/.gitignore index 4d8ebc8de2fc..ae37d91100a3 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ project/local-plugins.sbt .history .ensime .ensime_cache/ +.sbt-scripted/ # npm node_modules diff --git a/build.sbt b/build.sbt index 46468833a7d2..d045d37f2710 100644 --- a/build.sbt +++ b/build.sbt @@ -14,7 +14,6 @@ val `dotty-library-optimised` = Build.`dotty-library-optimised` val `dotty-sbt-bridge` = Build.`dotty-sbt-bridge` val `dotty-sbt-bridge-bootstrapped` = Build.`dotty-sbt-bridge-bootstrapped` val `dotty-language-server` = Build.`dotty-language-server` -val sjsSandbox = Build.sjsSandbox val `dotty-bench` = Build.`dotty-bench` val `dotty-bench-bootstrapped` = Build.`dotty-bench-bootstrapped` val `dotty-bench-optimised` = Build.`dotty-bench-optimised` diff --git a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala index ca21867fd711..06398c936083 100644 --- a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala +++ b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala @@ -13,11 +13,13 @@ import scala.tools.nsc.backend.jvm._ import dotty.tools.dotc import dotty.tools.dotc.backend.jvm.DottyPrimitives import dotty.tools.dotc.transform.Erasure +import dotty.tools.dotc.transform.SymUtils._ import dotty.tools.dotc.interfaces import java.util.Optional import scala.reflect.ClassTag import dotty.tools.dotc.core._ +import dotty.tools.dotc.sbt.ExtractDependencies import Periods._ import SymDenotations._ import Contexts._ @@ -113,14 +115,16 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter /* ---------------- q2 ---------------- */ - case class Item2(arrivalPos: Int, - mirror: asm.tree.ClassNode, - plain: asm.tree.ClassNode, - outFolder: scala.tools.nsc.io.AbstractFile) { + case class SubItem2(classNode: asm.tree.ClassNode, + file: scala.tools.nsc.io.AbstractFile) + + case class Item2(arrivalPos: Int, + mirror: SubItem2, + plain: SubItem2) { def isPoison = { arrivalPos == Int.MaxValue } } - private val poison2 = Item2(Int.MaxValue, null, null, null) + private val poison2 = Item2(Int.MaxValue, null, null) private val q2 = new _root_.java.util.LinkedList[Item2] /* ---------------- q3 ---------------- */ @@ -134,13 +138,13 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter */ case class SubItem3( jclassName: String, - jclassBytes: Array[Byte] + jclassBytes: Array[Byte], + jclassFile: scala.tools.nsc.io.AbstractFile ) case class Item3(arrivalPos: Int, mirror: SubItem3, - plain: SubItem3, - outFolder: scala.tools.nsc.io.AbstractFile) { + plain: SubItem3) { def isPoison = { arrivalPos == Int.MaxValue } } @@ -151,7 +155,7 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter else 1 } } - private val poison3 = Item3(Int.MaxValue, null, null, null) + private val poison3 = Item3(Int.MaxValue, null, null) private val q3 = new java.util.PriorityQueue[Item3](1000, i3comparator) /* @@ -159,7 +163,23 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter */ class Worker1(needsOutFolder: Boolean) { - val caseInsensitively = scala.collection.mutable.HashMap.empty[String, Symbol] + private val lowerCaseNames = mutable.HashMap.empty[String, Symbol] + private def checkForCaseConflict(javaClassName: String, classSymbol: Symbol) = { + val lowerCaseName = javaClassName.toLowerCase + lowerCaseNames.get(lowerCaseName) match { + case None => + lowerCaseNames.put(lowerCaseName, classSymbol) + case Some(dupClassSym) => + // Order is not deterministic so we enforce lexicographic order between the duplicates for error-reporting + val (cl1, cl2) = + if (classSymbol.effectiveName.toString < dupClassSym.effectiveName.toString) (classSymbol, dupClassSym) + else (dupClassSym, classSymbol) + ctx.atPhase(ctx.typerPhase) { implicit ctx => + ctx.warning(s"${cl1.show} differs only in case from ${cl2.showLocated}. " + + "Such classes will overwrite one another on case-insensitive filesystems.", cl1.pos) + } + } + } def run(): Unit = { while (true) { @@ -189,30 +209,6 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter val Item1(arrivalPos, cd, cunit) = item val claszSymbol = cd.symbol - // GenASM checks this before classfiles are emitted, https://github.com/scala/scala/commit/e4d1d930693ac75d8eb64c2c3c69f2fc22bec739 - def checkName(claszSymbol: Symbol): Unit = { - val lowercaseJavaClassName = claszSymbol.effectiveName.toString.toLowerCase - caseInsensitively.get(lowercaseJavaClassName) match { - case None => - caseInsensitively.put(lowercaseJavaClassName, claszSymbol) - case Some(dupClassSym) => - if (claszSymbol.effectiveName.toString != dupClassSym.effectiveName.toString) { - // Order is not deterministic so we enforce lexicographic order between the duplicates for error-reporting - val (cl1, cl2) = - if (claszSymbol.effectiveName.toString < dupClassSym.effectiveName.toString) (claszSymbol, dupClassSym) - else (dupClassSym, claszSymbol) - ctx.warning(s"Class ${cl1.effectiveName} differs only in case from ${cl2.effectiveName}. " + - "Such classes will overwrite one another on case-insensitive filesystems.", cl1.pos) - } - } - } - checkName(claszSymbol) - if (int.symHelper(claszSymbol).isModuleClass) { - val companionModule = claszSymbol.companionModule - if (int.symHelper(companionModule.owner).isPackageClass) - checkName(companionModule) - } - // -------------- mirror class, if needed -------------- val mirrorC = if (int.symHelper(claszSymbol).isTopLevelModuleClass) { @@ -253,12 +249,50 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter } + // ----------- create files + + val classNodes = List(mirrorC, plainC) + val classFiles = classNodes.map(cls => + if (outF != null && cls != null) { + try { + checkForCaseConflict(cls.name, claszSymbol) + getFileForClassfile(outF, cls.name, ".class") + } catch { + case e: FileConflictException => + ctx.error(s"error writing ${cls.name}: ${e.getMessage}") + null + } + } else null + ) + + // ----------- compiler and sbt's callbacks + + val (fullClassName, isLocal) = ctx.atPhase(ctx.sbtExtractDependenciesPhase) { implicit ctx => + (ExtractDependencies.classNameAsString(claszSymbol), claszSymbol.isLocal) + } + + for ((cls, clsFile) <- classNodes.zip(classFiles)) { + if (cls != null) { + val className = cls.name.replace('/', '.') + if (ctx.compilerCallback != null) + ctx.compilerCallback.onClassGenerated(sourceFile, convertAbstractFile(clsFile), className) + if (ctx.sbtCallback != null) { + if (isLocal) + ctx.sbtCallback.generatedLocalClass(sourceFile.jfile.orElse(null), clsFile.file) + else { + ctx.sbtCallback.generatedNonLocalClass(sourceFile.jfile.orElse(null), clsFile.file, + className, fullClassName) + } + } + } + } + // ----------- hand over to pipeline-2 val item2 = Item2(arrivalPos, - mirrorC, plainC, - outF) + SubItem2(mirrorC, classFiles(0)), + SubItem2(plainC, classFiles(1))) q2 add item2 // at the very end of this method so that no Worker2 thread starts mutating before we're done. @@ -288,12 +322,12 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter } else { try { - localOptimizations(item.plain) + localOptimizations(item.plain.classNode) addToQ3(item) } catch { case ex: Throwable => ex.printStackTrace() - ctx.error(s"Error while emitting ${item.plain.name}\n${ex.getMessage}") + ctx.error(s"Error while emitting ${item.plain.classNode.name}\n${ex.getMessage}") } } } @@ -307,18 +341,17 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter cw.toByteArray } - val Item2(arrivalPos, mirror, plain, outFolder) = item + val Item2(arrivalPos, SubItem2(mirror, mirrorFile), SubItem2(plain, plainFile)) = item - val mirrorC = if (mirror == null) null else SubItem3(mirror.name, getByteArray(mirror)) - val plainC = SubItem3(plain.name, getByteArray(plain)) + val mirrorC = if (mirror == null) null else SubItem3(mirror.name, getByteArray(mirror), mirrorFile) + val plainC = SubItem3(plain.name, getByteArray(plain), plainFile) if (AsmUtils.traceSerializedClassEnabled && plain.name.contains(AsmUtils.traceSerializedClassPattern)) { if (mirrorC != null) AsmUtils.traceClass(mirrorC.jclassBytes) AsmUtils.traceClass(plainC.jclassBytes) } - q3 add Item3(arrivalPos, mirrorC, plainC, outFolder) - + q3 add Item3(arrivalPos, mirrorC, plainC) } } // end of class BCodePhase.Worker2 @@ -416,25 +449,10 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter /* Pipeline that writes classfile representations to disk. */ private def drainQ3() = { - def sendToDisk(cfr: SubItem3, outFolder: scala.tools.nsc.io.AbstractFile): Unit = { + def sendToDisk(cfr: SubItem3): Unit = { if (cfr != null){ - val SubItem3(jclassName, jclassBytes) = cfr - try { - val outFile = - if (outFolder == null) null - else getFileForClassfile(outFolder, jclassName, ".class") - bytecodeWriter.writeClass(jclassName, jclassName, jclassBytes, outFile) - - val className = jclassName.replace('/', '.') - if (ctx.compilerCallback != null) - ctx.compilerCallback.onClassGenerated(sourceFile, convertAbstractFile(outFile), className) - if (ctx.sbtCallback != null) - ctx.sbtCallback.generatedClass(sourceFile.jfile.orElse(null), outFile.file, className) - } - catch { - case e: FileConflictException => - ctx.error(s"error writing $jclassName: ${e.getMessage}") - } + val SubItem3(jclassName, jclassBytes, jclassFile) = cfr + bytecodeWriter.writeClass(jclassName, jclassName, jclassBytes, jclassFile) } } @@ -447,9 +465,8 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter moreComing = !incoming.isPoison if (moreComing) { val item = incoming - val outFolder = item.outFolder - sendToDisk(item.mirror, outFolder) - sendToDisk(item.plain, outFolder) + sendToDisk(item.mirror) + sendToDisk(item.plain) expected += 1 } } diff --git a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala index 03eb441852f0..08a67ab3d1f0 100644 --- a/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala +++ b/compiler/src/dotty/tools/dotc/config/JavaPlatform.scala @@ -21,19 +21,12 @@ class JavaPlatform extends Platform { } // The given symbol is a method with the right name and signature to be a runnable java program. - def isJavaMainMethod(sym: SymDenotation)(implicit ctx: Context) = + def isMainMethod(sym: SymDenotation)(implicit ctx: Context) = (sym.name == nme.main) && (sym.info match { case MethodTpe(_, defn.ArrayOf(el) :: Nil, restpe) => el =:= defn.StringType && (restpe isRef defn.UnitClass) case _ => false }) - // The given class has a main method. - def hasJavaMainMethod(sym: Symbol)(implicit ctx: Context): Boolean = - (sym.info member nme.main).hasAltWith { - case x: SymDenotation => isJavaMainMethod(x) - case _ => false - } - /** Update classpath with a substituted subentry */ def updateClassPath(subst: Map[ClassPath, ClassPath]): Unit = currentClassPath.get match { case AggregateClassPath(entries) => diff --git a/compiler/src/dotty/tools/dotc/config/Platform.scala b/compiler/src/dotty/tools/dotc/config/Platform.scala index 062d9002d186..71e0d9cd107c 100644 --- a/compiler/src/dotty/tools/dotc/config/Platform.scala +++ b/compiler/src/dotty/tools/dotc/config/Platform.scala @@ -10,6 +10,8 @@ package config import io.{ClassPath, AbstractFile} import core.Contexts._, core.Symbols._ import core.SymbolLoader +import core.SymDenotations.SymDenotation +import core.StdNames.nme /** The platform dependent pieces of Global. */ @@ -35,5 +37,15 @@ abstract class Platform { /** Create a new class loader to load class file `bin` */ def newClassLoader(bin: AbstractFile)(implicit ctx: Context): SymbolLoader + + /** The given symbol is a method with the right name and signature to be a runnable program. */ + def isMainMethod(sym: SymDenotation)(implicit ctx: Context): Boolean + + /** The given class has a main method. */ + final def hasMainMethod(sym: Symbol)(implicit ctx: Context): Boolean = + sym.info.member(nme.main).hasAltWith { + case x: SymDenotation => isMainMethod(x) + case _ => false + } } diff --git a/compiler/src/dotty/tools/dotc/core/Names.scala b/compiler/src/dotty/tools/dotc/core/Names.scala index deaa29ecf891..1c2af332f8eb 100644 --- a/compiler/src/dotty/tools/dotc/core/Names.scala +++ b/compiler/src/dotty/tools/dotc/core/Names.scala @@ -422,7 +422,8 @@ object Names { "dotty$tools$dotc$core$NameOps$NameDecorator$$functionArityFor$extension", "dotty$tools$dotc$typer$Checking$CheckNonCyclicMap$$apply", "$plus$plus", - "readConstant") + "readConstant", + "extractedName") .contains(elem.getMethodName)) } diff --git a/compiler/src/dotty/tools/dotc/core/Phases.scala b/compiler/src/dotty/tools/dotc/core/Phases.scala index 85acb49e6eaf..555dd06feb11 100644 --- a/compiler/src/dotty/tools/dotc/core/Phases.scala +++ b/compiler/src/dotty/tools/dotc/core/Phases.scala @@ -209,6 +209,7 @@ object Phases { } private[this] var myTyperPhase: Phase = _ + private[this] var mySbtExtractDependenciesPhase: Phase = _ private[this] var myPicklerPhase: Phase = _ private[this] var myRefChecksPhase: Phase = _ private[this] var myPatmatPhase: Phase = _ @@ -223,6 +224,7 @@ object Phases { private[this] var myGenBCodePhase: Phase = _ final def typerPhase = myTyperPhase + final def sbtExtractDependenciesPhase = mySbtExtractDependenciesPhase final def picklerPhase = myPicklerPhase final def refchecksPhase = myRefChecksPhase final def patmatPhase = myPatmatPhase @@ -240,6 +242,7 @@ object Phases { def phaseOfClass(pclass: Class[_]) = phases.find(pclass.isInstance).getOrElse(NoPhase) myTyperPhase = phaseOfClass(classOf[FrontEnd]) + mySbtExtractDependenciesPhase = phaseOfClass(classOf[sbt.ExtractDependencies]) myPicklerPhase = phaseOfClass(classOf[Pickler]) myRefChecksPhase = phaseOfClass(classOf[RefChecks]) myElimRepeatedPhase = phaseOfClass(classOf[ElimRepeated]) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index c190dfb7262c..2003b086780e 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -12,6 +12,7 @@ import collection.BitSet import dotty.tools.io.AbstractFile import Decorators.SymbolIteratorDecorator import ast._ +import ast.Trees._ import annotation.tailrec import CheckRealizable._ import util.SimpleIdentityMap @@ -929,17 +930,18 @@ object SymDenotations { * except for a toplevel module, where its module class is returned. */ final def topLevelClass(implicit ctx: Context): Symbol = { - - def topLevel(d: SymDenotation): Symbol = - if (!exists || d.isEffectiveRoot || (d is PackageClass) || (d.owner is PackageClass)) - d.symbol - else - topLevel(d.owner) + @tailrec def topLevel(d: SymDenotation): Symbol = { + if (d.isTopLevelClass) d.symbol + else topLevel(d.owner) + } val sym = topLevel(this) if (sym.isClass) sym else sym.moduleClass } + final def isTopLevelClass(implicit ctx: Context): Boolean = + !this.exists || this.isEffectiveRoot || (this is PackageClass) || (this.owner is PackageClass) + /** The package class containing this denotation */ final def enclosingPackageClass(implicit ctx: Context): Symbol = if (this is PackageClass) symbol else owner.enclosingPackageClass diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 5b7a6b311ab2..6db3ecf9f1a9 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -568,13 +568,13 @@ object Symbols { /** The class file from which this class was generated, null if not applicable. */ final def binaryFile(implicit ctx: Context): AbstractFile = { val file = associatedFile - if (file != null && file.path.endsWith("class")) file else null + if (file != null && file.extension == "class") file else null } /** The source file from which this class was generated, null if not applicable. */ final def sourceFile(implicit ctx: Context): AbstractFile = { val file = associatedFile - if (file != null && !file.path.endsWith("class")) file + if (file != null && file.extension != "class") file else { val topLevelCls = denot.topLevelClass(ctx.withPhaseNoLater(ctx.flattenPhase)) topLevelCls.getAnnotation(defn.SourceFileAnnot) match { diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index 4be68b0879d4..3bcf99e8b3cc 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -2,17 +2,26 @@ package dotty.tools.dotc package sbt import ast.{Trees, tpd} -import core._, core.Decorators._ -import Annotations._, Contexts._, Flags._, Phases._, Trees._, Types._, Symbols._ -import Names._, NameOps._, StdNames._ +import core._ +import core.Decorators._ +import Annotations._ +import Contexts._ +import Flags._ +import Phases._ +import Trees._ +import Types._ +import Symbols._ +import NameOps._ import NameKinds.DefaultGetterName import typer.Inliner import typer.ErrorReporting.cyclicErrorMsg +import transform.ValueClasses import transform.SymUtils._ - -import dotty.tools.io.Path +import dotty.tools.io.File import java.io.PrintWriter +import xsbti.api.DefinitionType + import scala.collection.mutable /** This phase sends a representation of the API of classes to sbt via callbacks. @@ -46,20 +55,26 @@ class ExtractAPI extends Phase { val forceRun = dumpInc || ctx.settings.YforceSbtPhases.value if ((ctx.sbtCallback != null || forceRun) && !unit.isJava) { val sourceFile = unit.source.file + if (ctx.sbtCallback != null) + ctx.sbtCallback.startSource(sourceFile.file) + val apiTraverser = new ExtractAPICollector - val source = apiTraverser.apiSource(unit.tpdTree) + val classes = apiTraverser.apiSource(unit.tpdTree) + val mainClasses = apiTraverser.mainClasses if (dumpInc) { // Append to existing file that should have been created by ExtractDependencies - val pw = new PrintWriter(Path(sourceFile.jpath).changeExtension("inc").toFile + val pw = new PrintWriter(File(sourceFile.jpath).changeExtension("inc").toFile .bufferedWriter(append = true), true) try { - pw.println(DefaultShowAPI(source)) + classes.foreach(source => pw.println(DefaultShowAPI(source))) } finally pw.close() } - if (ctx.sbtCallback != null) - ctx.sbtCallback.api(sourceFile.file, source) + if (ctx.sbtCallback != null) { + classes.foreach(ctx.sbtCallback.api(sourceFile.file, _)) + mainClasses.foreach(ctx.sbtCallback.mainClass(sourceFile.file, _)) + } } } } @@ -113,24 +128,27 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder /** This cache is necessary for correctness, see the comment about inherited * members in `apiClassStructure` */ - private[this] val classLikeCache = new mutable.HashMap[ClassSymbol, api.ClassLike] + private[this] val classLikeCache = new mutable.HashMap[ClassSymbol, api.ClassLikeDef] /** This cache is optional, it avoids recomputing representations */ private[this] val typeCache = new mutable.HashMap[Type, api.Type] /** This cache is necessary to avoid unstable name hashing when `typeCache` is present, * see the comment in the `RefinedType` case in `computeType` * The cache key is (api of RefinedType#parent, api of RefinedType#refinedInfo). - */ + */ private[this] val refinedTypeCache = new mutable.HashMap[(api.Type, api.Definition), api.Structure] + private[this] val allNonLocalClassesInSrc = new mutable.HashSet[xsbti.api.ClassLike] + private[this] val _mainClasses = new mutable.HashSet[String] + private[this] object Constants { val emptyStringArray = Array[String]() - val local = new api.ThisQualifier - val public = new api.Public - val privateLocal = new api.Private(local) - val protectedLocal = new api.Protected(local) - val unqualified = new api.Unqualified - val thisPath = new api.This - val emptyType = new api.EmptyType + val local = api.ThisQualifier.create() + val public = api.Public.create() + val privateLocal = api.Private.create(local) + val protectedLocal = api.Protected.create(local) + val unqualified = api.Unqualified.create() + val thisPath = api.This.create() + val emptyType = api.EmptyType.create() val emptyModifiers = new api.Modifiers(false, false, false, false, false,false, false, false) } @@ -144,33 +162,38 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder * @param marker A special annotation to differentiate our type */ private def withMarker(tp: api.Type, marker: api.Annotation) = - new api.Annotated(tp, Array(marker)) + api.Annotated.of(tp, Array(marker)) private def marker(name: String) = - new api.Annotation(new api.Constant(Constants.emptyType, name), Array()) + api.Annotation.of(api.Constant.of(Constants.emptyType, name), Array()) val orMarker = marker("Or") val byNameMarker = marker("ByName") /** Extract the API representation of a source file */ - def apiSource(tree: Tree): api.SourceAPI = { - val classes = new mutable.ListBuffer[api.ClassLike] + def apiSource(tree: Tree): Seq[api.ClassLike] = { def apiClasses(tree: Tree): Unit = tree match { case PackageDef(_, stats) => stats.foreach(apiClasses) case tree: TypeDef => - classes += apiClass(tree.symbol.asClass) + apiClass(tree.symbol.asClass) case _ => } apiClasses(tree) forceThunks() - new api.SourceAPI(Array(), classes.toArray) + + allNonLocalClassesInSrc.toSeq } - def apiClass(sym: ClassSymbol): api.ClassLike = + def apiClass(sym: ClassSymbol): api.ClassLikeDef = classLikeCache.getOrElseUpdate(sym, computeClass(sym)) - private def computeClass(sym: ClassSymbol): api.ClassLike = { + def mainClasses: Set[String] = { + forceThunks() + _mainClasses.toSet + } + + private def computeClass(sym: ClassSymbol): api.ClassLikeDef = { import xsbti.api.{DefinitionType => dt} val defType = if (sym.is(Trait)) dt.Trait @@ -181,16 +204,34 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder val selfType = apiType(sym.givenSelfType) - val name = if (sym.is(ModuleClass)) sym.fullName.sourceModuleName else sym.fullName + val name = sym.fullName.stripModuleClassSuffix.toString + // We strip module class suffix. Zinc relies on a class and its companion having the same name - val tparams = sym.typeParams.map(apiTypeParameter) + val tparams = sym.typeParams.map(apiTypeParameter).toArray val structure = apiClassStructure(sym) + val acc = apiAccess(sym) + val modifiers = apiModifiers(sym) + val anns = apiAnnotations(sym).toArray + val topLevel = sym.isTopLevelClass + val childrenOfSealedClass = sym.children.sorted(classFirstSort).map(c => + if (c.isClass) + apiType(c.typeRef) + else + apiType(c.termRef) + ).toArray + + val cl = api.ClassLike.of( + name, acc, modifiers, anns, defType, api.SafeLazy.strict(selfType), api.SafeLazy.strict(structure), Constants.emptyStringArray, + childrenOfSealedClass, topLevel, tparams) - new api.ClassLike( - defType, strict2lzy(selfType), strict2lzy(structure), Constants.emptyStringArray, - tparams.toArray, name.toString, apiAccess(sym), apiModifiers(sym), - apiAnnotations(sym).toArray) + allNonLocalClassesInSrc += cl + + if (sym.isStatic && defType == DefinitionType.Module && ctx.platform.hasMainMethod(sym)) { + _mainClasses += name + } + + api.ClassLikeDef.of(name, acc, modifiers, anns, tparams, defType) } private[this] val LegacyAppClass = ctx.requiredClass("dotty.runtime.LegacyApp") @@ -198,15 +239,25 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder def apiClassStructure(csym: ClassSymbol): api.Structure = { val cinfo = csym.classInfo - val bases = - try linearizedAncestorTypes(cinfo) - catch { - case ex: CyclicReference => - // See neg/i1750a for an example where a cyclic error can arise. - // The root cause in this example is an illegal "override" of an inner trait - ctx.error(cyclicErrorMsg(ex), csym.pos) - defn.ObjectType :: Nil - } + val bases = { + val ancestorTypes0 = + try linearizedAncestorTypes(cinfo) + catch { + case ex: CyclicReference => + // See neg/i1750a for an example where a cyclic error can arise. + // The root cause in this example is an illegal "override" of an inner trait + ctx.error(cyclicErrorMsg(ex), csym.pos) + defn.ObjectType :: Nil + } + if (ValueClasses.isDerivedValueClass(csym)) { + val underlying = ValueClasses.valueClassUnbox(csym).info.finalResultType + // The underlying type of a value class should be part of the name hash + // of the value class (see the test `value-class-underlying`), this is accomplished + // by adding the underlying type to the list of parent types. + underlying :: ancestorTypes0 + } else + ancestorTypes0 + } val apiBases = bases.map(apiType) @@ -230,7 +281,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder // this works because of `classLikeCache` val apiInherited = lzy(apiDefinitions(inherited).toArray) - new api.Structure(strict2lzy(apiBases.toArray), strict2lzy(apiDecls.toArray), apiInherited) + api.Structure.of(api.SafeLazy.strict(apiBases.toArray), api.SafeLazy.strict(apiDecls.toArray), apiInherited) } def linearizedAncestorTypes(info: ClassInfo): List[Type] = { @@ -239,48 +290,48 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder info.baseClasses.tail.map(ref.baseType) } - def apiDefinitions(defs: List[Symbol]): List[api.Definition] = { - // The hash generated by sbt for definitions is supposed to be symmetric so - // we shouldn't have to sort them, but it actually isn't symmetric for - // definitions which are classes, therefore we need to sort classes to - // ensure a stable hash. - // Modules and classes come first and are sorted by name, all other - // definitions come later and are not sorted. - object classFirstSort extends Ordering[Symbol] { - override def compare(a: Symbol, b: Symbol) = { - val aIsClass = a.isClass - val bIsClass = b.isClass - if (aIsClass == bIsClass) { - if (aIsClass) { - if (a.is(Module) == b.is(Module)) - a.fullName.toString.compareTo(b.fullName.toString) - else if (a.is(Module)) - -1 - else - 1 - } else - 0 - } else if (aIsClass) - -1 - else - 1 - } + // The hash generated by sbt for definitions is supposed to be symmetric so + // we shouldn't have to sort them, but it actually isn't symmetric for + // definitions which are classes, therefore we need to sort classes to + // ensure a stable hash. + // Modules and classes come first and are sorted by name, all other + // definitions come later and are not sorted. + private object classFirstSort extends Ordering[Symbol] { + override def compare(a: Symbol, b: Symbol) = { + val aIsClass = a.isClass + val bIsClass = b.isClass + if (aIsClass == bIsClass) { + if (aIsClass) { + if (a.is(Module) == b.is(Module)) + a.fullName.toString.compareTo(b.fullName.toString) + else if (a.is(Module)) + -1 + else + 1 + } else + 0 + } else if (aIsClass) + -1 + else + 1 } + } + def apiDefinitions(defs: List[Symbol]): List[api.ClassDefinition] = { defs.sorted(classFirstSort).map(apiDefinition) } - def apiDefinition(sym: Symbol): api.Definition = { + def apiDefinition(sym: Symbol): api.ClassDefinition = { if (sym.isClass) { apiClass(sym.asClass) } else if (sym.isType) { apiTypeMember(sym.asType) } else if (sym.is(Mutable, butNot = Accessor)) { - new api.Var(apiType(sym.info), sym.name.toString, - apiAccess(sym), apiModifiers(sym), apiAnnotations(sym).toArray) + api.Var.of(sym.name.toString, apiAccess(sym), apiModifiers(sym), + apiAnnotations(sym).toArray, apiType(sym.info)) } else if (sym.isStable) { - new api.Val(apiType(sym.info), sym.name.toString, - apiAccess(sym), apiModifiers(sym), apiAnnotations(sym).toArray) + api.Val.of(sym.name.toString, apiAccess(sym), apiModifiers(sym), + apiAnnotations(sym).toArray, apiType(sym.info)) } else { apiDef(sym.asTerm) } @@ -301,14 +352,14 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder sym.owner.companionModule // default getters for class constructors are found in the companion object else sym.owner - (0 until pnames.length).map(i => + pnames.indices.map(i => qual.info.member(DefaultGetterName(sym.name, start + i)).exists) } else - (0 until pnames.length).map(Function.const(false)) + pnames.indices.map(Function.const(false)) val params = (pnames, ptypes, defaults).zipped.map((pname, ptype, isDefault) => - new api.MethodParameter(pname.toString, apiType(ptype), + api.MethodParameter.of(pname.toString, apiType(ptype), isDefault, api.ParameterModifier.Plain)) - new api.ParameterList(params.toArray, mt.isImplicitMethod) :: paramLists(restpe, params.length) + api.ParameterList.of(params.toArray, mt.isImplicitMethod) :: paramLists(restpe, params.length) case _ => Nil } @@ -323,8 +374,8 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder val vparamss = paramLists(sym.info) val retTp = sym.info.finalResultType.widenExpr - new api.Def(vparamss.toArray, apiType(retTp), tparams.toArray, - sym.name.toString, apiAccess(sym), apiModifiers(sym), apiAnnotations(sym).toArray) + api.Def.of(sym.name.toString, apiAccess(sym), apiModifiers(sym), + apiAnnotations(sym).toArray, tparams.toArray, vparamss.toArray, apiType(retTp)) } def apiTypeMember(sym: TypeSymbol): api.TypeMember = { @@ -336,17 +387,17 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder val tpe = sym.info if (sym.isAliasType) - new api.TypeAlias(apiType(tpe.bounds.hi), typeParams, name, access, modifiers, as.toArray) + api.TypeAlias.of(name, access, modifiers, as.toArray, typeParams, apiType(tpe.bounds.hi)) else { assert(sym.isAbstractType) - new api.TypeDeclaration(apiType(tpe.bounds.lo), apiType(tpe.bounds.hi), typeParams, name, access, modifiers, as.to) + api.TypeDeclaration.of(name, access, modifiers, as.toArray, typeParams, apiType(tpe.bounds.lo), apiType(tpe.bounds.hi)) } } // Hack to represent dotty types which don't have an equivalent in xsbti def combineApiTypes(apiTps: api.Type*): api.Type = { - new api.Structure(strict2lzy(apiTps.toArray), - strict2lzy(Array()), strict2lzy(Array())) + api.Structure.of(api.SafeLazy.strict(apiTps.toArray), + api.SafeLazy.strict(Array()), api.SafeLazy.strict(Array())) } def apiType(tp: Type): api.Type = { @@ -371,11 +422,11 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder // that some API changed when it didn't, leading to overcompilation // (recompiling more things than what is needed for incremental // compilation to be correct). - val prefix = if (sym.owner.is(Package)) + val prefix = if (sym.maybeOwner.is(Package)) // { type T } here T does not have an owner sym.owner.thisType else tp.prefix - new api.Projection(simpleType(prefix), sym.name.toString) + api.Projection.of(apiType(prefix), sym.name.toString) case AppliedType(tycon, args) => def processArg(arg: Type): api.Type = arg match { case arg @ TypeBounds(lo, hi) => // Handle wildcard parameters @@ -383,32 +434,32 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder Constants.emptyType else { val name = "_" - val ref = new api.ParameterRef(name) - new api.Existential(ref, + val ref = api.ParameterRef.of(name) + api.Existential.of(ref, Array(apiTypeParameter(name, 0, lo, hi))) } case _ => apiType(arg) } - val apiTycon = simpleType(tycon) + val apiTycon = apiType(tycon) val apiArgs = args.map(processArg) - new api.Parameterized(apiTycon, apiArgs.toArray) + api.Parameterized.of(apiTycon, apiArgs.toArray) case tl: TypeLambda => val apiTparams = tl.typeParams.map(apiTypeParameter) val apiRes = apiType(tl.resType) - new api.Polymorphic(apiRes, apiTparams.toArray) + api.Polymorphic.of(apiRes, apiTparams.toArray) case rt: RefinedType => val name = rt.refinedName.toString val parent = apiType(rt.parent) def typeRefinement(name: String, tp: TypeBounds): api.TypeMember = tp match { case TypeAlias(alias) => - new api.TypeAlias(apiType(alias), - Array(), name, Constants.public, Constants.emptyModifiers, Array()) + api.TypeAlias.of(name, + Constants.public, Constants.emptyModifiers, Array(), Array(), apiType(alias)) case TypeBounds(lo, hi) => - new api.TypeDeclaration(apiType(lo), apiType(hi), - Array(), name, Constants.public, Constants.emptyModifiers, Array()) + api.TypeDeclaration.of(name, + Constants.public, Constants.emptyModifiers, Array(), Array(), apiType(lo), apiType(hi)) } val decl = rt.refinedInfo match { case rinfo: TypeBounds => @@ -440,8 +491,8 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder // `apiFoo == apiBar` always imply `apiFoo eq apiBar`. This is what // `refinedTypeCache` is for. refinedTypeCache.getOrElseUpdate((parent, decl), { - val adecl: Array[api.Definition] = if (decl == null) Array() else Array(decl) - new api.Structure(strict2lzy(Array(parent)), strict2lzy(adecl), strict2lzy(Array())) + val adecl: Array[api.ClassDefinition] = if (decl == null) Array() else Array(decl) + api.Structure.of(api.SafeLazy.strict(Array(parent)), api.SafeLazy.strict(adecl), api.SafeLazy.strict(Array())) }) case tp: RecType => apiType(tp.parent) @@ -458,15 +509,15 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder case ExprType(resultType) => withMarker(apiType(resultType), byNameMarker) case ConstantType(constant) => - new api.Constant(apiType(constant.tpe), constant.stringValue) + api.Constant.of(apiType(constant.tpe), constant.stringValue) case AnnotatedType(tpe, annot) => - new api.Annotated(apiType(tpe), Array(apiAnnotation(annot))) + api.Annotated.of(apiType(tpe), Array(apiAnnotation(annot))) case tp: ThisType => apiThis(tp.cls) case tp: ParamRef => // TODO: Distinguishing parameters based on their names alone is not enough, // the binder is also needed (at least for type lambdas). - new api.ParameterRef(tp.paramName.toString) + api.ParameterRef.of(tp.paramName.toString) case tp: LazyRef => apiType(tp.ref) case tp: TypeVar => @@ -478,26 +529,17 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder } } - // TODO: Get rid of this method. See https://github.com/sbt/zinc/issues/101 - def simpleType(tp: Type): api.SimpleType = apiType(tp) match { - case tp: api.SimpleType => - tp - case _ => - ctx.debuglog("sbt-api: Not a simple type: " + tp.show) - Constants.emptyType - } - def apiLazy(tp: => Type): api.Type = { // TODO: The sbt api needs a convenient way to make a lazy type. // For now, we repurpose Structure for this. val apiTp = lzy(Array(apiType(tp))) - new api.Structure(apiTp, strict2lzy(Array()), strict2lzy(Array())) + api.Structure.of(apiTp, api.SafeLazy.strict(Array()), api.SafeLazy.strict(Array())) } def apiThis(sym: Symbol): api.Singleton = { val pathComponents = sym.ownersIterator.takeWhile(!_.isEffectiveRoot) - .map(s => new api.Id(s.name.toString)) - new api.Singleton(new api.Path(pathComponents.toArray.reverse ++ Array(Constants.thisPath))) + .map(s => api.Id.of(s.name.toString)) + api.Singleton.of(api.Path.of(pathComponents.toArray.reverse ++ Array(Constants.thisPath))) } def apiTypeParameter(tparam: ParamInfo): api.TypeParameter = @@ -505,7 +547,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder tparam.paramInfo.bounds.lo, tparam.paramInfo.bounds.hi) def apiTypeParameter(name: String, variance: Int, lo: Type, hi: Type): api.TypeParameter = - new api.TypeParameter(name, Array(), Array(), apiVariance(variance), + api.TypeParameter.of(name, Array(), Array(), apiVariance(variance), apiType(lo), apiType(hi)) def apiVariance(v: Int): api.Variance = { @@ -529,11 +571,11 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder if (sym.privateWithin eq NoSymbol) Constants.unqualified else - new api.IdQualifier(sym.privateWithin.fullName.toString) + api.IdQualifier.of(sym.privateWithin.fullName.toString) if (sym.is(Protected)) - new api.Protected(qualifier) + api.Protected.of(qualifier) else - new api.Private(qualifier) + api.Private.of(qualifier) } } @@ -556,7 +598,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder // To do this properly we would need a way to hash trees and types in // dotty itself. val printTypesCtx = ctx.fresh.setSetting(ctx.settings.XprintTypes, true) - annots += marker(Inliner.bodyToInline(s).show(printTypesCtx).toString) + annots += marker(Inliner.bodyToInline(s).show(printTypesCtx)) } // In the Scala2 ExtractAPI phase we only extract annotations that extend @@ -578,8 +620,8 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder // However, we still need to extract the annotation type in the way sbt expect // because sbt uses this information to find tests to run (for example // junit tests are annotated @org.junit.Test). - new api.Annotation( + api.Annotation.of( apiType(annot.tree.tpe), // Used by sbt to find tests to run - Array(new api.AnnotationArgument("FULLTREE", annot.tree.show.toString))) + Array(api.AnnotationArgument.of("FULLTREE", annot.tree.show))) } } diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala index 32b9827e8535..fdced5f83953 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala @@ -1,19 +1,29 @@ package dotty.tools.dotc package sbt -import ast.{Trees, tpd} -import core._, core.Decorators._ -import Contexts._, Flags._, Phases._, Trees._, Types._, Symbols._ -import Names._, NameOps._, StdNames._ - -import scala.collection.{Set, mutable} - -import dotty.tools.io.{AbstractFile, Path, ZipArchive, PlainFile} import java.io.File +import java.util.{Arrays, EnumSet} -import java.util.{Arrays, Comparator} +import dotty.tools.dotc.ast.Trees._ +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Contexts._ +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.NameOps._ +import dotty.tools.dotc.core.Names._ +import dotty.tools.dotc.core.Phases._ +import dotty.tools.dotc.core.StdNames._ +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types._ +import dotty.tools.dotc.transform.SymUtils._ +import dotty.tools.io +import dotty.tools.io.{AbstractFile, PlainFile, ZipArchive} +import xsbti.UseScope +import xsbti.api.DependencyContext +import xsbti.api.DependencyContext._ + +import scala.collection.{Set, mutable} -import xsbti.DependencyContext /** This phase sends information on classes' dependencies to sbt via callbacks. * @@ -35,6 +45,8 @@ import xsbti.DependencyContext * @see ExtractAPI */ class ExtractDependencies extends Phase { + import ExtractDependencies._ + override def phaseName: String = "sbt-deps" // This phase should be run directly after `Frontend`, if it is run after @@ -46,78 +58,129 @@ class ExtractDependencies extends Phase { val unit = ctx.compilationUnit val dumpInc = ctx.settings.YdumpSbtInc.value val forceRun = dumpInc || ctx.settings.YforceSbtPhases.value - if ((ctx.sbtCallback != null || forceRun) && !unit.isJava) { - val sourceFile = unit.source.file - val extractDeps = new ExtractDependenciesCollector - extractDeps.traverse(unit.tpdTree) + val shouldRun = !unit.isJava && (ctx.sbtCallback != null || forceRun) + + if (shouldRun) { + val collector = new ExtractDependenciesCollector + collector.traverse(unit.tpdTree) if (dumpInc) { - val names = extractDeps.usedNames.map(_.toString).toArray[Object] - val deps = extractDeps.topLevelDependencies.map(_.toString).toArray[Object] - val inhDeps = extractDeps.topLevelInheritanceDependencies.map(_.toString).toArray[Object] - Arrays.sort(names) + val deps = collector.dependencies.map(_.toString).toArray[Object] + val names = collector.usedNames.map { case (clazz, names) => s"$clazz: $names" }.toArray[Object] Arrays.sort(deps) - Arrays.sort(inhDeps) + Arrays.sort(names) - val pw = Path(sourceFile.jpath).changeExtension("inc").toFile.printWriter() + val pw = io.File(unit.source.file.jpath).changeExtension("inc").toFile.printWriter() + // val pw = Console.out try { - pw.println(s"// usedNames: ${names.mkString(",")}") - pw.println(s"// topLevelDependencies: ${deps.mkString(",")}") - pw.println(s"// topLevelInheritanceDependencies: ${inhDeps.mkString(",")}") + pw.println("Used Names:") + pw.println("===========") + names.foreach(pw.println) + pw.println() + pw.println("Dependencies:") + pw.println("=============") + deps.foreach(pw.println) } finally pw.close() } if (ctx.sbtCallback != null) { - extractDeps.usedNames.foreach(name => - ctx.sbtCallback.usedName(sourceFile.file, name.toString)) - extractDeps.topLevelDependencies.foreach(dep => - recordDependency(sourceFile.file, dep, DependencyContext.DependencyByMemberRef)) - extractDeps.topLevelInheritanceDependencies.foreach(dep => - recordDependency(sourceFile.file, dep, DependencyContext.DependencyByInheritance)) + collector.usedNames.foreach { + case (clazz, usedNames) => + val className = classNameAsString(clazz) + usedNames.names.foreach { + case (usedName, scopes) => + ctx.sbtCallback.usedName(className, usedName.toString, scopes) + } + } + + collector.dependencies.foreach(recordDependency) } } } - /** Record that `currentSourceFile` depends on the file where `dep` was loaded from. - * - * @param currentSourceFile The source file of the current unit - * @param dep The dependency - * @param context Describes how `currentSourceFile` depends on `dep` + /* + * Handles dependency on given symbol by trying to figure out if represents a term + * that is coming from either source code (not necessarily compiled in this compilation + * run) or from class file and calls respective callback method. */ - def recordDependency(currentSourceFile: File, dep: Symbol, context: DependencyContext) - (implicit ctx: Context) = { - val depFile = dep.associatedFile + def recordDependency(dep: ClassDependency)(implicit ctx: Context): Unit = { + val fromClassName = classNameAsString(dep.from) + val sourceFile = ctx.compilationUnit.source.file.file + + def binaryDependency(file: File, binaryClassName: String) = + ctx.sbtCallback.binaryDependency(file, binaryClassName, fromClassName, sourceFile, dep.context) + + def processExternalDependency(depFile: AbstractFile) = { + def binaryClassName(classSegments: List[String]) = + classSegments.mkString(".").stripSuffix(".class") + + depFile match { + case ze: ZipArchive#Entry => // The dependency comes from a JAR + for (zip <- ze.underlyingSource; zipFile <- Option(zip.file)) { + val classSegments = io.File(ze.path).segments + binaryDependency(zipFile, binaryClassName(classSegments)) + } + + case pf: PlainFile => // The dependency comes from a class file + val packages = dep.to.ownersIterator + .count(x => x.is(PackageClass) && !x.isEffectiveRoot) + // We can recover the fully qualified name of a classfile from + // its path + val classSegments = pf.givenPath.segments.takeRight(packages + 1) + binaryDependency(pf.file, binaryClassName(classSegments)) + + case _ => + ctx.warning(s"sbt-deps: Ignoring dependency $depFile of class ${depFile.getClass}}") + } + } + + val depFile = dep.to.associatedFile if (depFile != null) { - if (depFile.path.endsWith(".class")) { - /** Transform `List(java, lang, String.class)` into `java.lang.String` */ - def className(classSegments: List[String]) = - classSegments.mkString(".").stripSuffix(".class") - def binaryDependency(file: File, className: String) = - ctx.sbtCallback.binaryDependency(file, className, currentSourceFile, context) - - depFile match { - case ze: ZipArchive#Entry => - for (zip <- ze.underlyingSource; zipFile <- Option(zip.file)) { - val classSegments = Path(ze.path).segments - binaryDependency(zipFile, className(classSegments)) - } - case pf: PlainFile => - val packages = dep.ownersIterator - .filter(x => x.is(PackageClass) && !x.isEffectiveRoot).length - // We can recover the fully qualified name of a classfile from - // its path - val classSegments = pf.givenPath.segments.takeRight(packages + 1) - binaryDependency(pf.file, className(classSegments)) - case _ => - ctx.warning(s"sbt-deps: Ignoring dependency $depFile of class ${depFile.getClass}") - } - } else if (depFile.file != currentSourceFile) { - ctx.sbtCallback.sourceDependency(depFile.file, currentSourceFile, context) + // Cannot ignore inheritance relationship coming from the same source (see sbt/zinc#417) + def allowLocal = dep.context == DependencyByInheritance || dep.context == LocalDependencyByInheritance + if (depFile.extension == "class") { + // Dependency is external -- source is undefined + processExternalDependency(depFile) + } else if (allowLocal || depFile.file != sourceFile) { + // We cannot ignore dependencies coming from the same source file because + // the dependency info needs to propagate. See source-dependencies/trait-trait-211. + val toClassName = classNameAsString(dep.to) + ctx.sbtCallback.classDependency(toClassName, fromClassName, dep.context) } } } } +object ExtractDependencies { + def classNameAsString(sym: Symbol)(implicit ctx: Context): String = + sym.fullName.stripModuleClassSuffix.toString +} + +private case class ClassDependency(from: Symbol, to: Symbol, context: DependencyContext) + +/** An object that maintain the set of used names from within a class */ +private final class UsedNamesInClass { + private val _names = new mutable.HashMap[Name, EnumSet[UseScope]] + def names: collection.Map[Name, EnumSet[UseScope]] = _names + + def update(name: Name, scope: UseScope): Unit = { + val scopes = _names.getOrElseUpdate(name, EnumSet.noneOf(classOf[UseScope])) + scopes.add(scope) + } + + override def toString(): String = { + val builder = new StringBuilder + names.foreach { case (name, scopes) => + builder.append(name.mangledString) + builder.append(" in [") + scopes.forEach(scope => builder.append(scope.toString)) + builder.append("]") + builder.append(", ") + } + builder.toString() + } +} + /** Extract the dependency information of a compilation unit. * * To understand why we track the used names see the section "Name hashing @@ -126,85 +189,184 @@ class ExtractDependencies extends Phase { * specially, see the subsection "Dependencies introduced by member reference and * inheritance" in the "Name hashing algorithm" section. */ -private class ExtractDependenciesCollector(implicit val ctx: Context) extends tpd.TreeTraverser { +private class ExtractDependenciesCollector extends tpd.TreeTraverser { thisTreeTraverser => import tpd._ - private[this] val _usedNames = new mutable.HashSet[Name] - private[this] val _topLevelDependencies = new mutable.HashSet[Symbol] - private[this] val _topLevelInheritanceDependencies = new mutable.HashSet[Symbol] + private[this] val _usedNames = new mutable.HashMap[Symbol, UsedNamesInClass] + private[this] val _dependencies = new mutable.HashSet[ClassDependency] /** The names used in this class, this does not include names which are only * defined and not referenced. */ - def usedNames: Set[Name] = _usedNames + def usedNames: collection.Map[Symbol, UsedNamesInClass] = _usedNames - /** The set of top-level classes that the compilation unit depends on - * because it refers to these classes or something defined in them. - * This is always a superset of `topLevelInheritanceDependencies` by definition. + /** The set of class dependencies from this compilation unit. */ - def topLevelDependencies: Set[Symbol] = _topLevelDependencies + def dependencies: Set[ClassDependency] = _dependencies - /** The set of top-level classes that the compilation unit extends or that - * contain a non-top-level class that the compilaion unit extends. + /** Top level import dependencies are registered as coming from a first top level + * class/trait/object declared in the compilation unit. If none exists, issue warning. */ - def topLevelInheritanceDependencies: Set[Symbol] = _topLevelInheritanceDependencies + private[this] var _responsibleForImports: Symbol = _ + private def responsibleForImports(implicit ctx: Context) = { + def firstClassOrModule(tree: Tree) = { + val acc = new TreeAccumulator[Symbol] { + def apply(x: Symbol, t: Tree)(implicit ctx: Context) = + t match { + case typeDef: TypeDef => + typeDef.symbol + case other => + foldOver(x, other) + } + } + acc(NoSymbol, tree) + } + + if (_responsibleForImports == null) { + val tree = ctx.compilationUnit.tpdTree + _responsibleForImports = firstClassOrModule(tree) + if (!_responsibleForImports.exists) + ctx.warning("""|No class, trait or object is defined in the compilation unit. + |The incremental compiler cannot record the dependency information in such case. + |Some errors like unused import referring to a non-existent class might not be reported. + |""".stripMargin, tree.pos) + } + _responsibleForImports + } - private def addUsedName(name: Name) = - _usedNames += name + private[this] var lastOwner: Symbol = _ + private[this] var lastDepSource: Symbol = _ - private def addDependency(sym: Symbol): Unit = + /** + * Resolves dependency source (that is, the closest non-local enclosing + * class from a given `ctx.owner` + */ + private def resolveDependencySource(implicit ctx: Context): Symbol = { + def nonLocalEnclosingClass = { + var clazz = ctx.owner.enclosingClass + var owner = clazz + + while (!owner.is(PackageClass)) { + if (owner.isTerm) { + clazz = owner.enclosingClass + owner = clazz + } else { + owner = owner.owner + } + } + clazz + } + + if (lastOwner != ctx.owner) { + lastOwner = ctx.owner + val source = nonLocalEnclosingClass + lastDepSource = if (source.is(PackageClass)) responsibleForImports else source + } + + lastDepSource + } + + private def addUsedName(fromClass: Symbol, name: Name, scope: UseScope): Unit = { + val usedName = _usedNames.getOrElseUpdate(fromClass, new UsedNamesInClass) + usedName.update(name, scope) + } + + private def addUsedName(name: Name, scope: UseScope)(implicit ctx: Context): Unit = { + val fromClass = resolveDependencySource + if (fromClass.exists) { // can happen when visiting imports + assert(fromClass.isClass) + addUsedName(fromClass, name, scope) + } + } + + /** Mangle a JVM symbol name in a format better suited for internal uses by sbt. */ + private def mangledName(sym: Symbol)(implicit ctx: Context): Name = { + def constructorName = sym.owner.fullName ++ ";init;" + + if (sym.isConstructor) constructorName + else sym.name.stripModuleClassSuffix + } + + private def addMemberRefDependency(sym: Symbol)(implicit ctx: Context): Unit = if (!ignoreDependency(sym)) { - val tlClass = sym.topLevelClass - if (tlClass.ne(NoSymbol)) // Some synthetic type aliases like AnyRef do not belong to any class - _topLevelDependencies += sym.topLevelClass - addUsedName(sym.name) + val enclOrModuleClass = if (sym.is(ModuleVal)) sym.moduleClass else sym.enclosingClass + assert(enclOrModuleClass.isClass, s"$enclOrModuleClass, $sym") + + val fromClass = resolveDependencySource + if (fromClass.exists) { // can happen when visiting imports + assert(fromClass.isClass) + _dependencies += ClassDependency(fromClass, enclOrModuleClass, DependencyByMemberRef) + addUsedName(fromClass, mangledName(sym), UseScope.Default) + } } - private def ignoreDependency(sym: Symbol) = - sym.eq(NoSymbol) || + private def addInheritanceDependencies(tree: Template)(implicit ctx: Context): Unit = + if (tree.parents.nonEmpty) { + val depContext = + if (tree.symbol.owner.isLocal) LocalDependencyByInheritance + else DependencyByInheritance + val from = resolveDependencySource + tree.parents.foreach { parent => + _dependencies += ClassDependency(from, parent.tpe.classSymbol, depContext) + } + } + + private def ignoreDependency(sym: Symbol)(implicit ctx: Context) = + !sym.exists || + sym.is(PackageClass) || sym.isEffectiveRoot || sym.isAnonymousFunction || sym.isAnonymousClass - private def addInheritanceDependency(sym: Symbol): Unit = - _topLevelInheritanceDependencies += sym.topLevelClass - - /** Traverse the tree of a source file and record the dependencies which - * can be retrieved using `topLevelDependencies`, `topLevelInheritanceDependencies`, - * and `usedNames` + /** Traverse the tree of a source file and record the dependencies and used names which + * can be retrieved using `dependencies` and`usedNames`. */ override def traverse(tree: Tree)(implicit ctx: Context): Unit = { tree match { + case Match(selector, _) => + addPatMatDependency(selector.tpe) case Import(expr, selectors) => def lookupImported(name: Name) = expr.tpe.member(name).symbol def addImported(name: Name) = { // importing a name means importing both a term and a type (if they exist) - addDependency(lookupImported(name.toTermName)) - addDependency(lookupImported(name.toTypeName)) + addMemberRefDependency(lookupImported(name.toTermName)) + addMemberRefDependency(lookupImported(name.toTypeName)) } - selectors foreach { + selectors.foreach { case Ident(name) => addImported(name) case Thicket(Ident(name) :: Ident(rename) :: Nil) => addImported(name) - if (rename ne nme.WILDCARD) - addUsedName(rename) + if (rename ne nme.WILDCARD) { + addUsedName(rename, UseScope.Default) + } case _ => } + case t: TypeTree => + addTypeDependency(t.tpe) + case ref: RefTree => + addMemberRefDependency(ref.symbol) + addTypeDependency(ref.tpe) + case t: Template => + addInheritanceDependencies(t) + case _ => + } + + tree match { case Inlined(call, _, _) => // The inlined call is normally ignored by TreeTraverser but we need to // record it as a dependency traverse(call) - case t: TypeTree => - usedTypeTraverser.traverse(t.tpe) - case ref: RefTree => - addDependency(ref.symbol) - usedTypeTraverser.traverse(ref.tpe) - case t @ Template(_, parents, _, _) => - t.parents.foreach(p => addInheritanceDependency(p.tpe.classSymbol)) + case vd: ValDef if vd.symbol.is(ModuleVal) => + // Don't visit module val + case t: Template if t.symbol.owner.is(ModuleClass) => + // Don't visit self type of module class + traverse(t.constr) + t.parents.foreach(traverse) + t.body.foreach(traverse) case _ => + traverseChildren(tree) } - traverseChildren(tree) } /** Traverse a used type and record all the dependencies we need to keep track @@ -240,7 +402,9 @@ private class ExtractDependenciesCollector(implicit val ctx: Context) extends tp * The tests in sbt `types-in-used-names-a`, `types-in-used-names-b`, * `as-seen-from-a` and `as-seen-from-b` rely on this. */ - private object usedTypeTraverser extends TypeTraverser { + private abstract class TypeDependencyTraverser(implicit ctx: Context) extends TypeTraverser()(ctx) { + protected def addDependency(symbol: Symbol): Unit + val seen = new mutable.HashSet[Type] def traverse(tp: Type): Unit = if (!seen.contains(tp)) { seen += tp @@ -264,4 +428,22 @@ private class ExtractDependenciesCollector(implicit val ctx: Context) extends tp } } } + + def addTypeDependency(tpe: Type)(implicit ctx: Context) = { + val traverser = new TypeDependencyTraverser { + def addDependency(symbol: Symbol) = addMemberRefDependency(symbol) + } + traverser.traverse(tpe) + } + + def addPatMatDependency(tpe: Type)(implicit ctx: Context) = { + val traverser = new TypeDependencyTraverser { + def addDependency(symbol: Symbol) = + if (!ignoreDependency(symbol) && symbol.is(Sealed)) { + val usedName = mangledName(symbol) + addUsedName(usedName, UseScope.PatMatTarget) + } + } + traverser.traverse(tpe) + } } diff --git a/compiler/src/dotty/tools/dotc/sbt/ShowAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ShowAPI.scala index 0e6b19867950..35bdc8594066 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ShowAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ShowAPI.scala @@ -18,17 +18,17 @@ object DefaultShowAPI { def apply(d: Definition) = ShowAPI.showDefinition(d)(defaultNesting) def apply(d: Type) = ShowAPI.showType(d)(defaultNesting) - def apply(a: SourceAPI) = ShowAPI.showApi(a)(defaultNesting) + def apply(a: ClassLike) = ShowAPI.showApi(a)(defaultNesting) } object ShowAPI { private lazy val numDecls = Try { java.lang.Integer.parseInt(sys.props.get("sbt.inc.apidiff.decls").get) } getOrElse 0 - private def truncateDecls(decls: Array[Definition]): Array[Definition] = if (numDecls <= 0) decls else decls.take(numDecls) + private def truncateDecls(decls: Array[ClassDefinition]): Array[ClassDefinition] = if (numDecls <= 0) decls else decls.take(numDecls) private def lines(ls: Seq[String]): String = ls.mkString("\n", "\n", "\n") - def showApi(a: SourceAPI)(implicit nesting: Int) = - a.packages.map(pkg => "package " + pkg.name).mkString("\n") + lines(truncateDecls(a.definitions).map(showDefinition)) + def showApi(c: ClassLike)(implicit nesting: Int) = + showDefinition(c) def showDefinition(d: Definition)(implicit nesting: Int): String = d match { case v: Val => showMonoDef(v, "val") + ": " + showType(v.tpe) @@ -36,7 +36,9 @@ object ShowAPI { case d: Def => showPolyDef(d, "def") + showValueParams(d.valueParameters) + ": " + showType(d.returnType) case ta: TypeAlias => showPolyDef(ta, "type") + " = " + showType(ta.tpe) case td: TypeDeclaration => showPolyDef(td, "type") + showBounds(td.lowerBound, td.upperBound) - case cl: ClassLike => showPolyDef(cl, showDefinitionType(cl.definitionType)) + " extends " + showTemplate(cl) + case cl: ClassLike => showMonoDef(d, showDefinitionType(cl.definitionType)) + + showTypeParameters(cl.typeParameters) + " extends " + showTemplate(cl) + case cl: ClassLikeDef => showPolyDef(cl, showDefinitionType(cl.definitionType)) } private def showTemplate(cl: ClassLike)(implicit nesting: Int) = @@ -61,14 +63,17 @@ object ShowAPI { case s: Structure => s.parents.map(showType).mkString(" with ") + ( if (nesting <= 0) "{ }" - else truncateDecls(s.declared).map(showNestedDefinition).mkString(" {", "\n", "}")) + else truncateDecls(s.declared).map(showNestedDefinition).mkString(" {", "\n", "}") + ) case e: Existential => showType(e.baseType) + ( if (nesting <= 0) " forSome { }" - else e.clause.map(t => "type " + showNestedTypeParameter(t)).mkString(" forSome { ", "; ", " }")) + else e.clause.map(t => "type " + showNestedTypeParameter(t)).mkString(" forSome { ", "; ", " }") + ) case p: Polymorphic => showType(p.baseType) + ( if (nesting <= 0) " [ ]" - else showNestedTypeParameters(p.parameters)) + else showNestedTypeParameters(p.parameters) + ) } private def showPath(p: Path): String = p.components.map(showPathComponent).mkString(".") @@ -104,9 +109,7 @@ object ShowAPI { private def showValueParams(ps: Seq[ParameterList])(implicit nesting: Int): String = ps.map(pl => pl.parameters.map(mp => - mp.name + ": " + showParameterModifier(showType(mp.tpe), mp.modifier) + (if (mp.hasDefault) "= ..." else "") - ).mkString(if (pl.isImplicit) "(implicit " else "(", ", ", ")") - ).mkString("") + mp.name + ": " + showParameterModifier(showType(mp.tpe), mp.modifier) + (if (mp.hasDefault) "= ..." else "")).mkString(if (pl.isImplicit) "(implicit " else "(", ", ", ")")).mkString("") private def showParameterModifier(base: String, pm: ParameterModifier): String = pm match { case ParameterModifier.Plain => base @@ -154,3 +157,4 @@ object ShowAPI { private def showNestedTypeParameters(tps: Seq[TypeParameter])(implicit nesting: Int) = showTypeParameters(tps)(nesting - 1) private def showNestedDefinition(d: Definition)(implicit nesting: Int) = showDefinition(d)(nesting - 1) } + diff --git a/compiler/src/dotty/tools/dotc/sbt/ThunkHolder.scala b/compiler/src/dotty/tools/dotc/sbt/ThunkHolder.scala index 350819e3a3ce..2315c40955e4 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ThunkHolder.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ThunkHolder.scala @@ -25,97 +25,8 @@ private[sbt] trait ThunkHolder { * It will be forced by the next call to `forceThunks()` */ def lzy[T <: AnyRef](t: => T): api.Lazy[T] = { - val l = SafeLazyWrapper(() => t) + val l = api.SafeLazy.apply(() => t) thunks += l l } - - /** Store the parameter `s` in a `Lazy` container, since `s` is not by-name, there - * is nothing to force. - * - * TODO: Get rid of this method. It is only needed because some xsbti.api classes - * take lazy arguments when they could be strict, but this can be fixed in sbt, - * see https://github.com/sbt/zinc/issues/114 - */ - def strict2lzy[T <: AnyRef](t: T): api.Lazy[T] = - SafeLazyWrapper.strict(t) -} - -/** Wrapper around SafeLazy implementations. - * - * `xsbti.SafeLazy` is part of sbt but it is not part of the `interface` jar - * that dotty depends on, therefore we can only access it by reflection, - * and this will only succeed when dotty is run by sbt (otherwise - * `xsbti.SafeLazy` won't be on the classpath at all). - * - * For testing purposes, we still want to be able to run the sbt phases outside - * of sbt, using `-Yforce-sbt-phases` and `-Ydump-sbt-inc`, therefore we - * provide a copy of SafeLazy in `dotty.tools.dotc.sbt.SafeLazy` that we use - * when `xsbti.SafeLazy` is unavailable. - * - * This raises a question: why bother with `xsbti.SafeLazy` if we have our own - * version anyway? Because sbt uses Java serialization to persist the output of - * the incremental compilation analysis when sbt is stopped and restarted. If - * we used `dotty.tools.dotc.sbt.SafeLazy` with sbt, deserialization would fail - * and every restart of sbt would require a full recompilation. - * - * Note: this won't be needed once we switch to zinc 1.0 where `SafeLazy` becomes - * part of the `interface` jar, see https://github.com/sbt/zinc/issues/113 - */ -private object SafeLazyWrapper { - - @sharable private[this] val safeLazy = - try { - Class.forName("xsbti.SafeLazy") - } catch { - case e: ClassNotFoundException => - null - } - - @sharable private[this] val safeLazyApply = - if (safeLazy != null) - safeLazy.getMethod("apply", classOf[xsbti.F0[_]]) - else - null - @sharable private[this] val safeLazyStrict = - if (safeLazy != null) - safeLazy.getMethod("strict", classOf[Object]) - else - null - - def apply[T <: AnyRef](eval: () => T): xsbti.api.Lazy[T] = - if (safeLazyApply != null) - safeLazyApply - .invoke(null, new xsbti.F0[T] { def apply() = eval() }) - .asInstanceOf[xsbti.api.Lazy[T]] - else - SafeLazy(eval) - - def strict[T <: AnyRef](value: T): xsbti.api.Lazy[T] = - if (safeLazyStrict != null) - safeLazyStrict - .invoke(null, value) - .asInstanceOf[xsbti.api.Lazy[T]] - else - SafeLazy.strict(value) -} - -// Adapted from https://github.com/sbt/sbt/blob/0.13/compile/api/src/main/scala/xsbti/SafeLazy.scala -private object SafeLazy { - def apply[T <: AnyRef](eval: () => T): xsbti.api.Lazy[T] = - new Impl(eval) - - def strict[T <: AnyRef](value: T): xsbti.api.Lazy[T] = - new Strict(value) - - private[this] final class Impl[T <: AnyRef](private[this] var eval: () => T) extends xsbti.api.AbstractLazy[T] { - private[this] lazy val _t = { - val t = eval() - eval = null // clear the reference, ensuring the only memory we hold onto is the result - t - } - def get(): T = _t - } - - private[this] final class Strict[T <: AnyRef](val get: T) extends xsbti.api.Lazy[T] with java.io.Serializable } diff --git a/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala b/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala index 071b41444bfc..4b3ad0eb8255 100644 --- a/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala +++ b/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala @@ -70,8 +70,7 @@ class CollectEntryPoints extends MiniPhase { } def precise(implicit ctx: Context) = { val companion = sym.companionClass //sym.asClass.linkedClassOfClass - val javaPlatform = ctx.platform.asInstanceOf[JavaPlatform] - if (javaPlatform.hasJavaMainMethod(companion)) + if (ctx.platform.hasMainMethod(companion)) failNoForwarder("companion contains its own main method") else if (companion.exists && companion.info.member(nme.main).exists) // this is only because forwarders aren't smart enough yet @@ -80,7 +79,7 @@ class CollectEntryPoints extends MiniPhase { failNoForwarder("companion is a trait") // Now either succeed, or issue some additional warnings for things which look like // attempts to be java main methods. - else (possibles exists (x => javaPlatform.isJavaMainMethod(x.symbol))) || { + else (possibles exists (x => ctx.platform.isMainMethod(x.symbol))) || { possibles exists { m => m.symbol.info match { @@ -90,7 +89,7 @@ class CollectEntryPoints extends MiniPhase { if (t.resultType :: t.paramInfos exists (_.typeSymbol.isAbstractType)) fail("main methods cannot refer to type parameters or abstract types.", m.symbol.pos) else - javaPlatform.isJavaMainMethod(m.symbol) || fail("main method must have exact signature (Array[String])Unit", m.symbol.pos) + ctx.platform.isMainMethod(m.symbol) || fail("main method must have exact signature (Array[String])Unit", m.symbol.pos) case tp => fail(s"don't know what this is: $tp", m.symbol.pos) } diff --git a/compiler/src/dotty/tools/io/AbstractFile.scala b/compiler/src/dotty/tools/io/AbstractFile.scala index d0155ace3711..6ab93f7328ce 100644 --- a/compiler/src/dotty/tools/io/AbstractFile.scala +++ b/compiler/src/dotty/tools/io/AbstractFile.scala @@ -94,7 +94,7 @@ abstract class AbstractFile extends Iterable[AbstractFile] { /** Checks extension case insensitively. */ def hasExtension(other: String) = extension == other.toLowerCase - private val extension: String = Path.extension(name) + val extension: String = Path.extension(name) /** The absolute file, if this is a relative file. */ def absolute: AbstractFile diff --git a/compiler/src/dotty/tools/io/Path.scala b/compiler/src/dotty/tools/io/Path.scala index fbd2a4ac92c7..e5223a580a7d 100644 --- a/compiler/src/dotty/tools/io/Path.scala +++ b/compiler/src/dotty/tools/io/Path.scala @@ -138,14 +138,7 @@ class Path private[io] (val jpath: JPath) { if (p isSame this) Nil else p :: p.parents } // if name ends with an extension (e.g. "foo.jpg") returns the extension ("jpg"), otherwise "" - def extension: String = { - var i = name.length - 1 - while (i >= 0 && name.charAt(i) != '.') - i -= 1 - - if (i < 0) "" - else name.substring(i + 1) - } + def extension: String = Path.extension(name) // compares against extensions in a CASE INSENSITIVE way. def hasExtension(ext: String, exts: String*) = { val lower = extension.toLowerCase diff --git a/compiler/test/dotty/Jars.scala b/compiler/test/dotty/Jars.scala index 5294943d2554..f759e241c5b5 100644 --- a/compiler/test/dotty/Jars.scala +++ b/compiler/test/dotty/Jars.scala @@ -30,11 +30,11 @@ object Jars { dottyLib :: dottyCompiler :: dottyInterfaces :: dottyExtras /** Dotty runtime with compiler dependencies, used for quoted.Expr.run */ - val dottyRunWithCompiler: List[String] = + lazy val dottyRunWithCompiler: List[String] = dottyLib :: dottyCompiler :: dottyInterfaces :: scalaAsm :: Nil def scalaLibrary: String = sys.env.get("DOTTY_SCALA_LIBRARY") - .getOrElse(findJarFromRuntime("scala-library-2.")) + .getOrElse(findJarFromRuntime("scala-library")) /** Gets the scala 2.* library at runtime, note that doing this is unsafe * unless you know that the library will be on the classpath of the running diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index ca8dbd7b05ad..d91b789b4f86 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -55,14 +55,10 @@ class CompilationTests extends ParallelTesting { compileFile("tests/pos-scala2/rewrites.scala", scala2Mode.and("-rewrite")).copyToTarget() + compileFile("tests/pos-special/utf8encoded.scala", explicitUTF8) + compileFile("tests/pos-special/utf16encoded.scala", explicitUTF16) + - compileFile("tests/pos-special/i3323.scala", defaultOptions.and("-Xfatal-warnings")) + - compileFile("tests/pos-special/i3323b.scala", defaultOptions.and("-Xfatal-warnings")) + - compileFile("tests/pos-special/i3589-b.scala", defaultOptions.and("-Xfatal-warnings")) + - compileFile("tests/pos-special/i4166.scala", defaultOptions.and("-Xfatal-warnings")) + - compileFile("tests/pos-special/i4185.scala", defaultOptions.and("-Xfatal-warnings")) + compileFile("tests/pos-special/completeFromSource/Test.scala", defaultOptions.and("-sourcepath", "tests/pos-special")) + compileFile("tests/pos-special/completeFromSource/Test2.scala", defaultOptions.and("-sourcepath", "tests/pos-special")) + compileFile("tests/pos-special/completeFromSource/Test3.scala", defaultOptions.and("-sourcepath", "tests/pos-special", "-scansource")) + + compileFilesInDir("tests/pos-special/fatal-warnings", defaultOptions.and("-Xfatal-warnings")) + compileList( "compileMixed", List( diff --git a/dist/bin/common b/dist/bin/common index b5239ce28af0..660b25f220b3 100755 --- a/dist/bin/common +++ b/dist/bin/common @@ -118,7 +118,7 @@ DOTTY_LIB=$(find_lib "*dotty-library*") SCALA_ASM=$(find_lib "*scala-asm*") SCALA_LIB=$(find_lib "*scala-library*") SCALA_XML=$(find_lib "*scala-xml*") -SBT_INTF=$(find_lib "*sbt-interface*") +SBT_INTF=$(find_lib "*compiler-interface*") # debug DEBUG_STR=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 diff --git a/project/Build.scala b/project/Build.scala index 795dff662411..e93cbb946b88 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1,33 +1,22 @@ -import sbt.Keys._ -import sbt._ -import complete.DefaultParsers._ import java.io.File -import java.nio.channels.FileLock import java.nio.file._ -import java.util.Calendar - -import scala.reflect.io.Path -import sbtassembly.AssemblyKeys.assembly +import Modes._ +import com.typesafe.sbt.pgp.PgpKeys +import com.typesafe.sbteclipse.plugin.EclipsePlugin.EclipseKeys +import sbt.Keys._ +import sbt._ +import complete.DefaultParsers._ +import pl.project13.scala.sbt.JmhPlugin +import pl.project13.scala.sbt.JmhPlugin.JmhKeys.Jmh +import sbt.Package.ManifestAttributes +import sbt.ScriptedPlugin.autoImport._ import xerial.sbt.pack.PackPlugin import xerial.sbt.pack.PackPlugin.autoImport._ -import sbt.Package.ManifestAttributes - -import com.typesafe.sbteclipse.plugin.EclipsePlugin._ - import dotty.tools.sbtplugin.DottyPlugin.autoImport._ import dotty.tools.sbtplugin.DottyIDEPlugin.{ prepareCommand, runProcess } import dotty.tools.sbtplugin.DottyIDEPlugin.autoImport._ -import org.scalajs.sbtplugin.ScalaJSPlugin -import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._ - -import com.typesafe.sbt.pgp.PgpKeys - -import pl.project13.scala.sbt.JmhPlugin -import JmhPlugin.JmhKeys.Jmh - -import Modes._ /* In sbt 0.13 the Build trait would expose all vals to the shell, where you * can use them in "set a := b" like expressions. This re-exposes them. @@ -45,24 +34,20 @@ object Build { val dottyOrganization = "ch.epfl.lamp" val dottyGithubUrl = "https://github.com/lampepfl/dotty" + + val isRelease = sys.env.get("RELEASEBUILD") == Some("yes") + val dottyVersion = { - val isNightly = sys.env.get("NIGHTLYBUILD") == Some("yes") - val isRelease = sys.env.get("RELEASEBUILD") == Some("yes") - if (isNightly) - baseVersion + "-bin-" + VersionUtil.commitDate + "-" + VersionUtil.gitHash + "-NIGHTLY" - else if (isRelease) + def isNightly = sys.env.get("NIGHTLYBUILD") == Some("yes") + if (isRelease) baseVersion + else if (isNightly) + baseVersion + "-bin-" + VersionUtil.commitDate + "-" + VersionUtil.gitHash + "-NIGHTLY" else baseVersion + "-bin-SNAPSHOT" } val dottyNonBootstrappedVersion = dottyVersion + "-nonbootstrapped" - val jenkinsMemLimit = List("-Xmx1500m") - - val JENKINS_BUILD = "dotty.jenkins.build" - val DRONE_MEM = "dotty.drone.mem" - - val agentOptions = List( // "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" // "-agentpath:/home/dark/opt/yjp-2013-build-13072/bin/linux-x86-64/libyjpagent.so" @@ -132,19 +117,13 @@ object Build { // Settings shared globally (scoped in Global). Used in build.sbt lazy val globalSettings = Def.settings( - // Override `runCode` from sbt-dotty to use the language-server and - // vscode extension from the source repository of dotty instead of a - // published version. - runCode := (run in `dotty-language-server`).toTask("").value, - onLoad := (onLoad in Global).value andThen { state => def exists(submodule: String) = { val path = Paths.get(submodule) Files.exists(path) && { val fileStream = Files.list(path) - val nonEmpty = fileStream.iterator().hasNext() - fileStream.close() - nonEmpty + try fileStream.iterator().hasNext + finally fileStream.close() } } @@ -182,8 +161,13 @@ object Build { resourceDirectory in Compile := baseDirectory.value / "resources", resourceDirectory in Test := baseDirectory.value / "test-resources", + // Disable scaladoc generation, it's way too slow and we'll replace it + // by dottydoc anyway. We still publish an empty -javadoc.jar to make + // sonatype happy. + sources in (Compile, doc) := Seq(), + // Prevent sbt from rewriting our dependencies - ivyScala ~= (_ map (_ copy (overrideScalaVersion = false))), + scalaModuleInfo ~= (_.map(_.withOverrideScalaVersion(false))), libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % Test, @@ -198,21 +182,20 @@ object Build { ) // Settings used when compiling dotty using Scala 2 - lazy val commonNonBootstrappedSettings = commonSettings ++ publishSettings ++ Seq( + lazy val commonNonBootstrappedSettings = commonSettings ++ Seq( version := dottyNonBootstrappedVersion, scalaVersion := scalacVersion ) // Settings used when compiling dotty with a non-bootstrapped dotty lazy val commonBootstrappedSettings = commonSettings ++ Seq( - EclipseKeys.skipProject := true, version := dottyVersion, scalaVersion := dottyNonBootstrappedVersion, // Avoid having to run `dotty-sbt-bridge/publishLocal` before compiling a bootstrapped project scalaCompilerBridgeSource := - (dottyOrganization %% "dotty-sbt-bridge" % dottyVersion % Configurations.Component.name) - .artifacts(Artifact.sources("dotty-sbt-bridge").copy(url = + (dottyOrganization %% "dotty-sbt-bridge" % dottyVersion) + .artifacts(Artifact.sources("dotty-sbt-bridge").withUrl( // We cannot use the `packageSrc` task because a setting cannot depend // on a task. Instead, we make `compile` below depend on the bridge `packageSrc` Some((artifactPath in (`dotty-sbt-bridge`, Compile, packageSrc)).value.toURI.toURL))), @@ -226,7 +209,7 @@ object Build { // contain `scalaInstance.value.libraryJar` which in our case is the // non-bootstrapped dotty-library that will then take priority over // the bootstrapped dotty-library on the classpath or sourcepath. - classpathOptions ~= (_.copy(autoBoot = false)), + classpathOptions ~= (_.withAutoBoot(false)), // We still need a Scala bootclasspath equal to the JVM bootclasspath, // otherwise sbt 0.13 incremental compilation breaks (https://github.com/sbt/sbt/issues/3142) scalacOptions ++= Seq("-bootclasspath", sys.props("sun.boot.class.path")), @@ -250,7 +233,7 @@ object Build { Seq( dottyOrganization %% "dotty-library" % dottyNonBootstrappedVersion % Configurations.ScalaTool.name, dottyOrganization %% "dotty-compiler" % dottyNonBootstrappedVersion % Configurations.ScalaTool.name - ).map(_.withDottyCompat()) + ).map(_.withDottyCompat(scalaVersion.value)) else Seq() }, @@ -258,25 +241,42 @@ object Build { // Compile using the non-bootstrapped and non-published dotty managedScalaInstance := false, scalaInstance := { - val (libraryJar, compilerJar) = - if (bootstrapFromPublishedJars.value) { - val jars = update.value.select( + import sbt.internal.inc.ScalaInstance + import sbt.internal.inc.classpath.ClasspathUtilities + + val updateReport = update.value + var libraryJar = packageBin.in(`dotty-library`, Compile).value + var compilerJar = packageBin.in(`dotty-compiler`, Compile).value + + if (bootstrapFromPublishedJars.value) { + val jars = updateReport.select( configuration = configurationFilter(Configurations.ScalaTool.name), + module = moduleFilter(), artifact = artifactFilter(extension = "jar") ) - (jars.find(_.getName.startsWith("dotty-library_2.12")).get, - jars.find(_.getName.startsWith("dotty-compiler_2.12")).get) - } else - ((packageBin in (`dotty-library`, Compile)).value, - (packageBin in (`dotty-compiler`, Compile)).value) + libraryJar = jars.find(_.getName.startsWith("dotty-library_2.12")).get + compilerJar = jars.find(_.getName.startsWith("dotty-compiler_2.12")).get + } // All compiler dependencies except the library - val otherDependencies = (dependencyClasspath in (`dotty-compiler`, Compile)).value + val otherDependencies = dependencyClasspath.in(`dotty-compiler`, Compile).value .filterNot(_.get(artifact.key).exists(_.name == "dotty-library")) .map(_.data) - val loader = state.value.classLoaderCache(libraryJar :: compilerJar :: otherDependencies.toList) - new ScalaInstance(scalaVersion.value, loader, libraryJar, compilerJar, otherDependencies, None) + val allJars = libraryJar :: compilerJar :: otherDependencies.toList + val classLoader = state.value.classLoaderCache(allJars) + new ScalaInstance( + scalaVersion.value, + classLoader, + ClasspathUtilities.rootLoader, // FIXME: Should be a class loader which only includes the dotty-lib + // See: https://github.com/sbt/zinc/commit/9397b6aaf94ac3cfab386e3abd11c0ef9c2ceaff#diff-ea135f2f26f43e40ff045089da221e1e + // Should not matter, as it addresses an issue with `sbt run` that + // only occur when `(fork in run) := false` + libraryJar, + compilerJar, + allJars.toArray, + None + ) } ) @@ -329,8 +329,8 @@ object Build { // - publishes its own empty artifact "dotty" that depends on "dotty-library" and "dotty-compiler", // this is only necessary for compatibility with sbt which currently hardcodes the "dotty" artifact name lazy val dotty = project.in(file(".")).asDottyRoot(NonBootstrapped) - lazy val `dotty-bootstrapped` = project.asDottyRoot(Bootstrapped) - lazy val `dotty-optimised` = project.asDottyRoot(BootstrappedOptimised) + lazy val `dotty-bootstrapped` = project.asDottyRoot(Bootstrapped).disablePlugins(ScriptedPlugin) + lazy val `dotty-optimised` = project.asDottyRoot(BootstrappedOptimised).disablePlugins(ScriptedPlugin) lazy val `dotty-interfaces` = project.in(file("interfaces")). settings(commonScala2Settings). // Java-only project, so this is fine @@ -339,19 +339,18 @@ object Build { crossPaths := false, // Do not depend on the Scala library autoScalaLibrary := false, - // Let the sbt eclipse plugin know that this is a Java-only project - EclipseKeys.projectFlavor := EclipseProjectFlavor.Java, //Remove javac invalid options in Compile doc javacOptions in (Compile, doc) --= Seq("-Xlint:unchecked", "-Xlint:deprecation") ) private lazy val dottydocClasspath = Def.task { - val dottyLib = (packageAll in `dotty-compiler`).value("dotty-library") - val dottyInterfaces = (packageAll in `dotty-compiler`).value("dotty-interfaces") + val jars = (packageAll in `dotty-compiler`).value + val dottyLib = jars("dotty-library") + val dottyInterfaces = jars("dotty-interfaces") val otherDeps = (dependencyClasspath in Compile).value.map(_.data).mkString(File.pathSeparator) dottyLib + File.pathSeparator + dottyInterfaces + File.pathSeparator + otherDeps } - + // Settings shared between dotty-doc and dotty-doc-bootstrapped lazy val dottyDocSettings = Seq( baseDirectory in (Compile, run) := baseDirectory.value / "..", @@ -370,11 +369,11 @@ object Build { // Used by sbt-dotty to resolve the latest nightly val majorVersion = baseVersion.take(baseVersion.lastIndexOf('.')) IO.write(file("./docs/_site/versions/latest-nightly-base"), majorVersion) - + val sources = - (unmanagedSources in (Compile, compile)).value ++ - (unmanagedSources in (`dotty-compiler`, Compile)).value - val args: Seq[String] = Seq( + unmanagedSources.in(Compile, compile).value ++ + unmanagedSources.in(`dotty-compiler`, Compile).value + val args = Seq( "-siteroot", "docs", "-project", "Dotty", "-project-version", dottyVersion, @@ -387,9 +386,10 @@ object Build { }.value, dottydoc := Def.inputTaskDyn { - val args: Seq[String] = spaceDelimited("").parsed - val cp: Seq[String] = Seq("-classpath", dottydocClasspath.value) - (runMain in Compile).toTask(s""" dotty.tools.dottydoc.Main ${cp.mkString(" ")} """ + args.mkString(" ")) + val args = spaceDelimited("").parsed + val cp = Seq("-classpath", dottydocClasspath.value) + + (runMain in Compile).toTask(s" dotty.tools.dottydoc.Main -classpath $cp " + args.mkString(" ")) }.evaluated, libraryDependencies ++= { @@ -410,8 +410,8 @@ object Build { ) lazy val `dotty-doc` = project.in(file("doc-tool")).asDottyDoc(NonBootstrapped) - lazy val `dotty-doc-bootstrapped` = project.in(file("doc-tool")).asDottyDoc(Bootstrapped) - lazy val `dotty-doc-optimised` = project.in(file("doc-tool")).asDottyDoc(BootstrappedOptimised) + lazy val `dotty-doc-bootstrapped` = project.in(file("doc-tool")).asDottyDoc(Bootstrapped).disablePlugins(ScriptedPlugin) + lazy val `dotty-doc-optimised` = project.in(file("doc-tool")).asDottyDoc(BootstrappedOptimised).disablePlugins(ScriptedPlugin) def dottyDoc(implicit mode: Mode): Project = mode match { case NonBootstrapped => `dotty-doc` @@ -457,7 +457,7 @@ object Build { } catch { case _: UnsupportedOperationException | _: FileSystemException => // If the OS doesn't support symbolic links, copy the directory instead. - sbt.IO.copy(pairs, overwrite = true, preserveLastModified = true) + sbt.IO.copy(pairs, CopyOptions(overwrite = true, preserveLastModified = true, preserveExecutable = true)) } pairs.map(_._2) @@ -491,9 +491,9 @@ object Build { // get libraries onboard libraryDependencies ++= Seq( "org.scala-lang.modules" % "scala-asm" % "6.0.0-scala-1", // used by the backend - "com.typesafe.sbt" % "sbt-interface" % sbtVersion.value, - ("org.scala-lang.modules" %% "scala-xml" % "1.0.6").withDottyCompat(), - "org.scala-lang" % "scala-library" % scalacVersion % "test" + ("org.scala-lang.modules" %% "scala-xml" % "1.0.6").withDottyCompat(scalaVersion.value), + "org.scala-lang" % "scala-library" % scalacVersion % "test", + Dependencies.compilerInterface(sbtVersion.value), ), // For convenience, change the baseDirectory when running the compiler @@ -522,7 +522,7 @@ object Build { // packageAll should always be run before tests javaOptions ++= { val attList = (dependencyClasspath in Runtime).value - val pA = packageAll.value + val jars = packageAll.value // put needed dependencies on classpath: val path = for { @@ -537,15 +537,15 @@ object Build { // used for tests that compile dotty path.contains("scala-asm") || // needed for the xsbti interface - path.contains("sbt-interface") + path.contains("compiler-interface") || + path.contains("util-interface") } yield "-Xbootclasspath/p:" + path val ci_build = // propagate if this is a ci build - if (sys.props.isDefinedAt(JENKINS_BUILD)) - List(s"-D$JENKINS_BUILD=${sys.props(JENKINS_BUILD)}") ::: jenkinsMemLimit - else if (sys.props.isDefinedAt(DRONE_MEM)) - List("-Xmx" + sys.props(DRONE_MEM)) - else List() + sys.props.get("dotty.drone.mem") match { + case Some(prop) => List("-Xmx" + prop) + case _ => List() + } val tuning = if (sys.props.isDefinedAt("Oshort")) @@ -553,13 +553,13 @@ object Build { List("-XX:+TieredCompilation", "-XX:TieredStopAtLevel=1") else List() - val jars = List( - "-Ddotty.tests.classes.interfaces=" + pA("dotty-interfaces"), - "-Ddotty.tests.classes.library=" + pA("dotty-library"), - "-Ddotty.tests.classes.compiler=" + pA("dotty-compiler") + val jarOpts = List( + "-Ddotty.tests.classes.interfaces=" + jars("dotty-interfaces"), + "-Ddotty.tests.classes.library=" + jars("dotty-library"), + "-Ddotty.tests.classes.compiler=" + jars("dotty-compiler") ) - jars ::: tuning ::: agentOptions ::: ci_build ::: path.toList + jarOpts ::: tuning ::: agentOptions ::: ci_build ::: path.toList }, testCompilation := Def.inputTaskDyn { @@ -571,12 +571,10 @@ object Build { (testOnly in Test).toTask(cmd) }.evaluated, - // Override run to be able to run compiled classfiles dotr := { val args: List[String] = spaceDelimited("").parsed.toList - val java: String = Process("which" :: "java" :: Nil).!! val attList = (dependencyClasspath in Runtime).value - val _ = packageAll.value + val jars = packageAll.value def findLib(name: String) = attList .map(_.data.getAbsolutePath) @@ -584,27 +582,26 @@ object Build { .toList.mkString(":") val scalaLib = findLib("scala-library") - val dottyLib = packageAll.value("dotty-library") + val dottyLib = jars("dotty-library") def run(args: List[String]): Unit = { val fullArgs = insertClasspathInArgs(args, s".:$dottyLib:$scalaLib") - s"$java ${fullArgs.mkString(" ")}".! + runProcess("java" :: fullArgs, wait = true) } if (args.isEmpty) { println("Couldn't run `dotr` without args. Use `repl` to run the repl or add args to run the dotty application") - } else if (java == "") { - println("Couldn't find java executable on path, please install java to a default location") } else if (scalaLib == "") { println("Couldn't find scala-library on classpath, please run using script in bin dir instead") } else if (args.contains("-with-compiler")) { val args1 = args.filter(_ != "-with-compiler") val asm = findLib("scala-asm") - val dottyCompiler = packageAll.value("dotty-compiler") - val dottyInterfaces = packageAll.value("dotty-interfaces") + val dottyCompiler = jars("dotty-compiler") + val dottyInterfaces = jars("dotty-interfaces") run(insertClasspathInArgs(args1, s"$dottyCompiler:$dottyInterfaces:$asm")) } else run(args) }, + run := dotc.evaluated, dotc := runCompilerMain().evaluated, repl := runCompilerMain(repl = true).evaluated @@ -644,8 +641,9 @@ object Build { ) def runCompilerMain(repl: Boolean = false) = Def.inputTaskDyn { - val dottyLib = packageAll.value("dotty-library") - lazy val dottyCompiler = packageAll.value("dotty-compiler") + val jars = packageAll.value + val dottyLib = jars("dotty-library") + val dottyCompiler = jars("dotty-compiler") val args0: List[String] = spaceDelimited("").parsed.toList val decompile = args0.contains("-decompile") val debugFromTasty = args0.contains("-Ythrough-tasty") @@ -674,27 +672,22 @@ object Build { } lazy val nonBootstrapedDottyCompilerSettings = commonDottyCompilerSettings ++ Seq( - // Disable scaladoc generation, it's way too slow and we'll replace it - // by dottydoc anyway. We still publish an empty -javadoc.jar to make - // sonatype happy. - sources in (Compile, doc) := Seq(), - // packageAll packages all and then returns a map with the abs location packageAll := { Map( - "dotty-interfaces" -> (packageBin in (`dotty-interfaces`, Compile)).value, - "dotty-compiler" -> (packageBin in Compile).value, - "dotty-library" -> (packageBin in (`dotty-library`, Compile)).value, - "dotty-compiler-test" -> (packageBin in Test).value + "dotty-interfaces" -> packageBin.in(`dotty-interfaces`, Compile).value, + "dotty-compiler" -> packageBin.in(Compile).value, + "dotty-library" -> packageBin.in(`dotty-library`, Compile).value, + "dotty-compiler-test" -> packageBin.in(Test).value ).mapValues(_.getAbsolutePath) } ) lazy val bootstrapedDottyCompilerSettings = commonDottyCompilerSettings ++ Seq( packageAll := { - (packageAll in `dotty-compiler`).value ++ Seq( - ("dotty-compiler" -> (packageBin in Compile).value.getAbsolutePath), - ("dotty-library" -> (packageBin in (dottyLibrary(Bootstrapped), Compile)).value.getAbsolutePath) + packageAll.in(`dotty-compiler`).value ++ Seq( + "dotty-compiler" -> packageBin.in(Compile).value.getAbsolutePath, + "dotty-library" -> packageBin.in(`dotty-library-bootstrapped`, Compile).value.getAbsolutePath ) } ) @@ -703,8 +696,8 @@ object Build { if (mode == NonBootstrapped) nonBootstrapedDottyCompilerSettings else bootstrapedDottyCompilerSettings lazy val `dotty-compiler` = project.in(file("compiler")).asDottyCompiler(NonBootstrapped) - lazy val `dotty-compiler-bootstrapped` = project.in(file("compiler")).asDottyCompiler(Bootstrapped) - lazy val `dotty-compiler-optimised` = project.in(file("compiler")).asDottyCompiler(BootstrappedOptimised) + lazy val `dotty-compiler-bootstrapped` = project.in(file("compiler")).asDottyCompiler(Bootstrapped).disablePlugins(ScriptedPlugin) + lazy val `dotty-compiler-optimised` = project.in(file("compiler")).asDottyCompiler(BootstrappedOptimised).disablePlugins(ScriptedPlugin) def dottyCompiler(implicit mode: Mode): Project = mode match { case NonBootstrapped => `dotty-compiler` @@ -718,8 +711,8 @@ object Build { ) lazy val `dotty-library` = project.in(file("library")).asDottyLibrary(NonBootstrapped) - lazy val `dotty-library-bootstrapped`: Project = project.in(file("library")).asDottyLibrary(Bootstrapped) - lazy val `dotty-library-optimised`: Project = project.in(file("library")).asDottyLibrary(BootstrappedOptimised) + lazy val `dotty-library-bootstrapped`: Project = project.in(file("library")).asDottyLibrary(Bootstrapped).disablePlugins(ScriptedPlugin) + lazy val `dotty-library-optimised`: Project = project.in(file("library")).asDottyLibrary(BootstrappedOptimised).disablePlugins(ScriptedPlugin) def dottyLibrary(implicit mode: Mode): Project = mode match { case NonBootstrapped => `dotty-library` @@ -734,7 +727,7 @@ object Build { cleanSbtBridge := { val home = System.getProperty("user.home") val sbtOrg = "org.scala-sbt" - val bridgeDirectoryPattern = s"*${dottyVersion}*" + val bridgeDirectoryPattern = s"*$dottyVersion*" val log = streams.value.log log.info("Cleaning the dotty-sbt-bridge cache") @@ -745,10 +738,8 @@ object Build { description := "sbt compiler bridge for Dotty", resolvers += Resolver.typesafeIvyRepo("releases"), // For org.scala-sbt:api libraryDependencies ++= Seq( - "com.typesafe.sbt" % "sbt-interface" % sbtVersion.value, - "org.scala-sbt" % "api" % sbtVersion.value % "test", - ("org.specs2" %% "specs2-core" % "3.9.1" % "test").withDottyCompat(), - ("org.specs2" %% "specs2-junit" % "3.9.1" % "test").withDottyCompat() + Dependencies.compilerInterface(sbtVersion.value), + (Dependencies.zincApiinfo(sbtVersion.value) % Test).withDottyCompat(scalaVersion.value) ), // The sources should be published with crossPaths := false since they // need to be compiled by the project using the bridge. @@ -762,7 +753,7 @@ object Build { ) lazy val `dotty-sbt-bridge` = project.in(file("sbt-bridge")).asDottySbtBridge(NonBootstrapped) - lazy val `dotty-sbt-bridge-bootstrapped` = project.in(file("sbt-bridge")).asDottySbtBridge(Bootstrapped) + lazy val `dotty-sbt-bridge-bootstrapped` = project.in(file("sbt-bridge")).asDottySbtBridge(Bootstrapped).disablePlugins(ScriptedPlugin) lazy val `dotty-language-server` = project.in(file("language-server")). dependsOn(dottyCompiler(Bootstrapped)). @@ -797,53 +788,11 @@ object Build { runTask(Runtime, mainClass, allArgs: _*) }.dependsOn(compile in (`vscode-dotty`, Compile)).evaluated - ) - - /** A sandbox to play with the Scala.js back-end of dotty. - * - * This sandbox is compiled with dotty with support for Scala.js. It can be - * used like any regular Scala.js project. In particular, `fastOptJS` will - * produce a .js file, and `run` will run the JavaScript code with a JS VM. - * - * Simply running `dotty/run -scalajs` without this sandbox is not very - * useful, as that would not provide the linker and JS runners. - */ - lazy val sjsSandbox = project.in(file("sandbox/scalajs")). - enablePlugins(ScalaJSPlugin). - settings(commonNonBootstrappedSettings). - settings( - /* Remove the Scala.js compiler plugin for scalac, and enable the - * Scala.js back-end of dotty instead. - */ - libraryDependencies ~= { deps => - deps.filterNot(_.name.startsWith("scalajs-compiler")) - }, - scalacOptions += "-scalajs", - - // The main class cannot be found automatically due to the empty inc.Analysis - mainClass in Compile := Some("hello.world"), - - // While developing the Scala.js back-end, it is very useful to see the trees dotc gives us - scalacOptions += "-Xprint:labelDef", - - /* Debug-friendly Scala.js optimizer options. - * In particular, typecheck the Scala.js IR found on the classpath. - */ - scalaJSOptimizerOptions ~= { - _.withCheckScalaJSIR(true).withParallel(false) - } - ). - settings(compileWithDottySettings). - settings(inConfig(Compile)(Seq( - /* Make sure jsDependencyManifest runs after compile, otherwise compile - * might remove the entire directory afterwards. - */ - jsDependencyManifest := jsDependencyManifest.dependsOn(compile).value - ))) + ).disablePlugins(ScriptedPlugin) lazy val `dotty-bench` = project.in(file("bench")).asDottyBench(NonBootstrapped) - lazy val `dotty-bench-bootstrapped` = project.in(file("bench")).asDottyBench(Bootstrapped) - lazy val `dotty-bench-optimised` = project.in(file("bench")).asDottyBench(BootstrappedOptimised) + lazy val `dotty-bench-bootstrapped` = project.in(file("bench")).asDottyBench(Bootstrapped).disablePlugins(ScriptedPlugin) + lazy val `dotty-bench-optimised` = project.in(file("bench")).asDottyBench(BootstrappedOptimised).disablePlugins(ScriptedPlugin) // Depend on dotty-library so that sbt projects using dotty automatically // depend on the dotty-library @@ -877,29 +826,37 @@ object Build { lazy val `sbt-dotty` = project.in(file("sbt-dotty")). settings(commonSettings). settings( + version := { + val base = "0.2.0" + if (isRelease) base else base + "-SNAPSHOT" + }, + // Keep in sync with inject-sbt-dotty.sbt - libraryDependencies += Dependencies.`jackson-databind`, + libraryDependencies ++= Seq( + Dependencies.`jackson-databind`, + Dependencies.compilerInterface(sbtVersion.value) + ), unmanagedSourceDirectories in Compile += baseDirectory.value / "../language-server/src/dotty/tools/languageserver/config", - - sbtPlugin := true, - version := "0.1.7", - ScriptedPlugin.scriptedSettings, - ScriptedPlugin.sbtTestDirectory := baseDirectory.value / "sbt-test", - ScriptedPlugin.scriptedLaunchOpts += "-Dplugin.version=" + version.value, - ScriptedPlugin.scriptedLaunchOpts += "-Dplugin.scalaVersion=" + dottyVersion, - // By default scripted tests use $HOME/.ivy2 for the ivy cache. We need to override this value for the CI. - ScriptedPlugin.scriptedLaunchOpts ++= ivyPaths.value.ivyHome.map("-Dsbt.ivy.home=" + _.getAbsolutePath).toList, - ScriptedPlugin.scripted := ScriptedPlugin.scripted.dependsOn(Def.task { - val x0 = (publishLocal in `dotty-sbt-bridge-bootstrapped`).value - val x1 = (publishLocal in `dotty-interfaces`).value - val x2 = (publishLocal in `dotty-compiler-bootstrapped`).value - val x3 = (publishLocal in `dotty-library-bootstrapped`).value - val x4 = (publishLocal in `scala-library`).value - val x5 = (publishLocal in `scala-reflect`).value - val x6 = (publishLocal in `dotty-bootstrapped`).value // Needed because sbt currently hardcodes the dotty artifact - }).evaluated + sbtTestDirectory := baseDirectory.value / "sbt-test", + scriptedLaunchOpts ++= Seq( + "-Dplugin.version=" + version.value, + "-Dplugin.scalaVersion=" + dottyVersion, + "-Dsbt.boot.directory=" + ((baseDirectory in ThisBuild).value / ".sbt-scripted").getAbsolutePath // Workaround sbt/sbt#3469 + ), + // By default scripted tests use $HOME/.ivy2 for the ivy cache. We need to override this value for the CI. + scriptedLaunchOpts ++= ivyPaths.value.ivyHome.map("-Dsbt.ivy.home=" + _.getAbsolutePath).toList, + scriptedBufferLog := true, + scripted := scripted.dependsOn( + publishLocal in `dotty-sbt-bridge-bootstrapped`, + publishLocal in `dotty-interfaces`, + publishLocal in `dotty-compiler-bootstrapped`, + publishLocal in `dotty-library-bootstrapped`, + publishLocal in `scala-library`, + publishLocal in `scala-reflect`, + publishLocal in `dotty-bootstrapped` // Needed because sbt currently hardcodes the dotty artifact + ).evaluated ) lazy val `vscode-dotty` = project.in(file("vscode-dotty")). @@ -907,25 +864,25 @@ object Build { settings( EclipseKeys.skipProject := true, version := "0.1.3", // Keep in sync with package.json - autoScalaLibrary := false, publishArtifact := false, includeFilter in unmanagedSources := NothingFilter | "*.ts" | "**.json", watchSources in Global ++= (unmanagedSources in Compile).value, compile in Compile := { - val coursier = baseDirectory.value / "out/coursier" - val packageJson = baseDirectory.value / "package.json" + val workingDir = baseDirectory.value + val coursier = workingDir / "out/coursier" + val packageJson = workingDir / "package.json" if (!coursier.exists || packageJson.lastModified > coursier.lastModified) - runProcess(Seq("npm", "install"), wait = true, directory = baseDirectory.value) - val tsc = baseDirectory.value / "node_modules" / ".bin" / "tsc" - runProcess(Seq(tsc.getAbsolutePath, "--pretty", "--project", baseDirectory.value.getAbsolutePath), wait = true) + runProcess(Seq("npm", "install"), wait = true, directory = workingDir) + val tsc = workingDir / "node_modules" / ".bin" / "tsc" + runProcess(Seq(tsc.getAbsolutePath, "--pretty", "--project", workingDir.getAbsolutePath), wait = true) // Currently, vscode-dotty depends on daltonjorge.scala for syntax highlighting, // this is not automatically installed when starting the extension in development mode // (--extensionDevelopmentPath=...) runProcess(codeCommand.value ++ Seq("--install-extension", "daltonjorge.scala"), wait = true) - sbt.inc.Analysis.Empty + sbt.internal.inc.Analysis.Empty }, sbt.Keys.`package`:= { runProcess(Seq("vsce", "package"), wait = true, directory = baseDirectory.value) @@ -942,7 +899,7 @@ object Build { val inputArgs = spaceDelimited("").parsed val codeArgs = if (inputArgs.isEmpty) List((baseDirectory.value / "..").getAbsolutePath) else inputArgs val extensionPath = baseDirectory.value.getAbsolutePath - val processArgs = List(s"--extensionDevelopmentPath=${extensionPath}") ++ codeArgs + val processArgs = List(s"--extensionDevelopmentPath=$extensionPath") ++ codeArgs runProcess(codeCommand.value ++ processArgs, wait = true) }.dependsOn(compile in Compile).evaluated @@ -1040,7 +997,8 @@ object Build { compile := { val inputs = (compileInputs in compile).value - import inputs.config._ + val inputOptions = inputs.options() + import inputOptions._ val s = streams.value val logger = s.log @@ -1063,6 +1021,7 @@ object Build { // Compile + val run = (runner in compile).value val cachedCompile = FileFunction.cached(cacheDir / "compile", FilesInfo.lastModified, FilesInfo.exists) { dependencies => @@ -1093,13 +1052,12 @@ object Build { } def doCompile(sourcesArgs: List[String]): Unit = { - val run = (runner in compile).value run.run("dotty.tools.dotc.Main", compilerCp, "-classpath" :: cpStr :: - "-d" :: classesDirectory.getAbsolutePath() :: - options ++: + "-d" :: classesDirectory.getAbsolutePath :: + scalacOptions ++: sourcesArgs, - patchedLogger) foreach sys.error + patchedLogger) } // Work around the Windows limitation on command line length. @@ -1109,7 +1067,7 @@ object Build { (sourcesArgs.map(_.length).sum > 1536)) { IO.withTemporaryFile("sourcesargs", ".txt") { sourceListFile => IO.writeLines(sourceListFile, sourcesArgs) - doCompile(List("@"+sourceListFile.getAbsolutePath())) + doCompile(List("@"+sourceListFile.getAbsolutePath)) } } else { doCompile(sourcesArgs) @@ -1122,7 +1080,7 @@ object Build { cachedCompile((sources ++ allMyDependencies).toSet) // We do not have dependency analysis when compiling externally - sbt.inc.Analysis.Empty + sbt.internal.inc.Analysis.Empty } )) } @@ -1137,8 +1095,50 @@ object Build { ) lazy val dist = project.asDist(NonBootstrapped) - lazy val `dist-bootstrapped` = project.asDist(Bootstrapped) - lazy val `dist-optimised` = project.asDist(BootstrappedOptimised) + lazy val `dist-bootstrapped` = project.asDist(Bootstrapped).disablePlugins(ScriptedPlugin) + lazy val `dist-optimised` = project.asDist(BootstrappedOptimised).disablePlugins(ScriptedPlugin) + + // /** A sandbox to play with the Scala.js back-end of dotty. + // * + // * This sandbox is compiled with dotty with support for Scala.js. It can be + // * used like any regular Scala.js project. In particular, `fastOptJS` will + // * produce a .js file, and `run` will run the JavaScript code with a JS VM. + // * + // * Simply running `dotty/run -scalajs` without this sandbox is not very + // * useful, as that would not provide the linker and JS runners. + // */ + // lazy val sjsSandbox = project.in(file("sandbox/scalajs")). + // enablePlugins(ScalaJSPlugin). + // settings(commonNonBootstrappedSettings). + // settings( + // /* Remove the Scala.js compiler plugin for scalac, and enable the + // * Scala.js back-end of dotty instead. + // */ + // libraryDependencies ~= { deps => + // deps.filterNot(_.name.startsWith("scalajs-compiler")) + // }, + // scalacOptions += "-scalajs", + + // // The main class cannot be found automatically due to the empty inc.Analysis + // mainClass in Compile := Some("hello.world"), + + // // While developing the Scala.js back-end, it is very useful to see the trees dotc gives us + // scalacOptions += "-Xprint:labelDef", + + // /* Debug-friendly Scala.js optimizer options. + // * In particular, typecheck the Scala.js IR found on the classpath. + // */ + // scalaJSOptimizerOptions ~= { + // _.withCheckScalaJSIR(true).withParallel(false) + // } + // ). + // settings(compileWithDottySettings). + // settings(inConfig(Compile)(Seq( + // /* Make sure jsDependencyManifest runs after compile, otherwise compile + // * might remove the entire directory afterwards. + // */ + // jsDependencyManifest := jsDependencyManifest.dependsOn(compile).value + // ))) implicit class ProjectDefinitions(val project: Project) extends AnyVal { @@ -1169,7 +1169,7 @@ object Build { settings(dottyDocSettings) def asDottySbtBridge(implicit mode: Mode): Project = project.withCommonSettings. - dependsOn(dottyCompiler). + dependsOn(dottyCompiler % Provided). settings(dottySbtBridgeSettings) def asDottyBench(implicit mode: Mode): Project = project.withCommonSettings. @@ -1190,5 +1190,4 @@ object Build { case BootstrappedOptimised => commonOptimisedSettings }) } - } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 99d1dcb79d3e..7e52dbc95bd3 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -9,4 +9,7 @@ object Dependencies { "com.fasterxml.jackson.core" % "jackson-databind" % jacksonVersion val `jackson-dataformat-yaml` = "com.fasterxml.jackson.dataformat" % "jackson-dataformat-yaml" % jacksonVersion + + def compilerInterface(sbtVersion: String) = "org.scala-sbt" % "compiler-interface" % sbtVersion + def zincApiinfo(sbtVersion: String) = "org.scala-sbt" %% "zinc-apiinfo" % sbtVersion } diff --git a/project/VersionUtil.scala b/project/VersionUtil.scala index 399a468591c8..c127c8fee000 100644 --- a/project/VersionUtil.scala +++ b/project/VersionUtil.scala @@ -6,7 +6,7 @@ object VersionUtil { if (System.getProperty("os.name").toLowerCase.contains("windows")) s"cmd.exe /c project\\scripts\\build\\$scriptName.bat -p" else s"project/scripts/build/$scriptName" - Process(cmd).lines.head.trim + Process(cmd).lineStream.head.trim } /** Seven letters of the SHA hash is considered enough to uniquely identify a diff --git a/project/build.properties b/project/build.properties index 64317fdae59f..64cf32f7f961 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.15 +sbt.version=1.1.4 diff --git a/project/inject-sbt-dotty.sbt b/project/inject-sbt-dotty.sbt index 5ccc77fc2783..a73be671d462 100644 --- a/project/inject-sbt-dotty.sbt +++ b/project/inject-sbt-dotty.sbt @@ -5,6 +5,9 @@ unmanagedSourceDirectories in Compile += baseDirectory.value / "../sbt-dotty/src" // Keep in sync with `sbt-dotty` config in Build.scala -libraryDependencies += Dependencies.`jackson-databind` +libraryDependencies ++= Seq( + Dependencies.`jackson-databind`, + Dependencies.compilerInterface(sbtVersion.value) +) unmanagedSourceDirectories in Compile += baseDirectory.value / "../language-server/src/dotty/tools/languageserver/config" diff --git a/project/plugins.sbt b/project/plugins.sbt index 006705282c2a..8e9587ae889d 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -3,17 +3,14 @@ // e.g. addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.1.0") // Scala IDE project file generator -addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.1.0") +addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.2.4") -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.14") +// addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.21") -addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.4") +addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.1") -addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "1.1") - -addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") +addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0") addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.10.1") -addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.2.24") - +addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.3.2") diff --git a/project/scripted.sbt b/project/scripted.sbt index 76fdf267ff0d..a7d7ecccf2a9 100644 --- a/project/scripted.sbt +++ b/project/scripted.sbt @@ -1,2 +1,2 @@ // Used by the subproject dotty-bridge -libraryDependencies += "org.scala-sbt" % "scripted-plugin" % sbtVersion.value +libraryDependencies += "org.scala-sbt" %% "scripted-plugin" % sbtVersion.value diff --git a/sbt-bridge/src/xsbt/CompilerInterface.scala b/sbt-bridge/src/xsbt/CompilerInterface.scala index bf1488dad93b..ac828b996957 100644 --- a/sbt-bridge/src/xsbt/CompilerInterface.scala +++ b/sbt-bridge/src/xsbt/CompilerInterface.scala @@ -3,10 +3,8 @@ */ package xsbt -import xsbti.{ AnalysisCallback, Logger, Problem, Reporter, Severity, DependencyContext } -import xsbti.api.SourceAPI +import xsbti.{ AnalysisCallback, Logger, Reporter, Severity } import xsbti.compile._ -import Log.debug import java.io.File import dotty.tools.dotc.core.Contexts.ContextBase @@ -16,19 +14,19 @@ import dotty.tools.dotc.interfaces._ import java.net.URLClassLoader final class CompilerInterface { - def newCompiler(options: Array[String], output: Output, initialLog: Logger, - initialDelegate: Reporter, resident: Boolean): CachedCompiler = { + def newCompiler(options: Array[String], output: Output, initialLog: xsbti.Logger, + initialDelegate: xsbti.Reporter): CachedCompiler = { // The classloader that sbt uses to load the compiler bridge is broken // (see CompilerClassLoader#fixBridgeLoader for details). To workaround // this we construct our own ClassLoader and then run the following code // with it: - // new CachedCompilerImpl(options, output, resident) + // new CachedCompilerImpl(options, output) val bridgeLoader = getClass.getClassLoader val fixedLoader = CompilerClassLoader.fixBridgeLoader(bridgeLoader) val cciClass = fixedLoader.loadClass("xsbt.CachedCompilerImpl") cciClass.getConstructors.head - .newInstance(options, output, resident: java.lang.Boolean) + .newInstance(options, output) .asInstanceOf[CachedCompiler] } @@ -37,13 +35,13 @@ final class CompilerInterface { cached.run(sources, changes, callback, log, delegate, progress) } -class CachedCompilerImpl(args: Array[String], output: Output, resident: Boolean) extends CachedCompiler { +class CachedCompilerImpl(args: Array[String], output: Output) extends CachedCompiler { val outputArgs = output match { case multi: MultipleOutput => ??? case single: SingleOutput => - List("-d", single.outputDirectory.getAbsolutePath.toString) + List("-d", single.getOutputDirectory.getAbsolutePath.toString) } def commandArguments(sources: Array[File]): Array[String] = @@ -53,7 +51,7 @@ class CachedCompilerImpl(args: Array[String], output: Output, resident: Boolean) run(sources.toList, changes, callback, log, delegate, progress) } private[this] def run(sources: List[File], changes: DependencyChanges, callback: AnalysisCallback, log: Logger, delegate: Reporter, compileProgress: CompileProgress): Unit = { - debug(log, args.mkString("Calling Dotty compiler with arguments (CompilerInterface):\n\t", "\n\t", "")) + log.debug(() => args.mkString("Calling Dotty compiler with arguments (CompilerInterface):\n\t", "\n\t", "")) val ctx = (new ContextBase).initialCtx.fresh .setSbtCallback(callback) .setReporter(new DelegatingReporter(delegate)) @@ -67,6 +65,6 @@ class CachedCompilerImpl(args: Array[String], output: Output, resident: Boolean) } } -class InterfaceCompileFailed(override val arguments: Array[String], override val problems: Array[Problem]) extends xsbti.CompileFailed { +class InterfaceCompileFailed(override val arguments: Array[String], override val problems: Array[xsbti.Problem]) extends xsbti.CompileFailed { override val toString = "Compilation failed" } diff --git a/sbt-bridge/src/xsbt/DelegatingReporter.scala b/sbt-bridge/src/xsbt/DelegatingReporter.scala index ffc4792ecef0..6ac533a9cb13 100644 --- a/sbt-bridge/src/xsbt/DelegatingReporter.scala +++ b/sbt-bridge/src/xsbt/DelegatingReporter.scala @@ -9,7 +9,8 @@ import reporting._ import reporting.diagnostic.MessageContainer import reporting.diagnostic.messages import core.Contexts._ -import xsbti.{Maybe, Position} +import xsbti.{Position, Severity} +import java.util.Optional final class DelegatingReporter(delegate: xsbti.Reporter) extends Reporter with UniqueMessagePositions @@ -22,9 +23,9 @@ final class DelegatingReporter(delegate: xsbti.Reporter) extends Reporter def doReport(cont: MessageContainer)(implicit ctx: Context): Unit = { val severity = cont match { - case _: messages.Error => xsbti.Severity.Error - case _: messages.Warning => xsbti.Severity.Warn - case _ => xsbti.Severity.Info + case _: messages.Error => Severity.Error + case _: messages.Warning => Severity.Warn + case _ => Severity.Info } val position = @@ -32,13 +33,13 @@ final class DelegatingReporter(delegate: xsbti.Reporter) extends Reporter val pos = cont.pos val src = pos.source new Position { - val sourceFile: Maybe[java.io.File] = maybe(Option(src.file.file)) - val sourcePath: Maybe[String] = maybe(Option(src.file.path)) - val line: Maybe[Integer] = Maybe.just(pos.line) + val sourceFile: Optional[java.io.File] = maybe(Option(src.file.file)) + val sourcePath: Optional[String] = maybe(Option(src.file.path)) + val line: Optional[Integer] = Optional.of(pos.line) val lineContent: String = pos.lineContent.stripLineEnd - val offset: Maybe[Integer] = Maybe.just(pos.point) - val pointer: Maybe[Integer] = Maybe.just(pos.point - src.startOfLine(pos.point)) - val pointerSpace: Maybe[String] = Maybe.just( + val offset: Optional[Integer] = Optional.of(pos.point) + val pointer: Optional[Integer] = Optional.of(pos.point - src.startOfLine(pos.point)) + val pointerSpace: Optional[String] = Optional.of( ((lineContent: Seq[Char]).take(pointer.get).map { case '\t' => '\t'; case x => ' ' }).mkString ) } @@ -51,21 +52,21 @@ final class DelegatingReporter(delegate: xsbti.Reporter) extends Reporter sb.append(explanation(cont.contained())) } - delegate.log(position, sb.toString(), severity) + delegate.log(Problem(position, sb.toString(), severity)) } - private[this] def maybe[T](opt: Option[T]): Maybe[T] = opt match { - case None => Maybe.nothing[T] - case Some(s) => Maybe.just[T](s) + private[this] def maybe[T](opt: Option[T]): Optional[T] = opt match { + case None => Optional.empty[T] + case Some(s) => Optional.of[T](s) } private[this] val noPosition = new Position { - val line: Maybe[Integer] = Maybe.nothing[Integer] + val line: Optional[Integer] = Optional.empty[Integer] val lineContent: String = "" - val offset: Maybe[Integer] = Maybe.nothing[Integer] - val pointer: Maybe[Integer] = Maybe.nothing[Integer] - val pointerSpace: Maybe[String] = Maybe.nothing[String] - val sourceFile: Maybe[java.io.File] = Maybe.nothing[java.io.File] - val sourcePath: Maybe[String] = Maybe.nothing[String] + val offset: Optional[Integer] = Optional.empty[Integer] + val pointer: Optional[Integer] = Optional.empty[Integer] + val pointerSpace: Optional[String] = Optional.empty[String] + val sourceFile: Optional[java.io.File] = Optional.empty[java.io.File] + val sourcePath: Optional[String] = Optional.empty[String] } } diff --git a/sbt-bridge/src/xsbt/Log.scala b/sbt-bridge/src/xsbt/Log.scala deleted file mode 100644 index e514d7abbc46..000000000000 --- a/sbt-bridge/src/xsbt/Log.scala +++ /dev/null @@ -1,10 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package xsbt - -object Log { - def debug(log: xsbti.Logger, msg: => String) = log.debug(Message(msg)) - def settingsError(log: xsbti.Logger): String => Unit = - s => log.error(Message(s)) -} diff --git a/sbt-bridge/src/xsbt/Message.scala b/sbt-bridge/src/xsbt/Message.scala deleted file mode 100644 index 48f24f53349f..000000000000 --- a/sbt-bridge/src/xsbt/Message.scala +++ /dev/null @@ -1,8 +0,0 @@ -/* sbt -- Simple Build Tool - * Copyright 2008, 2009 Mark Harrah - */ -package xsbt - -object Message { - def apply[T](s: => T) = new xsbti.F0[T] { def apply() = s } -} diff --git a/sbt-bridge/src/xsbt/Problem.scala b/sbt-bridge/src/xsbt/Problem.scala new file mode 100644 index 000000000000..f92d8c284f89 --- /dev/null +++ b/sbt-bridge/src/xsbt/Problem.scala @@ -0,0 +1,12 @@ +package xsbt + +import xsbti.{Position, Severity} + +final case class Problem(override val position: Position, + override val message: String, + override val severity: Severity) extends xsbti.Problem { + override val category = "" + override def toString = s"[$severity] $position: $message" + +} + diff --git a/sbt-bridge/src/xsbt/ScaladocInterface.scala b/sbt-bridge/src/xsbt/ScaladocInterface.scala index 1eae8374d28b..c5124d8996e0 100644 --- a/sbt-bridge/src/xsbt/ScaladocInterface.scala +++ b/sbt-bridge/src/xsbt/ScaladocInterface.scala @@ -5,6 +5,7 @@ package xsbt import xsbti.{ Logger, Severity } import java.net.URL +import java.util.Optional class ScaladocInterface { def run(args: Array[String], log: Logger, delegate: xsbti.Reporter) = @@ -12,21 +13,21 @@ class ScaladocInterface { } class DottydocRunner(args: Array[String], log: Logger, delegate: xsbti.Reporter) { - def run(): Unit = delegate.log( + def run(): Unit = delegate.log(Problem( NoPosition, """|The dotty sbt-bridge currently does not support doc generation directly |via sbt. Please see the dotty documentation at dotty.epfl.ch""".stripMargin, Severity.Error - ) + )) private[this] val NoPosition = new xsbti.Position { - val line = xsbti.Maybe.nothing[Integer] + val line = Optional.empty[Integer] val lineContent = "" - val offset = xsbti.Maybe.nothing[Integer] - val sourcePath = xsbti.Maybe.nothing[String] - val sourceFile = xsbti.Maybe.nothing[java.io.File] - val pointer = xsbti.Maybe.nothing[Integer] - val pointerSpace = xsbti.Maybe.nothing[String] + val offset = Optional.empty[Integer] + val sourcePath = Optional.empty[String] + val sourceFile = Optional.empty[java.io.File] + val pointer = Optional.empty[Integer] + val pointerSpace = Optional.empty[String] } private def getStringSetting(name: String): Option[String] = diff --git a/sbt-bridge/test/xsbt/DependencySpecification.scala b/sbt-bridge/test/xsbt/DependencySpecification.scala index 60545091b666..d702c5993be3 100644 --- a/sbt-bridge/test/xsbt/DependencySpecification.scala +++ b/sbt-bridge/test/xsbt/DependencySpecification.scala @@ -1,92 +1,156 @@ -/** Adapted from https://github.com/sbt/sbt/blob/0.13/compile/interface/src/test/scala/xsbt/DependencySpecification.scala */ package xsbt -import org.junit.runner.RunWith -import xsbti.api.ClassLike -import xsbti.api.Def -import xsbt.api.SameAPI -import org.specs2.mutable.Specification -import org.specs2.runner.JUnitRunner - -import ScalaCompilerForUnitTesting.ExtractedSourceDependencies - -@RunWith(classOf[JUnitRunner]) -class DependencySpecification extends Specification { - - "Extracted source dependencies from public members" in { - val sourceDependencies = extractSourceDependenciesPublic - val memberRef = sourceDependencies.memberRef - val inheritance = sourceDependencies.inheritance - memberRef('A) === Set.empty - inheritance('A) === Set.empty - memberRef('B) === Set('A, 'D) - inheritance('B) === Set('D) - memberRef('C) === Set('A) - inheritance('C) === Set.empty - memberRef('D) === Set.empty - inheritance('D) === Set.empty - memberRef('E) === Set.empty - inheritance('E) === Set.empty - memberRef('F) === Set('A, 'B, 'C, 'D, 'E, 'G) - inheritance('F) === Set('A, 'E) - memberRef('H) === Set('B, 'E, 'G) +import xsbti.TestCallback.ExtractedClassDependencies + +import org.junit.Test +import org.junit.Assert._ + +class DependencySpecification { + + @Test + def extractedClassDependenciesFromPublicMembers = { + val classDependencies = extractClassDependenciesPublic + val memberRef = classDependencies.memberRef + val inheritance = classDependencies.inheritance + assertEquals(Set.empty, memberRef("A")) + assertEquals(Set.empty, inheritance("A")) + assertEquals(Set("A", "D"), memberRef("B")) + assertEquals(Set("D"), inheritance("B")) + assertEquals(Set("A"), memberRef("C")) + assertEquals(Set.empty, inheritance("C")) + assertEquals(Set.empty, memberRef("D")) + assertEquals(Set.empty, inheritance("D")) + assertEquals(Set.empty, memberRef("E")) + assertEquals(Set.empty, inheritance("E")) + assertEquals(Set("A", "B", "D", "E", "G", "C"), memberRef("F")) // C is the underlying type of MyC + assertEquals(Set("A", "E"), inheritance("F")) + assertEquals(Set("B", "E", "G"), memberRef("H")) // aliases and applied type constructors are expanded so we have inheritance dependency on B - inheritance('H) === Set('B, 'E) + assertEquals(Set("B", "E"), inheritance("H")) } - "Extracted source dependencies from private members" in { - val sourceDependencies = extractSourceDependenciesPrivate - val memberRef = sourceDependencies.memberRef - val inheritance = sourceDependencies.inheritance - memberRef('A) === Set.empty - inheritance('A) === Set.empty - memberRef('B) === Set.empty - inheritance('B) === Set.empty - memberRef('C) === Set('A) - inheritance('C) === Set('A) - memberRef('D) === Set('B) - inheritance('D) === Set('B) + @Test + def extractedClassDependenciesFromLocalMembers = { + val classDependencies = extractClassDependenciesLocal + val memberRef = classDependencies.memberRef + val inheritance = classDependencies.inheritance + val localInheritance = classDependencies.localInheritance + assertEquals(Set.empty, memberRef("A")) + assertEquals(Set.empty, inheritance("A")) + assertEquals(Set.empty, memberRef("B")) + assertEquals(Set.empty, inheritance("B")) + assertEquals(Set("A"), memberRef("C.Inner1")) + assertEquals(Set("A"), inheritance("C.Inner1")) + assertEquals(Set("B"), memberRef("D")) + assertEquals(Set.empty, inheritance("D")) + assertEquals(Set("B"), localInheritance("D")) + assertEquals(Set("B"), memberRef("E")) + assertEquals(Set.empty, inheritance("E")) + assertEquals(Set("B"), localInheritance("E")) } - "Extracted source dependencies with trait as first parent" in { - val sourceDependencies = extractSourceDependenciesTraitAsFirstPatent - val memberRef = sourceDependencies.memberRef - val inheritance = sourceDependencies.inheritance - memberRef('A) === Set.empty - inheritance('A) === Set.empty - memberRef('B) === Set('A) - inheritance('B) === Set('A) + @Test + def extractedClassDependenciesWithTraitAsFirstParent = { + val classDependencies = extractClassDependenciesTraitAsFirstPatent + val memberRef = classDependencies.memberRef + val inheritance = classDependencies.inheritance + assertEquals(Set.empty, memberRef("A")) + assertEquals(Set.empty, inheritance("A")) + assertEquals(Set("A"), memberRef("B")) + assertEquals(Set("A"), inheritance("B")) // verify that memberRef captures the oddity described in documentation of `Relations.inheritance` // we are mainly interested whether dependency on A is captured in `memberRef` relation so // the invariant that says that memberRef is superset of inheritance relation is preserved - memberRef('C) === Set('A, 'B) - inheritance('C) === Set('A, 'B) + assertEquals(Set("A", "B"), memberRef("C")) + assertEquals(Set("A", "B"), inheritance("C")) // same as above but indirect (C -> B -> A), note that only A is visible here - memberRef('D) === Set('A, 'C) - inheritance('D) === Set('A, 'C) + assertEquals(Set("A", "C"), memberRef("D")) + assertEquals(Set("A", "C"), inheritance("D")) } - /* - "Extracted source dependencies from macro arguments" in { - val sourceDependencies = extractSourceDependenciesFromMacroArgument - val memberRef = sourceDependencies.memberRef - val inheritance = sourceDependencies.inheritance - - memberRef('A) === Set('B, 'C) - inheritance('A) === Set.empty - memberRef('B) === Set.empty - inheritance('B) === Set.empty - memberRef('C) === Set.empty - inheritance('C) === Set.empty + @Test + def extractedClassDependenciesFromARefinement = { + val srcFoo = + "object Outer {\n class Inner { type Xyz }\n\n type TypeInner = Inner { type Xyz = Int }\n}" + val srcBar = "object Bar {\n def bar: Outer.TypeInner = null\n}" + + val compilerForTesting = new ScalaCompilerForUnitTesting + val classDependencies = + compilerForTesting.extractDependenciesFromSrcs(srcFoo, srcBar) + + val memberRef = classDependencies.memberRef + val inheritance = classDependencies.inheritance + assertEquals(Set.empty, memberRef("Outer")) + assertEquals(Set.empty, inheritance("Outer")) + assertEquals(Set("Outer", "Outer$.Inner"), memberRef("Bar")) + assertEquals(Set.empty, inheritance("Bar")) } - */ - private def extractSourceDependenciesPublic: ExtractedSourceDependencies = { + @Test + def extractedClassDependenciesOnAnObjectCorrectly = { + val srcA = + """object A { + | def foo = { B; () } + |}""".stripMargin + val srcB = "object B" + + val compilerForTesting = new ScalaCompilerForUnitTesting + val classDependencies = + compilerForTesting.extractDependenciesFromSrcs(srcA, srcB) + + val memberRef = classDependencies.memberRef + val inheritance = classDependencies.inheritance + assertEquals(Set("B"), memberRef("A")) + assertEquals(Set.empty, inheritance("A")) + assertEquals(Set.empty, memberRef("B")) + assertEquals(Set.empty, inheritance("B")) + } + + @Test + def extractedTopLevelImportDependencies = { + val srcA = + """ + |package abc + |object A { + | class Inner + |} + |class A2""".stripMargin + val srcB = "import abc.A; import abc.A.Inner; class B" + val srcC = "import abc.{A, A2}; class C" + val srcD = "import abc.{A2 => Foo}; class D" + val srcE = "import abc.A._; class E" + val srcF = "import abc._; class F" + val srcG = + """|package foo { + | package bar { + | import abc.A + | class G + | } + |} + """.stripMargin + val srcH = "class H { import abc.A }" + + val compilerForTesting = new ScalaCompilerForUnitTesting + val deps = compilerForTesting + .extractDependenciesFromSrcs(srcA, srcB, srcC, srcD, srcE, srcF, srcG, srcH) + .memberRef + + assertEquals(Set.empty, deps("A")) + assertEquals(Set("abc.A", "abc.A$.Inner"), deps("B")) + assertEquals(Set("abc.A", "abc.A2"), deps("C")) + assertEquals(Set("abc.A2"), deps("D")) + assertEquals(Set("abc.A"), deps("E")) + assertEquals(Set.empty, deps("F")) + assertEquals(Set("abc.A"), deps("foo.bar.G")) + assertEquals(Set("abc.A"), deps("H")) + } + + private def extractClassDependenciesPublic: ExtractedClassDependencies = { val srcA = "class A" val srcB = "class B extends D[A]" val srcC = """|class C { - | def a: A = null - |}""".stripMargin + | def a: A = null + |}""".stripMargin val srcD = "class D[T]" val srcE = "trait E[T]" val srcF = "trait F extends A with E[D[B]] { self: G.MyC => }" @@ -96,56 +160,34 @@ class DependencySpecification extends Specification { // E verifies the core type gets pulled out val srcH = "trait H extends G.T[Int] with (E[Int] @unchecked)" - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) - val sourceDependencies = compilerForTesting.extractDependenciesFromSrcs('A -> srcA, 'B -> srcB, 'C -> srcC, - 'D -> srcD, 'E -> srcE, 'F -> srcF, 'G -> srcG, 'H -> srcH) - sourceDependencies + val compilerForTesting = new ScalaCompilerForUnitTesting + val classDependencies = + compilerForTesting.extractDependenciesFromSrcs(srcA, srcB, srcC, srcD, srcE, srcF, srcG, srcH) + classDependencies } - private def extractSourceDependenciesPrivate: ExtractedSourceDependencies = { + private def extractClassDependenciesLocal: ExtractedClassDependencies = { val srcA = "class A" val srcB = "class B" val srcC = "class C { private class Inner1 extends A }" val srcD = "class D { def foo: Unit = { class Inner2 extends B } }" + val srcE = "class E { def foo: Unit = { new B {} } }" - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) - val sourceDependencies = - compilerForTesting.extractDependenciesFromSrcs('A -> srcA, 'B -> srcB, 'C -> srcC, 'D -> srcD) - sourceDependencies + val compilerForTesting = new ScalaCompilerForUnitTesting + val classDependencies = + compilerForTesting.extractDependenciesFromSrcs(srcA, srcB, srcC, srcD, srcE) + classDependencies } - private def extractSourceDependenciesTraitAsFirstPatent: ExtractedSourceDependencies = { + private def extractClassDependenciesTraitAsFirstPatent: ExtractedClassDependencies = { val srcA = "class A" val srcB = "trait B extends A" val srcC = "trait C extends B" val srcD = "class D extends C" - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) - val sourceDependencies = - compilerForTesting.extractDependenciesFromSrcs('A -> srcA, 'B -> srcB, 'C -> srcC, 'D -> srcD) - sourceDependencies - } - - /* - private def extractSourceDependenciesFromMacroArgument: ExtractedSourceDependencies = { - val srcA = "class A { println(B.printTree(C.foo)) }" - val srcB = """ - |import scala.language.experimental.macros - |import scala.reflect.macros._ - |object B { - | def printTree(arg: Any) = macro printTreeImpl - | def printTreeImpl(c: Context)(arg: c.Expr[Any]): c.Expr[String] = { - | val argStr = arg.tree.toString - | val literalStr = c.universe.Literal(c.universe.Constant(argStr)) - | c.Expr[String](literalStr) - | } - |}""".stripMargin - val srcC = "object C { val foo = 1 }" - - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) - val sourceDependencies = - compilerForTesting.extractDependenciesFromSrcs(List(Map('B -> srcB, 'C -> srcC), Map('A -> srcA))) - sourceDependencies + val compilerForTesting = new ScalaCompilerForUnitTesting + val classDependencies = + compilerForTesting.extractDependenciesFromSrcs(srcA, srcB, srcC, srcD) + classDependencies } - */ -} +} \ No newline at end of file diff --git a/sbt-bridge/test/xsbt/ExtractAPISpecification.scala b/sbt-bridge/test/xsbt/ExtractAPISpecification.scala index ce87134bc16d..dfbcbf2181a2 100644 --- a/sbt-bridge/test/xsbt/ExtractAPISpecification.scala +++ b/sbt-bridge/test/xsbt/ExtractAPISpecification.scala @@ -1,46 +1,126 @@ -/** Adapted from https://github.com/sbt/sbt/blob/0.13/compile/interface/src/test/scala/xsbt/ExtractAPISpecification.scala */ package xsbt -import org.junit.runner.RunWith import xsbti.api._ -import dotty.tools.dotc.sbt.DefaultShowAPI -import org.specs2.mutable.Specification -import org.specs2.runner.JUnitRunner +import xsbt.api.SameAPI -@RunWith(classOf[JUnitRunner]) -class ExtractAPISpecification extends Specification { +import org.junit.{ Test, Ignore } +import org.junit.Assert._ - "Existential types in method signatures" should { - "have stable names" in { stableExistentialNames } +class ExtractAPISpecification { + + @Test + def extractChildrenOfSealedClass = { + def compileAndGetFooClassApi(src: String): ClassLike = { + val compilerForTesting = new ScalaCompilerForUnitTesting + val apis = compilerForTesting.extractApiFromSrc(src) + val FooApi = apis.find(_.name() == "Foo").get + FooApi + } + val src1 = + """|sealed abstract class Foo + |case class C1(x: Int) extends Foo + |""".stripMargin + val fooClassApi1 = compileAndGetFooClassApi(src1) + val src2 = + """|sealed abstract class Foo + |case class C1(x: Int) extends Foo + |case class C2(x: Int) extends Foo + |""".stripMargin + val fooClassApi2 = compileAndGetFooClassApi(src2) + assertFalse(SameAPI(fooClassApi1, fooClassApi2)) + } + + @Ignore + def extractDefinitionTypeOfPackageObject = { + val src = "package object foo".stripMargin + val compilerForTesting = new ScalaCompilerForUnitTesting + val apis = compilerForTesting.extractApiFromSrc(src) + val Seq(fooClassApi) = apis.toSeq + assertEquals(DefinitionType.PackageModule, fooClassApi.definitionType) } - def stableExistentialNames: Boolean = { + @Test + def extractNestedClasses = { + val src = + """class A { + | class B + |}""".stripMargin + val compilerForTesting = new ScalaCompilerForUnitTesting + val apis = compilerForTesting.extractApiFromSrc(src).map(c => c.name -> c).toMap + assertEquals(Set("A", "A.B"), apis.keys) + } + + @Test + def extractLocalClasses = { + val src = + """class A + |class B + |class C { def foo: Unit = { class Inner2 extends B } } + |class D { def foo: Unit = { new B {} } }""".stripMargin + val compilerForTesting = new ScalaCompilerForUnitTesting + val apis = compilerForTesting.extractApiFromSrc(src).map(c => c.name -> c).toMap + assertEquals(Set("A", "B", "C", "D"), apis.keys) + } + + @Ignore + def extractFlatApiOfNestedClass = { + def compileAndGetFooClassApi(src: String): ClassLike = { + val compilerForTesting = new ScalaCompilerForUnitTesting + val apis = compilerForTesting.extractApiFromSrc(src) + val FooApi = apis.find(_.name() == "Foo").get + FooApi + } + val src1 = + """class Foo { + | class A + |}""".stripMargin + val fooClassApi1 = compileAndGetFooClassApi(src1) + val src2 = + """class Foo { + | class A { + | def foo: Int = 123 + | } + |}""".stripMargin + val fooClassApi2 = compileAndGetFooClassApi(src2) + assertTrue(SameAPI(fooClassApi1, fooClassApi2)) + } + + @Test + def extractPivateClasses = { + val src = + """private class A + |class B { private class Inner1 extends A } + |""".stripMargin + val compilerForTesting = new ScalaCompilerForUnitTesting + val apis = compilerForTesting.extractApiFromSrc(src).map(c => c.name -> c).toMap + assertEquals(Set("A", "B", "B.Inner1"), apis.keys) + } + + @Test + def extractStableExistentialNames = { def compileAndGetFooMethodApi(src: String): Def = { - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = false) + val compilerForTesting = new ScalaCompilerForUnitTesting val sourceApi = compilerForTesting.extractApiFromSrc(src) - val FooApi = sourceApi.definitions().find(_.name() == "Foo").get.asInstanceOf[ClassLike] + val FooApi = sourceApi.find(_.name() == "Foo").get val fooMethodApi = FooApi.structure().declared().find(_.name == "foo").get fooMethodApi.asInstanceOf[Def] } val src1 = """ - |class Box[T] - |class Foo { - | def foo: Box[_] = null - | - }""".stripMargin + |class Box[T] + |class Foo { + | def foo: Box[_] = null + | + }""".stripMargin val fooMethodApi1 = compileAndGetFooMethodApi(src1) val src2 = """ - |class Box[T] - |class Foo { - | def bar: Box[_] = null - | def foo: Box[_] = null - | - }""".stripMargin + |class Box[T] + |class Foo { + | def bar: Box[_] = null + | def foo: Box[_] = null + | + }""".stripMargin val fooMethodApi2 = compileAndGetFooMethodApi(src2) - - fooMethodApi1 == fooMethodApi2 - // Fails because xsbt.api is compiled with Scala 2.10 - // SameAPI.apply(fooMethodApi1, fooMethodApi2) + assertTrue("APIs are not the same.", SameAPI(fooMethodApi1, fooMethodApi2)) } /** @@ -48,47 +128,68 @@ class ExtractAPISpecification extends Specification { * is stable between compiling from source and unpickling. We compare extracted APIs of Global when Global * is compiled together with Namers or Namers is compiled first and then Global refers * to Namers by unpickling types from class files. - * - * See https://github.com/sbt/sbt/issues/2504 */ - "Self variable and no self type" in { - def selectNamer(api: SourceAPI): ClassLike = { - def selectClass(defs: Iterable[Definition], name: String): ClassLike = defs.collectFirst { - case cls: ClassLike if cls.name == name => cls - }.get - val global = selectClass(api.definitions, "Global") - val foo = selectClass(global.structure.declared, "Global.Foo") - selectClass(foo.structure.inherited, "Namers.Namer") + @Ignore + def extractStableRepresentationOfSelfVariableThatHasNoSelfType = { + def selectNamer(apis: Seq[ClassLike]): ClassLike = { + // TODO: this doesn't work yet because inherited classes are not extracted + apis.find(_.name == "Global.Foo.Namer").get } val src1 = """|class Namers { - | class Namer { thisNamer => } - |} - |""".stripMargin + | class Namer { thisNamer => } + |} + |""".stripMargin val src2 = """|class Global { - | class Foo extends Namers - |} - |""".stripMargin - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = false) - val apis = compilerForTesting.extractApisFromSrcs(reuseCompilerInstance = false)(List(src1, src2), List(src2)) + | class Foo extends Namers + |} + |""".stripMargin + val compilerForTesting = new ScalaCompilerForUnitTesting + val apis = + compilerForTesting.extractApisFromSrcs(reuseCompilerInstance = false)(List(src1, src2), + List(src2)) val _ :: src2Api1 :: src2Api2 :: Nil = apis.toList val namerApi1 = selectNamer(src2Api1) val namerApi2 = selectNamer(src2Api2) + assertTrue(SameAPI(namerApi1, namerApi2)) + } - DefaultShowAPI(namerApi1) == DefaultShowAPI(namerApi2) - // Fails because xsbt.api is compiled with Scala 2.10 - // SameAPI(namerApi1, namerApi2) + @Ignore + def extractDifferentRepresentationForAnInheritedClass = { + val src = + """|class A[T] { + | abstract class AA { def t: T } + |} + |class B extends A[Int] + """.stripMargin + val compilerForTesting = new ScalaCompilerForUnitTesting + val apis = compilerForTesting.extractApiFromSrc(src).map(a => a.name -> a).toMap + assertEquals(Set("A", "A.AA", "B", "B.AA"), apis.keySet) + assertNotEquals(apis("A.AA"), apis("B.AA")) + } + + @Test + def handlePackageObjectsAndTypeCompanions = { + val src = + """|package object abc { + | type BuildInfoKey = BuildInfoKey.Entry[_] + | object BuildInfoKey { + | sealed trait Entry[A] + | } + |} + """.stripMargin + val compilerForTesting = new ScalaCompilerForUnitTesting + val apis = compilerForTesting.extractApiFromSrc(src).map(a => a.name -> a).toMap + assertEquals(Set("abc.package", "abc.package$.BuildInfoKey", "abc.package$.BuildInfoKey$.Entry"), apis.keySet) } /** * Checks if self type is properly extracted in various cases of declaring a self type - * with our without a self variable. + * with or without a self variable. */ - "Self type" in { - def collectFirstClass(defs: Array[Definition]): ClassLike = defs.collectFirst { - case c: ClassLike => c - }.get + @Ignore + def representASelfTypeCorrectly = { val srcX = "trait X" val srcY = "trait Y" val srcC1 = "class C1 { this: C1 => }" @@ -97,21 +198,19 @@ class ExtractAPISpecification extends Specification { val srcC4 = "class C4 { thisC: X => }" val srcC5 = "class C5 extends AnyRef with X with Y { self: X with Y => }" val srcC6 = "class C6 extends AnyRef with X { self: X with Y => }" - // val srcC7 = "class C7 { _ => }" // DOTTY: Syntax not supported + // val srcC7 = "class C7 { _ => }" val srcC8 = "class C8 { self => }" - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = false) - val apis = compilerForTesting.extractApisFromSrcs(reuseCompilerInstance = true)( - List(srcX, srcY, srcC1, srcC2, srcC3, srcC4, srcC5, srcC6, srcC8) - ).map(x => collectFirstClass(x.definitions)) - val emptyType = new EmptyType + val compilerForTesting = new ScalaCompilerForUnitTesting + val apis = compilerForTesting + .extractApisFromSrcs(reuseCompilerInstance = true)( + List(srcX, srcY, srcC1, srcC2, srcC3, srcC4, srcC5, srcC6, srcC8) + ) + .map(_.head) + val emptyType = EmptyType.of() def hasSelfType(c: ClassLike): Boolean = c.selfType != emptyType val (withSelfType, withoutSelfType) = apis.partition(hasSelfType) - // DOTTY: In the scalac ExtractAPI phase, the self-type is only - // extracted if it differs from the type of the class for stability - // reasons. This isn't necessary in dotty because we always pickle - // the self type. - withSelfType.map(_.name).toSet === Set("C1", "C2", "C3", "C4", "C5", "C6", "C8") - withoutSelfType.map(_.name).toSet === Set("X", "Y") + assertEquals(Set("C3", "C4", "C5", "C6"), withSelfType.map(_.name).toSet) + assertEquals(Set("X", "Y", "C1", "C2", "C8"), withoutSelfType.map(_.name).toSet) } } diff --git a/sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala b/sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala index 5044c771872e..1c05fd3b6394 100644 --- a/sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala +++ b/sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala @@ -1,91 +1,99 @@ -/** Adapted from https://github.com/sbt/sbt/blob/0.13/compile/interface/src/test/scala/xsbt/ExtractUsedNamesSpecification.scala */ package xsbt -import org.junit.runner.RunWith -import xsbti.api.ClassLike -import xsbti.api.Def -import xsbti.api.Package -import xsbt.api.SameAPI -import org.junit.runners.JUnit4 +import xsbti.UseScope -import org.specs2.mutable.Specification +import org.junit.{ Test, Ignore } +import org.junit.Assert._ -@RunWith(classOf[JUnit4]) -class ExtractUsedNamesSpecification extends Specification { +class ExtractUsedNamesSpecification { - /** - * Standard names that appear in every compilation unit that has any class - * definition. - */ - private val standardNames = Set( - // All class extend Object - "Object", - // All class have a default constructor called - "", - // the return type of the default constructor is Unit - "Unit" - ) - - "imported name" in { - val src = """ - |package a { class A } - |package b { - | import a.{A => A2} - |}""".stripMargin - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) + @Test + def extractImportedName = { + val src = """package a { class A } + |package b { + | import a.{A => A2} + |}""".stripMargin + val compilerForTesting = new ScalaCompilerForUnitTesting val usedNames = compilerForTesting.extractUsedNamesFromSrc(src) val expectedNames = standardNames ++ Set("a", "A", "A2", "b") - usedNames === expectedNames + // names used at top level are attributed to the first class defined in a compilation unit + + assertEquals(expectedNames, usedNames("a.A")) } // test covers https://github.com/gkossakowski/sbt/issues/6 - "names in type tree" in { - val srcA = """| - |package a { - | class A { - | class C { class D } - | } - | class B[T] - | class BB - |}""".stripMargin - val srcB = """| - |package b { - | abstract class X { - | def foo: a.A#C#D - | def bar: a.B[a.BB] - | } - |}""".stripMargin - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) + @Test + def extractNameInTypeTree = { + val srcA = """|package a { + | class A { + | class C { class D } + | } + | class B[T] + |} + |package c { + | class BB + |} + | + |""".stripMargin + val srcB = """|package b { + | abstract class X { + | def foo: a.A#C#D + | def bar: a.B[c.BB] + | } + |}""".stripMargin + val compilerForTesting = new ScalaCompilerForUnitTesting val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB) - // DOTTY: unlike the scalac sbt phase, this does not contain "X", I believe this is safe - // TODO: report issue against sbt suggesting that they do the same - val expectedNames = standardNames ++ Set("a", "A", "B", "C", "D", "b", "BB") - usedNames === expectedNames + val expectedNames = standardNames ++ Set("a", "c", "A", "B", "C", "D", "b", "BB") + assertEquals(expectedNames, usedNames("b.X")) } // test for https://github.com/gkossakowski/sbt/issues/5 - "symbolic names" in { - val srcA = """| - |class A { - | def `=`: Int = 3 - |}""".stripMargin - val srcB = """| - |class B { - | def foo(a: A) = a.`=` - |}""".stripMargin - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) + @Test + def extractSymbolicNames = { + val srcA = """|class A { + | def `=`: Int = 3 + |}""".stripMargin + val srcB = """|class B { + | def foo(a: A) = a.`=` + |}""".stripMargin + val compilerForTesting = new ScalaCompilerForUnitTesting val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB) - - // DOTTY TODO: "Int" is not actually used, but we collect it because - // it's the inferred return type so it appears in a TypeTree - // We could avoid this by checking if the untyped tree has a return type - // but is it worth it? Revisit this after https://github.com/sbt/sbt/issues/1104 - // has landed. val expectedNames = standardNames ++ Set("A", "a", "=", "Int") - usedNames === expectedNames + assertEquals(expectedNames, usedNames("B")) + } + + @Test + def extractTypeNamesForObjectsDependingOnAbstractTypes = { + val srcA = + """abstract class A { + | type T + | object X { + | def foo(x: T): T = x + | } + |} + """.stripMargin + val srcB = "class B extends A { type T = Int }" + val srcC = "object C extends B" + val srcD = "object D { C.X.foo(12) }" + val compilerForTesting = new ScalaCompilerForUnitTesting + val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB, srcC, srcD) + val scalaVersion = scala.util.Properties.versionNumberString + val namesA = standardNames ++ Set("Nothing", "Any") + val namesAX = standardNames ++ Set("x", "T", "A", "Nothing", "Any") + val namesB = Set("A", "Int", "A;init;", "Unit") + val namesC = Set("B;init;", "B", "Unit") + val namesD = standardNames ++ Set("C", "X", "foo", "Int", "T") + assertEquals(namesA, usedNames("A")) + assertEquals(namesAX, usedNames("A.X")) + assertEquals(namesB, usedNames("B")) + assertEquals(namesC, usedNames("C")) + assertEquals(namesD, usedNames("D")) } - "extract names in the types of trees" in { + // See source-dependencies/types-in-used-names-a for an example where + // this is required. + @Test + def extractUsedNamesInTypeOfTree = { val src1 = """|class X0 |class X1 extends X0 |class Y @@ -104,55 +112,179 @@ class ExtractUsedNamesSpecification extends Specification { | def foo(m: M): N = ??? | def bar[Param >: P1 <: P0](p: Param): Param = ??? |}""".stripMargin - val src2 = """|object Test { + val src2 = """|object Test_lista { | val x = B.lista - | val y = B.at - | val z = B.as - | B.foo(???) - | B.bar(???) - |}""".stripMargin - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) + |} + |object Test_at { + | val x = B.at + |} + |object Test_as { + | val x = B.as + |} + |object Test_foo { + | val x = B.foo(???) + |} + |object Test_bar { + | val x = B.bar(???) + |} + |""".stripMargin + val compilerForTesting = new ScalaCompilerForUnitTesting val usedNames = compilerForTesting.extractUsedNamesFromSrc(src1, src2) - val expectedNames = standardNames ++ Set("Test", "Test$", "B", "B$", - "Predef", "Predef$", "???", "Nothing", - "lista", "List", "A", - "at", "T", "X1", "X0", - "as", "S", "Y", - "foo", "M", "N", - "bar", "P1", "P0") - usedNames === expectedNames + val expectedNames_lista = standardNames ++ Set("B", "lista", "List", "A") + val expectedNames_at = standardNames ++ Set("B", "at", "A", "T", "X0", "X1") + val expectedNames_as = standardNames ++ Set("B", "as", "S", "Y") + val expectedNames_foo = standardNames ++ Set("B", + "foo", + "M", + "N", + "Predef", + "???", + "Nothing") + val expectedNames_bar = standardNames ++ Set("B", + "bar", + "P1", + "P0", + "Predef", + "???", + "Nothing") + assertEquals(expectedNames_lista, usedNames("Test_lista")) + assertEquals(expectedNames_at, usedNames("Test_at")) + assertEquals(expectedNames_as, usedNames("Test_as")) + assertEquals(expectedNames_foo, usedNames("Test_foo")) + assertEquals(expectedNames_bar, usedNames("Test_bar")) + } + + @Test + def extractUsedNamesFromRefinement = { + val srcFoo = """|object Outer { + | class Inner { type Xyz } + | type TypeInner = Inner { type Xyz = Int } + |} + |""".stripMargin + val srcBar = """|object Bar { + | def bar: Outer.TypeInner = null + |} + |""".stripMargin + val compilerForTesting = new ScalaCompilerForUnitTesting + val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcFoo, srcBar) + val expectedNames = standardNames ++ Set("Outer", "TypeInner", "Inner", "Int") + assertEquals(expectedNames, usedNames("Bar")) } // test for https://github.com/gkossakowski/sbt/issues/3 - "used names from the same compilation unit" in { + @Test + def extractUsedNamesFromSameCompilationUnit = { val src = "class A { def foo: Int = 0; def bar: Int = foo }" - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) + val compilerForTesting = new ScalaCompilerForUnitTesting val usedNames = compilerForTesting.extractUsedNamesFromSrc(src) val expectedNames = standardNames ++ Set("A", "foo", "Int") - usedNames === expectedNames + assertEquals(expectedNames, usedNames("A")) } // pending test for https://issues.scala-lang.org/browse/SI-7173 - "names of constants" in { + @Test + def extractUsedNamesOfConstants = { val src = "class A { final val foo = 12; def bar: Int = foo }" - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) + val compilerForTesting = new ScalaCompilerForUnitTesting val usedNames = compilerForTesting.extractUsedNamesFromSrc(src) val expectedNames = standardNames ++ Set("A", "foo", "Int") - usedNames === expectedNames + assertEquals(expectedNames, usedNames("A")) } - // pending test for https://github.com/gkossakowski/sbt/issues/4 + // test for https://github.com/gkossakowski/sbt/issues/4 // TODO: we should fix it by having special treatment of `selectDynamic` and `applyDynamic` calls - "names from method calls on Dynamic" in { + @Ignore("Call to Dynamic is desugared in type checker so Select nodes is turned into string literal.") + def extractNamesFromMethodCallOnDynamic = { val srcA = """|import scala.language.dynamics - |class A extends Dynamic { - | def selectDynamic(name: String): Int = name.length - |}""".stripMargin + |class A extends Dynamic { + | def selectDynamic(name: String): Int = name.length + |}""".stripMargin val srcB = "class B { def foo(a: A): Int = a.bla }" - val compilerForTesting = new ScalaCompilerForUnitTesting(nameHashing = true) + val compilerForTesting = new ScalaCompilerForUnitTesting val usedNames = compilerForTesting.extractUsedNamesFromSrc(srcA, srcB) - val expectedNames = standardNames ++ Set("B", "A", "a", "Int", "selectDynamic", "bla") - usedNames === expectedNames - }.pendingUntilFixed("Call to Dynamic is desugared in type checker so Select nodes is turned into string literal.") + val expectedNames = standardNames ++ Set("A", "a", "Int", "selectDynamic", "bla") + assertEquals(expectedNames, usedNames("B")) + } + + @Test + def extractSealedClassScope = { + val sealedClassName = "Sealed" + val sealedClass = + s"""package base + | + |sealed class $sealedClassName + |object Usage extends $sealedClassName + |object Usage2 extends $sealedClassName + """.stripMargin + + def findPatMatUsages(in: String): Set[String] = { + val compilerForTesting = new ScalaCompilerForUnitTesting + val (_, callback) = + compilerForTesting.compileSrcs(List(List(sealedClass, in)), reuseCompilerInstance = false) + val clientNames = callback.usedNamesAndScopes.filterKeys(!_.startsWith("base.")) + + val names: Set[String] = clientNames.flatMap { + case (_, usages) => + usages.filter(_.scopes.contains(UseScope.PatMatTarget)).map(_.name) + }(collection.breakOut) + + names + } + + def classWithPatMatOfType(tpe: String = sealedClassName) = + s"""package client + |import base._ + | + |class test(a: $tpe) { + | a match { + | case _ => 1 + | } + |} + """.stripMargin + + assertEquals(Set(sealedClassName), findPatMatUsages(classWithPatMatOfType())) + // Option is sealed + assertEquals(Set(sealedClassName, "Option"), + findPatMatUsages(classWithPatMatOfType(s"Option[$sealedClassName]"))) + // Seq and Set is not + assertEquals(Set(sealedClassName), findPatMatUsages(classWithPatMatOfType(s"Seq[Set[$sealedClassName]]"))) + + def inNestedCase(tpe: String) = + s"""package client + |import base._ + | + |class test(a: Any) { + | a match { + | case _: $tpe => 1 + | } + |}""".stripMargin + + assertEquals(Set(), findPatMatUsages(inNestedCase(sealedClassName))) + val notUsedInPatternMatch = + s"""package client + |import base._ + | + |class test(a: Any) { + | a match { + | case _ => 1 + | } + | val aa: $sealedClassName = ??? + |}""".stripMargin + + assertEquals(Set(), findPatMatUsages(notUsedInPatternMatch)) + } + + /** + * Standard names that appear in every compilation unit that has any class + * definition. + */ + private val standardNames = Set( + // All classes extend Object + "Object", + // All classes have a default constructor called + "java.lang.Object;init;", + // the return type of the default constructor is Unit + "Unit" + ) } diff --git a/sbt-bridge/test/xsbt/ScalaCompilerForUnitTesting.scala b/sbt-bridge/test/xsbt/ScalaCompilerForUnitTesting.scala index fb27f9b9d276..e81d58a07744 100644 --- a/sbt-bridge/test/xsbt/ScalaCompilerForUnitTesting.scala +++ b/sbt-bridge/test/xsbt/ScalaCompilerForUnitTesting.scala @@ -4,29 +4,26 @@ package xsbt import xsbti.compile.SingleOutput import java.io.File import xsbti._ -import xsbti.api.SourceAPI -import sbt.IO._ -import xsbti.api.ClassLike -import xsbti.api.Definition -import xsbti.api.Def +import sbt.io.IO +import xsbti.api.{ ClassLike, Def, DependencyContext } +import DependencyContext._ import xsbt.api.SameAPI -import sbt.ConsoleLogger -import xsbti.DependencyContext._ +import sbt.internal.util.ConsoleLogger -import ScalaCompilerForUnitTesting.ExtractedSourceDependencies +import TestCallback.ExtractedClassDependencies /** * Provides common functionality needed for unit tests that require compiling * source code using Scala compiler. */ -class ScalaCompilerForUnitTesting(nameHashing: Boolean, includeSynthToNameHashing: Boolean = false) { +class ScalaCompilerForUnitTesting { import scala.language.reflectiveCalls /** * Compiles given source code using Scala compiler and returns API representation * extracted by ExtractAPI class. */ - def extractApiFromSrc(src: String): SourceAPI = { + def extractApiFromSrc(src: String): Seq[ClassLike] = { val (Seq(tempSrcFile), analysisCallback) = compileSrcs(src) analysisCallback.apis(tempSrcFile) } @@ -35,27 +32,50 @@ class ScalaCompilerForUnitTesting(nameHashing: Boolean, includeSynthToNameHashin * Compiles given source code using Scala compiler and returns API representation * extracted by ExtractAPI class. */ - def extractApisFromSrcs(reuseCompilerInstance: Boolean)(srcs: List[String]*): Seq[SourceAPI] = { + def extractApisFromSrcs(reuseCompilerInstance: Boolean)(srcs: List[String]*): Seq[Seq[ClassLike]] = { val (tempSrcFiles, analysisCallback) = compileSrcs(srcs.toList, reuseCompilerInstance) tempSrcFiles.map(analysisCallback.apis) } - def extractUsedNamesFromSrc(src: String): Set[String] = { - val (Seq(tempSrcFile), analysisCallback) = compileSrcs(src) - analysisCallback.usedNames(tempSrcFile) - } - /** * Extract used names from src provided as the second argument. + * If `assertDefaultScope` is set to true it will fail if there is any name used in scope other then Default * * The purpose of the first argument is to define names that the second * source is going to refer to. Both files are compiled in the same compiler * Run but only names used in the second src file are returned. */ - def extractUsedNamesFromSrc(definitionSrc: String, actualSrc: String): Set[String] = { + def extractUsedNamesFromSrc( + definitionSrc: String, + actualSrc: String, + assertDefaultScope: Boolean = true + ): Map[String, Set[String]] = { // we drop temp src file corresponding to the definition src file val (Seq(_, tempSrcFile), analysisCallback) = compileSrcs(definitionSrc, actualSrc) - analysisCallback.usedNames(tempSrcFile) + + if (assertDefaultScope) for { + (className, used) <- analysisCallback.usedNamesAndScopes + analysisCallback.TestUsedName(name, scopes) <- used + } assert(scopes.size() == 1 && scopes.contains(UseScope.Default), s"$className uses $name in $scopes") + + val classesInActualSrc = analysisCallback.classNames(tempSrcFile).map(_._1) + classesInActualSrc.map(className => className -> analysisCallback.usedNames(className)).toMap + } + + /** + * Extract used names from the last source file in `sources`. + * + * The previous source files are provided to successfully compile examples. + * Only the names used in the last src file are returned. + */ + def extractUsedNamesFromSrc(sources: String*): Map[String, Set[String]] = { + val (srcFiles, analysisCallback) = compileSrcs(sources: _*) + srcFiles + .map { srcFile => + val classesInSrc = analysisCallback.classNames(srcFile).map(_._1) + classesInSrc.map(className => className -> analysisCallback.usedNames(className)).toMap + } + .reduce(_ ++ _) } /** @@ -70,42 +90,23 @@ class ScalaCompilerForUnitTesting(nameHashing: Boolean, includeSynthToNameHashin * Symbols are used to express extracted dependencies between source code snippets. This way we have * file system-independent way of testing dependencies between source code "files". */ - def extractDependenciesFromSrcs(srcs: List[Map[Symbol, String]]): ExtractedSourceDependencies = { - val rawGroupedSrcs = srcs.map(_.values.toList) - val symbols = srcs.flatMap(_.keys) - val (tempSrcFiles, testCallback) = compileSrcs(rawGroupedSrcs, reuseCompilerInstance = true) - val fileToSymbol = (tempSrcFiles zip symbols).toMap - - val memberRefFileDeps = testCallback.sourceDependencies collect { - // false indicates that those dependencies are not introduced by inheritance + def extractDependenciesFromSrcs(srcs: List[List[String]]): ExtractedClassDependencies = { + val (_, testCallback) = compileSrcs(srcs, reuseCompilerInstance = true) + + val memberRefDeps = testCallback.classDependencies collect { case (target, src, DependencyByMemberRef) => (src, target) } - val inheritanceFileDeps = testCallback.sourceDependencies collect { - // true indicates that those dependencies are introduced by inheritance + val inheritanceDeps = testCallback.classDependencies collect { case (target, src, DependencyByInheritance) => (src, target) } - def toSymbols(src: File, target: File): (Symbol, Symbol) = (fileToSymbol(src), fileToSymbol(target)) - val memberRefDeps = memberRefFileDeps map { case (src, target) => toSymbols(src, target) } - val inheritanceDeps = inheritanceFileDeps map { case (src, target) => toSymbols(src, target) } - def pairsToMultiMap[A, B](pairs: Seq[(A, B)]): Map[A, Set[B]] = { - import scala.collection.mutable.{ HashMap, MultiMap } - val emptyMultiMap = new HashMap[A, scala.collection.mutable.Set[B]] with MultiMap[A, B] - val multiMap = pairs.foldLeft(emptyMultiMap) { - case (acc, (key, value)) => - acc.addBinding(key, value) - } - // convert all collections to immutable variants - multiMap.toMap.mapValues(_.toSet).withDefaultValue(Set.empty) + val localInheritanceDeps = testCallback.classDependencies collect { + case (target, src, LocalDependencyByInheritance) => (src, target) } - - ExtractedSourceDependencies(pairsToMultiMap(memberRefDeps), pairsToMultiMap(inheritanceDeps)) + ExtractedClassDependencies.fromPairs(memberRefDeps, inheritanceDeps, localInheritanceDeps) } - def extractDependenciesFromSrcs(srcs: (Symbol, String)*): ExtractedSourceDependencies = { - val symbols = srcs.map(_._1) - assert(symbols.distinct.size == symbols.size, - s"Duplicate symbols for srcs detected: $symbols") - extractDependenciesFromSrcs(List(srcs.toMap)) + def extractDependenciesFromSrcs(srcs: String*): ExtractedClassDependencies = { + extractDependenciesFromSrcs(List(srcs.toList)) } /** @@ -124,12 +125,12 @@ class ScalaCompilerForUnitTesting(nameHashing: Boolean, includeSynthToNameHashin * The sequence of temporary files corresponding to passed snippets and analysis * callback is returned as a result. */ - private def compileSrcs(groupedSrcs: List[List[String]], + def compileSrcs(groupedSrcs: List[List[String]], reuseCompilerInstance: Boolean): (Seq[File], TestCallback) = { // withTemporaryDirectory { temp => { - val temp = createTemporaryDirectory - val analysisCallback = new TestCallback(nameHashing, includeSynthToNameHashing) + val temp = IO.createTemporaryDirectory + val analysisCallback = new TestCallback val classesDir = new File(temp, "classes") classesDir.mkdir() @@ -158,13 +159,13 @@ class ScalaCompilerForUnitTesting(nameHashing: Boolean, includeSynthToNameHashin } } - private def compileSrcs(srcs: String*): (Seq[File], TestCallback) = { + def compileSrcs(srcs: String*): (Seq[File], TestCallback) = { compileSrcs(List(srcs.toList), reuseCompilerInstance = true) } private def prepareSrcFile(baseDir: File, fileName: String, src: String): File = { val srcFile = new File(baseDir, fileName) - sbt.IO.write(srcFile, src) + IO.write(srcFile, src) srcFile } @@ -184,14 +185,11 @@ class ScalaCompilerForUnitTesting(nameHashing: Boolean, includeSynthToNameHashin def hasErrors: Boolean = false def hasWarnings: Boolean = false def printWarnings(): Unit = () - def problems: Array[Problem] = Array.empty - def log(pos: Position, msg: String, sev: Severity): Unit = println(msg) + def problems(): Array[xsbti.Problem] = Array.empty + def log(problem: xsbti.Problem): Unit = println(problem.message) def comment(pos: Position, msg: String): Unit = () def printSummary(): Unit = () } } -object ScalaCompilerForUnitTesting { - case class ExtractedSourceDependencies(memberRef: Map[Symbol, Set[Symbol]], inheritance: Map[Symbol, Set[Symbol]]) -} diff --git a/sbt-bridge/test/xsbti/TestCallback.scala b/sbt-bridge/test/xsbti/TestCallback.scala index 99c8d963d555..7a065d5abdc9 100644 --- a/sbt-bridge/test/xsbti/TestCallback.scala +++ b/sbt-bridge/test/xsbti/TestCallback.scala @@ -3,33 +3,90 @@ package xsbti import java.io.File import scala.collection.mutable.ArrayBuffer -import xsbti.api.SourceAPI -import xsbti.DependencyContext._ +import xsbti.api.ClassLike +import xsbti.api.DependencyContext +import DependencyContext._ +import java.util.EnumSet -class TestCallback(override val nameHashing: Boolean, override val includeSynthToNameHashing: Boolean) extends AnalysisCallback +class TestCallback extends AnalysisCallback { - val sourceDependencies = new ArrayBuffer[(File, File, DependencyContext)] - val binaryDependencies = new ArrayBuffer[(File, String, File, DependencyContext)] - val products = new ArrayBuffer[(File, File, String)] - val usedNames = scala.collection.mutable.Map.empty[File, Set[String]].withDefaultValue(Set.empty) - val apis: scala.collection.mutable.Map[File, SourceAPI] = scala.collection.mutable.Map.empty - - def sourceDependency(dependsOn: File, source: File, inherited: Boolean): Unit = { - val context = if(inherited) DependencyByInheritance else DependencyByMemberRef - sourceDependency(dependsOn, source, context) - } - def sourceDependency(dependsOn: File, source: File, context: DependencyContext): Unit = { sourceDependencies += ((dependsOn, source, context)) } - def binaryDependency(binary: File, name: String, source: File, inherited: Boolean): Unit = { - val context = if(inherited) DependencyByInheritance else DependencyByMemberRef - binaryDependency(binary, name, source, context) - } - def binaryDependency(binary: File, name: String, source: File, context: DependencyContext): Unit = { binaryDependencies += ((binary, name, source, context)) } - def generatedClass(source: File, module: File, name: String): Unit = { products += ((source, module, name)) } - - def usedName(source: File, name: String): Unit = { usedNames(source) += name } - def api(source: File, sourceAPI: SourceAPI): Unit = { - assert(!apis.contains(source), s"The `api` method should be called once per source file: $source") - apis(source) = sourceAPI - } - def problem(category: String, pos: xsbti.Position, message: String, severity: xsbti.Severity, reported: Boolean): Unit = () + case class TestUsedName(name: String, scopes: EnumSet[UseScope]) + val classDependencies = new ArrayBuffer[(String, String, DependencyContext)] + val binaryDependencies = new ArrayBuffer[(File, String, String, File, DependencyContext)] + val products = new ArrayBuffer[(File, File)] + val usedNamesAndScopes = scala.collection.mutable.Map.empty[String, Set[TestUsedName]].withDefaultValue(Set.empty) + val classNames = scala.collection.mutable.Map.empty[File, Set[(String, String)]].withDefaultValue(Set.empty) + val apis: scala.collection.mutable.Map[File, Seq[ClassLike]] = scala.collection.mutable.Map.empty + + def usedNames = usedNamesAndScopes.mapValues(_.map(_.name)) + + override def startSource(source: File): Unit = { + assert(!apis.contains(source), s"startSource can be called only once per source file: $source") + apis(source) = Seq.empty + } + + override def binaryDependency(binary: File, name: String, fromClassName: String, source: File, context: DependencyContext): Unit = { + binaryDependencies += ((binary, name, fromClassName, source, context)) + } + + def generatedNonLocalClass(source: File, + module: File, + binaryClassName: String, + srcClassName: String): Unit = { + products += ((source, module)) + classNames(source) += ((srcClassName, binaryClassName)) + () + } + + def generatedLocalClass(source: File, module: File): Unit = { + products += ((source, module)) + () + } + + + override def classDependency(onClassName: String, sourceClassName: String, context: DependencyContext): Unit = { + if (onClassName != sourceClassName) classDependencies += ((onClassName, sourceClassName, context)) + } + + override def usedName(className: String, name: String, scopes: EnumSet[UseScope]): Unit = { + usedNamesAndScopes(className) += TestUsedName(name, scopes) + } + override def api(source: File, classApi: ClassLike): Unit = { + apis(source) = classApi +: apis(source) + } + override def problem(category: String, pos: xsbti.Position, message: String, severity: xsbti.Severity, reported: Boolean): Unit = () + override def dependencyPhaseCompleted(): Unit = () + override def apiPhaseCompleted(): Unit = () + override def enabled(): Boolean = true + def mainClass(source: File, className: String): Unit = () + +} + +object TestCallback { + case class ExtractedClassDependencies(memberRef: Map[String, Set[String]], + inheritance: Map[String, Set[String]], + localInheritance: Map[String, Set[String]]) + object ExtractedClassDependencies { + def fromPairs( + memberRefPairs: Seq[(String, String)], + inheritancePairs: Seq[(String, String)], + localInheritancePairs: Seq[(String, String)] + ): ExtractedClassDependencies = { + ExtractedClassDependencies(pairsToMultiMap(memberRefPairs), + pairsToMultiMap(inheritancePairs), + pairsToMultiMap(localInheritancePairs)) + } + + private def pairsToMultiMap[A, B](pairs: Seq[(A, B)]): Map[A, Set[B]] = { + import scala.collection.mutable.{ HashMap, MultiMap } + val emptyMultiMap = new HashMap[A, scala.collection.mutable.Set[B]] with MultiMap[A, B] + val multiMap = pairs.foldLeft(emptyMultiMap) { + case (acc, (key, value)) => + acc.addBinding(key, value) + } + // convert all collections to immutable variants + multiMap.toMap.mapValues(_.toSet).withDefaultValue(Set.empty) + } + } } + diff --git a/sbt-dotty/sbt-test/compilerReporter/simple/project/Reporter.scala b/sbt-dotty/sbt-test/compilerReporter/simple/project/Reporter.scala index d2dae1fc202a..1d72caa02cab 100644 --- a/sbt-dotty/sbt-test/compilerReporter/simple/project/Reporter.scala +++ b/sbt-dotty/sbt-test/compilerReporter/simple/project/Reporter.scala @@ -3,38 +3,29 @@ import Keys._ import KeyRanks.DTask object Reporter { - import xsbti.{Reporter, Problem, Position, Severity, Maybe} + import xsbti.{Reporter, Problem, Position, Severity} lazy val check = TaskKey[Unit]("check", "make sure compilation info are forwared to sbt") // compilerReporter is marked private in sbt - lazy val compilerReporter = TaskKey[Option[xsbti.Reporter]]("compilerReporter", "Experimental hook to listen (or send) compilation failure messages.", DTask) - - lazy val reporter = - Some(new xsbti.Reporter { + lazy val compilerReporter = TaskKey[xsbti.Reporter]("compilerReporter", "Experimental hook to listen (or send) compilation failure messages.", DTask) + + lazy val reporter = + new xsbti.Reporter { private val buffer = collection.mutable.ArrayBuffer.empty[Problem] def reset(): Unit = buffer.clear() def hasErrors: Boolean = buffer.exists(_.severity == Severity.Error) def hasWarnings: Boolean = buffer.exists(_.severity == Severity.Warn) def printSummary(): Unit = println(problems.mkString(System.lineSeparator)) def problems: Array[Problem] = buffer.toArray - def log(pos: Position, msg: String, sev: Severity): Unit = { - object MyProblem extends Problem { - def category: String = null - def severity: Severity = sev - def message: String = msg - def position: Position = pos - override def toString = s"custom: $position:$severity: $message" - } - buffer.append(MyProblem) - } + def log(problem: Problem): Unit = buffer.append(problem) def comment(pos: xsbti.Position, msg: String): Unit = () - }) + } lazy val checkSettings = Seq( compilerReporter in (Compile, compile) := reporter, - check <<= (compile in Compile).mapFailure( _ => { - val problems = reporter.get.problems + check := (compile in Compile).failure.map(_ => { + val problems = reporter.problems println(problems.toList) assert(problems.size == 1) @@ -43,6 +34,6 @@ object Reporter { // assert(problems.forall(_.position.offset.isDefined)) assert(problems.count(_.severity == Severity.Error) == 1) // not found: er1, - }) + }).value ) } diff --git a/sbt-dotty/sbt-test/sbt-dotty/example-project/build.sbt b/sbt-dotty/sbt-test/sbt-dotty/example-project/build.sbt index 07fd238624a3..6e0adfaf4d08 100644 --- a/sbt-dotty/sbt-test/sbt-dotty/example-project/build.sbt +++ b/sbt-dotty/sbt-test/sbt-dotty/example-project/build.sbt @@ -1,3 +1,4 @@ scalaVersion := sys.props("plugin.scalaVersion") -libraryDependencies += ("org.scala-lang.modules" %% "scala-xml" % "1.0.6").withDottyCompat() +libraryDependencies += +("org.scala-lang.modules" %% "scala-xml" % "1.0.6").withDottyCompat(scalaVersion.value) diff --git a/sbt-dotty/sbt-test/sbt-dotty/example-project/project/build.properties b/sbt-dotty/sbt-test/sbt-dotty/example-project/project/build.properties deleted file mode 100644 index 64317fdae59f..000000000000 --- a/sbt-dotty/sbt-test/sbt-dotty/example-project/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version=0.13.15 diff --git a/sbt-dotty/sbt-test/source-dependencies/abstract-type-override/build.sbt b/sbt-dotty/sbt-test/source-dependencies/abstract-type-override/build.sbt index 92d2093771e4..6c2c11e0926a 100644 --- a/sbt-dotty/sbt-test/source-dependencies/abstract-type-override/build.sbt +++ b/sbt-dotty/sbt-test/source-dependencies/abstract-type-override/build.sbt @@ -1,7 +1,9 @@ -InputKey[Unit]("check-number-of-compiler-iterations") <<= inputTask { (argTask: TaskKey[Seq[String]]) => - (argTask, compile in Compile) map { (args: Seq[String], a: sbt.inc.Analysis) => - assert(args.size == 1) - val expectedIterationsNumber = args(0).toInt - assert(a.compilations.allCompilations.size == expectedIterationsNumber, "a.compilations.allCompilations.size = %d (expected %d)".format(a.compilations.allCompilations.size, expectedIterationsNumber)) - } +import complete.DefaultParsers._ + +InputKey[Unit]("check-number-of-compiler-iterations") := { + val args = spaceDelimited("").parsed + val a = (compile in Compile).value.asInstanceOf[sbt.internal.inc.Analysis] + assert(args.size == 1) + val expectedIterationsNumber = args(0).toInt + assert(a.compilations.allCompilations.size == expectedIterationsNumber, "a.compilations.allCompilations.size = %d (expected %d)".format(a.compilations.allCompilations.size, expectedIterationsNumber)) } diff --git a/sbt-dotty/sbt-test/source-dependencies/abstract-type-override/test b/sbt-dotty/sbt-test/source-dependencies/abstract-type-override/test index b0bec415eadb..9ffa4fb17ccd 100644 --- a/sbt-dotty/sbt-test/source-dependencies/abstract-type-override/test +++ b/sbt-dotty/sbt-test/source-dependencies/abstract-type-override/test @@ -11,4 +11,4 @@ $ copy-file changes/Bar1.scala src/main/scala/Bar.scala # second iteration #> compile # check if there are only two compile iterations performed -> check-number-of-compiler-iterations 2 +> checkNumberOfCompilerIterations 2 diff --git a/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/JJ.java b/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/JJ.java new file mode 100644 index 000000000000..3b9d25102fdd --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/JJ.java @@ -0,0 +1,11 @@ +public class JJ { + public static void main(String[] args) { + // Declare anonymous class depending on Scala class + S s = new S() { + public void foo(String s) { + System.out.println(s); + } + }; + s.foo("ahoy"); + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/build.sbt b/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/build.sbt new file mode 100644 index 000000000000..f59b98eb6267 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/build.sbt @@ -0,0 +1 @@ +compileOrder := CompileOrder.Mixed diff --git a/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/changes/S1.scala b/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/changes/S1.scala new file mode 100644 index 000000000000..2400ac4da6df --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/changes/S1.scala @@ -0,0 +1,3 @@ +abstract class S { + def foo(s:String): Unit +} diff --git a/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/changes/S2.scala b/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/changes/S2.scala new file mode 100644 index 000000000000..dd84be54b8eb --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/changes/S2.scala @@ -0,0 +1,3 @@ +abstract class S { + def foo2(s:String): Unit +} diff --git a/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/test b/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/test new file mode 100644 index 000000000000..502d3699257f --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/anon-class-java-depends-on-scala/test @@ -0,0 +1,4 @@ +$ copy-file changes/S1.scala S.scala +> compile +$ copy-file changes/S2.scala S.scala +-> compile diff --git a/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/A.java b/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/A.java new file mode 100644 index 000000000000..0ae4e417add2 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/A.java @@ -0,0 +1,3 @@ +public class A { + public A() {} +} diff --git a/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/B.java b/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/B.java new file mode 100644 index 000000000000..71f266b3596f --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/B.java @@ -0,0 +1,3 @@ +public class B extends A { + public B() {} +} diff --git a/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/C.scala b/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/C.scala new file mode 100644 index 000000000000..23b26a9d6233 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/C.scala @@ -0,0 +1,5 @@ +class C { + def foo: Unit = { + val myFoo = new B {} + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/D.scala b/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/D.scala new file mode 100644 index 000000000000..765d291e5733 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/D.scala @@ -0,0 +1,5 @@ +class D extends C { + // mention abc name to check if local inheritance dependencies are _not_ included in member reference + // extension of inheritance invalidation + def bar(abc: Int): Int = abc +} diff --git a/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/changes/A2.java b/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/changes/A2.java new file mode 100644 index 000000000000..a56de2c644f4 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/changes/A2.java @@ -0,0 +1,6 @@ +public class A { + public A() {} + public String hey() { + return "Hey"; + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/disabled b/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/disabled new file mode 100644 index 000000000000..c4a216c9a182 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/disabled @@ -0,0 +1,18 @@ +# TODO: Implement checkRecompilations + +# Verifies if dependencies introduced by Java inheritance by local classes are handled correctly. +# See sbt-test 'local-class-inheritance' for a similar test in Scala. + +> compile + +$ copy-file changes/A2.java A.java +> compile +# D should be compiled only at the beginning; it depends by inheritance only +# on C and C's public interface is not affected by changes to A +> checkRecompilations 0 D +# A is explicitly changed +> checkRecompilations 1 A +# B is recompiled because it depends by inheritance on A +# C is recompiled because it depends by local inheritance on B but its +# dependencies (D) are not recompiled +> checkRecompilations 2 B C diff --git a/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/anon-java-scala-class/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/backtick-quoted-names/build.sbt b/sbt-dotty/sbt-test/source-dependencies/backtick-quoted-names/build.sbt deleted file mode 100644 index 8a38ef41424b..000000000000 --- a/sbt-dotty/sbt-test/source-dependencies/backtick-quoted-names/build.sbt +++ /dev/null @@ -1 +0,0 @@ -incOptions := incOptions.value.withNameHashing(true) diff --git a/sbt-dotty/sbt-test/source-dependencies/binary/build.sbt b/sbt-dotty/sbt-test/source-dependencies/binary/build.sbt new file mode 100644 index 000000000000..40d7ea353760 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/binary/build.sbt @@ -0,0 +1,5 @@ +lazy val dep = project.in(file("dep")) +lazy val use = project.in(file("use")). + settings( + unmanagedJars in Compile := Attributed.blank(packageBin.in(dep, Compile).value) :: Nil + ) diff --git a/sbt-dotty/sbt-test/source-dependencies/binary/project/P.scala b/sbt-dotty/sbt-test/source-dependencies/binary/project/P.scala index 9cabc95a4e82..e69de29bb2d1 100644 --- a/sbt-dotty/sbt-test/source-dependencies/binary/project/P.scala +++ b/sbt-dotty/sbt-test/source-dependencies/binary/project/P.scala @@ -1,10 +0,0 @@ -import sbt._ -import Keys._ - -object B extends Build -{ - lazy val dep = Project("dep", file("dep")) - lazy val use = Project("use", file("use")) settings( - unmanagedJars in Compile <+= packageBin in (dep, Compile) map Attributed.blank - ) -} diff --git a/sbt-dotty/sbt-test/source-dependencies/canon/build.sbt b/sbt-dotty/sbt-test/source-dependencies/canon/build.sbt index d23dff7054d2..d7524d433978 100644 --- a/sbt-dotty/sbt-test/source-dependencies/canon/build.sbt +++ b/sbt-dotty/sbt-test/source-dependencies/canon/build.sbt @@ -3,8 +3,10 @@ import complete.DefaultParsers._ val checkIterations = inputKey[Unit]("Verifies the accumlated number of iterations of incremental compilation.") checkIterations := { - val expected: Int = (Space ~> NatBasic).parsed - val actual: Int = (compile in Compile).value.compilations.allCompilations.size - assert(expected == actual, s"Expected $expected compilations, got $actual") + val analysis = (compile in Compile).value.asInstanceOf[sbt.internal.inc.Analysis] + + val expected: Int = (Space ~> NatBasic).parsed + val actual: Int = analysis.compilations.allCompilations.size + assert(expected == actual, s"Expected $expected compilations, got $actual") } diff --git a/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/changed/Sealed1.scala b/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/changed/Sealed1.scala new file mode 100644 index 000000000000..e3dc637f6c3c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/changed/Sealed1.scala @@ -0,0 +1,9 @@ +package foo + +sealed trait Sealed + +object Child1 extends Sealed with Base +object Child2 extends Sealed with OtherBase + +trait Base +trait OtherBase extends Base \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/changed/Sealed2.scala b/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/changed/Sealed2.scala new file mode 100644 index 000000000000..c58157acc81e --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/changed/Sealed2.scala @@ -0,0 +1,9 @@ +package foo + +sealed trait Sealed + +object Child1 extends Sealed with Base +object Child2 extends Sealed with OtherBase + +trait Base +trait OtherBase \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/incOptions.properties b/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/incOptions.properties new file mode 100644 index 000000000000..fad0545c8f66 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/incOptions.properties @@ -0,0 +1 @@ +scalac.options = -Xfatal-warnings \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/pending b/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/pending new file mode 100644 index 000000000000..a1c04ccbd3d2 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/pending @@ -0,0 +1,10 @@ +# Compilation with warning should fail +-> compile + +# Remove warning +$ copy-file changed/Sealed1.scala src/main/scala/foo/Sealed.scala +> compile + +# Add warning again without touching Child2 definition +$ copy-file changed/Sealed2.scala src/main/scala/foo/Sealed.scala +-> compile \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/src/main/scala/foo/Sealed.scala b/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/src/main/scala/foo/Sealed.scala new file mode 100644 index 000000000000..8c618c7dda03 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/src/main/scala/foo/Sealed.scala @@ -0,0 +1,9 @@ +package foo + +sealed trait Sealed + +object Child1 extends Sealed with Base +object Child2 extends Sealed // 'with Base' will be added then removed + +trait Base +trait OtherBase extends Base \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/src/main/scala/foo/Usage.scala b/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/src/main/scala/foo/Usage.scala new file mode 100644 index 000000000000..b5275c33ce39 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/changedTypeOfChildOfSealed/src/main/scala/foo/Usage.scala @@ -0,0 +1,8 @@ +package foo + +object Usage{ + def ala(a: Sealed) = a match { + case _: Base => + 1 + } +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/check-classes/A.scala b/sbt-dotty/sbt-test/source-dependencies/check-classes/A.scala new file mode 100644 index 000000000000..528ffce71c5f --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-classes/A.scala @@ -0,0 +1 @@ +object A \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/check-classes/disabled b/sbt-dotty/sbt-test/source-dependencies/check-classes/disabled new file mode 100644 index 000000000000..44f8ea3685a8 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-classes/disabled @@ -0,0 +1,4 @@ +# TODO: Implement checkClasses + +> compile +> checkClasses A.scala: A diff --git a/sbt-dotty/sbt-test/source-dependencies/check-classes/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/check-classes/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-classes/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/check-classes/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/check-classes/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-classes/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/check-dependencies/A.scala b/sbt-dotty/sbt-test/source-dependencies/check-dependencies/A.scala new file mode 100644 index 000000000000..7413db57f482 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-dependencies/A.scala @@ -0,0 +1 @@ +class A extends B with C \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/check-dependencies/B.scala b/sbt-dotty/sbt-test/source-dependencies/check-dependencies/B.scala new file mode 100644 index 000000000000..3a691aca2c27 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-dependencies/B.scala @@ -0,0 +1 @@ +trait B \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/check-dependencies/C.scala b/sbt-dotty/sbt-test/source-dependencies/check-dependencies/C.scala new file mode 100644 index 000000000000..4b6f6492bba5 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-dependencies/C.scala @@ -0,0 +1 @@ +trait C \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/check-dependencies/disabled b/sbt-dotty/sbt-test/source-dependencies/check-dependencies/disabled new file mode 100644 index 000000000000..13458f266160 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-dependencies/disabled @@ -0,0 +1,5 @@ +# TODO: implement checkDependencies + +> compile + +> checkDependencies A: B C \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/check-dependencies/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/check-dependencies/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-dependencies/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/check-dependencies/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/check-dependencies/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-dependencies/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/check-products/A.scala b/sbt-dotty/sbt-test/source-dependencies/check-products/A.scala new file mode 100644 index 000000000000..528ffce71c5f --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-products/A.scala @@ -0,0 +1 @@ +object A \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/check-products/disabled b/sbt-dotty/sbt-test/source-dependencies/check-products/disabled new file mode 100644 index 000000000000..23a04dd3dc4f --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-products/disabled @@ -0,0 +1,4 @@ +# TODO: implement check-products + +> compile +> checkProducts A.scala: A.class A$.class \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/check-products/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/check-products/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-products/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/check-products/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/check-products/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-products/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/check-recompilations/A.scala b/sbt-dotty/sbt-test/source-dependencies/check-recompilations/A.scala new file mode 100644 index 000000000000..44662722128f --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-recompilations/A.scala @@ -0,0 +1,3 @@ +object A { + def foo = 0 +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/check-recompilations/B.scala b/sbt-dotty/sbt-test/source-dependencies/check-recompilations/B.scala new file mode 100644 index 000000000000..45553257be07 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-recompilations/B.scala @@ -0,0 +1,3 @@ +object B { + def bar = A.foo +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/check-recompilations/C.scala b/sbt-dotty/sbt-test/source-dependencies/check-recompilations/C.scala new file mode 100644 index 000000000000..9581d2b04a44 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-recompilations/C.scala @@ -0,0 +1,3 @@ +object C { + def baz = A.foo +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/check-recompilations/D.scala b/sbt-dotty/sbt-test/source-dependencies/check-recompilations/D.scala new file mode 100644 index 000000000000..dfcc8661936e --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-recompilations/D.scala @@ -0,0 +1,3 @@ +object D { + def foobar = C.baz +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/check-recompilations/changes/A.scala b/sbt-dotty/sbt-test/source-dependencies/check-recompilations/changes/A.scala new file mode 100644 index 000000000000..5cb6c0e738f6 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-recompilations/changes/A.scala @@ -0,0 +1,3 @@ +object A { + def foo = "" +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/check-recompilations/disabled b/sbt-dotty/sbt-test/source-dependencies/check-recompilations/disabled new file mode 100644 index 000000000000..9449b39b0266 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-recompilations/disabled @@ -0,0 +1,12 @@ +# TODO: Implement checkRecompilations + +> compile + +$ copy-file changes/A.scala A.scala + +> compile + +> checkRecompilations 0 +> checkRecompilations 1 A +> checkRecompilations 2 B C +> checkRecompilations 3 D diff --git a/sbt-dotty/sbt-test/source-dependencies/check-recompilations/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/check-recompilations/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-recompilations/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/check-recompilations/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/check-recompilations/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/check-recompilations/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/changes/A1.scala b/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/changes/A1.scala new file mode 100644 index 000000000000..bb0e18f9fd13 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/changes/A1.scala @@ -0,0 +1,5 @@ +class A { + class AA +} + +class A2 diff --git a/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/changes/A2.scala b/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/changes/A2.scala new file mode 100644 index 000000000000..2063aea9e5bb --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/changes/A2.scala @@ -0,0 +1,8 @@ +class A { + class AA { + // add a member to an inner class, dependencies on A shouldn't be recompiled + def foo: Int = 123 + } +} + +class A2 diff --git a/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/disabled b/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/disabled new file mode 100644 index 000000000000..d8506accf74f --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/disabled @@ -0,0 +1,23 @@ +# TODO: implement checkRecompilations + +# Test for class-based invalidation of dependencies +# by inheritance. The source file A.scala defines +# A and A2 classes. The B.scala inherits from A2 but +# in the test api of A is modified. B.scala shouldn't +# get invalidated if invalidation happens at +# class level +# this test case covers https://github.com/sbt/sbt/issues/2320 + +# introduces first compile iteration +> compile +$ copy-file changes/A1.scala src/main/scala/A.scala +# second iteration and third iteration (due to invalidation of C) +> compile +$ copy-file changes/A2.scala src/main/scala/A.scala +# fourth iteration +> compile +# check if there were exactly four compile iterations performed +> checkRecompilations 0 B +> checkRecompilations 1 +> checkRecompilations 2 C +> checkRecompilations 3 A A2 A.AA diff --git a/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/src/main/scala/A.scala b/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/src/main/scala/A.scala new file mode 100644 index 000000000000..cee8704bb6a0 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/src/main/scala/A.scala @@ -0,0 +1,5 @@ +class A { + // class AA +} + +class A2 diff --git a/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/src/main/scala/B.scala b/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/src/main/scala/B.scala new file mode 100644 index 000000000000..11a098a4261d --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/src/main/scala/B.scala @@ -0,0 +1 @@ +class B extends A2 diff --git a/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/src/main/scala/C.scala b/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/src/main/scala/C.scala new file mode 100644 index 000000000000..3316d2e6ad68 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/class-based-inheritance/src/main/scala/C.scala @@ -0,0 +1 @@ +class C extends A diff --git a/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/changes/A1.scala b/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/changes/A1.scala new file mode 100644 index 000000000000..7de71fc6589d --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/changes/A1.scala @@ -0,0 +1,7 @@ +class A1 { + //def foo: Int = 123 +} + +class A2 { + def bar: Int = 42 +} diff --git a/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/disabled b/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/disabled new file mode 100644 index 000000000000..c0ec7f6f0f5c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/disabled @@ -0,0 +1,13 @@ +# TODO: Implement checkRecompilations + +# Test for class-based invalidation of dependencies by member reference +# This test checks if name hashes are tracked at the class level so +# only classes that depend on an API of a modified class are invalidated + +# introduces first compile iteration +> compile +$ copy-file changes/A1.scala src/main/scala/A.scala +# second iteration +> compile +> checkRecompilations 0 B1 B2 +> checkRecompilations 1 A1 A2 diff --git a/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/incOptions.properties b/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/incOptions.properties new file mode 100644 index 000000000000..b538c59f6192 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/incOptions.properties @@ -0,0 +1 @@ +recompileAllFraction = 1.0 diff --git a/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/src/main/scala/A.scala b/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/src/main/scala/A.scala new file mode 100644 index 000000000000..6bf9c6b139b9 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/src/main/scala/A.scala @@ -0,0 +1,7 @@ +class A1 { + def foo: Int = 123 +} + +class A2 { + def bar: Int = 42 +} diff --git a/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/src/main/scala/B.scala b/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/src/main/scala/B.scala new file mode 100644 index 000000000000..833512600062 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/class-based-memberRef/src/main/scala/B.scala @@ -0,0 +1,6 @@ +class B1(a1: A1) + +class B2(a2: A2) { + def foo: Int = 53 + def abc: Int = foo + a2.bar +} diff --git a/sbt-dotty/sbt-test/source-dependencies/compactify/build.sbt b/sbt-dotty/sbt-test/source-dependencies/compactify/build.sbt index f44ca08623f2..121f59cd756b 100644 --- a/sbt-dotty/sbt-test/source-dependencies/compactify/build.sbt +++ b/sbt-dotty/sbt-test/source-dependencies/compactify/build.sbt @@ -1,6 +1,7 @@ -TaskKey[Unit]("output-empty") <<= classDirectory in Configurations.Compile map { outputDirectory => - def classes = (outputDirectory ** "*.class").get - if(!classes.isEmpty) sys.error("Classes existed:\n\t" + classes.mkString("\n\t")) else () +TaskKey[Unit]("output-empty") := { + val outputDirectory = (classDirectory in Compile).value + val classes = (outputDirectory ** "*.class").get + if (classes.nonEmpty) sys.error("Classes existed:\n\t" + classes.mkString("\n\t")) else () } // apparently Travis CI stopped allowing long file names diff --git a/sbt-dotty/sbt-test/source-dependencies/compactify/test b/sbt-dotty/sbt-test/source-dependencies/compactify/test index e2abf578b8c0..b56be3e5d4aa 100644 --- a/sbt-dotty/sbt-test/source-dependencies/compactify/test +++ b/sbt-dotty/sbt-test/source-dependencies/compactify/test @@ -1,8 +1,8 @@ # Marked pending due to https://github.com/sbt/sbt/issues/1553 -> output-empty +> outputEmpty > compile --> output-empty +-> outputEmpty $ delete src/main/scala/For.scala src/main/scala/Nested.scala > compile -> output-empty \ No newline at end of file +> outputEmpty \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/A.scala b/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/A.scala new file mode 100644 index 000000000000..b8c61e25e190 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/A.scala @@ -0,0 +1,2 @@ +class A(a: Int) +object A { val x = 3 } diff --git a/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/B.scala b/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/B.scala new file mode 100644 index 000000000000..5578bb7a6d23 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/B.scala @@ -0,0 +1 @@ +class B { val y = A.x } diff --git a/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/build.sbt b/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/build.sbt new file mode 100644 index 000000000000..d7524d433978 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/build.sbt @@ -0,0 +1,12 @@ +import complete.DefaultParsers._ + +val checkIterations = inputKey[Unit]("Verifies the accumlated number of iterations of incremental compilation.") + +checkIterations := { + val analysis = (compile in Compile).value.asInstanceOf[sbt.internal.inc.Analysis] + + val expected: Int = (Space ~> NatBasic).parsed + val actual: Int = analysis.compilations.allCompilations.size + assert(expected == actual, s"Expected $expected compilations, got $actual") +} + diff --git a/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/changes/A2.scala b/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/changes/A2.scala new file mode 100644 index 000000000000..da14d8945d10 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/changes/A2.scala @@ -0,0 +1,2 @@ +class A(a: String) +object A { val x = 3 } diff --git a/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/test b/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/test new file mode 100644 index 000000000000..efd4edd50098 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/constructors-unrelated/test @@ -0,0 +1,6 @@ +> compile +$ copy-file changes/A2.scala A.scala +# Second compilation round, there should be no third round (we don't need to recompile B.scala) +> compile +# Check that there were only two rounds of compilation +> checkIterations 2 diff --git a/sbt-dotty/sbt-test/source-dependencies/continuations/Foo.scala b/sbt-dotty/sbt-test/source-dependencies/continuations/Foo.scala new file mode 100644 index 000000000000..580f7175ab62 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/continuations/Foo.scala @@ -0,0 +1,3 @@ +trait Foo +trait BarA +trait BarB \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/continuations/Use.scala b/sbt-dotty/sbt-test/source-dependencies/continuations/Use.scala new file mode 100644 index 000000000000..bdb8484647d6 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/continuations/Use.scala @@ -0,0 +1,6 @@ +import scala.util.continuations._ + +class Use { + val a = new Baz + def bar: (Foo with BarA) @cpsParam[Unit, Unit] = a.foo +} diff --git a/sbt-dotty/sbt-test/source-dependencies/continuations/build.sbt b/sbt-dotty/sbt-test/source-dependencies/continuations/build.sbt new file mode 100644 index 000000000000..b0f2eb814602 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/continuations/build.sbt @@ -0,0 +1,7 @@ +autoCompilerPlugins := true + +addCompilerPlugin("org.scala-lang.plugins" % "continuations" % "2.9.2") + +scalaVersion := "2.9.2" + +scalacOptions += "-P:continuations:enable" \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/continuations/changes/Def1.scala b/sbt-dotty/sbt-test/source-dependencies/continuations/changes/Def1.scala new file mode 100644 index 000000000000..3cc76e19313e --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/continuations/changes/Def1.scala @@ -0,0 +1,5 @@ +import scala.util.continuations._ + +class Baz { + def foo = shiftUnit[Foo with BarA, Unit, Unit](null) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/continuations/changes/Def2.scala b/sbt-dotty/sbt-test/source-dependencies/continuations/changes/Def2.scala new file mode 100644 index 000000000000..9a176dffbf76 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/continuations/changes/Def2.scala @@ -0,0 +1,5 @@ +import scala.util.continuations._ + +class Baz { + def foo = shiftUnit[Foo with BarB, Unit, Unit](null) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/continuations/pending b/sbt-dotty/sbt-test/source-dependencies/continuations/pending new file mode 100644 index 000000000000..b89168c25a4d --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/continuations/pending @@ -0,0 +1,13 @@ +# Compile code with a type (Foo with BarA) @cpsParam... +# The spec says only simple types can be annotated, but scalac allows any type. +$ copy-file changes/Def1.scala Def.scala +> compile + +# To ensure it was properly processed, change it to (Foo with BarB) @cpsParam... +# This should invalidate Use.scala, which expects it to be BarA and so compilation should fail. +$ delete Def.scala +$ copy-file changes/Def2.scala Def.scala +-> compile + +# The following command is added to fail this test intentionally since this scripted doesn't handle compiler plugins +> x diff --git a/sbt-dotty/sbt-test/source-dependencies/continuations/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/continuations/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/continuations/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/continuations/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/continuations/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/continuations/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/cross-source/pending b/sbt-dotty/sbt-test/source-dependencies/cross-source/pending new file mode 100644 index 000000000000..d7964677feb9 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/cross-source/pending @@ -0,0 +1,16 @@ +# A.scala needs B.scala, it won't be in source list +> ++2.11.4 +-> compile + +# A.scala needs B.scala, it would be in source list +> ++2.10.4 +> compile + +# A.scala needs B.scala, it would be in source list +> ++2.9.3 +> compile + +# Injecting the wrong B.scala in source list +$ copy-file src/main/scala-2.10/B.scala src/main/scala-2.9.3/B.scala +> ++2.9.3 +-> compile diff --git a/sbt-dotty/sbt-test/source-dependencies/cross-source/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/cross-source/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/cross-source/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/cross-source/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/cross-source/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/cross-source/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/cross-source/src/main/scala-2.10/B.scala b/sbt-dotty/sbt-test/source-dependencies/cross-source/src/main/scala-2.10/B.scala new file mode 100644 index 000000000000..fa8ad30ea336 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/cross-source/src/main/scala-2.10/B.scala @@ -0,0 +1,3 @@ +object B { + def show(what: String): String = s"String interpolation is ${what.toUpperCase}!" +} diff --git a/sbt-dotty/sbt-test/source-dependencies/cross-source/src/main/scala-2.9.3/B.scala b/sbt-dotty/sbt-test/source-dependencies/cross-source/src/main/scala-2.9.3/B.scala new file mode 100644 index 000000000000..b1d5f8d3aa42 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/cross-source/src/main/scala-2.9.3/B.scala @@ -0,0 +1,3 @@ +object B { + def show(what: String): String = "String interpolation is " + what.toUpperCase +} diff --git a/sbt-dotty/sbt-test/source-dependencies/cross-source/src/main/scala/A.scala b/sbt-dotty/sbt-test/source-dependencies/cross-source/src/main/scala/A.scala new file mode 100644 index 000000000000..9a6957a19978 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/cross-source/src/main/scala/A.scala @@ -0,0 +1,3 @@ +class A { + def show(what: String): String = B.show(what) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/empty-modified-names/T.scala b/sbt-dotty/sbt-test/source-dependencies/empty-modified-names/T.scala new file mode 100644 index 000000000000..bda3ba02d27c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/empty-modified-names/T.scala @@ -0,0 +1,3 @@ +trait T2 +trait T1 extends T2 { def foo: String } +class C1 extends T1 { def foo = "test" } diff --git a/sbt-dotty/sbt-test/source-dependencies/empty-modified-names/changes/T.scala b/sbt-dotty/sbt-test/source-dependencies/empty-modified-names/changes/T.scala new file mode 100644 index 000000000000..1d0c3cfef089 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/empty-modified-names/changes/T.scala @@ -0,0 +1,3 @@ +trait T2 { def foo: String } +trait T1 extends T2 +class C1 extends T1 { def foo = "test" } diff --git a/sbt-dotty/sbt-test/source-dependencies/empty-modified-names/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/empty-modified-names/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/empty-modified-names/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/empty-modified-names/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/empty-modified-names/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/empty-modified-names/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/empty-modified-names/test b/sbt-dotty/sbt-test/source-dependencies/empty-modified-names/test new file mode 100644 index 000000000000..f8822aa9bb86 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/empty-modified-names/test @@ -0,0 +1,3 @@ +> compile +$ copy-file changes/T.scala T.scala +> compile diff --git a/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/changed/FactoryProvider.scala b/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/changed/FactoryProvider.scala new file mode 100644 index 000000000000..7a15e03339f1 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/changed/FactoryProvider.scala @@ -0,0 +1,6 @@ +package foo + +object FactoryProvider { + import Types.:: + type MyFactory = FactoryA :: Nil +} diff --git a/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/src/main/scala/foo/FactoryProvider.scala b/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/src/main/scala/foo/FactoryProvider.scala new file mode 100644 index 000000000000..224479fd9da5 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/src/main/scala/foo/FactoryProvider.scala @@ -0,0 +1,6 @@ +package foo + +object FactoryProvider { + import Types.:: + type MyFactory = FactoryB :: Nil +} diff --git a/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/src/main/scala/foo/Lib.scala b/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/src/main/scala/foo/Lib.scala new file mode 100644 index 000000000000..16c7f75a7b07 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/src/main/scala/foo/Lib.scala @@ -0,0 +1,36 @@ +package foo + +trait Factory { + type Product +} + +trait Type extends Factory { + type Prepend[P <: Factory] <: Type +} + +trait Types[A <: Factory, B <: Type] extends Type { + override type Prepend[P <: Factory] = Types[P, Types[A, B]] + type Product = A#Product with B#Product +} + +object Types { + type ::[H <: Factory, T <: Type] = T#Prepend[H] +} + +trait Nil extends Type { + type Prepend[P <: Factory] = Types[P, Nil] +} + +trait FactoryA extends Type { + override type Product = A +} + +trait A + +trait FactoryB extends Factory { + override type Product = B +} + +trait B { + def foo = 1 +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/src/main/scala/foo/Usage.scala b/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/src/main/scala/foo/Usage.scala new file mode 100644 index 000000000000..a59a0ce86589 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/src/main/scala/foo/Usage.scala @@ -0,0 +1,7 @@ +package foo + +trait Usage { + def x: FactoryProvider.type#MyFactory#Product + + x.foo +} diff --git a/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/test b/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/test new file mode 100644 index 000000000000..1b3bdede11da --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/expanded-type-projection/test @@ -0,0 +1,5 @@ +> compile +$ copy-file changed/FactoryProvider.scala src/main/scala/foo/FactoryProvider.scala +-> compile +> clean +-> compile \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/export-jars/build.sbt b/sbt-dotty/sbt-test/source-dependencies/export-jars/build.sbt new file mode 100644 index 000000000000..8b39c7cf680e --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/export-jars/build.sbt @@ -0,0 +1,2 @@ +lazy val root = project.in(file(".")).dependsOn(a) +lazy val a = project.in(file("a")) diff --git a/sbt-dotty/sbt-test/source-dependencies/export-jars/changes/build2.sbt b/sbt-dotty/sbt-test/source-dependencies/export-jars/changes/build2.sbt index 0f5735bc81d0..3fa7cbb370fb 100644 --- a/sbt-dotty/sbt-test/source-dependencies/export-jars/changes/build2.sbt +++ b/sbt-dotty/sbt-test/source-dependencies/export-jars/changes/build2.sbt @@ -1 +1,4 @@ +lazy val root = Project("root", file(".")) dependsOn(a) +lazy val a = Project("a", file("a")) + exportJars := true \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/export-jars/project/Build.scala b/sbt-dotty/sbt-test/source-dependencies/export-jars/project/Build.scala deleted file mode 100644 index 4a783acbe158..000000000000 --- a/sbt-dotty/sbt-test/source-dependencies/export-jars/project/Build.scala +++ /dev/null @@ -1,7 +0,0 @@ -import sbt._ - -object Build extends Build -{ - lazy val root = Project("root", file(".")) dependsOn(a) - lazy val a = Project("a", file("a")) -} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/ext/build.sbt b/sbt-dotty/sbt-test/source-dependencies/ext/build.sbt index 8aaec76ecfc2..bbc053fa6385 100644 --- a/sbt-dotty/sbt-test/source-dependencies/ext/build.sbt +++ b/sbt-dotty/sbt-test/source-dependencies/ext/build.sbt @@ -3,7 +3,9 @@ import complete.DefaultParsers._ val checkIterations = inputKey[Unit]("Verifies the accumlated number of iterations of incremental compilation.") checkIterations := { - val expected: Int = (Space ~> NatBasic).parsed - val actual: Int = (compile in Compile).value.compilations.allCompilations.size - assert(expected == actual, s"Expected $expected compilations, got $actual") + val analysis = (compile in Compile).value.asInstanceOf[sbt.internal.inc.Analysis] + + val expected: Int = (Space ~> NatBasic).parsed + val actual: Int = analysis.compilations.allCompilations.size + assert(expected == actual, s"Expected $expected compilations, got $actual") } \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/changes/A1.scala b/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/changes/A1.scala new file mode 100644 index 000000000000..c56e4578f9a3 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/changes/A1.scala @@ -0,0 +1,3 @@ +import scala.languageFeature.higherKinds +trait A +object A diff --git a/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/changes/A2.scala b/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/changes/A2.scala new file mode 100644 index 000000000000..1b749793de4f --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/changes/A2.scala @@ -0,0 +1,5 @@ +import scala.languageFeature.higherKinds +trait A +object A { + implicit def m[MM[_], A]: MM[A] = ??? +} diff --git a/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/changes/B.scala b/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/changes/B.scala new file mode 100644 index 000000000000..c49ed55ad89a --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/changes/B.scala @@ -0,0 +1,2 @@ + +trait B extends A diff --git a/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/changes/C.scala b/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/changes/C.scala new file mode 100644 index 000000000000..7fd8032223f0 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/changes/C.scala @@ -0,0 +1,3 @@ +object Test { + implicitly[M[B]] +} diff --git a/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/changes/M.scala b/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/changes/M.scala new file mode 100644 index 000000000000..9fd664beae5a --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/changes/M.scala @@ -0,0 +1,6 @@ +import scala.languageFeature.higherKinds + +class M[A](a: A) +object M { + implicit def m[MM[_], A]: MM[A] = ??? +} diff --git a/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/test b/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/test new file mode 100644 index 000000000000..e5f50944f1d4 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/implicit-search-higher-kinded/test @@ -0,0 +1,14 @@ +# Tests if dependencies on implicit scope are tracked properly +# We use higher kinded types in order to make type checker to +# infer more and thus obscure true dependencies +$ copy-file changes/A1.scala A.scala +$ copy-file changes/B.scala B.scala +$ copy-file changes/M.scala M.scala +$ copy-file changes/C.scala C.scala +> compile + +$ copy-file changes/A2.scala A.scala +-> compile + +> clean +-> compile diff --git a/sbt-dotty/sbt-test/source-dependencies/import-class/B.scala b/sbt-dotty/sbt-test/source-dependencies/import-class/B.scala index 0489f4a26c29..eb81ff6bd6e7 100644 --- a/sbt-dotty/sbt-test/source-dependencies/import-class/B.scala +++ b/sbt-dotty/sbt-test/source-dependencies/import-class/B.scala @@ -1 +1,3 @@ import a.A + +class B diff --git a/sbt-dotty/sbt-test/source-dependencies/inherited-deps-java/test b/sbt-dotty/sbt-test/source-dependencies/inherited-deps-java/disabled similarity index 100% rename from sbt-dotty/sbt-test/source-dependencies/inherited-deps-java/test rename to sbt-dotty/sbt-test/source-dependencies/inherited-deps-java/disabled diff --git a/sbt-dotty/sbt-test/source-dependencies/inherited_type_params/build.sbt b/sbt-dotty/sbt-test/source-dependencies/inherited_type_params/build.sbt index a5982f901fd7..e25f1beeaaba 100644 --- a/sbt-dotty/sbt-test/source-dependencies/inherited_type_params/build.sbt +++ b/sbt-dotty/sbt-test/source-dependencies/inherited_type_params/build.sbt @@ -1,7 +1,8 @@ name := "test" -TaskKey[Unit]("check-same") <<= compile in Configurations.Compile map { analysis => - analysis.apis.internal foreach { case (_, api) => - assert( xsbt.api.SameAPI(api.api, api.api) ) - } -} \ No newline at end of file +TaskKey[Unit]("check-same") := { + val analysis = (compile in Compile).value.asInstanceOf[sbt.internal.inc.Analysis] + analysis.apis.internal.foreach { case (_, api) => + assert(xsbt.api.SameAPI(api.api, api.api)) + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/inherited_type_params/test b/sbt-dotty/sbt-test/source-dependencies/inherited_type_params/test index 8434347c5a23..353461049b5f 100644 --- a/sbt-dotty/sbt-test/source-dependencies/inherited_type_params/test +++ b/sbt-dotty/sbt-test/source-dependencies/inherited_type_params/test @@ -1 +1 @@ -> check-same \ No newline at end of file +> checkSame \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/JJ.java b/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/JJ.java new file mode 100644 index 000000000000..1ae12067396e --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/JJ.java @@ -0,0 +1,12 @@ +public class JJ { + public static void main(String[] args) { + // Declare anonymous class depending on Scala class + class FromScala extends S { + public void foo(String s) { + System.out.println(s); + } + } + S s = new FromScala(); + s.foo("ahoy"); + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/build.sbt b/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/build.sbt new file mode 100644 index 000000000000..f59b98eb6267 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/build.sbt @@ -0,0 +1 @@ +compileOrder := CompileOrder.Mixed diff --git a/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/changes/S1.scala b/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/changes/S1.scala new file mode 100644 index 000000000000..2400ac4da6df --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/changes/S1.scala @@ -0,0 +1,3 @@ +abstract class S { + def foo(s:String): Unit +} diff --git a/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/changes/S2.scala b/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/changes/S2.scala new file mode 100644 index 000000000000..dd84be54b8eb --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/changes/S2.scala @@ -0,0 +1,3 @@ +abstract class S { + def foo2(s:String): Unit +} diff --git a/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/test b/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/test new file mode 100644 index 000000000000..502d3699257f --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/inner-class-java-depends-on-scala/test @@ -0,0 +1,4 @@ +$ copy-file changes/S1.scala S.scala +> compile +$ copy-file changes/S2.scala S.scala +-> compile diff --git a/sbt-dotty/sbt-test/source-dependencies/java-analysis-serialization-error/build.sbt b/sbt-dotty/sbt-test/source-dependencies/java-analysis-serialization-error/build.sbt index 1b1ddefb98ce..f78f33916fce 100644 --- a/sbt-dotty/sbt-test/source-dependencies/java-analysis-serialization-error/build.sbt +++ b/sbt-dotty/sbt-test/source-dependencies/java-analysis-serialization-error/build.sbt @@ -1 +1 @@ -incOptions := incOptions.value.withNameHashing(true).withApiDebug(true) +incOptions := incOptions.value.withApiDebug(true) diff --git a/sbt-dotty/sbt-test/source-dependencies/java-anonymous/Outer.java b/sbt-dotty/sbt-test/source-dependencies/java-anonymous/Outer.java new file mode 100644 index 000000000000..6b0da572204b --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-anonymous/Outer.java @@ -0,0 +1,9 @@ +class Outer { + Object getAnonymous() { + return new Object() { + int foo() { + return 842; + } + }; + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/java-anonymous/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/java-anonymous/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-anonymous/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/java-anonymous/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/java-anonymous/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-anonymous/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/java-anonymous/test b/sbt-dotty/sbt-test/source-dependencies/java-anonymous/test new file mode 100644 index 000000000000..a43628b0f196 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-anonymous/test @@ -0,0 +1,2 @@ +# This test doesn't check anything due to: https://github.com/sbt/zinc/issues/83 +> compile diff --git a/sbt-dotty/sbt-test/source-dependencies/java-enum/changes/SomeEnum.java b/sbt-dotty/sbt-test/source-dependencies/java-enum/changes/SomeEnum.java new file mode 100644 index 000000000000..b0fed9f02725 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-enum/changes/SomeEnum.java @@ -0,0 +1,18 @@ +package pl.typosafe; + +/** + * Author: Krzysztof Romanowski + */ +public enum SomeEnum { + Baz, + Bar { + @Override + public int foo() { + return 2; + } + }; + + public int foo(){ + return 1; + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/java-enum/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/java-enum/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-enum/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/java-enum/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/java-enum/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-enum/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/java-enum/test b/sbt-dotty/sbt-test/source-dependencies/java-enum/test new file mode 100644 index 000000000000..6b3955e8d713 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-enum/test @@ -0,0 +1,2 @@ +$ copy-file changes/SomeEnum.java src/main/java/pl/typosafe/SomeEnum.java +> compile diff --git a/sbt-dotty/sbt-test/source-dependencies/java-inner/A.java b/sbt-dotty/sbt-test/source-dependencies/java-inner/A.java new file mode 100644 index 000000000000..bcd7b8034986 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-inner/A.java @@ -0,0 +1,5 @@ +class A { + class B { + public D d = new D(); + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/java-inner/C.java b/sbt-dotty/sbt-test/source-dependencies/java-inner/C.java new file mode 100644 index 000000000000..c52551876c67 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-inner/C.java @@ -0,0 +1,5 @@ + +class C { + A a = new A(); + A.B b = a.new B(); +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/java-inner/D.java b/sbt-dotty/sbt-test/source-dependencies/java-inner/D.java new file mode 100644 index 000000000000..5cbe69a4f706 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-inner/D.java @@ -0,0 +1,2 @@ + +class D {} diff --git a/sbt-dotty/sbt-test/source-dependencies/java-inner/disabled b/sbt-dotty/sbt-test/source-dependencies/java-inner/disabled new file mode 100644 index 000000000000..1ab92f1e7e65 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-inner/disabled @@ -0,0 +1,8 @@ +# TODO: Implement checkProducts, checkDependencies + +> compile +> checkProducts A.java: A.class A$B.class +> checkProducts C.java: C.class +> checkDependencies A: A.B +> checkDependencies A.B: A D +> checkDependencies C: A A.B diff --git a/sbt-dotty/sbt-test/source-dependencies/java-inner/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/java-inner/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-inner/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/java-inner/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/java-inner/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-inner/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/java-lambda-typeparams/Example.java b/sbt-dotty/sbt-test/source-dependencies/java-lambda-typeparams/Example.java new file mode 100644 index 000000000000..413bb68d59f8 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-lambda-typeparams/Example.java @@ -0,0 +1,26 @@ +package typeparameters; + +import java.util.function.Supplier; + +public class Example { + + static void call() { + Supplier> blah = () -> + new BaseBlah() { + @Override + protected O getResponseInternal(I i) { + return null; + } + }; + } + + public static void main(String[] args) { + Example.call(); + } +} + +abstract class BaseBlah { + protected O getResponseInternal(I i) { + return null; + } +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/java-lambda-typeparams/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/java-lambda-typeparams/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-lambda-typeparams/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/java-lambda-typeparams/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/java-lambda-typeparams/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-lambda-typeparams/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/java-lambda-typeparams/test b/sbt-dotty/sbt-test/source-dependencies/java-lambda-typeparams/test new file mode 100644 index 000000000000..73a68203f3f1 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-lambda-typeparams/test @@ -0,0 +1 @@ +> compile \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/java-name-with-dollars/JFunction2$mcJDD$sp.java b/sbt-dotty/sbt-test/source-dependencies/java-name-with-dollars/JFunction2$mcJDD$sp.java new file mode 100644 index 000000000000..a3e928e6c6e1 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-name-with-dollars/JFunction2$mcJDD$sp.java @@ -0,0 +1,5 @@ +public interface JFunction2$mcJDD$sp { + long apply$mcJDD$sp(double v1, double v2); + + Object apply(Object v1, Object v2); +} diff --git a/sbt-dotty/sbt-test/source-dependencies/java-name-with-dollars/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/java-name-with-dollars/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-name-with-dollars/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/java-name-with-dollars/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/java-name-with-dollars/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-name-with-dollars/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/java-name-with-dollars/test b/sbt-dotty/sbt-test/source-dependencies/java-name-with-dollars/test new file mode 100644 index 000000000000..a43628b0f196 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/java-name-with-dollars/test @@ -0,0 +1,2 @@ +# This test doesn't check anything due to: https://github.com/sbt/zinc/issues/83 +> compile diff --git a/sbt-dotty/sbt-test/source-dependencies/less-inter-inv-java/build.sbt b/sbt-dotty/sbt-test/source-dependencies/less-inter-inv-java/build.sbt index d23dff7054d2..d7524d433978 100644 --- a/sbt-dotty/sbt-test/source-dependencies/less-inter-inv-java/build.sbt +++ b/sbt-dotty/sbt-test/source-dependencies/less-inter-inv-java/build.sbt @@ -3,8 +3,10 @@ import complete.DefaultParsers._ val checkIterations = inputKey[Unit]("Verifies the accumlated number of iterations of incremental compilation.") checkIterations := { - val expected: Int = (Space ~> NatBasic).parsed - val actual: Int = (compile in Compile).value.compilations.allCompilations.size - assert(expected == actual, s"Expected $expected compilations, got $actual") + val analysis = (compile in Compile).value.asInstanceOf[sbt.internal.inc.Analysis] + + val expected: Int = (Space ~> NatBasic).parsed + val actual: Int = analysis.compilations.allCompilations.size + assert(expected == actual, s"Expected $expected compilations, got $actual") } diff --git a/sbt-dotty/sbt-test/source-dependencies/less-inter-inv/build.sbt b/sbt-dotty/sbt-test/source-dependencies/less-inter-inv/build.sbt index d23dff7054d2..d7524d433978 100644 --- a/sbt-dotty/sbt-test/source-dependencies/less-inter-inv/build.sbt +++ b/sbt-dotty/sbt-test/source-dependencies/less-inter-inv/build.sbt @@ -3,8 +3,10 @@ import complete.DefaultParsers._ val checkIterations = inputKey[Unit]("Verifies the accumlated number of iterations of incremental compilation.") checkIterations := { - val expected: Int = (Space ~> NatBasic).parsed - val actual: Int = (compile in Compile).value.compilations.allCompilations.size - assert(expected == actual, s"Expected $expected compilations, got $actual") + val analysis = (compile in Compile).value.asInstanceOf[sbt.internal.inc.Analysis] + + val expected: Int = (Space ~> NatBasic).parsed + val actual: Int = analysis.compilations.allCompilations.size + assert(expected == actual, s"Expected $expected compilations, got $actual") } diff --git a/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/A.java b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/A.java new file mode 100644 index 000000000000..0ae4e417add2 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/A.java @@ -0,0 +1,3 @@ +public class A { + public A() {} +} diff --git a/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/B.java b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/B.java new file mode 100644 index 000000000000..71f266b3596f --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/B.java @@ -0,0 +1,3 @@ +public class B extends A { + public B() {} +} diff --git a/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/C.scala b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/C.scala new file mode 100644 index 000000000000..2e16281b342d --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/C.scala @@ -0,0 +1,5 @@ +class C { + def foo: Unit = { + class Foo extends B + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/D.scala b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/D.scala new file mode 100644 index 000000000000..765d291e5733 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/D.scala @@ -0,0 +1,5 @@ +class D extends C { + // mention abc name to check if local inheritance dependencies are _not_ included in member reference + // extension of inheritance invalidation + def bar(abc: Int): Int = abc +} diff --git a/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/changes/A2.java b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/changes/A2.java new file mode 100644 index 000000000000..a56de2c644f4 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/changes/A2.java @@ -0,0 +1,6 @@ +public class A { + public A() {} + public String hey() { + return "Hey"; + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/disabled b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/disabled new file mode 100644 index 000000000000..c4a216c9a182 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/disabled @@ -0,0 +1,18 @@ +# TODO: Implement checkRecompilations + +# Verifies if dependencies introduced by Java inheritance by local classes are handled correctly. +# See sbt-test 'local-class-inheritance' for a similar test in Scala. + +> compile + +$ copy-file changes/A2.java A.java +> compile +# D should be compiled only at the beginning; it depends by inheritance only +# on C and C's public interface is not affected by changes to A +> checkRecompilations 0 D +# A is explicitly changed +> checkRecompilations 1 A +# B is recompiled because it depends by inheritance on A +# C is recompiled because it depends by local inheritance on B but its +# dependencies (D) are not recompiled +> checkRecompilations 2 B C diff --git a/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance-from-java/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/A.scala b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/A.scala new file mode 100644 index 000000000000..83d15dc739b5 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/A.scala @@ -0,0 +1 @@ +class A diff --git a/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/B.scala b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/B.scala new file mode 100644 index 000000000000..26e47fd25fdb --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/B.scala @@ -0,0 +1 @@ +class B extends A \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/C.scala b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/C.scala new file mode 100644 index 000000000000..2e16281b342d --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/C.scala @@ -0,0 +1,5 @@ +class C { + def foo: Unit = { + class Foo extends B + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/D.scala b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/D.scala new file mode 100644 index 000000000000..765d291e5733 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/D.scala @@ -0,0 +1,5 @@ +class D extends C { + // mention abc name to check if local inheritance dependencies are _not_ included in member reference + // extension of inheritance invalidation + def bar(abc: Int): Int = abc +} diff --git a/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/changes/A2.scala b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/changes/A2.scala new file mode 100644 index 000000000000..5220e08b9a07 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/changes/A2.scala @@ -0,0 +1,3 @@ +class A { + def abc: Int = 124 +} diff --git a/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/disabled b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/disabled new file mode 100644 index 000000000000..2feb6c3c5dc9 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/disabled @@ -0,0 +1,19 @@ +# TODO: Implement checkRecompilations + +# Verifies if dependencies introduced by inheritance by local classes are handled correctly. +# They are different from regular inheritance dependencies because they should not be +# invalidated transitively, see: https://github.com/sbt/sbt/issues/1104#issuecomment-169146039 + +> compile + +$ copy-file changes/A2.scala A.scala +> compile +# D should be compiled only at the beginning; it depends by inheritance only +# on C and C's public interface is not affected by changes to A +> checkRecompilations 0 D +# A is explicitly changed +> checkRecompilations 1 A +# B is recompiled because it depends by inheritance on A +# C is recompiled because it depends by local inheritance on B but its +# dependencies (D) are not recompiled +> checkRecompilations 2 B C diff --git a/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/local-class-inheritance/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/malformed-class-name-with-dollar/B.java b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name-with-dollar/B.java new file mode 100644 index 000000000000..2482b5bb465d --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name-with-dollar/B.java @@ -0,0 +1,5 @@ +public class B { + public static class C$ { + public static int x = 42; + } +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/malformed-class-name-with-dollar/changes/A.scala b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name-with-dollar/changes/A.scala new file mode 100644 index 000000000000..d17a3273a0c8 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name-with-dollar/changes/A.scala @@ -0,0 +1,3 @@ +object A { + def foo = B.C$.x +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/malformed-class-name-with-dollar/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name-with-dollar/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name-with-dollar/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/malformed-class-name-with-dollar/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name-with-dollar/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name-with-dollar/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/malformed-class-name-with-dollar/test b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name-with-dollar/test new file mode 100644 index 000000000000..b0353a1a0cca --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name-with-dollar/test @@ -0,0 +1,3 @@ +> compile +$ copy-file changes/A.scala A.scala +> compile diff --git a/sbt-dotty/sbt-test/source-dependencies/malformed-class-name/Boo.scala b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name/Boo.scala new file mode 100644 index 000000000000..ed982b750cdf --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name/Boo.scala @@ -0,0 +1,48 @@ +package repro + +abstract class Boo { + val b = Boo +} + +// Tests simple class in nested objects +object Boo { + object Foo { + class Impl + } +} + +abstract class Boo2 { + val b2 = Boo2 +} + +// Tests simple class in object, special-cased (by Scala) +// It does and should not trigger malformed class name. +object Boo2 { + class Impl +} + +abstract class Boo3 { + val b3 = Boo3 +} + +// Tests three nested object + class +object Boo3 { + object Foo2 { + object Bar { + class Impl + } + } +} + +abstract class Boo4 { + val b4 = Boo4 +} + +// Tests nested classes inside nested objects +object Boo4 { + object Foo3 { + class BarClass { + class Impl + } + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/malformed-class-name/changes/BooUser.java b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name/changes/BooUser.java new file mode 100644 index 000000000000..005f86c1ade0 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name/changes/BooUser.java @@ -0,0 +1,32 @@ +package repro; + +import repro.Boo; +import repro.Boo2; +import repro.Boo3; +import repro.Boo4; + +public class BooUser { + private static Boo getOwnBuf() { + return new Boo() {}; + } + + private static Boo2 getOwnBuf2() { + return new Boo2() {}; + } + + private static Boo3 getOwnBuf3() { + return new Boo3() {}; + } + + private static Boo4 getOwnBuf4() { + return new Boo4() {}; + } + + public static void main(String[] args) { + Boo boo = getOwnBuf(); + Boo2 boo2 = getOwnBuf2(); + Boo3 boo3 = getOwnBuf3(); + Boo4 boo4 = getOwnBuf4(); + System.out.println("Made a buf: " + boo); + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/malformed-class-name/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/malformed-class-name/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/malformed-class-name/test b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name/test new file mode 100644 index 000000000000..cbe46d17f239 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/malformed-class-name/test @@ -0,0 +1,3 @@ +> compile +$ copy-file changes/BooUser.java BooUser.java +> compile diff --git a/sbt-dotty/sbt-test/source-dependencies/nested-type-params/A.scala b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/A.scala new file mode 100644 index 000000000000..7bfd0a4e4b24 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/A.scala @@ -0,0 +1 @@ +trait A { def a = 1 } \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/nested-type-params/B.scala b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/B.scala new file mode 100644 index 000000000000..ab2df2a85ad6 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/B.scala @@ -0,0 +1 @@ +trait B { def b = 1.0 } \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/nested-type-params/Bar.scala b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/Bar.scala new file mode 100644 index 000000000000..331d12769d9a --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/Bar.scala @@ -0,0 +1 @@ +object Bar { Foo.provide.a } \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/nested-type-params/Foo.scala b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/Foo.scala new file mode 100644 index 000000000000..a1bf7a7ac8e1 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/Foo.scala @@ -0,0 +1,4 @@ +object Foo { + // Compilation will also fail if we change Providers.type#SomeProvider to Provider + def provide: Providers.type#SomeProvider#Operations = ??? +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/nested-type-params/Providers.scala b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/Providers.scala new file mode 100644 index 000000000000..8747ea143c43 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/Providers.scala @@ -0,0 +1,4 @@ +trait Provider { type Operations = A } + +object Providers { type SomeProvider = Provider } + diff --git a/sbt-dotty/sbt-test/source-dependencies/nested-type-params/changes/Bar.scala b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/changes/Bar.scala new file mode 100644 index 000000000000..2c9f2a546be9 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/changes/Bar.scala @@ -0,0 +1 @@ +object Bar { Foo.provide.b } \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/nested-type-params/changes/Providers.scala b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/changes/Providers.scala new file mode 100644 index 000000000000..e20f723e012d --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/changes/Providers.scala @@ -0,0 +1,4 @@ +trait Provider { type Operations = B } + +object Providers { type SomeProvider = Provider } + diff --git a/sbt-dotty/sbt-test/source-dependencies/nested-type-params/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/nested-type-params/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/nested-type-params/test b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/test new file mode 100644 index 000000000000..91ef01d64823 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/nested-type-params/test @@ -0,0 +1,11 @@ +> compile + +$ copy-file changes/Providers.scala Providers.scala +$ copy-file changes/Bar.scala Bar.scala + +# This should compile +> compile + +# Make sure that clean compile is fine +> clean +> compile diff --git a/sbt-dotty/sbt-test/source-dependencies/package-object-name/A.scala b/sbt-dotty/sbt-test/source-dependencies/package-object-name/A.scala new file mode 100644 index 000000000000..8f3c381af340 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/package-object-name/A.scala @@ -0,0 +1,3 @@ +package b + +class A diff --git a/sbt-dotty/sbt-test/source-dependencies/package-object-name/b.scala b/sbt-dotty/sbt-test/source-dependencies/package-object-name/b.scala new file mode 100644 index 000000000000..0a6e215a5258 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/package-object-name/b.scala @@ -0,0 +1 @@ +package object b extends A diff --git a/sbt-dotty/sbt-test/source-dependencies/package-object-name/changes/A1.scala b/sbt-dotty/sbt-test/source-dependencies/package-object-name/changes/A1.scala new file mode 100644 index 000000000000..b759f29cb5b6 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/package-object-name/changes/A1.scala @@ -0,0 +1,5 @@ +package b + +class A { + def foo = 1 +} diff --git a/sbt-dotty/sbt-test/source-dependencies/package-object-name/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/package-object-name/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/package-object-name/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/package-object-name/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/package-object-name/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/package-object-name/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/package-object-name/test b/sbt-dotty/sbt-test/source-dependencies/package-object-name/test new file mode 100644 index 000000000000..7c3c1b8f3bfd --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/package-object-name/test @@ -0,0 +1,3 @@ +> compile +$ copy-file changes/A1.scala A.scala +> compile diff --git a/sbt-dotty/sbt-test/source-dependencies/package-object-nested-class/A.scala b/sbt-dotty/sbt-test/source-dependencies/package-object-nested-class/A.scala new file mode 100644 index 000000000000..3ccf3c3a5561 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/package-object-nested-class/A.scala @@ -0,0 +1,6 @@ +package object abc { + object BuildInfoKey { + sealed trait Entry + } + class Foo +} diff --git a/sbt-dotty/sbt-test/source-dependencies/package-object-nested-class/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/package-object-nested-class/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/package-object-nested-class/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/package-object-nested-class/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/package-object-nested-class/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/package-object-nested-class/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/package-object-nested-class/test b/sbt-dotty/sbt-test/source-dependencies/package-object-nested-class/test new file mode 100644 index 000000000000..8a33e63a9c93 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/package-object-nested-class/test @@ -0,0 +1,2 @@ +> compile + diff --git a/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/Bar.scala b/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/Bar.scala new file mode 100644 index 000000000000..540cf78387f8 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/Bar.scala @@ -0,0 +1,7 @@ +import foo.Foo +import Baz.myNr + + +class Bar { + val nr: Int = implicitly +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/Baz.scala b/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/Baz.scala new file mode 100644 index 000000000000..f3d6070b846d --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/Baz.scala @@ -0,0 +1,3 @@ +object Baz { + implicit val myNr = 1234 +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/Foo.scala b/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/Foo.scala new file mode 100644 index 000000000000..6374dc2d5d73 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/Foo.scala @@ -0,0 +1,3 @@ +package foo + +trait Foo \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/foo.scala2 b/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/foo.scala2 new file mode 100644 index 000000000000..bb7e2e513814 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/foo.scala2 @@ -0,0 +1,3 @@ +package object foo { + implicit val Foo = 123 +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/pending b/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/pending new file mode 100644 index 000000000000..2c026c0b4e30 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/pending @@ -0,0 +1,4 @@ +> compile +$ copy-file foo.scala2 foo.scala +# clean compile fails correctly +-> compile \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/packageobject-and-traits/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/pkg-private-class/A.scala b/sbt-dotty/sbt-test/source-dependencies/pkg-private-class/A.scala new file mode 100644 index 000000000000..3669f315aab5 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/pkg-private-class/A.scala @@ -0,0 +1,4 @@ +package pkg1 +private class A { + def foo: Int = 1 +} diff --git a/sbt-dotty/sbt-test/source-dependencies/pkg-private-class/B.scala b/sbt-dotty/sbt-test/source-dependencies/pkg-private-class/B.scala new file mode 100644 index 000000000000..49fc39b478b6 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/pkg-private-class/B.scala @@ -0,0 +1,5 @@ +package pkg1 + +class B { + def bar(a: A): Int = a.foo +} diff --git a/sbt-dotty/sbt-test/source-dependencies/pkg-private-class/changes/A.scala b/sbt-dotty/sbt-test/source-dependencies/pkg-private-class/changes/A.scala new file mode 100644 index 000000000000..c1720c821deb --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/pkg-private-class/changes/A.scala @@ -0,0 +1,4 @@ +package pkg1 +private class A { + //def foo: Int = 1 +} diff --git a/sbt-dotty/sbt-test/source-dependencies/pkg-private-class/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/pkg-private-class/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/pkg-private-class/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/pkg-private-class/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/pkg-private-class/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/pkg-private-class/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/pkg-private-class/test b/sbt-dotty/sbt-test/source-dependencies/pkg-private-class/test new file mode 100644 index 000000000000..4896f0dcc042 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/pkg-private-class/test @@ -0,0 +1,5 @@ +> compile + +$ copy-file changes/A.scala A.scala + +-> compile diff --git a/sbt-dotty/sbt-test/source-dependencies/recorded-products/DefaultPkg.scala b/sbt-dotty/sbt-test/source-dependencies/recorded-products/DefaultPkg.scala new file mode 100644 index 000000000000..933a667d7b61 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/recorded-products/DefaultPkg.scala @@ -0,0 +1,3 @@ +class A + +object B diff --git a/sbt-dotty/sbt-test/source-dependencies/recorded-products/Local.scala b/sbt-dotty/sbt-test/source-dependencies/recorded-products/Local.scala new file mode 100644 index 000000000000..1229fe717146 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/recorded-products/Local.scala @@ -0,0 +1,12 @@ + +class Container { + def foo = { + class C + } + def bar = { + // anonymous class + new T {} + } +} + +trait T diff --git a/sbt-dotty/sbt-test/source-dependencies/recorded-products/Nested.scala b/sbt-dotty/sbt-test/source-dependencies/recorded-products/Nested.scala new file mode 100644 index 000000000000..1c908527bd0e --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/recorded-products/Nested.scala @@ -0,0 +1,17 @@ +package foo.bar + +class Outer { + object InnerO { + class A + object B + } + class InnerC { + trait T + } +} + +object Outer { + class X { + object Y + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/recorded-products/disabled b/sbt-dotty/sbt-test/source-dependencies/recorded-products/disabled new file mode 100644 index 000000000000..60dd3bd42cc9 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/recorded-products/disabled @@ -0,0 +1,6 @@ +# TODO: Implement checkProducts + +> compile +> checkProducts DefaultPkg.scala: A.class B$.class B.class +> checkProducts Nested.scala: foo/bar/Outer$InnerC$T.class foo/bar/Outer$InnerO$B$.class foo/bar/Outer$InnerO$.class foo/bar/Outer$X.class foo/bar/Outer$.class foo/bar/Outer.class foo/bar/Outer$InnerC.class foo/bar/Outer$X$Y$.class foo/bar/Outer$InnerO$A.class +> checkProducts Local.scala: T.class Container.class Container$$anon$1.class Container$C$1.class diff --git a/sbt-dotty/sbt-test/source-dependencies/recorded-products/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/recorded-products/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/recorded-products/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/recorded-products/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/recorded-products/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/recorded-products/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/replace-test-a/build.sbt b/sbt-dotty/sbt-test/source-dependencies/replace-test-a/build.sbt new file mode 100644 index 000000000000..c63b7bc8a91a --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/replace-test-a/build.sbt @@ -0,0 +1,14 @@ +import java.net.URLClassLoader + +lazy val root = project.in(file(".")). + settings( + TaskKey[Unit]("check-first") := checkTask("First").value, + TaskKey[Unit]("check-second") := checkTask("Second").value + ) + +def checkTask(className: String) = Def.task { + val runClasspath = (fullClasspath in Runtime).value + val cp = runClasspath.map(_.data.toURI.toURL).toArray + Class.forName(className, false, new URLClassLoader(cp)) + () +} diff --git a/sbt-dotty/sbt-test/source-dependencies/replace-test-a/project/Build.scala b/sbt-dotty/sbt-test/source-dependencies/replace-test-a/project/Build.scala deleted file mode 100644 index 9c2678540442..000000000000 --- a/sbt-dotty/sbt-test/source-dependencies/replace-test-a/project/Build.scala +++ /dev/null @@ -1,19 +0,0 @@ -import sbt._ -import Keys._ -import java.net.URLClassLoader - -object B extends Build -{ - lazy val root = Project("root", file(".")) settings( ss : _*) - - def ss = Seq( - TaskKey[Unit]("check-first") <<= checkTask("First"), - TaskKey[Unit]("check-second") <<= checkTask("Second") - ) - private def checkTask(className: String) = - fullClasspath in Configurations.Runtime map { runClasspath => - val cp = runClasspath.map(_.data.toURI.toURL).toArray - Class.forName(className, false, new URLClassLoader(cp)) - () - } -} diff --git a/sbt-dotty/sbt-test/source-dependencies/replace-test-a/test b/sbt-dotty/sbt-test/source-dependencies/replace-test-a/test index 4b4ad3a2b953..21dec1db9924 100644 --- a/sbt-dotty/sbt-test/source-dependencies/replace-test-a/test +++ b/sbt-dotty/sbt-test/source-dependencies/replace-test-a/test @@ -1,9 +1,9 @@ $ copy-file changes/first.scala src/main/scala/A.scala > compile -> check-first --> check-second +> checkFirst +-> checkSecond $ copy-file changes/second.scala src/main/scala/A.scala > compile --> check-first -> check-second \ No newline at end of file +-> checkFirst +> checkSecond \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/resident-package-object/build.sbt b/sbt-dotty/sbt-test/source-dependencies/resident-package-object/build.sbt new file mode 100644 index 000000000000..894036b97cc1 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/resident-package-object/build.sbt @@ -0,0 +1,3 @@ +incOptions := incOptions.value + .withApiDebug(true) + .withRelationsDebug(true) diff --git a/sbt-dotty/sbt-test/source-dependencies/restore-classes/build.sbt b/sbt-dotty/sbt-test/source-dependencies/restore-classes/build.sbt index 2231204ea3bf..cf38564cc570 100644 --- a/sbt-dotty/sbt-test/source-dependencies/restore-classes/build.sbt +++ b/sbt-dotty/sbt-test/source-dependencies/restore-classes/build.sbt @@ -5,8 +5,10 @@ crossTarget in Compile := target.value val checkIterations = inputKey[Unit]("Verifies the accumlated number of iterations of incremental compilation.") checkIterations := { - val expected: Int = (Space ~> NatBasic).parsed - val actual: Int = (compile in Compile).value.compilations.allCompilations.size - assert(expected == actual, s"Expected $expected compilations, got $actual") + val analysis = (compile in Compile).value.asInstanceOf[sbt.internal.inc.Analysis] + + val expected: Int = (Space ~> NatBasic).parsed + val actual: Int = analysis.compilations.allCompilations.size + assert(expected == actual, s"Expected $expected compilations, got $actual") } diff --git a/sbt-dotty/sbt-test/source-dependencies/same-file-used-names/build.sbt b/sbt-dotty/sbt-test/source-dependencies/same-file-used-names/build.sbt deleted file mode 100644 index 8a38ef41424b..000000000000 --- a/sbt-dotty/sbt-test/source-dependencies/same-file-used-names/build.sbt +++ /dev/null @@ -1 +0,0 @@ -incOptions := incOptions.value.withNameHashing(true) diff --git a/sbt-dotty/sbt-test/source-dependencies/struct-projection/A.scala b/sbt-dotty/sbt-test/source-dependencies/struct-projection/A.scala new file mode 100644 index 000000000000..f5a48b4ade9e --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct-projection/A.scala @@ -0,0 +1,3 @@ +object A { + def m: ({type T <: Int})#T = ??? +} diff --git a/sbt-dotty/sbt-test/source-dependencies/struct-projection/B.scala b/sbt-dotty/sbt-test/source-dependencies/struct-projection/B.scala new file mode 100644 index 000000000000..8890082305ea --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct-projection/B.scala @@ -0,0 +1,3 @@ +object B { + val x: Int = A.m +} diff --git a/sbt-dotty/sbt-test/source-dependencies/struct-projection/changes/A.scala b/sbt-dotty/sbt-test/source-dependencies/struct-projection/changes/A.scala new file mode 100644 index 000000000000..da04006f2628 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct-projection/changes/A.scala @@ -0,0 +1,3 @@ +object A { + def m: ({type T <: String})#T = ??? +} diff --git a/sbt-dotty/sbt-test/source-dependencies/struct-projection/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/struct-projection/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct-projection/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/struct-projection/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/struct-projection/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct-projection/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/struct-projection/test b/sbt-dotty/sbt-test/source-dependencies/struct-projection/test new file mode 100644 index 000000000000..ea02287e6997 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct-projection/test @@ -0,0 +1,4 @@ +> compile +$ copy-file changes/A.scala A.scala +# Compilation of B.scala should fail because type of A.m changed +-> compile diff --git a/sbt-dotty/sbt-test/source-dependencies/struct-usage/A.scala b/sbt-dotty/sbt-test/source-dependencies/struct-usage/A.scala new file mode 100644 index 000000000000..b295547546c4 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct-usage/A.scala @@ -0,0 +1,3 @@ +object A { + def x: { def q: Int } = sys.error("not important") +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/struct-usage/B.scala b/sbt-dotty/sbt-test/source-dependencies/struct-usage/B.scala new file mode 100644 index 000000000000..b32b6c72097e --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct-usage/B.scala @@ -0,0 +1,5 @@ +import scala.reflect.Selectable.reflectiveSelectable + +object B { + val y: Int = A.x.q +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/struct-usage/changes/A.scala b/sbt-dotty/sbt-test/source-dependencies/struct-usage/changes/A.scala new file mode 100644 index 000000000000..00841314fa55 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct-usage/changes/A.scala @@ -0,0 +1,3 @@ +object A { + def x: { def q: String } = sys.error("not important") +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/struct-usage/pending b/sbt-dotty/sbt-test/source-dependencies/struct-usage/pending new file mode 100644 index 000000000000..751cde01bc62 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct-usage/pending @@ -0,0 +1,5 @@ +> compile + +$ copy-file changes/A.scala A.scala + +-> compile \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/struct-usage/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/struct-usage/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct-usage/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/struct-usage/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/struct-usage/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct-usage/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/struct/A.scala b/sbt-dotty/sbt-test/source-dependencies/struct/A.scala new file mode 100644 index 000000000000..d17a6e20a2c1 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct/A.scala @@ -0,0 +1,3 @@ +object A { + def x: Int = 3 +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/struct/B.scala b/sbt-dotty/sbt-test/source-dependencies/struct/B.scala new file mode 100644 index 000000000000..901e7deeff67 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct/B.scala @@ -0,0 +1,6 @@ +import scala.reflect.Selectable.reflectiveSelectable + +object B { + def onX(m: { def x: Int } ) = + m.x +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/struct/C.scala b/sbt-dotty/sbt-test/source-dependencies/struct/C.scala new file mode 100644 index 000000000000..413cd6d636cd --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct/C.scala @@ -0,0 +1,4 @@ +object C { + def main(args: Array[String]) = + println(B.onX(A)) +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/struct/changes/A.scala b/sbt-dotty/sbt-test/source-dependencies/struct/changes/A.scala new file mode 100644 index 000000000000..dc9bbd3c07d5 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct/changes/A.scala @@ -0,0 +1,3 @@ +object A { + def x: Byte = 3 +} diff --git a/sbt-dotty/sbt-test/source-dependencies/struct/disabled b/sbt-dotty/sbt-test/source-dependencies/struct/disabled new file mode 100644 index 000000000000..72a7a8ebd654 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct/disabled @@ -0,0 +1,9 @@ +# Marked as pending because name hashing doesn't support structural types +# in some cases. See: https://github.com/sbt/sbt/issues/1545 + +> compile + +# modify A.scala so that it does not conform to the structural type in B.scala +$ copy-file changes/A.scala A.scala + +-> compile diff --git a/sbt-dotty/sbt-test/source-dependencies/struct/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/struct/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/struct/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/struct/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/struct/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/subproject-dependency-b/build.sbt b/sbt-dotty/sbt-test/source-dependencies/subproject-dependency-b/build.sbt new file mode 100644 index 000000000000..eb7dacf3414f --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/subproject-dependency-b/build.sbt @@ -0,0 +1,14 @@ +lazy val commonSettings = Seq( + logLevel := Level.Debug +) +lazy val provider = project.settings(commonSettings) +lazy val use = project.settings(commonSettings).dependsOn(provider) + +InputKey[Unit]("check-number-of-compiler-iterations") <<= inputTask { (argTask: TaskKey[Seq[String]]) => + (argTask, compile in Compile in use) map { (args: Seq[String], a: sbt.inc.Analysis) => + assert(args.size == 1) + val expectedIterationsNumber = args(0).toInt + assert(a.compilations.allCompilations.size == expectedIterationsNumber, + "a.compilations.allCompilations.size = %d (expected %d)".format(a.compilations.allCompilations.size, expectedIterationsNumber)) + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/subproject-dependency-b/changes/A1.scala b/sbt-dotty/sbt-test/source-dependencies/subproject-dependency-b/changes/A1.scala new file mode 100644 index 000000000000..fdd5b55b9c78 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/subproject-dependency-b/changes/A1.scala @@ -0,0 +1,4 @@ +// add a comment to trigger a recompilation of A.scala, this should not trigger a recompilation of B.scala +class A { + class Inner +} diff --git a/sbt-dotty/sbt-test/source-dependencies/subproject-dependency-b/changes/A2.scala b/sbt-dotty/sbt-test/source-dependencies/subproject-dependency-b/changes/A2.scala new file mode 100644 index 000000000000..93226f59e491 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/subproject-dependency-b/changes/A2.scala @@ -0,0 +1,5 @@ +class A { + class Inner { + def foo: Int = 187 + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/subproject-dependency-b/pending b/sbt-dotty/sbt-test/source-dependencies/subproject-dependency-b/pending new file mode 100644 index 000000000000..ee782c14ca26 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/subproject-dependency-b/pending @@ -0,0 +1,9 @@ +> compile + +$ copy-file changes/A1.scala provider/A.scala +> compile +> check-number-of-compiler-iterations 1 + +$ copy-file changes/A2.scala provider/A.scala +> compile +> check-number-of-compiler-iterations 1 diff --git a/sbt-dotty/sbt-test/source-dependencies/subproject-dependency-b/provider/A.scala b/sbt-dotty/sbt-test/source-dependencies/subproject-dependency-b/provider/A.scala new file mode 100644 index 000000000000..6e013e61e1a1 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/subproject-dependency-b/provider/A.scala @@ -0,0 +1,3 @@ +class A { + class Inner +} diff --git a/sbt-dotty/sbt-test/source-dependencies/subproject-dependency-b/use/B.scala b/sbt-dotty/sbt-test/source-dependencies/subproject-dependency-b/use/B.scala new file mode 100644 index 000000000000..d895226ce25e --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/subproject-dependency-b/use/B.scala @@ -0,0 +1,4 @@ +class B extends A { + // introduce dependency on Inner + def inner: Inner = null +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/trait-member-modified/build.sbt b/sbt-dotty/sbt-test/source-dependencies/trait-member-modified/build.sbt index 949d782317c0..1ebe8fad1a41 100644 --- a/sbt-dotty/sbt-test/source-dependencies/trait-member-modified/build.sbt +++ b/sbt-dotty/sbt-test/source-dependencies/trait-member-modified/build.sbt @@ -3,25 +3,22 @@ * b) checks overall number of compilations performed */ TaskKey[Unit]("check-compilations") := { - val analysis = (compile in Compile).value - val srcDir = (scalaSource in Compile).value - def relative(f: java.io.File): java.io.File = f.relativeTo(srcDir) getOrElse f + val analysis = (compile in Compile).value.asInstanceOf[sbt.internal.inc.Analysis] val allCompilations = analysis.compilations.allCompilations - val recompiledFiles: Seq[Set[java.io.File]] = allCompilations map { c => - val recompiledFiles = analysis.apis.internal.collect { - case (file, api) if api.compilation.startTime == c.startTime => relative(file) + val recompiledClasses: Seq[Set[String]] = allCompilations map { c => + val recompiledClasses = analysis.apis.internal.collect { + case (clazz, api) if api.compilationTimestamp() == c.getStartTime() => clazz } - recompiledFiles.toSet + recompiledClasses.toSet } - def recompiledFilesInIteration(iteration: Int, fileNames: Set[String]) = { - val files = fileNames.map(new java.io.File(_)) - assert(recompiledFiles(iteration) == files, "%s != %s".format(recompiledFiles(iteration), files)) + def recompiledFilesInIteration(iteration: Int, classNames: Set[String]): Unit = { + assert(recompiledClasses(iteration) == classNames, "%s != %s".format(recompiledClasses(iteration), classNames)) } assert(allCompilations.size == 2) // B.scala is just compiled at the beginning - recompiledFilesInIteration(0, Set("B.scala")) + recompiledFilesInIteration(0, Set("B")) // A.scala is changed and recompiled - recompiledFilesInIteration(1, Set("A.scala")) + recompiledFilesInIteration(1, Set("A")) } logLevel := Level.Debug diff --git a/sbt-dotty/sbt-test/source-dependencies/trait-member-modified/test b/sbt-dotty/sbt-test/source-dependencies/trait-member-modified/test index f8f7cb076b2b..183e1d40e805 100644 --- a/sbt-dotty/sbt-test/source-dependencies/trait-member-modified/test +++ b/sbt-dotty/sbt-test/source-dependencies/trait-member-modified/test @@ -6,4 +6,4 @@ $ copy-file changes/A1.scala src/main/scala/A.scala # only A.scala should be recompiled > compile # check if there are only two compile iterations performed -> check-compilations +> checkCompilations diff --git a/sbt-dotty/sbt-test/source-dependencies/trait-private-val/changes/Base.scala b/sbt-dotty/sbt-test/source-dependencies/trait-private-val/changes/Base.scala new file mode 100644 index 000000000000..b497cd62e871 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/trait-private-val/changes/Base.scala @@ -0,0 +1,8 @@ +package foo + +import scala.util.Random + +trait Base { + private val myRandom = Random.nextInt(100) + def somePublicMethod(): Int = 123 +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/trait-private-val/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/trait-private-val/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/trait-private-val/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/trait-private-val/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/trait-private-val/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/trait-private-val/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/trait-private-val/src/main/scala/foo/Base.scala b/sbt-dotty/sbt-test/source-dependencies/trait-private-val/src/main/scala/foo/Base.scala new file mode 100644 index 000000000000..7301d4d2bdb0 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/trait-private-val/src/main/scala/foo/Base.scala @@ -0,0 +1,5 @@ +package foo + +trait Base { + def somePublicMethod(): Int = 123 +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/trait-private-val/src/main/scala/foo/TestApp.scala b/sbt-dotty/sbt-test/source-dependencies/trait-private-val/src/main/scala/foo/TestApp.scala new file mode 100644 index 000000000000..d38e195be246 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/trait-private-val/src/main/scala/foo/TestApp.scala @@ -0,0 +1,5 @@ +package foo + +object TestApp extends App with Base{ + println("OK") +} \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/trait-private-val/test b/sbt-dotty/sbt-test/source-dependencies/trait-private-val/test new file mode 100644 index 000000000000..930e6cfd5f4b --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/trait-private-val/test @@ -0,0 +1,3 @@ +> run +$ copy-file changes/Base.scala src/main/scala/foo/Base.scala +> run \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/trait-private-var/test b/sbt-dotty/sbt-test/source-dependencies/trait-private-var/pending similarity index 100% rename from sbt-dotty/sbt-test/source-dependencies/trait-private-var/test rename to sbt-dotty/sbt-test/source-dependencies/trait-private-var/pending diff --git a/sbt-dotty/sbt-test/source-dependencies/transitive-class/A.scala b/sbt-dotty/sbt-test/source-dependencies/transitive-class/A.scala new file mode 100644 index 000000000000..68c5796a7c6b --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/transitive-class/A.scala @@ -0,0 +1 @@ +abstract class A diff --git a/sbt-dotty/sbt-test/source-dependencies/transitive-class/BC.scala b/sbt-dotty/sbt-test/source-dependencies/transitive-class/BC.scala new file mode 100644 index 000000000000..150a8f195a9f --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/transitive-class/BC.scala @@ -0,0 +1,2 @@ +class B extends A +class C extends B diff --git a/sbt-dotty/sbt-test/source-dependencies/transitive-class/D.scala b/sbt-dotty/sbt-test/source-dependencies/transitive-class/D.scala new file mode 100644 index 000000000000..08ebf2e9608c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/transitive-class/D.scala @@ -0,0 +1,4 @@ +class D extends C +object Hello extends App { + new D +} diff --git a/sbt-dotty/sbt-test/source-dependencies/transitive-class/changes/A.scala b/sbt-dotty/sbt-test/source-dependencies/transitive-class/changes/A.scala new file mode 100644 index 000000000000..a02e9e5db4e7 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/transitive-class/changes/A.scala @@ -0,0 +1,3 @@ +abstract class A { + def foo: String = "" +} diff --git a/sbt-dotty/sbt-test/source-dependencies/transitive-class/disabled b/sbt-dotty/sbt-test/source-dependencies/transitive-class/disabled new file mode 100644 index 000000000000..2e4cdfcc052e --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/transitive-class/disabled @@ -0,0 +1,5 @@ +> compile +$ copy-file changes/A.scala A.scala + +# TODO: implement checkRecompilations +> checkRecompilations 2 A B C D Hello diff --git a/sbt-dotty/sbt-test/source-dependencies/transitive-class/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/transitive-class/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/transitive-class/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/transitive-class/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/transitive-class/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/transitive-class/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/transitive-memberRef/build.sbt b/sbt-dotty/sbt-test/source-dependencies/transitive-memberRef/build.sbt index d24e304b1bc1..5180981ce13c 100644 --- a/sbt-dotty/sbt-test/source-dependencies/transitive-memberRef/build.sbt +++ b/sbt-dotty/sbt-test/source-dependencies/transitive-memberRef/build.sbt @@ -1,40 +1,36 @@ logLevel := Level.Debug -incOptions := incOptions.value.withNameHashing(true) - -// disable sbt's heauristic which recompiles everything in case +// disable sbt's heuristic which recompiles everything in case // some fraction (e.g. 50%) of files is scheduled to be recompiled // in this test we want precise information about recompiled files // which that heuristic would distort -incOptions := incOptions.value.copy(recompileAllFraction = 1.0) +incOptions := incOptions.value.withRecompileAllFraction(1.0) /* Performs checks related to compilations: * a) checks in which compilation given set of files was recompiled * b) checks overall number of compilations performed */ TaskKey[Unit]("check-compilations") := { - val analysis = (compile in Compile).value + val analysis = (compile in Compile).value.asInstanceOf[sbt.internal.inc.Analysis] val srcDir = (scalaSource in Compile).value - def relative(f: java.io.File): java.io.File = f.relativeTo(srcDir) getOrElse f val allCompilations = analysis.compilations.allCompilations - val recompiledFiles: Seq[Set[java.io.File]] = allCompilations map { c => - val recompiledFiles = analysis.apis.internal.collect { - case (file, api) if api.compilation.startTime == c.startTime => relative(file) + val recompiledClasses: Seq[Set[String]] = allCompilations map { c => + val recompiledClasses = analysis.apis.internal.collect { + case (clazz, api) if api.compilationTimestamp() == c.getStartTime() => clazz } - recompiledFiles.toSet + recompiledClasses.toSet } - def recompiledFilesInIteration(iteration: Int, fileNames: Set[String]) = { - val files = fileNames.map(new java.io.File(_)) - assert(recompiledFiles(iteration) == files, "%s != %s".format(recompiledFiles(iteration), files)) + def recompiledClassesInIteration(iteration: Int, classNames: Set[String]): Unit = { + assert(recompiledClasses(iteration) == classNames, "%s != %s".format(recompiledClasses(iteration), classNames)) } - // Y.scala is compiled only at the beginning as changes to A.scala do not affect it - recompiledFilesInIteration(0, Set("X.scala", "Y.scala")) - // A.scala is changed and recompiled - recompiledFilesInIteration(1, Set("A.scala")) - // change in A.scala causes recompilation of B.scala, C.scala, D.scala which depend on transtiviely - // and by inheritance on A.scala - // X.scala is also recompiled because it depends by member reference on B.scala - // Note that Y.scala is not recompiled because it depends just on X through member reference dependency - recompiledFilesInIteration(2, Set("B.scala", "C.scala", "D.scala")) + // test.Y is compiled only at the beginning as changes to test.A do not affect it + recompiledClassesInIteration(0, Set("test.X", "test.Y")) + // test.A is changed and recompiled + recompiledClassesInIteration(1, Set("test.A")) + // change in test.A causes recompilation of test.B, test.C, test.D which depend on transitively + // and by inheritance on test.A + // test.X is also recompiled because it depends by member reference on test.B + // Note that test.Y is not recompiled because it depends just on X through member reference dependency + recompiledClassesInIteration(2, Set("test.B", "test.C", "test.D")) assert(allCompilations.size == 3) } diff --git a/sbt-dotty/sbt-test/source-dependencies/transitive-memberRef/test b/sbt-dotty/sbt-test/source-dependencies/transitive-memberRef/test index 395f90229b5c..a39fe13a99ed 100644 --- a/sbt-dotty/sbt-test/source-dependencies/transitive-memberRef/test +++ b/sbt-dotty/sbt-test/source-dependencies/transitive-memberRef/test @@ -8,4 +8,4 @@ $ copy-file changes/A1.scala src/main/scala/A.scala # second iteration > compile # check in which compile iteration given source file got recompiled -> check-compilations +> checkCompilations diff --git a/sbt-dotty/sbt-test/source-dependencies/type-alias/build.sbt b/sbt-dotty/sbt-test/source-dependencies/type-alias/build.sbt index c5a1099aacad..00edfde1d056 100644 --- a/sbt-dotty/sbt-test/source-dependencies/type-alias/build.sbt +++ b/sbt-dotty/sbt-test/source-dependencies/type-alias/build.sbt @@ -1,3 +1 @@ logLevel in compile := Level.Debug - -incOptions := incOptions.value.withNameHashing(true) diff --git a/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/A.scala b/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/A.scala new file mode 100644 index 000000000000..7ff41856ca07 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/A.scala @@ -0,0 +1,6 @@ +abstract class A { + val t: AnyRef + object X { + def foo: t.type = t + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/B.scala b/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/B.scala new file mode 100644 index 000000000000..7ba16a6f2ceb --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/B.scala @@ -0,0 +1,3 @@ +class B extends A { + val t: String = "b" +} diff --git a/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/C.scala b/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/C.scala new file mode 100644 index 000000000000..00ebd17d5fdf --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/C.scala @@ -0,0 +1,3 @@ +object C extends B { + val proxy: String = t +} diff --git a/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/changes/B2.scala b/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/changes/B2.scala new file mode 100644 index 000000000000..d72933d9b60f --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/changes/B2.scala @@ -0,0 +1,3 @@ +class B extends A { + val t: Int = 1 +} diff --git a/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/test b/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/test new file mode 100644 index 000000000000..134321021e63 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/type-member-nested-object/test @@ -0,0 +1,4 @@ +> compile +$ copy-file changes/B2.scala B.scala +# Compilation of D.scala should fail because B is no longer a subtype of A +-> compile diff --git a/sbt-dotty/sbt-test/source-dependencies/typeref-only/build.sbt b/sbt-dotty/sbt-test/source-dependencies/typeref-only/build.sbt index 02813797f231..ea80ab8d990d 100644 --- a/sbt-dotty/sbt-test/source-dependencies/typeref-only/build.sbt +++ b/sbt-dotty/sbt-test/source-dependencies/typeref-only/build.sbt @@ -2,4 +2,4 @@ logLevel := Level.Debug // disable recompile all which causes full recompile which // makes it more difficult to test dependency tracking -incOptions ~= { _.copy(recompileAllFraction = 1.0) } +incOptions := incOptions.value.withRecompileAllFraction(1.0) diff --git a/sbt-dotty/sbt-test/source-dependencies/unexpanded-names/Foo.scala b/sbt-dotty/sbt-test/source-dependencies/unexpanded-names/Foo.scala new file mode 100644 index 000000000000..1b99e3e20e2c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/unexpanded-names/Foo.scala @@ -0,0 +1,19 @@ +class Fooo { + // This one is problematic because of expanded names + private[Fooo] object Bar +} + +package issue127 { + class Foo { + private[Foo] object Bar + class Baz { + private[Baz] object Bazz + } + } + + object Foo { + private[issue127] class Bippy + // This one is problematic because of expanded names + private[issue127] object Bippy + } +} diff --git a/sbt-dotty/sbt-test/source-dependencies/unexpanded-names/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/unexpanded-names/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/unexpanded-names/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/unexpanded-names/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/unexpanded-names/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/unexpanded-names/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/sbt-test/source-dependencies/unexpanded-names/test b/sbt-dotty/sbt-test/source-dependencies/unexpanded-names/test new file mode 100644 index 000000000000..e3d1046bf1bb --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/unexpanded-names/test @@ -0,0 +1,2 @@ +# See https://github.com/sbt/zinc/issues/127 +> compile \ No newline at end of file diff --git a/sbt-dotty/sbt-test/source-dependencies/value-class-underlying/C.scala b/sbt-dotty/sbt-test/source-dependencies/value-class-underlying/C.scala index 1a9a42bde96f..9a6a97533086 100644 --- a/sbt-dotty/sbt-test/source-dependencies/value-class-underlying/C.scala +++ b/sbt-dotty/sbt-test/source-dependencies/value-class-underlying/C.scala @@ -1,5 +1,6 @@ object C { def main(args: Array[String]): Unit = { - val x = B.foo + val duck = B.foo + println("duck: " + duck) // Need to use duck in an expression to see if it crashes or not } } diff --git a/sbt-dotty/sbt-test/source-dependencies/value-class-underlying/project/DottyInjectedPlugin.scala b/sbt-dotty/sbt-test/source-dependencies/value-class-underlying/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..ce3d46d79921 --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/value-class-underlying/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-language:Scala2" + ) +} diff --git a/sbt-dotty/sbt-test/source-dependencies/value-class-underlying/project/plugins.sbt b/sbt-dotty/sbt-test/source-dependencies/value-class-underlying/project/plugins.sbt new file mode 100644 index 000000000000..c17caab2d98c --- /dev/null +++ b/sbt-dotty/sbt-test/source-dependencies/value-class-underlying/project/plugins.sbt @@ -0,0 +1 @@ +addSbtPlugin("ch.epfl.lamp" % "sbt-dotty" % sys.props("plugin.version")) diff --git a/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala b/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala index 240e51479ce3..b78e0fa45f68 100644 --- a/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala +++ b/sbt-dotty/src/dotty/tools/sbtplugin/DottyIDEPlugin.scala @@ -55,7 +55,7 @@ object DottyIDEPlugin extends AutoPlugin { else { def matchingSetting(setting: Setting[_]) = setting.key.key == scalaVersion.key && - setting.key.scope.project.fold(ref => projRefs.contains(ref), ifGlobal = true, ifThis = true) + setting.key.scope.project.fold(ref => projRefs.contains(ref), ifZero = true, ifThis = true) val newSettings = extracted.session.mergeSettings.collect { case setting if matchingSetting(setting) => @@ -151,8 +151,8 @@ object DottyIDEPlugin extends AutoPlugin { * @param directory If not null, run `cmd` in this directory. */ def runProcess(cmd: Seq[String], wait: Boolean = false, directory: File = null): Unit = { - val pb0 = new ProcessBuilder(prepareCommand(cmd): _*).inheritIO() - val pb = if (directory != null) pb0.directory(directory) else pb0 + val pb = new ProcessBuilder(prepareCommand(cmd): _*).inheritIO() + if (directory != null) pb.directory(directory) if (wait) { val exitCode = pb.start().waitFor() if (exitCode != 0) { @@ -205,9 +205,9 @@ object DottyIDEPlugin extends AutoPlugin { origState } - private def projectConfigTask(config: Configuration): Initialize[Task[Option[ProjectConfig]]] = Def.task { - if ((sources in config).value.isEmpty) None - else { + private def projectConfigTask(config: Configuration): Initialize[Task[Option[ProjectConfig]]] = Def.taskDyn { + if ((sources in config).value.isEmpty) Def.task { None } + else Def.task { // Not needed to generate the config, but this guarantees that the // generated config is usable by an IDE without any extra compilation // step. diff --git a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala index 04b87f8004a4..c069d7e4c500 100644 --- a/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala +++ b/sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala @@ -2,7 +2,9 @@ package dotty.tools.sbtplugin import sbt._ import sbt.Keys._ -import sbt.inc.{ ClassfileManager, IncOptions } +// import sbt.inc.{ ClassfileManager, IncOptions } +import xsbti.compile._ +import java.util.Optional object DottyPlugin extends AutoPlugin { object autoImport { @@ -61,7 +63,7 @@ object DottyPlugin extends AutoPlugin { * }}} * you can replace it by: * {{{ - * libraryDependencies += ("a" %% "b" % "c").withDottyCompat() + * libraryDependencies += ("a" %% "b" % "c").withDottyCompat(scalaVersion.value) * }}} * This will have no effect when compiling with Scala 2.x, but when compiling * with Dotty this will change the cross-version to a Scala 2.x one. This @@ -70,19 +72,10 @@ object DottyPlugin extends AutoPlugin { * NOTE: Dotty's retro-compatibility with Scala 2.x will be dropped before * Dotty is released, you should not rely on it. */ - def withDottyCompat(): ModuleID = - moduleID.crossVersion match { - case _: CrossVersion.Binary => - moduleID.cross(CrossVersion.binaryMapped { version => - CrossVersion.partialVersion(version) match { - case Some((0, minor)) => - // Dotty v0.4 or greater is compatible with 2.12.x - if (minor >= 4) "2.12" - else "2.11" - case _ => - version - } - }) + def withDottyCompat(scalaVersion: String): ModuleID = + moduleID.crossVersion match { + case _: librarymanagement.Binary if scalaVersion.startsWith("0.") => + moduleID.cross(CrossVersion.constant("2.12")) case _ => moduleID } @@ -119,40 +112,39 @@ object DottyPlugin extends AutoPlugin { * corresponding .tasty or .hasTasty file is also deleted. */ def dottyPatchIncOptions(incOptions: IncOptions): IncOptions = { - val inheritedNewClassfileManager = incOptions.newClassfileManager - val newClassfileManager = () => new ClassfileManager { - private[this] val inherited = inheritedNewClassfileManager() + val inheritedNewClassFileManager = ClassFileManagerUtil.getDefaultClassFileManager(incOptions) + val tastyFileManager = new ClassFileManager { + private[this] val inherited = inheritedNewClassFileManager - def delete(classes: Iterable[File]): Unit = { + def delete(classes: Array[File]): Unit = { val tastySuffixes = List(".tasty", ".hasTasty") inherited.delete(classes flatMap { classFile => - val dottyFiles = if (classFile.getPath endsWith ".class") { + if (classFile.getPath endsWith ".class") { val prefix = classFile.getAbsolutePath.stripSuffix(".class") tastySuffixes.map(suffix => new File(prefix + suffix)).filter(_.exists) } else Nil - classFile :: dottyFiles }) } - def generated(classes: Iterable[File]): Unit = inherited.generated(classes) - def complete(success: Boolean): Unit = inherited.complete(success) + def generated(classes: Array[File]): Unit = {} + def complete(success: Boolean): Unit = {} } - incOptions.withNewClassfileManager(newClassfileManager) + val inheritedHooks = incOptions.externalHooks + val externalClassFileManager: Optional[ClassFileManager] = Option(inheritedHooks.getExternalClassFileManager.orElse(null)) match { + case Some(prevManager) => + Optional.of(WrappedClassFileManager.of(prevManager, Optional.of(tastyFileManager))) + case None => + Optional.of(tastyFileManager) + } + + val hooks = new DefaultExternalHooks(inheritedHooks.getExternalLookup, externalClassFileManager) + incOptions.withExternalHooks(hooks) } override def projectSettings: Seq[Setting[_]] = { Seq( - isDotty := { - val log = sLog.value + isDotty := scalaVersion.value.startsWith("0."), - sbtFullVersion(sbtVersion.value) match { - case Some((sbtMajor, sbtMinor, sbtPatch)) if sbtMajor == 0 && sbtMinor == 13 && sbtPatch < 15 => - log.error(s"The sbt-dotty plugin cannot work with this version of sbt (${sbtVersion.value}), sbt >= 0.13.15 is required.") - false - case _ => - scalaVersion.value.startsWith("0.") - } - }, scalaOrganization := { if (isDotty.value) "ch.epfl.lamp" @@ -161,17 +153,20 @@ object DottyPlugin extends AutoPlugin { }, incOptions in Compile := { + val inc = (incOptions in Compile).value if (isDotty.value) - dottyPatchIncOptions((incOptions in Compile).value) + dottyPatchIncOptions(inc) else - (incOptions in Compile).value + inc }, - scalaBinaryVersion := { + scalaCompilerBridgeSource := { + val scalaBridge = scalaCompilerBridgeSource.value + val dottyBridge = (scalaOrganization.value % "dotty-sbt-bridge" % scalaVersion.value).withConfigurations(Some(Configurations.Compile.name)).sources() if (isDotty.value) - scalaVersion.value.split("\\.").take(2).mkString(".") // Not needed with sbt >= 0.13.16 + dottyBridge else - scalaBinaryVersion.value + scalaBridge } ) } diff --git a/tests/neg-custom-args/fatal-warnings/i2673c.scala b/tests/neg-custom-args/fatal-warnings/i2673c.scala index 3e9cff320ea2..a677fab3081d 100644 --- a/tests/neg-custom-args/fatal-warnings/i2673c.scala +++ b/tests/neg-custom-args/fatal-warnings/i2673c.scala @@ -1,6 +1,6 @@ package Foos object Outer { - class X // error + case class X() // error object x } diff --git a/tests/pos-special/fatal-warnings/i2673.scala b/tests/pos-special/fatal-warnings/i2673.scala new file mode 100644 index 000000000000..9721f81da217 --- /dev/null +++ b/tests/pos-special/fatal-warnings/i2673.scala @@ -0,0 +1,6 @@ +package Foos + +object Outer { + class X + object x +} diff --git a/tests/pos-special/i3323.scala b/tests/pos-special/fatal-warnings/i3323.scala similarity index 100% rename from tests/pos-special/i3323.scala rename to tests/pos-special/fatal-warnings/i3323.scala diff --git a/tests/pos-special/i3323b.scala b/tests/pos-special/fatal-warnings/i3323b.scala similarity index 100% rename from tests/pos-special/i3323b.scala rename to tests/pos-special/fatal-warnings/i3323b.scala diff --git a/tests/pos-special/i3589-b.scala b/tests/pos-special/fatal-warnings/i3589b.scala similarity index 100% rename from tests/pos-special/i3589-b.scala rename to tests/pos-special/fatal-warnings/i3589b.scala diff --git a/tests/pos-special/i4166.scala b/tests/pos-special/fatal-warnings/i4166.scala similarity index 100% rename from tests/pos-special/i4166.scala rename to tests/pos-special/fatal-warnings/i4166.scala diff --git a/tests/pos-special/i4185.scala b/tests/pos-special/fatal-warnings/i4185.scala similarity index 100% rename from tests/pos-special/i4185.scala rename to tests/pos-special/fatal-warnings/i4185.scala diff --git a/tests/vulpix-tests/meta/sbt-output.check b/tests/vulpix-tests/meta/sbt-output.check index 65eb5cbf22f6..f8704de3ab6f 100644 --- a/tests/vulpix-tests/meta/sbt-output.check +++ b/tests/vulpix-tests/meta/sbt-output.check @@ -38,5 +38,5 @@ Testing tests/vulpix-tests/meta/pos/does-not-compile.scala [error] Failed: Total 3, Failed 3, Errors 0, Passed 0 [error] Failed tests: [error] dotty.tools.vulpix.VulpixMetaTests -[error] (dotty-compiler/test:testOnly) sbt.TestsFailedException: Tests unsuccessful +[error] (dotty-compiler / Test / testOnly) sbt.TestsFailedException: Tests unsuccessful [error] Total time: 3 s, completed Feb 5, 2018 5:10:12 PM SKIP