@@ -76,4 +76,133 @@ class MiscMacros(ctx: blackbox.Context) extends AbstractMacroCommons(ctx) {
7676 case t =>
7777 c.abort(t.pos, " Expected string literal here" )
7878 }
79+
80+ def typeString [T : WeakTypeTag ]: Tree = {
81+ val tpe = weakTypeOf[T ]
82+ typeStringParts(tpe) match {
83+ case List (Select (pre, TermName (" value" ))) => pre
84+ case trees => q " $CommonsPkg.misc.TypeString[ $tpe]( ${mkStringConcat(trees)}) "
85+ }
86+ }
87+
88+ def typeStringParts (tpe : Type ): List [Tree ] = {
89+ val resultTpe = getType(tq " $CommonsPkg.misc.TypeString[ $tpe] " )
90+ c.inferImplicitValue(resultTpe, withMacrosDisabled = true ) match {
91+ case EmptyTree => mkTypeString(tpe)
92+ case tree => List (q " $tree.value " )
93+ }
94+ }
95+
96+ def mkStringConcat (trees : List [Tree ]): Tree = trees match {
97+ case Nil => StringLiteral (" " )
98+ case single :: Nil => single
99+ case StringLiteral (str1) :: StringLiteral (str2) :: tail =>
100+ mkStringConcat(StringLiteral (str1 + str2) :: tail)
101+ case head :: tail =>
102+ q " $head+ ${mkStringConcat(tail)}"
103+ }
104+
105+ def join (trees : List [List [Tree ]], sep : String ): List [Tree ] = trees match {
106+ case Nil => Nil
107+ case List (single) => single
108+ case head :: tail => head ::: lit(sep) :: join(tail, sep)
109+ }
110+
111+ def lit (str : String ): Tree =
112+ StringLiteral (str)
113+
114+ def mkNameString (name : Name ): String =
115+ showCode(Ident (name))
116+
117+ def isRefTo (quantified : Symbol , arg : Type ): Boolean = arg match {
118+ case TypeRef (NoPrefix , `quantified`, Nil ) => true
119+ case _ => false
120+ }
121+
122+ def areIndependent (quantified : List [Symbol ]): Boolean =
123+ quantified.forall(first => quantified.forall(second => ! first.typeSignature.contains(second)))
124+
125+ def mkTypeDefString (s : Symbol , wildcard : Boolean ): List [Tree ] = s match {
126+ case ExistentialSingleton (_, name, sig) =>
127+ lit(s " val ${mkNameString(name)}: " ) :: mkTypeString(sig)
128+ case _ =>
129+ lit(if (wildcard) " _" else s " type ${mkNameString(s.name)}" ) :: mkTypeString(s.typeSignature)
130+ }
131+
132+ private val autoImported : Set [Symbol ] = Set (
133+ definitions.ScalaPackage ,
134+ definitions.JavaLangPackage ,
135+ definitions.PredefModule
136+ )
137+
138+ def isStaticPrefix (pre : Type ): Boolean = pre match {
139+ case SingleType (ppre, _) => isStaticPrefix(ppre)
140+ case ThisType (sym) => sym.isStatic && sym.isModuleClass
141+ case TypeRef (ppre, sym, Nil ) => sym.isStatic && sym.isModuleClass && isStaticPrefix(ppre)
142+ case _ => false
143+ }
144+
145+ def mkTypeString (tpe : Type ): List [Tree ] = tpe match {
146+ case _ if tpe.typeSymbol == definitions.AnyRefClass => List (lit(" AnyRef" ))
147+ case TypeRef (NoPrefix , ExistentialSingleton (_, name, _), Nil ) =>
148+ List (lit(mkNameString(name)))
149+ case TypeRef (_, sym, args) if definitions.FunctionClass .seq.contains(sym) =>
150+ val fargs = args.init
151+ val fres = args.last
152+ lit(" (" ) :: join(fargs.map(typeStringParts), " , " ) ::: lit(" ) => " ) :: typeStringParts(fres)
153+ case TypeRef (_, sym, args) if definitions.TupleClass .seq.contains(sym) =>
154+ lit(" (" ) :: join(args.map(typeStringParts), " , " ) ::: lit(" )" ) :: Nil
155+ case TypeRef (pre, sym, args) if ! sym.isParameter =>
156+ val dealiased = tpe.dealias
157+ if (dealiased.typeSymbol != sym && ! isStaticPrefix(pre)) {
158+ mkTypeString(dealiased)
159+ }
160+ else {
161+ val argsReprs =
162+ if (args.isEmpty) Nil
163+ else lit(" [" ) +: join(args.map(typeStringParts), " , " ) :+ lit(" ]" )
164+ mkTypePrefix(pre) ::: lit(mkNameString(sym.name)) :: argsReprs
165+ }
166+ case SingleType (_, _) =>
167+ mkTypePrefix(tpe) :+ lit(" type" )
168+ case ThisType (sym) if sym.isStatic && sym.isModuleClass =>
169+ List (lit(mkStaticPrefix(sym) + " type" ))
170+ case ExistentialType (quantified, TypeRef (pre, sym, args))
171+ if quantified.corresponds(args)(isRefTo) && quantified.forall(s => ! pre.contains(s)) && areIndependent(quantified) =>
172+ val wildcards = lit(" [" ) +: join(quantified.map(mkTypeDefString(_, wildcard = true )), " , " ) :+ lit(" ]" )
173+ mkTypePrefix(pre) ::: lit(mkNameString(sym.name)) :: wildcards
174+ case ExistentialType (quantified, underlying) =>
175+ val typeDefs = join(quantified.map(mkTypeDefString(_, wildcard = false )), " ; " )
176+ typeStringParts(underlying) ::: lit(" forSome {" ) :: typeDefs ::: lit(" }" ) :: Nil
177+ case TypeBounds (lo, hi) =>
178+ val loRepr =
179+ if (lo =:= typeOf[Nothing ]) Nil
180+ else lit(" >: " ) :: typeStringParts(lo)
181+ val hiRepr =
182+ if (hi =:= typeOf[Any ]) Nil
183+ else lit(" <: " ) :: typeStringParts(hi)
184+ loRepr ++ hiRepr
185+ case RefinedType (bases, scope) if scope.forall(_.isType) =>
186+ val basesRepr = join(bases.map(typeStringParts), " with " )
187+ val scopeRepr =
188+ if (scope.isEmpty) Nil
189+ else lit(" {" ) :: join(scope.map(mkTypeDefString(_, wildcard = false )).toList, " ; " ) ::: lit(" }" ) :: Nil
190+ basesRepr ++ scopeRepr
191+ case _ =>
192+ abort(s " Could not find nor materialize TypeString for $tpe" )
193+ }
194+
195+ def mkStaticPrefix (sym : Symbol ): String =
196+ if (sym == rootMirror.RootClass ) " "
197+ else mkStaticPrefix(sym.owner) + mkNameString(sym.name) + " ."
198+
199+ def mkTypePrefix (tpe : Type ): List [Tree ] = tpe match {
200+ case t if autoImported.contains(t.termSymbol) => Nil
201+ case NoPrefix => Nil
202+ case SingleType (pkg, sym) if sym.name == termNames.PACKAGE && pkg.typeSymbol.isPackageClass => mkTypePrefix(pkg)
203+ case SingleType (pre, sym) => mkTypePrefix(pre) :+ lit(mkNameString(sym.name) + " ." )
204+ case ThisType (sym) if sym.isStatic && sym.isModuleClass => List (lit(mkStaticPrefix(sym)))
205+ case TypeRef (NoPrefix , ExistentialSingleton (_, name, _), Nil ) => List (lit(mkNameString(name) + " ." ))
206+ case _ => mkTypeString(tpe) :+ lit(if (tpe.typeSymbol.isModuleClass || tpe.termSymbol != NoSymbol ) " ." else " #" )
207+ }
79208}
0 commit comments