tnilcheck.nim 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. discard """
  2. action: compile
  3. """
  4. import tables
  5. {.experimental: "strictNotNil".}
  6. type
  7. Nilable* = ref object
  8. a*: int
  9. field*: Nilable
  10. NonNilable* = Nilable not nil
  11. Nilable2* = nil NonNilable
  12. # proc `[]`(a: Nilable, b: int): Nilable =
  13. # nil
  14. # Nilable tests
  15. # test deref
  16. proc testDeref(a: Nilable) =
  17. echo a.a > 0 #[tt.Warning
  18. ^ can't deref a, it might be nil
  19. ]#
  20. # # # test if else
  21. proc testIfElse(a: Nilable) =
  22. if a.isNil:
  23. echo a.a #[tt.Warning
  24. ^ can't deref a, it is nil
  25. ]#
  26. else:
  27. echo a.a # ok
  28. proc testIfNoElse(a: Nilable) =
  29. if a.isNil:
  30. echo a.a #[tt.Warning
  31. ^ can't deref a, it is nil
  32. ]#
  33. echo a.a #[tt.Warning
  34. ^ can't deref a, it might be nil
  35. ]#
  36. proc testIfReturn(a: Nilable) =
  37. if not a.isNil:
  38. return
  39. echo a.a #[tt.Warning
  40. ^ can't deref a, it is nil
  41. ]#
  42. proc testIfBreak(a: seq[Nilable]) =
  43. for b in a:
  44. if not b.isNil:
  45. break
  46. echo b.a #[tt.Warning
  47. ^ can't deref b, it is nil
  48. ]#
  49. proc testIfContinue(a: seq[Nilable]) =
  50. for b in a:
  51. if not b.isNil:
  52. continue
  53. echo b.a #[tt.Warning
  54. ^ can't deref b, it is nil
  55. ]#
  56. proc testIfRaise(a: Nilable) =
  57. if not a.isNil:
  58. raise newException(ValueError, "")
  59. echo a.a #[tt.Warning
  60. ^ can't deref a, it is nil
  61. ]#
  62. proc testIfElif(a: Nilable) =
  63. var c = 0
  64. if c == 0:
  65. echo a.a #[tt.Warning
  66. ^ can't deref a, it might be nil
  67. ]#
  68. elif c == 1:
  69. echo a.a #[tt.Warning
  70. ^ can't deref a, it might be nil
  71. ]#
  72. elif not a.isNil:
  73. echo a.a # ok
  74. elif c == 2:
  75. echo 0
  76. else:
  77. echo a.a #[tt.Warning
  78. ^ can't deref a, it is nil
  79. ]#
  80. proc testAssignUnify(a: Nilable, b: int) =
  81. var a2 = a
  82. if b == 0:
  83. a2 = Nilable()
  84. echo a2.a #[tt.Warning
  85. ^ can't deref a2, it might be nil
  86. ]#
  87. # # test assign in branch and unifiying that with the main block after end of branch
  88. proc testAssignUnifyNil(a: Nilable, b: int) =
  89. var a2 = a
  90. if b == 0:
  91. a2 = nil
  92. echo a2.a #[tt.Warning
  93. ^ can't deref a2, it might be nil
  94. ]#
  95. # test loop
  96. proc testForLoop(a: Nilable) =
  97. var b = Nilable()
  98. for i in 0 .. 5:
  99. echo b.a #[tt.Warning
  100. ^ can't deref b, it might be nil
  101. ]#
  102. if i == 2:
  103. b = a
  104. echo b.a #[tt.Warning
  105. ^ can't deref b, it might be nil
  106. ]#
  107. # # TODO implement this after discussion
  108. # # proc testResultCompoundNonNilableElement(a: Nilable): (NonNilable, NonNilable) = #[t t.Warning
  109. # # ^ result might be not initialized, so it or an element might be nil
  110. # # ]#
  111. # # if not a.isNil:
  112. # # result[0] = a #[t t.Warning
  113. # # ^ can't assign nilable to non nilable: it might be nil
  114. # # #]
  115. # # proc testNonNilDeref(a: NonNilable) =
  116. # # echo a.a # ok
  117. # # # not only calls: we can use partitions for dependencies for field aliases
  118. # # # so we can detect on change what does this affect or was this mutated between us and the original field
  119. # # proc testRootAliasField(a: Nilable) =
  120. # # var aliasA = a
  121. # # if not a.isNil and not a.field.isNil:
  122. # # aliasA.field = nil
  123. # # # a.field = nil
  124. # # # aliasA = nil
  125. # # echo a.field.a # [tt.Warning
  126. # # ^ can't deref a.field, it might be nil
  127. # # ]#
  128. proc testAliasChanging(a: Nilable) =
  129. var b = a
  130. var aliasA = b
  131. b = Nilable()
  132. if not b.isNil:
  133. echo aliasA.a #[tt.Warning
  134. ^ can't deref aliasA, it might be nil
  135. ]#
  136. # # TODO
  137. # # proc testAliasUnion(a: Nilable) =
  138. # # var a2 = a
  139. # # var b = a2
  140. # # if a.isNil:
  141. # # b = Nilable()
  142. # # a2 = nil
  143. # # else:
  144. # # a2 = Nilable()
  145. # # b = a2
  146. # # if not b.isNil:
  147. # # echo a2.a #[ tt.Warning
  148. # # ^ can't deref a2, it might be nil
  149. # # ]#
  150. # # TODO after alias support
  151. # #proc callVar(a: var Nilable) =
  152. # # a.field = nil
  153. # # TODO ptr support
  154. # # proc testPtrAlias(a: Nilable) =
  155. # # # pointer to a: hm.
  156. # # # alias to a?
  157. # # var ptrA = a.addr # {0, 1}
  158. # # if not a.isNil: # {0, 1}
  159. # # ptrA[] = nil # {0, 1} 0: MaybeNil 1: MaybeNil
  160. # # echo a.a #[ tt.Warning
  161. # # ^ can't deref a, it might be nil
  162. # # ]#
  163. # # TODO field stuff
  164. # # currently it just doesnt support dot, so accidentally it shows a warning but because that
  165. # # not alias i think
  166. # # proc testFieldAlias(a: Nilable) =
  167. # # var b = a # {0, 1} {2}
  168. # # if not a.isNil and not a.field.isNil: # {0, 1} {2}
  169. # # callVar(b) # {0, 1} {2} 0: Safe 1: Safe
  170. # # echo a.field.a #[ tt.Warning
  171. # # ^ can't deref a.field, it might be nil
  172. # # ]#
  173. # #
  174. # # proc testUniqueHashTree(a: Nilable): Nilable =
  175. # # # TODO what would be a clash
  176. # # var field = 0
  177. # # if not a.isNil and not a.field.isNil:
  178. # # # echo a.field.a
  179. # # echo a[field].a
  180. # # result = Nilable()
  181. # # proc testSeparateShadowingResult(a: Nilable): Nilable =
  182. # # result = Nilable()
  183. # # if not a.isNil:
  184. # # var result: Nilable = nil
  185. # # echo result.a
  186. proc testCStringDeref(a: cstring) =
  187. echo a[0] #[tt.Warning
  188. ^ can't deref a, it might be nil
  189. ]#
  190. proc testNilablePtr(a: ptr int) =
  191. if not a.isNil:
  192. echo a[] # ok
  193. echo a[] #[tt.Warning
  194. ^ can't deref a, it might be nil
  195. ]#
  196. # # proc testNonNilPtr(a: ptr int not nil) =
  197. # # echo a[] # ok
  198. proc raiseCall: NonNilable = #[tt.Warning
  199. ^ return value is nil
  200. ]#
  201. raise newException(ValueError, "raise for test")
  202. # proc testTryCatch(a: Nilable) =
  203. # var other = a
  204. # try:
  205. # other = raiseCall()
  206. # except:
  207. # discard
  208. # echo other.a #[ tt.Warning
  209. # ^ can't deref other, it might be nil
  210. # ]#
  211. # # proc testTryCatchDetectNoRaise(a: Nilable) =
  212. # # var other = Nilable()
  213. # # try:
  214. # # other = nil
  215. # # other = a
  216. # # other = Nilable()
  217. # # except:
  218. # # other = nil
  219. # # echo other.a # ok
  220. # # proc testTryCatchDetectFinally =
  221. # # var other = Nilable()
  222. # # try:
  223. # # other = nil
  224. # # other = Nilable()
  225. # # except:
  226. # # other = Nilable()
  227. # # finally:
  228. # # other = nil
  229. # # echo other.a # can't deref other: it is nil
  230. # # proc testTryCatchDetectNilableWithRaise(b: bool) =
  231. # # var other = Nilable()
  232. # # try:
  233. # # if b:
  234. # # other = nil
  235. # # else:
  236. # # other = Nilable()
  237. # # var other2 = raiseCall()
  238. # # except:
  239. # # echo other.a # ok
  240. # # echo other.a # can't deref a: it might be nil
  241. # # proc testRaise(a: Nilable) =
  242. # # if a.isNil:
  243. # # raise newException(ValueError, "a == nil")
  244. # # echo a.a # ok
  245. # # proc testBlockScope(a: Nilable) =
  246. # # var other = a
  247. # # block:
  248. # # var other = Nilable()
  249. # # echo other.a # ok
  250. # # echo other.a # can't deref other: it might be nil
  251. # # ok we can't really get the nil value from here, so should be ok
  252. # # proc testDirectRaiseCall: NonNilable =
  253. # # var a = raiseCall()
  254. # # result = NonNilable()
  255. # # proc testStmtList =
  256. # # var a = Nilable()
  257. # # block:
  258. # # a = nil
  259. # # a = Nilable()
  260. # # echo a.a # ok
  261. proc callChange(a: Nilable) =
  262. if not a.isNil:
  263. a.field = nil
  264. proc testCallChangeField =
  265. var a = Nilable()
  266. a.field = Nilable()
  267. callChange(a)
  268. echo a.field.a #[ tt.Warning
  269. ^ can't deref a.field, it might be nil
  270. ]#
  271. proc testReassignVarWithField =
  272. var a = Nilable()
  273. a.field = Nilable()
  274. echo a.field.a # ok
  275. a = Nilable()
  276. echo a.field.a #[ tt.Warning
  277. ^ can't deref a.field, it might be nil
  278. ]#
  279. proc testItemDeref(a: var seq[Nilable]) =
  280. echo a[0].a #[tt.Warning
  281. ^ can't deref a[0], it might be nil
  282. ]#
  283. a[0] = Nilable() # good: now .. if we dont track, how do we know
  284. echo a[0].a # ok
  285. echo a[1].a #[tt.Warning
  286. ^ can't deref a[1], it might be nil
  287. ]#
  288. var b = 1
  289. if a[b].isNil:
  290. echo a[1].a #[tt.Warning
  291. ^ can't deref a[1], it might be nil
  292. ]#
  293. var c = 0
  294. echo a[c].a #[tt.Warning
  295. ^ can't deref a[c], it might be nil
  296. ]#
  297. # known false positive
  298. if not a[b].isNil:
  299. echo a[b].a #[tt.Warning
  300. ^ can't deref a[b], it might be nil
  301. ]#
  302. const c = 0
  303. if a[c].isNil:
  304. echo a[0].a #[tt.Warning
  305. ^ can't deref a[0], it is nil
  306. ]#
  307. a[c] = Nilable()
  308. echo a[0].a # ok
  309. # # # proc test10(a: Nilable) =
  310. # # # if not a.isNil and not a.b.isNil:
  311. # # # c_memset(globalA.addr, 0, globalA.sizeOf.csize_t)
  312. # # # globalA = nil
  313. # # # echo a.a # can't deref a: it might be nil