123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218(**************************************************************************)(* *)(* OCaml *)(* *)(* Alain Frisch, LexiFi *)(* *)(* Copyright 2012 Institut National de Recherche en Informatique et *)(* en Automatique. *)(* *)(* All rights reserved. This file is distributed under the terms of *)(* the GNU Lesser General Public License version 2.1, with the *)(* special exception on linking described in the file LICENSE. *)(* *)(**************************************************************************)openAsttypesopenParsetreeopenAst_helpermoduleAttribute_table=Hashtbl.Make(structtypet=stringwith_loclethash:t->int=Hashtbl.hashletequal:t->t->bool=(=)end)letunused_attrs=Attribute_table.create128letmark_usedt=Attribute_table.removeunused_attrst(* [attr_order] is used to issue unused attribute warnings in the order the
attributes occur in the file rather than the random order of the hash table
*)letattr_ordera1a2=Location.comparea1.loca2.locletcompiler_stops_before_attributes_consumed()=letstops_before_lambda=(* Clflags.stop_after is not a flag that Merlin consumes, so default to the None
behavior *)false(*
match !Clflags.stop_after with
| None -> false
| Some pass -> Clflags.Compiler_pass.(compare pass Lambda) < 0
*)instops_before_lambda||!Clflags.print_typesletunchecked_zero_alloc_attributes=Attribute_table.create1letmark_zero_alloc_attribute_checkedtxtloc=Attribute_table.removeunchecked_zero_alloc_attributes{txt;loc}letregister_zero_alloc_attributeattr=Attribute_table.replaceunchecked_zero_alloc_attributesattr(Warnings.backup())letwarn_unchecked_zero_alloc_attribute()=(* When using -i, attributes will not have been translated, so we can't
warn about missing ones. *)if!Clflags.print_typesthen()elseletkeys=List.of_seq(Attribute_table.to_seq_keysunchecked_zero_alloc_attributes)inletkeys=List.sortattr_orderkeysin(* Treatment of warnings is similar to [Typecore.force_delayed_checks]. *)letw_old=Warnings.backup()inList.iter(funsloc->letw=Attribute_table.findunchecked_zero_alloc_attributesslocinWarnings.restorew;Location.prerr_warningsloc.loc(Warnings.Unchecked_zero_alloc_attribute))keys;Warnings.restorew_oldletwarn_unused()=letkeys=List.of_seq(Attribute_table.to_seq_keysunused_attrs)inAttribute_table.clearunused_attrs;ifnot(compiler_stops_before_attributes_consumed())thenletkeys=List.sortattr_orderkeysinList.iter(funsloc->Location.prerr_warningsloc.loc(Warnings.Misplaced_attributesloc.txt))keys(* These are the attributes that are tracked in the builtin_attrs table for
misplaced attribute warnings. *)letbuiltin_attrs=["inline";"atomic";"inlined";"specialise";"specialised";"tailcall";"unrolled";"error";"alert";"deprecated";"deprecated_mutable";"warning";"warnerror";"ppwarning";"explicit_arity";"warn_on_literal_pattern";"immediate";"immediate64";"boxed";"unboxed";"principal";"noprincipal";"nolabels";"flambda_oclassic";"flambda_o3";"flambda_o4";"afl_inst_ratio";"local_opt";"curry";"extension.curry"(* [local] and [global] are never used and always trigger warning 53 *);"global";"extension.global";"local";"extension.local";"nontail";"extension.nontail";"tail";"extension.tail";"noalloc";"zero_alloc";"untagged";"unpacked";"poll";"loop";"tail_mod_cons";"unaliasable";"builtin";"no_effects";"no_coeffects";"only_generative_effects";"error_message";"layout_poly";"or_null";"or_null_reexport";"no_recursive_modalities";"jane.non_erasable.instances";"cold";"regalloc";"regalloc_param";"implicit_kind";"flatten_floats";"represent_as_float_array"]letbuiltin_attrs=lettbl=Hashtbl.create128inList.iter(funattr->Hashtbl.addtblattr())builtin_attrs;tblletdrop_ocaml_attr_prefixs=letlen=String.lengthsinifString.starts_with~prefix:"ocaml."s&&len>6thenString.subs6(len-6)elsesletis_builtin_attrs=Hashtbl.membuiltin_attrs(drop_ocaml_attr_prefixs)typecurrent_phase=Parser|Invariant_checkletregister_attrcurrent_phasename=matchcurrent_phasewith|Parserwhen!Clflags.all_ppx<>[]->()|Parser|Invariant_check->ifis_builtin_attrname.txtthenAttribute_table.replaceunused_attrsname()letident_of_payload=function|PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_ident{txt=Lidentid}},_)}]->Someid|_->Noneletstring_of_cst=function|Pconst_string(s,_,_)->Somes|_->Noneletint_of_cst=function|Pconst_integer(i,None)->Some(int_of_stringi)|_->Noneletstring_of_payload=function|PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_constantc},_)}]->string_of_cstc|_->Noneletint_of_payload=function|PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_constantc},_)}]->int_of_cstc|_->Noneletstring_of_opt_payloadp=matchstring_of_payloadpwith|Somes->s|None->""moduleStyle=Misc.Styleleterror_of_extensionext=letsubmessage_frommain_locmain_txt=function|{pstr_desc=Pstr_extension(({txt=("ocaml.error"|"error");loc},p),_)}->beginmatchpwith|PStr([{pstr_desc=Pstr_eval({pexp_desc=Pexp_constant(Pconst_string(msg,_,_))},_)}])->Location.msg~loc"%a"Format_doc.pp_print_textmsg|_->Location.msg~loc"Invalid syntax for sub-message of extension %a."Style.inline_codemain_txtend|{pstr_desc=Pstr_extension(({txt;loc},_),_)}->Location.msg~loc"Uninterpreted extension '%a'."Style.inline_codetxt|_->Location.msg~loc:main_loc"Invalid syntax for sub-message of extension %a."Style.inline_codemain_txtinmatchextwith|({txt=("ocaml.error"|"error")astxt;loc},p)->beginmatchpwith|PStr[]->raiseLocation.Already_displayed_error|PStr({pstr_desc=Pstr_eval({pexp_desc=Pexp_constant(Pconst_string(msg,_,_))},_)}::inner)->letsub=List.map(submessage_fromloctxt)innerinLocation.error_of_printer~loc~subFormat_doc.pp_print_textmsg|_->Location.errorf~loc"Invalid syntax for extension '%s'."txtend|({txt="call_pos";loc},_)->Location.errorf~loc"[%%call_pos] can only exist as the type of a labelled argument"|({txt;loc},_)->Location.errorf~loc"Uninterpreted extension '%s'."txtletattr_equals_builtin{attr_name={txt;_};_}s=(* Check for attribute s or ocaml.s. Avoid allocating a fresh string. *)txt=s||(String.lengthtxt=6+String.lengths&&String.starts_with~prefix:"ocaml."txt&&String.ends_with~suffix:stxt)letmark_alert_useda=ifattr_equals_builtina"deprecated"||attr_equals_builtina"alert"thenmark_useda.attr_nameletmark_alerts_usedl=List.itermark_alert_usedlletmark_warn_on_literal_pattern_usedl=List.iter(funa->ifattr_equals_builtina"warn_on_literal_pattern"thenmark_useda.attr_name)lletmark_deprecated_mutable_usedl=List.iter(funa->ifattr_equals_builtina"deprecated_mutable"thenmark_useda.attr_name)lletmark_payload_attrs_usedpayload=letiter={Ast_iterator.default_iteratorwithattribute=funselfa->mark_useda.attr_name;Ast_iterator.default_iterator.attributeselfa}initer.payloaditerpayloadletkind_and_message=function|PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_apply({pexp_desc=Pexp_ident{txt=Longident.Lidentid}},[Nolabel,{pexp_desc=Pexp_constant(Pconst_string(s,_,_))}])},_)}]->Some(id,s)|PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_ident{txt=Longident.Lidentid}},_)}]->Some(id,"")|_->Noneletcats1s2=ifs2=""thens1elses1^"\n"^s2letalert_attrx=ifattr_equals_builtinx"deprecated"thenSome(x,"deprecated",string_of_opt_payloadx.attr_payload)elseifattr_equals_builtinx"alert"thenbeginmatchkind_and_messagex.attr_payloadwith|Some(kind,message)->Some(x,kind,message)|None->None(* note: bad payloads detected by warning_attribute *)endelseNoneletalert_attrsl=List.filter_mapalert_attrlletalerts_of_attrsl=List.fold_left(funacc(_,kind,message)->letupd=function|None|Some""->Somemessage|Somes->Some(catsmessage)inMisc.Stdlib.String.Map.updatekindupdacc)Misc.Stdlib.String.Map.empty(alert_attrsl)letcheck_alertslocattrss=Misc.Stdlib.String.Map.iter(funkindmessage->Location.alertloc~kind(catsmessage))(alerts_of_attrsattrs)letcheck_alerts_inclusion~def~uselocattrs1attrs2s=letm2=alerts_of_attrsattrs2inMisc.Stdlib.String.Map.iter(funkindmsg->ifnot(Misc.Stdlib.String.Map.memkindm2)thenLocation.alert~def~use~kindloc(catsmsg))(alerts_of_attrsattrs1)letrecdeprecated_mutable_of_attrs=function|[]->None|attr::_whenattr_equals_builtinattr"deprecated_mutable"->Some(string_of_opt_payloadattr.attr_payload)|_::tl->deprecated_mutable_of_attrstlletcheck_deprecated_mutablelocattrss=matchdeprecated_mutable_of_attrsattrswith|None->()|Sometxt->Location.deprecatedloc(Printf.sprintf"mutating field %s"(catstxt))letcheck_deprecated_mutable_inclusion~def~uselocattrs1attrs2s=matchdeprecated_mutable_of_attrsattrs1,deprecated_mutable_of_attrsattrs2with|None,_|Some_,Some_->()|Sometxt,None->Location.deprecated~def~useloc(Printf.sprintf"mutating field %s"(catstxt))letrecattrs_of_sig_items=function|{psig_desc=Psig_attributea}::tl->a::attrs_of_sig_itemstl|_->[]letalerts_of_sig~mark{psg_items;_}=leta=attrs_of_sig_itemspsg_itemsinifmarkthenmark_alerts_useda;alerts_of_attrsaletrecattrs_of_str=function|{pstr_desc=Pstr_attributea}::tl->a::attrs_of_strtl|_->[]letalerts_of_str~markstr=leta=attrs_of_strstrinifmarkthenmark_alerts_useda;alerts_of_attrsaletwarn_payloadloctxtmsg=Location.prerr_warningloc(Warnings.Attribute_payload(txt,msg))letwarning_attribute?(ppwarning=true)=letprocesslocnameerrflagpayload=mark_usedname;matchstring_of_payloadpayloadwith|Somes->begintryOption.iter(Location.prerr_alertloc)(Warnings.parse_optionserrflags)withArg.Badmsg->warn_payloadlocname.txtmsgend|None->warn_payloadlocname.txt"A single string literal is expected"inletprocess_alertlocname=function|PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_constant(Pconst_string(s,_,_))},_)}]->beginmark_usedname;tryWarnings.parse_alert_optionswithArg.Badmsg->warn_payloadlocname.txtmsgend|k->matchkind_and_messagekwith|Some("all",_)->warn_payloadlocname.txt"The alert name 'all' is reserved"|Some_->(* Do [mark_used] in the [Some] case only if Warning 53 is
disabled. Later, they will be marked used (provided they are in a
valid place) in [compile_common], when they are extracted to be
persisted inside the [.cmi] file. *)ifnot(Warnings.is_active(Misplaced_attribute""))thenmark_usedname|None->begin(* Do [mark_used] in the [None] case, which is just malformed and
covered by the "Invalid payload" warning. *)mark_usedname;warn_payloadlocname.txt"Invalid payload"endinfun({attr_name;attr_loc;attr_payload}asattr)->ifattr_equals_builtinattr"warning"thenprocessattr_locattr_namefalseattr_payloadelseifattr_equals_builtinattr"warnerror"thenprocessattr_locattr_nametrueattr_payloadelseifattr_equals_builtinattr"alert"thenprocess_alertattr_locattr_nameattr_payloadelseifppwarning&&attr_equals_builtinattr"ppwarning"thenbeginmatchattr_payloadwith|PStr[{pstr_desc=Pstr_eval({pexp_desc=Pexp_constant(Pconst_string(s,_,_))},_);pstr_loc}]->(mark_usedattr_name;Location.prerr_warningpstr_loc(Warnings.Preprocessors))|_->(mark_usedattr_name;warn_payloadattr_locattr_name.txt"A single string literal is expected")endletwarning_scope?ppwarningattrsf=letprev=Warnings.backup()intryList.iter(warning_attribute?ppwarning)(List.revattrs);letret=f()inWarnings.restoreprev;retwithexn->Warnings.restoreprev;raiseexnlethas_attributenmattrs=List.exists(funa->ifattr_equals_builtinanmthen(mark_useda.attr_name;true)elsefalse)attrstypeattr_action=Mark_used_only|Returnletselect_attributesactionsattrs=List.filter(funa->List.exists(fun(nm,action)->attr_equals_builtinanm&&beginmark_useda.attr_name;action=Returnend)actions)attrsletselect_attributepattributes=letattributes=select_attributespattributesinletattr=matchattributeswith|[]->None|[attr]->Someattr|attr::{Parsetree.attr_name={txt;loc};_}::_->Location.prerr_warningloc(Warnings.Duplicated_attributetxt);Someattrinattrletwhen_attribute_isnmsattr~f=ifList.memattr.attr_name.txtnmsthenbeginmark_usedattr.attr_name;f()endtypejkind_attribute=|Immediate64|Immediateletjkind_attribute_of_string=function|"ocaml.immediate64"|"immediate64"->SomeImmediate64|"ocaml.immediate"|"immediate"->SomeImmediate|_->Noneletjkind_attribute_to_string=function|Immediate64->"immediate64"|Immediate->"immediate"letjkindattrs=letjkind=List.find_map(funa->matchjkind_attribute_of_stringa.attr_name.txtwith|Someattr->Some(a,attr)|None->None)attrsinmatchjkindwith|None->None|Some(a,l)->mark_useda.attr_name;Some(Location.mklocla.attr_loc)letwarn_on_literal_patternattrs=has_attribute"warn_on_literal_pattern"attrsletexplicit_arityattrs=has_attribute"explicit_arity"attrs(* The "ocaml.boxed (default)" and "ocaml.unboxed (default)"
attributes cannot be input by the user, they are added by the
compiler when applying the default setting. This is done to record
in the .cmi the default used by the compiler when compiling the
source file because the default can change between compiler
invocations. *)lethas_unboxedattrs=has_attribute"unboxed"attrslethas_boxedattrs=has_attribute"boxed"attrslethas_flatten_floatsattrs=has_attribute"flatten_floats"attrslethas_represent_as_float_arrayattrs=has_attribute"represent_as_float_array"attrslethas_unsafe_allow_any_mode_crossingattrs=has_attribute"unsafe_allow_any_mode_crossing"attrsletparse_empty_payloadattr=matchattr.attr_payloadwith|PStr[]->Some()|_->warn_payloadattr.attr_locattr.attr_name.txt"No attribute payload was expected";Noneletparse_int_payloadattr=matchint_of_payloadattr.attr_payloadwith|Somei->Somei|None->warn_payloadattr.attr_locattr.attr_name.txt"A constant payload of type int was expected";None(* merlin-jst: See {comments} below
let parse_ident_payload attr =
match ident_of_payload attr.attr_payload with
| Some i -> Some i
| None ->
warn_payload attr.attr_loc attr.attr_name.txt
"A constant payload of type ident was expected";
None
*)letclflags_attribute_without_payloadattr~nameclflags_ref=when_attribute_is[name;"ocaml."^name]attr~f:(fun()->matchparse_empty_payloadattrwith|Some()->clflags_ref:=true|None->())letclflags_attribute_without_payload'attr~name~f=when_attribute_is[name;"ocaml."^name]attr~f:(fun()->Option.iterf(parse_empty_payloadattr))letclflags_attribute_with_int_payloadattr~nameclflags_ref=when_attribute_is[name;"ocaml."^name]attr~f:(fun()->matchparse_int_payloadattrwith|Somei->clflags_ref:=i|None->())letprincipal_attributeattr=clflags_attribute_without_payloadattr~name:"principal"Clflags.principalletnoprincipal_attributeattr=clflags_attribute_without_payload'attr~name:"noprincipal"~f:(fun()->Clflags.principal:=false)letnolabels_attributeattr=clflags_attribute_without_payloadattr~name:"nolabels"Clflags.classic(* merlin-jst: {comments} The following functions check runtime configurations from
[ocaml-flambda/flambda-backend] that aren't upstream, so (1) Merlin doesn't need to
care about them, but (2) we don't want to bother to bring in all the stuff they depend
on (e.g., [Config.flambda], [Clflags.set_oclassic], etc.). Thus, we comment them out
silencing some unused variable warnings first. *)let_=clflags_attribute_with_int_payload(* merlin-jst: See {comments} above
let flambda_oclassic_attribute attr =
clflags_attribute_without_payload' attr
~name:"flambda_oclassic"
~f:(fun () ->
if Config.flambda || Config.flambda2 then Clflags.set_oclassic ())
let flambda_o3_attribute attr =
clflags_attribute_without_payload' attr
~name:"flambda_o3"
~f:(fun () -> if Config.flambda || Config.flambda2 then Clflags.set_o3 ())
let flambda_o4_attribute attr =
clflags_attribute_without_payload' attr
~name:"flambda_o4"
~f:(fun () -> if Config.flambda || Config.flambda2 then Clflags.set_o4 ())
let llvm_backend_attribute attr =
clflags_attribute_without_payload' attr
~name:"llvm_backend"
~f:(fun () -> Clflags.llvm_backend := true)
let inline_attribute attr =
when_attribute_is ["inline"; "ocaml.inline"] attr ~f:(fun () ->
let err_msg =
"Either specify an integer, or the form accepted by '-inline' in quotes"
in
match string_of_payload attr.attr_payload with
| Some s ->
Clflags.Float_arg_helper.parse s err_msg Clflags.inline_threshold
| None ->
match int_of_payload attr.attr_payload with
| Some i ->
let s = string_of_int i in
Clflags.Float_arg_helper.parse s err_msg Clflags.inline_threshold
| None -> warn_payload attr.attr_loc attr.attr_name.txt err_msg)
let parse_attribute_with_ident_payload attr ~name ~f =
when_attribute_is [name; "ocaml." ^ name] attr ~f:(fun () ->
match parse_ident_payload attr with
| Some i -> f i
| None -> ())
let zero_alloc_attribute ~in_signature (attr : Parsetree.attribute) =
let module A = Zero_alloc_annotations in
let msg =
if in_signature then
"Only 'all' and 'all_opt' are supported"
else
"Only 'all', 'all_opt', 'check', 'check_opt', 'check_all', and 'check_none' are supported"
in
let warn () =
warn_payload attr.attr_loc attr.attr_name.txt msg
in
let set_if_not_in_sig r v =
if not in_signature then
r := v
else
warn ()
in
parse_attribute_with_ident_payload attr
~name:"zero_alloc" ~f:(function
| "check" -> set_if_not_in_sig Clflags.zero_alloc_check A.Check.Check_default
| "check_opt" -> set_if_not_in_sig Clflags.zero_alloc_check A.Check.Check_opt_only
| "check_all" -> set_if_not_in_sig Clflags.zero_alloc_check A.Check.Check_all
| "check_none" -> set_if_not_in_sig Clflags.zero_alloc_check A.Check.No_check
| "all" -> Clflags.zero_alloc_assert := A.Assert.Assert_all
| "all_opt" -> Clflags.zero_alloc_assert := A.Assert.Assert_all_opt
| _ ->
warn ())
*)letattribute_with_ignored_payloadnameattr=when_attribute_is[name;"ocaml."^name]attr~f:(fun()->())letunsafe_allow_any_mode_crossing_attribute=attribute_with_ignored_payload"unsafe_allow_any_mode_crossing"(*
let afl_inst_ratio_attribute attr =
clflags_attribute_with_int_payload attr
~name:"afl_inst_ratio" Clflags.afl_inst_ratio
*)letparse_standard_interface_attributesattr=warning_attributeattr;principal_attributeattr;noprincipal_attributeattr;nolabels_attributeattr;(* merlin-jst: See {comments} above
zero_alloc_attribute ~in_signature:true attr;
*)unsafe_allow_any_mode_crossing_attributeattrletparse_standard_implementation_attributesattr=warning_attributeattr;principal_attributeattr;noprincipal_attributeattr;nolabels_attributeattr;(* merlin-jst: See {comments} above
inline_attribute attr;
afl_inst_ratio_attribute attr;
flambda_o3_attribute attr;
flambda_o4_attribute attr;
flambda_oclassic_attribute attr;
zero_alloc_attribute attr
*)unsafe_allow_any_mode_crossing_attributeattr(*
llvm_backend_attribute attr
*)lethas_local_optattrs=has_attribute"local_opt"attrslethas_layout_polyattrs=has_attribute"layout_poly"attrsletcurry_attr_name="extension.curry"lethas_curryattrs=has_attributecurry_attr_nameattrs||has_attribute"curry"attrslethas_or_nullattrs=has_attribute"or_null"attrslethas_or_null_reexportattrs=has_attribute"or_null_reexport"attrslethas_magic_staged_modesattrs=has_attribute"magic_staged_modes"attrsletcurry_attrloc=Ast_helper.Attr.mk~loc:Location.none(Location.mkloccurry_attr_nameloc)(PStr[]);;lettailcallattr=lethas_nontail=has_attribute"nontail"attrinlettail_attrs=select_attributes["tail",Return]attrinmatchhas_nontail,tail_attrswith|true,(_::_)->Error`Conflict|_,(_::_::_)->Error`Conflict|false,[]->OkNone|true,[]->Ok(Some`Nontail)|false,[{attr_payload=PStr[]}]->Ok(Some`Tail)|false,[t]->matchident_of_payloadt.attr_payloadwith|Some"hint"->Ok(Some`Tail_if_possible)|_->Location.prerr_warningt.attr_loc(Warnings.Attribute_payload(t.attr_name.txt,"Only 'hint' is supported"));Ok(Some`Tail_if_possible)leterror_message_attrl=letinnerx=matchx.attr_name.txtwith|"ocaml.error_message"|"error_message"->beginmatchstring_of_payloadx.attr_payloadwith|Some_asr->mark_usedx.attr_name;r|None->warn_payloadx.attr_locx.attr_name.txt"error_message attribute expects a string argument";Noneend|_->NoneinList.find_mapinnerlletget_implicit_jkind_attrx=letextract_var_and_jkindtyp=matchtyp.ptyp_descwith|Ptyp_var(var_name,Somejkind_annot)->var_name,jkind_annot|_->raiseExitinletparse_implicit_jkind_payload=function|PTyptyp->beginmatchtyp.ptyp_descwith|Ptyp_var(_,Some_)->(* Single variable: ('a : immediate) *)[extract_var_and_jkindtyp]|Ptyp_tupletyps->(* Multiple variables: ('a : immediate) * ('b : int) *)List.map(fun(label_opt,typ)->matchlabel_optwith|None->extract_var_and_jkindtyp|Some_->raiseExit)typs|_->raiseExitend|_->raiseExitinmatchx.attr_name.txtwith|"ocaml.implicit_kind"|"implicit_kind"->beginmatchparse_implicit_jkind_payloadx.attr_payloadwith|pairs->mark_usedx.attr_name;pairs|exceptionExit->warn_payloadx.attr_locx.attr_name.txt"implicit_kind attribute expects: \
('var1 : jkind1) * ('var2 : jkind2) ...";[]end|_->[]typezero_alloc_check={strict:bool;opt:bool;arity:int;loc:Location.t;custom_error_msg:stringoption;}typezero_alloc_assume={strict:bool;never_returns_normally:bool;never_raises:bool;arity:int;loc:Location.t;}typezero_alloc_attribute=|Default_zero_alloc|Ignore_assert_all|Checkofzero_alloc_check|Assumeofzero_alloc_assumeletis_zero_alloc_check_enabled~opt=match!Clflags.zero_alloc_checkwith|No_check->false|Check_all->true|Check_default->notopt|Check_opt_only->optletis_zero_alloc_attribute=["zero_alloc",Return]letget_payloadget_from_exp=letopenParsetreeinfunction|PStr[{pstr_desc=Pstr_eval(exp,[])}]->get_from_expexp|_->Result.Error()letget_optional_payloadget_from_exp=letopenParsetreeinfunction|PStr[]->Result.OkNone|other->Result.mapOption.some(get_payloadget_from_expother)letget_int_from_exp=letopenParsetreeinfunction|{pexp_desc=Pexp_constant(Pconst_integer(s,None))}->beginmatchMisc.Int_literal_converter.intswith|n->Result.Okn|exception(Failure_)->Result.Error()end|_->Result.Error()letget_construct_from_exp=letopenParsetreeinfunction|{pexp_desc=Pexp_construct({txt=Longident.Lidentconstr},None)}->Result.Okconstr|_->Result.Error()letget_bool_from_expexp=Result.bind(get_construct_from_expexp)(function|"true"->Result.Oktrue|"false"->Result.Okfalse|_->Result.Error())letget_int_payload=get_payloadget_int_from_expletget_optional_bool_payload=get_optional_payloadget_bool_from_expletget_id_from_exp=letopenParsetreeinfunction|{pexp_desc=Pexp_ident{txt=Longident.Lidentid}}->Result.Okid|_->Result.Error()typeparsed_payload=|Ident|Const_int|Const_stringletget_id_or_constant_from_exp=letopenParsetreeinfunction|{pexp_desc=Pexp_ident{txt=Longident.Lidentid}}->Result.Ok(Ident,id)|{pexp_desc=Pexp_constant(Pconst_integer(s,None))}->Result.Ok(Const_int,s)|{pexp_desc=Pexp_constant(Pconst_string(s,_loc,_so))}->Result.Ok(Const_string,s)|_->Result.Error()letget_ids_and_constants_from_expexp=letopenParsetreein(matchexpwith|{pexp_desc=Pexp_apply(exp,args)}->get_id_or_constant_from_expexp::List.map(function|(Asttypes.Nolabel,arg)->get_id_or_constant_from_exparg|(_,_)->Result.Error())args|_->[get_id_or_constant_from_expexp])|>List.fold_left(funaccr->matchacc,rwith|Result.Okids,Okid->Result.Ok(id::ids)|(Result.Error_|Ok_),_->Result.Error())(Ok[])|>Result.mapList.revletparse_optional_id_payloadtxtloc~emptycasespayload=let[@local]warn()=let(%>)fgx=g(fx)inletmsg=cases|>List.map(fst%>Printf.sprintf"'%s'")|>String.concat", "|>Printf.sprintf"It must be either %s or empty"inLocation.prerr_warningloc(Warnings.Attribute_payload(txt,msg));Error()inmatchget_optional_payloadget_id_from_exppayloadwith|Error()->warn()|OkNone->Okempty|Ok(Someid)->matchList.assoc_optidcaseswith|Somer->Okr|None->warn()(* Looks for `custom_error_message msg` in payload.
If present, this returns `msg` and an updated payload
with `customer_error_message msg` removed.
Preserves the order of the payload. *)letfilter_custom_error_messagepayload=letrecfind_msgaccpayload=matchpayloadwith|[]|[_]->None|(Ident,"custom_error_message")::(Const_string,msg)::payload->Some(msg,(List.revacc)@payload)|s1::payload->find_msg(s1::acc)payloadinfind_msg[]payload(* Looks for `arity n` in payload. If present, this returns `n` and an updated
payload with `arity n` removed. Note it may change the order of the payload,
which is fine because we sort it later. *)letfilter_aritypayload=letrecfind_arityaccpayload=matchpayloadwith|[]|[_]->None|(Ident,"arity")ass1::((Const_int,n)::payload)aspayload'->(matchint_of_string_optnwith|Somen->Some(n,acc@payload)|None->find_arity(s1::acc)payload')|s1::payload'->find_arity(s1::acc)payload'infind_arity[]payload(* If "assume_unless_opt" is not found returns None, otherwise
returns the rest of the payload. Note it may change the order of the payload,
which is fine because we sort it later. *)letfilter_assume_unless_optpayload=letrecfindaccpayload=matchpayloadwith|[]->None|"assume_unless_opt"::tl->Some(acc@tl)|hd::tl->find(hd::acc)tlinfind[]payloadletzero_alloc_lookup_table=(* These are the possible payloads (sans arity) paired with a function that
returns the corresponding check_attribute, given the arity and the loc. *)[(["assume"],funarityloc_->Assume{strict=false;never_returns_normally=false;never_raises=false;arity;loc;});(["assume_unless_opt"],funarityloc_->(* same as "assume" *)Assume{strict=false;never_returns_normally=false;never_raises=false;arity;loc;});(["strict"],funarityloccustom_error_msg->Check{strict=true;opt=false;arity;loc;custom_error_msg;});(["opt"],funarityloccustom_error_msg->Check{strict=false;opt=true;arity;loc;custom_error_msg;});(["opt";"strict";],funarityloccustom_error_msg->Check{strict=true;opt=true;arity;loc;custom_error_msg;});(["assume";"strict"],funarityloc_->Assume{strict=true;never_returns_normally=false;never_raises=false;arity;loc;});(["assume";"never_returns_normally"],funarityloc_->Assume{strict=false;never_returns_normally=true;never_raises=false;arity;loc;});(["assume";"never_returns_normally";"strict"],funarityloc_->Assume{strict=true;never_returns_normally=true;never_raises=false;arity;loc;});(["assume";"error"],funarityloc_->Assume{strict=true;never_returns_normally=true;never_raises=true;arity;loc;});(["ignore"],fun___->Ignore_assert_all)]letparse_zero_alloc_payload~loc~arity~custom_error_message~warn~emptypayload=(* This parses the remainder of the payload after arity has been parsed
out. *)matchpayloadwith|[]->empty|_::_->letpayload=List.sortString.comparepayloadinmatchList.assoc_optpayloadzero_alloc_lookup_tablewith|None->warn();Default_zero_alloc|Someca->caarityloccustom_error_messageletparse_zero_alloc_attribute~in_signature~on_application~default_arityattr=matchattrwith|None->Default_zero_alloc|Some{Parsetree.attr_name={txt;loc};attr_payload=payload}->letwarn()=let(%>)fgx=g(fx)inletmsg=letcustom_payloads=letfail___=assertfalsein[(["arity <int_constant>"],fail);(["custom_error_message <string_constant>"],fail)]in(zero_alloc_lookup_table@custom_payloads)|>List.map(fst%>String.concat" "%>Printf.sprintf"'%s'")|>String.concat", "|>Printf.sprintf"It must be either %s or empty"inLocation.prerr_warningloc(Warnings.Attribute_payload(txt,msg))inletemptyaritycustom_error_msg=Check{strict=false;opt=false;arity;loc;custom_error_msg;}inmatchget_optional_payloadget_ids_and_constants_from_exppayloadwith|Error()->warn();Default_zero_alloc|OkNone->emptydefault_arityNone|Ok(Somepayload)->letcustom_error_message,payload=matchfilter_custom_error_messagepayloadwith|None->None,payload|Some(custom_error_message,payload)->letis_assume=function|(Ident,("assume"|"assume_unless_opt"))->true|_->falseinifList.existsis_assumepayloadthen(warn_payloadloctxt"The \"custom_error_message\" payload is not supported with \"assume\".";None,payload)elseSomecustom_error_message,payloadinletarity,payload=matchfilter_aritypayloadwith|None->default_arity,payload|Some(user_arity,payload)->ifin_signaturethenuser_arity,payloadelse(warn_payloadloctxt"The \"arity\" field is only supported on \"zero_alloc\" in \
signatures";default_arity,payload)inlet_,payload=List.splitpayloadinletparsep=letempty=emptyaritycustom_error_messageinparse_zero_alloc_payload~loc~arity~custom_error_message~warn~emptypinmatchfilter_assume_unless_optpayloadwith|None->parsepayload|Somerest->ifin_signaturethen(warn_payloadloctxt"The payload \"assume_unless_opt\" is not supported \
in signatures.";(* Treat [@zero_alloc assume_unless_opt] as [@zero_alloc] in signatures. *)parserest)elseletno_other_payload=List.compare_length_withrest0=0inifno_other_payloadthen(ifis_zero_alloc_check_enabled~opt:truethen(ifon_applicationthen(* Treat as if there is no attribute.
Check is not allowed on applications. *)Default_zero_allocelse(* Treat [@zero_alloc assume_unless_opt] as [@zero_alloc],
forcing the function to be checked.
Setting [opt = false] to satisfy [@zero_alloc]
and not only [@zero_alloc opt] on the corresponding signatures. *)emptyaritycustom_error_message)else(* Treat "assume_unless_opt" as "assume".
Reuse standard parsing for better error messages. *)parsepayload)else((* No support for other payloads with "assume_unless_opt". *)warn();Default_zero_alloc)letget_zero_alloc_attribute~in_signature~on_application~default_arityl=letattr=select_attributeis_zero_alloc_attributelinletres=parse_zero_alloc_attribute~in_signature~on_application~default_arityattrin(matchattr,reswith|None,Default_zero_alloc->()|_,Default_zero_alloc->()|None,(Check_|Assume_|Ignore_assert_all)->assertfalse|Some_,Ignore_assert_all->()|Some_,Assume_->()|Someattr,Check{opt;_}->ifnotin_signature&&is_zero_alloc_check_enabled~opt&&!Clflags.native_codethen(* The warning for unchecked functions will not trigger if the check is
requested through the [@@@zero_alloc all] top-level annotation rather
than through the function annotation [@zero_alloc]. *)register_zero_alloc_attributeattr.attr_name);resletzero_alloc_attribute_only_assume_allowedza=matchzawith|Assumeassume->Someassume|Default_zero_alloc|Ignore_assert_all->None|Check{loc;_}->letname="zero_alloc"inletmsg="Only the following combinations are supported in this context: \
'zero_alloc assume', \
'zero_alloc assume_unless_opt', \
`zero_alloc assume strict`, \
`zero_alloc assume error`,\
`zero_alloc assume never_returns_normally`,\
`zero_alloc assume never_returns_normally strict`."inLocation.prerr_warningloc(Warnings.Attribute_payload(name,msg));Noneletassume_zero_alloc~inferredassume:Zero_alloc_utils.Assume_info.t=matchassumewith|{strict;never_returns_normally;never_raises;}->Zero_alloc_utils.Assume_info.create~strict~never_returns_normally~never_raises~inferredtypetracing_probe={name:string;name_loc:Location.t;enabled_at_init:bool;arg:Parsetree.expression;}letget_tracing_probe_payload(payload:Parsetree.payload)=let(let*)=Result.bindinlet*name,name_loc,args=matchpayloadwith|PStr([{pstr_desc=Pstr_eval({pexp_desc=(Pexp_apply({pexp_desc=(Pexp_constant(Pconst_string(name,_,None)));pexp_loc=name_loc;_},args));_},_)}])->Ok(name,name_loc,args)|_->Error()inletbool_of_string=function|"true"->Oktrue|"false"->Okfalse|_->Error()inlet*arg,enabled_at_init=matchargswith|[Nolabel,arg]->Ok(arg,false)|[Labelled"enabled_at_init",{pexp_desc=Pexp_construct({txt=Longident.Lidentb;_},None);_};Nolabel,arg]->let*enabled_at_init=bool_of_stringbinOk(arg,enabled_at_init)|_->Error()inOk{name;name_loc;enabled_at_init;arg}lethas_atomicattrs=has_attribute"atomic"attrs(* Merlin specific *)letmerlin_punned_let="merlin.punned-let"letmerlin_punned_record_pattern="merlin.punned-record-pattern"