From b10f45a78217b002f8ac6e2051ff932a1ac2e029 Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 7 Mar 2014 14:52:23 +0100 Subject: SI-8375 saner binary incompat errors for macros Inspired by Brian McKenna's RC1 migration experience, this commit improves macro impl binding validation in order to provide more helpful diagnostic for this quite frequent class of errors. --- test/files/lib/macro210.jar.desired.sha1 | 1 + .../neg/macro-incompatible-macro-engine-a.check | 7 +++++ .../Macros_2.flags | 1 + .../Macros_2.scala | 7 +++++ .../Plugin_1.scala | 35 ++++++++++++++++++++++ .../macro-incompatible-macro-engine-a/Test_3.scala | 4 +++ .../scalac-plugin.xml | 4 +++ .../neg/macro-incompatible-macro-engine-b.check | 7 +++++ .../neg/macro-incompatible-macro-engine-b.flags | 1 + .../Macros_2.flags | 1 + .../Macros_2.scala | 7 +++++ .../Plugin_1.scala | 35 ++++++++++++++++++++++ .../macro-incompatible-macro-engine-b/Test_3.scala | 4 +++ .../scalac-plugin.xml | 4 +++ .../neg/macro-incompatible-macro-engine-c.check | 4 +++ .../neg/macro-incompatible-macro-engine-c.scala | 3 ++ .../neg/macro-incompatible-macro-engine.check | 7 ----- .../macro-incompatible-macro-engine/Macros_2.flags | 1 - .../macro-incompatible-macro-engine/Macros_2.scala | 7 ----- .../macro-incompatible-macro-engine/Plugin_1.scala | 35 ---------------------- .../macro-incompatible-macro-engine/Test_3.scala | 4 --- .../scalac-plugin.xml | 4 --- 22 files changed, 125 insertions(+), 58 deletions(-) create mode 100644 test/files/lib/macro210.jar.desired.sha1 create mode 100644 test/files/neg/macro-incompatible-macro-engine-a.check create mode 100644 test/files/neg/macro-incompatible-macro-engine-a/Macros_2.flags create mode 100644 test/files/neg/macro-incompatible-macro-engine-a/Macros_2.scala create mode 100644 test/files/neg/macro-incompatible-macro-engine-a/Plugin_1.scala create mode 100644 test/files/neg/macro-incompatible-macro-engine-a/Test_3.scala create mode 100644 test/files/neg/macro-incompatible-macro-engine-a/scalac-plugin.xml create mode 100644 test/files/neg/macro-incompatible-macro-engine-b.check create mode 100644 test/files/neg/macro-incompatible-macro-engine-b.flags create mode 100644 test/files/neg/macro-incompatible-macro-engine-b/Macros_2.flags create mode 100644 test/files/neg/macro-incompatible-macro-engine-b/Macros_2.scala create mode 100644 test/files/neg/macro-incompatible-macro-engine-b/Plugin_1.scala create mode 100644 test/files/neg/macro-incompatible-macro-engine-b/Test_3.scala create mode 100644 test/files/neg/macro-incompatible-macro-engine-b/scalac-plugin.xml create mode 100644 test/files/neg/macro-incompatible-macro-engine-c.check create mode 100644 test/files/neg/macro-incompatible-macro-engine-c.scala delete mode 100644 test/files/neg/macro-incompatible-macro-engine.check delete mode 100644 test/files/neg/macro-incompatible-macro-engine/Macros_2.flags delete mode 100644 test/files/neg/macro-incompatible-macro-engine/Macros_2.scala delete mode 100644 test/files/neg/macro-incompatible-macro-engine/Plugin_1.scala delete mode 100644 test/files/neg/macro-incompatible-macro-engine/Test_3.scala delete mode 100644 test/files/neg/macro-incompatible-macro-engine/scalac-plugin.xml (limited to 'test/files') diff --git a/test/files/lib/macro210.jar.desired.sha1 b/test/files/lib/macro210.jar.desired.sha1 new file mode 100644 index 0000000000..ff87a55129 --- /dev/null +++ b/test/files/lib/macro210.jar.desired.sha1 @@ -0,0 +1 @@ +3794ec22d9b27f2b179bd34e9b46db771b934ec3 ?macro210.jar diff --git a/test/files/neg/macro-incompatible-macro-engine-a.check b/test/files/neg/macro-incompatible-macro-engine-a.check new file mode 100644 index 0000000000..8ae08bd164 --- /dev/null +++ b/test/files/neg/macro-incompatible-macro-engine-a.check @@ -0,0 +1,7 @@ +Test_3.scala:2: error: macro cannot be expanded, because it was compiled by an incompatible macro engine + Macros.foo + ^ +Test_3.scala:3: error: macro cannot be expanded, because it was compiled by an incompatible macro engine + Macros.foo + ^ +two errors found diff --git a/test/files/neg/macro-incompatible-macro-engine-a/Macros_2.flags b/test/files/neg/macro-incompatible-macro-engine-a/Macros_2.flags new file mode 100644 index 0000000000..966df731d0 --- /dev/null +++ b/test/files/neg/macro-incompatible-macro-engine-a/Macros_2.flags @@ -0,0 +1 @@ +-Xplugin:. \ No newline at end of file diff --git a/test/files/neg/macro-incompatible-macro-engine-a/Macros_2.scala b/test/files/neg/macro-incompatible-macro-engine-a/Macros_2.scala new file mode 100644 index 0000000000..39708eee49 --- /dev/null +++ b/test/files/neg/macro-incompatible-macro-engine-a/Macros_2.scala @@ -0,0 +1,7 @@ +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context + +object Macros { + def impl(c: Context) = c.universe.Literal(c.universe.Constant(())) + def foo: Unit = macro impl +} \ No newline at end of file diff --git a/test/files/neg/macro-incompatible-macro-engine-a/Plugin_1.scala b/test/files/neg/macro-incompatible-macro-engine-a/Plugin_1.scala new file mode 100644 index 0000000000..44ed91d2fb --- /dev/null +++ b/test/files/neg/macro-incompatible-macro-engine-a/Plugin_1.scala @@ -0,0 +1,35 @@ +package incompatibleMacroEngine + +import scala.tools.nsc.Global +import scala.tools.nsc.plugins.{Plugin => NscPlugin} + +class Plugin(val global: Global) extends NscPlugin { + import global._ + import analyzer._ + + val name = "incompatibleMacroEngine" + val description = "A sample analyzer plugin that crafts a macro impl binding with a non-standard macro engine." + val components = Nil + addMacroPlugin(MacroPlugin) + + object MacroPlugin extends MacroPlugin { + def fixupBinding(tree: Tree) = new Transformer { + override def transform(tree: Tree) = { + tree match { + case Literal(const @ Constant(x)) if tree.tpe == null => tree setType ConstantType(const) + case _ if tree.tpe == null => tree setType NoType + case _ => ; + } + super.transform(tree) + } + }.transform(tree) + + override def pluginsTypedMacroBody(typer: Typer, ddef: DefDef): Option[Tree] = { + val result = standardTypedMacroBody(typer, ddef) + val List(AnnotationInfo(atp, List(Apply(nucleus, _ :: others)), Nil)) = ddef.symbol.annotations + val updatedBinding = Apply(nucleus, Assign(Literal(Constant("macroEngine")), Literal(Constant("vxxx (implemented in the incompatibleMacroEngine plugin)"))) :: others) + ddef.symbol.setAnnotations(List(AnnotationInfo(atp, List(fixupBinding(updatedBinding)), Nil))) + Some(result) + } + } +} \ No newline at end of file diff --git a/test/files/neg/macro-incompatible-macro-engine-a/Test_3.scala b/test/files/neg/macro-incompatible-macro-engine-a/Test_3.scala new file mode 100644 index 0000000000..7e4fae5236 --- /dev/null +++ b/test/files/neg/macro-incompatible-macro-engine-a/Test_3.scala @@ -0,0 +1,4 @@ +object Test extends App { + Macros.foo + Macros.foo +} \ No newline at end of file diff --git a/test/files/neg/macro-incompatible-macro-engine-a/scalac-plugin.xml b/test/files/neg/macro-incompatible-macro-engine-a/scalac-plugin.xml new file mode 100644 index 0000000000..42b9cdd75d --- /dev/null +++ b/test/files/neg/macro-incompatible-macro-engine-a/scalac-plugin.xml @@ -0,0 +1,4 @@ + + incompatible-macro-engine + incompatibleMacroEngine.Plugin + \ No newline at end of file diff --git a/test/files/neg/macro-incompatible-macro-engine-b.check b/test/files/neg/macro-incompatible-macro-engine-b.check new file mode 100644 index 0000000000..2a7510cf86 --- /dev/null +++ b/test/files/neg/macro-incompatible-macro-engine-b.check @@ -0,0 +1,7 @@ +Test_3.scala:2: error: macro cannot be expanded, because it was compiled by an incompatible macro engine (internal diagnostic: expected = v7.0 (implemented in Scala 2.11.0-M8), actual = vxxx (implemented in the incompatibleMacroEngine plugin)) + Macros.foo + ^ +Test_3.scala:3: error: macro cannot be expanded, because it was compiled by an incompatible macro engine (internal diagnostic: expected = v7.0 (implemented in Scala 2.11.0-M8), actual = vxxx (implemented in the incompatibleMacroEngine plugin)) + Macros.foo + ^ +two errors found diff --git a/test/files/neg/macro-incompatible-macro-engine-b.flags b/test/files/neg/macro-incompatible-macro-engine-b.flags new file mode 100644 index 0000000000..037a693bbd --- /dev/null +++ b/test/files/neg/macro-incompatible-macro-engine-b.flags @@ -0,0 +1 @@ +-Ymacro-debug-lite \ No newline at end of file diff --git a/test/files/neg/macro-incompatible-macro-engine-b/Macros_2.flags b/test/files/neg/macro-incompatible-macro-engine-b/Macros_2.flags new file mode 100644 index 0000000000..966df731d0 --- /dev/null +++ b/test/files/neg/macro-incompatible-macro-engine-b/Macros_2.flags @@ -0,0 +1 @@ +-Xplugin:. \ No newline at end of file diff --git a/test/files/neg/macro-incompatible-macro-engine-b/Macros_2.scala b/test/files/neg/macro-incompatible-macro-engine-b/Macros_2.scala new file mode 100644 index 0000000000..39708eee49 --- /dev/null +++ b/test/files/neg/macro-incompatible-macro-engine-b/Macros_2.scala @@ -0,0 +1,7 @@ +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context + +object Macros { + def impl(c: Context) = c.universe.Literal(c.universe.Constant(())) + def foo: Unit = macro impl +} \ No newline at end of file diff --git a/test/files/neg/macro-incompatible-macro-engine-b/Plugin_1.scala b/test/files/neg/macro-incompatible-macro-engine-b/Plugin_1.scala new file mode 100644 index 0000000000..44ed91d2fb --- /dev/null +++ b/test/files/neg/macro-incompatible-macro-engine-b/Plugin_1.scala @@ -0,0 +1,35 @@ +package incompatibleMacroEngine + +import scala.tools.nsc.Global +import scala.tools.nsc.plugins.{Plugin => NscPlugin} + +class Plugin(val global: Global) extends NscPlugin { + import global._ + import analyzer._ + + val name = "incompatibleMacroEngine" + val description = "A sample analyzer plugin that crafts a macro impl binding with a non-standard macro engine." + val components = Nil + addMacroPlugin(MacroPlugin) + + object MacroPlugin extends MacroPlugin { + def fixupBinding(tree: Tree) = new Transformer { + override def transform(tree: Tree) = { + tree match { + case Literal(const @ Constant(x)) if tree.tpe == null => tree setType ConstantType(const) + case _ if tree.tpe == null => tree setType NoType + case _ => ; + } + super.transform(tree) + } + }.transform(tree) + + override def pluginsTypedMacroBody(typer: Typer, ddef: DefDef): Option[Tree] = { + val result = standardTypedMacroBody(typer, ddef) + val List(AnnotationInfo(atp, List(Apply(nucleus, _ :: others)), Nil)) = ddef.symbol.annotations + val updatedBinding = Apply(nucleus, Assign(Literal(Constant("macroEngine")), Literal(Constant("vxxx (implemented in the incompatibleMacroEngine plugin)"))) :: others) + ddef.symbol.setAnnotations(List(AnnotationInfo(atp, List(fixupBinding(updatedBinding)), Nil))) + Some(result) + } + } +} \ No newline at end of file diff --git a/test/files/neg/macro-incompatible-macro-engine-b/Test_3.scala b/test/files/neg/macro-incompatible-macro-engine-b/Test_3.scala new file mode 100644 index 0000000000..7e4fae5236 --- /dev/null +++ b/test/files/neg/macro-incompatible-macro-engine-b/Test_3.scala @@ -0,0 +1,4 @@ +object Test extends App { + Macros.foo + Macros.foo +} \ No newline at end of file diff --git a/test/files/neg/macro-incompatible-macro-engine-b/scalac-plugin.xml b/test/files/neg/macro-incompatible-macro-engine-b/scalac-plugin.xml new file mode 100644 index 0000000000..42b9cdd75d --- /dev/null +++ b/test/files/neg/macro-incompatible-macro-engine-b/scalac-plugin.xml @@ -0,0 +1,4 @@ + + incompatible-macro-engine + incompatibleMacroEngine.Plugin + \ No newline at end of file diff --git a/test/files/neg/macro-incompatible-macro-engine-c.check b/test/files/neg/macro-incompatible-macro-engine-c.check new file mode 100644 index 0000000000..804669f8b5 --- /dev/null +++ b/test/files/neg/macro-incompatible-macro-engine-c.check @@ -0,0 +1,4 @@ +macro-incompatible-macro-engine-c.scala:2: error: macro cannot be expanded, because it was compiled by an incompatible macro engine + MacroLibCompiledByScala210x.foo + ^ +one error found diff --git a/test/files/neg/macro-incompatible-macro-engine-c.scala b/test/files/neg/macro-incompatible-macro-engine-c.scala new file mode 100644 index 0000000000..037ac5f456 --- /dev/null +++ b/test/files/neg/macro-incompatible-macro-engine-c.scala @@ -0,0 +1,3 @@ +object Test extends App { + MacroLibCompiledByScala210x.foo +} \ No newline at end of file diff --git a/test/files/neg/macro-incompatible-macro-engine.check b/test/files/neg/macro-incompatible-macro-engine.check deleted file mode 100644 index 1d582e5ed6..0000000000 --- a/test/files/neg/macro-incompatible-macro-engine.check +++ /dev/null @@ -1,7 +0,0 @@ -Test_3.scala:2: error: macro cannot be expanded, because it was compiled by an incompatible macro engine vxxx (implemented in the incompatibleMacroEngine plugin) - Macros.foo - ^ -Test_3.scala:3: error: macro cannot be expanded, because it was compiled by an incompatible macro engine vxxx (implemented in the incompatibleMacroEngine plugin) - Macros.foo - ^ -two errors found diff --git a/test/files/neg/macro-incompatible-macro-engine/Macros_2.flags b/test/files/neg/macro-incompatible-macro-engine/Macros_2.flags deleted file mode 100644 index 966df731d0..0000000000 --- a/test/files/neg/macro-incompatible-macro-engine/Macros_2.flags +++ /dev/null @@ -1 +0,0 @@ --Xplugin:. \ No newline at end of file diff --git a/test/files/neg/macro-incompatible-macro-engine/Macros_2.scala b/test/files/neg/macro-incompatible-macro-engine/Macros_2.scala deleted file mode 100644 index 39708eee49..0000000000 --- a/test/files/neg/macro-incompatible-macro-engine/Macros_2.scala +++ /dev/null @@ -1,7 +0,0 @@ -import scala.language.experimental.macros -import scala.reflect.macros.blackbox.Context - -object Macros { - def impl(c: Context) = c.universe.Literal(c.universe.Constant(())) - def foo: Unit = macro impl -} \ No newline at end of file diff --git a/test/files/neg/macro-incompatible-macro-engine/Plugin_1.scala b/test/files/neg/macro-incompatible-macro-engine/Plugin_1.scala deleted file mode 100644 index 44ed91d2fb..0000000000 --- a/test/files/neg/macro-incompatible-macro-engine/Plugin_1.scala +++ /dev/null @@ -1,35 +0,0 @@ -package incompatibleMacroEngine - -import scala.tools.nsc.Global -import scala.tools.nsc.plugins.{Plugin => NscPlugin} - -class Plugin(val global: Global) extends NscPlugin { - import global._ - import analyzer._ - - val name = "incompatibleMacroEngine" - val description = "A sample analyzer plugin that crafts a macro impl binding with a non-standard macro engine." - val components = Nil - addMacroPlugin(MacroPlugin) - - object MacroPlugin extends MacroPlugin { - def fixupBinding(tree: Tree) = new Transformer { - override def transform(tree: Tree) = { - tree match { - case Literal(const @ Constant(x)) if tree.tpe == null => tree setType ConstantType(const) - case _ if tree.tpe == null => tree setType NoType - case _ => ; - } - super.transform(tree) - } - }.transform(tree) - - override def pluginsTypedMacroBody(typer: Typer, ddef: DefDef): Option[Tree] = { - val result = standardTypedMacroBody(typer, ddef) - val List(AnnotationInfo(atp, List(Apply(nucleus, _ :: others)), Nil)) = ddef.symbol.annotations - val updatedBinding = Apply(nucleus, Assign(Literal(Constant("macroEngine")), Literal(Constant("vxxx (implemented in the incompatibleMacroEngine plugin)"))) :: others) - ddef.symbol.setAnnotations(List(AnnotationInfo(atp, List(fixupBinding(updatedBinding)), Nil))) - Some(result) - } - } -} \ No newline at end of file diff --git a/test/files/neg/macro-incompatible-macro-engine/Test_3.scala b/test/files/neg/macro-incompatible-macro-engine/Test_3.scala deleted file mode 100644 index 7e4fae5236..0000000000 --- a/test/files/neg/macro-incompatible-macro-engine/Test_3.scala +++ /dev/null @@ -1,4 +0,0 @@ -object Test extends App { - Macros.foo - Macros.foo -} \ No newline at end of file diff --git a/test/files/neg/macro-incompatible-macro-engine/scalac-plugin.xml b/test/files/neg/macro-incompatible-macro-engine/scalac-plugin.xml deleted file mode 100644 index 42b9cdd75d..0000000000 --- a/test/files/neg/macro-incompatible-macro-engine/scalac-plugin.xml +++ /dev/null @@ -1,4 +0,0 @@ - - incompatible-macro-engine - incompatibleMacroEngine.Plugin - \ No newline at end of file -- cgit v1.2.3 From 384322b1cb824b0ff8a2a15a75d33d6a7cf8e85c Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Fri, 7 Mar 2014 23:00:06 +0100 Subject: Addresses pull request feedback --- src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala | 12 +++++++++--- src/compiler/scala/tools/nsc/typechecker/Macros.scala | 6 ++++-- test/files/neg/macro-incompatible-macro-engine-c.check | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) (limited to 'test/files') diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index 156f9d4f4a..d439bb5603 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala @@ -725,12 +725,18 @@ trait ContextErrors { NormalTypeError(expandee, "too many argument lists for " + fun) } - def MacroIncompatibleEngineError(diagnostic: String) = { - var message = s"macro cannot be expanded, because it was compiled by an incompatible macro engine" - if (macroDebugLite || macroDebugVerbose) message += s" (internal diagnostic: $diagnostic)" + private def MacroIncompatibleEngineError(friendlyMessage: String, internalMessage: String) = { + def debugDiagnostic = s"(internal diagnostic: $internalMessage)" + val message = if (macroDebugLite || macroDebugVerbose) s"$friendlyMessage $debugDiagnostic" else friendlyMessage issueNormalTypeError(lastTreeToTyper, message) } + def MacroCantExpand210xMacrosError(internalMessage: String) = + MacroIncompatibleEngineError("can't expand macros compiled by previous versions of Scala", internalMessage) + + def MacroCantExpandIncompatibleMacrosError(internalMessage: String) = + MacroIncompatibleEngineError("macro cannot be expanded, because it was compiled by an incompatible macro engine", internalMessage) + case object MacroExpansionException extends Exception with scala.util.control.ControlThrowable protected def macroExpansionError(expandee: Tree, msg: String, pos: Position = NoPosition) = { diff --git a/src/compiler/scala/tools/nsc/typechecker/Macros.scala b/src/compiler/scala/tools/nsc/typechecker/Macros.scala index f1dd00d4cd..9cf92ca5b9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Macros.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Macros.scala @@ -224,7 +224,8 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { val Apply(_, pickledPayload) = wrapped val payload = pickledPayload.map{ case Assign(k, v) => (unpickleAtom(k), unpickleAtom(v)) }.toMap - def fail(msg: String) = typer.TyperErrorGen.MacroIncompatibleEngineError(msg) + import typer.TyperErrorGen._ + def fail(msg: String) = MacroCantExpandIncompatibleMacrosError(msg) def unpickle[T](field: String, clazz: Class[T]): T = { def failField(msg: String) = fail(s"$field $msg") if (!payload.contains(field)) failField("is supposed to be there") @@ -236,8 +237,9 @@ trait Macros extends FastTrack with MacroRuntimes with Traces with Helpers { raw.asInstanceOf[T] } + if (!payload.contains("macroEngine")) MacroCantExpand210xMacrosError("macroEngine field not found") val macroEngine = unpickle("macroEngine", classOf[String]) - if (self.macroEngine != macroEngine) typer.TyperErrorGen.MacroIncompatibleEngineError(s"expected = ${self.macroEngine}, actual = $macroEngine") + if (self.macroEngine != macroEngine) MacroCantExpandIncompatibleMacrosError(s"expected = ${self.macroEngine}, actual = $macroEngine") val isBundle = unpickle("isBundle", classOf[Boolean]) val isBlackbox = unpickle("isBlackbox", classOf[Boolean]) diff --git a/test/files/neg/macro-incompatible-macro-engine-c.check b/test/files/neg/macro-incompatible-macro-engine-c.check index 804669f8b5..fb6c59ab7c 100644 --- a/test/files/neg/macro-incompatible-macro-engine-c.check +++ b/test/files/neg/macro-incompatible-macro-engine-c.check @@ -1,4 +1,4 @@ -macro-incompatible-macro-engine-c.scala:2: error: macro cannot be expanded, because it was compiled by an incompatible macro engine +macro-incompatible-macro-engine-c.scala:2: error: can't expand macros compiled by previous versions of Scala MacroLibCompiledByScala210x.foo ^ one error found -- cgit v1.2.3