diff --git a/facebook-clang-plugins b/facebook-clang-plugins index 5f2042abe..18ea5ae42 160000 --- a/facebook-clang-plugins +++ b/facebook-clang-plugins @@ -1 +1 @@ -Subproject commit 5f2042abe6cf15e2a17741259fa08c55bf61fb1b +Subproject commit 18ea5ae4205367ba3411210cb644ac983323818a diff --git a/infer/src/base/Logging.ml b/infer/src/base/Logging.ml index 298d08c66..6c003a967 100644 --- a/infer/src/base/Logging.ml +++ b/infer/src/base/Logging.ml @@ -233,9 +233,9 @@ let environment_info fmt = log ~to_console:false environment_info_file_fmts fmt let external_warning fmt = log ~to_console:(not Config.quiet) external_warning_file_fmts fmt -let external_error fmt = log ~to_console:(not Config.quiet) external_error_file_fmts fmt +let external_error fmt = log ~to_console:true external_error_file_fmts fmt -let internal_error fmt = log ~to_console:Config.developer_mode internal_error_file_fmts fmt +let internal_error fmt = log ~to_console:true internal_error_file_fmts fmt (** Type of location in ml source: __POS__ *) type ml_loc = string * int * int * int diff --git a/infer/src/clang/CType_decl.ml b/infer/src/clang/CType_decl.ml index c99f89b54..a3a6f97ad 100644 --- a/infer/src/clang/CType_decl.ml +++ b/infer/src/clang/CType_decl.ml @@ -222,17 +222,17 @@ and get_record_struct_type tenv definition_decl : Typ.desc = | Some _ -> sil_desc (* just reuse what is already in tenv *) | None - -> let is_complete_definition = record_decl_info.Clang_ast_t.rdi_is_complete_definition in + -> let is_translatable_definition = + let open Clang_ast_t in + record_decl_info.rdi_is_complete_definition + && not record_decl_info.rdi_is_dependent_type + in let extra_fields = if CTrans_models.is_objc_memory_model_controlled (Typ.Name.name sil_typename) then [Typ.Struct.objc_ref_counter_field] else [] in - let annots = - if Typ.Name.Cpp.is_class sil_typename then Annot.Class.cpp else Annot.Item.empty - (* No annotations for structs *) - in - if is_complete_definition then ( + if is_translatable_definition then ( CAst_utils.update_sil_types_map type_ptr sil_desc ; let non_statics = get_struct_fields tenv definition_decl in let fields = CGeneral_utils.append_no_duplicates_fields non_statics extra_fields in @@ -241,6 +241,10 @@ and get_record_struct_type tenv definition_decl : Typ.desc = let methods = [] in (* C++ methods are not put into tenv (info isn't used) *) let supers = get_superclass_list_cpp tenv definition_decl in + let annots = + if Typ.Name.Cpp.is_class sil_typename then Annot.Class.cpp + else (* No annotations for structs *) Annot.Item.empty + in Tenv.mk_struct tenv ~fields ~statics ~methods ~supers ~annots sil_typename |> ignore ; CAst_utils.update_sil_types_map type_ptr sil_desc ; sil_desc ) diff --git a/infer/src/clang/cAst_utils.ml b/infer/src/clang/cAst_utils.ml index bc18148db..d1add6194 100644 --- a/infer/src/clang/cAst_utils.ml +++ b/infer/src/clang/cAst_utils.ml @@ -136,8 +136,8 @@ let get_desugared_type type_ptr = let get_decl_from_typ_ptr typ_ptr = let typ_opt = get_desugared_type typ_ptr in let typ = match typ_opt with Some t -> t | _ -> assert false in - match typ with - | Clang_ast_t.RecordType (_, decl_ptr) | Clang_ast_t.ObjCInterfaceType (_, decl_ptr) + match (typ : Clang_ast_t.c_type) with + | RecordType (_, decl_ptr) | ObjCInterfaceType (_, decl_ptr) -> get_decl decl_ptr | _ -> None diff --git a/infer/src/clang/cFrontend_decl.ml b/infer/src/clang/cFrontend_decl.ml index f0f86bc15..b61771f74 100644 --- a/infer/src/clang/cFrontend_decl.ml +++ b/infer/src/clang/cFrontend_decl.ml @@ -8,11 +8,35 @@ *) open! IStd +module F = Format (** Translate declarations **) module L = Logging +let protect ~f ~recover ~pp_context = + let log_and_recover ~print fmt = + recover () ; + (if print then L.internal_error else L.(debug Capture Quiet)) ("%a@\n" ^^ fmt) pp_context () + in + try f () with + (* Always keep going in case of known limitations of the frontend, crash otherwise (by not + catching the exception) unless `--keep-going` was passed. Print errors we should fix + (t21762295) to the console. *) + | CFrontend_config.Unimplemented msg + -> log_and_recover ~print:false "Unimplemented feature:@\n %s@\n" msg + | CFrontend_config.IncorrectAssumption msg + -> (* FIXME(t21762295): we do not expect this to happen but it does *) + log_and_recover ~print:true "Known incorrect assumption in the frontend: %s@\n" msg + | CTrans_utils.Self.SelfClassException class_name + -> (* FIXME(t21762295): we do not expect this to happen but it does *) + log_and_recover ~print:true "Unexpected SelfClassException %a@\n" Typ.Name.pp class_name + | exn + -> let trace = Exn.backtrace () in + reraise_if exn ~f:(fun () -> + L.internal_error "%a: %a@\n%!" pp_context () Exn.pp exn ; not Config.keep_going ) ; + log_and_recover ~print:true "Frontend error: %a@\nBacktrace:@\n%s" Exn.pp exn trace + module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFrontend = struct let model_exists procname = Specs.summary_exists_in_models procname && not Config.models_mode @@ -21,52 +45,38 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron body has_return_param is_objc_method outer_context_opt extra_instrs = L.(debug Capture Verbose) "@\n@\n>>---------- ADDING METHOD: '%a' ---------<<@\n@\n" Typ.Procname.pp procname ; - let handle_frontend_failure ~print fmt = + let recover () = Cfg.remove_proc_desc cfg procname ; - CMethod_trans.create_external_procdesc cfg procname is_objc_method None ; - (if print then L.internal_error else L.(debug Capture Quiet)) - ("Aborting translation of method '%a':@\n" ^^ fmt) Typ.Procname.pp procname + CMethod_trans.create_external_procdesc cfg procname is_objc_method None in - try + let pp_context fmt () = + F.fprintf fmt "Aborting translation of method '%a'" Typ.Procname.pp procname + in + let f () = match Cfg.find_proc_desc_from_name cfg procname with - | Some procdesc - -> if Procdesc.is_defined procdesc && not (model_exists procname) then - let vars_to_destroy = CTrans_utils.Scope.compute_vars_to_destroy body in - let context = - CContext.create_context trans_unit_ctx tenv cg cfg procdesc class_decl_opt - has_return_param is_objc_method outer_context_opt vars_to_destroy - in - let start_node = Procdesc.get_start_node procdesc in - let exit_node = Procdesc.get_exit_node procdesc in - L.(debug Capture Verbose) - "@\n@\n>>---------- Start translating body of function: '%s' ---------<<@\n@." - (Typ.Procname.to_string procname) ; - let meth_body_nodes = - T.instructions_trans context body extra_instrs exit_node ~is_destructor_wrapper - in - let proc_attributes = Procdesc.get_attributes procdesc in - Procdesc.Node.add_locals_ret_declaration start_node proc_attributes - (Procdesc.get_locals procdesc) ; - Procdesc.node_set_succs_exn procdesc start_node meth_body_nodes [] ; - Cg.add_defined_node (CContext.get_cg context) (Procdesc.get_proc_name procdesc) - | None + | Some procdesc when Procdesc.is_defined procdesc && not (model_exists procname) + -> let vars_to_destroy = CTrans_utils.Scope.compute_vars_to_destroy body in + let context = + CContext.create_context trans_unit_ctx tenv cg cfg procdesc class_decl_opt + has_return_param is_objc_method outer_context_opt vars_to_destroy + in + let start_node = Procdesc.get_start_node procdesc in + let exit_node = Procdesc.get_exit_node procdesc in + L.(debug Capture Verbose) + "@\n@\n>>---------- Start translating body of function: '%s' ---------<<@\n@." + (Typ.Procname.to_string procname) ; + let meth_body_nodes = + T.instructions_trans context body extra_instrs exit_node ~is_destructor_wrapper + in + let proc_attributes = Procdesc.get_attributes procdesc in + Procdesc.Node.add_locals_ret_declaration start_node proc_attributes + (Procdesc.get_locals procdesc) ; + Procdesc.node_set_succs_exn procdesc start_node meth_body_nodes [] ; + Cg.add_defined_node (CContext.get_cg context) (Procdesc.get_proc_name procdesc) + | _ -> () - with - (* Always keep going in case of known limitations of the frontend, crash otherwise (by not - catching the exception) unless `--keep-going` was passed. Print errors we should fix - (t21762295) to the console. *) - | CFrontend_config.Unimplemented msg - -> handle_frontend_failure ~print:false "Unimplemented feature:@\n %s@\n" msg - | CFrontend_config.IncorrectAssumption msg - -> (* FIXME(t21762295): we do not expect this to happen but it does *) - handle_frontend_failure ~print:true "Known incorrect assumption in the frontend: %s@\n" msg - | CTrans_utils.Self.SelfClassException class_name - -> (* FIXME(t21762295): we do not expect this to happen but it does *) - handle_frontend_failure ~print:true "Unexpected SelfClassException %a@\n" Typ.Name.pp - class_name - | exn when Config.keep_going - -> handle_frontend_failure ~print:true "Frontend error: %a@\nBacktrace:@\n%s" Exn.pp exn - (Exn.backtrace ()) + in + protect ~f ~recover ~pp_context let function_decl trans_unit_ctx tenv cfg cg func_decl block_data_opt = let captured_vars, outer_context_opt = @@ -242,7 +252,9 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron let open Clang_ast_t in (* each procedure has different scope: start names from id 0 *) Ident.NameGenerator.reset () ; - let translate = translate_one_declaration trans_unit_ctx tenv cg cfg decl_trans_context in + let translate dec = + translate_one_declaration trans_unit_ctx tenv cg cfg decl_trans_context dec + in ( if should_translate_decl trans_unit_ctx dec decl_trans_context then let dec_ptr = (Clang_ast_proj.get_decl_tuple dec).di_pointer in match dec with @@ -336,7 +348,10 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron in let method_decls, no_method_decls = List.partition_tf ~f:is_method_decl decl_list in List.iter ~f:translate no_method_decls ; - ignore (CType_decl.add_types_from_decl_to_tenv tenv dec) ; + protect ~f:(fun () -> ignore (CType_decl.add_types_from_decl_to_tenv tenv dec)) + ~recover:Fn.id ~pp_context:(fun fmt () -> + F.fprintf fmt "Error adding types from decl '%a'" + (Pp.to_string ~f:Clang_ast_j.string_of_decl) dec ) ; List.iter ~f:translate method_decls | _ -> () ) ; diff --git a/infer/tests/codetoanalyze/cpp/errors/issues.exp b/infer/tests/codetoanalyze/cpp/errors/issues.exp index cce567224..16cef79c6 100644 --- a/infer/tests/codetoanalyze/cpp/errors/issues.exp +++ b/infer/tests/codetoanalyze/cpp/errors/issues.exp @@ -204,6 +204,7 @@ codetoanalyze/cpp/errors/subtyping/dynamic_cast.cpp, dynamic__cast::wrongReferen codetoanalyze/cpp/errors/subtyping/implicit_cast_with_const.cpp, implicit_cast_with_const::BaseDerefNPE, 2, NULL_DEREFERENCE, [start of procedure implicit_cast_with_const::BaseDerefNPE(),start of procedure Base,return from a call to implicit_cast_with_const::Base_Base,start of procedure implicit_cast_with_const::deref()] codetoanalyze/cpp/errors/subtyping/implicit_cast_with_const.cpp, implicit_cast_with_const::DerivedDerefNPE, 2, NULL_DEREFERENCE, [start of procedure implicit_cast_with_const::DerivedDerefNPE(),start of procedure Derived,start of procedure Base,return from a call to implicit_cast_with_const::Base_Base,return from a call to implicit_cast_with_const::Derived_Derived,start of procedure implicit_cast_with_const::deref()] codetoanalyze/cpp/errors/subtyping/subtyping_check.cpp, B_setFG, 4, DIVIDE_BY_ZERO, [start of procedure setFG,start of procedure setF,return from a call to A_setF,Condition is true] +codetoanalyze/cpp/errors/templates/dependent_parent.cpp, instantiate_class_bad, 2, DIVIDE_BY_ZERO, [start of procedure instantiate_class_bad(),start of procedure X,return from a call to X_X,start of procedure x_y,start of procedure Y,start of procedure Z,return from a call to Z_Z,return from a call to X::Y_Y,start of procedure y,start of procedure z,return from a call to Z_z,return from a call to X::Y_y,return from a call to X_x_y] codetoanalyze/cpp/errors/templates/mangling.cpp, bad_integral_types_templates, 4, DIVIDE_BY_ZERO, [start of procedure bad_integral_types_templates(),start of procedure IntTemplate,return from a call to IntTemplate<0>_IntTemplate,start of procedure CharTemplate,return from a call to CharTemplate<99>_CharTemplate,start of procedure LongTemplate,return from a call to LongTemplate<1234567890>_LongTemplate] codetoanalyze/cpp/errors/templates/mangling.cpp, bad_nullptr_templates, 2, DIVIDE_BY_ZERO, [start of procedure bad_nullptr_templates(),start of procedure NullPtrTemplate,return from a call to NullPtrTemplate_NullPtrTemplate] codetoanalyze/cpp/errors/templates/mangling.cpp, bad_packed_templates, 2, DIVIDE_BY_ZERO, [start of procedure bad_packed_templates(),start of procedure Tuple,return from a call to Tuple>_Tuple] diff --git a/infer/tests/codetoanalyze/cpp/errors/templates/dependent_parent.cpp b/infer/tests/codetoanalyze/cpp/errors/templates/dependent_parent.cpp new file mode 100644 index 000000000..cfbe38ca2 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/errors/templates/dependent_parent.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2017 - 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. + */ +struct Y { + int y(); +}; + +template +struct X { + struct Y; + int x_y() { + struct Y y; + return y.y(); + } +}; + +template +struct Z { + int z() { return 3; }; +}; + +template +struct X::Y : Z { + int y() { return Y::z() - 3; } +}; + +void instantiate_class_bad() { + struct X x; + int n = 1 / x.x_y(); +}