-
Notifications
You must be signed in to change notification settings - Fork 125
/
pass_CodingDirectives.ml
352 lines (315 loc) · 10.4 KB
/
pass_CodingDirectives.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
(*
Copyright © 2011 MLstate
This file is part of OPA.
OPA is free software: you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License, version 3, as published by
the Free Software Foundation.
OPA is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
more details.
You should have received a copy of the GNU Affero General Public License
along with OPA. If not, see <http://www.gnu.org/licenses/>.
*)
(* depends *)
module Format = BaseFormat
module List = BaseList
(* shorthands *)
module Q = QmlAst
(* *)
(*
Separation.
We store all the identifiers containing a corresponding coding directive.
*)
type deprecated_argument =
| Use of string
| Hint of string
let pp_deprecated_argument fmt = function
| Use use -> Format.fprintf fmt "Use @{<bright>%s@} instead" use
| Hint hint -> Format.pp_print_string fmt hint
type deprecated_infos = {
argument : deprecated_argument ;
pos : FilePos.pos ;
}
type todo_infos = FilePos.pos
type env = {
deprecated : deprecated_infos IdentMap.t ;
todo : todo_infos IdentMap.t ;
}
module S =
struct
type t = env
let pass = "pass_CodingDirectives"
let pp_map pp fmt map =
let iter ident info =
Format.fprintf fmt "%s : %a@\n" (Ident.original_name ident) pp info
in
IdentMap.iter iter map
let pp_argument fmt = function
| Use s -> Format.fprintf fmt "use=%S" s
| Hint s -> Format.fprintf fmt "hint=%S" s
let pp_deprecated fmt infos =
Format.fprintf fmt "%a, %a" FilePos.pp infos.pos pp_argument infos.argument
let pp_todo = FilePos.pp
let pp fmt env =
Format.fprintf fmt "DEPRECATED@\n@[<2>" ;
pp_map pp_deprecated fmt env.deprecated ;
Format.fprintf fmt "@]TODO@\n@[<2>" ;
pp_map pp_todo fmt env.todo ;
Format.fprintf fmt "@]";
()
end
module R = ObjectFiles.Make(S)
(*
Warnings
*)
let wclass =
let doc = "Coding directives warnings" in
let name = "coding" in
WarningClass.create ~name ~doc ~err:false ~enable:true ()
let wdeprecated =
let doc = "deprecated constructions" in
let name = "deprecated" in
WarningClass.create ~parent:wclass ~name ~doc ~err:false ~enable:true ()
let wtodo =
let doc = "unimplemented" in
let name = "todo" in
WarningClass.create ~parent:wclass ~name ~doc ~err:false ~enable:true ()
let warning_set =
WarningClass.Set.create_from_list [
wclass ;
wdeprecated ;
wtodo ;
]
(*
Code processing
*)
type 'infos packaged_infos = {
infos : 'infos ;
package : ObjectFiles.package ;
}
let process_code _gamma annotmap code =
(* structure to store everything, previous and current packages *)
let deprecated : deprecated_infos packaged_infos IdentTable.t = IdentTable.create 64 in
let todo : todo_infos packaged_infos IdentTable.t = IdentTable.create 64 in
(* structure to store only the current package *)
let this_deprecated : deprecated_infos IdentTable.t = IdentTable.create 64 in
let this_todo : todo_infos IdentTable.t = IdentTable.create 64 in
(* separation : load *)
let iter package env =
let iter table ident infos =
let p_infos = {
infos ;
package ;
} in
IdentTable.add table ident p_infos
in
let table table = IdentMap.iter (iter table) in
table deprecated env.deprecated ;
table todo env.todo ;
()
in
R.iter_with_name ~packages:true ~deep:false iter ;
(* process the code *)
(* 1/2 : collect directives, and remove them *)
let annotmap, code =
let package = ObjectFiles.get_current_package () in
let foldmap_expr ident annotmap expr =
let foldmap annotmap = function
| Q.Directive (label, `deprecated, args, _) -> (
let bad_args ?(label=label) () =
let context = QmlError.Context.label label in
QmlError.error context (
"Invalid arguments for directive @{<bright>@@deprecated@}@\n"^^
"@[<2>@{<bright>Hint@}:@\n"^^
"argument should be of type @{<bright>%s@},@ and "^^
"strings arguments should be literals only@]"
)
Opacapi.Types.Deprecated.argument
in
match args with
| [ hint ; sub_expr ] -> (
let argument =
match hint with
| Q.Record (label, [ field, Q.Const (_, Q.String literal)]) -> (
match field with
| "use" -> Use literal
| "hint" -> Hint literal
| _ -> bad_args ~label ()
)
| _ ->
let label = QmlAst.Label.expr hint in
bad_args ~label ()
in
let pos = QmlAst.Pos.expr expr in
let infos = {
argument ;
pos ;
} in
let pinfos = {
infos ;
package ;
} in
let () =
IdentTable.add deprecated ident pinfos ;
IdentTable.add this_deprecated ident infos ;
in
(*
Support for EI:
get the tsc of the @deprecated directive,
and put it on the sub expression
*)
let annotmap =
let sub_label = QmlAst.Label.expr sub_expr in
let tsc_opt = QmlAnnotMap.find_tsc_opt_label label annotmap in
let annotmap = QmlAnnotMap.add_tsc_opt_label sub_label tsc_opt annotmap in
annotmap
in
annotmap, sub_expr
)
| _ ->
(*
QmlDirective ensure the correct typing of this directive
*)
assert false
)
| Q.Directive (label, `todo, args, tys) ->
let infos = QmlAst.Pos.expr expr in
let pinfos = {
infos ;
package ;
} in
let () =
IdentTable.add todo ident pinfos ;
IdentTable.add this_todo ident infos ;
in
let expr = Q.Directive (label, `fail, args, tys) in
annotmap, expr
| expr -> annotmap, expr
in
QmlAstWalk.Expr.foldmap foldmap annotmap expr
in
let code_elt annotmap elt =
let val_ ~rec_ label vals =
let foldmap annotmap ((ident, expr) as tuple) =
let annotmap, fexpr = foldmap_expr ident annotmap expr in
annotmap,
if expr == fexpr
then
tuple
else
(ident, fexpr)
in
let annotmap, fvals = List.fold_left_map_stable foldmap annotmap vals in
annotmap,
if vals == fvals
then
elt
else
if rec_
then
Q.NewValRec (label, fvals)
else
Q.NewVal (label, fvals)
in
match elt with
| Q.NewVal (label, vals) -> val_ ~rec_:false label vals
| Q.NewValRec (label, vals) -> val_ ~rec_:true label vals
(*
Coding directive are not authorized on db values
*)
| Q.NewDbValue (_, dbdef) ->
let fmap () = function
| Q.Directive (label, ((`deprecated | `todo) as variant), _, _) ->
let context = QmlError.Context.label label in
QmlError.error context (
"The directive @{<bright>%s@} is not authorized in a database definition."
)
(QmlPrint.directive variant)
| expr -> (), expr
in
let (), _ = QmlAst.Db.foldmap_expr fmap () dbdef in
annotmap, elt
(*
Other declaration are kept as is
*)
| elt -> annotmap, elt
in
List.fold_left_map_stable code_elt annotmap code
in
(* 2/2 : produces warnings *)
let () =
let iter (toplevel_ident, expr) =
let iter tra = function
| Q.Directive (_, `module_, _, _) ->
(* This is for avoiding a warning in case of a module explosion *)
()
| Q.Ident (label, ident) ->
(* deprecated *)
Return.set_checkpoint (
fun break ->
let { infos ; package } =
try
IdentTable.find deprecated ident
with Not_found -> Return.return break ()
in
let pos = Annot.pos label in
let context = QmlError.Context.pos pos in
QmlError.warning ~wclass:wdeprecated context (
"This is a @{<bright>deprecated@} construction, as precised in:@\n"^^
"Package:%s,@\n"^^
"%a@\n"^^
"@[<2>@{<bright>Hint@}:@\n"^^
"%a@]"
)
(fst package)
FilePos.pp infos.pos
pp_deprecated_argument infos.argument
);
(* todo *)
Return.set_checkpoint (
fun break ->
let { infos ; package } =
try
IdentTable.find todo ident
with Not_found -> Return.return break ()
in
let pos = Annot.pos label in
let context = QmlError.Context.pos pos in
QmlError.warning ~wclass:wtodo context (
"This construction is @{<bright>not implemented@}, as precised in:@\n"^^
"Package:%s,@\n"^^
"%a@\n"^^
"This will fail at runtime."
)
(fst package)
FilePos.pp infos
)
| e -> tra e
in
(*
Contamination rule:
a deprecated function is authorized to use a deprecated construction
*)
if not (IdentTable.mem deprecated toplevel_ident)
then
QmlAstWalk.Expr.traverse_iter iter expr
in
List.iter (QmlAstWalk.Top.iter_name_expr iter) code
in
(* separation : store *)
let () =
let env =
let map table =
IdentTable.fold IdentMap.add table IdentMap.empty
in
let deprecated = map this_deprecated in
let todo = map this_todo in
{
deprecated ;
todo ;
}
in
R.save env
in
annotmap, code