summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2018-09-02 22:56:26 +0200
committerAndreas Rumpf <rumpf_a@web.de>2018-09-02 22:56:26 +0200
commit1948eadc24c8964b581240dc841e4eb369a1ad36 (patch)
tree5c9950326820b1ec845f4041c60ef14ed3abef8c /compiler
parent4cf704bb3e26e002c5ff479a01fffcd3e33f1cb2 (diff)
downloadNim-1948eadc24c8964b581240dc841e4eb369a1ad36.tar.gz
change runnableExamples implementation; fixes #8641; fixes #7135; runnableExamples works for templates and generics
Diffstat (limited to 'compiler')
-rw-r--r--compiler/docgen.nim59
-rw-r--r--compiler/docgen2.nim1
-rw-r--r--compiler/sem.nim23
-rw-r--r--compiler/semdata.nim1
-rw-r--r--compiler/semexprs.nim28
5 files changed, 70 insertions, 42 deletions
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 3842f0ad4..2910f2253 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -31,6 +31,7 @@ type
     isPureRst: bool
     conf*: ConfigRef
     cache*: IdentCache
+    runnableExamples*: PNode
 
   PDoc* = ref TDocumentor ## Alias to type less.
 
@@ -284,11 +285,60 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe
       dispA(d.conf, result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
             [rope(esc(d.target, literal))])
 
+proc testExamples*(d: PDoc) =
+  if d.runnableExamples == nil: return
+  let outputDir = d.conf.getNimcacheDir / "runnableExamples"
+  createDir(outputDir)
+  let inp = toFullPath(d.conf, d.runnableExamples.info)
+  let outp = outputDir / extractFilename(inp.changeFileExt"" & "_examples.nim")
+  let nimcache = outp.changeFileExt"" & "_nimcache"
+  renderModule(d.runnableExamples, inp, outp, conf = d.conf)
+  let backend = if isDefined(d.conf, "js"): "js"
+                elif isDefined(d.conf, "cpp"): "cpp"
+                elif isDefined(d.conf, "objc"): "objc"
+                else: "c"
+  if os.execShellCmd(os.getAppFilename() & " " & backend &
+                     " --nimcache:" & nimcache & " -r " & outp) != 0:
+    quit "[Examples] failed: see " & outp
+  else:
+    # keep generated source file `outp` to allow inspection.
+    rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp])
+    removeFile(outp.changeFileExt(ExeExt))
+    try:
+      removeDir(nimcache)
+    except OSError:
+      discard
+
+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) =
+  let inp = toFullPath(d.conf, n.info)
+  if d.runnableExamples == nil:
+    d.runnableExamples = newTree(nkStmtList,
+      newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp))))
+    d.runnableExamples.info = n.info
+  let imports = newTree(nkStmtList)
+  var savedLastSon = copyTree n.lastSon
+  extractImports(savedLastSon, imports)
+  for imp in imports: d.runnableExamples.add imp
+  d.runnableExamples.add newTree(nkBlockStmt, newNode(nkEmpty), copyTree savedLastSon)
+
+proc isRunnableExample(n: PNode): bool =
+  # Templates and generics don't perform symbol lookups.
+  result = n.kind == nkSym and n.sym.magic == mRunnableExamples or
+    n.kind == nkIdent and n.ident.s == "runnableExamples"
+
 proc getAllRunnableExamples(d: PDoc; n: PNode; dest: var Rope) =
   case n.kind
   of nkCallKinds:
-    if n[0].kind == nkSym and n[0].sym.magic == mRunnableExamples and
+    if isRunnableExample(n[0]) and
         n.len >= 2 and n.lastSon.kind == nkStmtList:
+      prepareExamples(d, n)
       dispA(d.conf, dest, "\n<p><strong class=\"examples_text\">$1</strong></p>\n",
           "\n\\textbf{$1}\n", [rope"Examples:"])
       inc d.listingCounter
@@ -627,6 +677,10 @@ proc generateDoc*(d: PDoc, n: PNode) =
   of nkImportStmt:
     for i in 0 .. sonsLen(n)-1: traceDeps(d, n.sons[i])
   of nkFromStmt, nkImportExceptStmt: traceDeps(d, n.sons[0])
+  of nkCallKinds:
+    var comm: Rope = nil
+    getAllRunnableExamples(d, n, comm)
+    if comm > nil: add(d.modDesc, comm)
   else: discard
 
 proc add(d: PDoc; j: JsonNode) =
@@ -819,6 +873,7 @@ proc commandDoc*(cache: IdentCache, conf: ConfigRef) =
   generateDoc(d, ast)
   writeOutput(d, conf.projectFull, HtmlExt)
   generateIndex(d)
+  testExamples(d)
 
 proc commandRstAux(cache: IdentCache, conf: ConfigRef; filename, outExt: string) =
   var filen = addFileExt(filename, "txt")
