summary refs log tree commit diff stats
path: root/tests/pragmas/tcustom_pragma.nim
diff options
context:
space:
mode:
Diffstat (limited to 'tests/pragmas/tcustom_pragma.nim')
-rw-r--r--tests/pragmas/tcustom_pragma.nim540
1 files changed, 540 insertions, 0 deletions
diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim
new file mode 100644
index 000000000..11a6df813
--- /dev/null
+++ b/tests/pragmas/tcustom_pragma.nim
@@ -0,0 +1,540 @@
+{.experimental: "notnil".}
+
+import macros, asyncmacro, asyncfutures
+
+block:
+  template myAttr() {.pragma.}
+
+  proc myProc():int {.myAttr.} = 2
+  const hasMyAttr = myProc.hasCustomPragma(myAttr)
+  static:
+    doAssert(hasMyAttr)
+
+block:
+  template myAttr(a: string) {.pragma.}
+
+  type
+    MyObj = object
+      myField1, myField2 {.myAttr: "hi".}: int
+
+    MyGenericObj[T] = object
+      myField1, myField2 {.myAttr: "hi".}: int
+
+    MyOtherObj = MyObj
+
+
+  var o: MyObj
+  static:
+    doAssert o.myField2.hasCustomPragma(myAttr)
+    doAssert(not o.myField1.hasCustomPragma(myAttr))
+    doAssert(not o.myField1.hasCustomPragma(MyObj))
+    doAssert(not o.myField1.hasCustomPragma(MyOtherObj))
+
+  var ogen: MyGenericObj[int]
+  static:
+    doAssert ogen.myField2.hasCustomPragma(myAttr)
+    doAssert(not ogen.myField1.hasCustomPragma(myAttr))
+    doAssert(not ogen.myField1.hasCustomPragma(MyGenericObj))
+    doAssert(not ogen.myField1.hasCustomPragma(MyGenericObj))
+
+
+import custom_pragma
+block: # A bit more advanced case
+  type
+    Subfield {.defaultValue: "catman".} = object
+      `c`* {.serializationKey: "cc".}: float
+
+    MySerializable = object
+      a {.serializationKey"asdf", defaultValue: 5.} : int
+      b {.custom_pragma.defaultValue"hello".} : int
+      field: Subfield
+      d {.alternativeKey("df", 5).}: float
+      e {.alternativeKey(V = 5).}: seq[bool]
+
+  proc myproc(x: int, s: string) {.alternativeKey(V = 5), serializationKey"myprocSS".} =
+    echo x, s
+
+
+  var s: MySerializable
+
+  const aDefVal = s.a.getCustomPragmaVal(defaultValue)
+  static: doAssert(aDefVal == 5)
+
+  const aSerKey = s.a.getCustomPragmaVal(serializationKey)
+  static: doAssert(aSerKey == "asdf")
+
+  const cSerKey = getCustomPragmaVal(s.field.c, serializationKey)
+  static: doAssert(cSerKey == "cc")
+
+  const procSerKey = getCustomPragmaVal(myproc, serializationKey)
+  static: doAssert(procSerKey == "myprocSS")
+
+  static: doAssert(hasCustomPragma(myproc, alternativeKey))
+
+  const hasFieldCustomPragma = s.field.hasCustomPragma(defaultValue)
+  static: doAssert(hasFieldCustomPragma == false)
+
+  # pragma on an object
+  static:
+    doAssert Subfield.hasCustomPragma(defaultValue)
+    doAssert(Subfield.getCustomPragmaVal(defaultValue) == "catman")
+
+    doAssert hasCustomPragma(type(s.field), defaultValue)
+
+  proc foo(s: var MySerializable) =
+    static: doAssert(s.a.getCustomPragmaVal(defaultValue) == 5)
+
+  foo(s)
+
+block: # ref types
+  type
+    Node = object of RootObj
+      left {.serializationKey:"l".}, right {.serializationKey:"r".}: NodeRef
+    NodeRef = ref Node
+    NodePtr = ptr Node
+
+    SpecialNodeRef = ref object of NodeRef
+      data {.defaultValue"none".}: string
+
+    MyFile {.defaultValue: "closed".} = ref object
+      path {.defaultValue: "invalid".}: string
+
+    TypeWithoutPragma = object
+
+  var s = NodeRef()
+
+  const
+    leftSerKey = getCustomPragmaVal(s.left, serializationKey)
+    rightSerKey = getCustomPragmaVal(s.right, serializationKey)
+  static:
+    doAssert leftSerKey == "l"
+    doAssert rightSerKey == "r"
+
+  var specS = SpecialNodeRef()
+
+  const
+    dataDefVal = hasCustomPragma(specS.data, defaultValue)
+    specLeftSerKey = hasCustomPragma(specS.left, serializationKey)
+  static:
+    doAssert dataDefVal == true
+    doAssert specLeftSerKey == true
+
+  var ptrS = NodePtr(nil)
+  const
+    ptrRightSerKey = getCustomPragmaVal(ptrS.right, serializationKey)
+  static:
+    doAssert ptrRightSerKey == "r"
+
+  var f = MyFile()
+  const
+    fileDefVal = f.getCustomPragmaVal(defaultValue)
+    filePathDefVal = f.path.getCustomPragmaVal(defaultValue)
+  static:
+    doAssert fileDefVal == "closed"
+    doAssert filePathDefVal == "invalid"
+
+  static:
+    doAssert TypeWithoutPragma.hasCustomPragma(defaultValue) == false
+
+block:
+  type
+    VariantKind = enum
+      variInt,
+      variFloat
+      variString
+      variNestedCase
+    Variant = object
+      case kind: VariantKind
+      of variInt: integer {.serializationKey: "int".}: BiggestInt
+      of variFloat: floatp: BiggestFloat
+      of variString: str {.serializationKey: "string".}: string
+      of variNestedCase:
+        case nestedKind: VariantKind
+        of variInt..variNestedCase: nestedItem {.defaultValue: "Nimmers of the world, unite!".}: int
+
+  let vari = Variant(kind: variInt)
+
+  const
+    hasIntSerKey = vari.integer.hasCustomPragma(serializationKey)
+    strSerKey = vari.str.getCustomPragmaVal(serializationKey)
+    nestedItemDefVal = vari.nestedItem.getCustomPragmaVal(defaultValue)
+
+  static:
+    doAssert hasIntSerKey
+    doAssert strSerKey == "string"
+    doAssert nestedItemDefVal == "Nimmers of the world, unite!"
+
+block:
+  template simpleAttr {.pragma.}
+
+  type Annotated {.simpleAttr.} = object
+
+  proc generic_proc[T]() =
+    doAssert Annotated.hasCustomPragma(simpleAttr)
+
+#--------------------------------------------------------------------------
+# Pragma on proc type
+
+type
+  MyAnnotatedProcType {.defaultValue(4).} = proc(x: int)
+
+let a {.defaultValue(4).}: proc(x: int)  = nil
+var b: MyAnnotatedProcType = nil
+var c: proc(x: int): void {.defaultValue(5).}  = nil
+var d {.defaultValue(44).}: MyAnnotatedProcType = nil
+static:
+  doAssert hasCustomPragma(a, defaultValue)
+  doAssert hasCustomPragma(MyAnnotatedProcType, defaultValue)
+  doAssert hasCustomPragma(b, defaultValue)
+  doAssert hasCustomPragma(typeof(c), defaultValue)
+  doAssert getCustomPragmaVal(d, defaultValue) == 44
+  doAssert getCustomPragmaVal(typeof(d), defaultValue) == 4
+
+# bug #8371
+template thingy {.pragma.}
+
+type
+  Cardinal = enum
+    north, east, south, west
+  Something = object
+    a: float32
+    case cardinal: Cardinal
+    of north:
+      b {.thingy.}: int
+    of east:
+      c: int
+    of south: discard
+    else: discard
+
+var foo: Something
+foo.cardinal = north
+doAssert foo.b.hasCustomPragma(thingy) == true
+
+proc myproc(s: string): int =
+  {.thingy.}:
+    s.len
+
+doAssert myproc("123") == 3
+
+let xx = compiles:
+  proc myproc_bad(s: string): int =
+    {.not_exist.}:
+      s.len
+doAssert: xx == false
+
+macro checkSym(s: typed{nkSym}): untyped =
+  let body = s.getImpl.body
+  doAssert body[1].kind == nnkPragmaBlock
+  doAssert body[1][0].kind == nnkPragma
+  doAssert body[1][0][0] == bindSym"thingy"
+
+checkSym(myproc)
+
+# var and let pragmas
+block:
+  template myAttr() {.pragma.}
+  template myAttr2(x: int) {.pragma.}
+  template myAttr3(x: string) {.pragma.}
+
+  type
+    MyObj2 = ref object
+    MyObjNotNil = MyObj2 not nil
+
+  let a {.myAttr,myAttr2(2),myAttr3:"test".}: int = 0
+  let b {.myAttr,myAttr2(2),myAttr3:"test".} = 0
+  var x {.myAttr,myAttr2(2),myAttr3:"test".}: int = 0
+  var y {.myAttr,myAttr2(2),myAttr3:"test".}: int
+  var z {.myAttr,myAttr2(2),myAttr3:"test".} = 0
+  var z2 {.myAttr.}: MyObjNotNil
+
+  template check(s: untyped) =
+    doAssert s.hasCustomPragma(myAttr)
+    doAssert s.hasCustomPragma(myAttr2)
+    doAssert s.getCustomPragmaVal(myAttr2) == 2
+    doAssert s.hasCustomPragma(myAttr3)
+    doAssert s.getCustomPragmaVal(myAttr3) == "test"
+
+  check(a)
+  check(b)
+  check(x)
+  check(y)
+  check(z)
+
+# pragma with multiple fields
+block:
+  template myAttr(first: string, second: int, third: float) {.pragma.}
+  let a {.myAttr("one", 2, 3.0).} = 0
+  let ps = a.getCustomPragmaVal(myAttr)
+  doAssert ps.first == ps[0] and ps.first == "one"
+  doAssert ps.second == ps[1] and ps.second == 2
+  doAssert ps.third == ps[2] and ps.third == 3.0
+
+# pragma with implicit&explicit generic types
+block:
+  template fooBar[T](x: T; c: static[int] = 42; m: char) {.pragma.}
+  var e {.fooBar("foo", 123, 'u').}: int
+  doAssert(hasCustomPragma(e, fooBar))
+  doAssert(getCustomPragmaVal(e, fooBar).c == 123)
+
+block:
+  macro expectedAst(expectedRepr: static[string], input: untyped): untyped =
+    doAssert input.treeRepr & "\n" == expectedRepr
+    return input
+
+  macro expectedAstRepr(expectedRepr: static[string], input: untyped): untyped =
+    doAssert input.repr == expectedRepr
+    return input
+
+  const procTypeAst = """
+ProcTy
+  FormalParams
+    Empty
+    IdentDefs
+      Ident "x"
+      Ident "int"
+      Empty
+  Pragma
+    Ident "async"
+"""
+
+  type
+    Foo = proc (x: int) {.expectedAst(procTypeAst), async.}
+
+  static: doAssert Foo is proc(x: int): Future[void]
+
+  const asyncProcTypeAst = """
+proc (s: string): Future[void] {..}"""
+  # using expectedAst would show `OpenSymChoice` for Future[void], which is fragile.
+  type
+    Bar = proc (s: string) {.async, expectedAstRepr(asyncProcTypeAst).}
+
+  static: doAssert Bar is proc(x: string): Future[void]
+
+  const typeAst = """
+TypeDef
+  PragmaExpr
+    Ident "Baz"
+    Pragma
+  Empty
+  ObjectTy
+    Empty
+    Empty
+    RecList
+      IdentDefs
+        Ident "x"
+        Ident "string"
+        Empty
+"""
+
+  type
+    Baz {.expectedAst(typeAst).} = object
+      x: string
+
+  static: doAssert Baz.x is string
+
+  const procAst = """
+ProcDef
+  Ident "bar"
+  Empty
+  Empty
+  FormalParams
+    Ident "string"
+    IdentDefs
+      Ident "s"
+      Ident "string"
+      Empty
+  Empty
+  Empty
+  StmtList
+    ReturnStmt
+      Ident "s"
+"""
+
+  proc bar(s: string): string {.expectedAst(procAst).} =
+    return s
+
+  static: doAssert bar("x") == "x"
+
+#------------------------------------------------------
+# bug #13909
+
+template dependency*(id: string, weight = 0.0) {.pragma.}
+
+type
+  MyObject* = object
+    provider*: proc(obj: string): pointer {.dependency("Data/" & obj, 16.1), noSideEffect.}
+
+proc myproc(obj: string): string {.dependency("Data/" & obj, 16.1).} =
+  result = obj
+
+# bug 12523
+template myCustomPragma {.pragma.}
+
+type
+  RefType = ref object
+    field {.myCustomPragma.}: int
+
+  ObjType = object
+    field {.myCustomPragma.}: int
+  RefType2 = ref ObjType
+
+block:
+  let x = RefType()
+  for fieldName, fieldSym in fieldPairs(x[]):
+    doAssert hasCustomPragma(fieldSym, myCustomPragma)
+
+block:
+  let x = RefType2()
+  for fieldName, fieldSym in fieldPairs(x[]):
+    doAssert hasCustomPragma(fieldSym, myCustomPragma)
+
+# bug 8457
+block:
+  template world {.pragma.}
+
+  type
+    Hello = ref object
+      a: float32
+      b {.world.}: int
+
+  discard Hello(a: 1.0, b: 12)
+
+# test routines
+block:
+  template prag {.pragma.}
+  proc hello {.prag.} = discard
+  iterator hello2: int {.prag.} = discard
+  template hello3(x: int): int {.prag.} = x
+  macro hello4(x: int): int {.prag.} = x
+  func hello5(x: int): int {.prag.} = x
+  doAssert hello.hasCustomPragma(prag)
+  doAssert hello2.hasCustomPragma(prag)
+  doAssert hello3.hasCustomPragma(prag)
+  doAssert hello4.hasCustomPragma(prag)
+  doAssert hello5.hasCustomPragma(prag)
+
+# test push doesn't break
+block:
+  template prag {.pragma.}
+  {.push prag.}
+  proc hello = discard
+  iterator hello2: int = discard
+  template hello3(x: int): int = x
+  macro hello4(x: int): int = x
+  func hello5(x: int): int = x
+  type
+    Foo = enum a
+    Bar[T] = ref object of RootObj
+      x: T
+      case y: bool
+      of false: discard
+      else:
+        when true: discard
+  for a in [1]: discard a
+  {.pop.}
+
+# issue #11511
+when false:
+  template myAttr {.pragma.}
+
+  type TObj = object
+      a {.myAttr.}: int
+
+  macro hasMyAttr(t: typedesc): untyped =
+    let objTy = t.getType[1].getType
+    let recList = objTy[2]
+    let sym = recList[0]
+    assert sym.kind == nnkSym and sym.eqIdent("a")
+    let hasAttr = sym.hasCustomPragma(myAttr)
+    newLit(hasAttr)
+
+  doAssert hasMyAttr(TObj)
+
+
+# bug #11415
+template noserialize() {.pragma.}
+
+type
+  Point[T] = object
+    x, y: T
+
+  ReplayEventKind = enum
+    FoodAppeared, FoodEaten, DirectionChanged
+
+  ReplayEvent = object
+    case kind: ReplayEventKind
+    of FoodEaten, FoodAppeared: # foodPos is in multiple branches
+      foodPos {.noserialize.}: Point[float]
+    of DirectionChanged:
+      playerPos: float
+let ev = ReplayEvent(
+    kind: FoodEaten,
+    foodPos: Point[float](x: 5.0, y: 1.0)
+  )
+
+doAssert ev.foodPos.hasCustomPragma(noserialize)
+
+
+when false:
+  # misc
+  {.pragma: haha.}
+  {.pragma: hoho.}
+  template hehe(key, val: string, haha) {.pragma.}
+
+  type A {.haha, hoho, haha, hehe("hi", "hu", "he").} = int
+
+  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")
+
+{.experimental: "dynamicBindSym".}
+
+# const
+block:
+  template myAttr() {.pragma.}
+  template myAttr2(x: int) {.pragma.}
+  template myAttr3(x: string) {.pragma.}
+
+  type
+    MyObj2 = ref object
+
+  const a {.myAttr,myAttr2(2),myAttr3:"test".}: int = 0
+  const b {.myAttr,myAttr2(2),myAttr3:"test".} = 0
+
+  macro forceHasCustomPragma(x: untyped, y: typed): untyped =
+    var x = bindSym(x.repr)
+    for c in x:
+      if c.symKind == nskConst:
+        x = c
+        break
+    result = getAst(hasCustomPragma(x, y))
+
+  macro forceGetCustomPragmaVal(x: untyped, y: typed): untyped =
+    var x = bindSym(x.repr)
+    for c in x:
+      if c.symKind == nskConst:
+        x = c
+        break
+    result = getAst(getCustomPragmaVal(x, y))
+
+  template check(s: untyped) =
+    doAssert forceHasCustomPragma(s, myAttr)
+    doAssert forceHasCustomPragma(s, myAttr2)
+    doAssert forceGetCustomPragmaVal(s, myAttr2) == 2
+    doAssert forceHasCustomPragma(s, myAttr3)
+    doAssert forceGetCustomPragmaVal(s, myAttr3) == "test"
+
+  check(a)
+  check(b)
+
+block: # https://forum.nim-lang.org/t/12522, backticks
+  template `mypragma`() {.pragma.}
+  # Error: invalid pragma: `mypragma`
+  type Test = object
+    field {.`mypragma`.}: int
+  doAssert Test().field.hasCustomPragma(mypragma)