summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorJake Leahy <jake@leahy.dev>2023-12-20 03:27:24 +1100
committerGitHub <noreply@github.com>2023-12-19 17:27:24 +0100
commitdb9d8003b074cb6d150f7ece467f29006ccefde7 (patch)
treea1c37b053d97f38911bb6d53355cb9a1aa45910c
parent6618448ced6f4f6d65d830d355e819996f654b57 (diff)
downloadNim-db9d8003b074cb6d150f7ece467f29006ccefde7.tar.gz
Don't crash for invalid toplevel parseStmt/Expr calls (#23089)
This code will crash `check`/`nimsuggest` since the `ra` register is
uninitialised

```nim
import macros

static:
  discard parseExpr("'")
```
Now it assigns an empty node so that it has something

Testament changes were so I could properly write a test. It would pass
even with a segfault since it could find the error
-rw-r--r--compiler/vm.nim3
-rw-r--r--testament/testament.nim13
-rw-r--r--tests/macros/tfail_parse.nim15
3 files changed, 27 insertions, 4 deletions
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 88419d5dd..d74a64908 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -1925,6 +1925,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
                             proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) =
                               if error.len == 0 and msg <= errMax:
                                 error = formatMsg(conf, info, msg, arg))
+
+      regs[ra].node = newNode(nkEmpty)
       if error.len > 0:
         c.errorFlag = error
       elif ast.len != 1:
@@ -1942,6 +1944,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
                                 error = formatMsg(conf, info, msg, arg))
       if error.len > 0:
         c.errorFlag = error
+        regs[ra].node = newNode(nkEmpty)
       else:
         regs[ra].node = ast
     of opcQueryErrorFlag:
diff --git a/testament/testament.nim b/testament/testament.nim
index 5f443ffba..e0a200e29 100644
--- a/testament/testament.nim
+++ b/testament/testament.nim
@@ -193,7 +193,6 @@ proc callNimCompiler(cmdTemplate, filename, options, nimcache: string,
         foundSuccessMsg = true
     elif not running(p):
       break
-  close(p)
   result.msg = ""
   result.file = ""
   result.output = ""
@@ -201,8 +200,9 @@ proc callNimCompiler(cmdTemplate, filename, options, nimcache: string,
   result.column = 0
 
   result.err = reNimcCrash
-  let exitCode = p.peekExitCode
-  case exitCode
+  result.exitCode = p.peekExitCode
+  close p
+  case result.exitCode
   of 0:
     if foundErrorMsg:
       result.debugInfo.add " compiler exit code was 0 but some Error's were found."
@@ -214,7 +214,7 @@ proc callNimCompiler(cmdTemplate, filename, options, nimcache: string,
     if foundSuccessMsg:
       result.debugInfo.add " compiler exit code was 1 but no `isSuccess` was true."
   else:
-    result.debugInfo.add " expected compiler exit code 0 or 1, got $1." % $exitCode
+    result.debugInfo.add " expected compiler exit code 0 or 1, got $1." % $result.exitCode
 
   if err =~ pegLineError:
     result.file = extractFilename(matches[0])
@@ -521,7 +521,12 @@ proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec,
             r.addResult(test, target, extraOptions, expected.output, bufB, reOutputsDiffer)
           compilerOutputTests(test, target, extraOptions, given, expected, r)
   of actionReject:
+    # Make sure its the compiler rejecting and not the system (e.g. segfault)
     cmpMsgs(r, expected, given, test, target, extraOptions)
+    if given.exitCode != QuitFailure:
+      r.addResult(test, target, extraOptions, "exitcode: " & $QuitFailure,
+                        "exitcode: " & $given.exitCode & "\n\nOutput:\n" &
+                        given.nimout, reExitcodesDiffer)
 
 proc targetHelper(r: var TResults, test: TTest, expected: TSpec, extraOptions: string) =
   for target in expected.targets:
diff --git a/tests/macros/tfail_parse.nim b/tests/macros/tfail_parse.nim
new file mode 100644
index 000000000..1925f2b69
--- /dev/null
+++ b/tests/macros/tfail_parse.nim
@@ -0,0 +1,15 @@
+discard """
+action: "reject"
+cmd: "nim check $file"
+errormsg: "expected expression, but got multiple statements [ValueError]"
+file: "macros.nim"
+"""
+
+import macros
+static:
+  discard parseStmt("'")
+  discard parseExpr("'")
+  discard parseExpr("""
+proc foo()
+proc foo() = discard
+""")