@@ -853,6 +908,7 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef; filename, outExt: string)
   d.modDesc = rope(modDesc)
   writeOutput(d, filename, outExt)
   generateIndex(d)
+  testExamples(d)
 
 proc commandRst2Html*(cache: IdentCache, conf: ConfigRef) =
   commandRstAux(cache, conf, conf.projectFull, HtmlExt)
@@ -876,6 +932,7 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) =
     let filename = getOutFile(conf, conf.projectFull, JsonExt)
     if not writeRope(content, filename):
       rawMessage(conf, errCannotOpenFile, filename)
+  testExamples(d)
 
 proc commandTags*(cache: IdentCache, conf: ConfigRef) =
   var ast = parseFile(conf.projectMainIdx, cache, conf)
diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim
index 068c47bb3..aa61dbd27 100644
--- a/compiler/docgen2.nim
+++ b/compiler/docgen2.nim
@@ -30,6 +30,7 @@ template closeImpl(body: untyped) {.dirty.} =
     body
     try:
       generateIndex(g.doc)
+      testExamples(g.doc)
     except IOError:
       discard
 
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 6128c02d1..7a83c3079 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -607,28 +607,6 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
       #if c.config.cmd == cmdIdeTools: findSuggest(c, n)
   rod.storeNode(c.graph, c.module, result)
 
-proc testExamples(c: PContext) =
-  let outputDir = c.config.getNimcacheDir / "runnableExamples"
-  createDir(outputDir)
-  let inp = toFullPath(c.config, c.module.info)
-  let outp = outputDir / extractFilename(inp.changeFileExt"" & "_examples.nim")
-  let nimcache = outp.changeFileExt"" & "_nimcache"
-  renderModule(c.runnableExamples, inp, outp, conf = c.config)
-  let backend = if isDefined(c.config, "js"): "js"
-                elif isDefined(c.config, "cpp"): "cpp"
-                elif isDefined(c.config, "objc"): "objc"
-                else: "c"
-  if os.execShellCmd(os.getAppFilename() & " " & backend & " --nimcache:" & nimcache & " -r " & outp) != 0:
-    quit "[Examples] failed: see " & outp
-  else:
-    # keep generated source file `outp` to allow inspection.
-    rawMessage(c.config, hintSuccess, ["runnableExamples: " & outp])
-    removeFile(outp.changeFileExt(ExeExt))
-    try:
-      removeDir(nimcache)
-    except OSError:
-      discard
-
 proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
   var c = PContext(context)
   if c.config.cmd == cmdIdeTools and not c.suggestionsMade:
@@ -644,7 +622,6 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
   popOwner(c)
   popProcCon(c)
   storeRemaining(c.graph, c.module)
-  if c.runnableExamples != nil: testExamples(c)
 
 const semPass* = makePass(myOpen, myProcess, myClose,
                           isFrontend = true)
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 2e59c07b0..6d6627690 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -141,7 +141,6 @@ type
       # the generic type has been constructed completely. See
       # tests/destructor/topttree.nim for an example that
       # would otherwise fail.
-    runnableExamples*: PNode
 
 template config*(c: PContext): ConfigRef = c.graph.config
 
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 04e76102b..ce953f17c 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1953,13 +1953,6 @@ proc setMs(n: PNode, s: PSym): PNode =
   n.sons[0] = newSymNode(s)
   n.sons[0].info = n.info
 
-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 semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
   # this is a hotspot in the compiler!
   # DON'T forget to update ast.SpecialSemMagics if you add a magic here!
@@ -2033,16 +2026,17 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
         result = magicsAfterOverloadResolution(c, result, flags)
   of mRunnableExamples:
     if c.config.cmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList:
-      if sfMainModule in c.module.flags:
-        let inp = toFullPath(c.config, c.module.info)
-        if c.runnableExamples == nil:
-          c.runnableExamples = newTree(nkStmtList,
-            newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp))))
-        let imports = newTree(nkStmtList)
-        var saved_lastSon = copyTree n.lastSon
-        extractImports(saved_lastSon, imports)
-        for imp in imports: c.runnableExamples.add imp
-        c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree saved_lastSon)
+      when false:
+        if sfMainModule in c.module.flags:
+          let inp = toFullPath(c.config, c.module.info)
+          if c.runnableExamples == nil:
+            c.runnableExamples = newTree(nkStmtList,
+              newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp))))
+          let imports = newTree(nkStmtList)
+          var savedLastSon = copyTree n.lastSon
+          extractImports(savedLastSon, imports)
+          for imp in imports: c.runnableExamples.add imp
+          c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree savedLastSon)
       result = setMs(n, s)
     else:
       result = c.graph.emptyNode