123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674(* Yoann Padioleau
*
* Copyright (C) 2010, 2014 Facebook
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation, with the
* special exception on linking described in file license.txt.
*
* This library 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.
*)openCommonopenCst_cppopenEntity_codeopenHighlight_codemoduleS=Scope_codemodulePI=Parse_infomoduleAst=Cst_cppmoduleV=Visitor_cppmoduleLib=Lib_parsing_cppmoduleT=Parser_cppmoduleTH=Token_helpers_cppmoduleType=Type_cpp(*****************************************************************************)(* Prelude *)(*****************************************************************************)(*
* TODO:
* - take into account class qualifier as a use
* - take inheritance as a class use
*)(*****************************************************************************)(* Helpers *)(*****************************************************************************)let(==~)=Common2.(==~)(*****************************************************************************)(* Helpers when have global analysis information *)(*****************************************************************************)leth_debug_functions=Common.hashset_of_list["DEBUG"]letfake_no_def2=NoUseletfake_no_use2=(NoInfoPlace,UniqueDef,MultiUse)(*****************************************************************************)(* Code highlighter *)(*****************************************************************************)letvisit_toplevel~tag_hook_prefs(*db_opt *)(toplevel,toks)=letalready_tagged=Hashtbl.create101inlettag=(funiicateg->tag_hookiicateg;Hashtbl.addalready_taggediitrue)in(* -------------------------------------------------------------------- *)(* Toks phase 1 *)(* -------------------------------------------------------------------- *)letrecaux_toksxs=matchxswith|[]->()(* a little bit pad specific *)|T.TComment(ii)::T.TCommentNewline(_ii2)::T.TComment(ii3)::T.TCommentNewline(_ii4)::T.TComment(ii5)::xs->lets=PI.str_of_infoiiinlets5=PI.str_of_infoii5in(match()with|_whens=~".*\\*\\*\\*\\*"&&s5=~".*\\*\\*\\*\\*"->tagiiCommentEstet;tagii5CommentEstet;tagii3CommentSection1|_whens=~".*------"&&s5=~".*------"->tagiiCommentEstet;tagii5CommentEstet;tagii3CommentSection2|_whens=~".*####"&&s5=~".*####"->tagiiCommentEstet;tagii5CommentEstet;tagii3CommentSection0|_->());aux_toksxs;lets=PI.str_of_infoiiinlets2=PI.str_of_infoii3in(match()with|_whens=~"//////////.*"&&s2=~"// .*"->tagii3CommentSection1|_->());aux_toksxs|T.TComment(ii)::xswhen(PI.str_of_infoii)=~"//IMPORTANT:"->tagiiCommentSection2;aux_toksxs(* heuristic for class/struct definitions.
*
* Must be before the heuristic for function definitions
* otherwise this pattern will never be exercised
*
* the code below has been commented because it generates
* too much false positives. For instance in 'struct foo * bar(...) '
* foo would be classified as the start of a struct definition
*
* don't want forward class declarations to generate noise
* | (T.Tclass(ii) | T.Tstruct(ii) | T.Tenum(ii))
* ::T.TCommentSpace ii2::T.TIdent(s, ii3)::T.TPtVirg _::xs ->
* aux_toks xs
* | (T.Tclass(ii) | T.Tstruct(ii) | T.Tenum (ii)
* | T.TIdent ("service", ii)
* )
* ::T.TCommentSpace ii2::T.TIdent(s, ii3)::xs
* when Ast.col_of_info ii = 0 ->
*
* tag ii3 (Class (Def2 fake_no_def2));
* aux_toks xs;
*)|(T.Tclass(ii)|T.Tstruct(ii)|T.Tenum(ii)(* thrift stuff *)|T.TIdent("service",ii))::T.TCommentSpace_ii2::T.TIdent(_s,ii3)::T.TCommentSpace_ii4::T.TOBrace_ii5::xswhenPI.col_of_infoii=0->tagii3(Entity(Class,(Def2fake_no_def2)));aux_toksxs;(* heuristic for function definitions *)|t1::xswhen(t1|>TH.info_of_tok|>PI.col_of_info=0)&&TH.is_not_commentt1->letline_t1=TH.line_of_tokt1inletrecfind_ident_parenxs=matchxswith|T.TIdent(_s,ii1)::T.TOPar_::_->tagii1(Entity(Function,(Def2NoUse)));|T.TIdent(_s,ii1)::T.TCommentSpace_::T.TOPar_::_->tagii1(Entity(Function,(Def2NoUse)));|_::xs->find_ident_parenxs|[]->()inletsame_line=(t1::xs)|>Common2.take_while(funt->TH.line_of_tokt=line_t1)infind_ident_parensame_line;aux_toksxs|_x::xs->aux_toksxsinaux_tokstoks;letis_at_toplevel=reftruein(* -------------------------------------------------------------------- *)(* Ast phase 1 *)(* -------------------------------------------------------------------- *)letvisitor=V.mk_visitor{(*V.default_visitor with *)V.kinfo=(fun(_k,_)_x->());V.kcompound=(fun(k,_)x->Common.save_excursionis_at_toplevelfalse(fun()->kx));V.kblock_decl=(fun(k,_)x->matchxwith|DeclList(xs_comma,_)->xs_comma|>Ast.uncomma|>List.iter(funonedecl->onedecl.v_namei|>Common.do_option(fun(name,_ini_opt)->letstorage=onedecl.v_storageinletcateg=matchstoragewith|StoTypedef_->Entity(Type,Def2fake_no_def2)|_whenType.is_function_typeonedecl.v_type->FunctionDeclNoUse(* could be a global too when the decl is at the top *)|Sto(Extern,_)->Entity(Global,(Def2fake_no_def2))|_when!is_at_toplevel->Entity(Global,(Def2fake_no_def2))|_->LocalDefinAst.ii_of_id_namename|>List.iter(funii->tagiicateg)););kx|MacroDecl_->kx|(Asm(_,_,_,_)|NameSpaceAlias(_,_,_,_,_)|UsingDirective(_,_,_,_)|UsingDecl_)->());V.kstmt=(fun(k,_)x->matchxwith|Labeled(Ast.Label(_s,_st)),ii->ii|>List.iter(funii->tagiiKeywordExn);kx|Jump(Goto_s),ii->let(_iigoto,lblii,_iiptvirg)=Common2.tuple_of_list3iiintaglbliiKeywordExn;kx|_->kx);V.kexpr=(fun(k,_)x->letebis,_=xinmatchebiswith|Id(name,idinfo)->(matchnamewith|(_,_,IdIdent(s,ii))->(* the Call case might have already tagged it with something *)ifnot(Hashtbl.memalready_taggedii)thenifs==~Parsing_hacks_lib.regexp_macrothentagii(Entity(Constant,(Use2fake_no_use2)))else(matchidinfo.Ast.i_scopewith|S.NoScope->()|S.Local->tagii(LocalUse)|S.Param->tagii(ParameterUse)|S.Global->tagii(Entity(Global,(Use2fake_no_use2)));(* todo? could invent a Static in highlight_code ? *)|S.Static->tagii(Entity(Global,(Use2fake_no_use2)));(* TODO *)|S.Class->()(* todo? valid only for PHP? *)|(S.ListBinded|S.LocalIterator|S.LocalExn|S.Closed)->failwith"scope not handled")|_->())|Call(e,_args)->(matchunwrapewith|Id(name,scope)->(matchnamewith|_,_,IdIdent(s,ii)->ifHashtbl.memh_debug_functionssthentagiiBuiltinCommentColorelse(matchscope.i_scopewith|S.Local|S.Param->tagiiPointerCall|_->tagii(Entity(Function,(Use2fake_no_use2))))|_->());|RecordAccess(_e,name)|RecordPtAccess(_e,name)->Ast.ii_of_id_namename|>List.iter(funii->letfile=PI.file_of_infoiiinifFile_type.file_type_of_filefile=*=File_type.PL(File_type.C"c")thentagiiPointerCallelsetagii(Entity(Method,(Use2fake_no_use2))))|_->(* dynamic stuff, should highlight! *)letii=Lib.ii_of_any(Expre)inii|>List.iter(funii->tagiiPointerCall););kx|RecordAccess(_e,name)|RecordPtAccess(_e,name)->(matchnamewith|_,_,IdIdent(_s,ii)->ifnot(Hashtbl.memalready_taggedii)thentagii(Entity(Field,(Use2fake_no_use2)));|_->());kx|New(_colon,_tok,_placement,ft,_args)->(matchftwith|_nq,((TypeName(name)),_)->Ast.ii_of_id_namename|>List.iter(funii->tagii(Entity(Class,(Use2fake_no_use2)));)|_->());kx|_->kx);V.kinit=(fun(k,_)x->matchxwith|InitDesignators(xs,_,_init)->xs|>List.iter(function|DesignatorField(_tok,(_s,tok2))->tagtok2(Entity(Field,(Use2fake_no_use2)))|_->());kx|_->kx);V.kparameter=(fun(k,_)x->(matchx.p_namewith|Some(_s,ii)->tagii(ParameterDef)|None->());kx);V.ktypeC=(fun(k,_)x->let(typeCbis,_)=xinmatchtypeCbiswith|TypeName(name)->Ast.ii_of_id_namename|>List.iter(funii->(* new Xxx and other places have priority *)ifnot(Hashtbl.memalready_taggedii)thentagii(Entity(Type,Use2fake_no_use2)));kx|Cst_cpp.EnumName(_tok,(_s,ii))->tagii(Entity(Type,Use2fake_no_use2))|StructUnionName(_su,(_s,ii))->tagii(StructNameUse);kx|TypenameKwd(_tok,name)->Ast.ii_of_id_namename|>List.iter(funii->tagii(Entity(Type,Use2fake_no_use2)));kx|EnumDef(_tok,_sopt,xs)->xs|>unbrace|>uncomma|>List.iter(funenum_elem->let(_,ii)=enum_elem.e_nameintagii(Entity(Constructor,(Def2fake_no_def2))));kx|_->kx);V.kfieldkind=(fun(k,_)x->matchxwith|FieldDeclonedecl->onedecl.v_namei|>Common.do_option(fun(name,_ini_opt)->letkind=(* poor's man object using function pointer; classic C idiom *)ifType.is_method_typeonedecl.v_typethenEntity(Method,(Def2fake_no_def2))elseEntity(Field,(Def2NoUse))inAst.ii_of_id_namename|>List.iter(funii->tagiikind));kx|BitField(sopt,_tok,_ft,_e)->(matchsoptwith|Some(_s,iiname)->tagiiname(Entity(Field,(Def2NoUse)))|None->()));V.kclass_def=(fun(k,_)def->letname=def.c_nameinname|>Common.do_option(funname->Ast.ii_of_id_namename|>List.iter(funii->tagii(Entity(Class,(Def2fake_no_def2)));));kdef);V.kfunc_def=(fun(k,_)def->letname=def.f_nameinAst.ii_of_id_namename|>List.iter(funii->ifnot(Hashtbl.memalready_taggedii)thentagii(Entity(Class,(Def2fake_no_def2))););kdef);V.kclass_member=(fun(k,_)def->(matchdefwith|MemberFuncx->letdef=matchxwith|FunctionOrMethoddef|Ast.Constructordef|Destructordef->definletname=def.f_nameinAst.ii_of_id_namename|>List.iter(funii->tagii(Entity(Method,(Def2fake_no_def2))));|(EmptyField_|UsingDeclInClass_|TemplateDeclInClass_|QualifiedIdInClass(_,_)|MemberField_|MemberDecl_|Access(_,_))->());kdef);V.kcpp=(fun(k,_)def->(matchdefwith|Ast.Define(_,_id,DefineFuncparams,_body)->params|>Ast.unparen|>Ast.uncomma|>List.iter(fun(name)->matchnamewith|(_s,[ii])->tagii(ParameterDef)|_->())|_->());kdef);V.kdeclaration=(fun(k,_)def->kdef);V.ktoplevel=(fun(k,_)def->(matchdefwith|NotParsedCorrectlyii->ii|>List.iter(funii->tagiiNotParsed)|_->());kdef);}invisitor(Topleveltoplevel);(* -------------------------------------------------------------------- *)(* toks phase 2 *)(* -------------------------------------------------------------------- *)toks|>List.iter(funtok->matchtokwith|T.TCommentii->ifnot(Hashtbl.memalready_taggedii)then(* a little bit syncweb specific *)lets=PI.str_of_infoiiin(matchswith(* yep, s e x are the syncweb markers *)|_whens=~"/\\*[sex]:"->tagiiCommentSyncweb|_->tagiiComment)|T.TInt(_,ii)|T.TFloat(_,ii)->tagiiNumber|T.TString(_s,ii)|T.TChar(_s,ii)->tagiiString|T.Tfalseii|T.Ttrueii->tagiiBoolean|T.TPtVirgii|T.TOParii|T.TOPar_CplusplusInitii|T.TOPar_Defineii|T.TCParii|T.TOBraceii|T.TOBrace_DefineInitii|T.TCBraceii|T.TOCroii|T.TCCroii|T.TDotii|T.TCommaii|T.TPtrOpii|T.TAssign(_,ii)|T.TEqii|T.TWhyii|T.TTildeii|T.TBangii|T.TEllipsisii|T.TColii->tagiiPunctuation|T.TIncii|T.TDecii|T.TOrLogii|T.TAndLogii|T.TOrii|T.TXorii|T.TAndii|T.TEqEqii|T.TNotEqii|T.TInfii|T.TSupii|T.TInfEqii|T.TSupEqii|T.TShlii|T.TShrii|T.TPlusii|T.TMinusii|T.TMulii|T.TDivii|T.TModii->tagiiOperator|T.Tshortii|T.Tintii->tagiiTypeInt|T.Tdoubleii|T.Tfloatii|T.Tlongii|T.Tunsignedii|T.Tsignedii|T.Tcharii->tagiiTypeInt(* TODO *)|T.Tvoidii->tagiiTypeVoid|T.Tboolii|T.Twchar_tii->tagiiTypeInt(* thrift stuff *)(* needed only when have FP in the typedef inference *)|T.TIdent(("string"|"i32"|"i64"|"i8"|"i16"|"byte"(* | "list" | "map" | "set"
| "binary"
*)),ii)->tagiiTypeInt|T.Tautoii|T.Tregisterii|T.Texternii|T.Tstaticii|T.Tconstii|T.Tconst_MacroDeclConstii|T.Tvolatileii|T.Tbreakii|T.Tcontinueii|T.Treturnii|T.Tdefaultii|T.Tsizeofii|T.Trestrictii->tagiiKeyword|T.Tgotoii->(* people often use goto as an try/throw exception mechanism
* so let's use the same color
*)tagiiKeyword|T.Tasmii|T.Tattributeii|T.Tinlineii|T.Ttypeofii->tagiiKeyword(* pp *)|T.TDefineii->tagiiDefine|T.TUndef(_,ii)->tagiiDefine(* todo: could be also a MacroFunc *)|T.TIdent_Define(_,ii)->tagii(Entity(Constant,(Def2NoUse)))(* TODO: have 2 tokens?
| T.TInclude_Filename (_, ii) ->
tag ii String
| T.TInclude_Start (ii, _aref) ->
tag ii Include
*)|T.TInclude(_,_,ii)->tagiiInclude|T.TIfdefii|T.TIfdefelseii|T.TIfdefelifii|T.TEndifii->tagiiIfdef|T.TIfdefBool(_,ii)|T.TIfdefMisc(_,ii)|T.TIfdefVersion(_,ii)->tagiiIfdef|T.TCppDirectiveOtherii->tagiiCppOther|T.Tnamespaceii->tagiiKeywordModule|T.Tthisii->tagii(Entity(Class,(Use2fake_no_use2)))|T.Tnewii|T.Tdeleteii->tagiiKeywordObject|T.Tvirtualii->tagiiKeywordObject|T.Ttemplateii|T.Ttypeidii|T.Ttypenameii|T.Toperatorii|T.Tpublicii|T.Tprivateii|T.Tprotectedii|T.Tfriendii|T.Tusingii|T.Tconst_castii|T.Tdynamic_castii|T.Tstatic_castii|T.Treinterpret_castii|T.Texplicitii|T.Tmutableii|T.Texportii->tagiiKeyword|T.TPtrOpStarii|T.TDotStarii->tagiiPunctuation|T.TColColii|T.TColCol_BeforeTypedefii->tagiiPunctuation|T.Ttypedefii|T.Tunionii|T.Tenumii->tagiiKeyword|T.Tifii|T.Telseii->tagiiKeywordConditional|T.Tswitchii|T.Tcaseii->tagiiKeywordConditional|T.Ttryii|T.Tcatchii|T.Tthrowii->tagiiKeywordExn(* thrift *)|T.TIdent(("throws"|"exception"),ii)->tagiiKeywordExn|T.Tforii|T.Tdoii|T.Twhileii->tagiiKeywordLoop|T.Tclassii|T.Tstructii->tagiiKeywordObject(* thrift *)|T.TIdent(("service"|"include"|"extends"),ii)->tagiiKeyword(* should be covered by the xxx_namei case above *)|T.TIdent(_,_ii)->()(* should be covered by TypeName above *)|T.TIdent_Typedef_|T.TIdent_TemplatenameInQualifier_BeforeTypedef_|T.TIdent_TemplatenameInQualifier_|T.TIdent_TypedefConstr_|T.TIdent_Constructor_|T.TIdent_Templatename_|T.TIdent_ClassnameInQualifier_BeforeTypedef_|T.TIdent_ClassnameInQualifier_|T.TIdent_MacroIterator_|T.TIdent_MacroDecl_|T.TIdent_MacroString_|T.TIdent_MacroStmt_->()|T.Tbool_Constrii|T.Tlong_Constrii|T.Tshort_Constrii|T.Twchar_t_Constrii|T.Tdouble_Constrii|T.Tfloat_Constrii|T.Tint_Constrii|T.Tchar_Constrii|T.Tunsigned_Constrii|T.Tsigned_Constrii->tagiiTypeInt|T.TInt_ZeroVirtual_|T.TCCro_new_|T.TOCro_new_->()|T.TSup_Templateii|T.TInf_Templateii->tagiiKeyword|T.TAny_Action_|T.TCPar_EOL_->()(* TODO *)|T.TComment_Pp(kind,ii)->(matchkindwith|Token_cpp.CppMacroExpanded|Token_cpp.CppPassingNormal->tagiiExpanded|_->tagiiPassed)|T.TComment_Cpp(_kind,ii)->tagiiPassed|T.TDefParamVariadic_|T.TCommentNewline_|T.TCommentNewline_DefineEndOfMacro_|T.TCppEscapedNewline_|T.TCommentSpace_|T.EOF_->()|T.TUnknownii->tagiiError);()