From dc35ca58e755c222c618599b8f5cd377af95e6b4 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 24 Nov 2020 09:48:05 +0100 Subject: [PATCH] Fix #10359: Add GivenSelector to reflection API --- .../quoted/runtime/impl/QuotesImpl.scala | 25 +++++++++- .../runtime/impl/printers/Extractors.scala | 1 + .../runtime/impl/printers/SourceCode.scala | 7 +++ library/src/scala/quoted/Quotes.scala | 34 +++++++++++--- .../tasty-inspector/i10359.scala | 46 +++++++++++++++++++ 5 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 tests/run-custom-args/tasty-inspector/i10359.scala diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 57e76e5545b7..f03b22a10fbf 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -1474,7 +1474,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler object SimpleSelectorTypeTest extends TypeTest[ImportSelector, SimpleSelector]: def unapply(x: ImportSelector): Option[SimpleSelector & x.type] = x match - case x: (untpd.ImportSelector & x.type) if x.renamed.isEmpty => Some(x) + case x: (untpd.ImportSelector & x.type) if x.renamed.isEmpty && !x.isGiven => Some(x) case _ => None // TODO: handle import bounds end SimpleSelectorTypeTest @@ -1534,6 +1534,29 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler end extension end OmitSelectorMethodsImpl + type GivenSelector = untpd.ImportSelector + + object GivenSelectorTypeTest extends TypeTest[ImportSelector, GivenSelector]: + def unapply(x: ImportSelector): Option[GivenSelector & x.type] = x match { + case self: (untpd.ImportSelector & x.type) if x.isGiven => Some(self) + case _ => None + } + end GivenSelectorTypeTest + + object GivenSelector extends GivenSelectorModule: + def unapply(x: GivenSelector): Option[Option[TypeTree]] = + Some(GivenSelectorMethodsImpl.bound(x)) + end GivenSelector + + object GivenSelectorMethodsImpl extends GivenSelectorMethods: + extension (self: GivenSelector): + def bound: Option[TypeTree] = + self.bound match + case untpd.TypedSplice(tpt) => Some(tpt) + case _ => None + end extension + end GivenSelectorMethodsImpl + type TypeRepr = dotc.core.Types.Type object TypeRepr extends TypeReprModule: diff --git a/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala b/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala index 3726e8106ba2..c644d121da2c 100644 --- a/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala +++ b/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala @@ -237,6 +237,7 @@ object Extractors { case SimpleSelector(id) => this += "SimpleSelector(" += id += ")" case RenameSelector(id1, id2) => this += "RenameSelector(" += id1 += ", " += id2 += ")" case OmitSelector(id) => this += "OmitSelector(" += id += ")" + case GivenSelector(bound) => this += "GivenSelector(" += bound += ")" } def visitSymbol(x: Symbol): this.type = diff --git a/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala b/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala index dfac11764de9..cb0f86b85db5 100644 --- a/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala +++ b/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala @@ -1226,6 +1226,13 @@ object SourceCode { case SimpleSelector(name) => this += name case OmitSelector(name) => this += name += " => _" case RenameSelector(name, newName) => this += name += " => " += newName + case GivenSelector(bound) => + bound match + case Some(tpt) => + this += "given " + printTree(tpt) + case _ => + this += "given" } private def printDefinitionName(tree: Definition): this.type = tree match { diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index a72d4ffedca5..8c3786b5b434 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -156,6 +156,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * +- ImportSelector -+- SimpleSelector * +- RenameSelector * +- OmitSelector + * +- GivenSelector * * +- Signature * @@ -1738,6 +1739,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * * SimpleSelector: `.bar` in `import foo.bar` * * RenameSelector: `.{bar => baz}` in `import foo.{bar => baz}` * * OmitSelector: `.{bar => _}` in `import foo.{bar => _}` + * * GivneSelector: `.given`/`.{given T}` in `import foo.given`/`import foo.{given T}` */ type ImportSelector <: AnyRef @@ -1745,6 +1747,9 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => trait ImportSelectorModule { this: ImportSelector.type => } + /** Simple import selector: `.bar` in `import foo.bar` */ + type SimpleSelector <: ImportSelector + given TypeTest[ImportSelector, SimpleSelector] = SimpleSelectorTypeTest protected val SimpleSelectorTypeTest: TypeTest[ImportSelector, SimpleSelector] @@ -1754,9 +1759,6 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => def unapply(x: SimpleSelector): Option[String] } - /** Simple import selector: `.bar` in `import foo.bar` */ - type SimpleSelector <: ImportSelector - given SimpleSelectorMethods as SimpleSelectorMethods = SimpleSelectorMethodsImpl protected val SimpleSelectorMethodsImpl: SimpleSelectorMethods @@ -1779,9 +1781,6 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => def unapply(x: RenameSelector): Option[(String, String)] } - /** Omit import selector: `.{bar => _}` in `import foo.{bar => _}` */ - type OmitSelector <: ImportSelector - given RenameSelectorMethods as RenameSelectorMethods = RenameSelectorMethodsImpl protected val RenameSelectorMethodsImpl: RenameSelectorMethods @@ -1794,6 +1793,9 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => end extension end RenameSelectorMethods + /** Omit import selector: `.{bar => _}` in `import foo.{bar => _}` */ + type OmitSelector <: ImportSelector + given TypeTest[ImportSelector, OmitSelector] = OmitSelectorTypeTest protected val OmitSelectorTypeTest: TypeTest[ImportSelector, OmitSelector] @@ -1812,6 +1814,26 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => def namePos: Position end OmitSelectorMethods + /** Omit import selector: `.given`/`.{given T}` in `import foo.given`/`import foo.{given T}` */ + type GivenSelector <: ImportSelector + + given TypeTest[ImportSelector, GivenSelector] = GivenSelectorTypeTest + protected val GivenSelectorTypeTest: TypeTest[ImportSelector, GivenSelector] + + val GivenSelector: GivenSelectorModule + + trait GivenSelectorModule { this: GivenSelector.type => + def unapply(x: GivenSelector): Option[Option[TypeTree]] + } + + given GivenSelectorMethods as GivenSelectorMethods = GivenSelectorMethodsImpl + protected val GivenSelectorMethodsImpl: GivenSelectorMethods + + trait GivenSelectorMethods: + extension (self: GivenSelector): + def bound: Option[TypeTree] + end GivenSelectorMethods + /////////////// // TYPES // /////////////// diff --git a/tests/run-custom-args/tasty-inspector/i10359.scala b/tests/run-custom-args/tasty-inspector/i10359.scala new file mode 100644 index 000000000000..d8a213f5628b --- /dev/null +++ b/tests/run-custom-args/tasty-inspector/i10359.scala @@ -0,0 +1,46 @@ +import scala.quoted._ +import scala.tasty.inspector._ + +object Bar { + class Givens { + given Int = 23 + given String = "the string" + } + class Foo { + val g: Givens = Givens() + import g.given + import g.{given} + import g.{given Int} + } +} + +// Case object implementation +sealed trait Flavor +case object Vanilla extends Flavor +case object Chocolate extends Flavor +case object Bourbon extends Flavor + +object Test { + def main(args: Array[String]): Unit = { + // Artefact of the current test infrastructure + // TODO improve infrastructure to avoid needing this code on each test + val classpath = dotty.tools.dotc.util.ClasspathFromClassloader(this.getClass.getClassLoader).split(java.io.File.pathSeparator).find(_.contains("runWithCompiler")).get + val allTastyFiles = dotty.tools.io.Path(classpath).walkFilter(_.extension == "tasty").map(_.toString).toList + val tastyFiles = allTastyFiles.filter(_.contains("TraitParams")) + + val inspect = new TestInspector() + inspect.inspectTastyFiles(allTastyFiles.filter(_.contains("Bar"))) + } +} + +class TestInspector() extends TastyInspector: + + protected def processCompilationUnit(using Quotes)(root: quotes.reflect.Tree): Unit = + import quotes.reflect._ + + val code = root.show + assert(code.contains("import Foo.this.g.{given}"), code) + assert(code.contains("import Foo.this.g.{given scala.Int}"), code) + + val extractors = root.showExtractors + assert(extractors.contains("GivenSelector"), extractors)