Repository: scalameta/scalameta
Pull request #2533 (2021-12-31 17:41:10)

ScaladocParser: in listBlockParser, explain indent
Test borrowed from #2489, but the parser behaviour is intentionally unchanged.


Patch:
diff --git a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScaladocParser.scala b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScaladocParser.scala
index fbe38973f..5216d318c 100644
--- a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScaladocParser.scala
+++ b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScaladocParser.scala
@@ -148,20 +148,21 @@ object ScaladocParser {
     }
   }
 
-  private def listBlockParser[_: P](minIndent: Int = 1): P[ListBlock] = P {
-    def listParser = (hspacesMinWithLen(minIndent) ~ listPrefix.! ~ hspaces1).flatMap {
-      case (indent, prefix) =>
-        def sep = (nl ~ hspacesMinWithLen(indent) ~ prefix ~ hspaces1).flatMap { x =>
-          if (x != indent) Fail else Pass
-        }
-        (textParser ~ listBlockParser(indent + 1).?)
-          .map { case (desc, list) => ListItem(desc, list) }
-          .rep(1, sep = sep)
-          .map(x => ListBlock(prefix, x))
+  private def listBlockParser[_: P](indent: Int = 0): P[ListBlock] = P {
+    /* https://docs.scala-lang.org/overviews/scaladoc/for-library-authors.html#other-formatting-notes
+     * For list blocks, "you must have extra space in front", hence min `indent + 1`. */
+    def listMarker = hspacesMinWithLen(indent + 1) ~ listPrefix.! ~ hspaces1
+    def listParser = listMarker.flatMap { case (listIndent, prefix) =>
+      def sep = nl ~ hspace.rep(exactly = listIndent) ~ prefix ~ hspaces1
+      listItemParser(listIndent).rep(1, sep = sep).map(x => ListBlock(prefix, x))
     }
     startOrNl ~ listParser
   }
 
+  private def listItemParser[_: P](indent: Int) = P {
+    (textParser ~ listBlockParser(indent).?).map { case (desc, list) => ListItem(desc, list) }
+  }
+
   private def tableParser[_: P]: P[Table] = P {
     def toRow(x: Seq[String]): Table.Row = Table.Row(x)
     def toAlign(x: String): Option[Table.Align] = {
diff --git a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala
index eb836889b..91bca25f1 100644
--- a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala
+++ b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala
@@ -707,7 +707,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
    * When token on `tokenPosition` is not a whitespace and is
    * a first non-whitespace character in a current line then a result is
    * a number of whitespace characters counted.
-   * Otherwise (-1, -1) is returned.
+   * Otherwise {{{(-1, -1)}}} is returned.
    *
    * Returns a tuple2 where:
    * - first value is indentation level
@@ -2334,7 +2334,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
   }
 
   /**
-   * Deals with Scala 3 concept of `inline x match { ...`
+   * Deals with Scala 3 concept of {{{inline x match { ...}}}.
    * Since matches can also be chained in Scala 3 we need to create
    * the Match first and only then add the the inline modifier.
    */
@@ -4281,10 +4281,12 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
   }
 
   /**
+   * ```scala
    * Given             ::= 'given' GivenDef
    * GivenDef          ::=  [GivenSig] (Type [‘=’ Expr] | StructuralInstance)
    * GivenSig          ::=  [id] [DefTypeParamClause] {UsingParamClause} ‘:’
    * StructuralInstance ::=  ConstrApp {‘with’ ConstrApp} ‘with’ TemplateBody
+   * ```
    */
   private def givenDecl(mods: List[Mod]): Stat = atPos(mods, auto) {
     accept[KwGiven]
@@ -4316,7 +4318,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
          * We are first trying to parse non-anonymous given, which
          *  - requires `:` for type, if none is found it means it's anonymous
          *  - might fail in cases that anonymous type looks like type param
-         *   `given Conversion[AppliedName, Expr.Apply[Id]] = ???`
+         *   {{{given Conversion[AppliedName, Expr.Apply[Id]] = ???}}}
          *   This will fail because type params cannot have `.`
          */
         case NonFatal(_) =>
diff --git a/scalameta/tokenizers/shared/src/main/scala/scala/meta/internal/tokenizers/Chars.scala b/scalameta/tokenizers/shared/src/main/scala/scala/meta/internal/tokenizers/Chars.scala
index d26059109..59694c828 100644
--- a/scalameta/tokenizers/shared/src/main/scala/scala/meta/internal/tokenizers/Chars.scala
+++ b/scalameta/tokenizers/shared/src/main/scala/scala/meta/internal/tokenizers/Chars.scala
@@ -127,7 +127,7 @@ object Chars {
    *  NameStart ::= ( Letter | '_' )
    *  }}}
    *  where Letter means in one of the Unicode general
-   *  categories `{ Ll, Lu, Lo, Lt, Nl }`.
+   *  categories {{{ Ll, Lu, Lo, Lt, Nl }}}.
    *
    *  We do not allow a name to start with ':'.
    *  See [3] and Appendix B of XML 1.0 specification
diff --git a/scalameta/tokenizers/shared/src/main/scala/scala/meta/internal/tokenizers/XmlParser.scala b/scalameta/tokenizers/shared/src/main/scala/scala/meta/internal/tokenizers/XmlParser.scala
index 3a54ce428..b94dd91cc 100644
--- a/scalameta/tokenizers/shared/src/main/scala/scala/meta/internal/tokenizers/XmlParser.scala
+++ b/scalameta/tokenizers/shared/src/main/scala/scala/meta/internal/tokenizers/XmlParser.scala
@@ -116,7 +116,7 @@ class XmlParser(dialect: Dialect) {
      *  NameStart ::= ( Letter | '_' )
      *  }}}
      *  where Letter means in one of the Unicode general
