123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852(**************************************************************************)(* *)(* This file is part of Frama-C. *)(* *)(* Copyright (C) 2007-2023 *)(* 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 licenses/LGPLv2.1). *)(* *)(**************************************************************************)moduleRte=structtypestatus_accessor=string*(Cil_types.kernel_function->bool->unit)*(Cil_types.kernel_function->bool)letget_all_status:(unit->status_accessorlist)optionref=refNoneletregister_get_all_statusgetter=get_all_status:=SomegetterendopenDesignopenCil_typesopenProperty_status(* Collect all properties that have a status *)letall_properties()=letglobals=refProperty.Set.emptyinletfunctions=refKernel_function.Map.emptyin(* Dispatch properties into globals and per-function map *)Property_status.iter(funip->matchProperty.get_kfipwith|None->globals:=Property.Set.addip!globals|Somekf->ifnot(Ast_info.is_frama_c_builtin(Kernel_function.get_namekf))thentryletfips=Kernel_function.Map.findkf!functionsinfips:=Property.Set.addip!fipswithNot_found->letips=Property.Set.singletonipinfunctions:=Kernel_function.Map.addkf(refips)!functions);!functions,!globalstypeproperty={module_name:Datatype.Filepath.t;function_name:string;kind:string;status_name:string;consolidated_status:Consolidation.consolidated_statusoption;consolidated_status_name:string;status_icon:Gtk_helper.Icon.kind;visible:bool;ip:Property.t;}letkf_name_and_modulekf=letname=Kernel_function.get_namekfinletloc=Kernel_function.get_locationkfinletfile=(fstloc).Filepath.pos_pathinname,fileletmake_propertyip=letstatus=Property_status.getipinletstatus_name=Format.asprintf"%a"Property_status.prettystatusinletcon_status=Consolidation.getipinletconsolidated_status_name=Format.asprintf"%a"Consolidation.prettycon_statusinletfunction_name,module_name=matchProperty.get_kfipwith|None->"",Datatype.Filepath.dummy(* TODO: it would be great to find the location
of global invariants or lemmas, but there isn't
enough information in the ast *)|Somekf->kf_name_and_modulekfinletkind=Format.asprintf"@[<hov>%a@]"Property.prettyipinletstatus_icon=Gtk_helper.Icon.Feedback(Feedback.getip)in{module_name=module_name;function_name=function_name;visible=true;ip=ip;kind=kind;status_name=status_name;consolidated_status=Somecon_status;consolidated_status_name=consolidated_status_name;status_icon=status_icon;}moduleRefreshers:sigtypecheck={id:int(* used to compare checks *);add:GPack.box->unit;get:unit->bool;set:bool->unit;reset:unit->unit(* change to default state if needed *);}valonlyCurrent:checkvalensures:checkvalextended:checkvalpreconditions:checkvalbehaviors:checkvalcomplete_disjoint:checkvalallocations:checkvalassigns:checkvalfrom:checkvaluser_assertions:checkvaluser_checks:checkvaluser_admits:checkvalrte:checkvalinvariant:checkvalvariant:checkvalterminates:checkvalstmtSpec:checkvalreachable:checkvalother:checkvalinstances:checkvallemmas:checkvalaxiomatic:checkvaltypeInvariants:checkvalglobalInvariants:checkvalrteNotGenerated:checkvalrteGenerated:checkvalvalid:checkvalvalidHyp:checkvalunknown:checkvalinvalid:checkvalinvalidHyp:checkvalconsidered_valid:checkvaluntried:checkvaldead:checkvalinconsistent:checkvalactive_alarm:Alarms.t->boolvalpack:GPack.box->unitvalset_refresh_needed:(bool->unit)refvalapply:unit->unitvalall_checks:unit->checklistend=struct(* Function to be called during the idle time of the GUI *)letrefreshers=ref[]letadd_refresherf=refreshers:=f::!refreshersletset_refresh_needed=ref(fun_->(*will be defined later*)())letapply()=List.iter(funf->f())!refresherstypecheck={id:int(* unique ID, used to compare checks *);add:GPack.box->unit(* pack the corresponding checkbox in the argument*);get:unit->bool(* state of the checkbox (set/unset *);set:bool->unit(* change checkbox state *);reset:unit->unit(* change to default state if needed *);}letlast_id=ref0letnext_id()=incrlast_id;!last_idletchecks:checklistref=ref[]letall_checks()=List.rev!checks(* ref below used by [add] to set the configuration, since it must be delayed
until the session directory has been set *)letfirst_extended_ref=reftrue(* This function must always be called at OCaml toplevel, because it registers
a new Frama-C state. *)letadd~name~hint?(default=true)?(set=(fun_b->()))()=letopenGtk_helperinletkey_name=String.map(func->ifc<'A'||c>'z'||(c>'Z'&&c<'a')then'_'elsec)nameinletkey_name="property_panel."^key_nameinletmoduleM=State_builder.Ref(Datatype.Bool)(structletname="show "^nameletdependencies=[]letdefault()=defaultend)inletget=M.getinlet()=Cmdline.run_after_extended_stage(fun()->(* avoid loading the configuration file several times *)if!first_extended_refthenbeginfirst_extended_ref:=false;Configuration.load()end);Cmdline.run_after_loading_stage(fun()->letv=Configuration.find_bool~defaultkey_nameinM.setv)inletsetv=Configuration.setkey_name(Configuration.ConfBoolv);setv;!set_refresh_neededtrue;M.setvinletaddhb=add_refresher(Gtk_helper.on_bool~tooltip:hinthbnamegetset)inletreset()=ifget()<>defaultthensetdefaultinletchk={id=next_id();get;set;add;reset}inchecks:=chk::!checks;chklethint_rtehint=ifOption.is_some!Rte.get_all_statusthenhintelsehint^" (but RteGen plugin is inactive)"letonlyCurrent=add~name:"Current function"~default:false~hint:"Only show properties related to current function"()letpreconditions=add~name:"Preconditions"~hint:"Show function preconditions"()letensures=add~name:"Postconditions"~hint:"Show function postconditions"()letextended=add~name:"Extended"~hint:"Show extended function annotation"()letbehaviors=add~name:"Behaviors"~default:false~hint:"Show function behaviors"()letcomplete_disjoint=add~name:"Complete/disjoint"~hint:"Show complete/disjoint behaviors"()letallocations=add~name:"Allocations"~hint:"Show function allocations"()letassigns=add~name:"Assigns"~hint:"Show function assigns"()letfrom=add~name:"From"()~hint:"Show functional dependencies in function assigns"letuser_assertions=add~name:"User assertions"~hint:"Show user assertions"()letuser_checks=add~name:"User checks"~hint:"Show user checks"()letuser_admits=add~name:"User admits"~hint:"Show user hypotheses"()(* Function called when RTEs are enabled or disabled. *)letset_rte=ref(fun_b->())letrte=add~set:(funb->!set_rteb)~name:"RTEs"~hint:(hint_rte"Show runtime errors")()letinvariant=add~name:"Invariant"~hint:"Show loop invariants"()letvariant=add~name:"Variant"~hint:"Show loop termination argument"()letterminates=add~name:"Terminates"~hint:"Show function termination clauses"()letstmtSpec=add~name:"Stmt contract"~hint:"Show statement contracts"()letlemmas=add~name:"Lemmas"~hint:"Show lemmas"()letaxiomatic=add~name:"Axiomatic"~default:false~hint:"Show global axiomatics"()letinstances=add~name:"Instances"~hint:"Show properties that are instances of root properties"()lettypeInvariants=add~name:"Type invariants"~hint:"Show type invariants"()letglobalInvariants=add~name:"Global invariants"~hint:"Show global invariants"()letother=add~name:"Other"~hint:"Show other properties"()letreachable=add~default:false~name:"Reachable"~hint:"Show 'reachable' hypotheses"()letrteNotGenerated=add~default:false~name:"Non generated"~hint:(hint_rte"Show RTEs assertions that remain to generate")()letrteGenerated=add~default:false~name:"Generated"~hint:(hint_rte"Show RTEs assertions that have been generated")()letvalid=add~name:"Valid"~hint:"Show properties that are proven valid"()letvalidHyp=add~name:"Valid under hyp."~hint:"Show properties that are are valid, but depend on some hypotheses"()letunknown=add~name:"Unknown"~hint:"Show properties with an 'unknown' status"()letinvalid=add~name:"Invalid"~hint:"Show properties that are proven invalid"()letinvalidHyp=add~name:"Invalid under hyp."~hint:"Show properties that are are invalid, but depend on \
some hypotheses"()letconsidered_valid=add~name:"Considered valid"~default:false~hint:"Show properties that are considered valid because \
the platform has no way to prove them"()letuntried=add~name:"Untried"~default:false~hint:"Show properties whose proof has not been attempted"()letdead=add~name:"Dead"~default:false~hint:"Show properties on unreachable code"()letinconsistent=add~name:"Inconsistent"~hint:"Show properties that have an inconsistent status"()letmake_expand(box:GPack.box)?tooltiptext=letkey_config="Properties."^textinletexpanded=Gtk_helper.Configuration.find_bool~default:truekey_configinletexpander=GBin.expander~expanded~packing:box#pack()inignore(expander#connect#activate~callback:(fun()->(* Save expansion of panels*)Gtk_helper.Configuration.setkey_config(Gtk_helper.Configuration.ConfBool(notexpander#expanded))));lethb=GPack.vbox~packing:expander#add()inletmarkup=Printf.sprintf"<span font_weight=\"bold\">%s</span>"textinletlabel=GMisc.label~markup()inGtk_helper.do_tooltip?tooltiplabel;expander#set_label_widgetlabel#coerce;hb,expander(* [list_alarms] is the instantiation of [add] for all the various kind
of alarms. It is computed by ad hoc introspection on the reprs field of the
datatype. [active_alarm] finds the category of the alarm, and returns
whether it should be shown according to the corresponding checkbox. *)letlist_alarms,active_alarm=(*[h] maps alarms hints to the corresponding [get] checkbox. *)leth=Datatype.String.Hashtbl.create16inletauxalarm=(* instantiates [add] for the category of [alarm] *)letname=Alarms.get_short_namealarminlethint=Alarms.get_descriptionalarminlet({get}ascheck)=add~name~hint()inDatatype.String.Hashtbl.addh(Alarms.get_short_namealarm)get;checkinletactive_alarmalarm=try(Datatype.String.Hashtbl.findh(Alarms.get_short_namealarm))()withNot_found->Gui_parameters.warning"Unregistered alarm type";true(* should not happen *)inList.mapauxAlarms.reprs,active_alarmletpack(box:GPack.box)=(* let hb = make_expand box
~tooltip:"Locations of the properties that are shown" "Where"
in *)onlyCurrent.add(*hb*)box;lethb,_=make_expandbox~tooltip:"Validity status of the properties that are shown""Status"invalid.addhb;validHyp.addhb;unknown.addhb;invalid.addhb;invalidHyp.addhb;considered_valid.addhb;untried.addhb;dead.addhb;inconsistent.addhb;lethb,_=make_expandbox~tooltip:"Which properties (precondition, assertion, etc) are shown""Kind"inpreconditions.addhb;ensures.addhb;extended.addhb;behaviors.addhb;complete_disjoint.addhb;allocations.addhb;assigns.addhb;from.addhb;user_assertions.addhb;user_checks.addhb;user_admits.addhb;rte.addhb;invariant.addhb;variant.addhb;terminates.addhb;stmtSpec.addhb;axiomatic.addhb;lemmas.addhb;typeInvariants.addhb;globalInvariants.addhb;instances.addhb;other.addhb;reachable.addhb;(*Pragma.add hb;*)lethb_category,expand_category=make_expandbox~tooltip:"Category of runtime errors leading to the emission of an \
assertion. Enabled only when RTEs are displayed.""RTE category"inList.iter(funcheck_alarm->check_alarm.addhb_category)list_alarms;lethb,_=make_expandbox~tooltip:"Whether assertions against runtime errors of a certain class \
have been generated""RTE emission"inrteNotGenerated.addhb;rteGenerated.addhb;(* Register additional callbacks *)set_rte:=(funb->hb_category#misc#set_sensitiveb;ifnotbthenexpand_category#set_expandedfalse);!set_rte(rte.get())(* For the initial state *);;;endopenRefreshers(* Process the rte statuses for the given kf, and add the result in the
accumulator. Filter the statuses according to user-selected filters*)letaux_rtekfacc(name,_,rte_status_get:Rte.status_accessor)=letst=rte_status_getkfinmatchst,rteGenerated.get(),rteNotGenerated.get()with|true,true,_|false,_,true->(* Considered that leaf functions are not verified internally *)letstatus_name,status=ifstthenifKernel_function.is_definitionkfthen"Generated",Feedback.Validelse"Considered generated",Feedback.Considered_validelse"Not generated",Feedback.Invalidinletfunction_name,module_name=kf_name_and_modulekfinletstatus_icon=Gtk_helper.Icon.Feedbackstatusinletip=Property.ip_othername(Property.OLGlob(Kernel_function.get_locationkf))in{module_name=module_name;function_name=function_name;visible=true;ip=ip;kind=Format.asprintf"@[<hov>%a@]"Property.prettyip;status_name="";consolidated_status=None;consolidated_status_name=status_name;status_icon=status_icon;}::acc|true,false,_|false,_,false->accletproperties_tab_label=refNone(* Used to change dynamically the label of the "Properties" tab. *)(* Lists of checkboxes (used by popup menus) *)(* all checks:
preconditions; ensures; behaviors; allocations; assigns; from;
assertions; invariant; variant; terminates; stmtSpec; axiomatic;
typeInvariants; globalInvariants; instances; other; reachable; valid;
validHyp; unknown; invalid; invalidHyp; considered_valid; untried; dead;
inconsistent; rteNotGenerated; rteGenerated *)(* [reset_checks to_check to_uncheck] sets all checks in [to_check] and
unsets all checks in [to_uncheck], then refreshes the view. *)letreset_checksto_checkto_uncheck=List.iter(funchk->chk.settrue)to_check;List.iter(funchk->chk.setfalse)to_uncheck;Refreshers.apply()letreset_checks_default()=List.iter(funchk->chk.reset())(all_checks());Refreshers.apply()letunproven_checks_true=[unknown;invalid;invalidHyp;inconsistent]letunproven_checks_false=[valid;validHyp;considered_valid;untried;dead]letcheck_default()=reset_checks_default()letcheck_unproven()=reset_checksunproven_checks_trueunproven_checks_falseletcheck_all()=reset_checks(all_checks())[]letcheck_none()=reset_checks[](all_checks())letmake_panel(main_ui:main_window_extension_points)=letcontainer=GPack.hbox()inletvb_left=GPack.vbox~packing:container#pack()inlethb_refresh_reset=GPack.hbox~packing:vb_left#pack()inletrefresh_button=GButton.button~packing:(hb_refresh_reset#pack~expand:true)()inletrefresh_label=GMisc.label~markup:"<b>Refresh</b>"()inrefresh_button#addrefresh_label#coerce;Refreshers.set_refresh_needed:=(funb->ifbthenrefresh_label#set_label"<b>Refresh</b>"elserefresh_label#set_label"Refresh");(* button to modify all checkboxes according to presets *)letchecks_menu=GMenu.menu()inletmi_check_def=GMenu.menu_item~label:"Reset all filters to default"()inchecks_menu#addmi_check_def;ignore(mi_check_def#connect#activate~callback:(fun()->check_default()));letmi_check_unproven=GMenu.menu_item~label:"Reset 'Status' filters to show only unproven/invalid"()inchecks_menu#addmi_check_unproven;ignore(mi_check_unproven#connect#activate~callback:(fun()->check_unproven()));letmi_check_all=GMenu.menu_item~label:"Select all"()inchecks_menu#addmi_check_all;ignore(mi_check_all#connect#activate~callback:(fun()->check_all()));letmi_check_none=GMenu.menu_item~label:"Unselect all"()inchecks_menu#addmi_check_none;ignore(mi_check_none#connect#activate~callback:(fun()->check_none()));leticon=GMisc.image~stock:`INDEX()inletreset_menu_button=GButton.button~packing:(hb_refresh_reset#pack~expand:false)()inreset_menu_button#misc#set_tooltip_text"Reconfigure filters according to presets";reset_menu_button#addicon#coerce;ignore(reset_menu_button#connect#clicked~callback:(fun()->checks_menu#popup~button:0~time:(GtkMain.Main.get_current_event_time())));letsc_buttons=GBin.scrolled_window~vpolicy:`AUTOMATIC~hpolicy:`NEVER()inletvb=GPack.vbox()inRefreshers.packvb;sc_buttons#add_with_viewportvb#coerce;vb_left#addsc_buttons#coerce;letmoduleMODEL=Gtk_helper.MAKE_CUSTOM_LIST(structtypet=propertyend)inletmodel=MODEL.custom_list()inletappendm=ifm.visiblethenmodel#insertminletclear()=model#clear()in(* TODO: this avoids some problems when changing projects, where
the property navigator displays outdated information. A better solution
would be to projectify what is being displayed *)Design.register_reset_extension(fun_->clear();match!properties_tab_labelwith|None->()|Somelabel->GtkMisc.Label.set_textlabel"Properties");letsc=GBin.scrolled_window~vpolicy:`AUTOMATIC~hpolicy:`AUTOMATIC~packing:(container#pack~expand:true~fill:true)()inletview=GTree.view~rules_hint:true~headers_visible:true~packing:sc#add()inview#selection#set_select_function(funpathcurrently_selected->ifnotcurrently_selectedthenbeginmatchmodel#custom_get_iterpathwith|Some{MODEL.finfo={ip=ip;}}->ignore(main_ui#scroll(Printer_tag.PIPip));(* Note: the code below generates double scrolling:
the previous call to main_ui#scroll causes the original source
viewer to scroll to the beginning of the function, and then
the code below re-scrolls it to the exact statement. *)main_ui#view_original(Property.locationip)|None->()end;true);lettop=`YALIGN0.0inletmake_view_columnrendererproperties~title=letcview=MODEL.make_view_columnmodelrendererproperties~titleincview#set_resizabletrue;ignore(view#append_columncview)in(* Function name column viewer *)make_view_column(GTree.cell_renderer_text[top])(function{function_name=m}->[`TEXTm])~title:"Function";(* Module name column viewer *)make_view_column(GTree.cell_renderer_text[top])(function{module_name=m}->[`TEXT(Filepath.Normalized.to_pretty_stringm)])~title:"File";(* Kind name column viewer *)make_view_column(GTree.cell_renderer_text[top])(function{kind=k}->[`TEXTk])~title:"Kind";(* Status colored column viewer *)make_view_column(GTree.cell_renderer_pixbuf[top])(function{status_icon=status_icon}->[`PIXBUF(Gtk_helper.Icon.getstatus_icon)])~title:"Status";(* Consolidated status name column viewer *)make_view_column(GTree.cell_renderer_text[top])(function{consolidated_status_name=k}->[`TEXTk])~title:"Consolidated Status";(* (Local) status name column viewer *)make_view_column(GTree.cell_renderer_text[top])(function{status_name=k}->[`TEXTk])~title:"Local Status";view#set_model(Somemodel#coerce);letvisibleip=letopenPropertyinmatchipwith|IPOther_->other.get()|IPReachable_->reachable.get()|IPBehavior{ib_kinstr=Kglobal}->behaviors.get()|IPBehavior{ib_kinstr=Kstmt_}->behaviors.get()&&stmtSpec.get()|IPPredicate{ip_kind=PKRequires_;ip_kinstr=Kglobal}->preconditions.get()|IPPredicate{ip_kind=PKRequires_;ip_kinstr=Kstmt_}->preconditions.get()&&stmtSpec.get()|IPPredicate{ip_kind=PKAssumes_}->false|IPPredicate{ip_kind=PKEnsures_;ip_kinstr=Kglobal}->ensures.get()|IPExtended_->extended.get()|IPPredicate{ip_kind=PKEnsures_;ip_kinstr=Kstmt_}->ensures.get()&&stmtSpec.get()|IPPredicate{ip_kind=PKTerminates}->terminates.get()|IPTypeInvariant_->typeInvariants.get()|IPGlobalInvariant_->globalInvariants.get()|IPAxiomatic_->axiomatic.get()&¬(onlyCurrent.get())|IPLemma_->lemmas.get()|IPComplete_->complete_disjoint.get()|IPDisjoint_->complete_disjoint.get()|IPCodeAnnot{ica_ca={annot_content=AAssert(_,{tp_kind})}asca}->beginmatchAlarms.findcawith|Somea->rte.get()&&active_alarma|None->matchtp_kindwith|Assert->user_assertions.get()|Check->user_checks.get()|Admit->user_admits.get()end|IPCodeAnnot{ica_ca={annot_content=AInvariant_}}->invariant.get()|IPCodeAnnot{ica_ca={annot_content=APragmap}}->Logic_utils.is_property_pragmap(* currently always false. *)|IPCodeAnnot_->false(* status of inner nodes *)|IPAllocation{ial_kinstr=Kglobal}->allocations.get()|IPAllocation{ial_kinstr=Kstmt_;ial_bhv=Id_loop_}->allocations.get()|IPAllocation{ial_kinstr=Kstmt_;ial_bhv=Id_contract_}->allocations.get()&&stmtSpec.get()|IPAssigns{ias_kinstr=Kglobal}->assigns.get()|IPAssigns{ias_kinstr=Kstmt_;ias_bhv=Id_loop_}->assigns.get()|IPAssigns{ias_kinstr=Kstmt_;ias_bhv=Id_contract_}->assigns.get()&&stmtSpec.get()|IPFrom_->from.get()|IPDecrease_->variant.get()|IPPropertyInstance_->instances.get()inletvisible_status_aux=function|Consolidation.Never_tried->untried.get()|Consolidation.Considered_valid->considered_valid.get()|Consolidation.Valid_->valid.get()|Consolidation.Valid_under_hyp_->validHyp.get()|Consolidation.Unknown_->unknown.get()|Consolidation.Invalid_->invalid.get()|Consolidation.Invalid_under_hyp_->invalidHyp.get()|Consolidation.Invalid_but_dead_|Consolidation.Valid_but_dead_|Consolidation.Unknown_but_dead_->dead.get()|Consolidation.Inconsistent_->inconsistent.get()inletvisible_status=Option.fold~some:visible_status_aux~none:trueinletfill_model()=letadd_ipip=ifvisibleipthenletp=make_propertyipinifvisible_statusp.consolidated_statusthenappendpinletby_kf,globals=all_properties()in(* Add global properties at the top of the list *)Property.Set.iteradd_ipglobals;(* Will the results for this kf be ultimately displayed *)letdisplaykf=not(Cil_builtins.is_unused_builtin(Kernel_function.get_vikf))&¬(onlyCurrent.get())||(letkfvi=Kernel_function.get_vikfinList.exists(function|GFun({svar=fvi},_)|GFunDecl(_,fvi,_)->Cil_datatype.Varinfo.equalfvikfvi|_->false)main_ui#file_tree#selected_globals)inletrte_get_all_statuses=match!Rte.get_all_statuswith|None->[]|Someregistered_getter->registered_getter()in(* All non-filtered RTE statuses for a given function *)letrte_kfkf=List.fold_left(aux_rtekf)[]rte_get_all_statusesin(* Add RTE statuses for all functions. We cannot simply iterate over
[by_kf], as functions without any property will not be present in it *)letwith_rte=letauxkfacc=ifdisplaykfthenletprops=try!(Kernel_function.Map.findkfby_kf)withNot_found->Property.Set.emptyin(kf,(props,rte_kfkf))::accelseaccinGlobals.Functions.foldaux[]in(* Sort functions by names, in a case-insensitive way *)letcmp(k1,_)(k2,_)=Extlib.compare_ignore_case(Kernel_function.get_namek1)(Kernel_function.get_namek2)inletby_kf=List.sortcmpwith_rtein(* Add the properties for all the relevant functions *)List.iter(fun(kf,(ips,rtes))->ifdisplaykfthenbeginProperty.Set.iteradd_ipips;List.iterappendrtes;end)by_kf;match!properties_tab_labelwith|None->()|Somelabel->lettext=Format.sprintf"Properties (%d)"(model#custom_iter_n_childrenNone)inGtkMisc.Label.set_textlabeltextinignore(letcallback_=main_ui#protect~cancelable:false(fun()->clear();Refreshers.apply();!Refreshers.set_refresh_neededfalse;fill_model())inrefresh_button#connect#released~callback);(* To fill at startup:
let (_:GtkSignal.id) = view#misc#connect#after#realize fill_model in *)lettab_label=(GMisc.label~text:"Properties"())#coerceinproperties_tab_label:=Some(GtkMisc.Label.casttab_label#as_widget);let(_:int)=main_ui#lower_notebook#append_page~tab_label(container#coerce)inregister_reset_extension(fun_->Refreshers.apply())(* Graphical markers in text showing the status of properties.
Aka. "bullets" in left margin *)lethighlighter(buffer:reactive_buffer)localizable~start~stop=matchlocalizablewith|Printer_tag.PIPppt->ifProperty.has_statuspptthenDesign.Feedback.markbuffer#buffer~offset:start(Property_status.Feedback.getppt)|PStmt(_,({skind=Instr(Call_|Local_init(_,ConsInit_,_))}asstmt))->letkfs=Statuses_by_call.all_functions_with_preconditionsstmtin(* We separate the consolidated statuses of the preconditions inside
guarded behaviors from those outside. For guarded behaviors, since we
do not keep track of the status of 'assumes' clauses, we cannot know
if they are active. Hence, we must weaken any 'Invalid' status into
'Unknown'. *)letfilter(ip_src,_ip_copy)=matchip_srcwith|Property.IPPredicate{Property.ip_kind=Property.PKRequiresbhv}->bhv.b_assumes=[]|_->falseinletips_sure,ips_unsure=Kernel_function.Hptset.fold(funkf(ips_sure,ips_unsure)->Statuses_by_call.setup_all_preconditions_proxieskf;letips_kf=Statuses_by_call.all_call_preconditions_at~warn_missing:falsekfstmtinletips_kf_sure,ips_kf_unsure=List.partitionfilterips_kfin(List.mapsndips_kf_sure@ips_sure),(List.mapsndips_kf_unsure@ips_unsure))kfs([],[])inletips=ips_sure@ips_unsureinifips<>[]thenletvalidity=Property_status.Feedback.get_conjunctionipsinletvalidity=matchvaliditywith|Feedback.Invalid_under_hyp->(* Weaken if the invalidity comes from [ips_unsure]. We do nothing
for statuses [Invalid] (a path should exist, hence the behavior
must be active), or [Invalid_but_dead] (equivalent to [True]) *)letinvalidip=Feedback.getip=Feedback.Invalid_under_hypinifList.existsinvalidips_unsure&¬(List.existsinvalidips_sure)thenFeedback.Unknownelsevalidity|_->validityin(* Positioning the bullet is tricky. We cannot use [start] as offset,
because the bullet ends up at the beginning of the spec (assertions,
contracts, etc) instead of in front of the function name. We use
the beginning of the C part of the statement (which has been computed
when the source was rendered). *)letoffset=tryPretty_source.stmt_startbuffer#locsstmtwithNot_found->Gui_parameters.error"Invalid internal state for statement %d"stmt.sid;stop(* fallback *)inDesign.Feedback.markbuffer#buffer~call_site:stmt~offsetvalidity|PStmt_|PStmtStart_|PGlobal_|PVDecl_|PTermLval_|PLval_|PExp_|PType_->()letextend(main_ui:main_window_extension_points)=make_panelmain_ui;(* There is a hack here. We need to access the state of
[main_ui#reactive_buffer] inside [highlighter], but it is not an argument
of the callback. Instead, we pass [main_ui] as an additional argument.
This only works because there is only one instance of [main_ui]. *)main_ui#register_source_highlighterhighlighterlet()=Design.register_extensionextend(*
Local Variables:
compile-command: "make -C ../../.."
End:
*)