Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalize main annotations #13727

Closed
wants to merge 121 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
3673ccf
Create classes for improved main annotations
timotheeandres Oct 4, 2021
65785d4
Fix parameter error in argsGetter
timotheeandres Oct 4, 2021
e9db3d0
Allow no parameters
timotheeandres Oct 4, 2021
5d3c199
Add first tests for new main annotation
timotheeandres Oct 4, 2021
6161b9b
Add tests for incorrect parameters passing
timotheeandres Oct 5, 2021
959aff9
Update MiMa filters
timotheeandres Oct 8, 2021
81c88c1
Split argGetter with/out default value
timotheeandres Oct 13, 2021
2fbdf66
Curry parsers with using
timotheeandres Oct 13, 2021
098fd27
Add more tests: no parentheses, by-name params
timotheeandres Oct 13, 2021
754a6d0
Add override keyword for safety
timotheeandres Oct 13, 2021
c8210d5
Test for unknown given parser
timotheeandres Oct 13, 2021
b727dab
Add class for main method exit code
timotheeandres Oct 14, 2021
ad291f2
Add tests for return types and overloading
timotheeandres Oct 14, 2021
c68d6f0
Extract usage and explain into main
timotheeandres Oct 15, 2021
1ca13aa
Extract run into main
timotheeandres Oct 15, 2021
891c6e4
Make Argument a trait, make name a def
timotheeandres Oct 18, 2021
d35237e
Update main code generation
timotheeandres Oct 19, 2021
1ebf1e5
Update tests with Java reflection (do not pass yet)
timotheeandres Oct 21, 2021
58d5460
Add argument type to Argument trait
timotheeandres Oct 21, 2021
652e1e0
Fix return type of vararg func
timotheeandres Oct 21, 2021
8ac03b7
Implement Scaladoc parsing
timotheeandres Oct 22, 2021
b9cc156
Add tests for top-level main, currying and genericity
timotheeandres Oct 22, 2021
e7d9bba
Simplify code in createValArgs
timotheeandres Oct 24, 2021
a05c4f1
Add rules to MiMa filters
timotheeandres Oct 24, 2021
cb4bb20
Fix tests overriding main methods
timotheeandres Oct 24, 2021
c5bed16
Remove debug println
timotheeandres Oct 25, 2021
c0d80d3
Fix vararg handling in MainProxies
timotheeandres Oct 25, 2021
c22d360
Cleaner self reference in main.scala
timotheeandres Oct 25, 2021
884ea20
Fix reflection in test
timotheeandres Oct 25, 2021
f18b3a4
Generate class instead of object, instanciate main
timotheeandres Oct 25, 2021
a3748d0
Instanciate correct class of annotation
timotheeandres Oct 26, 2021
a60bd11
Fix test outputs with new doc format
timotheeandres Oct 26, 2021
989bcfa
Add tests for multiple annotations, non-method
timotheeandres Oct 26, 2021
26b2d63
Clarification of arg generation
timotheeandres Oct 26, 2021
45f7ba2
Add vararg and default to top-level test
timotheeandres Oct 26, 2021
62b1670
Add support for default values
timotheeandres Oct 26, 2021
45490a9
Add "(optional)" for args with default values
timotheeandres Oct 26, 2021
7cf2f7f
Make help test more extensive
timotheeandres Oct 26, 2021
0ab2c43
Fix help test
timotheeandres Oct 27, 2021
0657c64
Implement doc line wraping
timotheeandres Oct 27, 2021
ce7471a
Add support for multiline arg doc
timotheeandres Oct 27, 2021
8ed3a9f
Remove {{ and }} from docstrings
timotheeandres Oct 27, 2021
8b68474
Remove wildcards from Argument matching
timotheeandres Oct 29, 2021
7eb8c0c
Extract default values getter for clarity
timotheeandres Oct 29, 2021
91f5ee4
Factorize getter code, check duplicate named param
timotheeandres Oct 29, 2021
cd5674e
Make MainProxy more explicit
timotheeandres Oct 29, 2021
ccfe578
Change explain formatting
timotheeandres Oct 30, 2021
8b42578
Use f.foo(i) instead of f foo i
timotheeandres Nov 1, 2021
91f6d55
Trim docstring after removing {{ and }}
timotheeandres Nov 1, 2021
87f9c09
Add test for anon. func. and implicit/using @main
timotheeandres Nov 2, 2021
78fa961
Factorize createArgs
timotheeandres Nov 2, 2021
a65b76f
Rework of Arguments, change usage format
timotheeandres Nov 2, 2021
52ef5dc
Misc. changes to tests
timotheeandres Nov 6, 2021
75e06a1
Forbid multiple @main annotations
timotheeandres Nov 7, 2021
395fd14
Make defaultValue by-name param
timotheeandres Nov 9, 2021
f300597
Remove redundant val in ExitCode
timotheeandres Nov 9, 2021
45acb09
Fix method call notation
timotheeandres Nov 9, 2021
6de547f
Generate code for children of MainAnnotation, not main
timotheeandres Nov 9, 2021
70ee28a
Forbid curried main methods
timotheeandres Nov 9, 2021
50de840
Add missing arrow in main.argGetterDefault
timotheeandres Nov 9, 2021
6bc549b
Make Command a trait
timotheeandres Nov 9, 2021
1b9c952
Fix issue where map is not member of Array[String | Null] | Null
timotheeandres Nov 10, 2021
de93b4d
Add test for multiple runs
timotheeandres Nov 10, 2021
bbcf4ea
Add test from doc example
timotheeandres Nov 13, 2021
bdddb3f
Make main class final
timotheeandres Nov 13, 2021
88a76df
Factorize code of main
timotheeandres Nov 13, 2021
f2bafb4
Move Command out of MainAnnotation
timotheeandres Nov 15, 2021
3cd2a88
Make by-name parameter a function for clarity
timotheeandres Nov 15, 2021
c4a6ff0
Print returned value when it is not Unit
timotheeandres Nov 15, 2021
7b685c4
Add more parser checks
timotheeandres Nov 15, 2021
a6e9da9
Make usage clearer by printing type
timotheeandres Nov 16, 2021
5065a09
Add help tests with homemade classes
timotheeandres Nov 16, 2021
63f30f8
Simplify main-annotation-help.scala
timotheeandres Nov 16, 2021
de73f3c
Update MiMa filters
timotheeandres Nov 16, 2021
2cad3dc
Add test for docstring starting with //
timotheeandres Nov 21, 2021
d32868a
Pass doc to command; main parameter for line length
timotheeandres Nov 21, 2021
4d4418e
Add constructor to main and update MiMa
timotheeandres Nov 22, 2021
bded202
Make Documentation a trait and use anonymous instance
timotheeandres Nov 22, 2021
aff99de
Add test for Option[T] and Either[T]
timotheeandres Nov 22, 2021
889dbd6
Remove ExitCode and result printing in main
timotheeandres Nov 28, 2021
4d0c149
Add test for function Parser
timotheeandres Nov 29, 2021
1be2e7d
Move Documentation back to MainProxies
timotheeandres Nov 29, 2021
8161163
Add test with Future[_] as return type
timotheeandres Dec 4, 2021
eafa508
Factorize annotation instanciation code
timotheeandres Dec 4, 2021
99d9c12
Pass object to argGetter instead of multiple params
timotheeandres Dec 4, 2021
282bfc6
Move code around for readability
timotheeandres Dec 4, 2021
0d1d1f9
Fix symbol error in Documentation
timotheeandres Dec 4, 2021
c8584df
Add main.Arg annotation for argument-related parameters
timotheeandres Dec 5, 2021
d1ab483
Move parameter documentation out of constructor
timotheeandres Dec 5, 2021
f2b5287
Update example in MainProxies
timotheeandres Dec 5, 2021
635b79e
Simplify code for assignations
timotheeandres Dec 5, 2021
485d9f8
Add documentation to MainProxies
timotheeandres Dec 5, 2021
1e71cb2
Parametrize -- in main
timotheeandres Dec 8, 2021
b4dc846
Use types in MainProxies for convenience
timotheeandres Dec 8, 2021
760f49c
Use default values' symbols instead of trees
timotheeandres Dec 8, 2021
af851b3
Change structure of ParameterInfos
timotheeandres Dec 8, 2021
300adcf
Add test to check lazyness of default value
timotheeandres Dec 8, 2021
e818f13
Simplify homemade annotations tests
timotheeandres Dec 9, 2021
07426c4
Split Arg into ShortName and Name
timotheeandres Dec 8, 2021
cf0c98c
Rename defaultValueOpt for clearer name
timotheeandres Dec 9, 2021
99f6ba3
Fix issue with explicit type for parameters
timotheeandres Dec 9, 2021
2be6820
Add crazy test for param annotations
timotheeandres Dec 9, 2021
c681a6f
Make Name and ShortName normal classes (remove case)
timotheeandres Dec 14, 2021
5b44edf
Update comments in MainProxies
timotheeandres Jan 21, 2022
6f16275
Quick clean up of MainProxies
timotheeandres Jan 21, 2022
86c6bed
Change default value index extraction
timotheeandres Jan 21, 2022
fc1fba6
Reformat switch cases
timotheeandres Jan 22, 2022
f1bc063
Remove unnecessary code
timotheeandres Jan 22, 2022
61744dd
Avoid vars in code
timotheeandres Jan 22, 2022
06514c4
Keep old version of MainProxies for #14156
timotheeandres Jan 22, 2022
5599dd5
Add more thorough documentation for main
timotheeandres Jan 22, 2022
823f320
Allow only standard letters as short names
timotheeandres Jan 23, 2022
a8da6b8
Rename docComment for clarity
timotheeandres Jan 25, 2022
36988bb
Move ArgumentKind inside Command
timotheeandres Feb 1, 2022
ddbb6d0
Add function to check name validity
timotheeandres Feb 3, 2022
a57ff52
Pass ParameterInfos in command instead of getters
timotheeandres Feb 5, 2022
c77f5d3
Move helper functions inside methods
timotheeandres Feb 5, 2022
5558948
Rework of main.Name and main.ShortName
timotheeandres Feb 6, 2022
4e99de0
Remove maxLineLength from main
timotheeandres Feb 6, 2022
b359465
Add -h to display help
timotheeandres Feb 6, 2022
4ce8911
Move helper functions' code inside run
timotheeandres Feb 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
356 changes: 333 additions & 23 deletions compiler/src/dotty/tools/dotc/ast/MainProxies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,33 @@ package ast

