diff options
25 files changed, 333 insertions, 132 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index 9b9e641cad..b6d37ab9a7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -12,21 +12,47 @@ import scala.collection.{ mutable, immutable } import mutable.ListBuffer import symtab.Flags._ -/** This phase adds super accessors for all super calls that either +/** This phase performs the following functions, each of which could be split out in a + * mini-phase: + * + * (1) Adds super accessors for all super calls that either * appear in a trait or have as a target a member of some outer class. - * It also replaces references to parameter accessors with aliases - * by super references to these aliases. The phase also checks that - * symbols accessed from super are not abstract, or are overridden by - * an abstract override. Finally, the phase also mangles the names - * of class-members which are private up to an enclosing non-package - * class, in order to avoid overriding conflicts. * - * This phase also sets SPECIALIZED flag on type parameters with + * (2) Converts references to parameter fields that have the same name as a corresponding + * public parameter field in a superclass to a reference to the superclass + * field (corresponding = super class field is initialized with subclass field). + * This info is pre-computed by the `alias` field in Typer. `dotc` follows a different + * route; it computes everything in SuperAccessors and changes the subclass field + * to a forwarder instead of manipulating references. This is more modular. + * + * (3) Adds protected accessors if the access to the protected member happens + * in a class which is not a subclass of the member's owner. + * + * (4) Mangles the names of class-members which are + * private up to an enclosing non-package class, in order to avoid overriding conflicts. + * This is a dubious, and it would be better to deprecate class-qualified privates. + * + * (5) This phase also sets SPECIALIZED flag on type parameters with * `@specialized` annotation. We put this logic here because the * flag must be set before pickling. * - * @author Martin Odersky - * @version 1.0 + * It also checks that: + * + * (1) Symbols accessed from super are not abstract, or are overridden by + * an abstract override. + * + * (2) If a symbol accessed accessed from super is defined in a real class (not a trait), + * there are no abstract members which override this member in Java's rules + * (see SI-4989; such an access would lead to illegal bytecode) + * + * (3) Super calls do not go to some synthetic members of Any (see isDisallowed) + * + * (4) Super calls do not go to synthetic field accessors + * + * (5) A class and its companion object do not both define a class or module with the + * same name. + * + * TODO: Rename phase to "Accessors" because it handles more than just super accessors */ abstract class SuperAccessors extends transform.Transform with transform.TypingTransformers { import global._ diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 66b1c2d87a..9a4d5e3c06 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4399,7 +4399,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (retry) { val Select(qual, name) = fun tryTypedArgs(args, forArgMode(fun, mode)) match { - case Some(args1) => + case Some(args1) if !args1.exists(arg => arg.exists(_.isErroneous)) => val qual1 = if (!pt.isError) adaptToArguments(qual, name, args1, pt, reportAmbiguous = true, saveErrors = true) else qual diff --git a/src/library/scala/collection/Iterable.scala b/src/library/scala/collection/Iterable.scala index a5ab8efd5c..afbffd36c6 100644 --- a/src/library/scala/collection/Iterable.scala +++ b/src/library/scala/collection/Iterable.scala @@ -38,7 +38,7 @@ trait Iterable[+A] extends Traversable[A] } /** $factoryInfo - * The current default implementation of a $Coll is a `Vector`. + * The current default implementation of a $Coll is a `List`. * @define coll iterable collection * @define Coll `Iterable` */ diff --git a/src/library/scala/collection/SetLike.scala b/src/library/scala/collection/SetLike.scala index 0c5c7e0b29..3e549f72cd 100644 --- a/src/library/scala/collection/SetLike.scala +++ b/src/library/scala/collection/SetLike.scala @@ -17,6 +17,9 @@ import parallel.ParSet /** A template trait for sets. * * $setNote + * '''Implementation note:''' + * This trait provides most of the operations of a `Set` independently of its representation. + * It is typically inherited by concrete implementations of sets. * $setTags * @since 2.8 * @@ -24,10 +27,6 @@ import parallel.ParSet * * A set is a collection that contains no duplicate elements. * - * '''Implementation note:''' - * This trait provides most of the operations of a `Set` independently of its representation. - * It is typically inherited by concrete implementations of sets. - * * To implement a concrete set, you need to provide implementations of the * following methods: * {{{ diff --git a/src/library/scala/collection/Traversable.scala b/src/library/scala/collection/Traversable.scala index b53724c568..a35750a35f 100644 --- a/src/library/scala/collection/Traversable.scala +++ b/src/library/scala/collection/Traversable.scala @@ -87,7 +87,7 @@ trait Traversable[+A] extends TraversableLike[A, Traversable[A]] } /** $factoryInfo - * The current default implementation of a $Coll is a `Vector`. + * The current default implementation of a $Coll is a `List`. */ object Traversable extends TraversableFactory[Traversable] { self => diff --git a/src/library/scala/collection/convert/DecorateAsScala.scala b/src/library/scala/collection/convert/DecorateAsScala.scala index c724831c54..5448f5f91c 100644 --- a/src/library/scala/collection/convert/DecorateAsScala.scala +++ b/src/library/scala/collection/convert/DecorateAsScala.scala @@ -135,6 +135,12 @@ trait DecorateAsScala { * If the Java `Map` was previously obtained from an implicit or explicit * call of `asMap(scala.collection.mutable.Map)` then the original * Scala `Map` will be returned. + * + * If the wrapped map is synchronized (e.g. from `java.util.Collections.synchronizedMap`), + * it is your responsibility to wrap all + * non-atomic operations with `underlying.synchronized`. + * This includes `get`, as `java.util.Map`'s API does not allow for an + * atomic `get` when `null` values may be present. * * @param m The `Map` to be converted. * @return An object with an `asScala` method that returns a Scala mutable diff --git a/src/library/scala/collection/convert/WrapAsScala.scala b/src/library/scala/collection/convert/WrapAsScala.scala index d4ab451b0d..ab151a6778 100644 --- a/src/library/scala/collection/convert/WrapAsScala.scala +++ b/src/library/scala/collection/convert/WrapAsScala.scala @@ -133,7 +133,13 @@ trait WrapAsScala { * If the Java `Map` was previously obtained from an implicit or * explicit call of `mapAsScalaMap(scala.collection.mutable.Map)` then * the original Scala Map will be returned. - * + * + * If the wrapped map is synchronized (e.g. from `java.util.Collections.synchronizedMap`), + * it is your responsibility to wrap all + * non-atomic operations with `underlying.synchronized`. + * This includes `get`, as `java.util.Map`'s API does not allow for an + * atomic `get` when `null` values may be present. + * * @param m The Map to be converted. * @return A Scala mutable Map view of the argument. */ diff --git a/src/library/scala/collection/convert/Wrappers.scala b/src/library/scala/collection/convert/Wrappers.scala index 7d1d6b3781..9f9732c62f 100644 --- a/src/library/scala/collection/convert/Wrappers.scala +++ b/src/library/scala/collection/convert/Wrappers.scala @@ -288,6 +288,13 @@ private[collection] trait Wrappers { override def empty: Repr = null.asInstanceOf[Repr] } + /** Wraps a Java map as a Scala one. If the map is to support concurrent access, + * use [[JConcurrentMapWrapper]] instead. If the wrapped map is synchronized + * (e.g. from `java.util.Collections.synchronizedMap`), it is your responsibility + * to wrap all non-atomic operations with `underlying.synchronized`. + * This includes `get`, as `java.util.Map`'s API does not allow for an + * atomic `get` when `null` values may be present. + */ case class JMapWrapper[A, B](underlying : ju.Map[A, B]) extends mutable.AbstractMap[A, B] with JMapWrapperLike[A, B, JMapWrapper[A, B]] { override def empty = JMapWrapper(new ju.HashMap[A, B]) } @@ -314,6 +321,10 @@ private[collection] trait Wrappers { def replace(k: A, oldval: B, newval: B) = underlying.replace(k, oldval, newval) } + /** Wraps a concurrent Java map as a Scala one. Single-element concurrent + * access is supported; multi-element operations such as maps and filters + * are not guaranteed to be atomic. + */ case class JConcurrentMapWrapper[A, B](underlying: juc.ConcurrentMap[A, B]) extends mutable.AbstractMap[A, B] with JMapWrapperLike[A, B, JConcurrentMapWrapper[A, B]] with concurrent.Map[A, B] { override def get(k: A) = { val v = underlying get k diff --git a/src/library/scala/collection/immutable/Iterable.scala b/src/library/scala/collection/immutable/Iterable.scala index 6e4eb1e45f..df322396d0 100644 --- a/src/library/scala/collection/immutable/Iterable.scala +++ b/src/library/scala/collection/immutable/Iterable.scala @@ -35,6 +35,7 @@ trait Iterable[+A] extends Traversable[A] } /** $factoryInfo + * The current default implementation of a $Coll is a `List`. * @define Coll `immutable.Iterable` * @define coll immutable iterable collection */ diff --git a/src/library/scala/collection/immutable/Traversable.scala b/src/library/scala/collection/immutable/Traversable.scala index 775d635fae..5fc0607a00 100644 --- a/src/library/scala/collection/immutable/Traversable.scala +++ b/src/library/scala/collection/immutable/Traversable.scala @@ -29,7 +29,7 @@ trait Traversable[+A] extends scala.collection.Traversable[A] } /** $factoryInfo - * The current default implementation of a $Coll is a `Vector`. + * The current default implementation of a $Coll is a `List`. * @define coll immutable traversable collection * @define Coll `immutable.Traversable` */ diff --git a/src/library/scala/collection/mutable/SetLike.scala b/src/library/scala/collection/mutable/SetLike.scala index d749167870..a377b03124 100644 --- a/src/library/scala/collection/mutable/SetLike.scala +++ b/src/library/scala/collection/mutable/SetLike.scala @@ -16,19 +16,20 @@ import scala.annotation.migration import parallel.mutable.ParSet /** A template trait for mutable sets of type `mutable.Set[A]`. + * + * This trait provides most of the operations of a `mutable.Set` independently of its representation. + * It is typically inherited by concrete implementations of sets. + * + * $setNote + * * @tparam A the type of the elements of the set * @tparam This the type of the set itself. * - * $setnote - * * @author Martin Odersky * @version 2.8 * @since 2.8 * - * @define setnote - * @note - * This trait provides most of the operations of a `mutable.Set` independently of its representation. - * It is typically inherited by concrete implementations of sets. + * @define setNote * * To implement a concrete mutable set, you need to provide implementations * of the following methods: @@ -36,13 +37,13 @@ import parallel.mutable.ParSet * def contains(elem: A): Boolean * def iterator: Iterator[A] * def += (elem: A): this.type - * def -= (elem: A): this.type</pre> + * def -= (elem: A): this.type * }}} * If you wish that methods like `take`, * `drop`, `filter` return the same kind of set, * you should also override: * {{{ - * def empty: This</pre> + * def empty: This * }}} * It is also good idea to override methods `foreach` and * `size` for efficiency. diff --git a/src/library/scala/collection/mutable/UnrolledBuffer.scala b/src/library/scala/collection/mutable/UnrolledBuffer.scala index 1f89199bdc..693c47d86e 100644 --- a/src/library/scala/collection/mutable/UnrolledBuffer.scala +++ b/src/library/scala/collection/mutable/UnrolledBuffer.scala @@ -300,27 +300,33 @@ object UnrolledBuffer extends ClassTagTraversableFactory[UnrolledBuffer] { if (next eq null) true else false // checks if last node was thrown out } else false - @tailrec final def insertAll(idx: Int, t: scala.collection.Traversable[T], buffer: UnrolledBuffer[T]): Unit = if (idx < size) { - // divide this node at the appropriate position and insert all into head - // update new next - val newnextnode = new Unrolled[T](0, new Array(array.length), null, buff) - Array.copy(array, idx, newnextnode.array, 0, size - idx) - newnextnode.size = size - idx - newnextnode.next = next - - // update this - nullout(idx, size) - size = idx - next = null - - // insert everything from iterable to this - var curr = this - for (elem <- t) curr = curr append elem - curr.next = newnextnode - - // try to merge the last node of this with the newnextnode - if (curr.tryMergeWithNext()) buffer.lastPtr = curr - } else insertAll(idx - size, t, buffer) + @tailrec final def insertAll(idx: Int, t: scala.collection.Traversable[T], buffer: UnrolledBuffer[T]): Unit = { + if (idx < size) { + // divide this node at the appropriate position and insert all into head + // update new next + val newnextnode = new Unrolled[T](0, new Array(array.length), null, buff) + Array.copy(array, idx, newnextnode.array, 0, size - idx) + newnextnode.size = size - idx + newnextnode.next = next + + // update this + nullout(idx, size) + size = idx + next = null + + // insert everything from iterable to this + var curr = this + for (elem <- t) curr = curr append elem + curr.next = newnextnode + + // try to merge the last node of this with the newnextnode + if (curr.tryMergeWithNext()) buffer.lastPtr = curr + } + else if (idx == size) { + var curr = this + for (elem <- t) curr = curr append elem + } else insertAll(idx - size, t, buffer) + } private def nullout(from: Int, until: Int) { var idx = from while (idx < until) { diff --git a/src/library/scala/io/BufferedSource.scala b/src/library/scala/io/BufferedSource.scala index 1c87a1f421..52fa525b24 100644 --- a/src/library/scala/io/BufferedSource.scala +++ b/src/library/scala/io/BufferedSource.scala @@ -93,7 +93,7 @@ class BufferedSource(inputStream: InputStream, bufferSize: Int)(implicit val cod val buf = new Array[Char](bufferSize) var n = 0 while (n != -1) { - n = charReader.read(buf) + n = allReader.read(buf) if (n>0) sb.appendAll(buf, 0, n) } sb.result diff --git a/src/reflect/scala/reflect/internal/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala index fcc377ba32..2ce861898f 100644 --- a/src/reflect/scala/reflect/internal/Printers.scala +++ b/src/reflect/scala/reflect/internal/Printers.scala @@ -596,18 +596,26 @@ trait Printers extends api.Printers { self: SymbolTable => } } - protected def emptyTree(tree: Tree) = tree match { - case EmptyTree | build.SyntacticEmptyTypeTree() => true - case _ => false + object EmptyTypeTree { + def unapply(tt: TypeTree): Boolean = tt match { + case build.SyntacticEmptyTypeTree() if tt.wasEmpty || tt.isEmpty => true + case _ => false + } } + protected def isEmptyTree(tree: Tree) = + tree match { + case EmptyTree | EmptyTypeTree() => true + case _ => false + } + protected def originalTypeTrees(trees: List[Tree]) = - trees.filter(!emptyTree(_)) map { - case tt: TypeTree => tt.original - case tree => tree + trees.filter(!isEmptyTree(_)) map { + case tt: TypeTree if tt.original != null => tt.original + case tree => tree } - val defaultClasses = List(tpnme.AnyRef) + val defaultClasses = List(tpnme.AnyRef, tpnme.Object) val defaultTraitsForCase = List(tpnme.Product, tpnme.Serializable) protected def removeDefaultTypesFromList(trees: List[Tree])(classesToRemove: List[Name] = defaultClasses)(traitsToRemove: List[Name]) = { def removeDefaultTraitsFromList(trees: List[Tree], traitsToRemove: List[Name]): List[Tree] = @@ -623,9 +631,10 @@ trait Printers extends api.Printers { self: SymbolTable => removeDefaultTraitsFromList(removeDefaultClassesFromList(trees, classesToRemove), traitsToRemove) } - protected def removeDefaultClassesFromList(trees: List[Tree], classesToRemove: List[Name] = defaultClasses) = + protected def removeDefaultClassesFromList(trees: List[Tree], classesToRemove: List[Name] = defaultClasses) = originalTypeTrees(trees) filter { case Select(Ident(sc), name) => !(classesToRemove.contains(name) && sc == nme.scala_) + case tt: TypeTree if tt.tpe != null => !(classesToRemove contains(newTypeName(tt.tpe.toString()))) case _ => true } @@ -637,7 +646,7 @@ trait Printers extends api.Printers { self: SymbolTable => } override def printOpt(prefix: String, tree: Tree) = - if (!emptyTree(tree)) super.printOpt(prefix, tree) + if (!isEmptyTree(tree)) super.printOpt(prefix, tree) override def printColumn(ts: List[Tree], start: String, sep: String, end: String) = { super.printColumn(ts.filter(!syntheticToRemove(_)), start, sep, end) @@ -952,7 +961,7 @@ trait Printers extends api.Printers { self: SymbolTable => def printTp = print("(", tp, ")") tp match { - case EmptyTree | build.SyntacticEmptyTypeTree() => printTp + case EmptyTree | EmptyTypeTree() => printTp // case for untypechecked trees case Annotated(annot, arg) if (expr ne null) && (arg ne null) && expr.equalsStructure(arg) => printTp // remove double arg - 5: 5: @unchecked case tt: TypeTree if tt.original.isInstanceOf[Annotated] => printTp @@ -963,7 +972,7 @@ trait Printers extends api.Printers { self: SymbolTable => // print only fun when targs are TypeTrees with empty original case TypeApply(fun, targs) => - if (targs.exists(emptyTree(_))) { + if (targs.exists(isEmptyTree(_))) { print(fun) } else super.printTree(tree) @@ -984,8 +993,8 @@ trait Printers extends api.Printers { self: SymbolTable => case treeInfo.Unapplied(body) => body match { case Select(qual, name) if name == nme.unapply => print(qual) - case TypeApply(Select(qual, name), args) if name == nme.unapply || name == nme.unapplySeq => - print(TypeApply(qual, args)) + case TypeApply(Select(qual, name), _) if name == nme.unapply || name == nme.unapplySeq => + print(qual) case _ => print(body) } case _ => print(fun) @@ -1061,7 +1070,11 @@ trait Printers extends api.Printers { self: SymbolTable => print("(", qualifier, ")#", blankForOperatorName(selector), printedName(selector)) case tt: TypeTree => - if (!emptyTree(tt)) print(tt.original) + if (!isEmptyTree(tt)) { + val original = tt.original + if (original != null) print(original) + else super.printTree(tree) + } case AppliedTypeTree(tp, args) => // it's possible to have (=> String) => String type but Function1[=> String, String] is not correct diff --git a/src/reflect/scala/reflect/internal/ReificationSupport.scala b/src/reflect/scala/reflect/internal/ReificationSupport.scala index ad8a2594dd..2caa30d27e 100644 --- a/src/reflect/scala/reflect/internal/ReificationSupport.scala +++ b/src/reflect/scala/reflect/internal/ReificationSupport.scala @@ -97,6 +97,8 @@ trait ReificationSupport { self: SymbolTable => def toStats(tree: Tree): List[Tree] = tree match { case EmptyTree => Nil case SyntacticBlock(stats) => stats + case defn if defn.isDef => defn :: Nil + case imp: Import => imp :: Nil case _ => throw new IllegalArgumentException(s"can't flatten $tree") } diff --git a/src/repl/scala/tools/nsc/interpreter/JavapClass.scala b/src/repl/scala/tools/nsc/interpreter/JavapClass.scala index 915fd57bf8..3cb6ba11c1 100644 --- a/src/repl/scala/tools/nsc/interpreter/JavapClass.scala +++ b/src/repl/scala/tools/nsc/interpreter/JavapClass.scala @@ -41,16 +41,16 @@ class JavapClass( * Byte data for filename args is retrieved with findBytes. */ def apply(args: Seq[String]): List[JpResult] = { - val (options, claases) = args partition (s => (s startsWith "-") && s.length > 1) + val (options, classes) = args partition (s => (s startsWith "-") && s.length > 1) val (flags, upgraded) = upgrade(options) import flags.{ app, fun, help, raw } - val targets = if (fun && !help) FunFinder(loader, intp).funs(claases) else claases - if (help || claases.isEmpty) + val targets = if (fun && !help) FunFinder(loader, intp).funs(classes) else classes + if (help || classes.isEmpty) List(JpResult(JavapTool.helper(printWriter))) else if (targets.isEmpty) List(JpResult("No anonfuns found.")) else - tool(raw, upgraded)(targets map (claas => targeted(claas, app))) + tool(raw, upgraded)(targets map (klass => targeted(klass, app))) } /** Cull our tool options. */ @@ -67,17 +67,18 @@ class JavapClass( case f: Failure[_] => (path, Failure(f.exception)) } - /** Find bytes. Handle "-", "-app", "Foo#bar" (by ignoring member), "#bar" (by taking "bar"). */ + /** Find bytes. Handle "-", "-app", "Foo#bar" (by ignoring member), "#bar" (by taking "bar"). + * @return the path to use for filtering, and the byte array + */ private def bytesFor(path: String, app: Boolean) = Try { def last = intp.get.mostRecentVar // fail if no intp - def req = path match { - case "-" => last - case HashSplit(prefix, member) => - if (prefix != null) prefix - else if (member != null) member - else "#" + val req = path match { + case "-" => last + case HashSplit(prefix, _) if prefix != null => prefix + case HashSplit(_, member) if member != null => member + case s => s } - val targetedBytes = if (app) findAppBody(req) else (req, findBytes(req)) + val targetedBytes = if (app) findAppBody(req) else (path, findBytes(req)) if (targetedBytes._2.isEmpty) throw new FileNotFoundException(s"Could not find class bytes for '$path'") targetedBytes } @@ -217,27 +218,36 @@ class JavapClass( // if apply is added here, it's for other than -fun: javap Foo#, perhaps m#? val filterOn = target.splitHashMember._2 map { s => if (s.isEmpty) "apply" else s } var filtering = false // true if in region matching filter - // true to output - def checkFilter(line: String) = if (filterOn.isEmpty) true else { + // turn filtering on/off given the pattern of interest + def filterStatus(line: String, pattern: String) = { // cheap heuristic, todo maybe parse for the java sig. // method sigs end in paren semi def isAnyMethod = line.endsWith(");") def isOurMethod = { val lparen = line.lastIndexOf('(') val blank = line.lastIndexOf(' ', lparen) - (blank >= 0 && line.substring(blank+1, lparen) == filterOn.get) - } - filtering = if (filtering) { - // next blank line terminates section - // for -public, next line is next method, more or less - line.trim.nonEmpty && !isAnyMethod - } else { - isAnyMethod && isOurMethod + if (blank < 0) false + else { + val method = line.substring(blank+1, lparen) + (method == pattern || ((method startsWith pattern+"$") && (method endsWith "$sp"))) + } } + filtering = + if (filtering) { + // next blank line terminates section + // for -public, next line is next method, more or less + line.trim.nonEmpty && !isAnyMethod + } else { + isAnyMethod && isOurMethod + } filtering } - for (line <- Source.fromString(preamble + written).getLines(); if checkFilter(line)) - printWriter write line+lineSeparator + // do we output this line? + def checkFilter(line: String) = filterOn map (filterStatus(line, _)) getOrElse true + for { + line <- Source.fromString(preamble + written).getLines() + if checkFilter(line) + } printWriter write f"$line%n" printWriter.flush() } } @@ -275,7 +285,7 @@ class JavapClass( override def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] = (inputs map { - case (claas, Success(ba)) => JpResult(showable(raw, claas, newPrinter(new ByteArrayInputStream(ba), newEnv(options)))) + case (klass, Success(ba)) => JpResult(showable(raw, klass, newPrinter(new ByteArrayInputStream(ba), newEnv(options)))) case (_, Failure(e)) => JpResult(e.toString) }).toList orFailed List(noToolError) } @@ -290,10 +300,10 @@ class JavapClass( //object TaskResult extends Enumeration { // val Ok, Error, CmdErr, SysErr, Abnormal = Value //} - val TaskClaas = loader.tryToInitializeClass[Task](JavapTool.Tool).orNull - override protected def failed = TaskClaas eq null + val TaskClass = loader.tryToInitializeClass[Task](JavapTool.Tool).orNull + override protected def failed = TaskClass eq null - val TaskCtor = TaskClaas.getConstructor( + val TaskCtor = TaskClass.getConstructor( classOf[Writer], classOf[JavaFileManager], classOf[DiagnosticListener[_]], @@ -344,8 +354,12 @@ class JavapClass( import Kind._ import StandardLocation._ import JavaFileManager.Location - import java.net.URI - def uri(name: String): URI = new URI(name) // new URI("jfo:" + name) + import java.net.{ URI, URISyntaxException } + + // name#fragment is OK, but otherwise fragile + def uri(name: String): URI = + try new URI(name) // new URI("jfo:" + name) + catch { case _: URISyntaxException => new URI("dummy") } def inputNamed(name: String): Try[ByteAry] = (managed find (_._1 == name)).get._2 def managedFile(name: String, kind: Kind) = kind match { @@ -379,19 +393,19 @@ class JavapClass( def showable(raw: Boolean, target: String): Showable = showWithPreamble(raw, target, reporter.reportable(raw)) // eventually, use the tool interface - def task(options: Seq[String], claases: Seq[String], inputs: Seq[Input]): Task = { + def task(options: Seq[String], classes: Seq[String], inputs: Seq[Input]): Task = { //ServiceLoader.load(classOf[javax.tools.DisassemblerTool]). - //getTask(writer, fileManager, reporter, options.asJava, claases.asJava) + //getTask(writer, fileManager, reporter, options.asJava, classes.asJava) import JavaConverters.asJavaIterableConverter - TaskCtor.newInstance(writer, fileManager(inputs), reporter, options.asJava, claases.asJava) + TaskCtor.newInstance(writer, fileManager(inputs), reporter, options.asJava, classes.asJava) .orFailed (throw new IllegalStateException) } // a result per input - private def applyOne(raw: Boolean, options: Seq[String], claas: String, inputs: Seq[Input]): Try[JpResult] = + private def applyOne(raw: Boolean, options: Seq[String], klass: String, inputs: Seq[Input]): Try[JpResult] = Try { - task(options, Seq(claas), inputs).call() + task(options, Seq(klass), inputs).call() } map { - case true => JpResult(showable(raw, claas)) + case true => JpResult(showable(raw, klass)) case _ => JpResult(reporter.reportable(raw)) } recoverWith { case e: java.lang.reflect.InvocationTargetException => e.getCause match { @@ -402,7 +416,7 @@ class JavapClass( reporter.clear() } override def apply(raw: Boolean, options: Seq[String])(inputs: Seq[Input]): List[JpResult] = (inputs map { - case (claas, Success(_)) => applyOne(raw, options, claas, inputs).get + case (klass, Success(_)) => applyOne(raw, options, klass, inputs).get case (_, Failure(e)) => JpResult(e.toString) }).toList orFailed List(noToolError) } @@ -534,6 +548,7 @@ class JavapClass( private def isTaskable(cl: ScalaClassLoader) = hasClass(cl, Tool) + /** Select the tool implementation for this platform. */ def apply() = if (isTaskable(loader)) new JavapTool7 else new JavapTool6 } } @@ -545,7 +560,8 @@ object JavapClass { intp: Option[IMain] = None ) = new JavapClass(loader, printWriter, intp) - val HashSplit = "(.*?)(?:#([^#]*))?".r + val HashSplit = "([^#]+)?(?:#(.+)?)?".r + // We enjoy flexibility in specifying either a fully-qualified class name com.acme.Widget // or a resource path com/acme/Widget.class; but not widget.out implicit class MaybeClassLike(val s: String) extends AnyVal { @@ -580,11 +596,11 @@ object JavapClass { /* only the file location from which the given class is loaded */ def locate(k: String): Option[Path] = { Try { - val claas = try cl loadClass k catch { + val klass = try cl loadClass k catch { case _: NoClassDefFoundError => null // let it snow } // cf ScalaClassLoader.originOfClass - claas.getProtectionDomain.getCodeSource.getLocation + klass.getProtectionDomain.getCodeSource.getLocation } match { case Success(null) => None case Success(loc) if loc.isFile => Some(Path(new JFile(loc.toURI))) diff --git a/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala b/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala index dce52af56a..47ddfb8aa9 100644 --- a/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/DocFactory.scala @@ -95,11 +95,11 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor val documentError: PartialFunction[Throwable, Unit] = { case NoCompilerRunException => reporter.info(null, "No documentation generated with unsuccessful compiler run", force = false) - case _: ClassNotFoundException => - () + case e @ (_:ClassNotFoundException | _:IllegalAccessException | _:InstantiationException | _:SecurityException | _:ClassCastException) => + reporter.error(null, s"Cannot load the doclet class ${settings.docgenerator.value} (specified with ${settings.docgenerator.name}): $e. Leaving the default settings will generate the html version of scaladoc.") } - /** Generate document(s) for all `files` containing scaladoc documenataion. + /** Generate document(s) for all `files` containing scaladoc documentation. * @param files The list of paths (relative to the compiler's source path, or absolute) of files to document. */ def document(files: List[String]) { def generate() = { diff --git a/test/files/neg/t8675.check b/test/files/neg/t8675.check new file mode 100644 index 0000000000..4e44fba918 --- /dev/null +++ b/test/files/neg/t8675.check @@ -0,0 +1,11 @@ +t8675.scala:13: error: type mismatch; + found : Boolean(true) + required: String + a.update(0, x[A]({new isString(true)})) // !!! allowed + ^ +t8675.scala:22: error: type mismatch; + found : Boolean(true) + required: String + new X().m(x[A]({new isString(true)})) // !!! allowed + ^ +two errors found diff --git a/test/files/neg/t8675.scala b/test/files/neg/t8675.scala new file mode 100644 index 0000000000..ca9bb57ffa --- /dev/null +++ b/test/files/neg/t8675.scala @@ -0,0 +1,24 @@ +class A(s: String) { + def foo(x: A) = x +} + +class isString(s: String) + +class Test { + + def x[A](a: Any): A = ??? + + def test { + val a = Array[A]() + a.update(0, x[A]({new isString(true)})) // !!! allowed + + // boils down to + class X { + def m(p: Any) {} + } + implicit class XOps(x: X) { + def m(p: Any) {} + } + new X().m(x[A]({new isString(true)})) // !!! allowed + } +} diff --git a/test/files/neg/t8675b.check b/test/files/neg/t8675b.check new file mode 100644 index 0000000000..cb7ac8af59 --- /dev/null +++ b/test/files/neg/t8675b.check @@ -0,0 +1,6 @@ +t8675b.scala:19: error: missing parameter type for expanded function +The argument types of an anonymous function must be fully known. (SLS 8.5) +Expected type was: List[Test.Reportable1[?,?]] => Boolean + for (path: List[Any] <- (null : Engine1).asRequirement.pathsIncludingSelf.toList) { + ^ +one error found diff --git a/test/files/neg/t8675b.scala b/test/files/neg/t8675b.scala new file mode 100644 index 0000000000..2c5015b1d0 --- /dev/null +++ b/test/files/neg/t8675b.scala @@ -0,0 +1,22 @@ +object Test { + trait Engine1 + + implicit class EngineTools1[Params, R](e: Engine1) { + def asRequirement: Requirement1[Params, R] = ??? + } + trait Requirement1[Params, R] { + def pathsIncludingSelf: Traversable[List[Reportable1[Params, R]]] + } + trait Reportable1[Params, R] + + // "missing paramater type" error was swallowed in 2.11.0 leading to a crash + // in the backend. + // + // This error is itself a regression (or at least a change) in 2.11.0-M7, + // specifically in SI-7944. The type paramaters to the implicit view + // `EngineTools1` are undetermined, and are now treated as type variables + // in the expected type of the closure argument to `withFilter`. + for (path: List[Any] <- (null : Engine1).asRequirement.pathsIncludingSelf.toList) { + ??? + } +} diff --git a/test/files/run/t8690.check b/test/files/run/t8690.check new file mode 100644 index 0000000000..72f076c4d8 --- /dev/null +++ b/test/files/run/t8690.check @@ -0,0 +1,2 @@ +non-empty iterator +abcdef diff --git a/test/files/run/t8690.scala b/test/files/run/t8690.scala new file mode 100644 index 0000000000..ab8b45b2a7 --- /dev/null +++ b/test/files/run/t8690.scala @@ -0,0 +1,12 @@ +import scala.io.Source +import java.io.ByteArrayInputStream + +object Test extends App { + val txt = "abcdef" + + val in = new ByteArrayInputStream(txt.getBytes()); + val source = Source.fromInputStream(in); + println(source.toString) // forces the BufferedSource to look at the head of the input + + println(source.mkString) // used to return "bcdef" ... +} diff --git a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala index 45392de582..409f07037e 100644 --- a/test/files/scalacheck/quasiquotes/TermConstructionProps.scala +++ b/test/files/scalacheck/quasiquotes/TermConstructionProps.scala @@ -310,4 +310,16 @@ object TermConstructionProps extends QuasiquoteProperties("term construction") { val cases = List(cq"a => b", cq"c => d") assertEqAst(q"{ case ..$cases }", "{ case a => b case c => d }") } + + property("SI-8609 a") = test { + val q1 = q"val x = 1" + val q2 = q"..$q1; val y = 2" + assert(q2 ≈ q"{ val x = 1; val y = 2 }") + } + + property("SI-8609 b") = test { + val q1 = q"import foo.bar" + val q2 = q"..$q1; val y = 2" + assert(q2 ≈ q"{ import foo.bar; val y = 2 }") + } } diff --git a/test/junit/scala/reflect/internal/PrintersTest.scala b/test/junit/scala/reflect/internal/PrintersTest.scala index 4587417a99..1458b942dc 100644 --- a/test/junit/scala/reflect/internal/PrintersTest.scala +++ b/test/junit/scala/reflect/internal/PrintersTest.scala @@ -33,10 +33,10 @@ object PrinterHelper { def wrapCode(source: String) = { val context = sm""" |trait PrintersContext { - | class baz extends scala.annotation.StaticAnnotation; - | class foo1[A, B] extends scala.annotation.StaticAnnotation; - | class foo2[A, B](a: scala.Int)(b: scala.Int) extends scala.annotation.StaticAnnotation; - | class foo3[Af, Bf](a: scala.Int)(b: scala.Float, c: PrintersContext.this.foo1[Af, Bf]) extends scala.annotation.StaticAnnotation; + | class baz extends scala.annotation.Annotation with scala.annotation.StaticAnnotation; + | class foo1[A, B] extends scala.annotation.Annotation with scala.annotation.StaticAnnotation; + | class foo2[A, B](a: scala.Int)(b: scala.Int) extends scala.annotation.Annotation with scala.annotation.StaticAnnotation; + | class foo3[Af, Bf](a: scala.Int)(b: scala.Float, c: PrintersContext.this.foo1[Af, Bf]) extends scala.annotation.Annotation with scala.annotation.StaticAnnotation; | trait A1; | trait B1; |${source.trim.lines map {" " + _} mkString s"$LF"} @@ -54,8 +54,12 @@ object PrinterHelper { } } - def assertTreeCode(tree: Tree)(code: String) = { - assertEquals("using quasiquote or given tree"+LF, code.trim, normalizeEOL(showCode(tree))) + def assertTreeCode(tree: Tree, typecheck: Boolean = false)(code: String) = { + if (typecheck) { + assertEquals("using quasiquote or given tree (typechecked)"+LF, code.trim, normalizeEOL(showCode(toolbox.typecheck(tree)))) + } else { + assertEquals("using quasiquote or given tree"+LF, code.trim, normalizeEOL(showCode(tree))) + } } def assertPrintedCode(source: String, checkTypedTree: Boolean = true, wrapCode: Boolean = false) = { @@ -312,17 +316,17 @@ trait BasePrintTests { @Test def testFunc1 = assertResultCode( code = "List(1, 2, 3).map((i: Int) => i - 1)")( parsedCode = "List(1, 2, 3).map(((i: Int) => i.-(1)))", - typedCode = sm"scala.collection.immutable.List.apply(1, 2, 3).map(((i: scala.Int) => i.-(1)))(scala.collection.immutable.List.canBuildFrom)") + typedCode = sm"scala.collection.immutable.List.apply[Int](1, 2, 3).map[Int, List[Int]](((i: scala.Int) => i.-(1)))(scala.collection.immutable.List.canBuildFrom[Int])") @Test def testFunc2 = assertResultCode( code = "val sum: Seq[Int] => Int = _ reduceLeft (_+_)")( parsedCode = "val sum: _root_.scala.Function1[Seq[Int], Int] = ((x$1) => x$1.reduceLeft(((x$2, x$3) => x$2.+(x$3))))", - typedCode = "val sum: _root_.scala.Function1[scala.`package`.Seq[scala.Int], scala.Int] = ((x$1) => x$1.reduceLeft(((x$2, x$3) => x$2.+(x$3))))") + typedCode = "val sum: _root_.scala.Function1[scala.`package`.Seq[scala.Int], scala.Int] = ((x$1: Seq[Int]) => x$1.reduceLeft[Int](((x$2: Int, x$3: Int) => x$2.+(x$3))))") @Test def testFunc3 = assertResultCode( code = "List(1, 2, 3) map (_ - 1)")( parsedCode = "List(1, 2, 3).map(((x$1) => x$1.-(1))) ", - typedCode = "scala.collection.immutable.List.apply(1, 2, 3).map(((x$1) => x$1.-(1)))(scala.collection.immutable.List.canBuildFrom)") + typedCode = "scala.collection.immutable.List.apply[Int](1, 2, 3).map[Int, List[Int]](((x$1: Int) => x$1.-(1)))(scala.collection.immutable.List.canBuildFrom[Int])") @Test def testFunc4 = assertResultCode( code = "val x: String => Int = ((str: String) => 1)")( @@ -401,7 +405,8 @@ trait ClassPrintTests { @Test def testClassWithImplicitParams = assertPrintedCode("class X(var i: scala.Int)(implicit val d: scala.Double, var f: scala.Float)") - @Test def testClassWithEarly = assertPrintedCode(sm""" + @Test def testClassWithEarly = + assertPrintedCode(sm""" |class X(var i: scala.Int) extends { | val a = i; | type B @@ -419,15 +424,22 @@ trait ClassPrintTests { | throw Throw2.this.e |}""") - /* - class Test { - val (a, b) = (1, 2) - } - */ - @Test def testClassWithAssignmentWithTuple1 = assertPrintedCode(sm""" + @Test def testClassWithAssignmentWithTuple1 = assertResultCode(sm""" |class Test { - | private[this] val x$$1 = (scala.Tuple2.apply(1, 2): @scala.unchecked) match { - | case scala.Tuple2((a @ _), (b @ _)) => scala.Tuple2.apply(a, b) + | val (a, b) = (1, 2) + |}""")( + parsedCode = sm""" + |class Test { + | private[this] val x$$1 = (scala.Tuple2(1, 2): @scala.unchecked) match { + | case scala.Tuple2((a @ _), (b @ _)) => scala.Tuple2(a, b) + | }; + | val a = x$$1._1; + | val b = x$$1._2 + |}""", + typedCode = sm""" + |class Test { + | private[this] val x$$1 = (scala.Tuple2.apply[Int, Int](1, 2): @scala.unchecked) match { + | case scala.Tuple2((a @ _), (b @ _)) => scala.Tuple2.apply[Int, Int](a, b) | }; | val a = Test.this.x$$1._1; | val b = Test.this.x$$1._2 @@ -448,8 +460,8 @@ trait ClassPrintTests { |}""", typedCode = sm""" |class Test { - | private[this] val x$$1 = (scala.Predef.ArrowAssoc(1).->(2): @scala.unchecked) match { - | case scala.Tuple2((a @ _), (b @ _)) => scala.Tuple2.apply(a, b) + | private[this] val x$$1 = (scala.Predef.ArrowAssoc[Int](1).->[Int](2): @scala.unchecked) match { + | case scala.Tuple2((a @ _), (b @ _)) => scala.Tuple2.apply[Int, Int](a, b) | }; | val a = Test.this.x$$1._1; | val b = Test.this.x$$1._2 @@ -462,8 +474,8 @@ trait ClassPrintTests { */ @Test def testClassWithPatternMatchInAssignment = assertPrintedCode(sm""" |class Test { - | private[this] val x$$1 = (scala.collection.immutable.List.apply(1, 3, 5): @scala.unchecked) match { - | case scala.collection.immutable.List((one @ _), (three @ _), (five @ _)) => scala.Tuple3.apply(one, three, five) + | private[this] val x$$1 = (scala.collection.immutable.List.apply[scala.Int](1, 3, 5): @scala.unchecked) match { + | case scala.collection.immutable.List((one @ _), (three @ _), (five @ _)) => scala.Tuple3.apply[scala.Int, scala.Int, scala.Int](one, three, five) | }; | val one = Test.this.x$$1._1; | val three = Test.this.x$$1._2; @@ -626,7 +638,7 @@ trait ClassPrintTests { @Test def testObjectWithPatternMatch1 = assertPrintedCode(sm""" |object PM1 { - | scala.collection.immutable.List.apply(1, 2) match { + | scala.collection.immutable.List.apply[scala.Int](1, 2) match { | case (i @ _) => i | } |}""") @@ -715,7 +727,7 @@ trait ClassPrintTests { |}""", typedCode = sm""" |object PM5 { - | scala.collection.immutable.List.apply(1, 2) match { + | scala.collection.immutable.List.apply[Int](1, 2) match { | case scala.`package`.::((x @ _), (xs @ _)) => x | } |}""") @@ -756,7 +768,7 @@ trait ClassPrintTests { @Test def testObjectWithPatternMatch8 = assertPrintedCode(sm""" |{ | object Extractor { - | def unapply(i: scala.Int) = scala.Some.apply(i) + | def unapply(i: scala.Int) = scala.Some.apply[scala.Int](i) | }; | object PM9 { | 42 match { @@ -991,7 +1003,7 @@ trait ValAndDefPrintTests { @Test def testDefWithLazyVal2 = assertPrintedCode(sm""" |def a = { - | lazy val test = { + | lazy val test: Unit = { | scala.Predef.println(); | scala.Predef.println() | }; @@ -1161,4 +1173,17 @@ trait QuasiTreesPrintTests { |case class X(x: Int, s: String) { | def y = "test" |}""") + + @Test def testQuasiCaseClassWithTypes1 = assertTreeCode(q"""case class X(x: ${typeOf[Int]}, s: ${typeOf[String]}){ def y = "test" }""")(sm""" + |case class X(x: Int, s: String) { + | def y = "test" + |}""") + + @Test def testQuasiCaseClassWithTypes2 = assertTreeCode(q"""case class X(x: ${typeOf[Int]}, s: ${typeOf[String]}){ def y = "test" }""", typecheck = true)(sm""" + |{ + | case class X(x: Int, s: String) { + | def y = "test" + | }; + | () + |}""") }
\ No newline at end of file |