diff options
71 files changed, 728 insertions, 231 deletions
diff --git a/bincompat-backward.whitelist.conf b/bincompat-backward.whitelist.conf index 57dc564e8a..9ea78a2977 100644 --- a/bincompat-backward.whitelist.conf +++ b/bincompat-backward.whitelist.conf @@ -34,6 +34,14 @@ filter { problemName=DirectMissingMethodProblem }, { + matchName="scala.reflect.runtime.SynchronizedOps.scala$reflect$runtime$SynchronizedOps$$super$newMappedBaseTypeSeq" + problemName=ReversedMissingMethodProblem + }, + { + matchName="scala.reflect.runtime.SynchronizedOps#SynchronizedBaseTypeSeq.lateMap" + problemName=DirectMissingMethodProblem + }, + { matchName="scala.collection.immutable.HashMap.contains0" problemName=DirectMissingMethodProblem }, diff --git a/bincompat-forward.whitelist.conf b/bincompat-forward.whitelist.conf index ad778f447a..fdd0f0fa22 100644 --- a/bincompat-forward.whitelist.conf +++ b/bincompat-forward.whitelist.conf @@ -44,6 +44,14 @@ filter { problemName=DirectMissingMethodProblem }, { + matchName="scala.reflect.runtime.SynchronizedOps.newMappedBaseTypeSeq" + problemName=DirectMissingMethodProblem + }, + { + matchName="scala.reflect.runtime.JavaUniverse.newMappedBaseTypeSeq" + problemName=DirectMissingMethodProblem + }, + { matchName="scala.collection.immutable.HashMap.contains0" problemName=DirectMissingMethodProblem }, @@ -415,6 +423,14 @@ filter { { matchName="scala.annotation.showAsInfix" problemName=MissingClassProblem + }, + { + matchName="scala.util.PropertiesTrait.coloredOutputEnabled" + problemName=DirectMissingMethodProblem + }, + { + matchName="scala.util.Properties.coloredOutputEnabled" + problemName=DirectMissingMethodProblem } ] } @@ -32,6 +32,7 @@ * - to modularize the Scala compiler or library further */ +import scala.build._ import VersionUtil._ // Scala dependencies: @@ -1017,6 +1018,9 @@ commands += Command("partest")(_ => PartestUtil.partestParser((baseDirectory in ("test/it:testOnly -- " + parsed) :: state } +// Watch the test files also so ~partest triggers on test case changes +watchSources ++= PartestUtil.testFilePaths((baseDirectory in ThisBuild).value, (baseDirectory in ThisBuild).value / "test") + // Add tab completion to scalac et al. commands ++= { val commands = diff --git a/project/BuildSettings.scala b/project/BuildSettings.scala index 76cd888a2d..8456f91f86 100644 --- a/project/BuildSettings.scala +++ b/project/BuildSettings.scala @@ -1,3 +1,5 @@ +package scala.build + import sbt._ /** This object defines keys that should be visible with an unqualified name in all .sbt files and the command line */ diff --git a/project/GenerateAnyVals.scala b/project/GenerateAnyVals.scala index 84454cb0ed..f349bfd16b 100644 --- a/project/GenerateAnyVals.scala +++ b/project/GenerateAnyVals.scala @@ -1,3 +1,5 @@ +package scala.build + /** Code generation of the AnyVal types and their companions. */ trait GenerateAnyValReps { self: GenerateAnyVals => diff --git a/project/JarJar.scala b/project/JarJar.scala index 918060c9ee..3cb9e4cfff 100644 --- a/project/JarJar.scala +++ b/project/JarJar.scala @@ -1,3 +1,5 @@ +package scala.build + import org.pantsbuild.jarjar import org.pantsbuild.jarjar._ import org.pantsbuild.jarjar.util._ diff --git a/project/MiMa.scala b/project/MiMa.scala index ceda8f5594..fb9bb175ab 100644 --- a/project/MiMa.scala +++ b/project/MiMa.scala @@ -1,3 +1,5 @@ +package scala.build + // It would be nice to use sbt-mima-plugin here, but the plugin is missing // at least two features we need: // * ability to run MiMa twice, swapping `curr` and `prev`, to detect diff --git a/project/Osgi.scala b/project/Osgi.scala index 082fd91ed1..b05751958a 100644 --- a/project/Osgi.scala +++ b/project/Osgi.scala @@ -1,3 +1,5 @@ +package scala.build + import aQute.bnd.osgi.Builder import aQute.bnd.osgi.Constants._ import java.util.Properties diff --git a/project/ParserUtil.scala b/project/ParserUtil.scala index cdaf8831a5..bbd9129dbe 100644 --- a/project/ParserUtil.scala +++ b/project/ParserUtil.scala @@ -1,3 +1,5 @@ +package scala.build + import sbt._ import sbt.complete.Parser._ import sbt.complete.Parsers._ diff --git a/project/PartestUtil.scala b/project/PartestUtil.scala index 23570a88ec..6d2c9a4c45 100644 --- a/project/PartestUtil.scala +++ b/project/PartestUtil.scala @@ -1,3 +1,5 @@ +package scala.build + import sbt._ import sbt.complete._, Parser._, Parsers._ @@ -24,6 +26,10 @@ object PartestUtil { isParentOf(testBase / srcPath, f, 2) || isParentOf(f, testBase / srcPath, Int.MaxValue) } } + + def testFilePaths(globalBase: File, testBase: File): Seq[java.io.File] = + (new TestFiles("files", globalBase, testBase)).allTestCases.map(_._1) + /** A parser for the custom `partest` command */ def partestParser(globalBase: File, testBase: File): Parser[String] = { val knownUnaryOptions = List( diff --git a/project/Quiet.scala b/project/Quiet.scala index 0a186d8f28..8ae08ad5a6 100644 --- a/project/Quiet.scala +++ b/project/Quiet.scala @@ -1,3 +1,5 @@ +package scala.build + import sbt._ import Keys._ diff --git a/project/ScalaOptionParser.scala b/project/ScalaOptionParser.scala index 27ed1f0e6f..0208921959 100644 --- a/project/ScalaOptionParser.scala +++ b/project/ScalaOptionParser.scala @@ -1,3 +1,5 @@ +package scala.build + import ParserUtil._ import sbt._ import sbt.complete.Parser._ diff --git a/project/ScalaTool.scala b/project/ScalaTool.scala index 98e18235c4..ace547c640 100644 --- a/project/ScalaTool.scala +++ b/project/ScalaTool.scala @@ -1,3 +1,5 @@ +package scala.build + import sbt._ import org.apache.commons.lang3.SystemUtils import org.apache.commons.lang3.StringUtils.replaceEach diff --git a/project/ScriptCommands.scala b/project/ScriptCommands.scala index 8d5d09943a..f6b700f007 100644 --- a/project/ScriptCommands.scala +++ b/project/ScriptCommands.scala @@ -1,3 +1,5 @@ +package scala.build + import sbt._ import Keys._ import BuildSettings.autoImport._ diff --git a/project/VersionUtil.scala b/project/VersionUtil.scala index 7c4909697f..ebc2488345 100644 --- a/project/VersionUtil.scala +++ b/project/VersionUtil.scala @@ -1,3 +1,5 @@ +package scala.build + import sbt._ import Keys._ import java.util.Properties diff --git a/project/plugins.sbt b/project/plugins.sbt index 80aef2c591..ca80c88a9e 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -20,3 +20,7 @@ buildInfoKeys := Seq[BuildInfoKey](buildClasspath) buildInfoPackage := "scalabuild" libraryDependencies += "com.typesafe" %% "mima-reporter" % "0.1.13" + +concurrentRestrictions in Global := Seq( + Tags.limitAll(1) // workaround for https://github.com/sbt/sbt/issues/2970 +) diff --git a/spec/05-classes-and-objects.md b/spec/05-classes-and-objects.md index 6738c7a5b7..5bd520589d 100644 --- a/spec/05-classes-and-objects.md +++ b/spec/05-classes-and-objects.md @@ -597,10 +597,12 @@ overridden in subclasses. A `final` class may not be inherited by a template. `final` is redundant for object definitions. Members of final classes or objects are implicitly also final, so the `final` modifier is generally redundant for them, too. Note, however, that -[constant value definitions](04-basic-declarations-and-definitions.html#value-declarations-and-definitions) do require -an explicit `final` modifier, even if they are defined in a final class or -object. `final` may not be applied to incomplete members, and it may not be -combined in one modifier list with `sealed`. +[constant value definitions](04-basic-declarations-and-definitions.html#value-declarations-and-definitions) +do require an explicit `final` modifier, +even if they are defined in a final class or object. +`final` is permitted for abstract classes +but it may not be applied to traits or incomplete members, +and it may not be combined in one modifier list with `sealed`. ### `sealed` The `sealed` modifier applies to class definitions. A diff --git a/spec/README.md b/spec/README.md index b19ce6441f..ad524dfdf3 100644 --- a/spec/README.md +++ b/spec/README.md @@ -8,7 +8,11 @@ Third, we'd like to support different output formats. An html page per chapter w ## Editing -We use Jekyll 2 and [Redcarpet](https://github.com/vmg/redcarpet) to generate the html. Essentially, this is what github pages use. +At the time of writing we are using Jekyll 3.3.0 and [Redcarpet 3.3.2](https://github.com/vmg/redcarpet) to generate the html. + +Check `Gemfile` for the current versions. + +We aim to track the configuration GitHub Pages use but at times differences will arise as GitHub Pages evolves. ## Building diff --git a/spec/_config.yml b/spec/_config.yml index 60e80ee05c..1a67f7de63 100644 --- a/spec/_config.yml +++ b/spec/_config.yml @@ -1,7 +1,7 @@ baseurl: /files/archive/spec/2.12 safe: true lsi: false -highlighter: null +highlighter: false markdown: redcarpet encoding: utf-8 redcarpet: diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 226c49ec07..3ed1570c1c 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -983,6 +983,8 @@ trait Scanners extends ScannersCommon { def intVal: Long = intVal(negated = false) + private val zeroFloat = raw"[0.]+(?:[eE][+-]?[0-9]+)?[fFdD]?".r + /** Convert current strVal, base to float value. */ def floatVal(negated: Boolean): Float = { @@ -990,8 +992,7 @@ trait Scanners extends ScannersCommon { val value: Float = java.lang.Float.parseFloat(strVal) if (value > Float.MaxValue) syntaxError("floating point number too large") - val zeroly = "0.fF" - if (value == 0.0f && strVal.exists(c => !zeroly.contains(c))) + if (value == 0.0f && !zeroFloat.pattern.matcher(strVal).matches) syntaxError("floating point number too small") if (negated) -value else value } catch { @@ -1010,8 +1011,7 @@ trait Scanners extends ScannersCommon { val value: Double = java.lang.Double.parseDouble(strVal) if (value > Double.MaxValue) syntaxError("double precision floating point number too large") - val zeroly = "0.dD" - if (value == 0.0d && strVal.exists(c => !zeroly.contains(c))) + if (value == 0.0d && !zeroFloat.pattern.matcher(strVal).matches) syntaxError("double precision floating point number too small") if (negated) -value else value } catch { diff --git a/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala b/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala index 1ea152b29c..fbd59eb04a 100644 --- a/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala +++ b/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala @@ -10,9 +10,10 @@ import java.util.function.IntFunction import java.util import java.util.Comparator -import scala.reflect.io.{AbstractFile, PlainFile} +import scala.reflect.io.{AbstractFile, PlainFile, PlainNioFile} import scala.tools.nsc.util.{ClassPath, ClassRepresentation} import FileUtils._ +import scala.collection.JavaConverters._ /** * A trait allowing to look for classpath entries in directories. It provides common logic for @@ -121,51 +122,78 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends Directo def asClassPathStrings: Seq[String] = Seq(dir.getPath) } -object JImageDirectoryLookup { - import java.nio.file._, java.net.URI, scala.collection.JavaConverters._ - def apply(): List[ClassPath] = { +object JrtClassPath { + import java.nio.file._, java.net.URI + def apply(): Option[ClassPath] = { try { val fs = FileSystems.getFileSystem(URI.create("jrt:/")) - val dir: Path = fs.getPath("/modules") - val modules = Files.list(dir).iterator().asScala.toList - modules.map(m => new JImageDirectoryLookup(fs, m.getFileName.toString)) + Some(new JrtClassPath(fs)) } catch { case _: ProviderNotFoundException | _: FileSystemNotFoundException => - Nil + None } } } -class JImageDirectoryLookup(fs: java.nio.file.FileSystem, module: String) extends DirectoryLookup[ClassFileEntryImpl] with NoSourcePaths { + +/** + * Implementation `ClassPath` based on the JDK 9 encapsulated runtime modules (JEP-220) + * + * https://bugs.openjdk.java.net/browse/JDK-8066492 is the most up to date reference + * for the structure of the jrt:// filesystem. + * + * The implementation assumes that no classes exist in the empty package. + */ +final class JrtClassPath(fs: java.nio.file.FileSystem) extends ClassPath with NoSourcePaths { import java.nio.file.Path, java.nio.file._ type F = Path - val dir: Path = fs.getPath("/modules/" + module) + private val dir: Path = fs.getPath("/packages") - protected def emptyFiles: Array[Path] = Array.empty - protected def getSubDir(packageDirName: String): Option[Path] = { - val packageDir = dir.resolve(packageDirName) - if (Files.exists(packageDir) && Files.isDirectory(packageDir)) Some(packageDir) - else None + // e.g. "java.lang" -> Seq("/modules/java.base") + private val packageToModuleBases: Map[String, Seq[Path]] = { + val ps = Files.newDirectoryStream(dir).iterator().asScala + def lookup(pack: Path): Seq[Path] = { + Files.list(pack).iterator().asScala.map(l => if (Files.isSymbolicLink(l)) Files.readSymbolicLink(l) else l).toList + } + ps.map(p => (p.toString.stripPrefix("/packages/"), lookup(p))).toMap } - protected def listChildren(dir: Path, filter: Option[Path => Boolean]): Array[Path] = { - import scala.collection.JavaConverters._ - val f = filter.getOrElse((p: Path) => true) - Files.list(dir).iterator().asScala.filter(f).toArray[Path] + + override private[nsc] def packages(inPackage: String): Seq[PackageEntry] = { + def matches(packageDottedName: String) = + if (packageDottedName.contains(".")) + packageOf(packageDottedName) == inPackage + else inPackage == "" + packageToModuleBases.keysIterator.filter(matches).map(PackageEntryImpl(_)).toVector + } + private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = { + if (inPackage == "") Nil + else { + packageToModuleBases.getOrElse(inPackage, Nil).flatMap(x => + Files.list(x.resolve(inPackage.replace('.', '/'))).iterator().asScala.filter(_.getFileName.toString.endsWith(".class"))).map(x => + ClassFileEntryImpl(new PlainNioFile(x))).toVector + } } - protected def getName(f: Path): String = f.getFileName.toString - protected def toAbstractFile(f: Path): AbstractFile = new scala.reflect.io.PlainNioFile(f) - protected def isPackage(f: Path): Boolean = Files.isDirectory(f) && mayBeValidPackage(f.getFileName.toString) + + override private[nsc] def list(inPackage: String): ClassPathEntries = + if (inPackage == "") ClassPathEntries(packages(inPackage), Nil) + else ClassPathEntries(packages(inPackage), classes(inPackage)) def asURLs: Seq[URL] = Seq(dir.toUri.toURL) - def asClassPathStrings: Seq[String] = asURLs.map(_.toString) + // We don't yet have a scheme to represent the JDK modules in our `-classpath`. + // java models them as entries in the new "module path", we'll probably need to follow this. + def asClassPathStrings: Seq[String] = Nil def findClassFile(className: String): Option[AbstractFile] = { - val relativePath = FileUtils.dirPath(className) + ".class" - val classFile = dir.resolve(relativePath) - if (Files.exists(classFile)) Some(new scala.reflect.io.PlainNioFile(classFile)) else None + if (!className.contains(".")) None + else { + val inPackage = packageOf(className) + packageToModuleBases.getOrElse(inPackage, Nil).iterator.flatMap{x => + val file = x.resolve(className.replace('.', '/') + ".class") + if (Files.exists(file)) new scala.reflect.io.PlainNioFile(file) :: Nil else Nil + }.take(1).toList.headOption + } } - override protected def createFileEntry(file: AbstractFile): ClassFileEntryImpl = ClassFileEntryImpl(file) - override protected def isMatchingFile(f: Path): Boolean = Files.isRegularFile(f) && f.getFileName.toString.endsWith(".class") - override private[nsc] def classes(inPackage: String): Seq[ClassFileEntry] = files(inPackage) + private def packageOf(dottedClassName: String): String = + dottedClassName.substring(0, dottedClassName.lastIndexOf(".")) } case class DirectoryClassPath(dir: File) extends JFileDirectoryLookup[ClassFileEntryImpl] with NoSourcePaths { diff --git a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala index 99263bf834..f1f5f37c36 100644 --- a/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/ConsoleReporter.scala @@ -14,8 +14,10 @@ import StringOps.{countElementsAsString => countAs, trimAllTrailingSpace => trim /** This class implements a Reporter that displays messages on a text console. */ -class ConsoleReporter(val settings: Settings, reader: BufferedReader, writer: PrintWriter) extends AbstractReporter { - def this(settings: Settings) = this(settings, Console.in, new PrintWriter(Console.err, true)) +class ConsoleReporter(val settings: Settings, reader: BufferedReader, writer: PrintWriter, echoWriter: PrintWriter) extends AbstractReporter { + def this(settings: Settings) = this(settings, Console.in, new PrintWriter(Console.err, true), new PrintWriter(Console.out, true)) + def this(settings: Settings, reader: BufferedReader, writer: PrintWriter) = + this(settings, reader, writer, writer) /** Whether a short file name should be displayed before errors */ var shortname: Boolean = false @@ -41,6 +43,12 @@ class ConsoleReporter(val settings: Settings, reader: BufferedReader, writer: Pr writer.flush() } + /** Prints the message to the echoWriter, which is usually stdout. */ + override def echo(msg: String): Unit = { + echoWriter.println(trimTrailing(msg)) + echoWriter.flush() + } + /** Prints the message with the given position indication. */ def printMessage(posIn: Position, msg: String): Unit = printMessage(formatMessage(posIn, msg, shortname)) diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index d948d151a6..dd44366692 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -122,11 +122,16 @@ abstract class SymbolLoaders { * and give them `completer` as type. */ def enterClassAndModule(root: Symbol, name: String, getCompleter: (ClassSymbol, ModuleSymbol) => SymbolLoader) { - val clazz = newClass(root, name) - val module = newModule(root, name) - val completer = getCompleter(clazz, module) - enterClass(root, clazz, completer) - enterModule(root, module, completer) + val clazz0 = newClass(root, name) + val module0 = newModule(root, name) + val completer = getCompleter(clazz0, module0) + // enterClass/Module may return an existing symbol instead of the ones we created above + // this may happen when there's both sources and binaries on the classpath, but the class + // name is different from the file name, so the classpath can't match the binary and source + // representation. `companionModule/Class` prefers the source version, so we should be careful + // to reuse the symbols returned below. + val clazz = enterClass(root, clazz0, completer) + val module = enterModule(root, module0, completer) if (!clazz.isAnonymousClass) { // Diagnostic for SI-7147 def msg: String = { diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala index dcffd7a6ab..f35dd6556f 100644 --- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala +++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala @@ -342,12 +342,16 @@ abstract class UnCurry extends InfoTransform * the whole tree with it. */ private def replaceElidableTree(tree: Tree): Tree = { + def elisionOf(t: Type): Tree = t.typeSymbol match { + case StringClass => Literal(Constant("")) setType t + case _ => gen.mkZero(t) + } tree match { case DefDef(_,_,_,_,_,rhs) => - val rhs1 = if (rhs == EmptyTree) rhs else Block(Nil, gen.mkZero(rhs.tpe)) setType rhs.tpe + val rhs1 = if (rhs == EmptyTree) rhs else Block(Nil, elisionOf(rhs.tpe)) setType rhs.tpe deriveDefDef(tree)(_ => rhs1) setSymbol tree.symbol setType tree.tpe case _ => - gen.mkZero(tree.tpe) setType tree.tpe + elisionOf(tree.tpe) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index d349597b14..7a3b8d2ab6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -62,11 +62,10 @@ trait Contexts { self: Analyzer => def warnUnusedImports(unit: CompilationUnit) = if (!unit.isJava) { for (imps <- allImportInfos.remove(unit)) { - for (imp <- imps.reverse.distinct) { + for (imp <- imps.distinct.reverse) { val used = allUsedSelectors(imp) - def isMask(s: ImportSelector) = s.name != nme.WILDCARD && s.rename == nme.WILDCARD - imp.tree.selectors filterNot (s => isMask(s) || used(s)) foreach { sel => + imp.tree.selectors filterNot (s => isMaskImport(s) || used(s)) foreach { sel => reporter.warning(imp posOf sel, "Unused import") } } @@ -74,6 +73,10 @@ trait Contexts { self: Analyzer => } } + def isMaskImport(s: ImportSelector): Boolean = s.name != nme.WILDCARD && s.rename == nme.WILDCARD + def isIndividualImport(s: ImportSelector): Boolean = s.name != nme.WILDCARD && s.rename != nme.WILDCARD + def isWildcardImport(s: ImportSelector): Boolean = s.name == nme.WILDCARD + var lastAccessCheckDetails: String = "" /** List of symbols to import from in a root context. Typically that @@ -1193,27 +1196,33 @@ trait Contexts { self: Analyzer => res } - final def lookupCompanionOf(original: Symbol): Symbol = { - if (original.isModuleClass) original.sourceModule - else lookupScopeEntry(original) match { - case null => NoSymbol - case entry => entry.owner.lookupCompanion(original) + final def lookupCompanionInIncompleteOwner(original: Symbol): Symbol = { + /* Search scopes in current and enclosing contexts for the definition of `symbol` */ + def lookupScopeEntry(symbol: Symbol): ScopeEntry = { + var res: ScopeEntry = null + var ctx = this + while (res == null && ctx.outer != ctx) { + val s = ctx.scope lookupSymbolEntry symbol + if (s != null) + res = s + else + ctx = ctx.outer + } + res } - } - /** Search scopes in current and enclosing contexts for the definition of `symbol` */ - private def lookupScopeEntry(symbol: Symbol): ScopeEntry = { - var res: ScopeEntry = null - var ctx = this - while (res == null && ctx.outer != ctx) { - val s = ctx.scope lookupSymbolEntry symbol - if (s != null) - res = s - else - ctx = ctx.outer + // 1) Must be owned by the same Scope, to ensure that in + // `{ class C; { ...; object C } }`, the class is not seen as a companion of the object. + // 2) Must be a class and module symbol, so that `{ class C; def C }` or `{ type T; object T }` are not companions. + lookupScopeEntry(original) match { + case null => NoSymbol + case entry => + def isCompanion(sym: Symbol): Boolean = + (original.isModule && sym.isClass || sym.isModule && original.isClass) && sym.isCoDefinedWith(original) + entry.owner.lookupNameInSameScopeAs(original, original.name.companionName).filter(isCompanion) } - res } + } //class Context /** A `Context` focussed on an `Import` tree */ diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 395bda234b..28169c9da1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -1955,9 +1955,12 @@ trait Namers extends MethodSynthesis { // Doing this generally would trigger cycles; that's what we also // use the lower-level scan through the current Context as a fall back. if (!currentRun.compiles(owner)) owner.initialize - original.companionSymbol orElse { - ctx.lookupCompanionOf(original) - } + + if (original.isModuleClass) original.sourceModule + else if (!owner.isTerm && owner.hasCompleteInfo) + original.companionSymbol + else + ctx.lookupCompanionInIncompleteOwner(original) } /** A version of `Symbol#linkedClassOfClass` that works with local companions, ala `companionSymbolOf`. */ diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index 990edcd86d..50743a922a 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -112,7 +112,7 @@ abstract class TreeCheckers extends Analyzer { else if (prevTrees exists (t => (t eq tree) || (t.symbol == sym))) () else { - val s1 = (prevTrees map wholetreestr).sorted.distinct + val s1 = (prevTrees map wholetreestr).distinct.sorted val s2 = wholetreestr(tree) if (s1 contains s2) () else movedMsgs += ("\n** %s moved:\n** Previously:\n%s\n** Currently:\n%s".format(ownerstr(sym), s1 mkString ", ", s2)) diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index b66dbf21c0..36b9a65334 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -11,6 +11,7 @@ import scala.collection.mutable.ListBuffer import scala.util.control.Exception.ultimately import symtab.Flags._ import PartialFunction._ +import scala.annotation.tailrec /** An interface to enable higher configurability of diagnostic messages * regarding type errors. This is barely a beginning as error messages are @@ -274,19 +275,54 @@ trait TypeDiagnostics { if (AnyRefTpe <:< req) notAnyRefMessage(found) else "" } + def finalOwners(tpe: Type): Boolean = (tpe.prefix == NoPrefix) || recursivelyFinal(tpe) + + @tailrec + final def recursivelyFinal(tpe: Type): Boolean = { + val prefix = tpe.prefix + if (prefix != NoPrefix) { + if (prefix.typeSymbol.isFinal) { + recursivelyFinal(prefix) + } else { + false + } + } else { + true + } + } + // TODO - figure out how to avoid doing any work at all // when the message will never be seen. I though context.reportErrors // being false would do that, but if I return "<suppressed>" under // that condition, I see it. def foundReqMsg(found: Type, req: Type): String = { - def baseMessage = ( - ";\n found : " + found.toLongString + existentialContext(found) + explainAlias(found) + - "\n required: " + req + existentialContext(req) + explainAlias(req) - ) - ( withDisambiguation(Nil, found, req)(baseMessage) - + explainVariance(found, req) - + explainAnyVsAnyRef(found, req) - ) + val foundWiden = found.widen + val reqWiden = req.widen + val sameNamesDifferentPrefixes = + foundWiden.typeSymbol.name == reqWiden.typeSymbol.name && + foundWiden.prefix.typeSymbol != reqWiden.prefix.typeSymbol + val easilyMistakable = + sameNamesDifferentPrefixes && + !req.typeSymbol.isConstant && + finalOwners(foundWiden) && finalOwners(reqWiden) && + !found.typeSymbol.isTypeParameterOrSkolem && !req.typeSymbol.isTypeParameterOrSkolem + + if (easilyMistakable) { + val longestNameLength = foundWiden.nameAndArgsString.length max reqWiden.nameAndArgsString.length + val paddedFoundName = foundWiden.nameAndArgsString.padTo(longestNameLength, ' ') + val paddedReqName = reqWiden.nameAndArgsString.padTo(longestNameLength, ' ') + ";\n found : " + (paddedFoundName + s" (in ${found.prefix.typeSymbol.fullNameString}) ") + explainAlias(found) + + "\n required: " + (paddedReqName + s" (in ${req.prefix.typeSymbol.fullNameString}) ") + explainAlias(req) + } else { + def baseMessage = { + ";\n found : " + found.toLongString + existentialContext(found) + explainAlias(found) + + "\n required: " + req + existentialContext(req) + explainAlias(req) + } + (withDisambiguation(Nil, found, req)(baseMessage) + + explainVariance(found, req) + + explainAnyVsAnyRef(found, req) + ) + } } def typePatternAdvice(sym: Symbol, ptSym: Symbol) = { @@ -315,14 +351,6 @@ trait TypeDiagnostics { def restoreName() = sym.name = savedName def modifyName(f: String => String) = sym setName newTypeName(f(sym.name.toString)) - /** Prepend java.lang, scala., or Predef. if this type originated - * in one of those. - */ - def qualifyDefaultNamespaces() = { - val intersect = Set(trueOwner, aliasOwner) intersect UnqualifiedOwners - if (intersect.nonEmpty && tp.typeSymbolDirect.name == tp.typeSymbol.name) preQualify() - } - // functions to manipulate the name def preQualify() = modifyName(trueOwner.fullName + "." + _) def postQualify() = if (!(postQualifiedWith contains trueOwner)) { postQualifiedWith ::= trueOwner; modifyName(_ + "(in " + trueOwner + ")") } @@ -414,12 +442,6 @@ trait TypeDiagnostics { if (td1 string_== td2) tds foreach (_.nameQualify()) - // If they have the same simple name, and either of them is in the - // scala package or predef, qualify with scala so it is not confusing why - // e.g. java.util.Iterator and Iterator are different types. - if (td1 name_== td2) - tds foreach (_.qualifyDefaultNamespaces()) - // If they still print identically: // a) If they are type parameters with different owners, append (in <owner>) // b) Failing that, the best we can do is append "(some other)" to the latter. diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 837ccf7e06..8333d5d295 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3333,15 +3333,15 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper def handleOverloaded = { val undetparams = context.undetparams - def funArgTypes(tps: List[Type]) = tps.map { tp => - val relTp = tp.asSeenFrom(pre, fun.symbol.owner) + def funArgTypes(tpAlts: List[(Type, Symbol)]) = tpAlts.map { case (tp, alt) => + val relTp = tp.asSeenFrom(pre, alt.owner) val argTps = functionOrSamArgTypes(relTp) //println(s"funArgTypes $argTps from $relTp") argTps.map(approximateAbstracts) } - def functionProto(argTps: List[Type]): Type = - try functionType(funArgTypes(argTps).transpose.map(lub), WildcardType) + def functionProto(argTpWithAlt: List[(Type, Symbol)]): Type = + try functionType(funArgTypes(argTpWithAlt).transpose.map(lub), WildcardType) catch { case _: IllegalArgumentException => WildcardType } // To propagate as much information as possible to typedFunction, which uses the expected type to @@ -3355,21 +3355,21 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper // do not receive special treatment: they are typed under WildcardType.) val altArgPts = if (settings.isScala212 && args.exists(treeInfo.isFunctionMissingParamType)) - try alts.map(alt => formalTypes(alt.info.paramTypes, argslen)).transpose // do least amount of work up front + try alts.map(alt => formalTypes(alt.info.paramTypes, argslen).map(ft => (ft, alt))).transpose // do least amount of work up front catch { case _: IllegalArgumentException => args.map(_ => Nil) } // fail safe in case formalTypes fails to align to argslen else args.map(_ => Nil) // will type under argPt == WildcardType val (args1, argTpes) = context.savingUndeterminedTypeParams() { val amode = forArgMode(fun, mode) - map2(args, altArgPts) { (arg, argPts) => + map2(args, altArgPts) { (arg, argPtAlts) => def typedArg0(tree: Tree) = { // if we have an overloaded HOF such as `(f: Int => Int)Int <and> (f: Char => Char)Char`, // and we're typing a function like `x => x` for the argument, try to collapse // the overloaded type into a single function type from which `typedFunction` // can derive the argument type for `x` in the function literal above val argPt = - if (argPts.nonEmpty && treeInfo.isFunctionMissingParamType(tree)) functionProto(argPts) + if (argPtAlts.nonEmpty && treeInfo.isFunctionMissingParamType(tree)) functionProto(argPtAlts) else WildcardType val argTyped = typedArg(tree, amode, BYVALmode, argPt) @@ -4666,19 +4666,20 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val qual1 = typedQualifier(qual) if (treeInfo.isVariableOrGetter(qual1)) { if (Statistics.canEnable) Statistics.stopTimer(failedOpEqNanos, opeqStart) - val erred = qual1.isErroneous || args.exists(_.isErroneous) + val erred = qual1.exists(_.isErroneous) || args.exists(_.isErroneous) if (erred) reportError(error) else { val convo = convertToAssignment(fun, qual1, name, args) silent(op = _.typed1(convo, mode, pt)) match { case SilentResultValue(t) => t - case err: SilentTypeError => reportError(SilentTypeError(advice1(convo, error.errors, err), error.warnings)) + case err: SilentTypeError => reportError( + SilentTypeError(advice1(convo, error.errors, err), error.warnings) + ) } } - } - else { + } else { if (Statistics.canEnable) Statistics.stopTimer(failedApplyNanos, appStart) val Apply(Select(qual2, _), args2) = tree - val erred = qual2.isErroneous || args2.exists(_.isErroneous) + val erred = qual2.exists(_.isErroneous) || args2.exists(_.isErroneous) reportError { if (erred) error else SilentTypeError(advice2(error.errors), error.warnings) } diff --git a/src/compiler/scala/tools/util/PathResolver.scala b/src/compiler/scala/tools/util/PathResolver.scala index 188cabbc8d..f845656980 100644 --- a/src/compiler/scala/tools/util/PathResolver.scala +++ b/src/compiler/scala/tools/util/PathResolver.scala @@ -234,7 +234,7 @@ final class PathResolver(settings: Settings) { // Assemble the elements! def basis = List[Traversable[ClassPath]]( - JImageDirectoryLookup.apply(), // 0. The Java 9 classpath (backed by the jrt:/ virtual system) + JrtClassPath.apply(), // 0. The Java 9 classpath (backed by the jrt:/ virtual system, if available) classesInPath(javaBootClassPath), // 1. The Java bootstrap class path. contentsOfDirsInPath(javaExtDirs), // 2. The Java extension class path. classesInExpandedPath(javaUserClassPath), // 3. The Java application class path. diff --git a/src/library/scala/util/Properties.scala b/src/library/scala/util/Properties.scala index 7b21351cf6..29a635fcbe 100644 --- a/src/library/scala/util/Properties.scala +++ b/src/library/scala/util/Properties.scala @@ -154,6 +154,12 @@ private[scala] trait PropertiesTrait { /* Some runtime values. */ private[scala] def isAvian = javaVmName contains "Avian" + private[scala] def coloredOutputEnabled: Boolean = propOrElse("scala.color", "auto") match { + case "auto" => System.console() != null && !isWin + case a if a.toLowerCase() == "true" => true + case _ => false + } + // This is looking for javac, tools.jar, etc. // Tries JDK_HOME first, then the more common but likely jre JAVA_HOME, // and finally the system property based javaHome. diff --git a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala index 0ef52213e5..1cdefff2e9 100644 --- a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala +++ b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala @@ -33,6 +33,9 @@ trait BaseTypeSeqs { protected def newBaseTypeSeq(parents: List[Type], elems: Array[Type]) = new BaseTypeSeq(parents, elems) + protected def newMappedBaseTypeSeq(orig: BaseTypeSeq, f: Type => Type) = + new MappedBaseTypeSeq(orig, f) + /** Note: constructor is protected to force everyone to use the factory method newBaseTypeSeq instead. * This is necessary because when run from reflection every base type sequence needs to have a * SynchronizedBaseTypeSeq as mixin. @@ -125,7 +128,7 @@ trait BaseTypeSeqs { newBaseTypeSeq(parents, arr) } - def lateMap(f: Type => Type): BaseTypeSeq = new MappedBaseTypeSeq(this, f) + def lateMap(f: Type => Type): BaseTypeSeq = newMappedBaseTypeSeq(this, f) def exists(p: Type => Boolean): Boolean = elems exists p diff --git a/src/reflect/scala/reflect/internal/Names.scala b/src/reflect/scala/reflect/internal/Names.scala index 9d39ef8b42..055f7c9d5b 100644 --- a/src/reflect/scala/reflect/internal/Names.scala +++ b/src/reflect/scala/reflect/internal/Names.scala @@ -296,11 +296,13 @@ trait Names extends api.Names { */ final def pos(s: String, start: Int): Int = { var i = pos(s.charAt(0), start) - while (i + s.length() <= len) { + val sLen = s.length() + if (sLen == 1) return i + while (i + sLen <= len) { var j = 1 while (s.charAt(j) == chrs(index + i + j)) { j += 1 - if (j == s.length()) return i + if (j == sLen) return i } i = pos(s.charAt(0), i + 1) } diff --git a/src/reflect/scala/reflect/internal/Scopes.scala b/src/reflect/scala/reflect/internal/Scopes.scala index 51fb31d36d..0435a2c1cf 100644 --- a/src/reflect/scala/reflect/internal/Scopes.scala +++ b/src/reflect/scala/reflect/internal/Scopes.scala @@ -291,25 +291,6 @@ trait Scopes extends api.Scopes { self: SymbolTable => null } - final def lookupCompanion(original: Symbol): Symbol = { - lookupSymbolEntry(original) match { - case null => - case entry => - var e = lookupEntry(original.name.companionName) - while (e != null) { - // 1) Must be owned by the same Scope, to ensure that in - // `{ class C; { ...; object C } }`, the class is not seen as a companion of the object. - // 2) Must be a class and module symbol, so that `{ class C; def C }` or `{ type T; object T }` are not companions. - def isClassAndModule(sym1: Symbol, sym2: Symbol) = sym1.isClass && sym2.isModule - if ((e.owner eq entry.owner) && (isClassAndModule(original, e.sym) || isClassAndModule(e.sym, original))) { - return if (e.sym.isCoDefinedWith(original)) e.sym else NoSymbol - } - e = lookupNextEntry(e) - } - } - NoSymbol - } - /** lookup a symbol entry matching given name. * @note from Martin: I believe this is a hotspot or will be one * in future versions of the type system. I have reverted the previous @@ -345,6 +326,20 @@ trait Scopes extends api.Scopes { self: SymbolTable => e } + final def lookupNameInSameScopeAs(original: Symbol, companionName: Name): Symbol = { + lookupSymbolEntry(original) match { + case null => + case entry => + var e = lookupEntry(companionName) + while (e != null) { + if (e.owner eq entry.owner) return e.sym + e = lookupNextEntry(e) + } + } + NoSymbol + } + + /** TODO - we can test this more efficiently than checking isSubScope * in both directions. However the size test might be enough to quickly * rule out most failures. diff --git a/src/reflect/scala/reflect/internal/TypeDebugging.scala b/src/reflect/scala/reflect/internal/TypeDebugging.scala index e9050b4e33..58359e66d9 100644 --- a/src/reflect/scala/reflect/internal/TypeDebugging.scala +++ b/src/reflect/scala/reflect/internal/TypeDebugging.scala @@ -59,7 +59,7 @@ trait TypeDebugging { object typeDebug { import scala.Console._ - private val colorsOk = sys.props contains "scala.color" + private val colorsOk = scala.util.Properties.coloredOutputEnabled private def inColor(s: String, color: String) = if (colorsOk && s != "") color + s + RESET else s private def inBold(s: String, color: String) = if (colorsOk && s != "") color + BOLD + s + RESET else s diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index b46f071717..dc12ef9352 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -967,6 +967,8 @@ trait Types */ def directObjectString = safeToString + def nameAndArgsString = typeSymbol.name.toString + /** A test whether a type contains any unification type variables. * Overridden with custom logic except where trivially true. */ @@ -2321,6 +2323,8 @@ trait Types private def preString = if (needsPreString) pre.prefixString else "" private def argsString = if (args.isEmpty) "" else args.mkString("[", ",", "]") + override def nameAndArgsString = typeSymbol.name.toString + argsString + private def refinementDecls = fullyInitializeScope(decls) filter (sym => sym.isPossibleInRefinement && sym.isPublic) private def refinementString = ( if (sym.isStructuralRefinement) @@ -2728,6 +2732,19 @@ trait Types arg.toString } + override def nameAndArgsString: String = underlying match { + case TypeRef(_, sym, args) if !settings.debug && isRepresentableWithWildcards => + sym.name + wildcardArgsString(quantified.toSet, args).mkString("[", ",", "]") + case TypeRef(_, sym, args) => + sym.name + args.mkString("[", ",", "]") + existentialClauses + case _ => underlying.typeSymbol.name + existentialClauses + } + + private def existentialClauses = { + val str = quantified map (_.existentialToString) mkString (" forSome { ", "; ", " }") + if (settings.explaintypes) "(" + str + ")" else str + } + /** An existential can only be printed with wildcards if: * - the underlying type is a typeref * - every quantified variable appears at most once as a type argument and @@ -2746,7 +2763,7 @@ trait Types tpe.typeSymbol.isRefinementClass && (tpe.parents exists isQuantified) } val (wildcardArgs, otherArgs) = args partition (arg => qset contains arg.typeSymbol) - wildcardArgs.distinct == wildcardArgs && + wildcardArgs.toSet.size == wildcardArgs.size && !(otherArgs exists (arg => isQuantified(arg))) && !(wildcardArgs exists (arg => isQuantified(arg.typeSymbol.info.bounds))) && !(qset contains sym) && @@ -2756,17 +2773,13 @@ trait Types } override def safeToString: String = { - def clauses = { - val str = quantified map (_.existentialToString) mkString (" forSome { ", "; ", " }") - if (settings.explaintypes) "(" + str + ")" else str - } underlying match { case TypeRef(pre, sym, args) if !settings.debug && isRepresentableWithWildcards => "" + TypeRef(pre, sym, Nil) + wildcardArgsString(quantified.toSet, args).mkString("[", ", ", "]") case MethodType(_, _) | NullaryMethodType(_) | PolyType(_, _) => - "(" + underlying + ")" + clauses + "(" + underlying + ")" + existentialClauses case _ => - "" + underlying + clauses + "" + underlying + existentialClauses } } diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala index 24f8aa88e6..07ae71538c 100644 --- a/src/reflect/scala/reflect/internal/transform/Erasure.scala +++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala @@ -161,6 +161,11 @@ trait Erasure { } if (newParents eq parents) tp else ClassInfoType(newParents, decls, clazz) + + // can happen while this map is being used before erasure (e.g. when reasoning about sam types) + // the regular mapOver will cause a class cast exception because TypeBounds don't erase to TypeBounds + case _: BoundedWildcardType => tp // skip + case _ => mapOver(tp) } diff --git a/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala b/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala index 3cede1b3c5..49ab0cb30e 100644 --- a/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala +++ b/src/reflect/scala/reflect/internal/util/AbstractFileClassLoader.scala @@ -92,7 +92,7 @@ class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) } } - private val packages = mutable.Map[String, Package]() + private[this] val packages = mutable.Map[String, Package]() override def definePackage(name: String, specTitle: String, specVersion: String, specVendor: String, implTitle: String, implVersion: String, implVendor: String, sealBase: URL): Package = { throw new UnsupportedOperationException() diff --git a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala index f0d96e0fd6..eadafc8abb 100644 --- a/src/reflect/scala/reflect/runtime/SynchronizedOps.scala +++ b/src/reflect/scala/reflect/runtime/SynchronizedOps.scala @@ -18,6 +18,12 @@ private[reflect] trait SynchronizedOps extends internal.SymbolTable if (elems.exists(_.isInstanceOf[RefinedType])) new BaseTypeSeq(parents, elems) with SynchronizedBaseTypeSeq else new BaseTypeSeq(parents, elems) + override protected def newMappedBaseTypeSeq(orig: BaseTypeSeq, f: Type => Type) = + // MappedBaseTypeSeq's are used rarely enough that we unconditionally mixin the synchronized + // wrapper, rather than doing this conditionally. A previous attempt to do that broke the "late" + // part of the "lateMap" contract in inspecting the mapped elements. + new MappedBaseTypeSeq(orig, f) with SynchronizedBaseTypeSeq + trait SynchronizedBaseTypeSeq extends BaseTypeSeq { override def apply(i: Int): Type = gilSynchronized { super.apply(i) } override def rawElem(i: Int) = gilSynchronized { super.rawElem(i) } @@ -28,11 +34,6 @@ private[reflect] trait SynchronizedOps extends internal.SymbolTable override def exists(p: Type => Boolean): Boolean = gilSynchronized { super.exists(p) } override lazy val maxDepth = gilSynchronized { maxDepthOfElems } override def toString = gilSynchronized { super.toString } - - override def lateMap(f: Type => Type): BaseTypeSeq = - // only need to synchronize BaseTypeSeqs if they contain refined types - if (map(f).toList.exists(_.isInstanceOf[RefinedType])) new MappedBaseTypeSeq(this, f) with SynchronizedBaseTypeSeq - else new MappedBaseTypeSeq(this, f) } // Scopes diff --git a/src/repl/scala/tools/nsc/interpreter/ExprTyper.scala b/src/repl/scala/tools/nsc/interpreter/ExprTyper.scala index 01e3a90950..f68705211f 100644 --- a/src/repl/scala/tools/nsc/interpreter/ExprTyper.scala +++ b/src/repl/scala/tools/nsc/interpreter/ExprTyper.scala @@ -13,6 +13,12 @@ trait ExprTyper { import global.{ reporter => _, Import => _, _ } import naming.freshInternalVarName + private def doInterpret(code: String): IR.Result = { + // interpret/interpretSynthetic may change the phase, which would have unintended effects on types. + val savedPhase = phase + try interpretSynthetic(code) finally phase = savedPhase + } + def symbolOfLine(code: String): Symbol = { def asExpr(): Symbol = { val name = freshInternalVarName() @@ -21,7 +27,7 @@ trait ExprTyper { // behind a def and strip the NullaryMethodType which wraps the expr. val line = "def " + name + " = " + code - interpretSynthetic(line) match { + doInterpret(line) match { case IR.Success => val sym0 = symbolOfTerm(name) // drop NullaryMethodType @@ -32,7 +38,7 @@ trait ExprTyper { def asDefn(): Symbol = { val old = repl.definedSymbolList.toSet - interpretSynthetic(code) match { + doInterpret(code) match { case IR.Success => repl.definedSymbolList filterNot old match { case Nil => NoSymbol @@ -43,7 +49,7 @@ trait ExprTyper { } } def asError(): Symbol = { - interpretSynthetic(code) + doInterpret(code) NoSymbol } beSilentDuring(asExpr()) orElse beSilentDuring(asDefn()) orElse asError() @@ -72,7 +78,7 @@ trait ExprTyper { def asProperType(): Option[Type] = { val name = freshInternalVarName() val line = "def %s: %s = ???" format (name, typeString) - interpretSynthetic(line) match { + doInterpret(line) match { case IR.Success => val sym0 = symbolOfTerm(name) Some(sym0.asMethod.returnType) diff --git a/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala index d6c0dafaf2..f455e71476 100644 --- a/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala +++ b/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala @@ -213,29 +213,40 @@ trait MemberHandlers { class ImportHandler(imp: Import) extends MemberHandler(imp) { val Import(expr, selectors) = imp + def targetType = intp.global.rootMirror.getModuleIfDefined("" + expr) match { case NoSymbol => intp.typeOfExpression("" + expr) - case sym => sym.thisType + case sym => sym.tpe } - private def importableTargetMembers = importableMembers(targetType).toList - // wildcard imports, e.g. import foo._ - private def selectorWild = selectors filter (_.name == nme.USCOREkw) - // renamed imports, e.g. import foo.{ bar => baz } - private def selectorRenames = selectors map (_.rename) filterNot (_ == null) + + private def isFlattenedSymbol(sym: Symbol) = + sym.owner.isPackageClass && + sym.name.containsName(nme.NAME_JOIN_STRING) && + sym.owner.info.member(sym.name.take(sym.name.indexOf(nme.NAME_JOIN_STRING))) != NoSymbol + + private def importableTargetMembers = + importableMembers(exitingTyper(targetType)).filterNot(isFlattenedSymbol).toList + + // non-wildcard imports + private def individualSelectors = selectors filter analyzer.isIndividualImport /** Whether this import includes a wildcard import */ - val importsWildcard = selectorWild.nonEmpty + val importsWildcard = selectors exists analyzer.isWildcardImport def implicitSymbols = importedSymbols filter (_.isImplicit) def importedSymbols = individualSymbols ++ wildcardSymbols - private val selectorNames = selectorRenames filterNot (_ == nme.USCOREkw) flatMap (_.bothNames) toSet - lazy val individualSymbols: List[Symbol] = exitingTyper(importableTargetMembers filter (m => selectorNames(m.name))) - lazy val wildcardSymbols: List[Symbol] = exitingTyper(if (importsWildcard) importableTargetMembers else Nil) + lazy val importableSymbolsWithRenames = { + val selectorRenameMap = individualSelectors.flatMap(x => x.name.bothNames zip x.rename.bothNames).toMap + importableTargetMembers flatMap (m => selectorRenameMap.get(m.name) map (m -> _)) + } + + lazy val individualSymbols: List[Symbol] = importableSymbolsWithRenames map (_._1) + lazy val wildcardSymbols: List[Symbol] = if (importsWildcard) importableTargetMembers else Nil /** Complete list of names imported by a wildcard */ lazy val wildcardNames: List[Name] = wildcardSymbols map (_.name) - lazy val individualNames: List[Name] = individualSymbols map (_.name) + lazy val individualNames: List[Name] = importableSymbolsWithRenames map (_._2) /** The names imported by this statement */ override lazy val importedNames: List[Name] = wildcardNames ++ individualNames diff --git a/src/repl/scala/tools/nsc/interpreter/ReplProps.scala b/src/repl/scala/tools/nsc/interpreter/ReplProps.scala index f3115d9800..a86069f198 100644 --- a/src/repl/scala/tools/nsc/interpreter/ReplProps.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplProps.scala @@ -17,7 +17,7 @@ class ReplProps { private def int(name: String) = Prop[Int](name) // This property is used in TypeDebugging. Let's recycle it. - val colorOk = bool("scala.color") + val colorOk = Properties.coloredOutputEnabled val info = bool("scala.repl.info") val debug = bool("scala.repl.debug") diff --git a/src/repl/scala/tools/nsc/interpreter/Scripted.scala b/src/repl/scala/tools/nsc/interpreter/Scripted.scala index 6aef486957..8d87d98e53 100644 --- a/src/repl/scala/tools/nsc/interpreter/Scripted.scala +++ b/src/repl/scala/tools/nsc/interpreter/Scripted.scala @@ -331,7 +331,7 @@ class WriterOutputStream(writer: Writer) extends OutputStream { byteBuffer.flip() val result = decoder.decode(byteBuffer, charBuffer, /*eoi=*/ false) if (byteBuffer.remaining == 0) byteBuffer.clear() - if (charBuffer.position > 0) { + if (charBuffer.position() > 0) { charBuffer.flip() writer write charBuffer.toString charBuffer.clear() diff --git a/src/scaladoc/scala/tools/nsc/doc/base/comment/Body.scala b/src/scaladoc/scala/tools/nsc/doc/base/comment/Body.scala index 7f4e52e88d..2524fb75fb 100644 --- a/src/scaladoc/scala/tools/nsc/doc/base/comment/Body.scala +++ b/src/scaladoc/scala/tools/nsc/doc/base/comment/Body.scala @@ -74,9 +74,8 @@ object EntityLink { def unapply(el: EntityLink): Option[(Inline, LinkTo)] = Some((el.title, el.link)) } final case class HtmlTag(data: String) extends Inline { - private val Pattern = """(?ms)\A<(/?)(.*?)[\s>].*\z""".r private val (isEnd, tagName) = data match { - case Pattern(s1, s2) => + case HtmlTag.Pattern(s1, s2) => (! s1.isEmpty, Some(s2.toLowerCase)) case _ => (false, None) @@ -86,8 +85,13 @@ final case class HtmlTag(data: String) extends Inline { isEnd && tagName == open.tagName } + def close = tagName collect { + case name if !HtmlTag.TagsNotToClose(name) && !data.endsWith(s"</$name>") => HtmlTag(s"</$name>") + } +} +object HtmlTag { + private val Pattern = """(?ms)\A<(/?)(.*?)[\s>].*\z""".r private val TagsNotToClose = Set("br", "img") - def close = tagName collect { case name if !TagsNotToClose(name) => HtmlTag(s"</$name>") } } /** The summary of a comment, usually its first sentence. There must be exactly one summary per body. */ diff --git a/src/scaladoc/scala/tools/nsc/doc/html/Page.scala b/src/scaladoc/scala/tools/nsc/doc/html/Page.scala index c720c4939f..fb4b3c0273 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/Page.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/Page.scala @@ -7,7 +7,7 @@ package scala package tools.nsc.doc.html import scala.tools.nsc.doc.model._ -import scala.tools.nsc.doc.base.comment +import scala.tools.nsc.doc.base.comment._ import java.io.{FileOutputStream, File} import scala.reflect.NameTransformer import java.nio.channels.Channels @@ -106,16 +106,21 @@ abstract class Page { case dtpl: DocTemplateEntity => dtpl.companion.isDefined case _ => false } +} - protected def inlineToStr(inl: comment.Inline): String = inl match { - case comment.Chain(items) => items flatMap (inlineToStr(_)) mkString "" - case comment.Italic(in) => inlineToStr(in) - case comment.Bold(in) => inlineToStr(in) - case comment.Underline(in) => inlineToStr(in) - case comment.Monospace(in) => inlineToStr(in) - case comment.Text(text) => text - case comment.Summary(in) => inlineToStr(in) - case comment.EntityLink(comment.Text(text), _) => text - case _ => inl.toString +object Page { + def inlineToStr(inl: Inline): String = inl match { + case Chain(items) => items flatMap (inlineToStr(_)) mkString "" + case Italic(in) => inlineToStr(in) + case Bold(in) => inlineToStr(in) + case Underline(in) => inlineToStr(in) + case Superscript(in) => inlineToStr(in) + case Subscript(in) => inlineToStr(in) + case Link(raw, title) => inlineToStr(title) + case Monospace(in) => inlineToStr(in) + case Text(text) => text + case Summary(in) => inlineToStr(in) + case HtmlTag(tag) => "<[^>]*>".r.replaceAllIn(tag, "") + case EntityLink(Text(text), _) => text } } diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala index 54bf42bbd5..fb2bf5049f 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala @@ -90,13 +90,13 @@ trait EntityPage extends HtmlPage { mbr match { case dtpl: DocTemplateEntity => dtpl.companion.fold(<span class="separator"></span>) { c: DocTemplateEntity => - <a class="object" href={relativeLinkTo(c)} title={c.comment.fold("")(com => inlineToStr(com.short))}></a> + <a class="object" href={relativeLinkTo(c)} title={c.comment.fold("")(com => Page.inlineToStr(com.short))}></a> } case _ => <span class="separator"></span> } } - <a class={mbr.kind} href={relativeLinkTo(mbr)} title={mbr.comment.fold("")(com => inlineToStr(com.short))}></a> - <a href={relativeLinkTo(mbr)} title={mbr.comment.fold("")(com => inlineToStr(com.short))}> + <a class={mbr.kind} href={relativeLinkTo(mbr)} title={mbr.comment.fold("")(com => Page.inlineToStr(com.short))}></a> + <a href={relativeLinkTo(mbr)} title={mbr.comment.fold("")(com => Page.inlineToStr(com.short))}> {mbr.name} </a> </li> @@ -897,7 +897,7 @@ trait EntityPage extends HtmlPage { } } if (!nameLink.isEmpty) - <a title={mbr.comment.fold("")(c => inlineToStr(c.short))} href={nameLink}> + <a title={mbr.comment.fold("")(c => Page.inlineToStr(c.short))} href={nameLink}> {nameHtml} </a> else nameHtml @@ -1065,7 +1065,7 @@ trait EntityPage extends HtmlPage { body.blocks flatMap (blockToStr(_)) mkString "" private def blockToStr(block: comment.Block): String = block match { - case comment.Paragraph(in) => inlineToStr(in) + case comment.Paragraph(in) => Page.inlineToStr(in) case _ => block.toString } diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/IndexScript.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/IndexScript.scala index 8f58a7b845..28304e76c7 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/IndexScript.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/page/IndexScript.scala @@ -87,7 +87,7 @@ class IndexScript(universe: doc.Universe) extends Page { /** Gets the short description i.e. the first sentence of the docstring */ def shortDesc(mbr: MemberEntity): String = mbr.comment.fold("") { c => - inlineToStr(c.short).replaceAll("\n", "") + Page.inlineToStr(c.short).replaceAll("\n", "") } /** Returns the json representation of the supplied members */ diff --git a/test/files/neg/no-predef.check b/test/files/neg/no-predef.check index a63d8c5ba5..f5c2e82fe1 100644 --- a/test/files/neg/no-predef.check +++ b/test/files/neg/no-predef.check @@ -1,11 +1,11 @@ no-predef.scala:2: error: type mismatch; - found : scala.Long(5L) - required: java.lang.Long + found : Long (in scala) + required: Long (in java.lang) def f1 = 5L: java.lang.Long ^ no-predef.scala:3: error: type mismatch; - found : java.lang.Long - required: scala.Long + found : Long (in java.lang) + required: Long (in scala) def f2 = new java.lang.Long(5) : Long ^ no-predef.scala:4: error: value map is not a member of String diff --git a/test/files/neg/t10207.check b/test/files/neg/t10207.check new file mode 100755 index 0000000000..3330db44a5 --- /dev/null +++ b/test/files/neg/t10207.check @@ -0,0 +1,4 @@ +t10207.scala:14: error: too many arguments (2) for method apply: (key: Int)scala.collection.mutable.ArrayBuffer[String] in trait MapLike + m(1, (_ => empty)) ++= AB("eins", "uno") + ^ +one error found diff --git a/test/files/neg/t10207.scala b/test/files/neg/t10207.scala new file mode 100644 index 0000000000..2dfc5d75c9 --- /dev/null +++ b/test/files/neg/t10207.scala @@ -0,0 +1,16 @@ + +// Was: +// warning: an unexpected type representation reached the compiler backend +// Now: +// error: too many arguments (2) for method apply: (key: Int)scala.collection.mutable.ArrayBuffer[String] in trait MapLike + +trait Test { + import collection.mutable.{Map=>MMap, ArrayBuffer=>AB} + + val m = MMap((1 -> AB("one"))) + + val empty = AB[String]() + + m(1, (_ => empty)) ++= AB("eins", "uno") +} + diff --git a/test/files/neg/t2102.check b/test/files/neg/t2102.check index b4f91a5319..6f70839d22 100644 --- a/test/files/neg/t2102.check +++ b/test/files/neg/t2102.check @@ -1,6 +1,6 @@ t2102.scala:2: error: type mismatch; - found : java.util.Iterator[Int] - required: scala.collection.Iterator[_] + found : Iterator[Int] (in java.util) + required: Iterator[_] (in scala.collection) val x: Iterator[_] = new java.util.ArrayList[Int]().iterator ^ one error found diff --git a/test/files/neg/type-diagnostics.check b/test/files/neg/type-diagnostics.check index c5e6dec3f8..fd327bcb66 100644 --- a/test/files/neg/type-diagnostics.check +++ b/test/files/neg/type-diagnostics.check @@ -1,6 +1,6 @@ type-diagnostics.scala:4: error: type mismatch; - found : scala.collection.Set[String] - required: scala.collection.immutable.Set[String] + found : Set[String] (in scala.collection) + required: Set[String] (in scala.collection.immutable) def f = Calculator("Hello", binding.keySet: collection.Set[String]) ^ type-diagnostics.scala:13: error: type mismatch; diff --git a/test/files/pos/overloaded_ho_fun.scala b/test/files/pos/overloaded_ho_fun.scala index 2699ad35f8..17176715f0 100644 --- a/test/files/pos/overloaded_ho_fun.scala +++ b/test/files/pos/overloaded_ho_fun.scala @@ -49,3 +49,18 @@ object sorting { // def andThen[C](g: Bijection[B, C]): Bijection[A, C] = ??? // def compose[T](g: Bijection[T, A]) = g andThen this // } + +object SI10194 { + trait X[A] { + def map[B](f: A => B): Unit + } + + trait Y[A] extends X[A] { + def map[B](f: A => B)(implicit ordering: Ordering[B]): Unit + } + + trait Z[A] extends Y[A] + + (null: Y[Int]).map(x => x.toString) // compiled + (null: Z[Int]).map(x => x.toString) // didn't compile +} diff --git a/test/files/pos/sam_erasure_boundedwild.scala b/test/files/pos/sam_erasure_boundedwild.scala new file mode 100644 index 0000000000..1ec27e0ea4 --- /dev/null +++ b/test/files/pos/sam_erasure_boundedwild.scala @@ -0,0 +1,11 @@ +class Test { + trait Q[T] { + def toArray[T](x: Array[T]): Array[T] + def toArray(): Array[T] + } + + def crashTyper: Array[_] = { + val x : Q[_] = ??? + x.toArray // crashes while doing overload resolution + } +}
\ No newline at end of file diff --git a/test/files/run/elidable-opt.check b/test/files/run/elidable-opt.check index 88cf98e0d1..969b9a420a 100644 --- a/test/files/run/elidable-opt.check +++ b/test/files/run/elidable-opt.check @@ -11,4 +11,4 @@ false 0 0.0 0.0 -null + diff --git a/test/files/run/elidable.check b/test/files/run/elidable.check index 88cf98e0d1..969b9a420a 100644 --- a/test/files/run/elidable.check +++ b/test/files/run/elidable.check @@ -11,4 +11,4 @@ false 0 0.0 0.0 -null + diff --git a/test/files/run/elidable.scala b/test/files/run/elidable.scala index 02785972bb..fed1c7b392 100644 --- a/test/files/run/elidable.scala +++ b/test/files/run/elidable.scala @@ -3,31 +3,36 @@ import elidable._ // runs -Xelide-below WARNING or 900 +object Fail { + def fail(msg: String): Unit = throw new IllegalStateException(s"Expected failure: $msg") +} +import Fail.fail + trait T { @elidable(FINEST) def f1() @elidable(SEVERE) def f2() - @elidable(FINEST) def f3() = assert(false, "Should have been elided.") + @elidable(FINEST) def f3() = fail("Should have been elided.") def f4() } class C extends T { def f1() = println("Good for me, I was not elided. C.f1") def f2() = println("Good for me, I was not elided. C.f2") - @elidable(FINEST) def f4() = assert(false, "Should have been elided.") + @elidable(FINEST) def f4() = fail("Should have been elided.") } object O { - @elidable(FINEST) def f1() = assert(false, "Should have been elided.") - @elidable(INFO) def f2() = assert(false, "Should have been elided.") + @elidable(FINEST) def f1() = fail("Should have been elided.") + @elidable(INFO) def f2() = fail("Should have been elided.") @elidable(SEVERE) def f3() = println("Good for me, I was not elided. O.f3") - @elidable(INFO) def f4 = assert(false, "Should have been elided (no parens).") + @elidable(INFO) def f4 = fail("Should have been elided (no parens).") } object Test { - @elidable(FINEST) def f1() = assert(false, "Should have been elided.") - @elidable(INFO) def f2() = assert(false, "Should have been elided.") + @elidable(FINEST) def f1() = fail("Should have been elided.") + @elidable(INFO) def f2() = fail("Should have been elided.") @elidable(SEVERE) def f3() = println("Good for me, I was not elided. Test.f3") - @elidable(INFO) def f4 = assert(false, "Should have been elided (no parens).") + @elidable(INFO) def f4 = fail("Should have been elided (no parens).") @elidable(FINEST) def f5() = {} @elidable(FINEST) def f6() = true @@ -38,12 +43,12 @@ object Test { @elidable(FINEST) def fb() = 1l @elidable(FINEST) def fc() = 1.0f @elidable(FINEST) def fd() = 1.0 - @elidable(FINEST) def fe() = "s" + @elidable(FINEST) def fe() = { fail("Should have been elided to empty string.") ; "hello, world" } /* variable elisions? see test/files/neg/t10068.scala - @elidable(INFO) val goner1: Int = { assert(false, "Should have been elided.") ; 42 } - @elidable(INFO) lazy val goner2: Int = { assert(false, "Should have been elided.") ; 42 } - @elidable(INFO) var goner3: Int = { assert(false, "Should have been elided.") ; 42 } + @elidable(INFO) val goner1: Int = { fail("Should have been elided.") ; 42 } + @elidable(INFO) lazy val goner2: Int = { fail("Should have been elided.") ; 42 } + @elidable(INFO) var goner3: Int = { fail("Should have been elided.") ; 42 } @elidable(INFO) var goner4: Nothing = _ */ @@ -74,6 +79,19 @@ object Test { println(fc()) println(fd()) println(fe()) + if (!fe().isEmpty) fail(s"Not empty: [${fe()}]") +/* +() +false +0 +0 +0 +0 +0 +0.0 +0.0 + // was: null +*/ // this one won't show up in the output because a call to f1 is elidable when accessed through T (c:T).f1() diff --git a/test/files/run/literals.scala b/test/files/run/literals.scala index 25501123b5..a7962e5cd9 100644 --- a/test/files/run/literals.scala +++ b/test/files/run/literals.scala @@ -6,7 +6,7 @@ object Test { - /* I add a couple of Unicode identifier tests here temporarily */ + /* I add a couple of Unicode identifier tests here "temporarily" */ def \u03b1\u03c1\u03b5\u03c4\u03b7 = "alpha rho epsilon tau eta" @@ -80,6 +80,9 @@ object Test { check_success("1e1f == 10.0f", 1e1f, 10.0f) check_success(".3f == 0.3f", .3f, 0.3f) check_success("0f == 0.0f", 0f, 0.0f) + check_success("0f == -0.000000000000000000e+00f", 0f, -0.000000000000000000e+00f) + check_success("0f == -0.000000000000000000e+00F", 0f, -0.000000000000000000e+00F) + check_success("0f == -0.0000000000000000e14f", 0f, -0.0000000000000000e14f) check_success("01.23f == 1.23f", 01.23f, 1.23f) check_success("3.14f == 3.14f", 3.14f, 3.14f) check_success("6.022e23f == 6.022e23f", 6.022e23f, 6.022e23f) @@ -96,6 +99,11 @@ object Test { check_success(".3 == 0.3", .3, 0.3) check_success("0.0 == 0.0", 0.0, 0.0) check_success("0d == 0.0", 0d, 0.0) + check_success("0d == 0.000000000000000000e+00d", 0d, 0.000000000000000000e+00d) + check_success("0d == -0.000000000000000000e+00d", 0d, -0.000000000000000000e+00d) + check_success("0d == -0.000000000000000000e+00D", 0d, -0.000000000000000000e+00D) + check_success("0.0 == 0.000000000000000000e+00", 0.0, 0.000000000000000000e+00) + check_success("0.0 == -0.000000000000000000e+00", 0.0, -0.000000000000000000e+00) check_success("01.23 == 1.23", 01.23, 1.23) check_success("01.23d == 1.23d", 01.23d, 1.23d) check_success("3.14 == 3.14", 3.14, 3.14) diff --git a/test/files/run/t10026.check b/test/files/run/t10026.check new file mode 100644 index 0000000000..15a62794a9 --- /dev/null +++ b/test/files/run/t10026.check @@ -0,0 +1 @@ +List(1, 2, 3) diff --git a/test/files/run/t10026.scala b/test/files/run/t10026.scala new file mode 100644 index 0000000000..a56840c8c2 --- /dev/null +++ b/test/files/run/t10026.scala @@ -0,0 +1,11 @@ +import scala.reflect.runtime.universe +import scala.tools.reflect.ToolBox + +object Test { + def main(args: Array[String]): Unit = { + val classloader = getClass.getClassLoader + val toolbox = universe.runtimeMirror(classloader).mkToolBox() + println(toolbox.compile(toolbox.parse("Array(1, 2, 3).toList")).apply()) + } +} + diff --git a/test/files/run/t9880-9881.check b/test/files/run/t9880-9881.check new file mode 100644 index 0000000000..36513e249a --- /dev/null +++ b/test/files/run/t9880-9881.check @@ -0,0 +1,36 @@ + +scala> // import in various ways + +scala> import java.util.Date +import java.util.Date + +scala> import scala.util._ +import scala.util._ + +scala> import scala.reflect.runtime.{universe => ru} +import scala.reflect.runtime.{universe=>ru} + +scala> import ru.TypeTag +import ru.TypeTag + +scala> + +scala> // show the imports + +scala> :imports + 1) import java.lang._ (...) + 2) import scala._ (...) + 3) import scala.Predef._ (...) + 4) import java.util.Date (...) + 5) import scala.util._ (...) + 6) import scala.reflect.runtime.{universe=>ru} (...) + 7) import ru.TypeTag (...) + +scala> + +scala> // should be able to define this class with the imports above + +scala> class C[T](date: Date, rand: Random, typeTag: TypeTag[T]) +defined class C + +scala> :quit diff --git a/test/files/run/t9880-9881.scala b/test/files/run/t9880-9881.scala new file mode 100644 index 0000000000..0268c8c32c --- /dev/null +++ b/test/files/run/t9880-9881.scala @@ -0,0 +1,29 @@ +import scala.tools.partest.ReplTest +import scala.tools.nsc.Settings + +object Test extends ReplTest { + + override def transformSettings(s: Settings): Settings = { + s.Yreplclassbased.value = true + s + } + + lazy val normalizeRegex = """(import\s.*)\(.*\)""".r + + override def normalize(s: String): String = normalizeRegex.replaceFirstIn(s, "$1(...)") + + def code = + """ + |// import in various ways + |import java.util.Date + |import scala.util._ + |import scala.reflect.runtime.{universe => ru} + |import ru.TypeTag + | + |// show the imports + |:imports + | + |// should be able to define this class with the imports above + |class C[T](date: Date, rand: Random, typeTag: TypeTag[T]) + """.stripMargin +} diff --git a/test/junit/scala/reflect/internal/NamesTest.scala b/test/junit/scala/reflect/internal/NamesTest.scala index 549c10abed..d6182e7cca 100644 --- a/test/junit/scala/reflect/internal/NamesTest.scala +++ b/test/junit/scala/reflect/internal/NamesTest.scala @@ -92,4 +92,29 @@ class NamesTest { assert(h1 string_== h2) assert(h1 string_== h1y) } + + @Test + def pos(): Unit = { + def check(nameString: String, sub: String) = { + val name = TermName(nameString) + val javaResult = name.toString.indexOf(sub) match { case -1 => name.length case x => x } + val nameResult = name.pos(sub) + assertEquals(javaResult, nameResult) + if (sub.length == 1) { + val nameResultChar = name.pos(sub.head) + assertEquals(javaResult, nameResultChar) + } + } + + check("a", "a") // was "String index out of range: 1 + check("a", "b") + check("a", "ab") + check("a", "ba") + check("ab", "a") + check("ab", "b") + check("ab", "ab") + check("ab", "ba") + check("", "x") + check("", "xy") + } } diff --git a/test/junit/scala/sys/process/PipedProcessTest.scala b/test/junit/scala/sys/process/PipedProcessTest.scala index 53f053e9aa..3f403dbe75 100644 --- a/test/junit/scala/sys/process/PipedProcessTest.scala +++ b/test/junit/scala/sys/process/PipedProcessTest.scala @@ -7,13 +7,19 @@ import java.io.{InputStream, OutputStream, PipedInputStream, PipedOutputStream, ByteArrayOutputStream, IOException, Closeable} import java.lang.reflect.InvocationTargetException import scala.concurrent.{Await, Future} -import scala.concurrent.duration.{Duration, SECONDS} import scala.concurrent.ExecutionContext.Implicits.global import scala.util.control.Exception.ignoring -// Each test normally ends in a moment, but for failure cases, waits until one second. +// Each test normally ends in a moment, but for failure cases, waits two seconds. // SI-7350, SI-8768 +// one second wasn't always enough -- +// https://github.com/scala/scala-dev/issues/313 +object TestDuration { + import scala.concurrent.duration.{Duration, SECONDS} + val Standard = Duration(2, SECONDS) +} + @RunWith(classOf[JUnit4]) class PipedProcessTest { class ProcessMock(error: Boolean) extends Process { @@ -81,7 +87,7 @@ class PipedProcessTest { val f = Future { p.callRunAndExitValue(source, sink) } - Await.result(f, Duration(1, SECONDS)) + Await.result(f, TestDuration.Standard) assert(source.releaseCount == 0) assert(sink.releaseCount == 0) assert(a.destroyCount == 0) @@ -102,7 +108,7 @@ class PipedProcessTest { p.callRunAndExitValue(source, sink) } } - Await.result(f, Duration(1, SECONDS)) + Await.result(f, TestDuration.Standard) assert(source.releaseCount == 1) assert(sink.releaseCount == 1) assert(a.destroyCount == 0) @@ -123,7 +129,7 @@ class PipedProcessTest { p.callRunAndExitValue(source, sink) } } - Await.result(f, Duration(1, SECONDS)) + Await.result(f, TestDuration.Standard) assert(source.releaseCount == 1) assert(sink.releaseCount == 1) assert(a.destroyCount == 0) @@ -142,7 +148,7 @@ class PipedProcessTest { val f = Future { p.callRunAndExitValue(source, sink) } - Await.result(f, Duration(1, SECONDS)) + Await.result(f, TestDuration.Standard) assert(source.releaseCount == 1) assert(sink.releaseCount == 1) assert(a.destroyCount == 1) @@ -161,7 +167,7 @@ class PipedProcessTest { val f = Future { p.callRunAndExitValue(source, sink) } - Await.result(f, Duration(1, SECONDS)) + Await.result(f, TestDuration.Standard) assert(source.releaseCount == 1) assert(sink.releaseCount == 1) assert(a.destroyCount == 1) @@ -235,7 +241,7 @@ class PipeSourceSinkTest { source.join() sink.join() } - Await.result(f, Duration(1, SECONDS)) + Await.result(f, TestDuration.Standard) assert(in.closed == true) assert(out.closed == true) assert(source.isReleased == true) @@ -253,7 +259,7 @@ class PipeSourceSinkTest { source.release() sink.release() } - Await.result(f, Duration(1, SECONDS)) + Await.result(f, TestDuration.Standard) assert(out.closed == true) assert(source.isReleased == true) assert(sink.isReleased == true) @@ -270,13 +276,13 @@ class PipeSourceSinkTest { source.release() sink.release() } - Await.result(f, Duration(1, SECONDS)) + Await.result(f, TestDuration.Standard) assert(in.closed == true) assert(source.isReleased == true) assert(sink.isReleased == true) } - // PipeSource and PipeSink must release resources when interrupted during copy streams" + // PipeSource and PipeSink must release resources when interrupted during copy streams @Test def runloopInterrupted() { val in = new DebugInfinityInputStream @@ -290,7 +296,7 @@ class PipeSourceSinkTest { source.release() sink.release() } - Await.result(f, Duration(1, SECONDS)) + Await.result(f, TestDuration.Standard) assert(in.closed == true) assert(out.closed == true) assert(source.isReleased == true) diff --git a/test/junit/scala/tools/nsc/classpath/JrtClassPathTest.scala b/test/junit/scala/tools/nsc/classpath/JrtClassPathTest.scala new file mode 100644 index 0000000000..2c3c5134da --- /dev/null +++ b/test/junit/scala/tools/nsc/classpath/JrtClassPathTest.scala @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014 Contributor. All rights reserved. + */ +package scala.tools.nsc.classpath + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.tools.nsc.Settings +import scala.tools.nsc.backend.jvm.AsmUtils +import scala.tools.nsc.util.ClassPath +import scala.tools.util.PathResolver + +@RunWith(classOf[JUnit4]) +class JrtClassPathTest { + + @Test def lookupJavaClasses(): Unit = { + val specVersion = scala.util.Properties.javaSpecVersion + // Run the test using the JDK8 or 9 provider for rt.jar depending on the platform the test is running on. + val cp: ClassPath = + if (specVersion == "" || specVersion == "1.8") { + val settings = new Settings() + val resolver = new PathResolver(settings) + val elements = new ClassPathFactory(settings).classesInPath(resolver.Calculated.javaBootClassPath) + AggregateClassPath(elements) + } + else JrtClassPath().get + + assertEquals(Nil, cp.classes("")) + assertTrue(cp.packages("java").toString, cp.packages("java").exists(_.name == "java.lang")) + assertTrue(cp.classes("java.lang").exists(_.name == "Object")) + val jl_Object = cp.classes("java.lang").find(_.name == "Object").get + assertEquals("java/lang/Object", AsmUtils.classFromBytes(jl_Object.file.toByteArray).name) + assertTrue(cp.list("java.lang").packages.exists(_.name == "java.lang.annotation")) + assertTrue(cp.list("java.lang").classesAndSources.exists(_.name == "Object")) + assertTrue(cp.findClass("java.lang.Object").isDefined) + assertTrue(cp.findClassFile("java.lang.Object").isDefined) + } +} diff --git a/test/scaladoc/run/SI-9704.check b/test/scaladoc/run/SI-9704.check new file mode 100644 index 0000000000..5a73befd9b --- /dev/null +++ b/test/scaladoc/run/SI-9704.check @@ -0,0 +1,4 @@ +Chain(List(Chain(List(Text(Demonstrates a scala issue in which the closing link tag is duplicated), Text( +), HtmlTag(<a href="https://link">title</a>), Text( +), Text())))) +Done. diff --git a/test/scaladoc/run/SI-9704.scala b/test/scaladoc/run/SI-9704.scala new file mode 100644 index 0000000000..e6f071704e --- /dev/null +++ b/test/scaladoc/run/SI-9704.scala @@ -0,0 +1,22 @@ +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + override def code = """ + object Foo { + /** + * Demonstrates a scala issue in which the closing link tag is duplicated + * <a href="https://link">title</a> + */ + def bar = ??? + } + """ + + def scaladocSettings = "" + + def testModel(root: Package) = { + import access._ + val thing = root._object("Foo")._method("bar") + println(thing.comment.get.short) + } +} diff --git a/test/scaladoc/run/inlineToStr-strips-unwanted-text.check b/test/scaladoc/run/inlineToStr-strips-unwanted-text.check new file mode 100644 index 0000000000..986b58d469 --- /dev/null +++ b/test/scaladoc/run/inlineToStr-strips-unwanted-text.check @@ -0,0 +1,13 @@ +Chain(List(Chain(List(Text(This comment contains ), Superscript(Text(superscript)))))) +Chain(List(Chain(List(Text(This comment contains ), Subscript(Text(subscript)))))) +Chain(List(Chain(List(Text(This comment contains a link ), Link(https://scala.epfl.ch/,Text(https://scala.epfl.ch/)))))) +Chain(List(Chain(List(Text(This comment contains an ), HtmlTag(<strong>html tag</strong>))))) +Chain(List(Chain(List(Text(This comment contains a), HtmlTag(<br>), Text( single html tag))))) +Chain(List(Chain(List(Text(This comment contains nested ), HtmlTag(<strong>html<br> tags</strong>))))) +This comment contains superscript +This comment contains subscript +This comment contains a link https://scala.epfl.ch/ +This comment contains an html tag +This comment contains a single html tag +This comment contains nested html tags +Done. diff --git a/test/scaladoc/run/inlineToStr-strips-unwanted-text.scala b/test/scaladoc/run/inlineToStr-strips-unwanted-text.scala new file mode 100644 index 0000000000..f51b7d6b3f --- /dev/null +++ b/test/scaladoc/run/inlineToStr-strips-unwanted-text.scala @@ -0,0 +1,51 @@ +import scala.tools.nsc.doc.html.Page +import scala.tools.nsc.doc.model._ +import scala.tools.partest.ScaladocModelTest + +object Test extends ScaladocModelTest { + + override def code = """ + /** This comment contains ^superscript^ */ + class Foo { + /** This comment contains ,,subscript,, */ + def bar = ??? + + /** This comment contains a link [[https://scala.epfl.ch/]] */ + def baz = ??? + + /** This comment contains an <strong>html tag</strong> */ + def qux = ??? + + /** This comment contains a<br> single html tag */ + def quux = ??? + + /** This comment contains nested <strong>html<br> tags</strong> */ + def quuz = ??? + } + """ + def scaladocSettings = "" + + def testModel(root: Package) = { + import scala.tools.nsc.doc.base.comment._ + import access._ + + val foo = root._class("Foo") + val bar = foo._method("bar") + val baz = foo._method("baz") + val qux = foo._method("qux") + val quux = foo._method("quux") + val quuz = foo._method("quuz") + println(foo.comment.get.short) + println(bar.comment.get.short) + println(baz.comment.get.short) + println(qux.comment.get.short) + println(quux.comment.get.short) + println(quuz.comment.get.short) + println(Page.inlineToStr(foo.comment.get.short)) + println(Page.inlineToStr(bar.comment.get.short)) + println(Page.inlineToStr(baz.comment.get.short)) + println(Page.inlineToStr(qux.comment.get.short)) + println(Page.inlineToStr(quux.comment.get.short)) + println(Page.inlineToStr(quuz.comment.get.short)) + } +} diff --git a/test/scaladoc/run/shortDescription-annotation.scala b/test/scaladoc/run/shortDescription-annotation.scala index 0e2950f4f9..4f9a891133 100644 --- a/test/scaladoc/run/shortDescription-annotation.scala +++ b/test/scaladoc/run/shortDescription-annotation.scala @@ -1,3 +1,4 @@ +import scala.tools.nsc.doc.html.Page import scala.tools.nsc.doc.model._ import scala.tools.partest.ScaladocModelTest @@ -26,30 +27,18 @@ object Test extends ScaladocModelTest { import scala.tools.nsc.doc.base.comment._ import access._ - def inlineToStr(inl: Inline): String = inl match { - case Chain(items) => items flatMap (inlineToStr(_)) mkString "" - case Italic(in) => inlineToStr(in) - case Bold(in) => inlineToStr(in) - case Underline(in) => inlineToStr(in) - case Monospace(in) => inlineToStr(in) - case Text(text) => text - case Summary(in) => inlineToStr(in) - case EntityLink(Text(text), _) => text - case _ => inl.toString - } - val foo = rootPackage._package("a")._class("Foo") // Assert that the class has the correct short description - val classDesc = inlineToStr(foo.comment.get.short) + val classDesc = Page.inlineToStr(foo.comment.get.short) assert(classDesc == "This one should appear", classDesc) // Assert that the `foo` method has the correct short description - val fooDesc = inlineToStr(foo._method("foo").comment.get.short) + val fooDesc = Page.inlineToStr(foo._method("foo").comment.get.short) assert(fooDesc == "This comment should appear", fooDesc) // Assert that the `goo` method has the correct short description - val gooDesc = inlineToStr(foo._method("goo").comment.get.short) + val gooDesc = Page.inlineToStr(foo._method("goo").comment.get.short) assert(gooDesc == "This comment should appear", gooDesc) } } diff --git a/versions.properties b/versions.properties index 26e8324c44..f767dfab7b 100644 --- a/versions.properties +++ b/versions.properties @@ -21,7 +21,7 @@ scala-swing.version.number=2.0.0 scala-swing.version.osgi=2.0.0 jline.version=2.14.3 # this one is shaded and embedded in scala-compiler.jar -scala-asm.version=5.1.0-scala-1 +scala-asm.version=5.1.0-scala-2 # external modules, used internally (not shipped) partest.version.number=1.1.0 |