diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 736e1b08d1e7..cbb28da23e2d 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -46,6 +46,7 @@ class Compiler { List(new sjs.PrepJSInterop) :: // Additional checks and transformations for Scala.js (Scala.js only) List(new sbt.ExtractAPI) :: // Sends a representation of the API of classes to sbt via callbacks List(new SetRootTree) :: // Set the `rootTreeOrProvider` on class symbols + List(new CoverageTransformMacro) :: // Perform instrumentation for coverage transform (if -coverage is present) Nil /** Phases dealing with TASTY tree pickling and unpickling */ diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 6147cd78f3e6..3eb6cec04fdd 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -106,6 +106,9 @@ trait CommonScalaSettings: val explainTypes: Setting[Boolean] = BooleanSetting("-explain-types", "Explain type errors in more detail (deprecated, use -explain instead).", aliases = List("--explain-types", "-explaintypes")) val unchecked: Setting[Boolean] = BooleanSetting("-unchecked", "Enable additional warnings where generated code depends on assumptions.", initialValue = true, aliases = List("--unchecked")) val language: Setting[List[String]] = MultiStringSetting("-language", "feature", "Enable one or more language features.", aliases = List("--language")) + /* Coverage settings */ + val coverageOutputDir = PathSetting("-coverage", "Destination for coverage classfiles and instrumentation data.", "") + val coverageSourceroot = PathSetting("-coverage-sourceroot", "An alternative root dir of your sources used to relativize.", ".") /* Other settings */ val encoding: Setting[String] = StringSetting("-encoding", "encoding", "Specify character encoding used by source files.", Properties.sourceEncoding, aliases = List("--encoding")) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 60f375c116fc..3ab18d7f9958 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -14,6 +14,7 @@ import typer.ImportInfo.RootRef import Comments.CommentsContext import Comments.Comment import util.Spans.NoSpan +import Symbols.requiredModuleRef import scala.annotation.tailrec @@ -461,6 +462,8 @@ class Definitions { } def NullType: TypeRef = NullClass.typeRef + @tu lazy val InvokerModuleRef = requiredMethodRef("scala.runtime.Invoker") + @tu lazy val ImplicitScrutineeTypeSym = newPermanentSymbol(ScalaPackageClass, tpnme.IMPLICITkw, EmptyFlags, TypeBounds.empty).entered def ImplicitScrutineeTypeRef: TypeRef = ImplicitScrutineeTypeSym.typeRef diff --git a/compiler/src/dotty/tools/dotc/coverage/Coverage.scala b/compiler/src/dotty/tools/dotc/coverage/Coverage.scala new file mode 100644 index 000000000000..b4365e1c4389 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/coverage/Coverage.scala @@ -0,0 +1,30 @@ +package dotty.tools.dotc +package coverage + +import scala.collection.mutable + +class Coverage { + private val statementsById = mutable.Map[Int, Statement]() + + def statements = statementsById.values + + def addStatement(stmt: Statement): Unit = statementsById.put(stmt.id, stmt) +} + +case class Statement( + source: String, + location: Location, + id: Int, + start: Int, + end: Int, + line: Int, + desc: String, + symbolName: String, + treeName: String, + branch: Boolean, + var count: Int = 0, + ignored: Boolean = false +) { + def invoked(): Unit = count = count + 1 + def isInvoked = count > 0 +} diff --git a/compiler/src/dotty/tools/dotc/coverage/Location.scala b/compiler/src/dotty/tools/dotc/coverage/Location.scala new file mode 100644 index 000000000000..47f712a49da5 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/coverage/Location.scala @@ -0,0 +1,38 @@ +package dotty.tools.dotc +package coverage + +import ast.tpd._ +import dotty.tools.dotc.core.Contexts.Context + +/** @param packageName + * the name of the encosing package + * @param className + * the name of the closes enclosing class + * @param fullClassName + * the fully qualified name of the closest enclosing class + */ +final case class Location( + packageName: String, + className: String, + fullClassName: String, + classType: String, + method: String, + sourcePath: String +) + +object Location { + def apply(tree: Tree)(using ctx: Context): Location = { + + val packageName = ctx.owner.denot.enclosingPackageClass.name.toSimpleName.toString() + val className = ctx.owner.denot.enclosingClass.name.toSimpleName.toString() + + Location( + packageName, + className, + s"$packageName.$className", + "Class" /* TODO refine this further */, + ctx.owner.denot.enclosingMethod.name.toSimpleName.toString(), + ctx.source.file.absolute.toString() + ) + } +} diff --git a/compiler/src/dotty/tools/dotc/coverage/Serializer.scala b/compiler/src/dotty/tools/dotc/coverage/Serializer.scala new file mode 100644 index 000000000000..43fe45774249 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/coverage/Serializer.scala @@ -0,0 +1,83 @@ +package dotty.tools.dotc +package coverage + +import java.io._ + +import scala.io.Source + +object Serializer { + + val coverageFileName = "scoverage.coverage" + val coverageDataFormatVersion = "3.0" + // Write out coverage data to the given data directory, using the default coverage filename + def serialize(coverage: Coverage, dataDir: String, sourceRoot: String): Unit = + serialize(coverage, coverageFile(dataDir), new File(sourceRoot)) + + // Write out coverage data to given file. + def serialize(coverage: Coverage, file: File, sourceRoot: File): Unit = { + val writer = new BufferedWriter(new FileWriter(file)) + serialize(coverage, writer, sourceRoot) + writer.close() + } + + def serialize(coverage: Coverage, writer: Writer, sourceRoot: File): Unit = { + + def getRelativePath(filePath: String): String = { + val base = sourceRoot.getCanonicalFile().toPath() + val relPath = base.relativize(new File(filePath).getCanonicalFile().toPath()) + relPath.toString + } + + def writeHeader(writer: Writer): Unit = { + writer.write(s"""# Coverage data, format version: $coverageDataFormatVersion + |# Statement data: + |# - id + |# - source path + |# - package name + |# - class name + |# - class type (Class, Object or Trait) + |# - full class name + |# - method name + |# - start offset + |# - end offset + |# - line number + |# - symbol name + |# - tree name + |# - is branch + |# - invocations count + |# - is ignored + |# - description (can be multi-line) + |# '\f' sign + |# ------------------------------------------ + |""".stripMargin) + } + def writeStatement(stmt: Statement, writer: Writer): Unit = { + writer.write(s"""${stmt.id} + |${getRelativePath(stmt.location.sourcePath)} + |${stmt.location.packageName} + |${stmt.location.className} + |${stmt.location.classType} + |${stmt.location.fullClassName} + |${stmt.location.method} + |${stmt.start} + |${stmt.end} + |${stmt.line} + |${stmt.symbolName} + |${stmt.treeName} + |${stmt.branch} + |${stmt.count} + |${stmt.ignored} + |${stmt.desc} + |\f + |""".stripMargin) + } + + writeHeader(writer) + coverage.statements.toVector + .sortBy(_.id) + .foreach(stmt => writeStatement(stmt, writer)) + } + + def coverageFile(dataDir: File): File = coverageFile(dataDir.getAbsolutePath) + def coverageFile(dataDir: String): File = new File(dataDir, coverageFileName) +} diff --git a/compiler/src/dotty/tools/dotc/transform/CoverageTransformMacro.scala b/compiler/src/dotty/tools/dotc/transform/CoverageTransformMacro.scala new file mode 100644 index 000000000000..2d827dd25cac --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/CoverageTransformMacro.scala @@ -0,0 +1,204 @@ +package dotty.tools.dotc +package transform + +import java.io.File +import java.util.concurrent.atomic.AtomicInteger + +import collection.mutable +import core.Flags.JavaDefined +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.DenotTransformers.IdentityDenotTransformer +import dotty.tools.dotc.coverage.Coverage +import dotty.tools.dotc.coverage.Statement +import dotty.tools.dotc.coverage.Serializer +import dotty.tools.dotc.coverage.Location +import dotty.tools.dotc.core.Symbols.defn +import dotty.tools.dotc.core.Symbols.Symbol +import dotty.tools.dotc.core.Decorators.toTermName +import dotty.tools.dotc.util.SourcePosition +import dotty.tools.dotc.core.Constants.Constant +import dotty.tools.dotc.typer.LiftCoverage + +import scala.quoted + +/** Phase that implements code coverage, executed when the "-coverage + * OUTPUT_PATH" is added to the compilation. + */ +class CoverageTransformMacro extends MacroTransform with IdentityDenotTransformer { + import ast.tpd._ + + override def phaseName = "coverage" + + // Atomic counter used for assignation of IDs to difference statements + val statementId = new AtomicInteger(0) + + var outputPath = "" + + // Main class used to store all instrumented statements + val coverage = new Coverage + + override def run(using ctx: Context): Unit = { + + if (ctx.settings.coverageOutputDir.value.nonEmpty) { + outputPath = ctx.settings.coverageOutputDir.value + + // Ensure the dir exists + val dataDir = new File(outputPath) + val newlyCreated = dataDir.mkdirs() + + if (!newlyCreated) { + // If the directory existed before, let's clean it up. + dataDir.listFiles + .filter(_.getName.startsWith("scoverage")) + .foreach(_.delete) + } + + super.run + + + Serializer.serialize(coverage, outputPath, ctx.settings.coverageSourceroot.value) + } + } + + protected def newTransformer(using Context): Transformer = + new CoverageTransormer + + class CoverageTransormer extends Transformer { + var instrumented = false + + override def transform(tree: Tree)(using Context): Tree = { + tree match { + case tree: If => + cpy.If(tree)( + cond = transform(tree.cond), + thenp = instrument(transform(tree.thenp), branch = true), + elsep = instrument(transform(tree.elsep), branch = true) + ) + case tree: Try => + cpy.Try(tree)( + expr = instrument(transform(tree.expr), branch = true), + cases = instrumentCasees(tree.cases), + finalizer = instrument(transform(tree.finalizer), true) + ) + case Apply(fun, _) + if ( + fun.symbol.exists && + fun.symbol.isInstanceOf[Symbol] && + fun.symbol == defn.Boolean_&& || fun.symbol == defn.Boolean_|| + ) => + super.transform(tree) + case tree @ Apply(fun, args) if (fun.isInstanceOf[Apply]) => + // We have nested apply, we have to lift all arguments + // Example: def T(x:Int)(y:Int) + // T(f())(1) // should not be changed to {val $x = f(); T($x)}(1) but to {val $x = f(); val $y = 1; T($x)($y)} + liftApply(tree) + case tree: Apply => + if (LiftCoverage.needsLift(tree)) { + liftApply(tree) + } else { + super.transform(tree) + } + case Select(qual, _) if (qual.symbol.exists && qual.symbol.is(JavaDefined)) => + //Java class can't be used as a value, we can't instrument the + //qualifier ({;System}.xyz() is not possible !) instrument it + //as it is + instrument(tree) + case tree: Select => + if (tree.qualifier.isInstanceOf[New]) { + instrument(tree) + } else { + cpy.Select(tree)(transform(tree.qualifier), tree.name) + } + case tree: CaseDef => instrumentCaseDef(tree) + + case tree: Literal => instrument(tree) + case tree: Ident if (isWildcardArg(tree)) => + // We don't want to instrument wildcard arguments. `var a = _` can't be instrumented + tree + case tree: New => instrument(tree) + case tree: This => instrument(tree) + case tree: Super => instrument(tree) + case tree: PackageDef => + // We don't instrument the pid of the package, but we do instrument the statements + cpy.PackageDef(tree)(tree.pid, transform(tree.stats)) + case tree: Assign => cpy.Assign(tree)(tree.lhs, transform(tree.rhs)) + case tree: Template => + // Don't instrument the parents (extends) of a template since it + // causes problems if the parent constructor takes parameters + cpy.Template(tree)( + constr = super.transformSub(tree.constr), + body = transform(tree.body) + ) + case tree: Import => tree + // Catch EmptyTree since we can't match directly on it + case tree: Thicket if tree.isEmpty => tree + // For everything else just recurse and transform + case _ => + report.warning( + "Unmatched: " + tree.getClass + " " + tree.symbol, + tree.sourcePos + ) + super.transform(tree) + } + } + + def liftApply(tree: Apply)(using Context) = { + val buffer = mutable.ListBuffer[Tree]() + // NOTE: that if only one arg needs to be lifted, we just lift everything + val lifted = LiftCoverage.liftForCoverage(buffer, tree) + val instrumented = buffer.toList.map(transform) + //We can now instrument the apply as it is with a custom position to point to the function + Block( + instrumented, + instrument( + lifted, + tree.sourcePos, + false + ) + ) + } + + def instrumentCasees(cases: List[CaseDef])(using Context): List[CaseDef] = { + cases.map(instrumentCaseDef) + } + + def instrumentCaseDef(tree: CaseDef)(using Context): CaseDef = { + cpy.CaseDef(tree)(tree.pat, transform(tree.guard), transform(tree.body)) + } + + def instrument(tree: Tree, branch: Boolean = false)(using Context): Tree = { + instrument(tree, tree.sourcePos, branch) + } + + def instrument(tree: Tree, pos: SourcePosition, branch: Boolean)(using ctx: Context): Tree = { + if (pos.exists && !pos.span.isZeroExtent && !tree.isType) { + val id = statementId.incrementAndGet() + val statement = new Statement( + source = ctx.source.file.name, + location = Location(tree), + id = id, + start = pos.start, + end = pos.end, + line = ctx.source.offsetToLine(pos.point), + desc = tree.source.content.slice(pos.start, pos.end).mkString, + symbolName = tree.symbol.name.toSimpleName.toString(), + treeName = tree.getClass.getSimpleName, + branch + ) + coverage.addStatement(statement) + Block(List(invokeCall(id)), tree) + } else { + tree + } + } + + def invokeCall(id: Int)(using Context): Tree = { + ref(defn.InvokerModuleRef) + .select("invoked".toTermName) + .appliedToArgs( + List(Literal(Constant(id)), Literal(Constant(outputPath))) + ) + } + } + +} diff --git a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala index c04b5f1d2d85..b75028e4bc14 100644 --- a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -157,6 +157,30 @@ class LiftComplex extends Lifter { } object LiftComplex extends LiftComplex +/** Lift complex + lift the prefixes */ +object LiftCoverage extends LiftComplex { + + var liftEverything = false + + /** Return true if the apply needs a lift in the coverage phase + Return false if the args are empty, if one or more will be lifter by a + complex lifter. + */ + def needsLift(tree: tpd.Apply)(using Context): Boolean = + !tree.args.isEmpty && !tree.args.forall(super.noLift(_)) + + override def noLift(expr: tpd.Tree)(using Context) = + !liftEverything && super.noLift(expr) + + def liftForCoverage(defs: mutable.ListBuffer[tpd.Tree], tree: tpd.Apply)(using Context) = { + val liftedFun = liftApp(defs, tree.fun) + liftEverything = true + val liftedArgs = liftArgs(defs, tree.fun.tpe, tree.args) + liftEverything = false + tpd.cpy.Apply(tree)(liftedFun, liftedArgs) + } +} + object LiftErased extends LiftComplex: override def isErased = true diff --git a/compiler/test/dotty/tools/dotc/coverage/CoverageTests.scala b/compiler/test/dotty/tools/dotc/coverage/CoverageTests.scala new file mode 100644 index 000000000000..9dde3b9e8221 --- /dev/null +++ b/compiler/test/dotty/tools/dotc/coverage/CoverageTests.scala @@ -0,0 +1,75 @@ +package dotty.tools.dotc.coverage + +import java.util.stream.Collectors +import org.junit.Assert._ +import dotty.BootstrappedOnlyTests +import org.junit.experimental.categories.Category +import org.junit.Test +import java.nio.file.Files +import dotty.tools.dotc.Main +import scala.jdk.CollectionConverters._ + +import java.io.File +import java.nio.file.Path +import java.nio.file.FileSystems +import java.nio.file.Paths +import java.nio.charset.StandardCharsets +import scala.io.Source +import java.io.BufferedOutputStream +import java.io.FileOutputStream + +@main def updateExpect = + CoverageTests().runExpectTest(updateExpectFiles = true) + +@Category(Array(classOf[BootstrappedOnlyTests])) +class CoverageTests { + + val scalaFile = FileSystems.getDefault.getPathMatcher("glob:**.scala") + val rootSrc = Paths.get(System.getProperty("dotty.tools.dotc.coverage.test")) + val expectSrc = rootSrc.resolve("expect") + + @Category(Array(classOf[dotty.SlowTests])) + @Test def expectTests: Unit = + if (!scala.util.Properties.isWin) runExpectTest(updateExpectFiles = false) + + def runExpectTest(updateExpectFiles: Boolean): Unit = { + val target = generateCoverage(updateExpectFiles) + val input = Source.fromFile(new File(target.toString, "scoverage.coverage")) + val expectFile = new File(expectSrc.resolveSibling("scoverage.coverage.expect").toUri) + + if (updateExpectFiles) { + val outputStream = new BufferedOutputStream(new FileOutputStream(expectFile)) + try { + input.foreach(outputStream.write(_)) + } finally outputStream.close + } else { + val expected = new String(Files.readAllBytes(expectFile.toPath), StandardCharsets.UTF_8) + val obtained = input.mkString + + assertEquals(expected, obtained) + } + } + + def inputFiles(): List[Path] = { + val ls = Files.walk(expectSrc) + val files = + try ls.filter(p => scalaFile.matches(p)).collect(Collectors.toList).asScala + finally ls.close() + + files.toList + } + + def generateCoverage(updateExpectFiles: Boolean): Path = { + val target = Files.createTempDirectory("coverage") + val args = Array( + "-coverage", + target.toString, + "-coverage-sourceroot", + if (updateExpectFiles) "../" else ".", + "-usejavacp" + ) ++ inputFiles().map(_.toString) + val exit = Main.process(args) + assertFalse(s"dotc errors: ${exit.errorCount}", exit.hasErrors) + target + } +} diff --git a/library/src/scala/runtime/Invoker.scala b/library/src/scala/runtime/Invoker.scala new file mode 100644 index 000000000000..6a22427cbd18 --- /dev/null +++ b/library/src/scala/runtime/Invoker.scala @@ -0,0 +1,74 @@ +package scala.runtime + +import scala.collection.mutable +import java.nio.file.Path +import scala.collection.concurrent.TrieMap +import java.nio.file.Paths +import java.nio.file.Files +import java.nio.file.StandardOpenOption +import java.io.FileWriter +import java.io.File + +object Invoker { + private val runtimeUUID = java.util.UUID.randomUUID() + + private val MeasurementsPrefix = "scoverage.measurements." + private val threadFiles = new ThreadLocal[mutable.HashMap[String, FileWriter]] + + // For each data directory we maintain a thread-safe set tracking the ids + // that we've already seen and recorded. We're using a map as a set, so we + // only care about its keys and can ignore its values. + private val dataDirToIds = TrieMap.empty[String, TrieMap[Int, Any]] + + /** We record that the given id has been invoked by appending its id to the coverage data file. + * + * This will happen concurrently on as many threads as the application is using, so we use one + * file per thread, named for the thread id. + * + * This method is not thread-safe if the threads are in different JVMs, because the thread IDs + * may collide. You may not use `scoverage` on multiple processes in parallel without risking + * corruption of the measurement file. + * + * @param id + * the id of the statement that was invoked + * @param dataDir + * the directory where the measurement data is held + */ + def invoked(id: Int, dataDir: String): Unit = { + // [sam] we can do this simple check to save writing out to a file. + // This won't work across JVMs but since there's no harm in writing out the same id multiple + // times since for coverage we only care about 1 or more, (it just slows things down to + // do it more than once), anything we can do to help is good. This helps especially with code + // that is executed many times quickly, eg tight loops. + if (!dataDirToIds.contains(dataDir)) { + // Guard against SI-7943: "TrieMap method getOrElseUpdate is not thread-safe". + dataDirToIds.synchronized { + if (!dataDirToIds.contains(dataDir)) { + dataDirToIds(dataDir) = TrieMap.empty[Int, Any] + } + } + } + val ids = dataDirToIds(dataDir) + if (!ids.contains(id)) { + // Each thread writes to a separate measurement file, to reduce contention + // and because file appends via FileWriter are not atomic on Windows. + var files = threadFiles.get() + if (files == null) { + files = mutable.HashMap.empty[String, FileWriter] + threadFiles.set(files) + } + val writer = files.getOrElseUpdate( + dataDir, + new FileWriter(measurementFile(dataDir), true) + ) + + writer.append(Integer.toString(id)).append("\n").flush() + ids.put(id, ()) + } + } + + def measurementFile(dataDir: String): File = new File( + dataDir, + MeasurementsPrefix + runtimeUUID + "." + Thread.currentThread.getId + ) +} diff --git a/project/Build.scala b/project/Build.scala index ff99753add6e..a49875a999d6 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -561,8 +561,10 @@ object Build { ) }, - javaOptions += ( - s"-Ddotty.tools.dotc.semanticdb.test=${(ThisBuild / baseDirectory).value/"tests"/"semanticdb"}" + javaOptions ++= Seq( + s"-Ddotty.tools.dotc.semanticdb.test=${(ThisBuild / baseDirectory).value/"tests"/"semanticdb"}", + s"-Ddotty.tools.dotc.coverage.test=${(ThisBuild / baseDirectory).value/"tests"/"coverage"}", + ), testCompilation := Def.inputTaskDyn { diff --git a/tests/coverage/expect/ContextFunctions.scala b/tests/coverage/expect/ContextFunctions.scala new file mode 100644 index 000000000000..e1338b1a1f75 --- /dev/null +++ b/tests/coverage/expect/ContextFunctions.scala @@ -0,0 +1,30 @@ +package example + +/** + * Intersection Types: https://dotty.epfl.ch/docs/reference/new-types/intersection-types.html + * Taken from https://github.com/scala/scala3-example-project + */ +object IntersectionTypes: + + sealed trait X: + def x: Double + def tpe: X + + sealed trait Y: + def y: Double + def tpe: Y + + type P = Y & X + type PP = X & Y + + final case class Point(x: Double, y: Double) extends X with Y: + override def tpe: X & Y = ??? + + def test(): Unit = + def euclideanDistance(p1: X & Y, p2: X & Y) = + Math.sqrt(Math.pow(p2.y - p1.y, 2) + Math.pow(p2.x - p1.x, 2)) + + val p1: P = Point(3, 4) + val p2: PP = Point(6, 8) + println(euclideanDistance(p1, p2)) + diff --git a/tests/coverage/expect/Enum.scala b/tests/coverage/expect/Enum.scala new file mode 100644 index 000000000000..6f99daae5240 --- /dev/null +++ b/tests/coverage/expect/Enum.scala @@ -0,0 +1,42 @@ +package example + +/** + * Enum Types: https://dotty.epfl.ch/docs/reference/enums/adts.html + * Taken from https://github.com/scala/scala3-example-project + */ +object EnumTypes: + + enum ListEnum[+A]: + case Cons(h: A, t: ListEnum[A]) + case Empty + + + enum Planet(mass: Double, radius: Double): + private final val G = 6.67300E-11 + def surfaceGravity = G * mass / (radius * radius) + def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity + + case Mercury extends Planet(3.303e+23, 2.4397e6) + case Venus extends Planet(4.869e+24, 6.0518e6) + case Earth extends Planet(5.976e+24, 6.37814e6) + case Mars extends Planet(6.421e+23, 3.3972e6) + case Jupiter extends Planet(1.9e+27, 7.1492e7) + case Saturn extends Planet(5.688e+26, 6.0268e7) + case Uranus extends Planet(8.686e+25, 2.5559e7) + case Neptune extends Planet(1.024e+26, 2.4746e7) + end Planet + + def test(): Unit = + val emptyList = ListEnum.Empty + val list = ListEnum.Cons(1, ListEnum.Cons(2, ListEnum.Cons(3, ListEnum.Empty))) + println("Example 1: \n"+emptyList) + println(s"${list}\n") + + def calculateEarthWeightOnPlanets(earthWeight: Double) = + val mass = earthWeight/Planet.Earth.surfaceGravity + for p <- Planet.values do + println(s"Your weight on $p is ${p.surfaceWeight(mass)}") + + println("Example 2:") + calculateEarthWeightOnPlanets(80) + end test diff --git a/tests/coverage/expect/MultiversalEquality.scala b/tests/coverage/expect/MultiversalEquality.scala new file mode 100644 index 000000000000..ace2fd642c2c --- /dev/null +++ b/tests/coverage/expect/MultiversalEquality.scala @@ -0,0 +1,31 @@ +package example + +import scala.language.strictEquality + +/** + * Multiversal Equality: https://dotty.epfl.ch/docs/reference/contextual/multiversal-equality.html + * scala.CanEqual definition: https://github.com/lampepfl/dotty/blob/master/library/src/scala/CanEqual.scala + * Taken from https://github.com/scala/scala3-example-project + */ +object MultiversalEquality: + + def test(): Unit = + given CanEqual[Int, String] = CanEqual.derived + println(3 == "3") + + println(3 == 5.1) + + println(List(1, 2) == Vector(1, 2)) + + class A(a: Int) + class B(b: Int) + + val a = A(4) + val b = B(4) + + given CanEqual[A, B] = CanEqual.derived + given CanEqual[B, A] = CanEqual.derived + + println(a != b) + println(b == a) + diff --git a/tests/coverage/expect/ParameterUntupling.scala b/tests/coverage/expect/ParameterUntupling.scala new file mode 100644 index 000000000000..533c4c3f3362 --- /dev/null +++ b/tests/coverage/expect/ParameterUntupling.scala @@ -0,0 +1,19 @@ +package example + +/** + * Parameter Untupling: https://dotty.epfl.ch/docs/reference/other-new-features/parameter-untupling.html + * Taken from https://github.com/scala/scala3-example-project + */ +object ParameterUntupling: + + def test(): Unit = + val xs: List[String] = List("d", "o", "t", "t", "y") + + /** + * Current behaviour in Scala 2.12.2 : + * error: missing parameter type + * Note: The expected type requires a one-argument function accepting a 2-Tuple. + * Consider a pattern matching anonymous function, `{ case (s, i) => ... }` + */ + xs.zipWithIndex.map((s, i) => println(s"$i: $s")) + diff --git a/tests/coverage/expect/PatternMatching.scala b/tests/coverage/expect/PatternMatching.scala new file mode 100644 index 000000000000..e2f0403ef87e --- /dev/null +++ b/tests/coverage/expect/PatternMatching.scala @@ -0,0 +1,87 @@ + +package example + +/** + * Pattern Matching: https://dotty.epfl.ch/docs/reference/changed-features/pattern-matching.html + * Taken from https://github.com/scala/scala3-example-project + */ +object PatternMatching: + + object booleanPattern: + + object Even: + def unapply(s: String): Boolean = s.length % 2 == 0 + + + object productPattern: + + class Person(name: String, age: Int) extends Product: + // if we not define that, it will give compile error. + // we change the order + def _1 = age + def _2 = name + + // Not used by pattern matching: Product is only used as a marker trait. + def canEqual(that: Any): Boolean = ??? + def productArity: Int = ??? + def productElement(n: Int): Any = ??? + + object Person: + def unapply(a: (String, Int)): Person = Person(a._1, a._2) + + + object seqPattern: + + // adapted from https://danielwestheide.com/blog/the-neophytes-guide-to-scala-part-2-extracting-sequences/ + object Names: + def unapplySeq(name: String): Option[Seq[String]] = + val names = name.trim.split(" ") + if names.size < 2 then None + else Some(names.last :: names.head :: names.drop(1).dropRight(1).toList) + + + object namePattern: + + class Name(val name: String): + def get: String = name + def isEmpty = name.isEmpty + + object Name: + def unapply(s: String): Name = Name(s) + + + def test(): Unit = + import booleanPattern.* + + "even" match + case s @ Even() => println(s"$s has an even number of characters") + case s => println(s"$s has an odd number of characters") + + // https://dotty.epfl.ch/docs/reference/changed-features/vararg-splices.html + def containsConsecutive(list: List[Int]): Boolean = list match + case List(a, b, xs*) => a == b || containsConsecutive(b :: xs.toList) + case Nil | List(_, _*) => false + + println(containsConsecutive(List(1, 2, 3, 4, 5))) + println(containsConsecutive(List(1, 2, 3, 3, 5))) + + import productPattern.* + ("john", 42) match + case Person(n, a) => println(s"name: $n, age: $a") + + import seqPattern.* + + def greet(fullName: String) = fullName match + case Names(lastName, firstName, _*) => "Good morning, " + firstName + " " + lastName + "!" + case _ => "Welcome! Please make sure to fill in your name!" + + println(greet("Alan Turing")) + println(greet("john")) + println(greet("Wolfgang Amadeus Mozart")) + + import namePattern.* + "alice" match + case Name(n) => println(s"name is $n") + case _ => println("empty name") + +end PatternMatching diff --git a/tests/coverage/expect/StructuralTypes.scala b/tests/coverage/expect/StructuralTypes.scala new file mode 100644 index 000000000000..4b2ee1991ce6 --- /dev/null +++ b/tests/coverage/expect/StructuralTypes.scala @@ -0,0 +1,29 @@ +package example + +/** + * Structural Types: https://dotty.epfl.ch/docs/reference/changed-features/structural-types.html + * Taken from https://github.com/scala/scala3-example-project + */ +object StructuralTypes: + + case class Record(elems: (String, Any)*) extends Selectable: + def selectDynamic(name: String): Any = elems.find(_._1 == name).get._2 + + type Person = Record { + val name: String + val age: Int + } + + val person = Record("name" -> "Emma", "age" -> 42, "salary" -> 320L).asInstanceOf[Person] + + val invalidPerson = Record("name" -> "John", "salary" -> 42).asInstanceOf[Person] + + def test(): Unit = + println(person.name) + println(person.age) + + println(invalidPerson.name) + // age field is java.util.NoSuchElementException: None.get + //println(invalidPerson.age) + +end StructuralTypes diff --git a/tests/coverage/expect/TraitParams.scala b/tests/coverage/expect/TraitParams.scala new file mode 100644 index 000000000000..fa98a9b23805 --- /dev/null +++ b/tests/coverage/expect/TraitParams.scala @@ -0,0 +1,22 @@ +package example + +/** + * Trait Parameters: https://dotty.epfl.ch/docs/reference/other-new-features/trait-parameters.html + * Taken from https://github.com/scala/scala3-example-project + */ +object TraitParams: + + trait Base(val msg: String) + class A extends Base("Hello") + class B extends Base("Dotty!") + + // Union types only exist in Scala 3, so there's no chance that this will accidentally be compiled with Scala 2 + private def printMessages(msgs: (A | B)*) = println(msgs.map(_.msg).mkString(" ")) + + def test(): Unit = + printMessages(new A, new B) + + // Sanity check the classpath: this won't run if the Scala 3 jar is not present. + val x: Int => Int = identity + x(1) + diff --git a/tests/coverage/expect/TypeLambdas.scala b/tests/coverage/expect/TypeLambdas.scala new file mode 100644 index 000000000000..9769869b7805 --- /dev/null +++ b/tests/coverage/expect/TypeLambdas.scala @@ -0,0 +1,19 @@ +package example + +/** + * Type Lambdas: https://dotty.epfl.ch/docs/reference/new-types/type-lambdas.html + * Taken from https://github.com/scala/scala3-example-project + */ +object TypeLambdas: + + type M = [X, Y] =>> Map[Y, X] + + type Tuple = [X] =>> (X, X) + + def test(): Unit = + val m: M[String, Int] = Map(1 -> "1") + println(m) + + val tuple: Tuple[String] = ("a", "b") + println(tuple) + diff --git a/tests/coverage/expect/UnionTypes.scala b/tests/coverage/expect/UnionTypes.scala new file mode 100644 index 000000000000..a79d8dd8fca4 --- /dev/null +++ b/tests/coverage/expect/UnionTypes.scala @@ -0,0 +1,44 @@ + +package example + +/** + * Union Types: https://dotty.epfl.ch/docs/reference/new-types/union-types.html + * Taken from https://github.com/scala/scala3-example-project + */ +object UnionTypes: + + sealed trait Division + final case class DivisionByZero(msg: String) extends Division + final case class Success(double: Double) extends Division + + // You can create type aliases for your union types (sum types). + type DivisionResult = DivisionByZero | Success + + sealed trait List[+A] + case object Empty extends List[Nothing] + final case class Cons[+A](h: A, t: List[A]) extends List[A] + + private def safeDivide(a: Double, b: Double): DivisionResult = + if b == 0 then DivisionByZero("DivisionByZeroException") else Success(a / b) + + private def either(division: Division) = division match + case DivisionByZero(m) => Left(m) + case Success(d) => Right(d) + + def test(): Unit = + val divisionResultSuccess: DivisionResult = safeDivide(4, 2) + + // commutative + val divisionResultFailure: Success | DivisionByZero = safeDivide(4, 0) + + // calling `either` function with union typed value. + println(either(divisionResultSuccess)) + + // calling `either` function with union typed value. + println(either(divisionResultFailure)) + + val list: Cons[Int] | Empty.type = Cons(1, Cons(2, Cons(3, Empty))) + val emptyList: Empty.type | Cons[Any] = Empty + println(list) + println(emptyList) + diff --git a/tests/coverage/scoverage.coverage.expect b/tests/coverage/scoverage.coverage.expect new file mode 100644 index 000000000000..93a8107f4c15 --- /dev/null +++ b/tests/coverage/scoverage.coverage.expect @@ -0,0 +1,3030 @@ +# Coverage data, format version: 3.0 +# Statement data: +# - id +# - source path +# - package name +# - class name +# - class type (Class, Object or Trait) +# - full class name +# - method name +# - start offset +# - end offset +# - line number +# - symbol name +# - tree name +# - is branch +# - invocations count +# - is ignored +# - description (can be multi-line) +# ' ' sign +# ------------------------------------------ +1 +tests/coverage/expect/ParameterUntupling.scala +example +ParameterUntupling$ +Class +example.ParameterUntupling$ +test +276 +279 +9 + +Literal +false +0 +false +"d" + +2 +tests/coverage/expect/ParameterUntupling.scala +example +ParameterUntupling$ +Class +example.ParameterUntupling$ +test +281 +284 +9 + +Literal +false +0 +false +"o" + +3 +tests/coverage/expect/ParameterUntupling.scala +example +ParameterUntupling$ +Class +example.ParameterUntupling$ +test +286 +289 +9 + +Literal +false +0 +false +"t" + +4 +tests/coverage/expect/ParameterUntupling.scala +example +ParameterUntupling$ +Class +example.ParameterUntupling$ +test +291 +294 +9 + +Literal +false +0 +false +"t" + +5 +tests/coverage/expect/ParameterUntupling.scala +example +ParameterUntupling$ +Class +example.ParameterUntupling$ +test +296 +299 +9 + +Literal +false +0 +false +"y" + +6 +tests/coverage/expect/ParameterUntupling.scala +example +ParameterUntupling$ +Class +example.ParameterUntupling$ +test +271 +300 +9 +apply +Apply +false +0 +false +List("d", "o", "t", "t", "y") + +7 +tests/coverage/expect/ParameterUntupling.scala +example +ParameterUntupling$ +Class +example.ParameterUntupling$ +$anonfun +615 +617 +17 + +Literal +false +0 +false +: + +8 +tests/coverage/expect/ParameterUntupling.scala +example +ParameterUntupling$ +Class +example.ParameterUntupling$ +$anonfun +613 +619 +17 +apply +Apply +false +0 +false +$i: $s + +9 +tests/coverage/expect/ParameterUntupling.scala +example +ParameterUntupling$ +Class +example.ParameterUntupling$ +$anonfun +611 +620 +17 +s +Apply +false +0 +false +s"$i: $s" + +10 +tests/coverage/expect/ParameterUntupling.scala +example +ParameterUntupling$ +Class +example.ParameterUntupling$ +$anonfun +603 +621 +17 +println +Apply +false +0 +false +println(s"$i: $s") + +11 +tests/coverage/expect/ParameterUntupling.scala +example +ParameterUntupling$ +Class +example.ParameterUntupling$ +test +573 +622 +17 +map +Apply +false +0 +false +xs.zipWithIndex.map((s, i) => println(s"$i: $s")) + +12 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +450 +451 +13 + +Literal +false +0 +false +3 + +13 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +455 +458 +13 + +Literal +false +0 +false +"3" + +14 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +442 +459 +13 +println +Apply +false +0 +false +println(3 == "3") + +15 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +473 +474 +15 + +Literal +false +0 +false +3 + +16 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +478 +481 +15 + +Literal +false +0 +false +5.1 + +17 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +501 +502 +17 + +Literal +false +0 +false +1 + +18 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +504 +505 +17 + +Literal +false +0 +false +2 + +19 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +496 +506 +17 +apply +Apply +false +0 +false +List(1, 2) + +20 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +517 +518 +17 + +Literal +false +0 +false +1 + +21 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +520 +521 +17 + +Literal +false +0 +false +2 + +22 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +510 +522 +17 +apply +Apply +false +0 +false +Vector(1, 2) + +23 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +496 +522 +17 +== +Apply +false +0 +false +List(1, 2) == Vector(1, 2) + +24 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +488 +523 +17 +println +Apply +false +0 +false +println(List(1, 2) == Vector(1, 2)) + +25 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +578 +579 +22 + +Select +false +0 +false +A + +26 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +580 +581 +22 + +Literal +false +0 +false +4 + +27 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +595 +596 +23 + +Select +false +0 +false +B + +28 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +597 +598 +23 + +Literal +false +0 +false +4 + +29 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +694 +709 +28 +println +Apply +false +0 +false +println(a != b) + +30 +tests/coverage/expect/MultiversalEquality.scala +example +MultiversalEquality$ +Class +example.MultiversalEquality$ +test +714 +729 +29 +println +Apply +false +0 +false +println(b == a) + +31 +tests/coverage/expect/ContextFunctions.scala +example +IntersectionTypes$ +Class +example.IntersectionTypes$ +euclideanDistance +547 +558 +24 +- +Apply +false +0 +false +p2.y - p1.y + +32 +tests/coverage/expect/ContextFunctions.scala +example +IntersectionTypes$ +Class +example.IntersectionTypes$ +euclideanDistance +560 +561 +24 + +Literal +false +0 +false +2 + +33 +tests/coverage/expect/ContextFunctions.scala +example +IntersectionTypes$ +Class +example.IntersectionTypes$ +euclideanDistance +538 +562 +24 +pow +Apply +false +0 +false +Math.pow(p2.y - p1.y, 2) + +34 +tests/coverage/expect/ContextFunctions.scala +example +IntersectionTypes$ +Class +example.IntersectionTypes$ +euclideanDistance +574 +585 +24 +- +Apply +false +0 +false +p2.x - p1.x + +35 +tests/coverage/expect/ContextFunctions.scala +example +IntersectionTypes$ +Class +example.IntersectionTypes$ +euclideanDistance +587 +588 +24 + +Literal +false +0 +false +2 + +36 +tests/coverage/expect/ContextFunctions.scala +example +IntersectionTypes$ +Class +example.IntersectionTypes$ +euclideanDistance +565 +589 +24 +pow +Apply +false +0 +false +Math.pow(p2.x - p1.x, 2) + +37 +tests/coverage/expect/ContextFunctions.scala +example +IntersectionTypes$ +Class +example.IntersectionTypes$ +euclideanDistance +538 +589 +24 ++ +Apply +false +0 +false +Math.pow(p2.y - p1.y, 2) + Math.pow(p2.x - p1.x, 2) + +38 +tests/coverage/expect/ContextFunctions.scala +example +IntersectionTypes$ +Class +example.IntersectionTypes$ +euclideanDistance +528 +590 +24 +sqrt +Apply +false +0 +false +Math.sqrt(Math.pow(p2.y - p1.y, 2) + Math.pow(p2.x - p1.x, 2)) + +39 +tests/coverage/expect/ContextFunctions.scala +example +IntersectionTypes$ +Class +example.IntersectionTypes$ +test +614 +615 +26 + +Literal +false +0 +false +3 + +40 +tests/coverage/expect/ContextFunctions.scala +example +IntersectionTypes$ +Class +example.IntersectionTypes$ +test +617 +618 +26 + +Literal +false +0 +false +4 + +41 +tests/coverage/expect/ContextFunctions.scala +example +IntersectionTypes$ +Class +example.IntersectionTypes$ +test +643 +644 +27 + +Literal +false +0 +false +6 + +42 +tests/coverage/expect/ContextFunctions.scala +example +IntersectionTypes$ +Class +example.IntersectionTypes$ +test +646 +647 +27 + +Literal +false +0 +false +8 + +43 +tests/coverage/expect/ContextFunctions.scala +example +IntersectionTypes$ +Class +example.IntersectionTypes$ +test +653 +687 +28 +println +Apply +false +0 +false +println(euclideanDistance(p1, p2)) + +44 +tests/coverage/expect/TypeLambdas.scala +example +TypeLambdas$ +Class +example.TypeLambdas$ +test +310 +311 +13 + +Literal +false +0 +false +1 + +45 +tests/coverage/expect/TypeLambdas.scala +example +TypeLambdas$ +Class +example.TypeLambdas$ +test +315 +318 +13 + +Literal +false +0 +false +"1" + +46 +tests/coverage/expect/TypeLambdas.scala +example +TypeLambdas$ +Class +example.TypeLambdas$ +test +306 +319 +13 +apply +Apply +false +0 +false +Map(1 -> "1") + +47 +tests/coverage/expect/TypeLambdas.scala +example +TypeLambdas$ +Class +example.TypeLambdas$ +test +368 +371 +16 + +Literal +false +0 +false +"a" + +48 +tests/coverage/expect/TypeLambdas.scala +example +TypeLambdas$ +Class +example.TypeLambdas$ +test +373 +376 +16 + +Literal +false +0 +false +"b" + +49 +tests/coverage/expect/StructuralTypes.scala +example +Record +Class +example.Record +selectDynamic +319 +343 +9 +find +Apply +false +0 +false +elems.find(_._1 == name) + +50 +tests/coverage/expect/StructuralTypes.scala +example +StructuralTypes$ +Class +example.StructuralTypes$ + +442 +448 +16 + +Literal +false +0 +false +"name" + +51 +tests/coverage/expect/StructuralTypes.scala +example +StructuralTypes$ +Class +example.StructuralTypes$ + +452 +458 +16 + +Literal +false +0 +false +"Emma" + +52 +tests/coverage/expect/StructuralTypes.scala +example +StructuralTypes$ +Class +example.StructuralTypes$ + +460 +465 +16 + +Literal +false +0 +false +"age" + +53 +tests/coverage/expect/StructuralTypes.scala +example +StructuralTypes$ +Class +example.StructuralTypes$ + +469 +471 +16 + +Literal +false +0 +false +42 + +54 +tests/coverage/expect/StructuralTypes.scala +example +StructuralTypes$ +Class +example.StructuralTypes$ + +473 +481 +16 + +Literal +false +0 +false +"salary" + +55 +tests/coverage/expect/StructuralTypes.scala +example +StructuralTypes$ +Class +example.StructuralTypes$ + +485 +489 +16 + +Literal +false +0 +false +320L + +56 +tests/coverage/expect/StructuralTypes.scala +example +StructuralTypes$ +Class +example.StructuralTypes$ + +435 +490 +16 +apply +Apply +false +0 +false +Record("name" -> "Emma", "age" -> 42, "salary" -> 320L) + +57 +tests/coverage/expect/StructuralTypes.scala +example +StructuralTypes$ +Class +example.StructuralTypes$ + +542 +548 +18 + +Literal +false +0 +false +"name" + +58 +tests/coverage/expect/StructuralTypes.scala +example +StructuralTypes$ +Class +example.StructuralTypes$ + +552 +558 +18 + +Literal +false +0 +false +"John" + +59 +tests/coverage/expect/StructuralTypes.scala +example +StructuralTypes$ +Class +example.StructuralTypes$ + +560 +568 +18 + +Literal +false +0 +false +"salary" + +60 +tests/coverage/expect/StructuralTypes.scala +example +StructuralTypes$ +Class +example.StructuralTypes$ + +572 +574 +18 + +Literal +false +0 +false +42 + +61 +tests/coverage/expect/StructuralTypes.scala +example +StructuralTypes$ +Class +example.StructuralTypes$ + +535 +575 +18 +apply +Apply +false +0 +false +Record("name" -> "John", "salary" -> 42) + +62 +tests/coverage/expect/StructuralTypes.scala +example +StructuralTypes$ +Class +example.StructuralTypes$ +test +623 +643 +21 +println +Apply +false +0 +false +println(person.name) + +63 +tests/coverage/expect/StructuralTypes.scala +example +StructuralTypes$ +Class +example.StructuralTypes$ +test +648 +667 +22 +println +Apply +false +0 +false +println(person.age) + +64 +tests/coverage/expect/StructuralTypes.scala +example +StructuralTypes$ +Class +example.StructuralTypes$ +test +673 +700 +24 +println +Apply +false +0 +false +println(invalidPerson.name) + +65 +tests/coverage/expect/UnionTypes.scala +example +UnionTypes$ +Class +example.UnionTypes$ +safeDivide +663 +664 +21 + +Literal +false +0 +false +0 + +66 +tests/coverage/expect/UnionTypes.scala +example +UnionTypes$ +Class +example.UnionTypes$ +safeDivide +685 +710 +21 + +Literal +false +0 +false +"DivisionByZeroException" + +67 +tests/coverage/expect/UnionTypes.scala +example +UnionTypes$ +Class +example.UnionTypes$ +safeDivide +670 +711 +21 +apply +Apply +true +0 +false +DivisionByZero("DivisionByZeroException") + +68 +tests/coverage/expect/UnionTypes.scala +example +UnionTypes$ +Class +example.UnionTypes$ +safeDivide +717 +731 +21 +apply +Apply +false +0 +false +Success(a / b) + +69 +tests/coverage/expect/UnionTypes.scala +example +UnionTypes$ +Class +example.UnionTypes$ +safeDivide +717 +731 +21 + +Block +true +0 +false +Success(a / b) + +70 +tests/coverage/expect/UnionTypes.scala +example +UnionTypes$ +Class +example.UnionTypes$ +test +949 +950 +28 + +Literal +false +0 +false +4 + +71 +tests/coverage/expect/UnionTypes.scala +example +UnionTypes$ +Class +example.UnionTypes$ +test +952 +953 +28 + +Literal +false +0 +false +2 + +72 +tests/coverage/expect/UnionTypes.scala +example +UnionTypes$ +Class +example.UnionTypes$ +test +1044 +1045 +31 + +Literal +false +0 +false +4 + +73 +tests/coverage/expect/UnionTypes.scala +example +UnionTypes$ +Class +example.UnionTypes$ +test +1047 +1048 +31 + +Literal +false +0 +false +0 + +74 +tests/coverage/expect/UnionTypes.scala +example +UnionTypes$ +Class +example.UnionTypes$ +test +1112 +1150 +34 +println +Apply +false +0 +false +println(either(divisionResultSuccess)) + +75 +tests/coverage/expect/UnionTypes.scala +example +UnionTypes$ +Class +example.UnionTypes$ +test +1213 +1251 +37 +println +Apply +false +0 +false +println(either(divisionResultFailure)) + +76 +tests/coverage/expect/UnionTypes.scala +example +UnionTypes$ +Class +example.UnionTypes$ +test +1297 +1298 +39 + +Literal +false +0 +false +1 + +77 +tests/coverage/expect/UnionTypes.scala +example +UnionTypes$ +Class +example.UnionTypes$ +test +1305 +1306 +39 + +Literal +false +0 +false +2 + +78 +tests/coverage/expect/UnionTypes.scala +example +UnionTypes$ +Class +example.UnionTypes$ +test +1313 +1314 +39 + +Literal +false +0 +false +3 + +79 +tests/coverage/expect/UnionTypes.scala +example +UnionTypes$ +Class +example.UnionTypes$ +test +1300 +1323 +39 +apply +Apply +false +0 +false +Cons(2, Cons(3, Empty)) + +80 +tests/coverage/expect/UnionTypes.scala +example +UnionTypes$ +Class +example.UnionTypes$ +test +1292 +1324 +39 +apply +Apply +false +0 +false +Cons(1, Cons(2, Cons(3, Empty))) + +81 +tests/coverage/expect/PatternMatching.scala +example +Even$ +Class +example.Even$ +unapply +296 +306 +12 +% +Select +false +0 +false +s.length % + +82 +tests/coverage/expect/PatternMatching.scala +example +Even$ +Class +example.Even$ +unapply +307 +308 +12 + +Literal +false +0 +false +2 + +83 +tests/coverage/expect/PatternMatching.scala +example +Even$ +Class +example.Even$ +unapply +312 +313 +12 + +Literal +false +0 +false +0 + +84 +tests/coverage/expect/PatternMatching.scala +example +Person$ +Class +example.Person$ +unapply +797 +803 +29 + +Select +false +0 +false +Person + +85 +tests/coverage/expect/PatternMatching.scala +example +Names$ +Class +example.Names$ +unapplySeq +1047 +1062 +37 +split +Select +false +0 +false +name.trim.split + +86 +tests/coverage/expect/PatternMatching.scala +example +Names$ +Class +example.Names$ +unapplySeq +1063 +1066 +37 + +Literal +false +0 +false +" " + +87 +tests/coverage/expect/PatternMatching.scala +example +Names$ +Class +example.Names$ +unapplySeq +1092 +1093 +38 + +Literal +false +0 +false +2 + +88 +tests/coverage/expect/PatternMatching.scala +example +Names$ +Class +example.Names$ +unapplySeq +1099 +1103 +38 +None +Ident +true +0 +false +None + +89 +tests/coverage/expect/PatternMatching.scala +example +Names$ +Class +example.Names$ +unapplySeq +1161 +1162 +39 + +Literal +false +0 +false +1 + +90 +tests/coverage/expect/PatternMatching.scala +example +Names$ +Class +example.Names$ +unapplySeq +1150 +1163 +39 +refArrayOps +Apply +false +0 +false +names.drop(1) + +91 +tests/coverage/expect/PatternMatching.scala +example +Names$ +Class +example.Names$ +unapplySeq +1174 +1175 +39 + +Literal +false +0 +false +1 + +92 +tests/coverage/expect/PatternMatching.scala +example +Names$ +Class +example.Names$ +unapplySeq +1150 +1176 +39 +wrapRefArray +Apply +false +0 +false +names.drop(1).dropRight(1) + +93 +tests/coverage/expect/PatternMatching.scala +example +Names$ +Class +example.Names$ +unapplySeq +1117 +1184 +39 +apply +Apply +false +0 +false +Some(names.last :: names.head :: names.drop(1).dropRight(1).toList) + +94 +tests/coverage/expect/PatternMatching.scala +example +Names$ +Class +example.Names$ +unapplySeq +1117 +1184 +39 + +Block +true +0 +false +Some(names.last :: names.head :: names.drop(1).dropRight(1).toList) + +95 +tests/coverage/expect/PatternMatching.scala +example +Name$ +Class +example.Name$ +unapply +1361 +1365 +49 + +Select +false +0 +false +Name + +96 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1425 +1431 +55 + +Literal +false +0 +false +"even" + +97 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1475 +1508 +56 + +Literal +false +0 +false + has an even number of characters + +98 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1473 +1508 +56 +apply +Apply +false +0 +false +$s has an even number of characters + +99 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1471 +1509 +56 +s +Apply +false +0 +false +s"$s has an even number of characters" + +100 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1463 +1510 +56 +println +Apply +false +0 +false +println(s"$s has an even number of characters") + +101 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1548 +1580 +57 + +Literal +false +0 +false + has an odd number of characters + +102 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1546 +1580 +57 +apply +Apply +false +0 +false +$s has an odd number of characters + +103 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1544 +1581 +57 +s +Apply +false +0 +false +s"$s has an odd number of characters" + +104 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1536 +1582 +57 +println +Apply +false +0 +false +println(s"$s has an odd number of characters") + +105 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +containsConsecutive +1775 +1810 +61 +containsConsecutive +Apply +false +0 +false +containsConsecutive(b :: xs.toList) + +106 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +containsConsecutive +1843 +1848 +62 + +Literal +false +0 +false +false + +107 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1887 +1888 +64 + +Literal +false +0 +false +1 + +108 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1890 +1891 +64 + +Literal +false +0 +false +2 + +109 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1893 +1894 +64 + +Literal +false +0 +false +3 + +110 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1896 +1897 +64 + +Literal +false +0 +false +4 + +111 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1899 +1900 +64 + +Literal +false +0 +false +5 + +112 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1882 +1901 +64 +apply +Apply +false +0 +false +List(1, 2, 3, 4, 5) + +113 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1862 +1902 +64 +containsConsecutive +Apply +false +0 +false +containsConsecutive(List(1, 2, 3, 4, 5)) + +114 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1854 +1903 +64 +println +Apply +false +0 +false +println(containsConsecutive(List(1, 2, 3, 4, 5))) + +115 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1941 +1942 +65 + +Literal +false +0 +false +1 + +116 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1944 +1945 +65 + +Literal +false +0 +false +2 + +117 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1947 +1948 +65 + +Literal +false +0 +false +3 + +118 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1950 +1951 +65 + +Literal +false +0 +false +3 + +119 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1953 +1954 +65 + +Literal +false +0 +false +5 + +120 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1936 +1955 +65 +apply +Apply +false +0 +false +List(1, 2, 3, 3, 5) + +121 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1916 +1956 +65 +containsConsecutive +Apply +false +0 +false +containsConsecutive(List(1, 2, 3, 3, 5)) + +122 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1908 +1957 +65 +println +Apply +false +0 +false +println(containsConsecutive(List(1, 2, 3, 3, 5))) + +123 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +1992 +1998 +68 + +Literal +false +0 +false +"john" + +124 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +2000 +2002 +68 + +Literal +false +0 +false +42 + +125 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +2048 +2054 +69 + +Literal +false +0 +false +name: + +126 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +2056 +2063 +69 + +Literal +false +0 +false +, age: + +127 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +2048 +2065 +69 +apply +Apply +false +0 +false +name: $n, age: $a + +128 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +2046 +2066 +69 +s +Apply +false +0 +false +s"name: $n, age: $a" + +129 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +2038 +2067 +69 +println +Apply +false +0 +false +println(s"name: $n, age: $a") + +130 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +greet +2189 +2205 +74 + +Literal +false +0 +false +"Good morning, " + +131 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +greet +2220 +2223 +74 + +Literal +false +0 +false +" " + +132 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +greet +2237 +2240 +74 + +Literal +false +0 +false +"!" + +133 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +greet +2286 +2335 +75 + +Literal +false +0 +false +"Welcome! Please make sure to fill in your name!" + +134 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +2359 +2372 +77 + +Literal +false +0 +false +"Alan Turing" + +135 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +2345 +2374 +77 +println +Apply +false +0 +false +println(greet("Alan Turing")) + +136 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +2393 +2399 +78 + +Literal +false +0 +false +"john" + +137 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +2379 +2401 +78 +println +Apply +false +0 +false +println(greet("john")) + +138 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +2420 +2445 +79 + +Literal +false +0 +false +"Wolfgang Amadeus Mozart" + +139 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +2406 +2447 +79 +println +Apply +false +0 +false +println(greet("Wolfgang Amadeus Mozart")) + +140 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +2478 +2485 +82 + +Literal +false +0 +false +"alice" + +141 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +2525 +2533 +83 + +Literal +false +0 +false +name is + +142 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +2525 +2535 +83 +apply +Apply +false +0 +false +name is $n + +143 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +2523 +2536 +83 +s +Apply +false +0 +false +s"name is $n" + +144 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +2515 +2537 +83 +println +Apply +false +0 +false +println(s"name is $n") + +145 +tests/coverage/expect/PatternMatching.scala +example +PatternMatching$ +Class +example.PatternMatching$ +test +2568 +2580 +84 + +Literal +false +0 +false +"empty name" + +146 +tests/coverage/expect/TraitParams.scala +example +TraitParams$ +Class +example.TraitParams$ +printMessages +475 +490 +13 +map +Apply +false +0 +false +msgs.map(_.msg) + +147 +tests/coverage/expect/TraitParams.scala +example +TraitParams$ +Class +example.TraitParams$ +printMessages +500 +503 +13 + +Literal +false +0 +false +" " + +148 +tests/coverage/expect/TraitParams.scala +example +TraitParams$ +Class +example.TraitParams$ +printMessages +467 +505 +13 +println +Apply +false +0 +false +println(msgs.map(_.msg).mkString(" ")) + +149 +tests/coverage/expect/TraitParams.scala +example +TraitParams$ +Class +example.TraitParams$ +test +550 +551 +16 + +Select +false +0 +false +A + +150 +tests/coverage/expect/TraitParams.scala +example +TraitParams$ +Class +example.TraitParams$ +test +557 +558 +16 + +Select +false +0 +false +B + +151 +tests/coverage/expect/TraitParams.scala +example +TraitParams$ +Class +example.TraitParams$ +test +532 +559 +16 +printMessages +Apply +false +0 +false +printMessages(new A, new B) + +152 +tests/coverage/expect/TraitParams.scala +example +TraitParams$ +Class +example.TraitParams$ +test +685 +686 +20 + +Literal +false +0 +false +1 + +153 +tests/coverage/expect/Enum.scala +example +Planet +Class +example.Planet + +326 +337 +14 + +Literal +false +0 +false +6.67300E-11 + +154 +tests/coverage/expect/Enum.scala +example +Planet +Class +example.Planet +surfaceGravity +363 +390 +15 +/ +Apply +false +0 +false +G * mass / (radius * radius + +155 +tests/coverage/expect/Enum.scala +example +Planet +Class +example.Planet +surfaceWeight +436 +462 +16 +* +Apply +false +0 +false +otherMass * surfaceGravity + +156 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +test +988 +989 +30 + +Literal +false +0 +false +1 + +157 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +test +1005 +1006 +30 + +Literal +false +0 +false +2 + +158 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +test +1022 +1023 +30 + +Literal +false +0 +false +3 + +159 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +test +1008 +1040 +30 +apply +Apply +false +0 +false +ListEnum.Cons(3, ListEnum.Empty) + +160 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +test +991 +1041 +30 +apply +Apply +false +0 +false +ListEnum.Cons(2, ListEnum.Cons(3, ListEnum.Empty)) + +161 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +test +974 +1042 +30 +apply +Apply +false +0 +false +ListEnum.Cons(1, ListEnum.Cons(2, ListEnum.Cons(3, ListEnum.Empty))) + +162 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +test +1055 +1070 +31 + +Literal +false +0 +false +"Example 1: \n" + +163 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +test +1047 +1081 +31 +println +Apply +false +0 +false +println("Example 1: \n"+emptyList) + +164 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +test +1103 +1105 +32 + +Literal +false +0 +false +\n + +165 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +test +1096 +1105 +32 +apply +Apply +false +0 +false +${list}\n + +166 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +test +1094 +1106 +32 +s +Apply +false +0 +false +s"${list}\n" + +167 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +test +1086 +1107 +32 +println +Apply +false +0 +false +println(s"${list}\n") + +168 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +calculateEarthWeightOnPlanets +1187 +1226 +35 +/ +Apply +false +0 +false +earthWeight/Planet.Earth.surfaceGravity + +169 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +calculateEarthWeightOnPlanets +1242 +1255 +36 +refArrayOps +Apply +false +0 +false +Planet.values + +170 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +$anonfun +1277 +1292 +37 + +Literal +false +0 +false +Your weight on + +171 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +$anonfun +1294 +1298 +37 + +Literal +false +0 +false + is + +172 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +$anonfun +1277 +1322 +37 +apply +Apply +false +0 +false +Your weight on $p is ${p.surfaceWeight(mass)} + +173 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +$anonfun +1275 +1323 +37 +s +Apply +false +0 +false +s"Your weight on $p is ${p.surfaceWeight(mass)}" + +174 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +$anonfun +1267 +1324 +37 +println +Apply +false +0 +false +println(s"Your weight on $p is ${p.surfaceWeight(mass)}") + +175 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +calculateEarthWeightOnPlanets +1233 +1324 +36 +foreach +Apply +false +0 +false +for p <- Planet.values do + println(s"Your weight on $p is ${p.surfaceWeight(mass)}") + +176 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +test +1338 +1350 +39 + +Literal +false +0 +false +"Example 2:" + +177 +tests/coverage/expect/Enum.scala +example +EnumTypes$ +Class +example.EnumTypes$ +test +1386 +1388 +40 + +Literal +false +0 +false +80 +