diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index 27695542c..e213f0be4 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -824,6 +824,18 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s in CContext.add_block_static_var context procname (pvar, typ) ; let var_exp = Exp.Lvar pvar in + (* Captured variables without initialization do not have the correct types + inside of lambda bodies. Use the types stored in the procdesc. *) + let typ = + match procname with + | Procname.ObjC_Cpp cpp_pname when Procname.ObjC_Cpp.is_cpp_lambda cpp_pname -> + let pvar_name = Pvar.get_name pvar in + List.find (Procdesc.get_captured context.procdesc) ~f:(fun (captured_var, _, _) -> + Mangled.equal captured_var pvar_name ) + |> Option.value_map ~f:(fun (_, t, _) -> t) ~default:typ + | _ -> + typ + in let return = if Self.is_var_self pvar (CContext.is_objc_method context) && CType.is_class typ then let class_name = CContext.get_curr_class_typename stmt_info context in @@ -3200,7 +3212,18 @@ module CTrans_funct (F : CModule_type.CFrontend) : CModule_type.CTranslation = s let translate_normal_capture mode (pvar, typ) (trans_results_acc, captured_vars_acc) = match mode with | Pvar.ByReference -> - (trans_results_acc, (Exp.Lvar pvar, pvar, typ, mode) :: captured_vars_acc) + let ref_typ = + (* A variable captured by ref (except for ref variables) is missing ref in its type *) + match typ.Typ.desc with + | Tptr (_, Typ.Pk_reference) -> + typ + | _ when Pvar.is_this pvar -> + (* Special case for this *) + typ + | _ -> + Typ.mk (Tptr (typ, Typ.Pk_reference)) + in + (trans_results_acc, (Exp.Lvar pvar, pvar, ref_typ, mode) :: captured_vars_acc) | Pvar.ByValue -> translate_captured_var_assign (pvar, typ, mode) trans_results_acc captured_vars_acc in diff --git a/infer/tests/codetoanalyze/cpp/pulse/closures.cpp b/infer/tests/codetoanalyze/cpp/pulse/closures.cpp index 17718bbc9..70a9e5c4a 100644 --- a/infer/tests/codetoanalyze/cpp/pulse/closures.cpp +++ b/infer/tests/codetoanalyze/cpp/pulse/closures.cpp @@ -124,7 +124,7 @@ int ref_capture_return_local_lambda_ok() { return f().f; } -S& FN_ref_capture_return_local_lambda_bad() { +S& ref_capture_return_local_lambda_bad() { S x; auto f = [&x](void) -> S& { // no way to know if ok here @@ -211,7 +211,7 @@ void capture_by_value_bad() { } } -void capture_by_ref_ok_FP() { +void capture_by_ref_ok() { int value = 5; auto f = [&value]() -> int* { return new int(value); }; value++; @@ -297,7 +297,7 @@ S* update_inside_lambda_capture_only(S* s) { return object; } -int update_inside_lambda_capture_only_ok_FP(S* param_s) { +int update_inside_lambda_capture_only_ok(S* param_s) { return update_inside_lambda_capture_only(param_s)->f; } diff --git a/infer/tests/codetoanalyze/cpp/pulse/issues.exp b/infer/tests/codetoanalyze/cpp/pulse/issues.exp index 2ef84ec02..fd9b13b1a 100644 --- a/infer/tests/codetoanalyze/cpp/pulse/issues.exp +++ b/infer/tests/codetoanalyze/cpp/pulse/issues.exp @@ -11,14 +11,13 @@ codetoanalyze/cpp/pulse/closures.cpp, call_lambda_std_fun_bad, 5, USE_AFTER_DELE codetoanalyze/cpp/pulse/closures.cpp, call_std_fun_constructor_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `new` (modelled),return from call to `new` (modelled),assigned,was invalidated by `delete`,use-after-lifetime part of the trace starts here,passed as argument to `new` (modelled),return from call to `new` (modelled),assigned,when calling `call_std_fun_constructor_bad::lambda_closures.cpp:178:32::operator()` here,parameter `s` of call_std_fun_constructor_bad::lambda_closures.cpp:178:32::operator(),invalid access occurs here] codetoanalyze/cpp/pulse/closures.cpp, capture_by_ref_bad, 7, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] codetoanalyze/cpp/pulse/closures.cpp, capture_by_ref_init_bad, 7, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] -codetoanalyze/cpp/pulse/closures.cpp, capture_by_ref_ok_FP, 7, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] codetoanalyze/cpp/pulse/closures.cpp, capture_by_value_bad, 7, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] codetoanalyze/cpp/pulse/closures.cpp, capture_by_value_init_bad, 7, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,assigned,invalid access occurs here] codetoanalyze/cpp/pulse/closures.cpp, implicit_ref_capture_destroy_invoke_bad, 6, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidation part of the trace starts here,variable `s` declared here,is the address of a stack variable `s` whose lifetime has ended,use-after-lifetime part of the trace starts here,variable `s` declared here,value captured by by ref as `s`,invalid access occurs here] codetoanalyze/cpp/pulse/closures.cpp, reassign_lambda_capture_destroy_invoke_bad, 9, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidation part of the trace starts here,variable `s` declared here,is the address of a stack variable `s` whose lifetime has ended,use-after-lifetime part of the trace starts here,variable `s` declared here,value captured by by ref as `s`,invalid access occurs here] codetoanalyze/cpp/pulse/closures.cpp, ref_capture_destroy_invoke_bad, 6, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidation part of the trace starts here,variable `s` declared here,is the address of a stack variable `s` whose lifetime has ended,use-after-lifetime part of the trace starts here,variable `s` declared here,value captured by by ref as `s`,invalid access occurs here] +codetoanalyze/cpp/pulse/closures.cpp, ref_capture_return_local_lambda_bad, 7, STACK_VARIABLE_ADDRESS_ESCAPE, no_bucket, ERROR, [variable `x` declared here,value captured by by ref as `x`,passed as argument to `ref_capture_return_local_lambda_bad::lambda_closures.cpp:129:12::operator()`,return from call to `ref_capture_return_local_lambda_bad::lambda_closures.cpp:129:12::operator()`,returned here] codetoanalyze/cpp/pulse/closures.cpp, update_inside_lambda_as_argument_ok_FP, 1, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `update_inside_lambda_as_argument` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `update_inside_lambda_as_argument`,return from call to `update_inside_lambda_as_argument`,invalid access occurs here] -codetoanalyze/cpp/pulse/closures.cpp, update_inside_lambda_capture_only_ok_FP, 1, NULLPTR_DEREFERENCE, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `update_inside_lambda_capture_only` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `update_inside_lambda_capture_only`,return from call to `update_inside_lambda_capture_only`,invalid access occurs here] codetoanalyze/cpp/pulse/conditionals.cpp, add_test3_bad, 3, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `x` of add_test3_bad,was invalidated by call to `free()`,use-after-lifetime part of the trace starts here,parameter `x` of add_test3_bad,invalid access occurs here] codetoanalyze/cpp/pulse/conditionals.cpp, add_test5_bad, 5, USE_AFTER_FREE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `x` of add_test5_bad,was invalidated by call to `free()`,use-after-lifetime part of the trace starts here,parameter `x` of add_test5_bad,invalid access occurs here] codetoanalyze/cpp/pulse/deduplication.cpp, deduplication::SomeTemplatedClass::lifetime_error_bad, 2, USE_AFTER_DELETE, no_bucket, ERROR, [invalidation part of the trace starts here,parameter `a` of deduplication::SomeTemplatedClass::lifetime_error_bad,when calling `deduplication::SomeTemplatedClass::templated_wrapper_delete_ok` here,parameter `a` of deduplication::SomeTemplatedClass::templated_wrapper_delete_ok,was invalidated by `delete`,use-after-lifetime part of the trace starts here,parameter `a` of deduplication::SomeTemplatedClass::lifetime_error_bad,when calling `deduplication::SomeTemplatedClass::templated_wrapper_access_ok` here,parameter `a` of deduplication::SomeTemplatedClass::templated_wrapper_access_ok,invalid access occurs here] diff --git a/infer/tests/codetoanalyze/cpp/shared/lambda/lambda1.cpp.dot b/infer/tests/codetoanalyze/cpp/shared/lambda/lambda1.cpp.dot index fa0872683..e9440b15a 100644 --- a/infer/tests/codetoanalyze/cpp/shared/lambda/lambda1.cpp.dot +++ b/infer/tests/codetoanalyze/cpp/shared/lambda/lambda1.cpp.dot @@ -313,14 +313,14 @@ digraph cfg { "operator()#lambda_shared_lambda_lambda1.cpp:31:10#normal_capture#(3336792892144266867).563aa24976a73c4ea364dbb5afa3f73f_3" -> "operator()#lambda_shared_lambda_lambda1.cpp:31:10#normal_capture#(3336792892144266867).563aa24976a73c4ea364dbb5afa3f73f_2" ; -"operator()#lambda_shared_lambda_lambda1.cpp:36:3#capture_by_ref#(17277454583786497390).328aa336808e9a777a5cd630eb1ef54f_1" [label="1: Start capture_by_ref::lambda_shared_lambda_lambda1.cpp:36:3::operator()\nFormals: this:capture_by_ref::lambda_shared_lambda_lambda1.cpp:36:3*\nLocals: \nCaptured: [by ref]x:int \n " color=yellow style=filled] +"operator()#lambda_shared_lambda_lambda1.cpp:36:3#capture_by_ref#(17277454583786497390).328aa336808e9a777a5cd630eb1ef54f_1" [label="1: Start capture_by_ref::lambda_shared_lambda_lambda1.cpp:36:3::operator()\nFormals: this:capture_by_ref::lambda_shared_lambda_lambda1.cpp:36:3*\nLocals: \nCaptured: [by ref]x:int& \n " color=yellow style=filled] "operator()#lambda_shared_lambda_lambda1.cpp:36:3#capture_by_ref#(17277454583786497390).328aa336808e9a777a5cd630eb1ef54f_1" -> "operator()#lambda_shared_lambda_lambda1.cpp:36:3#capture_by_ref#(17277454583786497390).328aa336808e9a777a5cd630eb1ef54f_3" ; "operator()#lambda_shared_lambda_lambda1.cpp:36:3#capture_by_ref#(17277454583786497390).328aa336808e9a777a5cd630eb1ef54f_2" [label="2: Exit capture_by_ref::lambda_shared_lambda_lambda1.cpp:36:3::operator() \n " color=yellow style=filled] -"operator()#lambda_shared_lambda_lambda1.cpp:36:3#capture_by_ref#(17277454583786497390).328aa336808e9a777a5cd630eb1ef54f_3" [label="3: UnaryOperator \n n$0=*&x:int [line 36, column 12]\n *&x:int=(n$0 + 1) [line 36, column 12]\n " shape="box"] +"operator()#lambda_shared_lambda_lambda1.cpp:36:3#capture_by_ref#(17277454583786497390).328aa336808e9a777a5cd630eb1ef54f_3" [label="3: UnaryOperator \n n$0=*&x:int& [line 36, column 12]\n n$1=*n$0:int [line 36, column 12]\n *n$0:int=(n$1 + 1) [line 36, column 12]\n " shape="box"] "operator()#lambda_shared_lambda_lambda1.cpp:36:3#capture_by_ref#(17277454583786497390).328aa336808e9a777a5cd630eb1ef54f_3" -> "operator()#lambda_shared_lambda_lambda1.cpp:36:3#capture_by_ref#(17277454583786497390).328aa336808e9a777a5cd630eb1ef54f_2" ;