diff --git a/infer/src/clang/cScope.ml b/infer/src/clang/cScope.ml index 839dc5e5d..0387d3517 100644 --- a/infer/src/clang/cScope.ml +++ b/infer/src/clang/cScope.ml @@ -30,7 +30,7 @@ type 'a scope = {current: 'a list; current_kind: scope_kind; outers: ('a list * (** executes [f] in new scope where [kind] has been pushed on top *) let in_ kind scope ~f = - L.debug Capture Verbose "<@[%s|" (string_of_kind kind) ; + L.debug Capture Verbose "<@[%s|@," (string_of_kind kind) ; let scope = {current= []; current_kind= kind; outers= (scope.current, scope.current_kind) :: scope.outers} in @@ -38,7 +38,7 @@ let in_ kind scope ~f = let (current, current_kind), outers = match scope.outers with [] -> assert false | top :: rest -> (top, rest) in - L.debug Capture Verbose "@]%s>@\n" (string_of_kind scope.current_kind) ; + L.debug Capture Verbose "@]@;/%s>" (string_of_kind scope.current_kind) ; (scope.current, {current; current_kind; outers}, x) @@ -165,7 +165,7 @@ module Variables = struct in (* the reverse order is the one we want to destroy the variables in at the end of the scope *) - L.debug Capture Verbose "+%a" (Pp.seq ~sep:"," pp_var_decl) new_vars ; + L.debug Capture Verbose "+%a@," (Pp.seq ~sep:"," pp_var_decl) new_vars ; (rev_append new_vars scope, map) | _ -> ( let stmt_info, stmt_list = Clang_ast_proj.get_stmt_tuple stmt in @@ -203,7 +203,8 @@ module Variables = struct and visit_stmt_list stmt_list scope_map = - List.fold stmt_list ~f:(fun scope_map stmt -> visit_stmt stmt scope_map) ~init:scope_map + List.fold stmt_list ~init:scope_map ~f:(fun scope_map stmt -> + L.debug Capture Verbose "@;" ; visit_stmt stmt scope_map ) let empty_scope = {current= []; current_kind= InitialScope; outers= []} @@ -213,7 +214,7 @@ module Variables = struct end module CXXTemporaries = struct - let rec visit_stmt context stmt temporaries = + let rec visit_stmt_aux context stmt temporaries = match (stmt : Clang_ast_t.stmt) with | MaterializeTemporaryExpr ( stmt_info @@ -224,6 +225,7 @@ module CXXTemporaries = struct the reference *) None } ) -> let pvar, typ = CVar_decl.materialize_cpp_temporary context stmt_info expr_info in + L.debug Capture Verbose "+%a@," (Pvar.pp Pp.text) pvar ; let temporaries = (pvar, typ, expr_info.ei_qual_type) :: temporaries in visit_stmt_list context stmt_list temporaries | ExprWithCleanups _ -> @@ -242,15 +244,26 @@ module CXXTemporaries = struct Example of tricky case: [foo(x?y:z, w)] or [cond && y] where [y] generates a C++ temporary. *) temporaries + | LambdaExpr _ -> + (* do not analyze the code of another function *) temporaries | _ -> let _, stmt_list = Clang_ast_proj.get_stmt_tuple stmt in visit_stmt_list context stmt_list temporaries + and visit_stmt context stmt temporaries = + L.debug Capture Verbose "<@[%a|@," + (Pp.to_string ~f:Clang_ast_proj.get_stmt_kind_string) + stmt ; + let r = visit_stmt_aux context stmt temporaries in + L.debug Capture Verbose "@]@;/%a>" (Pp.to_string ~f:Clang_ast_proj.get_stmt_kind_string) stmt ; + r + + and visit_stmt_list context stmt_list temporaries = - List.fold stmt_list - ~f:(fun temporaries stmt -> visit_stmt context stmt temporaries) - ~init:temporaries + List.fold stmt_list ~init:temporaries ~f:(fun temporaries stmt -> + L.debug Capture Verbose "@;" ; + visit_stmt context stmt temporaries ) let get_destroyable_temporaries context stmt_list = visit_stmt_list context stmt_list [] diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index 07bef56b8..07f497bb8 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -2161,8 +2161,8 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s let field_exps = match Tenv.lookup tenv tname with | Some {fields} -> - List.filter_map fields ~f:(fun (fieldname, fieldtype, _) -> - Some (Exp.Lfield (var_exp, fieldname, var_typ), fieldtype) ) + List.map fields ~f:(fun (fieldname, fieldtype, _) -> + (Exp.Lfield (var_exp, fieldname, var_typ), fieldtype) ) | None -> assert false in @@ -2180,7 +2180,8 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s "assuming initListExpr is initializing the whole struct %a in one go" Exp.pp var_exp ; [init_expr_trans trans_state (var_exp, var_typ) stmt_info (Some stmt)] | _ -> - (* This can happen with union initializers. Skip them for now *) + (* This happens with some braced-init-list for instance; translate each sub-statement so + as not to lose instructions (we might even get the translation right) *) L.debug Capture Medium "couldn't translate initListExpr properly: list lengths do not match:@\n\ \ field_exps is %d: [%a]@\n\ @@ -2190,7 +2191,8 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s field_exps (List.length stmts) (Pp.seq ~sep:"," (Pp.to_string ~f:Clang_ast_proj.get_stmt_kind_string)) stmts ; - [] ) + let control, _ = instructions trans_state stmts in + [mk_trans_result (var_exp, var_typ) control] ) and initListExpr_builtin_trans trans_state stmt_info stmts var_exp var_typ = @@ -2283,12 +2285,11 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s match init_expr_opt with | None -> ( match - Option.map ~f:(fun qt -> qt.Clang_ast_t.qt_type_ptr) qual_type - |> Option.find_map ~f:CAst_utils.get_type + Option.bind qual_type ~f:(fun qt -> CAst_utils.get_type qt.Clang_ast_t.qt_type_ptr) with | Some (Clang_ast_t.VariableArrayType (_, _, stmt_pointer)) -> (* Set the dynamic length of the variable length array. Variable length array cannot - have an initialization expression. *) + have an initialization expression. *) init_dynamic_array trans_state var_exp_typ var_stmt_info stmt_pointer | _ -> (* Nothing to do if no init expression and not a variable length array *) diff --git a/infer/tests/codetoanalyze/cpp/pulse/std_visit.cpp b/infer/tests/codetoanalyze/cpp/pulse/std_visit.cpp new file mode 100644 index 000000000..3a55f189e --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/pulse/std_visit.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +#include +#include + +namespace std_visit { +struct A { + int s_; + ~A() {} + A() {} +}; + +// See https://en.cppreference.com/w/cpp/utility/variant/visit for more details +using var_t = std::variant; + +template +struct overloaded : Ts... { + using Ts::operator()...; +}; +template +overloaded(Ts...)->overloaded; + +void visit_ok(var_t x) { + std::visit(overloaded{ + [](float a) {}, + [](int i) {}, + [](const A& a) {}, + }, + x); +} + +void visit_all_ok(std::vector& vec) { + for (auto& x : vec) { + // the overloaded { .. } construct appears as an InitListExpr in + // the AST that the frontend needs to translate. Otherwise, since + // children of that InitListExpr create C++ temporaries, pulse + // will complain that they get destroyed because it never sees + // them being created (this is only a problem in a loop where the + // same temporary is re-used without having been re-initialized). + std::visit(overloaded{ + [](float a) {}, + [](int i) {}, + [](const A& a) {}, + }, + x); + } +} + +} // namespace std_visit