123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398(*
* Copyright (c) 2014 Leo White <lpw25@cl.cam.ac.uk>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*)openOdoc_modelmodulePaths=Odoc_model.Pathsletpoint_of_pos{Lexing.pos_lnum;pos_bol;pos_cnum;_}=letcolumn=pos_cnum -pos_bolin{Odoc_model.Location_.line=pos_lnum;column}letread_location{Location.loc_start;loc_end;_}={Odoc_model.Location_.file=loc_start.pos_fname;start=point_of_posloc_start;end_=point_of_posloc_end;}letempty_bodywarnings_tag={Comment.elements=[];warnings_tag}letemptywarnings_tag:Odoc_model.Comment.docs=empty_bodywarnings_tagletload_constant_string=function|{Parsetree.pexp_desc=#ifOCAML_VERSION<(4,11,0)Pexp_constant(Pconst_string(text,_))#elifOCAML_VERSION<(5,3,0)Pexp_constant(Pconst_string(text,_,_))#elsePexp_constant{pconst_desc=Pconst_string(text,_,_);_}#endif;pexp_loc=loc;_}->Some(text,loc)|_->Noneletload_payload=function|Parsetree.PStr[{pstr_desc=Pstr_eval (constant_string,_);_}]->load_constant_string constant_string|_->Noneletload_alert_name name=(Longident.lastname.Location.txt)letload_alert_name_and_payload=function|Parsetree.PStr[{pstr_desc=Pstr_eval({pexp_desc=expression;_},_);_}]->(matchexpressionwith|Pexp_apply({pexp_desc=Pexp_identname;_},[(_,payload)])->Some(load_alert_namename,load_constant_stringpayload)|Pexp_identname->Some(load_alert_namename,None)|_->None)|_->Noneletattribute_unpack=function|{Parsetree.attr_name={Location.txt=name;_};attr_payload;attr_loc}->(name,attr_payload,attr_loc)#ifdefinedOXCAML(* the input is a [Zero_alloc.t] which is not present in OCaml, so it can't be
a no-op *)letlang_value_attr_of_zero_alloc zero_alloc=matchZero_alloc.getzero_allocwith|Default_zero_alloc->None|Ignore_assert_all->None|Assume{arity;_}->Some(Lang.Value.Zero_alloc(Lang.Value.Zero_alloc.{opt=false;strict=false;arity;custom_error_msg =None}))|Check{strict;opt;arity;custom_error_msg}->Some (Lang.Value.Zero_alloc (Lang.Value.Zero_alloc.{opt;strict;arity;custom_error_msg }))#endif#ifdefinedOXCAMLletattrs_of_value_description (vd:Types.value_description)=letzero_alloc=lang_value_attr_of_zero_allocvd.val_zero_allocinmatchzero_allocwith|Someza->[za]|None->[]#elseletattrs_of_value_description(vd:Types.value_description)=[]#endif#ifdefinedOXCAMLletid_attrs_of_value_bindings vbs=vbs|>Typedtree.let_bound_idents_with_modes_sorts_and_checks|>List.fold_left(funtbl(ident,_,zero_alloc)->matchlang_value_attr_of_zero_alloczero_allocwith|None->tbl|Someattr->Ident.addident[attr]tbl)Ident.empty#elseletid_attrs_of_value_bindings_vbs=Ident.empty#endiftypepayload =string*Location.ttypeparsed_attribute=[`Textofpayload(* Standalone comment. *)|`Docofpayload(* Attached comment. *)|`StopofLocation.t(* [(**/**)]. *)|`Alertofstring*payload option*Location.t(* [`Alert (name, payload, loc)] is for [\[@@alert name "payload"\]] attributes. *) ](** Recognize an attribute. *)letparse_attribute:Parsetree.attribute->parsed_attributeoption=funattr->letname,attr_payload,attr_loc =attribute_unpackattrinmatchnamewith|"text"|"ocaml.text"->(matchload_payloadattr_payloadwith|Some("/*",_)->Some(`Stopattr_loc)|Somep->Some(`Textp)|None->None)|"doc"|"ocaml.doc"->((* We don't expect a stop-comment here. *)matchload_payloadattr_payloadwith|Somep->Some(`Docp)|None->None)|"deprecated"|"ocaml.deprecated"->Some(`Alert ("deprecated",(load_payloadattr_payload),attr_loc))|"alert"|"ocaml.alert"->(matchload_alert_name_and_payloadattr_payloadwithSome(name,payload)->Some(`Alert(name,payload,attr_loc))|None->None)|_->Noneletis_stop_commentattr=matchparse_attribute attrwithSome(`Stop_)->true|_->falseletpad_locloc={loc.Location.loc_startwithpos_cnum=loc.loc_start.pos_cnum+3}letast_to_comment ~internal_tagsparent ast_docsalerts=Odoc_model.Semantics.ast_to_comment~internal_tags~tags_allowed:true~parent_of_sections:parentast_docs alerts|>Error.raise_warningsletmk_alert_payload~locnamep=letp=matchpwithSome(p,_)->Somep|None->Noneinletelt=`Tag(`Alert(name,p))inletspan=read_locationlocinLocation_.atspanelt(* The same doc-comment text is often attached to many definitions (OxCaml's
ppx_template can produce tens of thousands of copies of one comment), so
parses are memoized by raw text. The parsed AST bakes in absolute source
locations, so a cached parse is only reused when it is location-
insensitive: it produced no warnings and contains no headings, references
or [{!modules ...}], whose locations feed warnings and ambiguous-heading
detection during linking. Anything else is re-parsed per occurrence. *)letrecinline_element_needs_own_location(x:Odoc_parser.Ast.inline_element)=matchxwith|`Reference_->true|`Styled(_,xs)->List.exists(fune->inline_element_needs_own_location(Odoc_parser.Loc.valuee))xs|`Space_|`Word_|`Code_span_|`Raw_markup_|`Link_|`Math_span_->falseletrecnestable_block_element_needs_own_location(x:Odoc_parser.Ast.nestable_block_element)=matchxwith|`Paragraphxs->List.exists(fune->inline_element_needs_own_location(Odoc_parser.Loc.valuee))xs|`Modules _->true|`Media(_,href,_,_)->(matchOdoc_parser.Loc.valuehrefwith|`Reference_->true|`Link_->false)|`List(_,_,yss)->List.exists(List.exists(fune->nestable_block_element_needs_own_location(Odoc_parser.Loc.valuee)))yss|`Table((grid,_),_)->List.exists(List.exists(fun(cell,_)->List.exists(fune->nestable_block_element_needs_own_location(Odoc_parser.Loc.valuee))cell))grid|`Code_block_|`Verbatim_|`Math_block_->falselettag_needs_own_location(x:Odoc_parser.Ast.tag)=matchxwith|`Deprecatedc|`Param(_,c)|`Raise(_,c)|`Returnc|`See(_,_,c)|`Before(_,c)|`Children_orderc|`Toc_statusc|`Order_categoryc|`Short_titlec->List.exists(fune->nestable_block_element_needs_own_location(Odoc_parser.Loc.valuee))c|`Author_|`Since_|`Version_|`Canonical_|`Inline|`Open|`Closed|`Hidden->falseletblock_element_needs_own_location(x:Odoc_parser.Ast.block_element)=matchxwith|`Heading_->true|`Tagt->tag_needs_own_location t|#Odoc_parser.Ast.nestable_block_elementasx->nestable_block_element_needs_own_location xletast_needs_own_location(ast:Odoc_parser.Ast.t)=List.exists(fune->block_element_needs_own_location (Odoc_parser.Loc.valuee))astletdoc_cache:(string,Odoc_parser.Ast.t)Hashtbl.t=Hashtbl.create256letattached~warnings_taginternal_tagsparentattrs=letrecloopacc_docsacc_alerts=function|attr::rest->(matchparse_attributeattrwith|Some(`Doc(str,loc))->letast_docs=matchHashtbl.find_optdoc_cachestr with|Somecached->cached|None->letparsed=Odoc_parser.parse_comment ~location:(pad_locloc)~text:strinletast=Semantics.merge_ast(Error.raise_parser_warningsparsed)in(matchOdoc_parser.warningsparsedwith|[]whennot(ast_needs_own_location ast)->Hashtbl.replacedoc_cachestrast|_->());astinloop (List.rev_appendast_docsacc_docs)acc_alertsrest|Some(`Alert(name,p,loc))->letelt=mk_alert_payload~locnamepinloopacc_docs(elt::acc_alerts)rest|Some(`Text_|`Stop_)|None->loopacc_docsacc_alertsrest)|[]->(List.revacc_docs,List.revacc_alerts)inletast_docs,alerts=loop[][]attrsinlet elements,warnings=ast_to_comment~internal_tagsparentast_docsalertsin{Comment.elements;warnings_tag},warningsletattached_no_tag~warnings_tagparentattrs=letx,()=attached~warnings_tagSemantics.Expect_noneparentattrsinxletread_string ~tags_allowed internal_tagsparentlocationstr=Odoc_model.Semantics.parse_comment~internal_tags~tags_allowed~containing_definition:parent~location~text:str|>Odoc_model.Error.raise_warningsletread_string_commentinternal_tagsparentlocstr=read_string~tags_allowed:trueinternal_tagsparent(pad_locloc)strletpageparentlocstr=letelements,tags =read_string~tags_allowed:falseOdoc_model.Semantics.Expect_page_tagsparentloc.Location.loc_startstrin{Comment.elements;warnings_tag=None},tagsletstandaloneparent~warnings_tag(attr :Parsetree.attribute):Odoc_model.Comment.docs_or_stopoption=matchparse_attribute attrwith|Some(`Stop _loc)->Some `Stop|Some(`Text(str,loc))->letelements,()=read_string_commentSemantics.Expect_noneparentlocstrinSome(`Docs{elements;warnings_tag })|Some(`Doc_)->None|Some(`Alert(name,_,attr_loc))->letw=Error.make"Alert %s not expected here."name(read_locationattr_loc)inError.raise_warningw;None|_->Noneletstandalone_multipleparent~warnings_tagattrs=letcoms=List.fold_left(funaccattr->matchstandaloneparent~warnings_tagattrwith|None->acc|Somecom->com::acc)[]attrsinList.revcomsletsplit_docsdocs=let recinnerfirstx=matchxwith|{Location_.value=`Heading_;_}::_->List.rev first,x|x::y->inner(x::first)y|[]->List.revfirst,[]ininner[]docsletextract_top_commentinternal_tags~warnings_tag~classifyparentitems=letclassifyx=matchclassifyxwith|Some(`Attributeattr)->(matchparse_attribute attrwith|Some(`Text_asp)->p|Some(`Doc_)->`Skip(* Unexpected, silently ignore *)|Some(`Alert(name,p,attr_loc))->letp=matchpwithSome(p,_)->Somep|None->Noneinletattr_loc=read_locationattr_locin`Alert(Location_.atattr_loc(`Tag(`Alert(name,p))))|Some(`Stop_)->`Return(* Stop at stop-comments. *)|None->`Skip(* Skip unrecognized attributes. *))|Some`Open->`Skip(* Skip open statements *)|None->`Returninletrecextract_tail_alertsacc=function(* Accumulate the alerts after the top-comment. Stop at the next comment.
[`Skip] items (open statements, unrecognised attributes) are preserved
in the returned items list -- dropping them silently loses [open struct ... end] declarations that follow the top docstring. *)|hd::tlasitems->(matchclassifyhdwith|`Text_|`Return->(items,acc)|`Alertalert->extract_tail_alerts(alert::acc)tl|`Skip->letitems,alerts=extract_tail_alertsacctlin(hd::items,alerts))|[]->([],acc)andextract=function(* Extract the first comment and accumulate the alerts before and after
it. *)|hd::tlasitems ->(matchclassifyhdwith|`Text(text,loc)->letast_docs =Odoc_parser.parse_comment~location:(pad_locloc)~text|>Error.raise_parser_warningsinletitems,alerts=extract_tail_alerts[]tlin(items,ast_docs,alerts)|`Alertalert->letitems,ast_docs,alerts=extracttlin(items,ast_docs,alert::alerts)|`Skip->letitems,ast_docs,alerts=extract tlin(hd::items,ast_docs,alerts)|`Return ->(items,[],[]))|[]->([],[],[])inletitems,ast_docs,alerts=extract itemsinletdocs,tags=ast_to_comment~internal_tags(parent:Paths.Identifier.Signature.t:>Paths.Identifier.LabelParent.t)ast_docs alertsinletd1,d2=split_docsdocsin(items,({Comment.elements=d1;warnings_tag},{Comment.elements=d2;warnings_tag}),tags)let extract_top_comment_classitems=letmkelementswarnings_tag={Comment.elements;warnings_tag}inmatchitemswith|Lang.ClassSignature.Comment(`Docsdoc)::tl->letd1,d2=split_docsdoc.elementsin(tl,(mkd1doc.warnings_tag,mkd2doc.warnings_tag))|_->(items,(mk[]None,mk[]None))letrecconv_canonical_module:Odoc_model.Reference.path->Paths.Path.Module.t=function|`Dot(parent,name)->`Dot(conv_canonical_moduleparent,Names.ModuleName.make_stdname)|`Rootname->`Root(Names.ModuleName.make_stdname)letconv_canonical_type :Odoc_model.Reference.path->Paths.Path.Type.toption=function|`Dot (parent,name)->Some(`DotT(conv_canonical_moduleparent,Names.TypeName.make_stdname))|_->Noneletconv_canonical_module_type :Odoc_model.Reference.path->Paths.Path.ModuleType.toption=function|`Dot(parent,name)->Some(`DotMT(conv_canonical_moduleparent,Names.ModuleTypeName.make_stdname))|_->None