nullSafety.ml 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695
  1. open Globals
  2. open Ast
  3. open Type
  4. type safety_message = {
  5. sm_msg : string;
  6. sm_pos : pos;
  7. }
  8. type safety_report = {
  9. mutable sr_errors : safety_message list;
  10. }
  11. let add_error report msg pos =
  12. let error = { sm_msg = ("Null safety: " ^ msg); sm_pos = pos; } in
  13. if not (List.mem error report.sr_errors) then
  14. report.sr_errors <- error :: report.sr_errors;
  15. type scope_type =
  16. | STNormal
  17. | STLoop
  18. | STClosure
  19. (* A closure which gets executed along the "normal" program flow without being delayed or stored somewhere *)
  20. | STImmediateClosure
  21. type safety_unify_error =
  22. | NullSafetyError
  23. exception Safety_error of safety_unify_error
  24. let safety_error () : unit = raise (Safety_error NullSafetyError)
  25. type safety_mode =
  26. | SMOff
  27. | SMLoose
  28. | SMStrict
  29. | SMStrictThreaded
  30. (**
  31. Terminates compiler process and prints user-friendly instructions about filing an issue in compiler repo.
  32. *)
  33. let fail ?msg hxpos mlpos =
  34. let msg =
  35. (Lexer.get_error_pos (Printf.sprintf "%s:%d:") hxpos) ^ ": "
  36. ^ "Null safety: " ^ (match msg with Some msg -> msg | _ -> "unexpected expression.") ^ "\n"
  37. ^ "Submit an issue to https://github.com/HaxeFoundation/haxe/issues with expression example and following information:"
  38. in
  39. match mlpos with
  40. | (file, line, _, _) ->
  41. Printf.eprintf "%s\n" msg;
  42. Printf.eprintf "%s:%d\n" file line;
  43. die "" __LOC__
  44. (**
  45. Returns human-readable string representation of specified type
  46. *)
  47. let str_type = s_type (print_context())
  48. (**
  49. Returns human-readable representation of specified expression
  50. *)
  51. let str_expr = s_expr_pretty false "\t" true str_type
  52. let is_string_type t =
  53. match t with
  54. | TInst ({ cl_path = ([], "String")}, _)
  55. | TAbstract ({ a_path = ([],"Null") },[TInst ({ cl_path = ([], "String")}, _)]) -> true
  56. | _ -> false
  57. (**
  58. Check for explicit `Null<>` typing
  59. *)
  60. let rec is_nullable_type ?(dynamic_is_nullable=false) = function
  61. | TMono r ->
  62. (match r.tm_type with None -> false | Some t -> is_nullable_type t)
  63. | TAbstract ({ a_path = ([],"Null") },[t]) ->
  64. true
  65. | TAbstract ({ a_path = ([],"Any") },[]) ->
  66. false
  67. | TAbstract (a,tl) when not (Meta.has Meta.CoreType a.a_meta) ->
  68. is_nullable_type (apply_params a.a_params tl a.a_this)
  69. | TLazy f ->
  70. is_nullable_type (lazy_type f)
  71. | TType (t,tl) ->
  72. is_nullable_type (apply_typedef t tl)
  73. | (TDynamic _) as t ->
  74. dynamic_is_nullable && t == t_dynamic
  75. | _ ->
  76. false
  77. (*
  78. (**
  79. Check if `callee` represents `trace`
  80. *)
  81. let is_trace_expr callee =
  82. match callee.eexpr with
  83. | TIdent "`trace" -> true
  84. | _ -> false *)
  85. (**
  86. If `expr` is a TCast or TMeta, returns underlying expression (recursively bypassing nested casts).
  87. Otherwise returns `expr` as is.
  88. If `stay_safe` is true, then casts to non-nullable types won't be revealed and an expression will stay intact.
  89. *)
  90. let rec reveal_expr ?(stay_safe=true) expr =
  91. match expr.eexpr with
  92. | TCast (e, _) when not stay_safe || is_nullable_type expr.etype -> reveal_expr e
  93. | TMeta (_, e) -> reveal_expr e
  94. | _ -> expr
  95. (**
  96. Try to get a human-readable representation of an `expr`
  97. *)
  98. let symbol_name expr =
  99. match (reveal_expr ~stay_safe:false expr).eexpr with
  100. | TField (_, access) -> field_name access
  101. | TIdent name -> name
  102. | TLocal { v_name = name } -> name
  103. | TNew _ -> "new"
  104. | _ -> ""
  105. type safety_subject =
  106. (*
  107. Fields accessed through a static access are identified by the class path and the field name.
  108. E.g.
  109. `pack.MyClass.field` is `((["pack"], "MyClass"), ["field"])`
  110. `pack.MyClass.field.sub` is `((["pack"], "MyClass"), ["field"; "sub"])`
  111. *)
  112. | SFieldOfClass of (path * (string list))
  113. (*
  114. Fields accessed through a local variable are identified by the var id and the field name.
  115. E.g.
  116. `v.field` is `(v.v_id, ["field"])`
  117. `v.field.sub` is `(v.v_id, ["field"; "sub"])`
  118. *)
  119. | SFieldOfLocalVar of (int * (string list))
  120. (*
  121. Fields accessed through `this` are identified by their names.
  122. E.g.
  123. `this.field` is `["field"]`
  124. `this.field.sub` is `["field"; "sub"]`
  125. *)
  126. | SFieldOfThis of (string list)
  127. (*
  128. Local variables - by tvar.v_id
  129. *)
  130. | SLocalVar of int
  131. (*
  132. For expressions, which cannot be checked agains `null` to become safe
  133. *)
  134. | SNotSuitable
  135. let rec get_subject mode expr =
  136. match (reveal_expr expr).eexpr with
  137. | TLocal v ->
  138. SLocalVar v.v_id
  139. | TField ({ eexpr = TTypeExpr _ }, FStatic (cls, field)) when (mode <> SMStrictThreaded) || (has_class_field_flag field CfFinal) ->
  140. SFieldOfClass (cls.cl_path, [field.cf_name])
  141. | TField ({ eexpr = TConst TThis }, (FInstance (_, _, field) | FAnon field)) when (mode <> SMStrictThreaded) || (has_class_field_flag field CfFinal) ->
  142. SFieldOfThis [field.cf_name]
  143. | TField ({ eexpr = TLocal v }, (FInstance (_, _, field) | FAnon field)) when (mode <> SMStrictThreaded) || (has_class_field_flag field CfFinal) ->
  144. SFieldOfLocalVar (v.v_id, [field.cf_name])
  145. | TField (e, (FInstance (_, _, field) | FAnon field)) when (mode <> SMStrictThreaded) ->
  146. (match get_subject mode e with
  147. | SFieldOfClass (path, fields) -> SFieldOfClass (path, field.cf_name :: fields)
  148. | SFieldOfThis fields -> SFieldOfThis (field.cf_name :: fields)
  149. | SFieldOfLocalVar (var_id, fields) -> SFieldOfLocalVar (var_id, field.cf_name :: fields)
  150. |_ -> SNotSuitable
  151. )
  152. |_ -> SNotSuitable
  153. (**
  154. Check if provided expression is a subject to null safety.
  155. E.g. a call cannot be such a subject, because we cannot track null-state of the call result.
  156. *)
  157. let rec is_suitable mode expr =
  158. match (reveal_expr expr).eexpr with
  159. | TField ({ eexpr = TConst TThis }, FInstance _)
  160. | TField ({ eexpr = TLocal _ }, (FInstance _ | FAnon _))
  161. | TField ({ eexpr = TTypeExpr _ }, FStatic _)
  162. | TLocal _ -> true
  163. | TField (target, (FInstance _ | FStatic _ | FAnon _)) when mode <> SMStrictThreaded -> is_suitable mode target
  164. |_ -> false
  165. (**
  166. Returns a list of metadata attached to `callee` arguments.
  167. E.g. for
  168. ```
  169. function(@:meta1 a:Type1, b:Type2, @:meta2 c:Type3)
  170. ```
  171. will return `[ [@:meta1], [], [@:meta2] ]`
  172. *)
  173. let get_arguments_meta callee expected_args_count =
  174. let rec empty_list n =
  175. if n <= 0 then []
  176. else [] :: (empty_list (n - 1))
  177. in
  178. match callee.eexpr with
  179. | TField (_, FAnon field)
  180. | TField (_, FClosure (_,field))
  181. | TField (_, FStatic (_, field))
  182. | TField (_, FInstance (_, _, field)) ->
  183. (try
  184. match get_meta Meta.HaxeArguments field.cf_meta with
  185. | _,[EFunction(_,{ f_args = args }),_],_ when expected_args_count = List.length args ->
  186. List.map (fun (_,_,m,_,_) -> m) args
  187. | _ ->
  188. raise Not_found
  189. with Not_found ->
  190. empty_list expected_args_count
  191. )
  192. | TFunction { tf_args = args } when expected_args_count = List.length args ->
  193. List.map (fun (v,_) -> v.v_meta) args
  194. | _ ->
  195. empty_list expected_args_count
  196. class unificator =
  197. object(self)
  198. val stack = new_rec_stack()
  199. (**
  200. Check if it's possible to pass a value of type `a` to a place where a value of type `b` is expected.
  201. Raises `Safety_error` exception if it's not.
  202. *)
  203. method unify a b =
  204. if a == b then
  205. ()
  206. else
  207. match a, b with
  208. (* if `b` is nullable, no more checks needed *)
  209. | _, TAbstract ({ a_path = ([],"Null") },[t]) ->
  210. ()
  211. | TAbstract ({ a_path = ([],"Null") },[t]), _ when not (is_nullable_type b) ->
  212. safety_error()
  213. | TInst (_, a_params), TInst(_, b_params) when (List.length a_params) = (List.length b_params) ->
  214. List.iter2 self#unify a_params b_params
  215. | TAnon a_anon, TAnon b_anon ->
  216. self#unify_anon_to_anon a_anon b_anon
  217. | TInst (a_cls, a_params), TAnon b_anon ->
  218. self#unify_class_to_anon a_cls a_params b_anon
  219. | TFun a_signature, TFun b_signature ->
  220. self#unify_functions a_signature b_signature
  221. (* patterns below are used to reveal real type *)
  222. | TLazy f, _ ->
  223. self#unify (lazy_type f) b
  224. | _, TLazy f -> self#unify a (lazy_type f)
  225. | TMono t, _ ->
  226. (match t.tm_type with None -> () | Some t -> self#unify t b)
  227. | _, TMono t ->
  228. (match t.tm_type with None -> () | Some t -> self#unify a t)
  229. | TType (t,tl), _ ->
  230. self#unify_rec a b (fun() -> self#unify (apply_typedef t tl) b)
  231. | _, TType (t,tl) ->
  232. self#unify_rec a b (fun() -> self#unify a (apply_typedef t tl))
  233. | TAbstract (abstr,tl), _ when not (Meta.has Meta.CoreType abstr.a_meta) ->
  234. self#unify (apply_params abstr.a_params tl abstr.a_this) b
  235. | _, TAbstract (abstr,tl) when not (Meta.has Meta.CoreType abstr.a_meta) ->
  236. self#unify a (apply_params abstr.a_params tl abstr.a_this)
  237. | _ ->
  238. ()
  239. method unify_rec (a:t) (b:t) (frun:unit->unit) =
  240. let checked =
  241. rec_stack_exists
  242. (fun(a2,b2) -> fast_eq a a2 && fast_eq b b2)
  243. stack
  244. in
  245. if not checked then begin
  246. try
  247. stack.rec_stack <- (a, b) :: stack.rec_stack;
  248. frun();
  249. stack.rec_stack <- List.tl stack.rec_stack
  250. with
  251. | e ->
  252. stack.rec_stack <- List.tl stack.rec_stack;
  253. raise e
  254. end
  255. method private unify_anon_to_anon (a:tanon) (b:tanon) =
  256. PMap.iter
  257. (fun name b_field ->
  258. let a_field =
  259. try Some (PMap.find name a.a_fields)
  260. with Not_found -> None
  261. in
  262. match a_field with
  263. | None -> ()
  264. | Some a_field -> self#unify a_field.cf_type b_field.cf_type
  265. )
  266. b.a_fields
  267. method private unify_class_to_anon (a:tclass) (a_params:tparams) (b:tanon) =
  268. PMap.iter
  269. (fun name b_field ->
  270. let a_field =
  271. try Some (PMap.find name a.cl_fields)
  272. with Not_found -> None
  273. in
  274. match a_field with
  275. | None -> ()
  276. | Some a_field ->
  277. let a_type = apply_params a.cl_params a_params a_field.cf_type in
  278. self#unify a_type b_field.cf_type
  279. )
  280. b.a_fields
  281. method private unify_functions (a_args, a_result) (b_args, b_result) =
  282. (* check return type *)
  283. (match b_result with
  284. | TAbstract ({ a_path = ([], "Void") }, []) -> ()
  285. | _ -> self#unify a_result b_result;
  286. );
  287. (* check arguments *)
  288. let rec traverse a_args b_args =
  289. match a_args, b_args with
  290. | [], _ | _, [] -> ()
  291. | (_, _, a_arg) :: a_rest, (_, _, b_arg) :: b_rest ->
  292. self#unify b_arg a_arg;
  293. traverse a_rest b_rest
  294. in
  295. traverse a_args b_args
  296. end
  297. (**
  298. Check if `expr` is a `trace` (not a call, but identifier itself)
  299. *)
  300. let is_trace expr =
  301. match expr.eexpr with
  302. | TIdent "`trace" -> true
  303. | TField (_, FStatic ({ cl_path = (["haxe"], "Log") }, { cf_name = "trace" })) -> true
  304. | _ -> false
  305. (**
  306. If `t` represents `Null<SomeType>` this function returns `SomeType`.
  307. *)
  308. let rec unfold_null t =
  309. match t with
  310. | TMono r -> (match r.tm_type with None -> t | Some t -> unfold_null t)
  311. | TAbstract ({ a_path = ([],"Null") }, [t]) -> unfold_null t
  312. | TAbstract (abstr,tl) when not (Meta.has Meta.CoreType abstr.a_meta) -> unfold_null (apply_params abstr.a_params tl abstr.a_this)
  313. | TLazy f -> unfold_null (lazy_type f)
  314. | TType (t,tl) -> unfold_null (apply_typedef t tl)
  315. | _ -> t
  316. (**
  317. Shadow Type.error to avoid raising unification errors, which should not be raised from null-safety checks
  318. *)
  319. let safety_error () : unit = raise (Safety_error NullSafetyError)
  320. let accessed_field_name access =
  321. match access with
  322. | FInstance (_, _, { cf_name = name }) -> name
  323. | FStatic (_, { cf_name = name }) -> name
  324. | FAnon { cf_name = name } -> name
  325. | FDynamic name -> name
  326. | FClosure (_, { cf_name = name }) -> name
  327. | FEnum (_, { ef_name = name }) -> name
  328. (**
  329. Collect nullable local vars which are checked against `null`.
  330. Returns a tuple of (vars_checked_to_be_null * vars_checked_to_be_not_null) in case `condition` evaluates to `true`.
  331. *)
  332. let rec process_condition mode condition (is_nullable_expr:texpr->bool) callback =
  333. let nulls = ref []
  334. and not_nulls = ref [] in
  335. let add to_nulls expr =
  336. let expr = reveal_expr expr in
  337. if to_nulls then nulls := expr :: !nulls
  338. else not_nulls := expr :: !not_nulls
  339. in
  340. let rec traverse positive e =
  341. match e.eexpr with
  342. | TUnop (Not, Prefix, e) -> traverse (not positive) e
  343. | TBinop (OpEq, { eexpr = TConst TNull }, checked_expr) when is_suitable mode checked_expr ->
  344. add positive checked_expr
  345. | TBinop (OpEq, checked_expr, { eexpr = TConst TNull }) when is_suitable mode checked_expr ->
  346. add positive checked_expr
  347. | TBinop (OpNotEq, { eexpr = TConst TNull }, checked_expr) when is_suitable mode checked_expr ->
  348. add (not positive) checked_expr
  349. | TBinop (OpNotEq, checked_expr, { eexpr = TConst TNull }) when is_suitable mode checked_expr ->
  350. add (not positive) checked_expr
  351. | TBinop (OpEq, e, checked_expr) when is_suitable mode checked_expr && not (is_nullable_expr e) ->
  352. if positive then not_nulls := checked_expr :: !not_nulls
  353. | TBinop (OpEq, checked_expr, e) when is_suitable mode checked_expr && not (is_nullable_expr e) ->
  354. if positive then not_nulls := checked_expr :: !not_nulls
  355. | TBinop (OpBoolAnd, left_expr, right_expr) when positive ->
  356. traverse positive left_expr;
  357. traverse positive right_expr
  358. | TBinop (OpBoolAnd, left_expr, right_expr) when not positive ->
  359. List.iter
  360. (fun e ->
  361. let _, not_nulls = process_condition mode left_expr is_nullable_expr callback in
  362. List.iter (add true) not_nulls
  363. )
  364. [left_expr; right_expr]
  365. | TBinop (OpBoolOr, left_expr, right_expr) when not positive ->
  366. traverse positive left_expr;
  367. traverse positive right_expr
  368. | TBinop (OpBoolOr, left_expr, right_expr) when positive ->
  369. List.iter
  370. (fun e ->
  371. let nulls, _ = process_condition mode left_expr is_nullable_expr callback in
  372. List.iter (add true) nulls
  373. )
  374. [left_expr; right_expr]
  375. | TParenthesis e -> traverse positive e
  376. | _ -> callback e
  377. in
  378. traverse true condition;
  379. (!nulls, !not_nulls)
  380. (**
  381. Check if metadata contains @:nullSafety(Off) meta
  382. **)
  383. let rec contains_unsafe_meta metadata =
  384. match metadata with
  385. | [] -> false
  386. | (Meta.NullSafety, [(EConst (Ident "Off"), _)], _) :: _ -> true
  387. | _ :: rest -> contains_unsafe_meta rest
  388. (**
  389. Check if metadata contains @:nullSafety or @:nullSafety(true) meta
  390. **)
  391. let rec contains_safe_meta metadata =
  392. match metadata with
  393. | [] -> false
  394. | (Meta.NullSafety, [], _) :: _
  395. | (Meta.NullSafety, [(EConst (Ident ("Loose" | "Strict" | "StrictThreaded")), _)], _) :: _ -> true
  396. | _ :: rest -> contains_safe_meta rest
  397. let safety_enabled meta =
  398. (contains_safe_meta meta) && not (contains_unsafe_meta meta)
  399. let safety_mode (metadata:Ast.metadata) =
  400. let rec traverse mode meta =
  401. match mode, meta with
  402. | Some SMOff, _
  403. | _, [] -> mode
  404. | _, (Meta.NullSafety, [(EConst (Ident "Off"), _)], _) :: _ ->
  405. Some SMOff
  406. | None, (Meta.NullSafety, ([] | [(EConst (Ident "Loose"), _)]), _) :: rest ->
  407. traverse (Some SMLoose) rest
  408. | _, (Meta.NullSafety, [(EConst (Ident "Strict"), _)], _) :: rest ->
  409. traverse (Some SMStrict) rest
  410. | _, (Meta.NullSafety, [(EConst (Ident "StrictThreaded"), _)], _) :: rest ->
  411. traverse (Some SMStrictThreaded) rest
  412. | _, _ :: rest ->
  413. traverse mode rest
  414. in
  415. match traverse None metadata with
  416. | Some mode -> mode
  417. | None -> SMOff
  418. let rec validate_safety_meta report (metadata:Ast.metadata) =
  419. match metadata with
  420. | [] -> ()
  421. | (Meta.NullSafety, args, pos) :: rest ->
  422. (match args with
  423. | ([] | [(EConst (Ident ("Off" | "Loose" | "Strict" | "StrictThreaded")), _)]) -> ()
  424. | _ -> add_error report "Invalid argument for @:nullSafety meta" pos
  425. );
  426. validate_safety_meta report rest
  427. | _ :: rest -> validate_safety_meta report rest
  428. (**
  429. Check if specified `field` represents a `var` field which will exist at runtime.
  430. *)
  431. let should_be_initialized field =
  432. not (has_class_field_flag field CfExtern)
  433. && match field.cf_kind with
  434. | Var { v_read = AccNormal | AccInline | AccNo } | Var { v_write = AccNormal | AccNo } -> true
  435. | Var _ -> Meta.has Meta.IsVar field.cf_meta
  436. | _ -> false
  437. (**
  438. Check if all items of the `needle` list exist in the same order in the beginning of the `haystack` list.
  439. *)
  440. let rec list_starts_with_list (haystack:string list) (needle:string list) =
  441. match haystack, needle with
  442. | _, [] -> true
  443. | [], _ -> false
  444. | current_haystack :: rest_haystack, current_needle :: rest_needle ->
  445. current_haystack = current_needle && list_starts_with_list rest_haystack rest_needle
  446. (**
  447. A class which is used to check if an anonymous function passed to a method will be executed
  448. before that method execution is finished.
  449. *)
  450. class immediate_execution =
  451. object(self)
  452. val cache = Hashtbl.create 500
  453. (**
  454. Get cached results of the previous checks for the specified `field`
  455. *)
  456. method private get_cache field =
  457. try
  458. Hashtbl.find cache field
  459. with
  460. | Not_found ->
  461. let field_cache = Hashtbl.create 5 in
  462. Hashtbl.add cache field field_cache;
  463. field_cache
  464. (**
  465. Check if a lambda passed to `arg_num`th argument of the `callee` function will be executed immediately without
  466. delaying it or storing it somewhere else.
  467. *)
  468. method check callee arg_num =
  469. match (reveal_expr callee).eexpr with
  470. | TField (_, FClosure (Some (cls, _), ({ cf_kind = Method (MethNormal | MethInline) } as field)))
  471. | TField (_, FStatic (cls, ({ cf_kind = Method (MethNormal | MethInline) } as field)))
  472. | TField (_, FInstance (cls, _, ({ cf_kind = Method (MethNormal | MethInline) } as field))) ->
  473. if PurityState.is_pure cls field then
  474. true
  475. else
  476. (match cls, field with
  477. (* known to be pure *)
  478. | { cl_path = ([], "Array") }, _ -> true
  479. (* try to analyze function code *)
  480. | _, ({ cf_expr = (Some { eexpr = TFunction fn }) } as field) when (has_class_field_flag field CfFinal) || not (FiltersCommon.is_overridden cls field) ->
  481. if arg_num < 0 || arg_num >= List.length fn.tf_args then
  482. false
  483. else begin
  484. let cache = self#get_cache field in
  485. if Hashtbl.mem cache arg_num then
  486. Hashtbl.find cache arg_num
  487. else begin
  488. Hashtbl.add cache arg_num true;
  489. let (arg_var, _) = List.nth fn.tf_args arg_num in
  490. let result = not (self#is_stored arg_var fn.tf_expr) in
  491. Hashtbl.replace cache arg_num result;
  492. result
  493. end
  494. end
  495. | _ ->
  496. false
  497. )
  498. | _ -> false
  499. (**
  500. Check if `fn_var` is passed somewhere else in `expr` (stored to a var/field, captured by a closure etc.)
  501. *)
  502. method private is_stored fn_var expr =
  503. match expr.eexpr with
  504. | TThrow { eexpr = TLocal v }
  505. | TReturn (Some { eexpr = TLocal v })
  506. | TCast ({ eexpr = TLocal v }, _)
  507. | TMeta (_, { eexpr = TLocal v })
  508. | TBinop (OpAssign, _, { eexpr = TLocal v }) when v.v_id = fn_var.v_id ->
  509. true
  510. | TFunction fn ->
  511. let rec captured e =
  512. match e.eexpr with
  513. | TLocal v -> v.v_id = fn_var.v_id
  514. | _ -> check_expr captured e
  515. in
  516. captured fn.tf_expr
  517. | TCall (callee, args) ->
  518. if self#is_stored fn_var callee then
  519. true
  520. else begin
  521. let arg_num = ref 0 in
  522. List.exists
  523. (fun arg ->
  524. let result =
  525. match arg.eexpr with
  526. | TLocal v when v.v_id = fn_var.v_id -> not (self#check callee !arg_num)
  527. | _ -> self#is_stored fn_var arg
  528. in
  529. incr arg_num;
  530. result
  531. )
  532. args
  533. end
  534. | _ -> check_expr (self#is_stored fn_var) expr
  535. end
  536. (**
  537. Each loop or function should have its own safety scope.
  538. *)
  539. class safety_scope (mode:safety_mode) (scope_type:scope_type) (safe_locals:(safety_subject,texpr) Hashtbl.t) (never_safe:(safety_subject,texpr) Hashtbl.t) =
  540. object (self)
  541. (** Local vars declared in current scope *)
  542. val declarations = Hashtbl.create 100
  543. method get_safe_locals = safe_locals
  544. method get_never_safe = never_safe
  545. method get_type = scope_type
  546. (**
  547. Reset local vars safety to the specified state
  548. *)
  549. method reset_to (state:(safety_subject,texpr) Hashtbl.t) =
  550. Hashtbl.clear safe_locals;
  551. Hashtbl.iter (Hashtbl.add safe_locals) state
  552. (**
  553. Should be called for each local var declared
  554. *)
  555. method declare_var v =
  556. Hashtbl.add declarations v.v_id v
  557. (**
  558. Check if local var was declared in this scope
  559. *)
  560. method owns_var v =
  561. Hashtbl.mem declarations v.v_id
  562. (**
  563. Check if local variable declared in this scope is guaranteed to not have a `null` value.
  564. *)
  565. method is_safe (expr:texpr) =
  566. not (is_nullable_type expr.etype)
  567. || match self#get_subject expr with
  568. | SNotSuitable ->
  569. false
  570. | subj ->
  571. not (Hashtbl.mem never_safe subj)
  572. && Hashtbl.mem safe_locals subj
  573. (* not (Hashtbl.mem never_safe local_var.v_id)
  574. && (
  575. Hashtbl.mem safe_locals local_var.v_id
  576. || not (is_nullable_type local_var.v_type)
  577. ) *)
  578. (**
  579. Add variable to the list of safe locals.
  580. *)
  581. method add_to_safety expr =
  582. match self#get_subject expr with
  583. | SNotSuitable -> ()
  584. | subj -> Hashtbl.replace safe_locals subj expr
  585. (**
  586. Remove variable from the list of safe locals.
  587. *)
  588. method remove_from_safety ?(forever=false) expr =
  589. match self#get_subject expr with
  590. | SNotSuitable -> ()
  591. | subj ->
  592. Hashtbl.remove safe_locals subj;
  593. if forever then
  594. Hashtbl.replace never_safe subj expr
  595. (**
  596. Remove locals, which don't exist in `sample`, from safety.
  597. *)
  598. method filter_safety (sample:(safety_subject,texpr) Hashtbl.t) =
  599. Hashtbl.iter
  600. (fun subj expr ->
  601. if not (Hashtbl.mem sample subj) then
  602. Hashtbl.remove safe_locals subj
  603. )
  604. (Hashtbl.copy safe_locals);
  605. (**
  606. Should be called upon assigning a value to `expr`.
  607. Removes subjects like `expr.subField` from safety.
  608. *)
  609. method reassigned (expr:texpr) =
  610. match self#get_subject expr with
  611. | SNotSuitable -> ()
  612. | subj ->
  613. (*
  614. If this is an assignment to a field, drop all safe field accesses first,
  615. because it could alter an object of those field accesses.
  616. *)
  617. (match subj with
  618. | SFieldOfClass _ | SFieldOfLocalVar _ | SFieldOfThis _ -> self#drop_safe_fields_in_strict_mode
  619. | _ -> ()
  620. );
  621. let add_to_remove safe_subj safe_fields fields to_remove =
  622. if list_starts_with_list (List.rev safe_fields) (List.rev fields) then
  623. safe_subj :: to_remove
  624. else
  625. to_remove
  626. in
  627. let remove_list =
  628. Hashtbl.fold
  629. (fun safe_subj safe_expr to_remove ->
  630. match safe_subj, subj with
  631. | SFieldOfLocalVar (safe_id, _), SLocalVar v_id when safe_id = v_id ->
  632. safe_subj :: to_remove
  633. | SFieldOfLocalVar (safe_id, safe_fields), SFieldOfLocalVar (v_id, fields) when safe_id = v_id ->
  634. add_to_remove safe_subj safe_fields fields to_remove
  635. | SFieldOfClass (safe_path, safe_fields), SFieldOfClass (path, fields) when safe_path = path ->
  636. add_to_remove safe_subj safe_fields fields to_remove
  637. | SFieldOfClass (safe_path, safe_fields), SFieldOfClass (path, fields) when safe_path = path ->
  638. add_to_remove safe_subj safe_fields fields to_remove
  639. | SFieldOfThis safe_fields, SFieldOfThis fields ->
  640. add_to_remove safe_subj safe_fields fields to_remove
  641. | _ -> to_remove
  642. )
  643. safe_locals []
  644. in
  645. List.iter (Hashtbl.remove safe_locals) remove_list
  646. (**
  647. Should be called upon a call.
  648. In Strict mode making a call removes all field accesses from safety.
  649. *)
  650. method call_made =
  651. self#drop_safe_fields_in_strict_mode
  652. (**
  653. Un-safe all field accesses if safety mode is one of strict modes
  654. *)
  655. method private drop_safe_fields_in_strict_mode =
  656. match mode with
  657. | SMOff | SMLoose -> ()
  658. | SMStrict | SMStrictThreaded ->
  659. let remove_list =
  660. Hashtbl.fold
  661. (fun subj expr to_remove ->
  662. match subj with
  663. | SFieldOfLocalVar _ | SFieldOfClass _ | SFieldOfThis _ -> subj :: to_remove
  664. | _ -> to_remove
  665. )
  666. safe_locals []
  667. in
  668. List.iter (Hashtbl.remove safe_locals) remove_list
  669. (**
  670. Wrapper for `get_subject` function
  671. *)
  672. method get_subject =
  673. get_subject mode
  674. end
  675. (**
  676. Class to simplify collecting lists of local vars, fields and other symbols checked against `null`.
  677. *)
  678. class local_safety (mode:safety_mode) =
  679. object (self)
  680. val mutable scopes = [new safety_scope mode STNormal (Hashtbl.create 100) (Hashtbl.create 100)]
  681. (**
  682. Drop collected data
  683. *)
  684. method clear =
  685. scopes <- [new safety_scope mode STNormal (Hashtbl.create 100) (Hashtbl.create 100)]
  686. (**
  687. Get the latest created scope.
  688. *)
  689. method private get_current_scope =
  690. match scopes with
  691. | current :: _-> current
  692. | [] -> fail ~msg:"List of scopes should never end." null_pos __POS__
  693. (**
  694. Get a copy of hashtable, which stores currently safe locals
  695. *)
  696. method get_safe_locals_copy =
  697. Hashtbl.copy (self#get_current_scope#get_safe_locals)
  698. (**
  699. Remove locals, which don't exist in `sample`, from safety.
  700. *)
  701. method filter_safety sample =
  702. self#get_current_scope#filter_safety sample
  703. (**
  704. Should be called upon local function declaration.
  705. *)
  706. method function_declared (immediate_execution:bool) (fn:tfunc) =
  707. let scope =
  708. if immediate_execution || mode = SMLoose then
  709. new safety_scope mode STImmediateClosure self#get_current_scope#get_safe_locals self#get_current_scope#get_never_safe
  710. else
  711. new safety_scope mode STClosure (Hashtbl.create 100) (Hashtbl.create 100)
  712. in
  713. scopes <- scope :: scopes;
  714. List.iter (fun (v, _) -> scope#declare_var v) fn.tf_args
  715. (**
  716. Should be called upon standalone block declaration.
  717. *)
  718. method block_declared =
  719. let scope = new safety_scope mode STNormal self#get_current_scope#get_safe_locals self#get_current_scope#get_never_safe in
  720. scopes <- scope :: scopes
  721. (**
  722. Should be called upon entering a loop.
  723. *)
  724. method loop_declared e =
  725. let scope = new safety_scope mode STLoop self#get_current_scope#get_safe_locals self#get_current_scope#get_never_safe in
  726. (* let scope = new safety_scope mode STLoop (Hashtbl.create 100) (Hashtbl.create 100) in *)
  727. scopes <- scope :: scopes;
  728. match e.eexpr with
  729. | TFor (v, _, _) -> scope#declare_var v
  730. | TWhile _ -> ()
  731. | _ -> fail ~msg:"Expected TFor or TWhile." e.epos __POS__
  732. (**
  733. Should be called upon leaving local function declaration.
  734. *)
  735. method scope_closed =
  736. match scopes with
  737. | [] -> fail ~msg:"No scopes left." null_pos __POS__
  738. | [scope] -> fail ~msg:"Cannot close the last scope." null_pos __POS__
  739. | _ :: rest -> scopes <- rest
  740. (**
  741. Should be called for each local var declared
  742. *)
  743. method declare_var ?(is_safe=false) (v:tvar) =
  744. let scope = self#get_current_scope in
  745. scope#declare_var v;
  746. if is_safe then scope#add_to_safety { eexpr = TVar (v, None); etype = v.v_type; epos = v.v_pos }
  747. (**
  748. Check if local variable is guaranteed to not have a `null` value.
  749. *)
  750. method is_safe expr =
  751. if not (is_nullable_type expr.etype) then
  752. true
  753. else
  754. let captured () =
  755. match expr.eexpr with
  756. | TLocal local_var ->
  757. let rec traverse scopes =
  758. match scopes with
  759. | [] -> false
  760. | current :: rest ->
  761. if current#owns_var local_var then
  762. false
  763. else if current#get_type = STClosure then
  764. true
  765. else
  766. traverse rest
  767. in
  768. traverse scopes
  769. | _ -> false
  770. in
  771. (mode = SMLoose || not (captured())) && self#get_current_scope#is_safe expr
  772. (**
  773. This method should be called upon passing `while`.
  774. It collects locals which are checked against `null` and executes callbacks for expressions with proper statuses of locals.
  775. *)
  776. method process_while expr is_nullable_expr (condition_callback:texpr->unit) (body_callback:(unit->unit)->texpr->unit) =
  777. match expr.eexpr with
  778. | TWhile (condition, body, DoWhile) ->
  779. let original_safe_locals = self#get_safe_locals_copy in
  780. condition_callback condition;
  781. let (_, not_nulls) = process_condition mode condition is_nullable_expr (fun _ -> ()) in
  782. body_callback
  783. (fun () ->
  784. List.iter
  785. (fun not_null ->
  786. match get_subject mode not_null with
  787. | SNotSuitable -> ()
  788. | subj ->
  789. if Hashtbl.mem original_safe_locals subj then
  790. self#get_current_scope#add_to_safety not_null
  791. )
  792. not_nulls
  793. )
  794. body
  795. | TWhile (condition, body, NormalWhile) ->
  796. condition_callback condition;
  797. let (nulls, not_nulls) = process_condition mode condition is_nullable_expr (fun _ -> ()) in
  798. (** execute `body` with known not-null variables *)
  799. List.iter self#get_current_scope#add_to_safety not_nulls;
  800. body_callback
  801. (fun () -> List.iter self#get_current_scope#add_to_safety not_nulls)
  802. body;
  803. List.iter self#get_current_scope#remove_from_safety not_nulls;
  804. | _ -> fail ~msg:"Expected TWhile" expr.epos __POS__
  805. (**
  806. Should be called for bodies of loops (for, while)
  807. *)
  808. method process_loop_body (first_check:unit->unit) (intermediate_action:(unit->unit) option) (second_check:unit->unit) =
  809. let original_safe_locals = self#get_safe_locals_copy in
  810. (** The first check to find out which vars will become unsafe in a loop *)
  811. first_check();
  812. (* If local var became safe in a loop, then we need to remove it from safety to make it unsafe outside of a loop again *)
  813. self#filter_safety original_safe_locals;
  814. Option.may (fun action -> action()) intermediate_action;
  815. (** The second check with unsafe vars removed from safety *)
  816. second_check()
  817. (**
  818. This method should be called upon passing `try`.
  819. *)
  820. method process_try (try_block:texpr) (catches:(tvar * texpr) list) (check_expr:texpr->unit) =
  821. let original_safe_locals = self#get_safe_locals_copy in
  822. check_expr try_block;
  823. (* Remove locals which became safe inside of a try block from safety *)
  824. self#filter_safety original_safe_locals;
  825. let safe_after_try = self#get_safe_locals_copy
  826. and safe_after_catches = self#get_safe_locals_copy in
  827. List.iter
  828. (fun (_, catch_block) ->
  829. self#get_current_scope#reset_to safe_after_try;
  830. check_expr catch_block;
  831. Hashtbl.iter
  832. (fun var_id v ->
  833. if not (self#is_safe v) then
  834. Hashtbl.remove safe_after_catches var_id
  835. )
  836. (Hashtbl.copy safe_after_catches)
  837. )
  838. catches;
  839. self#get_current_scope#reset_to safe_after_catches
  840. (**
  841. This method should be called upon passing `if`.
  842. It collects locals which are checked against `null` and executes callbacks for expressions with proper statuses of locals.
  843. *)
  844. method process_if expr is_nullable_expr (condition_callback:texpr->unit) (body_callback:texpr->unit) =
  845. match expr.eexpr with
  846. | TIf (condition, if_body, else_body) ->
  847. condition_callback condition;
  848. let (nulls_in_if, not_nulls) =
  849. process_condition mode condition is_nullable_expr (fun _ -> ())
  850. in
  851. (* Don't touch expressions, which already was safe before this `if` *)
  852. let filter = List.filter (fun e -> not (self#is_safe e)) in
  853. let not_nulls = filter not_nulls in
  854. let not_condition =
  855. { eexpr = TUnop (Not, Prefix, condition); etype = condition.etype; epos = condition.epos }
  856. in
  857. let (_, else_not_nulls) =
  858. process_condition mode not_condition is_nullable_expr (fun _ -> ())
  859. in
  860. let else_not_nulls = filter else_not_nulls in
  861. let initial_safe = self#get_safe_locals_copy in
  862. (** execute `if_body` with known not-null variables *)
  863. List.iter self#get_current_scope#add_to_safety not_nulls;
  864. body_callback if_body;
  865. let safe_after_if = self#get_safe_locals_copy in
  866. (* List.iter self#get_current_scope#remove_from_safety not_nulls; *)
  867. self#get_current_scope#reset_to initial_safe;
  868. (** execute `else_body` with known not-null variables *)
  869. let handle_dead_end body safe_vars =
  870. if DeadEnd.has_dead_end body then
  871. List.iter self#get_current_scope#add_to_safety safe_vars
  872. in
  873. (match else_body with
  874. | None ->
  875. (*
  876. `if` gets executed only when each of `nulls_in_if` is `null`.
  877. That means if they become safe in `if`, then they are safe after `if` too.
  878. *)
  879. List.iter (fun e ->
  880. let subj = self#get_current_scope#get_subject e in
  881. if Hashtbl.mem safe_after_if subj then
  882. self#get_current_scope#add_to_safety e;
  883. ) nulls_in_if;
  884. (* These became unsafe in `if` *)
  885. Hashtbl.iter (fun subj e ->
  886. if not (Hashtbl.mem safe_after_if subj) then
  887. self#get_current_scope#remove_from_safety e;
  888. ) initial_safe;
  889. (** If `if_body` terminates execution, then bypassing `if` means `else_not_nulls` are safe now *)
  890. handle_dead_end if_body else_not_nulls
  891. | Some else_body ->
  892. List.iter self#get_current_scope#add_to_safety else_not_nulls;
  893. body_callback else_body;
  894. let safe_after_else = self#get_safe_locals_copy in
  895. self#get_current_scope#reset_to initial_safe;
  896. (* something was safe before `if..else`, but became unsafe in `if` or in `else` *)
  897. Hashtbl.iter (fun subj e ->
  898. if not (Hashtbl.mem safe_after_if subj && Hashtbl.mem safe_after_else subj) then
  899. self#get_current_scope#remove_from_safety e;
  900. Hashtbl.remove safe_after_if subj;
  901. Hashtbl.remove safe_after_else subj;
  902. ) initial_safe;
  903. (* something became safe in both `if` and `else` *)
  904. Hashtbl.iter (fun subj e ->
  905. if Hashtbl.mem safe_after_else subj then
  906. self#get_current_scope#add_to_safety e
  907. ) safe_after_if;
  908. (** If `if_body` terminates execution, then bypassing `if` means `else_not_nulls` are safe now *)
  909. handle_dead_end if_body else_not_nulls;
  910. (** If `else_body` terminates execution, then bypassing `else` means `not_nulls` are safe now *)
  911. handle_dead_end else_body not_nulls
  912. );
  913. | _ -> fail ~msg:"Expected TIf" expr.epos __POS__
  914. (**
  915. Handle boolean AND outside of `if` condition.
  916. *)
  917. method process_and left_expr right_expr is_nullable_expr (callback:texpr->unit) =
  918. callback left_expr;
  919. let (_, not_nulls) = process_condition mode left_expr is_nullable_expr (fun e -> ()) in
  920. List.iter self#get_current_scope#add_to_safety not_nulls;
  921. callback right_expr;
  922. List.iter self#get_current_scope#remove_from_safety not_nulls
  923. (**
  924. Handle boolean OR outside of `if` condition.
  925. *)
  926. method process_or left_expr right_expr is_nullable_expr (callback:texpr->unit) =
  927. let (nulls, _) = process_condition mode left_expr is_nullable_expr callback in
  928. List.iter self#get_current_scope#add_to_safety nulls;
  929. callback right_expr;
  930. List.iter self#get_current_scope#remove_from_safety nulls
  931. (**
  932. Remove subject from the safety list if a nullable value is assigned or if an object with safe field is reassigned.
  933. *)
  934. method handle_assignment is_nullable_expr left_expr (right_expr:texpr) =
  935. if is_suitable mode left_expr then
  936. self#get_current_scope#reassigned left_expr;
  937. if is_nullable_expr right_expr then
  938. match left_expr.eexpr with
  939. | TLocal v ->
  940. let captured = ref false in
  941. let rec traverse (lst:safety_scope list) =
  942. match lst with
  943. | [] -> ()
  944. | current :: rest ->
  945. if current#owns_var v then
  946. current#remove_from_safety ~forever:!captured left_expr
  947. else begin
  948. captured := !captured || current#get_type = STClosure;
  949. current#remove_from_safety ~forever:!captured left_expr;
  950. traverse rest
  951. end
  952. in
  953. traverse scopes
  954. | _ -> ()
  955. else if is_nullable_type left_expr.etype then
  956. self#get_current_scope#add_to_safety left_expr
  957. method call_made =
  958. self#get_current_scope#call_made
  959. end
  960. (**
  961. This class is used to recursively check typed expressions for null-safety
  962. *)
  963. class expr_checker mode immediate_execution report =
  964. object (self)
  965. val local_safety = new local_safety mode
  966. val mutable return_types = []
  967. val mutable in_closure = false
  968. (* if this flag is `true` then spotted errors and warnings will not be reported *)
  969. val mutable is_pretending = false
  970. (* val mutable cnt = 0 *)
  971. (**
  972. Get safety mode for this expression checker
  973. *)
  974. method get_mode = mode
  975. (**
  976. Register an error
  977. *)
  978. method error msg (positions:Globals.pos list) =
  979. if not is_pretending then begin
  980. let rec get_first_valid_pos positions =
  981. match positions with
  982. | [] -> null_pos
  983. | p :: rest ->
  984. if p <> null_pos then p
  985. else get_first_valid_pos rest
  986. in
  987. add_error report msg (get_first_valid_pos positions)
  988. end
  989. (**
  990. Check if `e` is nullable even if the type is reported not-nullable.
  991. Haxe type system lies sometimes.
  992. *)
  993. method private is_nullable_expr e =
  994. let e = reveal_expr e in
  995. match e.eexpr with
  996. | TConst TNull -> true
  997. | TConst _ -> false
  998. | TParenthesis e -> self#is_nullable_expr e
  999. | TMeta (m, _) when contains_unsafe_meta [m] -> false
  1000. | TMeta (_, e) -> self#is_nullable_expr e
  1001. | TThrow _ -> false
  1002. | TReturn _ -> false
  1003. | TBinop ((OpAssign | OpAssignOp _), _, right) -> self#is_nullable_expr right
  1004. | TBlock exprs ->
  1005. local_safety#block_declared;
  1006. let rec traverse exprs =
  1007. match exprs with
  1008. | [] -> false
  1009. | [e] -> self#is_nullable_expr e
  1010. | e :: exprs ->
  1011. (match e.eexpr with
  1012. | TVar (v,_) -> local_safety#declare_var v
  1013. | _ -> ()
  1014. );
  1015. traverse exprs
  1016. in
  1017. let is_nullable = traverse exprs in
  1018. local_safety#scope_closed;
  1019. is_nullable
  1020. (* (match exprs with
  1021. | [] -> false
  1022. | _ -> self#is_nullable_expr (List.hd (List.rev exprs))
  1023. ) *)
  1024. | TIf _ ->
  1025. let nullable = ref false in
  1026. let check body = nullable := !nullable || self#is_nullable_expr body in
  1027. local_safety#process_if e self#is_nullable_expr (fun _ -> ()) check;
  1028. !nullable
  1029. | _ ->
  1030. is_nullable_type e.etype && not (local_safety#is_safe e)
  1031. (**
  1032. Check if `expr` can be passed to a place where `to_type` is expected.
  1033. This method has side effect: it logs an error if `expr` has a type parameter incompatible with the type parameter of `to_type`.
  1034. E.g.: `Array<Null<String>>` vs `Array<String>` returns `true`, but also adds a compilation error.
  1035. *)
  1036. method can_pass_expr expr to_type p =
  1037. match expr.eexpr, to_type with
  1038. | TLocal v, _ when contains_unsafe_meta v.v_meta -> true
  1039. | TObjectDecl fields, TAnon to_type ->
  1040. List.for_all
  1041. (fun ((name, _, _), field_expr) ->
  1042. try
  1043. let field_to_type = PMap.find name to_type.a_fields in
  1044. self#can_pass_expr field_expr field_to_type.cf_type p
  1045. with Not_found -> false)
  1046. fields
  1047. | _, _ ->
  1048. if self#is_nullable_expr expr && not (is_nullable_type ~dynamic_is_nullable:true to_type) then
  1049. false
  1050. else begin
  1051. let expr_type = unfold_null expr.etype in
  1052. try
  1053. new unificator#unify expr_type to_type;
  1054. true
  1055. with
  1056. | Safety_error err ->
  1057. self#error ("Cannot unify " ^ (str_type expr_type) ^ " with " ^ (str_type to_type)) [p; expr.epos];
  1058. (* returning `true` because error is already logged in the line above *)
  1059. true
  1060. | e ->
  1061. fail ~msg:"Null safety unification failure" expr.epos __POS__
  1062. end
  1063. (**
  1064. Should be called for the root expressions of a method or for then initialization expressions of fields.
  1065. *)
  1066. method check_root_expr e =
  1067. self#check_expr e;
  1068. local_safety#clear;
  1069. return_types <- [];
  1070. in_closure <- false
  1071. (**
  1072. Recursively checks an expression
  1073. *)
  1074. method private check_expr e =
  1075. match e.eexpr with
  1076. | TConst _ -> ()
  1077. | TLocal _ -> ()
  1078. | TArray (arr, idx) -> self#check_array_access arr idx e.epos
  1079. | TBinop (op, left_expr, right_expr) -> self#check_binop op left_expr right_expr e.epos
  1080. | TField (target, access) -> self#check_field target access e.epos
  1081. | TTypeExpr _ -> ()
  1082. | TParenthesis e -> self#check_expr e
  1083. | TObjectDecl fields -> List.iter (fun (_, e) -> self#check_expr e) fields
  1084. | TArrayDecl items -> self#check_array_decl items e.etype e.epos
  1085. | TCall (callee, args) -> self#check_call callee args e.epos
  1086. | TNew _ -> self#check_new e
  1087. | TUnop (_, _, expr) -> self#check_unop expr e.epos
  1088. | TFunction fn -> self#check_function fn
  1089. | TVar (v, init_expr) -> self#check_var v init_expr e.epos
  1090. | TBlock exprs -> self#check_block exprs e.epos
  1091. | TFor _ -> self#check_for e
  1092. | TIf _ -> self#check_if e
  1093. | TWhile _ -> self#check_while e
  1094. | TSwitch switch -> self#check_switch switch e.epos
  1095. | TTry (try_block, catches) -> self#check_try try_block catches
  1096. | TReturn (Some expr) -> self#check_return expr e.epos
  1097. | TReturn None -> ()
  1098. | TBreak -> ()
  1099. | TContinue -> ()
  1100. | TThrow expr -> self#check_throw expr e.epos
  1101. | TCast (expr, _) -> self#check_cast expr e.etype e.epos
  1102. | TMeta (m, _) when contains_unsafe_meta [m] -> ()
  1103. | TMeta ((Meta.NullSafety, _, _) as m, e) -> validate_safety_meta report [m]; self#check_expr e
  1104. | TMeta (_, e) -> self#check_expr e
  1105. | TEnumIndex idx -> self#check_enum_index idx e.epos
  1106. | TEnumParameter (e, _, _) -> self#check_expr e (** Checking enum value itself is not needed here because this expr always follows after TEnumIndex *)
  1107. | TIdent _ -> ()
  1108. (**
  1109. Check expressions in a block
  1110. *)
  1111. method private check_block exprs p =
  1112. local_safety#block_declared;
  1113. let rec traverse exprs =
  1114. match exprs with
  1115. | [] -> ()
  1116. | e :: rest ->
  1117. self#check_expr e;
  1118. traverse rest
  1119. in
  1120. traverse exprs;
  1121. local_safety#scope_closed
  1122. (**
  1123. Don't allow to use nullable values as items in declaration of not-nullable arrays
  1124. *)
  1125. method private check_array_decl items arr_type p =
  1126. (match Abstract.follow_with_abstracts arr_type with
  1127. | TInst ({ cl_path = ([], "Array") }, [item_type]) ->
  1128. List.iter
  1129. (fun e ->
  1130. if not (self#can_pass_expr e item_type e.epos) then
  1131. self#error ("Cannot use nullable value of " ^ (str_type e.etype) ^ " as an item in Array<" ^ (str_type item_type) ^ ">") [e.epos; p]
  1132. )
  1133. items;
  1134. | _ -> ()
  1135. );
  1136. List.iter self#check_expr items
  1137. (**
  1138. Deal with nullable enum values
  1139. *)
  1140. method private check_enum_index idx p =
  1141. if self#is_nullable_expr idx then
  1142. self#error "Cannot access nullable enum value." [idx.epos; p];
  1143. self#check_expr idx
  1144. (**
  1145. Check try...catch
  1146. *)
  1147. method private check_try try_block catches =
  1148. local_safety#process_try try_block catches self#check_expr
  1149. (**
  1150. Don't use nullable value as a condition in `while`
  1151. *)
  1152. method private check_while e =
  1153. match e.eexpr with
  1154. | TWhile _ ->
  1155. let check_condition condition =
  1156. if self#is_nullable_expr condition then
  1157. self#error "Cannot use nullable value as a condition in \"while\"." [condition.epos; e.epos];
  1158. self#check_expr condition
  1159. in
  1160. local_safety#loop_declared e;
  1161. local_safety#process_while
  1162. e
  1163. self#is_nullable_expr
  1164. check_condition
  1165. (* self#check_loop_body; *)
  1166. (fun handle_condition_effect body ->
  1167. self#check_loop_body
  1168. (Some handle_condition_effect)
  1169. body
  1170. );
  1171. local_safety#scope_closed
  1172. | _ -> fail ~msg:"Expected TWhile." e.epos __POS__
  1173. (**
  1174. Don't iterate on nullable values
  1175. *)
  1176. method private check_for e =
  1177. match e.eexpr with
  1178. | TFor (v, iterable, body) ->
  1179. if self#is_nullable_expr iterable then
  1180. self#error "Cannot iterate over nullable value." [iterable.epos; e.epos];
  1181. self#check_expr iterable;
  1182. local_safety#declare_var v;
  1183. local_safety#loop_declared e;
  1184. self#check_loop_body None body;
  1185. local_safety#scope_closed
  1186. | _ -> fail ~msg:"Expected TFor." e.epos __POS__
  1187. (**
  1188. Handle safety inside of loops
  1189. *)
  1190. method private check_loop_body (handle_condition_effect:(unit->unit) option) body =
  1191. local_safety#process_loop_body
  1192. (* Start pretending to ignore errors *)
  1193. (fun () ->
  1194. is_pretending <- true;
  1195. self#check_expr body
  1196. )
  1197. handle_condition_effect
  1198. (* Now we know, which vars will become unsafe in this loop. Stop pretending and check again *)
  1199. (fun () ->
  1200. is_pretending <- false;
  1201. self#check_expr body;
  1202. )
  1203. (**
  1204. Don't throw nullable values
  1205. *)
  1206. method private check_throw e p =
  1207. if self#is_nullable_expr e then
  1208. self#error "Cannot throw nullable value." [p; e.epos];
  1209. self#check_expr e
  1210. (**
  1211. Don't cast nullable expressions to not-nullable types
  1212. *)
  1213. method private check_cast expr to_type p =
  1214. self#check_expr expr;
  1215. match to_type with
  1216. (* untyped cast *)
  1217. | TMono _ -> ()
  1218. (* typed cast and type check *)
  1219. | _ ->
  1220. if not (self#can_pass_expr expr to_type p) then
  1221. self#error "Cannot cast nullable value to not nullable type." [p; expr.epos]
  1222. (**
  1223. Check safety in a function
  1224. *)
  1225. method private check_function ?(immediate_execution=false) fn =
  1226. local_safety#function_declared immediate_execution fn;
  1227. return_types <- fn.tf_type :: return_types;
  1228. if immediate_execution || mode = SMLoose then
  1229. begin
  1230. let original_safe_locals = local_safety#get_safe_locals_copy in
  1231. (* Start pretending to ignore errors *)
  1232. is_pretending <- true;
  1233. self#check_expr fn.tf_expr;
  1234. (* Now we know, which vars will become unsafe in this closure. Stop pretending and perform real check *)
  1235. is_pretending <- false;
  1236. local_safety#filter_safety original_safe_locals;
  1237. self#check_expr fn.tf_expr
  1238. end
  1239. else
  1240. self#check_expr fn.tf_expr;
  1241. return_types <- List.tl return_types;
  1242. local_safety#scope_closed
  1243. (**
  1244. Don't return nullable values as not-nullable return types.
  1245. *)
  1246. method private check_return e p =
  1247. self#check_expr e;
  1248. match return_types with
  1249. | t :: _ when not (self#can_pass_expr e t p) ->
  1250. self#error ("Cannot return nullable value of " ^ (str_type e.etype) ^ " as " ^ (str_type t)) [p; e.epos]
  1251. | _ -> ()
  1252. (**
  1253. Check safety in `switch` expressions.
  1254. *)
  1255. method private check_switch switch p =
  1256. let target = switch.switch_subject in
  1257. let cases = switch.switch_cases in
  1258. let default = switch.switch_default in
  1259. if self#is_nullable_expr target then
  1260. self#error "Cannot switch on nullable value." [target.epos; p];
  1261. self#check_expr target;
  1262. let rec traverse_cases cases =
  1263. match cases with
  1264. | [] -> ()
  1265. | case :: rest ->
  1266. self#check_expr case.case_expr;
  1267. traverse_cases rest
  1268. in
  1269. traverse_cases cases;
  1270. match default with
  1271. | None -> ()
  1272. | Some e -> self#check_expr e
  1273. (**
  1274. Check safety in `if` expressions
  1275. *)
  1276. method private check_if expr =
  1277. let check_condition e =
  1278. if self#is_nullable_expr e then
  1279. self#error "Cannot use nullable value as condition in \"if\"." [e.epos; expr.epos];
  1280. self#check_expr e
  1281. in
  1282. local_safety#process_if expr self#is_nullable_expr check_condition self#check_expr
  1283. (**
  1284. Check array access on nullable values or using nullable indexes
  1285. *)
  1286. method private check_array_access arr idx p =
  1287. if self#is_nullable_expr arr then
  1288. self#error "Cannot perform array access on nullable value." [p; arr.epos];
  1289. if self#is_nullable_expr idx then
  1290. self#error "Cannot use nullable value as an index for array access." [p; idx.epos];
  1291. self#check_expr arr;
  1292. self#check_expr idx
  1293. (**
  1294. Don't perform unsafe binary operations
  1295. *)
  1296. method private check_binop op left_expr right_expr p =
  1297. let check_both () =
  1298. self#check_expr left_expr;
  1299. self#check_expr right_expr
  1300. in
  1301. match op with
  1302. | OpEq | OpNotEq -> check_both()
  1303. | OpBoolAnd ->
  1304. local_safety#process_and left_expr right_expr self#is_nullable_expr self#check_expr
  1305. | OpBoolOr ->
  1306. local_safety#process_or left_expr right_expr self#is_nullable_expr self#check_expr
  1307. (* String concatenation is safe if one of operands is safe *)
  1308. | OpAdd
  1309. | OpAssignOp OpAdd when is_string_type left_expr.etype || is_string_type right_expr.etype ->
  1310. check_both();
  1311. if is_nullable_type left_expr.etype && is_nullable_type right_expr.etype then
  1312. self#error "Cannot concatenate two nullable values." [p; left_expr.epos; right_expr.epos]
  1313. | OpAssign ->
  1314. check_both();
  1315. if not (self#can_pass_expr right_expr left_expr.etype p) then
  1316. match left_expr.eexpr with
  1317. | TLocal v when contains_unsafe_meta v.v_meta -> ()
  1318. | _ ->
  1319. self#error "Cannot assign nullable value here." [p; right_expr.epos; left_expr.epos]
  1320. else
  1321. local_safety#handle_assignment self#is_nullable_expr left_expr right_expr;
  1322. | _->
  1323. if self#is_nullable_expr left_expr || self#is_nullable_expr right_expr then
  1324. self#error "Cannot perform binary operation on nullable value." [p; left_expr.epos; right_expr.epos];
  1325. check_both()
  1326. (**
  1327. Don't perform unops on nullable values
  1328. *)
  1329. method private check_unop e p =
  1330. if self#is_nullable_expr e then
  1331. self#error "Cannot perform unary operation on nullable value." [p; e.epos];
  1332. self#check_expr e
  1333. (**
  1334. Don't assign nullable value to not-nullable variable on var declaration
  1335. *)
  1336. method private check_var v init p =
  1337. local_safety#declare_var v;
  1338. match init with
  1339. | None -> ()
  1340. (* Local named functions like `function fn() {}`, which are generated as `var fn = null; fn = function(){}` *)
  1341. | Some { eexpr = TConst TNull } when v.v_kind = VUser TVOLocalFunction -> ()
  1342. (* `_this = null` is generated for local `inline function` *)
  1343. | Some { eexpr = TConst TNull } when v.v_kind = VGenerated -> ()
  1344. | Some e ->
  1345. let local = { eexpr = TLocal v; epos = v.v_pos; etype = v.v_type } in
  1346. self#check_binop OpAssign local e p
  1347. (**
  1348. Make sure nobody tries to access a field on a nullable value
  1349. *)
  1350. method private check_field target access p =
  1351. self#check_expr target;
  1352. if self#is_nullable_expr target then
  1353. self#error ("Cannot access \"" ^ accessed_field_name access ^ "\" of a nullable value.") [p; target.epos];
  1354. (**
  1355. Check constructor invocation: don't pass nullable values to not-nullable arguments
  1356. *)
  1357. method private check_new e_new =
  1358. match e_new.eexpr with
  1359. | TNew (cls, params, args) ->
  1360. let ctor =
  1361. try
  1362. Some (get_constructor cls)
  1363. with
  1364. | Not_found -> None
  1365. in
  1366. (match ctor with
  1367. | None ->
  1368. List.iter self#check_expr args
  1369. | Some cf ->
  1370. let traverse t =
  1371. match follow t with
  1372. | TFun (types, _) -> self#check_args e_new args types
  1373. | _ -> fail ~msg:"Unexpected constructor type." e_new.epos __POS__
  1374. in
  1375. let ctor_type = apply_params cls.cl_params params cf.cf_type in
  1376. traverse ctor_type
  1377. )
  1378. | _ -> fail ~msg:"TNew expected" e_new.epos __POS__
  1379. (**
  1380. Check calls: don't call a nullable value, dont' pass nulable values to not-nullable arguments
  1381. *)
  1382. method private check_call callee args p =
  1383. if self#is_nullable_expr callee then
  1384. self#error "Cannot call a nullable value." [callee.epos; p];
  1385. (match callee.eexpr with
  1386. | TFunction fn | TParenthesis { eexpr = TFunction fn } ->
  1387. self#check_function ~immediate_execution:true fn
  1388. | _ ->
  1389. self#check_expr callee
  1390. );
  1391. (match follow callee.etype with
  1392. | TFun (types, _) ->
  1393. if is_trace callee then
  1394. let real_args =
  1395. match List.rev args with
  1396. | { eexpr = TObjectDecl fields } :: [first_arg] ->
  1397. (try
  1398. let arr =
  1399. snd (List.find (fun ((name, _, _), _) -> name = "customParams") fields)
  1400. in
  1401. match arr.eexpr with
  1402. | TArrayDecl rest_args -> first_arg :: rest_args
  1403. | _ -> args
  1404. with Not_found -> args
  1405. )
  1406. | _ -> args
  1407. in
  1408. List.iter self#check_expr real_args
  1409. else begin
  1410. self#check_args callee args types
  1411. end
  1412. | _ ->
  1413. List.iter self#check_expr args
  1414. );
  1415. local_safety#call_made
  1416. (**
  1417. Check if specified expressions can be passed to a call which expects `types`.
  1418. *)
  1419. method private check_args callee args types =
  1420. let rec traverse arg_num args types meta =
  1421. match (args, types, meta) with
  1422. | (arg :: args, (arg_name, optional, t) :: types, arg_meta :: meta) ->
  1423. let unsafe_argument = contains_unsafe_meta arg_meta in
  1424. if
  1425. not optional && not unsafe_argument
  1426. && not (self#can_pass_expr arg t arg.epos)
  1427. then begin
  1428. let fn_str = match symbol_name callee with "" -> "" | name -> " of function \"" ^ name ^ "\""
  1429. and arg_str = if arg_name = "" then "" else " \"" ^ arg_name ^ "\"" in
  1430. self#error ("Cannot pass nullable value to not-nullable argument" ^ arg_str ^ fn_str ^ ".") [arg.epos; callee.epos]
  1431. end;
  1432. (match arg.eexpr with
  1433. | TFunction fn ->
  1434. self#check_function ~immediate_execution:(immediate_execution#check callee arg_num) fn
  1435. | TCast(e,None) when unsafe_argument && fast_eq arg.etype t ->
  1436. self#check_expr e
  1437. | _ ->
  1438. self#check_expr arg
  1439. );
  1440. traverse (arg_num + 1) args types meta;
  1441. | _ -> ()
  1442. in
  1443. let meta = get_arguments_meta callee (List.length types) in
  1444. traverse 0 args types meta
  1445. end
  1446. class class_checker cls immediate_execution report =
  1447. let cls_meta = cls.cl_meta @ (match cls.cl_kind with KAbstractImpl a -> a.a_meta | _ -> []) in
  1448. object (self)
  1449. val is_safe_class = (safety_enabled cls_meta)
  1450. val mutable checker = new expr_checker SMLoose immediate_execution report
  1451. val mutable mode = None
  1452. (**
  1453. Entry point for checking a class
  1454. *)
  1455. method check =
  1456. validate_safety_meta report cls_meta;
  1457. if is_safe_class && (not (has_class_flag cls CExtern)) && (not (has_class_flag cls CInterface)) then
  1458. self#check_var_fields;
  1459. let check_field is_static f =
  1460. validate_safety_meta report f.cf_meta;
  1461. match (safety_mode (cls_meta @ f.cf_meta)) with
  1462. | SMOff -> ()
  1463. | mode ->
  1464. (match f.cf_expr with
  1465. | None -> ()
  1466. | Some expr ->
  1467. (self#get_checker mode)#check_root_expr expr
  1468. );
  1469. self#check_accessors is_static f
  1470. in
  1471. if is_safe_class then
  1472. Option.may ((self#get_checker (safety_mode cls_meta))#check_root_expr) cls.cl_init;
  1473. Option.may (check_field false) cls.cl_constructor;
  1474. List.iter (check_field false) cls.cl_ordered_fields;
  1475. List.iter (check_field true) cls.cl_ordered_statics;
  1476. (**
  1477. Check if a getter/setter for non-nullable property return safe values.
  1478. E.g.
  1479. ```
  1480. var str(get,never):String;
  1481. function get_str() return (null:Null<String>); //should fail null safety check
  1482. ```
  1483. *)
  1484. method private check_accessors is_static field =
  1485. match field.cf_kind with
  1486. | Var { v_read = read_access; v_write = write_access } when not (is_nullable_type field.cf_type) ->
  1487. let fields = if is_static then cls.cl_statics else cls.cl_fields in
  1488. let check_accessor prefix =
  1489. let accessor =
  1490. try Some (PMap.find (prefix ^ field.cf_name) fields)
  1491. with Not_found -> None
  1492. in
  1493. match accessor with
  1494. | None -> ()
  1495. | Some accessor ->
  1496. if self#is_in_safety accessor then
  1497. match accessor.cf_expr with
  1498. | Some ({ eexpr = TFunction fn } as accessor_expr) ->
  1499. let fn = { fn with tf_type = field.cf_type } in
  1500. (self#get_checker self#class_safety_mode)#check_root_expr { accessor_expr with eexpr = TFunction fn }
  1501. | _ -> ()
  1502. in
  1503. if read_access = AccCall then check_accessor "get_";
  1504. if write_access = AccCall then check_accessor "set_"
  1505. | _ -> ()
  1506. (**
  1507. Get safety mode for the current class
  1508. *)
  1509. method private class_safety_mode =
  1510. match mode with
  1511. | Some mode -> mode
  1512. | None ->
  1513. let m = safety_mode cls_meta in
  1514. mode <- Some m;
  1515. m
  1516. (**
  1517. Get an instance of expression checker with safety mode set to `mode`
  1518. *)
  1519. method private get_checker mode =
  1520. if checker#get_mode <> mode then
  1521. checker <- new expr_checker mode immediate_execution report;
  1522. checker
  1523. (**
  1524. Check if field should be checked by null safety
  1525. *)
  1526. method private is_in_safety field =
  1527. (is_safe_class && not (contains_unsafe_meta field.cf_meta)) || safety_enabled field.cf_meta
  1528. (**
  1529. Check `var` fields are initialized properly
  1530. *)
  1531. method check_var_fields =
  1532. let check_field is_static field =
  1533. validate_safety_meta report field.cf_meta;
  1534. if should_be_initialized field then
  1535. if not (is_nullable_type field.cf_type) && self#is_in_safety field then
  1536. match field.cf_expr with
  1537. | None ->
  1538. if is_static then
  1539. checker#error
  1540. ("Field \"" ^ field.cf_name ^ "\" is not nullable thus should have an initial value.")
  1541. [field.cf_pos]
  1542. | Some e ->
  1543. if not (checker#can_pass_expr e field.cf_type e.epos) then
  1544. checker#error ("Cannot set nullable initial value for not-nullable field \"" ^ field.cf_name ^ "\".") [field.cf_pos]
  1545. in
  1546. List.iter (check_field false) cls.cl_ordered_fields;
  1547. List.iter (check_field true) cls.cl_ordered_statics;
  1548. self#check_fields_initialization_in_constructor ()
  1549. (**
  1550. Check instance fields without initial values are properly initialized in constructor
  1551. *)
  1552. method private check_fields_initialization_in_constructor () =
  1553. let fields_to_initialize = Hashtbl.create 20
  1554. (* Compiler-autogenerated local vars for transfering `this` to local functions *)
  1555. and this_vars = Hashtbl.create 5 in
  1556. List.iter
  1557. (fun f ->
  1558. if
  1559. should_be_initialized f
  1560. && not (is_nullable_type f.cf_type)
  1561. && not (contains_unsafe_meta f.cf_meta)
  1562. then
  1563. match f.cf_expr with
  1564. | Some _ -> ()
  1565. | None -> Hashtbl.add fields_to_initialize f.cf_name f
  1566. )
  1567. cls.cl_ordered_fields;
  1568. let rec check_unsafe_usage init_list safety_enabled e =
  1569. if Hashtbl.length init_list > 0 then
  1570. match e.eexpr with
  1571. | TField ({ eexpr = TConst TThis }, FInstance (_, _, field)) ->
  1572. if Hashtbl.mem init_list field.cf_name then
  1573. checker#error ("Cannot use field " ^ field.cf_name ^ " until initialization.") [e.epos]
  1574. | TField ({ eexpr = TConst TThis }, FClosure (_, field)) ->
  1575. checker#error ("Cannot use method " ^ field.cf_name ^ " until all instance fields are initialized.") [e.epos];
  1576. | TCall ({ eexpr = TField ({ eexpr = TConst TThis }, FInstance (_, _, field)) }, args) ->
  1577. checker#error ("Cannot call method " ^ field.cf_name ^ " until all instance fields are initialized.") [e.epos];
  1578. List.iter (check_unsafe_usage init_list safety_enabled) args
  1579. | TConst TThis when safety_enabled ->
  1580. checker#error "Cannot use \"this\" until all instance fields are initialized." [e.epos]
  1581. | TLocal v when safety_enabled && Hashtbl.mem this_vars v.v_id ->
  1582. checker#error "Cannot use \"this\" until all instance fields are initialized." [e.epos]
  1583. | TMeta ((Meta.NullSafety, [(EConst (Ident "Off"), _)], _), e) ->
  1584. iter (check_unsafe_usage init_list false) e
  1585. | TMeta ((Meta.NullSafety, _, _), e) ->
  1586. iter (check_unsafe_usage init_list true) e
  1587. | _ ->
  1588. iter (check_unsafe_usage init_list safety_enabled) e
  1589. in
  1590. let rec traverse init_list e =
  1591. (match e.eexpr with
  1592. | TBinop (OpAssign, { eexpr = TField ({ eexpr = TConst TThis }, FInstance (_, _, f)) }, right_expr) ->
  1593. Hashtbl.remove init_list f.cf_name;
  1594. ignore (traverse init_list right_expr)
  1595. | TWhile (condition, body, DoWhile) ->
  1596. check_unsafe_usage init_list true condition;
  1597. ignore (traverse init_list body)
  1598. | TBlock exprs ->
  1599. List.iter (fun e -> ignore (traverse init_list e)) exprs
  1600. | TIf (_, if_block, Some else_block) ->
  1601. let if_init_list = traverse (Hashtbl.copy init_list) if_block
  1602. and else_init_list = traverse (Hashtbl.copy init_list) else_block in
  1603. Hashtbl.clear init_list;
  1604. Hashtbl.iter (Hashtbl.replace init_list) if_init_list;
  1605. Hashtbl.iter (Hashtbl.replace init_list) else_init_list
  1606. (* var _gthis = this *)
  1607. | TVar (v, Some { eexpr = TConst TThis }) ->
  1608. Hashtbl.add this_vars v.v_id v
  1609. | _ ->
  1610. check_unsafe_usage init_list true e
  1611. );
  1612. init_list
  1613. in
  1614. (match cls.cl_constructor with
  1615. | Some { cf_expr = Some { eexpr = TFunction { tf_expr = e } } } ->
  1616. ignore (traverse fields_to_initialize e);
  1617. | _ -> ()
  1618. );
  1619. Hashtbl.iter
  1620. (fun name field ->
  1621. checker#error
  1622. ("Field \"" ^ name ^ "\" is not nullable thus should have an initial value or should be initialized in constructor.")
  1623. [field.cf_pos]
  1624. )
  1625. fields_to_initialize
  1626. end
  1627. (**
  1628. Run null safety checks.
  1629. *)
  1630. let run (com:Common.context) (types:module_type list) =
  1631. let timer = Timer.timer ["null safety"] in
  1632. let report = { sr_errors = [] } in
  1633. let immediate_execution = new immediate_execution in
  1634. let traverse module_type =
  1635. match module_type with
  1636. | TEnumDecl enm -> ()
  1637. | TTypeDecl typedef -> ()
  1638. | TAbstractDecl abstr -> ()
  1639. | TClassDecl cls -> (new class_checker cls immediate_execution report)#check
  1640. in
  1641. List.iter traverse types;
  1642. timer();
  1643. match com.callbacks#get_null_safety_report with
  1644. | [] ->
  1645. List.iter (fun err -> com.error err.sm_msg err.sm_pos) (List.rev report.sr_errors)
  1646. | callbacks ->
  1647. let errors =
  1648. List.map (fun err -> (err.sm_msg, err.sm_pos)) report.sr_errors
  1649. in
  1650. List.iter (fun fn -> fn errors) callbacks
  1651. ;;