package reason

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file reason_parser_explain.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
(* See the comments in menhir_error_processor.ml *)

module Parser = Reason_parser
module Interp = Parser.MenhirInterpreter
module Raw = Reason_parser_explain_raw

let identlike_keywords =
  let reverse_table =
    lazy
      (let table = Hashtbl.create 7 in
       let flip_add k v = Hashtbl.add table v k in
       Hashtbl.iter flip_add Reason_declarative_lexer.keyword_table;
       table)
  in
  function
  | Parser.SIG -> Some "sig"
  | Parser.MODULE -> Some "module"
  | Parser.BEGIN -> Some "begin"
  | Parser.END -> Some "end"
  | Parser.OBJECT -> Some "object"
  | Parser.SWITCH -> Some "switch"
  | Parser.TO -> Some "to"
  | Parser.THEN -> Some "then"
  | Parser.TYPE -> Some "type"
  | token ->
    (match Hashtbl.find (Lazy.force reverse_table) token with
    | name -> Some name
    | exception Not_found -> None)

let keyword_confused_with_ident state token =
  match identlike_keywords token with
  | Some name
    when Raw.transitions_on_lident state || Raw.transitions_on_uident state ->
    name
    ^ " is a reserved keyword, it cannot be used as an identifier. Try `"
    ^ name
    ^ "_` or `_"
    ^ name
    ^ "` instead"
  | _ -> raise Not_found

let uppercased_instead_of_lowercased state token =
  match token with
  | Parser.UIDENT name when Raw.transitions_on_lident state ->
    let name = String.uncapitalize_ascii name in
    if Hashtbl.mem Reason_declarative_lexer.keyword_table name
    then "variables and labels should be lowercased"
    else
      Printf.sprintf "variables and labels should be lowercased. Try `%s'" name
  | _ -> raise Not_found

let semicolon_might_be_missing state _token =
  (*let state = Interp.current_state_number env in*)
  if Raw.transitions_on_semi state
  then "syntax error, consider adding a `;' before"
  else raise Not_found

let token_specific_message = function
  | Parser.UNDERSCORE ->
    "underscore is not a valid identifier. Use _ only in pattern matching and \
     partial function application"
  | _ -> raise Not_found

let unclosed_parenthesis is_opening_symbol closing_symbol check_function env =
  let state = Interp.current_state_number env in
  if check_function state
  then
    let rec find_opening_location = function
      | None -> None
      | Some env ->
        let found =
          match Interp.top env with
          | Some (Interp.Element (state, _, startp, endp))
            when is_opening_symbol (Interp.X (Interp.incoming_symbol state)) ->
            Some (startp, endp)
          | Some (Interp.Element (state, _, _, _))
            when Interp.X (Interp.incoming_symbol state) = closing_symbol ->
            raise Not_found
          | _ -> None
        in
        (match found with
        | Some _ -> found
        | _ -> find_opening_location (Interp.pop env))
    in
    try find_opening_location (Some env) with Not_found -> None
  else None

let check_unclosed env =
  let check (message, opening_symbols, closing_symbol, check_function) =
    match
      unclosed_parenthesis
        (fun x -> List.mem x opening_symbols)
        closing_symbol
        check_function
        env
    with
    | None -> None
    | Some (loc_start, _) ->
      Some
        (Format.asprintf
           "Unclosed %S (opened line %d, column %d)"
           message
           loc_start.pos_lnum
           (loc_start.pos_cnum - loc_start.pos_bol))
  in
  let rec check_list = function
    | [] -> raise Not_found
    | x :: xs ->
      (match check x with None -> check_list xs | Some result -> result)
  in
  check_list
    [ ( "("
      , Interp.[ X (T T_LPAREN) ]
      , Interp.X (T T_RPAREN)
      , Raw.transitions_on_rparen )
    ; ( "{"
      , Interp.[ X (T T_LBRACE); X (T T_LBRACELESS) ]
      , Interp.X (T T_RBRACE)
      , Raw.transitions_on_rbrace )
    ; ( "["
      , Interp.
          [ X (T T_LBRACKET)
          ; X (T T_LBRACKETAT)
          ; X (T T_LBRACKETBAR)
          ; X (T T_LBRACKETGREATER)
          ; X (T T_LBRACKETLESS)
          ; X (T T_LBRACKETPERCENT)
          ; X (T T_LBRACKETPERCENTPERCENT)
          ]
      , Interp.X (T T_RBRACKET)
      , Raw.transitions_on_rbracket )
    ]

let message env (token, _, _) =
  let state = Interp.current_state_number env in
  (* Identify a keyword used as an identifier *)
  try keyword_confused_with_ident state token with
  | Not_found ->
    (try check_unclosed env with
    | Not_found ->
      (* Identify an uppercased identifier in a lowercase place *)
      (try uppercased_instead_of_lowercased state token with
      | Not_found ->
        (try semicolon_might_be_missing state token with
        | Not_found ->
          (try token_specific_message token with
          | Not_found ->
            (* Is there a message for this specific state ? *)
            (* TODO: we don't know what to say *)
            "Syntax error"))))
OCaml

Innovation. Community. Security.