run runnableExamples in the module scope (#11732) [feature]

This commit is contained in:
Timothee Cour 2019-07-22 07:22:57 -07:00 • committed by Andreas Rumpf
commit 8c93c692b9
4 changed files with 81 additions and 17 deletions

View file

@ -415,12 +415,6 @@ proc runAllExamples(d: PDoc) =
rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp.string]) rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp.string])
removeFile(outp.changeFileExt(ExeExt)) removeFile(outp.changeFileExt(ExeExt))
proc extractImports(n: PNode; result: PNode) =
if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}:
result.add copyTree(n)
n.kind = nkEmpty
return
for i in 0..<n.safeLen: extractImports(n[i], result)
proc prepareExamples(d: PDoc; n: PNode) = proc prepareExamples(d: PDoc; n: PNode) =
var docComment = newTree(nkCommentStmt) var docComment = newTree(nkCommentStmt)
@ -430,12 +424,20 @@ proc prepareExamples(d: PDoc; n: PNode) =
docComment, docComment,
newTree(nkImportStmt, newStrNode(nkStrLit, d.filename))) newTree(nkImportStmt, newStrNode(nkStrLit, d.filename)))
runnableExamples.info = n.info runnableExamples.info = n.info
let imports = newTree(nkStmtList) for a in n.lastSon: runnableExamples.add a
var savedLastSon = copyTree n.lastSon
extractImports(savedLastSon, imports)
for imp in imports: runnableExamples.add imp
runnableExamples.add newTree(nkBlockStmt, newNode(nkEmpty), copyTree savedLastSon)
testExample(d, runnableExamples) testExample(d, runnableExamples)
when false:
proc extractImports(n: PNode; result: PNode) =
if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}:
result.add copyTree(n)
n.kind = nkEmpty
return
for i in 0..<n.safeLen: extractImports(n[i], result)
let imports = newTree(nkStmtList)
var savedLastSon = copyTree n.lastSon
extractImports(savedLastSon, imports)
for imp in imports: runnableExamples.add imp
runnableExamples.add newTree(nkBlockStmt, newNode(nkEmpty), copyTree savedLastSon)
proc getAllRunnableExamplesRec(d: PDoc; n, orig: PNode; dest: var Rope) = proc getAllRunnableExamplesRec(d: PDoc; n, orig: PNode; dest: var Rope) =
if n.info.fileIndex != orig.info.fileIndex: return if n.info.fileIndex != orig.info.fileIndex: return

View file

@ -198,8 +198,8 @@ proc initUri*(): Uri =
## **See also:** ## **See also:**
## * `Uri type <#Uri>`_ for available fields in the URI type ## * `Uri type <#Uri>`_ for available fields in the URI type
runnableExamples: runnableExamples:
var uri: Uri var uri2: Uri
assert initUri() == uri assert initUri() == uri2
result = Uri(scheme: "", username: "", password: "", hostname: "", port: "", result = Uri(scheme: "", username: "", password: "", hostname: "", port: "",
path: "", query: "", anchor: "") path: "", query: "", anchor: "")

View file

@ -128,7 +128,7 @@ when defined(nimHasRunnableExamples):
## a ``runnableExamples`` section is ignored. ## a ``runnableExamples`` section is ignored.
## - The documentation generator is aware of these examples and considers them ## - The documentation generator is aware of these examples and considers them
## part of the ``##`` doc comment. As the last step of documentation ## part of the ``##`` doc comment. As the last step of documentation
## generation the examples are put into an ``$file_example.nim`` file, ## generation each runnableExample is put in its own file ``$file_examples$i.nim``,
## compiled and tested. The collected examples are ## compiled and tested. The collected examples are
## put into their own module to ensure the examples do not refer to ## put into their own module to ensure the examples do not refer to
## non-exported symbols. ## non-exported symbols.
@ -136,11 +136,13 @@ when defined(nimHasRunnableExamples):
## Usage: ## Usage:
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## proc double(x: int): int = ## proc double*(x: int): int =
## ## This proc doubles a number. ## ## This proc doubles a number.
## runnableExamples: ## runnableExamples:
## ## at module scope
## assert double(5) == 10 ## assert double(5) == 10
## assert double(21) == 42 ## block: ## at block scope
## defer: echo "done"
## ##
## result = 2 * x ## result = 2 * x
else: else:

View file

@ -0,0 +1,60 @@
discard """
cmd: "nim doc $file"
action: "compile"
nimout: '''
foo1
foo2
foo3
foo5
foo6
foo7
foo8
foo9
'''
joinable: false
"""
proc fun*() =
runnableExamples:
block: # `defer` only allowed inside a block
defer: echo "foo1"
runnableExamples:
# `fun*` only allowed at top level
proc fun*()=echo "foo2"
fun()
block:
defer: echo "foo3"
runnableExamples:
# ditto
proc fun*()=echo "foo5"
fun()
runnableExamples:
# `codeReordering` only allowed at top level
{.experimental: "codeReordering".}
proc fun1() = fun2()
proc fun2() = echo "foo6"
fun1()
runnableExamples:
# only works at top level
import std/macros
macro myImport(a: static string): untyped =
newTree(nnkImportStmt, [newLit a])
myImport "str" & "utils"
doAssert declared(isAlphaAscii)
echo "foo7"
# also check for runnableExamples at module scope
runnableExamples:
block:
defer: echo "foo8"
runnableExamples:
proc fun*()=echo "foo9"
fun()
# note: there are yet other examples where putting runnableExamples at module
# scope is needed, for example when using an `include` before an `import`, etc.