From 6d7521809b9947c7cd254460f4f16a4738d9163f Mon Sep 17 00:00:00 2001 From: Andrzej Kotulski Date: Tue, 8 Dec 2015 09:30:18 -0800 Subject: [PATCH] Update fcp to get automatic location visitor Summary: public Use generic location visitor from facebook-clang-plugins Therefore, there is no need to write our custom visitor which is hard to maintain This introduces some level of magic, but makes it easier to maintain the code. Reviewed By: jvillard Differential Revision: D2734282 fb-gh-sync-id: ed9711a --- facebook-clang-plugins | 2 +- infer/src/clang/cAstProcessor.ml | 351 ------------------ infer/src/clang/cAstProcessor.mli | 18 - infer/src/clang/cMain.ml | 8 +- .../c/frontend/loops/for_only_body.c.dot | 4 +- .../errors/npe/Nonnull_attribute_example.dot | 4 +- 6 files changed, 7 insertions(+), 380 deletions(-) delete mode 100644 infer/src/clang/cAstProcessor.ml delete mode 100644 infer/src/clang/cAstProcessor.mli diff --git a/facebook-clang-plugins b/facebook-clang-plugins index 545b4f6e2..07c2aabb2 160000 --- a/facebook-clang-plugins +++ b/facebook-clang-plugins @@ -1 +1 @@ -Subproject commit 545b4f6e27473523ad624e9a6305d7fa234946d4 +Subproject commit 07c2aabb21fb201ac01b261095b665a53a00a9a1 diff --git a/infer/src/clang/cAstProcessor.ml b/infer/src/clang/cAstProcessor.ml deleted file mode 100644 index 64bb0541f..000000000 --- a/infer/src/clang/cAstProcessor.ml +++ /dev/null @@ -1,351 +0,0 @@ -(* - * 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. - *) - -(** Module to preprocess location information in the AST. - The original location information is incremental, each location is a delta - w.r.t. the previous one. This module processes the AST and makes locations explicit. *) - -open Utils - -module L = Logging -module F = Format - - -(** Get the sub-declarations of the current declaration. *) -let decl_get_sub_decls decl = - let open Clang_ast_t in - match decl with - | CXXRecordDecl (_, _, _, _, decl_list, _, _, _) - | ClassTemplateSpecializationDecl (_, _, _, _, decl_list, _, _, _) - | RecordDecl (_, _, _, _, decl_list, _, _) - | ObjCInterfaceDecl (_, _, decl_list, _, _) - | ObjCProtocolDecl (_, _, decl_list, _, _) - | ObjCCategoryDecl (_, _, decl_list, _, _) - | ObjCCategoryImplDecl (_, _, decl_list, _, _) - | ObjCImplementationDecl (_, _, decl_list, _, _) - | EnumDecl (_, _, _, _, decl_list, _, _) - | LinkageSpecDecl (_, decl_list, _) - | NamespaceDecl (_, _, decl_list, _, _) -> - decl_list - | ClassTemplateDecl (_, _, template_decl_info) - | FunctionTemplateDecl (_, _, template_decl_info) -> - template_decl_info.tdi_specializations - | _ -> - [] - - -(** Set the sub-declarations of the current declaration. *) -let decl_set_sub_decls decl decl_list' = - let open Clang_ast_t in - match decl with - | CXXRecordDecl (decl_info, name, opt_type, type_ptr, decl_list, decl_context_info, record_decl_info, cxx_record_info) -> - CXXRecordDecl (decl_info, name, opt_type, type_ptr, decl_list', decl_context_info, record_decl_info, cxx_record_info) - | ClassTemplateSpecializationDecl (decl_info, name, opt_type, type_ptr, decl_list, decl_context_info, record_decl_info, cxx_record_info) -> - ClassTemplateSpecializationDecl (decl_info, name, opt_type, type_ptr, decl_list', decl_context_info, record_decl_info, cxx_record_info) - | RecordDecl (decl_info, name, opt_type, type_ptr, decl_list, decl_context_info, record_decl_info) -> - RecordDecl (decl_info, name, opt_type, type_ptr, decl_list', decl_context_info, record_decl_info) - | ObjCInterfaceDecl (decl_info, name, decl_list, decl_context_info, obj_c_interface_decl_info) -> - ObjCInterfaceDecl (decl_info, name, decl_list', decl_context_info, obj_c_interface_decl_info) - | ObjCProtocolDecl (decl_info, name, decl_list, decl_context_info, obj_c_protocol_decl_info) -> - ObjCProtocolDecl (decl_info, name, decl_list', decl_context_info, obj_c_protocol_decl_info) - | ObjCCategoryDecl (decl_info, name, decl_list, decl_context_info, category_decl_info) -> - ObjCCategoryDecl (decl_info, name, decl_list', decl_context_info, category_decl_info) - | ObjCCategoryImplDecl (decl_info, name, decl_list, decl_context_info, category_impl_info) -> - ObjCCategoryImplDecl (decl_info, name, decl_list', decl_context_info, category_impl_info) - | ObjCImplementationDecl (decl_info, class_name, decl_list, decl_context_info, idi) -> - ObjCImplementationDecl (decl_info, class_name, decl_list', decl_context_info, idi) - | EnumDecl (decl_info, name, opt_type, type_ptr, decl_list, decl_context_info, enum_decl_info) -> - EnumDecl (decl_info, name, opt_type, type_ptr, decl_list', decl_context_info, enum_decl_info) - | LinkageSpecDecl (decl_info, decl_list, decl_context_info) -> - LinkageSpecDecl (decl_info, decl_list', decl_context_info) - | NamespaceDecl (decl_info, name, decl_list, decl_context_info, namespace_decl_info) -> - NamespaceDecl (decl_info, name, decl_list', decl_context_info, namespace_decl_info) - | ClassTemplateDecl (decl_info, name, template_decl_info) -> - let template_decl_info' = { tdi_specializations = decl_list' } in - ClassTemplateDecl (decl_info, name, template_decl_info') - | FunctionTemplateDecl (decl_info, name, template_decl_info) -> - let template_decl_info' = { tdi_specializations = decl_list' } in - FunctionTemplateDecl (decl_info, name, template_decl_info') - | _ -> - decl - - -(** Pretty print a source location. *) -let pp_source_loc fmt source_loc = - let file = match source_loc.Clang_ast_t.sl_file with - | Some file -> file - | None -> "None" in - let line = match source_loc.Clang_ast_t.sl_line with - | Some n -> string_of_int n - | None -> "None" in - let column = match source_loc.Clang_ast_t.sl_column with - | Some n -> string_of_int n - | None -> "None" in - if file = "None" && line = "None" && column = "None" - then F.fprintf fmt "_" - else F.fprintf fmt "%s:%s:%s" file line column - - -(** Pretty print a source range. *) -let pp_source_range fmt (sloc1, sloc2) = - F.fprintf fmt "<%a, %a>" pp_source_loc sloc1 pp_source_loc sloc2 - - -(** Pretty print an AST. *) -let pp_ast_decl fmt ast_decl = - let rec dump_stmt prefix stmt = - let prefix1 = prefix ^ " " in - let stmt_str = Clang_ast_proj.get_stmt_kind_string stmt in - let stmt_info, stmt_list = Clang_ast_proj.get_stmt_tuple stmt in - let decl_list = match stmt with - | Clang_ast_t.DeclStmt (_, _, decl_list) -> decl_list - | Clang_ast_t.BlockExpr (_, _, _, decl) -> [decl] - | _ -> [] in - F.fprintf fmt "%s%s %a@\n" - prefix - stmt_str - pp_source_range stmt_info.Clang_ast_t.si_source_range; - IList.iter (dump_stmt prefix1) stmt_list; - IList.iter (dump_decl prefix1) decl_list - and dump_decl prefix decl = - let prefix1 = prefix ^ " " in - let open Clang_ast_t in - match decl with - | BlockDecl (decl_info, block_decl_info) -> - F.fprintf fmt "%sBlockDecl %a@\n" - prefix - pp_source_range decl_info.di_source_range; - Option.may (dump_stmt prefix1) block_decl_info.bdi_body - | FunctionDecl (decl_info, name, tp, fdecl_info) -> - F.fprintf fmt "%sFunctionDecl %s %a@\n" - prefix - name.Clang_ast_t.ni_name - pp_source_range decl_info.di_source_range; - IList.iter (dump_decl prefix1) fdecl_info.fdi_decls_in_prototype_scope; - IList.iter (dump_decl prefix1) fdecl_info.fdi_parameters; - Option.may (dump_stmt prefix1) fdecl_info.fdi_body - | ObjCMethodDecl (decl_info, name, obj_c_method_decl_info) -> - F.fprintf fmt "%sObjCMethodDecl %s %a@\n" - prefix - name.Clang_ast_t.ni_name - pp_source_range decl_info.di_source_range; - Option.may (dump_stmt prefix1) obj_c_method_decl_info.omdi_body - | VarDecl (decl_info, name, type_ptr, var_decl_info) -> - F.fprintf fmt "%sVarDecl %a@\n" - prefix - pp_source_range decl_info.di_source_range; - Option.may (dump_stmt prefix1) var_decl_info.vdi_init_expr - | _ -> - let decl_kind_str = Clang_ast_proj.get_decl_kind_string decl in - let decl_info = Clang_ast_proj.get_decl_tuple decl in - let decl_list = decl_get_sub_decls decl in - F.fprintf fmt "%s%s %a@\n" - prefix - decl_kind_str - pp_source_range decl_info.di_source_range; - IList.iter (dump_decl prefix1) decl_list in - - let decl_str = Clang_ast_proj.get_decl_kind_string ast_decl in - match ast_decl with - | Clang_ast_t.TranslationUnitDecl (_, decl_list, _, _) -> - F.fprintf fmt "%s (%d declarations)@\n" decl_str (IList.length decl_list); - IList.iter (dump_decl "") decl_list - | _ -> - assert false - - -(** Compose incremental location information and make locations explicit. *) -module LocComposer : sig - (** Status of the composer. *) - type status - - (** Create a new composer with the initial status. *) - val create : unit -> status - - (** Compose a new source_range to the current one. *) - val compose : status -> Clang_ast_t.source_range -> Clang_ast_t.source_range - - (** Set the current file if specified in the source_range. - The composer will not descend into file included from the current one. - For locations in included files, it will return instead the last known - location of the current file. *) - val set_current_file : status -> Clang_ast_t.source_range -> unit -end = struct - type status = - { mutable curr_file: string option; - mutable curr_source_range: Clang_ast_t.source_range; - mutable in_curr_file : bool } - - let empty_sloc = { Clang_ast_t.sl_file = None; sl_line = None; sl_column = None } - - let create () = - { curr_file = None; - curr_source_range = (empty_sloc, empty_sloc); - in_curr_file = true; } - - let set_current_file st (sloc1, sloc2) = - match sloc1.Clang_ast_t.sl_file, sloc2.Clang_ast_t.sl_file with - | _, Some fname - | Some fname, None -> - st.curr_file <- Some fname; - st.in_curr_file <- true - | _ -> - () - - let sloc_is_current_file st sloc = match st.curr_file, sloc.Clang_ast_t.sl_file with - | Some curr_f, Some f -> - Some (f = curr_f) - | None, _ -> None - | _, None -> None - - let update_sloc st old_sloc new_sloc = - match sloc_is_current_file st new_sloc with - | Some true -> - st.in_curr_file <- true; - new_sloc - | Some false -> - st.in_curr_file <- false; - old_sloc - | None -> - if st.in_curr_file - then - let update x_opt y_opt = - if y_opt <> None then y_opt else x_opt in - let open Clang_ast_t in - { sl_file = update old_sloc.sl_file new_sloc.sl_file; - sl_line = update old_sloc.sl_line new_sloc.sl_line; - sl_column = update old_sloc.sl_column new_sloc.sl_column } - else - old_sloc - - let update_status st (sloc1, sloc2) = - if sloc1 = empty_sloc && sloc2 = empty_sloc - then - () - else - let _, old_sloc2 = st.curr_source_range in - let new_sloc1 = update_sloc st old_sloc2 sloc1 in - let new_sloc2 = update_sloc st new_sloc1 sloc2 in - st.curr_source_range <- (new_sloc1, new_sloc2) - - let compose st source_range = - update_status st source_range; - st.curr_source_range -end - - -let ctor_initializer_process_locs loc_composer ctor_init = - let range' = LocComposer.compose loc_composer ctor_init.Clang_ast_t.xci_source_range in - { ctor_init with Clang_ast_t.xci_source_range = range'} - -(** Apply a location composer to the locations in a statement. *) -let rec stmt_process_locs loc_composer stmt = - let update (stmt_info, stmt_list) = - let range' = LocComposer.compose loc_composer stmt_info.Clang_ast_t.si_source_range in - let stmt_info' = - { stmt_info with - Clang_ast_t.si_source_range = range' } in - let stmt_list' = IList.map (stmt_process_locs loc_composer) stmt_list in - (stmt_info', stmt_list') in - let open Clang_ast_t in - match Clang_ast_proj.update_stmt_tuple update stmt with - | DeclStmt (stmt_info, stmt_list, decl_list) -> - let decl_list' = IList.map (decl_process_locs loc_composer) decl_list in - DeclStmt (stmt_info, stmt_list, decl_list') - | BlockExpr (stmt_info, stmt_list, expr_info, block_decl) -> - let block_decl' = decl_process_locs loc_composer block_decl in - BlockExpr (stmt_info, stmt_list, expr_info, block_decl') - | OpaqueValueExpr (stmt_info, stmt_list, expr_info, opaque_value_ei) -> - let source_expr = opaque_value_ei.ovei_source_expr in - let ovei_source_expr' = Option.map (stmt_process_locs loc_composer) source_expr in - let opaque_value_ei' = { ovei_source_expr = ovei_source_expr' } in - OpaqueValueExpr (stmt_info, stmt_list, expr_info, opaque_value_ei') - | stmt' -> - stmt' - -(** Apply a location composer to the locations in a declaration. *) -and decl_process_locs loc_composer decl = - let decl' = - let update decl_info = - let range' = LocComposer.compose loc_composer decl_info.Clang_ast_t.di_source_range in - { decl_info with - Clang_ast_t.di_source_range = range' } in - let decl_list = decl_get_sub_decls decl in - let decl1 = Clang_ast_proj.update_decl_tuple update decl in - let decl_list' = IList.map (decl_process_locs loc_composer) decl_list in - decl_set_sub_decls decl1 decl_list' in - let open Clang_ast_t in - let get_updated_fun_decl (decl_info', name, tp, fdecl_info) = - let fdi_decls_in_prototype_scope' = - IList.map (decl_process_locs loc_composer) fdecl_info.fdi_decls_in_prototype_scope in - let fdi_parameters' = - IList.map (decl_process_locs loc_composer) fdecl_info.fdi_parameters in - let body' = Option.map (stmt_process_locs loc_composer) fdecl_info.fdi_body in - let fdecl_info' = - { fdecl_info with - fdi_body = body'; - fdi_parameters = fdi_parameters'; - fdi_decls_in_prototype_scope = fdi_decls_in_prototype_scope'; } in - (decl_info', name, tp, fdecl_info') in - let get_updated_method_decl (decl_info', name, tp, fdecl_info, method_info) = - let di', n', tp', fdi' = get_updated_fun_decl (decl_info', name, tp, fdecl_info) in - let ctor_init = method_info.xmdi_cxx_ctor_initializers in - let ctor_init' = IList.map (ctor_initializer_process_locs loc_composer) ctor_init in - let method_info' = { method_info with xmdi_cxx_ctor_initializers = ctor_init'} in - (di', n', tp', fdi', method_info') in - match decl' with - | BlockDecl (decl_info', block_decl_info) -> - let parameters' = IList.map (decl_process_locs loc_composer) block_decl_info.bdi_parameters in - let body' = - Option.map (stmt_process_locs loc_composer) block_decl_info.bdi_body in - let block_decl_info' = - { block_decl_info with - bdi_body = body'; - bdi_parameters = parameters'; } in - BlockDecl (decl_info', block_decl_info') - | FunctionDecl fun_info -> FunctionDecl (get_updated_fun_decl fun_info) - | CXXMethodDecl meth_info -> CXXMethodDecl (get_updated_method_decl meth_info) - | CXXConstructorDecl meth_info -> CXXConstructorDecl (get_updated_method_decl meth_info) - | ObjCMethodDecl (decl_info', name, obj_c_method_decl_info) -> - let body' = - Option.map (stmt_process_locs loc_composer) obj_c_method_decl_info.omdi_body in - let obj_c_method_decl_info' = { obj_c_method_decl_info with omdi_body = body' } in - ObjCMethodDecl (decl_info', name, obj_c_method_decl_info') - | VarDecl (decl_info, string, type_ptr, var_decl_info) -> - let vdi_init_expr' = - Option.map (stmt_process_locs loc_composer) var_decl_info.vdi_init_expr in - let var_decl_info' = - { var_decl_info with vdi_init_expr = vdi_init_expr' } in - VarDecl (decl_info, string, type_ptr, var_decl_info') - | _ -> - decl' - - -(** Process locations in the AST by making them explicit. - Each toplevel declaration determines the current file, - and once diving into the details of the declaration, location - information about other (include) files is ignored. *) -let ast_decl_process_locs loc_composer ast_decl = - - let toplevel_decl_process_locs decl = - let decl_info = Clang_ast_proj.get_decl_tuple decl in - LocComposer.set_current_file loc_composer decl_info.Clang_ast_t.di_source_range; - decl_process_locs loc_composer decl in - - match ast_decl with - | Clang_ast_t.TranslationUnitDecl (decl_info, decl_list, decl_context_info, type_list) -> - let decl_list' = IList.map toplevel_decl_process_locs decl_list in - Clang_ast_t.TranslationUnitDecl (decl_info, decl_list', decl_context_info, type_list) - | _ -> - assert false - - -let preprocess_ast_decl ast_decl = - let loc_composer = LocComposer.create () in - ast_decl_process_locs loc_composer ast_decl diff --git a/infer/src/clang/cAstProcessor.mli b/infer/src/clang/cAstProcessor.mli deleted file mode 100644 index f0719083a..000000000 --- a/infer/src/clang/cAstProcessor.mli +++ /dev/null @@ -1,18 +0,0 @@ -(* - * 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. - *) - -(** Module to preprocess location information in the AST. - The original location information is incremental, each location is a delta - w.r.t. the previous one. This module processes the AST and makes locations explicit. *) - -(** Pretty print an AST. *) -val pp_ast_decl : Format.formatter -> Clang_ast_t.decl -> unit - -(** Preprocess the AST to make locations explicit. *) -val preprocess_ast_decl : Clang_ast_t.decl -> Clang_ast_t.decl diff --git a/infer/src/clang/cMain.ml b/infer/src/clang/cMain.ml index acb1e0f32..357f4b7f4 100644 --- a/infer/src/clang/cMain.ml +++ b/infer/src/clang/cMain.ml @@ -97,18 +97,14 @@ let do_run source_path ast_path = | Some path -> path, validate_decl_from_file path | None -> "stdin of " ^ source_path, validate_decl_from_stdin () in - let ast_decl' = CAstProcessor.preprocess_ast_decl ast_decl in - Printing.log_out "Original AST@.%a@." CAstProcessor.pp_ast_decl ast_decl; - Printing.log_out "AST with explicit locations:@.%a@." CAstProcessor.pp_ast_decl ast_decl'; - - let decl_index, _, type_index = Clang_ast_main.index_node_pointers ast_decl' in + let decl_index, _, type_index = Clang_ast_main.index_node_pointers ast_decl in CFrontend_config.pointer_decl_index := decl_index; CFrontend_config.pointer_type_index := type_index; CFrontend_config.json := ast_filename; CLocation.check_source_file source_path; let source_file = CLocation.source_file_from_path source_path in print_endline ("Start translation of AST from " ^ !CFrontend_config.json); - CFrontend.do_source_file source_file ast_decl'; + CFrontend.do_source_file source_file ast_decl; print_endline ("End translation AST file " ^ !CFrontend_config.json ^ "... OK!") with (Yojson.Json_error s) as exc -> Printing.log_err "%s\n" s; diff --git a/infer/tests/codetoanalyze/c/frontend/loops/for_only_body.c.dot b/infer/tests/codetoanalyze/c/frontend/loops/for_only_body.c.dot index b49e57561..5e3d1871c 100644 --- a/infer/tests/codetoanalyze/c/frontend/loops/for_only_body.c.dot +++ b/infer/tests/codetoanalyze/c/frontend/loops/for_only_body.c.dot @@ -7,11 +7,11 @@ digraph iCFG { 7 -> 4 ; -6 [label="6: Prune (false branch) \n PRUNE((1 == 0), false); [line 12]\n " shape="invhouse"] +6 [label="6: Prune (false branch) \n PRUNE((1 == 0), false); [line 14]\n " shape="invhouse"] 6 -> 3 ; -5 [label="5: Prune (true branch) \n PRUNE((1 != 0), true); [line 12]\n " shape="invhouse"] +5 [label="5: Prune (true branch) \n PRUNE((1 != 0), true); [line 14]\n " shape="invhouse"] 5 -> 7 ; diff --git a/infer/tests/codetoanalyze/objc/errors/npe/Nonnull_attribute_example.dot b/infer/tests/codetoanalyze/objc/errors/npe/Nonnull_attribute_example.dot index 378ffbcc8..3db2f9364 100644 --- a/infer/tests/codetoanalyze/objc/errors/npe/Nonnull_attribute_example.dot +++ b/infer/tests/codetoanalyze/objc/errors/npe/Nonnull_attribute_example.dot @@ -14,7 +14,7 @@ digraph iCFG { 14 -> 17 ; -13 [label="13: Call _fun___infer_assume \n n$10=*&name:class NSString * [line -1]\n n$11=_fun___infer_assume((n$10 != 0):class NSString *) [line -1]\n REMOVE_TEMPS(n$10,n$11); [line -1]\n " shape="box"] +13 [label="13: Call _fun___infer_assume \n n$10=*&name:class NSString * [line 31]\n n$11=_fun___infer_assume((n$10 != 0):class NSString *) [line 31]\n REMOVE_TEMPS(n$10,n$11); [line 31]\n " shape="box"] 13 -> 12 ; @@ -29,7 +29,7 @@ digraph iCFG { 10 -> 13 ; -9 [label="9: Call _fun___infer_assume \n n$5=*&a:class A * [line -1]\n n$6=_fun___infer_assume((n$5 != 0):class A *) [line -1]\n REMOVE_TEMPS(n$5,n$6); [line -1]\n " shape="box"] +9 [label="9: Call _fun___infer_assume \n n$5=*&a:class A * [line 37]\n n$6=_fun___infer_assume((n$5 != 0):class A *) [line 37]\n REMOVE_TEMPS(n$5,n$6); [line 37]\n " shape="box"] 9 -> 8 ;