2 * Copyright 2005-2009, Ecole des Mines de Nantes, University of Copenhagen
3 * Yoann Padioleau, Julia Lawall, Rene Rydhof Hansen, Henrik Stuart, Gilles Muller, Nicolas Palix
4 * This file is part of Coccinelle.
6 * Coccinelle is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, according to version 2 of the License.
10 * Coccinelle is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with Coccinelle. If not, see <http://www.gnu.org/licenses/>.
18 * The authors reserve the right to distribute this or future versions of
19 * Coccinelle under other licenses.
23 (* given parsed minus code and a stream of + code, figure out where to put
24 the + code in the mcode of the minus code *)
26 (* Need to be able to find the nearest inhabited line rather than just
27 adding 1 or subtracting 1 to the actual line number. This is an issue for
28 plus.ml as well. This problem is dealt with by the logical line field,
29 which is not incremented for blank lines. *)
31 module Ast
= Ast_cocci
32 module Ast0
= Ast0_cocci
33 module V0
= Visitor_ast0
35 (* --------------------------------------------------------------------- *)
36 (* --------------------------------------------------------------------- *)
37 (* Step 1: convert minus/context code to an ordered stream of tokens *)
40 Minus
of Ast.info
* Ast.anything list list
ref
41 | Context
of Ast.info
* Ast.anything
Ast.befaft
ref
45 (_
,_
,Ast.MINUS
(info
,plus_stream
)) -> [Minus
(info
,plus_stream
)]
46 | (_
,_
,Ast.CONTEXT
(info
,plus_stream
)) -> [Context
(info
,plus_stream
)]
47 | _
-> failwith
"not possible 1"
49 let bad_mcode = function
50 (_
,_
,Ast.MINUS
(info
,plus_stream
)) -> Bad
(info
)
51 | (_
,_
,Ast.CONTEXT
(info
,plus_stream
)) -> Bad
(info
)
52 | _
-> failwith
"not possible 2"
57 Minus
(info
,plus_stream
) -> Bad
(info
)
58 | Context
(info
,plus_stream
) -> Bad
(info
)
62 (* --------------------------------------------------------------------- *)
66 let option_default = []
68 (* --------------------------------------------------------------------- *)
70 let get_option f
= function
72 | None
-> option_default
74 let ident recursor k i
= k i
(* nothing special to do *)
76 let expression recursor k e
=
77 match Ast0.unwrap e
with
78 Ast0.Edots
(dots
,whencode
) | Ast0.Ecircles
(dots
,whencode
)
79 | Ast0.Estars
(dots
,whencode
) ->
81 (get_option (function x
-> make_bad(recursor
.V0.combiner_expression x
))
85 let donothing recursor k ft
= k ft
87 (* needs a case for things to which new code cannot be attached *)
88 let parameterTypeDef recursor k p
=
89 match Ast0.unwrap p
with
90 Ast0.Pdots
(dots
) -> [bad_mcode dots
]
91 | Ast0.Pcircles
(dots
) -> [bad_mcode dots
]
94 let statement recursor k s
=
95 match Ast0.unwrap s
with
96 Ast0.Dots
(d
,whencode
) | Ast0.Circles
(d
,whencode
)
97 | Ast0.Stars
(d
,whencode
) ->
101 make_bad(recursor
.V0.combiner_statement_dots x
))
105 let top_level recursor k t
=
106 match Ast0.unwrap t
with
107 Ast0.FILEINFO
(old_file
,new_file
) ->
108 [bad_mcode old_file
;bad_mcode new_file
]
109 | Ast0.ERRORWORDS
(exps
) ->
110 make_bad (List.concat
(List.map recursor
.V0.combiner_expression exps
))
114 V0.combiner
bind option_default
115 mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode mcode
116 donothing donothing donothing
117 ident expression donothing donothing parameterTypeDef donothing
120 let rule code
= List.concat
(List.map
recursor.V0.combiner_top_level code
)
122 (* --------------------------------------------------------------------- *)
123 (* --------------------------------------------------------------------- *)
124 (* Step 2: merge the plus stream with the minus/context tokens *)
129 let (_
,_
,_
,start
,_
) = List.hd
(List.hd l
) in
133 let (_
,_
,_
,_
,finish
) = List.hd
(List.rev
(List.hd
(List.rev l
))) in
136 let get_real_start l
=
137 let (_
,start
,_
,_
,_
) = List.hd
(List.hd l
) in
140 let get_real_finish l
=
141 let (_
,_
,finish
,_
,_
) = List.hd
(List.rev
(List.hd
(List.rev l
))) in
144 let get_minus_next_line mline
= function
146 | Bad
(info
)::xs
-> info
.Ast.logical_line
147 | Minus
(info
,_
)::xs
-> info
.Ast.logical_line
148 | Context
(info
,_
)::xs
-> info
.Ast.logical_line
150 let drop_lines l
= List.map
(List.map
(function (x
,_
,_
,_
,_
) -> x
)) l
152 let rec merge minus_stream plus_stream
=
153 match (minus_stream
,plus_stream
) with
155 | ([],plus
::plus_stream
) ->
158 "minus stream ran out before plus stream\n(plus code begins on line %d)\n"
159 (get_real_start plus
))
160 | (Bad
(info
)::minus_stream
,plus
::plus_stream
) ->
161 let pfinish = get_finish plus
in
162 if info
.Ast.logical_line
> pfinish
166 "plus code starting on line %d has no minus or context code to attach to\n"
167 (get_real_start plus
))
168 else merge minus_stream
(plus
::plus_stream
)
169 | (((Minus
(info
,cell
)::minus_stream
) as all_minus
),plus
::plus_stream
) ->
170 let mline = info
.Ast.logical_line
in
171 let mnext_line = get_minus_next_line mline minus_stream
in
172 let pstart = get_start plus
in
173 let pfinish = get_finish plus
in
174 if pstart < mline && pfinish > mline
175 then (cell
:= (drop_lines plus
) @ !cell
; merge minus_stream plus_stream
)
176 else if pfinish + 1 = mline
177 then (cell
:= (drop_lines plus
) @ !cell
; merge all_minus plus_stream
)
178 else if not
(mline = mnext_line) && (pstart - 1 = mline)
179 then (cell
:= !cell
@ (drop_lines plus
); merge minus_stream plus_stream
)
180 else if pfinish < mline
182 Printf.printf
"failed to merge + code between lines %d and %d"
183 (get_real_start plus
) (get_real_finish plus
)
184 else merge minus_stream
(plus
::plus_stream
)
185 | (((Context
(info
,cell
)::minus_stream
) as all_minus
),plus
::plus_stream
) ->
186 let mline = info
.Ast.logical_line
in
187 let mnext_line = get_minus_next_line mline minus_stream
in
188 let pstart = get_start plus
in
189 let pfinish = get_finish plus
in
190 if pfinish + 1 = mline
191 then (cell
:= Ast.BEFORE
(drop_lines plus
); merge all_minus plus_stream
)
192 else if not
(mline = mnext_line) && (pstart - 1 = mline)
196 Ast.BEFORE x
-> cell
:= Ast.BEFOREAFTER
(x
,drop_lines plus
)
197 | _
-> cell
:= Ast.AFTER
(drop_lines plus
));
198 merge minus_stream plus_stream
200 else if pfinish < mline
202 Printf.printf
"failed to merge + code between lines %d and %d"
203 (get_real_start plus
) (get_real_finish plus
)
204 else merge minus_stream
(plus
::plus_stream
)
206 (* --------------------------------------------------------------------- *)
207 (* --------------------------------------------------------------------- *)
210 let do_merge minus plus_stream
=
211 let minus_tokens = rule minus
in
212 merge minus_tokens plus_stream