2 * Copyright (c) 2015, Facebook, Inc.
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.
15 module List
= Core_list
17 (* ========================= TYPES =========================== *)
19 (* for processing php identifiers such that we can use them
20 to denote regex special characters *)
22 (* no special pattern commands, just a normal identifier to match *)
24 (* not and or are modifiers to an identifier, so the
25 identifier is still relevant *)
29 (* Identifier is not relevant for these *)
39 | NodeList
of ast_node list
42 type skipany_ast_node
=
43 | SkipanyBlock
of block
46 (* Put patches in a set because it is common for many of the same
47 patch to be put in the environment when patterns include SkipAny
55 if p1
.Hh_match_utils.start_loc
<> p2
.Hh_match_utils.start_loc
56 then p1
.Hh_match_utils.start_loc
- p2
.Hh_match_utils.start_loc
57 else p1
.Hh_match_utils.end_loc
- p2
.Hh_match_utils.end_loc
)
60 module MetavarMap
= Map.Make
(String
)
62 (* Encapsulated global state of the matcher which can
63 keep track of metavariables used in matching *)
65 (* references to the text file, source*)
66 file
: Relative_path.t
;
68 (* things to accumulate as we match *)
70 comments
: (Pos.t
* string) list
;
71 metavars
: metavar_tgt
MetavarMap.t
;
72 (* optional pieces related to patching *)
73 transformations
: patch_maps
;
77 (* We don't use a set for result because only time we get duplicates
78 is when merging results from different branches, and there will be
79 distinct matches starting at the same position with nontrivial operations
80 to check for equality. *)
82 | Matches
of (ast_node
* File_pos.t
) list
85 let dummy_success_res = Matches
[(DummyNode
, File_pos.dummy
)]
87 (* for SkipAny matching *)
89 Ast.Expr
(Pos.none
, (String
(Pos.none
, "__KSTAR")))
91 let is_skip_any_stmt = function
92 | Ast.Expr
(_
, (String
(_
, "__DUMMYSKIPANY"))) -> true
95 (* ======================== HELPERS ======================== *)
97 (* should merge the two environments (mainly for correct remembering of
98 metavariable bindings with .* type matches and patches that need to
99 be applied). May result in binding multiple values to the same
103 (env2
: matcher_env
) : matcher_env
=
104 { env1
with patches
= PatchSet.union env1
.patches env2
.patches
}
106 (* small helper to return the correct env depending on whether
107 the ret passed in is a fail or not (when matches fail we should revert
108 the env to what it was before the failed match to make sure it is correct) *)
109 let revert_env_if_no_match
110 (ret
: match_result
* matcher_env
)
111 (old_env
: matcher_env
) : (match_result
* matcher_env
) =
113 | NoMatch
-> (NoMatch
, old_env
)
116 let _contains_match = function
120 (* For when we concatenate matches from SkipAny or KStar where there may be
122 TODO: implement some kind of duplicate-removal system that works;
123 position-based deduplication does not work *)
124 let concat_match_results_nodup
125 (results
: match_result list
) : match_result
=
127 ~f
:(fun res_acc new_res
->
128 match res_acc
, new_res
with
129 | Matches matches_so_far
, Matches new_matches
->
130 Matches
(List.rev_append new_matches matches_so_far
)
131 | Matches _
, NoMatch
-> res_acc
132 | NoMatch
, Matches _
-> new_res
133 | NoMatch
, NoMatch
-> NoMatch
)
137 let concat_match_results (results
: match_result list
) : match_result
=
139 ~f
:(fun res_acc new_res
->
140 match res_acc
, new_res
with
141 | Matches matches_so_far
, Matches new_matches
->
142 Matches
(matches_so_far
@ new_matches
)
143 | Matches _
, NoMatch
-> res_acc
144 | NoMatch
, Matches _
-> new_res
145 | NoMatch
, NoMatch
-> NoMatch
)
149 let find_substring to_search substring
=
150 let re = Str.regexp_string substring
in
151 try Str.search_forward
re to_search
0
154 let remove_keyword str keyword keyword_start
=
155 let keylen = String.length keyword
in
156 let len = String.length str
in
157 (String.sub str
0 keyword_start
) ^
158 (String.sub str
(keyword_start
+ keylen) (len - keyword_start
- keylen))
160 (* Returns a string corresponding to the metavariable name
161 if the identifier is a metavariable *)
162 let check_mvar (id
: string) : string option =
163 let found_meta_key = find_substring id
"META" in
164 if found_meta_key = -1
167 (* get everything after the "META" *)
169 id
(found_meta_key + 4)
170 (String.length id
- (found_meta_key + 4)))
172 (* Instantiates an identifier metavariable (or returns the identifier if it
174 let instantiate_mvar_id (env
: matcher_env
) (id
: string) : string =
175 let mvar_id = check_mvar id
in
180 match MetavarMap.find mvar_name env
.metavars
with
183 with Not_found
-> failwith
("metavar " ^ mvar_name ^
" not found")
188 (binding
: string) : matcher_env
=
189 match check_mvar id
with
194 "Regular expressions and metavariables can't be used in the same pattern"
196 (* Add binding if one does not exist,
197 WILL NOT OVERWRITE EXISTING BINDING *)
200 if MetavarMap.mem mvar env
.metavars
then env
.metavars
201 else MetavarMap.add mvar
(Literal binding
) env
.metavars
}
203 (* Expr is a metavariable if it matches "__.*META.*" (can contain other
204 keywords, the part after the meta is the metavariable name) *)
205 let is_meta_expr = function
206 | (_
, (String
(_
, str
))) -> check_mvar str
209 (* NOTE: it is fine that meta exprs and stmts are the same because we check
211 let is_meta_stmt = function
212 | Ast.Expr e
-> is_meta_expr e
218 (is_meta_fn
: a
-> string option)
220 (binding
: ast_node
) : matcher_env
=
221 match is_meta_fn node
with
224 if MetavarMap.mem mvar_name env
.metavars
then env
227 MetavarMap.add mvar_name
(NodeList
[binding
]) env
.metavars
}
229 (* Override the ast_constructor's relevant methods to instantiate metavariables
230 whenever one is found.
231 NOTE: will instantiate ALL metavariables that are a children of the node that
233 class metavar_instantiating_ctr
(env
: matcher_env
) = object
234 inherit [unit] AstConstructor.ast_constructor
as super
236 val mvar_map
= env
.metavars
;
239 method! on_stmt env s
= begin
240 match is_meta_stmt s
with
241 | Some mvar_name
-> begin
243 let mval = MetavarMap.find mvar_name mvar_map
in
246 (* Verify that the list is homogenous, If it is,
247 replace the statement with it *)
250 | Hh_match_utils.Stmt _
-> false
252 then super#on_stmt env s
256 | Hh_match_utils.Stmt mvar_val
-> mvar_val
258 failwith
("should never happen unless you" ^
259 " metavariables are broken")) vals
260 | _
-> super#on_stmt env s
261 with Not_found
-> super#on_stmt env s
end
262 | _
-> super#on_stmt env s
265 method! on_expr env e
= begin
266 match is_meta_expr e
with
267 | Some mvar_name
-> begin
269 let mval = MetavarMap.find mvar_name mvar_map
in
274 | Hh_match_utils.Expr _
-> false
276 then super#on_expr env e
280 | Hh_match_utils.Expr mvar_val
-> mvar_val
281 | _
-> failwith
("should never happen unless you" ^
282 " metavariables are broken")) vals
283 | _
-> super#on_expr env e
284 with Not_found
-> super#on_expr env e
end
285 | _
-> super#on_expr env e
288 method! on_id env id
= begin
289 let id = super#on_id env
id in
290 fst
id, instantiate_mvar_id match_env
(snd
id)
293 method! on_pstring env pstr
= begin
294 let pstr = super#on_pstring env
pstr in
295 fst
pstr, instantiate_mvar_id match_env
(snd
pstr)
299 (* turns a target node into text that can be put into a patch, instantiating
300 any metavariables contained and unparsing the stmt *)
301 let stmt_to_text env stmts
=
302 (* instantiate relevant metavariables *)
305 ~f
:((new metavar_instantiating_ctr env
)#on_stmt
()) |>
306 List.concat
|> Unparser.Unparse.u_naked_block
|>
309 (* see comment on stmt_to_text *)
310 let expr_to_text env exprs
=
311 (* instantiate relevant metavariables *)
314 ~f
:((new metavar_instantiating_ctr env
)#on_expr
()) |>
316 (fun expr_list
-> Expr_list expr_list
) |>
317 Unparser.Unparse.u_expr_
|>
320 let instantiate_mvar_stmt env stm
=
321 match is_meta_stmt stm
with
324 AstConstructor.list_to_single
325 (new metavar_instantiating_ctr env
)#on_stmt
() stm
327 let instantiate_mvar_expr env exp
=
328 match is_meta_expr exp
with
331 AstConstructor.list_to_single
332 (new metavar_instantiating_ctr env
)#on_expr
() exp
334 (* Will get all non-metavariable keywords *)
335 let process_identifier (id : string) : id_type
=
336 let not_keyword = "__NOT" in
337 let not_action = function key
-> Not
(remove_keyword id not_keyword key
) in
338 let or_keyword = "__OR" in
339 let or_action = function key
-> Or
(remove_keyword id or_keyword key
) in
340 let regexp_keyword = "__REGEXP" in
341 let regexp_action = function _
-> RegExp
in
342 let wildcard_keyword = "__ANY" in
343 let wildcard_action = function _
-> Wildcard
in
344 let wildcard_name_keyword = "__SOMENAME" in
345 let wildcard_name_action = function _
-> WildcardName
in
346 let do_not_care_keyword = "__SOMENODE" in
347 let do_not_care_action = function _
-> DoNotCare
in
348 let skip_any_keyword = "__SKIPANY" in
349 let skip_any_action = function _
-> SkipAnyTok
in
350 let star_keyword = "__KSTAR" in
351 let star_action = function _
-> KStar
in
353 [not_keyword, not_action; or_keyword, or_action;
354 regexp_keyword, regexp_action;
355 wildcard_keyword, wildcard_action;
356 wildcard_name_keyword, wildcard_name_action;
357 do_not_care_keyword, do_not_care_action;
358 skip_any_keyword, skip_any_action;
359 star_keyword, star_action] in
360 let rec check_keywords = function
361 (* default to normal identifier *)
363 | (keyword
, action
) :: tail
->
364 let substr_pos = find_substring id keyword
in
366 then (action
substr_pos)
367 else check_keywords tail
in
368 check_keywords actions
370 (* for readability these are left *)
371 let get_id_type (id : (Pos.t
* string)) : id_type
=
372 process_identifier (snd
id)
377 (env
: matcher_env
) : (match_result
* matcher_env
) =
378 let env'
= add_mvar_id env p_s t_s
in
379 let p_s = instantiate_mvar_id env'
p_s in
380 let match_strings str1 str2
=
381 let remove_leading_slash str
=
382 let len = String.length str
in
383 if len > 1 && str
.[0] = '
\\'
384 then String.sub str
1 (len - 1)
386 (remove_leading_slash str1
) = (remove_leading_slash str2
) in
388 match process_identifier p_s with
389 | Wildcard
| WildcardName
| KStar
-> true
390 | Or p_str
| Normal p_str
-> match_strings t_s p_str
391 | Not p_str
-> not
(match_strings t_s p_str
)
392 (* this should be handled by match_regexp *)
393 | RegExp
-> failwith
"Regular expressions should be handled in match_regexp"
394 (* haven't implemented these things *)
397 then dummy_success_res, env'
401 (t_id
: Pos.t
* string)
402 (p_id
: Pos.t
* string)
403 (env : matcher_env
) : (match_result
* matcher_env
) =
404 let env'
= {env with uses_regexp
= true} in
405 if not
(MetavarMap.is_empty
env.metavars
)
407 "Regular expressions and metavariables can't be used in the same pattern"
409 let p_line = Pos.line
(fst p_id
) in
410 let is_same_line_as_pattern comment
= (Pos.line
(fst comment
) = p_line) in
411 let comment = List.find
env.comments
is_same_line_as_pattern in
412 let remove_leading_slash str
=
413 let len = String.length str
in
414 if len > 1 && str
.[0] = '
\\'
415 then String.sub str
1 (len - 1)
417 let text = remove_leading_slash (snd t_id
) in
419 | None
-> NoMatch
, env'
420 | Some
(_
, comment) ->
421 if Str.string_match
(Str.regexp
comment) text 0
422 then dummy_success_res, env'
426 (* for use in matching lists and readibility in matching functions
427 has a similar signature to other match methods *)
429 (t_id
: Pos.t
* string)
430 (p_id
: Pos.t
* string)
431 (env : matcher_env
) : (match_result
* matcher_env
) =
433 match process_identifier (snd p_id
) with
434 | RegExp
-> match_regexp t_id p_id
env
435 | _
-> match_string (snd t_id
) (snd p_id
) env
438 | Matches _
-> (Matches
[(DummyNode
, Pos.pos_start
(fst t_id
))], env'
)
439 | NoMatch
-> NoMatch
, env
441 (* acc is the match results from the child node *)
445 (node_pos
: File_pos.t
) : match_result
=
447 | Matches so_far
-> Matches
((node
, node_pos
) :: so_far
)
448 | NoMatch
-> Matches
[node
, node_pos
]
450 (* for finding the __SKIPANY string in the file *)
451 let is_skip_any_unproc (elem
: stmt
) =
453 | Ast.Expr
(_
,(String
pstr)) -> get_id_type pstr = SkipAnyTok
456 (* list of stmts that correspond to blocks *)
457 type block_accum
= block list
459 (* return of on_block will be an accumulator of all the blocks in reverse
460 order of how they were found (ending with the block given to it).
461 i.e. order is last :: 2nd last :: ... :: block given *)
462 class block_finding_visitor
() =
464 inherit [block_accum
] Ast_visitor.ast_visitor
as super
466 method! on_block acc block
=
468 let acc = block
:: acc in
469 super#on_block
acc block
;
473 (* used to get (in reverse order) all expressions in a file *)
474 class expr_finding_visitor
() =
476 inherit [expr list
] Ast_visitor.ast_visitor
as super
478 method! on_expr
acc exp
=
480 let acc = exp
:: acc in
481 super#on_expr
acc exp
;
485 (* these functions must be in this module because I am passing them arguments
486 of multiple different types (match list is used on def list, stmt list etc)*)
489 (* TODO: implement disjunction matching correctly - compare with all
490 possible elements of the disjunction, if any of them match,
491 count as match. Simple, but requires preprocessing of the
492 input string for disjunction handling (or just a special case
493 that continues to consume until you are out of disjunction cases *)
494 (* Try to match a list of ast nodes. If paired with the correct handle_sa_fn
495 and handle_sa_hd_fn will also match SkipAny statements. *)
496 let match_list_with_skips (type a
)
497 (is_star_fn
: a
-> bool)
498 (* for stmts, exprs for being able to make mvars out of
500 (is_meta_fn
: (a
-> string option) option)
501 (to_node_fn
: (a
-> ast_node
) option)
502 (match_elem_fn
: a
-> a
-> matcher_env
-> match_result
* matcher_env
)
503 (* handles checking for and preprocessing SkipAny tokens as well as
504 matching processed __SKIPANYDUMMY tokens when they are removed from
507 (a list
-> a list
-> a list
-> matcher_env
->
508 match_result
* matcher_env
) ->
509 a list
-> a list
-> a list
-> matcher_env
->
510 match_result
* matcher_env
)
511 (* handles the case when our pattern starts with a SkipAny, like the
512 case where our pattern starts with a KStar. None means the pattern
513 is not a SkipAny, Some NoMatch means the pattern was a SkipAny that
516 (a list
-> a list
-> a list
-> matcher_env
->
517 match_result
* matcher_env
) ->
518 a
-> a list
-> a list
-> a list
-> matcher_env
->
519 (match_result
* matcher_env
) option)
522 (env0
: matcher_env
) : match_result
* matcher_env
=
523 (* for case where we have some of both text and pattern left
524 e.g. text = [stmt, ...] pattern = [stmt, ...]*)
525 let rec try_match_heads
530 (skipped_text
: a list
)
531 (env : matcher_env
) :
532 match_result
* matcher_env
=
533 (* If the pattern starts with a SkipAny, we want to handle that. *)
536 try_match_lists p_hd t_tl p_tl
(t_hd
:: skipped_text
) env in
537 match skipany_ret with
538 (* if we handled a SkipAny, we want to pass that result back, it has
539 already matched all the rest of the pattern (including the part
542 (* Otherwise that call did nothing and we need to continue matching *)
544 (* check for pattern, text match *)
545 let (elt_match
, env'
) = match_elem_fn t_hd p_hd
env in
546 if elt_match
= NoMatch
547 (* e.g. text = [stmt1, ...] pattern = [stmt2, ...] *)
551 handle_sa_fn try_match_lists t_tl p_tl skipped_text
env'
in
553 (* first elements don't match:
554 e.g. text = [stmt1, ...] pattern = [stmt2, ...] *)
555 | NoMatch
-> NoMatch
, env
556 (* first elements match:
557 e.g. text = [stmt1, ...] pattern = [stmt1, ...] *)
559 concat_match_results_nodup [elt_match
; res], env'
561 (* ========== functions for if a kleene star is encountered ============*)
562 (* consume all leading KStars from the pattern *)
563 and consume_stars
(p_list
: a list
) : a list
* string option =
565 | hd
:: tl
when is_star_fn hd
->
566 let res = consume_stars tl
in
567 let meta_keyword = match snd
res with
568 | Some key
-> Some key
570 match is_meta_fn
with
571 | Some is_meta_fn
-> is_meta_fn hd
573 fst
res, meta_keyword
576 (* try the rest of the pattern based off all possible amounts of text the
577 kleene star could consume by recursing with it removing 0, 1, 2 ...
578 elements from the text *)
579 and handle_star_helper
582 (skipped_text
: a list
)
584 (meta_key
: string option) :
585 match_result
* matcher_env
=
586 let add_mvar (env : matcher_env
) : matcher_env
=
587 match meta_key
, to_node_fn
with
588 | Some key
, Some to_node_fn
->
589 if MetavarMap.mem key
env.metavars
then env
591 let mapped_nodes = List.map ~f
:to_node_fn skipped_text
in
595 key
(NodeList
(List.rev
mapped_nodes)) env.metavars
}
599 revert_env_if_no_match
600 (try_match_lists
[] p_list skipped_text
(add_mvar env)) env
602 let rec_res, rec_env
=
604 t_tl p_list
(t_hd
:: skipped_text
) env meta_key
in
605 let norec_res, norec_env
=
606 try_match_lists t_list p_list skipped_text
(add_mvar env) in
607 match rec_res, norec_res with
608 | NoMatch
, NoMatch
-> NoMatch
, env
609 | Matches _
, NoMatch
-> rec_res, rec_env
610 | NoMatch
, Matches _
-> norec_res, norec_env
611 | Matches _
, Matches _
->
612 (concat_match_results_nodup [rec_res; norec_res],
613 merge_envs rec_env norec_env
)
615 (* consumes the leading KStars from the pattern then tries to do matching,
616 returning the results of all successful matches with different amounts
617 of text consumed by the KStar *)
618 and handle_star_match
621 (_skipped_text
: a list
)
622 (env : matcher_env
) :
623 match_result
* matcher_env
=
624 let p_list, meta_key
= consume_stars
p_list in
625 handle_star_helper t_list
p_list [] env meta_key
627 (*========================= end kstar fns ==========================*)
629 (* handles trying to match the lists that are our pattern and text
630 Used for base cases to terminate the recursion when we can determine
631 from list structure if a match cannot be made. Otherwise calls the
632 correct function to match the lists *)
636 (skipped_text
: a list
)
637 (env : matcher_env
) :
638 match_result
* matcher_env
=
639 match t_list
, p_list with
640 (* Pattern starst with a KStar
641 e.g. text = [] pattern = [".*", ...] *)
642 | _
, p_hd
:: _
when is_star_fn p_hd
->
643 handle_star_match t_list
p_list [] env
644 (* we're out of text, pattern must be empty *)
645 | [], [] -> dummy_success_res, env
646 (* e.g. text = [] pattern = [stmt1, ...] *)
647 | [], _
-> NoMatch
, env
648 (* more text, pattern must match *)
649 (* e.g. text = [stmt1, ...] pattern = [] *)
650 | _t_hd
:: _t_tl
, [] -> NoMatch
, env
651 (* normal case of having both text, pattern left *)
652 | t_hd
:: t_tl
, p_hd
:: p_tl
->
653 revert_env_if_no_match
654 (try_match_heads t_hd t_tl p_hd p_tl skipped_text
env) env in
655 revert_env_if_no_match
656 (handle_sa_fn try_match_lists t_list
p_list [] env0
) env0
658 (* these two functions are used for normal list matching that does not have
659 to handle SkipAny tokens in the pattern *)
660 let dummy_act_if_skipany
661 (_try_match_star_fn
:
662 'a list
-> 'a list
-> 'a list
-> matcher_env
->
663 match_result
* matcher_env
)
667 (_skipped_text
: 'a list
)
668 (_env
: matcher_env
) : (match_result
* matcher_env
) option =
673 'a list
-> 'a list
-> 'a list
-> matcher_env
->
674 match_result
* matcher_env
)
677 (skipped_text
: 'a list
)
678 (env : matcher_env
) :
679 match_result
* matcher_env
=
680 try_match_star_fn t_list
p_list skipped_text
env
682 (* normal list matching (not handling SkipAny cases *)
683 let match_list (type a
)
684 (is_star_fn
: a
-> bool)
685 (* for stmts, exprs for being able to make mvars out of
688 ?
(to_node_fn
: (a
-> ast_node
) option = None
)
689 (match_elem_fn
: a
-> a
-> matcher_env
-> match_result
* matcher_env
)
692 (env0
: matcher_env
) : match_result
* matcher_env
=
693 match_list_with_skips
705 (match_fn
: 'a
-> 'a
-> matcher_env
-> (match_result
* matcher_env
))
708 (env : matcher_env
) :
709 (match_result
* matcher_env
) =
710 match p_opt
, t_opt
with
711 | Some p_opt
, Some t_opt
->
712 match_fn t_opt p_opt
env
714 | Some _
, None
-> (dummy_success_res, env)
716 dummy_success_res, env
718 (* because lists of pairs occur fairly often *)
720 (match_fn1
: 'a
-> 'a
-> matcher_env
-> (match_result
* matcher_env
))
721 (match_fn2
: 'b
-> 'b
-> matcher_env
-> (match_result
* matcher_env
)):
722 ('a
* 'b
-> 'a
* 'b
-> matcher_env
-> match_result
* matcher_env
) =
723 (fun (t_vals
: 'a
* 'b
) (p_vals
: 'a
* 'b
) (env : matcher_env
) ->
725 let t_v1, t_v2
= t_vals
in
726 let p_v1, p_v2
= p_vals
in
727 let v1_res, env = match_fn1
t_v1 p_v1 env in
729 | NoMatch
-> NoMatch
, old_env
731 let v2_res, env = match_fn2 t_v2 p_v2
env in
733 | NoMatch
-> NoMatch
, old_env
735 (concat_match_results [v1_res; v2_res]), env)
737 (* matches all the different attributes of a node given a list, short
738 circuits and returns NoMatch and the original environment if any of
740 let rec match_attributes
741 (actions : (matcher_env
-> match_result
* matcher_env
) list
)
742 (env0
: matcher_env
) : match_result
* matcher_env
=
744 | [] -> dummy_success_res, env0
746 let res, env = action env0
in
748 | NoMatch
-> NoMatch
, env0
750 let tl_res, env = match_attributes tl
env in
752 | NoMatch
-> NoMatch
, env0
754 (concat_match_results [res; tl_res]), env
756 (* checks if a patch should be created based on the env (transformation
757 list), the pattern node and creates it if necessary *)
762 (adjust_fn
: string -> int -> int -> int * int)
763 (delete_list
: a list
)
764 (* assoc list from pattern -> target *)
765 (transf_list
: (a
* a list
) list
)
766 (node_to_txt_fn
: matcher_env
-> a list
-> string)
767 (extent_find_fn
: a
-> File_pos.t
* File_pos.t
)
768 (ret
: match_result
* matcher_env
) : (match_result
* matcher_env
) =
769 let res, env = ret
in
770 let create_patch result_str
=
771 let elem_ext = extent_find_fn text_elem
in
773 { start_loc
= File_pos.offset
(fst
elem_ext);
774 end_loc
= File_pos.offset
(snd
elem_ext);
776 range_adjustment_fn
= adjust_fn
} in
777 res, { env with patches
= PatchSet.add
newpatch env.patches
} in
778 (* check if the element is in the list of nodes to be deleted*)
779 if List.exists ~f
:(fun to_del
-> pat_elem
== to_del
) delete_list
782 (* check if we have a relevant transformation*)
785 let tgt_node = List.assq pat_elem transf_list
in
786 create_patch (node_to_txt_fn
env tgt_node)
787 with Not_found
-> ret
790 (* =================== MATCHING FUNCTIONS ==================== *)
791 (* All match functions take 2 Ast nodes of the same type and try to match them.
792 The matching is sensitive to wildcards and will recursively try to match
793 child nodes on the ast.
795 First it checks for wildcards, metavariables and other special cases that
796 allow us to do regex-type matching.
797 Then it matches by type: e.g. if we have two def nodes they should both be
798 type Fun_ or both type Class_ etc.
799 Then it checks if all attributes of each node are the same, calling the
800 proper match methods if necessary. If any of these matches for its children
801 fail, then the match returns failure.
802 e.g. when matching a fun_ we check the name then match the parameters, if
803 those succeed then we try matching the body etc.
805 The result returned is the result which is a list of matches containing the
806 node that was matched and the position of the beginning of the element
807 matched. If part of the text was matched with a kleene star, no matches
808 will be added to the list. (A dummy success value may be returned if
809 necessary). The modified matching environemnt (or the original environment
810 if it wasn't modified or the match failed) *)
811 let rec match_ast_nodes
812 (text_node
: ast_node
)
813 (pattern_node
: ast_node
)
814 (env : matcher_env
) :
815 (match_result
* matcher_env
) =
816 match text_node
, pattern_node
with
817 | Program t_prog
, Program p_prog
->
818 match_program t_prog p_prog
env
819 | Def t_def
, Def p_def
->
820 match_def t_def p_def
env
821 | Fun_ t_fun_
, Fun_ p_fun_
->
822 match_fun_ t_fun_ p_fun_
env
823 | Tparam t_tparam
, Tparam p_tparam
->
824 match_tparam t_tparam p_tparam
env
825 | Fun_param t_fun_param
, Fun_param p_fun_param
->
826 match_fun_param t_fun_param p_fun_param
env
827 | Class_ t_class_
, Class_ p_class_
->
828 match_class_ t_class_ p_class_
env
829 | Class_elt t_class_elt
, Class_elt p_class_elt
->
830 match_class_elt t_class_elt p_class_elt
env
831 | Hh_match_utils.Method t_method_
, Hh_match_utils.Method p_method_
->
832 match_method_ t_method_ p_method_
env
833 | Hh_match_utils.Stmt t_stmt
, Hh_match_utils.Stmt p_stmt
->
834 match_stmt t_stmt p_stmt
env
835 | Hh_match_utils.Expr t_expr
, Hh_match_utils.Expr p_expr
->
836 match_expr t_expr p_expr
env
837 | DummyNode
, _
| _
, DummyNode
| _
, Nodes _
| Nodes _
, _
->
838 failwith
"cannot match a non-AST construct"
839 | _
, _
-> (NoMatch
, env)
842 (t_program
: program
)
843 (p_program
: program
)
844 (env : matcher_env
) : (match_result
* matcher_env
) =
845 (* TODO add ability for def-lv skip_any *)
846 LM.match_list is_star_def match_def t_program p_program
env
848 and match_tconstraint
849 (t_tconstraint
: tconstraint
)
850 (p_tconstraint
: tconstraint
)
851 (env : matcher_env
) : (match_result
* matcher_env
) =
852 LM.match_option match_hint t_tconstraint p_tconstraint
env
854 and match_typedef_kind
855 (t_typedef_kind
: typedef_kind
)
856 (p_typedef_kind
: typedef_kind
)
857 (env : matcher_env
) : (match_result
* matcher_env
) =
858 match t_typedef_kind
, p_typedef_kind
with
859 | Alias t_h
, Alias p_h
860 | NewType t_h
, NewType p_h
-> match_hint t_h p_h
env
861 | _
, _
-> NoMatch
, env
863 and match_expr_list el1 el2
env =
867 ~is_meta_fn
:(Some
is_meta_expr)
868 ~to_node_fn
:(Some
(fun exp
-> Hh_match_utils.Expr exp
))
871 and match_user_attribute
872 (t_user_attribute
: user_attribute
)
873 (p_user_attribute
: user_attribute
)
874 (env : matcher_env
) : (match_result
* matcher_env
) =
876 [match_id_res t_user_attribute
.ua_name p_user_attribute
.ua_name
;
878 t_user_attribute
.ua_params
879 p_user_attribute
.ua_params
]
882 (* TODO maybe allow wildcarding namespaces *)
883 (* ignores ns_uses because that doesn't seem relevant for the matcher*)
885 (t_namespace
: Namespace_env.env)
886 (p_namespace
: Namespace_env.env)
887 (env : matcher_env
) : (match_result
* matcher_env
) =
888 match t_namespace
.Namespace_env.ns_name
,
889 p_namespace
.Namespace_env.ns_name
with
890 | Some t_name
, Some p_name
when t_name
= p_name
-> dummy_success_res, env
891 | None
, None
-> dummy_success_res, env
892 | _
, _
-> NoMatch
, env
895 (t_typedef
: typedef
)
896 (p_typedef
: typedef
)
897 (env : matcher_env
) : (match_result
* matcher_env
) =
899 [match_id_res t_typedef
.t_id p_typedef
.t_id
;
905 match_tconstraint t_typedef
.t_constraint p_typedef
.t_constraint
;
906 match_typedef_kind t_typedef
.t_kind p_typedef
.t_kind
;
908 is_star_user_attribute
910 t_typedef
.t_user_attributes
911 p_typedef
.t_user_attributes
;
912 match_namespace t_typedef
.t_namespace p_typedef
.t_namespace
]
916 (t_cst_kind
: cst_kind
)
917 (p_cst_kind
: cst_kind
)
918 (env : matcher_env
) : (match_result
* matcher_env
) =
919 if t_cst_kind
= p_cst_kind
920 then dummy_success_res, env
926 (env : matcher_env
) : (match_result
* matcher_env
) =
928 [match_cst_kind t_gconst
.cst_kind p_gconst
.cst_kind
;
929 match_id_res t_gconst
.cst_name p_gconst
.cst_name
;
930 LM.match_option match_hint t_gconst
.cst_type p_gconst
.cst_type
;
931 match_expr t_gconst
.cst_value p_gconst
.cst_value
;
932 match_namespace t_gconst
.cst_namespace p_gconst
.cst_namespace
]
938 (env : matcher_env
) : (match_result
* matcher_env
) =
939 match t_def
, p_def
with
940 | Fun t_fun_
, Fun p_fun_
->
941 match_fun_ t_fun_ p_fun_
env
942 | Class t_class_
, Class p_class_
->
943 match_class_ t_class_ p_class_
env
944 | Ast.Stmt t_stmt
, Ast.Stmt p_stmt
->
945 match_stmt t_stmt p_stmt
env
946 | Typedef t_typedef
, Typedef p_typedef
->
947 match_typedef t_typedef p_typedef
env
948 | Constant t_gconst
, Constant p_gconst
->
949 match_gconst t_gconst p_gconst
env
950 | Namespace
(t_id
, t_program
), Namespace
(p_id
, p_program
) ->
952 [match_id_res t_id p_id
;
953 match_program t_program p_program
]
955 (* I spent the last 10 minutes trying to figure out WTF this matcher is doing
956 and perhaps how to even just strip the namespace kind, but it doesn't
957 typecheck and I don't care. I don't think anyone is even using this
958 code. If you care, you can fix it. *)
960 | NamespaceUse t_iil, NamespaceUse p_iil ->
961 revert_env_if_no_match
964 (LM.match_pair_fn match_id_res match_id_res)
970 | _
, _
-> NoMatch
, env
975 (env : matcher_env
) : (match_result
* matcher_env
) =
977 then dummy_success_res, env
980 (* ignores f_mode, f_mtime *)
984 (env : matcher_env
) : (match_result
* matcher_env
) =
986 [match_id_res t_fun_
.f_name p_fun_
.f_name
;
992 LM.match_option match_hint t_fun_
.f_ret p_fun_
.f_ret
;
993 match_bool t_fun_
.f_ret_by_ref p_fun_
.f_ret_by_ref
;
1000 is_star_user_attribute
1001 match_user_attribute
1002 t_fun_
.f_user_attributes
1003 p_fun_
.f_user_attributes
;
1004 match_fun_kind t_fun_
.f_fun_kind p_fun_
.f_fun_kind
;
1005 match_namespace t_fun_
.f_namespace p_fun_
.f_namespace
;
1006 match_stmt
(Block t_fun_
.f_body
) (Block p_fun_
.f_body
)] in
1007 match get_id_type t_fun_
.f_name
with
1008 | Wildcard
-> dummy_success_res, env
1009 | DoNotCare
-> match_stmt
(Block t_fun_
.f_body
) (Block p_fun_
.f_body
) env
1010 | _
-> LM.match_attributes attributes env
1013 (t_fun_kind
: fun_kind
)
1014 (p_fun_kind
: fun_kind
)
1015 (env : matcher_env
) : (match_result
* matcher_env
) =
1016 if t_fun_kind
= p_fun_kind
1017 then dummy_success_res, env
1021 (t_variance
: variance
)
1022 (p_variance
: variance
)
1023 (env : matcher_env
) : (match_result
* matcher_env
) =
1024 if t_variance
= p_variance
1025 then dummy_success_res, env
1028 and match_constraint_kind
1029 (t_ck
: constraint_kind
)
1030 (p_ck
: constraint_kind
)
1031 (env : matcher_env
) : (match_result
* matcher_env
) =
1033 then dummy_success_res, env
1039 (env : matcher_env
) : (match_result
* matcher_env
) =
1040 let (t_variance
, t_id
, t_chopt
) = t_tparam
in
1041 let (p_variance
, p_id
, p_chopt
) = p_tparam
in
1043 [match_variance t_variance p_variance
;
1044 match_id_res t_id p_id
;
1047 (LM.match_pair_fn match_constraint_kind match_hint
)
1055 (env : matcher_env
) : (match_result
* matcher_env
) =
1057 then dummy_success_res, env
1061 (t_fun_param
: fun_param
)
1062 (p_fun_param
: fun_param
)
1063 (env : matcher_env
) : (match_result
* matcher_env
) =
1065 [match_id_res t_fun_param
.param_id p_fun_param
.param_id
;
1068 t_fun_param
.param_hint
1069 p_fun_param
.param_hint
;
1070 match_bool t_fun_param
.param_is_reference p_fun_param
.param_is_reference
;
1071 match_bool t_fun_param
.param_is_variadic p_fun_param
.param_is_variadic
;
1074 t_fun_param
.param_expr
1075 p_fun_param
.param_expr
;
1078 t_fun_param
.param_modifier
1079 p_fun_param
.param_modifier
;
1081 is_star_user_attribute
1082 match_user_attribute
1083 t_fun_param
.param_user_attributes
1084 p_fun_param
.param_user_attributes
] in
1085 match get_id_type p_fun_param
.param_id
with
1086 | DoNotCare
| Wildcard
-> dummy_success_res, env
1087 | _
-> LM.match_attributes attributes env
1089 and match_class_kind
1090 (t_class_kind
: class_kind
)
1091 (p_class_kind
: class_kind
)
1092 (env : matcher_env
) : (match_result
* matcher_env
) =
1093 if t_class_kind
= p_class_kind
1094 then dummy_success_res, env
1100 (env : matcher_env
) : (match_result
* matcher_env
) =
1102 [match_hint t_enum_
.e_base p_enum_
.e_base
;
1103 LM.match_option match_hint t_enum_
.e_constraint p_enum_
.e_constraint
]
1109 (env0
: matcher_env
) : (match_result
* matcher_env
) =
1111 [match_id_res t_class_
.c_name p_class_
.c_name
;
1112 match_bool t_class_
.c_final p_class_
.c_final
;
1113 match_bool t_class_
.c_is_xhp p_class_
.c_is_xhp
;
1115 is_star_user_attribute
1116 match_user_attribute
1117 t_class_
.c_user_attributes
1118 p_class_
.c_user_attributes
;
1119 match_class_kind t_class_
.c_kind p_class_
.c_kind
;
1133 t_class_
.c_implements
1134 p_class_
.c_implements
;
1135 match_namespace t_class_
.c_namespace p_class_
.c_namespace
;
1136 LM.match_option match_enum_ t_class_
.c_enum p_class_
.c_enum
;
1142 match get_id_type p_class_
.c_name
with
1143 | Wildcard
-> dummy_success_res, env0
1144 | DoNotCare
-> LM.match_list
1150 | _
-> LM.match_attributes actions env0
1155 (env : matcher_env
) : (match_result
* matcher_env
) =
1156 match t_cat
, p_cat
with
1157 | CA_hint t_h
, CA_hint p_h
->
1158 match_hint t_h p_h
env
1159 | CA_enum t_sl
, CA_enum p_sl
->
1160 revert_env_if_no_match
1161 (LM.match_list (fun _
-> false) match_string t_sl p_sl
env)
1163 | _
, _
-> NoMatch
, env
1168 (env : matcher_env
) : (match_result
* matcher_env
) =
1170 [match_ca_type t_caf
.ca_type p_caf
.ca_type
;
1171 match_id_res t_caf
.ca_id p_caf
.ca_id
;
1172 LM.match_option match_expr t_caf
.ca_value p_caf
.ca_value
;
1173 match_bool t_caf
.ca_required p_caf
.ca_required
]
1179 (env : matcher_env
) : (match_result
* matcher_env
) =
1181 [match_id_res t_tc
.tconst_name p_tc
.tconst_name
;
1182 match_bool t_tc
.tconst_abstract p_tc
.tconst_abstract
;
1185 t_tc
.tconst_constraint
1186 p_tc
.tconst_constraint
;
1187 LM.match_option match_hint t_tc
.tconst_type p_tc
.tconst_type
]
1190 and match_class_attr
1191 (t_class_attr
: class_attr
)
1192 (p_class_attr
: class_attr
)
1193 (env : matcher_env
) : (match_result
* matcher_env
) =
1194 match t_class_attr
, p_class_attr
with
1195 | CA_name t_id
, CA_name p_id
->
1196 match_id_res t_id p_id
env
1197 | CA_field t_cf
, CA_field p_cf
->
1198 match_ca_field t_cf p_cf
env
1199 | _
, _
-> NoMatch
, env
1201 and match_trait_req_kind
1202 (t_trk
: trait_req_kind
)
1203 (p_trk
: trait_req_kind
)
1204 (env : matcher_env
) : (match_result
* matcher_env
) =
1206 then dummy_success_res, env
1210 (_
, t_cv_id
, t_cv_expr
: class_var
)
1211 (_
, p_cv_od
, p_cv_expr
: class_var
)
1212 (env : matcher_env
) : (match_result
* matcher_env
) =
1214 [match_id_res t_cv_id p_cv_od
;
1215 LM.match_option match_expr t_cv_expr p_cv_expr
]
1219 (t_class_elt
: class_elt
)
1220 (p_class_elt
: class_elt
)
1221 (env : matcher_env
) : (match_result
* matcher_env
) =
1222 match t_class_elt
, p_class_elt
with
1223 | Ast.Method t_method_
, Ast.Method p_method_
->
1224 match_method_ t_method_ p_method_
env
1225 | Const
(t_hopt
, t_iel
), Const
(p_hopt
, p_iel
) ->
1227 [LM.match_option match_hint t_hopt p_hopt
;
1230 (LM.match_pair_fn match_id_res match_expr
)
1234 | AbsConst
(t_hopt
, t_id
), AbsConst
(p_hopt
, p_id
) ->
1236 [LM.match_option match_hint t_hopt p_hopt
;
1237 match_id_res t_id p_id
]
1239 | Attributes t_cal
, Attributes p_cal
->
1240 revert_env_if_no_match
1241 (LM.match_list (fun _
-> false) match_class_attr t_cal p_cal
env)
1243 | TypeConst t_tc
, TypeConst p_tc
->
1244 match_typeconst t_tc p_tc
env
1245 | ClassUse t_h
, ClassUse p_h
1246 | XhpAttrUse t_h
, XhpAttrUse p_h
->
1247 match_hint t_h p_h
env
1248 | ClassTraitRequire
(t_trk
, t_h
), ClassTraitRequire
(p_trk
, p_h
) ->
1250 [match_trait_req_kind t_trk p_trk
;
1253 | ClassVars
(t_kl
, t_hopt
, t_cvl
), ClassVars
(p_kl
, p_hopt
, p_cvl
) ->
1255 [LM.match_list (fun _
-> false) match_kind t_kl p_kl
;
1256 LM.match_option match_hint t_hopt p_hopt
;
1257 LM.match_list is_star_class_var match_class_var t_cvl p_cvl
]
1259 | XhpAttr
(t_hopt
, t_cv
, t_b
, t_pelopt
),
1260 XhpAttr
(p_hopt
, p_cv
, p_b
, p_pelopt
) ->
1262 [LM.match_option match_hint t_hopt p_hopt
;
1264 match_class_var t_cv p_cv
;
1267 (* we don't care about the position *)
1268 (fun _ _
env -> dummy_success_res, env)
1269 (* want to match the expr list correctly*)
1274 | _
, _
-> (NoMatch
, env)
1277 (t_method_
: method_
)
1278 (p_method_
: method_
)
1279 (env : matcher_env
) : (match_result
* matcher_env
) =
1281 [match_id_res t_method_
.m_name p_method_
.m_name
;
1291 p_method_
.m_tparams
;
1297 LM.match_option match_hint t_method_
.m_ret p_method_
.m_ret
;
1298 match_bool t_method_
.m_ret_by_ref p_method_
.m_ret_by_ref
;
1300 is_star_user_attribute
1301 match_user_attribute
1302 t_method_
.m_user_attributes
1303 p_method_
.m_user_attributes
;
1304 match_fun_kind t_method_
.m_fun_kind p_method_
.m_fun_kind
;
1306 (Block t_method_
.m_body
)
1307 (Block p_method_
.m_body
)] in
1308 match get_id_type p_method_
.m_name
with
1309 | Wildcard
-> dummy_success_res, env
1310 | DoNotCare
-> match_stmt
1311 (Block t_method_
.m_body
)
1312 (Block p_method_
.m_body
)
1314 | _
-> LM.match_attributes attributes env
1319 (env : matcher_env
) : (match_result
* matcher_env
) =
1320 (* maybe have TODO something to make sure wildcarding works correctly here *)
1321 match t_case
, p_case
with
1322 | Default t_b
, Default p_b
->
1323 match_stmt
(Block t_b
) (Block p_b
) env
1324 | Case
(t_e
, t_b
), Case
(p_e
, p_b
) ->
1326 [match_expr t_e p_e
;
1327 match_stmt
(Block t_b
) (Block p_b
)]
1329 | _
, _
-> NoMatch
, env
1332 (t_as_expr
: as_expr
)
1333 (p_as_expr
: as_expr
)
1334 (env : matcher_env
) : (match_result
* matcher_env
) =
1335 match t_as_expr
, p_as_expr
with
1336 | As_v t_e
, As_v p_e
->
1337 match_expr t_e p_e
env
1338 | As_kv
(t_e1
, t_e2
), As_kv
(p_e1
, p_e2
) ->
1340 [match_expr t_e1 p_e1
;
1341 match_expr t_e2 p_e2
]
1343 | _
, _
-> NoMatch
, env
1348 (env : matcher_env
) : (match_result
* matcher_env
) =
1349 let (t_i1
, t_i2
, t_b
) = t_catch
in
1350 let (p_i1
, p_i2
, p_b
) = p_catch
in
1352 [match_id_res t_i1 p_i1
;
1353 match_id_res t_i2 p_i2
;
1354 match_stmt
(Block t_b
) (Block p_b
)]
1357 (* def is KStar iff it is a class, or function or string literal with
1359 and is_star_def
(p_def
: def
) : bool =
1361 | Class p_class
-> (get_id_type p_class
.c_name
) = KStar
1362 | Fun p_fun_
-> (get_id_type p_fun_
.f_name
) = KStar
1363 | Ast.Stmt p_stmt
-> is_star_stmt p_stmt
1366 (* TODO implement this correctly *)
1367 and is_star_hint
(_p_hint
: hint
) : bool = false
1369 (* tparams cannot be KStar matched but this function is in case that changes *)
1370 and is_star_tparam
(_p_tparam
: tparam
) : bool = false
1372 (* fun_params cannot be KStar matched but this function is in case
1374 and is_star_fun_param
(_p_fun_param
: fun_param
) : bool = false
1376 (* user_attributes cannot be KStar matched but this function
1377 allows us to change that later *)
1378 and is_star_user_attribute
(_p_uatt
: user_attribute
) : bool = false
1380 (* doesn't make sense to star match a list of class variables *)
1381 and is_star_class_var
(_p_cvar
: class_var
) : bool = false
1383 (* class_elt is KStar iff it is a method with name "__KSTAR" *)
1384 and is_star_celt
(celt
: class_elt
) : bool =
1386 | Ast.Method meth
->
1387 let id_type = (get_id_type meth
.m_name
) in
1391 (* stmt is KStar type iff it is the string literal "__KSTAR" *)
1393 (p_stmt
: stmt
) : bool =
1395 | Ast.Expr
(_
,(String
pstr)) -> get_id_type pstr = KStar
1398 (* stmt is KStar type iff it is the string literal "__KSTAR" *)
1399 and is_star_expr
(p_expr
: expr
) : bool =
1400 match snd p_expr
with
1401 | String
pstr -> get_id_type pstr = KStar
1404 (* catch is KStar type iff the name of the variable the exception is bound
1406 e.g. catch (Exception $__KSTAR *)
1407 and is_star_catch
(p_catch
: catch
) : bool =
1408 let (_
,ident
,_
) = p_catch
in
1409 get_id_type ident
= KStar
1411 (* case is type KStar iff if the expression to match is "__KSTAR"
1412 e.g. case "__KSTAR": *)
1413 and is_star_case
(p_case
: case
) : bool =
1415 | Case
(exp
,_
) -> is_star_expr exp
1416 | Default _
-> false
1418 (* is KStar type iff the value is "__KSTAR" *)
1419 and is_star_afield
(afl
: afield
) : bool =
1421 | AFvalue exp
-> is_star_expr exp
1422 | AFkvalue
(exp
,_
) -> is_star_expr exp
1424 (* stmt is a wildcard iff it consists of just a wildcard expr:
1426 and is_wildcard_stmt
(p_stm
: stmt
) : bool =
1428 | Ast.Expr
(_
,(String
pstr)) -> (get_id_type pstr) = Wildcard
1431 (* handle_sa_hd_fn for stmt list for SkipAny matching, see comment on
1432 match_list_with_skips *)
1434 (try_match_list_fn
:
1435 stmt list
-> stmt list
-> stmt list
-> matcher_env
->
1436 match_result
* matcher_env
)
1440 (skipped_text
: stmt list
)
1441 (env : matcher_env
) : (match_result
* matcher_env
) option =
1442 if is_skip_any_stmt p_hd
1444 (revert_env_if_no_match
1445 (try_match try_match_list_fn t_tl
(p_hd
:: p_tl
) skipped_text
env)
1449 (* Tries to match the text with the pattern recursively, sensitive to KStars
1451 handle_sa_fn for stmt list SkipAny matching,
1452 see comment on match_list_with_skips*)
1454 (try_match_list_fn
:
1455 stmt list
-> stmt list
-> stmt list
-> matcher_env
->
1456 match_result
* matcher_env
)
1457 (t_list
: stmt list
)
1458 (p_list : stmt list
)
1459 (skipped_text
: stmt list
)
1460 (env : matcher_env
) :
1461 match_result
* matcher_env
=
1462 (* given a chunk of text that was skipped over by a SkipAny
1463 find all the possible chunks that could match the SkipAny pattern
1464 (all child blocks + self) *)
1465 let find_child_text (text_chunk
: stmt list
) : block list
=
1466 let visitor = new block_finding_visitor
() in
1467 visitor#on_block
[] text_chunk
in
1469 (* given an output of find_child_text, find all the matches,
1470 update env if necessary *)
1471 let match_child_text
1472 (child_text
: block list
)
1473 (pattern
: stmt list
)
1474 (env : matcher_env
) : match_result
* matcher_env
=
1476 ~f
:(fun (res_so_far
, env) text_blk
->
1477 let text_res, env'
=
1478 match_stmt
(Block text_blk
) (Block pattern
) env in
1479 (concat_match_results_nodup [res_so_far
; text_res]), env'
)
1480 ~init
:(NoMatch
, env)
1483 (* Try to match a SkipAny: match sa_pattern over all children of
1484 skipped_text, as well as making sure the rest of the pattern and
1486 let match_single_skipany t_list
p_list skipped_text sa_pattern
env =
1487 (* Find all blocks that are children of the relevant
1489 NOTE skipped_text is reversed because of the way we appended to it, so
1490 we need to reverse it before finding children *)
1491 let sa_text = find_child_text (List.rev skipped_text
) in
1492 (* look for matches over these child blocks *)
1493 let cur_res, env'
= match_child_text sa_text sa_pattern
env in
1494 (* check to see if remaining text matches remaining pattern (note these
1495 must be done in order so that the environment is correct - consistent
1496 with a linear pass through the file) *)
1497 let later_res, env2
= try_match try_match_list_fn t_list
p_list [] env'
in
1498 match cur_res, later_res with
1499 | NoMatch
, NoMatch
| Matches _
, NoMatch
1500 | NoMatch
, Matches _
-> NoMatch
, env
1501 (* if both matches succeeded, we have a match *)
1502 | Matches _
, Matches _
->
1503 concat_match_results_nodup [cur_res; later_res],
1506 (* Handle the case where the pattern started with a SkipAny, figuring
1507 out all possible amounts of text that the SkipAny could match over *)
1508 let rec handle_leading_skipany t_list
p_list skipped_text sa_pattern
env =
1510 (* no text left, try matching pattern with the skipped text *)
1512 match skipped_text
with
1516 match_single_skipany [] p_list skipped_text sa_pattern
env end
1518 (* Try skipping more of the text *)
1519 let rec_res, rec_env
=
1520 handle_leading_skipany
1521 t_tl
p_list (t_hd
:: skipped_text
) sa_pattern
env in
1522 (* Try matching the SkipAny with the current amount of skipped text *)
1523 let norec_res, norec_env
=
1524 match_single_skipany t_list
p_list skipped_text sa_pattern
env in
1525 (* if either of those worked, we return a success *)
1526 match rec_res, norec_res with
1527 | NoMatch
, NoMatch
-> NoMatch
, env
1528 | Matches _
, NoMatch
-> rec_res, rec_env
1529 | NoMatch
, Matches _
-> norec_res, norec_env
1530 | Matches _
, Matches _
->
1531 concat_match_results_nodup [rec_res; norec_res],
1532 (merge_envs rec_env norec_env
) in
1534 (* preprocess a SkipAny token from the pattern if we find one to find
1535 the pattern that will be matched later with the text it skips *)
1537 | hd
:: tl
when is_skip_any_unproc hd
->begin
1538 (* next block is the pattern we want to recursively match*)
1539 let skipany_body, after_skipany
=
1544 | Block bl
-> star_stmt :: bl
@ [star_stmt]
1545 (* incorrectly formatted pattern - SkipAny not followed by
1546 block representing pattern to match *)
1547 | _
-> failwith
"missing block after skip stmt" in
1549 (* incorrectly formatted pattern - SkipAny not followed by block
1550 representing pattern to match *)
1551 | _
-> failwith
"missing block after skip stmt" in
1552 (* Deal with the leading SkipAny *)
1553 let p_list = star_stmt :: after_skipany
in
1554 handle_leading_skipany t_list
p_list skipped_text
skipany_body env
1556 (* If there wasn't a leading SkipAny, don't do anything, just call the
1557 try_match_list_fn *)
1559 revert_env_if_no_match
1560 (try_match_list_fn t_list
p_list skipped_text
env) env
1562 (* matches stmts by recursively matching their subcomponents *)
1566 (env0
: matcher_env
) : (match_result
* matcher_env
) =
1568 add_mvar_node env0
is_meta_stmt p_stmt
(Hh_match_utils.Stmt t_stmt
) in
1569 let p_stmt = instantiate_mvar_stmt env p_stmt in
1570 let old_env = env in
1571 (if is_wildcard_stmt
p_stmt
1574 | Noop
-> NoMatch
, env
1575 | _
-> (dummy_success_res, env)
1577 match t_stmt
, p_stmt with
1579 (dummy_success_res, env)
1580 | Fallthrough
, Fallthrough
->
1581 (dummy_success_res, env)
1582 | Ast.Expr t_expr
, Ast.Expr p_expr
->
1583 match_expr t_expr p_expr
env
1584 | Block t_sl
, Block p_sl
->
1585 LM.match_list_with_skips
1588 (Some
(fun stm
-> Hh_match_utils.Stmt stm
))
1595 | Break loc
, Break _
1596 | Continue loc
, Continue _
->
1597 (* use start because we want line number of beginning of construct *)
1598 (Matches
[(DummyNode
, Pos.pos_start loc
)], env)
1599 | Throw t_e
, Throw p_e
->
1600 match_expr t_e p_e
env
1601 | Return
(loc
, t_eopt
), Return
(_
, p_eopt
) ->
1602 let opt_res = LM.match_option match_expr t_eopt p_eopt
env in
1603 (* if it matched a return where the expression does not have a
1605 if fst
opt_res == dummy_success_res
1606 then (Matches
[(DummyNode
, Pos.pos_start loc
)], env)
1608 | Static_var t_el
, Static_var p_el
->
1609 match_expr_list t_el p_el
env
1610 | If
(t_e
, t_b1
, t_b2
), If
(p_e
, p_b1
, p_b2
) ->
1612 [match_expr t_e p_e
;
1613 match_stmt
(Block t_b1
) (Block p_b1
);
1614 match_stmt
(Block t_b2
) (Block p_b2
)]
1616 | Do
(t_b
, t_e
), Do
(p_b
, p_e
) ->
1618 [match_stmt
(Block t_b
) (Block p_b
);
1621 | While
(t_e
, t_b
) , While
(p_e
, p_b
) ->
1623 [match_expr t_e p_e
;
1624 match_stmt
(Block t_b
) (Block p_b
)]
1626 | For
(t_e1
, t_e2
, t_e3
, t_b
), For
(p_e1
, p_e2
, p_e3
, p_b
) ->
1628 [match_expr t_e1 p_e1
;
1629 match_expr t_e2 p_e2
;
1630 match_expr t_e3 p_e3
;
1631 match_stmt
(Block t_b
) (Block p_b
)]
1633 | Switch
(t_e
, t_cl
), Switch
(p_e
, p_cl
) ->
1635 [match_expr t_e p_e
;
1636 LM.match_list is_star_case match_case t_cl p_cl
]
1638 | Foreach
(t_e
, t_aopt
, t_ase
, t_b
), Foreach
(p_e
, p_aopt
, p_ase
, p_b
) ->
1639 let match_aopt t_aopt p_aopt
env =
1640 match t_aopt
, p_aopt
with
1641 | None
, Some _
| Some _
, None
-> NoMatch
, env
1642 | None
, None
| Some _
, Some _
-> dummy_success_res, env in
1644 [match_expr t_e p_e
;
1645 match_aopt t_aopt p_aopt
;
1646 match_as_expr t_ase p_ase
;
1647 match_stmt
(Block t_b
) (Block p_b
)]
1649 | Try
(t_b1
, t_cl
, t_b2
), Try
(p_b1
, p_cl
, p_b2
) ->
1651 [match_stmt
(Block t_b1
) (Block p_b1
);
1652 LM.match_list is_star_catch match_catch t_cl p_cl
;
1653 match_stmt
(Block t_b2
) (Block p_b2
)]
1656 (dummy_success_res, env)
1657 | _
, _
-> (NoMatch
, old_env))
1659 t_stmt
p_stmt adjust_range_stmt
1660 env.transformations
.stmt_delete_list
1661 env.transformations
.stmt_transf_map
1663 (Ast_code_extent.source_extent_stmt
env.file
env.source
)
1664 |> function ret
-> revert_env_if_no_match ret env0
1666 and is_wildcard_expr
(exp
: expr_
) : bool =
1668 | String
pstr -> (get_id_type pstr) = Wildcard
1674 (env : matcher_env
) : (match_result
* matcher_env
) =
1675 match t_afl
, p_afl
with
1676 | AFvalue t_e
, AFvalue p_e
-> match_expr t_e p_e
env
1677 | AFkvalue
(t_e1
, t_e2
), AFkvalue
(p_e1
, p_e2
) ->
1679 [match_expr t_e1 p_e1
;
1680 match_expr t_e2 p_e2
]
1682 | _
, _
-> (NoMatch
, env)
1684 and match_shape_field_name
1685 (t_sfn
: shape_field_name
)
1686 (p_sfn
: shape_field_name
)
1687 (env : matcher_env
) : (match_result
* matcher_env
) =
1688 let old_env = env in
1689 match t_sfn
, p_sfn
with
1690 | SFlit t_pstr
, SFlit p_pstr
->
1691 revert_env_if_no_match (match_id_res t_pstr p_pstr
env) old_env
1692 | SFclass_const
(t_i
, t_pstr
), SFclass_const
(p_i
, p_pstr
) ->
1694 [match_id_res t_i p_i
;
1695 match_id_res t_pstr p_pstr
]
1697 | _
, _
-> (NoMatch
, old_env)
1699 and match_og_null_flavor
1700 (t_onf
: og_null_flavor
)
1701 (p_onf
: og_null_flavor
)
1702 (env : matcher_env
) : (match_result
* matcher_env
) =
1704 then dummy_success_res, env
1710 (env : matcher_env
) : (match_result
* matcher_env
) =
1712 then dummy_success_res, env
1718 (env : matcher_env
) : (match_result
* matcher_env
) =
1719 match t_bop
, p_bop
with
1720 | Eq t_bopt
, Eq p_bopt
->
1721 LM.match_option match_bop t_bopt p_bopt
env
1724 then dummy_success_res, env
1727 and match_import_flavor
1728 (t_if
: import_flavor
)
1729 (p_if
: import_flavor
)
1730 (env : matcher_env
) : (match_result
* matcher_env
) =
1732 then dummy_success_res, env
1735 (* Tries to match the text with the pattern recursively, trying to match
1736 the given pattern in the program specified *)
1739 (pat
: skipany_ast_node
)
1740 (env : matcher_env
) : match_result
* matcher_env
=
1741 (* given a chunk of text that was skipped over by a SkipAny
1742 find all the possible chunks that could match the SkipAny pattern
1743 (all child blocks + self) *)
1744 let find_child_text visitor =
1745 visitor#on_program
[] text in
1747 (* given an output of find_child_text, find all the matches,
1748 update env if necessary *)
1749 let match_child_text
1753 (env : matcher_env
) : match_result
* matcher_env
=
1755 ~f
:(fun (res_so_far
, env) text_node
->
1756 let text_res, env'
= match_fn text_node pat
env in
1757 (concat_match_results_nodup [res_so_far
; text_res]), env'
)
1758 ~init
:(NoMatch
, env)
1762 | SkipanyExpr e
-> match_child_text
1763 (find_child_text (new expr_finding_visitor
())) e match_expr
env
1764 | SkipanyBlock b
-> match_child_text
1765 (find_child_text (new block_finding_visitor
())) b
1766 match_statements_in_block
env
1768 and handle_expr_skipany
1771 (env : matcher_env
) : match_result
* matcher_env
=
1772 handle_skipany
text (SkipanyExpr p_expr
) env
1774 and handle_stmt_skipany
1777 (env : matcher_env
) : match_result
* matcher_env
=
1778 handle_skipany
text (SkipanyBlock p_stmts
) env
1780 (* Finds all matches of the statement list p_stmts inside the block t_block *)
1781 and match_statements_in_block
1783 (p_stmts
: stmt list
)
1784 (env : matcher_env
) : (match_result
* matcher_env
) =
1785 let p_len = List.length p_stmts
in
1787 (* Walk through a list of statements and check at each one if the sequence of
1788 statements starting there matches the pattern *)
1789 let rec find_matching_sublists t_stmts
env : match_result list
* matcher_env
=
1792 | _
::t_stmts'
as stmts
->
1793 let t_stmts = List.take stmts
p_len in
1794 (* Produces an option on a list of matched statements and the matching
1795 environment. If there is one statement that isn't matched by the
1796 pattern this will be None *)
1797 let rec match_stmt_lists t_stmts p_stmts
env =
1798 match (t_stmts, p_stmts
) with
1799 | ([], []) -> Some
([], env)
1800 | (ts
::t_stmts'
, ps
::p_stmts'
) -> begin
1801 match match_stmt ts ps
env with
1802 | (NoMatch
, _
) -> None
1803 | (m
, env'
) -> begin
1804 match match_stmt_lists t_stmts' p_stmts'
env'
with
1806 | Some
(l
, env''
) -> Some
(m
:: l
, env''
) end
1810 let (matches
, env'
) = match match_stmt_lists t_stmts p_stmts
env with
1814 (* Collapse match results into one match, and recurse *)
1815 let (l
, e
) = find_matching_sublists t_stmts'
env'
in
1816 (concat_match_results_nodup matches
:: l
, e
) in
1818 let matches, env'
= find_matching_sublists t_block
env in
1819 (concat_match_results_nodup matches, env'
)
1824 (env0
: matcher_env
) : (match_result
* matcher_env
) =
1825 (* remember do revert *)
1827 add_mvar_node env0
is_meta_expr p_expr
(Hh_match_utils.Expr t_expr
) in
1828 let p_expr = instantiate_mvar_expr env p_expr in
1829 let (t_pos
, t_expr_
) = t_expr
in
1830 let (_
, p_expr_
) = p_expr in
1832 Matches
[(Hh_match_utils.Expr t_expr
, Pos.pos_start t_pos
)] in
1833 (* if pattern is a wildcard we will always match*)
1834 (if is_wildcard_expr p_expr_
1835 then (success_res, env)
1837 (* if the match was successful, we stick on a result corresponding to the
1840 match t_expr_
, p_expr_
with
1841 | Array t_afl
, Array p_afl
->
1842 LM.match_list is_star_afield match_afield t_afl p_afl
env
1843 | Shape t_sfnel
, Shape p_sfnel
->
1846 (LM.match_pair_fn match_shape_field_name match_expr
)
1850 | Collection
(t_i
, t_al
), Collection
(p_i
, p_al
) ->
1852 [match_id_res t_i p_i
;
1862 | Yield_break
, Yield_break
->
1863 dummy_success_res, env
1864 | Id_type_arguments
(t_id
, t_hl
), Id_type_arguments
(p_id
, p_hl
) ->
1866 [match_id_res t_id p_id
;
1867 LM.match_list (fun _
-> false) match_hint t_hl p_hl
]
1870 | Lvar t_id
, Lvar p_id
->
1871 match_id_res t_id p_id
env
1872 | Obj_get
(t_e1
, t_e2
, t_onf
), Obj_get
(p_e1
, p_e2
, p_onf
) ->
1874 [match_expr t_e1 p_e1
;
1875 match_expr t_e2 p_e2
;
1876 match_og_null_flavor t_onf p_onf
]
1878 | Array_get
(t_e
, t_eopt
), Array_get
(p_e
, p_eopt
) ->
1880 [match_expr t_e p_e
;
1881 LM.match_option match_expr t_eopt p_eopt
]
1883 | Class_get
(t_id
, t_pstr
), Class_get
(p_id
, p_pstr
)
1884 | Class_const
(t_id
, t_pstr
), Class_const
(p_id
, p_pstr
) ->
1886 [match_id_res t_id p_id
;
1887 match_id_res t_pstr p_pstr
]
1889 | Call
(t_e
, t_el1
, t_el2
), Call
(p_e
, p_el1
, p_el2
) ->
1891 [match_expr t_e p_e
;
1892 match_expr_list t_el1 p_el1
;
1893 match_expr_list t_el2 p_el2
]
1895 | New
(t_e
, t_el1
, t_el2
), New
(p_e
, p_el1
, p_el2
) ->
1897 [match_expr t_e p_e
;
1898 match_expr_list t_el1 p_el1
;
1899 match_expr_list t_el2 p_el2
]
1901 | Int t_pstr
, Int p_pstr
1902 | Float t_pstr
, Float p_pstr
1903 | String t_pstr
, String p_pstr
->
1904 revert_env_if_no_match (match_id_res t_pstr p_pstr
env) env
1905 | String2 t_el
, String2 p_el
->
1907 [match_expr_list t_el p_el
]
1909 | Yield t_af
, Yield p_af
->
1910 revert_env_if_no_match (match_afield t_af p_af
env) env
1911 | Clone t_e
, Clone p_e
1912 | Await t_e
, Await p_e
1913 | Unsafeexpr t_e
, Unsafeexpr p_e
->
1914 revert_env_if_no_match (match_expr t_e p_e
env) env
1915 | List t_el
, List p_el
1916 | Expr_list t_el
, Expr_list p_el
->
1917 match_expr_list t_el p_el
env
1918 | Cast
(t_h
, t_e
), Cast
(p_h
, p_e
) ->
1920 [match_hint t_h p_h
;
1923 | Unop
(t_uop
, t_e
), Unop
(p_uop
, p_e
) ->
1925 [match_uop t_uop p_uop
;
1928 | Binop
(t_bop
, t_e1
, t_e2
), Binop
(p_bop
, p_e1
, p_e2
) ->
1930 [match_bop t_bop p_bop
;
1931 match_expr t_e1 p_e1
;
1932 match_expr t_e2 p_e2
]
1934 | Eif
(t_e1
, t_eopt
, t_e2
), Eif
(p_e1
, p_eopt
, p_e2
) ->
1936 [match_expr t_e1 p_e1
;
1937 LM.match_option match_expr t_eopt p_eopt
;
1938 match_expr t_e2 p_e2
]
1940 | InstanceOf
(t_e1
, t_e2
), InstanceOf
(p_e1
, p_e2
) ->
1942 [match_expr t_e1 p_e1
;
1943 match_expr t_e2 p_e2
]
1945 | Efun
(t_f
, t_ibl
), Efun
(p_f
, p_ibl
) ->
1947 [match_fun_ t_f p_f
;
1950 (LM.match_pair_fn match_id_res match_bool
)
1954 | Lfun t_f
, Lfun p_f
->
1955 revert_env_if_no_match (match_fun_ t_f p_f
env) env
1956 | Xml
(t_i
, t_iel
, t_el
), Xml
(p_i
, p_iel
, p_el
) ->
1957 (*TODO make XML matching order-insensitive*)
1959 [match_id_res t_i p_i
;
1962 (LM.match_pair_fn match_id_res match_expr
)
1965 match_expr_list t_el p_el
]
1967 | Import
(t_if
, t_e
), Import
(p_if
, p_e
) ->
1969 [match_import_flavor t_if p_if
;
1972 | _
, _
-> (NoMatch
, env) in
1973 (match fst
exp_res with
1974 | NoMatch
-> exp_res
1976 concat_match_results [(fst
exp_res); success_res], snd
exp_res))
1978 t_expr
p_expr adjust_range_expr
1979 env.transformations
.expr_delete_list
1980 env.transformations
.expr_transf_map
1982 (Ast_code_extent.source_extent_expr
env.file
env.source
)
1983 |> function ret
-> revert_env_if_no_match ret env0
1985 and match_shape_field
1986 (t_sf
: shape_field
)
1987 (p_sf
: shape_field
)
1988 (env : matcher_env
) : (match_result
* matcher_env
) =
1989 match t_sf
, p_sf
with
1990 | {sf_optional
=t_sf_optional
; sf_name
=t_sfn
; sf_hint
=t_sfh
},
1991 {sf_optional
=p_sf_optional
; sf_name
=p_sfn
; sf_hint
=p_sfh
}
1992 when t_sf_optional
= p_sf_optional
->
1993 let t_sf_pair = (t_sfn
, t_sfh
) in
1994 let p_sf_pair = (p_sfn
, p_sfh
) in
1995 (LM.match_pair_fn match_shape_field_name match_hint
)
2004 (env : matcher_env
) : (match_result
* matcher_env
) =
2006 match (snd t_hint
), (snd p_hint
) with
2007 | Hoption t_hint
, Hoption p_hint
->
2008 match_hint t_hint p_hint
env
2009 | Hfun
(t_hl
, t_b
, t_h
), Hfun
(p_hl
, p_b
, p_h
) ->
2011 [LM.match_list (fun _
-> false) match_hint t_hl p_hl
;
2015 | Htuple t_hl
, Htuple p_hl
->
2016 LM.match_list (fun _
-> false) match_hint t_hl p_hl
env
2017 | Happly
(t_id
, t_hl
), Happly
(p_id
, p_hl
) ->
2019 [match_id_res t_id p_id
;
2020 LM.match_list (fun _
-> false) match_hint t_hl p_hl
]
2022 | Hshape t_sfl
, Hshape p_sfl
->
2023 LM.match_list (fun _
-> false) match_shape_field t_sfl p_sfl
env
2024 | Haccess
(t_id1
, t_id2
, t_idl
), Haccess
(p_id1
, p_id2
, p_idl
) ->
2026 [match_id_res t_id1 p_id1
;
2027 match_id_res t_id2 p_id2
;
2028 LM.match_list (fun _
-> false) match_id_res t_idl p_idl
]
2030 | _
, _
-> NoMatch
, env in
2031 match fst
child_res with
2032 | NoMatch
-> NoMatch
, env
2035 (fst
child_res) (Hint t_hint
) (Pos.pos_start
(fst t_hint
))),
2038 (* to help with printing, will also remove any
2039 dummy_success_res elements *)
2040 let sort_and_remove_duplicates compare l
=
2041 let sl = List.sort
compare l
in
2042 let rec go l
acc = match l
with
2043 | [] -> List.rev
acc
2044 | (x
::xs
) when x
= (DummyNode
, File_pos.dummy
) -> go xs
acc
2045 | [x
] -> List.rev
(x
::acc)
2047 if File_pos.line
(snd x1
) = File_pos.line
(snd x2
)
2048 then go (x2
::xs
) acc
2049 else go (x2
::xs
) (x1
::acc)
2052 (* Actual function for finding matching nodes *)
2055 (text_file
: Relative_path.t
)
2056 (text_content
: string)
2057 (pattern_parsed
: Parser_hack.parser_return
)
2058 : (ast_node
* File_pos.t
) list
=
2062 (Program pattern_parsed
.Parser_hack.ast
)
2064 source
= text_content
;
2065 uses_regexp
= false;
2066 comments
= pattern_parsed
.Parser_hack.comments
;
2067 metavars
= MetavarMap.empty
;
2069 { stmt_delete_list
= [];
2070 expr_delete_list
= [];
2071 stmt_transf_map
= [];
2072 expr_transf_map
= [] };
2073 patches
= PatchSet.empty
} in
2074 match fst
match_res with
2075 | Matches result
-> result
2078 (* takes a program that consists of a single toplevel block i.e. a pattern used
2079 with the -s switch, and returns a list of that blocks' statements *)
2080 let get_skipany_stmt = function
2081 | [Ast.Stmt
(Ast.Block stmts
)] -> Some stmts
2084 let find_matches_expr_or_stmt
2086 (text_file
: Relative_path.t
)
2087 (text_content
: string)
2088 (pattern_parsed
: Parser_hack.parser_return
)
2090 (skipany_handler_fn
) : (ast_node
* File_pos.t
) list
=
2091 match skipany_fn pattern_parsed
.Parser_hack.ast
with
2092 | Some stmts
-> begin
2094 skipany_handler_fn
text stmts
2096 source
= text_content
;
2097 uses_regexp
= false;
2098 comments
= pattern_parsed
.Parser_hack.comments
;
2099 metavars
= MetavarMap.empty
;
2101 { stmt_delete_list
= [];
2102 expr_delete_list
= [];
2103 stmt_transf_map
= [];
2104 expr_transf_map
= [] };
2105 patches
= PatchSet.empty
} in
2107 | Matches result
-> result
2111 (* function for searching for statements *)
2112 let find_matches_stmt
2114 (text_file
: Relative_path.t
)
2115 (text_content
: string)
2116 (pattern_parsed
: Parser_hack.parser_return
)
2117 : (ast_node
* File_pos.t
) list
=
2118 find_matches_expr_or_stmt text text_file text_content pattern_parsed
2119 get_skipany_stmt handle_stmt_skipany
2121 (* Gets the expression that is the pattern while verifying the pattern
2122 is of the correct form (returning None if the pattern is not) *)
2123 let get_skipany_expr = function
2124 | [Ast.Stmt
(Ast.Expr exp
)] -> Some exp
2127 (* function for searching for expressions *)
2128 let find_matches_expr
2130 (text_file
: Relative_path.t
)
2131 (text_content
: string)
2132 (pattern_parsed
: Parser_hack.parser_return
)
2133 : (ast_node
* File_pos.t
) list
=
2134 find_matches_expr_or_stmt text text_file text_content pattern_parsed
2135 get_skipany_expr handle_expr_skipany
2137 (* general patching function for statements and expressions *)
2138 let patch_expr_or_stmt
2140 (text_file
: Relative_path.t
)
2141 (text_content
: string)
2142 (pattern_parsed
: Parser_hack.parser_return
)
2143 (transformations
: patch_maps
)
2144 ~
(use_hh_format
: bool)
2145 (patch_fn
) (skipany_fn
): string option =
2146 match skipany_fn pattern_parsed
.Parser_hack.ast
with
2153 source
= text_content
;
2154 uses_regexp
= false;
2155 comments
= pattern_parsed
.Parser_hack.comments
;
2156 metavars
= MetavarMap.empty
;
2158 patches
= PatchSet.empty
} in
2162 if PatchSet.is_empty
env.patches
2165 Some
(Patcher.apply_patches
2167 ~patches
:(PatchSet.elements
env.patches
)
2168 ~format_result
:use_hh_format
) end
2171 (* function for patching statements *)
2174 (text_file
: Relative_path.t
)
2175 (text_content
: string)
2176 (pattern_parsed
: Parser_hack.parser_return
)
2177 (transformations
: patch_maps
)
2178 ~
(use_hh_format
: bool) : string option =
2179 patch_expr_or_stmt text text_file text_content pattern_parsed transformations
2180 use_hh_format handle_stmt_skipany
get_skipany_stmt
2182 (* function for patching expressions *)
2185 (text_file
: Relative_path.t
)
2186 (text_content
: string)
2187 (pattern_parsed
: Parser_hack.parser_return
)
2188 (transformations
: patch_maps
)
2189 ~
(use_hh_format
: bool) :
2191 patch_expr_or_stmt text text_file text_content pattern_parsed transformations
2192 use_hh_format handle_expr_skipany
get_skipany_expr
2196 (text_file
: Relative_path.t
)
2197 (text_content
: string)
2198 (pattern_parsed
: Parser_hack.parser_return
)
2199 (transformations
: patch_maps
)
2200 ~
(use_hh_format
: bool) :
2205 (Program pattern_parsed
.Parser_hack.ast
)
2207 source
= text_content
;
2208 uses_regexp
= false;
2209 comments
= pattern_parsed
.Parser_hack.comments
;
2210 metavars
= MetavarMap.empty
;
2211 transformations
= transformations
;
2212 patches
= PatchSet.empty
} in
2216 if PatchSet.is_empty
env.patches
2219 Some
(Patcher.apply_patches
2221 ~patches
:(PatchSet.elements
env.patches
)
2222 ~format_result
:use_hh_format
)
2225 (matches : (ast_node
* File_pos.t
) list
)
2226 (text_code
: string) : string =
2228 sort_and_remove_duplicates
2229 (fun (m1
:(ast_node
* File_pos.t
))
2230 (m2
:(ast_node
* File_pos.t
)) ->
2231 File_pos.line
(snd m1
) - File_pos.line
(snd m2
))
2233 (* format a single match as "line num: line" *)
2234 let format_single_match (single_match
: ast_node
* File_pos.t
) : string =
2235 let pos = snd single_match
in
2236 if File_pos.is_dummy
pos then "" else
2237 let line_num, bol_pos
= File_pos.line_beg
pos in
2238 let eol_pos = String.index_from text_code bol_pos '
\n'
in
2242 (String.sub text_code bol_pos
(eol_pos - bol_pos
)) in
2245 (List.map ~f
:format_single_match match_list))