From 7ff93c05f339ef442da78182e014d3f98cc59095 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 21 Sep 2021 16:27:29 +0200 Subject: [PATCH] add type modes to DottyTypeStealer --- .../test/dotty/tools/DottyTypeStealer.scala | 41 +++++++++++++++---- docs/docs/contributing/workflow.md | 39 +++++++++++++----- 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/compiler/test/dotty/tools/DottyTypeStealer.scala b/compiler/test/dotty/tools/DottyTypeStealer.scala index 9544cb2da06e..24adda5887a2 100644 --- a/compiler/test/dotty/tools/DottyTypeStealer.scala +++ b/compiler/test/dotty/tools/DottyTypeStealer.scala @@ -13,33 +13,58 @@ import dotc.core.Types.Type * The type signatures will then be printed (singleton types * are widened.) * + * @param kind the kind of type we are inspecting [`rhs`, `method`, `class`, `type`] * @param source top level Scala definitions, e.g. `"class O { type X }"` * @param typeStrings Scala type signatures, e.g. `"O#X"` * * @syntax markdown */ -@main def printTypes(source: String, typeStrings: String*) = { - val (_, tpes) = DottyTypeStealer.stealType(source, typeStrings*) - tpes.foreach(println) +@main def printTypes(source: String, kind: String, typeStrings: String*) = { + val k = DottyTypeStealer.Kind.lookup(kind) + val (_, tpes) = DottyTypeStealer.stealType(source, k, typeStrings*) + tpes.foreach(t => println(s"$t [${t.getClass}]")) } object DottyTypeStealer extends DottyTest { - def stealType(source: String, typeStrings: String*): (Context, List[Type]) = { + + enum Kind: + case `rhs`, `method`, `class`, `type` + + def format(name: String, arg: String) = this match + case `rhs` => s"val $name: $arg = ???" + case `method` => s"def $name $arg = ???" + case `class` => s"class $name $arg" + case `type` => s"type $name $arg" + + object Kind: + + def lookup(kind: String): Kind = + values.find(_.productPrefix == kind).getOrElse { + println(s"unknown kind `$kind`, assuming `$rhs`") + rhs + } + + end Kind + + + def stealType(source: String, kind: Kind, typeStrings: String*): (Context, List[Type]) = { val dummyName = "x_x_x" - val vals = typeStrings.zipWithIndex.map{case (s, x)=> s"val ${dummyName}$x: $s = ???"}.mkString("\n") + val vals = typeStrings.zipWithIndex.map{case (s, x) => kind.format(dummyName + x, s) }.mkString("\n") val gatheredSource = s" ${source}\n object A$dummyName {$vals}" var scontext : Context = null var tp: List[Type] = null checkCompile("typer", gatheredSource) { (tree, context) => given Context = context - val findValDef: (List[ValDef], tpd.Tree) => List[ValDef] = + val findMemberDef: (List[MemberDef], tpd.Tree) => List[MemberDef] = (acc , tree) => tree match { + case t: DefDef if t.name.startsWith(dummyName) => t :: acc case t: ValDef if t.name.startsWith(dummyName) => t :: acc + case t: TypeDef if t.name.startsWith(dummyName) => t :: acc case _ => acc } - val d = new DeepFolder[List[ValDef]](findValDef).foldOver(Nil, tree) - tp = d.map(_.tpe.widen).reverse + val d = new DeepFolder[List[MemberDef]](findMemberDef).foldOver(Nil, tree) + tp = d.map(_.symbol.info).reverse scontext = context } (scontext, tp) diff --git a/docs/docs/contributing/workflow.md b/docs/docs/contributing/workflow.md index 3399cc655210..956ce2998c75 100644 --- a/docs/docs/contributing/workflow.md +++ b/docs/docs/contributing/workflow.md @@ -40,35 +40,52 @@ can be enabled through the `dotty.tools.dotc.config.Printers` object. Change any ## Inspecting Types with Type Stealer ## You can inspect types with the main method `dotty.tools.printTypes` from the sbt shell, -passing at least two arguments. The first argument is a string that introduces some -Scala definitions, the following arguments are type signatures, (i.e. the return type -of a definition) that are allowed to reference definitions from the first argument. - -The type signatures will then be printed, displaying their internal structure, using +passing at least three arguments: +- The first argument is a string that introduces some +Scala definitions +- The second argument introduces how the the remaining arguments should be interpreted, +comprising of + - `rhs` - the return type of a definition + - `class` - the signature of a class, after its name + - `method` - the signature of a method, after its name + - `type` - the signature of a type, after its name +- The remaining arguments are type signatures, these may reference definitions introduced by the first argument. + +Each type signature is then be printed, displaying their internal structure, alongside their class, using the same representation that can later be used in pattern matching to decompose the type. Here, we inspect a refinement of a class `Box`: ```bash $ sbt -> scala3-compiler-bootstrapped/Test/runMain dotty.tools.printTypes "class Box { def x: Any }" "Box { def x: Int }" -RefinedType(TypeRef(ThisType(TypeRef(NoPrefix,module class )),class Box),x,ExprType(TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object scala),class Int))) +> scala3-compiler-bootstrapped/Test/runMain dotty.tools.printTypes "class Box { def x: Any }" "rhs" "Box { def x: Int }" +RefinedType(TypeRef(ThisType(TypeRef(NoPrefix, module class )),class Box), x, ExprType(TypeRef(TermRef(ThisType(TypeRef(NoPrefix, module class )), object scala), class Int))) [class dotty.tools.dotc.core.Types$CachedRefinedType] ``` -You can also pass the empty string as the first +You can also pass the empty string as the second argument, e.g. to inspect a standard library type: ```bash $ sbt -> scala3-compiler-bootstrapped/Test/runMain dotty.tools.printTypes "" "1 *: EmptyTuple" -AppliedType(TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object scala),class *:),List(ConstantType(Constant(1)), TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class scala)),object Tuple$package),type EmptyTuple))) +> scala3-compiler-bootstrapped/Test/runMain dotty.tools.printTypes "" "rhs" "1 *: EmptyTuple" +AppliedType(TypeRef(TermRef(ThisType(TypeRef(NoPrefix, module class )), object scala), class *:), List(ConstantType(Constant(1)), TypeRef(TermRef(ThisType(TypeRef(NoPrefix, module class scala)), object Tuple$package), type EmptyTuple))) ``` +Here are some other examples you can follow: +- `...printTypes "" class "[T] extends Foo[T] {}"` +- `...printTypes "" method "(x: Int): x.type"` +- `...printTypes "" type "<: Int" "= [T] =>> List[T]"` + If you want to further inspect the types, and not just print them, the object `dotty.tools.DottyTypeStealer` has a method `stealType`. It takes the same arguments as `printTypes`, but returns both a `Context` containing the definitions passed, along with the list of types: ```scala // compiler/test/dotty/tools/DottyTypeStealer.scala object DottyTypeStealer extends DottyTest { - def stealType(source: String, typeStrings: String*): (Context, List[Type]) = { + + enum Kind: + case `rhs`, `method`, `class`, `type` + ... + + def stealType(kind: Kind, source: String, typeStrings: String*): (Context, List[Type]) = { ... } }