daemon: debug segv correct use of dereferencing NULL.
[libguestfs.git] / generator / generator_checks.ml
1 (* libguestfs
2  * Copyright (C) 2009-2010 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  *)
18
19 (* Please read generator/README first. *)
20
21 open Generator_types
22 open Generator_utils
23 open Generator_actions
24
25 (* Check function names etc. for consistency. *)
26 let () =
27   let contains_uppercase str =
28     let len = String.length str in
29     let rec loop i =
30       if i >= len then false
31       else (
32         let c = str.[i] in
33         if c >= 'A' && c <= 'Z' then true
34         else loop (i+1)
35       )
36     in
37     loop 0
38   in
39
40   (* Check function names. *)
41   List.iter (
42     fun (name, _, _, _, _, _, _) ->
43       if String.length name >= 7 && String.sub name 0 7 = "guestfs" then
44         failwithf "function name %s does not need 'guestfs' prefix" name;
45       if name = "" then
46         failwithf "function name is empty";
47       if name.[0] < 'a' || name.[0] > 'z' then
48         failwithf "function name %s must start with lowercase a-z" name;
49       if String.contains name '-' then
50         failwithf "function name %s should not contain '-', use '_' instead."
51           name
52   ) (all_functions @ fish_commands);
53
54   (* Check function parameter/return names. *)
55   List.iter (
56     fun (name, style, _, _, _, _, _) ->
57       let check_arg_ret_name n =
58         if contains_uppercase n then
59           failwithf "%s param/ret %s should not contain uppercase chars"
60             name n;
61         if String.contains n '-' || String.contains n '_' then
62           failwithf "%s param/ret %s should not contain '-' or '_'"
63             name n;
64         if n = "value" then
65           failwithf "%s has a param/ret called 'value', which causes conflicts in the OCaml bindings, use something like 'val' or a more descriptive name" name;
66         if n = "int" || n = "char" || n = "short" || n = "long" then
67           failwithf "%s has a param/ret which conflicts with a C type (eg. 'int', 'char' etc.)" name;
68         if n = "i" || n = "n" then
69           failwithf "%s has a param/ret called 'i' or 'n', which will cause some conflicts in the generated code" name;
70         if n = "argv" || n = "args" then
71           failwithf "%s has a param/ret called 'argv' or 'args', which will cause some conflicts in the generated code" name;
72
73         (* List Haskell, OCaml and C keywords here.
74          * http://www.haskell.org/haskellwiki/Keywords
75          * http://caml.inria.fr/pub/docs/manual-ocaml/lex.html#operator-char
76          * http://en.wikipedia.org/wiki/C_syntax#Reserved_keywords
77          * Formatted via: cat c haskell ocaml|sort -u|grep -vE '_|^val$' \
78          *   |perl -pe 's/(.+)/"$1";/'|fmt -70
79          * Omitting _-containing words, since they're handled above.
80          * Omitting the OCaml reserved word, "val", is ok,
81          * and saves us from renaming several parameters.
82          *)
83         let reserved = [
84           "and"; "as"; "asr"; "assert"; "auto"; "begin"; "break"; "case";
85           "char"; "class"; "const"; "constraint"; "continue"; "data";
86           "default"; "deriving"; "do"; "done"; "double"; "downto"; "else";
87           "end"; "enum"; "exception"; "extern"; "external"; "false"; "float";
88           "for"; "forall"; "foreign"; "fun"; "function"; "functor"; "goto";
89           "hiding"; "if"; "import"; "in"; "include"; "infix"; "infixl";
90           "infixr"; "inherit"; "initializer"; "inline"; "instance"; "int";
91           "interface";
92           "land"; "lazy"; "let"; "long"; "lor"; "lsl"; "lsr"; "lxor";
93           "match"; "mdo"; "method"; "mod"; "module"; "mutable"; "new";
94           "newtype"; "object"; "of"; "open"; "or"; "private"; "qualified";
95           "rec"; "register"; "restrict"; "return"; "short"; "sig"; "signed";
96           "sizeof"; "static"; "struct"; "switch"; "then"; "to"; "true"; "try";
97           "type"; "typedef"; "union"; "unsigned"; "virtual"; "void";
98           "volatile"; "when"; "where"; "while";
99           ] in
100         if List.mem n reserved then
101           failwithf "%s has param/ret using reserved word %s" name n;
102       in
103
104       let ret, args, optargs = style in
105
106       (match ret with
107        | RErr -> ()
108        | RInt n | RInt64 n | RBool n
109        | RConstString n | RConstOptString n | RString n
110        | RStringList n | RStruct (n, _) | RStructList (n, _)
111        | RHashtable n | RBufferOut n ->
112            check_arg_ret_name n
113       );
114       List.iter (fun arg -> check_arg_ret_name (name_of_argt arg)) args;
115       List.iter (fun arg -> check_arg_ret_name (name_of_optargt arg)) optargs;
116   ) all_functions;
117
118   (* Maximum of 63 optargs permitted. *)
119   List.iter (
120     fun (name, (_, _, optargs), _, _, _, _, _) ->
121       if List.length optargs > 63 then
122         failwithf "maximum of 63 optional args allowed for %s" name;
123   ) all_functions;
124
125   (* Some parameter types not supported for daemon functions. *)
126   List.iter (
127     fun (name, (_, args, _), _, _, _, _, _) ->
128       let check_arg_type = function
129         | Pointer _ ->
130             failwithf "Pointer is not supported for daemon function %s."
131               name
132         | _ -> ()
133       in
134       List.iter check_arg_type args;
135   ) daemon_functions;
136
137   (* Check short descriptions. *)
138   List.iter (
139     fun (name, _, _, _, _, shortdesc, _) ->
140       if shortdesc.[0] <> Char.lowercase shortdesc.[0] then
141         failwithf "short description of %s should begin with lowercase." name;
142       let c = shortdesc.[String.length shortdesc-1] in
143       if c = '\n' || c = '.' then
144         failwithf "short description of %s should not end with . or \\n." name
145   ) (all_functions @ fish_commands);
146
147   (* Check long descriptions. *)
148   List.iter (
149     fun (name, _, _, _, _, _, longdesc) ->
150       if longdesc.[String.length longdesc-1] = '\n' then
151         failwithf "long description of %s should not end with \\n." name
152   ) (all_functions @ fish_commands);
153
154   (* Check proc_nrs. *)
155   List.iter (
156     fun (name, _, proc_nr, _, _, _, _) ->
157       if proc_nr <= 0 then
158         failwithf "daemon function %s should have proc_nr > 0" name
159   ) daemon_functions;
160
161   List.iter (
162     fun (name, _, proc_nr, _, _, _, _) ->
163       if proc_nr <> -1 then
164         failwithf "non-daemon function %s should have proc_nr -1" name
165   ) non_daemon_functions;
166
167   let proc_nrs =
168     List.map (fun (name, _, proc_nr, _, _, _, _) -> name, proc_nr)
169       daemon_functions in
170   let proc_nrs =
171     List.sort (fun (_,nr1) (_,nr2) -> compare nr1 nr2) proc_nrs in
172   let rec loop = function
173     | [] -> ()
174     | [_] -> ()
175     | (name1,nr1) :: ((name2,nr2) :: _ as rest) when nr1 < nr2 ->
176         loop rest
177     | (name1,nr1) :: (name2,nr2) :: _ ->
178         failwithf "%s and %s have conflicting procedure numbers (%d, %d)"
179           name1 name2 nr1 nr2
180   in
181   loop proc_nrs;
182
183   (* Check flags. *)
184   List.iter (
185     fun (name, _, _, flags, _, _, _) ->
186       List.iter (
187         function
188         | ProtocolLimitWarning
189         | FishOutput _
190         | NotInFish
191         | NotInDocs
192         | Progress -> ()
193         | FishAlias n ->
194             if contains_uppercase n then
195               failwithf "%s: guestfish alias %s should not contain uppercase chars" name n;
196             if String.contains n '_' then
197               failwithf "%s: guestfish alias %s should not contain '_'" name n
198         | DeprecatedBy n ->
199             (* 'n' must be a cross-ref to the name of another action. *)
200             if not (List.exists (
201                       function
202                       | (n', _, _, _, _, _, _) when n = n' -> true
203                       | _ -> false
204                     ) all_functions) then
205               failwithf "%s: DeprecatedBy flag must be cross-reference to another action" name
206         | Optional n ->
207             if contains_uppercase n then
208               failwithf "%s: Optional group name %s should not contain uppercase chars" name n;
209             if String.contains n '-' || String.contains n '_' then
210               failwithf "%s: Optional group name %s should not contain '-' or '_'" name n
211       ) flags
212   ) (all_functions @ fish_commands);
213
214   (* Check tests. *)
215   List.iter (
216     function
217       (* Ignore functions that have no tests.  We generate a
218        * warning when the user does 'make check' instead.
219        *)
220     | name, _, _, _, [], _, _ -> ()
221     | name, _, _, _, tests, _, _ ->
222         let funcs =
223           List.map (
224             fun (_, _, test) ->
225               match seq_of_test test with
226               | [] ->
227                   failwithf "%s has a test containing an empty sequence" name
228               | cmds -> List.map List.hd cmds
229           ) tests in
230         let funcs = List.flatten funcs in
231
232         let tested = List.mem name funcs in
233
234         if not tested then
235           failwithf "function %s has tests but does not test itself" name
236   ) all_functions