From b7ab1760a6bff4d0fe4d17a5afeedc4a9c08b6c5 Mon Sep 17 00:00:00 2001 From: Dino Distefano Date: Fri, 15 Apr 2016 03:10:48 -0700 Subject: [PATCH] Translation of lambda [part one] Summary:This diff translate cpp lambdas. For the moment it does not take care of captured variables. Captured variables will come in the next diff. Reviewed By: dulmarod Differential Revision: D3114790 fb-gh-sync-id: bf36450 fbshipit-source-id: bf36450 --- infer/src/clang/cFrontend_decl.ml | 6 +- infer/src/clang/cFrontend_utils.ml | 7 ++ infer/src/clang/cFrontend_utils.mli | 4 ++ infer/src/clang/cMethod_trans.ml | 10 +++ infer/src/clang/cMethod_trans.mli | 2 + infer/src/clang/cTrans.ml | 29 ++++++-- infer/src/clang/cTrans_utils.ml | 6 -- infer/src/clang/cTrans_utils.mli | 3 - .../cpp/frontend/lambda/lambda1.cpp | 28 ++++++++ infer/tests/endtoend/cpp/LambdaTest.java | 66 +++++++++++++++++++ 10 files changed, 144 insertions(+), 17 deletions(-) create mode 100644 infer/tests/codetoanalyze/cpp/frontend/lambda/lambda1.cpp create mode 100644 infer/tests/endtoend/cpp/LambdaTest.java diff --git a/infer/src/clang/cFrontend_decl.ml b/infer/src/clang/cFrontend_decl.ml index 9783bf149..a3062098f 100644 --- a/infer/src/clang/cFrontend_decl.ml +++ b/infer/src/clang/cFrontend_decl.ml @@ -195,9 +195,9 @@ struct | _ -> ()); match dec with (* Currently C/C++ record decl treated in the same way *) - | ClassTemplateSpecializationDecl (decl_info, _, _, _, decl_list, _, _, _) - | CXXRecordDecl (decl_info, _, _, _, decl_list, _, _, _) - | RecordDecl (decl_info, _, _, _, decl_list, _, _) when not decl_info.di_is_implicit -> + | ClassTemplateSpecializationDecl (_, _, _, _, decl_list, _, _, _) + | CXXRecordDecl (_, _, _, _, decl_list, _, _, _) + | RecordDecl (_, _, _, _, decl_list, _, _) -> let is_method_decl decl = match decl with | CXXMethodDecl _ | CXXConstructorDecl _ | CXXConversionDecl _ | CXXDestructorDecl _ | FunctionTemplateDecl _ -> diff --git a/infer/src/clang/cFrontend_utils.ml b/infer/src/clang/cFrontend_utils.ml index 41cfb5985..37b66449c 100644 --- a/infer/src/clang/cFrontend_utils.ml +++ b/infer/src/clang/cFrontend_utils.ml @@ -386,6 +386,12 @@ struct if decl_ptr' = (Some decl_ptr) then decl_opt else get_decl_opt decl_ptr' + let get_info_from_decl_ref decl_ref = + let name_info = match decl_ref.Clang_ast_t.dr_name with Some ni -> ni | _ -> assert false in + let decl_ptr = decl_ref.Clang_ast_t.dr_decl_pointer in + let type_ptr = match decl_ref.Clang_ast_t.dr_type_ptr with Some tp -> tp | _ -> assert false in + name_info, decl_ptr, type_ptr + (* let rec getter_attribute_opt attributes = match attributes with @@ -617,6 +623,7 @@ struct let is_cpp_translation language = language = CFrontend_config.CPP || language = CFrontend_config.OBJCPP + end diff --git a/infer/src/clang/cFrontend_utils.mli b/infer/src/clang/cFrontend_utils.mli index 41aa94917..b250dae30 100644 --- a/infer/src/clang/cFrontend_utils.mli +++ b/infer/src/clang/cFrontend_utils.mli @@ -130,6 +130,9 @@ sig val get_function_decl_with_body : Clang_ast_t.pointer -> Clang_ast_t.decl option + val get_info_from_decl_ref : Clang_ast_t.decl_ref -> + Clang_ast_t.named_decl_info * Clang_ast_t.pointer * Clang_ast_t.type_ptr + end module General_utils : @@ -195,4 +198,5 @@ sig Pvar.t val is_cpp_translation : CFrontend_config.lang -> bool + end diff --git a/infer/src/clang/cMethod_trans.ml b/infer/src/clang/cMethod_trans.ml index 7800dd431..f3dd7903b 100644 --- a/infer/src/clang/cMethod_trans.ml +++ b/infer/src/clang/cMethod_trans.ml @@ -462,6 +462,16 @@ let get_method_for_frontend_checks cfg cg loc = Cg.add_defined_node cg proc_name; pdesc +let get_procname_from_cpp_lambda context dec = + match dec with + | Clang_ast_t.CXXRecordDecl (_, _, _, _, _, _, _, cxx_rdi) -> + (match cxx_rdi.xrdi_lambda_call_operator with + | Some dr -> + let name_info, decl_ptr, type_ptr = Ast_utils.get_info_from_decl_ref dr in + create_procdesc_with_pointer context decl_ptr None name_info.ni_name type_ptr + | _ -> assert false (* We should not get here *)) + | _ -> assert false (* We should not get here *) + (* let instance_to_method_call_type instance = if instance then MCVirtual diff --git a/infer/src/clang/cMethod_trans.mli b/infer/src/clang/cMethod_trans.mli index 29553394b..8a2c6ce7f 100644 --- a/infer/src/clang/cMethod_trans.mli +++ b/infer/src/clang/cMethod_trans.mli @@ -51,3 +51,5 @@ val create_procdesc_with_pointer : CContext.t -> Clang_ast_t.pointer -> string o string -> Clang_ast_t.type_ptr -> Procname.t val get_method_for_frontend_checks : Cfg.cfg -> Cg.t -> Location.t -> Cfg.Procdesc.t + +val get_procname_from_cpp_lambda : CContext.t -> Clang_ast_t.decl -> Procname.t diff --git a/infer/src/clang/cTrans.ml b/infer/src/clang/cTrans.ml index 5af4f7d7a..544b3e2dc 100644 --- a/infer/src/clang/cTrans.ml +++ b/infer/src/clang/cTrans.ml @@ -460,7 +460,7 @@ struct let function_deref_trans trans_state decl_ref = let open CContext in let context = trans_state.context in - let name_info, decl_ptr, type_ptr = get_info_from_decl_ref decl_ref in + let name_info, decl_ptr, type_ptr = Ast_utils.get_info_from_decl_ref decl_ref in let decl_opt = Ast_utils.get_function_decl_with_body decl_ptr in Option.may (call_translation context) decl_opt; let name = Ast_utils.get_qualified_name name_info in @@ -491,7 +491,7 @@ struct let var_deref_trans trans_state stmt_info decl_ref = let open CContext in let context = trans_state.context in - let _, _, type_ptr = get_info_from_decl_ref decl_ref in + let _, _, type_ptr = Ast_utils.get_info_from_decl_ref decl_ref in let typ = CTypes_decl.type_ptr_to_sil_type context.tenv type_ptr in let procname = Cfg.Procdesc.get_proc_name context.procdesc in let sil_loc = CLocation.get_sil_location stmt_info context in @@ -518,7 +518,7 @@ struct let open CContext in let context = trans_state.context in let sil_loc = CLocation.get_sil_location stmt_info context in - let name_info, _, type_ptr = get_info_from_decl_ref decl_ref in + let name_info, _, type_ptr = Ast_utils.get_info_from_decl_ref decl_ref in Printing.log_out "!!!!! Dealing with field '%s' @." name_info.Clang_ast_t.ni_name; let field_typ = CTypes_decl.type_ptr_to_sil_type context.tenv type_ptr in let (obj_sil, class_typ) = extract_exp_from_list pre_trans_result.exps @@ -556,7 +556,7 @@ struct let open CContext in let context = trans_state.context in let sil_loc = CLocation.get_sil_location stmt_info context in - let name_info, decl_ptr, type_ptr = get_info_from_decl_ref decl_ref in + let name_info, decl_ptr, type_ptr = Ast_utils.get_info_from_decl_ref decl_ref in let decl_opt = Ast_utils.get_function_decl_with_body decl_ptr in Option.may (call_translation context) decl_opt; let method_name = Ast_utils.get_unqualified_name name_info in @@ -701,7 +701,7 @@ struct and enum_constant_trans trans_state decl_ref = let context = trans_state.context in - let _, _, type_ptr = get_info_from_decl_ref decl_ref in + let _, _, type_ptr = Ast_utils.get_info_from_decl_ref decl_ref in let typ = CTypes_decl.type_ptr_to_sil_type context.CContext.tenv type_ptr in let const_exp = get_enum_constant_expr context decl_ref.Clang_ast_t.dr_decl_pointer in { empty_res_trans with exps = [(const_exp, typ)] } @@ -2082,6 +2082,21 @@ struct else instruction trans_state' stmt in stmt_res_trans :: rest_stmts_res_trans + and lambdaExpr_trans trans_state expr_info decl = + let open CContext in + let type_ptr = expr_info.Clang_ast_t.ei_type_ptr in + let context = trans_state.context in + call_translation context decl; + let procname = Cfg.Procdesc.get_proc_name context.procdesc in + let lambda_pname = CMethod_trans.get_procname_from_cpp_lambda context decl in + let typ = CTypes_decl.type_ptr_to_sil_type context.tenv type_ptr in + (* We need to set the explicit dependency between the newly created lambda and the *) + (* defining procedure. We add an edge in the call graph.*) + Cg.add_edge context.cg procname lambda_pname; + let captured_vars = [] in (* TODO *) + let closure = Sil.Cclosure { name = lambda_pname; captured_vars } in + { empty_res_trans with exps = [(Sil.Const closure, typ)] } + and cxxNewExpr_trans trans_state stmt_info expr_info cxx_new_expr_info = let context = trans_state.context in let typ = CTypes_decl.get_type_from_expr_info expr_info context.CContext.tenv in @@ -2564,6 +2579,10 @@ struct | CXXStdInitializerListExpr (stmt_info, stmts, expr_info) -> cxxStdInitializerListExpr_trans trans_state stmt_info stmts expr_info + | LambdaExpr(_, _, expr_info, decl) -> + let trans_state' = { trans_state with priority = Free } in + lambdaExpr_trans trans_state' expr_info decl + | s -> (Printing.log_stats "\n!!!!WARNING: found statement %s. \nACTION REQUIRED: \ Translation need to be defined. Statement ignored.... \n" diff --git a/infer/src/clang/cTrans_utils.ml b/infer/src/clang/cTrans_utils.ml index 6fadeb37f..a01011ba9 100644 --- a/infer/src/clang/cTrans_utils.ml +++ b/infer/src/clang/cTrans_utils.ml @@ -595,12 +595,6 @@ let rec is_method_call s = | [] -> false | s'':: _ -> is_method_call s'') -let get_info_from_decl_ref decl_ref = - let name_info = match decl_ref.Clang_ast_t.dr_name with Some ni -> ni | _ -> assert false in - let decl_ptr = decl_ref.Clang_ast_t.dr_decl_pointer in - let type_ptr = match decl_ref.Clang_ast_t.dr_type_ptr with Some tp -> tp | _ -> assert false in - name_info, decl_ptr, type_ptr - let rec get_decl_ref_info s = match s with | Clang_ast_t.DeclRefExpr (_, _, _, decl_ref_expr_info) -> diff --git a/infer/src/clang/cTrans_utils.mli b/infer/src/clang/cTrans_utils.mli index b7c014954..05d90ff19 100644 --- a/infer/src/clang/cTrans_utils.mli +++ b/infer/src/clang/cTrans_utils.mli @@ -95,9 +95,6 @@ val is_method_call : Clang_ast_t.stmt -> bool val contains_opaque_value_expr : Clang_ast_t.stmt -> bool -val get_info_from_decl_ref : Clang_ast_t.decl_ref -> - Clang_ast_t.named_decl_info * Clang_ast_t.pointer * Clang_ast_t.type_ptr - val get_decl_ref_info : Clang_ast_t.stmt -> Clang_ast_t.decl_ref val builtin_trans : trans_state -> Location.t -> Clang_ast_t.stmt_info -> diff --git a/infer/tests/codetoanalyze/cpp/frontend/lambda/lambda1.cpp b/infer/tests/codetoanalyze/cpp/frontend/lambda/lambda1.cpp new file mode 100644 index 000000000..ec40890b4 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/frontend/lambda/lambda1.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +int bar() { + auto func = []() { + int i = 0; + return i; + }; + return 7 / func(); +} + +int foo() { + + auto y = [](int i) { return ++i; }; + return 5 / (4 - y(3)); +} + +int fooOK() { + + auto y = [](int i) { return i++; }; + return 5 / (4 - y(3)); +} diff --git a/infer/tests/endtoend/cpp/LambdaTest.java b/infer/tests/endtoend/cpp/LambdaTest.java new file mode 100644 index 000000000..19983c559 --- /dev/null +++ b/infer/tests/endtoend/cpp/LambdaTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +package endtoend.cpp; + +import static org.hamcrest.MatcherAssert.assertThat; +import static utils.matchers.ResultContainsNoErrorInMethod.doesNotContain; +import static utils.matchers.ResultContainsExactly.containsExactly; + +import com.google.common.collect.ImmutableList; + +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; + +import java.io.IOException; + +import utils.DebuggableTemporaryFolder; +import utils.InferException; +import utils.InferResults; +import utils.InferRunner; + +public class LambdaTest { + + public static final String FILE = + "infer/tests/codetoanalyze/cpp/frontend/lambda/lambda1.cpp"; + + private static ImmutableList inferCmd; + + public static final String DIVIDE_BY_ZERO = "DIVIDE_BY_ZERO"; + + @ClassRule + public static DebuggableTemporaryFolder folder = + new DebuggableTemporaryFolder(); + + @BeforeClass + public static void runInfer() throws InterruptedException, IOException { + inferCmd = InferRunner.createCPPInferCommand(folder, FILE); + } + + @Test + public void whenInferRunsOnDiv0FunctionsErrorIsFound() + throws InterruptedException, IOException, InferException { + String[] procedures = { + "bar", + "foo", + }; + InferResults inferResults = InferRunner.runInferCPP(inferCmd); + assertThat( + "Results should contain divide by 0 error", + inferResults, + containsExactly( + DIVIDE_BY_ZERO, + FILE, + procedures + ) + ); + } + +}