123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2017-2019 The MOPSA Project. *)(* *)(* This program is free software: you can redistribute it and/or modify *)(* it under the terms of the GNU Lesser General Public License as published *)(* by the Free Software Foundation, either version 3 of the License, or *)(* (at your option) any later version. *)(* *)(* 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 *)(* GNU Lesser General Public License for more details. *)(* *)(* You should have received a copy of the GNU Lesser General Public License *)(* along with this program. If not, see <http://www.gnu.org/licenses/>. *)(* *)(****************************************************************************)(** Positions and ranges *)(** {2 Positions} *)(** ============= *)typepos={pos_file:string;(** File name. *)pos_line:int;(** Line number. *)pos_column:int;(** Column number. *)}(** Position in a file. *)letget_pos_filep=p.pos_fileletget_pos_linep=p.pos_lineletget_pos_columnp=p.pos_columnletmk_posfilelinecolumn={pos_file=file;pos_line=line;pos_column=column}(** Comparison function of positions. *)letcompare_pos(pos1:pos)(pos2:pos)=ifpos1==pos2then0elseCompare.compose[(fun()->comparepos1.pos_filepos2.pos_file);(fun()->comparepos1.pos_linepos2.pos_line);(fun()->comparepos1.pos_columnpos2.pos_column);](** Return the relative path of `file` w.r.t. the current working directory *)letrelative_pathfile=letwd=Sys.getcwd()inifStr.string_match(Str.regexp("^"^(Str.quotewd)^"/.+"))file0thenletn1=String.lengthwdinletn2=String.lengthfilein"./"^String.subfile(n1+1)(n2-n1-1)elsefile(** {2 Ranges} *)(** ========== *)(** Location range of AST nodes. *)typerange=|R_programofstringlist(** list of source files *)(** Program range covering a list source files *)|R_origofpos(** start position. *)*pos(** end position *)(** Original source range with a start and an end positions. *)|R_freshofint(** non-original fresh range with unique id *)(** Fresh ranges with unique identifiers *)|R_taggedofrange_tag*range(** Tagged range with an annotation *)(** Range tags can be used to annotate AST nodes added by the abstract
domains that are not textually present in the source files. *)andrange_tag=|String_tagofstring|Range_tagofrangeletmk_tagged_rangetagrange=R_tagged(tag,range)letmk_string_tagfmt=Format.kasprintf(funtag->String_tagtag)fmtletmk_range_tagrange=Range_tagrangeletmk_range_tagged_rangertagrange=R_tagged(mk_range_tagrtag,range)(** Tag a range with a (formatted) annotation. *)lettag_rangerangefmt=Format.kasprintf(funtag->R_tagged(String_tagtag,range))fmtletmk_orig_rangepos1pos2=R_orig(pos1,pos2)letfresh_range_counter=ref0letmk_fresh_range()=incrfresh_range_counter;R_fresh!fresh_range_counterletmk_program_rangepl=R_programplletrecuntag_range=function|R_tagged(_,range)->untag_rangerange|range->rangeletrecmap_tagf=function|R_tagged(t,r)->R_tagged(t,map_tagfr)|r->frletget_range_startr=matchuntag_rangerwith|R_orig(pos,_)->pos|R_program[p]->mk_posp00|_->failwith"get_range_start: invalid argument"letget_range_endr=matchuntag_rangerwith|R_orig(_,pos)->pos|_->failwith"get_range_end: invalid argument"letset_range_startrl=map_tag(funr->matchrwith|R_orig(_,l')->R_orig(l,l')|_->failwith"set_range_start: called on non R_source")rletset_range_endrl'=map_tag(funr->matchrwith|R_orig(l,_)->R_orig(l,l')|_->failwith"set_range_end: called on non R_source")rletget_range_filer=matchuntag_rangerwith|R_orig(pos,_)->pos.pos_file|R_program[p]->p|_->failwith"get_range_file: invalid argument"letget_range_relative_filer=get_range_filer|>relative_pathletget_range_liner=letpos=get_range_startrinpos.pos_lineletget_range_columnr=letpos=get_range_startrinpos.pos_columnletis_orig_range=function|R_orig_->true|_->falseletis_program_range=function|R_program_->true|_->falseletmatch_range_filefiler=letpredf=Str.string_match(Str.regexp(".*"^(Str.quotefile)^"$"))f0||Str.string_match(Str.regexp(".*"^(Str.quotef)^"$"))file0inmatchuntag_rangerwith|R_orig(p,_)->predp.pos_file|R_programpl->List.existspredpl|_->falseletmatch_range_lineliner=matchuntag_rangerwith|R_orig(p,_)->p.pos_line=line|_->falseletfrom_lexing_pospos=letopenLexingin{pos_file=pos.pos_fname;pos_line=pos.pos_lnum;pos_column=pos.pos_cnum-pos.pos_bol;}letfrom_lexing_rangepos1pos2=mk_orig_range(from_lexing_pospos1)(from_lexing_pospos2)(** Comparison function of ranges. *)letreccompare_range(r1:range)(r2:range)=ifr1==r2then0elsematchr1,r2with|R_programpl1,R_programpl2->Compare.listcomparepl1pl2|R_orig(l1,l2),R_orig(l1',l2')->Compare.compose[(fun()->compare_posl1l1');(fun()->compare_posl2l2');]|R_tagged(t1,r1),R_tagged(t2,r2)->Compare.compose[(fun()->compare_ranger1r2);(fun()->compare_range_tagt1t2)]|R_fresh(uid1),R_fresh(uid2)->compareuid1uid2|_->comparer1r2andcompare_range_tagtag1tag2=matchtag1,tag2with|String_tags1,String_tags2->compares1s2|Range_tagr1,Range_tagr2->compare_ranger1r2|_->comparetag1tag2letsubset_range(r1:range)(r2:range):bool=ifr1==r2thentrueelsematchuntag_ranger1,untag_ranger2with|R_programpl1,R_programpl2->Compare.listcomparepl1pl2=0|R_fresh(uid1),R_fresh(uid2)->uid1=uid2|R_orig(l1,l2),R_orig(l1',l2')->l1.pos_file=l1'.pos_file&&l2.pos_file=l2'.pos_file&&(l1.pos_line>l1'.pos_line||(l1.pos_line=l1'.pos_line&&l1.pos_column>=l1'.pos_column))&&(l2.pos_line<l2'.pos_line||(l2.pos_line=l2'.pos_line&&l2.pos_column<=l2'.pos_column))|_->false(** {2 Range annotations} *)(** ===================== *)type'awith_range={content:'a;range:range;}letwith_rangearange={content=a;range;}letget_contenta=a.contentletget_rangea=a.rangeletbind_range(a:'awith_range)?(range=a.range)(f:'a->'b):'bwith_range={content=fa.content;range}letbind_pair_range(a:'awith_range)(f:'a->'b*'c):'bwith_range*'c=letb,c=fa.contentin{content=b;range=a.range},cletcompare_with_rangecmpab=Compare.compose[(fun()->compare_rangea.rangeb.range);(fun()->cmpa.contentb.content);](** {2 Pretty printers} *)(** =================== *)letpp_positionfmtpos=Format.fprintffmt"%s:%d:%d"pos.pos_filepos.pos_linepos.pos_columnletpp_relative_positionfmtpos=Format.fprintffmt"%s:%d:%d"(relative_pathpos.pos_file)pos.pos_linepos.pos_columnletrecpp_rangefmtrange=matchrangewith|R_programpl->Format.fprintffmt"{%a}"(Format.pp_print_list~pp_sep:(funfmt()->Format.fprintffmt", ")Format.pp_print_string)pl|R_orig(pos1,pos2)whenpos1.pos_file==pos2.pos_file&&pos1.pos_line==pos2.pos_line&&pos1.pos_column==pos2.pos_column->pp_positionfmtpos1|R_orig(pos1,pos2)whenpos1.pos_file==pos2.pos_file&&pos1.pos_line==pos2.pos_line->Format.fprintffmt"%s:%d.%d-%d"pos1.pos_filepos1.pos_linepos1.pos_columnpos2.pos_column|R_orig(pos1,pos2)whenpos1.pos_file==pos2.pos_file->Format.fprintffmt"%s:%d.%d-%d.%d"pos1.pos_filepos1.pos_linepos1.pos_columnpos2.pos_linepos2.pos_column|R_orig(pos1,pos2)->Format.fprintffmt"%a-%a"pp_positionpos1pp_positionpos2|R_freshuid->Format.fprintffmt"<%d>"uid|R_tagged(String_tagt,r)->Format.fprintffmt"%a::%s"pp_rangert|R_tagged(Range_tagrr,r)->Format.fprintffmt"%a:$%a"pp_rangerpp_rangerrletrecpp_relative_rangefmtrange=(* keep tagged range, pp_relative_range is used to generate addr partitioning unique names *)matchrangewith|R_programpl->Format.fprintffmt"{%a}"(Format.pp_print_list~pp_sep:(funfmt()->Format.fprintffmt", ")Format.pp_print_string)pl|R_orig(pos1,pos2)whenpos1.pos_file==pos2.pos_file&&pos1.pos_line==pos2.pos_line&&pos1.pos_column==pos2.pos_column->pp_relative_positionfmtpos1|R_orig(pos1,pos2)whenpos1.pos_file==pos2.pos_file&&pos1.pos_line==pos2.pos_line->Format.fprintffmt"%s:%d.%d-%d"(relative_pathpos1.pos_file)pos1.pos_linepos1.pos_columnpos2.pos_column|R_orig(pos1,pos2)whenpos1.pos_file==pos2.pos_file->Format.fprintffmt"%s:%d.%d-%d.%d"(relative_pathpos1.pos_file)pos1.pos_linepos1.pos_columnpos2.pos_linepos2.pos_column|R_orig(pos1,pos2)->Format.fprintffmt"%a-%a"pp_relative_positionpos1pp_relative_positionpos2|R_freshuid->Format.fprintffmt"<%d>"uid|R_tagged(String_tagt,r)->Format.fprintffmt"%a::%s"pp_relative_rangert|R_tagged(Range_tagrr,r)->Format.fprintffmt"%a:$%a"pp_relative_rangerpp_relative_rangerr