123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492(* Don't mask native Outcometree *)moduleOt=OutcometreeopenPpx_tools_411openMigrate_parsetreeopenAst_411.LongidentopenAst_411.AsttypesopenAst_411.ParsetreeopenAst_411.Ast_mapperopenAst_411.Ast_helperopenTypesmoduleTt=Ppx_types_migrateletraise_errorf?sub?locmessage=message|>Printf.kprintf(funstr->leterr=Location.error?sub?locstrinraise(Location.Errorerr))letreplace_locloc={default_mapperwithlocation=fun__->loc}letlazy_env=lazy((* It is important that the typing environment is not evaluated
right away, but only once the ppx-context has been loaded from
the AST, so that Config.load_path and the rest of the environment
context are correctly set.
The environment setting should happen when reading the
ppx-context attribute which is the very first structure/signature
item sent to ppx rewriters. In particular, this happens before
the [%import ] extensions are traversed, which are the places in
this code where 'env' is forced.
We would also have the option to not have a global environment, but
recompute the typing environment on each [%import ] extension. We don't
see any advantage in doing this, given that we compute the global/initial
environment that is the same at all program points.
*)(* We need to set recursive_types manually, because it is not part
of the context automatically saved by Ast_mapper (as of 4.06),
and this prevents loading the interface of recursive-types-using
modules. On the other hand, setting recursive_types more often
than necessary does not seem harmful. *)Clflags.recursive_types:=true;Compat.init_path();Compmisc.initial_env())letstring_of_lidlid=letrecprintlidacc=matchlidwith|Longident.Lidents->s::acc|Longident.Ldot(lid,id)->printlid("."::id::acc)|Longident.Lapply(la,lb)->printla("("::printlb(")"::acc))inString.concat""(printlid[])lettry_find_module~locenvlid=(* Note: we are careful to call `Env.lookup_module` and not
`Typetexp.lookup_module`, because we want to reason precisely
about the possible failures: we want to handle the case where
the module path does not exist, but let all the other errors
(invalid .cmi format, etc.) bubble up to the error handler.
`Env.lookup_module` allows to do this easily as it raises
a well-identified `Not_found` exception, while
`Typetexp.lookup_module` wraps the Not_found failure in
user-oriented data and is not meant for catching.
`Env.find_module` can raise `Not_found` again; we suspect that
it will not in the cases where `lookup_module` returned correctly,
but better be safe and bundle them in the same try..with.
*)tryletpath=Compat.lookup_module~loclidenvinletmodule_decl=Env.find_modulepathenvinSomemodule_decl.md_typewithNot_found->Nonelettry_find_module_type~locenvlid=(* Here again we prefer to handle the `Not_found` case, so we
use `Env.lookup_module` rather than `Typetexp.lookup_module`. *)trylet_path,modtype_decl=Env.lookup_modtype~loclidenvinSome(matchmodtype_decl.mtd_typewith|None->raise_errorf~loc"[%%import]: cannot access the signature of the abstract module %s"(string_of_lidlid)|Somemodule_type->module_type)withNot_found->Noneletrectry_open_module_typeenvmodule_type=matchCompat.migrate_module_typemodule_typewith|Mty_signaturesig_items->Somesig_items|Mty_functor_->None|(Mty_identpath|Mty_alias(_,path))->beginmatch(trySome(Env.find_modulepathenv)withNot_found->None)with|None->None|Somemodule_decl->try_open_module_typeenvmodule_decl.md_typeendletopen_module_type~locenvlidmodule_type=matchtry_open_module_typeenvmodule_typewith|Somesig_items->sig_items|None->raise_errorf~loc"[%%import]: cannot find the components of %s"(string_of_lidlid)letlocate_sig~locenvlid=lethead,path=matchLongident.flattenlidwith|head::path->Longident.Lidenthead,path|_->assertfalseinlethead_module_type=matchtry_find_module~locenvhead,lazy(try_find_module_type~locenvhead)with|Somemty,_->mty|None,lazy(Somemty)->mty|None,lazyNone->raise_errorf~loc"[%%import]: cannot locate module %s"(string_of_lidlid)inletget_sub_module_type(lid,module_type)path_item=letsig_items=open_module_type~locenvlidmodule_typeinletrecloopsig_items=match(sig_items:Compat.signature_item_407list)with|Sig_module(id,{md_type;_},_)::_whenIdent.nameid=path_item->md_type|Sig_modtype(id,{mtd_type=Somemd_type;_})::_whenIdent.nameid=path_item->md_type|_::sig_items->loopsig_items|[]->raise_errorf~loc"[%%import]: cannot find the signature of %s in %s"path_item(string_of_lidlid)inletsub_module_type=loop(List.mapCompat.migrate_signature_itemsig_items)in(Longident.Ldot(lid,path_item),sub_module_type)inlet(_lid,sub_module_type)=List.fold_leftget_sub_module_type(head,head_module_type)pathinopen_module_type~locenvlidsub_module_typelettry_get_tsig_itemf~loc:_sig_itemselem=letrecloopsig_items=matchsig_itemswith|item::sig_items->(matchfelemitemwithSomex->Somex|None->loopsig_items)|[]->Noneinloopsig_itemsletget_type_decl~locsig_itemsparent_lidelem=letselect_typeelemsigi=matchCompat.migrate_signature_itemsigiwith|Sig_type(id,type_decl,_)whenIdent.nameid=elem->Sometype_decl|_->Noneinmatchtry_get_tsig_itemselect_type~locsig_itemselemwith|None->raise_errorf"[%%import]: cannot find the type %s in %s"elem(string_of_lidparent_lid)|Somedecl->declletget_modtype_decl~locsig_itemsparent_lidelem=letselect_modtypeelemsigi=matchCompat.migrate_signature_itemsigiwith|Sig_modtype(id,type_decl)whenIdent.nameid=elem->Sometype_decl|_->Noneinmatchtry_get_tsig_itemselect_modtype~locsig_itemselemwith|None->raise_errorf"[%%import]: cannot find the module type %s in %s"elem(string_of_lidparent_lid)|Somedecl->declletlongident_of_path=Untypeast.lident_of_pathletreccore_type_of_type_expr~substtype_expr=matchtype_expr.descwith|TvarNone->Typ.any()|Tvar(Somevar)->beginmatchList.assoc(`Varvar)substwith|typ->typ|exceptionNot_found->Typ.varvarend|Tarrow(label,lhs,rhs,_)->letlabel=Tt.copy_arg_labellabelinletlhs=core_type_of_type_expr~substlhsinletlhs=matchlabelwith|Optional_->beginmatchlhswith|[%type:[%t?lhs]option]->lhs|_->assertfalseend|_->lhsinTyp.arrowlabellhs(core_type_of_type_expr~substrhs)|Ttuplexs->Typ.tuple(List.map(core_type_of_type_expr~subst)xs)|Tconstr(path,args,_)->letlid=longident_of_pathpathinletargs=(List.map(core_type_of_type_expr~subst)args)inbeginmatchList.assoc(`Lidlid)substwith|{ptyp_desc=Ptyp_constr(lid,_);_}astyp->{typwithptyp_desc=Ptyp_constr(lid,args)}|_->assertfalse|exceptionNot_found->Typ.constr{txt=longident_of_pathpath;loc=!default_loc}argsend|Tvariant{row_fields;_}->letfields=row_fields|>List.map(fun(label,row_field)->letlabel=Location.mknoloclabelinletdesc=matchrow_fieldwith|RpresentNone->Rtag(label,true,[])|Rpresent(Somettyp)->Rtag(label,false,[core_type_of_type_expr~substttyp])|_->assertfalsein{prf_desc=desc;prf_loc=!default_loc;prf_attributes=[];})inTyp.variantfieldsClosedNone|_->assertfalseletptype_decl_of_ttype_decl~manifest~substptype_namettype_decl=letsubst=matchmanifestwith|Some{ptyp_desc=Ptyp_constr(_,ptype_args);ptyp_loc;_}->subst@begintryList.map2(funtparampparam->matchtparamwith|{desc=Tvar(Somevar);_}->[`Varvar,pparam]|{desc=TvarNone;_}->[]|_->assertfalse)ttype_decl.type_paramsptype_args|>List.concatwithInvalid_argument_->raise_errorf~loc:ptyp_loc"Imported type has %d parameter(s), but %d are passed"(List.lengthttype_decl.type_params)(List.lengthptype_args)end|None->[]|_->assertfalseinletptype_params=List.map2(funparam_variance->core_type_of_type_expr~substparam,(* The equivalent of not specifying the variance explicitly.
Since the very purpose of ppx_import is to include the full definition,
it should always be sufficient to rely on the inferencer to deduce variance. *)Invariant)ttype_decl.type_paramsttype_decl.type_varianceandptype_kind=letmap_labels=List.map(funld->{pld_name={txt=Ident.nameld.ld_id;loc=ld.ld_loc};pld_mutable=Tt.copy_mutable_flagld.ld_mutable;pld_type=core_type_of_type_expr~substld.ld_type;pld_loc=ld.ld_loc;pld_attributes=Tt.copy_attributesld.ld_attributes;})inmatchttype_decl.type_kindwith|Type_abstract->Ptype_abstract|Type_open->Ptype_open|Type_record(labels,_)->Ptype_record(map_labelslabels)|Type_variantconstrs->letmap_args=function|Cstr_tuple(args)->Pcstr_tuple(List.map(core_type_of_type_expr~subst)args)|Cstr_record(labels)->Pcstr_record(map_labelslabels)inPtype_variant(constrs|>List.map(funcd->{pcd_name={txt=Ident.namecd.cd_id;loc=cd.cd_loc};pcd_args=map_argscd.cd_args;pcd_res=(matchcd.cd_reswithSomex->Some(core_type_of_type_expr~substx)|None->None);pcd_loc=cd.cd_loc;pcd_attributes=Tt.copy_attributescd.cd_attributes;}))andptype_manifest=matchttype_decl.type_manifestwith|Sometyp->Some(core_type_of_type_expr~substtyp)|None->manifestin{ptype_name;ptype_params;ptype_kind;ptype_manifest;ptype_cstrs=[];ptype_private=Tt.copy_private_flagttype_decl.type_private;ptype_attributes=Tt.copy_attributesttype_decl.type_attributes;ptype_loc=ttype_decl.type_loc;}letsubst_of_manifest{ptyp_attributes;ptyp_loc;_}=letrecsubst_of_exprexpr=matchexprwith|[%expr[%e?{pexp_desc=Pexp_ident({txt=src;_});_}]:=[%e?{pexp_desc=Pexp_ident(dst);pexp_attributes;pexp_loc;pexp_loc_stack}]]->[`Lidsrc,{ptyp_loc=pexp_loc;ptyp_loc_stack=pexp_loc_stack;ptyp_attributes=pexp_attributes;ptyp_desc=Ptyp_constr(dst,[]);}]|[%expr[%e?{pexp_desc=Pexp_ident({txt=src;_});_}]:=[%e?{pexp_desc=Pexp_ident(dst);pexp_attributes;pexp_loc;pexp_loc_stack;}];[%e?rest]]->(`Lidsrc,{ptyp_loc=pexp_loc;ptyp_loc_stack=pexp_loc_stack;ptyp_attributes=pexp_attributes;ptyp_desc=Ptyp_constr(dst,[])})::subst_of_exprrest|{pexp_loc;_}->raise_errorf~loc:pexp_loc"Invalid [@with] syntax"inmatchAst_convenience.find_attr"with"ptyp_attributeswith|None->[]|Some(PStr[{pstr_desc=Pstr_eval(expr,[]);_}])->subst_of_exprexpr|Some_->raise_errorf~loc:ptyp_loc"Invalid [@with] syntax"letuncapitalize=String.uncapitalize_asciiletis_self_referencelid=letfn=!Location.input_name|>Filename.basename|>Filename.chop_extension|>uncapitalizeinmatchlidwith|Ldot(_)->letmn=Longident.flattenlid|>List.hd|>uncapitalizeinfn=mn|_->falselettype_declaration~tool_namemappertype_decl=matchtype_declwith|{ptype_attributes;ptype_name;ptype_manifest=Some{ptyp_desc=Ptyp_extension({txt="import";loc},payload);_};_}->beginmatchpayloadwith|PTyp({ptyp_desc=Ptyp_constr({txt=lid;loc},_);_}asmanifest)->iftool_name="ocamldep"then(* Just put it as manifest *)ifis_self_referencelidthen{type_declwithptype_manifest=None}else{type_declwithptype_manifest=Somemanifest}elsewith_default_locloc(fun()->letttype_decl=letenv=Lazy.forcelazy_envinmatchlidwith|Lapply_->raise_errorf~loc"[%%import] cannot import a functor application %s"(string_of_lidlid)|Lident_ashead_id->(* In this case, we know for sure that the user intends this lident
as a type name, so we use Typetexp.find_type and let the failure
cases propagate to the user. *)Compat.find_typeenv~lochead_id|>snd|Ldot(parent_id,elem)->letsig_items=locate_sig~locenvparent_idinget_type_decl~locsig_itemsparent_ideleminletm,s=ifis_self_referencelidthenNone,[]elsebeginletsubst=subst_of_manifestmanifestinletsubst=subst@[`Lid(Lident(Longident.lastlid)),Typ.constr{txt=Lidentptype_name.txt;loc=ptype_name.loc}[]]inSomemanifest,substendinletptype_decl=ptype_decl_of_ttype_decl~manifest:m~subst:sptype_namettype_declin{ptype_declwithptype_attributes})|_->raise_errorf~loc"Invalid [%%import] syntax"end|_->default_mapper.type_declarationmappertype_declletreccut_tsig_block_of_rec_typesaccu(tsig:Compat.signature_item_407list)=matchtsigwith|Sig_type(id,ttype_decl,Trec_next)::rest->cut_tsig_block_of_rec_types((id,ttype_decl)::accu)rest|_->(List.revaccu,tsig)letrecpsig_of_tsig~subst(tsig:Compat.signature_item_407list)=matchtsigwith|Sig_type(id,ttype_decl,rec_flag)::rest->letaccu=[(id,ttype_decl)]inlet(rec_flag,(block,rest))=matchrec_flagwith|Trec_not->(Nonrecursive,(accu,rest))|Trec_first->(Recursive,cut_tsig_block_of_rec_typesaccurest)|Trec_next->assertfalseinletblock=block|>List.map(fun(id,ttype_decl)->ptype_decl_of_ttype_decl~manifest:None~subst(Location.mknoloc(Ident.nameid))ttype_decl)inletpsig_desc=Psig_type(rec_flag,block)in{psig_desc;psig_loc=Location.none}::psig_of_tsig~substrest|Sig_value(id,{val_type;val_kind;val_loc;val_attributes;_})::rest->letpval_prim=matchval_kindwith|Val_reg->[]|Val_primp->letoval=Ot.{oval_name="";oval_type=Otyp_abstract;oval_prims=[];oval_attributes=[]}inletoval=Primitive.printpovalinoval.Ot.oval_prims|_->assertfalsein{psig_desc=Psig_value{pval_name=Location.mknoloc(Ident.nameid);pval_loc=val_loc;pval_attributes=Tt.copy_attributesval_attributes;pval_prim;pval_type=core_type_of_type_expr~substval_type;};psig_loc=val_loc}::psig_of_tsig~substrest|[]->[]|_->assertfalseletmodule_type~tool_namemappermodtype_decl=matchmodtype_declwith|{pmty_attributes=_;pmty_desc=Pmty_extension({txt="import";loc},payload);_}->beginmatchpayloadwith|PTyp({ptyp_desc=Ptyp_package({txt=lid;loc}asalias,subst);_})->iftool_name="ocamldep"thenifis_self_referencelidthen(* Create a dummy module type to break the circular dependency *){modtype_declwithpmty_desc=Pmty_signature[]}else(* Just put it as alias *){modtype_declwithpmty_desc=Pmty_aliasalias}elsewith_default_locloc(fun()->letenv=Lazy.forcelazy_envinlettmodtype_decl=matchlidwith|Longident.Lapply_->raise_errorf~loc"[%%import] cannot import a functor application %s"(string_of_lidlid)|Longident.Lident_ashead_id->(* In this case, we know for sure that the user intends this lident
as a module type name, so we use Typetexp.find_type and
let the failure cases propagate to the user. *)Compat.find_modtypeenv~lochead_id|>snd|Longident.Ldot(parent_id,elem)->letsig_items=locate_sig~locenvparent_idinget_modtype_decl~locsig_itemsparent_ideleminmatchtmodtype_declwith|{mtd_type=Some(Mty_signaturetsig);_}->letsubst=List.map(fun({txt;_},typ)->`Lidtxt,typ)substinletpsig=psig_of_tsig~subst(List.mapCompat.migrate_signature_itemtsig)in{modtype_declwithpmty_desc=Pmty_signaturepsig}|{mtd_type=None;_}->raise_errorf~loc"Imported module is abstract"|_->raise_errorf~loc"Imported module is indirectly defined")|_->raise_errorf~loc"Invalid [%%import] syntax"end|_->default_mapper.module_typemappermodtype_decllet()=letopenMigrate_parsetreein(* Position 0 is the default, ppx_import should run pretty early,
thus using -10 *)Driver.register~name:"ppx_import"~args:[]~position:(-10)Versions.ocaml_411(funconfig_cookies->lettool_name=config.tool_nameinlettype_declaration=type_declaration~tool_nameinletmodule_type=module_type~tool_namein{default_mapperwithtype_declaration;module_type})