From c7491eb1c7d41c5eaecb20f9e2f82fd208a6713e Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Tue, 5 Jun 2012 20:36:51 +0200 Subject: improves traces and error messages --- .../scala/reflect/internal/TreePrinters.scala | 10 +++- src/compiler/scala/reflect/reify/Errors.scala | 5 +- .../scala/reflect/reify/phases/Reify.scala | 1 + .../scala/tools/nsc/ast/NodePrinters.scala | 2 + .../nsc/interpreter/AbstractFileClassLoader.scala | 2 +- .../scala/tools/nsc/typechecker/Implicits.scala | 18 ++++++-- .../scala/tools/nsc/typechecker/Macros.scala | 53 +++++++++------------- src/library/scala/reflect/ReflectionUtils.scala | 16 +++++-- .../scala/reflect/makro/internal/Utils.scala | 14 +++--- 9 files changed, 70 insertions(+), 51 deletions(-) diff --git a/src/compiler/scala/reflect/internal/TreePrinters.scala b/src/compiler/scala/reflect/internal/TreePrinters.scala index b3e4318fdc..e5a98c6b4e 100644 --- a/src/compiler/scala/reflect/internal/TreePrinters.scala +++ b/src/compiler/scala/reflect/internal/TreePrinters.scala @@ -27,7 +27,9 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => private def symNameInternal(tree: Tree, name: Name, decoded: Boolean): String = { val sym = tree.symbol - if (sym != null && sym != NoSymbol) { + if (sym.name.toString == nme.ERROR.toString) { + "<" + quotedName(name, decoded) + ": error>" + } else if (sym != null && sym != NoSymbol) { val prefix = if (sym.isMixinConstructor) "/*%s*/".format(quotedName(sym.owner.name, decoded)) else "" var suffix = "" if (settings.uniqid.value) suffix += ("#" + sym.id) @@ -167,6 +169,12 @@ trait TreePrinters extends api.TreePrinters { self: SymbolTable => } def printAnnotations(tree: Tree) { + if (inReflexiveMirror && tree.symbol != null && tree.symbol != NoSymbol) + // [Eugene++] todo. this is not 100% correct, but is necessary for sane printing + // the problem is that getting annotations doesn't automatically initialize the symbol + // so we might easily print something as if it doesn't have annotations, whereas it does + tree.symbol.initialize + val annots = tree.symbol.annotations match { case Nil => tree.asInstanceOf[MemberDef].mods.annotations case anns => anns diff --git a/src/compiler/scala/reflect/reify/Errors.scala b/src/compiler/scala/reflect/reify/Errors.scala index 4466f281b8..6edadf3a47 100644 --- a/src/compiler/scala/reflect/reify/Errors.scala +++ b/src/compiler/scala/reflect/reify/Errors.scala @@ -10,7 +10,10 @@ trait Errors { import mirror._ import definitions._ - def defaultErrorPosition = analyzer.enclosingMacroPosition + def defaultErrorPosition = { + val stack = currents collect { case t: Tree if t.pos != NoPosition => t.pos } + stack.headOption getOrElse 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/phases/Reify.scala b/src/compiler/scala/reflect/reify/phases/Reify.scala index e03ff5832c..0e0ce17bd0 100644 --- a/src/compiler/scala/reflect/reify/phases/Reify.scala +++ b/src/compiler/scala/reflect/reify/phases/Reify.scala @@ -37,6 +37,7 @@ trait Reify extends Symbols } def currentQuantified = flatCollect(reifyStack.currents)({ case ExistentialType(quantified, _) => quantified }) def current = reifyStack.currents.head + def currents = reifyStack.currents /** * Reifies any supported value. diff --git a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala index ce3106ab29..17d8def2e9 100644 --- a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala @@ -145,6 +145,8 @@ abstract class NodePrinters { str.toString } def printModifiers(tree: MemberDef) { + // [Eugene++] there's most likely a bug here (?) + // see `TreePrinters.printAnnotations` for more information val annots0 = tree.symbol.annotations match { case Nil => tree.mods.annotations case xs => xs map annotationInfoToString diff --git a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala index 605ecee6c7..7289a947f4 100644 --- a/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala +++ b/src/compiler/scala/tools/nsc/interpreter/AbstractFileClassLoader.scala @@ -15,7 +15,7 @@ import scala.collection.{ mutable, immutable } * * @author Lex Spoon */ -class AbstractFileClassLoader(root: AbstractFile, parent: ClassLoader) +class AbstractFileClassLoader(val root: AbstractFile, parent: ClassLoader) extends ClassLoader(parent) with ScalaClassLoader { diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 217cadaab8..6a2a9b850c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -1183,16 +1183,24 @@ trait Implicits { // if ``pre'' is not a PDT, e.g. if someone wrote // implicitly[scala.reflect.makro.Context#TypeTag[Int]] // then we need to fail, because we don't know the prefix to use during type reification - return failure(tp, "tag error: unsupported prefix type %s (%s)".format(pre, pre.kind)) + // upd. we also need to fail silently, because this is a very common situation + // e.g. quite often we're searching for BaseUniverse#TypeTag, e.g. for a type tag in any universe + // so that if we find one, we could convert it to whatever universe we need by the means of the `in` method + // if no tag is found in scope, we end up here, where we ask someone to materialize the tag for us + // however, since the original search was about a tag with no particular prefix, we cannot proceed + // this situation happens very often, so emitting an error message here (even if only for -Xlog-implicits) would be too much + //return failure(tp, "tag error: unsupported prefix type %s (%s)".format(pre, pre.kind)) + return SearchFailure } ) // todo. migrate hardcoded materialization in Implicits to corresponding implicit macros - var materializer = atPos(pos.focus)( - gen.mkMethodCall(TagMaterializers(tagClass), List(tp), List(prefix)) - ) + var materializer = atPos(pos.focus)(gen.mkMethodCall(TagMaterializers(tagClass), List(tp), List(prefix))) if (settings.XlogImplicits.value) println("materializing requested %s.%s[%s] using %s".format(pre, tagClass.name, tp, materializer)) if (context.macrosEnabled) success(materializer) - else failure(materializer, "macros are disabled") + // don't call `failure` here. if macros are disabled, we just fail silently + // otherwise -Xlog-implicits will spam the long with zillions of "macros are disabled" + // this is ugly but temporary, since all this code will be removed once I fix implicit macros + else SearchFailure } private val ManifestSymbols = Set[Symbol](PartialManifestClass, FullManifestClass, OptManifestClass) diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index c10901cdce..c21222f450 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -186,6 +186,7 @@ trait Macros extends Traces { macroLogVerbose("typechecking macro def %s at %s".format(ddef.symbol, ddef.pos)) if (!typer.checkFeature(ddef.pos, MacrosFeature, immediate = true)) { + macroLogVerbose("typecheck terminated unexpectedly: language.experimental.macros feature is not enabled") ddef.symbol setFlag IS_ERROR return EmptyTree } @@ -675,31 +676,20 @@ trait Macros extends Traces { private def macroRuntime(macroDef: Symbol): Option[MacroRuntime] = { macroTraceVerbose("looking for macro implementation: ")(macroDef) if (fastTrack contains macroDef) { - macroLogVerbose("macro expansion serviced by a fast track") + macroLogVerbose("macro expansion is serviced by a fast track") Some(fastTrack(macroDef)) } else { macroRuntimesCache.getOrElseUpdate(macroDef, { val runtime = { - macroTraceVerbose("looking for macro implementation: ")(macroDef) macroTraceVerbose("macroDef is annotated with: ")(macroDef.annotations) val ann = macroDef.getAnnotation(MacroImplAnnotation) - if (ann == None) { - macroTraceVerbose("@macroImpl annotation is missing (this means that macro definition failed to typecheck)")(macroDef) - return None - } + if (ann == None) { 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) { - macroTraceVerbose("@macroImpl annotation is malformed (this means that macro definition failed to typecheck)")(macroDef) - return None - } - + if (macroImpl == NoSymbol) { macroTraceVerbose("@macroImpl annotation is malformed (this means that macro definition failed to typecheck)")(macroDef); return None } macroLogVerbose("resolved implementation %s at %s".format(macroImpl, macroImpl.pos)) - if (macroImpl.isErroneous) { - macroTraceVerbose("macro implementation is erroneous (this means that either macro body or macro implementation signature failed to typecheck)")(macroDef) - return None - } + if (macroImpl.isErroneous) { macroTraceVerbose("macro implementation is erroneous (this means that either macro body or macro implementation signature failed to typecheck)")(macroDef); return None } def loadMacroImpl(macroMirror: Mirror): Option[(Object, macroMirror.Symbol)] = { try { @@ -711,14 +701,8 @@ trait Macros extends Traces { // 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 - 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 _ => "" - } - macroTraceVerbose("classpath is: ")(inferClasspath(macroMirror.classLoader)) + macroTraceVerbose("loading implementation class: ")(macroImpl.owner.fullName) + macroTraceVerbose("classloader is: ")(ReflectionUtils.show(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? @@ -749,7 +733,7 @@ trait Macros extends Traces { val implClass = macroMirror.classToJava(implClassSymbol) val implSource = implClass.getProtectionDomain.getCodeSource println("implClass is %s from %s".format(implClass, implSource)) - println("implClassLoader is %s with classpath %s".format(implClass.getClassLoader, inferClasspath(implClass.getClassLoader))) + println("implClassLoader is %s".format(implClass.getClassLoader, ReflectionUtils.show(implClass.getClassLoader))) } } @@ -786,6 +770,9 @@ trait Macros extends Traces { case ex: ClassNotFoundException => macroTraceVerbose("implementation class failed to load: ")(ex.toString) None + case ex: NoSuchMethodException => + macroTraceVerbose("implementation method failed to load: ")(ex.toString) + None } } @@ -985,15 +972,17 @@ trait Macros extends Traces { 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 %s: %s at %s".format(what, err.errMsg, err.errPos)) + this.fail(typer, tree, err.errPos, "failed to %s: %s".format(what, err.errMsg)) return expandee } val start = startTimer(macroExpandNanos) incCounter(macroExpandCount) try { macroExpand1(typer, expandee) match { - case Success(expanded) => + case Success(expanded0) => try { + val expanded = expanded0 // virtpatmat swallows the local for expandee from the match + // so I added this dummy local for the ease of debugging var expectedTpe = expandee.tpe // [Eugene] weird situation. what's the conventional way to deal with it? @@ -1046,11 +1035,11 @@ trait Macros extends Traces { private def Skip(expanded: Tree) = Other(expanded) private def Cancel(expandee: Tree) = Other(expandee) private def Failure(expandee: Tree) = Other(expandee) - private def fail(typer: Typer, expandee: Tree, msg: String = null) = { + private def fail(typer: Typer, expandee: Tree, pos: Position = NoPosition, msg: String = null) = { def msgForLog = if (msg != null && (msg contains "exception during macro expansion")) msg.split(EOL).drop(1).headOption.getOrElse("?") else msg macroLogLite("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) + val errorPos = if (pos != NoPosition) pos else (if (expandee.pos != NoPosition) expandee.pos else enclosingMacroPosition) + if (msg != null) typer.context.error(errorPos, msg) typer.infer.setError(expandee) Failure(expandee) } @@ -1069,7 +1058,7 @@ trait Macros extends Traces { // if a macro implementation is incompatible or any of the arguments are erroneous // 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" + val reason = if (expandee.symbol.isErroneous) "not found or incompatible macro implementation" else "erroneous arguments" macroTraceVerbose("cancelled macro expansion because of %s: ".format(reason))(expandee) return Cancel(typer.infer.setError(expandee)) } @@ -1105,7 +1094,7 @@ trait Macros extends Traces { val undetparams = calculateUndetparams(expandee) val nowDelayed = !typer.context.macrosEnabled || undetparams.nonEmpty - def failExpansion(msg: String = null) = fail(typer, expandee, msg) + def failExpansion(msg: String = null) = fail(typer, expandee, msg = msg) def performExpansion(args: List[Any]): MacroExpansionResult = { val numErrors = reporter.ERROR.count def hasNewErrors = reporter.ERROR.count > numErrors @@ -1257,7 +1246,7 @@ trait Macros extends Traces { None } } getOrElse realex.getMessage - fail(typer, expandee, "exception during macro expansion: " + message) + fail(typer, expandee, msg = "exception during macro expansion: " + message) } } diff --git a/src/library/scala/reflect/ReflectionUtils.scala b/src/library/scala/reflect/ReflectionUtils.scala index 6ea69cb80d..aa1285d5a0 100644 --- a/src/library/scala/reflect/ReflectionUtils.scala +++ b/src/library/scala/reflect/ReflectionUtils.scala @@ -33,20 +33,26 @@ object ReflectionUtils { System.getProperties.asScala.iterator } - private def searchForBootClasspath = ( + private def inferBootClasspath: String = ( systemProperties find (_._1 endsWith ".boot.class.path") map (_._2) getOrElse "" ) def show(cl: ClassLoader) = { - def inferClasspath(cl: ClassLoader) = cl match { - case cl: java.net.URLClassLoader => "[" + (cl.getURLs mkString ",") + "]" - case _ => "" + def inferClasspath(cl: ClassLoader): String = cl match { + case cl: java.net.URLClassLoader => + "[" + (cl.getURLs mkString ",") + "]" + case cl if cl != null && cl.getClass.getName == "scala.tools.nsc.interpreter.AbstractFileClassLoader" => + "[" + cl.asInstanceOf[{val root: api.RequiredFile}].root + "] and " + inferClasspath(cl.getParent) + case null => + inferBootClasspath + case _ => + "" } cl match { case cl if cl != null => "%s of type %s with classpath %s".format(cl, cl.getClass, inferClasspath(cl)) case null => - "primordial classloader with boot classpath [%s]".format(searchForBootClasspath) + "primordial classloader with boot classpath [%s]".format(inferClasspath(cl)) } } diff --git a/src/library/scala/reflect/makro/internal/Utils.scala b/src/library/scala/reflect/makro/internal/Utils.scala index 3af58e1c88..3032d8e05d 100644 --- a/src/library/scala/reflect/makro/internal/Utils.scala +++ b/src/library/scala/reflect/makro/internal/Utils.scala @@ -102,7 +102,7 @@ package internal { else gen.mkMethodCall(staticModule("scala.reflect.package"), newTermName("manifestToConcreteTypeTag"), List(tpe), List(manifestInScope)) } try c.typeCheck(result) - catch { case terr @ c.TypeError(pos, msg) => failTag(terr) } + catch { case terr @ c.TypeError(pos, msg) => failTag(result, terr) } } private def nonSyntheticManifestInScope(tpe: Type) = { @@ -116,7 +116,7 @@ package internal { def materializeExpr(prefix: Tree, expr: Tree): Tree = { val result = translatingReificationErrors(c.reifyTree(prefix, expr)) try c.typeCheck(result) - catch { case terr @ c.TypeError(pos, msg) => failExpr(terr) } + catch { case terr @ c.TypeError(pos, msg) => failExpr(result, terr) } } private def translatingReificationErrors(materializer: => Tree): Tree = { @@ -130,17 +130,19 @@ package internal { } } - private def failTag(reason: Any): Nothing = { + private def failTag(result: Tree, reason: Any): Nothing = { val Apply(TypeApply(fun, List(tpeTree)), _) = c.macroApplication val tpe = tpeTree.tpe val PolyType(_, MethodType(_, tagTpe)) = fun.tpe val tagModule = tagTpe.typeSymbol.companionSymbol if (c.compilerSettings.contains("-Xlog-implicits")) - c.echo(c.enclosingPosition, "cannot materialize " + tagModule.name + "[" + tpe + "] because:\n" + reason) + c.echo(c.enclosingPosition, s"cannot materialize ${tagModule.name}[$tpe] as $result because:\n$reason") c.abort(c.enclosingPosition, "No %s available for %s".format(tagModule.name, tpe)) } - private def failExpr(reason: Any): Nothing = - c.abort(c.enclosingPosition, "Cannot materialize Expr because:\n" + reason) + private def failExpr(result: Tree, reason: Any): Nothing = { + val Apply(_, expr :: Nil) = c.macroApplication + c.abort(c.enclosingPosition, s"Cannot materialize $expr as $result because:\n$reason") + } } } -- cgit v1.2.3