* fixes #18543

* make tests green again
This commit is contained in:
Andreas Rumpf 2021-07-27 19:04:55 +02:00 • committed by GitHub
commit 4920b06973
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 107 additions and 184 deletions

View file

@ -51,7 +51,6 @@
implementations. Old behavior can be obtained with implementations. Old behavior can be obtained with
`-d:nimLegacyParseQueryStrict`. `cgi.decodeData` which uses the same `-d:nimLegacyParseQueryStrict`. `cgi.decodeData` which uses the same
underlying code is also updated the same way. underlying code is also updated the same way.
- Custom pragma values have now an API for use in macros.
- On POSIX systems, the default signal handlers used for Nim programs (it's - On POSIX systems, the default signal handlers used for Nim programs (it's
used for printing the stacktrace on fatal signals) will now re-raise the used for printing the stacktrace on fatal signals) will now re-raise the

View file

@ -1068,11 +1068,11 @@ proc newEmptyNode*(): NimNode {.noSideEffect.} =
## Create a new empty node. ## Create a new empty node.
result = newNimNode(nnkEmpty) result = newNimNode(nnkEmpty)
proc newStmtList*(stmts: varargs[NimNode]): NimNode= proc newStmtList*(stmts: varargs[NimNode]): NimNode =
## Create a new statement list. ## Create a new statement list.
result = newNimNode(nnkStmtList).add(stmts) result = newNimNode(nnkStmtList).add(stmts)
proc newPar*(exprs: varargs[NimNode]): NimNode= proc newPar*(exprs: varargs[NimNode]): NimNode =
## Create a new parentheses-enclosed expression. ## Create a new parentheses-enclosed expression.
newNimNode(nnkPar).add(exprs) newNimNode(nnkPar).add(exprs)
@ -1494,148 +1494,80 @@ macro expandMacros*(body: typed): untyped =
echo body.toStrLit echo body.toStrLit
result = body result = body
proc findPragmaNodeInRecList(arg, fieldSym: NimNode): NimNode = proc customPragmaNode(n: NimNode): NimNode =
case arg.kind expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkCheckedFieldExpr})
of nnkRecList, nnkRecCase: let
for it in arg.children: typ = n.getTypeInst()
result = findPragmaNodeInRecList(it, fieldSym)
if result != nil: return
of nnkOfBranch:
return findPragmaNodeInRecList(arg[1], fieldSym)
of nnkElse:
return findPragmaNodeInRecList(arg[0], fieldSym)
of nnkIdentDefs:
for i in 0..<arg.len-2:
result = findPragmaNodeInRecList(arg[i], fieldSym)
if result != nil: return
of nnkAccQuoted, nnkIdent, nnkSym, nnkPostfix:
return nil
of nnkPragmaExpr:
var ident = arg[0]
if ident.kind == nnkPostfix: ident = ident[1]
if ident.kind == nnkAccQuoted: ident = ident[0]
if eqIdent(ident, fieldSym):
return arg[1]
else:
error("illegal arg: ", arg)
proc getPragmaNodeFromProcSym(sym: NimNode): NimNode = if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy:
sym.expectKind nnkSym return typ[1][1]
if sym.symKind != nskProc: error("expected proc sym", sym) elif typ.typeKind == ntyTypeDesc:
let impl = typ[1].getImpl()
let impl = sym.getImpl if impl[0].kind == nnkPragmaExpr:
expectKind(impl, nnkProcDef) return impl[0][1]
result = impl[4]
proc getPragmaNodeFromObjFieldSym(sym: NimNode): NimNode =
sym.expectKind nnkSym
if sym.symKind != nskField: error("expected field sym", sym)
# note this is not ``getTypeImpl``, because the result of
# ``getTypeImpl`` is cleaned up of any pragma expressions.
let impl = sym.owner.getImpl
impl.expectKind nnkTypeDef
let objectTy = if impl[2].kind == nnkRefTy: impl[2][0]
else: impl[2]
# only works on object types
objectTy.expectKind nnkObjectTy
let recList = objectTy[2]
recList.expectKind nnkRecList
result = findPragmaNodeInRecList(recList, sym)
proc getPragmaNodeFromTypeSym(sym: NimNode): NimNode =
sym.expectKind nnkSym
if sym.symKind != nskType: error("expected type sym", sym)
let impl = sym.getImpl
if impl.len > 0:
impl.expectKind nnkTypeDef
let pragmaExpr = impl[0]
if pragmaExpr.kind == nnkPragmaExpr:
result = pragmaExpr[1]
proc getPragmaNodeFromType(typ: NimNode): NimNode =
case typ.kind
of nnkSym:
result = getPragmaNodeFromTypeSym(typ)
of nnkProcTy:
result = typ[1]
else: error("illegal typ kind for argument: " & $typ.kind, typ)
proc getPragmaNodeFromVarLetSym(sym: NimNode): NimNode =
sym.expectKind nnkSym
if sym.symKind notin {nskVar, nskLet}: error("expected var/let sym", sym)
let impl = sym.getImpl
impl.expectKind nnkIdentDefs
impl.expectLen 3
let pragmaExpr = impl[0]
if pragmaExpr.kind == nnkPragmaExpr:
result = pragmaExpr[1]
proc getPragmasByName(pragmaExpr: NimNode, name: string): seq[NimNode] =
if pragmaExpr.kind == nnkPragma:
for it in pragmaExpr:
if it.kind in nnkPragmaCallKinds:
if eqIdent(it[0], name):
result.add it
elif it.kind == nnkSym:
if eqIdent(it, name):
result.add it
proc getCustomPragmaNodes(sym: NimNode, name: string): seq[NimNode] =
sym.expectKind nnkSym
case sym.symKind
of nskField:
result = getPragmaNodeFromObjFieldSym(sym).getPragmasByName(name)
of nskProc:
result = getPragmaNodeFromProcSym(sym).getPragmasByName(name)
of nskType:
result = getPragmaNodeFromTypeSym(sym).getPragmasByName(name)
of nskParam:
# When a typedesc parameter is passed to the macro, it will be of nskParam.
let typeInst = getTypeInst(sym)
if typeInst.kind == nnkBracketExpr and eqIdent(typeInst[0], "typeDesc"):
result = getPragmaNodeFromTypeSym(typeInst[1]).getPragmasByName(name)
else: else:
error("illegal sym kind for argument: " & $sym.symKind, sym) return impl[0] # handle types which don't have macro at all
of nskVar, nskLet:
# This checks the type of the sym too, this is consistent with how
# field expressions are handled too. If this is changed, make sure to
# change it for fields expressions too.
result = getPragmaNodeFromType(sym.getTypeInst).getPragmasByName(name)
result.add getPragmaNodeFromVarLetSym(sym).getPragmasByName(name)
else:
error("illegal sym kind for argument: " & $sym.symKind, sym)
since (1, 5): if n.kind == nnkSym: # either an variable or a proc
export getCustomPragmaNodes let impl = n.getImpl()
if impl.kind in RoutineNodes:
return impl.pragma
elif impl.kind == nnkIdentDefs and impl[0].kind == nnkPragmaExpr:
return impl[0][1]
else:
let timpl = typ.getImpl()
if timpl.len>0 and timpl[0].len>1:
return timpl[0][1]
else:
return timpl
proc hasCustomPragma*(n: NimNode, name: string): bool = if n.kind in {nnkDotExpr, nnkCheckedFieldExpr}:
n.expectKind nnkSym let name = $(if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1])
result = getCustomPragmaNodes(n, name).len > 0 let typInst = getTypeInst(if n.kind == nnkCheckedFieldExpr or n[0].kind == nnkHiddenDeref: n[0][0] else: n[0])
var typDef = getImpl(if typInst.kind == nnkVarTy: typInst[0] else: typInst)
while typDef != nil:
typDef.expectKind(nnkTypeDef)
let typ = typDef[2]
typ.expectKind({nnkRefTy, nnkPtrTy, nnkObjectTy})
let isRef = typ.kind in {nnkRefTy, nnkPtrTy}
if isRef and typ[0].kind in {nnkSym, nnkBracketExpr}: # defines ref type for another object(e.g. X = ref X)
typDef = getImpl(typ[0])
else: # object definition, maybe an object directly defined as a ref type
let
obj = (if isRef: typ[0] else: typ)
var identDefsStack = newSeq[NimNode](obj[2].len)
for i in 0..<identDefsStack.len: identDefsStack[i] = obj[2][i]
while identDefsStack.len > 0:
var identDefs = identDefsStack.pop()
if identDefs.kind == nnkRecCase:
identDefsStack.add(identDefs[0])
for i in 1..<identDefs.len:
let varNode = identDefs[i]
# if it is and empty branch, skip
if varNode[0].kind == nnkNilLit: continue
if varNode[1].kind == nnkIdentDefs:
identDefsStack.add(varNode[1])
else: # nnkRecList
for j in 0 ..< varNode[1].len:
identDefsStack.add(varNode[1][j])
proc getCustomPragmaNodesSmart(n: NimNode, name: string): seq[NimNode] = else:
case n.kind for i in 0 .. identDefs.len - 3:
of nnkDotExpr: let varNode = identDefs[i]
result = getCustomPragmaNodes(n[1], name) if varNode.kind == nnkPragmaExpr:
of nnkCheckedFieldExpr: var varName = varNode[0]
expectKind n[0], nnkDotExpr if varName.kind == nnkPostfix:
result = getCustomPragmaNodes(n[0][1], name) # This is a public field. We are skipping the postfix *
of nnkSym: varName = varName[1]
result = getCustomPragmaNodes(n, name) if eqIdent($varName, name):
of nnkTypeOfExpr: return varNode[1]
var typ = n.getTypeInst
while typ.kind == nnkBracketExpr and typ[0].eqIdent "typeDesc":
typ = typ[1]
result = getPragmaNodeFromType(typ).getPragmasByName(name)
of nnkBracketExpr:
discard
else:
n.expectKind({nnkDotExpr, nnkCheckedFieldExpr, nnkSym, nnkTypeOfExpr})
macro hasCustomPragma*(n: typed, cp: typed{nkSym}): bool = if obj[1].kind == nnkOfInherit: # explore the parent object
typDef = getImpl(obj[1][0])
else:
typDef = nil
macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped =
## Expands to `true` if expression `n` which is expected to be `nnkDotExpr` ## Expands to `true` if expression `n` which is expected to be `nnkDotExpr`
## (if checking a field), a proc or a type has custom pragma `cp`. ## (if checking a field), a proc or a type has custom pragma `cp`.
## ##
@ -1652,18 +1584,14 @@ macro hasCustomPragma*(n: typed, cp: typed{nkSym}): bool =
## var o: MyObj ## var o: MyObj
## assert(o.myField.hasCustomPragma(myAttr)) ## assert(o.myField.hasCustomPragma(myAttr))
## assert(myProc.hasCustomPragma(myAttr)) ## assert(myProc.hasCustomPragma(myAttr))
result = newLit(getCustomPragmaNodesSmart(n, $cp).len > 0) let pragmaNode = customPragmaNode(n)
for p in pragmaNode:
if (p.kind == nnkSym and p == cp) or
(p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp):
return newLit(true)
return newLit(false)
iterator iterOverFormalArgs(f: NimNode): tuple[name, typ, val: NimNode] = macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped =
f.expectKind nnkFormalParams
for i in 1..<f.len:
f[i].expectKind nnkIdentDefs
let typ = f[i][^2]
let val = f[i][^1]
for j in 0..<f[i].len-2:
yield (f[i][j], typ, val)
macro getCustomPragmaVal*(n: typed, cp: typed): untyped =
## Expands to value of custom pragma `cp` of expression `n` which is expected ## Expands to value of custom pragma `cp` of expression `n` which is expected
## to be `nnkDotExpr`, a proc or a type. ## to be `nnkDotExpr`, a proc or a type.
## ##
@ -1678,28 +1606,22 @@ macro getCustomPragmaVal*(n: typed, cp: typed): untyped =
## assert(o.myField.getCustomPragmaVal(serializationKey) == "mf") ## assert(o.myField.getCustomPragmaVal(serializationKey) == "mf")
## assert(o.getCustomPragmaVal(serializationKey) == "mo") ## assert(o.getCustomPragmaVal(serializationKey) == "mo")
## assert(MyObj.getCustomPragmaVal(serializationKey) == "mo") ## assert(MyObj.getCustomPragmaVal(serializationKey) == "mo")
n.expectKind {nnkDotExpr, nnkCheckedFieldExpr, nnkSym, nnkTypeOfExpr} result = nil
cp.expectKind {nnkSym, nnkOpenSymChoice, nnkClosedSymChoice} let pragmaNode = customPragmaNode(n)
let pragmaNodes = getCustomPragmaNodesSmart(n, $cp) for p in pragmaNode:
if pragmaNodes.len == 0: if p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp:
error(n.repr & " doesn't have any custom pragmas") if p.len == 2:
let pragmaNode = pragmaNodes[^1] result = p[1]
else:
case pragmaNode.kind let def = p[0].getImpl[3]
of nnkPragmaCallKinds: result = newTree(nnkPar)
if pragmaNode.len == 2: for i in 1 ..< def.len:
result = pragmaNode[1] let key = def[i][0]
else: let val = p[i]
# create a named tuple expression for pragmas with multiple arguments result.add newTree(nnkExprColonExpr, key, val)
result = newTree(nnkPar) break
var i = 1 if result.kind == nnkEmpty:
for (key, t, v) in iterOverFormalArgs(pragmaNode[0].getImpl[3]): error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error,
result.add nnkExprColonExpr.newTree(key, pragmaNode[i])
inc i
of nnkSym:
error("The named pragma " & cp.repr & " in " & n.repr & " has no arguments and therefore no value.")
else:
error(n.repr & " doesn't have a pragma named " & cp.repr, n)
macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped = macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped =
## Calls `callee` with `args` unpacked as individual arguments. ## Calls `callee` with `args` unpacked as individual arguments.

