1%%% Copyright (C) 2017  Tomas Abrahamsson
2%%%
3%%% Author: Tomas Abrahamsson <tab@lysator.liu.se>
4%%%
5%%% This library is free software; you can redistribute it and/or
6%%% modify it under the terms of the GNU Lesser General Public
7%%% License as published by the Free Software Foundation; either
8%%% version 2.1 of the License, or (at your option) any later version.
9%%%
10%%% This library 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 GNU
13%%% Lesser General Public License for more details.
14%%%
15%%% You should have received a copy of the GNU Lesser General Public
16%%% License along with this library; if not, write to the Free Software
17%%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18%%% MA  02110-1301  USA
19
20%%% @doc Generation of message verifiers
21%%% @private
22
23-module(gpb_gen_verifiers).
24
25-export([format_verifiers_top_function/3]).
26-export([format_verifiers/3]).
27
28-include("../include/gpb.hrl").
29-include("gpb_codegen.hrl").
30-include("gpb_compile.hrl").
31
32-import(gpb_lib, [replace_term/2, replace_tree/2,
33                  splice_trees/2, repeat_clauses/2]).
34
35format_verifiers_top_function(Defs, AnRes, Opts) ->
36    case {gpb_lib:contains_messages(Defs),
37          gpb_lib:get_records_or_maps_by_opts(Opts)} of
38        {false, records} -> format_verifiers_top_no_msgs_r();
39        {false, maps}    -> format_verifiers_top_no_msgs_m();
40        {true,  _}       -> format_verifiers_top_with_msgs(Defs, AnRes, Opts)
41    end.
42
43format_verifiers_top_no_msgs_r() ->
44    [?f("-spec verify_msg(_) -> no_return().~n", []),
45     gpb_codegen:format_fn(
46       verify_msg,
47       fun(Msg) -> call_self(Msg, []) end),
48     ?f("-spec verify_msg(_,_) -> no_return().~n", []),
49     gpb_codegen:format_fn(
50       verify_msg,
51       fun(Msg,_OptsOrMsgName) ->
52               mk_type_error(not_a_known_message, Msg, [])
53       end),
54     "\n",
55     ?f("-spec verify_msg(_,_,_) -> no_return().~n", []),
56     gpb_codegen:format_fn(
57       verify_msg,
58       fun(Msg,_MsgName,_Opts) ->
59               mk_type_error(not_a_known_message, Msg, [])
60       end),
61     "\n"].
62
63format_verifiers_top_no_msgs_m() ->
64    [?f("-spec verify_msg(_,_) -> no_return().~n", []),
65     gpb_codegen:format_fn(
66       verify_msg,
67       fun(Msg, MsgName) -> call_self(Msg, MsgName, []) end),
68     ?f("-spec verify_msg(_,_,_) -> no_return().~n", []),
69     gpb_codegen:format_fn(
70       verify_msg,
71       fun(Msg, _MsgName, _Opts) ->
72               mk_type_error(not_a_known_message, Msg, [])
73       end),
74     "\n"].
75
76format_verifiers_top_with_msgs(Defs, AnRes, Opts) ->
77    Mapping = gpb_lib:get_records_or_maps_by_opts(Opts),
78    [[gpb_codegen:format_fn(
79        verify_msg,
80        fun(Msg) when tuple_size(Msg) >= 1 ->
81                verify_msg(Msg, element(1, Msg), []);
82           (X) ->
83                mk_type_error(not_a_known_message, X, [])
84        end) || Mapping == records],
85     gpb_codegen:format_fn(
86       verify_msg,
87       fun(Msg, MsgName) when is_atom(MsgName) ->
88               call_self(Msg, MsgName, []);
89          ('Msg', Opts) when tuple_size('Msg') >= 1 ->
90               call_self('Msg', element(1,'Msg'), Opts);
91          ('X', _Opts) ->
92               mk_type_error(not_a_known_message, 'X', [])
93       end,
94       [repeat_clauses('Msg', [[replace_tree('Msg', ?expr(Msg))]
95                               || Mapping == records]),
96        repeat_clauses('X', [[replace_tree('X', ?expr(X))]
97                             || Mapping == records])]),
98     gpb_codegen:format_fn(
99       verify_msg,
100       fun(Msg, MsgName, Opts) ->
101               TrUserData = proplists:get_value(user_data, Opts),
102               case MsgName of
103                   '<msg-name-match>' ->
104                       '<verify-fn>'(Msg, [MsgName], TrUserData);
105                   _ ->
106                       mk_type_error(not_a_known_message, Msg, [])
107               end
108       end,
109       [repeat_clauses(
110          '<msg-name-match>',
111          [begin
112               DefaultVerifierFn = gpb_lib:mk_fn(v_msg_, MsgName),
113               ElemPath = [MsgName],
114               MVerifierFn = case gpb_gen_translators:has_translation(
115                                    ElemPath, verify, AnRes) of
116                                 {true, Transl} -> Transl;
117                                 false -> DefaultVerifierFn
118                             end,
119               [replace_term('<msg-name-match>', MsgName),
120                replace_term('<verify-fn>', MVerifierFn),
121                replace_term('<MsgName>', MsgName)]
122           end
123           || {{msg, MsgName}, _MsgDef} <- Defs])])].
124
125format_verifiers(Defs, AnRes, Opts) ->
126    [format_msg_verifiers(Defs, AnRes, Opts),
127     format_enum_verifiers(Defs, AnRes, Opts),
128     format_type_verifiers(AnRes, Opts),
129     format_map_verifiers(AnRes, Opts),
130     format_verifier_auxiliaries(Defs, Opts)
131    ].
132
133format_msg_verifiers(Defs, AnRes, Opts) ->
134    [format_msg_verifier(MsgName, MsgDef, AnRes, Opts)
135     || {_Type, MsgName, MsgDef} <- gpb_lib:msgs_or_groups(Defs)].
136
137format_msg_verifier(MsgName, MsgDef, AnRes, Opts) ->
138    FNames = gpb_lib:get_field_names(MsgDef),
139    FVars = [gpb_lib:var_f_n(I) || I <- lists:seq(1, length(FNames))],
140    MsgVar = ?expr(M),
141    {FieldMatching, NonOptKeys} =
142        case gpb_lib:get_mapping_and_unset_by_opts(Opts) of
143            records ->
144                {gpb_lib:mapping_match(MsgName, lists:zip(FNames, FVars), Opts),
145                 FNames};
146            #maps{unset_optional=present_undefined} ->
147                {gpb_lib:mapping_match(MsgName, lists:zip(FNames, FVars), Opts),
148                 FNames};
149            #maps{unset_optional=omitted} ->
150                FMap = gpb_lib:zip_for_non_opt_fields(MsgDef, FVars),
151                {?expr('mapmatch' = 'M',
152                       [replace_tree('mapmatch', gpb_lib:map_match(FMap, Opts)),
153                        replace_tree('M', MsgVar)]),
154                 [K || {K, _} <- FMap]}
155        end,
156    ExtraneousFieldsChecks =
157        case gpb_lib:get_mapping_and_unset_by_opts(Opts) of
158            records ->
159                [];
160            #maps{unset_optional=present_undefined} ->
161                [];
162            #maps{unset_optional=omitted}=Mapping ->
163                Names = case Mapping of
164                            #maps{oneof=tuples} ->
165                                FNames;
166                            #maps{oneof=flat} ->
167                                field_names_oneofs_expanded(MsgDef)
168                        end,
169                [?expr(lists:foreach(
170                         fun('<Key>') ->
171                                 ok;
172                            (OtherKey) ->
173                                 mk_type_error({extraneous_key, OtherKey},
174                                               'M', Path)
175                         end,
176                         maps:keys('M')),
177                       [repeat_clauses('<Key>',
178                                       [[replace_term('<Key>', Key)]
179                                         || Key <- Names]),
180                        replace_tree('M', MsgVar)])]
181        end,
182
183    FnName = gpb_lib:mk_fn(v_msg_, MsgName),
184    TrUserDataVar = ?expr(TrUserData),
185    [gpb_lib:nowarn_unused_function(FnName, 3),
186     gpb_lib:nowarn_dialyzer_attr(FnName,3,Opts),
187     gpb_codegen:format_fn(
188       FnName,
189       fun('<msg-match>', '<Path>', 'TrUserData') ->
190               '<verify-fields>',
191               '<maybe-verify-no-extraneous-fields>',
192               ok;
193          ('<M>', Path, _TrUserData) when is_map('<M>') ->
194               mk_type_error(
195                 {missing_fields, 'NonOptKeys'--maps:keys('<M>'), '<MsgName>'},
196                 '<M>', Path);
197          (X, Path, _TrUserData) ->
198               mk_type_error({expected_msg,'<MsgName>'}, X, Path)
199       end,
200       [replace_tree('<msg-match>', FieldMatching),
201        replace_tree('<Path>', if MsgDef == [], ExtraneousFieldsChecks == [] ->
202                                       ?expr(_Path);
203                                  true ->
204                                       ?expr(Path)
205                               end),
206        replace_tree('TrUserData', if FNames /= [] -> TrUserDataVar;
207                                      FNames =:= [] -> ?expr(_)
208                                   end),
209        splice_trees('<verify-fields>',
210                     field_verifiers(MsgName, MsgDef, FVars, MsgVar,
211                                     TrUserDataVar,
212                                     AnRes, Opts)),
213        splice_trees('<maybe-verify-no-extraneous-fields>',
214                     ExtraneousFieldsChecks),
215        repeat_clauses('<M>',
216                       case gpb_lib:get_records_or_maps_by_opts(Opts) of
217                           records ->
218                               []; % omit this clause
219                           maps ->
220                               [[replace_tree('<M>', ?expr(M)),
221                                 replace_term('NonOptKeys', NonOptKeys)]]
222                       end),
223        replace_term('<MsgName>', MsgName)])].
224
225field_names_oneofs_expanded(MsgDef) ->
226    gpb_lib:fold_msgdef_fields(
227      fun(#?gpb_field{name=FName}, Acc) -> [FName | Acc] end,
228      [],
229      MsgDef).
230
231field_verifiers(MsgName, Fields, FVars, MsgVar, TrUserDataVar, AnRes, Opts) ->
232    [field_verifier(MsgName, Field, FVar, MsgVar, TrUserDataVar, AnRes, Opts)
233     || {Field, FVar} <- lists:zip(Fields, FVars)].
234
235field_verifier(MsgName,
236               #?gpb_field{name=FName, type=Type, occurrence=Occurrence}=Field,
237               FVar, MsgVar, TrUserDataVar, AnRes, Opts) ->
238    FVerifierFn =
239        case Type of
240            {msg,FMsgName}  -> gpb_lib:mk_fn(v_msg_, FMsgName);
241            {group,GName}   -> gpb_lib:mk_fn(v_msg_, GName);
242            {enum,EnumName} -> gpb_lib:mk_fn(v_enum_, EnumName);
243            {map,KT,VT}     -> gpb_lib:mk_fn(v_, gpb_lib:map_type_to_msg_name(
244                                                   KT,VT));
245            Type            -> gpb_lib:mk_fn(v_type_, Type)
246        end,
247    ElemPath = gpb_gen_translators:mk_elempath_elem(MsgName, Field, false),
248    FVerifierFn2 = gpb_gen_translators:find_translation(ElemPath, verify,
249                                                        AnRes, FVerifierFn),
250    BReplacements = [replace_tree('<F>', FVar),
251                     replace_term('<FName>', FName),
252                     replace_term('<Type>', Type),
253                     replace_tree('TrUserData', TrUserDataVar)],
254    Replacements = [replace_term('<verify-fn>', FVerifierFn2)
255                    | BReplacements],
256    IsMapField = case Type of
257                     {map,_,_} -> true;
258                     _ -> false
259                 end,
260    MElemPath = [MsgName, FName],
261    MTransl = gpb_gen_translators:has_translation(MElemPath, verify, AnRes),
262    HasTranslation = case MTransl of
263                         {true, _} -> true;
264                         false -> false
265                     end,
266    case Occurrence of
267        required ->
268            %% FIXME: check especially for `undefined'
269            %% and if found, error out with required_field_not_set
270            %% specifying expected type
271            ?expr('<verify-fn>'('<F>', ['<FName>' | Path], 'TrUserData'),
272                  Replacements);
273        repeated when not IsMapField, HasTranslation ->
274            {true, RFVerifierFn} = MTransl,
275            RReplacements = [replace_term('<verify-fn>', RFVerifierFn)
276                             | BReplacements],
277            case gpb_lib:get_mapping_and_unset_by_opts(Opts) of
278                records ->
279                    ?expr('<verify-fn>'('<F>', ['<FName>' | Path],
280                                        'TrUserData'),
281                          RReplacements);
282                #maps{unset_optional=present_undefined} ->
283                    ?expr('<verify-fn>'('<F>', ['<FName>' | Path],
284                                        'TrUserData'),
285                          RReplacements);
286                #maps{unset_optional=omitted} ->
287                    ?expr(case 'M' of
288                              '#{<FName> := <F>}' ->
289                                  '<verify-fn>'('<F>', ['<FName>' | Path],
290                                                'TrUserData');
291                              _ ->
292                                  ok
293                          end,
294                          [replace_tree('#{<FName> := <F>}',
295                                        gpb_lib:map_match([{FName, FVar}],
296                                                          Opts)),
297                           replace_tree('M', MsgVar) | RReplacements])
298            end;
299        repeated when not IsMapField ->
300            case gpb_lib:get_mapping_and_unset_by_opts(Opts) of
301                records ->
302                    ?expr(if is_list('<F>') ->
303                                  %% _ = [...] to avoid dialyzer error
304                                  %% "Expression produces a value of type
305                                  %% ['ok'], but this value is unmatched"
306                                  %% with the -Wunmatched_returns flag.
307                                  _ = ['<verify-fn>'(Elem, ['<FName>' | Path],
308                                                     'TrUserData')
309                                       || Elem <- '<F>'],
310                                  ok;
311                             true ->
312                                  mk_type_error(
313                                    {invalid_list_of, '<Type>'},
314                                    '<F>',
315                                    ['<FName>' | Path])
316                          end,
317                          Replacements);
318                #maps{unset_optional=present_undefined} ->
319                    ?expr(if is_list('<F>') ->
320                                  %% _ = [...] to avoid dialyzer error
321                                  %% "Expression produces a value of type
322                                  %% ['ok'], but this value is unmatched"
323                                  %% with the -Wunmatched_returns flag.
324                                  _ = ['<verify-fn>'(Elem, ['<FName>' | Path],
325                                                     'TrUserData')
326                                       || Elem <- '<F>'],
327                                  ok;
328                             true ->
329                                  mk_type_error(
330                                    {invalid_list_of, '<Type>'},
331                                    '<F>',
332                                    ['<FName>' | Path])
333                          end,
334                          Replacements);
335                #maps{unset_optional=omitted} ->
336                    ?expr(case 'M' of
337                              '#{<FName> := <F>}' ->
338                                  if is_list('<F>') ->
339                                          %% _ = [...] to avoid dialyzer error
340                                          %% "Expression produces a value of type
341                                          %% ['ok'], but this value is unmatched"
342                                          %% with the -Wunmatched_returns flag.
343                                          _ = ['<verify-fn>'(Elem, ['<FName>' | Path],
344                                                             'TrUserData')
345                                               || Elem <- '<F>'],
346                                          ok;
347                                     true ->
348                                          mk_type_error(
349                                            {invalid_list_of, '<Type>'},
350                                            '<F>',
351                                            ['<FName>' | Path])
352                                  end;
353                              _ -> ok
354                          end,
355                          [replace_tree('#{<FName> := <F>}',
356                                        gpb_lib:map_match([{FName, FVar}],
357                                                          Opts)),
358                           replace_tree('M', MsgVar) | Replacements])
359            end;
360        repeated when IsMapField ->
361            MFVerifierFn = gpb_gen_translators:find_translation(
362                             MElemPath, verify, AnRes, FVerifierFn),
363            MReplacements = [replace_term('<verify-fn>', MFVerifierFn)
364                             | BReplacements],
365            case gpb_lib:get_mapping_and_unset_by_opts(Opts) of
366                records ->
367                    ?expr('<verify-fn>'('<F>', ['<FName>' | Path],
368                                        'TrUserData'),
369                          MReplacements);
370                #maps{unset_optional=present_undefined} ->
371                    ?expr('<verify-fn>'('<F>', ['<FName>' | Path],
372                                        'TrUserData'),
373                          MReplacements);
374                #maps{unset_optional=omitted} ->
375                    ?expr(case 'M' of
376                              '#{<FName> := <F>}' ->
377                                  '<verify-fn>'('<F>', ['<FName>' | Path],
378                                                'TrUserData');
379                              _ ->
380                                  ok
381                          end,
382                          [replace_tree('#{<FName> := <F>}',
383                                        gpb_lib:map_match([{FName, FVar}],
384                                                          Opts)),
385                           replace_tree('M', MsgVar) | MReplacements])
386            end;
387        optional ->
388            case gpb_lib:get_mapping_and_unset_by_opts(Opts) of
389                records ->
390                    ?expr(if '<F>' == undefined -> ok;
391                             true -> '<verify-fn>'('<F>', ['<FName>' | Path],
392                                                   'TrUserData')
393                          end,
394                          Replacements);
395                #maps{unset_optional=present_undefined} ->
396                    ?expr(if '<F>' == undefined -> ok;
397                             true -> '<verify-fn>'('<F>', ['<FName>' | Path],
398                                                   'TrUserData')
399                          end,
400                          Replacements);
401                #maps{unset_optional=omitted} ->
402                    ?expr(case 'M' of
403                              '#{<FName> := <F>}' ->
404                                  '<verify-fn>'('<F>', ['<FName>' | Path],
405                                                'TrUserData');
406                              _ ->
407                                  ok
408                          end,
409                          [replace_tree('#{<FName> := <F>}',
410                                        gpb_lib:map_match([{FName, FVar}],
411                                                          Opts)),
412                           replace_tree('M', MsgVar) | Replacements])
413            end
414    end;
415field_verifier(MsgName, #gpb_oneof{name=FName, fields=OFields},
416               FVar, MsgVar, TrUserDataVar, AnRes, Opts) ->
417    CfElemPath = [MsgName,FName],
418    case gpb_gen_translators:has_translation(CfElemPath, verify, AnRes) of
419        {true, Transl} ->
420            case gpb_lib:get_mapping_and_unset_by_opts(Opts) of
421                records ->
422                    tr_field_oneof_present_undefined_verifier(
423                      FName, FVar, Transl, TrUserDataVar);
424                #maps{unset_optional=present_undefined} ->
425                    tr_field_oneof_present_undefined_verifier(
426                      FName, FVar, Transl, TrUserDataVar);
427                #maps{unset_optional=omitted, oneof=tuples} ->
428                    tr_field_oneof_omitted_tuples_verifier(
429                      MsgVar, FName, FVar, Transl, TrUserDataVar, Opts);
430                #maps{unset_optional=omitted, oneof=flat} ->
431                    %% translate on individual oneof fields instead
432                    field_oneof_omitted_flat_verifier(
433                      MsgName, FName, OFields,
434                      FVar, MsgVar, TrUserDataVar,
435                      AnRes, Opts)
436            end;
437        false ->
438            case gpb_lib:get_mapping_and_unset_by_opts(Opts) of
439                records ->
440                    field_oneof_present_undefined_verifier(
441                      MsgName, FName, OFields,
442                      FVar, MsgVar, TrUserDataVar,
443                      AnRes);
444                #maps{unset_optional=present_undefined} ->
445                    field_oneof_present_undefined_verifier(
446                      MsgName, FName, OFields,
447                      FVar, MsgVar, TrUserDataVar,
448                      AnRes);
449                #maps{unset_optional=omitted, oneof=tuples} ->
450                    field_oneof_omitted_tuples_verifier(
451                      MsgName, FName, OFields,
452                      FVar, MsgVar, TrUserDataVar,
453                      AnRes, Opts);
454                #maps{unset_optional=omitted, oneof=flat} ->
455                    field_oneof_omitted_flat_verifier(
456                      MsgName, FName, OFields,
457                      FVar, MsgVar, TrUserDataVar,
458                      AnRes, Opts)
459            end
460    end.
461
462field_oneof_present_undefined_verifier(MsgName, FName, OFields,
463                                       FVar, MsgVar, TrUserDataVar,
464                                       AnRes) ->
465    ?expr(
466       case '<F>' of
467           undefined ->
468               ok;
469           '<oneof-pattern>' ->
470               '<verify-fn>'('<OFVar>', ['<OFName>', '<FName>' | Path],
471                             'TrUserData');
472           _ ->
473               mk_type_error(invalid_oneof, '<F>', ['<FName>' | Path])
474       end,
475       [replace_tree('<F>', FVar),
476        replace_term('<FName>', FName),
477        repeat_clauses(
478          '<oneof-pattern>',
479          [begin
480               FVerifierFn =
481                   case Type of
482                       {msg,FMsgName} ->
483                           gpb_lib:mk_fn(v_msg_, FMsgName);
484                       {enum,EnumName} ->
485                           gpb_lib:mk_fn(v_enum_, EnumName);
486                       Type ->
487                           gpb_lib:mk_fn(v_type_, Type)
488                   end,
489               ElemPath = gpb_gen_translators:mk_elempath_elem(
490                            MsgName, F, {true, FName}),
491               FVerifierFn2 = gpb_gen_translators:find_translation(
492                                ElemPath, verify, AnRes,
493                                FVerifierFn),
494               OFVar = gpb_lib:prefix_var("O", FVar),
495               [replace_tree('M', MsgVar),
496                replace_tree('<oneof-pattern>',
497                             ?expr({'<OFName>','<OFVar>'})),
498                replace_term('<verify-fn>', FVerifierFn2),
499                replace_tree('<OFVar>', OFVar),
500                replace_term('<OFName>', OFName),
501                replace_tree('TrUserData', TrUserDataVar)]
502           end
503           || #?gpb_field{name=OFName, type=Type}=F <- OFields])]).
504
505tr_field_oneof_present_undefined_verifier(FName, FVar, Transl, TrUserDataVar) ->
506    ?expr(if '<F>' =:= undefined ->
507                  ok;
508             true ->
509                  'Tr'('<F>', ['fname' | Path], 'TrUserData')
510          end,
511          [replace_tree('<F>', FVar),
512           replace_term('Tr', Transl),
513           replace_term('fname', FName),
514           replace_tree('TrUserData', TrUserDataVar)]).
515
516
517field_oneof_omitted_tuples_verifier(MsgName, FName, OFields,
518                                    FVar, MsgVar, TrUserDataVar,
519                                    AnRes, Opts) ->
520    ?expr(
521       case 'M' of
522           '<oneof-pattern>' ->
523               '<verify-fn>'('<OFVar>', ['<OFName>', '<FName>' | Path],
524                             'TrUserData');
525           '#{<FName> := <F>}' ->
526               mk_type_error(invalid_oneof, '<F>', ['<FName>' | Path]);
527           _ ->
528               ok
529       end,
530       [replace_tree('<F>', FVar),
531        replace_term('<FName>', FName),
532        replace_tree('M', MsgVar),
533        replace_tree('#{<FName> := <F>}',
534                     gpb_lib:map_match([{FName, FVar}], Opts)),
535        repeat_clauses(
536          '<oneof-pattern>',
537          [begin
538               FVerifierFn =
539                   case Type of
540                       {msg,FMsgName} -> gpb_lib:mk_fn(v_msg_, FMsgName);
541                       {enum,EnumName} -> gpb_lib:mk_fn(v_enum_, EnumName);
542                       Type -> gpb_lib:mk_fn(v_type_, Type)
543                   end,
544               ElemPath = gpb_gen_translators:mk_elempath_elem(
545                            MsgName, F, {true, FName}),
546               FVerifierFn2 = gpb_gen_translators:find_translation(
547                                ElemPath, verify, AnRes,
548                                FVerifierFn),
549               OFVar = gpb_lib:prefix_var("O", FVar),
550               Trs1 = [replace_tree('<OFVar>', OFVar),
551                       replace_term('<OFName>', OFName)],
552               OFPat = ?expr({'<OFName>','<OFVar>'}, Trs1),
553               [replace_tree('<oneof-pattern>',
554                             gpb_lib:map_match([{FName, OFPat}], Opts)),
555                replace_term('<verify-fn>', FVerifierFn2),
556                replace_tree('TrUserData', TrUserDataVar)
557                | Trs1]
558           end
559           || #?gpb_field{name=OFName, type=Type}=F <- OFields])]).
560
561tr_field_oneof_omitted_tuples_verifier(MsgVar, FName, FVar,
562                                       Transl, TrUserDataVar, Opts) ->
563    ?expr(case 'M' of
564              '#{fname := F}' ->
565                  'Tr'('F', ['fname' | Path], 'TrUserData');
566              _ ->
567                  ok
568          end,
569          [replace_tree('M', MsgVar),
570           replace_tree('#{fname := F}', gpb_lib:map_match([{FName, FVar}],
571                                                           Opts)),
572           replace_term('Tr', Transl),
573           replace_tree('F', FVar),
574           replace_term('fname', FName),
575           replace_tree('TrUserData', TrUserDataVar)]).
576
577field_oneof_omitted_flat_verifier(MsgName, FName, OFields,
578                                  FVar, MsgVar, TrUserDataVar,
579                                  AnRes, Opts) ->
580    %% FIXME: Verify at most one oneof field is set
581    OFNames = gpb_lib:get_field_names(OFields),
582    ?expr(
583       case 'M' of
584           '#{OFName := OFVar}' ->
585               case maps:keys(maps:with('OFNames', 'M')) of
586                   [_] ->
587                       ok;
588                   'OFDups' ->
589                       mk_type_error({multiple_oneof_keys, 'OFDups', 'FName'},
590                                     'M', ['FName' | Path])
591               end,
592               'verify-fn'('OFVar', ['OFName' | Path], 'TrUserData');
593           _ ->
594               ok
595       end,
596       [replace_tree('M', MsgVar),
597        replace_term('FName', FName),
598        replace_term('OFNames', OFNames),
599        repeat_clauses(
600          '#{OFName := OFVar}',
601          [begin
602               FVerifierFn =
603                   case Type of
604                       {msg,FMsgName} -> gpb_lib:mk_fn(v_msg_, FMsgName);
605                       {enum,EnumName} -> gpb_lib:mk_fn(v_enum_, EnumName);
606                       Type -> gpb_lib:mk_fn(v_type_, Type)
607                   end,
608               ElemPath = gpb_gen_translators:mk_elempath_elem(
609                            MsgName, F, {true, FName}),
610               FVerifierFn2 = gpb_gen_translators:find_translation(
611                                ElemPath, verify, AnRes,
612                                FVerifierFn),
613               OFVar = gpb_lib:prefix_var("O", FVar),
614               Trs1 = [replace_tree('OFVar', OFVar),
615                       replace_term('OFName', OFName)],
616               [replace_tree('#{OFName := OFVar}',
617                             gpb_lib:map_match([{OFName, OFVar}], Opts)),
618                replace_tree('OFDups', gpb_lib:prefix_var("OFDups", OFVar)),
619                replace_term('verify-fn', FVerifierFn2),
620                replace_tree('TrUserData', TrUserDataVar)
621                | Trs1]
622           end
623           || #?gpb_field{name=OFName, type=Type}=F <- OFields])]).
624
625format_enum_verifiers(Defs, #anres{used_types=UsedTypes}, Opts) ->
626    [format_enum_verifier(EnumName, Def, Opts)
627     || {{enum,EnumName}, Def} <- Defs,
628        gpb_lib:smember({enum, EnumName}, UsedTypes)].
629
630format_enum_verifier(EnumName, EnumMembers, Opts) ->
631    FnName = gpb_lib:mk_fn(v_enum_, EnumName),
632    [gpb_lib:nowarn_unused_function(FnName, 3),
633     gpb_lib:nowarn_dialyzer_attr(FnName, 3, Opts),
634     gpb_codegen:format_fn(
635       FnName,
636       fun('<sym>', _Path, _TrUserData) ->
637               ok;
638          (V, Path, TrUserData) when is_integer(V) ->
639               v_type_sint32(V, Path, TrUserData);
640          (X, Path, _TrUserData) ->
641               mk_type_error({invalid_enum, '<EnumName>'}, X, Path)
642       end,
643       [repeat_clauses('<sym>', [[replace_term('<sym>', EnumSym)]
644                                 || {EnumSym, _Value} <- EnumMembers]),
645        replace_term('<EnumName>', EnumName)])].
646
647format_type_verifiers(#anres{used_types=UsedTypes}, Opts) ->
648    NeedSInt32 = (gpb_lib:smember(sint32, UsedTypes) orelse
649                  gpb_lib:any_enum_field_exists(UsedTypes)),
650    NeedBool   = gpb_lib:smember(bool, UsedTypes),
651    NeedFloat  = gpb_lib:smember(float, UsedTypes),
652    NeedDouble = gpb_lib:smember(double, UsedTypes),
653    NeedString = gpb_lib:smember(string, UsedTypes),
654    NeedBytes  = gpb_lib:smember(bytes, UsedTypes),
655    [[format_int_verifier(sint32, signed, 32, Opts) || NeedSInt32],
656     [format_int_verifier(Type, Signedness, Bits, Opts)
657      || {Type, Signedness, Bits} <- [{sint64,   signed,   64},
658                                      {int32,    signed,   32},
659                                      {int64,    signed,   64},
660                                      {uint32,   unsigned, 32},
661                                      {uint64,   unsigned, 64},
662                                      {fixed32,  unsigned, 32},
663                                      {fixed64,  unsigned, 64},
664                                      {sfixed32, signed,   32},
665                                      {sfixed64, signed,   64}],
666         gpb_lib:smember(Type, UsedTypes)],
667     [format_bool_verifier(Opts)                || NeedBool],
668     [format_float_verifier(float, Opts)        || NeedFloat],
669     [format_float_verifier(double, Opts)       || NeedDouble],
670     [format_string_verifier(Opts)              || NeedString],
671     [format_bytes_verifier(Opts)               || NeedBytes]].
672
673format_int_verifier(IntType, Signedness, NumBits, Opts) ->
674    Min = case Signedness of
675              unsigned -> 0;
676              signed   -> -(1 bsl (NumBits-1))
677          end,
678    Max = case Signedness of
679              unsigned -> 1 bsl NumBits - 1;
680              signed   -> 1 bsl (NumBits-1) - 1
681          end,
682    FnName = gpb_lib:mk_fn(v_type_, IntType),
683    [gpb_lib:nowarn_unused_function(FnName, 3),
684     gpb_lib:nowarn_dialyzer_attr(FnName, 3, Opts),
685     gpb_codegen:format_fn(
686       FnName,
687       fun(N, _Path, _TrUserData) when '<Min>' =< N, N =< '<Max>' ->
688               ok;
689          (N, Path, _TrUserData) when is_integer(N) ->
690               mk_type_error({value_out_of_range, '<details>'}, N, Path);
691          (X, Path, _TrUserData) ->
692               mk_type_error({bad_integer, '<details>'}, X, Path)
693       end,
694       [replace_term('<Min>', Min),
695        replace_term('<Max>', Max),
696        splice_trees('<details>', [erl_syntax:atom(IntType),
697                                   erl_syntax:atom(Signedness),
698                                   erl_syntax:integer(NumBits)])])].
699
700format_bool_verifier(Opts) ->
701    FnName = gpb_lib:mk_fn(v_type_, bool),
702    [gpb_lib:nowarn_unused_function(FnName, 3),
703     gpb_lib:nowarn_dialyzer_attr(FnName, 3, Opts),
704     gpb_codegen:format_fn(
705       FnName,
706       fun(false, _Path, _TrUserData) -> ok;
707          (true, _Path, _TrUserData)  -> ok;
708          (0, _Path, _TrUserData)  -> ok;
709          (1, _Path, _TrUserData)  -> ok;
710          (X, Path, _TrUserData) -> mk_type_error(bad_boolean_value, X, Path)
711       end)].
712
713format_float_verifier(FlType, Opts) ->
714    BadTypeOfValue = list_to_atom(lists:concat(["bad_", FlType, "_value"])),
715    FnName = gpb_lib:mk_fn(v_type_, FlType),
716    [gpb_lib:nowarn_unused_function(FnName, 3),
717     gpb_lib:nowarn_dialyzer_attr(FnName, 3, Opts),
718     gpb_codegen:format_fn(
719       FnName,
720       fun(N, _Path, _TrUserData) when is_float(N) -> ok;
721          %% It seems a float for the corresponding integer value is
722          %% indeed packed when doing <<Integer:32/little-float>>.
723          %% So let verify accept integers too.
724          %% When such a value is unpacked, we get a float.
725          (N, _Path, _TrUserData) when is_integer(N) -> ok;
726          (infinity, _Path, _TrUserData)    -> ok;
727          ('-infinity', _Path, _TrUserData) -> ok;
728          (nan, _Path, _TrUserData)         -> ok;
729          (X, Path, _TrUserData) ->
730               mk_type_error('<bad_x_value>', X, Path)
731       end,
732       [replace_term('<bad_x_value>', BadTypeOfValue)])].
733
734format_string_verifier(Opts) ->
735    FnName = gpb_lib:mk_fn(v_type_, string),
736    [gpb_lib:nowarn_unused_function(FnName, 3),
737     gpb_lib:nowarn_dialyzer_attr(FnName, 3, Opts),
738     gpb_codegen:format_fn(
739       FnName,
740       fun(S, Path, _TrUserData) when is_list(S); is_binary(S) ->
741               try unicode:characters_to_binary(S) of
742                   B when is_binary(B) ->
743                       ok;
744                   {error, _, _} -> %% a non-UTF-8 binary
745                       mk_type_error(bad_unicode_string, S, Path)
746               catch error:badarg ->
747                       mk_type_error(bad_unicode_string, S, Path)
748               end;
749          (X, Path, _TrUserData) ->
750               mk_type_error(bad_unicode_string, X, Path)
751       end)].
752
753format_bytes_verifier(Opts) ->
754    FnName = gpb_lib:mk_fn(v_type_, bytes),
755    [gpb_lib:nowarn_unused_function(FnName, 3),
756     gpb_lib:nowarn_dialyzer_attr(FnName, 3, Opts),
757     gpb_codegen:format_fn(
758       FnName,
759       fun(B, _Path, _TrUserData) when is_binary(B) ->
760               ok;
761          (B, _Path, _TrUserData) when is_list(B) ->
762               ok;
763          (X, Path, _TrUserData) ->
764               mk_type_error(bad_binary_value, X, Path)
765       end)].
766
767format_map_verifiers(#anres{map_types=MapTypes}=AnRes, Opts) ->
768    MapsOrTuples = gpb_lib:get_2tuples_or_maps_for_maptype_fields_by_opts(Opts),
769    [format_map_verifier(KeyType, ValueType, MapsOrTuples, AnRes, Opts)
770     || {KeyType,ValueType} <- sets:to_list(MapTypes)].
771
772format_map_verifier(KeyType, ValueType, MapsOrTuples, AnRes, Opts) ->
773    MsgName = gpb_lib:map_type_to_msg_name(KeyType, ValueType),
774    FnName = gpb_lib:mk_fn(v_, MsgName),
775    KeyVerifierFn = gpb_lib:mk_fn(v_type_, KeyType),
776    ValueVerifierFn1 = case ValueType of
777                           {msg,FMsgName}  -> gpb_lib:mk_fn(v_msg_, FMsgName);
778                           {enum,EnumName} -> gpb_lib:mk_fn(v_enum_, EnumName);
779                           Type            -> gpb_lib:mk_fn(v_type_, Type)
780                       end,
781    ElemPath = [MsgName,value],
782    ValueVerifierFn2 = gpb_gen_translators:find_translation(
783                         ElemPath, verify, AnRes,
784                         ValueVerifierFn1),
785    [gpb_lib:nowarn_unused_function(FnName, 3),
786     gpb_lib:nowarn_dialyzer_attr(FnName, 3, Opts),
787     case MapsOrTuples of
788         '2tuples' ->
789             gpb_codegen:format_fn(
790               FnName,
791               fun(KVs, Path, TrUserData) when is_list(KVs) ->
792                       [case X of
793                            {Key, Value} ->
794                                'VerifyKey'(Key, ['key' | Path], TrUserData),
795                                'VerifyValue'(Value, ['value' | Path],
796                                              TrUserData);
797                            _ ->
798                                mk_type_error(invalid_key_value_tuple, X, Path)
799                        end
800                        || X <- KVs],
801                       ok;
802                  (X, Path, _TrUserData) ->
803                       mk_type_error(invalid_list_of_key_value_tuples, X, Path)
804               end,
805               [replace_term('VerifyKey', KeyVerifierFn),
806                replace_term('VerifyValue', ValueVerifierFn2)]);
807         maps ->
808             gpb_codegen:format_fn(
809               FnName,
810               fun(M, Path, TrUserData) when is_map(M) ->
811                       [begin
812                            'VerifyKey'(Key, ['key' | Path], TrUserData),
813                            'VerifyValue'(Value, ['value' | Path],
814                                         TrUserData)
815                        end
816                        || {Key, Value} <- maps:to_list(M)],
817                       ok;
818                  (X, Path, _TrUserData) ->
819                       mk_type_error(invalid_map, X, Path)
820               end,
821               [replace_term('VerifyKey', KeyVerifierFn),
822                replace_term('VerifyValue', ValueVerifierFn2)])
823     end].
824
825format_verifier_auxiliaries(Defs, Opts) ->
826    [gpb_lib:nowarn_unused_function(mk_type_error, 3),
827     "-spec mk_type_error(_, _, list()) -> no_return().\n",
828     gpb_codegen:format_fn(
829       mk_type_error,
830       fun(Error, ValueSeen, Path) ->
831               Path2 = prettify_path(Path),
832               erlang:error({gpb_type_error,
833                             {Error, [{value, ValueSeen},{path, Path2}]}})
834       end),
835     "\n",
836     case gpb_lib:contains_messages(Defs) of
837         false ->
838             gpb_codegen:format_fn(
839               prettify_path,
840               fun([]) -> top_level end);
841         true ->
842             [gpb_lib:nowarn_unused_function(prettify_path, 1),
843              gpb_lib:nowarn_dialyzer_attr(prettify_path, 1, Opts),
844              case gpb_lib:target_has_lists_join(Opts) of
845                  true ->
846                      format_prettify_path_with_lists_join();
847                  false ->
848                      format_prettify_path_with_string_join()
849              end]
850     end].
851
852format_prettify_path_with_lists_join() ->
853    gpb_codegen:format_fn(
854      prettify_path,
855      fun([]) ->
856              top_level;
857         (PathR) ->
858              list_to_atom(
859                lists:append(
860                  lists:join(".", lists:map(fun atom_to_list/1,
861                                            lists:reverse(PathR)))))
862      end).
863
864format_prettify_path_with_string_join() ->
865    gpb_codegen:format_fn(
866      prettify_path,
867      fun([]) ->
868              top_level;
869         (PathR) ->
870              list_to_atom(string:join(lists:map(fun atom_to_list/1,
871                                                 lists:reverse(PathR)),
872                                       "."))
873      end).
874