-     *  categories `{ Ll, Lu, Lo, Lt, Nl }`.
+     *  categories {{{ Ll, Lu, Lo, Lt, Nl }}}.
      *
      *  We do not allow a name to start with `:`.
      *  See [3] and Appendix B of XML 1.0 specification
diff --git a/scalameta/trees/shared/src/main/scala/scala/meta/internal/Scaladoc.scala b/scalameta/trees/shared/src/main/scala/scala/meta/internal/Scaladoc.scala
index 27ce0144d..1d8d9239a 100644
--- a/scalameta/trees/shared/src/main/scala/scala/meta/internal/Scaladoc.scala
+++ b/scalameta/trees/shared/src/main/scala/scala/meta/internal/Scaladoc.scala
@@ -12,7 +12,7 @@ object Scaladoc {
   sealed abstract class Term
 
   /** A single paragraph of the document */
-  final case class Paragraph(term: Seq[Term])
+  final case class Paragraph(terms: Seq[Term])
 
   /* Text */
 
@@ -22,7 +22,7 @@ object Scaladoc {
   }
 
   /** A description or other inline text block */
-  final case class Text(part: Seq[TextPart]) extends Term
+  final case class Text(parts: Seq[TextPart]) extends Term
 
   /** A single word, without whitespace */
   final case class Word(value: String) extends TextPart {
@@ -62,12 +62,12 @@ object Scaladoc {
   final case class Table(
       header: Table.Row,
       align: Seq[Table.Align],
-      row: Seq[Table.Row]
+      rows: Seq[Table.Row]
   ) extends Term
 
   object Table {
 
-    final case class Row(col: Seq[String])
+    final case class Row(cols: Seq[String])
 
     sealed abstract class Align {
       def leftPad(pad: Int): Int
@@ -100,7 +100,7 @@ object Scaladoc {
   final case class ListItem(text: Text, nested: Option[ListBlock] = None)
 
   /** Represents a list block */
-  final case class ListBlock(prefix: String, item: Seq[ListItem]) extends Term
+  final case class ListBlock(prefix: String, items: Seq[ListItem]) extends Term
 
   /* Tags */
 


Test patch:
diff --git a/scalameta/testkit/jvm/src/main/scala/scala/meta/testkit/Corpus.scala b/scalameta/testkit/jvm/src/main/scala/scala/meta/testkit/Corpus.scala
index ee878c70c..4493cd076 100644
--- a/scalameta/testkit/jvm/src/main/scala/scala/meta/testkit/Corpus.scala
+++ b/scalameta/testkit/jvm/src/main/scala/scala/meta/testkit/Corpus.scala
@@ -13,7 +13,12 @@ import org.rauschig.jarchivelib.ArchiverFactory
 /**
  * A collection of Scala source files to run [[SyntaxAnalysis]].
  *
- * @param url A zip file that matches the following structure:
+ * @param url A zip file that matches the structure described below.
+ * @param filter Files that don't match the filter are excluded from the
+ *               analysis.
+ *
+ * Here's the structure for `url` files:
+ * {{{
  *            repos/
  *              project1/
  *                COMMIT // <- git commit hash of project1 snapshot
@@ -23,11 +28,10 @@ import org.rauschig.jarchivelib.ArchiverFactory
  *              project2/
  *              ...
  *              projectN/
+ * }}}
  *            In particular, each project directory must contain a
  *            `COMMIT` and `URL` file. These files are used to construct
  *            links to the source files on Github.
- * @param filter Files that don't match the filter are excluded from the
- *               analysis.
  */
 case class Corpus(url: String, filter: String => Boolean)
 
@@ -36,7 +40,7 @@ object Corpus {
   /**
    * Corpus of ~3 million lines of code, 22 mb compressed.
    * The list of repos is originally taken from fastparse:
-   * https://github.com/lihaoyi/fastparse/blob/6cf2cb23cd5f628c4e956d5846228ee4ca988f5c/scalaparse/jvm/src/test/scala/scalaparse/ProjectTests.scala#L63-L115
+   * [[https://github.com/lihaoyi/fastparse/blob/6cf2cb23cd5f628c4e956d5846228ee4ca988f5c/scalaparse/jvm/src/test/scala/scalaparse/ProjectTests.scala#L63-L115]]
    */
   val fastparse = Corpus(
     url = "https://github.com/scalameta/scalafmt/releases/download/v0.1.4/repos.tar.gz",
diff --git a/scalameta/testkit/jvm/src/test/scala/scala/meta/testkit/ScalametaParserProperties.scala b/scalameta/testkit/jvm/src/test/scala/scala/meta/testkit/ScalametaParserProperties.scala
index 6e9450f02..a70cb8e29 100644
--- a/scalameta/testkit/jvm/src/test/scala/scala/meta/testkit/ScalametaParserProperties.scala
+++ b/scalameta/testkit/jvm/src/test/scala/scala/meta/testkit/ScalametaParserProperties.scala
@@ -20,7 +20,7 @@ object ScalametaParserProperties {
 
   /**
    * The following property did not hold:
-   * syntheticTree.structure == synthethicTree.syntax.parse.structure
+   * {{{syntheticTree.structure == synthethicTree.syntax.parse.structure}}}
    */
   case object PrettyPrinterBroken extends BugKind
 
diff --git a/scalameta/testkit/shared/src/main/scala/scala/meta/testkit/StructurallyEqual.scala b/scalameta/testkit/shared/src/main/scala/scala/meta/testkit/StructurallyEqual.scala
index 5d653c63d..725055f45 100644
--- a/scalameta/testkit/shared/src/main/scala/scala/meta/testkit/StructurallyEqual.scala
+++ b/scalameta/testkit/shared/src/main/scala/scala/meta/testkit/StructurallyEqual.scala
@@ -6,10 +6,10 @@ object StructurallyEqual {
 
   /**
    * Test if two trees are structurally equal.
-   * @return Left(errorMessage with minimal diff) if trees are not structurally
-   *         different, otherwise Right(Unit). To convert into exception with
+   * @return {{{Left(errorMessage with minimal diff)}}} if trees are not structurally
+   *         different, otherwise {{{Right(Unit)}}}. To convert into exception with
    *         meaningful error message,
-   *         val Right(_) = StructurallyEqual(a, b)
+   *         use {{{val Right(_) = StructurallyEqual(a, b)}}}
    */
   def apply(a: Tree, b: Tree): Either[AnyDiff, Unit] = {
     def loop(x: Any, y: Any): Boolean = {
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/BasePositionSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/BasePositionSuite.scala
index 2069818ae..0db713281 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/BasePositionSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/BasePositionSuite.scala
@@ -45,9 +45,9 @@ abstract class BasePositionSuite(defaultDialect: Dialect) extends ParseSuite {
    *   )
    * }}}
    *
-   * Observe that the line "Name.Anonymous {" indicates that the position of the
-   * anonymous name encloses the "{" token. The correct output should be
-   * "Name.Anonymous trait A @@{ self: B => }".
+   * Observe that the line {{{Name.Anonymous {}}} indicates that the position of the
+   * anonymous name encloses the `{` token. The correct output should be
+   * {{{Name.Anonymous trait A @@{ self: B =>}}}.
    */
   def checkPositions[T <: Tree: Parse](
       code: TestOptions,
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/ScaladocParserSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/ScaladocParserSuite.scala
index 620b04c5e..4ecd5dd91 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/ScaladocParserSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/ScaladocParserSuite.scala
@@ -433,6 +433,36 @@ class ScaladocParserSuite extends FunSuite {
     assertEquals(result, expectation)
   }
 
+  test("list - no leading spaces") {
+    val list11 = "List11"
+    val list12 = "List12"
+
+    val result =
+      parseString(
+        s"""
+        /**
+          * Some text:
+          *- $list11
+          *- $list12
+          */
+         """
+      )
+    val expected = Option(
+      Scaladoc(
+        Seq(
+          Paragraph(
+            Seq(
+              Text(Seq(Word("Some"), Word("text:"))),
+              Text(Seq(Word("-"), Word(list11))),
+              Text(Seq(Word("-"), Word(list12)))
+            )
+          )
+        )
+      )
+    )
+    assertEquals(result, expected)
+  }
+
   test("lists 1") {
     val list11 = "List11"
     val list12 = "List12"
@@ -681,7 +711,7 @@ class ScaladocParserSuite extends FunSuite {
     assertNotEquals(parsedScaladocOpt, None: Option[Scaladoc])
     val parsedScaladocParas = parsedScaladocOpt.get.para
     assertEquals(parsedScaladocParas.length, 1)
-    val parsedScaladocTerms = parsedScaladocParas.head.term
+    val parsedScaladocTerms = parsedScaladocParas.head.terms
 
     assertEquals(
       parsedScaladocTerms.count {
@@ -715,7 +745,7 @@ class ScaladocParserSuite extends FunSuite {
     // Inherit doc does not merge
     parsedScaladocTerms.foreach {
       case t: Tag if t.tag.optDesc && t.desc.isDefined =>
-        assertEquals(t.desc.get.part.takeRight(words.length), words)
+        assertEquals(t.desc.get.parts.takeRight(words.length), words)
       case _ =>
     }
   }
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/AndOrTypesSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/AndOrTypesSuite.scala
index 127eac145..1625eccba 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/AndOrTypesSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/AndOrTypesSuite.scala
@@ -7,9 +7,9 @@ import scala.meta._, Type._
 class AndOrTypesSuite extends BaseDottySuite {
 
   /**
-   *  All examples based on dotty documentation:
-   *  https://dotty.epfl.ch/docs/reference/new-types/intersection-types.html
-   *  https://dotty.epfl.ch/docs/reference/new-types/union-types.html
+   * All examples based on dotty documentation:
+   * - [[https://dotty.epfl.ch/docs/reference/new-types/intersection-types.html]]
+   * - [[https://dotty.epfl.ch/docs/reference/new-types/union-types.html]]
    */
   test("view bounds not allowed") {
     intercept[ParseException] {
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/EnumSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/EnumSuite.scala
index ca61d946a..3b759abbe 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/EnumSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/EnumSuite.scala
@@ -7,9 +7,9 @@ import scala.meta.tests.tokenizers.TokenizerSuite
 class EnumSuite extends BaseDottySuite {
 
   /**
-   *  All examples based on dotty documentation:
-   *  https://dotty.epfl.ch/docs/reference/enums/enums.html
-   *  https://dotty.epfl.ch/docs/reference/enums/adts.html
+   * All examples based on dotty documentation:
+   * - [[https://dotty.epfl.ch/docs/reference/enums/enums.html]]
+   * - [[https://dotty.epfl.ch/docs/reference/enums/adts.html]]
    */
   // ---------------------------------
   // ENUM
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/ExtensionMethodsSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/ExtensionMethodsSuite.scala
index 0ca26e5bc..9161326c4 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/ExtensionMethodsSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/ExtensionMethodsSuite.scala
@@ -9,10 +9,12 @@ class ExtensionMethodsSuite extends BaseDottySuite {
 
   /**
    * For checking examples in repl declare:
+   * {{{
    *  case class Circle(x: Int)
+   * }}}
    *
    *  All examples based on dotty documentation:
-   *  https://dotty.epfl.ch/docs/reference/contextual/extension-methods.html
+   * - [[https://dotty.epfl.ch/docs/reference/contextual/extension-methods.html]]
    */
 
   test("simple-method") {
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/GivenUsingSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/GivenUsingSuite.scala
index 6a72aa8c4..3d222831f 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/GivenUsingSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/GivenUsingSuite.scala
@@ -9,12 +9,14 @@ class GivenUsingSuite extends BaseDottySuite {
 
   /**
    * For checking examples in repl declare:
+   * {{{
    *  trait Ord[T] { def f(): Int }
+   * }}}
    *
    *  All examples based on dotty documentation:
-   *  https://dotty.epfl.ch/docs/reference/contextual/givens.html
-   *  https://dotty.epfl.ch/docs/reference/contextual/using-clauses.html
-   *  https://dotty.epfl.ch/docs/reference/contextual/given-imports.html
+   * - [[https://dotty.epfl.ch/docs/reference/contextual/givens.html]]
+   * - [[https://dotty.epfl.ch/docs/reference/contextual/using-clauses.html]]
+   * - [[https://dotty.epfl.ch/docs/reference/contextual/given-imports.html]]
    */
   // ---------------------------------
   // GIVEN
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/InlineSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/InlineSuite.scala
index e379e73d6..213cdf998 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/InlineSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/InlineSuite.scala
@@ -9,7 +9,7 @@ class InlineSuite extends BaseDottySuite {
 
   /**
    *  All examples based on dotty documentation:
-   *  https://dotty.epfl.ch/docs/reference/metaprogramming/inline.html
+   * - [[https://dotty.epfl.ch/docs/reference/metaprogramming/inline.html]]
    */
   test("inline-soft-keyword-pos") {
     // as ident
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MacroSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MacroSuite.scala
index dad21f099..94deec2b9 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MacroSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MacroSuite.scala
@@ -9,13 +9,14 @@ class MacroSuite extends BaseDottySuite {
 
   /**
    * All examples based on dotty documentation:
-   * https://dotty.epfl.ch/docs/reference/metaprogramming/toc.html
-   * https://dotty.epfl.ch/docs/reference/metaprogramming/macros.html
-   * https://dotty.epfl.ch/docs/reference/metaprogramming/tasty-reflect.html
+   * - [[https://dotty.epfl.ch/docs/reference/metaprogramming/toc.html]]
+   * - [[https://dotty.epfl.ch/docs/reference/metaprogramming/macros.html]]
+   * - [[https://dotty.epfl.ch/docs/reference/metaprogramming/tasty-reflect.html]]
    *
-   * - '{ ... } OR 'ident - QuotedMacroExpr
-   * - '[ ... ] - QuotedMacroType
-   * - ${ ... } OR $ident -  SplicedMacroExpr
+   * Variants:
+   * - `QuotedMacroType`: `'[ ... ]`
+   * - `QuotedMacroExpr`: `'{ ... }` OR `'ident`
+   * - `SplicedMacroExpr`: `${ ... }` OR $ident`
    */
   test("parse-single-quote-character") {
     runTestAssert[Stat]("val a = 'c'")(
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MinorDottySuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MinorDottySuite.scala
index d3d17ab09..3604bf998 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MinorDottySuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MinorDottySuite.scala
@@ -9,10 +9,10 @@ class MinorDottySuite extends BaseDottySuite {
 
   /**
    *  All examples based on dotty documentation:
-   *  https://dotty.epfl.ch/docs/reference/other-new-features/open-classes.html
-   *  https://dotty.epfl.ch/docs/reference/new-types/type-lambdas.html
-   *  https://dotty.epfl.ch/docs/reference/other-new-features/trait-parameters.html
-   *  https://dotty.epfl.ch/docs/reference/changed-features/wildcards.html
+   * - [[https://dotty.epfl.ch/docs/reference/other-new-features/open-classes.html]]
+   * - [[https://dotty.epfl.ch/docs/reference/new-types/type-lambdas.html]]
+   * - [[https://dotty.epfl.ch/docs/reference/other-new-features/trait-parameters.html]]
+   * - [[https://dotty.epfl.ch/docs/reference/changed-features/wildcards.html]]
    */
   test("open-class") {
     val Defn.Class(List(Mod.Open()), Type.Name("A"), _, _, _) =