From 0a38cd654381c90f2f62548a1fe6fe7c9528de5b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 2 Nov 2017 19:07:00 +0100 Subject: [PATCH 01/22] make tests green again --- lib/deprecated/pure/sockets.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/deprecated/pure/sockets.nim b/lib/deprecated/pure/sockets.nim index 153db9ed86..f068c7d560 100644 --- a/lib/deprecated/pure/sockets.nim +++ b/lib/deprecated/pure/sockets.nim @@ -262,7 +262,7 @@ proc socket*(domain: Domain = AF_INET, typ: SockType = SOCK_STREAM, # TODO: Perhaps this should just raise EOS when an error occurs. when defined(Windows): - result = newTSocket(winlean.socket(ord(domain), ord(typ), ord(protocol)), buffered) + result = newTSocket(winlean.socket(cint(domain), cint(typ), cint(protocol)), buffered) else: result = newTSocket(posix.socket(toInt(domain), toInt(typ), toInt(protocol)), buffered) From cccdd9b58ef35f13c74abfd10cfd416a8f70061b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 2 Nov 2017 20:39:08 +0100 Subject: [PATCH 02/22] fixes new liftLocals pass --- compiler/liftlocals.nim | 12 +++++++----- compiler/transf.nim | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/liftlocals.nim b/compiler/liftlocals.nim index f29a4e106e..3610a14862 100644 --- a/compiler/liftlocals.nim +++ b/compiler/liftlocals.nim @@ -52,17 +52,19 @@ proc lookupParam(params, dest: PNode): PSym = if params[i].kind == nkSym and params[i].sym.name.id == dest.ident.id: return params[i].sym -proc liftLocalsIfRequested*(prc: PSym) = +proc liftLocalsIfRequested*(prc: PSym; n: PNode): PNode = let liftDest = getPragmaVal(prc.ast, wLiftLocals) - if liftDest == nil: return + if liftDest == nil: return n let partialParam = lookupParam(prc.typ.n, liftDest) if partialParam.isNil: localError(liftDest.info, "'$1' is not a parameter of '$2'" % [$liftDest, prc.name.s]) - return + return n let objType = partialParam.typ.skipTypes(abstractPtrs) if objType.kind != tyObject or tfPartial notin objType.flags: localError(liftDest.info, "parameter '$1' is not a pointer to a partial object" % $liftDest) - return + return n var c = Ctx(partialParam: partialParam, objType: objType) - liftLocals(prc.ast, bodyPos, c) + let w = newTree(nkStmtList, n) + liftLocals(w, 0, c) + result = w[0] diff --git a/compiler/transf.nim b/compiler/transf.nim index c3bdd4ddc7..baf801cbf1 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -978,7 +978,7 @@ proc transformBody*(module: PSym, n: PNode, prc: PSym): PNode = liftDefer(c, result) #result = liftLambdas(prc, result) when useEffectSystem: trackProc(prc, result) - liftLocalsIfRequested(prc) + result = liftLocalsIfRequested(prc, result) if c.needsDestroyPass and newDestructors: result = injectDestructorCalls(prc, result) incl(result.flags, nfTransf) From 0f5261e9711c3fd57241874963bd5e45b11ed65e Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 2 Nov 2017 22:09:58 +0100 Subject: [PATCH 03/22] fixes #6675 --- lib/system.nim | 4 ++-- tests/array/troof1.nim | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 92c5e009f9..0cae4da87f 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -3493,14 +3493,14 @@ proc `[]`*[Idx, T, U, V](a: array[Idx, T], x: HSlice[U, V]): seq[T] = let xa = a ^^ x.a let L = (a ^^ x.b) - xa + 1 result = newSeq[T](L) - for i in 0.. Date: Fri, 3 Nov 2017 18:05:14 +0100 Subject: [PATCH 04/22] bugfix: detect captures in non-closure inner procs --- compiler/lambdalifting.nim | 5 ++++- tests/closure/tinvalidclosure3.nim | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tests/closure/tinvalidclosure3.nim diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index e64e0a898a..f8d107c84d 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -764,7 +764,10 @@ proc semCaptureSym*(s, owner: PSym) = var o = owner.skipGenericOwner while o.kind != skModule and o != nil: if s.owner == o: - owner.typ.callConv = ccClosure + if owner.typ.callConv in {ccClosure, ccDefault} or owner.kind == skIterator: + owner.typ.callConv = ccClosure + else: + discard "do not produce an error here, but later" #echo "computing .closure for ", owner.name.s, " ", owner.info, " because of ", s.name.s o = o.skipGenericOwner # since the analysis is not entirely correct, we don't set 'tfCapturesEnv' diff --git a/tests/closure/tinvalidclosure3.nim b/tests/closure/tinvalidclosure3.nim new file mode 100644 index 0000000000..31c4976f88 --- /dev/null +++ b/tests/closure/tinvalidclosure3.nim @@ -0,0 +1,12 @@ +discard """ + line: 9 + errormsg: "illegal capture 'x'" +""" + +proc outer(arg: string) = + var x = 0 + proc inner {.inline.} = + echo "inner", x + inner() + +outer("abc") \ No newline at end of file From cf9da7d7517ae8d7177a5efbbd1c39982131eb52 Mon Sep 17 00:00:00 2001 From: GULPF Date: Fri, 3 Nov 2017 21:08:09 +0100 Subject: [PATCH 05/22] Always compile tests in the JS category with the JS backend (#6680) --- tests/testament/categories.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index 675ff946f1..68e988975b 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -418,8 +418,9 @@ proc `&?.`(a, b: string): string = proc processSingleTest(r: var TResults, cat: Category, options, test: string) = let test = "tests" & DirSep &.? cat.string / test + let target = if cat.string.normalize == "js": targetJS else: targetC - if existsFile(test): testSpec r, makeTest(test, options, cat) + if existsFile(test): testSpec r, makeTest(test, options, cat, target = target) else: echo "[Warning] - ", test, " test does not exist" proc processCategory(r: var TResults, cat: Category, options: string) = From de97f381fa93de98afdbbfd900e9e9d66e2397c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20H=C3=B8is=C3=A6ther=20Rasch?= Date: Fri, 3 Nov 2017 21:09:01 +0100 Subject: [PATCH 06/22] Fix path for sh on Android (#6661) --- config/nim.cfg | 1 - lib/pure/osproc.nim | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/config/nim.cfg b/config/nim.cfg index 6ae55a9b2a..a146c4ebf4 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -98,7 +98,6 @@ path="$lib/pure" clang.options.linker = "-landroid-glob" clang.cpp.options.linker = "-landroid-glob" tcc.options.linker = "-landroid-glob" - define:"useShPath:/system/bin/sh" @end @end diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 71d3d9c727..d768a7de99 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -767,7 +767,9 @@ elif not defined(useNimRtl): var sysCommand: string var sysArgsRaw: seq[string] if poEvalCommand in options: - const useShPath {.strdefine.} = "/bin/sh" + const useShPath {.strdefine.} = + when not defined(android): "/bin/sh" + else: "/system/bin/sh" sysCommand = useShPath sysArgsRaw = @[sysCommand, "-c", command] assert args.len == 0, "`args` has to be empty when using poEvalCommand." From da2fd42e67d3182b550840bc148db88cbc3d469e Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Fri, 3 Nov 2017 20:58:32 +0000 Subject: [PATCH 07/22] Add comments on "untestable" tests directory --- readme.md | 1 + tests/untestable/readme.markdown | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index ac24658d99..6fbe60c6ae 100644 --- a/readme.md +++ b/readme.md @@ -127,6 +127,7 @@ However, if you are short on time, you can just run the tests specific to your changes by only running the corresponding categories of tests. Travis CI verifies that all tests pass before allowing the pull request to be accepted, so only running specific tests should be harmless. +Integration tests should go in ``tests/untestable``. If you're looking for ways to contribute, please look at our [issue tracker][nim-issues]. There are always plenty of issues labelled [``Easy``][nim-issues-easy]; these should diff --git a/tests/untestable/readme.markdown b/tests/untestable/readme.markdown index fcb7f4f288..de1ba9459f 100644 --- a/tests/untestable/readme.markdown +++ b/tests/untestable/readme.markdown @@ -1,2 +1,9 @@ -This directory contains tests which are not automatically executed -for various reasons. Mainly due to dependencies on external services. \ No newline at end of file +This directory contains integration tests which are not automatically executed +for various reasons: +- dependency on external services +- dependency on files / configuration / state of the local host +- tests that are extremely slow or require large amounts of memory or storage +- tests that spawn local daemons + +Integration tests can become stale very quickly. Automated ./koch tests are +strongly recommended. From 742f43e57211c22fe1faa0c7ef991b9b0a1eadc9 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 5 Nov 2017 01:25:39 +0100 Subject: [PATCH 08/22] fixes #6609; 'if' expressions support multiple statements; minor breaking change --- changelog.md | 15 ++++++- compiler/parser.nim | 70 +++++++++++++++++++++++++------- lib/packages/docutils/rstgen.nim | 3 +- lib/system/sysstr.nim | 2 +- tests/parser/tletcolon.nim | 18 ++++++++ 5 files changed, 91 insertions(+), 17 deletions(-) diff --git a/changelog.md b/changelog.md index eebd47fb8b..5947c49e51 100644 --- a/changelog.md +++ b/changelog.md @@ -39,5 +39,18 @@ - Added ``typetraits.$`` as an alias for ``typetraits.name``. - ``os.getEnv`` now takes an optional ``default`` parameter that tells ``getEnv`` what to return if the environment variable does not exist. -- Removed PDCurses wrapper from the stdlib and published it as a separate +- Removed PDCurses wrapper from the stdlib and published it as a separate Nimble package. +- The parsing rules of ``if`` expressions were changed so that multiple + statements are allowed in the branches. We found few code examples that + now fail because of this change, but here is one: + +.. code-block:: nim + + t[ti] = if exp_negative: '-' else: '+'; inc(ti) + +This now needs to be written as: + +.. code-block:: nim + + t[ti] = (if exp_negative: '-' else: '+'); inc(ti) diff --git a/compiler/parser.nim b/compiler/parser.nim index 1139221890..e3bb68da43 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -785,21 +785,58 @@ proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode = #| 'else' colcom expr #| ifExpr = 'if' condExpr #| whenExpr = 'when' condExpr - result = newNodeP(kind, p) - while true: - getTok(p) # skip `if`, `elif` - var branch = newNodeP(nkElifExpr, p) + when true: + result = newNodeP(kind, p) + while true: + getTok(p) # skip `if`, `when`, `elif` + var branch = newNodeP(nkElifExpr, p) + optInd(p, branch) + addSon(branch, parseExpr(p)) + colcom(p, branch) + addSon(branch, parseStmt(p)) + skipComment(p, branch) + addSon(result, branch) + if p.tok.tokType != tkElif: break # or not sameOrNoInd(p): break + if p.tok.tokType == tkElse: # and sameOrNoInd(p): + var branch = newNodeP(nkElseExpr, p) + eat(p, tkElse) + colcom(p, branch) + addSon(branch, parseStmt(p)) + addSon(result, branch) + else: + var + b: PNode + wasIndented = false + result = newNodeP(kind, p) + + getTok(p) + let branch = newNodeP(nkElifExpr, p) addSon(branch, parseExpr(p)) colcom(p, branch) + let oldInd = p.currInd + if realInd(p): + p.currInd = p.tok.indent + wasIndented = true + echo result.info, " yes ", p.currInd addSon(branch, parseExpr(p)) - optInd(p, branch) - addSon(result, branch) - if p.tok.tokType != tkElif: break - var branch = newNodeP(nkElseExpr, p) - eat(p, tkElse) - colcom(p, branch) - addSon(branch, parseExpr(p)) - addSon(result, branch) + result.add branch + while sameInd(p) or not wasIndented: + case p.tok.tokType + of tkElif: + b = newNodeP(nkElifExpr, p) + getTok(p) + optInd(p, b) + addSon(b, parseExpr(p)) + of tkElse: + b = newNodeP(nkElseExpr, p) + getTok(p) + else: break + colcom(p, b) + addSon(b, parseStmt(p)) + addSon(result, b) + if b.kind == nkElseExpr: break + if wasIndented: + p.currInd = oldInd proc parsePragma(p: var TParser): PNode = #| pragma = '{.' optInd (exprColonExpr comma?)* optPar ('.}' | '}') @@ -2036,8 +2073,13 @@ proc parseStmt(p: var TParser): PNode = if a.kind != nkEmpty: addSon(result, a) else: - parMessage(p, errExprExpected, p.tok) - getTok(p) + # This is done to make the new 'if' expressions work better. + # XXX Eventually we need to be more strict here. + if p.tok.tokType notin {tkElse, tkElif}: + parMessage(p, errExprExpected, p.tok) + getTok(p) + else: + break if not p.hasProgress and p.tok.tokType == tkEof: break else: # the case statement is only needed for better error messages: diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 13016bfc03..6fed401417 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -798,7 +798,8 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) = if arg.valid: let htmlOut = if isObject: "" & content & "" - else: "" + else: + "" dispA(d.target, result, htmlOut, "\\includegraphics$2{$1}", [arg, options]) if len(n) >= 3: renderRstToOut(d, n.sons[2], result) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 0627ef2fb9..d9586b00d4 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -509,7 +509,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat, # insert exponent t[ti] = 'E'; inc(ti) - t[ti] = if exp_negative: '-' else: '+'; inc(ti) + t[ti] = (if exp_negative: '-' else: '+'); inc(ti) inc(ti, 3) # insert adjusted exponent diff --git a/tests/parser/tletcolon.nim b/tests/parser/tletcolon.nim index 6b86535c82..eab7a8eddb 100644 --- a/tests/parser/tletcolon.nim +++ b/tests/parser/tletcolon.nim @@ -32,3 +32,21 @@ let other = x: echo "no" let outer = y(5): echo "yes" + + +# bug #6609 +type + TextureInternalFormat = enum RED, RGB, RGBA + +const channels = 4 + +let format = + if channels == 1: + TextureInternalFormat.RED + elif channels == 3: + TextureInternalFormat.RGB + elif channels == 4: + TextureInternalFormat.RGBA + else: + echo "Texture Format Unknown, assuming RGB" #This echo causes an error + TextureInternalFormat.RGB From 3bd6b7ddc7e7cd8f1551b79f3dbd74c539db14d1 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 5 Nov 2017 02:51:20 +0100 Subject: [PATCH 09/22] improve the error messages for bug #6692 --- compiler/msgs.nim | 4 ++-- compiler/semexprs.nim | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 8d43103dbe..2668c72ae0 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -61,7 +61,7 @@ type errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects, errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX, errCannotInstantiateX, errExprHasNoAddress, errXStackEscape, - errVarForOutParamNeeded, + errVarForOutParamNeededX, errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX, errAmbiguousCallXYZ, errWrongNumberOfArguments, errWrongNumberOfArgumentsInCall, @@ -268,7 +268,7 @@ const errCannotInstantiateX: "cannot instantiate: \'$1\'", errExprHasNoAddress: "expression has no address", errXStackEscape: "address of '$1' may not escape its stack frame", - errVarForOutParamNeeded: "for a \'var\' type a variable needs to be passed", + errVarForOutParamNeededX: "for a \'var\' type a variable needs to be passed; but '$1' is immutable", errPureTypeMismatch: "type mismatch", errTypeMismatch: "type mismatch: got (", errButExpected: "but expected one of: ", diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 7a16f495a7..d600b1c486 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -465,7 +465,7 @@ proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ)) addSon(result, n) if isAssignable(c, n) notin {arLValue, arLocalLValue}: - localError(n.info, errVarForOutParamNeeded) + localError(n.info, errVarForOutParamNeededX, $n) proc analyseIfAddressTaken(c: PContext, n: PNode): PNode = result = n @@ -509,9 +509,10 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = for i in countup(1, sonsLen(n) - 1): if i < sonsLen(t) and t.sons[i] != nil and skipTypes(t.sons[i], abstractInst-{tyTypeDesc}).kind == tyVar: - if isAssignable(c, n.sons[i]) notin {arLValue, arLocalLValue}: - if n.sons[i].kind != nkHiddenAddr: - localError(n.sons[i].info, errVarForOutParamNeeded) + let it = n[i] + if isAssignable(c, it) notin {arLValue, arLocalLValue}: + if it.kind != nkHiddenAddr: + localError(it.info, errVarForOutParamNeededX, $it) return for i in countup(1, sonsLen(n) - 1): if n.sons[i].kind == nkHiddenCallConv: From cd6d734f8bd447e61caee2b135dad6a528587c3d Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 5 Nov 2017 02:51:32 +0100 Subject: [PATCH 10/22] fixes #6692 --- lib/system.nim | 8 +++--- tests/array/troofregression.nim | 46 +++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 tests/array/troofregression.nim diff --git a/lib/system.nim b/lib/system.nim index 0cae4da87f..c29a756ed4 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -3527,18 +3527,20 @@ proc `[]=`*[T, U, V](s: var seq[T], x: HSlice[U, V], b: openArray[T]) = else: spliceImpl(s, a, L, b) -proc `[]`*[T](s: openArray[T]; i: BackwardsIndex): T {.inline.} = s[s.len - int(i)] +proc `[]`*[T](s: openArray[T]; i: BackwardsIndex): T {.inline.} = + system.`[]`(s, s.len - int(i)) + proc `[]`*[Idx, T](a: array[Idx, T]; i: BackwardsIndex): T {.inline.} = a[Idx(a.len - int(i) + int low(a))] proc `[]`*(s: string; i: BackwardsIndex): char {.inline.} = s[s.len - int(i)] proc `[]`*[T](s: var openArray[T]; i: BackwardsIndex): var T {.inline.} = - s[s.len - int(i)] + system.`[]`(s, s.len - int(i)) proc `[]`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex): var T {.inline.} = a[Idx(a.len - int(i) + int low(a))] proc `[]=`*[T](s: var openArray[T]; i: BackwardsIndex; x: T) {.inline.} = - s[s.len - int(i)] = x + system.`[]=`(s, s.len - int(i), x) proc `[]=`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex; x: T) {.inline.} = a[Idx(a.len - int(i) + int low(a))] = x proc `[]=`*(s: var string; i: BackwardsIndex; x: char) {.inline.} = diff --git a/tests/array/troofregression.nim b/tests/array/troofregression.nim new file mode 100644 index 0000000000..0b96123a4a --- /dev/null +++ b/tests/array/troofregression.nim @@ -0,0 +1,46 @@ +############################### +#### part from Arraymancer + +type + MetadataArray* = object + data*: array[8, int] + len*: int + +# Commenting the converter removes the error "lib/system.nim(3536, 3) Error: for a 'var' type a variable needs to be passed" +converter toMetadataArray*(se: varargs[int]): MetadataArray {.inline.} = + result.len = se.len + for i in 0..= "0.17.3": + type Index = int or BackwardsIndex + template `^^`(s, i: untyped): untyped = + when i is BackwardsIndex: + s.len - int(i) + else: i +else: + type Index = int + template `^^`(s, i: untyped): untyped = + i + +## With Nim devel from the start of the week (~Oct30) I managed to trigger "lib/system.nim(3536, 4) Error: expression has no address" +## but I can't anymore after updating Nim (Nov5) +## Now commenting this plain compiles and removes the error "lib/system.nim(3536, 3) Error: for a 'var' type a variable needs to be passed" +proc `[]`*(a: var MetadataArray, idx: Index): var int {.inline.} = + a.data[a ^^ idx] + + +############################## +### Completely unrelated lib that triggers the issue + +type + MySeq[T] = ref object + data: seq[T] + +proc test[T](sx: MySeq[T]) = + # Removing the backward index removes the error "lib/system.nim(3536, 3) Error: for a 'var' type a variable needs to be passed" + echo sx.data[^1] # error here + +let s = MySeq[int](data: @[1, 2, 3]) +s.test() From e32bfef5ccdfc19a67cbc3a783cc53179b142fe5 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 5 Nov 2017 13:02:46 +0100 Subject: [PATCH 11/22] make tests green again --- tests/lexer/tind1.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/lexer/tind1.nim b/tests/lexer/tind1.nim index 8a2aea9b2f..ffbde48fd1 100644 --- a/tests/lexer/tind1.nim +++ b/tests/lexer/tind1.nim @@ -1,6 +1,6 @@ discard """ line: 24 - errormsg: "expression expected, but found 'keyword else'" + errormsg: "invalid indentation" """ import macros @@ -11,7 +11,7 @@ var x = if 4 != 5: else: "no" -macro mymacro(n): untyped {.immediate.} = +macro mymacro(n, b): untyped = discard mymacro: From eee43e4f8febf3186186d96d59b3aa72502bf466 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 5 Nov 2017 16:33:26 +0100 Subject: [PATCH 12/22] fixes the new to be documented imports --- compiler/modulepaths.nim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim index d7b5f147d9..5d112c6b91 100644 --- a/compiler/modulepaths.nim +++ b/compiler/modulepaths.nim @@ -98,6 +98,7 @@ proc resolveDollar(project, source, pkg, subdir: string; info: TLineInfo): strin proc scriptableImport(pkg, sub: string; info: TLineInfo): string = result = resolveDollar(gProjectFull, info.toFullPath(), pkg, sub, info) + if result.isNil: result = "" proc lookupPackage(pkg, subdir: PNode): string = let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: "" @@ -166,7 +167,8 @@ proc checkModuleName*(n: PNode; doLocalError=true): int32 = let fullPath = findModule(modulename, n.info.toFullPath) if fullPath.len == 0: if doLocalError: - localError(n.info, errCannotOpenFile, modulename) + let m = if modulename.len > 0: modulename else: $n + localError(n.info, errCannotOpenFile, m) result = InvalidFileIDX else: - result = fullPath.fileInfoIdx \ No newline at end of file + result = fullPath.fileInfoIdx From 4e4d466d060f9b55cb2adb3ad02e212281df4962 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 5 Nov 2017 16:53:05 +0100 Subject: [PATCH 13/22] minor breaking change: for loop bodies now get their own scope --- changelog.md | 8 ++++++++ compiler/semgnrc.nim | 2 ++ compiler/semstmts.nim | 2 ++ compiler/semtempl.nim | 2 ++ 4 files changed, 14 insertions(+) diff --git a/changelog.md b/changelog.md index 5947c49e51..7aa52ece28 100644 --- a/changelog.md +++ b/changelog.md @@ -41,6 +41,14 @@ what to return if the environment variable does not exist. - Removed PDCurses wrapper from the stdlib and published it as a separate Nimble package. +- Bodies of ``for`` loops now get their own scope: + +.. code-block:: nim + # now compiles: + for i in 0..4: + let i = i + 1 + echo i + - The parsing rules of ``if`` expressions were changed so that multiple statements are allowed in the branches. We found few code examples that now fail because of this change, but here is one: diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 3cdb68df64..e28a7851ff 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -335,8 +335,10 @@ proc semGenericStmt(c: PContext, n: PNode, n.sons[L - 2] = semGenericStmt(c, n.sons[L-2], flags, ctx) for i in countup(0, L - 3): addTempDecl(c, n.sons[i], skForVar) + openScope(c) n.sons[L - 1] = semGenericStmt(c, n.sons[L-1], flags, ctx) closeScope(c) + closeScope(c) of nkBlockStmt, nkBlockExpr, nkBlockType: checkSonsLen(n, 2) openScope(c) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 540ef4c074..80fad3f23e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -697,7 +697,9 @@ proc semForVars(c: PContext, n: PNode): PNode = if sfGenSym notin v.flags and not isDiscardUnderscore(v): addForVarDecl(c, v) inc(c.p.nestedLoopCounter) + openScope(c) n.sons[length-1] = semStmt(c, n.sons[length-1]) + closeScope(c) dec(c.p.nestedLoopCounter) proc implicitIterator(c: PContext, it: string, arg: PNode): PNode = diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 426290c788..1c9d8271af 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -366,8 +366,10 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = n.sons[L-2] = semTemplBody(c, n.sons[L-2]) for i in countup(0, L - 3): addLocalDecl(c, n.sons[i], skForVar) + openScope(c) n.sons[L-1] = semTemplBody(c, n.sons[L-1]) closeScope(c) + closeScope(c) of nkBlockStmt, nkBlockExpr, nkBlockType: checkSonsLen(n, 2) openScope(c) From 9e4e1949e8e599bcfa560178243aabaf4b1bb68b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 5 Nov 2017 21:31:02 +0100 Subject: [PATCH 14/22] concepts: fixes the stack overflow that happens for #6691 --- compiler/sigmatch.nim | 3 ++- tests/concepts/tinfrecursion.nim | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 tests/concepts/tinfrecursion.nim diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 231dd80f47..97b18306b2 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1057,9 +1057,10 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, else: isNone of tyUserTypeClass, tyUserTypeClassInst: - if c.c.matchedConcept != nil: + if c.c.matchedConcept != nil and c.c.matchedConcept.depth <= 4: # consider this: 'var g: Node' *within* a concept where 'Node' # is a concept too (tgraph) + inc c.c.matchedConcept.depth let x = typeRel(c, a, f, flags + {trDontBind}) if x >= isGeneric: return isGeneric diff --git a/tests/concepts/tinfrecursion.nim b/tests/concepts/tinfrecursion.nim new file mode 100644 index 0000000000..60db410dee --- /dev/null +++ b/tests/concepts/tinfrecursion.nim @@ -0,0 +1,13 @@ + +# bug #6691 +type + ConceptA = concept c + + ConceptB = concept c + c.myProc(ConceptA) + + Obj = object + +proc myProc(obj: Obj, x: ConceptA) = discard + +echo Obj is ConceptB From 4ea09e4df5c47cbca064d4f3b2388237f09c605f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 5 Nov 2017 21:44:22 +0100 Subject: [PATCH 15/22] attempt to make travis green again --- tests/async/tasyncdial.nim | 1 + tests/stdlib/thttpclient.nim | 1 + tests/stdlib/tnetdial.nim | 1 + tests/testament/specs.nim | 4 ++++ 4 files changed, 7 insertions(+) diff --git a/tests/async/tasyncdial.nim b/tests/async/tasyncdial.nim index d70e14020e..fa81235fef 100644 --- a/tests/async/tasyncdial.nim +++ b/tests/async/tasyncdial.nim @@ -4,6 +4,7 @@ discard """ OK AF_INET OK AF_INET6 ''' + disabled: "travis" """ import diff --git a/tests/stdlib/thttpclient.nim b/tests/stdlib/thttpclient.nim index e759e29772..54588d3f0b 100644 --- a/tests/stdlib/thttpclient.nim +++ b/tests/stdlib/thttpclient.nim @@ -2,6 +2,7 @@ discard """ cmd: "nim c --threads:on -d:ssl $file" exitcode: 0 output: "OK" + disabled: "travis" """ import strutils diff --git a/tests/stdlib/tnetdial.nim b/tests/stdlib/tnetdial.nim index da6088d709..6951501793 100644 --- a/tests/stdlib/tnetdial.nim +++ b/tests/stdlib/tnetdial.nim @@ -2,6 +2,7 @@ discard """ cmd: "nim c --threads:on $file" exitcode: 0 output: "OK" + disabled: "travis" """ import os, net, nativesockets, asyncdispatch diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim index 89e786d481..e5506e7962 100644 --- a/tests/testament/specs.nim +++ b/tests/testament/specs.nim @@ -12,6 +12,8 @@ import parseutils, strutils, os, osproc, streams, parsecfg var compilerPrefix* = "compiler" / "nim " +let isTravis = existsEnv("TRAVIS") + proc cmdTemplate*(): string = compilerPrefix & "$target --lib:lib --hints:on -d:testing $options $file" @@ -174,6 +176,8 @@ proc parseSpec*(filename: string): TSpec = when defined(unix): result.err = reIgnored of "posix": when defined(posix): result.err = reIgnored + of "travis": + if isTravis: result.err = reIgnored else: raise newException(ValueError, "cannot interpret as a bool: " & e.value) of "cmd": From e4b08186292d95e2b57ac1b4116ee3469f89224a Mon Sep 17 00:00:00 2001 From: skilchen Date: Mon, 6 Nov 2017 09:14:28 +0100 Subject: [PATCH 16/22] fixes #6532 array bounds check for non zero based arrays on the js backend (#6550) --- compiler/jsgen.nim | 2 +- tests/js/tarrayboundscheck.nim | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100755 tests/js/tarrayboundscheck.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index a7c4e6b426..8be53f11db 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1067,7 +1067,7 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = else: r.res = "chckIndx($1, $2, strlen($3))-$2" % [b.res, rope(first), a.res] else: - r.res = "chckIndx($1, $2, $3.length-1)-$2" % [b.res, rope(first), a.res] + r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), a.res] elif first != 0: r.res = "($1)-$2" % [b.res, rope(first)] else: diff --git a/tests/js/tarrayboundscheck.nim b/tests/js/tarrayboundscheck.nim new file mode 100755 index 0000000000..f0eaeb89de --- /dev/null +++ b/tests/js/tarrayboundscheck.nim @@ -0,0 +1,44 @@ +discard """ + output: '''idx out of bounds: -1 +month out of bounds: 0 +Jan +Feb +Mar +Apr +May +Jun +Jul +Aug +Sep +Oct +Nov +Dec +month out of bounds: 13 +idx out of bounds: 14 +''' +""" + +{.push boundChecks:on.} + +# see issue #6532: +# js backend 0.17.3: array bounds check for non zero based arrays is buggy + +proc test_arrayboundscheck() = + var months: array[1..12, string] = + ["Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] + + var indices = [0,1,2,3,4,5,6,7,8,9,10,11,12,13] + + for i in -1 .. 14: + try: + let idx = indices[i] + try: + echo months[idx] + except: + echo "month out of bounds: ", idx + except: + echo "idx out of bounds: ", i + +test_arrayboundscheck() +{.pop.} \ No newline at end of file From 8b294b118671a94ffa5acc02685a038588be467a Mon Sep 17 00:00:00 2001 From: Kartik Saranathan Date: Mon, 6 Nov 2017 03:15:09 -0500 Subject: [PATCH 17/22] fix broken link in manual (#6697) --- doc/manual/pragmas.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/pragmas.txt b/doc/manual/pragmas.txt index 023895f730..db7ce7e63e 100644 --- a/doc/manual/pragmas.txt +++ b/doc/manual/pragmas.txt @@ -316,7 +316,7 @@ factor. immediate pragma ---------------- -See `Ordinary vs immediate templates`_. +See `Typed vs untyped parameters`_. compilation option pragmas From 0586529ff512f56995cba647fcecb1e9c1dba29d Mon Sep 17 00:00:00 2001 From: olwi <32296608+olwi@users.noreply.github.com> Date: Mon, 6 Nov 2017 10:36:06 +0200 Subject: [PATCH 18/22] Adds optional maxsplit parameter to splitWhitespace (#6503) (#6690) * Adds optional maxsplit parameter to splitWhitespace() (#6503) * Adds an example to splitWhitespace doc comment. Minor fixes to several doc comments --- lib/pure/strutils.nim | 67 +++++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 2b87e0d430..0b55e6b1d8 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -578,15 +578,46 @@ iterator split*(s: string, seps: set[char] = Whitespace, else: splitCommon(s, seps, maxsplit, 1) -iterator splitWhitespace*(s: string): string = - ## Splits at whitespace. - oldSplit(s, Whitespace, -1) +iterator splitWhitespace*(s: string, maxsplit: int = -1): string = + ## Splits the string ``s`` at whitespace stripping leading and trailing + ## whitespace if necessary. If ``maxsplit`` is specified and is positive, + ## no more than ``maxsplit`` splits is made. + ## + ## The following code: + ## + ## .. code-block:: nim + ## let s = " foo \t bar baz " + ## for ms in [-1, 1, 2, 3]: + ## echo "------ maxsplit = ", ms, ":" + ## for item in s.splitWhitespace(maxsplit=ms): + ## echo '"', item, '"' + ## + ## ...results in: + ## + ## .. code-block:: + ## ------ maxsplit = -1: + ## "foo" + ## "bar" + ## "baz" + ## ------ maxsplit = 1: + ## "foo" + ## "bar baz " + ## ------ maxsplit = 2: + ## "foo" + ## "bar" + ## "baz " + ## ------ maxsplit = 3: + ## "foo" + ## "bar" + ## "baz" + ## + oldSplit(s, Whitespace, maxsplit) -proc splitWhitespace*(s: string): seq[string] {.noSideEffect, +proc splitWhitespace*(s: string, maxsplit: int = -1): seq[string] {.noSideEffect, rtl, extern: "nsuSplitWhitespace".} = - ## The same as the `splitWhitespace <#splitWhitespace.i,string>`_ + ## The same as the `splitWhitespace <#splitWhitespace.i,string,int>`_ ## iterator, but is a proc that returns a sequence of substrings. - accumulateResult(splitWhitespace(s)) + accumulateResult(splitWhitespace(s, maxsplit)) iterator split*(s: string, sep: char, maxsplit: int = -1): string = ## Splits the string `s` into substrings using a single separator. @@ -671,7 +702,7 @@ iterator rsplit*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): string = ## Splits the string `s` into substrings from the right using a ## string separator. Works exactly the same as `split iterator - ## <#split.i,string,char>`_ except in reverse order. + ## <#split.i,string,char,int>`_ except in reverse order. ## ## .. code-block:: nim ## for piece in "foo bar".rsplit(WhiteSpace): @@ -691,7 +722,7 @@ iterator rsplit*(s: string, sep: char, maxsplit: int = -1): string = ## Splits the string `s` into substrings from the right using a ## string separator. Works exactly the same as `split iterator - ## <#split.i,string,char>`_ except in reverse order. + ## <#split.i,string,char,int>`_ except in reverse order. ## ## .. code-block:: nim ## for piece in "foo:bar".rsplit(':'): @@ -710,7 +741,7 @@ iterator rsplit*(s: string, sep: string, maxsplit: int = -1, keepSeparators: bool = false): string = ## Splits the string `s` into substrings from the right using a ## string separator. Works exactly the same as `split iterator - ## <#split.i,string,string>`_ except in reverse order. + ## <#split.i,string,string,int>`_ except in reverse order. ## ## .. code-block:: nim ## for piece in "foothebar".rsplit("the"): @@ -791,13 +822,13 @@ proc countLines*(s: string): int {.noSideEffect, proc split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[string] {. noSideEffect, rtl, extern: "nsuSplitCharSet".} = - ## The same as the `split iterator <#split.i,string,set[char]>`_, but is a + ## The same as the `split iterator <#split.i,string,set[char],int>`_, but is a ## proc that returns a sequence of substrings. accumulateResult(split(s, seps, maxsplit)) proc split*(s: string, sep: char, maxsplit: int = -1): seq[string] {.noSideEffect, rtl, extern: "nsuSplitChar".} = - ## The same as the `split iterator <#split.i,string,char>`_, but is a proc + ## The same as the `split iterator <#split.i,string,char,int>`_, but is a proc ## that returns a sequence of substrings. accumulateResult(split(s, sep, maxsplit)) @@ -806,7 +837,7 @@ proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEff ## Splits the string `s` into substrings using a string separator. ## ## Substrings are separated by the string `sep`. This is a wrapper around the - ## `split iterator <#split.i,string,string>`_. + ## `split iterator <#split.i,string,string,int>`_. doAssert(sep.len > 0) accumulateResult(split(s, sep, maxsplit)) @@ -814,7 +845,7 @@ proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEff proc rsplit*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[string] {.noSideEffect, rtl, extern: "nsuRSplitCharSet".} = - ## The same as the `rsplit iterator <#rsplit.i,string,set[char]>`_, but is a + ## The same as the `rsplit iterator <#rsplit.i,string,set[char],int>`_, but is a ## proc that returns a sequence of substrings. ## ## A possible common use case for `rsplit` is path manipulation, @@ -836,7 +867,7 @@ proc rsplit*(s: string, seps: set[char] = Whitespace, proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string] {.noSideEffect, rtl, extern: "nsuRSplitChar".} = - ## The same as the `split iterator <#rsplit.i,string,char>`_, but is a proc + ## The same as the `rsplit iterator <#rsplit.i,string,char,int>`_, but is a proc ## that returns a sequence of substrings. ## ## A possible common use case for `rsplit` is path manipulation, @@ -858,7 +889,7 @@ proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string] proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEffect, rtl, extern: "nsuRSplitString".} = - ## The same as the `split iterator <#rsplit.i,string,string>`_, but is a proc + ## The same as the `rsplit iterator <#rsplit.i,string,string,int>`_, but is a proc ## that returns a sequence of substrings. ## ## A possible common use case for `rsplit` is path manipulation, @@ -2599,6 +2630,12 @@ bar doAssert s.split(' ', maxsplit=1) == @["", "this is an example "] doAssert s.split(" ", maxsplit=4) == @["", "this", "is", "an", "example "] + doAssert s.splitWhitespace() == @["this", "is", "an", "example"] + doAssert s.splitWhitespace(maxsplit=1) == @["this", "is an example "] + doAssert s.splitWhitespace(maxsplit=2) == @["this", "is", "an example "] + doAssert s.splitWhitespace(maxsplit=3) == @["this", "is", "an", "example "] + doAssert s.splitWhitespace(maxsplit=4) == @["this", "is", "an", "example"] + block: # formatEng tests doAssert formatEng(0, 2, trim=false) == "0.00" doAssert formatEng(0, 2) == "0" From 11f6e626d72aa9863b9ab0f1fa80f8695416ba1e Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Mon, 6 Nov 2017 16:37:21 +0800 Subject: [PATCH 19/22] pass on testament args to sub-process (#6688) --- tests/testament/tester.nim | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 0daf4089e5..71d9c2b9f5 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -439,6 +439,8 @@ proc main() = var optPrintResults = false var optFailing = false + var targetsStr = "" + var p = initOptParser() p.next() while p.kind == cmdLongoption: @@ -446,7 +448,9 @@ proc main() = of "print", "verbose": optPrintResults = true of "failing": optFailing = true of "pedantic": discard "now always enabled" - of "targets": targets = parseTargets(p.val.string) + of "targets": + targetsStr = p.val.string + targets = parseTargets(targetsStr) of "nim": compilerPrefix = p.val.string else: quit Usage p.next() @@ -457,7 +461,10 @@ proc main() = case action of "all": let testsDir = "tests" & DirSep - let myself = quoteShell(findExe("tests" / "testament" / "tester")) + var myself = quoteShell(findExe("tests" / "testament" / "tester")) & + " '--nim:" & compilerPrefix & "'" + if targetsStr.len > 0: + myself &= " '--targets:" & targetsStr & "'" var cmds: seq[string] = @[] let rest = if p.cmdLineRest.string.len > 0: " " & p.cmdLineRest.string else: "" for kind, dir in walkDir(testsDir): From 87a46765eee6601ccc53b2c38d1c8f3a9b045124 Mon Sep 17 00:00:00 2001 From: Dmitry Atamanov Date: Mon, 6 Nov 2017 11:50:42 +0300 Subject: [PATCH 20/22] Stream-writer for a rope (#6603) --- lib/pure/ropes.nim | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index 6e97237e04..5b8d43d716 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -17,6 +17,7 @@ ## runtime efficiency. include "system/inclrtl" +import streams {.deadCodeElim: on.} @@ -130,7 +131,7 @@ proc insertInCache(s: string, tree: Rope): Rope = result.left = t t.right = nil -proc rope*(s: string): Rope {.rtl, extern: "nro$1Str".} = +proc rope*(s: string = nil): Rope {.rtl, extern: "nro$1Str".} = ## Converts a string to a rope. if s.len == 0: result = nil @@ -242,10 +243,13 @@ proc write*(f: File, r: Rope) {.rtl, extern: "nro$1".} = ## writes a rope to a file. for s in leaves(r): write(f, s) +proc write*(s: Stream, r: Rope) {.rtl, extern: "nro$1".} = + ## writes a rope to a stream. + for rs in leaves(r): write(s, rs) + proc `$`*(r: Rope): string {.rtl, extern: "nroToString".}= ## converts a rope back to a string. - result = newString(r.len) - setLen(result, 0) + result = newStringOfCap(r.len) for s in leaves(r): add(result, s) when false: From 2d892caba8d864db2da40ff22590bb01ddb0b0f0 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 6 Nov 2017 19:25:28 +0100 Subject: [PATCH 21/22] fixes ropes.nim regression; make tests green again --- lib/pure/ropes.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index 5b8d43d716..6ddd61afa6 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -243,7 +243,7 @@ proc write*(f: File, r: Rope) {.rtl, extern: "nro$1".} = ## writes a rope to a file. for s in leaves(r): write(f, s) -proc write*(s: Stream, r: Rope) {.rtl, extern: "nro$1".} = +proc write*(s: Stream, r: Rope) {.rtl, extern: "nroWriteStream".} = ## writes a rope to a stream. for rs in leaves(r): write(s, rs) From 4dad120d47e5b2654a6e0728d5d387b59aed66aa Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 6 Nov 2017 20:32:06 +0100 Subject: [PATCH 22/22] fixes a tester regression --- tests/testament/tester.nim | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim index 71d9c2b9f5..d75c9d770d 100644 --- a/tests/testament/tester.nim +++ b/tests/testament/tester.nim @@ -461,10 +461,9 @@ proc main() = case action of "all": let testsDir = "tests" & DirSep - var myself = quoteShell(findExe("tests" / "testament" / "tester")) & - " '--nim:" & compilerPrefix & "'" + var myself = quoteShell(findExe("tests" / "testament" / "tester")) if targetsStr.len > 0: - myself &= " '--targets:" & targetsStr & "'" + myself &= " '--targets:" & targetsStr & "'" var cmds: seq[string] = @[] let rest = if p.cmdLineRest.string.len > 0: " " & p.cmdLineRest.string else: "" for kind, dir in walkDir(testsDir):