diff options
38 files changed, 1059 insertions, 207 deletions
diff --git a/src/compiler/scala/reflect/internal/JvmClassInfo.scala b/src/compiler/scala/reflect/internal/JvmClassInfo.scala new file mode 100644 index 0000000000..d47f51e512 --- /dev/null +++ b/src/compiler/scala/reflect/internal/JvmClassInfo.scala @@ -0,0 +1,440 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.reflect.internal + +import java.io.{ DataInput, InputStream, DataInputStream, ByteArrayInputStream, BufferedInputStream, FileInputStream } +import scala.tools.nsc.io.{ Directory } +import scala.reflect.NameTransformer.decode +import scala.tools.util.StringOps.trimTrailingSpace +import ConstantPool._ + +final case class JvmVersion(minorVersion: Int, majorVersion: Int) + +trait ClassfileModel { + type Result + type Entry + type InterfaceInfo + type MemberInfo + type AttributeInfo + type InnerClassInfo + + protected implicit def EntryArrayTag: ArrayTag[Entry] + protected implicit def InterfaceInfoArrayTag: ArrayTag[InterfaceInfo] + protected implicit def MemberInfoArrayTag: ArrayTag[MemberInfo] + protected implicit def AttributeInfoArrayTag: ArrayTag[AttributeInfo] + protected implicit def InnerClassInfoArrayTag: ArrayTag[InnerClassInfo] + + // These could be implemented to jump forward in the stream if the + // result is not wanted. + def readConstantPoolEntry(): Entry + def readInterface(): InterfaceInfo + def readMember(): MemberInfo + def readAttribute(): AttributeInfo + def readInnerClass(): InnerClassInfo + + def createInfo( + version: JvmVersion, + entries: Array[Entry], + flags: Int, + name: String, + superName: String, + interfaces: Array[InterfaceInfo], + fields: Array[MemberInfo], + methods: Array[MemberInfo], + attributes: Array[AttributeInfo] + ): Result +} + +abstract class StreamingClassfileModel extends ClassfileModel { + protected[this] val in: DataInput + private[this] var name: String = _ + private[this] var entries: Array[PoolEntry] = _ + + type Entry = PoolEntry + + // These translate null into "", it's less troublesome. + protected def nameAt(idx: Int) = entries(idx) match { + case x: Name_Info => stringAt(x.name_index).replace('/', '.') + case _ => "" + } + protected def stringAt(idx: Int) = entries(idx) match { + case x: Utf8_info => x.stringValue + case _ => "" + } + + protected def u4 = in.readInt + protected def u2 = in.readUnsignedShort.toChar + protected def u1 = in.readUnsignedByte + + // The constant_pool table is indexed from 1 to constant_pool_count−1. + protected def readConstantPool(): Array[Entry] = { + val count = u2 + val entries = new Array[Entry](count) + var i = 1 + while (i < count) { + val entry = readConstantPoolEntry() + entries(i) = entry + i += entry.width + } + entries + } + protected def readInterfaces() = { + val count = u2 + val interfaces = new Array[InterfaceInfo](count) + var i = 0 + while (i < count) { + interfaces(i) = readInterface() + i += 1 + } + interfaces + } + protected def readMembers() = { + val count = u2 + val arr = new Array[MemberInfo](count) + var i = 0 + while (i < count) { + arr(i) = readMember() + i += 1 + } + arr + } + protected def readAttributes(): Array[AttributeInfo] = { + val count = u2 + val arr = new Array[AttributeInfo](count) + var i = 0 + while (i < count) { + arr(i) = readAttribute() + i += 1 + } + arr + } + protected def readInnerClasses() = { + val count = u2 + val arr = new Array[InnerClassInfo](count) + var i = 0 + while (i < count) { + arr(i) = readInnerClass() + i += 1 + } + arr + } + protected def thisClass = name + + def parse() = { + assert(u4 == 0xCAFEBABE, "Bad magic number") + val version = JvmVersion(u2, u2) + this.entries = readConstantPool() + val flags = u2.toShort + this.name = nameAt(u2) + val superName = nameAt(u2) + val interfaces = readInterfaces() + val fields = readMembers() + val methods = readMembers() + val attributes = readAttributes() + + try createInfo(version, entries, flags, name, superName, interfaces, fields, methods, attributes) + finally entries = null + } +} + +abstract class ScalacClassfileModel extends StreamingClassfileModel { + type Result = JvmClassInfo + type InterfaceInfo = String + type MemberInfo = JvmMemberInfo + type AttributeInfo = JvmAttributeInfo + type InnerClassInfo = JvmInnerClassInfo + + protected implicit def EntryArrayTag = arrayTag[PoolEntry] + protected implicit def InterfaceInfoArrayTag = arrayTag[InterfaceInfo] + protected implicit def MemberInfoArrayTag = arrayTag[MemberInfo] + protected implicit def AttributeInfoArrayTag = arrayTag[AttributeInfo] + protected implicit def InnerClassInfoArrayTag = arrayTag[InnerClassInfo] + + def readConstantPoolEntry(): PoolEntry + def readInterface(): String + def readMember(): JvmMemberInfo + def readAttribute(): JvmAttributeInfo + def readInnerClass(): JvmInnerClassInfo + + def createInfo( + version: JvmVersion, + entries: Array[PoolEntry], + flags: Int, + name: String, + superName: String, + interfaces: Array[String], + fields: Array[JvmMemberInfo], + methods: Array[JvmMemberInfo], + attributes: Array[JvmAttributeInfo] + ): JvmClassInfo = new JvmClassInfo(name, superName, interfaces, fields, methods, attributes) +} + +class JvmClassInfoBuilder(protected[this] val in: DataInput) extends ScalacClassfileModel { + def readInterface(): InterfaceInfo = nameAt(u2) + def readMember(): JvmMemberInfo = new JvmMemberInfo(u2.toShort, stringAt(u2), stringAt(u2), readAttributes()) + def readInnerClass(): JvmInnerClassInfo = new JvmInnerClassInfo(thisClass, nameAt(u2), nameAt(u2), stringAt(u2), u2.toShort) + + def readConstantPoolEntry(): Entry = (u1: @annotation.switch) match { + case CONSTANT_Utf8 => new Utf8_info(in.readUTF) + case CONSTANT_Integer => new Integer_info(in.readInt) + case CONSTANT_Float => new Float_info(in.readFloat) + case CONSTANT_Long => new Long_info(in.readLong) + case CONSTANT_Double => new Double_info(in.readDouble) + case CONSTANT_Class => new Class_info(u2) + case CONSTANT_String => new String_info(u2) + case CONSTANT_Fieldref => new Fieldref_info(u2, u2) + case CONSTANT_Methodref => new Methodref_info(u2, u2) + case CONSTANT_InterfaceMethodref => new InterfaceMethodref_info(u2, u2) + case CONSTANT_NameAndType => new NameAndType_info(u2, u2) + } + + // field_info attributes: + // ConstantValue (§4.7.2), Synthetic (§4.7.8), Signature (§4.7.9), Deprecated (§4.7.15), + // RuntimeVisibleAnnotations (§4.7.16) and RuntimeInvisibleAnnotations (§4.7.17). + // + // method_info attributes: + // Code (§4.7.3), Exceptions (§4.7.5), Synthetic (§4.7.8), Signature (§4.7.9), Deprecated (§4.7.15), + // RuntimeVisibleAnnotations (§4.7.16), RuntimeInvisibleAnnotations (§4.7.17), RuntimeVisibleParameterAnnotations (§4.7.18), + // RuntimeInvisibleParameterAnnotations (§4.7.19) and AnnotationDefault (§4.7.20). + + def readAttribute(): AttributeInfo = stringAt(u2) match { + case "Signature" => u4 ; new SignatureAttr(stringAt(u2)) + case "InnerClasses" => u4 ; new InnerClassesAttr(readInnerClasses()) + case name => val bytes = new Array[Byte](u4) ; in.readFully(bytes) ; new GenericAttr(name, bytes) + } +} + +object Classify { + + /* + + + 4.2.2 Unqualified Names + +Names of methods, fields and local variables are stored as unqualified +names. Unqualified names must not contain the characters '.', ';', '[' +or '/'. Method names are further constrained so that, with the exception +of the special method names <init> and <clinit> (§3.9), they must not +contain the characters '<' or '>'. + + 4.3 Descriptors and Signatures + +A descriptor is a string representing the type of a field or method. +Descriptors are represented in the class file format using modified +UTF-8 strings (§4.4.7) and thus may be drawn, where not further +constrained, from the entire Unicode character set. A signature is a +string representing the generic type of a field or method, or generic +type information for a class declaration. +*/ + +} + +object ConstantPool { + type UShort = Char + + final val CONSTANT_Utf8 = 1 + final val CONSTANT_Integer = 3 + final val CONSTANT_Float = 4 + final val CONSTANT_Long = 5 + final val CONSTANT_Double = 6 + final val CONSTANT_Class = 7 + final val CONSTANT_String = 8 + final val CONSTANT_Fieldref = 9 + final val CONSTANT_Methodref = 10 + final val CONSTANT_InterfaceMethodref = 11 + final val CONSTANT_NameAndType = 12 + + abstract class Name_Info(tag: Byte) extends PoolEntry(tag) { + def name_index: UShort + } + abstract class Ref_Info(tag: Byte) extends PoolEntry(tag) { + def class_index: UShort + def name_and_type_index: UShort + } + class Class_info(val name_index: UShort) extends Name_Info(CONSTANT_Class) { } + class Double_info(val value: Double) extends PoolEntry(CONSTANT_Double) { + override def width = 2 + } + class Fieldref_info(val class_index: UShort, val name_and_type_index: UShort) extends Ref_Info(CONSTANT_Fieldref) + class Float_info(val value: Float) extends PoolEntry(CONSTANT_Float) + class Integer_info(val value: Int) extends PoolEntry(CONSTANT_Integer) + class InterfaceMethodref_info(val class_index: UShort, val name_and_type_index: UShort) extends Ref_Info(CONSTANT_InterfaceMethodref) + class Long_info(val value: Long) extends PoolEntry(CONSTANT_Long) { + override def width = 2 + } + class Methodref_info(val class_index: UShort, val name_and_type_index: UShort) extends Ref_Info(CONSTANT_Methodref) + class NameAndType_info(val name_index: UShort, val descriptor_index: UShort) extends Name_Info(CONSTANT_NameAndType) { + override def toString = "NameAndType #%s:#%s;".format(name_index, descriptor_index) + } + class String_info(val string_index: UShort) extends PoolEntry(CONSTANT_String) { } + class Utf8_info(override val stringValue: String) extends PoolEntry(CONSTANT_Utf8) { + override def toString = ("Asciz " + stringValue).trim + } + + abstract class PoolEntry(tag: Byte) { + def width = 1 + def stringValue: String = sys.error("Not a String-valued constant pool entry: " + this) + override def toString = ( + getClass.getName.split("[.$]").last + "/" + tag + ) + } + object NoEntry extends PoolEntry(-1) { } +} + +abstract class JvmInfo(attributes: Array[JvmAttributeInfo]) { + // def flags: Short + def name: String + + val signature = attributes collectFirst { case x: SignatureAttr => x.value } getOrElse "" + val innerClasses = attributes collectFirst { case x: InnerClassesAttr => x.value } getOrElse Array() +} + + +class JvmClassInfo( + val name: String, + val superName: String, + val interfaces: Array[String], + val fields: Array[JvmMemberInfo], + val methods: Array[JvmMemberInfo], + attributes: Array[JvmAttributeInfo] +) extends JvmInfo(attributes) { + + def members = fields ++ methods sortBy (_.decodedName) + def memberDescriptors = members map (_.toErasedString) + def memberSignatures = members filter (_.hasSignature) map (_.toGenericString) + def descriptorsString = if (memberDescriptors.nonEmpty) memberDescriptors.mkString("\n-- Member Descriptors --\n", "\n", "\n") else "" + def signaturesString = if (memberSignatures.nonEmpty) memberSignatures.mkString("\n-- Member Signatures --\n", "\n", "\n") else "" + def innersString = if (innerClasses.isEmpty) "" else innerClasses.mkString("\n-- Inner Classes --\n", "\n", "\n") + def membersString = descriptorsString + signaturesString + def extendsString = if (superName == "") "" else " extends " + superName + def implementsString = if (interfaces.isEmpty) "" else interfaces.mkString("Implements: ", ", ", "") + + private def group(label: String, xs: Traversable[(String, String)]) = + xs map { case (name, value) => line(label, name, value) } mkString "\n" + + private def line(label: String, name: String, data: String) = + trimTrailingSpace(" %-15s %30s %s".format(label, name, data)) + + override def toString = ( + List( + "class " + name + extendsString, + if (signature == "") "" else line("class sig", "", signature), + group("interface", interfaces map (x => (("", x)))), + (innerClasses map (ic => line(ic.kind, ic.innerName, ic.nestString))).sorted.mkString("\n"), + group("descriptor", members map (x => (x.name, x.descriptor))), + group("signature", members filter (_.hasSignature) map (x => (x.name, x.signature))) + ) map trimTrailingSpace filterNot (_ == "") mkString ("", "\n", "\n") + ) +} + +// method_info or field_info { +// u2 access_flags; +// u2 name_index; +// u2 descriptor_index; +// u2 attributes_count; +// attribute_info attributes[attributes_count]; +// } +class JvmMemberInfo( + val flags: Short, + val name: String, + val descriptor: String, + attributes: Array[JvmAttributeInfo] +) extends JvmInfo(attributes) { + def decodedName = decode(name) + def hasSignature = signature != "" + def toErasedString = "%-30s %s".format(decodedName, descriptor) + def toGenericString = "%-30s %s".format(decodedName, signature) + + override def toString = ( + if (hasSignature) toGenericString else toErasedString + ) +} + +abstract class JvmAttributeInfo { + def name: String + def value: Any +} +class GenericAttr(val name: String, val value: Array[Byte]) extends JvmAttributeInfo { + // attribute_info { + // u2 attribute_name_index; + // u4 attribute_length; + // u1 info[attribute_length]; + // } +} +class SignatureAttr(val value: String) extends JvmAttributeInfo { + def name = "Signature" +} +class InnerClassesAttr(val value: Array[JvmInnerClassInfo]) extends JvmAttributeInfo { + def name = "InnerClasses" +} + +// package foo { class Foo { class Bar } } +// +// javap would say +// Bar = class foo.Foo$Bar of class foo.Foo +// which is translated as +// innerClass = foo.Foo$Bar +// outerClass = foo.Foo +// innerName = Bar + +class JvmInnerClassInfo( + thisClass: String, // classfile which is being parsed + val innerClass: String, // the full name of the inner/nested class + val outerClass: String, // the full name of the outer class - must be a prefix of innerClass + val innerName: String, // the simple name of the inner class - should (?) be a suffix of innerClass + val flags: Short // flags +) { + val isEntryOfEnclosingClass = !isAnonymousClass && (innerClass == thisClass) + val isEntryOfNestedClass = !isAnonymousClass && (outerClass == thisClass) + + def isTopLevelClass = outerClass == "" + def isAnonymousClass = innerName == "" + def isMemberClass = !isTopLevelClass + + def kind = ( + if (isEntryOfEnclosingClass) "inner/enclosing" + else if (isEntryOfNestedClass) "inner/nested" + else if (isAnonymousClass) "inner/anon" + else "inner" + ) + def nestString = ( + if (isEntryOfEnclosingClass) "enclosing class: " + outerClass + else if (isEntryOfNestedClass) "member class: " + innerClass + else if (isAnonymousClass) "anonymous class: " + innerClass + else innerClass + " in " + outerClass + ) + override def toString = innerName + "=" + nestString +} + +object JvmClassInfo { + private def classFiles(path: String) = + Directory(path).deepFiles filter (_ hasExtension "class") + + def classInfoMap(path: String): Map[String, JvmClassInfo] = { + classFiles(path) map (f => (f.path, JvmClassInfo fromFile f.jfile)) toMap + } + def classInfoList(path: String): List[(String, JvmClassInfo)] = { + classInfoMap(path).toList sortBy (_._1) + } + + def fromFile(file: java.io.File) = + fromStream(new BufferedInputStream(new FileInputStream(file))) + + def fromBytes(bytes: Array[Byte]) = + fromStream(new ByteArrayInputStream(bytes)) + + def fromPath(path: String) = + fromStream(new BufferedInputStream(new FileInputStream(path))) + + def fromStream(in0: InputStream) = { + val in = new DataInputStream(in0) + try fromDataInput(in) finally in.close() + } + + def fromDataInput(in: DataInput): JvmClassInfo = { + new JvmClassInfoBuilder(in) parse + } +} diff --git a/src/compiler/scala/reflect/makro/runtime/Context.scala b/src/compiler/scala/reflect/makro/runtime/Context.scala index 15a4118b85..ca02822788 100644 --- a/src/compiler/scala/reflect/makro/runtime/Context.scala +++ b/src/compiler/scala/reflect/makro/runtime/Context.scala @@ -14,7 +14,8 @@ abstract class Context extends scala.reflect.makro.Context with Settings with Symbols with Typers - with Util { + with Util + with Traces { val mirror: Global diff --git a/src/compiler/scala/reflect/makro/runtime/Traces.scala b/src/compiler/scala/reflect/makro/runtime/Traces.scala new file mode 100644 index 0000000000..6b61842316 --- /dev/null +++ b/src/compiler/scala/reflect/makro/runtime/Traces.scala @@ -0,0 +1,8 @@ +package scala.reflect.makro +package runtime + +trait Traces extends util.Traces { + self: Context => + + def globalSettings = mirror.settings +} diff --git a/src/compiler/scala/reflect/makro/runtime/Typers.scala b/src/compiler/scala/reflect/makro/runtime/Typers.scala index 98dbd65b72..c61e492250 100644 --- a/src/compiler/scala/reflect/makro/runtime/Typers.scala +++ b/src/compiler/scala/reflect/makro/runtime/Typers.scala @@ -9,8 +9,7 @@ trait Typers { def openImplicits: List[(Type, Tree)] = callsiteTyper.context.openImplicits def typeCheck(tree: Tree, pt: Type = mirror.WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree = { - def trace(msg: Any) = if (mirror.settings.Ymacrodebug.value) println(msg) - trace("typechecking %s with expected type %s, implicit views = %s, macros = %s".format(tree, pt, !withImplicitViewsDisabled, !withMacrosDisabled)) + macroLogVerbose("typechecking %s with expected type %s, implicit views = %s, macros = %s".format(tree, pt, !withImplicitViewsDisabled, !withMacrosDisabled)) val wrapper1 = if (!withImplicitViewsDisabled) (callsiteTyper.context.withImplicitsEnabled[Tree] _) else (callsiteTyper.context.withImplicitsDisabled[Tree] _) val wrapper2 = if (!withMacrosDisabled) (callsiteTyper.context.withMacrosEnabled[Tree] _) else (callsiteTyper.context.withMacrosDisabled[Tree] _) def wrapper (tree: => Tree) = wrapper1(wrapper2(tree)) @@ -21,25 +20,24 @@ trait Typers { // (also see reflect.runtime.ToolBoxes.typeCheckExpr for a workaround that might work for you) wrapper(callsiteTyper.silent(_.typed(tree, mirror.analyzer.EXPRmode, pt)) match { case mirror.analyzer.SilentResultValue(result) => - trace(result) + macroLogVerbose(result) result case error @ mirror.analyzer.SilentTypeError(_) => - trace(error.err.errMsg) + macroLogVerbose(error.err.errMsg) if (!silent) throw new mirror.TypeError(error.err.errPos, error.err.errMsg) mirror.EmptyTree }) } def inferImplicitValue(pt: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, pos: Position = enclosingPosition): Tree = { - def trace(msg: Any) = if (mirror.settings.Ymacrodebug.value) println(msg) - trace("inferring implicit value of type %s, macros = %s".format(pt, !withMacrosDisabled)) + macroLogVerbose("inferring implicit value of type %s, macros = %s".format(pt, !withMacrosDisabled)) import mirror.analyzer.SearchResult val context = callsiteTyper.context val wrapper1 = if (!withMacrosDisabled) (context.withMacrosEnabled[SearchResult] _) else (context.withMacrosDisabled[SearchResult] _) def wrapper (inference: => SearchResult) = wrapper1(inference) wrapper(mirror.analyzer.inferImplicit(mirror.EmptyTree, pt, true, false, context, !silent, pos)) match { case failure if failure.tree.isEmpty => - trace("implicit search has failed. to find out the reason, turn on -Xlog-implicits") + macroLogVerbose("implicit search has failed. to find out the reason, turn on -Xlog-implicits") if (context.hasErrors) throw new mirror.TypeError(context.errBuffer.head.errPos, context.errBuffer.head.errMsg) mirror.EmptyTree case success => @@ -48,8 +46,7 @@ trait Typers { } def inferImplicitView(tree: Tree, from: Type, to: Type, silent: Boolean = true, withMacrosDisabled: Boolean = false, reportAmbiguous: Boolean = true, pos: Position = enclosingPosition): Tree = { - def trace(msg: Any) = if (mirror.settings.Ymacrodebug.value) println(msg) - trace("inferring implicit view from %s to %s for %s, macros = %s, reportAmbiguous = %s".format(from, to, tree, !withMacrosDisabled, reportAmbiguous)) + macroLogVerbose("inferring implicit view from %s to %s for %s, macros = %s, reportAmbiguous = %s".format(from, to, tree, !withMacrosDisabled, reportAmbiguous)) import mirror.analyzer.SearchResult val context = callsiteTyper.context val wrapper1 = if (!withMacrosDisabled) (context.withMacrosEnabled[SearchResult] _) else (context.withMacrosDisabled[SearchResult] _) @@ -58,7 +55,7 @@ trait Typers { val viewTpe = mirror.TypeRef(fun1.typeConstructor.prefix, fun1, List(from, to)) wrapper(mirror.analyzer.inferImplicit(tree, viewTpe, reportAmbiguous, true, context, !silent, pos)) match { case failure if failure.tree.isEmpty => - trace("implicit search has failed. to find out the reason, turn on -Xlog-implicits") + macroLogVerbose("implicit search has failed. to find out the reason, turn on -Xlog-implicits") if (context.hasErrors) throw new mirror.TypeError(context.errBuffer.head.errPos, context.errBuffer.head.errMsg) mirror.EmptyTree case success => diff --git a/src/compiler/scala/reflect/makro/util/Traces.scala b/src/compiler/scala/reflect/makro/util/Traces.scala new file mode 100644 index 0000000000..2363cc4bac --- /dev/null +++ b/src/compiler/scala/reflect/makro/util/Traces.scala @@ -0,0 +1,18 @@ +package scala.reflect.makro +package util + +trait Traces { + def globalSettings: tools.nsc.Settings + + // [Eugene] lots of ways to log: + // 1) trace(...) + // 2) log(...) + // 3) if (foo) { doStuff(); includingSomeLogs(); } + // what is the conventional way of unifying this? + val macroDebugLite = globalSettings.YmacrodebugLite.value + val macroDebugVerbose = globalSettings.YmacrodebugVerbose.value + val macroTraceLite = scala.tools.nsc.util.trace when (macroDebugLite || macroDebugVerbose) + val macroTraceVerbose = scala.tools.nsc.util.trace when macroDebugVerbose + @inline final def macroLogLite(msg: => Any) { if (macroDebugLite || macroDebugVerbose) println(msg) } + @inline final def macroLogVerbose(msg: => Any) { if (macroDebugVerbose) println(msg) } +}
\ No newline at end of file diff --git a/src/compiler/scala/reflect/reify/Errors.scala b/src/compiler/scala/reflect/reify/Errors.scala index 1a881455f2..4466f281b8 100644 --- a/src/compiler/scala/reflect/reify/Errors.scala +++ b/src/compiler/scala/reflect/reify/Errors.scala @@ -10,8 +10,7 @@ trait Errors { import mirror._ import definitions._ - lazy val defaultErrorPosition: Position = - mirror.analyzer.openMacros.find(c => c.macroApplication.pos != NoPosition).map(_.macroApplication.pos).getOrElse(NoPosition) + def defaultErrorPosition = analyzer.enclosingMacroPosition // expected errors: these can happen if the user casually writes whatever.reify(...) // hence we don't crash here, but nicely report a typechecking error and bail out asap diff --git a/src/compiler/scala/reflect/reify/codegen/Types.scala b/src/compiler/scala/reflect/reify/codegen/Types.scala index 8fa24c5b00..1f336f4b0f 100644 --- a/src/compiler/scala/reflect/reify/codegen/Types.scala +++ b/src/compiler/scala/reflect/reify/codegen/Types.scala @@ -99,7 +99,7 @@ trait Types { // if this fails, it might produce the dreaded "erroneous or inaccessible type" error // to find out the whereabouts of the error run scalac with -Ydebug if (reifyDebug) println("launching implicit search for %s.%s[%s]".format(prefix, tagClass.name, tpe)) - typer.resolveTypeTag(positionBearer.pos, prefix.tpe, tpe, concrete) match { + typer.resolveTypeTag(defaultErrorPosition, prefix.tpe, tpe, concrete) match { case failure if failure.isEmpty => if (reifyDebug) println("implicit search was fruitless") EmptyTree diff --git a/src/compiler/scala/reflect/reify/codegen/Util.scala b/src/compiler/scala/reflect/reify/codegen/Util.scala index 8d7cf39ff7..bb369a1adb 100644 --- a/src/compiler/scala/reflect/reify/codegen/Util.scala +++ b/src/compiler/scala/reflect/reify/codegen/Util.scala @@ -14,8 +14,6 @@ trait Util { object reifiedNodePrinters extends { val global: mirror.type = mirror } with tools.nsc.ast.NodePrinters with NodePrinters val reifiedNodeToString = reifiedNodePrinters.reifiedNodeToString - val positionBearer = mirror.analyzer.openMacros.find(c => c.macroApplication.pos != NoPosition).map(_.macroApplication).getOrElse(EmptyTree).asInstanceOf[Tree] - def reifyList(xs: List[Any]): Tree = mkList(xs map reify) diff --git a/src/compiler/scala/reflect/reify/package.scala b/src/compiler/scala/reflect/reify/package.scala index fa11c6313d..3f9d6200cd 100644 --- a/src/compiler/scala/reflect/reify/package.scala +++ b/src/compiler/scala/reflect/reify/package.scala @@ -31,8 +31,12 @@ package object reify { def reifyErasure(global: Global)(typer0: global.analyzer.Typer, tpe: global.Type, concrete: Boolean = true): global.Tree = { import global._ import definitions._ - val positionBearer = analyzer.openMacros.find(_.macroApplication.pos != NoPosition).map(_.macroApplication).getOrElse(EmptyTree).asInstanceOf[Tree] - val inScope = typer0.context.withMacrosDisabled(typer0.resolveErasureTag(positionBearer.pos, tpe, concrete = concrete), typer0.resolveArrayTag(positionBearer.pos, tpe)) + import analyzer.enclosingMacroPosition + + def erasureTagInScope = typer0.context.withMacrosDisabled(typer0.resolveErasureTag(enclosingMacroPosition, tpe, concrete = concrete)) + def arrayTagInScope = typer0.context.withMacrosDisabled(typer0.resolveArrayTag(enclosingMacroPosition, tpe)) + val inScope = (erasureTagInScope, arrayTagInScope) + inScope match { case (success, _) if !success.isEmpty => Select(success, nme.erasure) @@ -44,7 +48,7 @@ package object reify { val componentErasure = reifyErasure(global)(typer0, componentTpe, concrete) gen.mkMethodCall(arrayClassMethod, List(componentErasure)) } else { - if (tpe.isSpliceable && concrete) throw new ReificationError(positionBearer.pos, "tpe %s is an unresolved spliceable type".format(tpe)) + if (tpe.isSpliceable && concrete) throw new ReificationError(enclosingMacroPosition, "tpe %s is an unresolved spliceable type".format(tpe)) var erasure = tpe.erasure if (tpe.typeSymbol.isDerivedValueClass && global.phase.id < global.currentRun.erasurePhase.id) erasure = tpe gen.mkNullaryCall(Predef_classOf, List(erasure)) diff --git a/src/compiler/scala/tools/cmd/program/DumpClass.scala b/src/compiler/scala/tools/cmd/program/DumpClass.scala new file mode 100644 index 0000000000..a583f1d3ea --- /dev/null +++ b/src/compiler/scala/tools/cmd/program/DumpClass.scala @@ -0,0 +1,40 @@ +/* NEST (New Scala Test) + * Copyright 2007-2011 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools +package cmd +package program + +import scala.reflect.internal.JvmClassInfo +import scala.tools.nsc.io.Directory + +object DumpClass { + private val usage = """ + |Usage: dump-class [options] <path> <path> ... + | + |Parses and dumps the bytecode of all classes found at the given paths. + |""".stripMargin + + private val unaryOps = List( + "signatures" -> "dump signatures" + ) + private val info = Simple.scalaProgramInfo("dump-class", usage) + private val spec = Simple(info, unaryOps, Nil, null) + + def deepInfos(dir: String) = { + val files = Directory(dir).deepFiles.toList filter (_ hasExtension "class") + files.sortBy(_.path) map (f => (f.path, JvmClassInfo fromPath f.path)) + } + + def main(args: Array[String]): Unit = { + val runner = spec instance args + import runner._ + + if (args.isEmpty) + println(usage) + else + (residualArgs flatMap deepInfos) sortBy (_._1) map (_._2) foreach println + } +} diff --git a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala index 108c8fcbfa..5d849b9622 100644 --- a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala @@ -63,10 +63,14 @@ abstract class NodePrinters { } trait DefaultPrintAST extends PrintAST { + val printPos = settings.Xprintpos.value || settings.Yposdebug.value + + def showNameAndPos(tree: NameTree) = showPosition(tree) + showName(tree.name) def showDefTreeName(tree: DefTree) = showName(tree.name) + def showPosition(tree: Tree) = if (printPos) tree.pos.show else "" def showFlags(tree: MemberDef) = flagsToString(tree.symbol.flags | tree.mods.flags) - def showLiteral(lit: Literal) = lit.value.escapedStringValue - def showTypeTree(tt: TypeTree) = "<tpt>" + emptyOrComment(showType(tt)) + def showLiteral(lit: Literal) = showPosition(lit) + lit.value.escapedStringValue + def showTypeTree(tt: TypeTree) = showPosition(tt) + "<tpt>" + emptyOrComment(showType(tt)) def showName(name: Name) = name match { case nme.EMPTY | tpnme.EMPTY => "<empty>" case name => "\"" + name + "\"" @@ -97,17 +101,21 @@ abstract class NodePrinters { private var level = 0 def showName(name: Name): String + def showPosition(tree: Tree): String + def showNameAndPos(tree: NameTree): String def showDefTreeName(defTree: DefTree): String def showFlags(tree: MemberDef): String def showLiteral(lit: Literal): String def showTypeTree(tt: TypeTree): String def showAttributes(tree: Tree): String // symbol and type - def showRefTreeName(tree: Tree): String = tree match { - case SelectFromTypeTree(qual, name) => showRefTreeName(qual) + "#" + showName(name) - case Select(qual, name) => showRefTreeName(qual) + "." + showName(name) - case Ident(name) => showName(name) - case _ => "" + tree + def showRefTreeName(tree: Tree): String = { + tree match { + case SelectFromTypeTree(qual, name) => showRefTreeName(qual) + "#" + showName(name) + case Select(qual, name) => showRefTreeName(qual) + "." + showName(name) + case id @ Ident(name) => showNameAndPos(id) + case _ => "" + tree + } } def showRefTree(tree: RefTree): String = { def prefix0 = showRefTreeName(tree.qualifier) @@ -116,7 +124,7 @@ abstract class NodePrinters { case Select(_, _) => prefix0 + "." case _ => "" }) - prefix + showName(tree.name) + emptyOrComment(showAttributes(tree)) + prefix + showNameAndPos(tree) + emptyOrComment(showAttributes(tree)) } def emptyOrComment(s: String) = if (s == "") "" else " // " + s @@ -191,8 +199,9 @@ abstract class NodePrinters { } } + def treePrefix(tree: Tree) = showPosition(tree) + tree.printingPrefix def printMultiline(tree: Tree)(body: => Unit) { - printMultiline(tree.printingPrefix, showAttributes(tree))(body) + printMultiline(treePrefix(tree), showAttributes(tree))(body) } def printMultiline(prefix: String, comment: String)(body: => Unit) { printLine(prefix + "(", comment) @@ -218,10 +227,12 @@ abstract class NodePrinters { } def printSingle(tree: Tree, name: Name) { - println(tree.printingPrefix + "(" + showName(name) + ")" + showAttributes(tree)) + println(treePrefix(tree) + "(" + showName(name) + ")" + showAttributes(tree)) } def traverse(tree: Tree) { + showPosition(tree) + tree match { case AppliedTypeTree(tpt, args) => applyCommon(tree, tpt, args) case ApplyDynamic(fun, args) => applyCommon(tree, fun, args) @@ -230,6 +241,19 @@ abstract class NodePrinters { case Throw(Ident(name)) => printSingle(tree, name) + case b @ Bind(name, body) => + printMultiline(tree) { + println(showDefTreeName(b)) + traverse(body) + } + + case ld @ LabelDef(name, params, rhs) => + printMultiline(tree) { + showNameAndPos(ld) + traverseList("()", "params")(params) + traverse(rhs) + } + case Function(vparams, body) => printMultiline(tree) { traverseList("()", "parameter")(vparams) @@ -309,7 +333,7 @@ abstract class NodePrinters { val ps0 = parents map { p => if (p.tpe eq null) p match { case x: RefTree => showRefTree(x) - case x => "" + x + case x => showPosition(x) + x } else showName(newTypeName(p.tpe.typeSymbol.fullName)) } @@ -353,7 +377,7 @@ abstract class NodePrinters { case _ => tree match { case t: RefTree => println(showRefTree(t)) - case t if t.productArity == 0 => println(tree.printingPrefix) + case t if t.productArity == 0 => println(treePrefix(t)) case t => printMultiline(tree)(tree.productIterator foreach traverseAny) } } diff --git a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala index 93fa9a60f6..f702f44338 100755 --- a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala @@ -397,7 +397,7 @@ trait MarkupParsers { /** xScalaPatterns ::= patterns */ - def xScalaPatterns: List[Tree] = escapeToScala(parser.seqPatterns(), "pattern") + def xScalaPatterns: List[Tree] = escapeToScala(parser.xmlSeqPatterns(), "pattern") def reportSyntaxError(pos: Int, str: String) = parser.syntaxError(pos, str) def reportSyntaxError(str: String) { diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index bca1cc4596..337ca7671c 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1705,11 +1705,11 @@ self => * was threaded through methods as boolean seqOK. */ trait SeqContextSensitive extends PatternContextSensitive { - /** Returns Some(tree) if it finds a star and prematurely ends parsing. - * This is an artifact of old implementation which has proven difficult - * to cleanly extract. - */ - def interceptStarPattern(top: Tree): Option[Tree] + // is a sequence pattern _* allowed? + def isSequenceOK: Boolean + + // are we in an XML pattern? + def isXML: Boolean = false def functionArgType(): Tree = argType() def argType(): Tree = { @@ -1796,45 +1796,98 @@ self => /** {{{ * Pattern3 ::= SimplePattern * | SimplePattern {Id [nl] SimplePattern} - * SeqPattern3 ::= SeqSimplePattern [ `*' | `?' | `+' ] - * | SeqSimplePattern {Id [nl] SeqSimplePattern} * }}} */ def pattern3(): Tree = { + var top = simplePattern(badPattern3) + // after peekahead + def acceptWildStar() = atPos(top.pos.startOrPoint, in.prev.offset)(Star(stripParens(top))) + def peekahead() = { + in.prev copyFrom in + in.nextToken() + } + def pushback() = { + in.next copyFrom in + in copyFrom in.prev + } + // See SI-3189, SI-4832 for motivation. Cf SI-3480 for counter-motivation. + // TODO: dredge out the remnants of regexp patterns. + // /{/ peek for _*) or _*} (for xml escape) + if (isSequenceOK) { + top match { + case Ident(nme.WILDCARD) if (isRawStar) => + peekahead() + in.token match { + case RBRACE if (isXML) => return acceptWildStar() + case RPAREN if (!isXML) => return acceptWildStar() + case _ => pushback() + } + case _ => + } + } val base = opstack - var top = simplePattern() - interceptStarPattern(top) foreach { x => return x } - while (isIdent && in.name != raw.BAR) { - top = reduceStack( - false, base, top, precedence(in.name), treeInfo.isLeftAssoc(in.name)) + top = reduceStack(false, base, top, precedence(in.name), treeInfo.isLeftAssoc(in.name)) val op = in.name opstack = OpInfo(top, op, in.offset) :: opstack ident() - top = simplePattern() + top = simplePattern(badPattern3) } stripParens(reduceStack(false, base, top, 0, true)) } + def badPattern3(): Tree = { + def isComma = in.token == COMMA + def isAnyBrace = in.token == RPAREN || in.token == RBRACE + val badStart = "illegal start of simple pattern" + // better recovery if don't skip delims of patterns + var skip = !(isComma || isAnyBrace) + val msg = if (!opstack.isEmpty && opstack.head.operator == nme.STAR) { + opstack.head.operand match { + case Ident(nme.WILDCARD) => + if (isSequenceOK && isComma) + "bad use of _* (a sequence pattern must be the last pattern)" + else if (isSequenceOK && isAnyBrace) { + skip = true // do skip bad paren; scanner may skip bad brace already + "bad brace or paren after _*" + } else if (!isSequenceOK && isAnyBrace) + "bad use of _* (sequence pattern not allowed)" + else badStart + case _ => + if (isSequenceOK && isAnyBrace) + "use _* to match a sequence" + else if (isComma || isAnyBrace) + "trailing * is not a valid pattern" + else badStart + } + } else { + badStart + } + syntaxErrorOrIncomplete(msg, skip) + errorPatternTree + } /** {{{ * SimplePattern ::= varid * | `_' * | literal * | XmlPattern - * | StableId [TypeArgs] [`(' [SeqPatterns] `)'] + * | StableId /[TypeArgs]/ [`(' [Patterns] `)'] + * | StableId [`(' [Patterns] `)'] + * | StableId [`(' [Patterns] `,' [varid `@'] `_' `*' `)'] * | `(' [Patterns] `)' - * SimpleSeqPattern ::= varid - * | `_' - * | literal - * | XmlPattern - * | `<' xLiteralPattern - * | StableId [TypeArgs] [`(' [SeqPatterns] `)'] - * | `(' [SeqPatterns] `)' * }}} * * XXX: Hook for IDE */ def simplePattern(): Tree = { + // simple diagnostics for this entry point + def badStart(): Tree = { + syntaxErrorOrIncomplete("illegal start of simple pattern", true) + errorPatternTree + } + simplePattern(badStart) + } + def simplePattern(onError: () => Tree): Tree = { val start = in.offset in.token match { case IDENTIFIER | BACKQUOTED_IDENT | THIS => @@ -1867,8 +1920,7 @@ self => case XMLSTART => xmlLiteralPattern() case _ => - syntaxErrorOrIncomplete("illegal start of simple pattern", true) - errorPatternTree + onError() } } } @@ -1879,16 +1931,16 @@ self => } /** The implementation for parsing inside of patterns at points where sequences are allowed. */ object seqOK extends SeqContextSensitive { - // See ticket #3189 for the motivation for the null check. - // TODO: dredge out the remnants of regexp patterns. - // ... and now this is back the way it was because it caused #3480. - def interceptStarPattern(top: Tree): Option[Tree] = - if (isRawStar) Some(atPos(top.pos.startOrPoint, in.skipToken())(Star(stripParens(top)))) - else None + val isSequenceOK = true } /** The implementation for parsing inside of patterns at points where sequences are disallowed. */ object noSeq extends SeqContextSensitive { - def interceptStarPattern(top: Tree) = None + val isSequenceOK = false + } + /** For use from xml pattern, where sequence is allowed and encouraged. */ + object xmlSeqOK extends SeqContextSensitive { + val isSequenceOK = true + override val isXML = true } /** These are default entry points into the pattern context sensitive methods: * they are all initiated from non-pattern context. @@ -1902,7 +1954,8 @@ self => /** Default entry points into some pattern contexts. */ def pattern(): Tree = noSeq.pattern() def patterns(): List[Tree] = noSeq.patterns() - def seqPatterns(): List[Tree] = seqOK.patterns() // Also called from xml parser + def seqPatterns(): List[Tree] = seqOK.patterns() + def xmlSeqPatterns(): List[Tree] = xmlSeqOK.patterns() // Called from xml parser def argumentPatterns(): List[Tree] = inParens { if (in.token == RPAREN) Nil else seqPatterns() diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 6a5c4c92bc..91e31cae97 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -138,7 +138,7 @@ trait ScalaSettings extends AbsScalaSettings val termConflict = ChoiceSetting ("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error") val inline = BooleanSetting ("-Yinline", "Perform inlining when possible.") val inlineHandlers = BooleanSetting ("-Yinline-handlers", "Perform exception handler inlining when possible.") - val YinlinerWarnings= BooleanSetting ("-Yinline-warnings", "Emit inlining warnings. (Normally surpressed due to high volume)") + val YinlinerWarnings= BooleanSetting ("-Yinline-warnings", "Emit inlining warnings. (Normally surpressed due to high volume)") val Xlinearizer = ChoiceSetting ("-Ylinearizer", "which", "Linearizer to use", List("normal", "dfs", "rpo", "dump"), "rpo") val log = PhasesSetting ("-Ylog", "Log operations during") val Ylogcp = BooleanSetting ("-Ylog-classpath", "Output information about what classpath is being applied.") @@ -172,7 +172,6 @@ trait ScalaSettings extends AbsScalaSettings val YrichExes = BooleanSetting ("-Yrich-exceptions", "Fancier exceptions. Set source search path with -D" + sys.SystemProperties.traceSourcePath.key) val Ybuilderdebug = ChoiceSetting ("-Ybuilder-debug", "manager", "Compile using the specified build manager.", List("none", "refined", "simple"), "none") val Yreifycopypaste = BooleanSetting ("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.") - val Ymacrocopypaste = BooleanSetting ("-Ymacro-copypaste", "Dump macro expansions in copypasteable representation.") val Yreplsync = BooleanSetting ("-Yrepl-sync", "Do not use asynchronous code for repl startup") val Ynotnull = BooleanSetting ("-Ynotnull", "Enable (experimental and incomplete) scala.NotNull.") val YmethodInfer = BooleanSetting ("-Yinfer-argument-types", "Infer types for arguments of overriden methods.") @@ -193,7 +192,8 @@ trait ScalaSettings extends AbsScalaSettings val Yidedebug = BooleanSetting("-Yide-debug", "Generate, validate and output trees using the interactive compiler.") val Yinferdebug = BooleanSetting("-Yinfer-debug", "Trace type inference and implicit search.") val Yissuedebug = BooleanSetting("-Yissue-debug", "Print stack traces when a context issues an error.") - val Ymacrodebug = BooleanSetting("-Ymacro-debug", "Trace macro-related activities: compilation, generation of synthetics, classloading, expansion, exceptions.") + val YmacrodebugLite = BooleanSetting("-Ymacro-debug-lite", "Trace essential macro-related activities.") + val YmacrodebugVerbose = BooleanSetting("-Ymacro-debug-verbose", "Trace all macro-related activities: compilation, generation of synthetics, classloading, expansion, exceptions.") val Ypmatdebug = BooleanSetting("-Ypmat-debug", "Trace all pattern matcher activity.") val Yposdebug = BooleanSetting("-Ypos-debug", "Trace position validation.") val Yreifydebug = BooleanSetting("-Yreify-debug", "Trace reification.") diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index 14f09d9dc9..1b8a43bf27 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -10,6 +10,7 @@ import scala.compat.Platform.EOL import scala.reflect.makro.runtime.{Context => MacroContext} import scala.reflect.runtime.Mirror import util.Statistics._ +import scala.reflect.makro.util._ /** * Code to deal with macros, namely with: @@ -36,13 +37,12 @@ import util.Statistics._ * (Expr(elems)) * (TypeTag(Int)) */ -trait Macros { self: Analyzer => +trait Macros extends Traces { + self: Analyzer => + import global._ import definitions._ - - val macroDebug = settings.Ymacrodebug.value - val macroCopypaste = settings.Ymacrocopypaste.value - val macroTrace = scala.tools.nsc.util.trace when macroDebug + def globalSettings = global.settings val globalMacroCache = collection.mutable.Map[Any, Any]() val perRunMacroCache = perRunCaches.newMap[Symbol, collection.mutable.Map[Any, Any]] @@ -136,11 +136,11 @@ trait Macros { self: Analyzer => } import SigGenerator._ - macroTrace("generating macroImplSigs for: ")(macroDef) - macroTrace("tparams are: ")(tparams) - macroTrace("vparamss are: ")(vparamss) - macroTrace("retTpe is: ")(retTpe) - macroTrace("macroImplSigs are: ")(paramsss, implRetTpe) + macroTraceVerbose("generating macroImplSigs for: ")(macroDef) + macroTraceVerbose("tparams are: ")(tparams) + macroTraceVerbose("vparamss are: ")(vparamss) + macroTraceVerbose("retTpe is: ")(retTpe) + macroTraceVerbose("macroImplSigs are: ")(paramsss, implRetTpe) } private def transformTypeTagEvidenceParams(paramss: List[List[Symbol]], transform: (Symbol, Symbol) => Option[Symbol]): List[List[Symbol]] = { @@ -183,7 +183,7 @@ trait Macros { self: Analyzer => */ def typedMacroBody(typer: Typer, ddef: DefDef): Tree = { import typer.context - if (macroDebug) println("typechecking macro def %s at %s".format(ddef.symbol, ddef.pos)) + macroLogVerbose("typechecking macro def %s at %s".format(ddef.symbol, ddef.pos)) if (!typer.checkFeature(ddef.pos, MacrosFeature, immediate = true)) { ddef.symbol setFlag IS_ERROR @@ -267,7 +267,7 @@ trait Macros { self: Analyzer => val rhs = ddef.rhs validatePreTyper(rhs) - if (hasErrors) macroTrace("macro def failed to satisfy trivial preconditions: ")(macroDef) + if (hasErrors) macroTraceVerbose("macro def failed to satisfy trivial preconditions: ")(macroDef) // we use typed1 instead of typed, because otherwise adapt is going to mess us up // if adapt sees <qualifier>.<method>, it will want to perform eta-expansion and will fail @@ -284,12 +284,7 @@ trait Macros { self: Analyzer => case Success(expanded) => try { val typechecked = typer.typed1(expanded, EXPRmode, WildcardType) - if (macroDebug) { - println("typechecked1:") - println(typechecked) - println(showRaw(typechecked)) - } - + macroLogVerbose("typechecked1:%n%s%n%s".format(typechecked, showRaw(typechecked))) typechecked } finally { openMacros = openMacros.tail @@ -312,7 +307,7 @@ trait Macros { self: Analyzer => var rhs1 = typecheckRhs(rhs) def typecheckedWithErrors = (rhs1 exists (_.isErroneous)) || reporter.ERROR.count != prevNumErrors hasErrors = hasErrors || typecheckedWithErrors - if (typecheckedWithErrors) macroTrace("body of a macro def failed to typecheck: ")(ddef) + if (typecheckedWithErrors) macroTraceVerbose("body of a macro def failed to typecheck: ")(ddef) val macroImpl = rhs1.symbol macroDef withAnnotation AnnotationInfo(MacroImplAnnotation.tpe, List(rhs1), Nil) @@ -330,7 +325,7 @@ trait Macros { self: Analyzer => validatePostTyper(rhs1) } if (hasErrors) - macroTrace("macro def failed to satisfy trivial preconditions: ")(macroDef) + macroTraceVerbose("macro def failed to satisfy trivial preconditions: ")(macroDef) } if (!hasErrors) { @@ -352,7 +347,7 @@ trait Macros { self: Analyzer => compatibilityError("number of parameter sections differ") def checkSubType(slot: String, reqtpe: Type, acttpe: Type): Unit = { - val ok = if (macroDebug) { + val ok = if (macroDebugVerbose) { if (reqtpe eq acttpe) println(reqtpe + " <: " + acttpe + "?" + EOL + "true") withTypesExplained(reqtpe <:< acttpe) } else reqtpe <:< acttpe @@ -437,7 +432,7 @@ trait Macros { self: Analyzer => val implicitParams = actparamss.flatten filter (_.isImplicit) if (implicitParams.length > 0) { reportError(implicitParams.head.pos, "macro implementations cannot have implicit parameters other than TypeTag evidences") - macroTrace("macro def failed to satisfy trivial preconditions: ")(macroDef) + macroTraceVerbose("macro def failed to satisfy trivial preconditions: ")(macroDef) } if (!hasErrors) { @@ -458,9 +453,9 @@ trait Macros { self: Analyzer => "\n found : "+showMeth(actparamss, actres, false)+ "\n"+addendum) - macroTrace("considering " + reqparamsss.length + " possibilities of compatible macro impl signatures for macro def: ")(ddef.name) + macroTraceVerbose("considering " + reqparamsss.length + " possibilities of compatible macro impl signatures for macro def: ")(ddef.name) val results = reqparamsss map (checkCompatibility(_, actparamss, reqres, actres)) - if (macroDebug) (reqparamsss zip results) foreach { case (reqparamss, result) => + if (macroDebugVerbose) (reqparamsss zip results) foreach { case (reqparamss, result) => println("%s %s".format(if (result.isEmpty) "[ OK ]" else "[FAILED]", reqparamss)) result foreach (errorMsg => println(" " + errorMsg)) } @@ -472,7 +467,7 @@ trait Macros { self: Analyzer => compatibilityError(mostRelevantMessage) } else { assert((results filter (_.isEmpty)).length == 1, results) - if (macroDebug) (reqparamsss zip results) filter (_._2.isEmpty) foreach { case (reqparamss, result) => + if (macroDebugVerbose) (reqparamsss zip results) filter (_._2.isEmpty) foreach { case (reqparamss, result) => println("typechecked macro impl as: " + reqparamss) } } @@ -596,11 +591,11 @@ trait Macros { self: Analyzer => val libraryClassLoader = { if (settings.XmacroPrimaryClasspath.value != "") { - if (macroDebug) println("primary macro mirror: initializing from -Xmacro-primary-classpath: %s".format(settings.XmacroPrimaryClasspath.value)) + macroLogVerbose("primary macro mirror: initializing from -Xmacro-primary-classpath: %s".format(settings.XmacroPrimaryClasspath.value)) val classpath = toURLs(settings.XmacroFallbackClasspath.value) ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) } else { - if (macroDebug) println("primary macro mirror: initializing from -cp: %s".format(global.classPath.asURLs)) + macroLogVerbose("primary macro mirror: initializing from -cp: %s".format(global.classPath.asURLs)) val classpath = global.classPath.asURLs var loader: ClassLoader = ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) @@ -626,7 +621,7 @@ trait Macros { self: Analyzer => throw new UnsupportedOperationException("Scala reflection not available on this platform") val fallbackClassLoader = { - if (macroDebug) println("fallback macro mirror: initializing from -Xmacro-fallback-classpath: %s".format(settings.XmacroFallbackClasspath.value)) + macroLogVerbose("fallback macro mirror: initializing from -Xmacro-fallback-classpath: %s".format(settings.XmacroFallbackClasspath.value)) val classpath = toURLs(settings.XmacroFallbackClasspath.value) ScalaClassLoader.fromURLs(classpath, self.getClass.getClassLoader) } @@ -649,24 +644,24 @@ trait Macros { self: Analyzer => private def macroRuntime(macroDef: Symbol): Option[MacroRuntime] = macroRuntimesCache.getOrElseUpdate(macroDef, { val runtime = { - macroTrace("looking for macro implementation: ")(macroDef) - macroTrace("macroDef is annotated with: ")(macroDef.annotations) + macroTraceVerbose("looking for macro implementation: ")(macroDef) + macroTraceVerbose("macroDef is annotated with: ")(macroDef.annotations) val ann = macroDef.getAnnotation(MacroImplAnnotation) if (ann == None) { - macroTrace("@macroImpl annotation is missing (this means that macro definition failed to typecheck)")(macroDef) + macroTraceVerbose("@macroImpl annotation is missing (this means that macro definition failed to typecheck)")(macroDef) return None } val macroImpl = ann.get.args(0).symbol if (macroImpl == NoSymbol) { - macroTrace("@macroImpl annotation is malformed (this means that macro definition failed to typecheck)")(macroDef) + macroTraceVerbose("@macroImpl annotation is malformed (this means that macro definition failed to typecheck)")(macroDef) return None } - if (macroDebug) println("resolved implementation %s at %s".format(macroImpl, macroImpl.pos)) + macroLogVerbose("resolved implementation %s at %s".format(macroImpl, macroImpl.pos)) if (macroImpl.isErroneous) { - macroTrace("macro implementation is erroneous (this means that either macro body or macro implementation signature failed to typecheck)")(macroDef) + macroTraceVerbose("macro implementation is erroneous (this means that either macro body or macro implementation signature failed to typecheck)")(macroDef) return None } @@ -680,14 +675,14 @@ trait Macros { self: Analyzer => // however, the code below doesn't account for these guys, because it'd take a look of time to get it right // for now I leave it as a todo and move along to more the important stuff - macroTrace("loading implementation class from %s: ".format(macroMirror))(macroImpl.owner.fullName) - macroTrace("classloader is: ")("%s of type %s".format(macroMirror.classLoader, if (macroMirror.classLoader != null) macroMirror.classLoader.getClass.toString else "primordial classloader")) + macroTraceVerbose("loading implementation class from %s: ".format(macroMirror))(macroImpl.owner.fullName) + macroTraceVerbose("classloader is: ")("%s of type %s".format(macroMirror.classLoader, if (macroMirror.classLoader != null) macroMirror.classLoader.getClass.toString else "primordial classloader")) def inferClasspath(cl: ClassLoader) = cl match { case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]" case null => "[" + scala.tools.util.PathResolver.Environment.javaBootClassPath + "]" case _ => "<unknown>" } - macroTrace("classpath is: ")(inferClasspath(macroMirror.classLoader)) + macroTraceVerbose("classpath is: ")(inferClasspath(macroMirror.classLoader)) // [Eugene] relies on the fact that macro implementations can only be defined in static classes // [Martin to Eugene] There's similar logic buried in Symbol#flatname. Maybe we can refactor? @@ -711,7 +706,7 @@ trait Macros { self: Analyzer => val implClassName = classfile(macroImpl.owner) val implClassSymbol: macroMirror.Symbol = macroMirror.symbolForName(implClassName) - if (macroDebug) { + if (macroDebugVerbose) { println("implClassSymbol is: " + implClassSymbol.fullNameString) if (implClassSymbol != macroMirror.NoSymbol) { @@ -723,7 +718,7 @@ trait Macros { self: Analyzer => } val implObjSymbol = implClassSymbol.companionModule - macroTrace("implObjSymbol is: ")(implObjSymbol.fullNameString) + macroTraceVerbose("implObjSymbol is: ")(implObjSymbol.fullNameString) if (implObjSymbol == macroMirror.NoSymbol) None else { @@ -733,29 +728,27 @@ trait Macros { self: Analyzer => val implObjClass = java.lang.Class.forName(implClassName, true, macroMirror.classLoader) implObjClass getField "MODULE$" get null } catch { - case ex: NoSuchFieldException => macroTrace("exception when loading implObj: ")(ex); null - case ex: NoClassDefFoundError => macroTrace("exception when loading implObj: ")(ex); null - case ex: ClassNotFoundException => macroTrace("exception when loading implObj: ")(ex); null + case ex: NoSuchFieldException => macroTraceVerbose("exception when loading implObj: ")(ex); null + case ex: NoClassDefFoundError => macroTraceVerbose("exception when loading implObj: ")(ex); null + case ex: ClassNotFoundException => macroTraceVerbose("exception when loading implObj: ")(ex); null } if (implObj == null) None else { val implMethSymbol = implObjSymbol.info.member(macroMirror.newTermName(macroImpl.name.toString)) - if (macroDebug) { - println("implMethSymbol is: " + implMethSymbol.fullNameString) - println("jimplMethSymbol is: " + macroMirror.methodToJava(implMethSymbol)) - } + macroLogVerbose("implMethSymbol is: " + implMethSymbol.fullNameString) + macroLogVerbose("jimplMethSymbol is: " + macroMirror.methodToJava(implMethSymbol)) if (implMethSymbol == macroMirror.NoSymbol) None else { - if (macroDebug) println("successfully loaded macro impl as (%s, %s)".format(implObj, implMethSymbol)) + macroLogVerbose("successfully loaded macro impl as (%s, %s)".format(implObj, implMethSymbol)) Some((implObj, implMethSymbol)) } } } } catch { case ex: ClassNotFoundException => - macroTrace("implementation class failed to load: ")(ex.toString) + macroTraceVerbose("implementation class failed to load: ")(ex.toString) None } } @@ -767,7 +760,7 @@ trait Macros { self: Analyzer => Some(runtime _) case None => if (settings.XmacroFallbackClasspath.value != "") { - if (macroDebug) println("trying to load macro implementation from the fallback mirror: %s".format(settings.XmacroFallbackClasspath.value)) + macroLogVerbose("trying to load macro implementation from the fallback mirror: %s".format(settings.XmacroFallbackClasspath.value)) val fallback = loadMacroImpl(fallbackMirror) fallback match { case Some((implObj, implMethSymbol)) => @@ -827,13 +820,13 @@ trait Macros { self: Analyzer => collectMacroArgs(expandee) val context = expandee.attachmentOpt[MacroAttachment].flatMap(_.macroContext).getOrElse(macroContext(typer, prefixTree, expandee)) var argss: List[List[Any]] = List(context) :: exprArgs.toList - macroTrace("argss: ")(argss) + macroTraceVerbose("argss: ")(argss) val ann = macroDef.getAnnotation(MacroImplAnnotation).getOrElse(throw new Error("assertion failed. %s: %s".format(macroDef, macroDef.annotations))) val macroImpl = ann.args(0).symbol var paramss = macroImpl.paramss val tparams = macroImpl.typeParams - macroTrace("paramss: ")(paramss) + macroTraceVerbose("paramss: ")(paramss) // we need to take care of all possible combos of nullary/empty-paramlist macro defs vs nullary/empty-arglist invocations // nullary def + nullary invocation => paramss and argss match, everything is okay @@ -845,7 +838,7 @@ trait Macros { self: Analyzer => val isEmptyParamlistDef = paramss_without_evidences.nonEmpty && paramss_without_evidences.last.isEmpty val isEmptyArglistInvocation = argss.nonEmpty && argss.last.isEmpty if (isEmptyParamlistDef && !isEmptyArglistInvocation) { - if (macroDebug) println("isEmptyParamlistDef && !isEmptyArglistInvocation: appending a List() to argss") + macroLogVerbose("isEmptyParamlistDef && !isEmptyArglistInvocation: appending a List() to argss") argss = argss :+ Nil } @@ -890,7 +883,7 @@ trait Macros { self: Analyzer => macroDef.owner) } else implRefTarg.tpe - if (macroDebug) println("resolved tparam %s as %s".format(tparam, tpe)) + macroLogVerbose("resolved tparam %s as %s".format(tparam, tpe)) resolved(tparam) = tpe param.tpe.typeSymbol match { case definitions.TypeTagClass => @@ -919,7 +912,7 @@ trait Macros { self: Analyzer => else as } val rawArgs = rawArgss.flatten - macroTrace("rawArgs: ")(rawArgs) + macroTraceVerbose("rawArgs: ")(rawArgs) Some(rawArgs) } @@ -927,16 +920,7 @@ trait Macros { self: Analyzer => * See more informations in comments to ``openMacros'' in ``scala.reflect.makro.Context''. */ var openMacros = List[MacroContext]() - private def openMacroPos = openMacros map (_.expandee.pos) find (_ ne NoPosition) getOrElse NoPosition - - // !!! YOu should use a method like this which manages the stack. - // That you presently need to spread this logic out is a major - // design flaw. - private def pushMacroContext[T](mc: MacroContext)(body: => T): T = { - openMacros ::= mc - try body - finally openMacros = openMacros.tail - } + def enclosingMacroPosition = openMacros map (_.macroApplication.pos) find (_ ne NoPosition) getOrElse NoPosition /** Performs macro expansion: * 1) Checks whether the expansion needs to be delayed (see ``mustDelayMacroExpansion'') @@ -945,13 +929,13 @@ trait Macros { self: Analyzer => * 4) Checks that the result is a tree bound to this universe * 5) Typechecks the result against the return type of the macro definition * - * If -Ymacro-debug is enabled, you will get detailed log of how exactly this function + * If -Ymacro-debug-lite is enabled, you will get basic notifications about macro expansion + * along with macro expansions logged in the form that can be copy/pasted verbatim into REPL. + * + * If -Ymacro-debug-verbose is enabled, you will get detailed log of how exactly this function * performs class loading and method resolution in order to load the macro implementation. * The log will also include other non-trivial steps of macro expansion. * - * If -Ymacro-copypaste is enabled along with -Ymacro-debug, you will get macro expansions - * logged in the form that can be copy/pasted verbatim into REPL (useful for debugging!). - * * @return * the expansion result if the expansion has been successful, * the fallback method invocation if the expansion has been unsuccessful, but there is a fallback, @@ -963,7 +947,7 @@ trait Macros { self: Analyzer => def macroExpand(typer: Typer, expandee: Tree, mode: Int = EXPRmode, pt: Type = WildcardType): Tree = { def fail(what: String, tree: Tree): Tree = { val err = typer.context.errBuffer.head - this.fail(typer, tree, "failed to perform %s: %s at %s".format(what, err.errMsg, err.errPos)) + this.fail(typer, tree, "failed to %s: %s at %s".format(what, err.errMsg, err.errPos)) return expandee } val start = startTimer(macroExpandNanos) @@ -984,31 +968,23 @@ trait Macros { self: Analyzer => } if (isNullaryInvocation) expectedTpe match { case NullaryMethodType(restpe) => - macroTrace("nullary invocation of a nullary method. unwrapping expectedTpe from " + expectedTpe + " to: ")(restpe) + macroTraceVerbose("nullary invocation of a nullary method. unwrapping expectedTpe from " + expectedTpe + " to: ")(restpe) expectedTpe = restpe case MethodType(Nil, restpe) => - macroTrace("nullary invocation of a method with an empty parameter list. unwrapping expectedTpe from " + expectedTpe + " to: ")(restpe) + macroTraceVerbose("nullary invocation of a method with an empty parameter list. unwrapping expectedTpe from " + expectedTpe + " to: ")(restpe) expectedTpe = restpe case _ => ; } - if (macroDebug) println("typechecking1 against %s: %s".format(expectedTpe, expanded)) + macroLogVerbose("typechecking1 against %s: %s".format(expectedTpe, expanded)) var typechecked = typer.context.withImplicitsEnabled(typer.typed(expanded, EXPRmode, expectedTpe)) - if (typer.context.hasErrors) fail("typecheck1", expanded) - if (macroDebug) { - println("typechecked1:") - println(typechecked) - println(showRaw(typechecked)) - } + if (typer.context.hasErrors) fail("typecheck against macro def return type", expanded) + macroLogVerbose("typechecked1:%n%s%n%s".format(typechecked, showRaw(typechecked))) - if (macroDebug) println("typechecking2 against %s: %s".format(pt, expanded)) + macroLogVerbose("typechecking2 against %s: %s".format(pt, expanded)) typechecked = typer.context.withImplicitsEnabled(typer.typed(typechecked, EXPRmode, pt)) - if (typer.context.hasErrors) fail("typecheck2", expanded) - if (macroDebug) { - println("typechecked2:") - println(typechecked) - println(showRaw(typechecked)) - } + if (typer.context.hasErrors) fail("typecheck against expected type", expanded) + macroLogVerbose("typechecked2:%n%s%n%s".format(typechecked, showRaw(typechecked))) typechecked } finally { @@ -1033,11 +1009,9 @@ trait Macros { self: Analyzer => private def Cancel(expandee: Tree) = Other(expandee) private def Failure(expandee: Tree) = Other(expandee) private def fail(typer: Typer, expandee: Tree, msg: String = null) = { - if (macroDebug || macroCopypaste) { - var msg1 = if (msg != null && (msg contains "exception during macro expansion")) msg.split(EOL).drop(1).headOption.getOrElse("?") else msg - if (macroDebug) println("macro expansion has failed: %s".format(msg1)) - } - val pos = if (expandee.pos != NoPosition) expandee.pos else openMacros.find(c => c.expandee.pos != NoPosition).map(_.expandee.pos).getOrElse(NoPosition) + def msgForLog = if (msg != null && (msg contains "exception during macro expansion")) msg.split(EOL).drop(1).headOption.getOrElse("?") else msg + macroLogVerbose("macro expansion has failed: %s".format(msgForLog)) + val pos = if (expandee.pos != NoPosition) expandee.pos else enclosingMacroPosition if (msg != null) typer.context.error(pos, msg) typer.infer.setError(expandee) Failure(expandee) @@ -1058,7 +1032,7 @@ trait Macros { self: Analyzer => // there is no sense to expand the macro itself => it will only make matters worse if (expandee.symbol.isErroneous || (expandee exists (_.isErroneous))) { val reason = if (expandee.symbol.isErroneous) "incompatible macro implementation" else "erroneous arguments" - macroTrace("cancelled macro expansion because of %s: ".format(reason))(expandee) + macroTraceVerbose("cancelled macro expansion because of %s: ".format(reason))(expandee) return Cancel(typer.infer.setError(expandee)) } @@ -1092,7 +1066,7 @@ trait Macros { self: Analyzer => val wasDelayed = isDelayed(expandee) val undetparams = calculateUndetparams(expandee) val nowDelayed = !typer.context.macrosEnabled || undetparams.nonEmpty - + def failExpansion(msg: String = null) = fail(typer, expandee, msg) def performExpansion(args: List[Any]): MacroExpansionResult = { val numErrors = reporter.ERROR.count @@ -1104,17 +1078,18 @@ trait Macros { self: Analyzer => failExpansion() // errors have been reported by the macro itself else expanded match { case expanded: Expr[_] => - if (macroDebug) println("original:") - macroDebugLog("" + expanded.tree + "\n" + showRaw(expanded.tree)) + macroLogVerbose("original:") + macroLogVerbose("" + expanded.tree + "\n" + showRaw(expanded.tree)) freeTerms(expanded.tree) foreach issueFreeError freeTypes(expanded.tree) foreach issueFreeError + if (hasNewErrors) failExpansion() + // inherit the position from the first position-ful expandee in macro callstack // this is essential for sane error messages // now macro expansion gets typechecked against the macro definition return type // however, this happens in macroExpand, not here in macroExpand1 - if (hasNewErrors) failExpansion() - else Success(atPos(openMacroPos.focus)(expanded.tree)) + else Success(atPos(enclosingMacroPosition.focus)(expanded.tree)) case _ => failExpansion( "macro must return a compiler-specific expr; returned value is " + ( @@ -1130,11 +1105,11 @@ trait Macros { self: Analyzer => else Skip(macroExpandAll(typer, expandee)) } else { - macroDebugLog("typechecking macro expansion %s at %s".format(expandee, expandee.pos)) + macroLogVerbose("typechecking macro expansion %s at %s".format(expandee, expandee.pos)) macroArgs(typer, expandee).fold(failExpansion(): MacroExpansionResult) { case args @ ((context: MacroContext) :: _) => if (nowDelayed) { - macroDebugLog("macro expansion is delayed: %s".format(expandee)) + macroLogVerbose("macro expansion is delayed: %s".format(expandee)) delayed += expandee -> undetparams // need to save typer context for `macroExpandAll` // need to save macro context to preserve enclosures @@ -1144,7 +1119,9 @@ trait Macros { self: Analyzer => else { // adding stuff to openMacros is easy, but removing it is a nightmare // it needs to be sprinkled over several different code locations - openMacros ::= args.head.asInstanceOf[MacroContext] + // why? https://github.com/scala/scala/commit/bd3eacbae21f39b1ac7fe8ade4ed71fa98e1a28d#L2R1137 + // todo. will be improved + openMacros ::= context var isSuccess = false try performExpansion(args) match { case x: Success => isSuccess = true ; x @@ -1158,7 +1135,7 @@ trait Macros { self: Analyzer => } } } - + try macroExpandInternal catch { case ex => handleMacroExpansionException(typer, expandee, ex) } } @@ -1179,7 +1156,7 @@ trait Macros { self: Analyzer => case first :: _ => Some(Select(qual, name) setPos tree.pos setSymbol first) case _ => - macroTrace("macro is not overridden: ")(tree) + macroTraceVerbose("macro is not overridden: ")(tree) notFound() } case Apply(fn, args) => @@ -1193,33 +1170,29 @@ trait Macros { self: Analyzer => case _ => None } case _ => - macroTrace("unexpected tree in fallback: ")(tree) + macroTraceVerbose("unexpected tree in fallback: ")(tree) notFound() } } fallBackToOverridden(expandee) match { case Some(tree1) => - macroTrace("falling back to: ")(tree1) + macroTraceVerbose("falling back to: ")(tree1) currentRun.macroExpansionFailed = true Fallback(tree1) case None => fail(typer, expandee) } } - - @inline final def macroDebugLog(msg: => String) { - if (macroDebug || macroCopypaste) println(msg) - } - private def handleMacroExpansionException(typer: Typer, expandee: Tree, ex: Throwable): MacroExpansionResult = { + private def handleMacroExpansionException(typer: Typer, expandee: Tree, ex: Throwable): MacroExpansionResult = { // [Eugene] any ideas about how to improve this one? val realex = ReflectionUtils.unwrapThrowable(ex) realex match { case realex: reflect.makro.runtime.AbortMacroException => - macroDebugLog("macro expansion has failed: %s".format(realex.msg)) + macroLogVerbose("macro expansion has failed: %s".format(realex.msg)) fail(typer, expandee) // error has been reported by abort case err: TypeError => - macroDebugLog("macro expansion has failed: %s at %s".format(err.msg, err.pos)) + macroLogVerbose("macro expansion has failed: %s at %s".format(err.msg, err.pos)) throw err // error should be propagated, don't report case _ => val message = { @@ -1271,24 +1244,24 @@ trait Macros { self: Analyzer => if (sub.symbol != null) traverse(sub.symbol) if (sub.tpe != null) sub.tpe foreach (sub => traverse(sub.typeSymbol)) }) - if (macroDebug) println("calculateUndetparams: %s".format(calculated)) + macroLogVerbose("calculateUndetparams: %s".format(calculated)) calculated map (_.id) } private val undetparams = perRunCaches.newSet[Int] def notifyUndetparamsAdded(newUndets: List[Symbol]): Unit = { undetparams ++= newUndets map (_.id) - if (macroDebug) newUndets foreach (sym => println("undetParam added: %s".format(sym))) + if (macroDebugVerbose) newUndets foreach (sym => println("undetParam added: %s".format(sym))) } def notifyUndetparamsInferred(undetNoMore: List[Symbol], inferreds: List[Type]): Unit = { undetparams --= undetNoMore map (_.id) - if (macroDebug) (undetNoMore zip inferreds) foreach {case (sym, tpe) => println("undetParam inferred: %s as %s".format(sym, tpe))} + if (macroDebugVerbose) (undetNoMore zip inferreds) foreach { case (sym, tpe) => println("undetParam inferred: %s as %s".format(sym, tpe))} if (!delayed.isEmpty) delayed.toList foreach { case (expandee, undetparams) if !undetparams.isEmpty => undetparams --= undetNoMore map (_.id) if (undetparams.isEmpty) { hasPendingMacroExpansions = true - macroTrace("macro expansion is pending: ")(expandee) + macroTraceVerbose("macro expansion is pending: ")(expandee) } case _ => // do nothing diff --git a/src/compiler/scala/tools/util/StringOps.scala b/src/compiler/scala/tools/util/StringOps.scala index 02eb364abe..725e0afb79 100644 --- a/src/compiler/scala/tools/util/StringOps.scala +++ b/src/compiler/scala/tools/util/StringOps.scala @@ -25,6 +25,16 @@ trait StringOps { val ys = oempty(xs: _*) if (ys.isEmpty) orElse else ys mkString sep } + def trimTrailingSpace(s: String) = { + if (s.length == 0 || !s.charAt(s.length - 1).isWhitespace) s + else { + var idx = s.length - 1 + while (idx >= 0 && s.charAt(idx).isWhitespace) + idx -= 1 + + s.substring(0, idx + 1) + } + } def decompose(str: String, sep: Char): List[String] = { def ws(start: Int): List[String] = diff --git a/src/library/scala/reflect/makro/Typers.scala b/src/library/scala/reflect/makro/Typers.scala index 1ced2daccd..c62c5f254c 100644 --- a/src/library/scala/reflect/makro/Typers.scala +++ b/src/library/scala/reflect/makro/Typers.scala @@ -29,7 +29,7 @@ trait Typers { * * If ``silent'' is false, ``TypeError'' will be thrown in case of a typecheck error. * If ``silent'' is true, the typecheck is silent and will return ``EmptyTree'' if an error occurs. - * Such errors don't vanish and can be inspected by turning on -Ymacro-debug. + * Such errors don't vanish and can be inspected by turning on -Ymacro-debug-verbose. * Unlike in ``inferImplicitValue'' and ``inferImplicitView'', ``silent'' is false by default. * * Typechecking can be steered with the following optional parameters: diff --git a/test/files/neg/t1878-typer.check b/test/files/neg/t1878-typer.check new file mode 100644 index 0000000000..e3a20d0be7 --- /dev/null +++ b/test/files/neg/t1878-typer.check @@ -0,0 +1,4 @@ +t1878-typer.scala:4: error: _* may only come last + case <p> { _* } </p> => + ^ +one error found diff --git a/test/files/neg/t1878-typer.scala b/test/files/neg/t1878-typer.scala new file mode 100644 index 0000000000..1eb0cb7dff --- /dev/null +++ b/test/files/neg/t1878-typer.scala @@ -0,0 +1,6 @@ +object Test extends App { + // illegal - bug #1764 + null match { + case <p> { _* } </p> => + } +} diff --git a/test/files/neg/t1878.check b/test/files/neg/t1878.check index b47367e12c..ac2071c3d8 100644 --- a/test/files/neg/t1878.check +++ b/test/files/neg/t1878.check @@ -1,21 +1,7 @@ -t1878.scala:3: error: _* may only come last +t1878.scala:3: error: bad use of _* (a sequence pattern must be the last pattern) val err1 = "" match { case Seq(f @ _*, ',') => f } - ^ -t1878.scala:3: error: scrutinee is incompatible with pattern type; - found : Seq[A] - required: String - val err1 = "" match { case Seq(f @ _*, ',') => f } - ^ -t1878.scala:3: error: not found: value f - val err1 = "" match { case Seq(f @ _*, ',') => f } - ^ -t1878.scala:3: error: value _2 is not a member of object Seq - val err1 = "" match { case Seq(f @ _*, ',') => f } - ^ -t1878.scala:9: error: _* may only come last + ^ +t1878.scala:9: error: bad use of _* (a sequence pattern must be the last pattern) val List(List(_*, arg2), _) = List(List(1,2,3), List(4,5,6)) - ^ -t1878.scala:13: error: _* may only come last - case <p> { _* } </p> => - ^ -6 errors found + ^ +two errors found diff --git a/test/files/neg/t1878.scala b/test/files/neg/t1878.scala index 5278fbb7bd..99fee48a96 100644 --- a/test/files/neg/t1878.scala +++ b/test/files/neg/t1878.scala @@ -8,8 +8,10 @@ object Test extends App { // illegal val List(List(_*, arg2), _) = List(List(1,2,3), List(4,5,6)) + /* see t1878-typer.scala // illegal - bug #1764 null match { case <p> { _* } </p> => } + */ } diff --git a/test/files/neg/t3189.check b/test/files/neg/t3189.check new file mode 100644 index 0000000000..3913c526a2 --- /dev/null +++ b/test/files/neg/t3189.check @@ -0,0 +1,4 @@ +t3189.scala:2: error: use _* to match a sequence + val Array(a,b*) = ("": Any) + ^ +one error found diff --git a/test/pending/neg/t3189.scala b/test/files/neg/t3189.scala index 4ea4bb7581..4ea4bb7581 100644 --- a/test/pending/neg/t3189.scala +++ b/test/files/neg/t3189.scala diff --git a/test/files/neg/t5702-neg-bad-and-wild.check b/test/files/neg/t5702-neg-bad-and-wild.check new file mode 100644 index 0000000000..eae81ad5f2 --- /dev/null +++ b/test/files/neg/t5702-neg-bad-and-wild.check @@ -0,0 +1,28 @@ +t5702-neg-bad-and-wild.scala:10: error: bad use of _* (a sequence pattern must be the last pattern) + case List(1, _*,) => // bad use of _* (a sequence pattern must be the last pattern) + ^ +t5702-neg-bad-and-wild.scala:10: error: illegal start of simple pattern + case List(1, _*,) => // bad use of _* (a sequence pattern must be the last pattern) + ^ +t5702-neg-bad-and-wild.scala:12: error: illegal start of simple pattern + case List(1, _*3,) => // illegal start of simple pattern + ^ +t5702-neg-bad-and-wild.scala:14: error: use _* to match a sequence + case List(1, x*) => // use _* to match a sequence + ^ +t5702-neg-bad-and-wild.scala:15: error: trailing * is not a valid pattern + case List(x*, 1) => // trailing * is not a valid pattern + ^ +t5702-neg-bad-and-wild.scala:16: error: trailing * is not a valid pattern + case (1, x*) => // trailing * is not a valid pattern + ^ +t5702-neg-bad-and-wild.scala:17: error: bad use of _* (sequence pattern not allowed) + case (1, x@_*) => // bad use of _* (sequence pattern not allowed) + ^ +t5702-neg-bad-and-wild.scala:23: error: bad use of _* (a sequence pattern must be the last pattern) + val K(ns @ _*, x) = k // bad use of _* (a sequence pattern must be the last pattern) + ^ +t5702-neg-bad-and-wild.scala:24: error: bad use of _* (sequence pattern not allowed) + val (b, _ * ) = Pair(5,6) // bad use of _* (sequence pattern not allowed) + ^ +9 errors found diff --git a/test/files/neg/t5702-neg-bad-and-wild.scala b/test/files/neg/t5702-neg-bad-and-wild.scala new file mode 100644 index 0000000000..3833a002b1 --- /dev/null +++ b/test/files/neg/t5702-neg-bad-and-wild.scala @@ -0,0 +1,29 @@ + +object Test { + case class K(i: Int) + + def main(args: Array[String]) { + val k = new K(9) + val is = List(1,2,3) + + is match { + case List(1, _*,) => // bad use of _* (a sequence pattern must be the last pattern) + // illegal start of simple pattern + case List(1, _*3,) => // illegal start of simple pattern + //case List(1, _*3:) => // poor recovery by parens + case List(1, x*) => // use _* to match a sequence + case List(x*, 1) => // trailing * is not a valid pattern + case (1, x*) => // trailing * is not a valid pattern + case (1, x@_*) => // bad use of _* (sequence pattern not allowed) + } + +// good syntax, bad semantics, detected by typer +//gowild.scala:14: error: star patterns must correspond with varargs parameters + val K(is @ _*) = k + val K(ns @ _*, x) = k // bad use of _* (a sequence pattern must be the last pattern) + val (b, _ * ) = Pair(5,6) // bad use of _* (sequence pattern not allowed) +// no longer complains +//bad-and-wild.scala:15: error: ')' expected but '}' found. + } +} + diff --git a/test/files/neg/t5702-neg-bad-brace.check b/test/files/neg/t5702-neg-bad-brace.check new file mode 100644 index 0000000000..503f7d95ed --- /dev/null +++ b/test/files/neg/t5702-neg-bad-brace.check @@ -0,0 +1,10 @@ +t5702-neg-bad-brace.scala:14: error: Unmatched closing brace '}' ignored here + case List(1, _*} => + ^ +t5702-neg-bad-brace.scala:14: error: illegal start of simple pattern + case List(1, _*} => + ^ +t5702-neg-bad-brace.scala:15: error: ')' expected but '}' found. + } + ^ +three errors found diff --git a/test/files/neg/t5702-neg-bad-brace.scala b/test/files/neg/t5702-neg-bad-brace.scala new file mode 100644 index 0000000000..16a341cf8c --- /dev/null +++ b/test/files/neg/t5702-neg-bad-brace.scala @@ -0,0 +1,17 @@ + +object Test { + + def main(args: Array[String]) { + val is = List(1,2,3) + + is match { +// the erroneous brace is ignored, so we can't halt on it. +// maybe brace healing can detect overlapping unmatched (...} +// In this case, the fix emits an extra error: +// t5702-neg-bad-brace.scala:10: error: Unmatched closing brace '}' ignored here +// t5702-neg-bad-brace.scala:10: error: illegal start of simple pattern (i.e., =>) +// t5702-neg-bad-brace.scala:11: error: ')' expected but '}' found. + case List(1, _*} => + } + } +} diff --git a/test/files/neg/t5702-neg-bad-xbrace.check b/test/files/neg/t5702-neg-bad-xbrace.check new file mode 100644 index 0000000000..d88638aee9 --- /dev/null +++ b/test/files/neg/t5702-neg-bad-xbrace.check @@ -0,0 +1,7 @@ +t5702-neg-bad-xbrace.scala:19: error: bad brace or paren after _* + case <year>{_*)}</year> => y + ^ +t5702-neg-bad-xbrace.scala:28: error: bad brace or paren after _* + val <top>{a, z@_*)}</top> = xml + ^ +two errors found diff --git a/test/files/neg/t5702-neg-bad-xbrace.scala b/test/files/neg/t5702-neg-bad-xbrace.scala new file mode 100644 index 0000000000..64bbdb18be --- /dev/null +++ b/test/files/neg/t5702-neg-bad-xbrace.scala @@ -0,0 +1,31 @@ + +object Test { + def main(args: Array[String]) { + /* PiS example, minus a brace + val yearMade = 1965 + val old = + <a>{ if (yearMade < 2000) <old>yearMade}</old> + else xml.NodeSeq.Empty } </a> + println(old) + */ + + // bad brace or paren after _* + // actually, we know it's a bad paren... + // we skip it because not in a context looking for rparen + val xyear = <year>1965</year> + val ancient = + <b>{ + val when = xyear match { + case <year>{_*)}</year> => y + case _ => "2035" + } + <old>{when}</old> + }</b> + println(ancient) + + val xml = <top><a>apple</a><b>boy</b><c>child</c></top> + // bad brace or paren after _* + val <top>{a, z@_*)}</top> = xml + println("A for "+ a +", ending with "+ z) + } +} diff --git a/test/files/neg/t5702-neg-ugly-xbrace.check b/test/files/neg/t5702-neg-ugly-xbrace.check new file mode 100644 index 0000000000..7d80bbf6be --- /dev/null +++ b/test/files/neg/t5702-neg-ugly-xbrace.check @@ -0,0 +1,19 @@ +t5702-neg-ugly-xbrace.scala:11: error: bad brace or paren after _* + val <top>{a, z@_*)</top> = xml + ^ +t5702-neg-ugly-xbrace.scala:12: error: Missing closing brace `}' assumed here + println("A for "+ a +", ending with "+ z) + ^ +t5702-neg-ugly-xbrace.scala:13: error: in XML literal: in XML content, please use '}}' to express '}' + } + ^ +t5702-neg-ugly-xbrace.scala:11: error: I encountered a '}' where I didn't expect one, maybe this tag isn't closed <top> + val <top>{a, z@_*)</top> = xml + ^ +t5702-neg-ugly-xbrace.scala:14: error: illegal start of simple pattern +} +^ +t5702-neg-ugly-xbrace.scala:14: error: '}' expected but eof found. +} + ^ +6 errors found diff --git a/test/files/neg/t5702-neg-ugly-xbrace.scala b/test/files/neg/t5702-neg-ugly-xbrace.scala new file mode 100644 index 0000000000..0ff7bfa09d --- /dev/null +++ b/test/files/neg/t5702-neg-ugly-xbrace.scala @@ -0,0 +1,14 @@ + +object Test { + def main(args: Array[String]) { + + val xml = <top><a>apple</a><b>boy</b><c>child</c></top> + // This is the more likely typo, and the uglier parse. + // We could turn it into a } if } does not follow (to + // avoid handing }} back to xml) but that is quite ad hoc. + // Assuming } for ) after _* would not be not outlandish. + // bad brace or paren after _* + val <top>{a, z@_*)</top> = xml + println("A for "+ a +", ending with "+ z) + } +} diff --git a/test/files/pos/t5702-pos-infix-star.scala b/test/files/pos/t5702-pos-infix-star.scala new file mode 100644 index 0000000000..756bcdd8de --- /dev/null +++ b/test/files/pos/t5702-pos-infix-star.scala @@ -0,0 +1,15 @@ + +object Test { + case class *(a: Int, b: Int) + type Star = * + case class P(a: Int, b: Star) // alias still required + + def main(args: Array[String]) { + val v = new *(6,7) + val x * y = v + printf("%d,%d\n",x,y) + val p = P(5, v) + val P(a, b * c) = p + printf("%d,%d,%d\n",a,b,c) + } +} diff --git a/test/files/run/inner-parse.check b/test/files/run/inner-parse.check new file mode 100644 index 0000000000..87ea9ddeb5 --- /dev/null +++ b/test/files/run/inner-parse.check @@ -0,0 +1,86 @@ +file Test$$anonfun$main$1.class +class Test$$anonfun$main$1 extends scala.runtime.AbstractFunction1$mcVL$sp + interface scala.Serializable + inner/anon anonymous class: Test$$anonfun$main$1 + descriptor <clinit> ()V + descriptor apply (Lscala/Tuple2;)V + descriptor apply (Ljava/lang/Object;)Ljava/lang/Object; + descriptor cwd$1 Ljava/lang/String; + descriptor serialVersionUID J + descriptor <init> (Ljava/lang/String;)V + signature apply (Lscala/Tuple2<Ljava/lang/String;Lscala/reflect/internal/JvmClassInfo;>;)V + +file Test$.class +class Test$ extends java.lang.Object + inner/anon anonymous class: Test$$anonfun$main$1 + descriptor <clinit> ()V + descriptor MODULE$ LTest$; + descriptor main ([Ljava/lang/String;)V + descriptor <init> ()V + +file Test.class +class Test extends java.lang.Object + inner/anon anonymous class: Test$$anonfun$main$1 + descriptor main ([Ljava/lang/String;)V + +file j/J_1$B$C$D.class +class j.J_1$B$C$D extends java.lang.Object + inner B j.J_1$B in j.J_1 + inner C j.J_1$B$C in j.J_1$B + inner/enclosing D enclosing class: j.J_1$B$C + descriptor <init> (Lj/J_1$B$C;)V + descriptor this$2 Lj/J_1$B$C; + +file j/J_1$B$C.class +class j.J_1$B$C extends java.lang.Object + inner B j.J_1$B in j.J_1 + inner/enclosing C enclosing class: j.J_1$B + inner/nested D member class: j.J_1$B$C$D + descriptor <init> (Lj/J_1$B;)V + descriptor this$1 Lj/J_1$B; + +file j/J_1$B.class +class j.J_1$B extends java.lang.Object + inner/enclosing B enclosing class: j.J_1 + inner/nested C member class: j.J_1$B$C + descriptor <init> (Lj/J_1;)V + descriptor this$0 Lj/J_1; + +file j/J_1.class +class j.J_1 extends java.lang.Object + interface java.util.RandomAccess + inner/nested B member class: j.J_1$B + descriptor <init> ()V + +file s/J_1$B$C$D.class +class s.J_1$B$C$D extends java.lang.Object + inner B s.J_1$B in s.J_1 + inner C s.J_1$B$C in s.J_1$B + inner/enclosing D enclosing class: s.J_1$B$C + descriptor $outer Ls/J_1$B$C; + descriptor s$J_1$B$C$D$$$outer ()Ls/J_1$B$C; + descriptor <init> (Ls/J_1$B$C;)V + +file s/J_1$B$C.class +class s.J_1$B$C extends java.lang.Object + inner B s.J_1$B in s.J_1 + inner/enclosing C enclosing class: s.J_1$B + inner/nested D member class: s.J_1$B$C$D + descriptor $outer Ls/J_1$B; + descriptor s$J_1$B$C$$$outer ()Ls/J_1$B; + descriptor <init> (Ls/J_1$B;)V + +file s/J_1$B.class +class s.J_1$B extends java.lang.Object + inner/enclosing B enclosing class: s.J_1 + inner/nested C member class: s.J_1$B$C + descriptor $outer Ls/J_1; + descriptor s$J_1$B$$$outer ()Ls/J_1; + descriptor <init> (Ls/J_1;)V + +file s/J_1.class +class s.J_1 extends java.lang.Object + interface java.util.RandomAccess + inner/nested B member class: s.J_1$B + descriptor <init> ()V + diff --git a/test/files/run/inner-parse/J_1.java b/test/files/run/inner-parse/J_1.java new file mode 100644 index 0000000000..920ab951ab --- /dev/null +++ b/test/files/run/inner-parse/J_1.java @@ -0,0 +1,9 @@ +package j; + +public class J_1 implements java.util.RandomAccess { // "random" marker interface + class B { + class C { + class D { } + } + } +} diff --git a/test/files/run/inner-parse/S_2.scala b/test/files/run/inner-parse/S_2.scala new file mode 100644 index 0000000000..fd144a40b7 --- /dev/null +++ b/test/files/run/inner-parse/S_2.scala @@ -0,0 +1,9 @@ +package s; + +class J_1 extends java.util.RandomAccess { + class B { + class C { + class D { } + } + } +} diff --git a/test/files/run/inner-parse/S_3.scala b/test/files/run/inner-parse/S_3.scala new file mode 100644 index 0000000000..296a651460 --- /dev/null +++ b/test/files/run/inner-parse/S_3.scala @@ -0,0 +1,12 @@ +import scala.reflect.internal.JvmClassInfo + +object Test { + def main(args: Array[String]): Unit = { + val cwd = sys.props("partest.output") + + for ((f, info) <- JvmClassInfo.classInfoList(cwd)) { + println("file " + f.stripPrefix(cwd + "/")) + println(info) + } + } +} diff --git a/test/pending/neg/t3189.check b/test/pending/neg/t3189.check deleted file mode 100644 index 43dd0f29a0..0000000000 --- a/test/pending/neg/t3189.check +++ /dev/null @@ -1,7 +0,0 @@ -t3189.scala:2: error: illegal start of simple pattern - val Array(a,b*) = ("": Any) - ^ -t3189.scala:3: error: ')' expected but '}' found. -} -^ -two errors found diff --git a/tools/dump-class b/tools/dump-class new file mode 100755 index 0000000000..0b4f2a73fa --- /dev/null +++ b/tools/dump-class @@ -0,0 +1,6 @@ +#!/bin/sh +# + +classpath=$($(dirname $BASH_SOURCE)/quickcp) + +java -cp "$classpath" scala.tools.nsc.MainGenericRunner -usejavacp scala.tools.cmd.program.DumpClass "$@"
\ No newline at end of file |