summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorTimothee Cour <timothee.cour2@gmail.com>2020-07-01 00:26:23 -0700
committerGitHub <noreply@github.com>2020-07-01 09:26:23 +0200
commit2867a33ebcaddccdb52b101d0ea5f75f1d4bd66f (patch)
treea00dc63b69e03065bb88d1b246d6c8f9ebc12f50
parent05384efec53256d8917a80d1117d8f822a8a2f55 (diff)
downloadNim-2867a33ebcaddccdb52b101d0ea5f75f1d4bd66f.tar.gz
fix #14846; add macros.extractDocCommentsAndRunnables (#14849)
* fix #14846; add macros.extractDocCommentsAndRunnables

* fixup

* update tests

* address comment
-rw-r--r--changelog.md1
-rw-r--r--lib/core/macros.nim36
-rw-r--r--lib/pure/asyncmacro.nim6
-rw-r--r--nimdoc/testproject/expected/testproject.html29
-rw-r--r--nimdoc/testproject/expected/testproject.idx3
-rw-r--r--nimdoc/testproject/expected/theindex.html12
-rw-r--r--nimdoc/testproject/testproject.nim14
7 files changed, 98 insertions, 3 deletions
diff --git a/changelog.md b/changelog.md
index f32d6f19f..287bce3da 100644
--- a/changelog.md
+++ b/changelog.md
@@ -116,6 +116,7 @@
 - Add `random.gauss`, that uses the ratio of uniforms method of sampling from a Gaussian distribution.
 - Add `typetraits.elementType` to get element type of an iterable.
 - `typetraits.$`: `$(int,)` is now `"(int,)"`; `$tuple[]` is now `"tuple[]"`
+- add `macros.extractDocCommentsAndRunnables` helper
 
 - `strformat.fmt` and `strformat.&` support `= specifier`. `fmt"{expr=}"` now expands to `fmt"expr={expr}"`.
 
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 5be3a2cce..838707280 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -1692,3 +1692,39 @@ when defined(nimMacrosSizealignof):
 
 proc isExported*(n: NimNode): bool {.noSideEffect.} =
   ## Returns whether the symbol is exported or not.
+
+proc extractDocCommentsAndRunnables*(n: NimNode): NimNode =
+  ## returns a `nnkStmtList` containing the top-level doc comments and
+  ## runnableExamples in `a`, stopping at the first child that is neither.
+  ## Example:
+  ##
+  ## .. code-block:: nim
+  ##  import macros
+  ##  macro transf(a): untyped =
+  ##    result = quote do:
+  ##      proc fun2*() = discard
+  ##    let header = extractDocCommentsAndRunnables(a.body)
+  ##    # correct usage: rest is appended
+  ##    result.body = header
+  ##    result.body.add quote do: discard # just an example
+  ##    # incorrect usage: nesting inside a nnkStmtList:
+  ##    # result.body = quote do: (`header`; discard)
+  ##
+  ##  proc fun*() {.transf.} =
+  ##    ## first comment
+  ##    runnableExamples: discard
+  ##    runnableExamples: discard
+  ##    ## last comment
+  ##    discard # first statement after doc comments + runnableExamples
+  ##    ## not docgen'd
+
+  result = newStmtList()
+  for ni in n:
+    case ni.kind
+    of nnkCommentStmt:
+      result.add ni
+    of nnkCall:
+      if ni[0].kind == nnkIdent and ni[0].strVal == "runnableExamples":
+        result.add ni
+      else: break
+    else: break
diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim
index 621a4b00c..219ef6c67 100644
--- a/lib/pure/asyncmacro.nim
+++ b/lib/pure/asyncmacro.nim
@@ -180,8 +180,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
   # Extract the documentation comment from the original procedure declaration.
   # Note that we're not removing it from the body in order not to make this
   # transformation even more complex.
-  if prc.body.len > 1 and prc.body[0].kind == nnkCommentStmt:
-    outerProcBody.add(prc.body[0])
+  let body2 = extractDocCommentsAndRunnables(prc.body)
 
   # -> var retFuture = newFuture[T]()
   var retFutureSym = genSym(nskVar, "retFuture")
@@ -276,9 +275,10 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
       (cast[type(f)](internalTmpFuture)).read()
 
   if procBody.kind != nnkEmpty:
-    result.body = quote:
+    body2.add quote do:
       `awaitDefinition`
       `outerProcBody`
+    result.body = body2
 
   #echo(treeRepr(result))
   #if prcName == "recvLineInto":
diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html
index a4f8876fd..648ecb629 100644
--- a/nimdoc/testproject/expected/testproject.html
+++ b/nimdoc/testproject/expected/testproject.html
@@ -200,6 +200,12 @@ function main() {
     title="low2[T: Ordinal | enum | range](x: T): T"><wbr />low2<span class="attachedType"></span></a></li>
   <li><a class="reference" href="#tripleStrLitTest"
     title="tripleStrLitTest()"><wbr />triple<wbr />Str<wbr />Lit<wbr />Test<span class="attachedType"></span></a></li>
+  <li><a class="reference" href="#asyncFun1"
+    title="asyncFun1(): Future[int]"><wbr />async<wbr />Fun1<span class="attachedType"></span></a></li>
+  <li><a class="reference" href="#asyncFun2"
+    title="asyncFun2(): owned(Future[void])"><wbr />async<wbr />Fun2<span class="attachedType"></span></a></li>
+  <li><a class="reference" href="#asyncFun3"
+    title="asyncFun3(): owned(Future[void])"><wbr />async<wbr />Fun3<span class="attachedType"></span></a></li>
 
   </ul>
 </li>
@@ -656,6 +662,29 @@ at indent 0
 </span><span class="Comment"># should be in</span></pre>
 
 </dd>
+<a id="asyncFun1"></a>
+<dt><pre><span class="Keyword">proc</span> <a href="#asyncFun1"><span class="Identifier">asyncFun1</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">int</span><span class="Other">]</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
+<dd>
+
+ok1
+
+</dd>
+<a id="asyncFun2"></a>
+<dt><pre><span class="Keyword">proc</span> <a href="#asyncFun2"><span class="Identifier">asyncFun2</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">owned</span><span class="Other">(</span><span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">void</span><span class="Other">]</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
+<dd>
+
+
+
+</dd>
+<a id="asyncFun3"></a>
+<dt><pre><span class="Keyword">proc</span> <a href="#asyncFun3"><span class="Identifier">asyncFun3</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">owned</span><span class="Other">(</span><span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">void</span><span class="Other">]</span><span class="Other">)</span> <span><span class="Other">{</span><span class="Other pragmadots">...</span><span class="Other">}</span></span><span class="pragmawrap"><span class="Other">{.</span><span class="pragma"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span></span><span class="Other">.}</span></span></pre></dt>
+<dd>
+
+
+<p><strong class="examples_text">Example:</strong></p>
+<pre class="listing"><span class="Keyword">discard</span></pre>ok1
+
+</dd>
 
 </dl></div>
 <div class="section" id="13">
diff --git a/nimdoc/testproject/expected/testproject.idx b/nimdoc/testproject/expected/testproject.idx
index 6d0bc6c5e..0b75b29d2 100644
--- a/nimdoc/testproject/expected/testproject.idx
+++ b/nimdoc/testproject/expected/testproject.idx
@@ -50,4 +50,7 @@ foo	testproject.html#foo.t,SomeType,SomeType	testproject: foo(a, b: SomeType)
 myfn	testproject.html#myfn.t	testproject: myfn()	
 z14	testproject.html#z14.t	testproject: z14()	
 z15	testproject.html#z15.t	testproject: z15()	
+asyncFun1	testproject.html#asyncFun1	testproject: asyncFun1(): Future[int]	
+asyncFun2	testproject.html#asyncFun2	testproject: asyncFun2(): owned(Future[void])	
+asyncFun3	testproject.html#asyncFun3	testproject: asyncFun3(): owned(Future[void])	
 testNimDocTrailingExample	testproject.html#testNimDocTrailingExample.t	testproject: testNimDocTrailingExample()	
diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html
index 8d34506a6..0b835c409 100644
--- a/nimdoc/testproject/expected/theindex.html
+++ b/nimdoc/testproject/expected/theindex.html
@@ -85,6 +85,18 @@ function main() {
 <li><a class="reference external"
           data-doc-search-tag="utils: aEnum(): untyped" href="subdir/subdir_b/utils.html#aEnum.t">utils: aEnum(): untyped</a></li>
           </ul></dd>
+<dt><a name="asyncFun1" href="#asyncFun1"><span>asyncFun1:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: asyncFun1(): Future[int]" href="testproject.html#asyncFun1">testproject: asyncFun1(): Future[int]</a></li>
+          </ul></dd>
+<dt><a name="asyncFun2" href="#asyncFun2"><span>asyncFun2:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: asyncFun2(): owned(Future[void])" href="testproject.html#asyncFun2">testproject: asyncFun2(): owned(Future[void])</a></li>
+          </ul></dd>
+<dt><a name="asyncFun3" href="#asyncFun3"><span>asyncFun3:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: asyncFun3(): owned(Future[void])" href="testproject.html#asyncFun3">testproject: asyncFun3(): owned(Future[void])</a></li>
+          </ul></dd>
 <dt><a name="aVariable" href="#aVariable"><span>aVariable:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
           data-doc-search-tag="testproject: aVariable" href="testproject.html#aVariable">testproject: aVariable</a></li>
diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim
index 5282c6f77..25cdf39a4 100644
--- a/nimdoc/testproject/testproject.nim
+++ b/nimdoc/testproject/testproject.nim
@@ -343,6 +343,20 @@ when true: # issue #14473
     toSeq([1,2])
   echo doit() # using doAssert or similar to avoid echo would "hide" the original bug
 
+when true: # issue #14846
+  import asyncdispatch
+  proc asyncFun1*(): Future[int] {.async.} =
+    ## ok1
+    result = 1
+  proc asyncFun2*() {.async.} = discard
+  proc asyncFun3*() {.async.} =
+    runnableExamples:
+      discard
+    ## ok1
+    discard
+    ## should be out
+    discard
+
 when true:
   template testNimDocTrailingExample*() =
     # this must be last entry in this file, it checks against a bug (that got fixed)