summaryrefslogtreecommitdiffstats
path: root/perl_checker.src/parser_helper.ml
diff options
context:
space:
mode:
Diffstat (limited to 'perl_checker.src/parser_helper.ml')
-rw-r--r--perl_checker.src/parser_helper.ml250
1 files changed, 189 insertions, 61 deletions
diff --git a/perl_checker.src/parser_helper.ml b/perl_checker.src/parser_helper.ml
index 0fe96b7..62a85f0 100644
--- a/perl_checker.src/parser_helper.ml
+++ b/perl_checker.src/parser_helper.ml
@@ -11,7 +11,12 @@ let get_pos (_, (_, pos)) = raw_pos2pos pos
let var_dollar_ = Deref(I_scalar, Ident(None, "_", raw_pos2pos bpos))
let var_STDOUT = Deref(I_star, Ident(None, "STDOUT", raw_pos2pos bpos))
+let is_var_dollar_ = function
+ | Deref(I_scalar, Ident(None, "_", _)) -> true
+ | _ -> false
+
let is_parenthesized = function
+ | List[]
| List[List[_]] -> true
| _ -> false
@@ -26,8 +31,8 @@ let rec un_parenthesize_full = function
let not_complex e =
if is_parenthesized e then true else
let rec not_complex_ op = function
- | Call_op("?:", _) -> false
- | Call_op(op', l) -> op <> op' && List.for_all (not_complex_ op') l
+ | Call_op("?:", _, _) -> false
+ | Call_op(op', l, _) -> op <> op' && List.for_all (not_complex_ op') l
| e -> not (is_parenthesized e)
in not_complex_ "" (un_parenthesize_full e)
@@ -39,6 +44,44 @@ let string_of_Ident = function
| Ident(None, s, _) -> s
| Ident(Some fq, s, _) -> fq ^ "::" ^ s
| _ -> internal_error "string_of_Ident"
+let context2s = function
+ | I_scalar -> "$"
+ | I_hash -> "%"
+ | I_array -> "@"
+ | I_func -> "&"
+ | I_raw -> ""
+ | I_star -> "*"
+let variable2s(context, ident) = context2s context ^ ident
+
+let non_scalar_context context = context = I_hash || context = I_array
+
+let rec is_same_fromparser a b =
+ match a, b with
+ | Undef, Undef -> true
+ | Ident(fq1, s1, _), Ident(fq2, s2, _) -> fq1 = fq2 && s1 = s2
+ | Num(s1, _), Num(s2, _)
+ | Raw_string(s1, _), Raw_string(s2, _) -> s1 = s2
+
+ | String(l1, _), String(l2, _) ->
+ List.for_all2 (fun (s1, e1) (s2, e2) -> s1 = s2 && is_same_fromparser e1 e2) l1 l2
+
+ | Ref(c1, e1), Ref(c2, e2)
+ | Deref(c1, e1), Deref(c2, e2) -> c1 = c2 && is_same_fromparser e1 e2
+
+ | Deref_with(c1, c_1, e1, e_1), Deref_with(c2, c_2, e2, e_2) -> c1 = c2 && c_1 = c_2 && is_same_fromparser e1 e2 && is_same_fromparser e_1 e_2
+
+ | Diamond(None), Diamond(None) -> true
+ | Diamond(Some e1), Diamond(Some e2) -> is_same_fromparser e1 e2
+
+ | List(l1), List(l2) -> List.for_all2 is_same_fromparser l1 l2
+
+ | Call_op(op1, l1, _), Call_op(op2, l2, _) -> op1 = op2 && List.for_all2 is_same_fromparser l1 l2
+ | Call(e1, l1), Call(e2, l2) -> is_same_fromparser e1 e2 && List.for_all2 is_same_fromparser l1 l2
+
+ | Method_call(e1, m1, l1), Method_call(e2, m2, l2) ->
+ is_same_fromparser e1 e2 && is_same_fromparser m1 m2 && List.for_all2 is_same_fromparser l1 l2
+
+ | _ -> false
let from_scalar (e, _) =
match e with
@@ -204,21 +247,51 @@ let check_parenthesized_first_argexpr word ((_, e), (_, (start, _)) as ex) =
let want_space = word.[0] = '-' in
if word = "return" then () else
match e with
- | [ Call_op(_, (e' :: l)) ]
+ | [ Call_op(_, (e' :: l), _) ]
| e' :: l ->
if is_parenthesized e' then
- if want_space then
- if l = [] then sp_n(ex) else die_with_rawpos (start, start) "can't handle this nicely"
- else
- if l = [] then sp_0(ex) else die_with_rawpos (start, start) "you must not have a space here"
+ if l = [] then
+ (if want_space then sp_n else sp_0) ex
+ else die_with_rawpos (start, start) "can't handle this nicely"
+ else
+ sp_p(ex)
| _ ->
if word = "time" then die_rule "please use time() instead of time";
sp_p(ex)
-let check_foreach (s, (_, pos)) = if s = "for" then warn pos "write \"foreach\" instead of \"for\""
-let check_for (s, (_, pos)) = if s = "foreach" then warn pos "write \"for\" instead of \"foreach\""
+let check_hash_subscript ((_, e), (_, pos)) =
+ let can_be_raw_string = function
+ | "" | "x" | "y" -> false (* special case for {'y'} otherwise the emacs mode goes wild, special case for {'x'} to have the same as {'y'} (since they usually go together) *)
+ | s ->
+ char_is_alpha s.[0] && (String.length s = 1 || string_forall_with char_is_alphanumerical_ 1 s)
+ in
+ match e with
+ | List [String ([(s, List [])], _)] when can_be_raw_string s -> warn pos (sprintf "{\"%s\"} can be written {%s}" s s)
+ | List [Raw_string(s, _)] when can_be_raw_string s -> warn pos (sprintf "{'%s'} can be written {%s}" s s)
+ | _ -> ()
+
+let check_arrow_needed ((_, e), _) ter =
+ match e with
+ | Deref_with(I_array, I_scalar, List [List [Call _]], _) -> () (* "->" needed for (f())[0]->{XX} *)
+ | Deref_with _ -> warn (sndsnd ter) "the arrow \"->\" is unneeded"
+ | _ -> ()
+
+let check_unneeded_var_dollar_ ((_, e), (_, pos)) = if is_var_dollar_ e then warn pos "\"$_ =~ /regexp/\" can be written \"/regexp/\""
+let check_unneeded_var_dollar_s ((_, e), (_, pos)) = if is_var_dollar_ e then warn pos "\"$_ =~ s/regexp/.../\" can be written \"s/regexp/.../\""
+let check_unneeded_var_dollar_not ((_, e), (_, pos)) = if is_var_dollar_ e then warn pos "\"$_ !~ /regexp/\" can be written \"!/regexp/\""
+
let check_MULT_is_x (s, _) = if s <> "x" then die_rule "syntax error"
let check_my (s, _) = if s <> "my" then die_rule "syntax error"
+let check_foreach (s, (_, pos)) = if s = "for" then warn pos "write \"foreach\" instead of \"for\""
+let check_for (s, (_, pos)) = if s = "foreach" then warn pos "write \"for\" instead of \"foreach\""
+let check_for_foreach (s, (_, pos)) ((_, expr), _) =
+ match expr with
+ | List [ Deref(I_scalar, _) ] ->
+ if s = "foreach" then warn pos "you are using the special fpons trick to locally set $_ with a value, for this please use \"for\" instead of \"foreach\""
+ | List [ Deref_with(context, I_scalar, _, _) ] when context <> I_func ->
+ if s = "foreach" then warn pos "you are using the special fpons trick to locally set $_ with a value, for this please use \"for\" instead of \"foreach\""
+ | _ ->
+ if s = "for" then warn pos "write \"foreach\" instead of \"for\""
let check_block_sub (l, (_, (_, end_)) as ter_lines) (_, (space, _) as ter_BRACKET_END) =
if l = [] then
@@ -254,6 +327,15 @@ let only_one_in_List ((_, e), both) =
| List l -> only_one(l, both)
| _ -> e
+let rec is_only_one_in_List = function
+ | [List l] -> is_only_one_in_List l
+ | [_] -> true
+ | _ -> false
+
+let is_not_a_scalar = function
+ | Deref_with(_, context, _, _)
+ | Deref(context, _) -> non_scalar_context context
+ | _ -> false
let maybe_to_Raw_string = function
| Ident(None, s, pos) -> Raw_string(s, pos)
@@ -267,8 +349,12 @@ let to_List = function
let deref_arraylen e = Call(Deref(I_func, Ident(None, "int", raw_pos2pos bpos)), [Deref(I_array, e)])
let to_Ident ((fq, name), (_, pos)) = Ident(fq, name, raw_pos2pos pos)
let to_Raw_string (s, (_, pos)) = Raw_string(s, raw_pos2pos pos)
-let to_Method_callP(object_, method_, para) = Method_callP(maybe_to_Raw_string object_, maybe_to_Raw_string method_, para)
-let to_Method_call (object_, method_, para) = Method_call (maybe_to_Raw_string object_, maybe_to_Raw_string method_, para)
+let to_Method_call (object_, method_, para) = Method_call(maybe_to_Raw_string object_, maybe_to_Raw_string method_, para)
+let to_Deref_with(from_context, to_context, ref_, para) =
+ if is_not_a_scalar ref_ then warn_rule "bad deref";
+ Deref_with(from_context, to_context, ref_, para)
+
+
let to_Local ((_, e), (_, pos)) =
let l =
match e with
@@ -281,14 +367,14 @@ let to_Local ((_, e), (_, pos)) =
| Deref(I_scalar, Ident _)
| Deref(I_array, Ident _)
| Deref(I_star, Ident _)
- | Deref_with(I_hash, Ident _, _)
- | Deref_with(I_hash, Deref(I_scalar, _), _)
- | Deref_with(I_hash, Deref_with(I_hash, Ident _, _), _)
- | Deref_with(I_hash, Deref_with(I_hash, Deref(I_scalar, Ident _), _), _) ->
+ | Deref_with(I_hash, I_scalar, Ident _, _)
+ | Deref_with(I_hash, I_scalar, Deref(I_scalar, _), _)
+ | Deref_with(I_hash, I_scalar, Deref_with(I_hash, I_scalar, Ident _, _), _)
+ | Deref_with(I_hash, I_scalar, Deref_with(I_hash, I_scalar, Deref(I_scalar, Ident _), _), _) ->
None
| _ -> die_with_rawpos pos "bad argument to \"local\""
) l in
- if local_vars = [] then Call_op("local", local_exprs)
+ if local_vars = [] then Call_op("local", local_exprs, raw_pos2pos pos)
else if local_exprs = [] then My_our("local", local_vars, raw_pos2pos pos)
else die_with_rawpos pos "bad argument to \"local\""
@@ -298,41 +384,43 @@ let op_p prio s e = sp_p e ; op prio s e
let sub_declaration (name, proto) body = Sub_declaration(name, proto, Block body)
let anonymous_sub body = Anonymous_sub (Block body)
-let call_op((prio, (prev_ter, op)), (_, (_, pos) as ter), para) =
+let cook_call_op(op, para, pos) =
+ let call = Call_op(op, para, raw_pos2pos pos) in
+ match op, para with
+ | "=", [My_our _; Ident(None, "undef", _)] ->
+ warn pos "no need to initialize variable, it's done by default" ;
+ call
+ | "=", [My_our _; List[]] ->
+ if Info.is_on_same_line_current pos then warn pos "no need to initialize variables, it's done by default" ;
+ call
+
+ | "=", [ Deref(I_star, String ([(sf1, List [])], _)); _ ] ->
+ warn_rule (sprintf "write *{'%s'} instead of *{\"%s\"}" sf1 sf1) ;
+ call
+
+ | "=", [ Deref(I_star, (Ident _ as f1)); Deref(I_star, (Ident _ as f2)) ] ->
+ let s1, s2 = string_of_Ident f1, string_of_Ident f2 in
+ warn pos (sprintf "\"*%s = *%s\" is better written \"*%s = \\&%s\"" s1 s2 s1 s2) ;
+ sub_declaration (f1, "") [ Deref(I_func, f2) ]
+ | "=", [ Deref(I_star, Raw_string(sf1, pos_f1)); Deref(I_star, (Ident _ as f2)) ] ->
+ let s2 = string_of_Ident f2 in
+ warn pos (sprintf "\"*{'%s'} = *%s\" is better written \"*{'%s'} = \\&%s\"" sf1 s2 sf1 s2) ;
+ sub_declaration (Ident(None, sf1, pos_f1), "") [ Deref(I_func, f2) ]
+
+ | "=", [ Deref(I_star, (Ident _ as f1)); Ref(I_scalar, Deref(I_func, (Ident _ as f2))) ] ->
+ sub_declaration (f1, "") [ Deref(I_func, f2) ]
+ | "=", [ Deref(I_star, Raw_string(sf1, pos_f1)); Ref(I_scalar, Deref(I_func, (Ident _ as f2))) ] ->
+ sub_declaration (Ident(None, sf1, pos_f1), "") [ Deref(I_func, f2) ]
+
+ | _ ->
+ call
+
+let call_op_((prio, (prev_ter, op)), ter, para) (sp, pos) =
sp_same prev_ter ter ;
+ (prio, cook_call_op(op, para, pos)), (sp, pos)
- let call = Call_op(op, para) in
- let call =
- match op, para with
- | "=", [List [My_our _]; Ident(None, "undef", _)] ->
- warn pos "no need to initialize variable, it's done by default" ;
- call
- | "=", [List [My_our _]; List[]] ->
- if Info.is_on_same_line_current pos then warn pos "no need to initialize variables, it's done by default" ;
- call
-
- | "=", [ Deref(I_star, String ([(sf1, List [])], _)); _ ] ->
- warn_rule (sprintf "write *{'%s'} instead of *{\"%s\"}" sf1 sf1) ;
- call
-
- | "=", [ Deref(I_star, (Ident _ as f1)); Deref(I_star, (Ident _ as f2)) ] ->
- let s1, s2 = string_of_Ident f1, string_of_Ident f2 in
- warn pos (sprintf "\"*%s = *%s\" is better written \"*%s = \\&%s\"" s1 s2 s1 s2) ;
- sub_declaration (f1, "") [ Deref(I_func, f2) ]
- | "=", [ Deref(I_star, Raw_string(sf1, pos_f1)); Deref(I_star, (Ident _ as f2)) ] ->
- let s2 = string_of_Ident f2 in
- warn pos (sprintf "\"*{'%s'} = *%s\" is better written \"*{'%s'} = \\&%s\"" sf1 s2 sf1 s2) ;
- sub_declaration (Ident(None, sf1, pos_f1), "") [ Deref(I_func, f2) ]
-
- | "=", [ Deref(I_star, (Ident _ as f1)); Ref(I_scalar, Deref(I_func, (Ident _ as f2))) ] ->
- sub_declaration (f1, "") [ Deref(I_func, f2) ]
- | "=", [ Deref(I_star, Raw_string(sf1, pos_f1)); Ref(I_scalar, Deref(I_func, (Ident _ as f2))) ] ->
- sub_declaration (Ident(None, sf1, pos_f1), "") [ Deref(I_func, f2) ]
-
- | _ ->
- call
- in
- prio, call
+let to_Call_op(op, para) (sp, pos) = Call_op(op, para, raw_pos2pos pos), (sp, pos)
+let to_Call_op_(prio, op, para) (sp, pos) = (prio, Call_op(op, para, raw_pos2pos pos)), (sp, pos)
let followed_by_comma ((_,e), _) (true_comma, _) =
if true_comma then e else
@@ -344,12 +432,6 @@ let call_func is_a_func (e, para) =
match e with
| Deref(I_func, Ident(None, f, _)) ->
let para' = match f with
- | "require" ->
- (match para with
- | [ Ident(_, _, pos) as s ] -> Some [ Raw_string(string_of_Ident s, pos) ]
- | [ String _ ]
- | [ Raw_string _ ] -> None
- | _ -> die_rule "use either \"require PACKAGE\" or \"require 'PACKAGE.pm'\"")
| "no" ->
(match para with
| [ Ident(_, _, pos) as s ] -> Some [ Raw_string(string_of_Ident s, pos) ]
@@ -357,7 +439,8 @@ let call_func is_a_func (e, para) =
| _ -> die_rule "use \"no PACKAGE <para>\"")
| "N" | "N_" ->
(match para with
- | [List(String _ :: _)] -> None
+ | [ List(String([ _s, List [] ], _) :: _) ] -> None
+ | [ List(String _ :: _) ] -> die_rule "don't use interpolated translated string, use %s or %d instead"
| _ -> die_rule (sprintf "%s() must be used with a string" f))
| "goto" ->
@@ -370,6 +453,11 @@ let call_func is_a_func (e, para) =
| [ Ident(None, s, pos) ] -> Some [ Raw_string(s, pos) ]
| _ -> die_rule (sprintf "%s must be used with a raw string" f))
+ | "length" ->
+ if para = [] then warn_rule "length() with no parameter !?" else
+ if is_not_a_scalar (List.hd para) then warn_rule "never use \"length @l\", it returns the length of the string int(@l)" ;
+ None
+
| _ -> None
in Call(e, some_or para' para)
| _ -> Call(e, para)
@@ -383,10 +471,34 @@ let call_one_scalar_para (e, (_, pos)) para =
| "defined" -> P_expr
| _ -> P_add
in
- pri, Call(Deref(I_func, Ident(None, e, raw_pos2pos pos)), para)
+ pri, call(Deref(I_func, Ident(None, e, raw_pos2pos pos)), para)
+
+
+let call_op_if_infix left right (sp, pos) =
+ (match left, right with
+ | List [Call_op("=", [Deref(context, _); _], _)], _ when non_scalar_context context -> ()
+ | List [Call_op("=", [v; _], _)],
+ List [Call_op("not", [v'], _)] when is_same_fromparser v v' ->
+ warn_rule "\"$foo = ... if !$foo\" can be written \"$foo ||= ...\""
+ | _ -> ());
+ Call_op("if infix", [ left ; right], raw_pos2pos pos), (sp, pos)
+
+let call_op_unless_infix left right (sp, pos) =
+ (match left, right with
+ | List [Call_op("=", [Deref(context, _); _], _)], _ when non_scalar_context context -> ()
+ | List [Call_op("=", [v; _], _)], List [v'] when is_same_fromparser v v' ->
+ warn_rule "\"$foo = ... unless $foo\" can be written \"$foo ||= ...\""
+ | _ -> ());
+ (match right with
+ | List [Call_op(op, _, _)] ->
+ (match op with
+ | "&&" | "||" | "not" | "ne" | "?:" -> warn_rule "don't use \"unless\" when the condition is complex, use \"if\" instead"
+ | _ -> ());
+ | _ -> ());
+ Call_op("unless infix", [ left ; right], raw_pos2pos pos), (sp, pos)
-let (current_lexbuf : Lexing.lexbuf option ref) = ref None
+let (current_lexbuf : Lexing.lexbuf option ref) = ref None
let rec list2tokens l =
let rl = ref l in
@@ -404,9 +516,25 @@ let parse_tokens parse tokens lexbuf_opt =
if tokens = [] then [] else
parse (list2tokens tokens) (some !current_lexbuf)
-let parse_interpolated parse l = List.map (fun (s, tokens) -> s, to_List(parse_tokens parse tokens None)) l
-
-let to_String parse (l, (_, pos)) = String(parse_interpolated parse l, raw_pos2pos pos)
+let parse_interpolated parse l =
+ let l' = List.map (fun (s, tokens) -> s, to_List(parse_tokens parse tokens None)) l in
+ match split_last l' with
+ | pl, ("", List []) -> pl
+ | _ -> l'
+
+let to_String parse strict (l, (_, pos)) =
+ let l' = parse_interpolated parse l in
+ (match l' with
+ | [ "", List [Deref(I_scalar, Ident(None, ident, _))]] ->
+ if strict then warn pos (sprintf "%s is better written without the double quotes" (variable2s(I_scalar, ident)))
+ | [ "", List [Deref(I_hash, _)]] ->
+ warn pos "don't use a hash in string context"
+ | [ "", List [Deref(I_array, _)]] ->
+ ()
+ | [("", _)] ->
+ if strict then warn pos "double quotes are unneeded"
+ | _ -> ());
+ String(l', raw_pos2pos pos)
let from_PATTERN parse ((s, opts), (_, pos)) =
[ String(parse_interpolated parse s, raw_pos2pos pos) ;