├── project ├── build.properties └── plugins.sbt ├── version.sbt ├── scripts └── dot-sbtrc ├── notes ├── 1.1.2 │ ├── add-noSelfType.markdown │ ├── add-Attachments.contains.markdown │ └── add-Symbol.isPrivateThis.markdown ├── 1.0.5.markdown ├── 1.0.6.markdown ├── about.markdown ├── 1.0.3.markdown ├── 1.0.2.markdown ├── 1.1.0.markdown ├── 1.0.7.markdown ├── 1.0.1.markdown ├── 1.0.4.markdown └── 1.1.1.markdown ├── .gitignore ├── CONTRIBUTORS ├── core └── src │ └── main │ ├── scala_2.10 │ └── macrocompat │ │ ├── aliases.scala │ │ ├── annotations.scala │ │ ├── contextreporter.scala │ │ ├── macrocompat.scala │ │ ├── bundle_2.10.scala │ │ └── compatcontext.scala │ └── scala_2.11+ │ └── macrocompat │ └── bundle_2.11.scala ├── CODE_OF_CONDUCT.md ├── test └── src │ ├── main │ └── scala │ │ └── testmacro │ │ ├── reporter.scala │ │ ├── delegate.scala │ │ ├── testannotations.scala │ │ ├── companionobject.scala │ │ ├── override.scala │ │ ├── exporthook.scala │ │ └── testmacro.scala │ └── test │ └── scala │ └── testmacro │ ├── companionobject.scala │ └── testmacro.scala ├── .travis.yml ├── native-test └── src │ └── main │ └── scala │ └── MacroCompatTests.scala ├── README.md └── LICENSE /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.2.8 2 | -------------------------------------------------------------------------------- /version.sbt: -------------------------------------------------------------------------------- 1 | version in ThisBuild := "1.1.2-SNAPSHOT" -------------------------------------------------------------------------------- /scripts/dot-sbtrc: -------------------------------------------------------------------------------- 1 | alias boot = ;reload ;project testJVM ;iflast shell 2 | -------------------------------------------------------------------------------- /notes/1.1.2/add-noSelfType.markdown: -------------------------------------------------------------------------------- 1 | * Added noSelfType (thanks to [@dwijnand][]). 2 | 3 | [@dwijnand]: https://github.com/dwijnand 4 | -------------------------------------------------------------------------------- /notes/1.1.2/add-Attachments.contains.markdown: -------------------------------------------------------------------------------- 1 | * Added to Attachments: contains (thanks to [@dwijnand][]). 2 | 3 | [@dwijnand]: https://github.com/dwijnand 4 | -------------------------------------------------------------------------------- /notes/1.1.2/add-Symbol.isPrivateThis.markdown: -------------------------------------------------------------------------------- 1 | * Added to Symbol: isPrivateThis and isProtectedThis (thanks to [@dwijnand][]). 2 | 3 | [@dwijnand]: https://github.com/dwijnand 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | scratch/ 2 | target/ 3 | .sbtrc 4 | local.sbt 5 | 6 | # Ignore project files for Eclipse 7 | /.classpath 8 | /*/.classpath 9 | /.settings 10 | /*/.settings 11 | /.project 12 | /*/.project 13 | /.worksheet 14 | /*/.worksheet 15 | 16 | # Ignore project files for IntelliJ 17 | /.idea 18 | /*/.idea 19 | *.iml 20 | *.iws 21 | 22 | # Ignore OS X metadata 23 | .DS_Store 24 | 25 | # Ignore vim swap files 26 | .swp 27 | .*.swp 28 | .swo 29 | .*.swo 30 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Adelbert Chang @adelbertchang 2 | Alexandre Archambault @alxarchambault 3 | Alistair Johnson @AlistairUSM 4 | Chris Hodapp @clhodapp 5 | Dale Wijnand @dwijnand 6 | Frank S. Thomas @fst9000 7 | Michael Pilquist @mpilquist 8 | Miles Sabin @milessabin 9 | Naoki Aoyama @AoiroAoino 10 | Philip Wills @philwills 11 | Travis Brown @travisbrown 12 | -------------------------------------------------------------------------------- /notes/1.0.5.markdown: -------------------------------------------------------------------------------- 1 | This is the final release of [macro-compat-1.0.5][macro-compat]. 2 | 3 | These release notes provide a summary of changes since macro-compat 1.0.4. 4 | 5 | * Added Type#dealias. 6 | 7 | * Added Annotation#tree. 8 | 9 | * Added support for usage of freshName with an empty parameter list. 10 | 11 | All the above contributed by [Alexandre Archambault][alxarchambault] ... much appreciated. 12 | 13 | Many thanks to everyone who has contributed ideas, enthusiasm and encouragement. 14 | 15 | [macro-compat]: https://github.com/milessabin/macro-compat 16 | [alxarchambault]: https://twitter.com/alxarchambault 17 | -------------------------------------------------------------------------------- /notes/1.0.6.markdown: -------------------------------------------------------------------------------- 1 | This is the final release of [macro-compat-1.0.6][macro-compat]. 2 | 3 | These release notes provide a summary of changes since macro-compat 1.0.5. 4 | 5 | * Added support for Modifiers.unapply. 6 | 7 | * Fixed a bug where typecheck in TYPEmode would only work correctly with types of kind \* -> \*. 8 | 9 | * Fixed an init-order bug (thanks to [Alexandre Archambault][alxarchambault]). 10 | 11 | Many thanks to everyone who has contributed ideas, enthusiasm and encouragement. 12 | 13 | [macro-compat]: https://github.com/milessabin/macro-compat 14 | [alxarchambault]: https://twitter.com/alxarchambault 15 | -------------------------------------------------------------------------------- /notes/about.markdown: -------------------------------------------------------------------------------- 1 | macro-compat is a small library which allows you to compile macros with 2 | Scala 2.10.x which are written to the Scala 2.11/2 macro API. 3 | 4 | Find the project online, 5 | 6 | - Source: [https://github.com/milessabin/macro-compat][source] 7 | - CI: [https://travis-ci.org/milessabin/macro-compat][ci] 8 | - Gitter: [https://gitter.im/milessabin/macro-compat][gitter] 9 | 10 | macro-compat is an Open Source project under the Apache License v2. 11 | 12 | [source]: https://github.com/milessabin/macro-compat 13 | [ci]: https://travis-ci.org/milessabin/macro-compat 14 | [gitter]: https://gitter.im/milessabin/macro-compat 15 | -------------------------------------------------------------------------------- /notes/1.0.3.markdown: -------------------------------------------------------------------------------- 1 | This is the final release of [macro-compat-1.0.3][macro-compat]. 2 | 3 | These release notes provide a summary of changes since macro-compat 1.0.2. 4 | 5 | * Added TypeName/TermName extractors. 6 | 7 | * Added Context#internal.gen.mkAttributedRef. 8 | 9 | * Added Context#untypecheck (thanks to [Naoki Aoyama][AoiroAoino]). 10 | 11 | * Added Symbol#companion (thanks to [Naoki Aoyama][AoiroAoino]). 12 | 13 | * Aligned Type#companion with 2.11.x behaviour. 14 | 15 | Many thanks to everyone who has contributed ideas, enthusiasm and 16 | encouragement. 17 | 18 | [macro-compat]: https://github.com/milessabin/macro-compat 19 | [AoiroAoino]: https://twitter.com/AoiroAoino 20 | -------------------------------------------------------------------------------- /notes/1.0.2.markdown: -------------------------------------------------------------------------------- 1 | This is the final release of [macro-compat-1.0.2][macro-compat]. 2 | 3 | These release notes provide a summary of changes since macro-compat 1.0.1. 4 | 5 | * There are multiple problems with `RangePositions` in Scala 2.10.5. As 6 | as a workaround these are now stripped from user macro result trees if 7 | the `-Yrangepos` compiler flag is enabled. 8 | 9 | * Bundle classes can now have parent traits/abstract classes. 10 | 11 | * Support for `Context#internal.constantType` has been added. 12 | 13 | * [Phil Wills][philwills] fixed a bug in the `companion` method on 14 | `Type`. 15 | 16 | Many thanks to everyone who has contributed ideas, enthusiasm and 17 | encouragement. 18 | 19 | [macro-compat]: https://github.com/milessabin/macro-compat 20 | [philwills]: https://twitter.com/philwills 21 | -------------------------------------------------------------------------------- /notes/1.1.0.markdown: -------------------------------------------------------------------------------- 1 | This is the final release of [macro-compat-1.1.0][macro-compat]. 2 | 3 | Binary compatibility was broken in 1.0.3 and again in 1.0.6. In version 1.0.7 binary compatability with 1.0.3-5 were 4 | been restored and 1.0.6 is now deprecated. The binary compatibility breaking changes have been moved to 1.1.0 and 5 | hopefully the addition of [MiMa][mima] to the build will make a recurrence of this sort of breakage much less likely 6 | in future. 7 | 8 | These release notes provide a summary of changes since macro-compat 1.0.7. 9 | 10 | + Binary compatibility breaking changes from 1.0.6 have been reinstated. 11 | 12 | Many thanks to everyone who has contributed ideas, enthusiasm and encouragement. 13 | 14 | [macro-compat]: https://github.com/milessabin/macro-compat 15 | [mima]: https://github.com/typesafehub/migration-manager 16 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | scalacOptions += "-deprecation" 2 | libraryDependencies += "org.slf4j" % "slf4j-nop" % "1.7.25" 3 | 4 | addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.8") 5 | addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.1") 6 | addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "1.0.0") 7 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.27") 8 | addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.3.0") 9 | addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.3") 10 | addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "0.4.0") 11 | addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "0.4.0") 12 | addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.3.7") 13 | -------------------------------------------------------------------------------- /notes/1.0.7.markdown: -------------------------------------------------------------------------------- 1 | This is the final release of [macro-compat-1.0.7][macro-compat]. 2 | 3 | Binary compatibility was broken in 1.0.3 and again in 1.0.6. In version 1.0.7 binary compatability with 1.0.3-5 has 4 | been restored and 1.0.6 is now deprecated. The binary compatibility breaking changes have been moved to 1.1.0 and 5 | hopefully the addition of [MiMa][mima] to the build will make a recurrence of this sort of breakage much less likely 6 | in future. 7 | 8 | These release notes provide a summary of changes since macro-compat 1.0.6. 9 | 10 | + Restored binary compatibility with versions 1.0.3-5. 11 | 12 | + Added [MiMa][mima] to the build. 13 | 14 | Many thanks to everyone who has contributed ideas, enthusiasm and encouragement. 15 | 16 | [macro-compat]: https://github.com/milessabin/macro-compat 17 | [mima]: https://github.com/typesafehub/migration-manager 18 | -------------------------------------------------------------------------------- /notes/1.0.1.markdown: -------------------------------------------------------------------------------- 1 | This is the final release of [macro-compat-1.0.1][macro-compat]. 2 | 3 | These release notes provide a summary of changes since macro-compat 1.0.0. 4 | 5 | * The `companion` method on `Type` and `paramLists` method on `MethodSymbol`. 6 | were contribued by [Phil Wills][philwills]. 7 | 8 | Many thanks to everyone who has contributed ideas, enthusiasm and 9 | encouragement, in particular to the contributors to 1.0.0: [Adelbert 10 | Chang][adelbertchang], [Alistair Johnson][inthenow], [Michael 11 | Pilquist][mpilquist] and [Travis Brown][travisbrown]. 12 | 13 | [macro-compat]: https://github.com/milessabin/macro-compat 14 | [adelbertchang]: https://twitter.com/adelbertchang 15 | [inthenow]: https://twitter.com/AlistairUSM 16 | [mpilquist]: https://twitter.com/mpilquist 17 | [philwills]: https://twitter.com/philwills 18 | [travisbrown]: https://twitter.com/travisbrown 19 | 20 | -------------------------------------------------------------------------------- /core/src/main/scala_2.10/macrocompat/aliases.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Miles Sabin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scala.reflect.macros 18 | 19 | object blackbox { 20 | type Context = macrocompat.CompatContext 21 | } 22 | 23 | object whitebox { 24 | type Context = macrocompat.CompatContext 25 | } 26 | 27 | -------------------------------------------------------------------------------- /notes/1.0.4.markdown: -------------------------------------------------------------------------------- 1 | This is the final release of [macro-compat-1.0.4][macro-compat]. 2 | 3 | These release notes provide a summary of changes since macro-compat 1.0.3. 4 | 5 | * Added Symbol#isConstructor. 6 | 7 | * Added Symbol#info and infoIn. 8 | 9 | * Added Type#decl and Context#internal.enclosingOwner. 10 | 11 | * Added compileTimeOnly annotation (thanks to [Alistair Johnson][inthenow]). 12 | 13 | * On 2.11.x the @bundle annotation is now implemented as a macro annotation which leaves its annottees unchanged. This 14 | completely eliminates all traces of macro-compat on 2.11.x builds. Thanks to [Eugene Burmako][xenoby] for the 15 | suggestion. 16 | 17 | Many thanks to everyone who has contributed ideas, enthusiasm and 18 | encouragement. 19 | 20 | [macro-compat]: https://github.com/milessabin/macro-compat 21 | [inthenow]: https://twitter.com/AlistairUSM 22 | [xenoby]: https://twitter.com/xeno_by 23 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | We are committed to providing a friendly, safe and welcoming environment for 4 | all, regardless of level of experience, gender, gender identity and expression, 5 | sexual orientation, disability, personal appearance, body size, race, 6 | ethnicity, age, religion, nationality, or other such characteristics. 7 | 8 | Everyone is expected to follow the [Scala Code of Conduct] when discussing the 9 | project on the available communication channels. If you are being harassed, 10 | please contact us immediately so that we can support you. 11 | 12 | ## Moderation 13 | 14 | Any questions, concerns, or moderation requests please contact a member of the 15 | project. 16 | 17 | - Miles Sabin: [gitter](https://gitter.im/milessabin) | [twitter](https://twitter.com/milessabin) | [email](mailto:miles@milessabin.com) 18 | 19 | [Scala Code of Conduct]: https://www.scala-lang.org/conduct/ 20 | -------------------------------------------------------------------------------- /core/src/main/scala_2.10/macrocompat/annotations.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Miles Sabin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package scala.annotation 18 | 19 | import scala.annotation.meta._ 20 | 21 | @getter @setter @beanGetter @beanSetter @companionClass @companionMethod 22 | final class compileTimeOnly(message: String) extends scala.annotation.StaticAnnotation 23 | -------------------------------------------------------------------------------- /test/src/main/scala/testmacro/reporter.scala: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2015 Miles Sabin 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package reporters 19 | 20 | import scala.reflect.macros.whitebox 21 | 22 | @macrocompat.bundle 23 | class ErrorReporterMacro(val c: whitebox.Context) { 24 | import c.universe._ 25 | def testReporterImplicit(t: Tree): Tree = { 26 | val casted = c.asInstanceOf[reflect.macros.runtime.Context] 27 | val typer = casted.callsiteTyper 28 | val ctx = typer.context 29 | val r = ctx.reporter 30 | r.errors.map(_.errMsg) 31 | t 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/scala_2.11+/macrocompat/bundle_2.11.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Miles Sabin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package macrocompat 18 | 19 | import scala.language.experimental.macros 20 | 21 | import scala.annotation.StaticAnnotation 22 | 23 | import scala.reflect.macros.blackbox 24 | 25 | class bundle extends StaticAnnotation { 26 | def macroTransform(annottees: Any*): Any = macro BundleMacro.bundleImpl 27 | } 28 | 29 | class BundleMacro(val c: blackbox.Context) { 30 | import c.universe._ 31 | 32 | def bundleImpl(annottees: Tree*): Tree = { 33 | q""" ..$annottees """ 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/src/main/scala/testmacro/delegate.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Miles Sabin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package delegate 18 | 19 | import scala.language.experimental.macros 20 | 21 | import scala.reflect.macros.whitebox 22 | 23 | import macrocompat.bundle 24 | 25 | class Delegate { 26 | def delegate: Unit = macro DelegateMacro.delegate 27 | } 28 | 29 | @bundle 30 | class DelegateMacro(val c: whitebox.Context) { outer => 31 | import c.universe._ 32 | 33 | class D(override val c: outer.c.type) extends Delegatee(c) 34 | def delegate: Tree = (new D(c)).delegate 35 | } 36 | 37 | @bundle 38 | class Delegatee(val c: whitebox.Context) { 39 | import c.universe._ 40 | 41 | def delegate: Tree = { 42 | q"()" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/src/main/scala/testmacro/testannotations.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Miles Sabin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package testannotations 18 | 19 | import scala.language.experimental.macros 20 | import scala.reflect.macros.whitebox 21 | import macrocompat.bundle 22 | 23 | import scala.annotation.{compileTimeOnly, StaticAnnotation} 24 | 25 | /** 26 | * Test is that this should compile 27 | */ 28 | @compileTimeOnly("enable macro paradise to expand macro annotations") 29 | class Testbundle extends StaticAnnotation { 30 | def macroTransform(annottees: Any*): Any = macro TestbundleMacros.impl 31 | } 32 | 33 | @bundle 34 | class TestbundleMacros(val c: whitebox.Context) { 35 | import c.universe._ 36 | 37 | def impl(annottees: c.Expr[Any]*): c.Expr[Any] = c.Expr[Any](q"") 38 | } 39 | -------------------------------------------------------------------------------- /test/src/test/scala/testmacro/companionobject.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Miles Sabin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package companionobject 18 | 19 | import org.junit.Test 20 | import org.junit.Assert._ 21 | 22 | class MacroCompatTests { 23 | @Test 24 | def classWithCompanionObject: Unit = { 25 | val res = ClassWithCompanionObject.foo 26 | assert(res == 1) 27 | } 28 | 29 | @Test 30 | def classWithCompanionObjectWithMixin: Unit = { 31 | val res = ClassWithCompanionObjectWithMixin.foo 32 | assert(res == 1) 33 | 34 | val res2 = ClassWithCompanionObjectWithMixin.bar 35 | assert(res2 == 1) 36 | } 37 | 38 | @Test 39 | def classWithCompanionObjectWithParent: Unit = { 40 | val res = ClassWithCompanionObjectWithParent.foo 41 | assert(res == 1) 42 | 43 | val res2 = ClassWithCompanionObjectWithParent.bar 44 | assert(res2 == 1) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /core/src/main/scala_2.10/macrocompat/contextreporter.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Miles Sabin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package macrocompat 18 | 19 | class ContextReporter[ 20 | C <: tools.nsc.typechecker.Contexts#Context 21 | ](val c: C) { 22 | def hasErrors = c.hasErrors 23 | def isBuffering = c.bufferErrors 24 | def isThrowing = c.throwErrors 25 | def errors = c.errBuffer.toVector 26 | def warnings = c.warningsBuffer.toVector 27 | def firstError = errors.headOption 28 | def clearAll(): Unit = { 29 | c.errBuffer.clear 30 | c.warningsBuffer.clear 31 | } 32 | def clearAllErrors(): Unit = c.errBuffer.clear 33 | } 34 | class TypecheckerContextExtensions[ 35 | C <: tools.nsc.typechecker.Contexts#Context 36 | ](val c: C) { 37 | def reporter = new ContextReporter[C](c) 38 | } 39 | 40 | object TypecheckerContextExtensions { 41 | implicit def contextExtensions[C <: tools.nsc.typechecker.Contexts#Context](c: C) = 42 | new TypecheckerContextExtensions[C](c) 43 | } 44 | -------------------------------------------------------------------------------- /test/src/main/scala/testmacro/companionobject.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Miles Sabin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package companionobject 18 | 19 | import scala.reflect.macros.whitebox 20 | 21 | 22 | @macrocompat.bundle 23 | class ClassWithoutCompanionObject(val c: whitebox.Context) 24 | 25 | 26 | @macrocompat.bundle 27 | class ClassWithCompanionObject(val c: whitebox.Context) 28 | 29 | object ClassWithCompanionObject { 30 | def foo = 1 31 | } 32 | 33 | 34 | trait ClassWithCompanionObjectMixin { 35 | def bar = 1 36 | } 37 | 38 | @macrocompat.bundle 39 | class ClassWithCompanionObjectWithMixin(val c: whitebox.Context) 40 | 41 | object ClassWithCompanionObjectWithMixin extends AnyRef with ClassWithCompanionObjectMixin { 42 | def foo = 1 43 | } 44 | 45 | 46 | abstract class ClassWithCompanionObjectParent { 47 | def bar = 1 48 | } 49 | 50 | @macrocompat.bundle 51 | class ClassWithCompanionObjectWithParent(val c: whitebox.Context) 52 | 53 | object ClassWithCompanionObjectWithParent extends ClassWithCompanionObjectParent { 54 | def foo = 1 55 | } 56 | -------------------------------------------------------------------------------- /test/src/main/scala/testmacro/override.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Miles Sabin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package overrides 18 | 19 | import scala.language.experimental.macros 20 | 21 | import scala.reflect.macros.{ blackbox, whitebox } 22 | 23 | import macrocompat.bundle 24 | 25 | object Override0 { 26 | def bar(i: Int): String = macro Sub0.barImpl 27 | } 28 | 29 | @bundle 30 | trait Super0 { 31 | val c: blackbox.Context 32 | 33 | import c.universe._ 34 | 35 | def fooImpl(i: Tree): Tree = q""" "foo" """ 36 | } 37 | 38 | @bundle 39 | class Sub0(val c: whitebox.Context) extends AnyRef with Super0 { 40 | import c.universe._ 41 | 42 | def barImpl(i: Tree): Tree = q""" "bar" """ 43 | } 44 | 45 | object Override1 { 46 | def foo(i: Int): String = macro Super1.fooImpl 47 | def bar(i: Int): String = macro Sub1.barImpl 48 | } 49 | 50 | @bundle 51 | class Super1(val c: blackbox.Context) { 52 | import c.universe._ 53 | 54 | def fooImpl(i: Tree): Tree = q""" "foo" """ 55 | } 56 | 57 | @bundle 58 | class Sub1(override val c: whitebox.Context) extends Super1(c) { 59 | import c.universe._ 60 | 61 | def barImpl(i: Tree): Tree = q""" "bar" """ 62 | } 63 | -------------------------------------------------------------------------------- /notes/1.1.1.markdown: -------------------------------------------------------------------------------- 1 | This is the final release of [macro-compat-1.1.1][macro-compat]. 2 | 3 | Despite being binary compatible with macro-compat 1.1.0, this release represents a major overhaul of the underlying 4 | implementation and a significant extension to the breadth of the support for 2.11.x style macros on 2.10.x. This has 5 | made it possible to fully support shapeless (as of version 2.3.0) with a single branch and no Scala version variant 6 | source. Given that shapeless contains some fairly large and sophisticated macros which exploit private internals of 7 | the macro API this is a significant milestone and suggests that macro-compat is ready for wider use. 8 | 9 | I would especially like to thank [Dale Wijnand][@dwijnand] for his relentless efforts adding support for features 10 | required by shapeless, and [Chris Hodapp][@clhodapp] for his work on and inspiration in supporting access to macro 11 | internals. 12 | 13 | These release notes provide a summary of changes since macro-compat 1.1.0, 14 | 15 | * Macro bundle rewrite now more closely matches 2.11.x structure, supporting bundle inheritance and companions. 16 | * Added to c.internal: newTermSymbol, polyType, setInfo, singleType, substituteSymbols, thisType, typeRef 17 | * Added c.internal.decorators (thanks to [@dwijnand][]). 18 | * Added to Symbol: isAbstract, overrides (thanks to [@dwijnand][]). 19 | * Added to Tree: nonEmpty (thanks to [@dwijnand][]). 20 | * Added to Type: finalResultType, paramLists, typeArgs (thanks to [@dwijnand][]). 21 | * dealias implementation match 2.11.x semantics. 22 | * Added support for use of internal ContextReporter (thanks to [@clhodapp][] 23 | * Added support for 2.11 style ImplicitCandidate (thanks to [@dwijnand][]). 24 | * Now uses macro-paradise 2.1.0. 25 | * Added 2.12.0-M3 to the cross build version list (thanks to [@dwijnand][]) 26 | 27 | Many thanks to everyone who has contributed ideas, enthusiasm and encouragement. 28 | 29 | [macro-compat]: https://github.com/milessabin/macro-compat 30 | [mima]: https://github.com/typesafehub/migration-manager 31 | [@clhodapp]: https://twitter.com/clhodapp 32 | [@dwijnand]: https://github.com/dwijnand 33 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | 3 | scala: 4 | - 2.10.7 5 | - 2.11.12 6 | - 2.12.8 7 | - 2.13.0-RC2 8 | 9 | jdk: 10 | - oraclejdk8 11 | 12 | cache: 13 | directories: 14 | - "$HOME/.ivy2/cache" 15 | - "$HOME/.sbt" 16 | 17 | before_cache: 18 | - find "$HOME/.sbt" -name "*.lock" -print -delete 19 | - find "$HOME/.ivy2" -name "ivydata-*.properties" -print -delete 20 | 21 | script: 22 | - if [[ "${TRAVIS_PULL_REQUEST}" == "false" && 23 | "${TRAVIS_BRANCH}" == "master" && 24 | $(cat version.sbt) =~ "-SNAPSHOT" 25 | ]]; then 26 | sbt ++${TRAVIS_SCALA_VERSION} validate publish ; 27 | else 28 | sbt ++${TRAVIS_SCALA_VERSION} validate ; 29 | fi 30 | 31 | sudo: false 32 | 33 | env: 34 | global: 35 | - secure: IpoxV5fFSLBFGiwrY9jdqb0WP/kj9gux0Sp6lettwCYVZxlsCmfsVSwAJsUoxM7vSR+vA45vhi7jjxFpo9VQX/QQctM15Ob8vb1cAwqatosuLFmSkC1bGphA38d/863MBkAQu8b5wG8suRsbPJJRhVP+Ll1O4E1/wNcabsg4+hn53XQtNsTJKIeuZWaGp1wrpct/1nlSY0nvS7kB2N4jwVGMgj+7BHR9/ATGL8bCuC3LHVRlFoylKlzX6Q0gNu+oLT2n/BckxmH0EGiPfauo+6GG8XSAB3mO1/czG4Lc+WBURNSVEA7fi2knK1vBpJ7qH043Cq1rD+KUJPzLy73Qnwx0Qv1ESrvhxByP4QReB10ZI4hE0QoLk9Bm5wfxnucrA4FX4WlJS7ivmo7sa4bLAXz/7TfnbllvW4rDiIRN7m/ksBahXBAT9yEIR9+pviF+Ulox7JMYqjCm4i5Fl+DvCQFaxJ5Kk56IBtqavQVjQmLWwFCnOtI35tRAuU+38zKAdpcsOoZg7bdGteRywf86oxEoCh3xwPIhkLpyIY0vVvBX6HT6ct+gDb9SnkMYvVmN6tYKY67EvEEMaTVJ1wL5l8rRYoRYZEMZ7Fu3UPTAoYXvfQZwR82x0ffr+uX8YayW+jnaL6L9Bmj07LvKfgkn7+4iwti3GBJjDJWT3IXwCw0= 36 | - secure: Exgiy4yr7UpfdSFCrOK2Kr7PHSPSXw8/wR54WvJUV43Ua0d2ux/9y14i6H6+h6+XXBETRqklzigHcFCSji278wBHzwNgbzg9CPeVTpwQuNLM+v0MYLss74YbWxp6L0N38DygQ2PNq77hFs+JYiFEb1PzDI5m2Rhoxz2e9ARDJ0wzZMaL5oFJ2EIpXpda73KJeaYL9hJYyIPst9GyfYJhEiB8iOztxOQNokon5u9jwYMwDW/GQuby+v55FrLf7LMwPDlnv02EK3GQZWCzuwnX4/ND8X9rzRUGTPPkEiroqdhHpdO9i0TM5l+6wUP7Q96xBdHhBV6m27qWGLPsS0+IqZvZxYIVdDrVaysmvAvRtzjbjinVVsep+zIJqcJYgBLu7x86Fa4Rm3QCmymGx6inCno0Xn+qvS2b7pc+cQK+CaJ7KjZuiYs8cNMFHBzCcVilJDmcy6RCYYhr1UJDXGPbTkd0UHKe0RRix36hZwJol1w2oCu2msaEte5xpY+kxt8UE7lzTP9MgBqEWIqfjdKjFg2yQ0X+kGyiU6Kh28ik+jZ4WuzqLMoR5qulDkVlL+2FtPfVWpfxQJZOYer7TYL9RjwNqkoKN9SDqXyOg51AWalMFoXlfTyI3ZcTu0OIjoaoR0m7ulKi7TgZ2rp3pqiLTkLLO1j9ScJdtRcN72SXGrg= 37 | 38 | matrix: 39 | include: 40 | ## Add Scala Native to the build matrix using Scala 2.11. 41 | - scala: 2.11.12 42 | sudo: required 43 | before_script: 44 | - sudo chmod +x /usr/local/bin/sbt 45 | before_install: 46 | - sudo apt-get update 47 | - curl https://raw.githubusercontent.com/scala-native/scala-native/master/scripts/travis_setup.sh | bash -x 48 | script: 49 | - if [[ "${TRAVIS_PULL_REQUEST}" == "false" && 50 | "${TRAVIS_BRANCH}" == "master" && 51 | $(cat version.sbt) =~ "-SNAPSHOT" 52 | ]]; then 53 | sbt ++${TRAVIS_SCALA_VERSION} nativeTest/run coreNative/publish ; 54 | else 55 | sbt ++${TRAVIS_SCALA_VERSION} nativeTest/run ; 56 | fi 57 | 58 | notifications: 59 | webhooks: 60 | urls: 61 | - https://webhooks.gitter.im/e/a4f66c5cc6ca6aae0e94 62 | -------------------------------------------------------------------------------- /native-test/src/main/scala/MacroCompatTests.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Miles Sabin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package testmacro 18 | 19 | object MacroCompatTests extends App { 20 | implicit class SymMinus(s: Symbol) { 21 | def -(f: => Unit): Unit = f 22 | } 23 | 24 | 'noArgMethodTree - { 25 | val res = TestObj.foo 26 | assert(res == 23) 27 | } 28 | 29 | 'oneArgMethodTree - { 30 | val res = TestObj.bar(23) 31 | assert(res == "bar") 32 | } 33 | 34 | 'variadicMethodTree - { 35 | val res = TestObj.baz(1, 2, 3) 36 | assert(res == 13) 37 | } 38 | 39 | 'genericMethodImplicitweakTypeTag - { 40 | val res = TestObj.quux( "foo" ) 41 | assert(res == "foo") 42 | } 43 | 44 | 'genericMethodExplicitweakTypeTag - { 45 | val res = TestObj.quuxExplicit( "foo" ) 46 | assert(res == "foo") 47 | } 48 | 49 | 'noArgMethodExpr - { 50 | val res = TestObj.fooE 51 | assert(res == 23) 52 | } 53 | 54 | 'oneArgMethodExpr - { 55 | val res = TestObj.barE(23) 56 | assert(res == "bar") 57 | } 58 | 59 | 'variadicMethodExpr - { 60 | val res = TestObj.bazE(1, 2, 3) 61 | assert(res == 13) 62 | } 63 | 64 | 'noArgMethodCTree - { 65 | val res = TestObj.fooCT 66 | assert(res == 23) 67 | } 68 | 69 | 'oneArgMethodCTree - { 70 | val res = TestObj.barCT(23) 71 | assert(res == "bar") 72 | } 73 | 74 | 'variadicMethodCTree - { 75 | val res = TestObj.bazCT(1, 2, 3) 76 | assert(res == 13) 77 | } 78 | 79 | 'symbolOf - { 80 | val res = TestObj.symbolOf(new Foo) 81 | assert(res == "class Foo") 82 | 83 | val res2 = TestObj.symbolOf(Foo) 84 | assert(res2 == "object Foo") 85 | 86 | val res3 = TestObj.symbolOf(new Bar) 87 | assert(res3 == "class Bar") 88 | 89 | val res4 = TestObj.symbolOf(Baz) 90 | assert(res4 == "object Baz") 91 | } 92 | 93 | 'accessingCompanion - { 94 | val res = TestObj.comp(new Foo) 95 | assert(res == "testmacro.Foo.type") 96 | 97 | val res2 = TestObj.comp(Foo) 98 | assert(res2 == "testmacro.Foo") 99 | 100 | val res3 = TestObj.comp(new Bar) 101 | assert(res3 == "") 102 | 103 | val res4 = TestObj.comp(Baz) 104 | assert(res4 == "") 105 | } 106 | 107 | 'accessingCompanionSymbol - { 108 | val res = TestObj.compSym(new Foo) 109 | assert(res == "object Foo") 110 | 111 | val res2 = TestObj.compSym(Foo) 112 | assert(res2 == "class Foo") 113 | 114 | val res3 = TestObj.compSym(new Bar) 115 | assert(res3 == "") 116 | 117 | val res4 = TestObj.compSym(Baz) 118 | assert(res4 == "") 119 | } 120 | 121 | 'companionReference - { 122 | val res: Foo.type = TestObj.ref(new Foo) 123 | assert((res: Foo.type) eq Foo) 124 | 125 | } 126 | 127 | def typecheck: Unit = { 128 | TestObj.typecheck 129 | } 130 | 131 | def modifiers: Unit = { 132 | TestObj.modifiers 133 | } 134 | 135 | 'untypecheck - { 136 | val res = TestObj.untypecheck(23) 137 | assert((res: Int) == 23) 138 | } 139 | 140 | def dealiasTypes: Unit = { 141 | type T = List[Int] 142 | TestObj.ensureOneTypeArg[T] 143 | } 144 | 145 | def freshName: Unit = { 146 | val nme = TestObj.freshName 147 | } 148 | 149 | def annotation: Unit = { 150 | class Annotation extends scala.annotation.StaticAnnotation 151 | @Annotation trait T 152 | 153 | val annTpe = TestObj.AnnotationType[T] 154 | val a = new Annotation 155 | val a0: annTpe.Ann = a 156 | } 157 | 158 | 'implicitCandidate - { 159 | import TestObj.materialize 160 | val res = implicitly[List[String]] 161 | assert(res == List("materialize")) 162 | } 163 | 164 | def finalResultType: Unit = { 165 | TestObj.finalResultType 166 | } 167 | 168 | def dealiasTypeArgs: Unit = { 169 | TestObj.dealiasTypeArgs 170 | } 171 | } 172 | 173 | class Foo 174 | object Foo 175 | 176 | class Bar 177 | 178 | object Baz 179 | -------------------------------------------------------------------------------- /test/src/test/scala/testmacro/testmacro.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Miles Sabin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package testmacro 18 | 19 | import org.junit.Test 20 | import org.junit.Assert._ 21 | 22 | class MacroCompatTests { 23 | @Test 24 | def noArgMethodTree: Unit = { 25 | val res = TestObj.foo 26 | assert(res == 23) 27 | } 28 | 29 | @Test 30 | def oneArgMethodTree: Unit = { 31 | val res = TestObj.bar(23) 32 | assert(res == "bar") 33 | } 34 | 35 | @Test 36 | def variadicMethodTree: Unit = { 37 | val res = TestObj.baz(1, 2, 3) 38 | assert(res == 13) 39 | } 40 | 41 | @Test 42 | def genericMethodImplicitweakTypeTag: Unit = { 43 | val res = TestObj.quux( "foo" ) 44 | assert(res == "foo") 45 | } 46 | 47 | @Test 48 | def genericMethodExplicitweakTypeTag: Unit = { 49 | val res = TestObj.quuxExplicit( "foo" ) 50 | assert(res == "foo") 51 | } 52 | 53 | @Test 54 | def noArgMethodExpr: Unit = { 55 | val res = TestObj.fooE 56 | assert(res == 23) 57 | } 58 | 59 | @Test 60 | def oneArgMethodExpr: Unit = { 61 | val res = TestObj.barE(23) 62 | assert(res == "bar") 63 | } 64 | 65 | @Test 66 | def variadicMethodExpr: Unit = { 67 | val res = TestObj.bazE(1, 2, 3) 68 | assert(res == 13) 69 | } 70 | 71 | @Test 72 | def noArgMethodCTree: Unit = { 73 | val res = TestObj.fooCT 74 | assert(res == 23) 75 | } 76 | 77 | @Test 78 | def oneArgMethodCTree: Unit = { 79 | val res = TestObj.barCT(23) 80 | assert(res == "bar") 81 | } 82 | 83 | @Test 84 | def variadicMethodCTree: Unit = { 85 | val res = TestObj.bazCT(1, 2, 3) 86 | assert(res == 13) 87 | } 88 | 89 | @Test 90 | def symbolOf: Unit = { 91 | val res = TestObj.symbolOf(new Foo) 92 | assert(res == "class Foo") 93 | 94 | val res2 = TestObj.symbolOf(Foo) 95 | assert(res2 == "object Foo") 96 | 97 | val res3 = TestObj.symbolOf(new Bar) 98 | assert(res3 == "class Bar") 99 | 100 | val res4 = TestObj.symbolOf(Baz) 101 | assert(res4 == "object Baz") 102 | } 103 | 104 | @Test 105 | def accessingCompanion: Unit = { 106 | val res = TestObj.comp(new Foo) 107 | assert(res == "testmacro.Foo.type") 108 | 109 | val res2 = TestObj.comp(Foo) 110 | assert(res2 == "testmacro.Foo") 111 | 112 | val res3 = TestObj.comp(new Bar) 113 | assert(res3 == "") 114 | 115 | val res4 = TestObj.comp(Baz) 116 | assert(res4 == "") 117 | } 118 | 119 | @Test 120 | def accessingCompanionSymbol: Unit = { 121 | val res = TestObj.compSym(new Foo) 122 | assert(res == "object Foo") 123 | 124 | val res2 = TestObj.compSym(Foo) 125 | assert(res2 == "class Foo") 126 | 127 | val res3 = TestObj.compSym(new Bar) 128 | assert(res3 == "") 129 | 130 | val res4 = TestObj.compSym(Baz) 131 | assert(res4 == "") 132 | } 133 | 134 | @Test 135 | def companionReference: Unit = { 136 | val res: Foo.type = TestObj.ref(new Foo) 137 | assert((res: Foo.type) eq Foo) 138 | 139 | } 140 | 141 | @Test 142 | def typecheck: Unit = { 143 | TestObj.typecheck 144 | } 145 | 146 | @Test 147 | def modifiers: Unit = { 148 | TestObj.modifiers 149 | } 150 | 151 | @Test 152 | def untypecheck: Unit = { 153 | val res = TestObj.untypecheck(23) 154 | assert((res: Int) == 23) 155 | } 156 | 157 | @Test 158 | def dealiasTypes: Unit = { 159 | type T = List[Int] 160 | TestObj.ensureOneTypeArg[T] 161 | } 162 | 163 | @Test 164 | def freshName: Unit = { 165 | val nme = TestObj.freshName 166 | } 167 | 168 | @Test 169 | def annotation: Unit = { 170 | class Annotation extends scala.annotation.StaticAnnotation 171 | @Annotation trait T 172 | 173 | val annTpe = TestObj.AnnotationType[T] 174 | val a = new Annotation 175 | val a0: annTpe.Ann = a 176 | } 177 | 178 | @Test 179 | def implicitCandidate: Unit = { 180 | import TestObj.materialize 181 | val res = implicitly[List[String]] 182 | assert(res == List("materialize")) 183 | } 184 | 185 | @Test 186 | def finalResultType: Unit = { 187 | TestObj.finalResultType 188 | } 189 | 190 | @Test 191 | def dealiasTypeArgs: Unit = { 192 | TestObj.dealiasTypeArgs 193 | } 194 | } 195 | 196 | class Foo 197 | object Foo 198 | 199 | class Bar 200 | 201 | object Baz 202 | -------------------------------------------------------------------------------- /test/src/main/scala/testmacro/exporthook.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Miles Sabin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package export 18 | 19 | import scala.language.experimental.macros 20 | 21 | import scala.annotation.StaticAnnotation 22 | import scala.reflect.macros.whitebox 23 | 24 | import macrocompat.bundle 25 | 26 | // https://issues.scala-lang.org/browse/SI-7332 27 | class Export[+T](val instance: T) extends AnyVal 28 | 29 | trait Exporter0[S[_]] { 30 | implicit def exports[F[t] >: S[t], T](implicit st: S[T]): Export[F[T]] = 31 | macro ExportMacro.exportsImpl0[F, T] 32 | } 33 | 34 | trait Exporter1[S[_[_]]] { 35 | implicit def exports[F[t[_]] >: S[t], T[_]](implicit st: S[T]): Export[F[T]] = 36 | macro ExportMacro.exportsImpl1[F, T] 37 | } 38 | 39 | trait Exporter00[S[_, _]] { 40 | implicit def exports[F[t, u] >: S[t, u], T, U](implicit st: S[T, U]): Export[F[T, U]] = 41 | macro ExportMacro.exportsImpl00[F, T, U] 42 | } 43 | 44 | class exported[A] extends StaticAnnotation { 45 | def macroTransform(annottees: Any*): Any = macro ExportMacro.exportedImpl 46 | } 47 | 48 | @bundle 49 | class ExportMacro(val c: whitebox.Context) { 50 | import c.universe._ 51 | 52 | def exportsImpl0[F[_], T](st: Tree) 53 | (implicit fTag: WeakTypeTag[F[_]], tTag: WeakTypeTag[T]): Tree = 54 | exportsImplAux(fTag.tpe.typeConstructor, List(tTag.tpe), st) 55 | 56 | def exportsImpl1[F[_[_]], T[_]](st: Tree) 57 | (implicit fTag: WeakTypeTag[F[Any]], tTag: WeakTypeTag[T[_]]): Tree = 58 | exportsImplAux(fTag.tpe.typeConstructor, List(tTag.tpe.typeConstructor), st) 59 | 60 | def exportsImpl00[F[_, _], T, U](st: Tree) 61 | (implicit fTag: WeakTypeTag[F[_, _]], tTag: WeakTypeTag[T], uTag: WeakTypeTag[U]): Tree = 62 | exportsImplAux(fTag.tpe.typeConstructor, List(tTag.tpe, uTag.tpe), st) 63 | 64 | def exportsImplAux(fTpe: Type, tTpes: List[Type], st: Tree): Tree = { 65 | val appTpe = appliedType(typeOf[Export[_]].typeConstructor, appliedType(fTpe, tTpes)) 66 | q"""new $appTpe($st)""" 67 | } 68 | 69 | def exportedImpl(annottees: Tree*): Tree = { 70 | val Apply(Select(New(AppliedTypeTree(_, List(tc))), _), _) = c.prefix.tree 71 | val tcTpe = c.typecheck(tc, c.TYPEmode).tpe 72 | val kind = tcTpe.typeParams.map(_.asType.typeParams.length) 73 | val suffix = 74 | kind match { 75 | case List(0) => "0" 76 | case List(1) => "1" 77 | case List(0, 0) => "00" 78 | case _ => 79 | c.abort(c.enclosingPosition, "$tc has an unsupported kind") 80 | } 81 | 82 | annottees match { 83 | case List(ClassDef(mods, typeName, List(), impl)) => 84 | val lpName = TypeName(c.freshName) 85 | val lpClass = ClassDef(mods, lpName, List(), impl) 86 | val termName = typeName.toTermName 87 | val methName = TermName(c.freshName) 88 | val (ts: List[TypeName], tds: List[Tree]) = (kind.map { k => 89 | val t = TypeName(c.freshName) 90 | (t, if(k == 0) q"type $t" else q"type $t[_]") 91 | }).unzip 92 | 93 | val importImpl = TermName(s"importImpl$suffix") 94 | 95 | q""" 96 | trait $typeName extends $termName.$lpName { 97 | import scala.language.experimental.macros 98 | implicit def $methName[..$tds]: $tc[..$ts] = macro _root_.export.ExportMacro.$importImpl[$tc, ..$ts] 99 | } 100 | object $termName { 101 | $lpClass 102 | } 103 | """ 104 | 105 | case other => 106 | c.abort(c.enclosingPosition, "Unexpected tree shape.") 107 | } 108 | } 109 | 110 | def importImpl0[F[_], T](implicit fTag: WeakTypeTag[F[_]], tTag: WeakTypeTag[T]): Tree = 111 | importImplAux(fTag.tpe.typeConstructor, List(tTag.tpe)) 112 | 113 | def importImpl1[F[_[_]], T[_]](implicit fTag: WeakTypeTag[F[Any]], tTag: WeakTypeTag[T[_]]): Tree = 114 | importImplAux(fTag.tpe.typeConstructor, List(tTag.tpe)) 115 | 116 | def importImpl00[F[_, _], T, U](implicit fTag: WeakTypeTag[F[_, _]], tTag: WeakTypeTag[T], uTag: WeakTypeTag[U]): Tree = 117 | importImplAux(fTag.tpe.typeConstructor, List(tTag.tpe, uTag.tpe)) 118 | 119 | def importImplAux(fTpe: Type, tTpes: List[Type]): Tree = { 120 | val appTpe = appliedType(typeOf[Export[_]].typeConstructor, appliedType(fTpe, tTpes)) 121 | 122 | val exporter = c.inferImplicitValue(appTpe, silent = true) 123 | if(exporter == EmptyTree) 124 | c.abort(c.enclosingPosition, s"Unable to find export of type $appTpe") 125 | 126 | q"""$exporter.instance""" 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /test/src/main/scala/testmacro/testmacro.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-6 Miles Sabin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package testmacro 18 | 19 | import scala.language.experimental.macros 20 | 21 | import scala.reflect.macros.whitebox 22 | 23 | import macrocompat.bundle 24 | 25 | object TestObj { 26 | def foo: Int = macro TestMacro.fooImpl 27 | def bar(i: Int): String = macro TestMacro.barImpl 28 | def baz(is: Int*): Int = macro TestMacro.bazImpl 29 | 30 | def fooE: Int = macro TestMacro.fooEImpl 31 | def barE(i: Int): String = macro TestMacro.barEImpl 32 | def bazE(is: Int*): Int = macro TestMacro.bazEImpl 33 | 34 | def fooCT: Int = macro TestMacro.fooCTImpl 35 | def barCT(i: Int): String = macro TestMacro.barCTImpl 36 | def bazCT(is: Int*): Int = macro TestMacro.bazCTImpl 37 | 38 | def quux[T](t: T): T = macro TestMacro.quuxImpl[T] 39 | 40 | def quuxExplicit[T](t: T): T = macro TestMacro.quuxExplicitImpl[T] 41 | 42 | def comp[T](t: T): String = macro TestMacro.compImpl[T] 43 | 44 | def symbolOf[T](t: T): String = macro TestMacro.symbolOfImpl[T] 45 | 46 | def compSym[T](t: T): String = macro TestMacro.compSymImpl[T] 47 | 48 | def ref[T](t: T): AnyRef = macro TestMacro.refImpl[T] 49 | 50 | def typecheck: Unit = macro TestMacro.typecheckImpl 51 | 52 | def untypecheck[T](t: T): T = macro TestMacro.untypecheckImpl[T] 53 | 54 | def modifiers: Unit = macro TestMacro.modifiersImpl 55 | 56 | def ensureOneTypeArg[T]: Unit = macro TestMacro.ensureOneTypeArgImpl[T] 57 | 58 | def freshName: String = macro TestMacro.freshNameImpl 59 | 60 | def finalResultType: Unit = macro TestMacro.finalResultType 61 | 62 | def dealiasTypeArgs: Unit = macro TestMacro.dealiasTypeArgs 63 | 64 | implicit def materialize: List[String] = macro TestMacro.materialize 65 | 66 | trait AnnotationType[T] { 67 | type Ann 68 | } 69 | object AnnotationType { 70 | def apply[T](implicit annTpe: AnnotationType[T]): Aux[T, annTpe.Ann] = annTpe 71 | type Aux[T, Ann0] = AnnotationType[T] { type Ann = Ann0 } 72 | implicit def materialize[T, Ann]: Aux[T, Ann] = macro TestMacro.annotationTypeImpl[T, Ann] 73 | def instance[T, Ann0]: Aux[T, Ann0] = new AnnotationType[T] { type Ann = Ann0 } 74 | } 75 | 76 | implicit def convertImpl[T](t: T): String = macro TestMacro.convertImpl[T] 77 | 78 | def implicitProperties: (String, String, String, String) = macro TestMacro.implicitProperties 79 | } 80 | 81 | @bundle 82 | abstract class TestMacroBase { 83 | val c: whitebox.Context 84 | import c.universe._ 85 | 86 | def abstractClassAbstractMethod: Tree 87 | 88 | def abstractClassMethodUsingCompanionObject: Tree = q"${TestMacroBase.foo}" 89 | } 90 | 91 | object TestMacroBase { 92 | def foo = 1 93 | } 94 | 95 | @bundle 96 | trait TestUtil { 97 | val c: whitebox.Context 98 | import c.universe._ 99 | 100 | def util(t: Type): Tree = { 101 | q""" () """ 102 | } 103 | 104 | def traitAbstractMethod: Tree 105 | 106 | def traitMethodUsingCompanionObject: Tree = q"${TestUtil.foo}" 107 | } 108 | 109 | object TestUtil { 110 | def foo = 1 111 | } 112 | 113 | @bundle 114 | class TestMacro(val c: whitebox.Context) extends TestMacroBase with TestUtil { 115 | import c.universe._ 116 | 117 | // Test for early use of context 118 | val intTpe = typeOf[Int] 119 | val bounds = internal.typeBounds(typeOf[Any], typeOf[Nothing]) 120 | 121 | def fooImpl: Tree = { 122 | val nme0 = TermName(c.freshName) 123 | val TermName(str0) = nme0 124 | val nme1 = TypeName(c.freshName) 125 | val TypeName(str1) = nme1 126 | val i = Ident(nme0) 127 | 128 | val nme2 = TermName(c.freshName("pfx")) 129 | val nme3 = c.freshName(TermName("pfx")) 130 | 131 | val nme4 = termNames.CONSTRUCTOR 132 | val nme5 = typeNames.WILDCARD 133 | 134 | q""" 23 """ 135 | } 136 | 137 | def barImpl(i: Tree): Tree = q""" "bar" """ 138 | 139 | def bazImpl(is: Tree*): Tree = q""" 13 """ 140 | 141 | def quuxImpl[T: WeakTypeTag](t: Tree): Tree = t 142 | 143 | def quuxExplicitImpl[T](t: Tree)(T: WeakTypeTag[T]): Tree = t 144 | 145 | def fooEImpl: c.Expr[Int] = c.Expr[Int](q""" 23 """) 146 | 147 | def barEImpl(i: c.Expr[Int]): c.Expr[String] = c.Expr[String](q""" "bar" """) 148 | 149 | def bazEImpl(is: c.Expr[Int]*): c.Expr[Int] = c.Expr[Int](q""" 13 """) 150 | 151 | def quuxEImpl[T: c.WeakTypeTag](t: c.Expr[T]): c.Expr[T] = c.Expr[T](t.tree) 152 | 153 | def fooCTImpl: c.Tree = q""" 23 """ 154 | 155 | def barCTImpl(i: Tree): c.Tree = q""" "bar" """ 156 | 157 | def bazCTImpl(is: Tree*): c.Tree = q""" 13 """ 158 | 159 | def compImpl[T: c.WeakTypeTag](t: c.Expr[T]): Tree = { 160 | val typ = weakTypeOf[T] 161 | q""" ${typ.companion.toString } """ 162 | } 163 | 164 | def symbolOfImpl[T: c.WeakTypeTag](t: c.Expr[T]): Tree = { 165 | val sym = symbolOf[T] 166 | q""" ${sym.toString } """ 167 | } 168 | 169 | def compSymImpl[T: c.WeakTypeTag](t: c.Expr[T]): Tree = { 170 | val sym = symbolOf[T] 171 | q""" ${sym.companion.toString } """ 172 | } 173 | 174 | def refImpl[T: c.WeakTypeTag](t: c.Expr[T]): Tree = { 175 | val sym = symbolOf[T] 176 | val ref = c.internal.gen.mkAttributedRef(sym.companion) 177 | q""" $ref """ 178 | } 179 | 180 | def typecheckImpl: Tree = { 181 | assert(c.typecheck(q""" 1+1 """, c.TERMmode).tpe <:< typeOf[Int]) 182 | assert(c.typecheck(tq""" Int """, c.TYPEmode).tpe =:= typeOf[Int]) 183 | assert(c.typecheck(tq""" List """, c.TYPEmode).tpe =:= typeOf[List[Any]].typeConstructor) 184 | assert(c.typecheck(tq""" List[Int] """, c.TYPEmode).tpe =:= typeOf[List[Int]]) 185 | assert(c.typecheck(tq""" Map """, c.TYPEmode).tpe =:= typeOf[Map[Any, Any]].typeConstructor) 186 | assert(c.typecheck(tq""" Map[Int, String] """, c.TYPEmode).tpe =:= typeOf[Map[Int, String]]) 187 | q""" () """ 188 | } 189 | 190 | def untypecheckImpl[T: c.WeakTypeTag](t: c.Expr[T]): Tree = { 191 | val tree = c.untypecheck(t.tree) 192 | q""" $tree """ 193 | } 194 | 195 | def modifiersImpl: Tree = { 196 | val mods = Modifiers(Flag.IMPLICIT, TypeName("Pw"), List(EmptyTree)) 197 | val Modifiers(flags, pw, annots) = mods 198 | q""" () """ 199 | } 200 | 201 | def ensureOneTypeArgImpl[T: c.WeakTypeTag]: Tree = 202 | weakTypeOf[T].dealias match { 203 | case TypeRef(_, _, List(_)) => q" () " 204 | case _ => c.abort( 205 | c.enclosingPosition, 206 | s"Cannot dealias ${weakTypeOf[T]} to a type with one type argument" 207 | ) 208 | } 209 | 210 | def freshNameImpl: Tree = { 211 | val name = c.freshName().toString 212 | q" $name " 213 | } 214 | 215 | def annotationTypeImpl[T: c.WeakTypeTag, R: c.WeakTypeTag]: Tree = { 216 | val tpe = weakTypeOf[T] 217 | val annotations = tpe.dealias.typeSymbol.annotations 218 | 219 | annotations.map { ann => 220 | // Ensuring we can call this one 221 | ann.tree.children.tail 222 | } 223 | 224 | // Test 2.11.x apply 225 | val tree = annotations.head.tree 226 | val ann2 = Annotation(tree) 227 | 228 | annotations match { 229 | case Nil => c.abort(c.enclosingPosition, s"No annotation found on $tpe") 230 | case ann :: _ => q" _root_.testmacro.TestObj.AnnotationType.instance[$tpe, ${ann.tree.tpe}] " 231 | } 232 | } 233 | 234 | def useUtil: Tree = 235 | util(typeOf[Int]) 236 | 237 | def const: Tree = 238 | TypeTree(internal.constantType(Constant(23))) 239 | 240 | def materialize: Tree = { 241 | import c.ImplicitCandidate 242 | 243 | val oi = c.openImplicits 244 | val ImplicitCandidate(pre, sym, pt, tree) = oi.head 245 | val s = sym.name.toString 246 | q"List($s)" 247 | } 248 | 249 | def convertImpl[T](t: Expr[T]): Tree = ??? 250 | 251 | def implicitProperties: Tree = { 252 | val i = c.openImplicits.head 253 | val pre = i.pre.toString 254 | val sym = i.sym.toString 255 | val pt = i.pt.toString 256 | val tree = i.tree.toString 257 | q"($pre, $sym, $pt, $tree)" 258 | } 259 | 260 | def traitAbstractMethod: Tree = q"()" 261 | 262 | def abstractClassAbstractMethod: Tree = q"()" 263 | 264 | def finalResultType: Tree = { 265 | trait Foo { 266 | def foo: Unit 267 | def bar(): Unit 268 | def baz(i: Int): Unit 269 | def quux(i: Int)(s: String): Unit 270 | } 271 | 272 | def check(nme: String, tpe: Type): Unit = 273 | assert(weakTypeOf[Foo].decl(TermName(nme)).typeSignature.finalResultType =:= tpe) 274 | 275 | check("foo", typeOf[Unit]) 276 | check("bar", typeOf[Unit]) 277 | check("baz", typeOf[Unit]) 278 | check("quux", typeOf[Unit]) 279 | 280 | q"()" 281 | } 282 | 283 | def dealiasTypeArgs: Tree = { 284 | val tpe = weakTypeOf[Either[Int, _]] 285 | val dealiased = tpe.dealias 286 | val tArgs = dealiased.typeArgs 287 | val normalized = appliedType(dealiased.typeConstructor, tArgs) 288 | q"()" 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /core/src/main/scala_2.10/macrocompat/macrocompat.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Miles Sabin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package macrocompat 18 | 19 | import scala.language.experimental.macros 20 | 21 | import scala.reflect.macros.{ Context, TypecheckException } 22 | 23 | // This trait is for backwards binary compatability with macrocompat-1.1.0 ONLY. 24 | // New functionality should be added to CompatContext. 25 | trait MacroCompat { 26 | val c: Context 27 | import c.universe._ 28 | 29 | object GlobalConversions { 30 | val global = c.universe.asInstanceOf[scala.tools.nsc.Global] 31 | 32 | implicit def globalType(tpe: Type): global.Type = tpe.asInstanceOf[global.Type] 33 | implicit def globalSymbol(sym: Symbol): global.Symbol = sym.asInstanceOf[global.Symbol] 34 | implicit def globalTypeSymbol(sym: TypeSymbol): global.TypeSymbol = sym.asInstanceOf[global.TypeSymbol] 35 | implicit def globalTree(tree: Tree): global.Tree = tree.asInstanceOf[global.Tree] 36 | implicit def globalAnnotation(ann: Annotation): global.Annotation = ann.asInstanceOf[global.Annotation] 37 | 38 | implicit def macroType(tpe: global.Type): Type = tpe.asInstanceOf[Type] 39 | implicit def macroSymbol(sym: global.Symbol): Symbol = sym.asInstanceOf[Symbol] 40 | implicit def macroTypeSymbol(sym: global.TypeSymbol): TypeSymbol = sym.asInstanceOf[TypeSymbol] 41 | implicit def macroTree(tree: global.Tree): Tree = tree.asInstanceOf[Tree] 42 | implicit def macroAnnotation(ann: global.Annotation): Annotation = ann.asInstanceOf[Annotation] 43 | } 44 | 45 | import GlobalConversions._ 46 | 47 | def symbolOf[T: WeakTypeTag]: TypeSymbol = 48 | weakTypeOf[T].typeSymbolDirect.asType 49 | 50 | object TypeName { 51 | def apply(s: String) = newTypeName(s) 52 | def unapply(name: TypeName): Option[String] = Some(name.toString) 53 | } 54 | 55 | object TermName { 56 | def apply(s: String) = newTermName(s) 57 | def unapply(name: TermName): Option[String] = Some(name.toString) 58 | } 59 | 60 | object Modifiers extends ModifiersCreator { 61 | def apply(flags: FlagSet, privateWithin: Name = typeNames.EMPTY, annots: List[Tree] = Nil): Modifiers = 62 | c.universe.Modifiers(flags, privateWithin, annots) 63 | 64 | def unapply(mods: Modifiers): Option[(FlagSet, Name, List[Tree])] = 65 | Some((mods.flags, mods.privateWithin, mods.annotations)) 66 | } 67 | 68 | lazy val termNames = nme 69 | lazy val typeNames = tpnme 70 | 71 | def freshName() = c.fresh 72 | def freshName(name: String) = c.fresh(name) 73 | def freshName[NameType <: Name](name: NameType) = c.fresh(name) 74 | 75 | implicit def mkContextOps(c0: c.type): this.type = this 76 | 77 | type TypecheckMode = Int 78 | val TERMmode = global.analyzer.EXPRmode 79 | val TYPEmode = global.analyzer.HKmode 80 | 81 | def typecheck(tree: Tree, mode: TypecheckMode = TERMmode, pt: Type = WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree = { 82 | val universe: global.type = global 83 | val callsiteTyper = c.asInstanceOf[reflect.macros.runtime.Context].callsiteTyper.asInstanceOf[global.analyzer.Typer] 84 | val context = callsiteTyper.context 85 | val withImplicitFlag = if (!withImplicitViewsDisabled) (context.withImplicitsEnabled[Tree] _) else (context.withImplicitsDisabled[Tree] _) 86 | val withMacroFlag = if (!withMacrosDisabled) (context.withMacrosEnabled[Tree] _) else (context.withMacrosDisabled[Tree] _) 87 | def withContext(tree: => Tree) = withImplicitFlag(withMacroFlag(tree)) 88 | def withWrapping(tree: Tree)(op: Tree => Tree) = if (mode == TERMmode) universe.wrappingIntoTerm(tree)(op.asInstanceOf[universe.Tree => universe.Tree]) else op(tree) 89 | def typecheckInternal(tree: Tree) = callsiteTyper.silent(_.typed(universe.duplicateAndKeepPositions(tree), mode, pt), reportAmbiguousErrors = false) 90 | withWrapping(tree)(wrappedTree => withContext(typecheckInternal(wrappedTree) match { 91 | case universe.analyzer.SilentResultValue(result) => 92 | result 93 | case error @ universe.analyzer.SilentTypeError(_) => 94 | if (!silent) throw new TypecheckException(error.err.errPos, error.err.errMsg) 95 | universe.EmptyTree 96 | })).asInstanceOf[Tree] 97 | } 98 | 99 | def untypecheck(tree: Tree): Tree = c.resetLocalAttrs(tree) 100 | 101 | implicit class TypeOps(tpe: Type) { 102 | def typeParams = tpe.typeSymbol.asType.typeParams 103 | 104 | def companion: Type = { 105 | val sym = tpe.typeSymbolDirect 106 | if (sym.isModule && !sym.hasPackageFlag) sym.companionSymbol.tpe 107 | else if (sym.isModuleClass && !sym.isPackageClass) sym.sourceModule.companionSymbol.tpe 108 | else if (sym.isClass && !sym.isModuleClass && !sym.isPackageClass) sym.companionSymbol.info 109 | else NoType 110 | } 111 | 112 | def decl(nme: Name): Symbol = tpe.declaration(nme) 113 | 114 | def decls = tpe.declarations 115 | 116 | def dealias: Type = tpe.normalize 117 | } 118 | 119 | implicit class MethodSymbolOps(sym: MethodSymbol) { 120 | def paramLists = sym.paramss 121 | } 122 | 123 | implicit class SymbolOps(sym: Symbol) { 124 | def companion: Symbol = { 125 | if (sym.isModule && !sym.hasPackageFlag) sym.companionSymbol 126 | else if (sym.isModuleClass && !sym.isPackageClass) sym.sourceModule.companionSymbol 127 | else if (sym.isClass && !sym.isModuleClass && !sym.isPackageClass) sym.companionSymbol 128 | else NoSymbol 129 | } 130 | 131 | def info: Type = sym.typeSignature 132 | def infoIn(site: Type): Type = sym.typeSignatureIn(site) 133 | 134 | def isConstructor: Boolean = sym.isMethod &&sym.asMethod.isConstructor 135 | } 136 | 137 | implicit class AnnotationOps(ann: Annotation) { 138 | // cut-n-pasted (with the comments) from 139 | // https://github.com/scala/scala/blob/v2.11.7/src/reflect/scala/reflect/internal/AnnotationInfos.scala#L348-L382 140 | private def annotationToTree(ann: global.Annotation): Tree = { 141 | import global._, definitions._ 142 | 143 | def reverseEngineerArgs(): List[Tree] = { 144 | def reverseEngineerArg(jarg: ClassfileAnnotArg): Tree = jarg match { 145 | case LiteralAnnotArg(const) => 146 | val tpe = if (const.tag == UnitTag) UnitTpe else ConstantType(const) 147 | Literal(const) setType tpe 148 | case ArrayAnnotArg(jargs) => 149 | val args = jargs map reverseEngineerArg 150 | // TODO: I think it would be a good idea to typecheck Java annotations using a more traditional algorithm 151 | // sure, we can't typecheck them as is using the `new jann(foo = bar)` syntax (because jann is going to be an @interface) 152 | // however we can do better than `typedAnnotation` by desugaring the aforementioned expression to 153 | // something like `new jann() { override def annotatedType() = ...; override def foo = bar }` 154 | // and then using the results of that typecheck to produce a Java-compatible classfile entry 155 | // in that case we're going to have correctly typed Array.apply calls, however that's 2.12 territory 156 | // and for 2.11 exposing an untyped call to ArrayModule should suffice 157 | Apply(Ident(ArrayModule), args.toList) 158 | case NestedAnnotArg(ann: Annotation) => 159 | annotationToTree(ann) 160 | case _ => 161 | EmptyTree 162 | } 163 | def reverseEngineerArgs(jargs: List[(Name, ClassfileAnnotArg)]): List[Tree] = jargs match { 164 | case (name, jarg) :: rest => AssignOrNamedArg(Ident(name), reverseEngineerArg(jarg)) :: reverseEngineerArgs(rest) 165 | case Nil => Nil 166 | } 167 | if (ann.javaArgs.isEmpty) ann.scalaArgs 168 | else reverseEngineerArgs(ann.javaArgs.toList) 169 | } 170 | 171 | // TODO: at the moment, constructor selection is unattributed, because AnnotationInfos lack necessary information 172 | // later on, in 2.12, for every annotation we could save an entire tree instead of just bits and pieces 173 | // but for 2.11 the current situation will have to do 174 | val ctorSelection = Select(New(TypeTree(ann.atp)), nme.CONSTRUCTOR) 175 | Apply(ctorSelection, reverseEngineerArgs()) setType ann.atp 176 | } 177 | 178 | def tree: Tree = annotationToTree(ann) 179 | } 180 | 181 | def appliedType(tc: Type, ts: List[Type]): Type = c.universe.appliedType(tc, ts) 182 | def appliedType(tc: Type, ts: Type*): Type = c.universe.appliedType(tc, ts.toList) 183 | 184 | def showCode(t: Tree): String = show(t) 185 | 186 | object internal { 187 | def constantType(c: Constant): ConstantType = ConstantType(c) 188 | 189 | def enclosingOwner: Symbol = { 190 | val internalContext = c.asInstanceOf[scala.reflect.macros.runtime.Context] 191 | val internalOwner = internalContext.callsiteTyper.context.owner 192 | internalOwner.asInstanceOf[Symbol] 193 | } 194 | 195 | object gen { 196 | def mkAttributedRef(sym: Symbol): Tree = 197 | global.gen.mkAttributedRef(sym) 198 | 199 | def mkAttributedRef(pre: Type, sym: Symbol): Tree = 200 | global.gen.mkAttributedRef(pre, sym) 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /core/src/main/scala_2.10/macrocompat/bundle_2.10.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-6 Miles Sabin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package macrocompat 18 | 19 | import scala.language.experimental.macros 20 | 21 | import scala.annotation.StaticAnnotation 22 | import scala.reflect.macros.Context 23 | 24 | class bundle extends StaticAnnotation { 25 | def macroTransform(annottees: Any*): Any = macro BundleMacro.bundleImpl 26 | } 27 | 28 | class BundleMacro[C <: Context](val c: C) { 29 | import c.universe._ 30 | import Flag._ 31 | 32 | object TreeE { 33 | val TreeNme = newTypeName("Tree") 34 | val CNme = newTermName("c") 35 | def unapply(t: Tree): Option[Tree] = t match { 36 | case Ident(TreeNme) => Some(tq"Any") 37 | case Select(Ident(CNme), TreeNme) => Some(tq"Any") 38 | case x => None 39 | } 40 | } 41 | 42 | object ExprE { 43 | val ExprNme = newTypeName("Expr") 44 | def unapply(t: Tree): Option[Tree] = t match { 45 | case AppliedTypeTree(ExprNme, List(arg)) => Some(arg) 46 | case AppliedTypeTree(Select(_, ExprNme), List(arg)) => Some(arg) 47 | case AppliedTypeTree(Ident(ExprNme), List(arg)) => Some(arg) 48 | case _ => None 49 | } 50 | } 51 | 52 | object WeakTypeTagE { 53 | val WTTNme = newTypeName("WeakTypeTag") 54 | def unapply(t: Tree): Option[Tree] = t match { 55 | case AppliedTypeTree(Ident(WTTNme), List(arg)) => Some(arg) 56 | case AppliedTypeTree(Select(_, WTTNme), List(arg)) => Some(arg) 57 | case _ => None 58 | } 59 | } 60 | 61 | object Repeat { 62 | val Scala = newTermName("scala") 63 | val Repeated = newTypeName("") 64 | 65 | def apply(t: Tree): Tree = 66 | AppliedTypeTree(Select(Select(Ident(nme.ROOTPKG), Scala), Repeated), List(t)) 67 | 68 | def unapply(t: Tree): Option[Tree] = t match { 69 | case AppliedTypeTree(Select(Select(Ident(nme.ROOTPKG), Scala), Repeated), List(tpt)) => Some(tpt) 70 | case _ => None 71 | } 72 | } 73 | 74 | def mkForwarder(d: DefDef, macroClassNme: TypeName): DefDef = { 75 | val DefDef(mods, name, tparams, vparamss, tpt, rhs) = d 76 | val ctxNme = newTermName(c.fresh) 77 | val ctxParam = q""" val $ctxNme: _root_.scala.reflect.macros.Context """ 78 | 79 | val targs = tparams.map(_.name) 80 | 81 | val cvparamss = vparamss.map(_.map { param => 82 | val ValDef(mods, nme, tpt, rhs) = param 83 | val ctpt = tpt match { 84 | case TreeE(t) => tq""" $ctxNme.Expr[$t] """ 85 | case ExprE(t) => tq""" $ctxNme.Expr[$t] """ 86 | case Repeat(TreeE(t)) => Repeat(tq""" $ctxNme.Expr[$t] """) 87 | case Repeat(ExprE(t)) => Repeat(tq""" $ctxNme.Expr[$t] """) 88 | case WeakTypeTagE(t) => tq""" $ctxNme.WeakTypeTag[$t] """ 89 | } 90 | ValDef(mods, nme, ctpt, rhs) 91 | }) 92 | val mi = newTermName(c.fresh("inst")) 93 | val cargss = vparamss.map(_.map { param => 94 | val ValDef(mods, nme, tpt, rhs) = param 95 | tpt match { 96 | case TreeE(_) => q""" $nme.tree.asInstanceOf[$mi.c.universe.Tree] """ 97 | case ExprE(t) => q""" $nme.asInstanceOf[$mi.c.Expr[$t]] """ 98 | case Repeat(TreeE(_)) => q""" $nme.map(_.tree.asInstanceOf[$mi.c.universe.Tree]): _* """ 99 | case Repeat(ExprE(t)) => q""" $nme.map(_.asInstanceOf[$mi.c.Expr[$t]]): _* """ 100 | case WeakTypeTagE(t) => q""" $nme.asInstanceOf[$mi.c.universe.WeakTypeTag[$t]] """ 101 | } 102 | }) 103 | 104 | val call = 105 | q""" 106 | val $mi = 107 | new $macroClassNme( 108 | new _root_.macrocompat.RuntimeCompatContext( 109 | $ctxNme.asInstanceOf[_root_.scala.reflect.macros.runtime.Context] 110 | ) 111 | ) 112 | $mi.${name.toTermName}[..$targs](...$cargss) 113 | """ 114 | 115 | val (ctpt, crhs) = 116 | tpt match { 117 | case ExprE(tpt) => ( 118 | tq""" $ctxNme.Expr[$tpt] """, 119 | q""" $ctxNme.Expr[$tpt](_root_.macrocompat.BundleMacro.fixPositions[$ctxNme.type]($ctxNme)($call.tree.asInstanceOf[$ctxNme.universe.Tree])) """ 120 | ) 121 | case TreeE(tpt) => ( 122 | tq""" $ctxNme.Expr[Nothing] """, 123 | q""" $ctxNme.Expr[Nothing](_root_.macrocompat.BundleMacro.fixPositions[$ctxNme.type]($ctxNme)($call.asInstanceOf[$ctxNme.universe.Tree])) """ 124 | ) 125 | } 126 | 127 | DefDef(mods, name, tparams, List(ctxParam) :: cvparamss, ctpt, crhs) 128 | } 129 | 130 | object MacroImpl { 131 | def unapply(d: DefDef): Option[DefDef] = { 132 | val DefDef(mods, name, tparams, vparamss, tpt, rhs) = d 133 | 134 | tpt match { 135 | case TreeE(_)|ExprE(_) 136 | if vparamss.forall(_.forall { 137 | case ValDef(mods, _, _, _) if mods hasFlag IMPLICIT => true 138 | case ValDef(_, _, TreeE(_)|ExprE(_)|Repeat(TreeE(_))|Repeat(ExprE(_))|WeakTypeTagE(_), _) => true 139 | case _ => false 140 | }) => Some(d) 141 | case _ => None 142 | } 143 | } 144 | } 145 | 146 | def stripPositions(tree: Tree): Tree = { 147 | if(!tree.isEmpty) { 148 | tree.setPos(NoPosition) 149 | tree.children.foreach(stripPositions) 150 | } 151 | tree 152 | } 153 | 154 | def fixPositions(tree: Tree): Tree = { 155 | val global = c.universe.asInstanceOf[scala.tools.nsc.Global] 156 | if(global.settings.Yrangepos.value) 157 | stripPositions(tree) 158 | else 159 | tree 160 | } 161 | 162 | def bundleImpl(annottees: Tree*): Tree = { 163 | annottees match { 164 | case List(clsDef: ClassDef) => mkMacroClsAndObjTree(clsDef, None) 165 | 166 | case List(clsDef: ClassDef, objDef: ModuleDef) => mkMacroClsAndObjTree(clsDef, Some(objDef)) 167 | 168 | case other => 169 | c.abort(c.enclosingPosition, "Unexpected tree shape.") 170 | } 171 | } 172 | 173 | def mkMacroClsAndObjTree(clsDef: ClassDef, objDef: Option[ModuleDef]) = { 174 | val ClassDef(mods, macroClassNme, tparams, Template(parents, self, body)) = clsDef 175 | 176 | // The private forwarding defintions (appliedType, Modifiers) below are needed because they need 177 | // to appear to be accessible as a result of import c.universe._. They can't be added to 178 | // c.compatUniverse because that results in import ambiguities. Note that the definitions are 179 | // private to avoid override conflicts in stacks of inherited bundles. 180 | val Block(stats, _) = 181 | q""" 182 | import c.compatUniverse._ 183 | import _root_.macrocompat.TypecheckerContextExtensions._ 184 | 185 | private def appliedType(tc: c.universe.Type, ts: _root_.scala.collection.immutable.List[c.universe.Type]): c.universe.Type = c.universe.appliedType(tc, ts) 186 | 187 | private def appliedType(tc: c.universe.Type, ts: c.universe.Type*): c.universe.Type = c.universe.appliedType(tc, ts.toList) 188 | 189 | private val Annotation = c.compatUniverse.CompatAnnotation 190 | 191 | private val Modifiers = c.compatUniverse.CompatModifiers 192 | """ 193 | 194 | // For now all macro bundles must have a Context constructor argument named "c". See, 195 | // https://gitter.im/scala/scala?at=55ef0ffe24362d5253fe3a51 196 | val ctxNme = newTermName("c") 197 | val mixinCtorNme = newTermName("$init$") 198 | 199 | val (prefix0, ctor :: suffix0) = body.span { 200 | case d: DefDef if d.name == nme.CONSTRUCTOR || d.name == mixinCtorNme => false 201 | case _ => true 202 | } 203 | 204 | val (prefix1, suffix1) = suffix0.span { 205 | case ValDef(_, nme, _, _) if nme == ctxNme => false 206 | case _ => true 207 | } 208 | 209 | val suffix2 = 210 | suffix1 match { 211 | case decl :: rest => prefix1 ++ (decl :: stats) ++ rest 212 | case _ => stats ++ prefix1 ++ suffix1 213 | } 214 | 215 | val newBody = prefix0 ++ List(ctor) ++ suffix2 216 | 217 | val macroClass = ClassDef(mods, macroClassNme, tparams, Template(parents, self, newBody)) 218 | 219 | val res = 220 | if(mods.hasFlag(ABSTRACT)) 221 | objDef match { 222 | case Some(obj) => 223 | q""" 224 | $macroClass 225 | 226 | $obj 227 | """ 228 | case _ => 229 | macroClass 230 | } 231 | else { 232 | val (objEarlydefns, objParents, objBody) = objDef match { 233 | case Some(q"$objMods object $objTname extends { ..$objEarlydefns } with ..$objParents { $objSelf => ..$objBody }") => (objEarlydefns, objParents, objBody) 234 | case None => (Nil, List(tq"_root_.scala.AnyRef"), Nil) 235 | } 236 | 237 | val defns = body collect { 238 | case MacroImpl(d: DefDef) => d 239 | } 240 | 241 | val forwarders = defns.map { d => mkForwarder(d, macroClassNme) } 242 | val macroObjectNme = macroClassNme.toTermName 243 | 244 | q""" 245 | $macroClass 246 | 247 | object $macroObjectNme extends { ..$objEarlydefns } with ..$objParents { 248 | ..$forwarders 249 | 250 | ..$objBody 251 | } 252 | """ 253 | } 254 | 255 | fixPositions(res) 256 | } 257 | } 258 | 259 | object BundleMacro { 260 | def inst[C <: Context](c: C) = new BundleMacro[c.type](c) 261 | 262 | def bundleImpl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = 263 | c.Expr[Any](inst(c).bundleImpl(annottees.map(_.tree): _*)) 264 | 265 | def fixPositions[C <: Context](c: C)(tree: c.Tree): c.Tree = 266 | inst(c).fixPositions(tree) 267 | } 268 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # macro-compat: cross version Scala macro support 2 | 3 | **macro-compat** is a small library which, in conjunction with the [macro-paradise][macro-paradise] compiler plugin, 4 | allows you to compile macros with Scala 2.10.x which are written to the Scala 2.11/12/13 macro API. This means that 5 | your macros can be written just once, for the current API, and still be portable to earlier Scala releases. 6 | 7 | [![Build Status](https://api.travis-ci.org/milessabin/macro-compat.png?branch=master)](https://travis-ci.org/milessabin/macro-compat) 8 | [![Stories in Ready](https://badge.waffle.io/milessabin/macro-compat.png?label=Ready)](https://waffle.io/milessabin/macro-compat) 9 | [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/milessabin/macro-compat) 10 | [![Maven Central](https://img.shields.io/maven-central/v/org.typelevel/macro-compat_2.12.svg)](https://maven-badges.herokuapp.com/maven-central/org.typelevel/macro-compat_2.12) 11 | [![Scala.js](https://www.scala-js.org/assets/badges/scalajs-0.6.8.svg)](https://www.scala-js.org) 12 | 13 | ## Why you should use macro-compat 14 | 15 | Scala macros are hard enough to write once ... writing them twice, once for each of the two API versions is even more 16 | of a chore. 17 | 18 | Currently people adopt one of the following approaches if they want portable macros, 19 | 20 | + They maintain separate git branches for Scala 2.11.x vs. 2.10.x variants. 21 | + They use SBT's [cross-version support for Scala sources][sbt-cross]. 22 | + They write an abstraction layer over the macro API to hide the differences. 23 | 24 | None of these is entirely satisfactory. 25 | 26 | The branching model has worked fairly well for [shapeless][shapeless] but has become more cumbersome with the arrival 27 | of Scala.JS ... a single branch build has become increasingly desirable. 28 | 29 | Using SBT's cross version source support is effectively maintaining an "internal branch" within a single real branch 30 | of your project, but without any of the tools which support managing branches effectively. Whilst this might be 31 | adequate for very small amounts of macro code it gets increasingly awkward and error prone as the amount of macro code 32 | grows. 33 | 34 | Writing an abstraction layer that hides the differences between the macro API versions (insofar as it does so by 35 | bringing Scala 2.10.x up to the 2.11.x API) is a _part_ of the solution proposed here. However it isn't enough. Two of 36 | the biggest improvements of the Scala 2.11.x macro API over 2.10.x were the introduction of 37 | [macro-bundles][macro-bundles] and the ability to type macro implementation method arguments and results as `Tree` 38 | rather than `Expr[T]`. Both of these allow the signatures of macro implementation methods to be written significantly 39 | more succintly and readably, and it would be a shame to have to give up on them just to remain compatible with Scala 40 | 2.10.x within a single branch. 41 | 42 | **macro-compat** provides a backport of (parts of) the Scala 2.11.x macro API to 2.10.x and also provides an 43 | annotation macro which provides support for macro bundles in 2.10.x and `Tree` as the type of macro implementation 44 | method arguments and results. The intention is that you write macro code as macro bundles, exactly as you would for 45 | Scala 2.11.x with the exception of a single `@bundle` annotation on the macro bundle class, 46 | 47 | ```scala 48 | import scala.language.experimental.macros 49 | import scala.reflect.macros.whitebox 50 | 51 | import macrocompat.bundle 52 | 53 | object Test { 54 | def foo: Int = macro TestMacro.fooImpl 55 | def bar(i: Int): String = macro TestMacro.barImpl 56 | def baz(is: Int*): Int = macro TestMacro.bazImpl 57 | } 58 | 59 | @bundle // macro-compat addition 60 | class TestMacro(val c: whitebox.Context) { 61 | import c.universe._ 62 | 63 | def fooImpl: Tree = q""" 23 """ // explicit return type : Tree required 64 | 65 | def barImpl(i: Tree): Tree = q""" "bar" """ 66 | 67 | def bazImpl(is: Tree*): Tree = q""" 13 """ 68 | } 69 | ``` 70 | 71 | This code compiles on Scala 2.10.x, 2.11.x, 2.12.x and 2.13.x. 72 | 73 | The `@bundle` annotation is implemented as a macro annotation via the [macro-paradise][macro-paradise] compiler plugin 74 | (the plugin is no longer necessary with 2.13.x). On Scala 2.11.x, 2.12.x and 2.13.x the annotation is simply 75 | eliminated during compilation, leaving no trace in the resulting binaries. On Scala 2.10.x the annotation macro 76 | transforms the macro bundle class to an object definition which is compatible with the 2.10.x macro API. 77 | 78 | ## Current status 79 | 80 | This is a young project, initially extracted out of the [export-hook][export-hook] project and massaged into a more or 81 | less usable form in free moments snatched during ICFP 2015. Since then a number of generous contributors have made 82 | additions to the backport component and it is now seeing use in several other projects. As of version 1.1.1 backport 83 | coverage has been expanded sufficiently to cover all the macro API usage in shapeless. I would be delighted for more 84 | projects to pick it up and extend it to cover their needs as well. 85 | 86 | If you would like to see or contribute particular extensions to the backport, please create issues here or hop on the 87 | [gitter channel][macrocompat-gitter]. Discussion is also welcome on the [shapeless][shapeless-gitter] and 88 | [cats][cats-gitter] gitter channels ... please let us know what you think. 89 | 90 | ## Using macro-compat 91 | 92 | Binary release artefacts are published to the [Sonatype OSS Repository Hosting service][sonatype] and synced to Maven 93 | Central. Snapshots of the master branch are built using [Travis CI][ci] and automatically published to the Sonatype 94 | OSS Snapshot repository. To include the Sonatype repositories in your SBT build you should add, 95 | 96 | ```scala 97 | resolvers ++= Seq( 98 | Resolver.sonatypeRepo("releases"), 99 | Resolver.sonatypeRepo("snapshots") 100 | ) 101 | ``` 102 | 103 | Builds are available for Scala 2.10.x, 2.11.x, 2.12.x and 2.13.x for Scala JDK and Scala.js. 104 | 105 | For Scala 2.12.x and earlier, add the following to your build, 106 | 107 | ```scala 108 | libraryDependencies ++= Seq( 109 | "org.typelevel" %% "macro-compat" % "1.1.1", 110 | "org.scala-lang" % "scala-compiler" % scalaVersion.value % "provided", 111 | compilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.patch) 112 | ) 113 | ``` 114 | 115 | For Scala 2.13.0-RC2 and later, add the following, 116 | 117 | ```scala 118 | libraryDependencies ++= Seq( 119 | "org.typelevel" %% "macro-compat" % "1.1.1", 120 | "org.scala-lang" % "scala-compiler" % scalaVersion.value % "provided" 121 | ) 122 | ``` 123 | 124 | ## Binary compatibility 125 | 126 | As of version 1.0.7 macro-compat uses [MiMa][mima] to verify binary compatibility within minor versions. Binary 127 | compatibility was broken in 1.0.3 and again in 1.0.6. In version 1.0.7 binary compatibility with 1.0.3-5 has been 128 | restored and 1.0.6 is now deprecated. The binary compatibility breaking changes were moved to 1.1.0 and hopefully 129 | the addition of MiMa to the build will make a recurrence of this sort of breakage much less likely in future. 130 | 131 | ## Building macro-compat 132 | 133 | macro-compat is built with SBT 0.13.16 or later. 134 | 135 | ## Participation 136 | 137 | The macro-compat project supports the [Scala Code of Conduct][codeofconduct] and wants all of its 138 | channels (Gitter, github, etc.) to be welcoming environments for everyone. 139 | 140 | [codeofconduct]: https://www.scala-lang.org/conduct/ 141 | 142 | ## Projects using macro-compat 143 | 144 | + [catalysts][catalysts] 145 | + [export-hook][export-hook] 146 | + [expressier][expressier] 147 | + [latr][latr] 148 | + [marley][marley] 149 | + [Monix][monix] 150 | + [Monocle][monocle] 151 | + [refined][refined] 152 | + [shapeless][shapeless] 153 | + [simulacrum][simulacrum] 154 | + [tfm][tfm] 155 | 156 | ## Contributors 157 | 158 | + Adelbert Chang [@adelbertchang](https://twitter.com/adelbertchang) 159 | + Alexandre Archambault [@alxarchambault](https://twitter.com/alxarchambault) 160 | + Alistair Johnson [@AlistairUSM](https://twitter.com/AlistairUSM) 161 | + Chris Hodapp [@clhodapp](https://twitter.com/clhodapp) 162 | + Dale Wijnand [@dwijnand](https://twitter.com/dwijnand) 163 | + Frank S. Thomas [@fst9000](https://twitter.com/fst9000) 164 | + Michael Pilquist [@mpilquist](https://twitter.com/mpilquist) 165 | + Miles Sabin [@milessabin](https://twitter.com/milessabin) 166 | + Naoki Aoyama [@AoiroAoino](https://twitter.com/AoiroAoino) 167 | + Philip Wills [@philwills](https://twitter.com/philwills) 168 | + Travis Brown [@travisbrown](https://twitter.com/travisbrown) 169 | + Your name here :-) 170 | 171 | [macro-paradise]: http://docs.scala-lang.org/overviews/macros/paradise.html 172 | [sbt-cross]: http://www.scala-sbt.org/0.13/docs/sbt-0.13-Tech-Previews.html#Cross-version+support+for+Scala+sources 173 | [shapeless]: https://github.com/milessabin/shapeless 174 | [macro-bundles]: http://docs.scala-lang.org/overviews/macros/bundles.html 175 | [export-hook]: https://github.com/milessabin/export-hook 176 | [expressier]: https://github.com/travisbrown/expressier 177 | [simulacrum]: https://github.com/mpilquist/simulacrum 178 | [tfm]: https://github.com/adelbertc/tfm 179 | [marley]: https://github.com/guardian/marley 180 | [monix]: https://monix.io 181 | [monocle]: https://github.com/julien-truffaut/Monocle 182 | [shapeless-gitter]: https://gitter.im/milessabin/shapeless 183 | [cats-gitter]: https://gitter.im/non/cats 184 | [macrocompat-gitter]: https://gitter.im/milessabin/macro-compat 185 | [typelevel]: http://typelevel.org/ 186 | [codeofconduct]: http://typelevel.org/conduct.html 187 | [catalysts]: https://github.com/typelevel/catalysts 188 | [sonatype]: https://oss.sonatype.org/index.html#nexus-search;quick~macro-compat 189 | [ci]: https://travis-ci.org/milessabin/macro-compat 190 | [mima]: https://github.com/typesafehub/migration-manager 191 | [refined]: https://github.com/fthomas/refined 192 | [latr]: https://github.com/runarorama/latr 193 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /core/src/main/scala_2.10/macrocompat/compatcontext.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015-6 Miles Sabin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package macrocompat 18 | 19 | import scala.language.experimental.macros 20 | import scala.language.postfixOps 21 | 22 | import scala.annotation.tailrec 23 | import scala.collection.immutable.ListMap 24 | import scala.reflect.internal.Flags._ 25 | import scala.reflect.macros.runtime.{ Context => RuntimeContext } 26 | import scala.reflect.macros.{ Attachments, Context, TypecheckException } 27 | import scala.reflect.{ ClassTag, classTag } 28 | 29 | // Interface only. Implementation should be in RuntimeCompatContext 30 | sealed trait CompatContext extends Context { 31 | val c: Context 32 | override lazy val universe: c.universe.type = c.universe 33 | 34 | import universe._ 35 | 36 | def freshName(): String 37 | def freshName(name: String): String 38 | def freshName[NameType <: Name](name: NameType): NameType 39 | 40 | case class ImplicitCandidate211(pre: Type, sym: Symbol, pt: Type, tree: Tree) 41 | val ImplicitCandidate: ImplicitCandidateCompanion 42 | abstract class ImplicitCandidateCompanion { 43 | def apply(pre: Type, sym: Symbol, pt: Type, tree: Tree): Unit 44 | def unapply(t: (Type, Tree)): Option[(Type, Symbol, Type, Tree)] 45 | } 46 | 47 | type TypecheckMode 48 | val TERMmode: TypecheckMode 49 | val TYPEmode: TypecheckMode 50 | 51 | def typecheck(tree: Tree, mode: TypecheckMode = TERMmode, pt: Type = WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree 52 | 53 | def untypecheck(tree: Tree): Tree 54 | 55 | val internal: Internal 56 | abstract class Internal { 57 | def constantType(c: Constant): ConstantType 58 | 59 | def polyType(tparams: List[Symbol], tpe: Type): Type 60 | 61 | def enclosingOwner: Symbol 62 | 63 | val gen: Gen 64 | abstract class Gen { 65 | def mkAttributedRef(sym: Symbol): Tree 66 | 67 | def mkAttributedRef(pre: Type, sym: Symbol): Tree 68 | } 69 | 70 | object decorators 71 | 72 | def thisType(sym: Symbol): Type 73 | 74 | def singleType(pre: Type, sym: Symbol): Type 75 | 76 | def typeRef(pre: Type, sym: Symbol, args: List[Type]): Type 77 | 78 | def setInfo(sym: Symbol, tpe: Type): Symbol 79 | 80 | def newTermSymbol(owner: Symbol, name: TermName, pos: Position = NoPosition, flags: FlagSet = NoFlags): TermSymbol 81 | 82 | def substituteSymbols(tree: Tree, from: List[Symbol], to: List[Symbol]): Tree 83 | 84 | def typeBounds(lo: Type, hi: Type): TypeBounds 85 | } 86 | 87 | val compatUniverse: CompatUniverse 88 | abstract class CompatUniverse { 89 | val internal: Internal 90 | 91 | val TypeName: TypeNameCompanion 92 | abstract class TypeNameCompanion { 93 | def apply(s: String): TypeName 94 | def unapply(name: TypeName): Option[String] 95 | } 96 | 97 | val TermName: TermNameCompanion 98 | abstract class TermNameCompanion { 99 | def apply(s: String): TermName 100 | def unapply(name: TermName): Option[String] 101 | } 102 | 103 | def symbolOf[T: WeakTypeTag]: TypeSymbol 104 | 105 | val termNames: TermNamesApi 106 | val typeNames: TypeNamesApi 107 | 108 | implicit def TypeOps(tpe: Type): TypeOps 109 | abstract class TypeOps { 110 | def typeParams: List[Symbol] 111 | def typeArgs: List[Type] 112 | def companion: Type 113 | def decl(nme: Name): Symbol 114 | def decls: MemberScope 115 | def dealias: Type 116 | def finalResultType: Type 117 | def paramLists: List[List[Symbol]] 118 | } 119 | 120 | implicit def MethodSymbolOps(sym: MethodSymbol): MethodSymbolOps 121 | abstract class MethodSymbolOps { 122 | def paramLists: List[List[Symbol]] 123 | } 124 | 125 | implicit def SymbolOps(sym: Symbol): SymbolOps 126 | abstract class SymbolOps { 127 | def companion: Symbol 128 | def info: Type 129 | def infoIn(site: Type): Type 130 | def isConstructor: Boolean 131 | def isAbstract: Boolean 132 | def overrides: List[Symbol] 133 | 134 | def isPrivateThis: Boolean 135 | def isProtectedThis: Boolean 136 | } 137 | 138 | implicit def TreeOps(tree: Tree): TreeOps 139 | abstract class TreeOps { 140 | def nonEmpty: Boolean 141 | } 142 | 143 | val CompatAnnotation: AnnotationCompanion 144 | abstract class AnnotationCompanion { 145 | def apply(tree: Tree): Annotation 146 | 147 | def apply(tpe: Type, scalaArgs: List[Tree], javaArgs: ListMap[Name, JavaArgument]): Annotation 148 | def unapply(ann: Annotation): Option[(Type, List[Tree], ListMap[Name, JavaArgument])] 149 | } 150 | 151 | implicit def AnnotationOps(ann: Annotation): AnnotationOps 152 | abstract class AnnotationOps { 153 | def tree: Tree 154 | } 155 | 156 | def showCode(t: Tree): String 157 | 158 | val CompatModifiers: CompatModifiers 159 | abstract class CompatModifiers extends ModifiersCreator { 160 | def apply(flags: FlagSet, privateWithin: Name = typeNames.EMPTY, annots: List[Tree] = Nil): Modifiers 161 | def unapply(mods: Modifiers): Option[(FlagSet, Name, List[Tree])] 162 | } 163 | 164 | implicit def tupleToImplicitCandidate(t: (Type, Tree)): ImplicitCandidate211 165 | 166 | implicit def AttachmentsOps(as: Attachments): AttachmentsOps 167 | abstract class AttachmentsOps { 168 | def contains[T: ClassTag]: Boolean 169 | def isEmpty: Boolean 170 | } 171 | 172 | def noSelfType: ValDef 173 | } 174 | } 175 | 176 | class RuntimeCompatContext(val c: RuntimeContext) extends RuntimeContext with CompatContext { outer => 177 | override lazy val universe: c.universe.type = c.universe 178 | 179 | import universe._ 180 | 181 | lazy val callsiteTyper = c.callsiteTyper.asInstanceOf[analyzer.Typer] 182 | lazy val prefix: Expr[PrefixType] = c.prefix.asInstanceOf[Expr[PrefixType]] 183 | lazy val expandee: Tree = c.expandee.asInstanceOf[Tree] 184 | 185 | def freshName() = c.fresh 186 | def freshName(name: String) = c.fresh(name) 187 | def freshName[NameType <: Name](name: NameType) = c.fresh(name) 188 | 189 | 190 | object ImplicitCandidate extends ImplicitCandidateCompanion { 191 | def apply(pre: Type, sym: Symbol, pt: Type, tree: Tree) = ImplicitCandidate211(pre, sym, pt, tree) 192 | def unapply(t: (Type, Tree)): Option[(Type, Symbol, Type, Tree)] = tryUnapply(t).right.toOption 193 | 194 | def tryUnapply(t: (Type, Tree)): Either[String, (Type, Symbol, Type, Tree)] = { 195 | val (pt, tree) = t 196 | callsiteTyper.context.openImplicits.filter(oi => oi.pt == pt && oi.tree == tree) match { 197 | case List(oi) => Right((oi.info.pre, oi.info.sym, oi.pt, oi.tree)) 198 | case Nil => Left(s"Failed to identify ImplicitCandidate for $t, none match") 199 | case xs => Left(s"Failed to identify ImplicitCandidate for $t, ${xs.size} match") 200 | } 201 | } 202 | } 203 | 204 | type TypecheckMode = Int 205 | val TERMmode = analyzer.EXPRmode 206 | val TYPEmode = analyzer.HKmode 207 | 208 | def typecheck(tree: Tree, mode: TypecheckMode = TERMmode, pt: Type = WildcardType, silent: Boolean = false, withImplicitViewsDisabled: Boolean = false, withMacrosDisabled: Boolean = false): Tree = { 209 | val context = callsiteTyper.context 210 | val withImplicitFlag = if (!withImplicitViewsDisabled) (context.withImplicitsEnabled[Tree] _) else (context.withImplicitsDisabled[Tree] _) 211 | val withMacroFlag = if (!withMacrosDisabled) (context.withMacrosEnabled[Tree] _) else (context.withMacrosDisabled[Tree] _) 212 | def withContext(tree: => Tree) = withImplicitFlag(withMacroFlag(tree)) 213 | def withWrapping(tree: Tree)(op: Tree => Tree) = if (mode == TERMmode) wrappingIntoTerm(tree)(op) else op(tree) 214 | def typecheckInternal(tree: Tree): analyzer.SilentResult[Tree] = 215 | callsiteTyper.silent(_.typed(duplicateAndKeepPositions(tree), mode, pt), reportAmbiguousErrors = false) 216 | withWrapping(tree)(wrappedTree => withContext(typecheckInternal(wrappedTree) match { 217 | case analyzer.SilentResultValue(result) => 218 | result 219 | case error @ analyzer.SilentTypeError(_) => 220 | if (!silent) throw new TypecheckException(error.err.errPos, error.err.errMsg) 221 | EmptyTree 222 | })) 223 | } 224 | 225 | def untypecheck(tree: Tree): Tree = resetLocalAttrs(tree) 226 | 227 | object internal extends Internal { 228 | def constantType(c: Constant): ConstantType = ConstantType(c) 229 | 230 | def polyType(tparams: List[Symbol], tpe: Type): Type = universe.genPolyType(tparams, tpe) 231 | 232 | def enclosingOwner: Symbol = callsiteTyper.context.owner 233 | 234 | object gen extends Gen { 235 | def mkAttributedRef(sym: Symbol): Tree = universe.gen.mkAttributedRef(sym) 236 | 237 | def mkAttributedRef(pre: Type, sym: Symbol): Tree = universe.gen.mkAttributedRef(pre, sym) 238 | } 239 | 240 | def thisType(sym: Symbol): Type = ThisType(sym) 241 | 242 | def singleType(pre: Type, sym: Symbol): Type = SingleType(pre, sym) 243 | 244 | def typeRef(pre: Type, sym: Symbol, args: List[Type]): Type = universe.typeRef(pre, sym, args) 245 | 246 | def setInfo(sym: Symbol, tpe: Type): Symbol = sym.setTypeSignature(tpe) 247 | 248 | def newTermSymbol(owner: Symbol, name: TermName, pos: Position = NoPosition, flags: FlagSet = NoFlags): TermSymbol = 249 | owner.newTermSymbol(name, pos, flags) 250 | 251 | def substituteSymbols(tree: Tree, from: List[Symbol], to: List[Symbol]): Tree = 252 | tree.substituteSymbols(from, to) 253 | 254 | def typeBounds(lo: Type, hi: Type): TypeBounds = TypeBounds(lo, hi) 255 | } 256 | 257 | object compatUniverse extends CompatUniverse { 258 | val internal = outer.internal 259 | 260 | object TypeName extends TypeNameCompanion { 261 | def apply(s: String) = newTypeName(s) 262 | def unapply(name: TypeName): Option[String] = Some(name.toString) 263 | } 264 | 265 | object TermName extends TermNameCompanion { 266 | def apply(s: String) = newTermName(s) 267 | def unapply(name: TermName): Option[String] = Some(name.toString) 268 | } 269 | 270 | def symbolOf[T: WeakTypeTag]: TypeSymbol = 271 | weakTypeOf[T].typeSymbolDirect.asType 272 | 273 | lazy val termNames = nme 274 | lazy val typeNames = tpnme 275 | 276 | implicit def TypeOps(tpe: Type): TypeOps = 277 | new TypeOps { 278 | def typeParams = tpe.typeParams 279 | 280 | def typeArgs: List[Type] = { 281 | import scala.language.reflectiveCalls 282 | def loop(tpe: Type): List[Type] = tpe match { 283 | case TypeRef(_, _, args) => args 284 | case et @ ExistentialType(_, underlying) => 285 | loop(underlying).map(arg => et.asInstanceOf[{ def maybeRewrap(tpe: Type): Type }].maybeRewrap(arg)) 286 | case _ => Nil 287 | } 288 | loop(tpe) 289 | } 290 | 291 | def companion: Type = { 292 | val sym = tpe.typeSymbolDirect 293 | if (sym.isModule && !sym.hasPackageFlag) sym.companionSymbol.tpe 294 | else if (sym.isModuleClass && !sym.isPackageClass) sym.sourceModule.companionSymbol.tpe 295 | else if (sym.isClass && !sym.isModuleClass && !sym.isPackageClass) sym.companionSymbol.info 296 | else NoType 297 | } 298 | 299 | def decl(nme: Name): Symbol = tpe.declaration(nme) 300 | 301 | def decls = tpe.declarations 302 | 303 | def dealias: Type = { 304 | @tailrec 305 | def loop(tpe: Type): Type = tpe match { 306 | case tr @ TypeRef(pre, sym, args) if sym.isAliasType => 307 | if(tr.typeParamsMatchArgs) loop(tr.betaReduce) else tpe 308 | case _ => tpe 309 | } 310 | loop(tpe) 311 | } 312 | 313 | def finalResultType: Type = { 314 | @tailrec 315 | def loop(tp: Type): Type = tp match { 316 | case PolyType(_, restpe) => loop(restpe) 317 | case MethodType(_, restpe) => loop(restpe) 318 | case NullaryMethodType(restpe) => loop(restpe) 319 | case _ => tp 320 | } 321 | loop(tpe) 322 | } 323 | 324 | def paramLists: List[List[Symbol]] = tpe.paramss map (_ map (x => x: Symbol)) 325 | } 326 | 327 | implicit def MethodSymbolOps(sym: MethodSymbol): MethodSymbolOps = 328 | new MethodSymbolOps { 329 | def paramLists = sym.paramss 330 | } 331 | 332 | implicit def SymbolOps(sym: Symbol): SymbolOps = 333 | new SymbolOps { 334 | def companion: Symbol = { 335 | if (sym.isModule && !sym.hasPackageFlag) sym.companionSymbol 336 | else if (sym.isModuleClass && !sym.isPackageClass) sym.sourceModule.companionSymbol 337 | else if (sym.isClass && !sym.isModuleClass && !sym.isPackageClass) sym.companionSymbol 338 | else NoSymbol 339 | } 340 | 341 | def info: Type = sym.typeSignature 342 | def infoIn(site: Type): Type = sym.typeSignatureIn(site) 343 | 344 | def isConstructor: Boolean = sym.isMethod &&sym.asMethod.isConstructor 345 | 346 | def isAbstract: Boolean = sym.isAbstractClass || sym.isDeferred || sym.isAbstractType 347 | 348 | def overrides: List[Symbol] = sym.allOverriddenSymbols 349 | 350 | def isPrivateThis: Boolean = (sym hasFlag PRIVATE) && (sym hasFlag LOCAL) 351 | 352 | def isProtectedThis: Boolean = (sym hasFlag PROTECTED) && (sym hasFlag LOCAL) 353 | } 354 | 355 | implicit def TreeOps(tree: Tree): TreeOps = 356 | new TreeOps { 357 | def nonEmpty = !tree.isEmpty 358 | } 359 | 360 | object CompatAnnotation extends AnnotationCompanion { 361 | import definitions._ 362 | 363 | def apply(tree: Tree): Annotation = treeToAnnotation(tree) 364 | 365 | def apply(tpe: Type, scalaArgs: List[Tree], javaArgs: ListMap[Name, JavaArgument]): Annotation = 366 | universe.Annotation(tpe, scalaArgs, javaArgs) 367 | 368 | def unapply(ann: Annotation): Option[(Type, List[Tree], ListMap[Name, JavaArgument])] = 369 | universe.Annotation.unapply(ann) 370 | 371 | // cut-n-pasted (with the comments) from 372 | // https://github.com/scala/scala/blob/v2.11.7/src/reflect/scala/reflect/internal/AnnotationInfos.scala#L348-L382 373 | def annotationToTree(ann: Annotation): Tree = { 374 | def reverseEngineerArgs(): List[Tree] = { 375 | def reverseEngineerArg(jarg: ClassfileAnnotArg): Tree = jarg match { 376 | case LiteralAnnotArg(const) => 377 | val tpe = if (const.tag == UnitTag) UnitTpe else ConstantType(const) 378 | Literal(const) setType tpe 379 | case ArrayAnnotArg(jargs) => 380 | val args = jargs map reverseEngineerArg 381 | // TODO: I think it would be a good idea to typecheck Java annotations using a more traditional algorithm 382 | // sure, we can't typecheck them as is using the `new jann(foo = bar)` syntax (because jann is going to be an @interface) 383 | // however we can do better than `typedAnnotation` by desugaring the aforementioned expression to 384 | // something like `new jann() { override def annotatedType() = ...; override def foo = bar }` 385 | // and then using the results of that typecheck to produce a Java-compatible classfile entry 386 | // in that case we're going to have correctly typed Array.apply calls, however that's 2.12 territory 387 | // and for 2.11 exposing an untyped call to ArrayModule should suffice 388 | Apply(Ident(ArrayModule), args.toList) 389 | case NestedAnnotArg(ann: Annotation) => 390 | annotationToTree(ann) 391 | case _ => 392 | EmptyTree 393 | } 394 | def reverseEngineerArgs(jargs: List[(Name, ClassfileAnnotArg)]): List[Tree] = jargs match { 395 | case (name, jarg) :: rest => AssignOrNamedArg(Ident(name), reverseEngineerArg(jarg)) :: reverseEngineerArgs(rest) 396 | case Nil => Nil 397 | } 398 | if (ann.javaArgs.isEmpty) ann.scalaArgs 399 | else reverseEngineerArgs(ann.javaArgs.toList) 400 | } 401 | 402 | // TODO: at the moment, constructor selection is unattributed, because AnnotationInfos lack necessary information 403 | // later on, in 2.12, for every annotation we could save an entire tree instead of just bits and pieces 404 | // but for 2.11 the current situation will have to do 405 | val ctorSelection = Select(New(TypeTree(ann.atp)), nme.CONSTRUCTOR) 406 | Apply(ctorSelection, reverseEngineerArgs()) setType ann.atp 407 | } 408 | 409 | // cut-n-pasted (with the comments) from 410 | // https://github.com/scala/scala/blob/v2.11.7/src/reflect/scala/reflect/internal/AnnotationInfos.scala#L384-L403 411 | def treeToAnnotation(tree: Tree): Annotation = tree match { 412 | case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => 413 | def encodeJavaArg(arg: Tree): ClassfileAnnotArg = arg match { 414 | case Literal(const) => LiteralAnnotArg(const) 415 | case Apply(ArrayModule, args) => ArrayAnnotArg(args map encodeJavaArg toArray) 416 | case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => NestedAnnotArg(treeToAnnotation(arg)) 417 | case _ => throw new Exception(s"unexpected java argument shape $arg: literals, arrays and nested annotations are supported") 418 | } 419 | def encodeJavaArgs(args: List[Tree]): List[(Name, ClassfileAnnotArg)] = args match { 420 | case AssignOrNamedArg(Ident(name), arg) :: rest => (name, encodeJavaArg(arg)) :: encodeJavaArgs(rest) 421 | case arg :: rest => throw new Exception(s"unexpected java argument shape $arg: only AssignOrNamedArg trees are supported") 422 | case Nil => Nil 423 | } 424 | val atp = tpt.tpe 425 | if (atp != null && (atp.typeSymbol isNonBottomSubClass StaticAnnotationClass)) AnnotationInfo(atp, args, Nil) 426 | else if (atp != null && (atp.typeSymbol isNonBottomSubClass ClassfileAnnotationClass)) AnnotationInfo(atp, Nil, encodeJavaArgs(args)) 427 | else throw new Exception(s"unexpected annotation type $atp: only subclasses of StaticAnnotation and ClassfileAnnotation are supported") 428 | case _ => 429 | throw new Exception("""unexpected tree shape: only q"new $annType(..$args)" is supported""") 430 | } 431 | } 432 | 433 | implicit def AnnotationOps(ann: Annotation): AnnotationOps = 434 | new AnnotationOps { 435 | def tree: Tree = CompatAnnotation.annotationToTree(ann) 436 | } 437 | 438 | def showCode(t: Tree): String = show(t) 439 | 440 | object CompatModifiers extends CompatModifiers { 441 | def apply(flags: FlagSet, privateWithin: Name = typeNames.EMPTY, annots: List[Tree] = Nil): Modifiers = 442 | Modifiers(flags, privateWithin, annots) 443 | 444 | def unapply(mods: Modifiers): Option[(FlagSet, Name, List[Tree])] = 445 | Some((mods.flags, mods.privateWithin, mods.annotations)) 446 | } 447 | 448 | implicit def tupleToImplicitCandidate(t: (Type, Tree)): ImplicitCandidate211 = { 449 | ImplicitCandidate.tryUnapply(t) match { 450 | case Left(s) => c.abort(c.enclosingPosition, s) 451 | case Right((pre, sym, pt, tree)) => ImplicitCandidate211(pre, sym, pt, tree) 452 | } 453 | } 454 | 455 | implicit def AttachmentsOps(as: Attachments): AttachmentsOps = 456 | new AttachmentsOps { 457 | private def matchesTag[T: ClassTag](datum: Any) = 458 | classTag[T].runtimeClass.isInstance(datum) 459 | 460 | def contains[T: ClassTag]: Boolean = 461 | !isEmpty && (as.all exists matchesTag[T]) 462 | 463 | def isEmpty: Boolean = true 464 | } 465 | 466 | lazy val noSelfType = emptyValDef 467 | } 468 | } 469 | --------------------------------------------------------------------------------