123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472(* Yoann Padioleau
*
* Copyright (C) 2002-2008 Yoann Padioleau
* Copyright (C) 2011 Facebook
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License (GPL)
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* file license.txt for more details.
*)openCommonmoduleTH=Token_helpers_cppmoduleTV=Token_views_cppmodulePI=Parse_infoopenParser_cppopenToken_views_cppopenParsing_hacks_lib(*****************************************************************************)(* Prelude *)(*****************************************************************************)(*
* This file gathers parsing heuristics related to C++.
* See also Token_views_cpp.set_context_tag and
* Parsing_hacks_typedef.filter_for_typedef that have
* heuristics specific to C++.
*
* TODO: * TIdent_TemplatenameInQualifier
*
*)(*****************************************************************************)(* Helpers *)(*****************************************************************************)letno_space_betweeni1i2=(PI.line_of_infoi1=PI.line_of_infoi2)&&(PI.col_of_infoi1+String.length(PI.str_of_infoi1))=PI.col_of_infoi2(*****************************************************************************)(* Template inference *)(*****************************************************************************)lettemplateLOOKAHEAD=30(* note: no need to check for TCPar to stop for instance the search,
* this is will be done automatically because we would be inside a
* Parenthised expression.
*)letrechave_a_tsup_quite_closexs=matchxswith|[]->false|x::xs->(matchxwith|{t=TSup_}->true(* false positive *)|{t=tok}whenTH.is_static_cast_liketok->false(* ugly: *)|{t=(TOBrace_|TPtVirg_|TCol_|TAssign_)}->false|{t=TInf_}->(* probably nested template, still try
* TODO: bug when have i < DEG<...>::foo(...)
* we should recurse!
*)have_a_tsup_quite_closexs(* bugfix: but want allow some binary operator :) like '*' *)|{t=tok}whenTH.is_binary_operator_except_startok->false|_->have_a_tsup_quite_closexs)(* precondition: there is a tsup *)letrecfind_tsup_quite_closetok_openxs=letrecauxaccxs=matchxswith|[]->raise(UnclosedSymbol(spf"PB: find_tsup_quite_close, no > for < at line %d"(TH.line_of_toktok_open.t)))|x::xs->(matchxwith|{t=TSupii}->List.revacc,(x,ii),xs|{t=TInf_}->(* recurse *)let(before,(tsuptok,_),after)=find_tsup_quite_closexxsin(* we don't care about this one, it will be eventually be
* transformed by the caller *)aux(tsuptok::(List.revbefore)@(x::acc))after|x->aux(x::acc)xs)inaux[]xs(* note: some macros in standard.h may expand to static_cast, so perhaps
* better to do template detection after macro expansion ?
*
* C-s for TInf_Template in the grammar and you will see all cases
* should be covered by the patterns below.
*)letfind_template_inf_supxs=letrecauxxs=matchxswith|[]->()(* template<...> *)|{t=Ttemplate_}::({t=TInfi2}astok2)::xs->change_toktok2(TInf_Templatei2);let(before_sup,(toksup,toksupi),rest)=find_tsup_quite_closetok2xsinchange_toktoksup(TSup_Templatetoksupi);(* recurse *)auxbefore_sup;auxrest(* static_cast<...> *)|{t=tok1}::({t=TInfi2}astok2)::xswhenTH.is_static_cast_liketok1->change_toktok2(TInf_Templatei2);let(before_sup,(toksup,toksupi),rest)=find_tsup_quite_closetok2xsinchange_toktoksup(TSup_Templatetoksupi);(* recurse *)auxbefore_sup;auxrest(*
* TODO: have_a_tsup_quite_close does not handle a relational < followed
* by a regular template.
*)|{t=TIdent(_,i1)}::({t=TInfi2}astok2)::xswhenno_space_betweeni1i2&&(* safe guard, and good style anyway *)have_a_tsup_quite_close(Common.take_safetemplateLOOKAHEADxs)->change_toktok2(TInf_Templatei2);let(before_sup,(toksup,toksupi),rest)=find_tsup_quite_closetok2xsinchange_toktoksup(TSup_Templatetoksupi);(* old: was changing to TIdent_Templatename but now first need
* to do the typedef inference and then can transform the
* TIdent_Typedef into a TIdent_Templatename
*)(* recurse *)auxbefore_sup;auxrest(* special cases which allow extra space between ident and <
* but I think it would be better for people to fix their code
* | {t=TIdent (s,i1)}::({t=TInf i2} as tok2)
* ::tok3::({t=TSup i4} as tok4)::xs ->
* ...
*
*)(* recurse *)|_::xs->auxxsinauxxs(*****************************************************************************)(* Main heuristics *)(*****************************************************************************)letreclassify_tokens_before_idents_or_typedefsxs=letgroups=List.revxsinletrecauxxs=matchxswith|[]->()(* xx::yy where yy is ident (funcall, variable, etc)
* need to do that recursively! if have a::b::c
*)|Tok{t=TIdent_|TIdent_ClassnameInQualifier_}::Tok{t=TColCol_}::Tok({t=TIdent(s2,i2)}astok2)::xs->change_toktok2(TIdent_ClassnameInQualifier(s2,i2));aux((Toktok2)::xs)(* xx::t wher et is a type
* TODO need to do that recursively! if have a::b::c
*)|Tok{t=TIdent_Typedef_}::Tok({t=TColColicolcol}astcolcol)::Tok({t=TIdent(s2,i2)}astok2)::xs->change_toktok2(TIdent_ClassnameInQualifier_BeforeTypedef(s2,i2));change_toktcolcol(TColCol_BeforeTypedeficolcol);auxxs(* xx::t<...> where t is a templatename *)|Tok{t=TIdent_Templatename_}::Tok({t=TColColicolcol}astcolcol)::Tok({t=TIdent(s2,i2)}astok2)::xs->change_toktok2(TIdent_ClassnameInQualifier_BeforeTypedef(s2,i2));change_toktcolcol(TColCol_BeforeTypedeficolcol);auxxs(* t<...> where t is a typedef *)|Angle(_,xs_angle,_)::Tok({t=TIdent_Typedef(s1,i1)}astok1)::xs->auxxs_angle;change_toktok1(TIdent_Templatename(s1,i1));(* recurse with tok1 too! *)aux(Toktok1::xs)(* TODO
* TIdent_TemplatenameInQualifier ?
*)|x::xs->(matchxwith|Tok_->()|Braces(_,xs,_)|Parens(_,xs,_)|Angle(_,xs,_)->aux(List.revxs));auxxsinauxgroups;()(* quite similar to filter_for_typedef
* TODO: at some point need have to remove this and instead
* have a correct filter_for_typedef that also returns
* nested types in template arguments (and some
* typedef heuristics that work on template_arguments too)
*
* TODO: once you don't use it, remove certain grammar rules (C-s TODO)
*)letfind_template_commentizegroups=(* remove template *)letrecauxxs=xs|>List.iter(function|TV.Braces(_,xs,_)->auxxs|TV.Parens(_,xs,_)->auxxs|TV.Angle(_,_xs,_)asangle->(* let's commentize everything *)[angle]|>TV.iter_token_multi(funtok->change_toktok(TComment_Cpp(Token_cpp.CplusplusTemplate,TH.info_of_toktok.t)))|TV.Toktok->(* todo? should also pass the static_cast<...> which normally
* expect some TInf_Template after. Right mow I manage
* that by having some extra rules in the grammar
*)(matchtok.twith|Ttemplate_->change_toktok(TComment_Cpp(Token_cpp.CplusplusTemplate,TH.info_of_toktok.t))|_->()))inauxgroups(* assumes a view without:
* - template arguments
*
* TODO: once you don't use it, remove certain grammar rules (C-s TODO)
*
* note: passing qualifiers is slightly less important than passing template
* arguments because they are before the name (as opposed to templates
* which are after) and most of our heuristics for typedefs
* look tokens forward, not backward (actually a few now look backward too)
*)letfind_qualifier_commentizexs=letrecauxxs=matchxswith|[]->()|({t=TIdent_}ast1)::({t=TColCol_}ast2)::xs->[t1;t2]|>List.iter(funtok->change_toktok(TComment_Cpp(Token_cpp.CplusplusQualifier,TH.info_of_toktok.t)));auxxs(* need also to pass the top :: *)|({t=TColCol_}ast2)::xs->[t2]|>List.iter(funtok->change_toktok(TComment_Cpp(Token_cpp.CplusplusQualifier,TH.info_of_toktok.t)));auxxs(* recurse *)|_::xs->auxxsinauxxs(* assumes a view where:
* - set_context_tag has been called.
* TODO: filter the 'explicit' keyword? filter the TCppDirectiveOther
* have a filter_for_constructor?
*)letfind_constructorxs=letrecauxxs=matchxswith|[]->()(* { Foo(... *)|{t=(TOBrace_|TCBrace_|TPtVirg_|Texplicit_);_}::({t=TIdent(s1,i1);where=(TV.InClassStructs2)::_;_}astok1)::{t=TOPar_}::xswhens1=s2->change_toktok1(TIdent_Constructor(s1,i1));auxxs(* public: Foo(... could also filter the privacy directives so
* need only one rule
*)|{t=(Tpublic_|Tprotected_|Tprivate_)}::{t=TCol_}::({t=TIdent(s1,i1);where=(TV.InClassStructs2)::_;_}astok1)::{t=TOPar_}::xswhens1=s2->change_toktok1(TIdent_Constructor(s1,i1));auxxs(* recurse *)|_::xs->auxxsinauxxs(* assumes a view where:
* - template have been filtered but NOT the qualifiers!
*)letfind_constructor_outside_classxs=letrecauxxs=matchxswith|[]->()|{t=TIdent(s1,_);_}::{t=TColCol_}::({t=TIdent(s2,i2);_}astok)::xswhens1=s2->change_toktok(TIdent_Constructor(s2,i2));aux(tok::xs)(* recurse *)|_::xs->auxxsinauxxs(* assumes have:
* - the typedefs
* - the right context
*
* TODO: filter the TCppDirectiveOther, have a filter_for_constructed?
*)letfind_constructed_object_and_morexs=letrecauxxs=matchxswith|[]->()|{t=(Tdelete_|Tnew_);_}::({t=TOCroi1}astok1)::({t=TCCroi2}astok2)::xs->change_toktok1(TOCro_newi1);change_toktok2(TCCro_newi2);auxxs(* xx yy(1 ... *)|{t=TIdent_Typedef_;_}::{t=TIdent_;_}::({t=TOPar(ii);where=InArgument::_;_}astok1)::xs->change_toktok1(TOPar_CplusplusInitii);auxxs(* int yy(1 ... *)|{t=tok;_}::{t=TIdent_;_}::({t=TOPar(ii);where=InArgument::_;_}astok1)::xswhenTH.is_basic_typetok->change_toktok1(TOPar_CplusplusInitii);auxxs(* xx& yy(1 ... *)|{t=TIdent_Typedef_;_}::{t=TAnd_}::{t=TIdent_;_}::({t=TOPar(ii);where=InArgument::_;_}astok1)::xs->change_toktok1(TOPar_CplusplusInitii);auxxs(* xx yy(zz)
* The InArgument heuristic can't guess anything when just have
* idents inside the parenthesis. It's probably a constructed
* object though.
* TODO? could be a function declaration, especially when at Toplevel.
* If inside a function, then very probably a constructed object.
*)|{t=TIdent_Typedef_;_}::{t=TIdent_;_}::({t=TOPar(ii);}astok1)::{t=TIdent_;_}::{t=TCPar_}::xs->change_toktok1(TOPar_CplusplusInitii);auxxs(* xx yy(zz, ww) *)|{t=TIdent_Typedef_;_}::{t=TIdent_;_}::({t=TOPar(ii);}astok1)::{t=TIdent_;_}::{t=TComma_}::{t=TIdent_;_}::{t=TCPar_}::xs->change_toktok1(TOPar_CplusplusInitii);auxxs(* xx yy(&zz) *)|{t=TIdent_Typedef_;_}::{t=TIdent_;_}::({t=TOPar(ii);}astok1)::{t=TAnd_}::{t=TIdent_;_}::{t=TCPar_}::xs->change_toktok1(TOPar_CplusplusInitii);auxxs(* int(), probably part of operator declaration
* could check that token before is a 'operator'
*)|({t=kind})::{t=TOPar_}::{t=TCPar_}::xswhenTH.is_basic_typekind->auxxs(* int(...) unless it's int( * xxx ) *)|({t=_kind})::{t=TOPar_}::{t=TMul_}::xs->auxxs|({t=kind}astok1)::{t=TOPar_}::xswhenTH.is_basic_typekind->letnewone=matchkindwith|Tcharii->Tchar_Constrii|Tshortii->Tshort_Constrii|Tintii->Tint_Constrii|Tdoubleii->Tdouble_Constrii|Tfloatii->Tfloat_Constrii|Tlongii->Tlong_Constrii|Tboolii->Tbool_Constrii|Tunsignedii->Tunsigned_Constrii|Tsignedii->Tsigned_Constrii|_->raiseImpossibleinchange_toktok1newone;auxxs(* recurse *)|_::xs->auxxsinauxxs