123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- open Globals
- open Ast
- open Type
- open Common
- open DisplayTypes
- let add_removable_code ctx s p prange =
- ctx.removable_code <- (s,p,prange) :: ctx.removable_code
- let error_in_diagnostics_run com p =
- let b = DiagnosticsPrinter.is_diagnostics_file com (com.file_keys#get p.pfile) in
- if b then com.has_error <- true;
- b
- let find_unused_variables com e =
- let vars = Hashtbl.create 0 in
- let pmin_map = Hashtbl.create 0 in
- let rec loop e = match e.eexpr with
- | TVar({v_kind = VUser _} as v,eo) when v.v_name <> "_" ->
- Hashtbl.add pmin_map e.epos.pmin v;
- let p = match eo with
- | None -> e.epos
- | Some e1 ->
- loop e1;
- { e.epos with pmax = e1.epos.pmin }
- in
- Hashtbl.replace vars v.v_id (v,p);
- | TLocal ({v_kind = VUser _} as v) ->
- Hashtbl.remove vars v.v_id;
- | _ ->
- Type.iter loop e
- in
- loop e;
- Hashtbl.iter (fun _ (v,p) ->
- let p = match (Hashtbl.find_all pmin_map p.pmin) with [_] -> p | _ -> null_pos in
- add_removable_code com "Unused variable" v.v_pos p
- ) vars
- let check_other_things com e =
- let had_effect = ref false in
- let no_effect p =
- add_diagnostics_message com "This code has no effect" p DKCompilerMessage Warning;
- in
- let pointless_compound s p =
- add_diagnostics_message com (Printf.sprintf "This %s has no effect, but some of its sub-expressions do" s) p DKCompilerMessage Warning;
- in
- let rec compound s el p =
- let old = !had_effect in
- had_effect := false;
- List.iter (loop true) el;
- if not !had_effect then no_effect p else pointless_compound s p;
- had_effect := old;
- and loop in_value e = match e.eexpr with
- | TBlock el ->
- let rec loop2 el = match el with
- | [] -> ()
- | [e] -> loop in_value e
- | e :: el -> loop false e; loop2 el
- in
- loop2 el
- | TMeta((Meta.Extern,_,_),_) ->
- (* This is so something like `[inlineFunc()]` is not reported. *)
- had_effect := true;
- | TConst _ | TLocal {v_kind = VUser _} | TTypeExpr _ | TFunction _ | TIdent _ when not in_value ->
- no_effect e.epos;
- | TConst _ | TLocal _ | TTypeExpr _ | TEnumParameter _ | TEnumIndex _ | TVar _ | TIdent _ ->
- ()
- | TField (_, fa) when PurityState.is_explicitly_impure fa -> ()
- | TFunction tf ->
- loop false tf.tf_expr
- | TCall({eexpr = TField(e1,fa)},el) when not in_value && PurityState.is_pure_field_access fa -> compound "call" el e.epos
- | TNew _ | TCall _ | TBinop ((Ast.OpAssignOp _ | Ast.OpAssign),_,_) | TUnop ((Ast.Increment | Ast.Decrement),_,_)
- | TReturn _ | TBreak | TContinue | TThrow _ | TCast (_,Some _)
- | TIf _ | TTry _ | TSwitch _ | TWhile _ | TFor _ ->
- had_effect := true;
- Type.iter (loop true) e
- | TParenthesis e1 | TMeta(_,e1) ->
- loop in_value e1
- | TArray _ | TCast (_,None) | TBinop _ | TUnop _
- | TField _ | TArrayDecl _ | TObjectDecl _ when in_value ->
- Type.iter (loop true) e;
- | TArray(e1,e2) -> compound "array access" [e1;e2] e.epos
- | TCast(e1,None) -> compound "cast" [e1] e.epos
- | TBinop(op,e1,e2) -> compound (Printf.sprintf "'%s' operator" (s_binop op)) [e1;e2] e.epos
- | TUnop(op,_,e1) -> compound (Printf.sprintf "'%s' operator" (s_unop op)) [e1] e.epos
- | TField(e1,_) -> compound "field access" [e1] e.epos
- | TArrayDecl el -> compound "array declaration" el e.epos
- | TObjectDecl fl -> compound "object declaration" (List.map snd fl) e.epos
- in
- loop true e
- let prepare_field dctx dectx com cf = match cf.cf_expr with
- | None -> ()
- | Some e ->
- find_unused_variables dctx e;
- check_other_things com e;
- DeprecationCheck.run_on_expr {dectx with field_meta = cf.cf_meta} e
- let collect_diagnostics dctx com =
- let open CompilationCache in
- let dectx = DeprecationCheck.create_context com in
- List.iter (function
- | TClassDecl c when DiagnosticsPrinter.is_diagnostics_file com (com.file_keys#get c.cl_pos.pfile) ->
- let dectx = {dectx with class_meta = c.cl_meta} in
- List.iter (prepare_field dctx dectx com) c.cl_ordered_fields;
- List.iter (prepare_field dctx dectx com) c.cl_ordered_statics;
- (match c.cl_constructor with None -> () | Some cf -> prepare_field dctx dectx com cf);
- | _ ->
- ()
- ) com.types;
- let handle_dead_blocks com = match com.cache with
- | Some cc ->
- let display_defines = adapt_defines_to_display_context com.defines in
- let is_true defines e =
- ParserEntry.is_true (ParserEntry.eval defines e)
- in
- Hashtbl.iter (fun file_key cfile ->
- if DisplayPosition.display_position#is_in_file (com.file_keys#get cfile.c_file_path) then begin
- let dead_blocks = cfile.c_pdi.pd_dead_blocks in
- let dead_blocks = List.filter (fun (_,e) -> not (is_true display_defines e)) dead_blocks in
- try
- let dead_blocks2 = Hashtbl.find dctx.dead_blocks file_key in
- (* Intersect *)
- let dead_blocks2 = List.filter (fun (p,_) -> List.mem_assoc p dead_blocks) dead_blocks2 in
- Hashtbl.replace dctx.dead_blocks file_key dead_blocks2
- with Not_found ->
- Hashtbl.add dctx.dead_blocks file_key dead_blocks
- end
- ) cc#get_files
- | None ->
- ()
- in
- handle_dead_blocks com
- let prepare com =
- let dctx = {
- removable_code = [];
- import_positions = PMap.empty;
- dead_blocks = Hashtbl.create 0;
- diagnostics_messages = [];
- unresolved_identifiers = [];
- missing_fields = PMap.empty;
- } in
- if not (List.exists (fun (_,_,_,sev,_) -> sev = MessageSeverity.Error) com.shared.shared_display_information.diagnostics_messages) then
- collect_diagnostics dctx com;
- let process_modules com =
- List.iter (fun m ->
- PMap.iter (fun p b ->
- if not (PMap.mem p dctx.import_positions) then
- dctx.import_positions <- PMap.add p b dctx.import_positions
- else if !b then begin
- let b' = PMap.find p dctx.import_positions in
- b' := true
- end
- ) m.m_extra.m_display.m_import_positions;
- ) com.modules;
- List.iter (function
- | MissingFields mf ->
- let p = mf.mf_pos in
- begin try
- let _,l = PMap.find p dctx.missing_fields in
- l := mf :: !l
- with Not_found ->
- dctx.missing_fields <- PMap.add p (mf.mf_on,ref [mf]) dctx.missing_fields
- end
- ) com.display_information.module_diagnostics
- in
- process_modules com;
- begin match com.get_macros() with
- | None -> ()
- | Some com -> process_modules com
- end;
- (* We do this at the end because some of the prepare functions might add information to the common context. *)
- dctx.diagnostics_messages <- com.shared.shared_display_information.diagnostics_messages;
- dctx.unresolved_identifiers <- com.display_information.unresolved_identifiers;
- dctx
- let secure_generated_code ctx e =
- (* This causes problems and sucks in general... need a different solution. But I forgot which problem this solved anyway. *)
- (* mk (TMeta((Meta.Extern,[],e.epos),e)) e.etype e.epos *)
- e
- let print com =
- let dctx = prepare com in
- Json.string_of_json (DiagnosticsPrinter.json_of_diagnostics com dctx)
- let run com =
- let dctx = prepare com in
- dctx
|