Make closure and soft type specifiers a parse error
[hiphop-php.git] / hphp / hack / test / full_fidelity / full_fidelity_unit_test.ml
blob37f4b7c835c584ae04ac867cbb2763b2c33bd1b4
1 (**
2 * Copyright (c) 2016, Facebook, Inc.
3 * All rights reserved.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
9 *)
11 module EditableTrivia = Full_fidelity_editable_trivia
12 module SourceText = Full_fidelity_source_text
13 module SyntaxTree = Full_fidelity_syntax_tree
14 .WithSyntax(Full_fidelity_positioned_syntax)
15 module PositionedTree = Full_fidelity_syntax_tree
16 .WithSyntax(Full_fidelity_positioned_syntax)
17 module ParserErrors = Full_fidelity_parser_errors
18 .WithSyntax(Full_fidelity_positioned_syntax)
19 module SyntaxError = Full_fidelity_syntax_error
20 module TestUtils = Full_fidelity_test_utils
21 module TriviaKind = Full_fidelity_trivia_kind
23 open Hh_core
24 open Ocaml_overrides
25 open OUnit
27 let test_files_dir = "./hphp/hack/test/full_fidelity/cases"
29 type test_case = {
30 (** Source files is loaded from <name>.php in the <cwd>/<test_files_dir>/ *)
31 name: string;
32 source: string;
33 expected: string;
34 test_function: string -> string;
35 value_mapper: string -> string;
38 let ident str = str
40 let write_file name contents =
41 let path = Filename.concat test_files_dir name in
42 let oc = open_out path in
43 Printf.fprintf oc "%s" contents;
44 close_out oc
46 let write_expectation_to_file name expected =
47 write_file (name ^ ".out") expected
49 let cat_file name =
50 let path = Filename.concat test_files_dir name in
51 let raw = Sys_utils.cat path in
52 (** cat adds an extra newline at the end. *)
53 if (String.length raw > 0) &&
54 (String.get raw (String.length raw - 1)) == '\n' then
55 String.sub raw 0 (String.length raw - 1)
56 else
57 raw
59 (** Create a test_case by reading input from <cwd>/<test_files_dir>/name.php
60 * and name.exp *)
61 let make_test_case_from_files ?(value_mapper=ident) name test_function =
62 let source = cat_file (name ^ ".php") in
63 let expected = cat_file (name ^ ".exp") in
65 name = name;
66 source = source;
67 expected = expected;
68 test_function = test_function;
69 value_mapper;
72 let remove_whitespace text =
73 let length = String.length text in
74 let buffer = Buffer.create length in
75 let rec aux i =
76 if i = length then
77 Buffer.contents buffer
78 else
79 let ch = String.get text i in
80 match ch with
81 | ' ' | '\n' | '\r' | '\t' -> aux (i + 1)
82 | _ -> begin Buffer.add_char buffer ch; aux (i + 1) end in
83 aux 0
86 let test_minimal source =
87 let file_path = Relative_path.(create Dummy "<test_minimal>") in
88 let source_text = SourceText.make file_path source in
89 let syntax_tree = SyntaxTree.make source_text in
90 TestUtils.to_formatted_sexp_string (SyntaxTree.root syntax_tree)
92 let test_trivia source =
93 let file_path = Relative_path.(create Dummy "<test_trivia>") in
94 let source_text = SourceText.make file_path source in
95 let syntax_tree = SyntaxTree.make source_text in
96 let editable = SyntaxTransforms.editable_from_positioned syntax_tree in
97 let (no_trivia_tree, trivia) = TestUtils.rewrite_editable_tree_no_trivia editable in
98 let pretty_no_trivia = Full_fidelity_pretty_printer.pretty_print no_trivia_tree in
99 let formatted_trivia = List.map trivia
100 (fun t ->
101 Printf.sprintf "%s: (%s)"
102 (TriviaKind.to_string @@ EditableTrivia.kind t)
103 (EditableTrivia.text t)
104 ) in
105 Printf.sprintf "%s\n%s" (String.trim pretty_no_trivia) (String.concat "\n" formatted_trivia)
107 let test_mode source =
108 let file_path = Relative_path.(create Dummy "<test_mode>") in
109 let source_text = SourceText.make file_path source in
110 let syntax_tree = SyntaxTree.make source_text in
111 let lang = SyntaxTree.language syntax_tree in
112 let mode = SyntaxTree.mode syntax_tree in
113 let is_strict = SyntaxTree.is_strict syntax_tree in
114 let is_hack = SyntaxTree.is_hack syntax_tree in
115 let is_php = SyntaxTree.is_php syntax_tree in
116 Printf.sprintf "Lang:%sMode:%sStrict:%bHack:%bPhp:%b"
117 lang mode is_strict is_hack is_php
119 let test_errors source =
120 let file_path = Relative_path.(create Dummy "<test_errors>") in
121 let source_text = SourceText.make file_path source in
122 let offset_to_position = SourceText.offset_to_position source_text in
123 let syntax_tree = PositionedTree.make source_text in
124 let errors = ParserErrors.parse_errors syntax_tree in
125 let mapper err = SyntaxError.to_positioned_string err offset_to_position in
126 let errors = List.map errors ~f:mapper in
127 Printf.sprintf "%s" (String.concat "\n" errors)
129 let trivia_tests =
130 [make_test_case_from_files "test_trivia" test_trivia]
132 let minimal_tests =
133 let mapper testname =
134 make_test_case_from_files
135 ~value_mapper:remove_whitespace testname test_minimal in
136 List.map
138 "test_simple";
139 (* TODO: This test is temporarily disabled because
140 $a ? $b : $c = $d
141 does not parse in the FF parser as it did in the original Hack parser,
142 due to a precedence issue. Re-enable this test once we either fix that,
143 or decide to take the breaking change.
144 "test_conditional"; *)
145 "test_statements";
146 "test_for_statements";
147 "test_try_statement";
148 "test_list_precedence";
149 "test_list_expression";
150 "test_foreach_statements";
151 "test_types_type_const";
152 "test_function_call";
153 "test_array_expression";
154 "test_varray_darray_expressions";
155 "test_varray_darray_types";
156 "test_attribute_spec";
157 "test_array_key_value_precedence";
158 "test_enum";
159 "test_class_with_attributes";
160 "test_class_with_qualified_name";
161 "test_namespace";
162 "test_empty_class";
163 "test_class_method_declaration";
164 "test_constructor_destructor";
165 "test_trait";
166 "test_type_const";
167 "test_class_const";
168 "test_type_alias";
169 "test_indirection";
170 "test_eval_deref";
171 "test_global_constant";
172 "test_closure_type";
173 "test_inclusion_directive";
174 "test_awaitable_creation";
175 "test_literals";
176 "test_variadic_type_hint";
177 "test_tuple_type_keyword";
178 "test_trailing_commas";
179 "context/test_extra_error_trivia";
180 "test_funcall_with_type_arguments";
181 "test_nested_namespace_declarations";
182 "test_xhp_attributes";
183 "test_xhp_require";
184 "test_spaces_preserved_in_string_containing_expression";
185 "test_inout_params";
186 "test_degenerate_ternary";
187 ] ~f:mapper
189 let error_tests =
190 let mapper testname =
191 make_test_case_from_files testname test_errors in
192 List.map
194 "is_expression/test_callable_hint";
195 "is_expression/test_soft_hint";
196 "test_default_param_errors";
197 "test_alias_errors";
198 "test_method_modifier_errors";
199 "test_errors_not_strict";
200 "test_errors_strict";
201 "test_no_errors_strict";
202 "test_statement_errors";
203 "test_expression_errors";
204 "test_errors_method";
205 "test_declaration_errors";
206 "test_errors_class";
207 "test_errors_array_type";
208 "test_errors_variadic_param";
209 "test_errors_variadic_param_default";
210 "test_errors_statements";
211 "test_implements_errors";
212 "test_object_creation_errors";
213 "test_classish_inside_function_errors";
214 "test_list_expression_errors";
215 "test_interface_method_errors";
216 "test_abstract_classish_errors";
217 "test_abstract_methodish_errors";
218 "test_async_errors";
219 "test_visibility_modifier_errors";
220 "test_legal_php";
221 "context/test_missing_name_in_expression";
222 "context/test_nested_function_lite";
223 "context/test_nested_function";
224 "context/test_method_decl_extra_token";
225 "context/test_recovery_to_classish1";
226 "context/test_recovery_to_classish2";
227 "context/test_recovery_to_classish3";
228 "context/test_single_extra_token_recovery";
229 "context/test_missing_foreach_value";
230 "test_namespace_error_recovery";
231 "test_correct_code1";
232 "test_misspelling_recovery";
233 "test_misspelling_recovery2";
234 "test_group_use_errors";
235 "test_abstract_initializers";
236 "test_mixed_bracketed_unbracketed_namespaces1";
237 "test_mixed_bracketed_unbracketed_namespaces2";
238 "test_var_phpism";
239 "test_var_phpism2";
240 "test_var_phpism3";
241 "test_xhp_attribute_enum_errors";
242 "test_shapes";
243 "test_abstract_final_errors";
244 "test_content_before_header";
245 "test_valid_php_no_markup_errors";
246 "test_question_mark_end_tag_errors";
247 "test_php_blocks_errors";
248 "test_inout_params_errors";
249 "test_variadic_ref_decorators";
250 "test_lambda_variadic_errors";
251 "test_lambda_no_typehints_errors";
252 ] ~f:mapper
254 let test_data = minimal_tests @ trivia_tests @ error_tests @
257 name = "test_mode_1";
258 source = "<?hh ";
259 expected = "Lang:hhMode:Strict:falseHack:truePhp:false";
260 test_function = test_mode;
261 value_mapper = ident;
264 name = "test_mode_2";
265 source = "";
266 expected = "Lang:phpMode:Strict:falseHack:falsePhp:true";
267 test_function = test_mode;
268 value_mapper = ident;
271 name = "test_mode_3";
272 source = "<?hh // strict ";
273 expected = "Lang:hhMode:strictStrict:trueHack:truePhp:false";
274 test_function = test_mode;
275 value_mapper = ident;
278 name = "test_mode_4";
279 source = "<?php // strict "; (* Not strict! *)
280 expected = "Lang:phpMode:strictStrict:falseHack:falsePhp:true";
281 test_function = test_mode;
282 value_mapper = ident;
285 name = "test_mode_5";
286 source = "<?hh/";
287 expected = "Lang:hhMode:Strict:falseHack:truePhp:false";
288 test_function = test_mode;
289 value_mapper = ident;
292 name = "test_mode_6";
293 source = "<?hh//";
294 expected = "Lang:hhMode:Strict:falseHack:truePhp:false";
295 test_function = test_mode;
296 value_mapper = ident;
300 let driver test () =
301 let actual = test.test_function test.source in
303 let expected = test.value_mapper test.expected in
304 let actual = test.value_mapper actual in
305 assert_equal expected actual
306 with
307 e ->
308 write_expectation_to_file test.name actual;
309 raise e
311 let run_test test =
312 test.name >:: (driver test)
314 let run_tests tests =
315 Printf.printf "%s" (Sys.getcwd());
316 List.map tests ~f:run_test
318 let test_suite =
319 "Full_fidelity_suite" >::: (run_tests test_data)
321 let main () =
322 run_test_tt_main test_suite
324 let _ = main ()