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
master
Dino Distefano 9 years ago committed by Facebook Github Bot 9
parent 6aca1cdfef
commit b7ab1760a6

@ -195,9 +195,9 @@ struct
| _ -> ()); | _ -> ());
match dec with match dec with
(* Currently C/C++ record decl treated in the same way *) (* Currently C/C++ record decl treated in the same way *)
| ClassTemplateSpecializationDecl (decl_info, _, _, _, decl_list, _, _, _) | ClassTemplateSpecializationDecl (_, _, _, _, decl_list, _, _, _)
| CXXRecordDecl (decl_info, _, _, _, decl_list, _, _, _) | CXXRecordDecl (_, _, _, _, decl_list, _, _, _)
| RecordDecl (decl_info, _, _, _, decl_list, _, _) when not decl_info.di_is_implicit -> | RecordDecl (_, _, _, _, decl_list, _, _) ->
let is_method_decl decl = match decl with let is_method_decl decl = match decl with
| CXXMethodDecl _ | CXXConstructorDecl _ | CXXConversionDecl _ | CXXMethodDecl _ | CXXConstructorDecl _ | CXXConversionDecl _
| CXXDestructorDecl _ | FunctionTemplateDecl _ -> | CXXDestructorDecl _ | FunctionTemplateDecl _ ->

@ -386,6 +386,12 @@ struct
if decl_ptr' = (Some decl_ptr) then decl_opt if decl_ptr' = (Some decl_ptr) then decl_opt
else get_decl_opt decl_ptr' 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 = let rec getter_attribute_opt attributes =
match attributes with match attributes with
@ -617,6 +623,7 @@ struct
let is_cpp_translation language = let is_cpp_translation language =
language = CFrontend_config.CPP || language = CFrontend_config.OBJCPP language = CFrontend_config.CPP || language = CFrontend_config.OBJCPP
end end

@ -130,6 +130,9 @@ sig
val get_function_decl_with_body : Clang_ast_t.pointer -> Clang_ast_t.decl option 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 end
module General_utils : module General_utils :
@ -195,4 +198,5 @@ sig
Pvar.t Pvar.t
val is_cpp_translation : CFrontend_config.lang -> bool val is_cpp_translation : CFrontend_config.lang -> bool
end end

@ -462,6 +462,16 @@ let get_method_for_frontend_checks cfg cg loc =
Cg.add_defined_node cg proc_name; Cg.add_defined_node cg proc_name;
pdesc 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 = let instance_to_method_call_type instance =
if instance then MCVirtual if instance then MCVirtual

@ -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 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_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

@ -460,7 +460,7 @@ struct
let function_deref_trans trans_state decl_ref = let function_deref_trans trans_state decl_ref =
let open CContext in let open CContext in
let context = trans_state.context 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 let decl_opt = Ast_utils.get_function_decl_with_body decl_ptr in
Option.may (call_translation context) decl_opt; Option.may (call_translation context) decl_opt;
let name = Ast_utils.get_qualified_name name_info in 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 var_deref_trans trans_state stmt_info decl_ref =
let open CContext in let open CContext in
let context = trans_state.context 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 typ = CTypes_decl.type_ptr_to_sil_type context.tenv type_ptr in
let procname = Cfg.Procdesc.get_proc_name context.procdesc in let procname = Cfg.Procdesc.get_proc_name context.procdesc in
let sil_loc = CLocation.get_sil_location stmt_info context in let sil_loc = CLocation.get_sil_location stmt_info context in
@ -518,7 +518,7 @@ struct
let open CContext in let open CContext in
let context = trans_state.context in let context = trans_state.context in
let sil_loc = CLocation.get_sil_location stmt_info 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; 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 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 let (obj_sil, class_typ) = extract_exp_from_list pre_trans_result.exps
@ -556,7 +556,7 @@ struct
let open CContext in let open CContext in
let context = trans_state.context in let context = trans_state.context in
let sil_loc = CLocation.get_sil_location stmt_info 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 let decl_opt = Ast_utils.get_function_decl_with_body decl_ptr in
Option.may (call_translation context) decl_opt; Option.may (call_translation context) decl_opt;
let method_name = Ast_utils.get_unqualified_name name_info in let method_name = Ast_utils.get_unqualified_name name_info in
@ -701,7 +701,7 @@ struct
and enum_constant_trans trans_state decl_ref = and enum_constant_trans trans_state decl_ref =
let context = trans_state.context 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.CContext.tenv type_ptr 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 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)] } { empty_res_trans with exps = [(const_exp, typ)] }
@ -2082,6 +2082,21 @@ struct
else instruction trans_state' stmt in else instruction trans_state' stmt in
stmt_res_trans :: rest_stmts_res_trans 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 = and cxxNewExpr_trans trans_state stmt_info expr_info cxx_new_expr_info =
let context = trans_state.context in let context = trans_state.context in
let typ = CTypes_decl.get_type_from_expr_info expr_info context.CContext.tenv 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 (stmt_info, stmts, expr_info) ->
cxxStdInitializerListExpr_trans trans_state 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 | s -> (Printing.log_stats
"\n!!!!WARNING: found statement %s. \nACTION REQUIRED: \ "\n!!!!WARNING: found statement %s. \nACTION REQUIRED: \
Translation need to be defined. Statement ignored.... \n" Translation need to be defined. Statement ignored.... \n"

@ -595,12 +595,6 @@ let rec is_method_call s =
| [] -> false | [] -> false
| s'':: _ -> is_method_call s'') | 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 = let rec get_decl_ref_info s =
match s with match s with
| Clang_ast_t.DeclRefExpr (_, _, _, decl_ref_expr_info) -> | Clang_ast_t.DeclRefExpr (_, _, _, decl_ref_expr_info) ->

@ -95,9 +95,6 @@ val is_method_call : Clang_ast_t.stmt -> bool
val contains_opaque_value_expr : 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 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 -> val builtin_trans : trans_state -> Location.t -> Clang_ast_t.stmt_info ->

@ -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));
}

@ -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<String> 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
)
);
}
}
Loading…
Cancel
Save