getCustomPragmaVal priority/override fixes (#17725)
* Adhere left-to-right rule for custom pragma priority * Improve error message for no custom pragmas * custom pragmas on var/let sym take priority over its type ones * Workaround & bug
This commit is contained in:
parent
da1c1a7117
commit
7c64e49d45
2 changed files with 50 additions and 38 deletions
|
|
@ -1536,6 +1536,14 @@ proc getPragmaNodeFromTypeSym(sym: NimNode): NimNode =
|
||||||
if pragmaExpr.kind == nnkPragmaExpr:
|
if pragmaExpr.kind == nnkPragmaExpr:
|
||||||
result = pragmaExpr[1]
|
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 =
|
proc getPragmaNodeFromVarLetSym(sym: NimNode): NimNode =
|
||||||
sym.expectKind nnkSym
|
sym.expectKind nnkSym
|
||||||
if sym.symKind notin {nskVar, nskLet}: error("expected var/let sym", sym)
|
if sym.symKind notin {nskVar, nskLet}: error("expected var/let sym", sym)
|
||||||
|
|
@ -1546,73 +1554,64 @@ proc getPragmaNodeFromVarLetSym(sym: NimNode): NimNode =
|
||||||
if pragmaExpr.kind == nnkPragmaExpr:
|
if pragmaExpr.kind == nnkPragmaExpr:
|
||||||
result = pragmaExpr[1]
|
result = pragmaExpr[1]
|
||||||
|
|
||||||
proc getPragmaByName(pragmaExpr: NimNode, name: string): NimNode =
|
proc getPragmasByName(pragmaExpr: NimNode, name: string): seq[NimNode] =
|
||||||
if pragmaExpr.kind == nnkPragma:
|
if pragmaExpr.kind == nnkPragma:
|
||||||
for it in pragmaExpr:
|
for it in pragmaExpr:
|
||||||
if it.kind in nnkPragmaCallKinds:
|
if it.kind in nnkPragmaCallKinds:
|
||||||
if eqIdent(it[0], name):
|
if eqIdent(it[0], name):
|
||||||
return it
|
result.add it
|
||||||
elif it.kind == nnkSym:
|
elif it.kind == nnkSym:
|
||||||
if eqIdent(it, name):
|
if eqIdent(it, name):
|
||||||
return it
|
result.add it
|
||||||
|
|
||||||
proc getCustomPragmaNode(sym: NimNode, name: string): NimNode =
|
proc getCustomPragmaNodes(sym: NimNode, name: string): seq[NimNode] =
|
||||||
sym.expectKind nnkSym
|
sym.expectKind nnkSym
|
||||||
case sym.symKind
|
case sym.symKind
|
||||||
of nskField:
|
of nskField:
|
||||||
result = getPragmaNodeFromObjFieldSym(sym).getPragmaByName(name)
|
result = getPragmaNodeFromObjFieldSym(sym).getPragmasByName(name)
|
||||||
of nskProc:
|
of nskProc:
|
||||||
result = getPragmaNodeFromProcSym(sym).getPragmaByName(name)
|
result = getPragmaNodeFromProcSym(sym).getPragmasByName(name)
|
||||||
of nskType:
|
of nskType:
|
||||||
result = getPragmaNodeFromTypeSym(sym).getPragmaByName(name)
|
result = getPragmaNodeFromTypeSym(sym).getPragmasByName(name)
|
||||||
of nskParam:
|
of nskParam:
|
||||||
# When a typedesc parameter is passed to the macro, it will be of nskParam.
|
# When a typedesc parameter is passed to the macro, it will be of nskParam.
|
||||||
let typeInst = getTypeInst(sym)
|
let typeInst = getTypeInst(sym)
|
||||||
if typeInst.kind == nnkBracketExpr and eqIdent(typeInst[0], "typeDesc"):
|
if typeInst.kind == nnkBracketExpr and eqIdent(typeInst[0], "typeDesc"):
|
||||||
result = getPragmaNodeFromTypeSym(typeInst[1]).getPragmaByName(name)
|
result = getPragmaNodeFromTypeSym(typeInst[1]).getPragmasByName(name)
|
||||||
else:
|
else:
|
||||||
error("illegal sym kind for argument: " & $sym.symKind, sym)
|
error("illegal sym kind for argument: " & $sym.symKind, sym)
|
||||||
of nskVar, nskLet:
|
of nskVar, nskLet:
|
||||||
# I think it is a bad idea to fall back to the typeSym. The API
|
# This checks the type of the sym too, this is consistent with how
|
||||||
# explicity requests a var/let symbol, not a type symbol.
|
# field expressions are handled too. If this is changed, make sure to
|
||||||
result = getPragmaNodeFromVarLetSym(sym).getPragmaByName(name) or
|
# change it for fields expressions too.
|
||||||
getPragmaNodeFromTypeSym(sym.getTypeInst).getPragmaByName(name)
|
result = getPragmaNodeFromType(sym.getTypeInst).getPragmasByName(name)
|
||||||
|
result.add getPragmaNodeFromVarLetSym(sym).getPragmasByName(name)
|
||||||
else:
|
else:
|
||||||
error("illegal sym kind for argument: " & $sym.symKind, sym)
|
error("illegal sym kind for argument: " & $sym.symKind, sym)
|
||||||
|
|
||||||
since (1, 5):
|
since (1, 5):
|
||||||
export getCustomPragmaNode
|
export getCustomPragmaNodes
|
||||||
|
|
||||||
proc hasCustomPragma*(n: NimNode, name: string): bool =
|
proc hasCustomPragma*(n: NimNode, name: string): bool =
|
||||||
n.expectKind nnkSym
|
n.expectKind nnkSym
|
||||||
let pragmaNode = getCustomPragmaNode(n, name)
|
result = getCustomPragmaNodes(n, name).len > 0
|
||||||
result = pragmaNode != nil
|
|
||||||
|
|
||||||
proc getCustomPragmaNodeSmart(n: NimNode, name: string): NimNode =
|
proc getCustomPragmaNodesSmart(n: NimNode, name: string): seq[NimNode] =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nnkDotExpr:
|
of nnkDotExpr:
|
||||||
result = getCustomPragmaNode(n[1], name)
|
result = getCustomPragmaNodes(n[1], name)
|
||||||
of nnkCheckedFieldExpr:
|
of nnkCheckedFieldExpr:
|
||||||
expectKind n[0], nnkDotExpr
|
expectKind n[0], nnkDotExpr
|
||||||
result = getCustomPragmaNode(n[0][1], name)
|
result = getCustomPragmaNodes(n[0][1], name)
|
||||||
of nnkSym:
|
of nnkSym:
|
||||||
result = getCustomPragmaNode(n, name)
|
result = getCustomPragmaNodes(n, name)
|
||||||
of nnkTypeOfExpr:
|
of nnkTypeOfExpr:
|
||||||
var typeSym = n.getTypeInst
|
var typ = n.getTypeInst
|
||||||
while typeSym.kind == nnkBracketExpr and typeSym[0].eqIdent "typeDesc":
|
while typ.kind == nnkBracketExpr and typ[0].eqIdent "typeDesc":
|
||||||
typeSym = typeSym[1]
|
typ = typ[1]
|
||||||
case typeSym.kind:
|
result = getPragmaNodeFromType(typ).getPragmasByName(name)
|
||||||
of nnkSym:
|
|
||||||
result = getCustomPragmaNode(typeSym, name)
|
|
||||||
of nnkProcTy:
|
|
||||||
# It is a bad idea to support this. The annotation can't be part
|
|
||||||
# of a symbol.
|
|
||||||
let pragmaExpr = typeSym[1]
|
|
||||||
result = getPragmaByName(pragmaExpr, name)
|
|
||||||
else:
|
|
||||||
typeSym.expectKind nnkSym
|
|
||||||
of nnkBracketExpr:
|
of nnkBracketExpr:
|
||||||
result = nil #false
|
discard
|
||||||
else:
|
else:
|
||||||
n.expectKind({nnkDotExpr, nnkCheckedFieldExpr, nnkSym, nnkTypeOfExpr})
|
n.expectKind({nnkDotExpr, nnkCheckedFieldExpr, nnkSym, nnkTypeOfExpr})
|
||||||
|
|
||||||
|
|
@ -1633,7 +1632,7 @@ 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(getCustomPragmaNodeSmart(n, $cp) != nil)
|
result = newLit(getCustomPragmaNodesSmart(n, $cp).len > 0)
|
||||||
|
|
||||||
iterator iterOverFormalArgs(f: NimNode): tuple[name, typ, val: NimNode] =
|
iterator iterOverFormalArgs(f: NimNode): tuple[name, typ, val: NimNode] =
|
||||||
f.expectKind nnkFormalParams
|
f.expectKind nnkFormalParams
|
||||||
|
|
@ -1644,7 +1643,7 @@ iterator iterOverFormalArgs(f: NimNode): tuple[name, typ, val: NimNode] =
|
||||||
for j in 0..<f[i].len-2:
|
for j in 0..<f[i].len-2:
|
||||||
yield (f[i][j], typ, val)
|
yield (f[i][j], typ, val)
|
||||||
|
|
||||||
macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped =
|
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.
|
||||||
##
|
##
|
||||||
|
|
@ -1659,12 +1658,15 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): 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})
|
n.expectKind {nnkDotExpr, nnkCheckedFieldExpr, nnkSym, nnkTypeOfExpr}
|
||||||
let pragmaNode = getCustomPragmaNodeSmart(n, $cp)
|
cp.expectKind {nnkSym, nnkOpenSymChoice, nnkClosedSymChoice}
|
||||||
|
let pragmaNodes = getCustomPragmaNodesSmart(n, $cp)
|
||||||
|
if pragmaNodes.len == 0:
|
||||||
|
error(n.repr & " doesn't have any custom pragmas")
|
||||||
|
let pragmaNode = pragmaNodes[^1]
|
||||||
|
|
||||||
case pragmaNode.kind
|
case pragmaNode.kind
|
||||||
of nnkPragmaCallKinds:
|
of nnkPragmaCallKinds:
|
||||||
assert pragmaNode[0] == cp
|
|
||||||
if pragmaNode.len == 2:
|
if pragmaNode.len == 2:
|
||||||
result = pragmaNode[1]
|
result = pragmaNode[1]
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -165,11 +165,14 @@ type
|
||||||
let a {.defaultValue(4).}: proc(x: int) = nil
|
let a {.defaultValue(4).}: proc(x: int) = nil
|
||||||
var b: MyAnnotatedProcType = nil
|
var b: MyAnnotatedProcType = nil
|
||||||
var c: proc(x: int): void {.defaultValue(5).} = nil
|
var c: proc(x: int): void {.defaultValue(5).} = nil
|
||||||
|
var d {.defaultValue(44).}: MyAnnotatedProcType = nil
|
||||||
static:
|
static:
|
||||||
doAssert hasCustomPragma(a, defaultValue)
|
doAssert hasCustomPragma(a, defaultValue)
|
||||||
doAssert hasCustomPragma(MyAnnotatedProcType, defaultValue)
|
doAssert hasCustomPragma(MyAnnotatedProcType, defaultValue)
|
||||||
doAssert hasCustomPragma(b, defaultValue)
|
doAssert hasCustomPragma(b, defaultValue)
|
||||||
doAssert hasCustomPragma(typeof(c), defaultValue)
|
doAssert hasCustomPragma(typeof(c), defaultValue)
|
||||||
|
doAssert getCustomPragmaVal(d, defaultValue) == 44
|
||||||
|
doAssert getCustomPragmaVal(typeof(d), defaultValue) == 4
|
||||||
|
|
||||||
# bug #8371
|
# bug #8371
|
||||||
template thingy {.pragma.}
|
template thingy {.pragma.}
|
||||||
|
|
@ -405,3 +408,10 @@ 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.}
|
||||||
|
|
||||||
|
var bb {.haha, hoho, hehe(1, 2), haha, hehe("hi", "hu", "he").} = 3
|
||||||
|
|
||||||
|
# left-to-right priority/override order for getCustomPragmaVal
|
||||||
|
assert bb.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue