summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2023-12-13 10:39:10 +0100
committerGitHub <noreply@github.com>2023-12-13 10:39:10 +0100
commitcd4ecddb30a64f5d2c3c6fdde955366c7976577f (patch)
tree16e9c2ffe00db23b01f16cb02bf1064fcd786b51
parent7e1ea50bc3a495bc8feb69ceb9856a0a28ecc2e9 (diff)
downloadNim-cd4ecddb30a64f5d2c3c6fdde955366c7976577f.tar.gz
nimpretty: check the rendered AST for wrong output (#23057)
-rw-r--r--compiler/layouter.nim25
-rw-r--r--compiler/parser.nim2
-rw-r--r--nimpretty/nimpretty.nim42
3 files changed, 55 insertions, 14 deletions
diff --git a/compiler/layouter.nim b/compiler/layouter.nim
index 7cff98b11..0121b1185 100644
--- a/compiler/layouter.nim
+++ b/compiler/layouter.nim
@@ -9,7 +9,7 @@
 
 ## Layouter for nimpretty.
 
-import idents, lexer, lineinfos, llstream, options, msgs, strutils, pathutils
+import idents, lexer, ast, lineinfos, llstream, options, msgs, strutils, pathutils
 
 const
   MinLineLen = 15
@@ -243,23 +243,28 @@ proc renderTokens*(em: var Emitter): string =
 
   return content
 
-proc writeOut*(em: Emitter, content: string)  =
+type
+  FinalCheck = proc (content: string; origAst: PNode): bool {.nimcall.}
+
+proc writeOut*(em: Emitter; content: string; origAst: PNode; check: FinalCheck) =
   ## Write to disk
   let outFile = em.config.absOutFile
   if fileExists(outFile) and readFile(outFile.string) == content:
     discard "do nothing, see #9499"
     return
-  var f = llStreamOpen(outFile, fmWrite)
-  if f == nil:
-    rawMessage(em.config, errGenerated, "cannot open file: " & outFile.string)
-    return
-  f.llStreamWrite content
-  llStreamClose(f)
 
-proc closeEmitter*(em: var Emitter) =
+  if check(content, origAst):
+    var f = llStreamOpen(outFile, fmWrite)
+    if f == nil:
+      rawMessage(em.config, errGenerated, "cannot open file: " & outFile.string)
+      return
+    f.llStreamWrite content
+    llStreamClose(f)
+
+proc closeEmitter*(em: var Emitter; origAst: PNode; check: FinalCheck) =
   ## Renders emitter tokens and write to a file
   let content = renderTokens(em)
-  em.writeOut(content)
+  em.writeOut(content, origAst, check)
 
 proc wr(em: var Emitter; x: string; lt: LayoutToken) =
   em.tokens.add x
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 072540dba..4ed38f739 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -155,8 +155,6 @@ proc openParser*(p: var Parser, filename: AbsoluteFile, inputStream: PLLStream,
 proc closeParser*(p: var Parser) =
   ## Close a parser, freeing up its resources.
   closeLexer(p.lex)
-  when defined(nimpretty):
-    closeEmitter(p.em)
 
 proc parMessage(p: Parser, msg: TMsgKind, arg = "") =
   ## Produce and emit the parser message `arg` to output.
diff --git a/nimpretty/nimpretty.nim b/nimpretty/nimpretty.nim
index e3b4c9a3a..8e8c58597 100644
--- a/nimpretty/nimpretty.nim
+++ b/nimpretty/nimpretty.nim
@@ -12,7 +12,7 @@
 when not defined(nimpretty):
   {.error: "This needs to be compiled with --define:nimPretty".}
 
-import ../compiler / [idents, msgs, syntaxes, options, pathutils, layouter]
+import ../compiler / [idents, llstream, ast, msgs, syntaxes, options, pathutils, layouter]
 
 import parseopt, strutils, os, sequtils
 
@@ -48,6 +48,42 @@ type
     indWidth*: Natural
     maxLineLen*: Positive
 
+proc goodEnough(a, b: PNode): bool =
+  if a.kind == b.kind and a.safeLen == b.safeLen:
+    case a.kind
+    of nkNone, nkEmpty, nkNilLit: result = true
+    of nkIdent: result = a.ident.id == b.ident.id
+    of nkSym: result = a.sym == b.sym
+    of nkType: result = true
+    of nkCharLit, nkIntLit..nkInt64Lit, nkUIntLit..nkUInt64Lit:
+      result = a.intVal == b.intVal
+    of nkFloatLit..nkFloat128Lit:
+      result = a.floatVal == b.floatVal
+    of nkStrLit, nkRStrLit, nkTripleStrLit:
+      result = a.strVal == b.strVal
+    else:
+      for i in 0 ..< a.len:
+        if not goodEnough(a[i], b[i]): return false
+      return true
+  elif a.kind == nkStmtList and a.len == 1:
+    result = goodEnough(a[0], b)
+  elif b.kind == nkStmtList and b.len == 1:
+    result = goodEnough(a, b[0])
+  else:
+    result = false
+
+proc finalCheck(content: string; origAst: PNode): bool {.nimcall.} =
+  var conf = newConfigRef()
+  let oldErrors = conf.errorCounter
+  var parser: Parser
+  parser.em.indWidth = 2
+  let fileIdx = fileInfoIdx(conf, AbsoluteFile "nimpretty_bug.nim")
+
+  openParser(parser, fileIdx, llStreamOpen(content), newIdentCache(), conf)
+  let newAst = parseAll(parser)
+  closeParser(parser)
+  result = conf.errorCounter == oldErrors # and goodEnough(newAst, origAst)
+
 proc prettyPrint*(infile, outfile: string, opt: PrettyOptions) =
   var conf = newConfigRef()
   let fileIdx = fileInfoIdx(conf, AbsoluteFile infile)
@@ -58,8 +94,10 @@ proc prettyPrint*(infile, outfile: string, opt: PrettyOptions) =
   parser.em.indWidth = opt.indWidth
   if setupParser(parser, fileIdx, newIdentCache(), conf):
     parser.em.maxLineLen = opt.maxLineLen
-    discard parseAll(parser)
+    let fullAst = parseAll(parser)
     closeParser(parser)
+    when defined(nimpretty):
+      closeEmitter(parser.em, fullAst, finalCheck)
 
 proc main =
   var outfile, outdir: string