diff options
Diffstat (limited to 'src')
17 files changed, 176 insertions, 124 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 226c49ec07..d72002f0a7 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -543,24 +543,36 @@ trait Scanners extends ScannersCommon { } fetchDoubleQuote() case '\'' => + def unclosedCharLit() = { + val msg = "unclosed character literal" + // previous token was Symbol contiguous with the orphan single quote at offset + if (token == SYMBOLLIT && offset == lastOffset) { + syntaxError(s"""$msg (or use " for string literal "$strVal")""") + } else { + syntaxError(msg) + } + } def fetchSingleQuote() = { nextChar() if (isIdentifierStart(ch)) charLitOr(getIdentRest) else if (isOperatorPart(ch) && (ch != '\\')) charLitOr(getOperatorRest) + else if (ch == '\'') { + nextChar() + val advice = if (ch == '\'') { do nextChar() while (ch == '\''); " (use '\\'' for single quote)" } else "" + syntaxError(s"empty character literal${advice}") + } else if (!isAtEnd && (ch != SU && ch != CR && ch != LF || isUnicodeEscape)) { getLitChar() - if (ch == '\'') { + if (ch != '\'') unclosedCharLit() + else { nextChar() token = CHARLIT setStrVal() - } else { - syntaxError("unclosed character literal") } } - else - syntaxError("unclosed character literal") + else unclosedCharLit() } fetchSingleQuote() case '.' => @@ -792,7 +804,7 @@ trait Scanners extends ScannersCommon { next.token = kwArray(idx) } } else { - syntaxError("invalid string interpolation: `$$', `$'ident or `$'BlockExpr expected") + syntaxError(s"invalid string interpolation $$$ch, expected: $$$$, $$identifier or $${expression}") } } else { val isUnclosedLiteral = !isUnicodeEscape && (ch == SU || (!multiLine && (ch == CR || ch == LF))) @@ -983,6 +995,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 +1004,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 +1023,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/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 7a71561978..503f64a44f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -64,9 +64,8 @@ trait Contexts { self: Analyzer => for (imps <- allImportInfos.remove(unit)) { for (imp <- imps.reverse.distinct) { 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 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/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/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/Formatting.scala b/src/repl/scala/tools/nsc/interpreter/Formatting.scala deleted file mode 100644 index 4a9548730a..0000000000 --- a/src/repl/scala/tools/nsc/interpreter/Formatting.scala +++ /dev/null @@ -1,35 +0,0 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package interpreter - -import util.stringFromWriter - -class Formatting(indent: Int) { - - private val indentation = " " * indent - - private def indenting(code: String): Boolean = { - /** Heuristic to avoid indenting and thereby corrupting """-strings and XML literals. */ - val tokens = List("\"\"\"", "</", "/>") - val noIndent = (code contains "\n") && (tokens exists code.contains) - - !noIndent - } - /** Indent some code by the width of the scala> prompt. - * This way, compiler error messages read better. - */ - def indentCode(code: String) = stringFromWriter(str => - for (line <- code.lines) { - if (indenting(code)) str print indentation - str println line - str.flush() - } - ) -} -object Formatting { - def forPrompt(prompt: String) = new Formatting(prompt.lines.toList.last.length) -} diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala index a351d2da95..2ae3b207b7 100644 --- a/src/repl/scala/tools/nsc/interpreter/IMain.scala +++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala @@ -111,11 +111,8 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends try body finally label = saved } - // the expanded prompt but without color escapes and without leading newline, for purposes of indenting - lazy val formatting = Formatting.forPrompt(replProps.promptText) lazy val reporter: ReplReporter = new ReplReporter(this) - import formatting.indentCode import reporter.{ printMessage, printUntruncatedMessage } // This exists mostly because using the reporter too early leads to deadlock. @@ -867,8 +864,8 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends |${preambleHeader format lineRep.readName} |${envLines mkString (" ", ";\n ", ";\n")} |$importsPreamble - |${indentCode(toCompute)}""".stripMargin - def preambleLength = preamble.length - toCompute.length - 1 + |${toCompute}""".stripMargin + def preambleLength = preamble.length - toCompute.length val generate = (m: MemberHandler) => m extraCodeToEvaluate Request.this 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/ReplReporter.scala b/src/repl/scala/tools/nsc/interpreter/ReplReporter.scala index 3a0b69f41e..b01d242d44 100644 --- a/src/repl/scala/tools/nsc/interpreter/ReplReporter.scala +++ b/src/repl/scala/tools/nsc/interpreter/ReplReporter.scala @@ -9,7 +9,7 @@ package interpreter import reporters._ import IMain._ -import scala.reflect.internal.util.Position +import scala.reflect.internal.util.{OffsetPosition, Position} /** Like ReplGlobal, a layer for ensuring extra functionality. */ @@ -40,14 +40,25 @@ class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, Console.i case INFO => RESET } + private val promptLength = replProps.promptText.lines.toList.last.length + private val indentation = " " * promptLength + + // colorized console labels + override protected def clabel(severity: Severity): String = { + val label0 = super.clabel(severity) + if (replProps.colorOk) s"${severityColor(severity)}${label0}${RESET}" else label0 + } + + // shift indentation for source text entered at prompt override def print(pos: Position, msg: String, severity: Severity) { - val prefix = ( - if (replProps.colorOk) - severityColor(severity) + clabel(severity) + RESET - else - clabel(severity) - ) - printMessage(pos, prefix + msg) + val adjusted = + if (pos.source.file.name == "<console>") + new OffsetPosition(pos.source, pos.offset.getOrElse(0)) { + override def lineContent = s"${indentation}${super.lineContent}" + override def lineCaret = s"${indentation}${super.lineCaret}" + } + else pos + super.print(adjusted, msg, severity) } override def printMessage(msg: String) { @@ -63,12 +74,8 @@ class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, Console.i else Console.println("[init] " + msg) } - override def displayPrompt() { - if (intp.totalSilence) () - else super.displayPrompt() - } + override def displayPrompt() = if (!intp.totalSilence) super.displayPrompt() override def rerunWithDetails(setting: reflect.internal.settings.MutableSettings#Setting, name: String) = s"; for details, enable `:setting $name' or `:replay $name'" - } 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() |