import core._
import Symbols._, Types._, Contexts._, Decorators._, util.Spans._, Flags._, Constants._
import StdNames.nme
import StdNames.{nme, tpnme}
import ast.Trees._
import Names.{Name, TermName}
import Comments.Comment
import NameKinds.DefaultGetterName
import Annotations.Annotation

/** Generate proxy classes for @main functions.
* A function like
*
* @main def f(x: S, ys: T*) = ...
*
* would be translated to something like
*
* import CommandLineParser._
* class f {
* @static def main(args: Array[String]): Unit =
* try
* f(
* parseArgument[S](args, 0),
* parseRemainingArguments[T](args, 1): _*
* )
* catch case err: ParseError => showError(err)
* }
*/
object MainProxies {

def mainProxies(stats: List[tpd.Tree])(using Context): List[untpd.Tree] = {
/** Generate proxy classes for @main functions.
* A function like
*
* @main def f(x: S, ys: T*) = ...
*
* would be translated to something like
*
* import CommandLineParser._
* class f {
* @static def main(args: Array[String]): Unit =
* try
* f(
* parseArgument[S](args, 0),
* parseRemainingArguments[T](args, 1): _*
* )
* catch case err: ParseError => showError(err)
* }
*/
def mainProxiesOld(stats: List[tpd.Tree])(using Context): List[untpd.Tree] = {
import tpd._
def mainMethods(stats: List[Tree]): List[Symbol] = stats.flatMap {
case stat: DefDef if stat.symbol.hasAnnotation(defn.MainAnnot) =>
Expand All @@ -36,11 +39,11 @@ object MainProxies {
case _ =>
Nil
}
mainMethods(stats).flatMap(mainProxy)
mainMethods(stats).flatMap(mainProxyOld)
}

import untpd._
def mainProxy(mainFun: Symbol)(using Context): List[TypeDef] = {
def mainProxyOld(mainFun: Symbol)(using Context): List[TypeDef] = {
val mainAnnotSpan = mainFun.getAnnotation(defn.MainAnnot).get.tree.span
def pos = mainFun.sourcePos
val argsRef = Ident(nme.args)
Expand Down Expand Up @@ -115,4 +118,311 @@ object MainProxies {
}
result
}

private type DefaultValueSymbols = Map[Int, Symbol]
private type ParameterAnnotationss = Seq[Seq[Annotation]]

/**
* Generate proxy classes for main functions.
* A function like
*
* /**
* * Lorem ipsum dolor sit amet
* * consectetur adipiscing elit.
* *
* * @param x my param x
* * @param ys all my params y
* */
* @main(80) def f(
* @main.Alias("myX") x: S,
* ys: T*
* ) = ...
*
* would be translated to something like
*
* final class f {
* static def main(args: Array[String]): Unit = {
* val cmd = new main(80).command(
* args,
* "f",
* "Lorem ipsum dolor sit amet consectetur adipiscing elit.",
* new scala.annotation.MainAnnotation.ParameterInfos("x", "S")
* .withDocumentation("my param x")
* .withAnnotations(new scala.main.Alias("myX")),
* new scala.annotation.MainAnnotation.ParameterInfos("ys", "T")
* .withDocumentation("all my params y")
* )
*
* val args0: () => S = cmd.argGetter[S]("x", None)
* val args1: () => Seq[T] = cmd.varargGetter[T]("ys")
*
* cmd.run(f(args0(), args1()*))
* }
* }
*/
def mainProxies(stats: List[tpd.Tree])(using Context): List[untpd.Tree] = {
import tpd._

/**
* Computes the symbols of the default values of the function. Since they cannot be infered anymore at this
* point of the compilation, they must be explicitely passed by [[mainProxy]].
*/
def defaultValueSymbols(scope: Tree, funSymbol: Symbol): DefaultValueSymbols =
scope match {
case TypeDef(_, template: Template) =>
template.body.flatMap((_: Tree) match {
case dd: DefDef if dd.name.is(DefaultGetterName) && dd.name.firstPart == funSymbol.name =>
val DefaultGetterName.NumberedInfo(index) = dd.name.info
List(index -> dd.symbol)
case _ => Nil
}).toMap
case _ => Map.empty
}

/** Computes the list of main methods present in the code. */
def mainMethods(scope: Tree, stats: List[Tree]): List[(Symbol, ParameterAnnotationss, DefaultValueSymbols, Option[Comment])] = stats.flatMap {
case stat: DefDef =>
val sym = stat.symbol
sym.annotations.filter(_.matches(defn.MainAnnot)) match {
case Nil =>
Nil
case _ :: Nil =>
val paramAnnotations = stat.paramss.flatMap(_.map(
valdef => valdef.symbol.annotations.filter(_.matches(defn.MainAnnotParameterAnnotation))
))
(sym, paramAnnotations.toVector, defaultValueSymbols(scope, sym), stat.rawComment) :: Nil
case mainAnnot :: others =>
report.error(s"method cannot have multiple main annotations", mainAnnot.tree)
Nil
}
case stat @ TypeDef(_, impl: Template) if stat.symbol.is(Module) =>
mainMethods(stat, impl.body)
case _ =>
Nil
}

// Assuming that the top-level object was already generated, all main methods will have a scope
mainMethods(EmptyTree, stats).flatMap(mainProxy)
}

def mainProxy(mainFun: Symbol, paramAnnotations: ParameterAnnotationss, defaultValueSymbols: DefaultValueSymbols, docComment: Option[Comment])(using Context): List[TypeDef] = {
val mainAnnot = mainFun.getAnnotation(defn.MainAnnot).get
def pos = mainFun.sourcePos
val cmdName: TermName = Names.termName("cmd")

val documentation = new Documentation(docComment)

/** A literal value (Boolean, Int, String, etc.) */
inline def lit(any: Any): Literal = Literal(Constant(any))

/** None */
inline def none: Tree = ref(defn.NoneModule.termRef)

/** Some(value) */
inline def some(value: Tree): Tree = Apply(ref(defn.SomeClass.companionModule.termRef), value)

/** () => value */
def unitToValue(value: Tree): Tree =
val anonName = nme.ANON_FUN
val defdef = DefDef(anonName, List(Nil), TypeTree(), value)
Block(defdef, Closure(Nil, Ident(anonName), EmptyTree))

/**
* Creates a list of references and definitions of arguments, the first referencing the second.
* The goal is to create the
* `val args0: () => S = cmd.argGetter[S]("x", None)`
* part of the code.
* For each tuple, the first element is a ref to `args0`, the second is the whole definition, the third
* is the ParameterInfos definition associated to this argument.
*/
def createArgs(mt: MethodType, cmdName: TermName): List[(Tree, ValDef, Tree)] =
mt.paramInfos.zip(mt.paramNames).zipWithIndex.map {
case ((formal, paramName), n) =>
val argName = nme.args ++ n.toString
val isRepeated = formal.isRepeatedParam

val (argRef, formalType, getterSym) = {
val argRef0 = Apply(Ident(argName), Nil)
if formal.isRepeatedParam then
(repeated(argRef0), formal.argTypes.head, defn.MainAnnotCommand_varargGetter)
else (argRef0, formal, defn.MainAnnotCommand_argGetter)
}

// The ParameterInfos
val parameterInfos = {
val param = paramName.toString
val paramInfosTree = New(
TypeTree(defn.MainAnnotParameterInfos.typeRef),
// Arguments to be passed to ParameterInfos' constructor
List(List(lit(param), lit(formalType.show)))
)

/*
* Assignations to be made after the creation of the ParameterInfos.
* For example:
* args0paramInfos.withDocumentation("my param x")
* is represented by the pair
* defn.MainAnnotationParameterInfos_withDocumentation -> List(lit("my param x"))
*/
var assignations: List[(Symbol, List[Tree])] = Nil
for (doc <- documentation.argDocs.get(param))
assignations = (defn.MainAnnotationParameterInfos_withDocumentation -> List(lit(doc))) :: assignations

val instanciatedAnnots = paramAnnotations(n).map(instanciateAnnotation).toList
if instanciatedAnnots.nonEmpty then
assignations = (defn.MainAnnotationParameterInfos_withAnnotations -> instanciatedAnnots) :: assignations

assignations.foldLeft[Tree](paramInfosTree){ case (tree, (setterSym, values)) => Apply(Select(tree, setterSym.name), values) }
}

val argParams =
if formal.isRepeatedParam then
List(lit(paramName.toString))
else
val defaultValueGetterOpt = defaultValueSymbols.get(n) match {
case None =>
none
case Some(dvSym) =>
some(unitToValue(ref(dvSym.termRef)))
}
List(lit(paramName.toString), defaultValueGetterOpt)

val argDef = ValDef(
argName,
TypeTree(),
Apply(TypeApply(Select(Ident(cmdName), getterSym.name), TypeTree(formalType) :: Nil), argParams),
)

(argRef, argDef, parameterInfos)
}
end createArgs

/** Turns an annotation (e.g. `@main(40)`) into an instance of the class (e.g. `new scala.main(40)`). */
def instanciateAnnotation(annot: Annotation): Tree =
val argss = {
def recurse(t: tpd.Tree, acc: List[List[Tree]]): List[List[Tree]] = t match {
case Apply(t, args: List[tpd.Tree]) => recurse(t, extractArgs(args) :: acc)
case _ => acc
}

def extractArgs(args: List[tpd.Tree]): List[Tree] =
args.flatMap {
case Typed(SeqLiteral(varargs, _), _) => varargs.map(arg => TypedSplice(arg))
case arg: Select if arg.name.is(DefaultGetterName) => Nil // Ignore default values, they will be added later by the compiler
case arg => List(TypedSplice(arg))
}

recurse(annot.tree, Nil)
}

New(TypeTree(annot.symbol.typeRef), argss)
end instanciateAnnotation

var result: List[TypeDef] = Nil
if (!mainFun.owner.isStaticOwner)
report.error(s"main method is not statically accessible", pos)
else {
var args: List[ValDef] = Nil
var mainCall: Tree = ref(mainFun.termRef)
var parameterInfoss: List[Tree] = Nil

mainFun.info match {
case _: ExprType =>
case mt: MethodType =>
if (mt.isImplicitMethod) {
report.error(s"main method cannot have implicit parameters", pos)
}
else mt.resType match {
case restpe: MethodType =>
report.error(s"main method cannot be curried", pos)
Nil
case _ =>
val (argRefs, argVals, paramInfoss) = createArgs(mt, cmdName).unzip3
args = argVals
mainCall = Apply(mainCall, argRefs)
parameterInfoss = paramInfoss
}
case _: PolyType =>
report.error(s"main method cannot have type parameters", pos)
case _ =>
report.error(s"main can only annotate a method", pos)
}

val cmd = ValDef(
cmdName,
TypeTree(),
Apply(
Select(instanciateAnnotation(mainAnnot), defn.MainAnnot_command.name),
Ident(nme.args) :: lit(mainFun.showName) :: lit(documentation.mainDoc) :: parameterInfoss
)
)
val run = Apply(Select(Ident(cmdName), defn.MainAnnotCommand_run.name), mainCall)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
val run = Apply(Select(Ident(cmdName), defn.MainAnnotCommand_run.name), mainCall)
val run = Apply(Select(Ident(cmdName), nme.run), mainCall)

val body = Block(cmd :: args, run)
val mainArg = ValDef(nme.args, TypeTree(defn.ArrayType.appliedTo(defn.StringType)), EmptyTree)
.withFlags(Param)
/** Replace typed `Ident`s that have been typed with a TypeSplice with the reference to the symbol.
* The annotations will be retype-checked in another scope that may not have the same imports.
*/
def insertTypeSplices = new TreeMap {
override def transform(tree: Tree)(using Context): Tree = tree match
case tree: tpd.Ident @unchecked => TypedSplice(tree)
case tree => super.transform(tree)
}
val annots = mainFun.annotations
.filterNot(_.matches(defn.MainAnnot))
.map(annot => insertTypeSplices.transform(annot.tree))
val mainMeth = DefDef(nme.main, (mainArg :: Nil) :: Nil, TypeTree(defn.UnitType), body)
.withFlags(JavaStatic)
.withAnnotations(annots)
val mainTempl = Template(emptyConstructor, Nil, Nil, EmptyValDef, mainMeth :: Nil)
val mainCls = TypeDef(mainFun.name.toTypeName, mainTempl)
.withFlags(Final | Invisible)
if (!ctx.reporter.hasErrors) result = mainCls.withSpan(mainAnnot.tree.span.toSynthetic) :: Nil
}
result
}

/** A class responsible for extracting the docstrings of a method. */
private class Documentation(docComment: Option[Comment]):
import util.CommentParsing._

/** The main part of the documentation. */
lazy val mainDoc: String = _mainDoc
/** The parameters identified by @param. Maps from parameter name to its documentation. */
lazy val argDocs: Map[String, String] = _argDocs

private var _mainDoc: String = ""
private var _argDocs: Map[String, String] = Map()

docComment match {
case Some(comment) => if comment.isDocComment then parseDocComment(comment.raw) else _mainDoc = comment.raw
case None =>
}

private def cleanComment(raw: String): String =
var lines: Seq[String] = raw.trim.split('\n').toSeq
lines = lines.map(l => l.substring(skipLineLead(l, -1), l.length).trim)
var s = lines.foldLeft("") {
case ("", s2) => s2
case (s1, "") if s1.last == '\n' => s1 // Multiple newlines are kept as single newlines
case (s1, "") => s1 + '\n'
case (s1, s2) if s1.last == '\n' => s1 + s2
case (s1, s2) => s1 + ' ' + s2
}
s.replaceAll(raw"\[\[", "").replaceAll(raw"\]\]", "").trim

private def parseDocComment(raw: String): Unit =
// Positions of the sections (@) in the docstring
val tidx: List[(Int, Int)] = tagIndex(raw)

// Parse main comment
var mainComment: String = raw.substring(skipLineLead(raw, 0), startTag(raw, tidx))
_mainDoc = cleanComment(mainComment)

// Parse arguments comments
val argsCommentsSpans: Map[String, (Int, Int)] = paramDocs(raw, "@param", tidx)
val argsCommentsTextSpans = argsCommentsSpans.view.mapValues(extractSectionText(raw, _))
val argsCommentsTexts = argsCommentsTextSpans.mapValues({ case (beg, end) => raw.substring(beg, end) })
_argDocs = argsCommentsTexts.mapValues(cleanComment(_)).toMap
end Documentation
}
Loading