1+ package dotty .tools .dotc
2+ package ast
3+
4+ import core ._
5+ import Symbols ._ , Types ._ , Contexts ._ , Decorators ._ , util .Spans ._ , Flags ._ , Constants ._
6+ import StdNames .nme
7+ import ast .Trees ._
8+
9+ /** Generate proxy classes for @main functions.
10+ * A function like
11+ *
12+ * @main f(x: S, ys: T*) = ...
13+ *
14+ * would be translated to something like
15+ *
16+ * import CommandLineParser._
17+ * class f {
18+ * @static def main(args: Array[String]): Unit =
19+ * try
20+ * f(
21+ * parseArgument[S](args, 0),
22+ * parseRemainingArguments[T](args, 1): _*
23+ * )
24+ * catch case err: ParseError => showError(err)
25+ * }
26+ */
27+ object MainProxies {
28+
29+ def mainProxies (stats : List [tpd.Tree ]) given Context : List [untpd.Tree ] = {
30+ import tpd ._
31+ def mainMethods (stats : List [Tree ]): List [Symbol ] = stats.flatMap {
32+ case stat : DefDef if stat.symbol.hasAnnotation(defn.MainAnnot ) =>
33+ stat.symbol :: Nil
34+ case stat @ TypeDef (name, impl : Template ) if stat.symbol.is(Module ) =>
35+ mainMethods(impl.body)
36+ case _ =>
37+ Nil
38+ }
39+ mainMethods(stats).flatMap(mainProxy)
40+ }
41+
42+ import untpd ._
43+ def mainProxy (mainFun : Symbol ) given (ctx : Context ): List [TypeDef ] = {
44+ val mainAnnotSpan = mainFun.getAnnotation(defn.MainAnnot ).get.tree.span
45+
46+ val argsRef = Ident (nme.args)
47+
48+ def addArgs (call : untpd.Tree , formals : List [Type ], restpe : Type , idx : Int ): untpd.Tree = {
49+ val args = formals.zipWithIndex map {
50+ (formal, n) =>
51+ val (parserSym, formalElem) =
52+ if (formal.isRepeatedParam) (defn.CLP_parseRemainingArguments , formal.argTypes.head)
53+ else (defn.CLP_parseArgument , formal)
54+ val arg = Apply (
55+ TypeApply (ref(parserSym.termRef), TypeTree (formalElem) :: Nil ),
56+ argsRef :: Literal (Constant (idx + n)) :: Nil )
57+ if (formal.isRepeatedParam) repeated(arg) else arg
58+ }
59+ val call1 = Apply (call, args)
60+ restpe match {
61+ case restpe : MethodType if ! restpe.isImplicitMethod =>
62+ if (formals.lastOption.getOrElse(NoType ).isRepeatedParam)
63+ ctx.error(s " varargs parameter of @main method must come last " , mainFun.sourcePos)
64+ addArgs(call1, restpe.paramInfos, restpe.resType, idx + args.length)
65+ case _ =>
66+ call1
67+ }
68+ }
69+
70+ var result : List [TypeDef ] = Nil
71+ if (! mainFun.owner.isStaticOwner)
72+ ctx.error(s " @main method is not statically accessible " , mainFun.sourcePos)
73+ else {
74+ var call = ref(mainFun.termRef)
75+ mainFun.info match {
76+ case _ : ExprType =>
77+ case mt : MethodType =>
78+ if (! mt.isImplicitMethod) call = addArgs(call, mt.paramInfos, mt.resultType, 0 )
79+ case _ : PolyType =>
80+ ctx.error(s " @main method cannot have type parameters " , mainFun.sourcePos)
81+ case _ =>
82+ ctx.error(s " @main can only annotate a method " , mainFun.sourcePos)
83+ }
84+ val errVar = Ident (nme.error)
85+ val handler = CaseDef (
86+ Typed (errVar, TypeTree (defn.CLP_ParseError .typeRef)),
87+ EmptyTree ,
88+ Apply (ref(defn.CLP_showError .termRef), errVar :: Nil ))
89+ val body = Try (call, handler :: Nil , EmptyTree )
90+ val mainArg = ValDef (nme.args, TypeTree (defn.ArrayType .appliedTo(defn.StringType )), EmptyTree )
91+ .withFlags(Param )
92+ val mainMeth = DefDef (nme.main, Nil , (mainArg :: Nil ) :: Nil , TypeTree (defn.UnitType ), body)
93+ .withFlags(JavaStatic )
94+ val mainTempl = Template (emptyConstructor, Nil , Nil , EmptyValDef , mainMeth :: Nil )
95+ val mainCls = TypeDef (mainFun.name.toTypeName, mainTempl)
96+ if (! ctx.reporter.hasErrors) result = mainCls.withSpan(mainAnnotSpan) :: Nil
97+ }
98+ result
99+ }
100+ }
0 commit comments