diagnostics.ml 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. open Globals
  2. open Ast
  3. open Type
  4. open Common
  5. open DisplayTypes
  6. let add_removable_code ctx s p prange =
  7. ctx.removable_code <- (s,p,prange) :: ctx.removable_code
  8. let error_in_diagnostics_run com p =
  9. let b = DiagnosticsPrinter.is_diagnostics_file com (com.file_keys#get p.pfile) in
  10. if b then com.has_error <- true;
  11. b
  12. let find_unused_variables com e =
  13. let vars = Hashtbl.create 0 in
  14. let pmin_map = Hashtbl.create 0 in
  15. let rec loop e = match e.eexpr with
  16. | TVar({v_kind = VUser _} as v,eo) when v.v_name <> "_" ->
  17. Hashtbl.add pmin_map e.epos.pmin v;
  18. let p = match eo with
  19. | None -> e.epos
  20. | Some e1 ->
  21. loop e1;
  22. { e.epos with pmax = e1.epos.pmin }
  23. in
  24. Hashtbl.replace vars v.v_id (v,p);
  25. | TLocal ({v_kind = VUser _} as v) ->
  26. Hashtbl.remove vars v.v_id;
  27. | _ ->
  28. Type.iter loop e
  29. in
  30. loop e;
  31. Hashtbl.iter (fun _ (v,p) ->
  32. let p = match (Hashtbl.find_all pmin_map p.pmin) with [_] -> p | _ -> null_pos in
  33. add_removable_code com "Unused variable" v.v_pos p
  34. ) vars
  35. let check_other_things com e =
  36. let had_effect = ref false in
  37. let no_effect p =
  38. add_diagnostics_message com "This code has no effect" p DKCompilerMessage Warning;
  39. in
  40. let pointless_compound s p =
  41. add_diagnostics_message com (Printf.sprintf "This %s has no effect, but some of its sub-expressions do" s) p DKCompilerMessage Warning;
  42. in
  43. let rec compound s el p =
  44. let old = !had_effect in
  45. had_effect := false;
  46. List.iter (loop true) el;
  47. if not !had_effect then no_effect p else pointless_compound s p;
  48. had_effect := old;
  49. and loop in_value e = match e.eexpr with
  50. | TBlock el ->
  51. let rec loop2 el = match el with
  52. | [] -> ()
  53. | [e] -> loop in_value e
  54. | e :: el -> loop false e; loop2 el
  55. in
  56. loop2 el
  57. | TMeta((Meta.Extern,_,_),_) ->
  58. (* This is so something like `[inlineFunc()]` is not reported. *)
  59. had_effect := true;
  60. | TConst _ | TLocal {v_kind = VUser _} | TTypeExpr _ | TFunction _ | TIdent _ when not in_value ->
  61. no_effect e.epos;
  62. | TConst _ | TLocal _ | TTypeExpr _ | TEnumParameter _ | TEnumIndex _ | TVar _ | TIdent _ ->
  63. ()
  64. | TField (_, fa) when PurityState.is_explicitly_impure fa -> ()
  65. | TFunction tf ->
  66. loop false tf.tf_expr
  67. | TCall({eexpr = TField(e1,fa)},el) when not in_value && PurityState.is_pure_field_access fa -> compound "call" el e.epos
  68. | TNew _ | TCall _ | TBinop ((Ast.OpAssignOp _ | Ast.OpAssign),_,_) | TUnop ((Ast.Increment | Ast.Decrement),_,_)
  69. | TReturn _ | TBreak | TContinue | TThrow _ | TCast (_,Some _)
  70. | TIf _ | TTry _ | TSwitch _ | TWhile _ | TFor _ ->
  71. had_effect := true;
  72. Type.iter (loop true) e
  73. | TParenthesis e1 | TMeta(_,e1) ->
  74. loop in_value e1
  75. | TArray _ | TCast (_,None) | TBinop _ | TUnop _
  76. | TField _ | TArrayDecl _ | TObjectDecl _ when in_value ->
  77. Type.iter (loop true) e;
  78. | TArray(e1,e2) -> compound "array access" [e1;e2] e.epos
  79. | TCast(e1,None) -> compound "cast" [e1] e.epos
  80. | TBinop(op,e1,e2) -> compound (Printf.sprintf "'%s' operator" (s_binop op)) [e1;e2] e.epos
  81. | TUnop(op,_,e1) -> compound (Printf.sprintf "'%s' operator" (s_unop op)) [e1] e.epos
  82. | TField(e1,_) -> compound "field access" [e1] e.epos
  83. | TArrayDecl el -> compound "array declaration" el e.epos
  84. | TObjectDecl fl -> compound "object declaration" (List.map snd fl) e.epos
  85. in
  86. loop true e
  87. let prepare_field dctx dectx com cf = match cf.cf_expr with
  88. | None -> ()
  89. | Some e ->
  90. find_unused_variables dctx e;
  91. check_other_things com e;
  92. DeprecationCheck.run_on_expr {dectx with field_meta = cf.cf_meta} e
  93. let collect_diagnostics dctx com =
  94. let open CompilationCache in
  95. let dectx = DeprecationCheck.create_context com in
  96. List.iter (function
  97. | TClassDecl c when DiagnosticsPrinter.is_diagnostics_file com (com.file_keys#get c.cl_pos.pfile) ->
  98. let dectx = {dectx with class_meta = c.cl_meta} in
  99. List.iter (prepare_field dctx dectx com) c.cl_ordered_fields;
  100. List.iter (prepare_field dctx dectx com) c.cl_ordered_statics;
  101. (match c.cl_constructor with None -> () | Some cf -> prepare_field dctx dectx com cf);
  102. | _ ->
  103. ()
  104. ) com.types;
  105. let handle_dead_blocks com = match com.cache with
  106. | Some cc ->
  107. let display_defines = adapt_defines_to_display_context com.defines in
  108. let is_true defines e =
  109. ParserEntry.is_true (ParserEntry.eval defines e)
  110. in
  111. Hashtbl.iter (fun file_key cfile ->
  112. if DisplayPosition.display_position#is_in_file (com.file_keys#get cfile.c_file_path) then begin
  113. let dead_blocks = cfile.c_pdi.pd_dead_blocks in
  114. let dead_blocks = List.filter (fun (_,e) -> not (is_true display_defines e)) dead_blocks in
  115. try
  116. let dead_blocks2 = Hashtbl.find dctx.dead_blocks file_key in
  117. (* Intersect *)
  118. let dead_blocks2 = List.filter (fun (p,_) -> List.mem_assoc p dead_blocks) dead_blocks2 in
  119. Hashtbl.replace dctx.dead_blocks file_key dead_blocks2
  120. with Not_found ->
  121. Hashtbl.add dctx.dead_blocks file_key dead_blocks
  122. end
  123. ) cc#get_files
  124. | None ->
  125. ()
  126. in
  127. handle_dead_blocks com
  128. let prepare com =
  129. let dctx = {
  130. removable_code = [];
  131. import_positions = PMap.empty;
  132. dead_blocks = Hashtbl.create 0;
  133. diagnostics_messages = [];
  134. unresolved_identifiers = [];
  135. missing_fields = PMap.empty;
  136. } in
  137. if not (List.exists (fun (_,_,_,sev,_) -> sev = MessageSeverity.Error) com.shared.shared_display_information.diagnostics_messages) then
  138. collect_diagnostics dctx com;
  139. let process_modules com =
  140. List.iter (fun m ->
  141. PMap.iter (fun p b ->
  142. if not (PMap.mem p dctx.import_positions) then
  143. dctx.import_positions <- PMap.add p b dctx.import_positions
  144. else if !b then begin
  145. let b' = PMap.find p dctx.import_positions in
  146. b' := true
  147. end
  148. ) m.m_extra.m_display.m_import_positions;
  149. ) com.modules;
  150. List.iter (function
  151. | MissingFields mf ->
  152. let p = mf.mf_pos in
  153. begin try
  154. let _,l = PMap.find p dctx.missing_fields in
  155. l := mf :: !l
  156. with Not_found ->
  157. dctx.missing_fields <- PMap.add p (mf.mf_on,ref [mf]) dctx.missing_fields
  158. end
  159. ) com.display_information.module_diagnostics
  160. in
  161. process_modules com;
  162. begin match com.get_macros() with
  163. | None -> ()
  164. | Some com -> process_modules com
  165. end;
  166. (* We do this at the end because some of the prepare functions might add information to the common context. *)
  167. dctx.diagnostics_messages <- com.shared.shared_display_information.diagnostics_messages;
  168. dctx.unresolved_identifiers <- com.display_information.unresolved_identifiers;
  169. dctx
  170. let secure_generated_code ctx e =
  171. (* This causes problems and sucks in general... need a different solution. But I forgot which problem this solved anyway. *)
  172. (* mk (TMeta((Meta.Extern,[],e.epos),e)) e.etype e.epos *)
  173. e
  174. let print com =
  175. let dctx = prepare com in
  176. Json.string_of_json (DiagnosticsPrinter.json_of_diagnostics com dctx)
  177. let run com =
  178. let dctx = prepare com in
  179. dctx