<commit_before>open Cfg
open Visitor
open Utils
open Set
open Cfg_refactor
open Cfg_printer.ErrorPrinter

class removeDeadCode = object(self)
  inherit default_visitor as super

  method visit_stmt node = match node.snode with
    | If(`ID_True,if_true,if_false) -> ChangeTo(if_true)

    | If(`ID_False,if_true,if_false)
    | If(`ID_Nil,if_true,if_false) -> ChangeTo(if_false)

    | _ -> super#visit_stmt node
end

type after_redirect_return_val = 
  | True
  | False
  | Anything
  | NoRedirect

let is_redirect name redirects = 
  begin try let return_value = StrMap.find name redirects in
	    match return_value with
	      | NoRedirect -> false
	      | _ -> true
    with Not_found -> false
  end


let string_of_after_redirect value = match value with
  | True -> "Always returns true after redirect"
  | False -> "Always returns false after redirect"
  | Anything -> "Anything is possible after redirect"
  | NoRedirect -> "No redirect found"
  

let possible_return_val_after_redirect ?(super=false) redirects cfg = 
  let rec return_val_after_redirect stmt prev = 
    let after_ear = fst(prev) in
    let prev_return = snd(prev) in
    let next = after_ear, prev_return in
    let combine (after1, r1) (after2,r2) = 
      let combine_after = after1 or after2 in
      let combine_return = 
	match r1, r2 with
	  | NoRedirect, other 
	  | other, NoRedirect -> other
	  | True, True -> True
	  | False, False -> False
	  | _, _  -> Anything
      in
      combine_after, combine_return
    in
    let rec find_in_seq lst prev = match lst with 
      | [] -> prev
      | x :: xs -> find_in_seq xs (return_val_after_redirect x prev) in
    match stmt.snode with    
      | MethodCall(_, ({mc_msg = `ID_Super})) ->
	if super then
	  true, Anything
	else
	  next
      | MethodCall(_, ({mc_target = None; mc_msg = `ID_MethodName(name)} as mc)) -> 
	if is_redirect name redirects then 
	  true, Anything
	else
	  begin
	    match mc with
	      | {mc_cb = Some( CB_Block(_, block))} -> return_val_after_redirect block next
	      | _ -> next
	  end
      | Return(Some( `ID_Nil))
      | Next(Some( `ID_Nil))
      | Return(Some(`ID_False)) 
      | Next(Some(`ID_False))
      | Return(None) 
      | Next(None) ->
	if after_ear then
	  false, False
	else
	  false, prev_return
      | Return(Some( `ID_True)) 
      | Next(Some( `ID_True)) ->
	if after_ear then
	  false, True
	else
	  false, prev_return
      | Return _ 
      | Next _ ->
	if after_ear then
	  false, Anything
	else
	  false, prev_return
      | Seq lst -> find_in_seq lst next
      | If(expr,if_true,if_false) -> combine (return_val_after_redirect if_true next) (return_val_after_redirect if_false next)
      | Case({case_else=Some(stmt as block_else)} as block) -> combine (find_in_seq (List.map snd block.case_whens) next) (return_val_after_redirect block_else next)
      | Case({case_else=None} as block) -> find_in_seq (List.map snd block.case_whens) next 
      | While(expr, stmt) -> return_val_after_redirect stmt next
      | For(_, _, stmt) -> return_val_after_redirect stmt next
      (* This is for when we find a redirect_to call *)
      | Module(_, _, stmt) -> return_val_after_redirect stmt (false, prev_return)
      | Method(_, _, stmt) -> return_val_after_redirect stmt (false, prev_return)
      | Class(_, _, stmt) -> return_val_after_redirect stmt (false, prev_return)
      | ExnBlock block -> 
	let e_else = match block.exn_else with
	  | Some(stmt) -> [return_val_after_redirect stmt next]
	  | None -> []
	in
	let e_ensure = match block.exn_ensure with
	  | Some(stmt) -> [return_val_after_redirect stmt next]
	  | None -> []
	in
	let rescue_stmts = List.map (fun rb -> rb.rescue_body) block.exn_rescue in
	let rescue_ears = List.map (fun stmt -> return_val_after_redirect stmt next) rescue_stmts in
	List.fold_left combine prev ([return_val_after_redirect block.exn_body next] @ rescue_ears @ e_else @ e_ensure) 
      | Begin(stmt) -> return_val_after_redirect stmt next
      | End(stmt) -> return_val_after_redirect stmt next
      | Defined(_,stmt) -> return_val_after_redirect stmt next

      (* An expression with just nil doesn't do anything *)
      | Expression `ID_Nil -> prev

      | MethodCall(_,_) 
      | Break _
      | Redo
      | Retry
      | Undef _
      | Yield _
      | Expression _
      | Assign _
      | Alias _
	-> next
  in
  let initial = (false, NoRedirect) in
  let is_ear, result = return_val_after_redirect cfg initial in
  result

module OrderedStmt = struct
  type t = stmt
  let compare stmt1 stmt2 = Pervasives.compare stmt1.sid stmt2.sid
  
end

module StmtMap = Map.Make(OrderedStmt) 

let rec has_a_redirect_method methods stmt = 
  let combine stmts =
    List.fold_left (or) (false) (List.map (has_a_redirect_method methods) stmts) in
  match stmt.snode with    
    | MethodCall(_, {mc_target = None; mc_msg = `ID_MethodName(name)}) -> StrSet.mem name methods
    | Seq lst -> combine lst
    | If(expr,if_true,if_false) -> (has_a_redirect_method methods if_true) or (has_a_redirect_method methods if_false)
    | Case({case_else=Some(stmt as block_else)} as block) -> (combine (List.map snd block.case_whens)) or (has_a_redirect_method methods block_else)
    | Case({case_else=None} as block) -> combine (List.map snd block.case_whens)
    | While(expr, stmt) -> has_a_redirect_method methods stmt
    | For(_, _, stmt) -> has_a_redirect_method methods stmt
    | MethodCall(_, {mc_cb = Some( CB_Block (params, block))}) -> has_a_redirect_method methods block
    | Module(_, _, stmt) -> false
    | Method(_, _, stmt) -> false
    | Class(_, _, stmt) -> false
    | ExnBlock block -> 
      let e_else = match block.exn_else with
	| Some(stmt) -> has_a_redirect_method methods stmt
	| None -> false
	in
	let e_ensure = match block.exn_ensure with
	  | Some(stmt) -> has_a_redirect_method methods stmt
	  | None -> false
	in
	let rescue_stmts = List.map (fun rb -> rb.rescue_body) block.exn_rescue in
	let rescue_has_redirect = combine rescue_stmts in
	(has_a_redirect_method methods block.exn_body) or rescue_has_redirect or e_else or e_ensure 
      | Begin(stmt)
      | End(stmt) 
      | Defined(_,stmt) 
	-> has_a_redirect_method methods stmt 
	
      | _ -> false

class findAllPossibleReturnValuesForRedirects redirects = object(self)
  inherit default_visitor as super
    
  val mutable redirect_map = redirects
<commit_msg>Fixed a bug in keeping track of the return values<commit_after>open Cfg
open Visitor
open Utils
open Set
open Cfg_refactor
open Cfg_printer.ErrorPrinter

class removeDeadCode = object(self)
  inherit default_visitor as super

  method visit_stmt node = match node.snode with
    | If(`ID_True,if_true,if_false) -> ChangeTo(if_true)

    | If(`ID_False,if_true,if_false)
    | If(`ID_Nil,if_true,if_false) -> ChangeTo(if_false)

    | _ -> super#visit_stmt node
end

type after_redirect_return_val = 
  | True
  | False
  | Anything
  | NoRedirect

let is_redirect name redirects = 
  begin try let return_value = StrMap.find name redirects in
	    match return_value with
	      | NoRedirect -> false
	      | _ -> true
    with Not_found -> false
  end


let string_of_after_redirect value = match value with
  | True -> "Always returns true after redirect"
  | False -> "Always returns false after redirect"
  | Anything -> "Anything is possible after redirect"
  | NoRedirect -> "No redirect found"
  

let possible_return_val_after_redirect ?(super=false) redirects cfg = 
  let rec return_val_after_redirect stmt prev = 
    let after_ear = fst(prev) in
    let prev_return = snd(prev) in
    let next = after_ear, prev_return in
    let combine_return  r1 r2 = 
      match r1, r2 with
	| NoRedirect, other 
	| other, NoRedirect -> other
	| True, True -> True
	| False, False -> False
	| _, _  -> Anything
    in
    let combine (after1, r1) (after2,r2) = 
      let combine_after = after1 or after2 in
      combine_after, combine_return r1 r2
    in
    let rec find_in_seq lst prev = match lst with 
      | [] -> prev
      | x :: xs -> find_in_seq xs (return_val_after_redirect x prev) in
    match stmt.snode with    
      | MethodCall(_, ({mc_msg = `ID_Super})) ->
	if super then
	  true, NoRedirect
	else
	  next
      | MethodCall(_, ({mc_target = None; mc_msg = `ID_MethodName(name)} as mc)) -> 
	if is_redirect name redirects then 
	  true, NoRedirect
	else
	  begin
	    match mc with
	      | {mc_cb = Some( CB_Block(_, block))} -> return_val_after_redirect block next
	      | _ -> next
	  end
      | Return(Some( `ID_Nil))
      | Next(Some( `ID_Nil))
      | Return(Some(`ID_False)) 
      | Next(Some(`ID_False))
      | Return(None) 
      | Next(None) ->
	if after_ear then
	  false, combine_return prev_return False
	else
	  false, prev_return
      | Return(Some( `ID_True)) 
      | Next(Some( `ID_True)) ->
	if after_ear then
	  false, combine_return prev_return True
	else
	  false, prev_return
      | Return _ 
      | Next _ ->
	if after_ear then
	  false, Anything
	else
	  false, prev_return
      | Seq lst -> find_in_seq lst next
      | If(expr,if_true,if_false) -> combine (return_val_after_redirect if_true next) (return_val_after_redirect if_false next)
      | Case({case_else=Some(stmt as block_else)} as block) -> combine (find_in_seq (List.map snd block.case_whens) next) (return_val_after_redirect block_else next)
      | Case({case_else=None} as block) -> find_in_seq (List.map snd block.case_whens) next 
      | While(expr, stmt) -> return_val_after_redirect stmt next
      | For(_, _, stmt) -> return_val_after_redirect stmt next
      (* This is for when we find a redirect_to call *)
      | Module(_, _, stmt) -> return_val_after_redirect stmt (false, prev_return)
      | Method(_, _, stmt) -> return_val_after_redirect stmt (false, prev_return)
      | Class(_, _, stmt) -> return_val_after_redirect stmt (false, prev_return)
      | ExnBlock block -> 
	let e_else = match block.exn_else with
	  | Some(stmt) -> [return_val_after_redirect stmt next]
	  | None -> []
	in
	let e_ensure = match block.exn_ensure with
	  | Some(stmt) -> [return_val_after_redirect stmt next]
	  | None -> []
	in
	let rescue_stmts = List.map (fun rb -> rb.rescue_body) block.exn_rescue in
	let rescue_ears = List.map (fun stmt -> return_val_after_redirect stmt next) rescue_stmts in
	List.fold_left combine prev ([return_val_after_redirect block.exn_body next] @ rescue_ears @ e_else @ e_ensure) 
      | Begin(stmt) -> return_val_after_redirect stmt next
      | End(stmt) -> return_val_after_redirect stmt next
      | Defined(_,stmt) -> return_val_after_redirect stmt next

      (* An expression with just nil doesn't do anything *)
      | Expression `ID_Nil -> prev

      | MethodCall(_,_) 
      | Break _
      | Redo
      | Retry
      | Undef _
      | Yield _
      | Expression _
      | Assign _
      | Alias _
	-> next
  in
  let initial = (false, NoRedirect) in
  let is_ear, result = return_val_after_redirect cfg initial in
  result

class findAllPossibleReturnValuesForRedirects redirects = object(self)
  inherit default_visitor as super
    
  val mutable redirect_map = redirects

{'fraction_non_alphanumeric': 0.09285201149425287, 'fraction_numerical': 0.0021551724137931034, 'mean_word_length': 3.150950428624674, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 110, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2437249', 'n_tokens_mistral': 3830, 'n_tokens_neox': 3711, 'n_words': 1022}