123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691(**************************************************************************)(* *)(* This file is part of the Frama-C's MetACSL plug-in. *)(* *)(* Copyright (C) 2018-2025 *)(* CEA (Commissariat à l'énergie atomique et aux énergies *)(* alternatives) *)(* *)(* 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, version 2.1. *)(* *)(* It 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. *)(* *)(* See the GNU Lesser General Public License version 2.1 *)(* for more details (enclosed in the file LICENSE) *)(* *)(**************************************************************************)openMeta_utilsopenMeta_optionsopenMeta_parseopenMeta_dispatchopenCil_typesletafter_presentp=letvis=object(_)inheritVisitor.frama_c_inplacevalmutablefound=falsemethodget()=foundmethod!vlogic_label=function|FormalLabel"After"->found<-true;Cil.DoChildren|_->Cil.DoChildrenendinignore(Visitor.visitFramacPredicate(vis:>Visitor.frama_c_visitor)p);vis#get()letreplace_after_beforeis_afterstmtpred=letvis=object(_)inheritVisitor.frama_c_inplacemethod!vlogic_label=function|FormalLabel"After"whenis_after->Cil.ChangeTo(BuiltinLabelHere)|FormalLabel"Before"whenis_after->Cil.ChangeTo(StmtLabel(refstmt))|FormalLabel"Before"->Cil.ChangeTo(BuiltinLabelHere)|_->Cil.DoChildrenendinVisitor.visitFramacPredicatevispred(*
* Add an unique C label to a statement if it does not have one already
*)letlabel_counter=ref0letadd_labelstmt=letloc=Current_loc.get()inletlabel_name="meta_pre_"^(string_of_int!label_counter)inifstmt.labels=[]then(stmt.labels<-(Label(label_name,loc,false))::stmt.labels;label_counter:=!label_counter+1)letdelayed_instances=Stmt_Hashtbl.create256letdelayed_contracts=Fundec_Hashtbl.create256letcfg_recomputation=refFundec_Set.empty(* Common class for contexts *)classcontext_visitorflagsall_mptable=object(self)inheritVisitor.frama_c_inplacevalmutablemp_todo=[]valskip_to_add=Stmt_Hashtbl.create50(* Fill mp_todo with MPs to process for the given function name *)methodfill_todofn=lettodo=find_hash_listStr_Hashtbl.find_opttablefninmp_todo<-List.map(Hashtbl.findall_mp)todo(* Determine is the current function is a target for some of the properties
* in the current context *)method!vfuncf=self#fill_todof.svar.vname;ifmp_todo=[]thenCil.SkipChildrenelse(Stmt_Hashtbl.clearskip_to_add;Cil.DoChildren)methodprivatecurr_func_cfg_recompute()=letf=Option.getself#current_funcincfg_recomputation:=Fundec_Set.addf!cfg_recomputation(* Remember that we must add a SKIP after the statement for an annotation to
* be put *)methodprivatecreate_next_stmtstmt=self#curr_func_cfg_recompute();letloc=Current_loc.get()inletnew_stmt=Cil.mkStmtOneInstr~valid_sid:true(Skiploc)inadd_to_hash_listStmt_Hashtbl.(find_opt,replace)skip_to_addstmtnew_stmt;new_stmt(* Insert registered SKIPs in corresponding blocks *)method!vblock_=Cil.DoChildrenPost(funblock->letrecaux=function|[]->[]|s::t->s::(find_hash_listStmt_Hashtbl.find_optskip_to_adds)@(auxt)inblock.bstmts<-auxblock.bstmts;block)(* Common method used by the subclasses to instantiate an ump *)methodinstantiate?(post=None)?(force_after=false)stmtassoc_listump=letloc=Property.locationump.ump_ipinletkf=Option.getself#current_kfinletvf=Kernel_function.get_vikfinletfunc_ptr=Cil_builder.Exp.(cil_term~loc(addr(varvf)))inletfunc_assoc="\\func",RepVariablefunc_ptrinletassoc_list=func_assoc::assoc_listin(* Do not instantiate anything on ghost statements *)ifnotstmt.ghostthenletpred=ump.ump_propertykf~stmtassoc_listin(* Apply potential post transformation *)letpost_pred=matchpostwith|None->pred|Somef->fpredin(* Simplify *)(* Discard if trivially true *)ifnot@@Logic_utils.is_trivially_truepost_predthen(* Check if the annotation should be inserted before of after the
* current statement *)letafter_pres=after_presentpost_predinletwill_be_after=force_after||after_presin(* Only add a pre C label when there is a legitimate reason *)ifafter_presthenadd_labelstmt;(* Create a SKIP after the stmt to attach the annot to *)letannot_stmt=ifwill_be_afterthenself#create_next_stmtstmtelsestmtin(* Replace After/Before by correct actual labels *)letlabelled=replace_after_beforewill_be_afterstmtpost_predinletfinalize()=letnamed=Meta_dispatch.name_mp_predflagslabelledump.ump_counterinletkind=ifump.ump_assertthenAssertelseCheckinletannot=Logic_const.(new_code_annotation(AAssert([],toplevel_predicate~kindnamed)))inAnnotations.add_code_annotump.ump_emitter~kfannot_stmtannot;letips=Property.ip_of_code_annotkfannot_stmtannotinMeta_dispatch.add_dependencyumpipsin(* Delay actual instanciation for correct numbering *)add_to_hash_listStmt_Hashtbl.(find_opt,replace)delayed_instancesstmtfinalizeendletget_callsites_ipskfrips=letcalls=Kernel_function.find_syntactic_callsiteskfinmatchcallswith(* if the function is never called (e.g. it is the entry point), then
do not set up a proxy over a vacuous set of properties. *)|[]->[]|_->letsegmented=List.map(funrip->Statuses_by_call.setup_precondition_proxykfrip;List.map(fun(_,stmt)->Statuses_by_call.precondition_at_callkfripstmt)calls)ripsinList.concatsegmented(* Simply modifies a function contract to ensures that pred is a weak invariant *)classweak_inv_visitorflagsfull_tabletable(pre,post)=object(self)inheritcontext_visitorflagsfull_tabletable(* Given a kf and a UMP, add it as requires/ensures to the kf *)methodadd_to_contractump=letkf=Option.getself#current_kfinletpred=ump.ump_propertykf[]inletfinalize()=letpred'=Meta_dispatch.name_mp_predflagspredump.ump_counterinletpred_name=(Emitter.get_nameump.ump_emitter)::pred'.pred_nameinletpred''={pred'withpred_name}inletkind=ifump.ump_assertthenAssertelseCheckinletdeps=ref[]inifprethen(letip_requires=Logic_const.new_predicate~kindpred''inAnnotations.add_requiresump.ump_emitterkf[ip_requires];letdefb=Option.get(Cil.find_default_behavior(Annotations.funspeckf))inletrips=Property.ip_of_requireskfKglobaldefbip_requires::!depsinletcallsites_ip=get_callsites_ipskfripsindeps:=rips@callsites_ip@!deps);beginmatchpostwith|`None->()|`Strict->letip_ensures=Logic_const.new_predicate~kindpred''inAnnotations.add_ensuresump.ump_emitterkf[(Normal,ip_ensures)];letdefb=Option.get(Cil.find_default_behavior(Annotations.funspeckf))inleteip=Property.ip_of_ensureskfKglobaldefb(Normal,ip_ensures)indeps:=eip::!deps|`Conditional->letip_assumes=Logic_const.new_predicate~kindpred''inletip_ensures=Logic_const.new_predicate~kindpred''inletbh={b_name=List.hdpred_name;b_requires=[];b_assumes=[ip_assumes];b_post_cond=[(Normal,ip_ensures)];b_assigns=WritesAny;b_allocation=FreeAllocAny;b_extended=[]}inAnnotations.add_behaviorsump.ump_emitterkf[bh];end;Meta_dispatch.add_dependencyump!depsin(* Delay actual instanciation for correct numbering *)letfundec=Kernel_function.get_definitionkfinadd_to_hash_listFundec_Hashtbl.(find_opt,replace)delayed_contractsfundecfinalizemethod!vfuncf=self#fill_todof.svar.vname;List.iter(self#add_to_contract)mp_todo;Cil.SkipChildrenend(* Given a tlval, return the term taking its address *)letaddr_of_tlvaltlval=lettyp=Cil.typeOfTermLvaltlvalinLogic_utils.mk_logic_AddrOftlvaltypletaddr_of_tlhost(h,_)=addr_of_tlval(h,TNoOffset)(* Instantiate properties at any call site, replacing \called by
* the called function. *)classcalling_visitorflagsall_mptable=object(self)inheritcontext_visitorflagsall_mptablemethodcalling_instancestmtcalledcalled_kfargs=letaddr_term=addr_of_tlvalcalledinletmain_assoc=("\\called",RepVariableaddr_term)inletassoc_list=matchcalled_kfwith|None->Self.warning~once:true"\\called_arg cannot be used with indirect calls";[main_assoc]|Somekf->letformals=Kernel_function.get_formalskfintryletparam_assocs=List.map2(funformalgiven->letgiven_term=Logic_utils.expr_to_termgivenin(formal.vorig_name,given_term))formalsargsin[main_assoc;("\\called_arg",RepAppparam_assocs)]withInvalid_argument_->Self.warning~once:true"\\called_arg cannot be used with variadic \
functions. Please use the Instantiate plugin";[main_assoc]inList.iter(self#instantiatestmtassoc_list)mp_todomethod!vinst=function|Call(_,fexpr,args,_)->letlv=matchfexpr.enodewithLvallv->lv|_->assertfalseinletstmt=Option.getself#current_stmtinletcalled_kf=Kernel_function.get_calledfexprinlettlval=Logic_utils.lval_to_term_lvallvinself#calling_instancestmttlvalcalled_kfargs;Cil.DoChildren|_->Cil.SkipChildrenend(*
* Retrieve the specification from a kf, replace every instance of its formal
* parameters by the actual arguments passed, extract "assigns" clauses and pass
* them to "process"
*)letinspect_contractprocessstmtckfargs=letvis=object(_)inheritVisitor.frama_c_inplacevalfargs=leth=Hashtbl.create(List.lengthargs)inlet_,oargs,_,_=Cil.splitFunctionType(Kernel_function.get_typeckf)inif(List.lengthargs)!=0thenList.iter2(fun(a,_,_)b->Hashtbl.addhab)(Option.getoargs)args;hmethod!vterm_=Cil.DoChildrenPost(funterm->matchterm.term_nodewith|TLval(host,_)|TAddrOf(host,_)|TStartOf(host,_)->beginmatchhostwith|TVarlovar->ifHashtbl.memfargslovar.lv_namethenLogic_utils.expr_to_term@@Hashtbl.findfargslovar.lv_nameelseterm|_->termend|_->term)endinletfb=letass=Annotations.fold_assumes(fun_pold->(Visitor.visitFramacPredicatevis(Logic_const.pred_of_id_predp))::old)ckfb.b_name[]inmatchb.b_assignswith|WritesAny->Self.warning"Cannot analyze footprint of function %a: no assigns \
clause. Assuming meta-properties with reading/writing contexts are \
valid in this function."Kernel_function.prettyckf|Writesl->processckfstmtvislassinAnnotations.iter_behaviorsfckf(* Instantiate properties at any point where a modification can happen, that is:
* - common assignments (including the result of a function)
* - calls to an external function (if the flag is set)
*)classwriting_visitorflagsall_mptable=object(self)inheritcontext_visitorflagsall_mptable(* Instantiates the property while replacing \written terms *)methodwriting_instance?poststmt=function|TVarlv,_whenflags.simpl&&Meta_simplify.is_not_orig_variablelv->()|written->letaddr_term=addr_of_tlvalwritteninletbase_addr_term=addr_of_tlhostwritteninletassoc_list=["\\written",RepVariableaddr_term;"\\lhost_written",RepVariablebase_addr_term]inList.iter(self#instantiate~poststmtassoc_list)mp_todo(* Inspect function specs to see what it assigns and instantiate the
* annotation for each assigns clause (with the precond leading to it) *)methodprivateexternal_assigns_stmtvislpreconds=List.iter(fun(it,_)->letterm=it.it_contentinletpropag=Visitor.visitFramacTermvisterminifnot@@Logic_utils.contains_resultpropagthenmatchpropag.term_nodewith|TLvalt->letpostprop=List.fold_left(funpropprec->Logic_const.pimplies(prec,prop))propprecondsinself#writing_instance~poststmtt;|_->assertfalse)l(* Instantiate an annotation for each affectation (potentially resulting
* from a call) and each call to an undefined function *)method!vstmt_auxstmt=ifnotstmt.ghostthen(matchstmt.skindwith|InstrSet(lval,_,_)->(* x = y, \written -> &x *)lettl=Logic_utils.lval_to_term_lvallvalinself#writing_instancestmttl;Cil.SkipChildren|InstrCall(lval,fexp,args,(source,_))->ifOption.is_somelvalthen((* x = f(y), \written -> &x *)lettl=Logic_utils.lval_to_term_lval(Option.getlval)inself#writing_instancestmttl);(matchKernel_function.get_calledfexpwith|Somekf->letcaller=Option.getself#current_kfinletcallee_def=Kernel_function.has_definitionkfinletis_check_assignsf=Kernel_function.Set.memfflags.check_callee_assignsinif(notcallee_def&&flags.check_external)||is_check_assignskf&¬(is_check_assignscaller)theninspect_contractself#external_assignsstmtkfargs|None->Self.warning~source"Indirect call '%a': assuming no 'writing' context-related \
assertions are needed"Printer.pp_expfexp;);Cil.SkipChildren|_->Cil.DoChildren)elseCil.SkipChildrenendmoduleTLvalSet=Cil_datatype.Term_lval.SetmoduleStmtHashtbl=Cil_datatype.Stmt.Hashtblclassreading_visitorflagsall_mptable=object(self)inheritcontext_visitorflagsall_mptable(* Instantiates the property while replacing \read terms *)methodreading_instance?poststmtread=letaddr_term=addr_of_tlvalreadinletbase_addr_term=addr_of_tlhostreadinletassoc_list=["\\read",RepVariableaddr_term;"\\lhost_read",RepVariablebase_addr_term]inList.iter(self#instantiate~poststmtassoc_list)mp_todo(* Hashtable : stmt -> set of tlvals used in it. Used to avoid duplicate
* annotations *)vallvals_by_stmt=StmtHashtbl.create20valmutablein_exp=false(* Visitor currently in an expression *)(* Allow detecting if we are currently in an expression *)method!vexpre=in_exp<-true;matche.enodewith|SizeOfE_|AlignOfE_->(* we're not evaluating the expression itself, merely its type. *)in_exp<-false;Cil.SkipChildren|AddrOf(_,o)|StartOf(_,o)->(* the toplevel lval is not read. However, we might read some lvals
when evaluating the offset. The host is always a Var in this
context, thus we don't need to visit it further. *)ignore(Visitor.visitFramacOffset(self:>Visitor.frama_c_visitor)o);in_exp<-false;Cil.SkipChildren|_->Cil.DoChildrenPost(fune->in_exp<-false;e)(* When encoutering an lval in an expression, add it to the statement set *)method!vlvallval=lettyp=Cil.typeOfLvallvalin(* Exclude functions names *)ifin_exp&¬(Ast_types.is_funtyp)thenbeginifOption.is_some(self#current_stmt)thenletstmt=Option.get(self#current_stmt)inbeginmatchstmt.skindwith|UnspecifiedSequence_->()(* ignore attributes from US node *)|_->lettl=Logic_utils.lval_to_term_lvallvalinletold_set=matchStmtHashtbl.find_optlvals_by_stmtstmtwith|Somes->s|None->TLvalSet.emptyinStmtHashtbl.addlvals_by_stmtstmt@@TLvalSet.addtlold_setendend;Cil.DoChildren(* Inspect function specs to see what it reads and instantiate the
* annotation for each \from clause (with the precond leading to it) *)methodpassthrough_fromkfstmtvislpreconds=List.iter(fun(_,deps)->matchdepswith|FromAny->Self.warning~once:true"Cannot fully analyze read footprint of function %a: no \\from \
part in some assigns clauses."Kernel_function.prettykf|Fromitl->List.iter(funit->letterm=it.it_contentinletpropag=Visitor.visitFramacTermvisterminmatchpropag.term_nodewith|TCast(false,_,{term_node=TLvalt})|TLvalt->letpostproperty=List.fold_left(funpropprec->Logic_const.pimplies(prec,prop))propertyprecondsinself#reading_instance~poststmtt|_->())itl)lmethodpassthrough_processstmtkfargs=letcond=(not(Kernel_function.has_definitionkf)&&flags.check_external)||Kernel_function.Set.(memkfflags.check_callee_assigns&¬(mem(Option.getself#current_kf)flags.check_callee_assigns))inifcondtheninspect_contractself#passthrough_fromstmtkfargsmethod!vstmt_auxstmt=ifnotstmt.ghostthen((* If the statement is a call to an undefined function, use its spec to
* generate \read *)beginmatchstmt.skindwith|InstrLocal_init(_,ConsInit(f,args,Plain_func),_)->letkf=Globals.Functions.getfinself#passthrough_processstmtkfargs|InstrCall(_,fexp,args,(source,_))->beginmatchKernel_function.get_calledfexpwith|Someckf->self#passthrough_processstmtckfargs|None->Self.warning~source"Indirect call '%a': assuming no 'reading' context related \
assertion are needed"Printer.pp_expfexpend|_->()end;letafters=letset=StmtHashtbl.find_optlvals_by_stmtsinifOption.is_somesetthenTLvalSet.iter(self#reading_instances)@@Option.getset;sin(* Visit every sub-expression to populate the tlval set then for each
* lval, instantiate the property. *)Cil.DoChildrenPostafter)elseCil.DoChildrenend(* Return the set of used lvals in a given predicate *)letlvals_of_predicatepred=lets=ref(SomeTLvalSet.empty)inletvis=object(self)inheritVisitor.frama_c_inplacemethod!vterm_lvaltlval=s:=Option.map(TLvalSet.addtlval)!s;Cil.DoChildrenmethod!vpredicate_node=function|Papp(li,_,_)->beginmatchli.l_bodywith|LBnone->()|LBinductive_|LBreads_->s:=None|LBpredp->ignore(Visitor.visitFramacPredicate(self:>Visitor.frama_c_visitor)p)|LBtermt->ignore(Visitor.visitFramacTerm(self:>Visitor.frama_c_visitor)t)end;Cil.DoChildren|_->Cil.DoChildrenendinignore(Visitor.visitFramacPredicatevispred);!s(*
* Modifies the function contract and add asserts to ensures that pred is a weak invariant
* AND that each assignment maintains the invariant
* A block of instructions can be left unchecked using `//@meta lenient` but this block must
* maintain the invariant at the end
*)classstrong_inv_visitorflagsall_mptable=object(self)inheritweak_inv_visitorflagsall_mptable(true,`Strict)valmutablelenient=false(* Given a lval which is to be modified, check if its modification
* can interfere with the truth of the invariant (only rejects obvious non-interferences)
*)methodpossible_interferencelvalump=lettlval=Logic_utils.lval_to_term_lvallvalinletkf=Option.get(self#current_kf)inletpred=ump.ump_propertykf[]inletpvars=lvals_of_predicatepredinmatchpvarswith|Someset->not(TLvalSet.fold(funpvar->(&&)(Meta_simplify.neq_lvalpvartlval))settrue)(* Could not determine what variables are used in the predicate *)|None->true(* Check the statement contract of a statement to check if a lenient is present *)methodcheck_annotsstmt=letaux_a=matcha.annot_contentwith|AExtended(_,_,e)->beginmatchewith|{ext_name="imeta";ext_kind=Ext_terms[{term_node=TConst(LStr"lenient")}]}->lenient<-true|_->()end|_->()inAnnotations.iter_code_annotauxstmtmethod!vstmt_auxstmt=self#check_annotsstmt;letnon_loopmodifump=(* If a lenient was found, automatically instantiate the predicate
* since we are not sure the invariant will be maintained inside the
* statement. Else do it only if interference with the invariant is
* possible (ie the statement may modify a variable used in the
* invariant *)letcan_interfere=notflags.simpl||matchmodifwith|None->false|Somem->self#possible_interferencemumpiniflenient||can_interferethenself#instantiate~force_after:truestmt[]umpinbeginmatchstmt.skindwith|Loop_->letkf=Option.get(self#current_kf)inList.iter(funump->letprop=ump.ump_propertykf~stmt[]inletkind=ifump.ump_assertthenAssertelseCheckinletannot=Logic_const.(new_code_annotation(AInvariant([],true,toplevel_predicate~kindprop)))inAnnotations.add_code_annotump.ump_emitter~kfstmtannot)mp_todo|InstrSet(lval,_,_)->List.iter(non_loop(Somelval))mp_todo|InstrCall_->List.iter(self#instantiate~force_after:truestmt[])mp_todo|_->List.iter(non_loopNone)mp_todoend;iflenientthen(lenient<-false;Cil.SkipChildren)elseCil.DoChildrenmethod!vfuncf=self#fill_todof.svar.vname;List.iter(self#add_to_contract)mp_todo;ifmp_todo=[]thenCil.SkipChildrenelseCil.DoChildrenend(* Make one copy-pass for each context, typing and instanciating MPs on the fly *)letannotateflagsall_mpby_context=letget_vistable=function|Weak_invariant->(newweak_inv_visitorflagsall_mptable(true,`Strict):>Visitor.frama_c_visitor)|Calling->(newcalling_visitorflagsall_mptable:>Visitor.frama_c_visitor)|Writing->(newwriting_visitorflagsall_mptable:>Visitor.frama_c_visitor)|Reading->(newreading_visitorflagsall_mptable:>Visitor.frama_c_visitor)|Strong_invariant->(newstrong_inv_visitorflagsall_mptable:>Visitor.frama_c_visitor)|Precond->(newweak_inv_visitorflagsall_mptable(true,`None):>Visitor.frama_c_visitor)|Postcond->(newweak_inv_visitorflagsall_mptable(false,`Strict):>Visitor.frama_c_visitor)|Conditional_invariant->(newweak_inv_visitorflagsall_mptable(false,`Conditional):>Visitor.frama_c_visitor)inList.iter(fun(ctx,table)->letvis=get_vistablectxinVisitor.visitFramacFileSameGlobalsvis(Ast.get()))by_context;(* Finally, actually instanciate all annotations in the correct order *)letfinal_vis=object(_)inheritVisitor.frama_c_inplacemethod!vfuncf=(* Reset assertion numbers *)Hashtbl.iter(fun_ump->ump.ump_counter:=0)all_mp;(* Instantiate delayed contracts *)lettodo=find_hash_listFundec_Hashtbl.find_optdelayed_contractsfinList.iter(funf->f())(List.revtodo);Cil.DoChildrenmethod!vstmt_auxstmt=(* Instantiate delayed annotations *)lettodo=find_hash_listStmt_Hashtbl.find_optdelayed_instancesstmtinList.iter(funf->f())(List.revtodo);Cil.DoChildrenendinVisitor.visitFramacFileSameGlobalsfinal_vis(Ast.get());letupdate_cfgf=Cfg.clearCFGinfo~clear_id:falsef;Cfg.cfgFunfinFundec_Set.iterupdate_cfg!cfg_recomputation;ifFundec_Set.is_empty!cfg_recomputationthen(* slight overapproximation: if we don't generate any annotation, then
the AST is unchanged. *)Ast.mark_as_grown()elseAst.mark_as_changed()