123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232(* Yoann Padioleau
*
* Copyright (C) 2010 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.
*)openCst_cppmoduleAst=Cst_cppmoduleV=Visitor_cppmoduleE=Error_codemoduleS=Scope_code(*****************************************************************************)(* Prelude *)(*****************************************************************************)(*
* This file mostly deals with scoping issues. Scoping is different
* from typing! Those are 2 orthogonal programming language notions.
*
* TODO: could move generic code in scope_code.ml
*)(*****************************************************************************)(* Types, constants *)(*****************************************************************************)typeenvironment=(Ast.name*(Scope_code.t*intref))listlist(*****************************************************************************)(* Environment *)(*****************************************************************************)(* Looking up a variable in the environment.
*)letreclookup_env2senv=matchenvwith|[]->raiseNot_found|[]::zs->lookup_env2szs|((a,b)::xs)::zs->ifAst.string_of_name_tmpa=sthenbelselookup_env2s(xs::zs)letlookup_envab=Common.profile_code"CheckVar.lookup_env"(fun()->lookup_env2ab)letlookup_env_optab=Common2.optionise(fun()->lookup_envab)letis_top_envenv=List.lengthenv=1(*****************************************************************************)(* (Semi) Globals, Julia's style *)(*****************************************************************************)(* use a ref because we may want to modify it *)let(initial_env:environmentref)=(* less: Env_php.globals_builtins +> List.map (fun s ->
* fake_dname s, (S.Global, ref 1)
* )
*)ref[[]](* opti: cache ? use hash ? *)let_scoped_env=ref!initial_env(* TODO use generic implem of Common ? *)letnew_scope()=_scoped_env:=[]::!_scoped_envletdel_scope()=_scoped_env:=List.tl!_scoped_envlettop_scope()=List.hd!_scoped_env(*
let do_in_new_scope f =
begin
new_scope();
let res = f() in
del_scope();
res
end
*)letadd_in_scopenamedef=let(current,older)=Common2.uncons!_scoped_envin_scoped_env:=(namedef::current)::olderletadd_binding2kv=(*
let info = Ast.info_of_name_tmp k in
if !Flag.debug_checker
then pr2 (spf "adding binding %s"
(Ast.string_of_info info));
*)add_in_scope(k,v)letadd_bindingkv=Common.profile_code"CV.add_binding"(fun()->add_binding2kv)(*****************************************************************************)(* checks *)(*****************************************************************************)letdo_in_new_scope_and_checkf=new_scope();letres=f()inlettop=top_scope()indel_scope();top|>List.rev|>List.iter(fun(name,(scope,aref))->if!aref=0thenlets=Ast.string_of_name_tmpnameinletii=List.hd(Ast.ii_of_id_namename)inE.errorii(E.UnusedVariable(s,scope)));res(*****************************************************************************)(* Scoped visitor *)(*****************************************************************************)(* For each introduced binding (param, exception, foreach, etc),
* we add the binding in the environment with a counter, a la checkModule.
* todo: ?(find_entity = None)
*)letvisit_progprog=lethooks={V.default_visitorwith(* 1: scoping management *)V.kcompound=(fun(k,_)x->do_in_new_scope_and_check(fun()->kx));V.kcpp=(fun(k,_)x->do_in_new_scope_and_check(fun()->(matchxwith|Define(_,_id,DefineFuncparams,_body)->params|>Ast.unparen|>Ast.uncomma|>List.iter(fun(name)->(matchnamewith|(s,[ii])->add_binding(None,noQscope,IdIdent(s,ii))(S.Param,ref0);|_->()););|_->());kx));(* 2: adding defs of name in environment *)V.kparameter=(fun(k,_)param->param.p_name|>Common.do_option(funident->add_binding(None,noQscope,IdIdentident)(S.Param,ref0););kparam);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)->letscope=ifis_top_env!_scoped_env||(matchonedecl.v_storagewith|Sto(Extern,_)->true|_->false)thenS.GlobalelseS.Localinadd_bindingname(scope,ref0);););kx|MacroDecl_->kx|(Asm(_,_,_,_)|NameSpaceAlias(_,_,_,_,_)|UsingDirective(_,_,_,_)|UsingDecl_)->());(* 3: checking uses *)V.kexpr=(fun(k,_)x->match(Ast.unwrapx)with|Id(name,idinfo)->(* assert scope_ref = S.Unknown ? *)lets=Ast.string_of_name_tmpnamein(matchlookup_env_opts!_scoped_envwith|None->idinfo.i_scope<-S.Global;|Some(scope,_)->idinfo.i_scope<-scope;);kx|_->kx);}inletvisitor=V.mk_visitorhooksinvisitor(Programprog)(*****************************************************************************)(* Main entry point *)(*****************************************************************************)letcheck_and_annotate_program2prog=(* globals (re)initialialisation *)_scoped_env:=!initial_env;visit_progprog;()letcheck_and_annotate_programa=Common.profile_code"Checker.variables"(fun()->check_and_annotate_program2a)