diff --git a/infer/src/erlang/ErlangAst.ml b/infer/src/erlang/ErlangAst.ml index 350208ee2f7..518cf788da3 100644 --- a/infer/src/erlang/ErlangAst.ml +++ b/infer/src/erlang/ErlangAst.ml @@ -71,6 +71,8 @@ type literal = Atom of string | Char of string | Float of float | Int of string (** {2 S8.4: Expressions} *) +type scope = {procname: (Procname.t[@sexp.opaque]); is_first_use: bool} [@@deriving sexp_of] + type body = expression list and simple_expression = @@ -104,7 +106,7 @@ and simple_expression = | TryCatch of {body: body; ok_cases: case_clause list; catch_cases: catch_clause list; after: body} | Tuple of expression list | UnaryOperator of unary_operator * expression - | Variable of {vname: string; mutable scope: (Procname.t option[@sexp.opaque])} + | Variable of {vname: string; mutable scope: scope option} and expression = {location: location; simple_expression: simple_expression} diff --git a/infer/src/erlang/ErlangScopes.ml b/infer/src/erlang/ErlangScopes.ml index 8b5edab189e..126abe7fe6e 100644 --- a/infer/src/erlang/ErlangScopes.ml +++ b/infer/src/erlang/ErlangScopes.ml @@ -172,20 +172,24 @@ let rec annotate_expression (env : (_, _) Env.t) lambda_cntr (scopes : scope lis | Variable v -> ( match scopes with | hd :: tl -> ( - if String.Set.mem hd.locals v.vname then ( + if String.equal "_" v.vname then ( + (* The anonymus variable is always fresh in the local scope *) + v.scope <- Some {procname= hd.procname; is_first_use= true} ; + scopes ) + else if String.Set.mem hd.locals v.vname then ( (* Known local var *) - v.scope <- Some hd.procname ; + v.scope <- Some {procname= hd.procname; is_first_use= false} ; scopes ) else (* Check if it's captured from outside *) match lookup_var tl v.vname with | Some procname -> let pvar = Pvar.mk (Mangled.from_string v.vname) procname in - v.scope <- Some procname ; + v.scope <- Some {procname; is_first_use= false} ; {hd with captured= Pvar.Set.add pvar hd.captured} :: tl | None -> (* It's a local we see here first *) - v.scope <- Some hd.procname ; + v.scope <- Some {procname= hd.procname; is_first_use= true} ; {hd with locals= String.Set.add hd.locals v.vname} :: tl ) | [] -> L.die InternalError "No scope found during variable annotation." ) diff --git a/infer/src/erlang/ErlangTranslator.ml b/infer/src/erlang/ErlangTranslator.ml index 590cc4d1194..28c940b4327 100644 --- a/infer/src/erlang/ErlangTranslator.ml +++ b/infer/src/erlang/ErlangTranslator.ml @@ -179,10 +179,10 @@ let unbox_integer env expr : Exp.t * Block.t = (unboxed_expr, {start; exit_success= unbox_block.exit_success; exit_failure= Node.make_nop env}) -let procname_exn scope = +let scope_exn (scope : Ast.scope option) = match scope with - | Some name -> - name + | Some scope -> + scope | None -> (* Can happen e.g. if we have the _ variable in record field initializers (T134336886) *) L.die InternalError "Scope not found for variable, probably missing annotation." @@ -210,7 +210,7 @@ let vars_of_pattern p = | UnaryOperator (_, e) -> f acc e | Variable {vname; scope} -> - let procname = procname_exn scope in + let procname = (scope_exn scope).procname in Pvar.Set.add (Pvar.mk (Mangled.from_string vname) procname) acc | Literal _ | Nil | RecordIndex _ -> acc @@ -523,7 +523,7 @@ and translate_pattern_number_expression (env : (_, _) Env.t) value location simp and translate_pattern_variable (env : (_, _) Env.t) value vname scope : Block.t = (* We also assign to _ so that stuff like f()->_=1. works. But if we start checking for re-binding, we should exclude _ from such checks. *) - let procname = procname_exn scope in + let procname = (scope_exn scope).procname in let store : Sil.instr = let e1 : Exp.t = Lvar (Pvar.mk (Mangled.from_string vname) procname) in let e2 : Exp.t = Var value in @@ -1486,7 +1486,7 @@ and translate_expression_unary_operator (env : (_, _) Env.t) ret_var (op : Ast.u and translate_expression_variable (env : (_, _) Env.t) ret_var vname scope : Block.t = - let procname = procname_exn scope in + let procname = (scope_exn scope).procname in let e = Exp.Lvar (Pvar.mk (Mangled.from_string vname) procname) in let load_instr = Sil.Load {id= ret_var; e; typ= any_typ; loc= env.location} in Block.make_instruction env [load_instr]