View file

@ -114,6 +114,7 @@ pkg "nimsvg"
pkg "nimterop", "nimble minitest" pkg "nimterop", "nimble minitest"
pkg "nimwc", "nim c nimwc.nim" pkg "nimwc", "nim c nimwc.nim"
pkg "nimx", "nim c --threads:on test/main.nim", allowFailure = true pkg "nimx", "nim c --threads:on test/main.nim", allowFailure = true
pkg "nimYAML", "nim c -r test/tserialization.nim"
pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter" pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter"
pkg "norm", "nim c -r tests/sqlite/trows.nim" pkg "norm", "nim c -r tests/sqlite/trows.nim"
pkg "npeg", "nimble testarc" pkg "npeg", "nimble testarc"

View file

@ -384,7 +384,7 @@ block:
discard Hello(a: 1.0, b: 12) discard Hello(a: 1.0, b: 12)
# issue #11511 # issue #11511
block: when false:
template myAttr {.pragma.} template myAttr {.pragma.}
type TObj = object type TObj = object
@ -395,23 +395,24 @@ block:
let recList = objTy[2] let recList = objTy[2]
let sym = recList[0] let sym = recList[0]
assert sym.kind == nnkSym and sym.eqIdent("a") assert sym.kind == nnkSym and sym.eqIdent("a")
let hasAttr = sym.hasCustomPragma("myAttr") let hasAttr = sym.hasCustomPragma(myAttr)
newLit(hasAttr) newLit(hasAttr)
doAssert hasMyAttr(TObj) doAssert hasMyAttr(TObj)
# misc when false:
{.pragma: haha.} # misc
{.pragma: hoho.} {.pragma: haha.}
template hehe(key, val: string, haha) {.pragma.} {.pragma: hoho.}
template hehe(key, val: string, haha) {.pragma.}
type A {.haha, hoho, haha, hehe("hi", "hu", "he").} = int type A {.haha, hoho, haha, hehe("hi", "hu", "he").} = int
assert A.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he") assert A.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he")
template hehe(key, val: int) {.pragma.} template hehe(key, val: int) {.pragma.}
var bb {.haha, hoho, hehe(1, 2), haha, hehe("hi", "hu", "he").} = 3 var bb {.haha, hoho, hehe(1, 2), haha, hehe("hi", "hu", "he").} = 3
# left-to-right priority/override order for getCustomPragmaVal # left-to-right priority/override order for getCustomPragmaVal
assert bb.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he") assert bb.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he")