Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ errors.

- Adds a new warning `--warning:ImplicitRangeConversion` that detects downsizing implicit conversions to range types (e.g., `int -> range[0..255]` or `range[1..256] -> range[0..255]`) that could cause runtime panics. Safe conversions like `range[0..255] -> range[0..65535]` and explicit casts do not trigger warnings. `int` to `Natural` and `Positive` conversions do not trigger warnings, which can be enabled with `--warning:systemRangeConversion`.


- `std/cmdline.commandLineParams()` for NimScript now consistently returns only the script arguments (excluding the `nim` executable and its flags), fixing inconsistency between `std/cmdline` and `std/parseopt`.


## Standard library additions and changes

[//]: # "Additions:"
Expand Down
27 changes: 24 additions & 3 deletions lib/std/cmdline.nim
Original file line number Diff line number Diff line change
Expand Up @@ -279,9 +279,15 @@ when declared(paramCount) or defined(nimdoc):
proc commandLineParams*(): seq[string] =
## Convenience proc which returns the command line parameters.
##
## This returns **only** the parameters. If you want to get the application
## Unlike `paramStr proc`_, returns **only** the parameters.
## If you want to get the application
## executable filename, call `getAppFilename() <os.html#getAppFilename>`_.
##
## When used from NimScript, arguments preceding and including the first
## `.nims` file are excluded, returning only the arguments intended for the
## script. If no token ending with `.nims` (case-insensitive) is present
## in the command line, this returns an empty sequence.
##
## **Availability**: On Posix there is no portable way to get the command
## line from a DLL and thus the proc isn't defined in this environment. You
## can test for its availability with `declared()
Expand All @@ -303,8 +309,23 @@ when declared(paramCount) or defined(nimdoc):
## # Do something else!
## ```
result = @[]
for i in 1..paramCount():
result.add(paramStr(i))
when defined(nimscript):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The compiler as it evaluates paramCount and paramStr at compile-time should do these fixups.

Copy link
Copy Markdown
Contributor Author

@ZoomRmc ZoomRmc Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, maybe it should but it doesn't. These procs are builtins for NimScript, as far as I see. Not sure what you want.

This code is not new, it's just moved copied from parseopt to a more fitting place.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want you to fix the root cause which is in compiler/scriptconfig.nim, lines:

  cbconf paramStr:
    setResult(a, os.paramStr(int a.getInt 0))
  cbconf paramCount:
    setResult(a, os.paramCount())

Copy link
Copy Markdown
Contributor Author

@ZoomRmc ZoomRmc Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First of all, I wouldn't know how to, I found the source but I'm not very confident at that level.

Secondly, wouldn't this break scripts inspecting the command line launched implicitly from config.nims on compilation?

# in config.nims:
for i in 1..paramCount():
  if paramStr(i) == "-foo":
    raise newException(Defect, "Build with 'foo' is not supported")

func isNimScriptExt(s: openArray[char]): bool =
let L = s.len
(L >= 5 and s[L-5] == '.' and (
(s[L-4] in {'n', 'N'}) and
(s[L-3] in {'i', 'I'}) and
(s[L-2] in {'m', 'M'}) and
(s[L-1] in {'s', 'S'})))
var firstNimsFound = false
for i in 0..paramCount(): # nimscript needs to check index 0
if firstNimsFound:
result.add(paramStr(i))
elif isNimScriptExt(paramStr(i)):
firstNimsFound = true
else:
for i in 1..paramCount():
result.add(paramStr(i))
else:
proc commandLineParams*(): seq[string] {.error:
"commandLineParams() unsupported by dynamic libraries".} =
Expand Down
12 changes: 10 additions & 2 deletions lib/system/nimscript.nim
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,19 @@ proc warningImpl(arg, orig: string) = discard
proc hintImpl(arg, orig: string) = discard

proc paramStr*(i: int): string =
## Retrieves the `i`'th command line parameter.
## Retrieves the `i`'th raw command line parameter seen by the Nim
## compiler or script host.
##
## Use `commandLineParams proc <cmdline.html#commandLineParams>`_ for the
## stripped arguments intended for the current script.
builtin

proc paramCount*(): int =
## Retrieves the number of command line parameters.
## Retrieves the number of raw command line parameters seen by the Nim
## compiler or script host.
##
## Use `commandLineParams proc <cmdline.html#commandLineParams>`_ for the
## stripped arguments intended for the current script.
builtin

proc switch*(key: string, val="") =
Expand Down
16 changes: 16 additions & 0 deletions tests/misc/trunner.nim
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,22 @@ tests/newconfig/bar/mfoo.nims""".splitLines
expected.add &"Hint: used config file '{b}' [Conf]\n"
doAssert outp.endsWith expected, outp & "\n" & expected

block: # commandLineParams vs paramCount/paramStr in config.nies
let implicitCmd = fmt"{nim} check --hints:off tests/newconfig/cmdline/_/test.nim"
const implicitExp = """nims cmdline test:
scriptArgs=[]; (paramCount > 0)==true; rawHasNims=false; rawHasMain=true;
parseopt got args: 0
"""
check execCmdEx(implicitCmd) == (implicitExp, 0)

let explicitCmd =
fmt"""{nim} e --hints:off tests/newconfig/cmdline/tcmdline.nims arg1 "arg2""""
const explicitExp = """nims cmdline test:
scriptArgs=[arg1, arg2]; (paramCount > 0)==true; rawHasNims=true; rawHasMain=false;
parseopt got args: 2
"""
check execCmdEx(explicitCmd) == (explicitExp, 0)

block: # bug #8219
let file = "tests/newconfig/mconfigcheck.nims"
let cmd = fmt"{nim} check --hints:off {file}"
Expand Down
1 change: 1 addition & 0 deletions tests/newconfig/cmdline/_/config.nims
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include ".."/"tcmdline.nims"
1 change: 1 addition & 0 deletions tests/newconfig/cmdline/_/test.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
discard
24 changes: 24 additions & 0 deletions tests/newconfig/cmdline/tcmdline.nims
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from std/strutils import endsWith, join
from std/os import commandLineParams
import std/parseopt

let cmdParams = commandLineParams().join(", ")
var
rawHasNims = false
rawHasMain = false
parsed: seq[string]

for i in 1..paramCount():
let arg = paramStr(i)
rawHasNims = rawHasNims or arg.endsWith(".nims")
rawHasMain = rawHasMain or arg.endsWith("test.nim")

# parseopt follows commandLineParams for NimScript.
var p = initOptParser()
for _, key, val in p.getopt():
parsed.add key

echo "nims cmdline test:\n",
" scriptArgs=[", cmdParams, "]; (paramCount > 0)==", paramCount() > 0,
"; rawHasNims=", rawHasNims, "; rawHasMain=", rawHasMain, ";\n",
" parseopt got args: ", parsed.len
12 changes: 10 additions & 2 deletions tests/test_nimscript.nims
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,16 @@ block: # cpDir, cpFile, dirExists, fileExists, mkDir, mvDir, mvFile, rmDir, rmF
rmDir(dname)

block:
# check parseopt can get command line:
discard initOptParser()
# parsing live input tested in misc/trunner
let cmdParams = commandLineParams()
doAssert cmdParams.len == 0, "commandLineParams should strip compiler args"
var parsed: seq[string]
var p = initOptParser()
for _, key, val in p.getopt():
parsed.add key
parsed.add val
doAssert parsed.len == 0, "initOptParser should strip compiler args"
doAssert parsed == cmdParams, "initOptParser.getopt != commandLineParams"

# issue #24780:

Expand Down
Loading